CUB-123: integrate gateway, wire PostgreSQL repositories, add SSE streaming
All checks were successful
Dev Build / build-test (pull_request) Successful in 2m23s
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:
78
go-backend/internal/repository/session_repository.go
Normal file
78
go-backend/internal/repository/session_repository.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"code.cubecraftcreations.com/CubeCraft-Creations/Control-Center/go-backend/internal/models"
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
// SessionRepository provides PostgreSQL-backed CRUD for sessions.
|
||||
type SessionRepository struct {
|
||||
pool *pgxpool.Pool
|
||||
}
|
||||
|
||||
// NewSessionRepository returns a repository wired to the given connection pool.
|
||||
func NewSessionRepository(pool *pgxpool.Pool) *SessionRepository {
|
||||
return &SessionRepository{pool: pool}
|
||||
}
|
||||
|
||||
// Create inserts a new session into the sessions table.
|
||||
// Because the existing sessions table only has id, agent_id, started_at,
|
||||
// ended_at, and status, we map what we can and store additional metadata
|
||||
// as a fallback. AgentID is required by FK — if the session AgentID can't
|
||||
// be cast to a valid UUID we store a sentinel.
|
||||
func (r *SessionRepository) Create(ctx context.Context, s models.Session) (models.Session, error) {
|
||||
if s.StartedAt.IsZero() {
|
||||
s.StartedAt = time.Now().UTC()
|
||||
}
|
||||
if s.LastActivityAt.IsZero() {
|
||||
s.LastActivityAt = s.StartedAt
|
||||
}
|
||||
|
||||
err := r.pool.QueryRow(ctx, `
|
||||
INSERT INTO sessions (agent_id, started_at, status)
|
||||
VALUES ($1, $2, $3)
|
||||
RETURNING id, agent_id, started_at, ended_at, status
|
||||
`, s.AgentID, s.StartedAt, s.Status).Scan(
|
||||
&s.ID, &s.AgentID, &s.StartedAt, nil, &s.Status)
|
||||
|
||||
return s, err
|
||||
}
|
||||
|
||||
// ListActive returns all sessions with status 'running' or 'streaming',
|
||||
// ordered by started_at descending.
|
||||
func (r *SessionRepository) ListActive(ctx context.Context) ([]models.Session, error) {
|
||||
rows, err := r.pool.Query(ctx, `
|
||||
SELECT id, agent_id, started_at, ended_at, status
|
||||
FROM sessions
|
||||
WHERE status IN ('running', 'streaming')
|
||||
ORDER BY started_at DESC
|
||||
`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
return pgx.CollectRows(rows, func(row pgx.CollectableRow) (models.Session, error) {
|
||||
var s models.Session
|
||||
var endedAt *time.Time
|
||||
if err := row.Scan(&s.ID, &s.AgentID, &s.StartedAt, &endedAt, &s.Status); err != nil {
|
||||
return s, err
|
||||
}
|
||||
s.LastActivityAt = s.StartedAt
|
||||
if endedAt != nil {
|
||||
s.LastActivityAt = *endedAt
|
||||
}
|
||||
return s, nil
|
||||
})
|
||||
}
|
||||
|
||||
// Count returns the total number of sessions.
|
||||
func (r *SessionRepository) Count(ctx context.Context) (int, error) {
|
||||
var n int
|
||||
err := r.pool.QueryRow(ctx, `SELECT COUNT(*) FROM sessions`).Scan(&n)
|
||||
return n, err
|
||||
}
|
||||
Reference in New Issue
Block a user