package config import ( "fmt" "log" "os" "path/filepath" "strconv" "strings" "time" "github.com/jackc/pgx/v5/pgxpool" ) type Config struct { Addr string DatabaseURL string RedisURL string JWTSecret []byte AdminUser string AdminPassword string BooksDir string CacheDir string ScanInterval time.Duration UploadMaxMB int64 TrustedProxies []string } func Load() (*Config, error) { env := func(k, def string) string { if v := os.Getenv(k); v != "" { return v } return def } envList := func(k, def string) []string { v := env(k, def) out := make([]string, 0, len(strings.Split(v, ","))) for _, s := range strings.Split(v, ",") { if s = strings.TrimSpace(s); s != "" { out = append(out, s) } } return out } scanSec, err := strconv.Atoi(env("SCAN_INTERVAL_SEC", "60")) if err != nil { return nil, fmt.Errorf("SCAN_INTERVAL_SEC: %w", err) } uploadMB, err := strconv.ParseInt(env("UPLOAD_MAX_MB", "200"), 10, 64) if err != nil { return nil, fmt.Errorf("UPLOAD_MAX_MB: %w", err) } secret := os.Getenv("JWT_SECRET") if secret == "" { return nil, fmt.Errorf("JWT_SECRET required") } // Validate required fields. dbURL := env("DATABASE_URL", "") if dbURL == "" { return nil, fmt.Errorf("DATABASE_URL is required") } if _, perr := pgxpool.ParseConfig(dbURL); perr != nil { return nil, fmt.Errorf("DATABASE_URL: %w", perr) } if env("REDIS_URL", "") == "" { log.Printf("redis disabled: rate-limit/scan-lock/page-cache off") } // resolveDir: macOS 开发机上 /var、/tmp 是指向 /private 的软链, // 启动时解析一次才能让 root_path 前缀校验对上真实路径;目录不存在/出错则保留原值(Docker 路径不受影响) resolveDir := func(dir string) string { if r, e := filepath.EvalSymlinks(dir); e == nil { return r } return dir } return &Config{ Addr: env("ADDR", ":8080"), DatabaseURL: env("DATABASE_URL", ""), RedisURL: env("REDIS_URL", ""), JWTSecret: []byte(secret), AdminUser: env("ADMIN_USER", ""), AdminPassword: env("ADMIN_PASSWORD", ""), BooksDir: resolveDir(env("BOOKS_DIR", "/data/books")), CacheDir: resolveDir(env("CACHE_DIR", "/data/cache")), ScanInterval: time.Duration(scanSec) * time.Second, UploadMaxMB: uploadMB, TrustedProxies: envList("TRUSTED_PROXY_CIDRS", "172.16.0.0/12"), }, nil }