package server import ( "encoding/json" "errors" "log/slog" "net/http" "ddc/internal/diskspace" "ddc/internal/files" "ddc/internal/jobs" "ddc/internal/preferences" ) type errorResponse struct { Error string `json:"error"` RequestID string `json:"requestId,omitempty"` } func writeJSON(writer http.ResponseWriter, status int, payload any) { writer.Header().Set("Content-Type", "application/json; charset=utf-8") writer.WriteHeader(status) if err := json.NewEncoder(writer).Encode(payload); err != nil { slog.Debug("could not write response", "error", err) } } func writeError(writer http.ResponseWriter, status int, message string) { writeJSON(writer, status, errorResponse{Error: message, RequestID: writer.Header().Get("X-Request-ID")}) } func writeFailure(writer http.ResponseWriter, request *http.Request, err error) { status := statusFor(err) if status != http.StatusInternalServerError { writeError(writer, status, err.Error()) return } requestID := requestIDFrom(request) slog.Error("request failed", "request_id", requestID, "method", request.Method, "path", request.URL.Path, "error", err, ) writeError(writer, status, internalErrorMessage(requestID)) } func decodeBody(writer http.ResponseWriter, request *http.Request, target any) bool { decoder := json.NewDecoder(request.Body) decoder.DisallowUnknownFields() err := decoder.Decode(target) if err == nil { return true } var tooLarge *http.MaxBytesError if errors.As(err, &tooLarge) { writeError(writer, http.StatusRequestEntityTooLarge, "request body is too large") return false } writeError(writer, http.StatusBadRequest, "invalid request body: "+err.Error()) return false } func statusFor(err error) int { switch { case errors.Is(err, jobs.ErrJobNotFound): return http.StatusNotFound case errors.Is(err, jobs.ErrJobBusy), errors.Is(err, files.ErrDestinationExists): return http.StatusConflict case errors.Is(err, jobs.ErrInvalidRequest), errors.Is(err, preferences.ErrInvalid): return http.StatusBadRequest case errors.Is(err, diskspace.ErrInsufficientSpace): return http.StatusInsufficientStorage default: return http.StatusInternalServerError } }