From 033ad65d5f6665ad672fc4629ae5c896531510cd Mon Sep 17 00:00:00 2001 From: Kenneth Date: Sun, 30 Nov 2025 19:19:33 +0000 Subject: [PATCH] feat: improve err logging --- apps/backend/go.mod | 16 +++--- apps/backend/go.sum | 36 +++++++------ apps/backend/internal/account/http.go | 11 ++-- apps/backend/internal/auth/http.go | 7 +-- apps/backend/internal/auth/middleware.go | 8 ++- apps/backend/internal/auth/service.go | 3 ++ apps/backend/internal/drexa/server.go | 11 ++-- apps/backend/internal/httperr/error.go | 42 ++++++++++++++++ apps/backend/internal/httperr/handler.go | 64 ++++++++++++++++++++++++ apps/backend/internal/upload/http.go | 10 ++-- 10 files changed, 170 insertions(+), 38 deletions(-) create mode 100644 apps/backend/internal/httperr/error.go create mode 100644 apps/backend/internal/httperr/handler.go diff --git a/apps/backend/go.mod b/apps/backend/go.mod index d86154f..0d76d2f 100644 --- a/apps/backend/go.mod +++ b/apps/backend/go.mod @@ -7,14 +7,16 @@ require ( github.com/gofiber/fiber/v2 v2.52.9 github.com/google/uuid v1.6.0 github.com/sqids/sqids-go v0.4.1 - github.com/uptrace/bun v1.2.15 - golang.org/x/crypto v0.40.0 + github.com/uptrace/bun v1.2.16 + github.com/uptrace/bun/extra/bundebug v1.2.16 + golang.org/x/crypto v0.45.0 gopkg.in/yaml.v3 v3.0.1 ) require ( - go.opentelemetry.io/otel v1.37.0 // indirect - go.opentelemetry.io/otel/trace v1.37.0 // indirect + github.com/fatih/color v1.18.0 // indirect + go.opentelemetry.io/otel v1.38.0 // indirect + go.opentelemetry.io/otel/trace v1.38.0 // indirect mellium.im/sasl v0.3.2 // indirect ) @@ -29,12 +31,12 @@ require ( github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect - github.com/uptrace/bun/dialect/pgdialect v1.2.15 - github.com/uptrace/bun/driver/pgdriver v1.2.15 + github.com/uptrace/bun/dialect/pgdialect v1.2.16 + github.com/uptrace/bun/driver/pgdriver v1.2.16 github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.51.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect - golang.org/x/sys v0.34.0 // indirect + golang.org/x/sys v0.38.0 // indirect ) diff --git a/apps/backend/go.sum b/apps/backend/go.sum index 9c7c0ac..2981614 100644 --- a/apps/backend/go.sum +++ b/apps/backend/go.sum @@ -2,6 +2,8 @@ github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1 github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/gabriel-vasile/mimetype v1.4.11 h1:AQvxbp830wPhHTqc1u7nzoLT+ZFxGY7emj5DR5DYFik= github.com/gabriel-vasile/mimetype v1.4.11/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= github.com/gofiber/fiber/v2 v2.52.9 h1:YjKl5DOiyP3j0mO61u3NTmK7or8GzzWzCFzkboyP5cw= @@ -34,16 +36,18 @@ github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/sqids/sqids-go v0.4.1 h1:eQKYzmAZbLlRwHeHYPF35QhgxwZHLnlmVj9AkIj/rrw= github.com/sqids/sqids-go v0.4.1/go.mod h1:EMwHuPQgSNFS0A49jESTfIQS+066XQTVhukrzEPScl8= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs= -github.com/uptrace/bun v1.2.15 h1:Ut68XRBLDgp9qG9QBMa9ELWaZOmzHNdczHQdrOZbEFE= -github.com/uptrace/bun v1.2.15/go.mod h1:Eghz7NonZMiTX/Z6oKYytJ0oaMEJ/eq3kEV4vSqG038= -github.com/uptrace/bun/dialect/pgdialect v1.2.15 h1:er+/3giAIqpfrXJw+KP9B7ujyQIi5XkPnFmgjAVL6bA= -github.com/uptrace/bun/dialect/pgdialect v1.2.15/go.mod h1:QSiz6Qpy9wlGFsfpf7UMSL6mXAL1jDJhFwuOVacCnOQ= -github.com/uptrace/bun/driver/pgdriver v1.2.15 h1:eZZ60ZtUUE6jjv6VAI1pCMaTgtx3sxmChQzwbvchOOo= -github.com/uptrace/bun/driver/pgdriver v1.2.15/go.mod h1:s2zz/BAeScal4KLFDI8PURwATN8s9RDBsElEbnPAjv4= +github.com/uptrace/bun v1.2.16 h1:QlObi6ZIK5Ao7kAALnh91HWYNZUBbVwye52fmlQM9kc= +github.com/uptrace/bun v1.2.16/go.mod h1:jMoNg2n56ckaawi/O/J92BHaECmrz6IRjuMWqlMaMTM= +github.com/uptrace/bun/dialect/pgdialect v1.2.16 h1:KFNZ0LxAyczKNfK/IJWMyaleO6eI9/Z5tUv3DE1NVL4= +github.com/uptrace/bun/dialect/pgdialect v1.2.16/go.mod h1:IJdMeV4sLfh0LDUZl7TIxLI0LipF1vwTK3hBC7p5qLo= +github.com/uptrace/bun/driver/pgdriver v1.2.16 h1:b1kpXKUxtTSGYow5Vlsb+dKV3z0R7aSAJNfMfKp61ZU= +github.com/uptrace/bun/driver/pgdriver v1.2.16/go.mod h1:H6lUZ9CBfp1X5Vq62YGSV7q96/v94ja9AYFjKvdoTk0= +github.com/uptrace/bun/extra/bundebug v1.2.16 h1:3OXAfHTU4ydu2+4j05oB1BxPx6+ypdWIVzTugl/7zl0= +github.com/uptrace/bun/extra/bundebug v1.2.16/go.mod h1:vk6R/1i67/S2RvUI5AH/m3P5e67mOkfDCmmCsAPUumo= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA= @@ -54,15 +58,15 @@ github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IU github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= -go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= -go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= -go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= -go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= -golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= -golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= +go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= +go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= +go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= +golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= -golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/apps/backend/internal/account/http.go b/apps/backend/internal/account/http.go index b702fea..016b640 100644 --- a/apps/backend/internal/account/http.go +++ b/apps/backend/internal/account/http.go @@ -4,6 +4,7 @@ import ( "errors" "github.com/get-drexa/drexa/internal/auth" + "github.com/get-drexa/drexa/internal/httperr" "github.com/get-drexa/drexa/internal/user" "github.com/gofiber/fiber/v2" "github.com/google/uuid" @@ -68,7 +69,7 @@ func (h *HTTPHandler) accountMiddleware(c *fiber.Ctx) error { if errors.Is(err, ErrAccountNotFound) { return c.SendStatus(fiber.StatusNotFound) } - return c.SendStatus(fiber.StatusInternalServerError) + return httperr.Internal(err) } c.Locals(currentAccountKey, account) @@ -92,7 +93,7 @@ func (h *HTTPHandler) registerAccount(c *fiber.Ctx) error { tx, err := h.db.BeginTx(c.Context(), nil) if err != nil { - return c.SendStatus(fiber.StatusInternalServerError) + return httperr.Internal(err) } defer tx.Rollback() @@ -109,17 +110,17 @@ func (h *HTTPHandler) registerAccount(c *fiber.Ctx) error { if errors.Is(err, ErrAccountAlreadyExists) { return c.SendStatus(fiber.StatusConflict) } - return c.SendStatus(fiber.StatusBadRequest) + return httperr.Internal(err) } result, err := h.authService.GenerateTokenForUser(c.Context(), tx, u) if err != nil { - return c.SendStatus(fiber.StatusInternalServerError) + return httperr.Internal(err) } err = tx.Commit() if err != nil { - return c.SendStatus(fiber.StatusInternalServerError) + return httperr.Internal(err) } return c.JSON(registerAccountResponse{ diff --git a/apps/backend/internal/auth/http.go b/apps/backend/internal/auth/http.go index 64b607f..58cea2b 100644 --- a/apps/backend/internal/auth/http.go +++ b/apps/backend/internal/auth/http.go @@ -3,6 +3,7 @@ package auth import ( "errors" + "github.com/get-drexa/drexa/internal/httperr" "github.com/get-drexa/drexa/internal/user" "github.com/gofiber/fiber/v2" "github.com/uptrace/bun" @@ -41,7 +42,7 @@ func (h *HTTPHandler) Login(c *fiber.Ctx) error { tx, err := h.db.BeginTx(c.Context(), nil) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal server error"}) + return httperr.Internal(err) } defer tx.Rollback() @@ -50,11 +51,11 @@ func (h *HTTPHandler) Login(c *fiber.Ctx) error { if errors.Is(err, ErrInvalidCredentials) { return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Invalid credentials"}) } - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal server error"}) + return httperr.Internal(err) } if err := tx.Commit(); err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal server error"}) + return httperr.Internal(err) } return c.JSON(loginResponse{ diff --git a/apps/backend/internal/auth/middleware.go b/apps/backend/internal/auth/middleware.go index 63b350e..e3ee848 100644 --- a/apps/backend/internal/auth/middleware.go +++ b/apps/backend/internal/auth/middleware.go @@ -2,8 +2,10 @@ package auth import ( "errors" + "log/slog" "strings" + "github.com/get-drexa/drexa/internal/httperr" "github.com/get-drexa/drexa/internal/user" "github.com/gofiber/fiber/v2" "github.com/uptrace/bun" @@ -17,11 +19,13 @@ func NewBearerAuthMiddleware(s *Service, db *bun.DB) fiber.Handler { return func(c *fiber.Ctx) error { authHeader := c.Get("Authorization") if authHeader == "" { + slog.Info("no auth header") return c.SendStatus(fiber.StatusUnauthorized) } parts := strings.Split(authHeader, " ") if len(parts) != 2 || parts[0] != "Bearer" { + slog.Info("invalid auth header") return c.SendStatus(fiber.StatusUnauthorized) } @@ -30,15 +34,17 @@ func NewBearerAuthMiddleware(s *Service, db *bun.DB) fiber.Handler { if err != nil { var e *InvalidAccessTokenError if errors.As(err, &e) { + slog.Info("invalid access token") return c.SendStatus(fiber.StatusUnauthorized) } var nf *user.NotFoundError if errors.As(err, &nf) { + slog.Info("user not found") return c.SendStatus(fiber.StatusUnauthorized) } - return c.SendStatus(fiber.StatusInternalServerError) + return httperr.Internal(err) } c.Locals(authenticatedUserKey, u) diff --git a/apps/backend/internal/auth/service.go b/apps/backend/internal/auth/service.go index 24671d1..5b1e288 100644 --- a/apps/backend/internal/auth/service.go +++ b/apps/backend/internal/auth/service.go @@ -4,6 +4,7 @@ import ( "context" "encoding/hex" "errors" + "log/slog" "github.com/get-drexa/drexa/internal/password" "github.com/get-drexa/drexa/internal/user" @@ -94,11 +95,13 @@ func (s *Service) AuthenticateWithEmailAndPassword(ctx context.Context, db bun.I func (s *Service) AuthenticateWithAccessToken(ctx context.Context, db bun.IDB, token string) (*user.User, error) { claims, err := ParseAccessToken(token, &s.tokenConfig) if err != nil { + slog.Info("failed to parse access token", "error", err) return nil, err } id, err := uuid.Parse(claims.Subject) if err != nil { + slog.Info("failed to parse access token subject", "error", err) return nil, newInvalidAccessTokenError(err) } diff --git a/apps/backend/internal/drexa/server.go b/apps/backend/internal/drexa/server.go index e26e2c3..5f6d1c7 100644 --- a/apps/backend/internal/drexa/server.go +++ b/apps/backend/internal/drexa/server.go @@ -8,19 +8,24 @@ import ( "github.com/get-drexa/drexa/internal/auth" "github.com/get-drexa/drexa/internal/blob" "github.com/get-drexa/drexa/internal/database" + "github.com/get-drexa/drexa/internal/httperr" "github.com/get-drexa/drexa/internal/upload" "github.com/get-drexa/drexa/internal/user" "github.com/get-drexa/drexa/internal/virtualfs" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/logger" + "github.com/uptrace/bun/extra/bundebug" ) func NewServer(c Config) (*fiber.App, error) { - app := fiber.New() - db := database.NewFromPostgres(c.Database.PostgresURL) - + app := fiber.New(fiber.Config{ + ErrorHandler: httperr.ErrorHandler, + }) app.Use(logger.New()) + db := database.NewFromPostgres(c.Database.PostgresURL) + db.AddQueryHook(bundebug.NewQueryHook(bundebug.WithVerbose(true))) + // Initialize blob store based on config var blobStore blob.Store switch c.Storage.Backend { diff --git a/apps/backend/internal/httperr/error.go b/apps/backend/internal/httperr/error.go new file mode 100644 index 0000000..93d58e9 --- /dev/null +++ b/apps/backend/internal/httperr/error.go @@ -0,0 +1,42 @@ +package httperr + +import ( + "fmt" + + "github.com/gofiber/fiber/v2" +) + +// HTTPError represents an HTTP error with a status code and underlying error. +type HTTPError struct { + Code int + Message string + Err error +} + +// Error implements the error interface. +func (e *HTTPError) Error() string { + if e.Err != nil { + return fmt.Sprintf("HTTP %d: %s: %v", e.Code, e.Message, e.Err) + } + return fmt.Sprintf("HTTP %d: %s", e.Code, e.Message) +} + +// Unwrap returns the underlying error. +func (e *HTTPError) Unwrap() error { + return e.Err +} + +// NewHTTPError creates a new HTTPError with the given status code, message, and underlying error. +func NewHTTPError(code int, message string, err error) *HTTPError { + return &HTTPError{ + Code: code, + Message: message, + Err: err, + } +} + +// Internal creates a new HTTPError with status 500. +func Internal(err error) *HTTPError { + return NewHTTPError(fiber.StatusInternalServerError, "Internal", err) +} + diff --git a/apps/backend/internal/httperr/handler.go b/apps/backend/internal/httperr/handler.go new file mode 100644 index 0000000..b543d25 --- /dev/null +++ b/apps/backend/internal/httperr/handler.go @@ -0,0 +1,64 @@ +package httperr + +import ( + "errors" + "log/slog" + + "github.com/gofiber/fiber/v2" +) + +// ErrorHandler is a global error handler for Fiber that logs errors and returns appropriate responses. +func ErrorHandler(c *fiber.Ctx, err error) error { + // Default status code + code := fiber.StatusInternalServerError + message := "Internal" + + // Check if it's our custom HTTPError + var httpErr *HTTPError + if errors.As(err, &httpErr) { + code = httpErr.Code + message = httpErr.Message + + // Log the error with underlying error details + if httpErr.Err != nil { + slog.Error("HTTP error", + "status", code, + "message", message, + "error", httpErr.Err.Error(), + "path", c.Path(), + "method", c.Method(), + ) + } else { + slog.Warn("HTTP error", + "status", code, + "message", message, + "path", c.Path(), + "method", c.Method(), + ) + } + } else { + // Check if it's a Fiber error + var fiberErr *fiber.Error + if errors.As(err, &fiberErr) { + code = fiberErr.Code + message = fiberErr.Message + } else { + // Generic error - log it + slog.Error("Unhandled error", + "status", code, + "error", err.Error(), + "path", c.Path(), + "method", c.Method(), + ) + } + } + + // Set Content-Type header + c.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSONCharsetUTF8) + + // Return JSON response + return c.Status(code).JSON(fiber.Map{ + "error": message, + }) +} + diff --git a/apps/backend/internal/upload/http.go b/apps/backend/internal/upload/http.go index dab07a3..1e54009 100644 --- a/apps/backend/internal/upload/http.go +++ b/apps/backend/internal/upload/http.go @@ -4,6 +4,7 @@ import ( "errors" "github.com/get-drexa/drexa/internal/account" + "github.com/get-drexa/drexa/internal/httperr" "github.com/gofiber/fiber/v2" "github.com/uptrace/bun" ) @@ -50,7 +51,10 @@ func (h *HTTPHandler) Create(c *fiber.Ctx) error { Name: req.Name, }) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal server error"}) + if errors.Is(err, ErrNotFound) { + return c.SendStatus(fiber.StatusNotFound) + } + return httperr.Internal(err) } return c.JSON(upload) @@ -67,7 +71,7 @@ func (h *HTTPHandler) ReceiveContent(c *fiber.Ctx) error { err := h.service.ReceiveUpload(c.Context(), h.db, account.ID, uploadID, c.Request().BodyStream()) defer c.Request().CloseBodyStream() if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal server error"}) + return httperr.Internal(err) } return c.SendStatus(fiber.StatusNoContent) @@ -90,7 +94,7 @@ func (h *HTTPHandler) Update(c *fiber.Ctx) error { if errors.Is(err, ErrNotFound) { return c.SendStatus(fiber.StatusNotFound) } - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal server error"}) + return httperr.Internal(err) } return c.JSON(upload) }