Files
veilix.app/docs/09-infrastructure.md
T
andreypr207andClaude Opus 5.5 515acfb69d Документация: этапы по фазам, решения D-026…D-033
- Новый порядок этапов (D-026): фаза 1 — концепт на dev без внешних
  сервисов (1b сайт, 1c вход, 1d ключи, 1e счёт и демо-оплата,
  1f демо-выдача, 1g продления, 1h рефералы); фаза 2 — Telegram;
  фаза 3 — ALTCHA, ЮKassa, почта, Twenty, Chatwoot, Umami, выдача
  через API и публичный запуск; фаза 4 — звёзды и эквайринги.
- Решения: демо-оплата по списку IP (D-027), выдача ключей через
  адаптер manual/demo/api (D-028), страницы сайта из блоков Payload
  (D-029), пересборка через revalidatePath (D-030), RU-адрес у mass
  (D-031), счётчик свободных серверов (D-032), переводы на 5 языков
  в каждом этапе (D-033).
- Тексты описывают работающий продукт; Вейликс — выделенный IP на
  общем сервере; Telegram поддержки — @veilix_support; сроки ответа
  не обещаем; VPN-серверы не ведут журналов.
- Ссылки на этапы во всех документах, Q-12 решён (ЮKassa для RUB),
  R-19 (библиотеки для headless Chromium) сделано.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 03:48:10 +02:00

112 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 09. Инфраструктура
## 1. Сервер (проверено 18–20.10.2026)
| Что | Значение |
|---|---|
| ОС | Ubuntu 26.04.1 LTS |
| Ресурсы | 4 vCPU, 7.7 ГБ RAM, 8 ГБ swap, 145 ГБ диска (занято 24 ГБ) |
| IP | 169.58.76.183 — сайты (PTR `fr-ub2gs3.veilix.cc`, прямая запись совпадает); 173.212.216.183 — основной, через него по умолчанию идёт исходящий трафик (PTR `vmi3593751.contaboserver.net`) |
| Установлено | Node 24.22, PostgreSQL 18.6, nginx 1.28 (модули geoip2 и brotli подключены), Redis 8, Postfix 3.10, OpenDKIM, certbot 4.0, Docker 29 (не используем), git |
| Нет | pnpm, база GeoLite2 (`.mmdb`), сертификаты TLS, geoipupdate |
| DNS | все 15 хостов проекта (вместе с `umi.veilix.cc`) указывают на 169.58.76.183; DNS-хостинг — Gcore |
## 2. Папки
```
/home/vx_dev/veilix_dev/ — dev (пользователь vx_dev): рабочая копия git, .env, media/
/home/vx_prod/veilix/ — prod (пользователь vx_prod), закрыто для остальных
releases/<дата-тег>/ — собранные релизы (храним 5 последних)
current -> releases/… — симлинк на работающий релиз
shared/.env — секреты prod
shared/media/ — загруженные файлы prod
/home/umami/umami/ — Umami (пользователь umami)
```
vx — личный пользователь администратора, к проекту отношения не имеет. Под vx_prod нет редактора и ИИ-агентов, VSCode Remote под vx_prod не подключаем ([D-004](decisions/D-004-environments.md)). Домашние папки всех пользователей — `750`, владелец — сам пользователь. Пустые папки `/home/vx/veilix.app`, `/home/vx/my.veilix.app`, `/home/vx/tg.veilix.app` проекту не нужны.
## 3. Процессы
1. Каждый сервис — под своим пользователем ОС, как systemd user-юнит. Root один раз включает `loginctl enable-linger` для vx_dev, vx_prod и umami, чтобы юниты работали без входа в систему.
2. `veilix-dev.service` (vx_dev): `pnpm dev` на 127.0.0.1:3100 из `/home/vx_dev/veilix_dev`, перезапуск при сбое. Файл юнита — `ops/systemd/veilix-dev.service` в репозитории, подключён через `systemctl --user link`.
3. `veilix.service` (vx_prod): `pnpm start` на порту 3000 из `/home/vx_prod/veilix/current`, `NODE_ENV=production`, перезапуск всегда.
4. `umami.service` (umami): Umami на порту 3300.
5. Фоновые задачи Payload выполняются внутри процессов приложения ([02-architecture.md](02-architecture.md), раздел 7).
6. Логи — journald, у каждого пользователя свои (`journalctl --user -u veilix` под vx_prod). Сессии Claude логи prod не видят: при разборе проблем prod их присылает пользователь.
7. Проверка живости — `/svc/health` (с проверкой базы).
## 4. nginx
| Хосты | Куда | Доступ |
|---|---|---|
| veilix.app, veilixvpn.com, my.veilix.app, my.veilixvpn.com, tg.veilix.app, tg.veilixvpn.com | 127.0.0.1:3000 | открыт |
| plc.veilix.cc | 127.0.0.1:3000 | только разрешённые IP |
| dev.veilix.app, dev.my.veilix.app, dev.veilixvpn.com, dev.my.veilixvpn.com | 127.0.0.1:3100 | только разрешённые IP или пароль; `X-Robots-Tag: noindex` |
| dev.tg.veilix.app, dev.tg.veilixvpn.com | 127.0.0.1:3100 | открыт для Telegram; приложение пускает только разрешённые Telegram-аккаунты |
| dev.plc.veilix.cc | 127.0.0.1:3100 | только разрешённые IP |
| umi.veilix.cc | 127.0.0.1:3300 | только разрешённые IP |
1. Слушает 169.58.76.183, порты 80 (редирект на HTTPS и проверка certbot) и 443. TLS 1.2 и 1.3, HTTP/2, HSTS.
2. Сертификаты — certbot, проверка через HTTP.
3. Сжатие — brotli и gzip.
4. GeoIP: блок `geoip2` в `/etc/nginx/conf/ip.conf`, база `/var/lib/GeoIP/GeoLite2-Country.mmdb` с `auto_reload`. Переменная `$geoip_country_code` уходит приложению заголовком `X-Geo-Country`; такой же заголовок от клиента nginx затирает. Старый модуль GeoIP v1 (`modules-enabled/50-mod-http-geoip.conf`) отключён: он занимает те же имена переменных, и `nginx -t` падает с ошибкой «the duplicate "geoip_country_code" variable».
5. Заголовки к приложению — общий файл `/etc/nginx/snippets/veilix_proxy.conf` (плюс `maps/upgrade.conf` для WebSocket), его подключает каждый `location` с `proxy_pass`: `Host`, `X-Real-IP` и `X-Forwarded-For` (оба — `$remote_addr`: nginx стоит первым, цепочку от клиента не принимаем), `X-Forwarded-Proto`, `X-Geo-Country`, заголовки WebSocket (нужны горячей перезагрузке dev). `proxy_set_header` не наследуется в `location` со своими `proxy_set_header`, поэтому файл подключается в каждый такой `location`.
6. В `nginx.conf` подключаются только `sites-enabled/*.conf`: файлы сайтов должны заканчиваться на `.conf`. Сайты: `sites-available/02_vx_veilix-prod.conf`, `02_vx_veilix-dev.conf`, `02_vx_plc-prod.conf`, `02_vx_plc-dev.conf`. Сертификаты Let's Encrypt (certbot, проверка через nginx): `veilix.app` (6 prod-хостов брендов), `dev.veilix.app` (6 dev-хостов), `plc.veilix.cc`, `dev.plc.veilix.cc`.
a. Доступ по IP — файл `conf/ip_list_admin.conf` со строками `allow …;`, в `location` после него `deny all;`. Подключается на хостах админки и dev, кроме `dev.tg.*`.
b. `dev.tg.*` — отдельный `server` без ограничения по IP, с `X-Robots-Tag: noindex`.
c. На порту 80 — `location / { return 301 https://$host$request_uri; }`. Именно внутри `location`, а не на уровне `server`: так certbot при продлении может вставить свой `location` для проверки домена.
7. Лимиты запросов (`limit_req`) на эндпоинты входа и формы.
8. Для tg.* (этап 2a) разрешить показ во фрейме Telegram Web (`frame-ancestors` для доменов telegram.org).
9. Пути трекера Umami на доменах брендов проксируются на 127.0.0.1:3300 ([08-integrations.md](08-integrations.md), раздел 8).
10. Конфигурацию готовит Claude текстом на согласование, применяет root.
## 5. PostgreSQL
1. Базы `veilix_dev`, `veilix_prod` и `umami`. Владелец каждой — роль с именем пользователя ОС: `vx_dev`, `vx_prod`, `umami`.
2. Вход по Unix-сокету с проверкой пользователя ОС: стандартное правило Ubuntu `local all all peer` в `pg_hba.conf` пускает пользователя ОС только под ролью с тем же именем. Паролей нет, `pg_hba.conf` и `pg_ident.conf` править не нужно.
3. У каждой базы снято право подключения для всех (`REVOKE CONNECT … FROM PUBLIC`): войти может только владелец. vx_dev в `veilix_prod` не попадёт.
4. Роль владеет своей базой: Payload нужны права на изменение схемы (push в dev, миграции в prod).
5. Строка подключения: `postgresql://vx_dev@/veilix_dev?host=/var/run/postgresql` (для prod — `vx_prod@/veilix_prod`).
## 6. Почта (Postfix)
1. `smtp_bind_address = 169.58.76.183`, `myhostname = fr-ub2gs3.veilix.cc`: письма уходят с IP, у которого обратная запись совпадает с прямой.
2. OpenDKIM: ключи для veilix.app и veilixvpn.com (селектор, напр. `mail`).
3. DNS для каждого из двух доменов:
a. SPF: `v=spf1 ip4:169.58.76.183 include:<почтовый хостинг> -all`. Хостинг нужен, потому что ответы поддержки уходят через него (пункт 5); плюс SMTP-провайдер, если используем;
b. DKIM: TXT `mail._domainkey.<домен>` с открытым ключом;
c. DMARC: `_dmarc.<домен>` — сначала `v=DMARC1; p=none; rua=mailto:…`, после проверки — `p=quarantine`.
4. Проверить, что хостер не закрывает исходящий порт 25 и что 169.58.76.183 нет в чёрных списках.
5. Почта поддержки (Q-16): ящики `help@veilix.app` и `help@veilixvpn.com` — на внешнем почтовом хостинге, MX-записи доменов указывают на него (пользователь, R-18). На этапе 3e Chatwoot забирает письма по IMAP и отвечает через SMTP хостинга. Доменов брендов в `mydestination` Postfix нет и быть не должно: тогда письма администраторам на `help@…` уходят на хостинг, а не остаются на этом сервере.
## 7. Бэкапы
1. Prod-база: каждую ночь `pg_dump -Fc veilix_prod`, хранить 14 ежедневных и 8 еженедельных, копия на другой сервер (куда — Q-14). Раз в месяц — проверочное восстановление в отдельную базу.
2. `shared/media` — синхронизация на тот же внешний сервер.
3. Dev не бэкапим: код — в git, данные dev — тестовые.
## 8. Задачи для root по этапам
| № | Этап | Задача | Статус на 22.10.2026 |
|---|---|---|---|
| R-00 | 0 | Переезд разработки: перенести `/home/vx/veilix_dev` в `/home/vx_dev/veilix_dev` (владелец vx_dev); SSH-ключ vx_dev для VSCode; Claude Code под vx_dev | сделано |
| R-01 | 0 | Репозиторий в Gitea; SSH-ключ vx_dev с правом push; для vx_prod — ключ только на чтение (deploy key) | vx_dev — сделано: `origin` — `ssh://git@gte.apr.sh:26789/andreypr207/veilix.app.git`, ключ `~/.ssh/veilix_gitea_ed25519` добавлен в Gitea как deploy key с правом записи, `main` отправлен. Ключ vx_prod — на этапе 1h |
| R-02 | 1a | PostgreSQL: роли `vx_dev`, `vx_prod`, `umami` и их базы (раздел 5) | сделано и проверено: vx_dev входит в `veilix_dev`, в `veilix_prod` — отказ |
| R-03 | 1a | pnpm: `corepack enable pnpm` | сделано |
| R-04 | 1a | Пользователи vx_dev, vx_prod, umami; `loginctl enable-linger` для них | сделано |
| R-05 | 1a | nginx и сертификаты для всех хостов (раздел 4) | сделано: опечатка исправлена, `X-Robots-Tag` на всех dev-хостах |
| R-06 | 1a | MaxMind: `geoipupdate` (ключ в `/etc/GeoIP.conf`, база в `/var/lib/GeoIP`, таймер `geoipupdate.timer`); отключить старый модуль GeoIP v1; блок `geoip2` (раздел 4, пункт 4) | сделано |
| R-07 | 3c | Postfix, OpenDKIM, DNS-записи SPF, DKIM, DMARC — или данные SMTP-провайдера | |
| R-08 | 3h | PostgreSQL для prod — если не сделано вместе с R-02 | сделано в R-02 |
| R-09 | 3h | nginx и сертификаты для prod-хостов | сделано вместе с R-05 |
| R-10 | 3f | Umami: nginx и сертификат для `umi.veilix.cc` (DNS уже есть; база — в R-02) | |
| R-11 | 3h | Бэкапы prod-базы и media на внешний сервер (Q-14) | |
| R-12 | 3d, 3e | VPS с Chatwoot и Twenty, ключи API; 2 бота поддержки в BotFather | |
| R-13 | 2a | Боты в BotFather: 2 основных и тестовые; домен для Telegram Login; адреса MiniApp | |
| R-14 | 3b, 4b | Аккаунты платёжных систем (сначала тестовые), адреса вебхуков | |
| R-15 | 3h | По желанию (Q-18): sudo-правило, разрешающее vx_dev запускать от имени vx_prod только команду выкладки | |
| R-16 | 3h | По желанию: правило nftables — к 127.0.0.1:3000 подключаются только `www-data` (nginx) и vx_prod ([10-security.md](10-security.md), раздел 7) | |
| R-17 | 1a | nginx dev: отдельный `location /_next/hmr` с `proxy_read_timeout 1h` во всех трёх server-блоках dev на 443: `dev.plc.veilix.cc`, сайты и кабинеты, `dev.tg.*`. В Next 16.4 WebSocket горячей перезагрузки — `/_next/hmr`, и сервер может молчать дольше 30 секунд, а `nginx.conf` закрывает соединение после 30 секунд тишины (`proxy_read_timeout 30`) | сделано: на всех семи dev-хостах соединение держится 70 секунд без сообщений |
| R-18 | 3c | Почта поддержки (Q-16): ящики `help@veilix.app` и `help@veilixvpn.com` на почтовом хостинге, MX-записи доменов; хостинг — в SPF (раздел 6). До этого письма администраторам и заявки видны только в админке и журнале писем | |
| R-19 | 1b | Библиотеки для headless Chromium — сверка страниц с макетами по скриншотам и Lighthouse: `libatk1.0-0t64 libatk-bridge2.0-0t64 libatspi2.0-0t64 libasound2t64 libxcomposite1 libxdamage1 libxfixes3 libxrandr2`. Сам браузер (Chrome for Testing) лежит в папке vx_dev, без root | сделано 22.10.2026: скриншоты снимаются |