diff --git a/Services/ConfigService.cs b/Services/ConfigService.cs index 6769955..bf8c349 100644 --- a/Services/ConfigService.cs +++ b/Services/ConfigService.cs @@ -92,44 +92,16 @@ public class ConfigService : IConfigService 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"); + // Return the configured SiteId as-is. No normalization. + // Returns empty string if not configured. + return _config.SiteId ?? string.Empty; } - public void SetSiteId(string siteId) + 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; + // Store the SiteId exactly as provided. No automatic normalization. + // Caller is responsible for formatting. + _config.SiteId = siteId ?? string.Empty; SaveConfig(); } @@ -246,22 +218,30 @@ public class ConfigService : IConfigService { try { - if (!File.Exists(_configPath)) + if (!File.Exists(_configPath)) { - var defaults = new AppConfig(); + System.Diagnostics.Debug.WriteLine("[ConfigService] Config file not found. Creating with defaults."); + var defaults = new AppConfig(); SaveConfig(defaults); - return defaults; + return defaults; } - var json = File.ReadAllText(_configPath); - var config = JsonSerializer.Deserialize(json); - config ??= new AppConfig(); + var json = File.ReadAllText(_configPath); + var config = JsonSerializer.Deserialize(json); + config ??= new AppConfig(); MigrateScanIntervalFromSecondsIfNeeded(config); - return config; + + System.Diagnostics.Debug.WriteLine($"[ConfigService] Config loaded from {_configPath}"); + System.Diagnostics.Debug.WriteLine($"[ConfigService] SiteId: '{config.SiteId}' (empty=not configured)"); + System.Diagnostics.Debug.WriteLine($"[ConfigService] MySqlConnectionString: {(string.IsNullOrWhiteSpace(config.MySqlConnectionString) ? "NOT CONFIGURED" : MaskConnectionString(config.MySqlConnectionString))}"); + System.Diagnostics.Debug.WriteLine($"[ConfigService] HrmsLookupConnectionString: {(string.IsNullOrWhiteSpace(config.HrmsLookupConnectionString) ? "NOT CONFIGURED" : MaskConnectionString(config.HrmsLookupConnectionString))}"); + + return config; } - catch + catch (Exception ex) { - return new AppConfig(); + System.Diagnostics.Debug.WriteLine($"[ConfigService] ERROR loading config: {ex.Message}"); + return new AppConfig(); } } @@ -308,18 +288,49 @@ public class ConfigService : IConfigService { try { - var json = JsonSerializer.Serialize(config, new JsonSerializerOptions + // Log what's being saved + System.Diagnostics.Debug.WriteLine($"[ConfigService] Saving config..."); + System.Diagnostics.Debug.WriteLine($"[ConfigService] SiteId: '{_config.SiteId}'"); + System.Diagnostics.Debug.WriteLine($"[ConfigService] MySqlConnectionString: {(string.IsNullOrWhiteSpace(_config.MySqlConnectionString) ? "NOT CONFIGURED" : MaskConnectionString(_config.MySqlConnectionString))}"); + System.Diagnostics.Debug.WriteLine($"[ConfigService] HrmsLookupConnectionString: {(string.IsNullOrWhiteSpace(_config.HrmsLookupConnectionString) ? "NOT CONFIGURED" : MaskConnectionString(_config.HrmsLookupConnectionString))}"); + + var json = JsonSerializer.Serialize(config, new JsonSerializerOptions { WriteIndented = true }); File.WriteAllText(_configPath, json); + System.Diagnostics.Debug.WriteLine($"[ConfigService] Config saved to {_configPath}"); } - catch + catch (Exception ex) { - // Ignore write failures (e.g., read-only location). + System.Diagnostics.Debug.WriteLine($"[ConfigService] ERROR saving config: {ex.Message}"); } } + /// + /// Masks sensitive connection string values for logging. + /// Shows server and database but hides password. + /// + private static string MaskConnectionString(string connectionString) + { + if (string.IsNullOrWhiteSpace(connectionString)) + return string.Empty; + + // Simple masking: show everything before 'Password=' and mask the rest + var passwordIndex = connectionString.IndexOf("Password=", StringComparison.OrdinalIgnoreCase); + if (passwordIndex >= 0) + { + var semicolonAfterPassword = connectionString.IndexOf(';', passwordIndex); + if (semicolonAfterPassword >= 0) + { + return connectionString.Substring(0, passwordIndex) + "Password=***" + connectionString.Substring(semicolonAfterPassword); + } + return connectionString.Substring(0, passwordIndex) + "Password=***"; + } + + return connectionString; // No password found, return as-is + } + private sealed class AppConfig { public string SyncApiEndpoint { get; set; } = "https://api.example.com/uind/sync"; @@ -328,10 +339,10 @@ public class ConfigService : IConfigService public int ScanTimeoutSeconds { get; set; } = 0; public int ScanIntervalDays { get; set; } = 0; public int ScanIntervalHours { get; set; } = 0; - public int ScanIntervalMinutes { get; set; } = 1; - public int ScanIntervalSeconds { get; set; } = 0; + public int ScanIntervalMinutes { get; set; } = 0; + public int ScanIntervalSeconds { get; set; } = 2; public string AdminCardId { get; set; } = "ADMIN"; - public string SiteId { get; set; } = "SITE : 1"; + public string SiteId { get; set; } = string.Empty; // No default - must be explicitly configured public string DeviceId { get; set; } = string.Empty; // Admin credential persistence (optional). @@ -343,6 +354,6 @@ public class ConfigService : IConfigService public string MySqlConnectionString { get; set; } = string.Empty; // Local HRMS MySQL for employee lookup by RFID. Separate from production sync. - public string HrmsLookupConnectionString { get; set; } = "Server=192.168.90.147;Port=3306;Database=hrms;User Id=utopia;Password=Utopia01;SslMode=None;"; + public string HrmsLookupConnectionString { get; set; } = string.Empty; } } diff --git a/Services/ProductionMealScheduleService.cs b/Services/ProductionMealScheduleService.cs index 3454b16..beafb5a 100644 --- a/Services/ProductionMealScheduleService.cs +++ b/Services/ProductionMealScheduleService.cs @@ -5,7 +5,7 @@ namespace UtopiaCanteenSystem.Services; /// /// Meal schedule CRUD against production HRMS MySQL table hrms.meal_schedule. -/// Uses IConfigService.GetHrmsLookupConnectionString() (same DB as employee lookup). +/// Uses IConfigService.GetMySqlConnectionString() (production database connection). /// Schema: id (bigint), meal_name (varchar), start_time (time), end_time (time), created_at, updated_at, location_site_id (int). /// No is_active column; all rows are treated as active. /// @@ -22,7 +22,7 @@ public class ProductionMealScheduleService : IMealScheduleService /// public IReadOnlyList GetActiveSchedulesForSite(string siteId) { - var connStr = _configService.GetHrmsLookupConnectionString(); + var connStr = _configService.GetMySqlConnectionString(); if (string.IsNullOrWhiteSpace(connStr)) return Array.Empty(); @@ -49,7 +49,7 @@ public class ProductionMealScheduleService : IMealScheduleService /// public async Task> GetAllSchedulesAsync(CancellationToken cancellationToken = default) { - var connStr = _configService.GetHrmsLookupConnectionString(); + var connStr = _configService.GetMySqlConnectionString(); if (string.IsNullOrWhiteSpace(connStr)) return Array.Empty(); @@ -67,9 +67,9 @@ public class ProductionMealScheduleService : IMealScheduleService /// public async Task CreateAsync(MealSchedule schedule, CancellationToken cancellationToken = default) { - var connStr = _configService.GetHrmsLookupConnectionString(); + var connStr = _configService.GetMySqlConnectionString(); if (string.IsNullOrWhiteSpace(connStr)) - throw new InvalidOperationException("HRMS MySQL connection string not configured."); + throw new InvalidOperationException("Production MySQL connection string not configured."); var utc = DateTime.UtcNow; var mealName = (schedule.MealName ?? string.Empty).Trim(); @@ -93,9 +93,9 @@ public class ProductionMealScheduleService : IMealScheduleService /// public async Task UpdateAsync(MealSchedule schedule, CancellationToken cancellationToken = default) { - var connStr = _configService.GetHrmsLookupConnectionString(); + var connStr = _configService.GetMySqlConnectionString(); if (string.IsNullOrWhiteSpace(connStr)) - throw new InvalidOperationException("HRMS MySQL connection string not configured."); + throw new InvalidOperationException("Production MySQL connection string not configured."); var utc = DateTime.UtcNow; var mealName = (schedule.MealName ?? string.Empty).Trim(); @@ -117,9 +117,9 @@ public class ProductionMealScheduleService : IMealScheduleService /// public async Task DeleteAsync(long id, CancellationToken cancellationToken = default) { - var connStr = _configService.GetHrmsLookupConnectionString(); + var connStr = _configService.GetMySqlConnectionString(); if (string.IsNullOrWhiteSpace(connStr)) - throw new InvalidOperationException("HRMS MySQL connection string not configured."); + throw new InvalidOperationException("Production MySQL connection string not configured."); await using var conn = new MySqlConnection(connStr); await conn.OpenAsync(cancellationToken).ConfigureAwait(false); diff --git a/ViewModels/ScannerDashboardViewModel.cs b/ViewModels/ScannerDashboardViewModel.cs index 625ec44..a38ca40 100644 --- a/ViewModels/ScannerDashboardViewModel.cs +++ b/ViewModels/ScannerDashboardViewModel.cs @@ -215,13 +215,21 @@ public partial class ScannerDashboardViewModel : ObservableObject _clockTimer.Tick += (_, _) => CurrentTime = DateTime.Now.ToString("MM/dd/yyyy, hh:mm:ss tt"); _clockTimer.Start(); - // Load initial site from config (e.g. "SITE : 1" -> "1"). - var siteId = _configService.GetSiteId(); - if (!string.IsNullOrWhiteSpace(siteId) && siteId.StartsWith("SITE : ", StringComparison.OrdinalIgnoreCase)) + // Load initial site from config. + var siteId = _configService.GetSiteId(); + if (!string.IsNullOrWhiteSpace(siteId)) { - var num = siteId.Substring("SITE : ".Length).Trim(); - if (num.Length > 0 && num.All(char.IsDigit)) - SiteNumber = num; + // Extract digits from legacy format "SITE : X"or use value directly if already numeric + if (siteId.StartsWith("SITE : ", StringComparison.OrdinalIgnoreCase)) + { + var num = siteId.Substring("SITE : ".Length).Trim(); + if (num.Length > 0 && num.All(char.IsDigit)) + SiteNumber = num; + } + else if (siteId.All(char.IsDigit)) + { + SiteNumber = siteId; + } } RefreshScannerStatus(); diff --git a/appsettings.example.json b/appsettings.example.json index a6ce857..d0f6780 100644 --- a/appsettings.example.json +++ b/appsettings.example.json @@ -3,13 +3,14 @@ "ScannerConnected": false, "ScanIntervalDays": 0, "ScanIntervalHours": 0, - "ScanIntervalMinutes": 1, + "ScanIntervalMinutes": 0, + "ScanIntervalSeconds": 2, "AdminCardId": "ADMIN", "SiteId": "02", "DeviceId": "", "RememberAdminCredentials": false, "SavedAdminUsername": "", "SavedAdminPasswordProtected": "", - "MySqlConnectionString": "Server=localhost;Database=hrms;User=root;Password=CHANGE_ME;Port=3306;" + "MySqlConnectionString": "SERVER=utopia-industries-rr.c5qech8o9lgg.us-east-1.rds.amazonaws.com;DATABASE=hrms;UID=uind_hrms_user;PASSWORD=UINDHRMS01;" }