Files

66 lines
6.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Web Manager
The CMS behind the public website. Editors assemble localized pages from ready-made blocks in the
admin panel; the public Next.js site renders what they publish.
| Part | Source | Owns |
| ---------------------- | ------------------------------------------- | -------------------------------------------------------- |
| Backend (`WebManager`) | `apps/backend/app/base_modules/WebManager/` | Data, validation, draft/publish transactions, APIs |
| Admin (`Web Studio`) | `apps/admin/src/core/WebStudio/` | Editor UI, drafts, publishing actions, preview, settings |
| Public site | `apps/web/web-runtime/`, `apps/web/app/` | Server rendering, layouts, blocks, caching, SEO |
## Documentation
| Page | Purpose |
| ------------------------------- | ------------------------------------------------------------------ |
| [Backend](./backend.md) | Data model, HTTP API, query budget, tuning constants. |
| [Admin](./admin.md) | Web Studio: routes, editor, config files, UI conventions. |
| [Web](./web.md) | Public site rendering, layouts, hero, SEO, degradation. |
| [Integration](./integration.md) | Contracts, cross-app flows, caching, env wiring, extending, tests. |
## For Editors
A page is a title, an optional hero, an ordered list of blocks, and SEO fields. Blocks are the
prebuilt sections — text columns, FAQ, media, banner, article list — so building a page is picking
blocks and filling them in, not writing markup.
| Term | What it means |
| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| **Page** | One address in one language, e.g. `/en/about-us`. |
| **Group key** | Ties the same page across languages together (`about-us` links `/en/about-us` and `/uk/pro-nas`) so the site can offer a language switch. |
| **Page type** | Landing, Blog, or Blog item. Decides the layout and which blocks are offered. |
| **Hero** | The opening section. Four intensities: none, low, middle, high. |
| **Draft** | Your working copy. Never visible to visitors. |
| **Published** | Live on the site. |
| **Unpublished** | Taken down, but kept — content, blocks and address stay reserved, so you can put it back. |
| **Preview** | A private, time-limited link that renders the draft exactly as the site would. |
| **Site settings** | Header menus, footer, and legal links — shared by every page in one language. |
Everything you edit is a draft. A live page keeps serving the old version until you press Publish,
so an unfinished edit can never reach visitors. Nothing is deleted unless you press Delete.
## Page Lifecycle
```txt
create publish
(none) ────────► draft ───────────► published ◄──────┐
│ │ │
discard│ │unpublish │publish
▼ ▼ │
deleted unpublished ───────┘
published + open draft clone: visitors see the published row, the editor edits the clone
```
Every write happens in one transaction with rows locked `FOR UPDATE`; routes and permissions are in [backend.md](./backend.md), purge behavior in [integration.md](./integration.md#caching-and-revalidation).
- **Create** — `createDraft` validates the payload, asserts the `locale` + `path` is free across every root row (any status), and inserts the page with `status = draft` plus its components. Nothing public yet.
- **Save a draft** — `ensureDraft` edits a `draft` or `unpublished` page in place; for a `published` page it reuses or creates the draft clone. Components are replaced wholesale in the submitted order; a clone inherits its original's address.
- **Edit a published page** — the clone is a second row with `draft_of_id` pointing at the original (one clone per page, enforced by a partial unique index). Edit always opens the clone; the original keeps serving until the clone is published.
- **Preview** — a signed token (HMAC over `APP_KEY`, TTL 900s clamped to 60–3600s) lets the web app render the draft in an iframe sheet: `force-dynamic`, `noindex`, disallowed in robots. Absent, tampered, or expired tokens render "preview unavailable".
- **Publish** — publishes the **last saved** draft (the confirmation dialog says so). A pending clone is always preferred, so publishing can never drop the editor's work: a root draft goes live directly; a clone is folded into its original and deleted. A _structural_ change (first publish, republish, or changed locale/path/title/group key/page type) purges the whole cache tag; anything else purges only the affected page tags.
- **Unpublish** — same permission as publish; only valid on a published page (else 409). Sets `status = unpublished`, keeping content, components, and any open clone; the address stays reserved. Full purge — the URL now 404s.
- **Republish** — publish again; an unpublished page is edited in place, and republishing counts as structural.
- **Discard a draft** — deletes the draft row and its components; a clone's original is untouched. No purge.
- **Delete** — the only destructive action, behind a confirmation dialog. Components and any open clone cascade with the row; full purge if it was published.