refactor(api): enforce protocol-neutral service boundaries
- refactor: move HTTP status and DTO concerns out of model and service layers into API and handler code. - feat: add admin service boundary and route auth through services instead of direct repository access. - test: add architecture and error tests covering package boundaries, redaction, and service behavior. - docs: record the protocol-neutral boundary decision and update architecture and roadmap notes.
This commit is contained in:
@@ -10,6 +10,7 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
@@ -52,6 +53,24 @@ type updateFileRequest struct {
|
||||
ParentID *string `json:"parent_id"`
|
||||
}
|
||||
|
||||
type fileInfoResponse struct {
|
||||
ID string `json:"id"`
|
||||
UserID string `json:"user_id"`
|
||||
ParentID *string `json:"parent_id"`
|
||||
Name string `json:"name"`
|
||||
Size int64 `json:"size"`
|
||||
MimeType string `json:"mime_type"`
|
||||
IsDir bool `json:"is_dir"`
|
||||
Hash string `json:"hash,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
type fileListResponse struct {
|
||||
Files []fileInfoResponse `json:"files"`
|
||||
Total int64 `json:"total"`
|
||||
}
|
||||
|
||||
// Upload handles POST /api/v1/files.
|
||||
// If the content type is multipart/form-data, it uploads a file.
|
||||
// If the content type is application/json, it creates a directory.
|
||||
@@ -81,7 +100,7 @@ func (h *FileHandler) Upload(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, dir)
|
||||
c.JSON(http.StatusCreated, toFileInfoResponse(dir))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -130,7 +149,7 @@ func (h *FileHandler) Upload(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, info)
|
||||
c.JSON(http.StatusCreated, toFileInfoResponse(info))
|
||||
}
|
||||
|
||||
func nextUploadFilePart(reader *multipart.Reader) (*multipart.Part, string, error) {
|
||||
@@ -217,7 +236,7 @@ func (h *FileHandler) List(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, list)
|
||||
c.JSON(http.StatusOK, toFileListResponse(list))
|
||||
}
|
||||
|
||||
// Get handles GET /api/v1/files/:id.
|
||||
@@ -231,7 +250,7 @@ func (h *FileHandler) Get(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, info)
|
||||
c.JSON(http.StatusOK, toFileInfoResponse(info))
|
||||
}
|
||||
|
||||
// Download handles GET /api/v1/files/:id/content.
|
||||
@@ -299,7 +318,7 @@ func (h *FileHandler) Update(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, info)
|
||||
c.JSON(http.StatusOK, toFileInfoResponse(info))
|
||||
}
|
||||
|
||||
// Delete handles DELETE /api/v1/files/:id.
|
||||
@@ -314,3 +333,29 @@ func (h *FileHandler) Delete(c *gin.Context) {
|
||||
|
||||
c.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func toFileInfoResponse(info *service.FileInfo) fileInfoResponse {
|
||||
return fileInfoResponse{
|
||||
ID: info.ID,
|
||||
UserID: info.UserID,
|
||||
ParentID: info.ParentID,
|
||||
Name: info.Name,
|
||||
Size: info.Size,
|
||||
MimeType: info.MimeType,
|
||||
IsDir: info.IsDir,
|
||||
Hash: info.Hash,
|
||||
CreatedAt: info.CreatedAt,
|
||||
UpdatedAt: info.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
func toFileListResponse(list *service.FileList) fileListResponse {
|
||||
items := make([]fileInfoResponse, 0, len(list.Files))
|
||||
for i := range list.Files {
|
||||
items = append(items, toFileInfoResponse(&list.Files[i]))
|
||||
}
|
||||
return fileListResponse{
|
||||
Files: items,
|
||||
Total: list.Total,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user