Utopia-Canteen-System/Services/ConfigService.cs

251 lines
7.1 KiB
C#

using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using UtopiaCanteenSystem.Data;
namespace UtopiaCanteenSystem.Services;
/// <summary>
/// JSON-backed configuration (including remember-me credentials) stored under
/// LocalApplicationData so it survives app updates when deployed from a file server.
/// </summary>
public class ConfigService : IConfigService
{
private readonly string _configPath;
private AppConfig _config;
public ConfigService()
{
_configPath = DatabasePath.GetConfigPath();
_config = LoadConfig();
}
public string GetSyncApiEndpoint() => _config.SyncApiEndpoint ?? string.Empty;
public void SetSyncApiEndpoint(string endpoint)
{
_config.SyncApiEndpoint = endpoint ?? string.Empty;
SaveConfig();
}
public bool GetScannerConnected() => _config.ScannerConnected;
public void SetScannerConnected(bool connected)
{
_config.ScannerConnected = connected;
SaveConfig();
}
public int GetScanTimeoutSeconds() => _config.ScanTimeoutSeconds;
public void SetScanTimeoutSeconds(int seconds)
{
_config.ScanTimeoutSeconds = seconds;
SaveConfig();
}
public string GetAdminCardId() => _config.AdminCardId ?? string.Empty;
public void SetAdminCardId(string cardId)
{
_config.AdminCardId = cardId ?? string.Empty;
SaveConfig();
}
public string GetSiteId()
{
// Normalize to a 2-digit string (e.g. "02", "07", "12").
// Accepts legacy values like "SITE : 2" or "2" and converts them.
var raw = _config.SiteId;
if (string.IsNullOrWhiteSpace(raw))
return "01";
var digits = new string(raw.Where(char.IsDigit).ToArray());
if (string.IsNullOrWhiteSpace(digits))
return "01";
if (!int.TryParse(digits, out var numeric))
return "01";
return Math.Clamp(numeric, 0, 99).ToString("D2");
}
public void SetSiteId(string siteId)
{
// Store the normalized 2-digit site code.
var normalized = siteId;
if (string.IsNullOrWhiteSpace(normalized))
{
normalized = "01";
}
else
{
var digits = new string(normalized.Where(char.IsDigit).ToArray());
if (string.IsNullOrWhiteSpace(digits) || !int.TryParse(digits, out var numeric))
{
normalized = "01";
}
else
{
normalized = Math.Clamp(numeric, 0, 99).ToString("D2");
}
}
_config.SiteId = normalized;
SaveConfig();
}
public string GetDeviceId()
{
//var id = _config.DeviceId ?? string.Empty;
var id = Environment.MachineName;
if (string.IsNullOrWhiteSpace(id))
{
id = Guid.NewGuid().ToString("N");
_config.DeviceId = id;
SaveConfig();
}
return id;
}
public void SetDeviceId(string deviceId)
{
_config.DeviceId = deviceId ?? string.Empty;
SaveConfig();
}
public bool GetRememberAdminCredentials() => _config.RememberAdminCredentials;
public void SetRememberAdminCredentials(bool remember)
{
_config.RememberAdminCredentials = remember;
SaveConfig();
}
public string GetSavedAdminUsername() => _config.SavedAdminUsername ?? string.Empty;
public void SetSavedAdminUsername(string username)
{
_config.SavedAdminUsername = username ?? string.Empty;
SaveConfig();
}
public string GetSavedAdminPassword()
{
if (!_config.RememberAdminCredentials)
return string.Empty;
var protectedValue = _config.SavedAdminPasswordProtected ?? string.Empty;
if (string.IsNullOrWhiteSpace(protectedValue))
return string.Empty;
try
{
var bytes = Convert.FromBase64String(protectedValue);
var clear = ProtectedData.Unprotect(bytes, null, DataProtectionScope.CurrentUser);
return Encoding.UTF8.GetString(clear);
}
catch
{
return string.Empty;
}
}
public void SetSavedAdminPassword(string password)
{
if (!_config.RememberAdminCredentials)
{
_config.SavedAdminPasswordProtected = string.Empty;
SaveConfig();
return;
}
try
{
var clear = Encoding.UTF8.GetBytes(password ?? string.Empty);
var protectedBytes = ProtectedData.Protect(clear, null, DataProtectionScope.CurrentUser);
_config.SavedAdminPasswordProtected = Convert.ToBase64String(protectedBytes);
}
catch
{
_config.SavedAdminPasswordProtected = string.Empty;
}
SaveConfig();
}
/// <summary>
/// Returns MySQL connection string for sync. Set via appsettings.json (copy from appsettings.example.json).
/// No default credentials; returns empty if not configured.
/// </summary>
public string GetMySqlConnectionString() => (_config.MySqlConnectionString ?? string.Empty).Trim();
public void SetMySqlConnectionString(string connectionString)
{
_config.MySqlConnectionString = connectionString ?? string.Empty;
SaveConfig();
}
private AppConfig LoadConfig()
{
try
{
if (!File.Exists(_configPath))
{
var defaults = new AppConfig();
SaveConfig(defaults);
return defaults;
}
var json = File.ReadAllText(_configPath);
var config = JsonSerializer.Deserialize<AppConfig>(json);
return config ?? new AppConfig();
}
catch
{
return new AppConfig();
}
}
private void SaveConfig()
{
SaveConfig(_config);
}
private void SaveConfig(AppConfig config)
{
try
{
var json = JsonSerializer.Serialize(config, new JsonSerializerOptions
{
WriteIndented = true
});
File.WriteAllText(_configPath, json);
}
catch
{
// Ignore write failures (e.g., read-only location).
}
}
private sealed class AppConfig
{
public string SyncApiEndpoint { get; set; } = "https://api.example.com/uind/sync";
public bool ScannerConnected { get; set; } = false;
public int ScanTimeoutSeconds { get; set; } = 60;
public string AdminCardId { get; set; } = "ADMIN";
public string SiteId { get; set; } = "SITE : 1";
public string DeviceId { get; set; } = string.Empty;
// Admin credential persistence (optional).
public bool RememberAdminCredentials { get; set; } = false;
public string SavedAdminUsername { get; set; } = string.Empty;
public string SavedAdminPasswordProtected { get; set; } = string.Empty;
// MySQL connection string for sync. Set in appsettings.json (see appsettings.example.json). No default.
public string MySqlConnectionString { get; set; } = string.Empty;
}
}