Архитектура и границы приложений
Для чего нужен каждый компонент
| Компонент | Задача | Граница |
|---|---|---|
| Next.js | Страницы, tRPC, webhook, резервный media proxy | Только опубликованный контент через CMS API |
| Mantine | Компоненты, тема и Styles API | Данные приходят через props |
| contracts/Zod | Типы и проверка HTTP данных | TypeScript не заменяет runtime validation |
| Strapi | Редактирование, Draft & Publish, REST и upload provider | Собственные npm/React 18/TS 5 |
| Garage | S3 API и public website bucket | Один узел, без AWS ACL и без HA |
| PostgreSQL | База Strapi | Отдельный контейнер, тот же VPS |
| Caddy | TLS, reverse proxy | Не заменяет права CMS/API token |
Картина на одном VPS
Исходник схемы
flowchart LR Visitor[Посетитель] --> Caddy Caddy --> Web[Next.js] Caddy --> CMS[Strapi] Caddy -->|media origin| S3[Garage] Web -->|read-only / tagged cache| CMS Web -->|legacy media proxy| S3 CMS -->|S3 upload| S3 CMS --> CMSDB[(PostgreSQL: strapi)] CMS -->|webhook| Web
База Strapi находится в PostgreSQL; все контейнеры — на одном VPS. Сбой базы, CMS или хоста отражается на обновлении контента. Готовые страницы кеширует Next.js. Read-only token не передаётся браузеру; панель защищена собственным входом Strapi, а публичные изображения читаются без пароля. Draft media-файлы не становятся приватными автоматически.
Структура монорепозитория
apps/
web/ Next.js, CMS adapter и серверные тесты
docs/ Статический сайт учебника
packages/
contracts/ Zod-схемы и типы
ui/ Независимый React/UIKit на Mantine и Storybook
infra/
strapi/ Самостоятельный npm-проект CMS
postgres/ Первичное создание пользователей и баз
garage/ S3-совместимое объектное хранилище
docs/ Образ и конфигурация учебника
storybook/ Образ и конфигурация каталога компонентов
Caddyfile* Локальный и production reverse proxy
scripts/
setup-env.mjs Локальные секреты
deploy/ Подготовка хоста и применение релизов
.sourcecraft/ci.yaml CI, образы и доставка
docs/ Это руководство
В pnpm workspace входят только apps/* и packages/*. infra/strapi использует
свой package-lock.json, React 18 и TypeScript 5. Изоляция позволяет использовать
современные версии в сайте, не заставляя Strapi разделять несовместимые зависимости.
Зависимости пакетов
Исходник схемы
flowchart TD Web[web: Next.js] --> Contracts[contracts: Zod] Web --> UI[ui: React и Mantine] UI --> Mantine[Mantine] Docs[docs: Next.js и MDX] Strapi[Strapi: отдельный npm lockfile]
Стрелки обозначают импорты: web использует contracts и ui. UIKit не импортирует
Next.js или CMS; приложение передаёт данные и адаптер ссылок. Docs самостоятельно
читает Markdown/MDX и не обращается к CMS.
workspace:* означает локальный пакет, а не скачивание одноимённого пакета из npm.
pnpm -r build учитывает зависимости workspace: сначала собирается contracts.
Для сборки workspace отдельный оркестратор не нужен; команды выполняет pnpm.
Пути данных
Чтение: Strapi REST → серверный CMS client → Zod/contracts → repository → Server Component или tRPC → посетитель. Все страницы используют одну модель данных. Полная pagination читается до конца; ошибка второй страницы не выдаётся за полный список.
Обновление: Strapi webhook → HTTP handler → Zod → инвалидирование кеша Next.js. Страницы обновляются при следующем посещении без полной пересборки.
Медиа: браузер → Caddy → Garage website endpoint. CMS-адаптер нормализует
адрес до публичного /filename; Caddy добавляет внутренний префикс strapi/.
Next.js участвует в передаче только для старых local uploads через /api/media/.
Импорт: явный снимок публичного источника → нормализация HTML/дат → upload → Document Service create/publish. Архив нужен для аудита, не для runtime fallback.
Сборка, запуск и проверка типов
- Сборка превращает TypeScript contracts в JavaScript, а Next.js — в production-приложение со статически сгенерированными страницами и ISR.
- Запуск исполняет уже подготовленный код. В dev Next.js обрабатывает исходники.
- Проверка типов ищет ошибки без выполнения бизнес-операций.
- Runtime-валидация проверяет данные, поступившие от внешнего источника.
TypeScript не защищает автоматически от неверного HTTP JSON: сетевой клиент может не использовать TypeScript вообще. Поэтому webhook проверяется Zod в web.
Принятые решения
Next.js выбран для страниц и серверного API. Vike в репозитории не используется.
Mantine выбран для интерфейса. Jest работает через next/jest, поэтому отдельный
Vite-проект для тестирования не нужен. Vitest и Playwright не установлены.
Отдельное исключение — SPA ожидания холодного стенда
в apps/standby: Vite собирает React/Mantine в один HTML с встроенными стилями
и скриптом. Его обслуживает Python-диспетчер, пока контейнеры стенда остановлены.
Он не заменяет Next.js или Storybook и не читает CMS-контент.
Strapi собирается из стандартного проекта своим Dockerfile. Мы не полагаемся на неопределённый сторонний «готовый Strapi image».
Где смотреть код
Workspace, root scripts, Dockerfile, Compose, контракты.
Наблюдаемость
Новый самостоятельный Compose-стек описан в infra/observability:
браузер → Umami, ошибки Next.js → GlitchTip, Node/Blackbox Exporter → Prometheus →
Grafana. UIKit не импортирует SDK аналитики или ошибок; интеграция находится
в apps/web. Базы аналитики и ошибок отделены от Strapi ролями и владельцами.
Внешняя доставка алертов отключена; Telegram является учебным упражнением. Все системы наблюдения размещаются на том же VPS и разделяют его отказ. Пояснение DevOps · Практическая глава.