Files
nysoure/server/api/file.go

276 lines
6.7 KiB
Go

package api
import (
"encoding/json"
"fmt"
"net/url"
"nysoure/server/config"
"nysoure/server/middleware"
"nysoure/server/model"
"nysoure/server/service"
"nysoure/server/utils"
"strconv"
"strings"
"time"
"github.com/gofiber/fiber/v3"
)
func AddFileRoutes(router fiber.Router) {
fileGroup := router.Group("/files")
{
fileGroup.Use(middleware.NewRequestLimiter(10, time.Hour)).Post("/upload/init", initUpload)
fileGroup.Post("/upload/block/:id/:index", uploadBlock)
fileGroup.Post("/upload/finish/:id", finishUpload)
fileGroup.Post("/upload/cancel/:id", cancelUpload)
fileGroup.Use(middleware.NewRequestLimiter(50, time.Hour)).Post("/redirect", createRedirectFile)
fileGroup.Post("/upload/url", createServerDownloadTask)
fileGroup.Get("/:id", getFile)
fileGroup.Put("/:id", updateFile)
fileGroup.Delete("/:id", deleteFile)
fileGroup.Get("/download/local", downloadLocalFile)
fileGroup.Use(middleware.NewDynamicRequestLimiter(config.MaxDownloadsPerDayForSingleIP, 24*time.Hour)).Get("/download/:id", downloadFile)
}
}
func initUpload(c fiber.Ctx) error {
uid := c.Locals("uid").(uint)
type InitUploadRequest struct {
Filename string `json:"filename"`
Description string `json:"description"`
FileSize int64 `json:"file_size"`
ResourceID uint `json:"resource_id"`
StorageID uint `json:"storage_id"`
}
var req InitUploadRequest
if err := c.Bind().Body(&req); err != nil {
return model.NewRequestError("Invalid request parameters")
}
result, err := service.CreateUploadingFile(uid, req.Filename, req.Description, req.FileSize, req.ResourceID, req.StorageID)
if err != nil {
return err
}
return c.JSON(model.Response[*model.UploadingFileView]{
Success: true,
Data: result,
})
}
func uploadBlock(c fiber.Ctx) error {
uid := c.Locals("uid").(uint)
id, err := strconv.ParseUint(c.Params("id"), 10, 32)
if err != nil {
return model.NewRequestError("Invalid file ID")
}
index, err := strconv.Atoi(c.Params("index"))
if err != nil {
return model.NewRequestError("Invalid block index")
}
data := c.Body()
if err := service.UploadBlock(uid, uint(id), index, data); err != nil {
return err
}
return c.JSON(model.Response[any]{
Success: true,
Message: fmt.Sprintf("Block %d uploaded successfully", index),
})
}
func finishUpload(c fiber.Ctx) error {
uid := c.Locals("uid").(uint)
id, err := strconv.ParseUint(c.Params("id"), 10, 32)
if err != nil {
return model.NewRequestError("Invalid file ID")
}
md5 := c.Query("md5")
if md5 == "" {
return model.NewRequestError("MD5 checksum is required")
}
result, err := service.FinishUploadingFile(uid, uint(id), md5)
if err != nil {
return err
}
return c.JSON(model.Response[*model.FileView]{
Success: true,
Data: result,
})
}
func cancelUpload(c fiber.Ctx) error {
uid := c.Locals("uid").(uint)
id, err := strconv.ParseUint(c.Params("id"), 10, 32)
if err != nil {
return model.NewRequestError("Invalid file ID")
}
if err := service.CancelUploadingFile(uid, uint(id)); err != nil {
return err
}
return c.JSON(model.Response[any]{
Success: true,
Message: "Upload cancelled successfully",
})
}
func createRedirectFile(c fiber.Ctx) error {
uid := c.Locals("uid").(uint)
type CreateRedirectFileRequest struct {
Filename string `json:"filename"`
Description string `json:"description"`
ResourceID uint `json:"resource_id"`
RedirectURL string `json:"redirect_url"`
}
var req CreateRedirectFileRequest
if err := c.Bind().Body(&req); err != nil {
return model.NewRequestError("Invalid request parameters")
}
result, err := service.CreateRedirectFile(uid, req.Filename, req.Description, req.ResourceID, req.RedirectURL)
if err != nil {
return err
}
return c.JSON(model.Response[*model.FileView]{
Success: true,
Data: result,
})
}
func getFile(c fiber.Ctx) error {
file, err := service.GetFile(c.Params("id"))
if err != nil {
return err
}
return c.JSON(model.Response[*model.FileView]{
Success: true,
Data: file,
})
}
func updateFile(c fiber.Ctx) error {
uid := c.Locals("uid").(uint)
type UpdateFileRequest struct {
Filename string `json:"filename"`
Description string `json:"description"`
}
var req UpdateFileRequest
if err := c.Bind().Body(&req); err != nil {
return model.NewRequestError("Invalid request parameters")
}
result, err := service.UpdateFile(uid, c.Params("id"), req.Filename, req.Description)
if err != nil {
return err
}
return c.JSON(model.Response[*model.FileView]{
Success: true,
Data: result,
})
}
func deleteFile(c fiber.Ctx) error {
uid := c.Locals("uid").(uint)
if err := service.DeleteFile(uid, c.Params("id")); err != nil {
return err
}
return c.JSON(model.Response[any]{
Success: true,
Message: "File deleted successfully",
})
}
func downloadFile(c fiber.Ctx) error {
cfToken := c.Query("cf_token")
s, filename, err := service.DownloadFile(c.Params("id"), cfToken)
if err != nil {
return err
}
if strings.HasPrefix(s, "http") {
return c.Redirect().Status(fiber.StatusFound).To(s)
}
data := map[string]string{
"path": s,
"filename": filename,
}
j, _ := json.Marshal(data)
token, err := utils.GenerateTemporaryToken(string(j))
if err != nil {
return model.NewInternalServerError("Failed to generate download token")
}
return c.Redirect().Status(fiber.StatusFound).To(fmt.Sprintf("%s/api/files/download/local?token=%s", c.BaseURL(), token))
}
func downloadLocalFile(c fiber.Ctx) error {
token := c.Query("token")
if token == "" {
return model.NewRequestError("Download token is required")
}
data, err := utils.ParseTemporaryToken(token)
if err != nil {
return model.NewRequestError("Invalid or expired download token")
}
var fileData map[string]string
if err := json.Unmarshal([]byte(data), &fileData); err != nil {
return model.NewInternalServerError("Failed to parse download data")
}
path := fileData["path"]
filename := fileData["filename"]
c.Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", url.PathEscape(filename)))
return c.SendFile(path, fiber.SendFile{
ByteRange: true,
})
}
func createServerDownloadTask(c fiber.Ctx) error {
uid := c.Locals("uid").(uint)
type InitUploadRequest struct {
Url string `json:"url"`
Filename string `json:"filename"`
Description string `json:"description"`
ResourceID uint `json:"resource_id"`
StorageID uint `json:"storage_id"`
}
var req InitUploadRequest
if err := c.Bind().Body(&req); err != nil {
return model.NewRequestError("Invalid request parameters")
}
result, err := service.CreateServerDownloadTask(uid, req.Url, req.Filename, req.Description, req.ResourceID, req.StorageID)
if err != nil {
return err
}
return c.JSON(model.Response[*model.FileView]{
Success: true,
Data: result,
})
}