diff --git a/frontend/src/network/network.ts b/frontend/src/network/network.ts index 355f694..a7dac4c 100644 --- a/frontend/src/network/network.ts +++ b/frontend/src/network/network.ts @@ -529,6 +529,10 @@ class Network { }; } } + + getFileDownloadLink(fileId: number): string { + return `${this.apiBaseUrl}/files/download/${fileId}`; + } } export const network = new Network(); diff --git a/frontend/src/pages/resource_details_page.tsx b/frontend/src/pages/resource_details_page.tsx index da26a48..604beeb 100644 --- a/frontend/src/pages/resource_details_page.tsx +++ b/frontend/src/pages/resource_details_page.tsx @@ -147,7 +147,10 @@ function FileTile({file}: { file: RFile }) {

{file.description}

-
diff --git a/server/api/file.go b/server/api/file.go index 7f93a3e..16342e9 100644 --- a/server/api/file.go +++ b/server/api/file.go @@ -6,6 +6,7 @@ import ( "nysoure/server/model" "nysoure/server/service" "strconv" + "strings" ) func AddFileRoutes(router fiber.Router) { @@ -19,6 +20,7 @@ func AddFileRoutes(router fiber.Router) { fileGroup.Get("/:id", getFile) fileGroup.Put("/:id", updateFile) fileGroup.Delete("/:id", deleteFile) + fileGroup.Get("/download/:id", downloadFile) } } @@ -200,3 +202,20 @@ func deleteFile(c fiber.Ctx) error { Message: "File deleted successfully", }) } + +func downloadFile(c fiber.Ctx) error { + idStr, err := strconv.ParseUint(c.Params("id"), 10, 32) + if err != nil { + return model.NewRequestError("Invalid file ID") + } + id := uint(idStr) + s, filename, err := service.DownloadFile(id) + if err != nil { + return err + } + if strings.HasPrefix(s, "http") { + return c.Redirect().Status(fiber.StatusFound).To(s) + } + c.Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename)) + return c.SendFile(s) +} diff --git a/server/dao/file.go b/server/dao/file.go index f694325..c88a1b9 100644 --- a/server/dao/file.go +++ b/server/dao/file.go @@ -85,7 +85,7 @@ func CreateFile(filename string, description string, resourceID uint, storageID func GetFile(id uint) (*model.File, error) { f := &model.File{} - if err := db.Where("id = ?", id).First(f).Error; err != nil { + if err := db.Preload("Storage").Where("id = ?", id).First(f).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, model.NewNotFoundError("file not found") } diff --git a/server/service/file.go b/server/service/file.go index 8688eff..0307eb5 100644 --- a/server/service/file.go +++ b/server/service/file.go @@ -346,3 +346,28 @@ func GetFile(fid uint) (*model.FileView, error) { return file.ToView(), nil } + +func DownloadFile(fid uint) (string, string, error) { + file, err := dao.GetFile(fid) + if err != nil { + log.Error("failed to get file: ", err) + return "", "", model.NewNotFoundError("file not found") + } + + if file.StorageID == nil { + if file.RedirectUrl != "" { + return file.RedirectUrl, file.Filename, nil + } + return "", "", model.NewRequestError("file is not available") + } + + iStorage := storage.NewStorage(file.Storage) + if iStorage == nil { + log.Error("failed to find storage: ", err) + return "", "", model.NewInternalServerError("failed to find storage") + } + + path, err := iStorage.Download(file.StorageKey) + + return path, file.Filename, err +}