# evening_detective_server Шалон для Go сервисов (имя репо должно быть snake case) Методы http должны быть с префиксом /api Инициализация ```shell make generate go mod tidy docker compose -f docker-compose-db.yml up -d cp .env.example .env mkdir migrations ``` Запуск ```shell make run ``` Сборка ```shell make build-builder make build-linux ``` По умолчанию используется архитектура Docker-среды. Для другой архитектуры укажите, например, `make build-linux GOARCH=arm64` или `make build-linux GOARCH=amd64`. Миграции ```shell goose -dir ./migrations create {migration name} sql goose -dir ./migrations postgres "user=postgres password=postgres host=localhost dbname=evening_detective_server sslmode=disable" up ``` Тестирование ```shell make test ``` ## PDF с QR-кодами команд Кнопка «QR команд» на странице управления игрой скачивает `GET /api/games/{id}/teams/qr.pdf` с авторизацией организатора, автора или администратора. Без команд кнопка скрыта; прямой запрос для пустой игры возвращает `FailedPrecondition`. Модуль `internal/modules/team_qr_pdf` формирует A4 в книжной ориентации, 12 карточек (3 × 4) с названиями команд и пунктирными линиями для разрезания. Имя файла: `YYYY-MM-DD_Название игры.pdf`, дата берётся из `games.start_at`. Недопустимые символы имени файла заменяются на `_`. Для рабочих ссылок задайте `APP_BASE_URL` — тот же публичный адрес приложения, который используется существующими QR-кодами фронтенда (`VITE_API_URL`). Пустой или некорректный адрес приводит к ошибке скачивания. PDF не сохраняется в хранилище и отдаётся с `Cache-Control: no-store`. Шрифт DejaVu Sans Condensed встроен в бинарник вместе с лицензией; системные шрифты не нужны. Поддерживается кириллица, длинные названия переносятся и уменьшаются до размера 6 pt. Символы вне BMP (например, emoji) вызывают явную ошибку генерации из-за ограничения PDF-библиотеки. ## Закрывашка сценария Автор (или администратор) сохраняет закрывашку через `PUT /api/scenarios/{id}/conclusion`: ```json {"conclusion":{"text":"Разгадка дела","video":"ending_abc.mp4"}} ``` Видео сначала загружается через `POST /api/files/upload` (MP4/WebM, до 64 МБ); в `video` передаётся возвращённое имя файла. Текст и видео можно задавать отдельно. `{"conclusion":null}` удаляет закрывашку. Она возвращается в `story.conclusion` полного сценария, но не передаётся в историю игроков, включая MCP. Закрывашка переносится вместе с ZIP-архивом; действуют общие лимиты импорта архива (32 МБ на архив и отдельный файл). ## Документация и юридические документы - [`docs/TERMS.md`](docs/TERMS.md) — Пользовательское соглашение (оферта); раздаётся сервером по постоянному URL `/api/terms`; - [`docs/PRIVACY.md`](docs/PRIVACY.md) — Политика конфиденциальности и обработки персональных данных (152-ФЗ); раздаётся сервером по постоянному URL `/api/privacy`; - [`docs/CONSENT.md`](docs/CONSENT.md) — текст согласия на обработку ПДн для формы регистрации (URL `/api/consent`); - [`docs/COMPLIANCE.md`](docs/COMPLIANCE.md) — чек-лист приведения сервиса в соответствие с документами. Юридические эндпоинты: - `POST /api/auth/signup` — регистрация; требует `accept_terms: true` и `accept_privacy: true` (факт акцепта фиксируется в таблице `user_agreements`); - `DELETE /api/auth/delete-account` — удаление учётной записи и персональных данных (авторизован JWT, подтверждение паролем в HTTP-заголовке `X-Password`). ## Агент «Юрист по ПО» (DSH) В каталоге [`agents/software-lawyer`](agents/software-lawyer) лежит агент-пресет для DeepSeek Harness: специализированный консультант по правовым вопросам разработки ПО (лицензии, авторские права, персональные данные 152-ФЗ/GDPR, договоры). Отвечает на основе встроенной базы знаний (`skills/software-law/references/`), без внешних API. Установка (копирует пресет в `~/.dsh/.agent-presets/software-lawyer`): ```shell ./agents/software-lawyer/install.sh ``` После установки пресет «Юрист по ПО» появится в списке агентов при создании новой сессии в веб-интерфейсе DSH. Повторная установка с перезаписью: `./agents/software-lawyer/install.sh --force`. Структура: - `agent.cordis.yml` — композиция агента (полный набор инструментов, как у пресета `standard`, + персона юриста и скилл-база); - `preset.yml` — отображаемое имя и описание; - `skills/software-law/` — база знаний: `SKILL.md` (правила консультаций) и `references/` (лицензии, авторские права, ПДн, договоры). ## MCP-сервер (встроен в основной сервис) MCP-сервер (Model Context Protocol) встроен в основной бинарь и доступен как HTTP-эндпоинт `/api/mcp` (streamable HTTP) на REST-gateway — чтобы LLM-клиент (Claude Desktop, IDE с MCP-поддержкой и т.п.) мог играть в «Вечерний детектив». Инструменты вызывают игровые сервисы напрямую, отдельный процесс не нужен. Ходы через `make_move` сохраняются с `actions.via_mcp = true`; через REST/gRPC — с `false`. Отметку задаёт сервер, клиент её не передаёт. У старых ходов значение `false`: их источник не отслеживался. Это отметка канала MCP, а не гарантия того, кто фактически играет. Инструменты: | Инструмент | Назначение | |---|---| | `connect` | Подключиться к игре по ссылке `/team-story/{id}?password=...`; возвращает id команды, пароль и текущую историю | | `get_team_story` | История команды: точки, двери, улики, вопросы дела + инфо об игре (по паролю команды, без авторизации) | | `make_move` | Ход команды в точку по её коду; возвращает обновлённую историю (по паролю команды, без авторизации) | | `submit_answers` | Ответы команды на вопросы дела (массив `{question_code, answer}`); возвращает обновлённую историю (по паролю команды, без авторизации) | Ссылку на игру (`/team-story/{id}?password=...`) выдаёт команде организатор личным каналом — например, вместе с паролем. Принимаются относительные и абсолютные ссылки (в т.ч. с любым хостом): HTTP-запросы по ним не выполняются, из ссылки берутся только id команды и пароль. Игроку авторизация не нужна: команда идентифицируется паролем из ссылки. Запуск — обычный запуск основного сервиса: ```shell make run ``` MCP-эндпоинт: `http://localhost:8090/api/mcp`. ВНИМАНИЕ: REST-gateway слушает `:8090` (gRPC — `:8080`), а `docker-compose.yml` публикует наружу только `8080` — для доступа к `/api/mcp` извне нужна публикация порта 8090 в compose либо проксирование пути `/api/mcp` через reverse-proxy (nginx/caddy). Эндпоинт публичный (как и REST `/api/teams/{id}/story`); секрет — пароль команды. Подключение к MCP-клиенту (пример для Claude Desktop / аналогов): ```json { "mcpServers": { "evening-detective": { "type": "http", "url": "http://localhost:8090/api/mcp" } } } ```