215 lines
13 KiB
Markdown
215 lines
13 KiB
Markdown
# 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`:
|
||
|
||
```powershell
|
||
.\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 нет `&&`):
|
||
```powershell
|
||
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** (здесь `&&` поддерживается):
|
||
```bat
|
||
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:**
|
||
```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) Фронт (в отдельном терминале)
|
||
```bash
|
||
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)
|
||
|
||
```bash
|
||
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`.
|
||
Вручную (тот же эффект):
|
||
```bash
|
||
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 (когда будете подключать реальный вход):**
|
||
1. Создать бота у [@BotFather](https://t.me/BotFather) → получить **токен** и **username**.
|
||
2. `/setdomain` у BotFather → оба домена: `forbiddenstars.ru` (prod) и `forbidden-stars.ru` (dev/test).
|
||
3. В `.env`: `TELEGRAM_BOT_TOKEN=...`, `TELEGRAM_BOT_USERNAME=...` (без `@`).
|
||
4. Виджету нужен 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-туннель (дома белого IP нет — CGNAT):
|
||
|
||
| | домен | источник | туннель |
|
||
|---|---|---|---|
|
||
| prod | `forbiddenstars.ru` | Pi, `app:8000` | autossh, постоянно (VPS:9000) |
|
||
| dev/test | `forbidden-stars.ru` | ПК, vite:5173 / app:8080 | `ssh -R`, по требованию (VPS:9001) |
|
||
|
||
- Прод и dev/test на **разных доменах** → могут работать одновременно. Dev и test делят
|
||
слот `9001` — по очереди.
|
||
- Публикация локалки: в `.env` поставить `LOCAL_PUBLIC=vps` и запустить `run.ps1` — лаунчер
|
||
сам поднимет SSH-туннель. `LOCAL_PUBLIC=local` — только localhost.
|
||
- `COOKIE_SECURE` выводится автоматически (HTTPS-домен ⇒ Secure-cookie; dev+localhost ⇒ нет).
|
||
- Пошаговая настройка — в [`deploy/`](deploy/README.md): `vps/` (Caddy, сертификаты, туннель-юзер),
|
||
`pi/` (Docker-приложение + autossh-служба).
|
||
|
||
## Дополнения и фракции
|
||
|
||
| Дополнение | Фракции |
|
||
|---|---|
|
||
| База (всем) | Орки, Ультрамарины, Эльдары, Хаоситы |
|
||
| Forgotten Worlds | Имперская гвардия, Тау, Некроны, Тираниды |
|
||
| Forsaken Voids | Инквизиция, Сёстры битвы, Друкхари, Адептус Механикус |
|
||
|
||
Группа отмечает имеющиеся дополнения; при создании партии доступны только фракции
|
||
этих дополнений (база — всегда).
|