chore(api-gateway): merge review docs and update dependencies
This commit is contained in:
@@ -1,28 +1,50 @@
|
||||
// Package config loads runtime configuration for push-gateway from environment
|
||||
// variables. Required variables (JWT_SECRET in production, INTERNAL_API_TOKEN
|
||||
// when not in DevMode) cause a fatal exit when missing.
|
||||
package config
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/edu-cloud/shared-go/env"
|
||||
)
|
||||
|
||||
// Config 持有 push-gateway 运行时配置
|
||||
// Config holds all push-gateway runtime configuration.
|
||||
type Config struct {
|
||||
Port string
|
||||
JWTSecret string
|
||||
DevMode bool
|
||||
RedisURL string
|
||||
OTLPEndpoint string
|
||||
InternalAPIKey string
|
||||
Port string
|
||||
JWTSecret string
|
||||
DevMode bool
|
||||
RedisURL string
|
||||
OTLPEndpoint string
|
||||
InternalAPIToken string
|
||||
// WebSocket
|
||||
AllowedOrigins []string
|
||||
MaxConnsPerUser int
|
||||
HeartbeatInterval int // seconds
|
||||
// JWT RS256 (via shared-go/jwks, iam /.well-known/jwks.json)
|
||||
JWKSURL string
|
||||
// Kafka
|
||||
KafkaBrokers []string
|
||||
KafkaNotificationTopic string
|
||||
KafkaConsumerGroup string
|
||||
// Instance identity (for Redis SET membership)
|
||||
InstanceID string
|
||||
}
|
||||
|
||||
// devJWTSecret 是 DevMode 下的默认 JWT 密钥(仅用于本地联调,生产必须配置 JWT_SECRET)
|
||||
// devJWTSecret is the default JWT secret used only in DevMode for local
|
||||
// development. Production deployments MUST set JWT_SECRET.
|
||||
const devJWTSecret = "p1-dev-secret-change-in-production"
|
||||
|
||||
// Load 从环境变量加载配置。
|
||||
// 非 DevMode 下若 JWT_SECRET 未配置则 fatal 退出;DevMode 下使用默认密钥并打印 warning。
|
||||
// Load reads configuration from environment variables.
|
||||
//
|
||||
// In DevMode (DEV_MODE=true) JWT_SECRET defaults to a dev value and internal
|
||||
// API token validation is skipped; in production an unset JWT_SECRET or
|
||||
// INTERNAL_API_TOKEN is fatal.
|
||||
func Load() *Config {
|
||||
devMode := getEnv("DEV_MODE", "false") == "true"
|
||||
jwtSecret := getEnv("JWT_SECRET", "")
|
||||
devMode := env.GetBool("DEV_MODE", false)
|
||||
jwtSecret := env.Get("JWT_SECRET", "")
|
||||
if jwtSecret == "" {
|
||||
if devMode {
|
||||
log.Println("warning: JWT_SECRET not set, using dev default (DEV_MODE=true)")
|
||||
@@ -32,20 +54,64 @@ func Load() *Config {
|
||||
}
|
||||
}
|
||||
|
||||
internalToken := env.Get("INTERNAL_API_TOKEN", "")
|
||||
if internalToken == "" && !devMode {
|
||||
log.Fatal("INTERNAL_API_TOKEN must be set in non-dev mode")
|
||||
}
|
||||
|
||||
return &Config{
|
||||
Port: getEnv("PUSH_GATEWAY_PORT", "8081"),
|
||||
JWTSecret: jwtSecret,
|
||||
DevMode: devMode,
|
||||
RedisURL: getEnv("REDIS_URL", ""),
|
||||
OTLPEndpoint: getEnv("OTEL_EXPORTER_OTLP_ENDPOINT", "http://localhost:4318"),
|
||||
InternalAPIKey: getEnv("INTERNAL_API_KEY", ""),
|
||||
Port: env.Get("PUSH_GATEWAY_PORT", "8081"),
|
||||
JWTSecret: jwtSecret,
|
||||
DevMode: devMode,
|
||||
RedisURL: env.Get("REDIS_URL", "redis://localhost:6379/0"),
|
||||
OTLPEndpoint: env.Get("OTEL_EXPORTER_OTLP_ENDPOINT", "localhost:4318"),
|
||||
InternalAPIToken: internalToken,
|
||||
AllowedOrigins: parseOrigins(env.Get("WS_ALLOWED_ORIGINS", "")),
|
||||
MaxConnsPerUser: env.GetInt("MAX_CONNS_PER_USER", 5),
|
||||
HeartbeatInterval: env.GetInt("HEARTBEAT_INTERVAL_SECONDS", 30),
|
||||
JWKSURL: env.Get("JWKS_URL", "http://localhost:50052/.well-known/jwks.json"),
|
||||
KafkaBrokers: parseBrokers(env.Get("KAFKA_BROKERS", "localhost:9092")),
|
||||
KafkaNotificationTopic: env.Get("KAFKA_NOTIFICATION_TOPIC", "edu.notification.requested"),
|
||||
KafkaConsumerGroup: env.Get("KAFKA_CONSUMER_GROUP", "push-gateway"),
|
||||
InstanceID: env.Get("INSTANCE_ID", generateInstanceID()),
|
||||
}
|
||||
}
|
||||
|
||||
// getEnv 读取环境变量,缺失时返回 fallback
|
||||
func getEnv(key, fallback string) string {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
return v
|
||||
// parseOrigins splits a comma-separated list of allowed WebSocket origins.
|
||||
// Empty string yields nil (all origins rejected in production).
|
||||
func parseOrigins(raw string) []string {
|
||||
if raw == "" {
|
||||
return nil
|
||||
}
|
||||
return fallback
|
||||
parts := strings.Split(raw, ",")
|
||||
out := make([]string, 0, len(parts))
|
||||
for _, p := range parts {
|
||||
if trimmed := strings.TrimSpace(p); trimmed != "" {
|
||||
out = append(out, trimmed)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// parseBrokers splits a comma-separated list of Kafka broker addresses.
|
||||
func parseBrokers(raw string) []string {
|
||||
parts := strings.Split(raw, ",")
|
||||
out := make([]string, 0, len(parts))
|
||||
for _, p := range parts {
|
||||
if trimmed := strings.TrimSpace(p); trimmed != "" {
|
||||
out = append(out, trimmed)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// generateInstanceID returns a stable process-unique identifier. Falls back to
|
||||
// the hostname when INSTANCE_ID is not explicitly set, allowing each replica to
|
||||
// be uniquely identifiable in the Redis online-presence SET.
|
||||
func generateInstanceID() string {
|
||||
host, err := os.Hostname()
|
||||
if err != nil || host == "" {
|
||||
return "push-gateway-unknown"
|
||||
}
|
||||
return host
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user