На главной партии приходят из разных групп, поэтому название нужно; на странице самой группы оно повторяет заголовок страницы. Блок получил проп showGroupName (по умолчанию true) — главная не меняется. #2 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ps52xzuUXnWrJ5SRnEeaLk
Forbidden Stars — учёт партий
Мобильное веб-приложение для учёта партий настольной игры Forbidden Stars: профили игроков (вход через Telegram, пока — dev-заглушка), группы, создание партий с рандомом фракций и фильтром по дополнениям группы, статистика и общий топ, админ-панель.
- Бэкенд / ядро + API: Python · FastAPI · SQLModel · SQLite
- Фронтенд: React · Vite · TypeScript (SPA, общается с ядром только по REST API)
- Хостинг: Raspberry Pi 4 (ARM64) в Docker
Структура
backend/ FastAPI: ядро, REST API, БД, миграции, seed
frontend/ React + Vite SPA
Dockerfile multi-stage сборка (фронт собирается node, отдаётся FastAPI)
docker-compose.yml
.env.example
Локальная разработка
Единый лаунчер (run.ps1 / run.sh)
После разовой настройки (ниже) dev и test запускаются одной командой — что именно,
решает APP_ENV в корневом .env:
.\run.ps1 # Windows (Linux / macOS / Git Bash: ./run.sh)
APP_ENV в .env |
что делает лаунчер |
|---|---|
development |
uvicorn --reload (бэк) + vite (фронт) нативно, в двух окнах |
test |
docker compose прод-клон на :8080 (со сборкой образа) |
production |
не запускает — прод деплоится отдельно (см. «Git и деплой») |
Разовая настройка перед первым запуском — поднять venv бэка и зависимости фронта
(после неё повседневный цикл — просто .\run.ps1). Vite проксирует /api на бэкенд.
1) Бэкенд
Windows PowerShell (команды по одной — в PowerShell 5.1 нет &&):
cd backend
python -m venv .venv
.\.venv\Scripts\Activate.ps1 # если ругается на политику: Set-ExecutionPolicy -Scope CurrentUser RemoteSigned
pip install -e ".[dev]" # .[dev] — один аргумент (пакет + dev-зависимости)
Copy-Item ..\.env.example ..\.env # ЕДИНЫЙ .env лежит в КОРНЕ репозитория
alembic upgrade head # применит миграции и сидинг
python -m app.bootstrap # создаст/синхронизирует администратора из .env
uvicorn app.main:app --reload # http://localhost:8000 (Swagger: /api/docs)
Windows cmd.exe (здесь && поддерживается):
cd backend
python -m venv .venv
.venv\Scripts\activate.bat
pip install -e ".[dev]"
copy ..\.env.example ..\.env
alembic upgrade head
python -m app.bootstrap
uvicorn app.main:app --reload
Linux / macOS / Git Bash:
cd backend
python -m venv .venv && . .venv/Scripts/activate # на *nix: . .venv/bin/activate
pip install -e ".[dev]"
cp ../.env.example ../.env
alembic upgrade head
python -m app.bootstrap
uvicorn app.main:app --reload
Единый
.env— в корне репозитория (ForbidenStarsApp/.env), рядом с.env.example. Его читают и бэкенд (через абсолютный путь, независимо от рабочей папки), иdocker compose. Файл — локальный, на каждой машине свой (dev/prod различаются строкойAPP_ENV).
2) Фронт (в отдельном терминале)
cd frontend
npm install
npm run gen:api # сгенерирует типы из живого OpenAPI (бэкенд должен быть запущен)
npm run dev # http://127.0.0.1:5173 или http://localhost:5173 (оба стека)
Вход в dev-режиме — экран /login: в деве доступны оба метода (Telegram + вход по нику),
в проде — только Telegram (см. раздел «Аутентификация»).
Production (Docker на Pi)
cp .env.example .env # заполните SECRET_KEY, ADMIN_PASSWORD и пр.
docker compose build # на ARM64 собирается нативно
docker compose up -d # приложение на :8000, БД на томе
FastAPI отдаёт собранный SPA и API с одного origin. Миграции и бутстрап админа
выполняются автоматически при старте (entrypoint.sh).
Test — локальный прод-клон в контейнере
Тот же образ и поведение, что и прод (FastAPI отдаёт SPA, БД на томе, вход игроков только через Telegram), но на своей машине — для проверки прод-сборки до выката на Pi. Изолированные тома и порт 8080 (не конфликтует с dev-uvicorn на :8000).
Проще всего — через лаунчер: поставить APP_ENV=test в .env и запустить .\run.ps1.
Вручную (тот же эффект):
docker compose -f docker-compose.test.yml up -d --build
# открыть http://localhost:8080 (Swagger: /api/docs)
docker compose -f docker-compose.test.yml down -v # остановить и стереть тестовые данные
- Окружение
test(прод-клон), ноCOOKIE_SECURE=false(локально по HTTP). - Читает тот же
.env, что dev/prod (отдельного.env.testбольше нет); внутри контейнераAPP_ENVфорсится вtest(см.docker-compose.test.yml). - Данные — на отдельных томах
db-data-test/uploads-data-test(не пересекаются с dev и Pi). - Вход: админ-панель (
/admin/login) работает сразу по логину/паролю; вход игроков — только через Telegram (нужен бот + публичный HTTPS/туннель наlocalhost:8080). - Не используйте
$в секретах. Единый.envчитают и pydantic (dev —$дословно), и docker compose (test/prod —$= подстановка переменной). Чтобы значение совпадало везде, вSECRET_KEY/ADMIN_PASSWORDне должно быть$. Удобно генерировать так:python -c "import secrets;print(secrets.token_urlsafe(48))"(даёт только[A-Za-z0-9_-]).
Аутентификация
Методы входа зависят от окружения (APP_ENV):
| dev | test / prod | |
|---|---|---|
| Telegram Login Widget | ✓ | ✓ (единственный) |
| Вход по нику (stub) | ✓ | ✗ (физически отсутствует) |
- Stub-вход (по нику) — только для разработки. Его код физически не попадает в прод:
файлы
backend/app/auth/dev_stub.pyиbackend/app/routers/dev_auth.pyисключены из Docker-образа (.dockerignore), роутер подключается лишь приAPP_ENV=development(app/main.py), а на фронте dev-блок вырезается из прод-сборки (import.meta.env.DEV). - Telegram: сервер проверяет подпись виджета (HMAC по
TELEGRAM_BOT_TOKEN).GET /api/auth/configотдаёт доступные методы иtelegram_bot_usernameдля виджета.
Настройка Telegram (когда будете подключать реальный вход):
- Создать бота у @BotFather → получить токен и username.
/setdomainу BotFather → оба домена:forbiddenstars.ru(prod) иforbidden-stars.ru(dev/test).- В
.env:TELEGRAM_BOT_TOKEN=...,TELEGRAM_BOT_USERNAME=...(без@). - Виджету нужен HTTPS-домен (см. «Домен и публикация») — по голому HTTP/localhost он не работает.
Админ-вход (секретная панель, логин+пароль) — отдельный механизм, доступен во всех окружениях.
Окружения (dev / test / prod)
Один и тот же код; контур задаёт APP_ENV в едином .env (его читает лаунчер):
| dev | test (прод-клон локально) | prod (Pi) | |
|---|---|---|---|
| Запуск | .\run.ps1 → uvicorn --reload + vite |
.\run.ps1 → docker compose -f docker-compose.test.yml |
docker compose up -d |
APP_ENV |
development |
test (форсится в compose) |
production (форсится в compose) |
| Env-файл | единый .env |
единый .env |
единый .env (на Pi) |
| Раздача SPA | Vite (HMR), :5173 | FastAPI, :8080 | FastAPI, :8000 |
| База данных | backend/data/dev/… |
том db-data-test (/data) |
том db-data (/data) |
| Вход игроков | Telegram + ник (stub) | только Telegram | только Telegram |
- Один
.envна машину в корне (рядом с.env.example).APP_ENVв нём решает, что запустит лаунчер (development/test); прод-контейнер это значение игнорирует и всегдаproduction. Отдельного.env.testбольше нет. - Структура БД одна (общие миграции Alembic), файлы разные: dev →
DEV_DATABASE_URL(backend/data/dev/), test и prod →PROD_DATABASE_URL(том/data; у test и prod это РАЗНЫЕ тома). Данные дева в образ не попадают (data/в.dockerignore). - В Docker идёт только прод-код: dev-вход (stub) и тесты физически исключены из образа
(
.dockerignore);test— тот же образ, что и прод, просто локально и сAPP_ENV=test.
Git и деплой
- Ветка
dev— рабочая: весь код, лаунчер, тесты. Повседневная разработка иtestздесь. - Ветка
main— релиз прода: готовое промоутишь изdevчерезmerge dev→main. Файлы во всех ветках одинаковы (окружение задаёт.env/compose, а не ветка) → merge безболезненный; чистоту прод-образа обеспечивает.dockerignore, а не разные наборы файлов. - Деплой на Pi:
git pullветкиmain→docker compose up -d --build. - Чистая выгрузка в папку без git (опц.):
scripts/export-prod.sh <dir> main(черезgit archive+export-ignore— в папку идёт ровно прод-набор, без лаунчера/тестов/dev-входа).
Секреты (.env) и данные (data/, *.db) в git не идут — см. .gitignore.
Домен и публикация
Публичные адреса отдаёт VPS-привратник (Caddy + HTTPS твоими сертификатами), а
приложение само открывает к нему SSH reverse-туннель (дома белого IP нет — CGNAT).
У test/prod туннель — отдельный контейнер в их docker-compose, и портов на хост
они не публикуют (доступны только через домен):
| домен | как выставляется | слот VPS | |
|---|---|---|---|
| prod | forbiddenstars.ru |
туннель-контейнер (постоянно) → app:8000 |
9000 |
| test | forbidden-stars.ru |
туннель-контейнер → app:8000 |
9001 |
| dev | forbidden-stars.ru |
run.ps1 при LOCAL_PUBLIC=vps → localhost:5173 |
9001 |
- Прод (9000) и dev/test (9001) на разных слотах/доменах → прод и (dev|test) работают одновременно. Dev и test делят слот 9001 → по очереди.
- Dev по умолчанию только на localhost;
LOCAL_PUBLIC=vps+run.ps1выставляет его на домен. COOKIE_SECUREвыводится автоматически (HTTPS-домен ⇒ Secure-cookie; dev+localhost ⇒ нет).- Пошаговая настройка — в
deploy/:vps/(Caddy, сертификаты, юзерtunnel),pi/(app + туннель в Docker), образ туннеля —deploy/tunnel/. Ключ —deploy/tunnel/id_tunnel.
Дополнения и фракции
| Дополнение | Фракции |
|---|---|
| База (всем) | Орки, Ультрамарины, Эльдары, Хаоситы |
| Forgotten Worlds | Имперская гвардия, Тау, Некроны, Тираниды |
| Forsaken Voids | Инквизиция, Сёстры битвы, Друкхари, Адептус Механикус |
Группа отмечает имеющиеся дополнения; при создании партии доступны только фракции этих дополнений (база — всегда).