package config import ( "encoding/json" "fmt" "os" ) type ServerConfig struct { AppName string `json:"app_name"` ReadTimeoutSeconds int `json:"read_timeout_seconds"` WriteTimeoutSeconds int `json:"write_timeout_seconds"` IdleTimeoutSeconds int `json:"idle_timeout_seconds"` DisableStartupMessage bool `json:"disable_startup_message"` Auth AuthConfig `json:"auth"` Mail MailConfig `json:"mail"` Db DbConfig `json:"db_config"` RolesConfigPath string `json:"roles_config_path"` } type AuthConfig struct { Secret string `json:"secret"` Issuer string `json:"issuer"` AccessTokenExpiryMinutes int `json:"access_token_expiry_minutes"` RefreshTokenExpiryMinutes int `json:"refresh_token_expiry_minutes"` } type MailConfig struct { Mode string `json:"mode"` From string `json:"from"` DebugDir string `json:"debug_dir"` TemplatesDir string `json:"templates_dir"` MailTemplatesDir string `json:"mail_templates_dir"` FrontendBaseURL string `json:"frontend_base_url"` ResetPasswordPath string `json:"reset_password_path"` SMTP SMTPMailConfig `json:"smtp"` } type SMTPMailConfig struct { Host string `json:"host"` Port int `json:"port"` Username string `json:"username"` Password string `json:"password"` } type DbConfig struct { Driver string DSN string } var Config *ServerConfig = nil func GetConfig() (*ServerConfig, error) { if Config == nil { var err error Config, err = loadConfig() if err != nil { fmt.Printf("Failed to load config: %v\n", err) return nil, err } } return Config, nil } func envOrDefault(key, defaultValue string) string { if value, exists := os.LookupEnv(key); exists { return value } return defaultValue } func loadConfig() (*ServerConfig, error) { path := envOrDefault("CONFIG_PATH", "configs/config.json") data, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("read config: %w", err) } var cfg ServerConfig if err := json.Unmarshal(data, &cfg); err != nil { return nil, fmt.Errorf("parse config: %w", err) } if secret := os.Getenv("AUTH_SECRET"); secret != "" { cfg.Auth.Secret = secret } if cfg.Auth.Secret == "" { return nil, fmt.Errorf("auth.secret must be set") } if cfg.Auth.AccessTokenExpiryMinutes <= 0 { return nil, fmt.Errorf("auth.access_token_expiry_minutes must be greater than zero") } if cfg.Auth.RefreshTokenExpiryMinutes <= 0 { return nil, fmt.Errorf("auth.refresh_token_expiry_minutes must be greater than zero") } if cfg.Mail.Mode == "" { cfg.Mail.Mode = "file" } if cfg.Mail.MailTemplatesDir == "" { cfg.Mail.MailTemplatesDir = "templates/mailTemplates" } if cfg.Mail.ResetPasswordPath == "" { cfg.Mail.ResetPasswordPath = "/#reset-password" } if cfg.Mail.Mode != "smtp" && cfg.Mail.Mode != "file" { return nil, fmt.Errorf("mail.mode must be either smtp or file") } if cfg.Mail.From == "" { return nil, fmt.Errorf("mail.from must be set") } if cfg.Mail.Mode == "smtp" { if cfg.Mail.SMTP.Host == "" { return nil, fmt.Errorf("mail.smtp.host must be set when mail.mode=smtp") } if cfg.Mail.SMTP.Port <= 0 { return nil, fmt.Errorf("mail.smtp.port must be greater than zero when mail.mode=smtp") } } else if cfg.Mail.DebugDir == "" { cfg.Mail.DebugDir = "data/mail-debug" } cfg.Db = DbConfig{ Driver: envOrDefault("DB_driver", "sqlite"), DSN: envOrDefault("DB_dsn", "file:./data/data.db?_foreign_keys=on"), } return &cfg, nil }