mirror of
https://github.com/ente-io/ente.git
synced 2025-06-25 19:32:26 +00:00
107 lines
3.4 KiB
Go
107 lines
3.4 KiB
Go
package handler
|
|
|
|
import (
|
|
"database/sql"
|
|
"errors"
|
|
"io"
|
|
"net/http"
|
|
"syscall"
|
|
|
|
"github.com/ente-io/museum/ente"
|
|
"github.com/ente-io/museum/pkg/utils/auth"
|
|
"github.com/ente-io/stacktrace"
|
|
"github.com/gin-contrib/requestid"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/go-playground/validator/v10"
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// Error parses the error, translates it into an HTTP response and aborts
|
|
// the request
|
|
func Error(c *gin.Context, err error) {
|
|
contextLogger := log.WithError(err).
|
|
WithFields(log.Fields{
|
|
"req_id": requestid.Get(c),
|
|
"user_id": auth.GetUserID(c.Request.Header),
|
|
})
|
|
isClientError := false
|
|
// Tip: To trigger the "unexpected EOF" error, connect with:
|
|
//
|
|
// echo "GET /ping HTTP/1.0\r\nContent-Length: 300\r\n\r\n" | nc localhost 8080
|
|
if errors.Is(err, ente.ErrStorageLimitExceeded) ||
|
|
errors.Is(err, ente.ErrNoActiveSubscription) ||
|
|
errors.Is(err, io.ErrUnexpectedEOF) ||
|
|
errors.Is(err, syscall.EPIPE) ||
|
|
errors.Is(err, syscall.ECONNRESET) {
|
|
isClientError = true
|
|
}
|
|
unWrappedErr := errors.Unwrap(err)
|
|
enteApiErr, isEnteApiErr := unWrappedErr.(*ente.ApiError)
|
|
if isEnteApiErr && enteApiErr.HttpStatusCode >= 400 && enteApiErr.HttpStatusCode < 500 {
|
|
isClientError = true
|
|
}
|
|
if isClientError {
|
|
contextLogger.Warn("Request failed")
|
|
} else {
|
|
contextLogger.Error("Request failed")
|
|
}
|
|
if isEnteApiErr {
|
|
c.AbortWithStatusJSON(enteApiErr.HttpStatusCode, enteApiErr)
|
|
} else if httpStatus := httpStatusCode(err); httpStatus != 0 {
|
|
c.AbortWithStatus(httpStatus)
|
|
} else {
|
|
if _, ok := stacktrace.RootCause(err).(validator.ValidationErrors); ok {
|
|
c.AbortWithStatus(http.StatusBadRequest)
|
|
} else if isClientError {
|
|
c.AbortWithStatus(http.StatusBadRequest)
|
|
} else {
|
|
c.AbortWithStatus(http.StatusInternalServerError)
|
|
}
|
|
}
|
|
}
|
|
|
|
// If `err` directly maps to an HTTP status code, return the HTTP status code.
|
|
// Otherwise return 0.
|
|
func httpStatusCode(err error) int {
|
|
switch {
|
|
case errors.Is(err, ente.ErrNotFound) ||
|
|
errors.Is(err, sql.ErrNoRows):
|
|
return http.StatusNotFound
|
|
case errors.Is(err, ente.ErrBadRequest) ||
|
|
errors.Is(err, ente.ErrCannotDowngrade) ||
|
|
errors.Is(err, ente.ErrCannotSwitchPaymentProvider):
|
|
return http.StatusBadRequest
|
|
case errors.Is(err, ente.ErrTooManyBadRequest):
|
|
return http.StatusTooManyRequests
|
|
case errors.Is(err, ente.ErrPermissionDenied):
|
|
return http.StatusForbidden
|
|
case errors.Is(err, ente.ErrIncorrectOTT) ||
|
|
errors.Is(err, ente.ErrIncorrectTOTP) ||
|
|
errors.Is(err, ente.ErrInvalidPassword) ||
|
|
errors.Is(err, ente.ErrAuthenticationRequired):
|
|
return http.StatusUnauthorized
|
|
case errors.Is(err, ente.ErrExpiredOTT):
|
|
return http.StatusGone
|
|
case errors.Is(err, ente.ErrNoActiveSubscription) ||
|
|
errors.Is(err, ente.ErrSharingDisabledForFreeAccounts):
|
|
return http.StatusPaymentRequired
|
|
case errors.Is(err, ente.ErrStorageLimitExceeded):
|
|
return http.StatusUpgradeRequired
|
|
case errors.Is(err, ente.ErrFileTooLarge):
|
|
return http.StatusRequestEntityTooLarge
|
|
case errors.Is(err, ente.ErrVersionMismatch) ||
|
|
errors.Is(err, ente.ErrCanNotInviteUserWithPaidPlan):
|
|
return http.StatusConflict
|
|
case errors.Is(err, ente.ErrBatchSizeTooLarge):
|
|
return http.StatusRequestEntityTooLarge
|
|
case errors.Is(err, ente.ErrCanNotInviteUserAlreadyInFamily):
|
|
return http.StatusNotAcceptable
|
|
case errors.Is(err, ente.ErrFamilySizeLimitReached):
|
|
return http.StatusPreconditionFailed
|
|
case errors.Is(err, ente.ErrNotImplemented):
|
|
return http.StatusNotImplemented
|
|
default:
|
|
return 0
|
|
}
|
|
}
|