Этап 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
@@ -0,0 +1,26 @@
|
||||
# D-022. Proxy: внутренние маршруты по хосту, домены из внутреннего эндпоинта
|
||||
|
||||
Статус: принято, 21.10.2026. Уточняет [D-005](D-005-multitenancy.md), пункт 3.
|
||||
|
||||
## Контекст
|
||||
|
||||
1. Языки, валюты, основной домен и индексация заданы у домена, а не у сайта. Страница, собранная на весь сайт, не знает, на каком зеркале её открыли: у зеркала со своим набором языков переключатель, `hreflang` и `canonical` были бы чужими.
|
||||
2. Proxy в Next 16 работает отдельно от кода страниц, и документация Next запрещает полагаться на общие с ним модули и глобальные переменные. Payload внутри proxy создал бы второй экземпляр поверх общего `global._payload`.
|
||||
|
||||
## Решение
|
||||
|
||||
1. Во внутреннем маршруте — хост: `/m/{хост}/{язык}/{валюта}/…` — сайт, `/c/{хост}/{язык}/…` — кабинет, `/t/{хост}/…` — MiniApp. Сайт и настройки страница берёт из записи домена.
|
||||
2. Proxy получает список активных доменов у `/svc/internal/domains` (заголовок `x-vx-internal-token` с `INTERNAL_TOKEN` из `.env`) и держит его в памяти 15 секунд. Устаревший список отдаётся сразу и обновляется в фоне. Правка домена в админке применяется в пределах 15 секунд.
|
||||
3. Proxy свой, а не middleware next-intl: тот не умеет брать домены из базы, класть хост и валюту в путь и учитывать `X-Geo-Country`. next-intl используется для сообщений, форматирования и ссылок. Ботов определяет библиотека `isbot`.
|
||||
4. `skipProxyUrlNormalize: true` в `next.config.ts`. Без него адрес запроса в proxy строится от `localhost`, а роутер Next слушает `127.0.0.1`, и любое переписывание считается внешним.
|
||||
|
||||
## Рассмотренные варианты
|
||||
|
||||
1. Код сайта во внутреннем маршруте, как было в 02 — ломается, как только у зеркала свой набор языков или валют.
|
||||
2. Local API Payload прямо в proxy — второй экземпляр Payload и пул соединений в отдельной сборке, конфликт по `global._payload`.
|
||||
3. Сброс кэша proxy из хука Payload — хук срабатывает до фиксации транзакции, и proxy мог бы перечитать старые данные; при кэше в 15 секунд выигрыш не нужен.
|
||||
|
||||
## Последствия
|
||||
|
||||
1. Статических вариантов страниц столько, сколько сочетаний «домен × язык × валюта»; сейчас у сайта по одному домену каждого типа.
|
||||
2. Если эндпоинт недоступен, proxy работает по последнему полученному списку, а без него отвечает 503.
|
||||
@@ -0,0 +1,21 @@
|
||||
# D-023. Языки сайта
|
||||
|
||||
Статус: принято, 21.10.2026. Уточняет [D-006](D-006-localization.md), пункты 4 и 5.
|
||||
|
||||
## Контекст
|
||||
|
||||
В модели данных языки были только у доменов. Но языки контента — свойство сайта: домен может показывать хоть один язык, а контент сайта нужен на всех его языках.
|
||||
|
||||
## Решение
|
||||
|
||||
1. У сайта поле `locales` — языки контента. По умолчанию все языки конфига. Английский обязателен: на него откатывается непереведённый контент.
|
||||
2. Языки домена — часть языков сайта. Язык нельзя убрать у сайта, пока он включён на каком-то домене сайта.
|
||||
3. `filterAvailableLocales` показывает в админке языки сайта, выбранного в переключателе; без выбранного сайта — все языки.
|
||||
|
||||
## Рассмотренные варианты
|
||||
|
||||
Языки сайта как объединение языков его доменов — без отдельного поля, но тогда контент на языке существует, только пока язык включён на домене.
|
||||
|
||||
## Последствия
|
||||
|
||||
Когда появятся pt-br и ja, их можно включить одному бренду: редакторы второго не увидят пустых языков.
|
||||
@@ -0,0 +1,18 @@
|
||||
# D-024. Перенаправление на выбранный язык
|
||||
|
||||
Статус: принято, 21.10.2026. Уточняет [D-007](D-007-language-detection.md), пункт 2.
|
||||
|
||||
## Контекст
|
||||
|
||||
По D-007 перенаправление шло только при первом заходе без cookie. Тогда выбравший русский при следующем заходе на `veilix.app/` видел бы английский.
|
||||
|
||||
## Решение
|
||||
|
||||
1. Без префикса открывается язык домена по умолчанию (`domains.defaultLocale`, сейчас везде en). Адрес с его префиксом (`/en/…`) перенаправляется на адрес без префикса.
|
||||
2. Адрес без префикса и cookie `NEXT_LOCALE` с языком, включённым на домене, — перенаправление на этот язык, если он не язык по умолчанию.
|
||||
3. Cookie нет — язык браузера среди включённых на домене, затем страна из `X-Geo-Country`, затем язык по умолчанию; перенаправление, если вышел не язык по умолчанию.
|
||||
4. Cookie ставит только переключатель языка. Поисковых ботов (`isbot`) не перенаправляем никогда; перенаправляются только запросы GET и HEAD.
|
||||
|
||||
## Последствия
|
||||
|
||||
Так же по умолчанию работает next-intl. Выбор английского в переключателе тоже запоминается и отключает перенаправление по языку браузера.
|
||||
@@ -8,9 +8,9 @@
|
||||
| [D-002](D-002-payload-version.md) | Payload 3, переход на 4 после стабильного релиза | принято |
|
||||
| [D-003](D-003-hosting.md) | Без Docker: всё на хосте, процессы под systemd | принято |
|
||||
| [D-004](D-004-environments.md) | Dev и prod на одном сервере под разными пользователями | принято |
|
||||
| [D-005](D-005-multitenancy.md) | Сайты — тенанты, домены в базе, маршрутизация по Host | принято |
|
||||
| [D-006](D-006-localization.md) | Переводы: поля Payload и next-intl; 5 языков | принято |
|
||||
| [D-007](D-007-language-detection.md) | Определение языка; MaxMind через nginx | принято |
|
||||
| [D-005](D-005-multitenancy.md) | Сайты — тенанты, домены в базе, маршрутизация по Host | принято, уточнено в D-022 |
|
||||
| [D-006](D-006-localization.md) | Переводы: поля Payload и next-intl; 5 языков | принято, уточнено в D-023 |
|
||||
| [D-007](D-007-language-detection.md) | Определение языка; MaxMind через nginx | принято, уточнено в D-024 |
|
||||
| [D-008](D-008-uuid.md) | UUID и случайные публичные коды | принято |
|
||||
| [D-009](D-009-billing-model.md) | Заказы, платежи, движения по счёту; всё через баланс | принято |
|
||||
| [D-010](D-010-currencies.md) | Валюты и платёжные системы — сущности; платёжные системы привязаны к валютам | принято |
|
||||
@@ -25,3 +25,6 @@
|
||||
| [D-019](D-019-external-services.md) | Chatwoot и Twenty CRM на отдельном VPS | принято |
|
||||
| [D-020](D-020-key-models.md) | Модели ключей dedicated и mass | принято |
|
||||
| [D-021](D-021-admin-host.md) | Админка на plc.veilix.cc с доступом по IP | принято |
|
||||
| [D-022](D-022-proxy-routing.md) | Proxy: внутренние маршруты по хосту, домены из внутреннего эндпоинта | принято |
|
||||
| [D-023](D-023-site-locales.md) | Языки сайта — поле `sites.locales`, домены показывают их часть | принято |
|
||||
| [D-024](D-024-locale-redirect.md) | Перенаправление на выбранный язык | принято |
|
||||
Reference in new issue
Block a user