104 lines
3.0 KiB
Go
104 lines
3.0 KiB
Go
package handler
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
|
|
"github.com/edgeai/gateway/pkg/api"
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
// ErrorCode constants.
|
|
const (
|
|
ErrAuthFailed = "AUTH_FAILED"
|
|
ErrPermissionDenied = "PERMISSION_DENIED"
|
|
ErrPolicyBlocked = "POLICY_BLOCKED"
|
|
ErrRateLimited = "RATE_LIMITED"
|
|
ErrQuotaExceeded = "QUOTA_EXCEEDED"
|
|
ErrQueueFull = "QUEUE_FULL"
|
|
ErrInvalidRequest = "INVALID_REQUEST"
|
|
ErrContextTooLarge = "CONTEXT_TOO_LARGE"
|
|
ErrQueueTimeout = "QUEUE_TIMEOUT"
|
|
ErrFirstTokenTimeout = "FIRST_TOKEN_TIMEOUT"
|
|
ErrInferenceTimeout = "INFERENCE_TIMEOUT"
|
|
ErrRequestCancelled = "REQUEST_CANCELLED"
|
|
ErrModelUnavailable = "MODEL_UNAVAILABLE"
|
|
ErrResourceExhausted = "RESOURCE_EXHAUSTED"
|
|
ErrInternalError = "INTERNAL_ERROR"
|
|
)
|
|
|
|
// httpStatusForCode maps error codes to HTTP status codes.
|
|
var httpStatusForCode = map[string]int{
|
|
ErrAuthFailed: http.StatusUnauthorized,
|
|
ErrPermissionDenied: http.StatusForbidden,
|
|
ErrPolicyBlocked: http.StatusForbidden,
|
|
ErrRateLimited: http.StatusTooManyRequests,
|
|
ErrQuotaExceeded: http.StatusTooManyRequests,
|
|
ErrQueueFull: http.StatusTooManyRequests,
|
|
ErrInvalidRequest: http.StatusBadRequest,
|
|
ErrContextTooLarge: http.StatusBadRequest,
|
|
ErrQueueTimeout: http.StatusRequestTimeout,
|
|
ErrFirstTokenTimeout: http.StatusRequestTimeout,
|
|
ErrInferenceTimeout: http.StatusRequestTimeout,
|
|
ErrRequestCancelled: http.StatusConflict,
|
|
ErrModelUnavailable: http.StatusServiceUnavailable,
|
|
ErrResourceExhausted: http.StatusServiceUnavailable,
|
|
ErrInternalError: http.StatusInternalServerError,
|
|
}
|
|
|
|
// GatewayError represents a structured error with code, message, and request ID.
|
|
type GatewayError struct {
|
|
Code string
|
|
Message string
|
|
RequestID string
|
|
}
|
|
|
|
func (e *GatewayError) Error() string {
|
|
return e.Message
|
|
}
|
|
|
|
// NewGatewayError creates a GatewayError with a generated request ID.
|
|
func NewGatewayError(code, message string) *GatewayError {
|
|
return &GatewayError{
|
|
Code: code,
|
|
Message: message,
|
|
RequestID: uuid.New().String(),
|
|
}
|
|
}
|
|
|
|
// NewGatewayErrorWithID creates a GatewayError with an existing request ID.
|
|
func NewGatewayErrorWithID(code, message, requestID string) *GatewayError {
|
|
return &GatewayError{
|
|
Code: code,
|
|
Message: message,
|
|
RequestID: requestID,
|
|
}
|
|
}
|
|
|
|
// WriteError writes a structured error response.
|
|
func WriteError(w http.ResponseWriter, err *GatewayError) {
|
|
status, ok := httpStatusForCode[err.Code]
|
|
if !ok {
|
|
status = http.StatusInternalServerError
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(status)
|
|
|
|
resp := api.ErrorResponse{
|
|
Error: api.ErrorBody{
|
|
Code: err.Code,
|
|
Message: err.Message,
|
|
RequestID: err.RequestID,
|
|
},
|
|
}
|
|
json.NewEncoder(w).Encode(resp)
|
|
}
|
|
|
|
// WriteJSON writes a JSON response with the given status code.
|
|
func WriteJSON(w http.ResponseWriter, status int, data any) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(status)
|
|
json.NewEncoder(w).Encode(data)
|
|
}
|