// Package main provides the API Gateway service entry point. package main import ( "context" "fmt" "net/http" "os" "time" "git.dcentral.systems/toolz/goplt/internal/client" "git.dcentral.systems/toolz/goplt/internal/di" "git.dcentral.systems/toolz/goplt/internal/health" "git.dcentral.systems/toolz/goplt/internal/metrics" "git.dcentral.systems/toolz/goplt/internal/server" "git.dcentral.systems/toolz/goplt/pkg/config" "git.dcentral.systems/toolz/goplt/pkg/errorbus" "git.dcentral.systems/toolz/goplt/pkg/logger" "git.dcentral.systems/toolz/goplt/pkg/registry" "git.dcentral.systems/toolz/goplt/services/gateway" "go.opentelemetry.io/otel/trace" "go.uber.org/fx" ) func main() { // Create DI container with core kernel services container := di.NewContainer( // Invoke lifecycle hooks fx.Invoke(di.RegisterLifecycleHooks), // Create API Gateway fx.Invoke(func( cfg config.ConfigProvider, log logger.Logger, healthRegistry *health.Registry, metricsRegistry *metrics.Metrics, errorBus errorbus.ErrorPublisher, tracer trace.TracerProvider, serviceRegistry registry.ServiceRegistry, clientFactory *client.ServiceClientFactory, lc fx.Lifecycle, ) { // Create HTTP server using server foundation srv, err := server.NewServer(cfg, log, healthRegistry, metricsRegistry, errorBus, tracer) if err != nil { log.Error("Failed to create API Gateway server", logger.Error(err), ) os.Exit(1) } // Setup gateway routes gateway, err := gateway.NewGateway(cfg, log, clientFactory, serviceRegistry) if err != nil { log.Error("Failed to create API Gateway", logger.Error(err), ) os.Exit(1) } gateway.SetupRoutes(srv.Router()) // Determine port and host for registration gatewayPort := cfg.GetInt("gateway.port") if gatewayPort == 0 { gatewayPort = cfg.GetInt("server.port") if gatewayPort == 0 { gatewayPort = 8080 } } // In Docker, always use the Docker service name for health checks // Consul (also in Docker) needs to reach the service via Docker DNS gatewayHost := cfg.GetString("gateway.host") if os.Getenv("ENVIRONMENT") == "production" || os.Getenv("DOCKER") == "true" { gatewayHost = "api-gateway" // Docker service name - required for Consul health checks } else if gatewayHost == "" { gatewayHost = cfg.GetString("server.host") if gatewayHost == "" || gatewayHost == "0.0.0.0" { gatewayHost = "localhost" // Local development } } serviceInstance := ®istry.ServiceInstance{ ID: fmt.Sprintf("api-gateway-%d", os.Getpid()), Name: "api-gateway", Address: gatewayHost, Port: gatewayPort, Tags: []string{"gateway", "http"}, Metadata: map[string]string{ "version": "1.0.0", "protocol": "http", }, } // Register lifecycle hooks lc.Append(fx.Hook{ OnStart: func(ctx context.Context) error { // Register with service registry if err := serviceRegistry.Register(ctx, serviceInstance); err != nil { log.Warn("Failed to register API Gateway with service registry", logger.Error(err), ) // Continue anyway - gateway can work without registry } else { log.Info("API Gateway registered with service registry", logger.String("service_id", serviceInstance.ID), ) } // Start HTTP server addr := fmt.Sprintf("%s:%d", cfg.GetString("server.host"), gatewayPort) log.Info("API Gateway starting", logger.String("addr", addr), ) errChan := make(chan error, 1) go func() { if err := srv.Start(); err != nil && err != http.ErrServerClosed { log.Error("API Gateway server failed", logger.String("error", err.Error()), ) errChan <- err } }() // Wait a short time to detect immediate binding errors select { case err := <-errChan: return fmt.Errorf("API Gateway failed to start: %w", err) case <-time.After(500 * time.Millisecond): log.Info("API Gateway started successfully", logger.String("addr", addr), ) return nil } }, OnStop: func(ctx context.Context) error { // Deregister from service registry if err := serviceRegistry.Deregister(ctx, serviceInstance.ID); err != nil { log.Warn("Failed to deregister API Gateway from service registry", logger.Error(err), ) } else { log.Info("API Gateway deregistered from service registry") } // Shutdown HTTP server return srv.Shutdown(ctx) }, }) }), ) // Create root context ctx := context.Background() // Start the application if err := container.Start(ctx); err != nil { log := logger.GetGlobalLogger() if log != nil { log.Error("Failed to start API Gateway", logger.Error(err), ) } else { fmt.Fprintf(os.Stderr, "Failed to start API Gateway: %v\n", err) } os.Exit(1) } }