CUB-123: integrate gateway, wire PostgreSQL repositories, add SSE streaming
All checks were successful
Dev Build / build-test (pull_request) Successful in 2m23s

- Create repository/ package with pgx-backed CRUD for agents, sessions, tasks, projects
- Define AgentRepo/SessionRepo/TaskRepo/ProjectRepo interfaces
- Update handler to use repository interfaces instead of in-memory stores
- Add SSE broker with GET /api/events endpoint (text/event-stream)
- Add gateway client that polls OpenClaw for agent states
- Add GATEWAY_URL and GATEWAY_POLL_INTERVAL config fields
- Seed 5 demo agents (Otto, Rex, Dex, Hex, Pip) on empty DB
- Update router to wire SSE broker
- All 21 handler tests pass with mock repos
This commit is contained in:
2026-05-08 19:58:06 -04:00
parent 4a2e660a4a
commit e8ced74429
16 changed files with 1207 additions and 109 deletions

View File

@@ -13,9 +13,10 @@ import (
"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"
"code.cubecraftcreations.com/CubeCraft-Creations/Control-Center/go-backend/internal/store"
)
func main() {
@@ -28,32 +29,51 @@ func main() {
}))
slog.SetDefault(logger)
// ── Database (optional until CUB-120 schema is ready) ──────────────────
var pool *db.Pool
if cfg.DatabaseURL != "" {
var err error
pool, err = db.New(cfg.DatabaseURL)
if err != nil {
slog.Warn("database connection failed; running without DB", "error", err)
}
// ── 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)
}
// ── Stores (in-memory for now; PostgreSQL after CUB-120) ────────────────
agentStore := store.NewAgentStore()
sessionStore := store.NewSessionStore()
taskStore := store.NewTaskStore()
projectStore := store.NewProjectStore()
// ── SSE Broker ─────────────────────────────────────────────────────────
broker := handler.NewBroker()
// ── HTTP handler ───────────────────────────────────────────────────────
h := handler.NewHandler(agentStore, sessionStore, taskStore, projectStore)
h := handler.NewHandler(agentRepo, sessionRepo, taskRepo, projectRepo)
// ── Router ─────────────────────────────────────────────────────────────
r := router.New(&router.Dependencies{
Handler: h,
DB: pool,
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),
@@ -78,18 +98,16 @@ func main() {
<-quit
slog.Info("shutting down server...")
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
cancel() // stop gateway polling
if err := srv.Shutdown(ctx); err != nil {
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)
}
if pool != nil {
pool.Close()
}
slog.Info("server exited cleanly")
}