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
79 lines
2.3 KiB
Go
79 lines
2.3 KiB
Go
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
|
|
}
|