main.py: openapi.json/docs/redoc отдаются только в dev/test (нужны для gen:api),
в production отключены. Caddyfile (edge): HSTS, X-Content-Type-Options, X-Frame-Options,
Referrer-Policy, Permissions-Policy, скрыт Server; CSP подготовлена, но выключена до
проверки на test-клоне (строгая политика ломает SPA/Telegram-виджет/SSE). #61
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XfTsytzT6TojfmprRDKiV6
После запуска dev сайт уходил в вечную загрузку без единой ошибки в логе. Открытая
вкладка держит SSE-поток /api/events, и сервер его сам не закрывает. При любой правке
.py (pull, переключение ветки, мёрж) uvicorn --reload останавливает старый процесс,
а тот в graceful shutdown ждёт закрытия всех соединений. Лимита по умолчанию нет,
поэтому ожидание длится вечно: новый процесс не стартует, слушающий сокет остаётся
у reloader'а, соединения принимаются в backlog и никем не обслуживаются. Запрос
/api/users/me висит, RequireAuth крутит спиннер, в логе только "Reloading...".
Воспроизведено тем же способом, каким запускает run.ps1 (uvicorn в отдельном окне):
при открытом SSE и тронутом .py /api/health не отвечал, хотя TCP-соединение
устанавливалось за 12 мс. Сервер ожил ровно в момент закрытия SSE.
Теперь uvicorn запускается с --timeout-graceful-shutdown: по истечении лимита он
отменяет висящие задачи запросов и доводит перезапуск до конца. В dev лимит 2 с, и
тот же сценарий отвечает 200 примерно через 4 с после правки файла. В entrypoint
лимит 10 с: в контейнере тот же механизм держал остановку до SIGKILL по
stop_grace_period (30 с), и lifespan-shutdown не выполнялся. Команды ручного запуска
в README дополнены тем же флагом.
#47
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XfTsytzT6TojfmprRDKiV6
- deploy/pi/README: пошагово от чистого Pi (Docker → SSH-ключ туннеля на самом Pi → 2 файла → up),
с объяснением назначения ключа и кодирования в base64 (TUNNEL_KEY_B64).
- .env.example/README: PowerShell-команда base64 через Resolve-Path (абсолютный путь — иначе
[IO.File]::ReadAllBytes искал относительно домашнего каталога .NET и падал).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Теперь на Pi нужны только docker-compose.yml и .env — без репозитория, сборки и файла ключа.
- docker-compose.yml: pull_policy: always (образы тянутся из реестра, сборка не запускается);
ключ туннеля больше не монтируется томом — берётся из .env (TUNNEL_KEY_B64, base64);
у VPS_TUNNEL_HOST убран :? (ломал бы buildx bake на ПК, где переменная не задана) — проверка
обязательности перенесена в tunnel.sh (рантайм).
- deploy/tunnel/tunnel.sh: приватный ключ из TUNNEL_KEY_B64 (base64) с фолбэком на смонтированный
/key/id_tunnel (для dev/test, где репозиторий на хосте).
- .env.example: TUNNEL_KEY_B64 + как сгенерировать base64 (Git Bash / PowerShell).
- deploy/pi/README: полный поток «голый Pi → 2 файла»: Docker, curl compose+.env из Gitea, up;
обновление, автозапуск, бэкап как опция.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- scripts/build-push.ps1: arm64 buildx bake + push, аналог build-push.sh для Windows-дева (ASCII).
- deploy/pi/README: указать .ps1 для Windows и почему `bash ...` в PowerShell не работает.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- docker-compose.yml: image у app и tunnel → путь реестра ${IMAGE_REGISTRY}/...:${IMAGE_TAG}
(build: оставлен фолбэком для сборки на Pi). Тома/настройки прежние.
- scripts/build-push.sh: buildx bake под linux/arm64 + push обоих образов в реестр (запуск на ПК).
- .env.example: блок ОБРАЗЫ ПРОДА (IMAGE_REGISTRY=gitea.arseniev.info/notbigghost, IMAGE_TAG=latest).
- deploy/pi/README: запуск/обновление через docker compose pull; альтернатива — сборка на Pi;
заметка про HTTPS-реестр / insecure-registries.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- docker-compose.yml: слот туннеля параметризован (${VPS_TUNNEL_PORT:-9000}), VPS_TUNNEL_HOST
стал обязательным (${...:?}), лимиты ресурсов из .env (APP_MEM_LIMIT/APP_CPUS),
stop_grace_period 30s (чек-пойнт WAL SQLite), security_opt no-new-privileges на обоих сервисах;
healthcheck/логирование/тома сохранены (имена томов не менялись — данные не осиротеют).
- .env.example: VPS_TUNNEL_PORT по умолчанию НЕ задаётся (каждый контур берёт свой слот:
dev→9001, prod→9000, test→9001); добавлены ADMIN_NICKNAME, DEV/PROD_ACHIEVEMENTS_DIR,
APP_MEM_LIMIT/APP_CPUS.
- deploy/pi/README: чек-лист .env для Pi; запуск одним `docker compose up -d --build`.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>