bdf6ff0ae8
Fail fast on missing config and reject default encryption keys; use SHA-256 ingress key lookup, trusted-proxy-aware client IP, sentinel gateway errors with correct HTTP status codes, daily provider key counter reset, Anthropic multimodal forwarding, and cache Get methods that return copies. Co-authored-by: Cursor <cursoragent@cursor.com>
114 lines
2.6 KiB
Go
114 lines
2.6 KiB
Go
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
type Config struct {
|
|
Server ServerConfig `yaml:"server"`
|
|
Database DatabaseConfig `yaml:"database"`
|
|
Admin AdminConfig `yaml:"admin"`
|
|
Cache CacheConfig `yaml:"cache"`
|
|
HealthCheck HealthCheckConfig `yaml:"health_check"`
|
|
Security SecurityConfig `yaml:"security"`
|
|
Gateway GatewayConfig `yaml:"gateway"`
|
|
}
|
|
|
|
type GatewayConfig struct {
|
|
MaxRetries int `yaml:"max_retries"`
|
|
RetryBackoffMs int `yaml:"retry_backoff_ms"`
|
|
}
|
|
|
|
type ServerConfig struct {
|
|
Addr string `yaml:"addr"`
|
|
}
|
|
|
|
type DatabaseConfig struct {
|
|
Path string `yaml:"path"`
|
|
}
|
|
|
|
type AdminConfig struct {
|
|
Username string `yaml:"username"`
|
|
Password string `yaml:"password"`
|
|
}
|
|
|
|
type CacheConfig struct {
|
|
UsageFlushInterval time.Duration `yaml:"usage_flush_interval"`
|
|
}
|
|
|
|
type HealthCheckConfig struct {
|
|
Interval time.Duration `yaml:"interval"`
|
|
}
|
|
|
|
type SecurityConfig struct {
|
|
EncryptionKey string `yaml:"encryption_key"`
|
|
SessionTTL time.Duration `yaml:"session_ttl"`
|
|
IPAllowlist []string `yaml:"ip_allowlist"`
|
|
TrustedProxies []string `yaml:"trusted_proxies"`
|
|
LoginMaxAttempts int `yaml:"login_max_attempts"`
|
|
LoginLockout time.Duration `yaml:"login_lockout"`
|
|
}
|
|
|
|
func Load(path string) (*Config, error) {
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("read config: %w", err)
|
|
}
|
|
cfg := Default()
|
|
if err := yaml.Unmarshal(data, cfg); err != nil {
|
|
return nil, fmt.Errorf("parse config: %w", err)
|
|
}
|
|
resolveEnv(cfg)
|
|
return cfg, nil
|
|
}
|
|
|
|
func Default() *Config {
|
|
return &Config{
|
|
Server: ServerConfig{Addr: ":8080"},
|
|
Database: DatabaseConfig{
|
|
Path: "./data/luminary.db",
|
|
},
|
|
Admin: AdminConfig{
|
|
Username: "admin",
|
|
Password: "admin123",
|
|
},
|
|
Cache: CacheConfig{
|
|
UsageFlushInterval: 5 * time.Second,
|
|
},
|
|
HealthCheck: HealthCheckConfig{
|
|
Interval: 30 * time.Second,
|
|
},
|
|
Security: SecurityConfig{
|
|
EncryptionKey: "change-me-in-production-32bytes!!",
|
|
SessionTTL: 24 * time.Hour,
|
|
LoginMaxAttempts: 5,
|
|
LoginLockout: 15 * time.Minute,
|
|
},
|
|
Gateway: GatewayConfig{
|
|
MaxRetries: 3,
|
|
RetryBackoffMs: 200,
|
|
},
|
|
}
|
|
}
|
|
|
|
func resolveEnv(cfg *Config) {
|
|
cfg.Admin.Username = resolveEnvValue(cfg.Admin.Username)
|
|
cfg.Admin.Password = resolveEnvValue(cfg.Admin.Password)
|
|
cfg.Security.EncryptionKey = resolveEnvValue(cfg.Security.EncryptionKey)
|
|
}
|
|
|
|
func resolveEnvValue(v string) string {
|
|
if strings.HasPrefix(v, "env.") {
|
|
key := strings.TrimPrefix(v, "env.")
|
|
if val := os.Getenv(key); val != "" {
|
|
return val
|
|
}
|
|
}
|
|
return v
|
|
}
|