← Toate știrile
ARTICOL
22 septembrie 2026

De ce am construit Osprey, CMS-ul nostru headless

Osprey păstrează schemele de conținut în TypeScript, livrează conținutul publicat la momentul build-ului și returnează un 404, nu o eroare de permisiune, pentru tot ce este privat.

Majoritatea CMS-urilor headless obligă la o alegere: un instrument cu editor vizual care tratează schema ca pe un blob de bază de date generat o singură dată, sau o soluție construită intern în care fiecare încărcare de pagină apelează CMS-ul în timp real. Am construit Osprey, propriul nostru CMS headless, tocmai ca să eliminăm această alegere: o schemă de conținut versionată ca și codul, și un site livrat ca fișiere statice. Iată ce face de fapt codul, nu doar ce spune prezentarea.

Rânduri de rafturi înalte pline cu cărți în interiorul unei biblioteci
Foto: Szeronine (CC BY 4.0), Wikimedia Commons.

Schemele sunt cod, nu un blob de bază de date

În Osprey, o colecție este definită în TypeScript și trăiește în același repozitoriu cu restul aplicației. Asta înseamnă același control al versiunilor, aceeași revizuire de cod și aceleași tipuri end-to-end pe care o echipă le aplică deja codului aplicației, extinse acum la modelul de conținut. Interfața de administrare este o comoditate pentru editarea intrărilor; nu este niciodată sursa de adevăr pentru schemă.

Conținutul ajunge la momentul build-ului, nu la fiecare cerere

Loader-ul @osprey/astro preia intrările publicate de la endpoint-ul REST public al Osprey în timpul build-ului și le predă content collections din Astro, fără să fie nevoie de o cheie API pentru această citire publică. Site-ul livrat este HTML static pur: Osprey este o dependență de build, nu de runtime. Dacă un build rulează în timp ce CMS-ul este lent sau inaccesibil, loader-ul păstrează intrările deja din cache în loc să eșueze build-ul.

Privat implicit, invizibil când este refuzat

O colecție este expusă în API-ul public doar atunci când propria regulă access.read admite explicit un apelant anonim; tot restul rămâne privat. O cerere către o colecție privată, sau către un document nepublicat, se întoarce cu 404 în loc de o eroare de permisiune, astfel încât API-ul nu confirmă niciodată că o colecție privată există.

Metadatele SEO călătoresc cu fiecare intrare

Fiecare intrare returnată de API-ul public poartă un obiect SEO îmbinat: titlul, descrierea, etichetele Open Graph și Twitter și URL-ul canonic proprii intrării au prioritate, iar valorile implicite la nivel de workspace completează ce lipsește. Aceeași încărcătură include directiva robots rezolvată și legăturile hreflang către fiecare traducere publicată a paginii respective, astfel încât un frontend își poate reda propriile meta-etichete fără să reconstruiască această logică.

Un singur deployment, mai mulți tenanți

Osprey este multi-tenant: un singur deployment poate servi mai multe site-uri, fiecare limitat la propriul tenant. Pe rutele publice de conținut, tenantul din care se citește este stabilit pe baza antetului host al cererii înseși, nu pe baza unei valori furnizate de client, astfel încât o cerere anonimă nu poate citi conținutul altui tenant trimițând un alt id. Fiecare interogare a bazei de date executată de API este limitată intern la tenant, fără să depindă de memoria fiecărui handler.

Mecanisme GDPR, nu doar o afirmație

Osprey include un endpoint de dreptul la uitare care redactează datele personale — trimiteri de formulare, actori din jurnalul de audit, conturi de utilizator — pentru un e-mail sau id dat, limitat la un tenant. Un endpoint separat de export produce un manifest complet al conținutului unui tenant pentru cererile de portabilitate a datelor. Managerul de consimțământ pentru cookie-uri integrat tratează drept mereu active doar cookie-urile strict necesare și setează implicit orice altă categorie ca refuzată până când vizitatorul își dă acordul, cu „refuză tot” având aceeași greutate vizuală ca „acceptă tot”. Deoarece Osprey este auto-găzduit, o echipă îl poate rula și în întregime pe infrastructură europeană; garanțiile dedicate de rezidență a datelor în UE fac parte din planul enterprise al produsului, nu dintr-o instalare implicită.

Headless prin design

Osprey stochează conținutul și îl servește ca JSON; nu impune cum trebuie să arate un site. O echipă poate construi frontend-ul în Astro, Next.js sau orice generator de site-uri statice care poate apela un API REST, și poate folosi Osprey doar ca strat de conținut din spate. Planurile actuale, inclusiv variantele auto-găzduită și enterprise, sunt listate pe pagina de produs Osprey.

Ce e de făcut

  • Definiți modelul de conținut ca și colecții TypeScript și versionați-l alături de codul aplicației.
  • Setați o regulă access.read explicită pe fiecare colecție pe care vreți să o serviți prin API-ul public; lăsați-o nesetată pentru ca acea colecție să rămână privată.
  • Adăugați loader-ul @osprey/astro, sau apelați direct API-ul REST, ca conținutul să fie preluat la momentul build-ului, nu la fiecare cerere.
  • Setați câmpurile SEO per intrare doar acolo unde trebuie să difere de valorile implicite ale site-ului, și lăsați-le pe acestea să completeze restul.
  • Folosiți endpoint-urile integrate de export și de dreptul la uitare pentru cererile de portabilitate și ștergere a datelor, în loc să construiți altele proprii.
  • Dacă găzduirea exclusiv în UE contează pentru conformitatea voastră, găzduiți Osprey pe infrastructură europeană sau întrebați despre garanțiile de rezidență ale planului enterprise.

Surse: pagina de produs Osprey, getosprey.dev.