- Entities: Site (singleton, navOrder JSON), Page (slug/title/seoDescription/status enum),
Block (single table, Type enum + Data JSON; RichText=Markdown), Media. Migration
AddCmsEntities, seeds 6 Jimdo-mirror pages (placeholder Heading each; abgabetiere also an
auto AbgabetiereList block) + Site singleton nav.
- GET /api/site-snapshot: one JSON doc (site nav + pages + ordered blocks {id,order,type,data});
AbgabetiereList(auto) resolved from live ForSale gerbils -> {name, farbschlag, group(=Becken),
photos[urls], aiSaleText:null} (no price per god). SiteSnapshotService.
- CRUD: GET/POST/PUT/DELETE pages; add/update/delete blocks; PUT .../blocks/order reorder.
Block type allowlisted by enum; data validated as JSON object.
81 lines
2.7 KiB
C#
81 lines
2.7 KiB
C#
using System.ComponentModel.DataAnnotations;
|
|
|
|
namespace GerbilManagerWebAPI.Models
|
|
{
|
|
/// <summary>Publication state of a CMS page (serialised as string name).</summary>
|
|
public enum PageStatus
|
|
{
|
|
Draft = 0,
|
|
Published = 1,
|
|
}
|
|
|
|
/// <summary>
|
|
/// CMS block kinds. RichText holds MARKDOWN (sanitised on render). AbgabetiereList is
|
|
/// dynamic — resolved from live ForSale animals at snapshot time. Serialised as string.
|
|
/// </summary>
|
|
public enum BlockType
|
|
{
|
|
Heading = 0,
|
|
RichText = 1,
|
|
Image = 2,
|
|
Gallery = 3,
|
|
ContactInfo = 4,
|
|
AbgabetiereList = 5,
|
|
}
|
|
|
|
/// <summary>Singleton site settings (WEB epic CMS).</summary>
|
|
public class Site
|
|
{
|
|
/// <summary>Fixed id — there is exactly one Site row.</summary>
|
|
public static readonly Guid SingletonId = new("5172e000-0000-0000-0000-000000000001");
|
|
|
|
[Key]
|
|
public Guid Id { get; set; }
|
|
public string DefaultLocale { get; set; } = "de";
|
|
/// <summary>Ordered page ids for the public nav (JSON list).</summary>
|
|
public List<Guid> NavOrder { get; set; } = new();
|
|
}
|
|
|
|
/// <summary>A CMS page (one of the public website's pages).</summary>
|
|
public class Page
|
|
{
|
|
[Key]
|
|
public Guid Id { get; set; }
|
|
/// <summary>Stable URL slug (e.g. "abgabetiere") — the human key.</summary>
|
|
public required string Slug { get; set; }
|
|
public required string Title { get; set; }
|
|
public string? SeoDescription { get; set; }
|
|
public PageStatus Status { get; set; } = PageStatus.Draft;
|
|
|
|
public ICollection<Block> Blocks { get; } = new List<Block>();
|
|
}
|
|
|
|
/// <summary>
|
|
/// A content block on a page (single table, discriminated by <see cref="Type"/>).
|
|
/// Type-specific payload lives in <see cref="Data"/> as JSON (RichText = Markdown).
|
|
/// </summary>
|
|
public class Block
|
|
{
|
|
[Key]
|
|
public Guid Id { get; set; }
|
|
public Guid PageId { get; set; }
|
|
/// <summary>Explicit ordering within the page (AI tools reorder by this).</summary>
|
|
public int Order { get; set; }
|
|
public BlockType Type { get; set; }
|
|
/// <summary>Type-specific fields as a JSON object string.</summary>
|
|
public string Data { get; set; } = "{}";
|
|
}
|
|
|
|
/// <summary>A media asset referenced by Image/Gallery blocks.</summary>
|
|
public class Media
|
|
{
|
|
[Key]
|
|
public Guid Id { get; set; }
|
|
public required string FileName { get; set; }
|
|
public required string Url { get; set; }
|
|
public string? Alt { get; set; }
|
|
public int? Width { get; set; }
|
|
public int? Height { get; set; }
|
|
}
|
|
}
|