// ===================================================================== // RETAIL PHARMACY PLATFORM — Counter Helper // HelperConfig.cs // // The token is generated once, at first run, and never transmitted // anywhere. The setup wizard on the local node reads it from this file // and stores it in the node's `setting` table (scope COUNTER, key // helper_token). Both sides are on the same machine, so it never // crosses a wire. // ===================================================================== using System; using System.IO; using System.Security.Cryptography; using System.Text.Json; using System.Text.Json.Serialization; namespace CounterHelper; public sealed class HelperConfig { [JsonPropertyName("port")] public int Port { get; set; } = 7331; [JsonPropertyName("token")] public string Token { get; set; } = ""; [JsonPropertyName("counter_id")] public string CounterId { get; set; } = ""; [JsonPropertyName("default_printer")] public string DefaultPrinter { get; set; } = ""; [JsonPropertyName("data_dir")] public string DataDir { get; set; } = ""; [JsonPropertyName("update_url")] public string UpdateUrl { get; set; } = ""; [JsonPropertyName("update_pubkey")] public string UpdatePubKey { get; set; } = ""; private static string DefaultDataDir => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "CaresoftCounter"); public static HelperConfig Load() { var dir = DefaultDataDir; Directory.CreateDirectory(dir); var path = Path.Combine(dir, "helper.json"); HelperConfig cfg; if (File.Exists(path)) { cfg = JsonSerializer.Deserialize(File.ReadAllText(path)) ?? new HelperConfig(); } else { cfg = new HelperConfig(); } if (string.IsNullOrWhiteSpace(cfg.DataDir)) cfg.DataDir = dir; if (string.IsNullOrWhiteSpace(cfg.Token)) cfg.Token = NewToken(); // Pick a sensible default printer on first run so the setup // wizard has something to test with rather than an empty box. if (string.IsNullOrWhiteSpace(cfg.DefaultPrinter)) { try { var printers = RawPrinter.List(); foreach (var p in printers) { var u = p.ToUpperInvariant(); // The hardware actually behind Indian chemist counters. if (u.Contains("LX-300") || u.Contains("EPSON") || u.Contains("TVS") || u.Contains("POS") || u.Contains("THERMAL")) { cfg.DefaultPrinter = p; break; } } if (string.IsNullOrWhiteSpace(cfg.DefaultPrinter) && printers.Count > 0) cfg.DefaultPrinter = printers[0]; } catch { /* leave blank; the wizard will ask */ } } Save(cfg, path); return cfg; } private static void Save(HelperConfig cfg, string path) { var tmp = path + ".tmp"; File.WriteAllText(tmp, JsonSerializer.Serialize(cfg, new JsonSerializerOptions { WriteIndented = true })); if (File.Exists(path)) File.Delete(path); File.Move(tmp, path); } /// 32 bytes of CSPRNG, hex encoded. private static string NewToken() { var b = new byte[32]; RandomNumberGenerator.Fill(b); return Convert.ToHexString(b).ToLowerInvariant(); } }