- pipeline: 8-stage order, NextStage (testing skippable), gate pure functions (agents may never advance; only in_progress->awaiting_review) - tasks: domain layer over cc_* tables — CRUD, claim/heartbeat/release leases, advance-request, decisions with strict suggestion validation, stage transitions, external submit, stage settings, overview, lease reaping - server: agent + console routes, SSE broker broadcasting every event insert, 60s lease reaper goroutine, 1MB body limit, domain error mapping - archtest: allowlist controlcenter/* and engineeringdashboard packages
52 lines
1.4 KiB
Go
52 lines
1.4 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"log/slog"
|
|
"net/http"
|
|
|
|
"chat-agent-system/internal/controlcenter/tasks"
|
|
)
|
|
|
|
const maxBodyBytes = 1 << 20
|
|
|
|
func decodeJSON(w http.ResponseWriter, r *http.Request, v any) bool {
|
|
err := json.NewDecoder(io.LimitReader(r.Body, maxBodyBytes)).Decode(v)
|
|
if errors.Is(err, io.EOF) {
|
|
return true // empty body is fine for endpoints with all-optional fields
|
|
}
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, "invalid json body: "+err.Error())
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func writeDomainError(w http.ResponseWriter, err error) {
|
|
switch {
|
|
case errors.Is(err, tasks.ErrNotFound):
|
|
writeError(w, http.StatusNotFound, err.Error())
|
|
case errors.Is(err, tasks.ErrConflict), errors.Is(err, tasks.ErrBadState):
|
|
writeError(w, http.StatusConflict, err.Error())
|
|
case errors.Is(err, tasks.ErrBadInput):
|
|
writeError(w, http.StatusBadRequest, err.Error())
|
|
default:
|
|
slog.Error("handler", "err", err)
|
|
writeError(w, http.StatusInternalServerError, "internal error")
|
|
}
|
|
}
|
|
|
|
// appendEvent is the single write path for cc_task_events; every insert is
|
|
// broadcast to SSE subscribers.
|
|
func (s *Server) appendEvent(ctx context.Context, taskID, agent, phase, message string, payload any) {
|
|
e, err := tasks.AppendEvent(ctx, s.store, taskID, agent, phase, message, payload)
|
|
if err != nil {
|
|
slog.Error("append event", "task", taskID, "phase", phase, "err", err)
|
|
return
|
|
}
|
|
s.broker.Publish(e)
|
|
}
|