Files
split-trainer/docker-compose.yml
darko fcafe1ae15 feat: аккаунты по почте с подтверждением, сбросом пароля и синхронизацией прогресса
API аккаунтов на чистой стандартной библиотеке Python (server/splitapi):
- регистрация по почте: пароль задаётся по ссылке из письма, аккаунт создаётся только после подтверждения
- вход, выход, сброс пароля по почте (токены одноразовые, в базе только SHA-256), удаление аккаунта
- пароли scrypt, лимиты попыток по паре «адрес + IP», IP и аккаунту, защита от CSRF
- хранение прогресса пользователя с оптимистичной блокировкой версии
- SMTP через переменные окружения; без SMTP на публичном адресе API не стартует

Клиент: public/auth.js (вход, регистрация, сброс, синхронизация), public/sync.js
(чистое слияние прогресса). Без API сайт работает как раньше.

Развёртывание: Dockerfile.api, сервис splitapi в docker-compose, проксирование /api/
в nginx с ограничением частоты, deploy/install-api-debian.sh (systemd), .env.example.

Тесты: 216 для бэкенда (unittest, покрытие около 97%), 16 для клиентского слияния.
Документация: раздел «Аккаунты и почта» в docs/DEPLOY.md, раздел 9 в docs/ИНСТРУКЦИЯ.md.
2026-10-16 11:25:09 +03:00

69 lines
2.6 KiB
YAML

# Запуск: docker compose up -d --build
# Сайт по умолчанию открыт только на localhost: он предназначен для работы за обратным прокси.
# BIND=0.0.0.0 откроет его наружу, PORT=9000 сменит порт. Все переменные можно положить в файл .env рядом (образец: .env.example).
services:
split-trainer:
build: .
image: split-trainer:latest
container_name: split-trainer
restart: unless-stopped
ports:
- "${BIND:-127.0.0.1}:${PORT:-8080}:8080"
read_only: true
tmpfs:
- /tmp
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
mem_limit: 64m
pids_limit: 64
logging:
driver: json-file
options:
max-size: "5m"
max-file: "3"
# API аккаунтов. Порт наружу не публикуется: к нему ходит только nginx из контейнера выше по имени splitapi.
# Если API не нужен, сайт работает и без него (просто без входа): уберите этот сервис.
splitapi:
build:
context: .
dockerfile: Dockerfile.api
image: split-trainer-api:latest
container_name: splitapi
restart: unless-stopped
environment:
# Должен совпадать с адресом, по которому открывают сайт (схема, домен, порт): из него строятся ссылки в письмах и проверяется Origin
PUBLIC_URL: ${PUBLIC_URL:-http://127.0.0.1:${PORT:-8080}}
# Верим X-Real-IP, потому что порт API не публикуется и до него доходит только nginx. Не публикуйте порт 8000.
TRUST_PROXY: "1"
MAIL_BACKEND: ${MAIL_BACKEND:-}
MAIL_FROM: ${MAIL_FROM:-}
SMTP_HOST: ${SMTP_HOST:-}
SMTP_PORT: ${SMTP_PORT:-}
SMTP_SECURITY: ${SMTP_SECURITY:-}
SMTP_USER: ${SMTP_USER:-}
SMTP_PASSWORD: ${SMTP_PASSWORD:-}
ALLOW_REGISTRATION: ${ALLOW_REGISTRATION:-true}
SESSION_DAYS: ${SESSION_DAYS:-}
volumes:
- splitapi-data:/data
read_only: true
tmpfs:
- /tmp
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
mem_limit: 384m # scrypt занимает десятки МБ на каждый одновременный вход
pids_limit: 64
logging:
driver: json-file
options:
max-size: "5m"
max-file: "3"
volumes:
splitapi-data: