85ff3a7faf
PR Checks / test-and-build (pull_request) Successful in 9m54s
VERDICT from verifier (D:\Projects\Game\docs\review-report.md):
REQUEST_CHANGES — wizard was functionally broken at runtime.
## Critical
C-1. Choice-button customId was missing the 'choice:' segment.
ButtonCustomId emitted 'wizard:btn:<step>:<value>' but the
dispatcher's switch matches parts[1] == 'choice'. Every choice
button (D&D 5e, Pathfinder, Waitlist, Publish, Confirm) fell
into the default branch and showed 'Unknown button'.
Fix: split into 3 customId helpers:
ChoiceButtonCustomId(step, value) -> 'wizard:btn:choice:<step>:<value>'
ControlButtonCustomId(action) -> 'wizard:btn:<action>:1' (back/cancel/skip/create)
ModalTriggerButtonCustomId(modalStep) -> 'wizard:btn:modal:<modalStep>'
Bulk-rewrote all 66 Btn() call sites in DiscordWizardStep.cs.
C-2. "Другое…" modal-trigger buttons were unrouted in dispatcher.
Added 'parts[1] == "modal"' branch that opens the modal via
InteractionCallback.Modal(BuildModal(parts[2], draft.ChatId)).
C-3. DiscordWizardSubmitter was leaking ex.Message from
CreateSessionHandler to the user-visible draft embed. Postgres
exceptions expose schema/constraint names. Replaced with
generic user-facing error; full exception still logged
server-side on the existing catch block.
## I-3 — parser-roundtrip tests (the gap that let C-1/C-2 through)
Added two real behavioural tests (not string-grep) to
DiscordWizardInteractionModuleSourceTests:
- Renderer_And_Dispatcher_Agree_On_Wire_Format
- ControlButtons_Are_Parsed_As_Control_Not_Choice
These mirror NetCord's [ComponentInteraction("wizard")] prefix
strip, run the parser, and assert the dispatcher would route to
the right branch. Catches the entire class of 'renderer and
dispatcher disagree on the wire format' regressions.
## I-6 — BuildResumeRow (cascading fix from C-1)
After C-1, BuildResumeRow's ButtonCustomId('cancel', '1') would
emit the wrong format. Switched to direct format strings
('wizard:btn:cancel:1', 'wizard:btn:resume:continue', etc.) which
match the dispatcher's 'back'/'cancel'/'create'/'resume' cases
directly, not the 'choice' prefix.
## Version sync (3.8.0 -> 3.9.0)
Directory.Build.props: <Version>3.9.0</Version>
compose.yaml: all 3 image tags -> 3.9.0
Version_ShouldBeSynchronizedForDiscordFeatureRelease test now green.
## Stats
build: 0 warnings, 0 errors
format: 0 of 279 files need changes
tests: 583 passed, 2 skipped (pre-existing), 0 failed
files: 7 changed, 226 +, 79 -
131 lines
4.1 KiB
YAML
131 lines
4.1 KiB
YAML
services:
|
|
db:
|
|
image: postgres:17-alpine
|
|
restart: always
|
|
environment:
|
|
POSTGRES_USER: gmrelay
|
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}
|
|
POSTGRES_DB: gmrelay_db
|
|
volumes:
|
|
- pgdata:/var/lib/postgresql/data
|
|
networks:
|
|
- gmrelay
|
|
healthcheck:
|
|
test: [ "CMD-SHELL", "pg_isready -U gmrelay -d gmrelay_db" ]
|
|
interval: 3s
|
|
timeout: 3s
|
|
retries: 10
|
|
|
|
db-backup:
|
|
image: postgres:17-alpine
|
|
restart: unless-stopped
|
|
depends_on:
|
|
db:
|
|
condition: service_healthy
|
|
environment:
|
|
POSTGRES_USER: gmrelay
|
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}
|
|
POSTGRES_DB: gmrelay_db
|
|
PGPASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}
|
|
BACKUP_RETENTION_DAYS: ${BACKUP_RETENTION_DAYS:-7}
|
|
volumes:
|
|
- pgbackups:/backups
|
|
networks:
|
|
- gmrelay
|
|
entrypoint: ["sh", "-c"]
|
|
command:
|
|
- |
|
|
cat > /usr/local/bin/backup.sh << 'EOF'
|
|
#!/bin/sh
|
|
set -e
|
|
TMPFILE="/tmp/backup_$$.sql"
|
|
pg_dump -h db -U gmrelay -d gmrelay_db > "$TMPFILE"
|
|
gzip "$TMPFILE"
|
|
mv "$TMPFILE.gz" "/backups/gmrelay_db_$(date +%Y%m%d_%H%M%S).sql.gz"
|
|
find /backups -name 'gmrelay_db_*.sql.gz' -type f -mtime +${BACKUP_RETENTION_DAYS} -delete
|
|
EOF
|
|
chmod +x /usr/local/bin/backup.sh
|
|
echo "0 3 * * * /usr/local/bin/backup.sh" | crontab -
|
|
crond -f
|
|
|
|
bot:
|
|
image: git.codeanddice.ru/toutsu/gmrelay-bot:3.9.0
|
|
restart: always
|
|
depends_on:
|
|
db:
|
|
condition: service_healthy
|
|
environment:
|
|
- "ConnectionStrings__gmrelaydb=Host=db;Port=5432;Database=gmrelay_db;Username=gmrelay;Password=${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}"
|
|
- "Telegram__BotToken=${TELEGRAM_BOT_TOKEN:?Set TELEGRAM_BOT_TOKEN in .env}"
|
|
- "Telegram__MiniAppUrl=${TELEGRAM_MINI_APP_URL:-}"
|
|
networks:
|
|
- gmrelay
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "wget -qO- http://localhost:8081/health || exit 1"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 3
|
|
|
|
discord:
|
|
image: git.codeanddice.ru/toutsu/gmrelay-discord-bot:3.9.0
|
|
restart: always
|
|
depends_on:
|
|
db:
|
|
condition: service_healthy
|
|
bot:
|
|
condition: service_healthy
|
|
environment:
|
|
- "ConnectionStrings__gmrelaydb=Host=db;Port=5432;Database=gmrelay_db;Username=gmrelay;Password=${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}"
|
|
- "Discord__Token=${DISCORD_BOT_TOKEN:?Set DISCORD_BOT_TOKEN in .env}"
|
|
networks:
|
|
- gmrelay
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "wget -qO- http://localhost:8082/health || exit 1"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 3
|
|
|
|
web:
|
|
image: git.codeanddice.ru/toutsu/gmrelay-web:3.9.0
|
|
restart: always
|
|
depends_on:
|
|
db:
|
|
condition: service_healthy
|
|
bot:
|
|
condition: service_healthy
|
|
environment:
|
|
- "ConnectionStrings__gmrelaydb=Host=db;Port=5432;Database=gmrelay_db;Username=gmrelay;Password=${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}"
|
|
- "Telegram__BotToken=${TELEGRAM_BOT_TOKEN:?Set TELEGRAM_BOT_TOKEN in .env}"
|
|
- "Telegram__BotUsername=${TELEGRAM_BOT_USERNAME:?Set TELEGRAM_BOT_USERNAME in .env}"
|
|
- "Telegram__MiniAppUrl=${TELEGRAM_MINI_APP_URL:-}"
|
|
- "Discord__ClientId=${DISCORD_CLIENT_ID:-}"
|
|
- "Discord__ClientSecret=${DISCORD_CLIENT_SECRET:-}"
|
|
- "Discord__RedirectUri=${DISCORD_REDIRECT_URI:-}"
|
|
- "PortfolioCovers__StoragePath=/app/portfolio-covers"
|
|
ports:
|
|
- "${GMRELAY_WEB_PORT:-8080}:8080"
|
|
volumes:
|
|
- web_keys:/app/dataprotection-keys
|
|
- portfolio_covers:/app/portfolio-covers
|
|
networks:
|
|
- gmrelay
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "wget -qO- http://localhost:8080/health || exit 1"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 3
|
|
|
|
volumes:
|
|
pgdata:
|
|
name: ${POSTGRES_VOLUME_NAME:-game_pgdata}
|
|
web_keys:
|
|
name: ${WEB_KEYS_VOLUME_NAME:-gmrelay_web_keys}
|
|
pgbackups:
|
|
name: ${BACKUP_VOLUME_NAME:-game_pgbackups}
|
|
portfolio_covers:
|
|
name: ${PORTFOLIO_COVERS_VOLUME_NAME:-gmrelay_portfolio_covers}
|
|
|
|
networks:
|
|
gmrelay:
|
|
driver: bridge
|