// Command server starts the Control Center Go backend API server. package main import ( "context" "fmt" "log/slog" "net/http" "os" "os/signal" "syscall" "time" "code.cubecraftcreations.com/CubeCraft-Creations/Control-Center/go-backend/internal/config" "code.cubecraftcreations.com/CubeCraft-Creations/Control-Center/go-backend/internal/db" "code.cubecraftcreations.com/CubeCraft-Creations/Control-Center/go-backend/internal/gateway" "code.cubecraftcreations.com/CubeCraft-Creations/Control-Center/go-backend/internal/handler" "code.cubecraftcreations.com/CubeCraft-Creations/Control-Center/go-backend/internal/repository" "code.cubecraftcreations.com/CubeCraft-Creations/Control-Center/go-backend/internal/router" ) func main() { // ── Configuration ────────────────────────────────────────────────────── cfg := config.Load() // ── Logging ──────────────────────────────────────────────────────────── logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ Level: parseLogLevel(cfg.LogLevel), })) slog.SetDefault(logger) // ── Database ─────────────────────────────────────────────────────────── pool, err := db.New(cfg.DatabaseURL) if err != nil { slog.Error("database connection failed", "error", err) os.Exit(1) } defer pool.Close() // ── Repositories (PostgreSQL-backed) ─────────────────────────────────── agentRepo := repository.NewAgentRepository(pool.Pool) sessionRepo := repository.NewSessionRepository(pool.Pool) taskRepo := repository.NewTaskRepository(pool.Pool) projectRepo := repository.NewProjectRepository(pool.Pool) // ── Seed demo agents on first boot ───────────────────────────────────── if err := gateway.SeedDemoAgents(context.Background(), agentRepo); err != nil { slog.Error("seed demo agents failed", "error", err) os.Exit(1) } // ── SSE Broker ───────────────────────────────────────────────────────── broker := handler.NewBroker() // ── HTTP handler ─────────────────────────────────────────────────────── h := handler.NewHandler(agentRepo, sessionRepo, taskRepo, projectRepo) // ── Router ───────────────────────────────────────────────────────────── r := router.New(&router.Dependencies{ Handler: h, Pool: pool, CORSOrigin: cfg.CORSOrigin, Broker: broker, }) // ── Gateway client (polls OpenClaw for agent states) ─────────────────── gwClient := gateway.NewClient(gateway.Config{ URL: cfg.GatewayURL, PollInterval: cfg.GatewayPollInterval, }, agentRepo, broker) ctx, cancel := context.WithCancel(context.Background()) defer cancel() go gwClient.Start(ctx) // ── Server ───────────────────────────────────────────────────────────── srv := &http.Server{ Addr: fmt.Sprintf(":%d", cfg.Port), Handler: r, ReadTimeout: 10 * time.Second, WriteTimeout: 15 * time.Second, IdleTimeout: 60 * time.Second, } // Graceful shutdown quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) go func() { slog.Info("server starting", "port", cfg.Port, "env", cfg.Environment) if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { slog.Error("server failed", "error", err) os.Exit(1) } }() <-quit slog.Info("shutting down server...") cancel() // stop gateway polling shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 15*time.Second) defer shutdownCancel() if err := srv.Shutdown(shutdownCtx); err != nil { slog.Error("server forced to shutdown", "error", err) os.Exit(1) } slog.Info("server exited cleanly") } func parseLogLevel(level string) slog.Level { switch level { case "debug": return slog.LevelDebug case "warn": return slog.LevelWarn case "error": return slog.LevelError default: return slog.LevelInfo } }