Этап 0: документация и макеты

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
andreypr207andClaude Opus 5.5 committed 2026-10-21 03:30:38 +02:00
commit 3400f58a5a
75 files changed
+14283

No files matched your search

+116
View File
@@ -0,0 +1,116 @@
# 02. Архитектура
## 1. Стек
| Что | Версия на 20.10.2026 | Комментарий |
|---|---|---|
| Node.js | 24 LTS (на сервере 24.22) | |
| pnpm | 12.x | ставится root через `corepack enable pnpm`; версия фиксируется полем `packageManager` |
| Next.js | 16.4.x | App Router, React Server Components |
| React | 19.x | |
| Payload CMS | 3.91.x | Payload 4 в бете; переход после стабильного релиза ([D-002](decisions/D-002-payload-version.md)) |
| PostgreSQL | 18 (на хосте) | адаптер `@payloadcms/db-postgres`, `idType: 'uuid'` |
| Tailwind CSS | 4.x | токены из макета как CSS-переменные |
| next-intl | 4.x | переводы интерфейса, префиксы языков в URL |
| Nodemailer | через `@payloadcms/email-nodemailer` | транспорты SMTP, sendmail, «только журнал» |
| ALTCHA | 3.x | капча без третьих сторон |
| grammY | 1.x | Telegram-боты, этап 3 |
| Stripe SDK | актуальный | этап 4 |
Точные версии фиксируются в `package.json` на этапе 1a. Если к старту этапа 1a выйдет стабильный Payload 4, начинаем сразу на нём.
## 2. Одно приложение на все домены
1. Одно Next.js-приложение, Payload установлен внутрь как библиотека: админка, API и фронтенды — один процесс ([D-001](decisions/D-001-single-app.md)).
2. Proxy Next.js (`src/proxy.ts`, бывший middleware) по заголовку Host находит запись в коллекции «Домены» и узнаёт сайт (`dedicated` или `mass`) и тип фронтенда: сайт, кабинет или MiniApp. Список доменов кэшируется в памяти и сбрасывается при изменении записи.
3. Proxy переписывает запрос во внутренний маршрут вида `/{тип}/{сайт}/{язык}/…`, для маркетинговых страниц ещё и `/{валюта}/`. Снаружи эти пути недоступны: прямой запрос к ним даёт 404.
4. Хост админки (`plc.veilix.cc`, `dev.plc.veilix.cc`) задаётся в `.env`. Только на нём работают `/admin` и REST API Payload. На доменах брендов эти пути закрыты.
5. Сайтам, кабинетам и MiniApp хост админки не нужен:
a. данные Payload они читают в том же процессе через Local API — это вызов функции, а не HTTP-запрос;
b. запросы браузера (формы, вход, действия в кабинете) идут на тот домен, где открыта страница: server actions и эндпоинты `/svc/…`;
c. вебхуки платёжных систем и Telegram приходят на публичные домены, не на хост админки;
d. файлы медиатеки отдаются через домены сайтов.
6. Ядро общее (биллинг, ключи, вход, рефералы, почта), UI-кит общий, а страницы у каждого бренда свои: данные в дизайнах представлены по-разному.
## 3. Структура кода (план)
```
src/
proxy.ts — домен → сайт, тип фронтенда, язык, валюта
payload.config.ts
app/
(payload)/ — админка и REST API Payload (стандартные файлы)
(front)/m/[site]/[locale]/[currency]/… — маркетинговые сайты
(front)/c/[site]/[locale]/… — кабинеты и оформление заказа
(front)/t/[site]/… — MiniApp (этап 3)
svc/… — свои эндпоинты: ALTCHA, вебхуки, health
payload/
collections/ globals/ hooks/ access/ fields/
core/ — бизнес-логика без привязки к UI
billing/ keys/ auth/ referral/ email/ payments/ currency/ geo/
brands/
dedicated/ mass/ — страницы и компоненты каждого бренда
ui/ — общие компоненты: кнопки, поля, шиты, переключатели, флаги, карта
i18n/ — конфиг next-intl и messages/{en,ru,de,fr,es}.json
styles/ — Tailwind, токены брендов, темы
```
Бизнес-логика живёт в `core/` и вызывается одинаково из хуков Payload, server actions, вебхуков, фоновых задач и (на этапе 3) бота.
## 4. Окружения
| | dev | prod |
|---|---|---|
| Пользователь ОС | vx_dev (под ним работают редактор и сессии Claude) | vx_prod (без редактора и ИИ-агентов) |
| Папка | `/home/vx_dev/veilix_dev` — рабочая копия git | `/home/vx_prod/veilix` — `releases/`, симлинк `current`, `shared/` |
| Запуск | `pnpm dev` (горячая перезагрузка) | `pnpm start` из `current` после `pnpm build` |
| Порт | 3100 | 3000 |
| База | `veilix_dev` | `veilix_prod` |
| Схема базы | синхронизируется автоматически (push) | миграции Payload, применяются при выкладке |
| Домены | `dev.*`, `dev.plc.veilix.cc` | основные, `plc.veilix.cc` |
| Секреты | `/home/vx_dev/veilix_dev/.env` | `/home/vx_prod/veilix/shared/.env` |
| Файлы (media) | `/home/vx_dev/veilix_dev/media` | `/home/vx_prod/veilix/shared/media` |
1. Без Docker: оба окружения — обычные процессы Node, каждый под своим пользователем ОС, запускаются одинаково — systemd user-юнитами: `veilix-dev.service` у vx_dev, `veilix.service` у vx_prod. У vx_dev нет доступа к файлам, секретам, логам и базе prod. Подробно — [D-003](decisions/D-003-hosting.md), [D-004](decisions/D-004-environments.md).
2. Dev закрыт от посторонних в nginx и запрещён к индексации, см. [09-infrastructure.md](09-infrastructure.md).
3. Платёжные системы в dev — только в тестовом режиме.
## 5. Выкладка в prod
1. В dev: изменения схемы оформляются миграцией (`payload migrate:create`), коммит, push в Gitea, тег релиза.
2. Выкладка — команда `deploy <тег>` пользователя vx_prod, появится на этапе 1h. Запускает её пользователь; по желанию — sudo-правило, разрешающее vx_dev запускать только эту команду (Q-18). Шаги:
a. взять тег из Gitea (у vx_prod ключ только на чтение) в новую папку `releases/<дата-тег>`;
b. `pnpm install --frozen-lockfile` и `pnpm build`; ссылки на `shared/.env` и `shared/media`;
c. применить миграции к `veilix_prod`;
d. переключить симлинк `current` на новый релиз и перезапустить `veilix.service`;
e. проверить `/svc/health`; хранить 5 последних релизов.
3. Откат: переключить `current` на прошлый релиз и перезапустить. Миграции пишем так, чтобы прошлый релиз продолжал работать с новой схемой (сначала добавляем, удаляем позже).
4. Простой при выкладке — только время перезапуска процесса, несколько секунд.
## 6. Рендеринг и кэширование
1. Маркетинговые страницы собираются статически во всех вариантах «язык × валюта» и кэшируются. Proxy выбирает вариант по префиксу языка и cookie валюты.
2. Изменение в админке (цена, статус локации, FAQ) вызывает хук Payload, и Next.js пересобирает затронутые страницы за секунды (on-demand revalidation по тегам). Запасной вариант — плановое обновление раз в 5 минут.
3. При оформлении заказа наличие и цена проверяются заново на сервере. Устаревшая страница не может продать то, чего нет.
4. Кабинет рендерится на сервере (SSR) для каждого запроса, без кэша.
5. Маркетинговые домены не ставят cookies, кроме выбора языка, валюты и темы.
## 7. Фоновые задачи
Встроенная очередь Payload (Jobs Queue) с расписанием cron. Исполнитель работает внутри процесса приложения (`autoRun`), в dev его можно отключить переменной окружения.
| Задача | Расписание | Что делает |
|---|---|---|
| `renewals` | каждые 15 минут | продление ключей, у которых срок подходит (см. [04-billing.md](04-billing.md)) |
| `reminders` | каждый час | напоминания за 3 дня до окончания |
| `expiry` | каждые 15 минут | перевод в льготный период и в «истёк» |
| `send-email` | по событию | отправка письма с повторами при ошибке |
| `cleanup` | раз в сутки | старые коды входа, сессии, записи журнала писем |
| `reconcile` | раз в сутки | сверка кэша баланса с суммой движений по счёту |
## 8. Чего не используем
1. Docker — не нужен ни в dev, ни в prod ([D-003](decisions/D-003-hosting.md)).
2. n8n — боты пишем в коде на grammY ([D-015](decisions/D-015-telegram-bots.md)).
3. listmonk — шаблоны писем в Payload, отправка своим транспортом ([D-014](decisions/D-014-email.md)).
4. Redis — стоит на сервере, но пока не нужен: лимиты запросов и очередь задач работают на PostgreSQL.