using System.Text.Json; using System.Text.Json.Nodes; using SoroushAsadi.Data; namespace SoroushAsadi.Services; /// /// Merges hardcoded default content with admin overrides stored in SQLite. /// Sections are keyed by name ("hero", "services", etc.). /// The stored JSON is {"fa": {...}, "en": {...}} for bilingual sections, /// or a slug-keyed map for "posts". /// public class ContentService(AppDbContext db) { private static readonly JsonSerializerOptions _json = new() { PropertyNameCaseInsensitive = true }; // ── Public API ──────────────────────────────────────────────────────── /// Returns the merged content for a section as a JsonNode. /// Callers get the locale-specific sub-object (e.g. node["en"]). public JsonNode? GetSection(string key) { try { var row = db.ContentSections.Find(key); if (row is null) return null; return JsonNode.Parse(row.DataJson); } catch { return null; } } /// Returns merged bilingual content for the given section + locale. public JsonNode? GetSectionLocale(string key, string locale) { var node = GetSection(key); return node?[locale]; } /// Returns all section rows (for admin listing). public IReadOnlyList GetSectionKeys() => db.ContentSections.Select(s => s.Key).ToList(); /// Upserts a section's JSON data. public void SaveSection(string key, string json) { var row = db.ContentSections.Find(key); if (row is null) { row = new Models.ContentSection { Key = key }; db.ContentSections.Add(row); } row.DataJson = json; row.UpdatedAt = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); db.SaveChanges(); } /// Deletes a section override (restores built-in default). public void DeleteSection(string key) { var row = db.ContentSections.Find(key); if (row is not null) { db.ContentSections.Remove(row); db.SaveChanges(); } } // ── Posts (stored under key "posts") ───────────────────────────────── public const string PostsKey = "posts"; /// Returns the slug→PostContent map from DB, or empty. public Dictionary GetPostOverrides() { try { var row = db.ContentSections.Find(PostsKey); if (row is null) return []; var parsed = JsonNode.Parse(row.DataJson); if (parsed is not JsonObject obj) return []; return obj.ToDictionary(kv => kv.Key, kv => kv.Value!); } catch { return []; } } /// Saves a single post override. public void SavePost(string slug, JsonNode content) { var row = db.ContentSections.Find(PostsKey); JsonObject obj; if (row is null) { obj = []; row = new Models.ContentSection { Key = PostsKey }; db.ContentSections.Add(row); } else { obj = JsonNode.Parse(row.DataJson) as JsonObject ?? []; } obj[slug] = content.DeepClone(); row.DataJson = obj.ToJsonString(); row.UpdatedAt = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); db.SaveChanges(); } }