Files
wxapp-guantong/backend/internal/config/config.go
T
2026-09-22 22:51:19 +08:00

168 lines
4.7 KiB
Go

package config
import (
"bufio"
"fmt"
"os"
"path/filepath"
"strings"
)
type Config struct {
Addr string
DatabasePath string
SchemaPath string
LogLevel string
AllowOrigins string
UploadDir string
WeChatAppID string
WeChatAppSecret string
WeChatAPIBase string
AdminAuthMode string
AdminSessionSecret string
AdminPasswordChangeRedirectURL string
OIDCIssuer string
OIDCClientID string
OIDCClientSecret string
OIDCRedirectURI string
OIDCScopes string
}
const (
AdminAuthModeNone = "none"
AdminAuthModeOIDC = "oidc"
)
func Load() Config {
loadEnvFiles()
return Config{
Addr: env("GUAHAO_ADDR", ":9800"),
DatabasePath: env("GUAHAO_DB_PATH", "data/smilefirst.db"),
SchemaPath: env("GUAHAO_SCHEMA_PATH", "migrations/schema.sql"),
LogLevel: env("GUAHAO_LOG_LEVEL", "info"),
AllowOrigins: env("GUAHAO_ALLOW_ORIGINS", "*"),
UploadDir: env("GUAHAO_UPLOAD_DIR", "data/uploads"),
WeChatAppID: env("GUAHAO_WECHAT_APPID", ""),
WeChatAppSecret: env("GUAHAO_WECHAT_SECRET", ""),
WeChatAPIBase: env("GUAHAO_WECHAT_API_BASE", "https://api.weixin.qq.com"),
AdminAuthMode: env("GUAHAO_ADMIN_AUTH_MODE", AdminAuthModeOIDC),
AdminSessionSecret: env("GUAHAO_ADMIN_SESSION_SECRET", ""),
AdminPasswordChangeRedirectURL: env("GUAHAO_ADMIN_PASSWORD_CHANGE_REDIRECT_URL", "https://gh.yqbmb.com/admin/price-inquiries"),
OIDCIssuer: env("GUAHAO_OIDC_ISSUER", ""),
OIDCClientID: env("GUAHAO_OIDC_CLIENT_ID", ""),
OIDCClientSecret: env("GUAHAO_OIDC_CLIENT_SECRET", ""),
OIDCRedirectURI: env("GUAHAO_OIDC_REDIRECT_URI", ""),
OIDCScopes: env("GUAHAO_OIDC_SCOPES", "openid profile email"),
}
}
func (c Config) AdminAuthModeValue() string {
mode := strings.ToLower(strings.TrimSpace(c.AdminAuthMode))
if mode == "" {
return AdminAuthModeOIDC
}
return mode
}
func (c Config) AdminAuthDisabled() bool {
return c.AdminAuthModeValue() == AdminAuthModeNone
}
func (c Config) AdminOIDCLoginEnabled() bool {
return c.AdminAuthModeValue() == AdminAuthModeOIDC
}
func (c Config) ValidateAdminAuth() error {
switch c.AdminAuthModeValue() {
case AdminAuthModeNone:
return nil
case AdminAuthModeOIDC:
return c.validateOIDCConfiguration()
default:
return fmt.Errorf("invalid admin auth mode %q: use %s or %s", c.AdminAuthMode, AdminAuthModeNone, AdminAuthModeOIDC)
}
}
func (c Config) validateOIDCConfiguration() error {
var missing []string
if strings.TrimSpace(c.OIDCIssuer) == "" {
missing = append(missing, "GUAHAO_OIDC_ISSUER")
}
if strings.TrimSpace(c.OIDCClientID) == "" {
missing = append(missing, "GUAHAO_OIDC_CLIENT_ID")
}
if c.OIDCClientSecret == "" {
missing = append(missing, "GUAHAO_OIDC_CLIENT_SECRET")
}
if strings.TrimSpace(c.OIDCRedirectURI) == "" {
missing = append(missing, "GUAHAO_OIDC_REDIRECT_URI")
}
if c.AdminSessionSecret == "" {
missing = append(missing, "GUAHAO_ADMIN_SESSION_SECRET")
}
if len(missing) > 0 {
return fmt.Errorf("missing OIDC admin configuration: set %s in .env or environment", strings.Join(missing, ", "))
}
return nil
}
func env(key, fallback string) string {
value := strings.TrimSpace(os.Getenv(key))
if value == "" {
return fallback
}
return value
}
func loadEnvFiles() {
if explicit := strings.TrimSpace(os.Getenv("GUAHAO_ENV_FILE")); explicit != "" {
_ = loadEnvFile(explicit)
return
}
for _, path := range []string{".env", filepath.Join("..", ".env")} {
_ = loadEnvFile(path)
}
}
func loadEnvFile(path string) error {
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if line == "" || strings.HasPrefix(line, "#") {
continue
}
line = strings.TrimPrefix(line, "export ")
key, value, ok := strings.Cut(line, "=")
if !ok {
continue
}
key = strings.TrimSpace(key)
if key == "" {
continue
}
if _, exists := os.LookupEnv(key); exists {
continue
}
_ = os.Setenv(key, cleanEnvValue(value))
}
return scanner.Err()
}
func cleanEnvValue(value string) string {
value = strings.TrimSpace(value)
if len(value) < 2 {
return value
}
quote := value[0]
if (quote == '\'' || quote == '"') && value[len(value)-1] == quote {
return value[1 : len(value)-1]
}
return value
}