mirror of
https://github.com/Dvorinka/Primora.git
synced 2026-06-04 12:33:01 +00:00
132 lines
3.6 KiB
Go
132 lines
3.6 KiB
Go
package config
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
type Config struct {
|
|
Env string
|
|
ServerPort string
|
|
DatabaseURL string
|
|
DragonflyURL string
|
|
StorageRoot string
|
|
AuthInternalBaseURL string
|
|
PublicURL string
|
|
UserRateLimitPerMin int
|
|
APIKeyRateLimitPerMin int
|
|
|
|
JWTIssuer string
|
|
JWTAudience string
|
|
JWTSecret string
|
|
JWTTTLSeconds int
|
|
|
|
MailFrom string
|
|
ResendAPIKey string
|
|
SMTPHost string
|
|
SMTPPort int
|
|
SMTPUser string
|
|
SMTPPassword string
|
|
SMTPSecure bool
|
|
}
|
|
|
|
func Load() (Config, error) {
|
|
cfg := Config{
|
|
Env: getenv("NODE_ENV", "development"),
|
|
ServerPort: getenv("BACKEND_PORT", "8080"),
|
|
DatabaseURL: os.Getenv("DATABASE_URL"),
|
|
DragonflyURL: getenv("DRAGONFLY_URL", "redis://localhost:6379/0"),
|
|
StorageRoot: getenv("BACKEND_STORAGE_ROOT", "./tmp/storage"),
|
|
AuthInternalBaseURL: getenv("AUTH_INTERNAL_BASE_URL", "http://auth:3001"),
|
|
PublicURL: getenv("VITE_APP_URL", "http://localhost"),
|
|
UserRateLimitPerMin: 240,
|
|
APIKeyRateLimitPerMin: 600,
|
|
JWTIssuer: getenv("JWT_ISSUER", "primora-auth"),
|
|
JWTAudience: getenv("JWT_AUDIENCE", "primora-api"),
|
|
JWTSecret: os.Getenv("JWT_SECRET"),
|
|
MailFrom: getenv("MAIL_FROM", "Primora <no-reply@primora.local>"),
|
|
ResendAPIKey: os.Getenv("RESEND_API_KEY"),
|
|
SMTPHost: getenv("SMTP_HOST", "localhost"),
|
|
SMTPUser: os.Getenv("SMTP_USER"),
|
|
SMTPPassword: os.Getenv("SMTP_PASSWORD"),
|
|
}
|
|
|
|
smtpPort, err := strconv.Atoi(getenv("SMTP_PORT", "1025"))
|
|
if err != nil {
|
|
return Config{}, fmt.Errorf("parse SMTP_PORT: %w", err)
|
|
}
|
|
cfg.SMTPPort = smtpPort
|
|
|
|
jwtTTLSeconds, err := strconv.Atoi(getenv("JWT_TTL_SECONDS", "900"))
|
|
if err != nil {
|
|
return Config{}, fmt.Errorf("parse JWT_TTL_SECONDS: %w", err)
|
|
}
|
|
cfg.JWTTTLSeconds = jwtTTLSeconds
|
|
|
|
userRateLimitPerMin, err := parseNonNegativeIntEnv("USER_RATE_LIMIT_PER_MINUTE", cfg.UserRateLimitPerMin)
|
|
if err != nil {
|
|
return Config{}, err
|
|
}
|
|
cfg.UserRateLimitPerMin = userRateLimitPerMin
|
|
|
|
apiKeyRateLimitPerMin, err := parseNonNegativeIntEnv("API_KEY_RATE_LIMIT_PER_MINUTE", cfg.APIKeyRateLimitPerMin)
|
|
if err != nil {
|
|
return Config{}, err
|
|
}
|
|
cfg.APIKeyRateLimitPerMin = apiKeyRateLimitPerMin
|
|
|
|
smtpSecure, err := strconv.ParseBool(getenv("SMTP_SECURE", "false"))
|
|
if err != nil {
|
|
return Config{}, fmt.Errorf("parse SMTP_SECURE: %w", err)
|
|
}
|
|
cfg.SMTPSecure = smtpSecure
|
|
|
|
var missing []string
|
|
if cfg.DatabaseURL == "" {
|
|
missing = append(missing, "DATABASE_URL")
|
|
}
|
|
if cfg.JWTSecret == "" {
|
|
missing = append(missing, "JWT_SECRET")
|
|
}
|
|
if cfg.StorageRoot == "" {
|
|
missing = append(missing, "BACKEND_STORAGE_ROOT")
|
|
}
|
|
if cfg.AuthInternalBaseURL == "" {
|
|
missing = append(missing, "AUTH_INTERNAL_BASE_URL")
|
|
}
|
|
if cfg.ResendAPIKey == "" && cfg.SMTPHost == "" {
|
|
missing = append(missing, "RESEND_API_KEY or SMTP_HOST")
|
|
}
|
|
if len(missing) > 0 {
|
|
return Config{}, errors.New("missing required environment values: " + strings.Join(missing, ", "))
|
|
}
|
|
|
|
return cfg, nil
|
|
}
|
|
|
|
func getenv(key, fallback string) string {
|
|
value := os.Getenv(key)
|
|
if value == "" {
|
|
return fallback
|
|
}
|
|
return value
|
|
}
|
|
|
|
func parseNonNegativeIntEnv(key string, fallback int) (int, error) {
|
|
raw := os.Getenv(key)
|
|
if raw == "" {
|
|
return fallback, nil
|
|
}
|
|
value, err := strconv.Atoi(raw)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("parse %s: %w", key, err)
|
|
}
|
|
if value < 0 {
|
|
return 0, fmt.Errorf("%s must be >= 0", key)
|
|
}
|
|
return value, nil
|
|
}
|