package httpapi import ( "encoding/json" "errors" "net/http" "time" "git.118im.com/im_team/im_workbench/internal/auth" "git.118im.com/im_team/im_workbench/internal/store" ) const ( reasonMalformedRequest = "MALFORMED_REQUEST" reasonMethodNotAllowed = "METHOD_NOT_ALLOWED" reasonAuthRequired = "AUTH_REQUIRED" reasonForbidden = "FORBIDDEN" reasonNotFound = "RESOURCE_NOT_FOUND" reasonConflict = "CONFLICT" reasonValidationFailed = "VALIDATION_FAILED" reasonInternal = "INTERNAL" ) type apiError struct { Code int `json:"code"` Reason string `json:"reason"` Message string `json:"message"` Details map[string]any `json:"details,omitempty"` RequestID string `json:"request_id,omitempty"` ServerTime string `json:"server_time"` } func writeSuccess(w http.ResponseWriter, r *http.Request, data any) { writeJSON(w, r, http.StatusOK, map[string]any{ "code": 0, "data": data, "request_id": requestID(r), "server_time": time.Now().UTC().Format(time.RFC3339Nano), }) } func writeError(w http.ResponseWriter, r *http.Request, status, code int, reason, message string, details map[string]any) { writeJSON(w, r, status, apiError{ Code: code, Reason: reason, Message: message, Details: details, RequestID: requestID(r), ServerTime: time.Now().UTC().Format(time.RFC3339Nano), }) } func writeStoreError(w http.ResponseWriter, r *http.Request, err error) { switch { case errors.Is(err, ErrNotFound), errors.Is(err, store.ErrNotFound): writeError(w, r, http.StatusNotFound, 30004, reasonNotFound, "resource not found", nil) case errors.Is(err, ErrForbidden), errors.Is(err, store.ErrForbidden), errors.Is(err, auth.ErrForbidden): writeError(w, r, http.StatusForbidden, 30041, reasonForbidden, "forbidden", nil) case errors.Is(err, ErrConflict), errors.Is(err, store.ErrConflict): writeError(w, r, http.StatusConflict, 30040, reasonConflict, "conflict", nil) case errors.Is(err, store.ErrPolicy): writeError(w, r, http.StatusBadRequest, 30000, reasonValidationFailed, "validation failed", nil) default: writeError(w, r, http.StatusInternalServerError, 90000, reasonInternal, "internal error", nil) } } func writeJSON(w http.ResponseWriter, r *http.Request, status int, value any) { w.Header().Set("Content-Type", "application/json; charset=utf-8") w.Header().Set("X-IM-Schema", firstNonEmpty(r.Header.Get("X-IM-Schema"), "1")) if rid := requestID(r); rid != "" { w.Header().Set("X-Request-ID", rid) } w.WriteHeader(status) _ = json.NewEncoder(w).Encode(value) } func requestID(r *http.Request) string { return r.Header.Get("X-Request-ID") } func firstNonEmpty(values ...string) string { for _, value := range values { if value != "" { return value } } return "" } func firstNonNilInt64(values ...*int64) *int64 { for _, value := range values { if value != nil { return value } } return nil }