23 KiB
Вечерний детектив
Онлайн-детективная игра: команды проходят дело из точек, дверей, ключей и улик.
Структура workspace
Корневой репозиторий agents хранит рабочие инструкции и документацию. Код живёт
в двух отдельных репозиториях:
evening_detective_server/— Go-бэкенд;evening_detective_frontend/— Vue/TypeScript-фронтенд;email_server/— старый собственный почтовый сервер, не нужен для локального запуска.
У каждого из двух основных проектов своя история Git и свой remote. Не удаляйте
вложенные .git и не смешивайте коммиты между репозиториями.
Локальный запуск
Требования: Go, Docker Compose, Node.js 20.19+ или 22.12+, npm.
1. Зависимости сервера
cd evening_detective_server
docker compose -f docker-compose-db.yml up -d
cp .env.example .env
make run
Сервис запускает:
- gRPC на
http://localhost:8080; - REST gateway и Swagger на
http://localhost:8090.
Для локального фронтенда в evening_detective_frontend/.env укажите:
VITE_API_URL=http://localhost:8090
2. Фронтенд
В другом терминале:
cd evening_detective_frontend
npm install
cp .env.example .env
npm run dev
Фронтенд будет доступен на http://localhost:5173.
3. Локальный пользователь
cd evening_detective_server
make dev-user
По умолчанию создаётся пользователь dev@example.com с паролем dev123456.
Для изменения используйте DEV_USER_EMAIL и DEV_USER_PASSWORD.
Автономная площадка (ED-029)
venue/ — окружение для проведения игры на площадке без интернета.
Локальная разработка описана выше и запускается отдельно.
После подготовки сертификата и локального DNS (инструкция ниже) из корня workspace запустите:
make prod-build
make venue
make prod-build заранее готовит артефакты для prod/ и venue/.
Деплой не компилирует Go/Vue и не устанавливает зависимости исходного кода.
Если артефактов нет, запуск завершится с предложением выполнить make prod-build.
Первый запуск с интернетом сам создаёт настройки и случайные секреты в
venue/.env, готовит Docker-образы из готовых файлов, запускает миграции и создаёт организатора.
Каждый make venue пересобирает Docker-образ из готовых файлов и принудительно
пересоздаёт контейнеры с HTTPS. При наличии runtime-образов и Alpine запуск
работает без интернета; отсутствующие образы скачиваются при первой подготовке.
Email и пароль для входа указаны в VENUE_USER_EMAIL и VENUE_USER_PASSWORD
в venue/.env; существующий файл настроек не перезаписывается.
Аккаунт организатора при запуске синхронизируется с этими значениями.
После изменения кода обновите площадку, пока интернет доступен:
make prod-build
make venue
Сайт доступен по https://evening-detective.crabs-games.art; API и материалы
обслуживаются тем же прокси. Для подключения по LAN задайте постоянный IP ноутбука
в VENUE_BIND_IP в venue/.env и настройте локальный DNS, как описано ниже.
Приложение, база и RustFS подключены только к внутренней Docker-сети. Прокси дополнительно подключён к сети публикации порта. База и RustFS сохраняются в именованных томах. Миграции Goose выполняются до старта приложения. Для остановки без потери данных:
docker compose -f venue/docker-compose.yml -f venue/docker-compose.https.yml --env-file venue/.env down
Не используйте down -v: это удаляет данные. После изменений кода или миграций
выполните make venue, пока интернет доступен. Секреты, сборка,
сертификаты и пакеты игры игнорируются Git в venue/.
APP_MODE=venue отключает SMTP, фоновые письма участия/отзывов и внешний webhook
регистрации. Аккаунты готовятся через bootstrap; почтовая регистрация аккаунта
и сброс пароля возвращают явную ошибку без изменения данных. Команды создаёт
организатор на странице игры; участие подтверждается по существующим правилам:
при создании в последние 24 часа до игры или при первом игровом действии.
Для дополнительных операторов используйте отдельные локальные email
и пароли в .env и снова выполните make venue (старые аккаунты сохраняются).
Используйте только тестовые игры до реализации возврата результатов онлайн.
Проверки для разработчика находятся в tests/venue/ и не нужны для запуска
площадки. deploy.py проверяет сценарии запуска без Docker; smoke.py проверяет
работающее окружение и создаёт тестовый сценарий, игру, команду и материал через API:
python3 tests/venue/deploy.py
python3 tests/venue/smoke.py
make venue
python3 tests/venue/smoke.py --verify
Последняя команда проверяет ранее созданные записи и файл после перезапуска.
Тестовые данные остаются в локальной базе; повторное создание запускайте только
когда нужен новый набор. Дождитесь ответа /api/test/ping перед проверкой.
Учёт устройств (этап 12)
Venue автоматически учитывает браузеры игроков, организаторов и экранов:
POST /api/devices/heartbeat публично регистрирует подключение, а
GET /api/devices?gameID=<ID> возвращает сгруппированные устройства организатору,
администратору или разработчику. Фильтр необязателен; при его использовании
список содержит только подключения выбранной игры. Панель состояния — этап 13.
UUID браузерного профиля хранится отдельно от авторизации в localStorage; каждая вкладка и перезагрузка страницы создают новый UUID подключения. Без localStorage устройство получает временный UUID. Контекст страницы полностью заменяется при heartbeat и очищается при навигации. Организатор проверяется по JWT и правам игры, команда — по паролю и неотменённому участию, экран — по существующей сессии; игру команды и экрана определяет сервер. Credentials используются только для проверки и не сохраняются в реестре.
Heartbeat отправляется сразу и каждые 20 секунд без параллельных запросов,
а также при возвращении вкладки и восстановлении сети. Ошибки не блокируют
игру и не показывают toast; повтор выполняется по интервалу. В prod ответ
содержит enabled=false, после чего браузер прекращает heartbeat;
список устройств в prod недоступен.
Реестр хранится только в памяти backend: UUID, серверные времена,
последние IP/User-Agent и проверенные ID игры/команды/экрана. IP не участвует
в идентификации и авторизации. Связь актуальна строго менее 60 секунд;
устройство доступно, если доступно хотя бы одно его подключение в списке.
Подключения удаляются через 15 минут отсутствия при heartbeat или чтении.
Предел — 1024 подключения; новые отклоняются с ResourceExhausted, если
после очистки нет места. После перезапуска открытые браузеры регистрируются
заново. Времена устройства вычисляются по сохранённым подключениям.
«Нет связи» означает отсутствие heartbeat сайта, а не отключение от Wi-Fi. UUID обозначает браузерный профиль, а не физический телефон; очистка localStorage создаёт новое устройство. История посещений, полный URL, имена игроков, email и credentials в список не попадают.
Прежние домены и HTTPS (этап 3)
Подготовка в репозитории готова; проверка реального Cudy и Android/iPhone без WAN
ещё требуется. make venue всегда запускает HTTPS с пересборкой Docker-образа
и перезапуском контейнеров. Для первого запуска:
-
Пока интернет доступен, подготовьте артефакты через
make prod-build. Запускmake venueвыполните после подготовки сертификата и локального DNS. -
Выпустите отдельный доверенный сертификат с новым ключом для двух имён:
evening-detective.crabs-games.artиevening-detective-api.crabs-games.art. Используйте DNS-01: проверяются TXT-записи_acme-challengeв публичном DNS; публичные A/AAAA-записи production менять не нужно. Не копируйте ключ production. Например, на машине с установленным Certbot:certbot certonly --manual --preferred-challenges dns \ --cert-name evening-detective-venue \ -d evening-detective.crabs-games.art \ -d evening-detective-api.crabs-games.artСледуйте подсказкам Certbot для TXT-записей. Выпуск сертификата выполняется отдельно от приложения; Certbot не входит в runtime площадки.
-
Скопируйте полную цепочку (сертификат сервера и промежуточные сертификаты) в
venue/certificates/fullchain.pem, незашифрованный закрытый ключ — вvenue/certificates/privkey.pem. Ограничьте доступ к каталогу и ключу владельцем (режимы 700 и 600); процесс Nginx в контейнере должен иметь доступ на чтение. Каталог игнорируется Git. Перед игрой проверьте дату окончания сертификата:openssl x509 -in venue/certificates/fullchain.pem -noout -dates -
На Cudy вручную закрепите IP ноутбука в Advanced Settings → IP/MAC Binding. На TR3000 с прошивкой 2.5.27 DE в прочитанных разделах Custom DNS и DHCP нет формы статических записей. Поэтому venue запускает отдельный CoreDNS на ноутбуке. В Advanced Settings → DHCP Server задайте Preferred DNS = IP ноутбука, Alternate DNS оставьте пустым. Переподключите телефоны к Wi-Fi, чтобы они получили новые параметры DHCP. Custom DNS роутера менять не требуется. Проверьте клиентскую изоляцию и настройки Private DNS/DoH устройств: запросы двух доменов должны приходить на локальный DNS, а клиенты — достигать ноутбука. Скрипты проекта не меняют роутер и не прошивают его.
-
В
venue/.envзадайтеVENUE_BIND_IP=<постоянный-IP-ноутбука>и разрешите входящие TCP 80/443 и TCP/UDP 53 из LAN в межсетевом экране ноутбука.VENUE_BIND_IPдолжен быть конкретным IPv4-адресом ноутбука, не0.0.0.0: он используется и для публикации портов, и для DNS-ответов. Порты должны быть свободны. Для HTTPS менятьAPP_BASE_URLв.envне нужно: Compose-дополнение задаёт прежний HTTPS-домен интерфейса для QR-ссылок и материалов. -
Запустите:
make venue
Команда проверяет доверие цепочке через системное хранилище OpenSSL, оба домена, срок действия и соответствие ключа до обращения к Docker. Ошибка не переключает работающее окружение на HTTP. Docker-образ приложения пересобирается при каждом запуске из готовых артефактов, без компиляции Go/Vue. При наличии всех базовых образов интернет не нужен. Доверие на реальных телефонах проверяется отдельно.
Интерфейс открывается по https://evening-detective.crabs-games.art, API — по
https://evening-detective-api.crabs-games.art. Интерфейс использует свой /api/;
прежний API-домен также обслуживается локально. HTTP на портах 80 и прежнем
VENUE_PORT перенаправляется на HTTPS с сохранением пути и параметров.
CoreDNS публикуется только на VENUE_BIND_IP:53 по UDP/TCP. Оба домена получают
этот IPv4 локально; AAAA-запросы не получают внешние адреса. Для них интернет
не требуется. Остальные домены пересылаются на 1.1.1.1/8.8.8.8 и работают при
доступном интернете; их недоступность без WAN не мешает локальным доменам.
DNS подключён к сети публикации портов и не имеет доступа к внутренней сети
backend/БД. Образ CoreDNS скачивается при первой подготовке и используется
повторно без скачивания. При изменении IP ноутбука обновите VENUE_BIND_IP,
настройки DHCP Cudy и повторите make venue.
Проверка DNS до настройки клиентов:
python3 tests/venue/dns.py --server 192.168.10.155
После продления сертификата с доступным интернетом замените оба PEM-файла и
повторите make venue: Nginx проверяет конфигурацию и перечитывает сертификат.
Продление не выполняется автоматически. После обновления кода выполните
make prod-build, затем make venue; контейнеры всегда перезапускаются с HTTPS.
Для остановки HTTPS-окружения с сохранением данных:
docker compose -f venue/docker-compose.yml -f venue/docker-compose.https.yml \
--env-file venue/.env down
Проверка после настройки DNS и запуска (первый smoke создаёт тестовые данные):
python3 tests/venue/smoke.py
python3 tests/venue/smoke.py --verify
TLS-проверка в smoke включена; самоподписанный сертификат и обход предупреждений
не подходят для приёмки. На Android и iPhone отключите WAN и мобильные данные,
подключитесь к Wi-Fi площадки и проверьте DNS обоих доменов, отсутствие TLS-ошибок,
вход организатора, материалы и QR-ссылки тестовой игры. Повторите после перезапуска.
Проверьте запрос доступа к камере отдельно: распознавание QR в текущем интерфейсе
также требует поддержки браузером BarcodeDetector.
Этап 3 сохраняет адреса, но не переносит данные production. Production-QR может вести на игру или билет, которых в локальной базе ещё нет; перенос и возврат данных относятся к этапам 4–11. До их завершения используйте только тестовые игры.
Проверки перед изменениями
Сервер:
cd evening_detective_server
make test
Фронтенд:
cd evening_detective_frontend
npm run type-check
npm run lint
npm run lint:css
При изменении protobuf сначала обновляйте сгенерированный код существующими
командами make generate на сервере и npm run proto:gen на фронтенде.
Сгенерированные файлы руками не редактируйте.
Сборка и публикация
Из корня workspace можно собрать и опубликовать root, backend и frontend одной командой:
make publish MSG="Исправить подсчёт ходов в игре"
Команда вызывает локальные build-команды репозиториев, создаёт коммит с
указанным сообщением и выполняет git push.
Общая команда собирает бэкенд один раз и сохраняет артефакты в обе папки:
prod/ и venue/. Обе используют backend/evening_detective_server,
frontend/dist/ и migrations/; Compose называется docker-compose.yml,
образ бэкенда описан в Dockerfile.backend. Фронтенд собирается отдельно для
каждого окружения: production сохраняет текущие Vite-настройки, venue использует
API через свой прокси (VITE_API_URL=/.). Локальные Goose и SQL подготовки
аккаунта лежат в venue/backend/.
Для сборки артефактов обоих окружений используйте:
make build
make prod-build выполняет ту же общую сборку. make venue пересобирает
Docker-образ из готовых артефактов и перезапускает площадку с HTTPS.
make venue-update и make venue-https сохранены как алиасы make venue. Команды
обновляют только генерируемые артефакты в prod; конфигурация prod не меняется.
Окружение и секреты
Файлы .env предназначены только для локальных значений и не коммитятся.
Начинайте настройку с .env.example. Секреты SMTP, JWT, S3, webhook и внешних
API храните только в окружении или секрет-хранилище.
Публичный адрес приложения задаётся через APP_BASE_URL; он используется в
ссылках из писем и QR-кодах команд.
Рабочий процесс
- Перед изменением прочитайте корневой
AGENTS.mdи инструкции целевого проекта. - Делайте минимальное изменение в одном дочернем репозитории.
- Запускайте релевантные проверки.
- Коммиты и push выполняет только пользователь; агент никогда не коммитит и не отправляет изменения.
Текущие подробные инструкции:
AGENTS.md— правила workspace;evening_detective_frontend/AGENTS.md— правила фронтенда;evening_detective_server/README.md— детали бэкенда.