package config import ( "bufio" "os" "strings" ) type Config struct { Port string JWTSecret string AdminUsername string AdminPassword string OSSEndpoint string OSSAccessKeyID string OSSAccessKeySecret string OSSBucket string DBDsn string AllowRegistration bool LoginRequireCaptcha bool RegisterRequireCaptcha bool } func getenv(key, def string) string { v := os.Getenv(key) if v == "" { return def } return v } func Load() Config { // Auto-load .env if present to reduce startup parameters. loadDotEnv() return Config{ Port: getenv("PORT", "8080"), JWTSecret: getenv("JWT_SECRET", "change-me"), AdminUsername: os.Getenv("ADMIN_USERNAME"), AdminPassword: os.Getenv("ADMIN_PASSWORD"), OSSEndpoint: os.Getenv("OSS_ENDPOINT"), OSSAccessKeyID: os.Getenv("OSS_ACCESS_KEY_ID"), OSSAccessKeySecret: os.Getenv("OSS_ACCESS_KEY_SECRET"), OSSBucket: os.Getenv("OSS_BUCKET"), DBDsn: os.Getenv("DB_DSN"), AllowRegistration: parseBool(getenv("ALLOW_REGISTRATION", "true")), LoginRequireCaptcha: parseBool(getenv("LOGIN_REQUIRE_CAPTCHA", "true")), RegisterRequireCaptcha: parseBool(getenv("REGISTER_REQUIRE_CAPTCHA", "true")), } } // loadDotEnv loads environment variables from a local .env file if present. // It only sets variables that are not already defined in the process env. func loadDotEnv() { f, err := os.Open(".env") if err != nil { return } defer f.Close() s := bufio.NewScanner(f) for s.Scan() { line := strings.TrimSpace(s.Text()) if line == "" || strings.HasPrefix(line, "#") { continue } if i := strings.Index(line, "="); i != -1 { key := strings.TrimSpace(line[:i]) val := strings.TrimSpace(line[i+1:]) if len(val) >= 2 { if (val[0] == '"' && val[len(val)-1] == '"') || (val[0] == '\'' && val[len(val)-1] == '\'') { val = val[1 : len(val)-1] } } if os.Getenv(key) == "" { _ = os.Setenv(key, val) } } } } func parseBool(v string) bool { switch strings.ToLower(strings.TrimSpace(v)) { case "1", "true", "yes", "on": return true default: return false } }