Remove production *pgxpool.Pool from Server, centralize domain→HTTP status mapping, and expose Runtime ports for task-store readiness, duty heal, and artifact vault registry. Allow app→docduty for heal wiring.
80 lines
2.3 KiB
Go
80 lines
2.3 KiB
Go
package server
|
|
|
|
import (
|
|
"errors"
|
|
"net/http"
|
|
|
|
"chat-agent-system/internal/docgov"
|
|
"chat-agent-system/internal/projectworkflow"
|
|
)
|
|
|
|
// domainHTTPStatus is the single adapter that maps known domain sentinel /
|
|
// typed errors to HTTP status codes. Callers that need a different default
|
|
// (400 vs 500) must apply it when ok is false.
|
|
//
|
|
// This is the only production place in internal/server that should match
|
|
// docgov.Err* (and closely related domain sentinels) for HTTP status selection.
|
|
// handlers.go must call this mapper (or writeDocGovError / writeTaskError)
|
|
// instead of branching on docgov.Err* directly.
|
|
func domainHTTPStatus(err error) (status int, ok bool) {
|
|
if err == nil {
|
|
return 0, false
|
|
}
|
|
var busy *docgov.DocumentBusyError
|
|
if errors.As(err, &busy) {
|
|
return http.StatusConflict, true
|
|
}
|
|
var ce *docgov.ContentError
|
|
if errors.As(err, &ce) {
|
|
if ce.Code == docgov.ErrCodeDocumentNotFound {
|
|
return http.StatusNotFound, true
|
|
}
|
|
return http.StatusConflict, true
|
|
}
|
|
switch {
|
|
case errors.Is(err, docgov.ErrDocumentBusy),
|
|
errors.Is(err, docgov.ErrDocumentLocked),
|
|
errors.Is(err, docgov.ErrForceEditNotAllowed),
|
|
errors.Is(err, docgov.ErrLeaseNotHeld),
|
|
errors.Is(err, docgov.ErrLeaseExpired):
|
|
return http.StatusConflict, true
|
|
case errors.Is(err, docgov.ErrDocumentNotFound),
|
|
errors.Is(err, docgov.ErrProposalNotFound):
|
|
return http.StatusNotFound, true
|
|
case errors.Is(err, docgov.ErrUnauthorizedActor):
|
|
return http.StatusForbidden, true
|
|
case errors.Is(err, projectworkflow.ErrPrerequisiteNotMet):
|
|
return http.StatusBadRequest, true
|
|
default:
|
|
return 0, false
|
|
}
|
|
}
|
|
|
|
func writeTaskError(w http.ResponseWriter, err error, payload any) {
|
|
status := http.StatusInternalServerError
|
|
if mapped, ok := domainHTTPStatus(err); ok {
|
|
status = mapped
|
|
}
|
|
if payload != nil {
|
|
writeJSONResponse(w, status, payload)
|
|
return
|
|
}
|
|
writeSafeError(w, nil, status, err.Error())
|
|
}
|
|
|
|
func writeDocGovError(w http.ResponseWriter, err error) {
|
|
var busy *docgov.DocumentBusyError
|
|
if errors.As(err, &busy) {
|
|
writeJSONResponse(w, http.StatusConflict, map[string]any{
|
|
"error": "document_busy",
|
|
"detail": busy,
|
|
})
|
|
return
|
|
}
|
|
status := http.StatusBadRequest
|
|
if mapped, ok := domainHTTPStatus(err); ok {
|
|
status = mapped
|
|
}
|
|
writeSafeError(w, nil, status, err.Error())
|
|
}
|