Autorski CMS z AI

· 6 min czytania

Autorski headless CMS i panel do zarządzania stronami, podstronami, tłumaczeniami i mediami z jednego miejsca — z AI podpiętym do pracy nad treścią przez MCP.

Autorski headless CMS i panel do zarządzania stronami, podstronami, tłumaczeniami i mediami z jednego miejsca — z AI podpiętym do pracy nad treścią przez MCP.

W skrócie

Problem
Zestaw stron klienckich, każda na własnej instalacji CMS-a, każda z osobnymi aktualizacjami — i każda z formularzem logowania, bazą i PHP wystawionymi na publicznej domenie klienta.
Co powstało
Jeden wielofirmowy panel treści pod wszystkimi stronami: kreator stron z bloków, tłumaczenia, media, menu i przekierowania, a każda publiczna strona publikowana jako statyczny build.
Moja rola
Główny autor — model treści, silniki bloków i kolekcji, panel, warstwy REST i MCP, uruchamianie klientów i pakiet rdzenia frontu.
Co jest w tym ciekawego
CMS jest wystawiony przez MCP, więc tę samą treść można edytować w panelu albo pisać wprost z terminala — człowiek czy narzędzie AI, na tych samych uprawnieniach.
Efekt
Jeden kod do łatania zamiast wielu, żadnego CMS-a na domenie klienta i AI w procesie pracy nad treścią, a nie w osobnym oknie czatu.

To, co firma nazywa „stroną internetową”, to zwykle dwie rzeczy: witryna, którą widzą odwiedzający, i miejsce, w którym ktoś siada i ją zmienia. Ten projekt to ta druga część — napisany od zera panel treści, z którego prowadzi się cały zestaw stron klienckich.

To praca komercyjna, więc nazwa klienta i produktu zostaje poza tym opisem. Wszystko poniżej dotyczy tego, co system robi i jak jest zbudowany.

Co zastępuje

Domyślna odpowiedź na „potrzebujemy strony, którą klient sam edytuje” to jeden WordPress na klienta. Działa dokładnie do momentu, w którym masz ich kilkanaście. Wtedy każda strona to osobna instalacja z własnym rdzeniem, motywem i wtyczkami do aktualizowania; każda ma stronę logowania na publicznej domenie klienta; i każda wystawia bazę oraz PHP każdemu, kto ich poszuka. Nikt nie ma czasu pilnować kilkunastu takich instalacji, więc po cichu się starzeją — aż coś się zepsuje albo ktoś się włamie.

Wiem, jak to wygląda z drugiej strony — ratowanie WordPressa zaczyna się zwykle po tym, jak nieaktualizowana wtyczka wpuści kogoś do środka.

Jeden panel, wiele statycznych stron
Osobna instalacja CMS na klienta
01Jeden kod do łatania — aktualizacja bezpieczeństwa wychodzi raz i obejmuje każdą stronę.
02Po stronie klienta nie ma nic do aktualizowania ani panelu, o którym trzeba pamiętać.
03Publiczna strona to statyczny build: bez bazy, bez PHP, bez panelu na domenie klienta.
04Zero wtyczek z zewnątrz — każdy typ treści to kod, który sam utrzymuję.
05Jeden pipeline wdrożeniowy, używany przy każdym wydaniu, a nie raz na rok.
01N instalacji, każda z własnym rdzeniem, motywem i wtyczkami.
02Klient ma sam zauważyć komunikat o aktualizacji i coś z nim zrobić.
03Żywy CMS, baza i formularz logowania na każdej publicznej domenie.
04Wtyczki, nad których kodem i tempem aktualizacji nikt nie panuje.
05N ścieżek wdrożenia, w większości ręcznych i rozjeżdżających się.

Co panel naprawdę daje osobie, która w nim pracuje

Zanim technologia zacznie mieć znaczenie: to miejsce, w którym osoba nietechniczna otwiera podstronę, zmienia ją i publikuje.

W panelu
  • Składasz stronę z bloków Hero, lista funkcji, cennik, FAQ, galeria, panel kontaktowy — przeciągasz je w odpowiednią kolejność i wypełniasz pola. Bez HTML-a i bez shortcode’ów.
  • Pilnujesz wszystkich języków Każda podstrona ma jedną wersję na język, a osobny ekran pokazuje, których tłumaczeń brakuje — razem z polami SEO, nie tylko treścią.
  • Wgrywasz zdjęcie raz Wersje pod www powstają w tle, więc redaktor wrzucający 4 MB z telefonu nie spowalnia po cichu całej strony.
  • Prowadzisz kolekcje, nie tylko podstrony Wpisy blogowe, case studies, usługi, plany cenowe, opinie, słownik pojęć — każda kolekcja ma własne pola, listing i kategorie.
  • Masz pod ręką całą hydraulikę Menu, przekierowania, raport 404, sitemapa i hreflang, obrazki do social mediów — to, od czego zależy widoczność, siedzi obok treści.
  • Rozdajesz właściwe klucze Trzy role, a autorowi można przydzielić dokładnie te kolekcje, nad którymi pracuje. Osoba od wpisów blogowych nie widzi cennika.
Lista podstron w panelu — 42 pozycje filtrowane po typie (page, home, pricing, blog, glossary, portfolio, section), każdy wiersz z nazwą, typem, sekcją, statusem i datą aktualizacji

Kreator stron

Podstrona nie jest kawałkiem HTML-a. To uporządkowana lista typowanych bloków, trzymana osobno dla każdego języka, a każdy blok zna swoje pola. Typów bloków jest dziś 32 — hero, treść, funkcje, opinie, cennik, FAQ, galeria, zespół, godziny otwarcia, mapa, kontakt, wideo, zakładki, karuzela i reszta.

Otwarta paleta bloków w edytorze podstrony — każdy typ bloku z opisem prostym językiem, co robi i kiedy po niego sięgnąć: Hero, Rich content, Paragraph, Button i reszta rejestru

Dwie rzeczy z tej listy są ważniejsze niż jej długość:

  • Nowy typ bloku to wpis w konfiguracji, nie nowy moduł. Paleta, którą widzi redaktor, opis przy każdym typie i pola w samym bloku pochodzą z jednego pliku konfiguracyjnego. Frontend rejestruje komponent pod tą nazwą i tyle.
  • Zdolność wspólną dla wielu bloków deklaruje się raz, a nie kopiuje do każdego. Tło sekcji, wyrównanie, animacja pojawiania i identyfikator kotwicy mają jedną definicję, a blok tylko mówi, które z nich obsługuje — robi tak 26 z 32 typów.
  • Każdy blok dostaje trwałe id przy pierwszym zapisie. Dzięki temu można podmienić jeden blok z zewnątrz — zapytaniem do API albo narzędziem AI — nie ruszając reszty podstrony.

Obok edytora stoi konspekt podstrony: jeden wiersz na blok, z fragmentem jego prawdziwej treści, więc długa strona daje się przeglądać zamiast zamieniać w ścianę zwiniętych paneli.

Edytor bloków na podstronie usługi: po lewej konspekt struktury z sześcioma blokami i fragmentem ich prawdziwej treści (Hero, Testimonials, Rich content, Features / steps z 6 krokami, Pricing table z 6 planami, CTA banner), pośrodku te same bloki jako wiersze do przestawiania, po prawej publikacja, autor i obrazek OG

Nowe typy treści bez pisania kodu

Druga połowa silnika treści to kolekcje. Strona, która potrzebuje „case studies”, „godzin otwarcia dla każdego oddziału” albo „planów cenowych, ale w trzech osobnych zestawach”, nie dostaje pisanego pod siebie modułu. Kolekcję definiuje się w panelu — wybierasz typ elementu, włączasz pola, ustawiasz ikonę — i pojawia się w menu z własnym ekranem edycji, własnymi kategoriami i własnym endpointem API.

Każdy klient ma przy tym włączone tylko te kolekcje, z których faktycznie korzysta. Reszta jest wyłączona i nie zaśmieca mu menu.

Ekran sekcji — dziesięć typów kolekcji jako kafle (Blog, Glossary, Portfolio, Services, Plans, Testimonials i cztery warianty planów), każdy z liczbą elementów, checkboxem włączania i linkiem do edycji pól; dwa są aktywne

Tłumaczenia, których stan widać

Wielojęzyczność zwykle sypie się po cichu: ktoś dodaje angielską podstronę, nikt nie dodaje polskiej, a lukę wyłapuje po miesiącach przypadkowy odwiedzający. Panel zamiast tego cały czas liczy — ile podstron jest przetłumaczonych w danym języku, których brakuje i czy wypełniono także tytuł i opis SEO, nie tylko samą treść.

Ekran kompletności tłumaczeń — 91,9% przetłumaczonych, 34 z 37 podstron, SEO 21 z 37, ostrzeżenie o 3 brakujących kombinacjach podstrona/język i tabela ze statusem tłumaczenia i SEO dla każdej pozycji

Najciekawsza część: AI jako pełnoprawny klient CMS-a

Wszystko, co panel potrafi odczytać, CMS wystawia dwa razy — jako REST API i jako serwer MCP, za tym samym uwierzytelnieniem. MCP to protokół, którym asystenci AI rozmawiają z zewnętrznymi systemami, więc ta druga warstwa oznacza, że narzędzie AI pracuje bezpośrednio na treści, zamiast dostawać ją wklejoną z ręki.

Narzędzi jest tam dziś 82: wypisz i przeczytaj podstrony, przeszukaj treść, pobierz schemat bloków, utwórz podstronę, podmień pojedynczy blok po jego id, ustaw metadane, ogarnij kategorie, menu i przekierowania, wgraj media, uruchom przebudowę. Klient może podpiąć Claude’a do własnego panelu swoim własnym loginem — OAuth z PKCE, a nie token wklejony do pliku konfiguracyjnego — i poprosić o szkic podstrony, uzupełnienie brakujących opisów albo przetłumaczenie sekcji, a zmiany lądują w CMS-ie, nie w oknie czatu.

Dwoje drzwi do tej samej treści

Panel to jedno wejście. Terminal to drugie. MCP jest protokołem, a nie funkcją jednej aplikacji, więc te same narzędzia, które odpowiadają oknu czatu, odpowiadają też Claude Code — programista zostaje tam, gdzie już jest, i pisze do CMS-a z linii poleceń: tworzy podstronę, wypełnia jej bloki, ustawia metadane, uruchamia przebudowę.

To nie jest ścieżka na pokaz. Teksty na stronach postawionych na tym systemie weszły z terminala, a nie przez klikanie w panelu. Napisanie dwunastu podstron to inna robota, kiedy zamiast otwierać dwanaście formularzy podaje się brief i strukturę.

Oboje drzwi prowadzą do tej samej treści, tej samej walidacji i tych samych uprawnień. Osoba, która raz na kwartał poprawia stronę „O nas”, dostaje panel, który niczego od niej nie wymaga; programista stawiający całą stronę dostaje linię poleceń. Żadne z nich nie musi udawać drugiego.

Ciekawym problemem inżynierskim nie były same narzędzia, tylko zabezpieczenia:

  • Poświadczenie nigdy nie jest szersze niż jego właściciel Każde chronione narzędzie sprawdza dwie rzeczy: czy osoba ma uprawnienie i czy poświadczenie ma pasujące pozwolenie. Poświadczenie tylko do odczytu nie uruchomi wdrożenia ani nie wyda cudzego limitu API.
  • Zawsze w granicach jednego klienta To właściciel poświadczenia decyduje, czyją treść ono widzi. Nie ma parametru z id klienta, który dałoby się podmienić, więc nie ma czego pomylić między najemcami.
  • Limity, które faktycznie ograniczają Narzędzia wydające prawdziwe pieniądze — wdrożenia, tagi wydań, limity zewnętrznych API — mają ostrzejszy przydział niż narzędzia do odczytu.
  • Spisany kontrakt do przeczytania Format bloków ma własną specyfikację, a typy na froncie są generowane ze schematu backendu. Narzędzie AI edytujące podstronę pracuje na tym samym dokumencie co programista.
Ekran poświadczeń dostępu — dwa aktywne wpisy z datą utworzenia i ostatniego użycia oraz przyciskiem unieważnienia, i formularz tworzenia nowego z checkboxami pozwoleń (odczyt, zapis, wdrożenia, integracje, dostęp do wszystkich klientów, wszystkie pozwolenia) i terminem ważności, pod zdaniem: poświadczenie zawsze może mniej niż jego właściciel

Od zmiany do strony na produkcji

Droga publikacji
  1. Krok 1

    Ktoś wprowadza zmianę

    Redaktor przestawia bloki i poprawia teksty w panelu — albo narzędzie AI podmienia jeden blok przez MCP. Te same dane, ta sama walidacja.

  2. Krok 2

    Zapis jako treść o strukturze

    Bloki idą do bazy jako typowany JSON, osobno dla każdego języka, każdy z trwałym id. Nic nie jest zapisywane jako gotowy HTML, więc tę samą treść można później wyrenderować inaczej.

  3. Krok 3

    Publikacja kolejkuje build

    Panel wprost mówi, że produkcja jest starsza niż treść, a jeden przycisk uruchamia przebudowę.

  4. Krok 4

    Strona pyta API o swoje podstrony

    Statyczny build loguje się własnym poświadczeniem i pobiera gotowe podstrony — z blokami i rozwiązanymi referencjami — w jednym przebiegu.

  5. Krok 5

    Nowy build wchodzi na produkcję

    Redaktorzy widzą kolejno: w kolejce → budowanie → wydane, a poprzednie wydanie zostaje, żeby dało się na nie wrócić.

Przebudowa trwa od 10 do 20 sekund. Ta liczba sprawia, że „to opublikujmy” jest zdaniem, które można powiedzieć klientowi od ręki, a nie tematem do ustalenia na później — a panel trzyma historię, więc powrót na dowolne wcześniejsze wydanie to jeden przycisk.

Ekran wdrożeń frontendu — ostatni build zakończony w 13 sekund, lista zachowanych wydań z aktywnym oznaczonym na zielono i przyciskiem powrotu przy każdym starszym, oraz wykres czasu budowania w sekundach dla ostatnich sześciu wdrożeń, wszystkie między 11 a 19 sekund

Odwiedzający dostaje na końcu stronę statyczną: HTML, CSS i obrazki z CDN-u. Żadnego zapytania do bazy, żadnego procesu PHP, nic, do czego można się zalogować. Treść mieszka w panelu, publiczna strona jest jej migawką.

Ponieważ kontraktem między jednym a drugim jest zwykły JSON po HTTP, front też nie jest zabetonowany. Dziś renderuje go Astro; strona w Next.js, Nuxcie czy SvelteKicie korzystałaby z dokładnie tych samych endpointów, a backend nie musiałby się zmienić.

Uruchomienie nowej strony

Dodanie klienta to jeden formularz. Wszystko po nim dzieje się w tle, jako łańcuch kroków, z których każdy da się powtórzyć osobno: repozytorium, aplikacja na serwerze, podpięcie wdrożeń z gita, certyfikat. Panel pokazuje, na którym kroku jest, zamiast jednego „udało się / nie udało”, a nieudany przebieg można wznowić bez dublowania tego, co już powstało.

Moja rola

Jestem głównym autorem systemu: wielofirmowego modelu treści, silników bloków i kolekcji, panelu w Filamencie, warstw REST i MCP, łańcucha uruchamiania klientów, procesu obróbki obrazów i pakietu Astro, z którego budowane są strony klientów. W pierwszych dziesięciu tygodniach weszło około 1900 commitów, w większości moich, przy niewielkim zespole i code review na każdym pull requeście.

Efekt

  • Jeden backend, wiele stron Aktualizacja bezpieczeństwa, nowy typ bloku albo nowa funkcja panelu trafiają od razu na wszystkie strony, zamiast być powtarzane instalacja po instalacji.
  • Na publicznej domenie nie ma czego atakować Żadnego CMS-a, bazy ani formularza logowania na stronie klienta — tylko statyczny build.
  • AI w procesie, a nie obok niego Treść może szkicować, poprawiać i publikować asystent AI pracujący bezpośrednio na CMS-ie, na tych samych uprawnieniach co człowiek.

Technologie

Laravel

Framework, na którym stoi cały backend — model treści, kolejka, API, uruchamianie klientów i wdrożenia.

Filament

Warstwa panelu. Kreator stron to jego komponent Builder, sterowany w całości konfiguracją.

MariaDB

Treść, tłumaczenia, metadane mediów i ustawienia każdego klienta, z każdym zapytaniem ograniczonym do jednego najemcy.

Astro + TypeScript

Statyczne frontendy. Typy bloków są generowane ze schematu backendu, więc obie strony nie mogą się rozjechać.

REST API

Warstwa odczytu używana przy budowaniu stron, opisana w OpenAPI i uwierzytelniana osobno dla każdego klienta.

MCP

82 narzędzia odwzorowujące API dla asystentów AI, z pozwoleniami per poświadczenie i limitami na tych kosztownych.

Powrót do portfolio

Powiązane wpisy

Czytaj więcej