config.go 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. package main
  2. import (
  3. "fmt"
  4. "os"
  5. "strconv"
  6. "strings"
  7. )
  8. // config 是后端启动所需的全部配置。
  9. //
  10. // 来源见 docs/architecture.md 第四节:全部通过环境变量提供,
  11. // 缺失必填项则拒绝启动。cookie 名、有效期、bcrypt cost 等常量
  12. // 不在配置里暴露,直接写在代码中。
  13. type config struct {
  14. // 必填
  15. ListenAddr string // HAVEN_LISTEN_ADDR
  16. DBDSN string // HAVEN_DB_DSN
  17. FrontendDir string // HAVEN_FRONTEND_DIR
  18. GogsBaseURL string // HAVEN_GOGS_BASE_URL
  19. CookieDomain string // HAVEN_COOKIE_DOMAIN
  20. // 可选,带默认值
  21. CookieSecure bool // HAVEN_COOKIE_SECURE,默认 true
  22. TotpIssuer string // HAVEN_TOTP_ISSUER,默认 Haven
  23. LogLevel string // HAVEN_LOG_LEVEL,默认 info
  24. }
  25. // loadConfig 从环境变量读取并校验配置。
  26. // 任一必填项缺失或取值非法都返回错误,由调用方拒绝启动。
  27. func loadConfig() (config, error) {
  28. var cfg config
  29. var missing []string
  30. cfg.ListenAddr = strings.TrimSpace(os.Getenv("HAVEN_LISTEN_ADDR"))
  31. if cfg.ListenAddr == "" {
  32. missing = append(missing, "HAVEN_LISTEN_ADDR")
  33. }
  34. cfg.DBDSN = strings.TrimSpace(os.Getenv("HAVEN_DB_DSN"))
  35. if cfg.DBDSN == "" {
  36. missing = append(missing, "HAVEN_DB_DSN")
  37. }
  38. cfg.FrontendDir = strings.TrimSpace(os.Getenv("HAVEN_FRONTEND_DIR"))
  39. if cfg.FrontendDir == "" {
  40. missing = append(missing, "HAVEN_FRONTEND_DIR")
  41. }
  42. cfg.GogsBaseURL = strings.TrimSpace(strings.TrimSuffix(os.Getenv("HAVEN_GOGS_BASE_URL"), "/"))
  43. if cfg.GogsBaseURL == "" {
  44. missing = append(missing, "HAVEN_GOGS_BASE_URL")
  45. }
  46. cfg.CookieDomain = strings.TrimSpace(os.Getenv("HAVEN_COOKIE_DOMAIN"))
  47. if cfg.CookieDomain == "" {
  48. missing = append(missing, "HAVEN_COOKIE_DOMAIN")
  49. }
  50. if len(missing) > 0 {
  51. return config{}, fmt.Errorf("缺少必填环境变量: %s", strings.Join(missing, ", "))
  52. }
  53. cfg.CookieSecure = true
  54. if raw := strings.TrimSpace(os.Getenv("HAVEN_COOKIE_SECURE")); raw != "" {
  55. v, err := strconv.ParseBool(raw)
  56. if err != nil {
  57. return config{}, fmt.Errorf("HAVEN_COOKIE_SECURE 取值非法 %q,应为 true 或 false", raw)
  58. }
  59. cfg.CookieSecure = v
  60. }
  61. cfg.TotpIssuer = strings.TrimSpace(os.Getenv("HAVEN_TOTP_ISSUER"))
  62. if cfg.TotpIssuer == "" {
  63. cfg.TotpIssuer = "Haven"
  64. }
  65. cfg.LogLevel = strings.ToLower(strings.TrimSpace(os.Getenv("HAVEN_LOG_LEVEL")))
  66. if cfg.LogLevel == "" {
  67. cfg.LogLevel = "info"
  68. }
  69. switch cfg.LogLevel {
  70. case "debug", "info", "warn", "error":
  71. default:
  72. return config{}, fmt.Errorf("HAVEN_LOG_LEVEL 取值非法 %q,应为 debug / info / warn / error", cfg.LogLevel)
  73. }
  74. return cfg, nil
  75. }
苏ICP备2026082406号
苏公网安备 待审核