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" ) type fileServiceProvider interface { ListFiles(ctx context.Context, folderID *int64, page, pageSize int, search string) ([]model.FileResponse, int64, error) GetFileMetadata(ctx context.Context, fileID int64) (*model.File, *model.FileBlob, error) DownloadFile(ctx context.Context, fileID int64, rangeHeader string) (io.ReadCloser, *model.File, *model.FileBlob, int64, int64, error) DeleteFile(ctx context.Context, fileID int64) error } type FileHandler struct { svc fileServiceProvider logger *zap.Logger } func NewFileHandler(svc *service.FileService, logger *zap.Logger) *FileHandler { return &FileHandler{svc: svc, logger: logger} } func (h *FileHandler) ListFiles(c *gin.Context) { page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10")) if page < 1 { page = 1 } if pageSize < 1 { pageSize = 10 } var folderID *int64 if v := c.Query("folder_id"); v != "" { id, err := strconv.ParseInt(v, 10, 64) if err != nil { h.logger.Warn("invalid folder_id", zap.String("folder_id", v)) server.BadRequest(c, "invalid folder_id") return } folderID = &id } search := c.Query("search") files, total, err := h.svc.ListFiles(c.Request.Context(), folderID, page, pageSize, search) if err != nil { h.logger.Error("failed to list files", zap.Error(err)) server.InternalError(c, err.Error()) return } server.OK(c, model.ListFilesResponse{ Files: files, Total: total, Page: page, PageSize: pageSize, }) } func (h *FileHandler) GetFile(c *gin.Context) { id, err := strconv.ParseInt(c.Param("id"), 10, 64) if err != nil { h.logger.Warn("invalid file id", zap.String("id", c.Param("id"))) server.BadRequest(c, "invalid id") return } file, blob, err := h.svc.GetFileMetadata(c.Request.Context(), id) if err != nil { h.logger.Error("failed to get file", zap.Int64("id", id), zap.Error(err)) server.InternalError(c, err.Error()) return } resp := model.FileResponse{ ID: file.ID, Name: file.Name, FolderID: file.FolderID, Size: blob.FileSize, MimeType: blob.MimeType, SHA256: file.BlobSHA256, CreatedAt: file.CreatedAt, UpdatedAt: file.UpdatedAt, } server.OK(c, resp) } func (h *FileHandler) DownloadFile(c *gin.Context) { id, err := strconv.ParseInt(c.Param("id"), 10, 64) if err != nil { h.logger.Warn("invalid file id for download", zap.String("id", c.Param("id"))) server.BadRequest(c, "invalid id") return } rangeHeader := c.GetHeader("Range") reader, file, blob, start, end, err := h.svc.DownloadFile(c.Request.Context(), id, rangeHeader) if err != nil { h.logger.Error("failed to download file", zap.Int64("id", id), zap.Error(err)) server.InternalError(c, err.Error()) return } if rangeHeader != "" { server.StreamRange(c, reader, start, end, blob.FileSize, blob.MimeType) } else { server.StreamFile(c, reader, file.Name, blob.FileSize, blob.MimeType) } } func (h *FileHandler) DeleteFile(c *gin.Context) { id, err := strconv.ParseInt(c.Param("id"), 10, 64) if err != nil { h.logger.Warn("invalid file id for delete", zap.String("id", c.Param("id"))) server.BadRequest(c, "invalid id") return } if err := h.svc.DeleteFile(c.Request.Context(), id); err != nil { h.logger.Error("failed to delete file", zap.Int64("id", id), zap.Error(err)) server.InternalError(c, err.Error()) return } h.logger.Info("file deleted", zap.Int64("id", id)) server.OK(c, gin.H{"message": "file deleted"}) }