Stage 6: gateway edge (Connect/FlatBuffers over h2c, platform/email/guest auth, sessions, rate-limit, admin passthrough, live push bridge)
Tests · Go / test (push) Successful in 8s
Tests · Integration / integration (push) Successful in 11s
Tests · Go / test (pull_request) Successful in 6s
Tests · Integration / integration (pull_request) Successful in 10s

New public ingress and the first network edge. Framework + a vertical slice of
operations end-to-end; remaining ops reuse the same transcode pattern in Stage 7.

Contracts (new module scrabble/pkg):
- push.proto (backend->gateway gRPC server-stream) + scrabble.fbs (FlatBuffers
  edge payloads), committed generated Go; buf/flatc Makefiles (dev-time codegen).

Backend:
- REST handlers on the /api/v1 groups: internal session endpoints
  (telegram/guest/email login -> mint, resolve, revoke) and the user slice
  (profile, submit_play, state, lobby enqueue/poll, chat).
- internal/notify in-process Publisher hub + internal/pushgrpc gRPC server
  (BACKEND_GRPC_ADDR) streaming your_turn/opponent_moved/chat/nudge/match_found;
  emission in game.commit, social, matchmaker.
- migration 00005 accounts.is_guest; guests are durable rows excluded from stats;
  ProvisionGuest; email-as-login (RequestLoginCode/LoginWithCode).

Gateway (new module scrabble/gateway):
- Connect Gateway service over h2c (Execute + Subscribe), FlatBuffers<->JSON
  transcode registry, Telegram initData HMAC validator (seam), session cache,
  token-bucket rate limiter (3 classes), push fan-out hub, backend REST + push
  gRPC client, admin Basic-Auth reverse proxy.

go.work: use ./pkg, ./gateway + replace scrabble/pkg. CI: gateway/**, pkg/**
path filters; unit build/vet/test span all three modules. Docs (PLAN,
ARCHITECTURE, FUNCTIONAL+ru, TESTING, READMEs) updated; gateway/pkg unit tests +
guest/email-login integration tests.
This commit is contained in:
Ilia Denisov
2026-06-02 22:38:24 +02:00
parent 104eb2a978
commit 408da3f201
98 changed files with 8134 additions and 57 deletions
+16 -4
View File
@@ -18,7 +18,9 @@ import (
"go.uber.org/zap"
"scrabble/backend/internal/account"
"scrabble/backend/internal/game"
"scrabble/backend/internal/lobby"
"scrabble/backend/internal/session"
"scrabble/backend/internal/social"
"scrabble/backend/internal/telemetry"
)
@@ -42,10 +44,13 @@ type Deps struct {
// SessionsReady reports whether the session cache has been warmed. A nil
// func skips the session-readiness check.
SessionsReady func() bool
// Social, Matchmaker, Invitations and Emails are the Stage 4 domain services.
// They are held for the REST/stream handlers the gateway adds in Stage 6 (like
// the route groups, this is scaffolding exposed via accessors); the server
// itself does not route to them yet.
// Sessions, Accounts and Games are the identity, account and game-domain
// services the Stage 6 REST handlers route to.
Sessions *session.Service
Accounts *account.Store
Games *game.Service
// Social, Matchmaker, Invitations and Emails are the Stage 4 domain services
// the Stage 6 REST handlers route to.
Social *social.Service
Matchmaker *lobby.Matchmaker
Invitations *lobby.InvitationService
@@ -61,6 +66,9 @@ type Server struct {
pingTimeout time.Duration
sessionsReady func() bool
sessions *session.Service
accounts *account.Store
games *game.Service
social *social.Service
matchmaker *lobby.Matchmaker
invitations *lobby.InvitationService
@@ -94,6 +102,9 @@ func New(addr string, deps Deps) *Server {
db: deps.DB,
pingTimeout: pingTimeout,
sessionsReady: deps.SessionsReady,
sessions: deps.Sessions,
accounts: deps.Accounts,
games: deps.Games,
social: deps.Social,
matchmaker: deps.Matchmaker,
invitations: deps.Invitations,
@@ -102,6 +113,7 @@ func New(addr string, deps Deps) *Server {
}
s.registerProbes(engine)
s.registerAPIGroups(engine)
s.registerRoutes()
return s
}