Commits
verifyRefappToken validates refapp's HMAC-SHA256 login JWT (shared
REFAPP_TOKEN_SECRET) and maps it to a discord-type actor, so refserver audit
logs and participant checks attribute actions to the real user. Mirrors
refapp's token version check; documents the bounded revocation gap.
Breaks the chicken-and-egg of needing an admin key to create keys via the
API. On startup, if the ApiKey collection is empty, refserver seeds an
admin-scoped key from REFSERVER_BOOTSTRAP_API_KEY (idempotent; refuses if other
keys already exist). Lets the combined stack deploy refserver for the first time
without manual key insertion.
Mirrors refapp/refbot: Dockerfile builds the TS image (exposes PORT 3000 +
WS_PORT 3002), scripts/release.js bumps version, tags/pushes git, then builds
and pushes registry.ellite.dev/alpinesystem/refserver:{latest,vX.Y.Z} via the
Portainer Docker API. PORTAINER_STACK_ID gates the redeploy step so the image
is pushed safely before the stack exists; REGISTRY_PASSWORD + PORTAINER_TOKEN
are required for the build/push.
- auth.test.ts: the Bearer test sent an API key expecting 200, but Bearer is
the Discord OAuth scheme and correctly rejects API keys. Now mocks Discord's
/users/@me so a real Discord token authenticates (200) and asserts an API key
on the Bearer scheme is rejected (401).
- README: update status to Phases 1-8 complete; document config + rate-limit
knobs and the ApiKey/health rate-limit exemption; note dist/ is built at deploy.
- .env.example: raise RATE_LIMIT_MAX default 100 -> 600 to match code and
explain the SPA-polling rationale.
- Enforce viewerDiscordId participant membership on GET /matches/:id/player
(403 when not a participant) in both in-memory and DB-only branches.
- Skip per-IP rate limit for trusted ApiKey service-to-service calls and
health checks; raise default RATE_LIMIT_MAX to 600 for SPA polling headroom.
- Refresh OpenAPI spec (viewerDiscordId query param + 403 response).
- Add regression test for DB-only branch participant enforcement.
- Fix indentation regression in createApp() rate-limit block.
Document pools, guild config, item-level chart/player endpoints,
match state/status/field-override/bulk ops, event deactivate/delete,
and DELETE /api-keys/{id}. Remove stale skeleton entries (event pool
generation and player CSV import) that have no implementation.
Fix Match.currentChart type (string -> integer).
Verified with a route<->spec cross-check: 42/42 operations match.
GET /events and /events/:id now return match/player counts. New PATCH /events/:id/deactivate. Bearer tokens are validated against Discord and mapped to a discord actor (admin if in DISCORD_ALLOWED_USERS, else read-only). Removed the unused jwt/jose scaffolding and its empty-secret default; hardened the Discord token cache (hashed keys, bounded size).
Adds check-in/approval lifecycle to match engine:
- createPendingMatch: creates match in 'pending' status with check-in state
- checkInPlayer: marks player as checked in
- approveMatch: transitions from pending to ban-phase
New routes:
- POST /matches/:id/check-in (player check-in)
- POST /matches/:id/approve (referee approval)
- POST /matches/:id/restart (restart completed match)
- GET /matches/:id/player (filtered player view)
- GET /pools, GET /pools/tier/:tier, GET /pools/match (pools read)
Session metadata: findMatchByChannelId for channelId→matchId lookup.
268 tests pass, typecheck clean.
Add API key auth middleware (ApiKey/Bearer schemes, scope enforcement),
Express route handlers for events, matches (ban/pick/ready/result/revert/
stop lifecycle), charts search, players search, API key management CRUD.
Enhance WebSocket server with match/event subscribe/unsubscribe protocol.
Integration tests with supertest against mongodb-memory-server. 260 total
tests pass, typecheck clean.
Port state/match.js into src/engine/match.ts keyed by matchId with plain
player data (no Discord objects). Add GeneratedPools model, event singleton
service, pools persistence layer with getPoolByTier/getMatchPool, and
audit logging service with query/filter support. All 228 tests pass,
typecheck clean.
Adds src/external/ brackets (Brackets.json normalizer), startgg (GraphQL client with fetchOpenSets), spinshare (API + zip parsing + DB caching), spindata (HTTP + WebSocket with watchChartEnd). Config extended for STARTGG/SpinShare/spindata env vars. 166 headless node:test tests pass.
Adds src/util/ scoreRank, roll, names, chartHashCheck, parseFriendlyCharts, playerImport, formatMappool, poolGenerator, revertAction, judgeResult.
All 112 headless node:test tests pass; npm run test:unit and npm run test both pass.
Breaks the chicken-and-egg of needing an admin key to create keys via the
API. On startup, if the ApiKey collection is empty, refserver seeds an
admin-scoped key from REFSERVER_BOOTSTRAP_API_KEY (idempotent; refuses if other
keys already exist). Lets the combined stack deploy refserver for the first time
without manual key insertion.
Mirrors refapp/refbot: Dockerfile builds the TS image (exposes PORT 3000 +
WS_PORT 3002), scripts/release.js bumps version, tags/pushes git, then builds
and pushes registry.ellite.dev/alpinesystem/refserver:{latest,vX.Y.Z} via the
Portainer Docker API. PORTAINER_STACK_ID gates the redeploy step so the image
is pushed safely before the stack exists; REGISTRY_PASSWORD + PORTAINER_TOKEN
are required for the build/push.
- auth.test.ts: the Bearer test sent an API key expecting 200, but Bearer is
the Discord OAuth scheme and correctly rejects API keys. Now mocks Discord's
/users/@me so a real Discord token authenticates (200) and asserts an API key
on the Bearer scheme is rejected (401).
- README: update status to Phases 1-8 complete; document config + rate-limit
knobs and the ApiKey/health rate-limit exemption; note dist/ is built at deploy.
- .env.example: raise RATE_LIMIT_MAX default 100 -> 600 to match code and
explain the SPA-polling rationale.
- Enforce viewerDiscordId participant membership on GET /matches/:id/player
(403 when not a participant) in both in-memory and DB-only branches.
- Skip per-IP rate limit for trusted ApiKey service-to-service calls and
health checks; raise default RATE_LIMIT_MAX to 600 for SPA polling headroom.
- Refresh OpenAPI spec (viewerDiscordId query param + 403 response).
- Add regression test for DB-only branch participant enforcement.
- Fix indentation regression in createApp() rate-limit block.
Document pools, guild config, item-level chart/player endpoints,
match state/status/field-override/bulk ops, event deactivate/delete,
and DELETE /api-keys/{id}. Remove stale skeleton entries (event pool
generation and player CSV import) that have no implementation.
Fix Match.currentChart type (string -> integer).
Verified with a route<->spec cross-check: 42/42 operations match.
GET /events and /events/:id now return match/player counts. New PATCH /events/:id/deactivate. Bearer tokens are validated against Discord and mapped to a discord actor (admin if in DISCORD_ALLOWED_USERS, else read-only). Removed the unused jwt/jose scaffolding and its empty-secret default; hardened the Discord token cache (hashed keys, bounded size).
Adds check-in/approval lifecycle to match engine:
- createPendingMatch: creates match in 'pending' status with check-in state
- checkInPlayer: marks player as checked in
- approveMatch: transitions from pending to ban-phase
New routes:
- POST /matches/:id/check-in (player check-in)
- POST /matches/:id/approve (referee approval)
- POST /matches/:id/restart (restart completed match)
- GET /matches/:id/player (filtered player view)
- GET /pools, GET /pools/tier/:tier, GET /pools/match (pools read)
Session metadata: findMatchByChannelId for channelId→matchId lookup.
268 tests pass, typecheck clean.
Add API key auth middleware (ApiKey/Bearer schemes, scope enforcement),
Express route handlers for events, matches (ban/pick/ready/result/revert/
stop lifecycle), charts search, players search, API key management CRUD.
Enhance WebSocket server with match/event subscribe/unsubscribe protocol.
Integration tests with supertest against mongodb-memory-server. 260 total
tests pass, typecheck clean.
Port state/match.js into src/engine/match.ts keyed by matchId with plain
player data (no Discord objects). Add GeneratedPools model, event singleton
service, pools persistence layer with getPoolByTier/getMatchPool, and
audit logging service with query/filter support. All 228 tests pass,
typecheck clean.