-
Notifications
You must be signed in to change notification settings - Fork 0
Admin Panel
apps/admin (package cms-admin): one FastAPI process serving the API and the server-rendered UI — Jinja templates on AdminLTE 4 (MIT, vendored verbatim with its license; Bootstrap 5 bundled), used CSS-only: the admin ships zero JavaScript, no CDN, no build step. It drives cms-core, cms-validation and cms-build through their public APIs and never bypasses them.
Try it without installing anything: the demo site ships a read-only static snapshot at https://sardine.ph7x.com/admin/ — the real admin's pages, captured with demo content; nothing there saves, by construction.
pip install -e apps/admin
cms admin create-user editor-in-chief --role admin # prompts for a password
SARDINE_STORAGE_URL="sqlite:///content.sqlite3" \
SARDINE_ADMIN_COOKIE_SECURE=0 \
uvicorn --factory cms_admin.app:create_appSign in at /login. There are no default credentials — the first account always comes from the CLI.
| Variable | Default | Meaning |
|---|---|---|
SARDINE_STORAGE_URL |
sqlite:///content.db |
Storage URL (create_storage) |
SARDINE_ADMIN_SESSION_HOURS |
12 |
Session lifetime |
SARDINE_ADMIN_COOKIE_SECURE |
1 |
Set 0 only for plain-http local development |
SARDINE_MEDIA_DIR |
media |
Media upload directory (the project's media/) |
SARDINE_ADMIN_UPLOAD_MAX_MB |
10 |
Upload size limit in MB |
SARDINE_PROJECT_DIR |
. |
Project directory holding sardine.toml (panel builds) |
SARDINE_ADMIN_PUBLISH_GATE |
1 |
Set 0 to allow publishing despite validation errors |
- Roles (least-privilege ladder):
editor < reviewer < publisher < admin, enforced server-side. - Passwords: argon2id hashes; sessions server-side with only the token digest stored; cookies
HttpOnly+Secure+SameSite=Strict. - CSRF: synchronizer tokens on every authenticated state-changing request, double-submit token on the login form; failed-login rate limiting.
- Accounts live in the storage database (shared migrations) and are never exported.
- Dashboard: content by workflow status, the translation coverage matrix (missing/outdated/complete per language), a live validation report with the publish-gate verdict.
- Articles: list with per-language state badges, create, EN source editor (slug, category, tags, cover) with a preview rendered by the builder's own Markdown renderer (raw HTML disabled), and a side-by-side translation editor per language — the EN source read-only next to the translation form.
- Pages and sections: metadata editor, ordered typed sections (add/reorder/remove), a dynamic field editor with kind-aware suggestions, and per-section side-by-side translation.
Editing a source marks its translations outdated automatically (checksum model). Statuses are displayed but transitions arrive with the workflow phase.
-
Media library: uploads validated server-side (MIME sniffed from the bytes, size limit, parsed image dimensions), mandatory EN alt text plus translatable alt per language, and reference-checked deletion — assets used by covers or sections refuse to die.
-
Workflow and publishing: role-gated transitions (draft → review → published → archived; publishing needs the publisher rung), the publish gate blocking on the entity's validation errors, and the Publishing panel — preview builds served under
/preview/, build & export with target extras written to the project output, every run recorded on the dashboard. -
Hardening: security headers on every response — a CSP with no script source at all (the admin ships zero JavaScript), frame denial, nosniff — and the axe accessibility gate (WCAG 2.2 AA) running over the admin pages in CI, the same methodology as the public site. The full admin guide lives in
docs/ADMIN_GUIDE.md, anti-drift-checked.
Milestone 3 is complete: the full editorial cycle — create, translate, review, publish, build, export — works from the browser, with every security and accessibility gate green in CI. (role-gated transitions, publish gate, builds/exports from the panel), and the hardening pass (CSP, security suites, axe over admin pages). The phased plan lives in docs/PLAN.md.
python -m cms_admin.demo_export --storage content.sqlite3 --out _site/admin boots the real admin on a throwaway copy of the content, signs in as a generated demo user and captures every editorial page as inert static HTML (links prefixed, CSRF stripped, forms neutralized, read-only banner). The demo deploy runs it automatically.
Sardine CMS — multilingual, static-first CMS framework · Repository · Live demo · Apache-2.0