package config import ( "log/slog" "os" "strconv" "strings" "time" ) type Config struct { AppEnv string AppPublicURL string AppTimezone string HTTPAddr string LogLevel slog.Level DatabaseURL string SuperAdminEmail string SuperAdminPassword string JWTSecret string AccessTokenTTL time.Duration RefreshTokenTTL time.Duration CORSAllowedOrigins []string CalendarProvider string CalendarAPIBaseURL string CalendarAPIKey string CalendarCacheTTL time.Duration UploadMaxSizeMB int UploadAllowedMIMEs []string StorageDriver string StorageLocalPath string SMTPHost string SMTPPort string SMTPUser string SMTPPassword string SMTPFromEmail string SMTPFromName string } func Load() Config { return Config{ AppEnv: env("APP_ENV", "development"), AppPublicURL: env("APP_PUBLIC_URL", "http://localhost:5173"), AppTimezone: env("APP_TIMEZONE", "America/Sao_Paulo"), HTTPAddr: env("HTTP_ADDR", ":8080"), LogLevel: logLevel(env("LOG_LEVEL", "info")), DatabaseURL: env("DATABASE_URL", "postgres://mira:mira@localhost:5432/mira?sslmode=disable"), SuperAdminEmail: env("SUPER_ADMIN_EMAIL", ""), SuperAdminPassword: env("SUPER_ADMIN_PASSWORD", ""), JWTSecret: env("JWT_SECRET", ""), AccessTokenTTL: minutes("JWT_ACCESS_TOKEN_TTL_MINUTES", 480), RefreshTokenTTL: hours("JWT_REFRESH_TOKEN_TTL_DAYS", 7*24), CORSAllowedOrigins: list("CORS_ALLOWED_ORIGINS", "http://localhost:5173"), CalendarProvider: env("CALENDAR_PROVIDER", "brasilapi"), CalendarAPIBaseURL: env("CALENDAR_API_BASE_URL", "https://brasilapi.com.br"), CalendarAPIKey: env("CALENDAR_API_KEY", ""), CalendarCacheTTL: hours("CALENDAR_CACHE_TTL_HOURS", 24), UploadMaxSizeMB: integer("UPLOAD_MAX_SIZE_MB", 10), UploadAllowedMIMEs: list("UPLOAD_ALLOWED_MIME_TYPES", "image/jpeg,image/png,image/webp,application/pdf,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document"), StorageDriver: env("STORAGE_DRIVER", "local"), StorageLocalPath: env("STORAGE_LOCAL_PATH", "/var/lib/mira/uploads"), SMTPHost: env("SMTP_HOST", ""), SMTPPort: env("SMTP_PORT", "587"), SMTPUser: env("SMTP_USER", ""), SMTPPassword: envAlias("SMTP_PASS", "SMTP_PASSWORD", ""), SMTPFromEmail: envAlias("MAIL_FROM", "SMTP_FROM_EMAIL", ""), SMTPFromName: env("SMTP_FROM_NAME", "Mira"), } } func env(key, fallback string) string { value := strings.TrimSpace(os.Getenv(key)) if value == "" { return fallback } return value } func envAlias(primary string, secondary string, fallback string) string { value := strings.TrimSpace(os.Getenv(primary)) if value != "" { return value } return env(secondary, fallback) } func list(key, fallback string) []string { raw := env(key, fallback) parts := strings.Split(raw, ",") values := make([]string, 0, len(parts)) for _, part := range parts { value := strings.TrimSpace(part) if value != "" { values = append(values, value) } } return values } func integer(key string, fallback int) int { value, err := strconv.Atoi(env(key, "")) if err != nil { return fallback } return value } func minutes(key string, fallback int) time.Duration { return time.Duration(integer(key, fallback)) * time.Minute } func hours(key string, fallback int) time.Duration { return time.Duration(integer(key, fallback)) * time.Hour } func logLevel(value string) slog.Level { switch strings.ToLower(value) { case "debug": return slog.LevelDebug case "warn": return slog.LevelWarn case "error": return slog.LevelError default: return slog.LevelInfo } }