You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
196 lines
5.5 KiB
Go
196 lines
5.5 KiB
Go
package config
|
|
|
|
import (
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"knowfoolery/backend/services/gateway-service/internal/infra/routing"
|
|
sharedredis "knowfoolery/backend/shared/infra/database/redis"
|
|
"knowfoolery/backend/shared/infra/observability/logging"
|
|
"knowfoolery/backend/shared/infra/observability/metrics"
|
|
"knowfoolery/backend/shared/infra/observability/tracing"
|
|
"knowfoolery/backend/shared/infra/utils/envutil"
|
|
)
|
|
|
|
// CORSConfig controls gateway CORS behavior.
|
|
type CORSConfig struct {
|
|
AllowedOrigins []string
|
|
AllowedMethods string
|
|
AllowedHeaders string
|
|
AllowCredentials bool
|
|
MaxAgeSeconds int
|
|
}
|
|
|
|
// SecurityHeadersConfig controls security-related HTTP response headers.
|
|
type SecurityHeadersConfig struct {
|
|
ContentSecurityPolicy string
|
|
EnableHSTS bool
|
|
HSTSMaxAge int
|
|
FrameOptions string
|
|
ContentTypeOptions bool
|
|
ReferrerPolicy string
|
|
PermissionsPolicy string
|
|
}
|
|
|
|
// RateLimitConfig controls request rate limits.
|
|
type RateLimitConfig struct {
|
|
GeneralRequests int
|
|
AuthRequests int
|
|
APIRequests int
|
|
AdminRequests int
|
|
Window time.Duration
|
|
}
|
|
|
|
// Config is the runtime configuration for gateway-service.
|
|
type Config struct {
|
|
AppName string
|
|
Port int
|
|
|
|
PublicPrefix string
|
|
UpstreamTimeout time.Duration
|
|
Upstreams routing.Upstreams
|
|
|
|
CORS CORSConfig
|
|
Security SecurityHeadersConfig
|
|
Rate RateLimitConfig
|
|
|
|
Redis sharedredis.Config
|
|
Tracing tracing.Config
|
|
Metrics metrics.Config
|
|
Logging logging.Config
|
|
|
|
ZitadelBaseURL string
|
|
ZitadelIssuer string
|
|
ZitadelAudience string
|
|
ZitadelClientID string
|
|
ZitadelSecret string
|
|
}
|
|
|
|
// FromEnv builds config from environment variables.
|
|
func FromEnv() Config {
|
|
env := envutil.String("ENVIRONMENT", "development")
|
|
serviceName := "gateway-service"
|
|
|
|
logCfg := logging.DefaultConfig()
|
|
logCfg.ServiceName = serviceName
|
|
logCfg.Environment = env
|
|
logCfg.Level = envutil.String("LOG_LEVEL", logCfg.Level)
|
|
|
|
traceCfg := tracing.ConfigFromEnv()
|
|
if traceCfg.ServiceName == "knowfoolery" {
|
|
traceCfg.ServiceName = serviceName
|
|
}
|
|
traceCfg.Environment = env
|
|
|
|
metricsCfg := metrics.ConfigFromEnv()
|
|
if metricsCfg.ServiceName == "knowfoolery" {
|
|
metricsCfg.ServiceName = serviceName
|
|
}
|
|
|
|
prefix := normalizePrefix(envutil.String("GATEWAY_PUBLIC_PREFIX", "/api/v1"))
|
|
|
|
cfg := Config{
|
|
AppName: "Know Foolery - Gateway Service",
|
|
Port: envutil.Int("GATEWAY_INTERNAL_PORT", 18086),
|
|
|
|
PublicPrefix: prefix,
|
|
UpstreamTimeout: envutil.Duration("GATEWAY_UPSTREAM_TIMEOUT", 3*time.Second),
|
|
Upstreams: routing.Upstreams{
|
|
GameSession: envutil.String("GAME_SESSION_BASE_URL", "http://localhost:8080"),
|
|
QuestionBank: envutil.String("QUESTION_BANK_BASE_URL", "http://localhost:8081"),
|
|
User: envutil.String("USER_SERVICE_BASE_URL", "http://localhost:8082"),
|
|
Leaderboard: envutil.String("LEADERBOARD_BASE_URL", "http://localhost:8083"),
|
|
Admin: envutil.String("ADMIN_SERVICE_BASE_URL", "http://localhost:8085"),
|
|
},
|
|
|
|
CORS: CORSConfig{
|
|
AllowedOrigins: parseCSV(envutil.String("GATEWAY_ALLOWED_ORIGINS", "http://localhost:5173")),
|
|
AllowedMethods: envutil.String("GATEWAY_ALLOWED_METHODS", "GET,POST,PUT,DELETE,OPTIONS"),
|
|
AllowedHeaders: envutil.String("GATEWAY_ALLOWED_HEADERS", "Origin,Content-Type,Accept,Authorization"),
|
|
AllowCredentials: parseBool(
|
|
"GATEWAY_ALLOW_CREDENTIALS",
|
|
true,
|
|
),
|
|
MaxAgeSeconds: envutil.Int("GATEWAY_CORS_MAX_AGE_SECONDS", 300),
|
|
},
|
|
|
|
Security: SecurityHeadersConfig{
|
|
ContentSecurityPolicy: envutil.String(
|
|
"GATEWAY_CSP",
|
|
"default-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'",
|
|
),
|
|
EnableHSTS: parseBool("GATEWAY_ENABLE_HSTS", true),
|
|
HSTSMaxAge: envutil.Int("GATEWAY_HSTS_MAX_AGE", 31536000),
|
|
FrameOptions: envutil.String("GATEWAY_FRAME_OPTIONS", "DENY"),
|
|
ContentTypeOptions: parseBool("GATEWAY_CONTENT_TYPE_OPTIONS", true),
|
|
ReferrerPolicy: envutil.String("GATEWAY_REFERRER_POLICY", "strict-origin-when-cross-origin"),
|
|
PermissionsPolicy: envutil.String(
|
|
"GATEWAY_PERMISSIONS_POLICY",
|
|
"geolocation=(), microphone=(), camera=(), payment=(), usb=()",
|
|
),
|
|
},
|
|
|
|
Rate: RateLimitConfig{
|
|
GeneralRequests: envutil.Int("GATEWAY_RATE_GENERAL", 100),
|
|
AuthRequests: envutil.Int("GATEWAY_RATE_AUTH", 5),
|
|
APIRequests: envutil.Int("GATEWAY_RATE_API", 60),
|
|
AdminRequests: envutil.Int("GATEWAY_RATE_ADMIN", 30),
|
|
Window: envutil.Duration("GATEWAY_RATE_WINDOW", time.Minute),
|
|
},
|
|
|
|
Redis: sharedredis.ConfigFromEnv(),
|
|
Tracing: traceCfg,
|
|
Metrics: metricsCfg,
|
|
Logging: logCfg,
|
|
|
|
ZitadelBaseURL: envutil.String("ZITADEL_URL", ""),
|
|
ZitadelIssuer: envutil.String("ZITADEL_ISSUER", ""),
|
|
ZitadelAudience: envutil.String("ZITADEL_AUDIENCE", ""),
|
|
ZitadelClientID: envutil.String("ZITADEL_CLIENT_ID", ""),
|
|
ZitadelSecret: envutil.String("ZITADEL_CLIENT_SECRET", ""),
|
|
}
|
|
|
|
return cfg
|
|
}
|
|
|
|
func normalizePrefix(raw string) string {
|
|
trimmed := strings.TrimSpace(raw)
|
|
if trimmed == "" {
|
|
return "/api/v1"
|
|
}
|
|
if !strings.HasPrefix(trimmed, "/") {
|
|
trimmed = "/" + trimmed
|
|
}
|
|
return strings.TrimRight(trimmed, "/")
|
|
}
|
|
|
|
func parseCSV(raw string) []string {
|
|
if strings.TrimSpace(raw) == "" {
|
|
return nil
|
|
}
|
|
|
|
parts := strings.Split(raw, ",")
|
|
out := make([]string, 0, len(parts))
|
|
for _, part := range parts {
|
|
v := strings.TrimSpace(part)
|
|
if v == "" {
|
|
continue
|
|
}
|
|
out = append(out, v)
|
|
}
|
|
return out
|
|
}
|
|
|
|
func parseBool(key string, fallback bool) bool {
|
|
raw := envutil.String(key, "")
|
|
if raw == "" {
|
|
return fallback
|
|
}
|
|
parsed, err := strconv.ParseBool(raw)
|
|
if err != nil {
|
|
return fallback
|
|
}
|
|
return parsed
|
|
}
|