feat(handler): add upload, file, and folder handlers with routes
Add UploadHandler (5 endpoints), FileHandler (4 endpoints), FolderHandler (4 endpoints) with Gin route registration in server.go. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
154
internal/handler/upload_handler.go
Normal file
154
internal/handler/upload_handler.go
Normal file
@@ -0,0 +1,154 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"gcy_hpc_server/internal/model"
|
||||
"gcy_hpc_server/internal/server"
|
||||
"gcy_hpc_server/internal/service"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// uploadServiceProvider defines the interface for upload operations.
|
||||
type uploadServiceProvider interface {
|
||||
InitUpload(ctx context.Context, req model.InitUploadRequest) (interface{}, error)
|
||||
UploadChunk(ctx context.Context, sessionID int64, chunkIndex int, reader io.Reader, size int64) error
|
||||
CompleteUpload(ctx context.Context, sessionID int64) (*model.FileResponse, error)
|
||||
GetUploadStatus(ctx context.Context, sessionID int64) (*model.UploadSessionResponse, error)
|
||||
CancelUpload(ctx context.Context, sessionID int64) error
|
||||
}
|
||||
|
||||
// UploadHandler handles HTTP requests for chunked file uploads.
|
||||
type UploadHandler struct {
|
||||
svc uploadServiceProvider
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewUploadHandler creates a new UploadHandler.
|
||||
func NewUploadHandler(svc *service.UploadService, logger *zap.Logger) *UploadHandler {
|
||||
return &UploadHandler{svc: svc, logger: logger}
|
||||
}
|
||||
|
||||
// InitUpload initiates a new chunked upload session.
|
||||
// POST /api/v1/files/uploads
|
||||
func (h *UploadHandler) InitUpload(c *gin.Context) {
|
||||
var req model.InitUploadRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.logger.Warn("invalid request body for init upload", zap.Error(err))
|
||||
server.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
result, err := h.svc.InitUpload(c.Request.Context(), req)
|
||||
if err != nil {
|
||||
h.logger.Error("failed to init upload", zap.Error(err))
|
||||
server.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
switch resp := result.(type) {
|
||||
case model.FileResponse:
|
||||
server.OK(c, resp)
|
||||
case model.UploadSessionResponse:
|
||||
server.Created(c, resp)
|
||||
default:
|
||||
server.Created(c, resp)
|
||||
}
|
||||
}
|
||||
|
||||
// UploadChunk uploads a single chunk of an upload session.
|
||||
// PUT /api/v1/files/uploads/:id/chunks/:index
|
||||
func (h *UploadHandler) UploadChunk(c *gin.Context) {
|
||||
sessionID, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
h.logger.Warn("invalid session id", zap.String("id", c.Param("id")))
|
||||
server.BadRequest(c, "invalid session id")
|
||||
return
|
||||
}
|
||||
|
||||
chunkIndex, err := strconv.Atoi(c.Param("index"))
|
||||
if err != nil {
|
||||
h.logger.Warn("invalid chunk index", zap.String("index", c.Param("index")))
|
||||
server.BadRequest(c, "invalid chunk index")
|
||||
return
|
||||
}
|
||||
|
||||
file, header, err := c.Request.FormFile("chunk")
|
||||
if err != nil {
|
||||
h.logger.Warn("missing chunk file in request", zap.Error(err))
|
||||
server.BadRequest(c, "missing chunk file")
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
if err := h.svc.UploadChunk(c.Request.Context(), sessionID, chunkIndex, file, header.Size); err != nil {
|
||||
h.logger.Error("failed to upload chunk", zap.Int64("session_id", sessionID), zap.Int("chunk_index", chunkIndex), zap.Error(err))
|
||||
server.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
server.OK(c, gin.H{"message": "chunk uploaded"})
|
||||
}
|
||||
|
||||
// CompleteUpload finalizes an upload session and assembles the file.
|
||||
// POST /api/v1/files/uploads/:id/complete
|
||||
func (h *UploadHandler) CompleteUpload(c *gin.Context) {
|
||||
sessionID, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
h.logger.Warn("invalid session id for complete", zap.String("id", c.Param("id")))
|
||||
server.BadRequest(c, "invalid session id")
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.svc.CompleteUpload(c.Request.Context(), sessionID)
|
||||
if err != nil {
|
||||
h.logger.Error("failed to complete upload", zap.Int64("session_id", sessionID), zap.Error(err))
|
||||
server.InternalError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
server.Created(c, resp)
|
||||
}
|
||||
|
||||
// GetUploadStatus returns the current status of an upload session.
|
||||
// GET /api/v1/files/uploads/:id
|
||||
func (h *UploadHandler) GetUploadStatus(c *gin.Context) {
|
||||
sessionID, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
h.logger.Warn("invalid session id for status", zap.String("id", c.Param("id")))
|
||||
server.BadRequest(c, "invalid session id")
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.svc.GetUploadStatus(c.Request.Context(), sessionID)
|
||||
if err != nil {
|
||||
h.logger.Error("failed to get upload status", zap.Int64("session_id", sessionID), zap.Error(err))
|
||||
server.InternalError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
server.OK(c, resp)
|
||||
}
|
||||
|
||||
// CancelUpload cancels and cleans up an upload session.
|
||||
// DELETE /api/v1/files/uploads/:id
|
||||
func (h *UploadHandler) CancelUpload(c *gin.Context) {
|
||||
sessionID, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
h.logger.Warn("invalid session id for cancel", zap.String("id", c.Param("id")))
|
||||
server.BadRequest(c, "invalid session id")
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.CancelUpload(c.Request.Context(), sessionID); err != nil {
|
||||
h.logger.Error("failed to cancel upload", zap.Int64("session_id", sessionID), zap.Error(err))
|
||||
server.InternalError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
server.OK(c, gin.H{"message": "upload cancelled"})
|
||||
}
|
||||
Reference in New Issue
Block a user