168 lines
4.7 KiB
Go
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
|
|
}
|