package config import ( "fmt" "os" "time" "forgejo.riotpiao.com/rock/homelab-frontend/internal/serviceadapter" ) // Config holds the gateway configuration. type Config struct { // ListenAddr is the address to listen on for HTTP traffic. ListenAddr string // ShutdownTimeout is the maximum time to wait for in-flight requests // to complete before forcing shutdown. ShutdownTimeout time.Duration // Routes maps route names to their upstream configuration. Routes map[string]*Route // Models maps model names to their upstream configuration. // Multiple models can point to the same upstream address. Models map[string]*ModelUpstream // Adapters holds service adapter definitions for X-Service routing. Adapters []*serviceadapter.ServiceAdapter // Auth holds JWT authentication configuration for /v1/* endpoints. Auth AuthConfig } // ModelUpstream holds upstream configuration for a specific model. type ModelUpstream struct { // Name is the model name clients send (e.g., "reasoning", "ornith:35b"). Name string // Address is the upstream server address (host:port). Address string // Path is the upstream path for this model (e.g., "/v1/chat/completions"). Path string // AuthRequired indicates whether this model requires JWT authentication. AuthRequired bool } // AuthConfig holds JWT authentication configuration. type AuthConfig struct { // Enabled globally enables/disables auth for /v1/* endpoints. Enabled bool // Issuer is the expected JWT issuer (iss claim). Issuer string // Audience is the expected JWT audience (aud claim). Audience string // JWKSURL is the URL to fetch JSON Web Key Set for signature validation. JWKSURL string // RequiredCapability is the permission required for LLM inference (e.g., "llm:inference"). RequiredCapability string } // Route represents a single route and its upstream configuration. type Route struct { // Name is the route identifier. Name string // Upstream holds the upstream server configuration. Upstream Upstream } // Upstream holds upstream server configuration for a route. type Upstream struct { // Address is the upstream server address (host:port). Address string // PathRewrite is an optional path prefix rewrite. Empty string means no rewrite. PathRewrite string // ConnectTimeout is the maximum time to establish a connection to the upstream. ConnectTimeout time.Duration // ReadTimeout is the maximum time to read a response from the upstream. ReadTimeout time.Duration // WriteTimeout is the maximum time to write a request to the upstream. WriteTimeout time.Duration // MaxBodySize is the maximum request body size in bytes. MaxBodySize int64 // AuthRequired indicates whether this route requires authentication. AuthRequired bool } // LookupModel finds a model by name (case-sensitive, exact match). func (c *Config) LookupModel(name string) *ModelUpstream { if c == nil || c.Models == nil { return nil } return c.Models[name] } // ModelNames returns a sorted list of all known model names. func (c *Config) ModelNames() []string { if c == nil || c.Models == nil { return nil } names := make([]string, 0, len(c.Models)) for name := range c.Models { names = append(names, name) } return names } // Load reads configuration from environment variables with defaults. func Load() (*Config, error) { listenAddr := "127.0.0.1:8080" // Allow override via environment variable if addr, ok := os.LookupEnv("LISTEN_ADDR"); ok { listenAddr = addr } shutdownTimeout := 30 * time.Second // Allow override via environment variable if timeout, ok := os.LookupEnv("SHUTDOWN_TIMEOUT"); ok { d, err := time.ParseDuration(timeout) if err != nil { return nil, fmt.Errorf("invalid SHUTDOWN_TIMEOUT: %w", err) } shutdownTimeout = d } // Load routes, models, adapters, and auth from config file routes := make(map[string]*Route) models := make(map[string]*ModelUpstream) var adapters []*serviceadapter.ServiceAdapter var authConfig AuthConfig if configPath, ok := os.LookupEnv("CONFIG_PATH"); ok { loadedRoutes, loadedModels, loadedAdapters, loadedAuth, err := LoadRoutesAndModelsFromFile(configPath) if err != nil { return nil, err } routes = loadedRoutes models = loadedModels adapters = loadedAdapters authConfig = loadedAuth } return &Config{ ListenAddr: listenAddr, ShutdownTimeout: shutdownTimeout, Routes: routes, Models: models, Adapters: adapters, Auth: authConfig, }, nil }