package main import ( "context" "errors" "log" "net/http" "os" "os/signal" "syscall" "time" "github.com/redis/go-redis/v9" "app-deploy-platform/backend/internal/bootstrap" "app-deploy-platform/backend/internal/config" "app-deploy-platform/backend/internal/database" transport "app-deploy-platform/backend/internal/http" "app-deploy-platform/backend/internal/orchestrator" "app-deploy-platform/backend/internal/queue" "app-deploy-platform/backend/internal/service" ) func main() { cfg := config.Load() db, err := database.Open(cfg) if err != nil { log.Fatalf("open database: %v", err) } var redisClient *redis.Client if cfg.Cache.RedisAddr != "" { redisClient = redis.NewClient(&redis.Options{ Addr: cfg.Cache.RedisAddr, Password: cfg.Cache.RedisPassword, }) if err := redisClient.Ping(context.Background()).Err(); err != nil { log.Printf("redis unavailable, continue without cache: %v", err) redisClient = nil } } jobQueue, err := queue.New(cfg) if err != nil { log.Printf("queue init failed, fallback to memory queue: %v", err) jobQueue = queue.NewMemoryQueue(128) } defer jobQueue.Close() deployExecutor := orchestrator.NewDeployCommandExecutor( cfg.Deploy.OperatorScript, cfg.Deploy.ConfigPath, cfg.Deploy.ScriptWorkDir, ) buildExecutor := orchestrator.NewBuildCommandExecutor( cfg.Build.OperatorScript, cfg.Deploy.ConfigPath, cfg.Build.ScriptWorkDir, ) manager := service.NewManager(db, jobQueue, deployExecutor, buildExecutor, redisClient, cfg) if err := bootstrap.Seed(context.Background(), db, cfg); err != nil { log.Fatalf("bootstrap seed failed: %v", err) } appCtx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) defer cancel() go manager.StartConsumer(appCtx) go manager.StartHealthProbe(appCtx) router := transport.NewRouter(cfg, manager) server := &http.Server{ Addr: ":" + cfg.Platform.HTTPPort, Handler: router, ReadHeaderTimeout: 10 * time.Second, } go func() { log.Printf("app-deploy-platform backend listening on %s", server.Addr) if err := server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { log.Fatalf("listen: %v", err) } }() <-appCtx.Done() shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 15*time.Second) defer shutdownCancel() if err := server.Shutdown(shutdownCtx); err != nil { log.Printf("server shutdown: %v", err) } }