Files
SEEN/backend/internal/config/config.go
T
2026-04-10 12:06:24 +02:00

195 lines
4.5 KiB
Go

package config
import (
"fmt"
"os"
"slices"
"strconv"
"strings"
"time"
"github.com/joho/godotenv"
)
type Config struct {
Env string
AppName string
HTTP HTTPConfig
CORS CORSConfig
Postgres PostgresConfig
Cache CacheConfig
Auth AuthConfig
TMDB TMDBConfig
IGDB IGDBConfig
}
type HTTPConfig struct {
Host string
Port int
ReadTimeout time.Duration
WriteTimeout time.Duration
}
type CORSConfig struct {
AllowedOrigins []string
AllowedMethods []string
AllowedHeaders []string
ExposedHeaders []string
AllowCredentials bool
MaxAge time.Duration
}
type PostgresConfig struct {
URL string
MaxConns int32
MinConns int32
}
type CacheConfig struct {
Addr string
Password string
DB int
}
type AuthConfig struct {
AccessTokenTTLMinutes int
RefreshTokenTTLHours int
JWTSecret string
}
type TMDBConfig struct {
APIKey string
BaseURL string
}
type IGDBConfig struct {
ClientID string
ClientSecret string
BaseURL string
TokenURL string
}
func Load() Config {
_ = godotenv.Load()
return Config{
Env: getString("SEEN_ENV", "development"),
AppName: getString("SEEN_APP_NAME", "seen"),
HTTP: HTTPConfig{
Host: getString("SEEN_HTTP_HOST", "0.0.0.0"),
Port: getInt("SEEN_HTTP_PORT", 8081),
ReadTimeout: getDuration("SEEN_HTTP_READ_TIMEOUT", 15*time.Second),
WriteTimeout: getDuration("SEEN_HTTP_WRITE_TIMEOUT", 15*time.Second),
},
CORS: CORSConfig{
AllowedOrigins: getCSV("SEEN_CORS_ALLOWED_ORIGINS", []string{}),
AllowedMethods: getCSV("SEEN_CORS_ALLOWED_METHODS", []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"}),
AllowedHeaders: getCSV("SEEN_CORS_ALLOWED_HEADERS", []string{"Accept", "Authorization", "Content-Type", "X-Request-ID"}),
ExposedHeaders: getCSV("SEEN_CORS_EXPOSED_HEADERS", []string{"X-Request-ID"}),
AllowCredentials: getBool("SEEN_CORS_ALLOW_CREDENTIALS", false),
MaxAge: getDuration("SEEN_CORS_MAX_AGE", 24*time.Hour),
},
Postgres: PostgresConfig{
URL: getString("SEEN_POSTGRES_URL", "postgres://seen:seen@localhost:5432/seen?sslmode=disable"),
MaxConns: int32(getInt("SEEN_POSTGRES_MAX_CONNS", 10)),
MinConns: int32(getInt("SEEN_POSTGRES_MIN_CONNS", 2)),
},
Cache: CacheConfig{
Addr: getString("SEEN_CACHE_ADDR", "localhost:6379"),
Password: getString("SEEN_CACHE_PASSWORD", ""),
DB: getInt("SEEN_CACHE_DB", 0),
},
Auth: AuthConfig{
AccessTokenTTLMinutes: getInt("SEEN_AUTH_ACCESS_TOKEN_TTL_MINUTES", 30),
RefreshTokenTTLHours: getInt("SEEN_AUTH_REFRESH_TOKEN_TTL_HOURS", 24*30),
JWTSecret: getString("SEEN_AUTH_JWT_SECRET", "replace-in-production"),
},
TMDB: TMDBConfig{
APIKey: getString("SEEN_TMDB_API_KEY", ""),
BaseURL: getString("SEEN_TMDB_BASE_URL", "https://api.themoviedb.org/3"),
},
IGDB: IGDBConfig{
ClientID: getString("SEEN_IGDB_CLIENT_ID", ""),
ClientSecret: getString("SEEN_IGDB_CLIENT_SECRET", ""),
BaseURL: getString("SEEN_IGDB_BASE_URL", "https://api.igdb.com/v4"),
TokenURL: getString("SEEN_IGDB_TOKEN_URL", "https://id.twitch.tv/oauth2/token"),
},
}
}
func (c HTTPConfig) Addr() string {
return fmt.Sprintf("%s:%d", c.Host, c.Port)
}
func getString(key string, fallback string) string {
if value, ok := os.LookupEnv(key); ok {
return value
}
return fallback
}
func getInt(key string, fallback int) int {
value, ok := os.LookupEnv(key)
if !ok {
return fallback
}
parsed, err := strconv.Atoi(value)
if err != nil {
return fallback
}
return parsed
}
func getDuration(key string, fallback time.Duration) time.Duration {
value, ok := os.LookupEnv(key)
if !ok {
return fallback
}
parsed, err := time.ParseDuration(value)
if err != nil {
return fallback
}
return parsed
}
func getBool(key string, fallback bool) bool {
value, ok := os.LookupEnv(key)
if !ok {
return fallback
}
switch strings.ToLower(strings.TrimSpace(value)) {
case "1", "true", "yes", "on":
return true
case "0", "false", "no", "off":
return false
default:
return fallback
}
}
func getCSV(key string, fallback []string) []string {
value, ok := os.LookupEnv(key)
if !ok {
return slices.Clone(fallback)
}
parts := strings.Split(value, ",")
items := make([]string, 0, len(parts))
for _, part := range parts {
trimmed := strings.TrimSpace(part)
if trimmed == "" {
continue
}
items = append(items, trimmed)
}
if len(items) == 0 {
return slices.Clone(fallback)
}
return items
}