package main import ( "context" "fmt" "log" "net/http" "os" "os/signal" "syscall" "k8s.io/client-go/dynamic" "k8s.io/client-go/rest" "forgejo.riotpiao.com/rock/homelab-frontend/internal/config" "forgejo.riotpiao.com/rock/homelab-frontend/internal/proxy" "forgejo.riotpiao.com/rock/homelab-frontend/internal/server" "forgejo.riotpiao.com/rock/homelab-frontend/internal/serviceadapter" "forgejo.riotpiao.com/rock/homelab-frontend/internal/temporal" ) func main() { // 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) // nil uses default logger dispatcher := serviceadapter.NewDispatcher(registry) // Start ServiceAdapter informer to watch CRDs (8.1 integration) var informerMgr *serviceadapter.InformerManager if kubeConfig, err := rest.InClusterConfig(); err == nil { if dynamicClient, err := dynamic.NewForConfig(kubeConfig); err == nil { if informer, err := serviceadapter.NewInformerManager(dynamicClient, registry, "api"); err == nil { informerMgr = informer if err := informer.Start(context.Background()); err != nil { log.Printf("warning: failed to start ServiceAdapter informer: %v", err) } } else { log.Printf("warning: failed to create ServiceAdapter informer: %v", err) } } else { log.Printf("warning: failed to create dynamic client: %v", err) } } else { log.Printf("warning: not running in cluster, ServiceAdapter CRDs won't be watched") } // Create router that handles health endpoints, X-Service (ServiceAdapter) routing, // temporal endpoints, and passes others to upstream handler router := server.NewRouter(healthChecker, dispatcher, temporalHandler, upstreamHandler) srv.SetHandler(router) // 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) // Stop informer if running if informerMgr != nil { informerMgr.Stop() } // 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) }