
pacod-cms е система за управление на съдържание с отворен код за редакционни сайтове. Една инсталация обслужва много сайтове, всеки със собствена база данни, файлове и кеш; редакторите работят в многоезично студио, а читателите получават бързи публични сайтове, маршрутизирани по хост.
Управление на съдържание
- Лиценз: Apache-2.0
- Версия: 0.0.1 · Предварителна
- Написан на: Java
- Технологии: Java 25, Spring Boot 4.1, Thymeleaf, MongoDB, Maven, Caddy, Gotenberg, Docker
- Вижте в GitHub · Докладвайте проблем
Възможности
Редакционно ядро
Статии, категории, автори, тагове и постоянни страници, с обща библиотека с изображения за целия сайт.
Мултисайт по замисъл
Всеки сайт има собствена MongoDB база данни, файлове и кеш и отговаря на хостовете, които заявява.
Реклама и статистика
Рекламни резервации с конструктор на HTML5 банери, link-in-bio страница за профили в социалните мрежи и статистика за читатели, реклама и обхождащи ботове, така че редакторите виждат какво се чете и какво се обхожда. Резервации за отделни раздели и икона на GitHub за страницата с връзки предстоят (още не е издадено, в клона blog-site).
Многоезичност
Съдържание на много езици с конфигурируем оригинален език и AI превод на статия или цял сайт като задача, като всеки отговор се отхвърля, ако не запазва точно маркирането на статията; самото студио е на английски, български, немски и руски. Руски език за собствените надписи на публичния сайт предстои (още не е издадено, в клона blog-site).
AI помощ при писане
Заглавия, цяла статия по кратко задание, пасаж на мястото на курсора и текст, оформен в стилизирани блокове, без да се губи нито дума на автора.
AI дизайн
Тема, оформление на страница или дизайнът на целия сайт, изготвени по няколко думи чрез доработка на текущата чернова; само чернови, с една отмяна.
Организации и роли
Организации, сайтове и покани, с роли за всяка организация и за всеки сайт, съхранявани в pacod-id.
PDF файлове и броеве на списание
PDF файлове на статиите като фонова задача, в оформлението за печат на сайта или в научен формат на две колони, с резюме и ключови думи, взети от статията или написани от AI; брой на списание събира статии в по един PDF за всеки език с корица, уводна статия, съдържание и номерирани страници и се публикува в архива на сайта.
Експорт и възстановяване
Сайт или цяла организация се изтегля като един ZIP и се възстановява като нов, в тази инсталация или в друга.

Екранни снимки





Един стек, много сайтове
pacod-cms обслужва много сайтове на много организации от един стек. Всеки сайт има собствена MongoDB база данни, собствена папка с файлове и собствени записи в кеша и отговаря на хостовете, които заявява. Всеки, който се регистрира, може да създаде организация, да стане неин администратор, да създава сайтове в нея и да кани хора да работят по тях; администратор на платформата задава лимитите.
Дизайнът изхожда от това, че собственикът на един сайт може да е враждебен към всички останали в инсталацията. Сайтовете изпълняват скриптове, написани от собствениците им — HTML5 банери, таг мениджъри, текстове на статии, — и платформата трябва да държи тези скриптове и тези данни там, където им е мястото.
Как се свързват частите
| Част | Какво прави |
|---|---|
pacod-cms-core |
Библиотека: документи, услуги, мултитенантността, контролната равнина, начално съдържание, моделът на дизайна и Java порт на механизма за правила на pacod-id. |
pacod-cms-site |
Публичният сайт за всички сайтове наведнъж: страници, JSON само за четене под /web/v1/**, медия, броячи на трафика. |
pacod-cms-studio |
Студиото: порталът за организации, сайтове и членове, екраните за редактиране, фоновите задачи, AI. |
| Caddy | Входната врата, маршрутизира по хост: хоста на студиото към студиото, този на pacod-id към pacod-id, всеки друг хост към сайта. |
| Gotenberg | Генерира PDF файловете на статиите и на броевете на списанието от изгледите за печат на сайта. |
Двете приложения никога не се извикват едно друго. И двете работят директно с MongoDB, така че редакция в студиото стига до публичния сайт през базата данни; кешовете на сайта изтичат след 60 секунди, вместо да бъдат изчиствани, така че редакцията е видима до минута.
От хоста до страницата
Всяка единица работа принадлежи на точно един сайт, обвързан с нея за цялото ѝ времетраене.
- Caddy предава заявката на сайта. В продукция Caddy поисква сертификата на сайт при първото посещение, но едва след като сайтът потвърди, че хостът принадлежи на активен сайт.
- Сайтът търси хоста в контролната равнина, преди да се изпълни каквото и да е друго. Хост, който не е заявен от активен сайт, получава обикновен 404, никога сайт по подразбиране.
- При обвързан сайт всяка операция с MongoDB отива в базата данни на този сайт. Ако няма обвързан сайт, слоят за данни хвърля грешка, вместо да гадае.
- Оформлението на типа страница — области с подредени секции — се разрешава спрямо набора шаблони на сайта, а данните на всяка секция се зареждат предварително. Шаблоните никога не правят заявки; секция, чиито данни не се заредят, се пропуска и записва в лога, а страницата пак се изобразява.
В студиото сайтът се определя от пътя (/sites/<id>/mgmt/…); фоновата работа обвързва сайта, за
който се изпълнява.
Къде живеят данните
| Къде | Какво |
|---|---|
pacod_cms |
Контролната равнина: организации, сайтове, домейни, настройки на платформата, опашката със задачи. |
site_<id> |
По една база данни на сайт: статии, страници, изображения, банери, менюта, настройки, оформления, тема, статистика. |
sites/<id>/ |
Файловете на сайта: изображения към статии, рекламни материали за банери, PDF файлове. PDF файловете на брой в чернова стоят в непублична папка, която само студиото сервира. |
| pacod-id | Хора, сесии и присвоени роли. CMS не съхранява акаунти. |
Идентичността на сайта — име, лога, данни за контакт, идентификатори на трети страни — е данни, а
не маркировка. Идентификатор на трета страна се изобразява само когато е зададен и има формата на
своя доставчик, така че нов сайт не зарежда никакво проследяване. Оригиналният език на
съдържанието на сайта е последният резервен вариант при липсващ превод и юридически валидният
текст. Каноничен адрес, hreflang и метаданни на статията в заглавната част (head) на всяка страница
за читатели, както и Atom емисия за всеки сайт и за всяка категория, предстоят (още не е издадено, в
клона blog-site).
Дизайн без писане на код
В раздела Дизайн на студиото всеки сайт избира един от три набора шаблони — editorial, начална
страница с решетки от статии и колона до съдържанието; minimal, една спокойна колона; или
magazine, изграден около изображенията, с основна статия на цялата ширина, снимки в мозайка и
статии, които се отварят със своята снимка — и подрежда оформление за всеки тип страница:
начална, статия, категория, най-нови, постоянна страница и търсене. Сред секциите са списъци със
статии (най-новите, от категория или най-четените), форматиран текст, призив за действие, поле за
търсене и връзките към социалните мрежи; секция, която показва текста на постоянна страница на
езика на читателя, предстои (още не е издадено, в клона blog-site). Темата задава цветове със
стойности за нощен режим, шрифтове и ширини.
Текстът на статия може да съдържа стилизирани блокове от затворен списък: встъпителен абзац, каре с
бележка, изнесен цитат, каре с основните факти, две колони и фигура с надпис — обикновена или
подравнена вляво или вдясно. Сайтовете ги стилизират според темата, също и в изгледите за печат, а
значи и в PDF файловете, а редакторът ги показва така, както ще ги видят читателите. Пет от тях са в
менюто Блокове на редактора; AI оформлението може да използва и шестте. Примери с код,
вмъквани от редактора и оцветявани с Prism в сайтовете, предстоят (още не е издадено, в клона
blog-site).
Смяната на набора първо показва какво ще се загуби, и нищо не се премахва: секциите, които новият набор не може да покаже, се запазват и се връщат при обратна смяна. Оформленията и темата остават чернови, докато някой с право да ги публикува не го направи, независимо дали ги е написал човек или AI. Асистентът за дизайн изготвя тема, оформление или дизайна на целия сайт по няколко думи; подробностите са в раздела за AI по-долу. Прегледът показва черновите от собствения адрес на сайта, в ширина за компютър, таблет или телефон, чрез подписан токен, валиден 30 минути, и не отчита нищо.
Нов набор шаблони е код, който операторът инсталира, а не качен файл: шаблон на Thymeleaf може всичко, което може и сайтът.
PDF файлове и броеве на списание
Дългата работа се изпълнява в студиото като задача, най-много по една на организация наведнъж, с лог на живо и бутон за отказ. Експортът в PDF е такава задача: Gotenberg превръща всяка статия в по един PDF за всеки език, на който е написана, а статията сочи своя PDF, щом той съществува.
- Два формата. Стандартният е изгледът за печат на сайта. Научният е оформление на научна статия: заглавна част на цялата ширина със заглавието, авторите, резюмето и ключовите думи, след това текстът в две колони.
- Два начина на изготвяне. Само с код взема резюмето и ключовите думи от описанието и таговете на статията. С AI възлага на pacod-gw да напише резюме, ключови думи и кратко заглавие за колонтитула за всяка статия и всеки език. Текстът на статията никога не се променя.
- Настройките за PDF съдържат стойностите по подразбиране на всеки сайт: формат и начин, хартия (A4, Letter или A5) и полета, името на списанието за всеки език и неговия ISSN, колонтитул и номера на страниците. Всяко изпълнение може да избере друг формат и начин.
Брой на списание събира статии в по един PDF за всеки език. Той има том, номер и месец, заглавие
и уводна статия за всеки език и по желание изображение за корица; статиите му се избират една по
една или се попълват от месеца му, след което се подреждат. При генериране се изготвят корица,
уводната статия, съдържание с номера на страниците и след това статиите, с отметка за всяка статия и
последователна номерация на страниците. PDF файловете на брой в чернова се изтеглят само от
студиото. Публикуването поставя броя и неговите PDF файлове на сайта под /issues и в картата на
сайта.
Модел на сигурността
Кой какво може
pacod-id притежава акаунтите и ролите. Токените не носят роли: студиото следи потока с присвоени
роли на pacod-id и само решава всяко разрешение спрямо манифеста на CMS (authz/cms.yaml). Ролите
са администратор на платформата, провижънър, администратор на организация, администратор на сайт,
дизайнер, редактор, автор и анализатор, присвоени на ниво платформа, организация или сайт. Авторът
пише; редакторът също и публикува.
- Всеки обработчик в студиото декларира разрешението, от което се нуждае; такъв, който не декларира нищо, се отказва. Никоя роля не е неявна.
- Когато потокът на pacod-id не е отговарял една минута, студиото връща 503, вместо да решава с роли, които може вече да са отнети. Публичните сайтове не са засегнати.
Сайтовете остават разделени
- Студиото и pacod-id са на различен регистрируем домейн от сайтовете, иначе студиото отказва
да стартира. В противен случай скрипт на сайт би могъл да задава бисквитки за тях и да обезсили
защитата им от CSRF. Инсталация, чиито сайтове са до един доверени, например демо на един домейн,
може да приеме този риск с
PACOD_SITE_DOMAIN_SHARED=true: тогава студиото стартира с предупреждение, а сайт пак никога не може да заеме собствения хост на студиото или на pacod-id. - Студиото никога не сервира HTML или скриптове на сайт. Прегледите на чернови и на HTML5 банери се вграждат от собствения произход (origin) на сайта.
- Gotenberg е изолиран: достига само вътрешния адрес на сайта и не изпълнява JavaScript.
- До читателя стигат само публикувани статии — по всеки маршрут, във всеки списък, карта на сайта и PDF. Брой на списание в чернова никога не е публичен.
AI, който не пипа ключовете ви
Всяко извикване на AI минава през pacod-gw; студиото никога не държи ключ на доставчик. За нови организации AI е изключен по подразбиране: администратор на платформата го включва за всяка организация, а администраторът на сайт може да го изключи за своя сайт.
Писане
В редактора на статии помощта при писане работи с текста такъв, какъвто е в момента, запазен или не:
- Преформулиране на избран текст по шест начина: по-ясно, по-кратко, по-подробно, коригирано, по-официално или по-приятелски.
- Заглавия: до три заглавия за статията.
- Чернова на цяла статия — заглавие, описание и текст — по кратко задание, къса, средна или дълга (около 300, 600 или 1200 думи), на оригиналния език на сайта. Използва само фактите от заданието; където липсва факт, пише заместител в квадратни скоби, който авторът да попълни, и никога не измисля.
- Писане на мястото на курсора: пасаж по кратко задание, на езика и в стила на текста около него, по същото правило за фактите.
- Оформяне на избран текст или на целия текст в заглавия, списъци, акценти и стилизираните блокове. Всяка дума на автора трябва да остане, в своя ред, иначе отговорът се отхвърля. Текст, който съдържа фрейм, видео, скрипт или формуляр, се отказва преди всяко извикване, защото оформянето би го изгубило.
- Резюме и тагове за статията и алтернативен текст за всеки език, който модел за изображения пише въз основа на самата картина.
Отговорите се изчистват до кратък списък от тагове и схеми на връзки (http, https, mailto,
относителни). Клас остава само там, където назовава стилизиран блок на собствения му елемент; без
style, без скриптове, без изображение, което текстът на автора не е съдържал, и без връзка към
адрес, който текстът на автора, заданието или статията не са дали. Числа, имена и изнесени цитати,
които отговорът съдържа, а материалът му не, се изброяват до него, за да ги провери авторът —
евристика, а не доказателство. Преформулиране,
пасаж или оформяне първо се показва в преглед и се вмъква като една стъпка за отмяна; черновата
попълва формуляра. Нищо не се запазва, докато авторът не запази.
Дизайн
- Асистентът за дизайн изготвя тема или оформлението на един тип страница по няколко думи. По подразбиране доработва текущата чернова, която моделът вижда, и променя само каквото иска заданието; при поискване започва отначало. Всеки отговор се проверява така, сякаш го е въвел оператор, а едно щракване отменя промяната или я връща.
- Дизайнът на целия сайт — темата и оформлението на всеки тип страница — се изготвя по едно задание като задача. Всяка стъпка е самостоятелна, така че неуспешна стъпка не спира останалите; задачата може безопасно да се изпълни отново, може да се откаже и записва цената си. Един сайт има най-много една задача за дизайн наведнъж.
И в двата случая се променят само чернови; за публикуването пак е нужен някой, който има право.
Превод и PDF файлове
- Преводът на статии се изпълнява като задача. Преведеният текст трябва да запази точно
маркировката на оригинала — може да се променят само
alt,title,aria-labelиplaceholder, — иначе се отхвърля. - Преводът на сайта обхваща постоянните страници, имената на раздели, менютата, страницата с връзки и, ако е отметнато, таговете, без да презаписва редакция, направена по време на изпълнението; най-много 2000 извиквания на изпълнение.
- Заглавната част на PDF: за PDF файлове, изготвени с AI, резюме, ключови думи и заглавие за колонтитула за всяка статия и всеки език, проверени като обикновен текст. Използват се отново, докато статията не бъде редактирана.
Какво трябва да издържи всеки отговор
- Всеки отговор трябва да отговаря на строга JSON схема. Отговор, който моделът е прекъснал или отказал, е неуспех и нищо от него не се записва.
- Текстът на човека се изпраща като материал и на модела се казва, че той никога не е инструкция, дори когато звучи като такава.
- Гейтуеят вижда само непрозрачни идентификатори, никога имена или имейл адреси.
Три среди, един Makefile
| dev | stage | prod | |
|---|---|---|---|
| Входна врата | HTTP, плюс HTTPS от локалния CA на Caddy | HTTPS (Let's Encrypt) | HTTPS (Let's Encrypt или ваша собствена двойка сертификат и ключ) |
| Поща от pacod-id | прихваща се от mailpit | прихваща се от mailpit | вашият релей |
| AI | мок гейтуей | мок гейтуей | вашият pacod-gw или изключен |
| Демо сайтове | да | да | няма |
Stage е конфигурацията на продукцията, с прихваната поща и симулиран AI. make check отказва
стартиране, докато липсва тайна или конфигурацията е несигурна. Правете резервни копия на .env,
базите данни в MongoDB, файловете на сайтовете и PostgreSQL на pacod-id. /mgmt-health на студиото
проверява само дали то работи: студио, което не достига pacod-id, остава „здраво“, докато екраните му
връщат 503, и съобщава за това в лога си. Сайтът и студиото пишат логове в общия формат на pacod,
като JSON или текст (APP_LOG_FORMAT).
Експортът и възстановяването пренасят сайт от една инсталация в друга. Сайт или цяла организация се изтегля от студиото като един ZIP със съдържанието и файловете си, без статистиката на посещенията, членовете и хостовете. Възстановяването проверява целия архив, преди да създаде каквото и да е, и винаги създава нов сайт или нова организация, в тази инсталация или в друга; качвания до 2 GiB се приемат само по тези два пътя. Резервните копия на цялата платформа остават дъмпове на MongoDB.
Нова инсталация в dev или stage създава три демо сайта: Demo Journal (editorial), Demo Notes
(minimal) и Starward (magazine) — списание за космически пътувания в измислена звездна система:
24 обемни статии, които използват всеки стилизиран блок, петима автори, свои страници, менюта,
оформления, тема и два публикувани броя на списанието, 37 снимки на NASA с посочено авторство и пет
измислени рекламодателя с HTML5 рекламни материали.
make site-create … DEMO=space или make seed SITE=<id> DEMO=space дава на друг сайт
съдържанието на Starward. Зареждане на сайт от собствена папка (SEED_DIR=<folder>), проверена
изцяло, преди да се запише каквото и да е, предстои (още не е издадено, в клона blog-site).
Всяка среда работи в pacod-net, Docker мрежата, която всички проекти на pacod споделят. Входните
точки, до които стигат само сървъри, като вътрешната на pacod-id, са във втора, вътрешна мрежа,
pacod-admin-net, към която се присъединява студиото. Собствените
услуги на CMS — сайтът, студиото, MongoDB и Gotenberg — са в един compose файл; собствените му копия
на споделените части — Caddy, pacod-id с неговия PostgreSQL, гейтуеят за разработка и прихващачът на
поща — са във втори, който Makefile зарежда заедно с първия. На една машина се побира само един стек,
който изпълнява споделените части; работата зад Caddy и pacod-id, които вече работят на хоста
(PROXY_MODE=external), предстои (още не е издадено, в клона blog-site).
pacod-demo зарежда само
първия файл и изпълнява CMS заедно с другите проекти зад един Caddy, един pacod-id, един PostgreSQL и
един гейтуей; той е достъпен на demo.pacod.io.
Какво е тествано и къде са границите
- Модулните тестове се изпълняват с български като оригинален език, така че се изпитва конфигурацията, различна от тази по подразбиране.
- Интеграционните тестове пускат два сайта в една истинска MongoDB и проверяват, че остават разделени: маршрутизация, разпознаване на хоста, кешове, съхранение и статистика.
make e2eпроверява вписването и портала на работещия стек.- По-новите AI заявки и отговори — картата с черновата, списъкът със заглавия, доработката и отмяната в асистента за дизайн, задачата за дизайн и списъкът с факти за проверка — още не са проверени на работещ стек.
Ограничения и още за четене
Известни ограничения във v1:
- Идентичността на сайта (
siteSettings) все още няма екран в студиото; редактира се в базата данни. - Демо съдържанието е написано на английски, затова може да го получи само сайт, чийто език на съдържанието е английски.
- Списъкът с членове не показва роли на ниво платформа.
Пътната карта изброява какво v1 отлага, сред тях API за запис, ключ за гейтуея и бюджет за AI за всяка организация, отделен работен процес, хранилище S3 и импорт на съдържание.
В хранилището:
- Архитектура — как заявката намира своя сайт
- Сигурност — какво разделя сайтовете и студиото
- Набори шаблони — как да напишете собствен набор
- Стартиране и Внедряване — среди, TLS, хостове, настройки
- Пътна карта — какво включва v1 и какво е отложено
Бърз старт
Машина за разработка
make init
make up
open http://studio.localhost:8000/
Още един сайт от командния ред
make site-create SLUG=acme NAME="Acme News" HOSTS=acme.localhost DEMO=1
Работи с
- pacod-id: Студиото вписва хората през pacod-id, който съхранява и ролите им за всяка организация и за всеки сайт.
- pacod-gw: AI функциите минават през шлюза — студиото никога не държи ключ на доставчик.
- pacod-demo: Пуска CMS заедно с другите проекти зад един Caddy, един pacod-id и един шлюз — студиото на живо и три демо сайта.
Собственият код на проекта е под Apache-2.0. Студиото включва редактора TinyMCE (GPL-2.0-or-later), затова студиото в изграден вид се разпространява като цяло при условията на GPL-3.0. Публичният сайт не го съдържа.