Files
ForbiddenStarsApp/deploy
NotBigGhostandClaude Opus 5 63fd90e017 Документация: сверка README и deploy/*.md с кодом
- README: test без порта на хосте (только через домен, Secure-cookie), прод из реестра
  (build-push + docker compose up -d), лимиты перебора и регистраций, dev_admin.py в списке
  dev-кода, структура репозитория, раздел о бэкапах, отличия test от prod; отмечены
  известные проблемы (#69, #71, #72, #73).
- deploy/README.md: три сервиса (app + tunnel + backup), источники ключа туннеля для Pi,
  test и dev-туннеля, слот 9000 у временного прода.
- deploy/pi/README.md: контейнер backup, fail-fast по секретам, ADMIN_PASSWORD только при
  первом создании админа (#73), порядок обновления.
- deploy/vps/README.md: туннель-контейнер вместо autossh, сниппет (edge), единые имена
  файлов в примере сборки сертификатов, дописывать authorized_keys через >>.
- deploy/backup/README.md: первый бэкап на новом Pi, выбор снимка с данными при
  восстановлении (#74), метка keep, --no-pre-restore, служебные команды, причины unhealthy.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LqSoRj99iwVEH5U5fnZgsd
2026-09-14 20:06:54 +03:00
..

Публикация: домены, VPS, туннели

Приложение крутится дома (Pi — прод) и на твоём ПК (dev/test). Дома белого IP нет (CGNAT), поэтому наружу выставляем через VPS-привратник: на нём Caddy терминирует HTTPS твоими сертификатами и проксирует трафик в SSH reverse-туннели, которые приложение само открывает к VPS.

                       ┌──────────── VPS 186.246.51.17 (Caddy) ───────────┐
 forbiddenstars.ru ───►│ :443 (cert твой) → 127.0.0.1:9000               │
                       │                        ▲ туннель-КОНТЕЙНЕР        │
                       │                        └── Pi : app:8000   PROD   │
 forbidden-stars.ru ──►│ :443 (cert твой) → 127.0.0.1:9001               │
                       │                        ▲ контейнер (test) ИЛИ     │
                       │                        ▲ ssh с ПК (dev)           │
                       │                        ├── ПК test : app:8000     │
                       │                        └── ПК dev  : vite:5173     │
                       └───────────────────────────────────────────────────┘
  • PROD — Pi. docker compose up -d поднимает три сервиса: app + tunnel + backup (образы из Gitea-реестра, собираются на ПК scripts/build-push.ps1). У app портов на хост нет — наружу его выставляет только туннель-контейнер (ssh -R 9000:app:8000 к VPS). Работает постоянно: при обрыве ssh завершается, и Docker перезапускает контейнер (restart: unless-stopped). См. pi/.
  • TEST — ПК. То же самое: docker compose -f docker-compose.test.yml up собирает образы локально и поднимает app + tunnel (ssh -R 9001:app:8000) + backup (без расписания и без VPS). Портов на хост нет — тест виден только на forbidden-stars.ru. Обычно запускается лаунчером при APP_ENV=test.
  • DEV — ПК, нативно (uvicorn+vite). По умолчанию только на localhost; при LOCAL_PUBLIC=vps лаунчер (run.ps1 / run.sh) дополнительно поднимает SSH-туннель с ПК (ssh -R 9001:localhost:5173) → дев виден на forbidden-stars.ru.
  • DEV и TEST делят слот 9001 (forbidden-stars.ru) → поднимай что-то одно за раз. PROD на отдельном слоте 9000 (forbiddenstars.ru) — работает независимо. Временный прод на ПК (docker-compose.temp.yml) тоже занимает 9000 — одновременно с Pi нельзя.

Ключи туннеля (приватные, в git не идут; публичные части — в authorized_keys пользователя tunnel на VPS):

  • Pi — TUNNEL_KEY_B64 (base64 приватного ключа) в .env; файла ключа на Pi нет.
  • ПК, test и временный прод — файл deploy/tunnel/id_tunnel, монтируется в туннель-контейнер.
  • ПК, dev — run.ps1/run.sh зовут системный ssh без -i, то есть с ключом по умолчанию из ~/.ssh. Он должен быть в authorized_keys (можно тем же, что id_tunnel).

Настройка по шагам:

  1. VPS — vps/README.md: Caddy, файрвол, пользователь tunnel, сертификаты, Caddyfile.
  2. Pi (прод) — pi/README.md: ключ туннеля в TUNNEL_KEY_B64, .env, docker compose up -d.
  3. ПК (dev/test) — ключ в deploy/tunnel/id_tunnel (для test-контейнера) и/или ключ по умолчанию в ~/.ssh (для dev-туннеля); pubkey — в authorized_keys у tunnel@VPS.
  4. Бэкапы — backup/README.md: контейнер backup (restic) делает снимки на Pi и на VPS (fsbackup@VPS, только SFTP), скрипты ПК скачивают их и проверяют восстановление на тест-клоне.

Секреты не в git: сертификаты/ключи (*.pem, *.key, id_tunnel*, id_backup*) живут на VPS/Pi/ПК, в репозитории только Caddyfile, страница-заглушка, образы deploy/tunnel/ и deploy/backup/ и шаблоны.

Telegram-вход требует HTTPS-домен: у BotFather /setdomain укажи оба домена (forbiddenstars.ru и forbidden-stars.ru).

Реал-тайм (SSE)

Приложение шлёт события «данные изменились» через SSE: GET /api/events (text/event-stream). Это обычный HTTP — проходит через Caddy и SSH-туннель без доп. настроек, КРОМЕ одного: для пути /api/events в Caddyfile выделен отдельный handle без encode и с flush_interval -1 (иначе сжатие/буферизация задержат поток). После правки Caddyfile на VPS: sudo systemctl reload caddy (или restart).

Шина событий — in-memory, рассчитана на один процесс (uvicorn --workers 1, как в контейнере). Если когда-нибудь поднимешь несколько воркеров/реплик — шину нужно вынести во внешний брокер (Redis pub/sub), иначе события увидит только тот воркер, что принял мутацию.

SSE-поток сам не закрывается, поэтому uvicorn запускается с --timeout-graceful-shutdown (10 с в entrypoint.sh, 2 с в run.*). Без него остановка ждёт закрытия всех соединений: в dev --reload при открытой вкладке висит вечно, в контейнере остановку обрывает только SIGKILL по stop_grace_period.