Этап 1a: фундамент
- Next 16.4 и Payload 3.91 в одном приложении, PostgreSQL с UUID, TypeScript 6, ESLint 9, Prettier, Vitest - Payload: администраторы с ролями, multi-tenant по сайтам, локализация en/ru/de/fr/es, интерфейс админки ru/en, языки сайта (sites.locales) - Коллекции sites, domains, currencies, countries, locations, tags, tariffs, payment-systems (manual), media и начальные данные (pnpm seed) - Proxy: хост → домен, язык и валюта, внутренние маршруты по хосту; /admin и /api закрыты на доменах брендов - Токены и темы брендов, оболочки сайта и кабинета, переключатели языка, валюты и темы, заглушки страниц - /svc/health, юнит systemd veilix-dev.service - Документация: решения D-022…D-024, команды в CLAUDE.md Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
3400f58a5a
commit
7e331c2c9d
140 files changed
+15885
-43
No files matched your search
+30
-18
@@ -11,19 +11,22 @@
|
||||
| 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-переменные |
|
||||
| TypeScript | 6.0 | не 7.0: typescript-eslint из eslint-config-next поддерживает только TS < 6.1 |
|
||||
| ESLint | 9.39 | не 10: eslint-plugin-react, import и jsx-a11y не заявляют поддержку ESLint 10 |
|
||||
| Vitest | 5.x | модульные тесты |
|
||||
| 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, начинаем сразу на нём.
|
||||
Точные версии закреплены в `package.json`: на этапе 1a — Next 16.4.2, Payload 3.91.0, React 19.3.1, next-intl 4.14.9, Tailwind 4.3.3, pnpm 12.14. На старте 1a Payload 4 был в бете (4.0.0-beta.3), поэтому начали на 3.91 ([D-002](decisions/D-002-payload-version.md)). Телеметрия Next.js и Payload выключена.
|
||||
|
||||
## 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.
|
||||
2. Proxy Next.js (`src/proxy.ts`, бывший middleware) по заголовку Host находит домен и узнаёт сайт (`dedicated` или `mass`) и тип фронтенда: сайт, кабинет или MiniApp. Список активных доменов proxy берёт у `/svc/internal/domains` (по токену из `.env`) и держит в памяти 15 секунд: правка домена в админке применяется в пределах 15 секунд ([D-022](decisions/D-022-proxy-routing.md)).
|
||||
3. Proxy переписывает запрос во внутренний маршрут по хосту: `/m/{хост}/{язык}/{валюта}/…` — сайт, `/c/{хост}/{язык}/…` — кабинет, `/t/{хост}/…` — MiniApp. Снаружи эти пути недоступны: прямой запрос к ним получает ещё один префикс и попадает в 404. Решения proxy — чистая функция `src/core/routing/decide.ts` с модульными тестами.
|
||||
4. Хост админки (`plc.veilix.cc`, `dev.plc.veilix.cc`) задаётся в `.env`. Только на нём работают `/admin` и REST API Payload. На доменах брендов эти пути закрыты.
|
||||
5. Сайтам, кабинетам и MiniApp хост админки не нужен:
|
||||
a. данные Payload они читают в том же процессе через Local API — это вызов функции, а не HTTP-запрос;
|
||||
@@ -32,29 +35,38 @@
|
||||
d. файлы медиатеки отдаются через домены сайтов.
|
||||
6. Ядро общее (биллинг, ключи, вход, рефералы, почта), UI-кит общий, а страницы у каждого бренда свои: данные в дизайнах представлены по-разному.
|
||||
|
||||
## 3. Структура кода (план)
|
||||
## 3. Структура кода
|
||||
|
||||
```
|
||||
src/
|
||||
proxy.ts — домен → сайт, тип фронтенда, язык, валюта
|
||||
proxy.ts — хост → домен, язык, валюта, внутренний маршрут
|
||||
payload.config.ts
|
||||
payload-types.ts — типы Payload (pnpm generate:types)
|
||||
app/
|
||||
(payload)/ — админка и REST API Payload (стандартные файлы)
|
||||
(front)/m/[site]/[locale]/[currency]/… — маркетинговые сайты
|
||||
(front)/c/[site]/[locale]/… — кабинеты и оформление заказа
|
||||
(front)/t/[site]/… — MiniApp (этап 3)
|
||||
svc/… — свои эндпоинты: ALTCHA, вебхуки, health
|
||||
(front)/m/[host]/[locale]/[currency]/(site)/… — маркетинговые сайты
|
||||
(front)/c/[host]/[locale]/(shell)/… — кабинет с навигацией
|
||||
(front)/c/[host]/[locale]/(plain)/… — вход и оформление заказа
|
||||
(front)/t/[host]/… — MiniApp (этап 3)
|
||||
(front)/context.ts — домен, язык и валюта страницы, данные оболочек
|
||||
svc/… — свои эндпоинты: health, internal/domains; дальше ALTCHA, вебхуки
|
||||
payload/
|
||||
collections/ globals/ hooks/ access/ fields/
|
||||
collections/ access/ fields/ hooks/ components/
|
||||
core/ — бизнес-логика без привязки к UI
|
||||
billing/ keys/ auth/ referral/ email/ payments/ currency/ geo/
|
||||
sites.ts domains/ routing/ currencies/ payments/ — этап 1a
|
||||
billing/ keys/ auth/ referral/ email/ geo/ — следующие этапы
|
||||
brands/
|
||||
dedicated/ mass/ — страницы и компоненты каждого бренда
|
||||
ui/ — общие компоненты: кнопки, поля, шиты, переключатели, флаги, карта
|
||||
i18n/ — конфиг next-intl и messages/{en,ru,de,fr,es}.json
|
||||
styles/ — Tailwind, токены брендов, темы
|
||||
dedicated/ mass/ — различия брендов; страницы и компоненты — с этапа 1b
|
||||
ui/ — общие компоненты: оболочки сайта и кабинета, переключатели, тема, логотип, иконки
|
||||
i18n/ — языки, конфиг next-intl и messages/{en,ru,de,fr,es}.json
|
||||
styles/ — Tailwind, токены брендов
|
||||
seed/ — начальные данные (pnpm seed)
|
||||
ops/systemd/ — юниты systemd
|
||||
scripts/ — служебные скрипты (очистка dev-базы)
|
||||
```
|
||||
|
||||
Корневые layout сайта и кабинета дают только документ: `<html>` с `lang`, `data-site` и скриптом темы. Шапка, подвал и навигация — во вложенных группах `(site)`, `(shell)` и `(plain)`.
|
||||
|
||||
Бизнес-логика живёт в `core/` и вызывается одинаково из хуков Payload, server actions, вебхуков, фоновых задач и (на этапе 3) бота.
|
||||
|
||||
## 4. Окружения
|
||||
@@ -66,7 +78,7 @@ src/
|
||||
| Запуск | `pnpm dev` (горячая перезагрузка) | `pnpm start` из `current` после `pnpm build` |
|
||||
| Порт | 3100 | 3000 |
|
||||
| База | `veilix_dev` | `veilix_prod` |
|
||||
| Схема базы | синхронизируется автоматически (push) | миграции Payload, применяются при выкладке |
|
||||
| Схема базы | синхронизируется автоматически (push); если push задаёт вопросы — `pnpm db:reset` и `pnpm seed` | миграции 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` |
|
||||
@@ -77,7 +89,7 @@ src/
|
||||
|
||||
## 5. Выкладка в prod
|
||||
|
||||
1. В dev: изменения схемы оформляются миграцией (`payload migrate:create`), коммит, push в Gitea, тег релиза.
|
||||
1. В dev: изменения схемы оформляются миграцией (`payload migrate:create`), коммит, push в Gitea, тег релиза. До первой выкладки (этап 1h) миграции не накапливаем: перед ней создаём одну начальную по итоговой схеме. Механизм проверен на этапе 1a: миграция, применённая к пустой схеме, создаёт те же таблицы, что push в dev.
|
||||
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`;
|
||||
@@ -89,7 +101,7 @@ src/
|
||||
|
||||
## 6. Рендеринг и кэширование
|
||||
|
||||
1. Маркетинговые страницы собираются статически во всех вариантах «язык × валюта» и кэшируются. Proxy выбирает вариант по префиксу языка и cookie валюты.
|
||||
1. Маркетинговые страницы собираются статически в вариантах «домен × язык × валюта» при первом запросе (ISR) и кэшируются. Proxy выбирает вариант по префиксу языка и cookie валюты.
|
||||
2. Изменение в админке (цена, статус локации, FAQ) вызывает хук Payload, и Next.js пересобирает затронутые страницы за секунды (on-demand revalidation по тегам). Запасной вариант — плановое обновление раз в 5 минут.
|
||||
3. При оформлении заказа наличие и цена проверяются заново на сервере. Устаревшая страница не может продать то, чего нет.
|
||||
4. Кабинет рендерится на сервере (SSR) для каждого запроса, без кэша.
|
||||
|
||||
Reference in new issue
Block a user