mirror of
https://github.com/IS1DI/x0gp.git
synced 2026-07-16 20:47:55 +00:00
feat(server): initial implementation
This commit captures the full server code accumulated across
several development sessions. It includes the following layers,
applied in roughly this order during development:
1. Base infrastructure
- cmd/poc-server HTTP+WebSocket server, /health, /stats
- internal/transport wire types (Envelope + all message types)
- internal/catalog, internal/realtime, internal/stats
- internal/storage/postgres with migrations 001-004
- .env, .env.example, docker-compose, Dockerfile, scripts/
2. Swagger documentation
- go get github.com/swaggo/http-swagger
- swag init produces docs/docs.go
- /swagger/index.html UI, /swagger/doc.json spec
- annotations on health/stats/ws and catalog handlers
3. Races API
- internal/races/{store,service,keyset,types}.go
- migration 005_races.sql: finished_races, race_plans, race_queue
- GET /api/races with keyset pagination, status filter
- GET /api/races/upcoming
- POST /api/races/queue/join, GET, DELETE
- POST/GET /api/races/plans, DELETE /{id}
- lobby.RaceMeta simplification (no host_id/host_name)
4. Races seeder
- internal/races/seed with deterministic generator
- --seed-races / --reset CLI flags in main.go
- 30 finished + 5 live + 5 plans + 4 queue entries
5. Drivers and clans
- internal/drivers, internal/clans (CRUD, validation)
- migration 008_drivers_clans.sql
- /api/clans and /api/drivers endpoints
- 3-letter uppercase nickname/tag validation
- lobby.DriverMeta: Nickname, AvatarURL, ClanID, ClanTag
6. Podium
- internal/transport.RacePodiumEntry
- lobby.SetDriverProfile for in-memory metadata sync
- migration 007_podium.sql (podium JSONB column)
- seeder populates top-3 per finished race
7. Live races persistence
- migration 009_live_persistence.sql: live_races,
live_race_drivers, lobby_drivers
- internal/races/live_store.go: LiveStore with write-side
mirror for lobby.Service mutations
- Service.collectLive and Upcoming read from Postgres
- main.go RestoreFromDB rehydrates lobby on startup
8. Unify live + finished into one races table
- migration 010_unify_races.sql: rename finished_races to
races, expand status CHECK, merge live rows
- PgStore now hosts both write paths; LiveStore is a
thin facade implementing lobby.Persistence
- seeder resetAll drops only finished/cancelled rows and
race_plans / race_queue / lobby_drivers
Each layer is consistent on its own; cross-layer changes are
visible in the file history. A future refactor may split this
commit into the per-stage boundaries listed above.
This commit is contained in:
+502
@@ -0,0 +1,502 @@
|
||||
# x0gp — Server Plan (Go)
|
||||
|
||||
Документ фиксирует серверную архитектуру: транспорт, модули, протокол,
|
||||
хранение, биллинг, деплой.
|
||||
|
||||
Дата фиксации архитектурных решений: 2026-06-18.
|
||||
Дата обновления (домашний сервер для PoC и MVP): 2026-06-18.
|
||||
Дата обновления v2 (Orange Pi 5 Pro + Coolify + статический IP): 2026-06-18.
|
||||
|
||||
Архитектурные выборы, на которых основан план:
|
||||
1. Транспорт клиент-сервер: **WebSocket + бинарный protobuf**.
|
||||
2. Управление машинками: **Wi-Fi, ESP32-S3 агент**.
|
||||
3. Модель доверия: **authoritative server + client-side prediction**.
|
||||
4. Позиционирование: **IR-магнитные секторы + overhead-CV (гибрид)**.
|
||||
|
||||
---
|
||||
|
||||
## 1. Цели и не-цели
|
||||
|
||||
### 1.1. Цели
|
||||
|
||||
- Round-trip "команда на экране -> отклик машинки" в худшем случае 200 мс,
|
||||
в типичном 80-120 мс.
|
||||
- Поддержка одновременно 50-100 активных заездов на 1 Edge-инстанс.
|
||||
- Устойчивость к обрывам связи (клиент теряет сеть на 0.5-2 с без потери
|
||||
состояния).
|
||||
- Anti-cheat: защита от подмены телеметрии, авто-команд, грязного руления.
|
||||
- Биллинг заездов и турниров (ЮKassa для РФ, Stripe для остального).
|
||||
- Горизонтальное масштабирование (stateless API, sticky-сессии на WS).
|
||||
|
||||
### 1.2. Не-цели (для MVP)
|
||||
|
||||
- Физика шин (скорость не считаем, считаем положение с CV).
|
||||
- Запись/воспроизведение VR-реплеев.
|
||||
- Полноценный matchmaking с ELO (только базовый лобби).
|
||||
- Мобильное приложение (только Web, мобилка во второй очереди).
|
||||
|
||||
---
|
||||
|
||||
## 2. Высокоуровневая архитектура
|
||||
|
||||
```
|
||||
+----------------------+
|
||||
| React (Web client) |
|
||||
| React Native (mobile)|
|
||||
+----------+-----------+
|
||||
|
|
||||
WebSocket (binary protobuf)
|
||||
|
|
||||
+----------v-----------+
|
||||
| Edge / API Gateway |
|
||||
| (Go, stateless) |
|
||||
+----+------+-----+----+
|
||||
| | |
|
||||
+-------------+ | +-------------+
|
||||
| | |
|
||||
+------v------+ +--------v-------+ +------v------+
|
||||
| Race Engine | | Realtime Hub | | REST API |
|
||||
| (stateful) | | (WS fan-out) | | (chi/echo) |
|
||||
+-------------+ +----------------+ +-------------+
|
||||
| | |
|
||||
+---------+---------+ |
|
||||
| |
|
||||
+--------v--------+ |
|
||||
| Redis | pub/sub, state |
|
||||
+-----------------+ |
|
||||
| |
|
||||
+--------v--------+ |
|
||||
| Postgres / TS |<-------------------+
|
||||
+-----------------+
|
||||
```
|
||||
|
||||
**Edge / API Gateway** — единая точка входа, TLS, rate-limit, auth, маршрутизация.
|
||||
**Race Engine** — authoritative state гонки, tick 60 Hz.
|
||||
**Realtime Hub** — WebSocket-концентратор, fan-out игрокам, сериализация.
|
||||
**REST API** — управление пользователями, лобби, платежами, история.
|
||||
|
||||
---
|
||||
|
||||
## 3. Транспорт
|
||||
|
||||
### 3.1. WebSocket (основной)
|
||||
|
||||
- Библиотека: **gorilla/websocket** или **nbio** (последняя — non-blocking,
|
||||
лучше для тысяч соединений).
|
||||
- Формат кадров: **бинарный protobuf** (для latency) + опционально JSON для
|
||||
не-real-time сообщений (чат лобби).
|
||||
- Subprotocol: `x0gp.v1`.
|
||||
- Ping/pong: каждые 5 с, читаем RTT для диагностики.
|
||||
|
||||
### 3.2. Альтернатива на будущее
|
||||
|
||||
- WebRTC DataChannel — если потребуется суб-50 мс jitter.
|
||||
- Собственный UDP+KCP — если появится конкурентная потребность.
|
||||
- В плане не реализуем, но держим "port" в протоколе для миграции.
|
||||
|
||||
### 3.3. REST API
|
||||
|
||||
- HTTP/2, chi router.
|
||||
- OpenAPI 3.1 спецификация, кодогенерация клиента.
|
||||
- Авторизация: JWT (access 15 мин) + refresh token (30 дней).
|
||||
- Idempotency-Key для платёжных эндпоинтов.
|
||||
|
||||
---
|
||||
|
||||
## 4. Структура Go-модулей
|
||||
|
||||
```
|
||||
server/
|
||||
cmd/
|
||||
api-gateway/ # REST + WS, stateless
|
||||
race-engine/ # stateful, authoritative tick
|
||||
agent-linker/ # запускается в помещении (на gateway-ПК)
|
||||
indexer/ # пишет телеметрию в Timescale
|
||||
replay-recorder/ # (опц.) пишет replay в S3
|
||||
internal/
|
||||
auth/ # JWT, сессии, refresh, MFA
|
||||
billing/ # ЮKassa/Stripe, биллинг сессий
|
||||
race/ # сессии, круги, DNF, рестарты, секторы
|
||||
realtime/ # WS hub, fan-out, per-client очереди
|
||||
transport/ # protobuf-схемы, кодеки
|
||||
control/ # приём команд, rate-limit, prediction snapshots
|
||||
prediction/ # client-side prediction серверные корректировки
|
||||
telemetry/ # агрегация, пересылка в TS
|
||||
anti_cheat/ # эвристики: sanity check, повторы, geo
|
||||
tournament/ # bracket, лэддер, регистрация
|
||||
storage/
|
||||
postgres/ # sqlx/pgx, миграции
|
||||
redis/ # сессии, pub/sub, лобби
|
||||
timescale/ # телеметрия
|
||||
obs/ # логирование, метрики, трейсы
|
||||
proto/
|
||||
client_server.proto # команды, телеметрия для игрока
|
||||
server_agent.proto # команды и состояние для машинки
|
||||
api/
|
||||
openapi.yaml
|
||||
migrations/ # SQL
|
||||
deploy/
|
||||
docker-compose.yml # dev
|
||||
helm/ # prod
|
||||
scripts/
|
||||
loadtest/ # 100 виртуальных гонщиков
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Протокол (protobuf)
|
||||
|
||||
### 5.1. Клиент -> Сервер
|
||||
|
||||
- `ClientHello { version, token, locale }`
|
||||
- `JoinRace { raceId, carClass }`
|
||||
- `LeaveRace {}`
|
||||
- `InputState { seq, tsMs, steering (-1..1), throttle (0..1), brake (0..1), gear (-1..0..1), buttons }` (60-120 Hz)
|
||||
- `ChatMessage { text }` (rate-limited)
|
||||
- `Ping { tsMs }`
|
||||
|
||||
### 5.2. Сервер -> Клиент
|
||||
|
||||
- `ServerHello { sessionId, raceTick, config }`
|
||||
- `RaceSnapshot { tick, tsMs, cars: [{id, x, y, heading, speed, lap, sector, lastLapMs}], leaderboard }` (30-60 Hz)
|
||||
- `InputAck { seq, appliedAtMs }`
|
||||
- `Correction { deltaSteering, deltaThrottle, reason }` (редко, при расхождении)
|
||||
- `RaceEvent { type: START|LAP|SECTOR|DNF|END, payload }`
|
||||
- `TournamentUpdate { ... }`
|
||||
- `Pong { tsMs }`
|
||||
|
||||
### 5.3. Сервер -> Бортовой агент
|
||||
|
||||
- `AgentCommand { carId, steering, throttle, brake, gear, flags, expiresAtMs }` (60-120 Hz)
|
||||
- `AgentConfig { maxSteeringRate, maxThrottleRate, rampLimits }`
|
||||
|
||||
### 5.4. Бортовой агент -> Сервер
|
||||
|
||||
- `AgentHello { carId, fwVersion, hwId, rssi }`
|
||||
- `AgentTelemetry { carId, tsMs, steeringActual, throttleActual, rpm, voltage, current, tempEsc, tempMotor, imu: {ax, ay, az, gx, gy, gz}, irSensor: {front, rear}, batteryPct }` (10-30 Hz)
|
||||
- `Heartbeat { tsMs, uptimeSec, errors }` (1 Hz)
|
||||
- `AgentLog { level, msg }`
|
||||
|
||||
### 5.5. Gateway-агент -> Edge-сервер
|
||||
|
||||
- `TrackHello { trackId, hwId, publicKey, version }`
|
||||
- `CarFrame { carId, tsMs, position: {x,y,z}, heading, velocity, confidence }` (из CV, 30-60 Hz)
|
||||
- `SectorEvent { carId, sector, tsMs, lap }`
|
||||
- `ReplayChunk { raceId, tsMs, jpegBytes }` (по запросу)
|
||||
- `LocalCommand { carId, payload }` (forwarded from server)
|
||||
|
||||
---
|
||||
|
||||
## 6. Race Engine (authoritative tick)
|
||||
|
||||
### 6.1. Tick
|
||||
|
||||
- Фиксированный шаг: **16.67 мс (60 Hz)**.
|
||||
- На каждом тике:
|
||||
1. Принять все InputState от игроков за последние N мс (батч).
|
||||
2. Применить к authoritative state машинок (с учётом ramp limits).
|
||||
3. Сформировать команды бортовым агентам.
|
||||
4. Отправить команды агенту в помещении.
|
||||
5. Через ~33 мс получить телеметрию и CV-обновления.
|
||||
6. Скорректировать state.
|
||||
7. Сформировать RaceSnapshot для клиентов.
|
||||
|
||||
### 6.2. Client-side prediction
|
||||
|
||||
- Клиент хранит последний применённый state и локально симулирует физику
|
||||
между snapshot-ами.
|
||||
- При получении Snapshot клиент сверяет, если расхождение > порога — плавная
|
||||
коррекция (lerp 100-200 мс), без "телепорта".
|
||||
- Сервер шлёт `Correction` только если расхождение > критического.
|
||||
|
||||
### 6.3. Anti-cheat эвристики
|
||||
|
||||
- Sanity check входящих команд (steering/throttle в допустимом диапазоне,
|
||||
скорость изменения не выше физического предела).
|
||||
- Сравнение CV-позиции с заявленной (на сервере) — если расходятся > N метров,
|
||||
пометка DQF.
|
||||
- Повторы replay: после заезда сверяем логгированные команды с CV-треком.
|
||||
- Device fingerprint + поведенческий профиль.
|
||||
- Rate limit: > 200 Hz InputState = бан.
|
||||
|
||||
### 6.4. Состояния гонки
|
||||
|
||||
- `LOBBY` -> `COUNTDOWN` (3..1) -> `RACING` -> `FINISHED`.
|
||||
- DNF по: таймаут без input 5 с, потеря связи с агентом > 3 с, флаг
|
||||
администратора.
|
||||
- RESTART: только на LoBBY или между раундами.
|
||||
|
||||
---
|
||||
|
||||
## 7. Realtime Hub
|
||||
|
||||
- Один WS-коннект на игрока.
|
||||
- Подписки: `race:{raceId}`, `tournament:{id}`, `user:{id}`.
|
||||
- Fan-out: при Snapshot race-движок публикует в Redis pub/sub, Hub читает
|
||||
и шлёт подписанным клиентам.
|
||||
- Backpressure: если клиент не успевает читать — дропаем старые Snapshot,
|
||||
шлём только последний.
|
||||
- Sticky-сессии: cookie `x0gp_sticky` при балансировке на L7.
|
||||
|
||||
---
|
||||
|
||||
## 8. Хранение
|
||||
|
||||
### 8.1. PostgreSQL (relational)
|
||||
|
||||
Таблицы (основные):
|
||||
- `users` (id, email, phone, password_hash, created_at, locale)
|
||||
- `sessions` (id, user_id, refresh_hash, ip, ua, expires_at)
|
||||
- `wallets` (user_id, balance, currency)
|
||||
- `payments` (id, user_id, provider, ext_id, amount, status, created_at)
|
||||
- `races` (id, track_id, class, started_at, ended_at, winner_id, replay_url)
|
||||
- `race_results` (race_id, user_id, car_id, lap_times[], best_lap, dnf_reason)
|
||||
- `tournaments` (id, name, format, entry_fee, prize_pool, status)
|
||||
- `tournament_entries` (tournament_id, user_id, seed, eliminated_at)
|
||||
- `cars` (id, hw_id, model, fw_version, status, last_seen)
|
||||
- `tracks` (id, name, layout_id, geo_city, status)
|
||||
|
||||
Миграции: `golang-migrate/migrate`.
|
||||
|
||||
### 8.2. Redis
|
||||
|
||||
- `session:{token}` -> userId (TTL 15 мин)
|
||||
- `refresh:{token}` -> userId (TTL 30 дн)
|
||||
- `race:{id}:state` -> serialized race state (TTL 1 час)
|
||||
- `lobby:{id}` -> set of userIds
|
||||
- `ratelimit:{user}:{op}` -> token bucket
|
||||
- pub/sub channel `race_events`
|
||||
|
||||
### 8.3. TimescaleDB (time-series)
|
||||
|
||||
- hypertable `telemetry` (ts, race_id, car_id, field, value)
|
||||
- 7 дней горячих данных, потом cold в S3.
|
||||
- continuous aggregates для "лучший круг на треке", "топ-скорости".
|
||||
|
||||
---
|
||||
|
||||
## 9. Биллинг
|
||||
|
||||
### 9.1. Провайдеры
|
||||
|
||||
- **ЮKassa** — для РФ.
|
||||
- **Stripe** — для остального мира (фаза 3).
|
||||
|
||||
### 9.2. Сценарии
|
||||
|
||||
- Пополнение кошелька (one-time payment).
|
||||
- Списание за заезд (раз в N минут или за сессию).
|
||||
- Входной взнос в турнир.
|
||||
- Призовые начисления победителям.
|
||||
|
||||
### 9.3. Идемпотентность и реконсиляция
|
||||
|
||||
- Каждый платёж имеет `Idempotency-Key` на стороне клиента и `ext_id` от
|
||||
провайдера.
|
||||
- Webhook-эндпоинт `/webhooks/yookassa` с проверкой подписи.
|
||||
- Ежедневный job сверяет балансы с провайдером.
|
||||
|
||||
---
|
||||
|
||||
## 10. Авторизация
|
||||
|
||||
- Email + password (argon2id) — базовый сценарий.
|
||||
- OAuth Yandex / VK / Google — для удобства.
|
||||
- MFA TOTP — обязательно для админов и pro-игроков.
|
||||
- JWT: HS256 в PoC, RS256 в проде.
|
||||
- Refresh-token rotation, device-binding.
|
||||
|
||||
---
|
||||
|
||||
## 11. Anti-cheat
|
||||
|
||||
Уровни:
|
||||
1. **Sanity** — диапазоны, скорости, ramp limits (всегда).
|
||||
2. **CV-check** — расхождение с overhead-камерой > 1 м -> пометка.
|
||||
3. **Replay verify** — после заезда можно пересчитать (асинхронно).
|
||||
4. **Behavioral** — паттерны (слишком "идеальные" круги = подозрительно).
|
||||
5. **Manual review** — оператор смотрит replay при споре.
|
||||
|
||||
---
|
||||
|
||||
## 12. Observability
|
||||
|
||||
- Логи: `slog` (stdlib) в JSON, отправка в Loki.
|
||||
- Метрики: `prometheus/client_golang`, экспорт `/metrics`.
|
||||
- Трейсы: OpenTelemetry, OTLP в Jaeger/Tempo.
|
||||
- Ключевые метрики:
|
||||
- `ws_connections_total`
|
||||
- `rtt_ms_p50/p95/p99` (по `(clientSendTs - serverRecvTs)`)
|
||||
- `tick_lag_ms` (если тик опаздывает > 5 мс — алерт).
|
||||
- `input_rate_per_client`
|
||||
- `dropped_snapshots_per_client`
|
||||
- `gateway_online{trackId}` (heartbeat агенты в помещении).
|
||||
|
||||
---
|
||||
|
||||
## 13. Деплой и инфраструктура
|
||||
|
||||
**Базовое решение: PoC и MVP крутятся на Orange Pi 5 Pro через Coolify.**
|
||||
Перенос на облако/VPS — когда появится коммерческая нагрузка и SLA обязательства.
|
||||
|
||||
### 13.1. Orange Pi 5 Pro (PoC и MVP)
|
||||
|
||||
**Платформа: Orange Pi 5 Pro** (Rockchip RK3588S, ARM64). Уже есть в наличии.
|
||||
|
||||
**Характеристики:**
|
||||
- CPU: 8 cores (4× Cortex-A76 @ 2.4 GHz + 4× Cortex-A55 @ 1.8 GHz)
|
||||
- RAM: 4 / 8 / 16 / 32 GB LPDDR4x (зависит от версии)
|
||||
- GPU: Mali-G610 MP4
|
||||
- **NPU: 6 TOPS** (RKNN) — для CV-инференса (YOLO/ByteTrack) **на борту**
|
||||
- Сеть: 2× 2.5 GbE (один WAN, один LAN с трассой — **физическая изоляция**)
|
||||
- Wi-Fi 6 (резерв)
|
||||
- USB 3.0 × 2, PCIe 2.1 x1, SATA
|
||||
- ОС: Ubuntu (уже стоит, с **Coolify**)
|
||||
|
||||
**Coolify как платформа деплоя:**
|
||||
- Self-hosted PaaS, уже развёрнут.
|
||||
- UI для управления сервисами, доменами, SSL, env-vars.
|
||||
- Push-to-deploy из Git-репо (GitHub/Gitea).
|
||||
- Сервисы = Docker Compose под капотом, но с UI и без боли.
|
||||
- Встроенные: PostgreSQL, Redis, MinIO (S3-совместимое), Plausible, Uptime Kuma.
|
||||
- Версионирование деплоев, rollback в 1 клик.
|
||||
|
||||
**Доступ из интернета (статический IP уже есть):**
|
||||
- **Основной вариант**: Caddy reverse proxy + Let's Encrypt напрямую.
|
||||
Coolify умеет провизить LE-сертификаты из коробки.
|
||||
- Подходит, т.к. провайдер даёт статический IP, 443/tcp открыт на роутере
|
||||
через port-forward на Orange Pi.
|
||||
- Cloudflare Tunnel не нужен (нет смысла добавлять лишний hop).
|
||||
- WAF: Crowdsec через Coolify (опц., можно отложить).
|
||||
|
||||
**Архитектура:**
|
||||
```
|
||||
+---------------- ORANGE PI 5 PRO (Ubuntu + Coolify) ----------------+
|
||||
| |
|
||||
| Coolify (docker compose orchestration) |
|
||||
| +-- api-gateway ------------------------------------+ |
|
||||
| +-- race-engine ----------------------------------+ |
|
||||
| +-- postgres+timescale (через Coolify UI) | |
|
||||
| +-- redis -----------------------------------+ |
|
||||
| +-- caddy (LE) -----------------------------------+ |
|
||||
| +-- prometheus+grafana -----------------------------------+ |
|
||||
| +-- minio (S3 replays) -----------------------------------+ |
|
||||
| |
|
||||
| Natively (systemd) - требуют прямой доступ к железу: |
|
||||
| +-- agent-linker (Wi-Fi к машинкам) |
|
||||
| +-- cv-tracker (USB камеры, NPU RKNN) |
|
||||
| +-- node_exporter (мониторинг) |
|
||||
| |
|
||||
+---------------------------------------------------------------------+
|
||||
2.5 GbE #1 (WAN, к роутеру) 2.5 GbE #2 (LAN к трассе)
|
||||
| |
|
||||
vv
|
||||
+--------------+ +---------------------+
|
||||
| Internet | | Switch (или точка- |
|
||||
| (clients) | | точка) |
|
||||
+--------------+ +----------+----------+
|
||||
|
|
||||
+------v------+
|
||||
| x0gp-track |
|
||||
| AP + камеры |
|
||||
+-------------+
|
||||
```
|
||||
|
||||
**Изоляция сети трассы:**
|
||||
- LAN-порт Orange Pi → выделенный коммутатор (или напрямую к точке доступа
|
||||
x0gp-track).
|
||||
- В этой сети только машинки и камеры — никаких домашних устройств.
|
||||
- AP настраивается на изолированный SSID / VLAN.
|
||||
- Это даёт предсказуемый latency (нет соседского трафика) и безопасность.
|
||||
|
||||
**Использование NPU RK3588S:**
|
||||
- RKNN Toolkit 2 для конвертации моделей (PyTorch → ONNX → RKNN).
|
||||
- Поддерживаются YOLOv8/v9, ByteTrack через onnxruntime-rknn.
|
||||
- Целевой инференс: 60-120 FPS на одной камере 1080p (с запасом).
|
||||
- Если NPU не подходит — fallback на CPU ARM A76.
|
||||
- Альтернативный inference-сервер: Ultralytics + onnxruntime-rknn.
|
||||
|
||||
**ARM64-специфика:**
|
||||
- Docker-образы должны быть `linux/arm64` или `multi-arch`.
|
||||
- Postgres+TimescaleDB, Redis, Caddy, Prometheus, Grafana — есть для arm64.
|
||||
- Go-сервисы — `GOARCH=arm64` при сборке (по умолчанию если собирать на
|
||||
Orange Pi).
|
||||
- CV-инференс на Python — pin версии onnxruntime-rknn под aarch64.
|
||||
|
||||
**Бэкапы:**
|
||||
- Ежедневный `pg_dump` (cron в Coolify или отдельный scheduled task).
|
||||
- Backup в MinIO (S3-совместимое) внутри Coolify.
|
||||
- Опционально off-site: rclone → Backblaze B2 / Yandex Object Storage.
|
||||
- Retention: 14 дней локально, 30 дней off-site.
|
||||
|
||||
**Безопасность:**
|
||||
- SSH: только ключи, fail2ban.
|
||||
- Coolify UI: за reverse proxy с basic auth или Cloudflare Access.
|
||||
- Порты наружу: 80/tcp + 443/tcp (для Caddy/LE).
|
||||
- Postgres и Redis слушают только 127.0.0.1 внутри Coolify.
|
||||
- UPS для Orange Pi (microSD карты не любят внезапные отключения).
|
||||
|
||||
### 13.2. Production (после MVP, когда появится коммерческая нагрузка)
|
||||
|
||||
- VPS в Москве и СПб (2 региона) — Selectel / Yandex Cloud / Timeweb.
|
||||
- Managed Postgres + Redis (для снятия с Orange Pi задач бэкапа и HA).
|
||||
- Kubernetes (Yandex Cloud / Selectel / AWS) — HPA по метрикам
|
||||
ws_connections, tick_lag.
|
||||
- Redis Cluster, Postgres с read-replica.
|
||||
- CDN для статики (CloudFront / Cloudflare).
|
||||
- WAF (CrowdSec).
|
||||
- Orange Pi остаётся как **gateway-агент + CV-инференс** в помещении с
|
||||
трассой (NPU продолжаем использовать для позиционирования).
|
||||
- Edge-сервер (Go) переезжает на VPS, к которому подключаются несколько
|
||||
Orange Pi из разных помещений.
|
||||
|
||||
---
|
||||
|
||||
## 14. Производительность и SLO
|
||||
|
||||
| Метрика | Цель |
|
||||
|---|---|
|
||||
| API p99 latency | < 100 мс |
|
||||
| WS input->ack | < 30 мс на Edge |
|
||||
| Tick stability | 60 Hz +/- 2 мс |
|
||||
| Один race-engine | 100 активных гонок |
|
||||
| Один api-gateway | 5000 WS-клиентов |
|
||||
| Доступность | 99.5% (PoC), 99.9% (prod) |
|
||||
|
||||
---
|
||||
|
||||
## 15. Roadmap (server)
|
||||
|
||||
| Фаза | Срок | Что делаем |
|
||||
|---|---|---|
|
||||
| **S0: PoC моно-сервис** | 1-2 нед | Один Go-бинарь: WS + REST + race state in-memory + локальный agent-linker. SQLite. Без биллинга. |
|
||||
| **S1: MVP модульный** | 4-6 нед | Разделение на api-gateway / race-engine / agent-linker. Postgres + Redis. Auth, JWT, лобби. |
|
||||
| **S2: CV + телеметрия** | 8-10 нед | Интеграция с overhead-CV. TimescaleDB. Replay recorder (S3-совместимое). |
|
||||
| **S3: Биллинг** | 10-12 нед | ЮKassa, кошелёк, сессии под оплатой. Webhooks. Админка. |
|
||||
| **S4: Турниры** | 14-18 нед | Bracket, лэддер, призовые. Анти-чит уровня 2-3. |
|
||||
| **S5: Продакшн** | 4-6 мес | K8s, мульти-регион, SLO, on-call. Stripe для остального мира. Мобильное приложение. |
|
||||
|
||||
---
|
||||
|
||||
## 16. Открытые вопросы
|
||||
|
||||
- **Стек frontend**: React + Vite, какие библиотеки для WS (reconnect,
|
||||
backoff)? Кандидаты: `reconnecting-websocket`, `socket.io-client` (overkill,
|
||||
но быстро), свой тонкий клиент на `WebSocket` API.
|
||||
- **Cloudflare Tunnel vs WireGuard vs статический IP** — что выберем для
|
||||
доступа к домашнему серверу? Дефолт: Cloudflare Tunnel (проще, есть
|
||||
free-tier, базовый WAF). Уточним после тестов latency.
|
||||
- **Бэкапы Postgres на NAS** — есть ли дома NAS или использовать внешнее
|
||||
S3-совместимое (Backblaze B2 / Yandex Object Storage) для off-site бэкапов?
|
||||
- **CDN/streaming для replay** — Mux / Cloudflare Stream / своё? Out of scope
|
||||
для MVP, но в плане держим "порт".
|
||||
- **Интеграция со стриминговыми платформами (Twitch)** — out of scope для
|
||||
MVP, но в плане держим "порт".
|
||||
- **Язык CV-сервиса**: Go (gocv) или Python (быстрее итерация, ONNX-рантайм)?
|
||||
Дефолт: Python на PoC для скорости итераций, Go для prod-инференса
|
||||
(меньше ресурсов, проще деплой).
|
||||
- **Конкретные характеристики домашнего сервера** (CPU/RAM/диск/ОС) — нужно
|
||||
для оценки, сколько одновременных гонок он потянет. В PLAN.md пока заложен
|
||||
разумный минимум (4 CPU / 16 GB / 500 GB SSD).
|
||||
- **ЮKassa-аккаунт** — есть ли уже ИП/ООО для приёма платежей, или
|
||||
регистрировать при переходе на биллинг?
|
||||
Reference in New Issue
Block a user