package api import ( "encoding/json" "log/slog" "net/http" "git.fabledsword.com/bvandeusen/minstrel/internal/apierror" ) // errorBody / errorPayload preserve the existing wire envelope: // // {"error": {"code": "...", "message": "..."}} // // Kept separate from the Subsonic envelope so native clients don't have // to parse two shapes. type errorBody struct { Error errorPayload `json:"error"` } type errorPayload struct { Code string `json:"code"` Message string `json:"message"` } // writeErr emits the failure envelope for err. If err is or wraps // *apierror.Error, its Status/Code/Message drive the response; // otherwise it's mapped to a 500 server_error. func writeErr(w http.ResponseWriter, err error) { apiErr := apierror.From(err) w.Header().Set("Content-Type", "application/json") w.WriteHeader(apiErr.Status) _ = json.NewEncoder(w).Encode(errorBody{Error: errorPayload{ Code: apiErr.Code, Message: apiErr.Message, }}) } // writeErrWithLog logs the error at Error level and writes the response. // Use for 500-class errors where the operator needs the cause in logs. func writeErrWithLog(w http.ResponseWriter, logger *slog.Logger, msg string, err error) { if logger != nil { logger.Error(msg, "err", err) } writeErr(w, err) } // writeJSON emits a 2xx response with the given body JSON-encoded. Mirrors // writeErr's shape so handlers have one shape for success and one for // failure. func writeJSON(w http.ResponseWriter, status int, body any) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) _ = json.NewEncoder(w).Encode(body) }