package api import ( "strings" "sync/atomic" ) // AdmissionController decides whether a classified public operation may // start. Implementations must be safe for concurrent requests. type AdmissionController interface { Allow(operation string) bool } // AdmissionGate is the operator-controlled overload gate for new matchmaking // work. Degraded mode is deliberately narrow: existing matches can continue // to report results and clients can still use read/recovery/event endpoints. type AdmissionGate struct { degraded atomic.Bool } func NewAdmissionGate(degraded bool) *AdmissionGate { gate := &AdmissionGate{} gate.degraded.Store(degraded) return gate } func (g *AdmissionGate) SetDegraded(value bool) { if g != nil { g.degraded.Store(value) } } func (g *AdmissionGate) Degraded() bool { return g != nil && g.degraded.Load() } func (g *AdmissionGate) Allow(operation string) bool { if !g.Degraded() { return true } switch operation { case "login", "queue", "proposal", "allocation": return false default: return true } } func admissionOperation(path, method string) string { if method == "GET" || method == "HEAD" || method == "OPTIONS" { return "" } path = strings.TrimSuffix(path, "/") switch { case path == "/v1/session/steam" || path == "/api/v1/session/steam": return "login" case path == "/v1/queue" || path == "/api/v1/queue/tickets": return "queue" case underPath(path, "/v1/queue/") || underPath(path, "/api/v1/queue/tickets/"): return "queue" case underPath(path, "/v1/proposals/") || underPath(path, "/api/v1/proposals/"): return "proposal" default: return "" } } func underPath(path, prefix string) bool { return strings.HasPrefix(path, prefix) && len(path) > len(prefix) }