84 lines
2.5 KiB
Go
84 lines
2.5 KiB
Go
package server
|
|
|
|
import (
|
|
"errors"
|
|
"log/slog"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"chat-agent-system/internal/controlcenter/audit"
|
|
)
|
|
|
|
// SetAudit attaches the audit runner used by the /api/v1/audits routes.
|
|
func (s *Server) SetAudit(r *audit.Runner) { s.audit = r }
|
|
|
|
func (s *Server) handleAuditRunList(w http.ResponseWriter, r *http.Request) {
|
|
limit, _ := strconv.Atoi(r.URL.Query().Get("limit"))
|
|
out, err := audit.ListRuns(r.Context(), s.store, limit)
|
|
if err != nil {
|
|
writeAuditError(w, err)
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, out)
|
|
}
|
|
|
|
func (s *Server) handleAuditFindingList(w http.ResponseWriter, r *http.Request) {
|
|
q := r.URL.Query()
|
|
limit, _ := strconv.Atoi(q.Get("limit"))
|
|
out, err := audit.ListFindings(r.Context(), s.store, q.Get("status"), q.Get("severity"), limit)
|
|
if err != nil {
|
|
writeAuditError(w, err)
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, out)
|
|
}
|
|
|
|
// handleAuditRun triggers a manual run in the background and returns the run
|
|
// id immediately; a second trigger while one is running gets 409.
|
|
func (s *Server) handleAuditRun(w http.ResponseWriter, r *http.Request) {
|
|
if s.audit == nil {
|
|
writeError(w, http.StatusServiceUnavailable, "audit runner not configured")
|
|
return
|
|
}
|
|
runID, err := audit.StartManual(r.Context(), s.audit)
|
|
if err != nil {
|
|
writeAuditError(w, err)
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusAccepted, map[string]any{"run_id": runID})
|
|
}
|
|
|
|
func (s *Server) handleAuditFindingConvert(w http.ResponseWriter, r *http.Request) {
|
|
fi, err := audit.ConvertFinding(r.Context(), s.store, r.PathValue("id"))
|
|
if err != nil {
|
|
writeAuditError(w, err)
|
|
return
|
|
}
|
|
slog.Info("audit finding converted", "finding", fi.ID, "task", fi.TaskID)
|
|
writeJSON(w, http.StatusOK, fi)
|
|
}
|
|
|
|
func (s *Server) handleAuditFindingDismiss(w http.ResponseWriter, r *http.Request) {
|
|
id := r.PathValue("id")
|
|
if err := audit.DismissFinding(r.Context(), s.store, id); err != nil {
|
|
writeAuditError(w, err)
|
|
return
|
|
}
|
|
slog.Info("audit finding dismissed", "finding", id)
|
|
writeJSON(w, http.StatusOK, map[string]string{"status": "dismissed"})
|
|
}
|
|
|
|
func writeAuditError(w http.ResponseWriter, err error) {
|
|
switch {
|
|
case errors.Is(err, audit.ErrAlreadyRunning):
|
|
writeError(w, http.StatusConflict, err.Error())
|
|
case errors.Is(err, audit.ErrNotFound):
|
|
writeError(w, http.StatusNotFound, err.Error())
|
|
case errors.Is(err, audit.ErrBadState):
|
|
writeError(w, http.StatusConflict, err.Error())
|
|
default:
|
|
slog.Error("audit handler", "err", err)
|
|
writeError(w, http.StatusInternalServerError, "internal error")
|
|
}
|
|
}
|