Add structured logging and centralized error handling
- Initialize slog in the serve command with terminal/file support - Introduce `AppError` with HTTP status for unified service-layer errors - Replace ad-hoc `api.Error` calls with `api.RespondError` - Wrap internal errors with `model.NewInternalError` and add reference IDs - Add `RequestID` middleware and switch router from `gin.Default` to `gin.New`
This commit is contained in:
+17
-27
@@ -1,9 +1,9 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"mime"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -49,13 +49,14 @@ func (h *FileHandler) Upload(c *gin.Context) {
|
||||
if mediaType == "application/json" {
|
||||
var req createDirRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
api.Error(c, http.StatusBadRequest, "invalid request: "+err.Error())
|
||||
slog.DebugContext(c.Request.Context(), "create dir bind failed", "error", err)
|
||||
api.Error(c, http.StatusBadRequest, "invalid request body")
|
||||
return
|
||||
}
|
||||
|
||||
dir, err := h.fileService.CreateDir(c.Request.Context(), userID, req.ParentID, req.Name)
|
||||
if err != nil {
|
||||
api.Error(c, mapError(err), err.Error())
|
||||
api.RespondError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -65,7 +66,8 @@ func (h *FileHandler) Upload(c *gin.Context) {
|
||||
|
||||
// File upload via multipart.
|
||||
if err := c.Request.ParseMultipartForm(32 << 20); err != nil {
|
||||
api.Error(c, http.StatusBadRequest, "invalid multipart form: "+err.Error())
|
||||
slog.DebugContext(c.Request.Context(), "parse multipart form failed", "error", err)
|
||||
api.Error(c, http.StatusBadRequest, "invalid multipart form")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -77,20 +79,21 @@ func (h *FileHandler) Upload(c *gin.Context) {
|
||||
|
||||
header, err := c.FormFile("file")
|
||||
if err != nil {
|
||||
api.Error(c, http.StatusBadRequest, "missing file field: "+err.Error())
|
||||
slog.DebugContext(c.Request.Context(), "form file missing", "error", err)
|
||||
api.Error(c, http.StatusBadRequest, "missing file field")
|
||||
return
|
||||
}
|
||||
|
||||
file, err := header.Open()
|
||||
if err != nil {
|
||||
api.Error(c, http.StatusInternalServerError, "open uploaded file: "+err.Error())
|
||||
api.RespondError(c, model.NewInternalError("open uploaded file", err))
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
info, err := h.fileService.Upload(c.Request.Context(), userID, parentID, header.Filename, file)
|
||||
if err != nil {
|
||||
api.Error(c, mapError(err), err.Error())
|
||||
api.RespondError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -124,7 +127,7 @@ func (h *FileHandler) List(c *gin.Context) {
|
||||
|
||||
list, err := h.fileService.List(c.Request.Context(), userID, parentID, offset, limit)
|
||||
if err != nil {
|
||||
api.Error(c, mapError(err), err.Error())
|
||||
api.RespondError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -138,7 +141,7 @@ func (h *FileHandler) Get(c *gin.Context) {
|
||||
|
||||
info, err := h.fileService.Get(c.Request.Context(), userID, fileID)
|
||||
if err != nil {
|
||||
api.Error(c, mapError(err), err.Error())
|
||||
api.RespondError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -152,7 +155,7 @@ func (h *FileHandler) Download(c *gin.Context) {
|
||||
|
||||
reader, info, err := h.fileService.Download(c.Request.Context(), userID, fileID)
|
||||
if err != nil {
|
||||
api.Error(c, mapError(err), err.Error())
|
||||
api.RespondError(c, err)
|
||||
return
|
||||
}
|
||||
defer reader.Close()
|
||||
@@ -177,7 +180,8 @@ func (h *FileHandler) Update(c *gin.Context) {
|
||||
|
||||
var req updateFileRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
api.Error(c, http.StatusBadRequest, "invalid request: "+err.Error())
|
||||
slog.DebugContext(c.Request.Context(), "update file bind failed", "error", err)
|
||||
api.Error(c, http.StatusBadRequest, "invalid request body")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -189,7 +193,7 @@ func (h *FileHandler) Update(c *gin.Context) {
|
||||
|
||||
info, err := h.fileService.Update(c.Request.Context(), userID, fileID, req.Name, req.ParentID)
|
||||
if err != nil {
|
||||
api.Error(c, mapError(err), err.Error())
|
||||
api.RespondError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -202,23 +206,9 @@ func (h *FileHandler) Delete(c *gin.Context) {
|
||||
fileID := c.Param("id")
|
||||
|
||||
if err := h.fileService.Delete(c.Request.Context(), userID, fileID); err != nil {
|
||||
api.Error(c, mapError(err), err.Error())
|
||||
api.RespondError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
c.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// mapError maps sentinel errors to HTTP status codes.
|
||||
func mapError(err error) int {
|
||||
switch {
|
||||
case errors.Is(err, model.ErrNotFound):
|
||||
return http.StatusNotFound
|
||||
case errors.Is(err, model.ErrForbidden):
|
||||
return http.StatusForbidden
|
||||
case errors.Is(err, model.ErrDuplicate):
|
||||
return http.StatusConflict
|
||||
default:
|
||||
return http.StatusInternalServerError
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user