4.0 KiB
Kontrakt CMS FDMTech — v1
Właściciel schematu: backend CI4 w /workspace/Sensory/sensory.fdmtech.pl. Dedykowana baza CMS (grupa CI4 cms); renderer PDO nigdy nie korzysta z bazy ani tabel telemetrii. Brak konfiguracji/zmian produkcyjnych w ramach implementacji. Wszystkie daty CMS: UTC, Y-m-d H:i:s. Tabele bez prefiksu.
cms_pages
id INT PK auto, slug VARCHAR(160) UNIQUE, title VARCHAR(200), draft_json MEDIUMTEXT (projekt GrapesJS), draft_html MEDIUMTEXT, draft_css MEDIUMTEXT, draft_seo_title VARCHAR(200), draft_seo_description VARCHAR(320), published_html MEDIUMTEXT NULL, published_css MEDIUMTEXT NULL, seo_title VARCHAR(200) NULL, seo_description VARCHAR(320) NULL, published_title VARCHAR(200) NULL, status VARCHAR(20) (draft/published), published_at DATETIME NULL, version INT (optymistyczna blokada), created_by INT, updated_by INT, created_at, updated_at DATETIME.
Slug pojedynczy: [a-z0-9]+(?:-[a-z0-9]+)*; home oznacza /. Slug jest stały po utworzeniu. title jest roboczy; publicznie używaj published_title. Zapis szkicu nie zmienia publicznych pól ani statusu. Publikacja atomowo ustawia HTML/CSS, tytuł, SEO i datę oraz tworzy rewizję. Wycofanie zmienia status na draft.
Odczyt PDO (parametryzowany):
SELECT id, slug, published_title AS title, published_html, published_css,
seo_title, seo_description, published_at
FROM cms_pages WHERE slug = :slug AND status = 'published'
AND published_at IS NOT NULL LIMIT 1;
Brak rekordu = 404 / jawny fallback aplikacji. Nigdy nie czytaj draft_*. Payload to fragment body HTML i osobny arkusz CSS po restrykcyjnej filtracji serwerowej. Renderer escapiuje tytuły/SEO, osadza HTML w kontenerze <main id="cms-content"> (wymagane: CSS jest ograniczony do tego ID), CSS jako arkusz/element style; zalecany CSP bez skryptów treści. Nie wykonuje projektu JSON. Obrazy mają absolutne URL endpointu Sensory /cms-media/{losowa-nazwa}.
cms_settings
key VARCHAR(80) PK, value TEXT (JSON), updated_at DATETIME.
Klucze: site_name (string), contact_email (string), contact_phone (string), navigation (tablica obiektów {label:string,url:string}). SELECT \key`, `value` FROM cms_settings. JSON dekodować i walidować typy, tekst/atrybuty escapować. Brak klucza: domyślna wartość renderera. Nawigacja dopuszcza lokalne ścieżki zaczynające się pojedynczym /` i HTTPS.
cms_inquiries
id INT PK auto, name VARCHAR(160), email VARCHAR(254), company VARCHAR(200) domyślnie pusty, phone VARCHAR(60) domyślnie pusty, message TEXT, source VARCHAR(160) domyślnie audit, consent_at DATETIME NOT NULL, status VARCHAR(20) domyślnie new (new, in_progress, closed), created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL.
Renderer może INSERT wyłącznie jawnych pól: name, email, company, phone, message, source, consent_at, created_at, updated_at. Status nadawany przez DB. Walidacja: name 1–160, poprawny email <=254, company <=200, phone <=60, message 1–10000, source <=160. Zgoda wymagana; czas UTC po stronie serwera. CSRF, honeypot/rate limit i limity żądania są odpowiedzialnością formularza publicznego. Nie udostępniać odczytu skrzynki publicznie.
Tabele prywatne
cms_revisions (snapshot projektu, HTML/CSS i metadanych przy publikacji), cms_media (metadane bezpiecznych obrazów). Renderer ich nie potrzebuje. Bez FK do użytkowników/telemetrii Sensory.
Uprawnienia połączenia publicznego
SELECT tylko na cms_pages i cms_settings; INSERT tylko na cms_inquiries. Dla ścisłego oddzielenia szkiców nadać SELECT na wskazane kolumny publiczne cms_pages zamiast całej tabeli — lista obejmuje także status, bo zapytanie odczytu filtruje po nim w WHERE (MariaDB wymaga uprawnień do kolumn z klauzuli WHERE). Bez UPDATE/DELETE i bez dostępu do pozostałych baz. Dane dostępu PDO i CI4 dostarcza operator poza repo. Migracje uruchamia operator wyłącznie po weryfikacji docelowej bazy.