using System.Text.Json; using System.Text.Json.Serialization; namespace Hokm.Server.Site; /// Admin-editable links + flags shown on the marketing site (bargevasat.ir). public class SiteLinks { // Android stores public string BazaarUrl { get; set; } = ""; public bool BazaarEnabled { get; set; } = false; public string MyketUrl { get; set; } = ""; public bool MyketEnabled { get; set; } = false; // Direct APK (optional, for sideloading) public string DirectApkUrl { get; set; } = ""; public bool DirectApkEnabled { get; set; } = false; // Play on web / PWA public string WebPlayUrl { get; set; } = "https://app.bargevasat.ir"; public bool IosPwaEnabled { get; set; } = true; // iOS = Add to Home Screen // Socials / support public string Instagram { get; set; } = ""; public string Telegram { get; set; } = ""; public string SupportEmail { get; set; } = ""; public string SupportPhone { get; set; } = ""; public string AppVersion { get; set; } = ""; } /// Shared-token admin auth (set ADMIN_TOKEN in ENV_FILE). public class AdminOptions { public string Token { get; set; } = ""; } /// /// Loads/persists as a JSON file under a writable data dir /// (mount a volume at it in prod). No DB migration required. /// public class SiteLinksService { private readonly string _path; private readonly object _gate = new(); private SiteLinks _current; private static readonly JsonSerializerOptions JsonOpts = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, DefaultIgnoreCondition = JsonIgnoreCondition.Never, WriteIndented = true, }; public SiteLinksService(IConfiguration config) { var dataDir = config["Site:DataDir"]; if (string.IsNullOrWhiteSpace(dataDir)) dataDir = "/data"; try { Directory.CreateDirectory(dataDir); } catch { /* fall back below */ } if (!CanWrite(dataDir)) dataDir = AppContext.BaseDirectory; // dev fallback _path = Path.Combine(dataDir, "site-links.json"); _current = Load() ?? Seed(config); // Persist the seed so the file exists for the admin to edit. if (!File.Exists(_path)) TrySave(_current); } public SiteLinks Get() { lock (_gate) return Clone(_current); } public SiteLinks Update(SiteLinks next) { lock (_gate) { _current = next; TrySave(_current); return Clone(_current); } } private SiteLinks? Load() { try { if (!File.Exists(_path)) return null; return JsonSerializer.Deserialize(File.ReadAllText(_path), JsonOpts); } catch { return null; } } private void TrySave(SiteLinks v) { try { File.WriteAllText(_path, JsonSerializer.Serialize(v, JsonOpts)); } catch { /* read-only fs in dev — keep in-memory only */ } } // Seed defaults from config (Site section) when no file exists yet. private static SiteLinks Seed(IConfiguration config) { var seeded = config.GetSection("Site:Links").Get(); return seeded ?? new SiteLinks(); } private static SiteLinks Clone(SiteLinks v) => JsonSerializer.Deserialize(JsonSerializer.Serialize(v, JsonOpts), JsonOpts)!; private static bool CanWrite(string dir) { try { var probe = Path.Combine(dir, ".write-test"); File.WriteAllText(probe, "ok"); File.Delete(probe); return true; } catch { return false; } } }