package rest import ( "log/slog" "net/http" "os" "strconv" "strings" "time" "goAuth/internal/env" ) const ( // Binds to loopback by default so a local run is not exposed on the // network. Override with the ADDR env var (use :8080 to listen on all // interfaces, e.g. inside a container). DefaultAddr = "localhost:8080" DefaultDatabasePath = "gochat.db" DefaultSessionCookie = "session" DefaultCsrfCookie = "csrf" DefaultShortSession = 24 * time.Hour DefaultLongSession = 30 * 24 * time.Hour DefaultTokenSize = 32 // DefaultCleanupInterval is how often expired sessions, spent/expired // invites and never-activated users are purged in the background. A // non-positive value disables the background cleanup. DefaultCleanupInterval = 1 * time.Hour ) type Config struct { Addr string DatabasePath string // PublicBaseURL is the externally reachable base URL of this service, used // to build absolute links (e.g. the avatar URL embedded in Jitsi JWTs). // Leave empty to fall back to the request's Host. PublicBaseURL string // CleanupInterval controls the background maintenance loop that purges // expired sessions, spent/expired invites and never-activated users. // Non-positive disables it. CleanupInterval time.Duration Jitsi JitsiConfig Cors CorsConfig Sessions SessionConfig Cookies CookieConfig } type JitsiConfig struct { Secret string AppId string AppName string Domain string } type CorsConfig struct { Origins []string Methods []string Headers []string } type SessionConfig struct { ShortDuration time.Duration LongDuration time.Duration TokenSize int } type CookieConfig struct { SessionName string CsrfName string Secure bool } func DefaultConfig() Config { return Config{ Addr: DefaultAddr, DatabasePath: DefaultDatabasePath, CleanupInterval: DefaultCleanupInterval, Jitsi: JitsiConfig{ Domain: "meet.jit.si", }, Cors: CorsConfig{ Origins: []string{"http://localhost:3000", "http://localhost:5173"}, Methods: []string{ http.MethodGet, http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete, http.MethodOptions, }, Headers: []string{"Content-Type", "X-CSRF-Token"}, }, Sessions: SessionConfig{ ShortDuration: DefaultShortSession, LongDuration: DefaultLongSession, TokenSize: DefaultTokenSize, }, Cookies: CookieConfig{ SessionName: DefaultSessionCookie, CsrfName: DefaultCsrfCookie, Secure: true, }, } } func LoadConfig() Config { if err := env.LoadEnvDefault(); err != nil { slog.Debug("env file not loaded", slog.String("op", "rest.LoadConfig"), slog.Any("error", err)) } return ConfigFromEnvironment() } func ConfigFromEnvironment() Config { cfg := DefaultConfig() if v := os.Getenv("ADDR"); v != "" { cfg.Addr = v } if v := os.Getenv("DB_PATH"); v != "" { cfg.DatabasePath = v } if v := os.Getenv("PUBLIC_BASE_URL"); v != "" { cfg.PublicBaseURL = strings.TrimRight(v, "/") } if v := os.Getenv("CORS_ORIGINS"); v != "" { cfg.Cors.Origins = splitAndTrim(v) } if v := os.Getenv("COOKIE_SECURE"); v != "" { if parsed, err := strconv.ParseBool(v); err == nil { cfg.Cookies.Secure = parsed } } if v := os.Getenv("SESSION_SHORT_DURATION"); v != "" { if parsed, err := time.ParseDuration(v); err == nil { cfg.Sessions.ShortDuration = parsed } } if v := os.Getenv("SESSION_LONG_DURATION"); v != "" { if parsed, err := time.ParseDuration(v); err == nil { cfg.Sessions.LongDuration = parsed } } if v := os.Getenv("CLEANUP_INTERVAL"); v != "" { if parsed, err := time.ParseDuration(v); err == nil { cfg.CleanupInterval = parsed } } cfg.Jitsi.Secret = os.Getenv("JITSI_SECRET") cfg.Jitsi.AppId = os.Getenv("JITSI_APP_ID") cfg.Jitsi.AppName = os.Getenv("JITSI_APP_NAME") if v := os.Getenv("JITSI_DOMAIN"); v != "" { cfg.Jitsi.Domain = v } return cfg } func splitAndTrim(value string) []string { parts := strings.Split(value, ",") result := make([]string, 0, len(parts)) for _, part := range parts { if trimmed := strings.TrimSpace(part); trimmed != "" { result = append(result, trimmed) } } return result }