package main import ( "context" "fmt" "log" "net/http" "os" "os/signal" "syscall" "forgejo.riotpiao.com/rock/homelab-frontend/internal/config" "forgejo.riotpiao.com/rock/homelab-frontend/internal/proxy" "forgejo.riotpiao.com/rock/homelab-frontend/internal/s3" "forgejo.riotpiao.com/rock/homelab-frontend/internal/server" "forgejo.riotpiao.com/rock/homelab-frontend/internal/serviceadapter" "forgejo.riotpiao.com/rock/homelab-frontend/internal/temporal" "forgejo.riotpiao.com/rock/homelab-frontend/internal/tracing" ) func main() { ctx := context.Background() // Initialize OpenTelemetry tracing tracingCfg := tracing.DefaultConfig() shutdownTracer, err := tracing.Init(ctx, tracingCfg) if err != nil { log.Printf("warning: failed to initialize tracing: %v", err) } else { log.Printf("tracing initialized: service=%s endpoint=%s", tracingCfg.ServiceName, tracingCfg.OTLPEndpoint) defer func() { if err := shutdownTracer(ctx); err != nil { log.Printf("error shutting down tracer: %v", err) } }() } // Load configuration cfg, err := config.Load() if err != nil { fmt.Fprintf(os.Stderr, "failed to load config: %v\n", err) os.Exit(1) } // Determine if auth is enabled by checking if any route requires it authEnabled := false for _, route := range cfg.Routes { if route.Upstream.AuthRequired { authEnabled = true break } } // Create the reverse proxy handler that routes requests based on configuration upstreamHandler := proxy.New(cfg) // Create the Temporal workflow handler // Temporal server address can be configured via environment variable temporalHostPort := os.Getenv("TEMPORAL_HOST_PORT") if temporalHostPort == "" { temporalHostPort = "localhost:7233" } log.Printf("Temporal server: %s", temporalHostPort) temporalHandler := temporal.NewHandler(temporalHostPort) // Create server with health checker srv := server.New(cfg.ListenAddr, cfg.ShutdownTimeout, nil) // Initialize health checker with config validity and auth status healthChecker := server.NewHealthChecker(true, authEnabled) srv.SetHealthChecker(healthChecker) // Create ServiceAdapter registry and dispatcher (phase 8) registry := serviceadapter.NewRegistry(nil) for _, a := range cfg.Adapters { _ = registry.Add(a) } log.Printf("%d service adapters loaded", registry.Count()) dispatcher := serviceadapter.NewDispatcher(registry) // Create S3/SigV4 handler for MinIO access via JWT var s3Handler http.Handler minioEndpoint := os.Getenv("MINIO_ENDPOINT") if minioEndpoint == "" { minioEndpoint = "https://minio-api.riotpiao.com" } minioAccessKey := os.Getenv("MINIO_ACCESS_KEY") minioSecretKey := os.Getenv("MINIO_SECRET_KEY") if minioAccessKey != "" && minioSecretKey != "" { s3h, err := s3.NewSigV4Handler(minioEndpoint, minioAccessKey, minioSecretKey, upstreamHandler.(*proxy.Handler).JWTValidator()) if err != nil { log.Printf("warning: failed to create S3 handler: %v", err) s3Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.Error(w, "S3 handler not configured", http.StatusServiceUnavailable) }) } else { s3Handler = s3h log.Printf("S3/SigV4 handler initialized for MinIO at %s", minioEndpoint) } } else { log.Printf("S3/SigV4 handler disabled: MINIO_ACCESS_KEY or MINIO_SECRET_KEY not set") s3Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.Error(w, "S3 handler not configured", http.StatusServiceUnavailable) }) } // Create router that handles health endpoints, X-Service (ServiceAdapter) routing, // Temporal workflow endpoints, S3/SigV4 endpoints, and passes others to upstream handler router := server.NewRouter(healthChecker, dispatcher, temporalHandler, s3Handler, upstreamHandler) // Wrap router with tracing middleware tracedRouter := tracing.Middleware(router) srv.SetHandler(tracedRouter) // Set up signal handling sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGINT) // Start server in a goroutine var serverErr error go func() { log.Printf("gateway listening on %s", srv.Addr()) serverErr = srv.ListenAndServe() if serverErr != nil && serverErr != http.ErrServerClosed { log.Printf("server error: %v", serverErr) } }() // Wait for shutdown signal sig := <-sigChan log.Printf("received signal: %v", sig) // Gracefully shutdown the server if err := srv.Shutdown(context.Background()); err != nil { fmt.Fprintf(os.Stderr, "shutdown error: %v\n", err) os.Exit(1) } log.Printf("gateway shutdown complete") os.Exit(0) }