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; }
}
}