legal-pages
Opt-in wrapper around template-resolver text-blocks that registers four public HTML routes (/legal/impressum, /legal/datenschutz, /legal/imprint, /legal/privacy) with Markdown-to-HTML rendering and a boot-time job that hard-fails in production when the required DE blocks (imprint/de, privacy/de) are not seeded in SYSTEM_TENANT. Requires anonymousAccess: { defaultTenantId: SYSTEM_TENANT_ID } and extraContext.templateResolver to be wired at app bootstrap; for per-tenant imprints or a custom layout call template-resolver:query:by-slug directly.
Quick example
Section titled “Quick example”From recipes-legal-pages — the smallest working mount:
// Legal-Pages Sample//// DACH-Apps (DE/AT/CH) sind verpflichtet ein Impressum (TMG/DDG §5) und// eine Datenschutzerklärung (DSGVO Art. 13) öffentlich zugänglich zu// haben. Das ist 1) für jede App identisch und 2) nervig manuell pro// App neu zu basteln.//// Lösung: zwei opt-in bundled-features kombinieren://// - `template-resolver` — der Content-Store (Entity `template-resource`,// kind `text-block` mit slug+locale+title+content, scoped per Tenant).// Auch nutzbar für FAQ, About, ToS, Marketing-Snippets — nicht// legal-spezifisch.//// - `legal-pages` — opt-in-Wrapper darauf, der vier feste Public-// Routes (`/legal/impressum`, `/legal/datenschutz`, `/legal/imprint`,// `/legal/privacy`) registriert und Markdown→HTML rendered. Plus// Boot-Check der in Production hart fehlt wenn die DE-Pflicht-Blocks// fehlen.//// Tenant-Modell: 1 App = X Tenants = 1 Impressum. Alle Subdomains// teilen sich die SYSTEM_TENANT_ID-Version. Wer pro-Tenant-Impressums// braucht, muss sein eigenes Routing davorsetzen oder die by-slug-// query mit tenant-specific tenantId nutzen.//// Voraussetzungen für Production:// - `anonymousAccess` muss in runProdApp/runDevApp konfiguriert sein// (defaultTenantId = SYSTEM_TENANT_ID), sonst antworten die// legal-pages-Routes mit 503// - `extraContext.templateResolver = createTemplateResolverApi(db)` muss// gewired sein, sonst wirft der Boot-Check mit Wiring-Hinweis// - Beim ersten Boot müssen die TextBlocks geseedet sein —// template-resolver/seeding `seedTextBlock` oder via API// `template-resolver:write:set` mit TenantAdmin-Token
import { createLegalPagesFeature, LEGAL_REQUIRED_BLOCKS, LEGAL_ROUTES,} from "@cosmicdrift/kumiko-bundled-features/legal-pages";import { createTemplateResolverFeature } from "@cosmicdrift/kumiko-bundled-features/template-resolver";
// Beide Features aktivieren — template-resolver ist Foundation, legal-pages// requires sie. r.requires("template-resolver") greift automatisch im// legal-pages-Feature.export const templateResolverFeature = createTemplateResolverFeature();export const legalPagesFeature = createLegalPagesFeature();
// Re-exports für Tests + andere Demosexport { LEGAL_REQUIRED_BLOCKS, LEGAL_ROUTES };📄 On GitHub: samples/recipes/legal-pages/src/feature.ts
Live preview
Section titled “Live preview”
How it fits
Section titled “How it fits”What this feature needs to run (Requires).
flowchart TB
n_legal_pages["legal-pages"]
subgraph how_reqs["Requires"]
n_template_resolver["template-resolver"]
end
n_template_resolver --> n_legal_pages
Getting started
Section titled “Getting started”Start with recipes-legal-pages for a step-by-step walkthrough with runnable code and integration tests.
Dependencies
Section titled “Dependencies”- Requires:
template-resolver - Activation: always on (not toggleable)