Files
Tomas Dvorak dfc079288f hot fix #1
2026-01-26 08:13:18 +01:00

326 lines
9.4 KiB
Go

package config
import (
"log"
"os"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/joho/godotenv"
)
// Config holds all configuration for the application
type Config struct {
// App settings
AppEnv string
Port string
Debug bool
Premium bool
// Database settings
DatabaseURL string
MaxIdleConnections int
MaxOpenConnections int
ConnMaxLifetime time.Duration
// JWT settings
JWTSecret string
JWTExpiration time.Duration
// Server settings
ReadTimeout time.Duration
WriteTimeout time.Duration
IdleTimeout time.Duration
// Security
ContentSecurityPolicy string
// File upload settings
UploadDir string
MaxUploadSize int64
AllowedMimeTypes []string
// Email settings
SMTPHost string
SMTPPort int
SMTPUser string
SMTPPassword string
SMTPFrom string
SMTPFromName string
SMTPEncryption string
SMTPAuth bool
SMTPSkipVerify bool
// Email templates
EmailTemplateDir string
// Contact settings
ContactEmail string
AdminEmail string
// Admin access token (optional) to allow token-based admin access
AdminAccessToken string
// Newsletter settings
NewsletterEnabled bool
// CORS settings
AllowedOrigins []string
// External services
ScraperBaseURL string
FrontendBaseURL string
PublicAPIBaseURL string
ZoneramaAPIBase string
// Umami Analytics
UmamiURL string
UmamiUsername string
UmamiPassword string
UmamiWebsiteID string
ErrorIngestURL string
ErrorIngestToken string
// Antivirus (optional)
ClamAVEnabled bool
ClamAVHost string
ClamAVPort int
// Feature flags
RembgEnabled bool
// Club data mode: "auto" (FACR integration) or "manual" (no FACR/http scraping)
ClubDataMode string
// E-shop settings
EshopEnabled bool
EshopFrontendURL string
EshopAPIURL string
// Stripe (E-shop payments)
StripeEnabled bool
StripeSecretKey string
StripePublishableKey string
StripeWebhookSecret string
StripeCurrency string
StripeReturnURL string
// Revolut (E-shop payments)
RevolutEnabled bool
RevolutEnvironment string
RevolutAPIKey string
RevolutPublicKey string
RevolutWebhookSecret string
RevolutReturnURL string
RevolutWebhookURL string
// Packeta / Zasilkovna
PacketaAPIPassword string
PacketaWidgetAPIKey string
PacketaEshopName string
PacketaEnv string
// DeepSeek AI Support
DeepSeekAPIKey string
DeepSeekBaseURL string
}
var AppConfig *Config
// LoadConfig loads the configuration from environment variables
func LoadConfig() {
// Load .env file if it exists
_ = godotenv.Load()
AppConfig = &Config{
// App settings
AppEnv: getEnv("APP_ENV", "development"),
Port: getEnv("PORT", "8080"),
Debug: getEnvAsBool("DEBUG", true),
Premium: getEnvAsBool("PREMIUM", false),
// Database settings
DatabaseURL: getEnv("DATABASE_URL", "postgres://postgres:postgres@localhost:5432/fotbal_club?sslmode=disable"),
MaxIdleConnections: getEnvAsInt("DB_MAX_IDLE_CONNS", 10),
MaxOpenConnections: getEnvAsInt("DB_MAX_OPEN_CONNS", 100),
ConnMaxLifetime: time.Duration(getEnvAsInt("DB_CONN_MAX_LIFETIME", 60)) * time.Minute,
// JWT settings
JWTSecret: getEnv("JWT_SECRET", "default-secret-key-change-in-production"),
JWTExpiration: time.Duration(getEnvAsInt("JWT_EXPIRATION_HOURS", 24)) * time.Hour,
// Server settings
ReadTimeout: time.Duration(getEnvAsInt("READ_TIMEOUT", 5)) * time.Second,
WriteTimeout: time.Duration(getEnvAsInt("WRITE_TIMEOUT", 10)) * time.Second,
IdleTimeout: time.Duration(getEnvAsInt("IDLE_TIMEOUT", 120)) * time.Second,
// Security
ContentSecurityPolicy: getEnv("CONTENT_SECURITY_POLICY", "default-src 'self' data: blob: https: http:; img-src * data: blob:; style-src 'self' 'unsafe-inline' https: http:; script-src 'self' 'unsafe-inline' 'unsafe-eval' https: http:; connect-src *; frame-ancestors 'self';"),
// File upload settings
UploadDir: getEnv("UPLOAD_DIR", "./uploads"),
MaxUploadSize: int64(getEnvAsInt("MAX_UPLOAD_SIZE", 20)) * 1024 * 1024, // 20MB default
AllowedMimeTypes: []string{
// Images
"image/jpeg",
"image/png",
"image/gif",
"image/webp",
"image/svg+xml",
// Documents
"application/pdf",
"application/msword", // .doc
"application/vnd.openxmlformats-officedocument.wordprocessingml.document", // .docx
"application/vnd.ms-excel", // .xls
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", // .xlsx
"application/vnd.ms-powerpoint", // .ppt
"application/vnd.openxmlformats-officedocument.presentationml.presentation", // .pptx
// Text
"text/plain",
// Archives
"application/zip",
"application/x-zip-compressed",
"application/x-rar-compressed",
"application/vnd.rar",
},
// Email settings
SMTPHost: getEnv("SMTP_HOST", "smtp.example.com"),
SMTPPort: getEnvAsInt("SMTP_PORT", 587),
SMTPUser: getEnv("SMTP_USER", ""),
SMTPPassword: getEnv("SMTP_PASSWORD", ""),
SMTPFrom: getEnv("SMTP_FROM", "noreply@fotbal-club.cz"),
SMTPFromName: getEnv("SMTP_FROM_NAME", "Fotbal Club"),
SMTPEncryption: getEnv("SMTP_ENCRYPTION", "tls"),
SMTPAuth: getEnvAsBool("SMTP_AUTH", true),
SMTPSkipVerify: getEnvAsBool("SMTP_SKIP_VERIFY", false),
// Email templates - using absolute path to templates
EmailTemplateDir: getEnv("EMAIL_TEMPLATE_DIR", filepath.Join("templates", "emails")),
// Contact settings
ContactEmail: getEnv("CONTACT_EMAIL", "help@tdvorak.dev"),
AdminEmail: getEnv("ADMIN_EMAIL", "help@tdvorak.dev"),
AdminAccessToken: getEnv("ADMIN_ACCESS_TOKEN", ""),
// Newsletter settings
NewsletterEnabled: getEnvAsBool("NEWSLETTER_ENABLED", true),
// CORS settings
AllowedOrigins: []string{
"http://localhost:3000",
"http://localhost:8080",
},
// External services
ScraperBaseURL: getEnv("FACR_SCRAPER_BASE_URL", "http://localhost:8081"),
FrontendBaseURL: getEnv("FRONTEND_BASE_URL", "http://localhost:3000"),
PublicAPIBaseURL: getEnv("PUBLIC_API_BASE_URL", "http://localhost:8080/api/v1"),
ZoneramaAPIBase: getEnv("ZONERAMA_API_BASE", "https://zonerama.tdvorak.dev"),
// Umami Analytics
UmamiURL: getEnv("UMAMI_URL", ""),
UmamiUsername: getEnv("UMAMI_USERNAME", ""),
UmamiPassword: getEnv("UMAMI_PASSWORD", ""),
UmamiWebsiteID: getEnv("UMAMI_WEBSITE_ID", ""),
ErrorIngestURL: getEnv("ERROR_INGEST_URL", ""),
ErrorIngestToken: getEnv("ERROR_INGEST_TOKEN", ""),
// Antivirus (optional)
ClamAVEnabled: getEnvAsBool("CLAMAV_ENABLED", false),
ClamAVHost: getEnv("CLAMAV_HOST", "127.0.0.1"),
ClamAVPort: getEnvAsInt("CLAMAV_PORT", 3310),
// Feature flags
RembgEnabled: getEnvAsBool("REMBG_ENABLED", true),
// Club data mode: auto (FACR integration) or manual (no FACR/http scraping)
ClubDataMode: strings.ToLower(strings.TrimSpace(getEnv("CLUB_DATA_MODE", "auto"))),
// E-shop settings
EshopEnabled: getEnvAsBool("ESHOP_ENABLED", false),
EshopFrontendURL: getEnv("ESHOP_FRONTEND_URL", ""),
EshopAPIURL: getEnv("ESHOP_API_URL", ""),
// Stripe (E-shop payments)
StripeEnabled: getEnvAsBool("STRIPE_ENABLED", false),
StripeSecretKey: getEnv("STRIPE_SECRET_KEY", ""),
StripePublishableKey: getEnv("STRIPE_PUBLISHABLE_KEY", ""),
StripeWebhookSecret: getEnv("STRIPE_WEBHOOK_SECRET", ""),
StripeCurrency: getEnv("STRIPE_CURRENCY", "CZK"),
StripeReturnURL: getEnv("STRIPE_RETURN_URL", ""),
// Revolut (E-shop payments)
RevolutEnabled: getEnvAsBool("REVOLUT_ENABLED", false),
RevolutEnvironment: getEnv("REVOLUT_ENVIRONMENT", "sandbox"),
RevolutAPIKey: getEnv("REVOLUT_API_KEY", ""),
RevolutPublicKey: getEnv("REVOLUT_PUBLIC_KEY", ""),
RevolutWebhookSecret: getEnv("REVOLUT_WEBHOOK_SECRET", ""),
RevolutReturnURL: getEnv("REVOLUT_RETURN_URL", ""),
RevolutWebhookURL: getEnv("REVOLUT_WEBHOOK_URL", ""),
// Packeta / Zasilkovna
PacketaAPIPassword: getEnv("PACKETA_API_PASSWORD", ""),
PacketaWidgetAPIKey: getEnv("PACKETA_WIDGET_API_KEY", ""),
PacketaEshopName: getEnv("PACKETA_ESHP_NAME", "MyClubEshop"),
PacketaEnv: getEnv("PACKETA_ENV", "test"),
// DeepSeek AI Support (used by e-shop support chat)
DeepSeekAPIKey: getEnv("DEEPSEEK_API_KEY", ""),
DeepSeekBaseURL: getEnv("DEEPSEEK_BASE_URL", "https://api.deepseek.com"),
}
// Override allowed origins if specified in environment (comma-separated)
if origins := os.Getenv("ALLOWED_ORIGINS"); origins != "" {
parts := strings.Split(origins, ",")
var list []string
for _, p := range parts {
v := strings.TrimSpace(p)
if v != "" {
list = append(list, v)
}
}
if len(list) > 0 {
AppConfig.AllowedOrigins = list
}
}
}
func getEnv(key, defaultValue string) string {
value := os.Getenv(key)
if value == "" {
return defaultValue
}
return value
}
func getEnvAsInt(key string, defaultValue int) int {
valueStr := os.Getenv(key)
if valueStr == "" {
return defaultValue
}
value, err := strconv.Atoi(valueStr)
if err != nil {
log.Printf("Invalid value for %s: %v. Using default: %d", key, err, defaultValue)
return defaultValue
}
return value
}
func getEnvAsBool(key string, defaultValue bool) bool {
valueStr := os.Getenv(key)
if valueStr == "" {
return defaultValue
}
value, err := strconv.ParseBool(valueStr)
if err != nil {
log.Printf("Invalid boolean value for %s: %v. Using default: %t", key, err, defaultValue)
return defaultValue
}
return value
}