httpapi

package
v0.9.77 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 6, 2026 License: GPL-3.0 Imports: 78 Imported by: 0

Documentation

Overview

Package httpapi assembles the ggscale-server HTTP router. The /v1 subtree holds all real routes; everything outside falls through to chi's NotFound (returning 404). /metrics is mounted at root and is intentionally versionless.

Index

Constants

View Source
const RealtimePath = v1Prefix + "/ws"

RealtimePath is the realtime WebSocket route.

Variables

This section is empty.

Functions

func NewRouter

func NewRouter(d Deps) http.Handler

NewRouter builds the ggscale-server HTTP handler.

func OpenAPIDoc

func OpenAPIDoc(version string) *huma.OpenAPI

OpenAPIDoc builds the /v1 OpenAPI document by registering every operation into one shared doc. It needs no live dependencies: the handler closures are registered but never invoked, so a zero Deps suffices.

The register* list below MUST mirror NewRouter's registrations. NewRouter spreads them across middleware-scoped chi groups (for auth/scope binding); here they collapse onto one adapter because the document only needs the operation metadata, not the middleware. TestOpenAPIDoc_covers_expected_paths guards against drift.

Types

type APIWork

type APIWork struct {
	Realtime *realtime.Drain
	Inflight *lifecycle.Inflight
}

APIWork is the API server's meaning of work for the lifecycle contract. Draining refuses new realtime WebSocket upgrades; plain HTTP requests are still served, because the load balancer stops sending them once /readyz turns 503. A drain waits on open sockets plus in-flight HTTP requests. River and the matchmaker worker are deliberately not part of it: River's queue pause is cluster-wide, not per node, and both already stop in the normal shutdown path.

func NewAPIWork

func NewAPIWork() APIWork

NewAPIWork returns the API server's work, with in-flight counting that leaves realtime upgrades to the realtime drain.

func (APIWork) ActiveWork

func (w APIWork) ActiveWork() int64

ActiveWork reports open realtime sockets plus in-flight HTTP requests.

func (APIWork) BeginDrain

func (w APIWork) BeginDrain()

BeginDrain refuses new realtime upgrades.

func (APIWork) Details

func (w APIWork) Details() map[string]any

Details is the API server's "details" object in the /lifecycle JSON.

func (APIWork) EndDrain

func (w APIWork) EndDrain()

EndDrain accepts realtime upgrades again.

func (APIWork) RefusedWork

func (w APIWork) RefusedWork() int64

RefusedWork reports realtime upgrades refused while draining.

type Deps

type Deps struct {
	// PartyEnqueueEnabled lets party leaders queue and rematch. When false,
	// both return 503 party_enqueue_disabled.
	PartyEnqueueEnabled bool
	// PartyCodeLimits bound wrong party-code guesses. Zero fields use
	// party.DefaultCodeLimits.
	PartyCodeLimits party.CodeLimits
	Version         string
	Commit          string

	// RequestTimeout bounds non-streaming requests; 0 disables the deadline
	// middleware (used by unit-test fixtures). WebSocket paths are exempt.
	RequestTimeout time.Duration

	Pool *db.Pool
	// ReadPool serves staleness-tolerant reads. nil defaults to Pool, so a
	// deployment without a replica (or a test fixture) routes reads to the
	// primary unchanged.
	ReadPool *db.Pool
	Lookup   tenant.Lookup
	Limiter  ratelimit.Limiter
	// RateLimitOverrides (may be nil) supplies per-tenant/project rate-limit
	// overrides. Wrap the DB store in a CachedOverrideStore.
	RateLimitOverrides ratelimit.OverrideStore
	// TokenIPLimits (may be nil) overrides the tier→per-IP bucket derivation
	// for the token auth routes; nil uses ratelimit.TokenIPLimitsForTier.
	TokenIPLimits func(tenant.Tier) ratelimit.Limits
	// StorageMaxValueBytes is the platform default cap on a storage object's
	// value; 0 uses the compiled fallback (1 MiB).
	StorageMaxValueBytes int64
	// DeleteGracePeriod is how long a requested player deletion stays
	// cancellable before the purge sweep hard-deletes the data; 0 uses the
	// compiled fallback (30 days).
	DeleteGracePeriod time.Duration
	// StorageLimits (may be nil) resolves per-tenant/project storage-size
	// overrides on top of StorageMaxValueBytes.
	StorageLimits storagelimit.LimitStore
	// ProxyTrust resolves the real client IP for per-IP limits when the server
	// is behind a trusted reverse proxy / load balancer. nil = RemoteAddr only.
	ProxyTrust *ratelimit.ProxyTrust
	Signer     *auth.Signer
	Mailer     mailer.Mailer
	MailFrom   string
	// EnqueuePasswordReset inserts the durable forgot-password delivery job
	// (jobs.PasswordResetEmailArgs). nil = no job queue; the web handlers
	// fall back to in-process delivery.
	EnqueuePasswordReset func(ctx context.Context, surface, email string) error
	// TwoFactor encrypts TOTP secrets and signs 2FA pending cookies for the
	// control panel and player surfaces. nil = 2FA enrollment unavailable.
	TwoFactor *twofactor.Cipher
	// CredentialCipher seals stored provider credentials (e.g. the Steam Web
	// API key) at rest and unseals them on read. nil reads values as stored
	// (unit tests; legacy plaintext rows).
	CredentialCipher *secretseal.Cipher
	// SteamAuth verifies Steam session tickets for POST /v1/auth/steam. nil
	// defaults to the production Valve client; tests inject a fake.
	SteamAuth steamauth.Verifier
	// EmailVerifySigningKey signs verification cookies for both web surfaces.
	EmailVerifySigningKey []byte
	Cache                 cache.Store
	Registry              *prometheus.Registry
	// Metrics carries the business/health counters. nil is a no-op (unit tests).
	Metrics *observability.Metrics
	RBAC    *rbac.Authorizer
	Now     func() time.Time

	// Fleet is the allocator for game-server slots. nil until a backend is
	// wired. The matchmaker checks for nil and degrades to a not-implemented
	// error when unset.
	Fleet *fleet.Manager

	// Hub fans WS messages out to connected players. nil disables /v1/ws.
	Hub                  *realtime.Hub
	RealtimeMaxPerTenant int64
	RealtimeMaxPerPlayer int64
	// RealtimeHeartbeat overrides the WS heartbeat/revalidation interval.
	// Zero uses the production default (30s); tests shorten it.
	RealtimeHeartbeat time.Duration
	// TenantConnectionCap coordinates regional capacity through PostgreSQL
	// leases while keeping socket admission in process memory.
	TenantConnectionCap ratelimit.ConnectionCap

	// Matchmaker is the ticket queue. nil disables /v1/matchmaker/*.
	Matchmaker matchmaker.Queue
	// MatchmakerTicketTTL bounds how long a queued ticket lives. 0
	// disables expiry.
	MatchmakerTicketTTL time.Duration

	// GameSessions creates game sessions (shared with the matchmaker
	// worker's game_session mode).
	GameSessions *gamesession.Service

	// ServerList is the in-memory game-server heartbeat registry that
	// backs the server-browser endpoint. nil disables /v1/fleets/*.
	ServerList *serverlist.Registry

	// RelayIssuer mints TURN-REST credentials. nil disables /v1/relay/*.
	RelayIssuer *relay.Issuer
	// RelayNodes picks the relay URLs each credential set lists. nil lists
	// none, so credential requests get 503.
	RelayNodes RelayNodeSelector
	// RelayNodeAdmin backs the platform-admin relay node routes. They mount
	// only when it and PlatformAdminToken are both set.
	RelayNodeAdmin RelayNodeAdmin
	// PlatformAdminToken guards the platform-admin API (/v1/admin/*). Empty
	// leaves it unmounted.
	PlatformAdminToken string
	// RelayMeter enforces the per-class monthly relay-session allowance at
	// credential issuance. nil disables metering (unit tests).
	RelayMeter *relaymeter.Meter

	ControlPanel          controlpanel.Config
	ControlPanelBootstrap *controlpanel.Bootstrap
	// BillingHandoffKey signs the control panel's short-lived billing upgrade
	// handoff tokens. nil = no upgrade link renders.
	BillingHandoffKey []byte
	// ControlPanelPluginInfo is the closure the admin/plugins page calls to
	// snapshot the running fleet plugin. nil when no plugin backend is
	// configured — the page renders "no plugin backend" in that case.
	ControlPanelPluginInfo func() *controlpanel.PluginSnapshot

	// Players controls whether the player-facing /v1/players/p/{projectID}/
	// site is mounted.
	Players players.Config

	// CORSAllowedOrigins lists the origins the API router answers preflight
	// from. Empty in dev falls back to "*"; config.Validate refuses an
	// empty list in production.
	CORSAllowedOrigins []string

	// MetricsAuthToken, when non-empty, gates /metrics behind a bearer token.
	// Empty leaves /metrics open (dev / explicitly-unauthenticated deployments).
	MetricsAuthToken string

	// MCPEnabled mounts POST /mcp for coding agents (FEATURE_MCP_ENABLED).
	// MCPTokenRatePerSecond / MCPTokenBurst set the per-token bucket.
	MCPEnabled            bool
	MCPTokenRatePerSecond float64
	MCPTokenBurst         float64
	// MCPAuthFailuresPerMinute / MCPAuthFailureBurst set the per-IP bucket
	// for failed /mcp authentications. Zero uses the mcp package default.
	MCPAuthFailuresPerMinute float64
	MCPAuthFailureBurst      float64
	// MCPMaxProjectAPIKeys limits the create_api_key tool.
	MCPMaxProjectAPIKeys int64
	// CORSMaxProjectOrigins limits the set_allowed_origins tool.
	CORSMaxProjectOrigins int

	// EntitlementAPIToken, when non-empty, mounts the internal declarative
	// entitlement API at /internal/entitlements behind this bearer token —
	// outside /v1, so it never enters openapi.yaml or the SDKs. Empty (the
	// default) leaves the surface unmounted entirely.
	EntitlementAPIToken string

	// Lifecycle, when non-nil, mounts /readyz and the /lifecycle routes at
	// root next to /metrics. Operators keep them off the public edge the same
	// way they keep /metrics off it.
	Lifecycle *lifecycle.Controller
	// RealtimeDrain, when non-nil, tracks open /v1/ws sockets and refuses new
	// upgrades with 503 while the process drains.
	RealtimeDrain *realtime.Drain
	// contains filtered or unexported fields
}

Deps carries values the router needs but doesn't construct.

Pool / Lookup / Limiter / Signer are all required to mount the tenant-protected /v1/auth/* subgroup. When any is nil, only the public /v1/healthz + /metrics routes are mounted — useful for unit tests that don't need authenticated paths.

type RelayNodeAdmin

type RelayNodeAdmin interface {
	Create(ctx context.Context, spec relaynode.Spec) (relaynode.Node, error)
	List(ctx context.Context) ([]relaynode.Node, error)
	Retire(ctx context.Context, id int64) (relaynode.Node, error)
}

RelayNodeAdmin is the relay node registry the admin routes manage. relaynode.Store implements it.

type RelayNodeSelector

type RelayNodeSelector interface {
	Select(ctx context.Context, region string) (relaynode.Selection, error)
}

RelayNodeSelector picks the relay URLs for a credential request. relaynode.Selector implements it.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL