81 lines
4.3 KiB
Markdown
81 lines
4.3 KiB
Markdown
# Domain model
|
|
|
|
## Site (§12.1)
|
|
|
|
```
|
|
Site: id UUID, name TEXT, description TEXT NULL, canonical_url TEXT,
|
|
default_theme_id TEXT, created_at/updated_at TIMESTAMPTZ
|
|
```
|
|
|
|
Even though v1 proposes one site per installation, `site_id` scopes persisted data, leaving a migration path to multi-site without designing tenancy now.
|
|
|
|
## Content entry (§12.2)
|
|
|
|
```
|
|
ContentEntry: id UUID, site_id UUID, content_type TEXT, slug TEXT, title TEXT,
|
|
status TEXT, excerpt TEXT NULL, published_at TIMESTAMPTZ NULL,
|
|
current_revision_id UUID NULL, created_at/updated_at, deleted_at NULL
|
|
```
|
|
|
|
Initial statuses: `draft`, `published`. Initial registered content type: `core.post`. Future content types are registry contributions, not `if type === ...` branches.
|
|
|
|
## Content revision (§12.3)
|
|
|
|
```
|
|
ContentRevision: id UUID, content_id UUID, revision_number BIGINT,
|
|
document_version INTEGER, document JSONB, created_by UUID, created_at
|
|
```
|
|
|
|
Unique `(content_id, revision_number)`.
|
|
|
|
## Identifier policy (§12.4)
|
|
|
|
PostgreSQL 18 provides UUIDv7 generation — use UUIDv7 for major aggregates (globally unique opaque IDs with better temporal/index locality than UUIDv4).
|
|
|
|
## Canonical content document (§13)
|
|
|
|
A post body is a versioned semantic block document:
|
|
|
|
```json
|
|
{ "version": 1, "blocks": [ { "id": "019d...", "type": "core.paragraph", "version": 1, "props": { "text": "Hello world." } } ] }
|
|
```
|
|
|
|
Every block persists block instance ID, globally unique block type ID, block schema version, and validated properties. Theme CSS classes / concrete theme markup must not be stored as semantic content.
|
|
|
|
## `core.markdown` block (§13.1, v1.1)
|
|
|
|
Markdown authoring was a locked product decision omitted from v1.0; it is restored as a block inside the block model. `props.source` is CommonMark + GFM (tables, strikethrough, task lists, autolinks) + footnotes; raw HTML disabled. Rules:
|
|
|
|
1. Exactly **one** server-side Markdown renderer ending in one sanitiser with an explicit allowlist; the admin preview calls it (no second client-side renderer).
|
|
2. A v0.1 post is a single `core.markdown` block by default — editor is a Markdown text editor with server-rendered preview.
|
|
3. When the block editor arrives, new content splits into finer blocks; existing `core.markdown` blocks are never bulk-converted.
|
|
4. Export emits `props.source` as `.md` files with frontmatter — the portability guarantee.
|
|
|
|
## Initial blocks (§14)
|
|
|
|
`core.heading`, `core.paragraph`, `core.quote`, `core.code`, `core.divider`, `core.image` (once media ships). **v1.1:** `core.markdown` joins the set as the primary authoring block.
|
|
|
|
Missing block behaviour (§14.1): public page still renders with a safe placeholder (no raw props, error logs include IDs); admin shows the missing dependency and preserves the original payload (no silent deletion).
|
|
|
|
## Page composition (§15)
|
|
|
|
Home is an ordered list of versioned section instances:
|
|
|
|
```json
|
|
{ "version": 1, "sections": [ { "id": "home-intro", "type": "core.site-intro", "version": 1, "enabled": true, "settings": {} }, { "id": "home-posts", "type": "core.post-list", "version": 1, "enabled": true, "settings": { "limit": 20 } } ] }
|
|
```
|
|
|
|
Initial composition UI supports only enable / disable / reorder / configure. Explicitly excluded: arbitrary pixel positioning, nested no-code canvas, user-authored raw HTML layouts, raw CSS editor.
|
|
|
|
## Navigation (§28)
|
|
|
|
Persisted model: id, site_id, label, destination_type, destination, position, enabled, open_in_new_context, created_at, updated_at. Initial destination types: `internal_route`, `external_url`. Semantic `<nav>`, editable labels/order/visibility, external URL validation, disabled-extension destination yields an admin warning (never a silently broken link).
|
|
|
|
## Column vs JSONB (§23.3)
|
|
|
|
Normal columns for data routinely constrained/joined/sorted/indexed/referenced/filtered. JSONB for versioned block props, settings, page section configuration, and sparse namespaced metadata.
|
|
|
|
## Core table namespace (§23.1)
|
|
|
|
`eppp_sites`, `eppp_site_settings`, `eppp_users`, `eppp_admin_sessions`, `eppp_content`, `eppp_content_revisions`, `eppp_page_compositions`, `eppp_navigation`, `eppp_extensions`, `eppp_extension_settings`, `eppp_extension_migrations`, `eppp_visitor_identities`, `eppp_visitor_preferences`, `eppp_media`, `eppp_jobs`, `eppp_job_runs`, `eppp_core_migrations`. Extension tables use the `ext_<name>_<table>` prefix.
|