package config import ( "log/slog" "os" "strconv" "sync" "time" ) type Config struct { BaseURL string Username string Password string Semester string TWFID string Recognizer RecognizerConfig } type RecognizerConfig struct { APIURL string APIKey string Model string Timeout time.Duration } var ( cfg *Config cfgOnce sync.Once ) func Load() *Config { cfgOnce.Do(func() { cfg = &Config{ BaseURL: getEnv("JWTS_BASE_URL", "http://jwts.hitwh.edu.cn"), Username: getEnv("JWTS_USERNAME", ""), Password: getEnv("JWTS_PASSWORD", ""), Semester: getEnv("JWTS_SEMESTER", "2025-20262"), TWFID: getEnv("JWTS_TWFID", ""), Recognizer: RecognizerConfig{ APIURL: getEnv("RECOGNIZER_API_URL", "https://api.littlelan.cn/v1/chat/completions"), APIKey: getEnv("RECOGNIZER_API_KEY", ""), Model: getEnv("RECOGNIZER_MODEL", "qwen3.5-plus"), Timeout: getEnvDuration("RECOGNIZER_TIMEOUT", 120*time.Second), }, } if cfg.Recognizer.APIKey == "" { slog.Warn("RECOGNIZER_API_KEY not set, image recognition will not work") } }) return cfg } func Get() *Config { if cfg == nil { return Load() } return cfg } func getEnv(key, defaultValue string) string { val := os.Getenv(key) if val == "" { return defaultValue } return val } func getEnvInt(key string, defaultValue int) int { val := os.Getenv(key) if val == "" { return defaultValue } if i, err := strconv.Atoi(val); err == nil { return i } return defaultValue } func getEnvDuration(key string, defaultValue time.Duration) time.Duration { val := os.Getenv(key) if val == "" { return defaultValue } if d, err := time.ParseDuration(val); err == nil { return d } return defaultValue } var ( BaseURL = Get().BaseURL Username = Get().Username Password = Get().Password Semester = Get().Semester TWFID = Get().TWFID RecognizerAPIURL = Get().Recognizer.APIURL RecognizerAPIKey = Get().Recognizer.APIKey RecognizerModel = Get().Recognizer.Model )