Docker/Voltaserve/api/router/file_router.go
2024-04-17 20:22:30 +02:00

928 lines
25 KiB
Go

package router
import (
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"voltaserve/config"
"voltaserve/errorpkg"
"voltaserve/helper"
"voltaserve/infra"
"voltaserve/model"
"voltaserve/service"
"github.com/go-playground/validator/v10"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/log"
"github.com/golang-jwt/jwt/v5"
)
type FileRouter struct {
fileSvc *service.FileService
workspaceSvc *service.WorkspaceService
config config.Config
}
func NewFileRouter() *FileRouter {
return &FileRouter{
fileSvc: service.NewFileService(),
workspaceSvc: service.NewWorkspaceService(),
config: config.GetConfig(),
}
}
func (r *FileRouter) AppendRoutes(g fiber.Router) {
g.Post("/", r.Upload)
g.Post("/create_folder", r.CreateFolder)
g.Get("/list", r.ListByPath)
g.Get("/get", r.GetByPath)
g.Post("/batch_delete", r.BatchDelete)
g.Post("/batch_get", r.BatchGet)
g.Get("/:id", r.GetByID)
g.Patch("/:id", r.Patch)
g.Delete("/:id", r.Delete)
g.Get("/:id/list", r.List)
g.Get("/:id/get_item_count", r.GetItemCount)
g.Get("/:id/get_path", r.GetPath)
g.Get("/:id/get_ids", r.GetIDs)
g.Post("/:id/move", r.Move)
g.Post("/:id/rename", r.Rename)
g.Post("/:id/copy", r.Copy)
g.Get("/:id/get_size", r.GetSize)
g.Post("/grant_user_permission", r.GrantUserPermission)
g.Post("/revoke_user_permission", r.RevokeUserPermission)
g.Post("/grant_group_permission", r.GrantGroupPermission)
g.Post("/revoke_group_permission", r.RevokeGroupPermission)
g.Get("/:id/get_user_permissions", r.GetUserPermissions)
g.Get("/:id/get_group_permissions", r.GetGroupPermissions)
}
// Upload godoc
//
// @Summary Upload
// @Description Upload
// @Tags Files
// @Id files_upload
// @Accept x-www-form-urlencoded
// @Produce json
// @Param workspace_id query string true "Workspace ID"
// @Param parent_id query string false "Parent ID"
// @Param name query string false "Name"
// @Success 200 {object} service.File
// @Failure 404 {object} errorpkg.ErrorResponse
// @Failure 400 {object} errorpkg.ErrorResponse
// @Failure 500 {object} errorpkg.ErrorResponse
// @Router /files [post]
func (r *FileRouter) Upload(c *fiber.Ctx) error {
userID := GetUserID(c)
workspaceID := c.Query("workspace_id")
if workspaceID == "" {
return errorpkg.NewMissingQueryParamError("workspace_id")
}
parentID := c.Query("parent_id")
if parentID == "" {
workspace, err := r.workspaceSvc.Find(workspaceID, userID)
if err != nil {
return err
}
parentID = workspace.RootID
}
fh, err := c.FormFile("file")
if err != nil {
return err
}
ok, err := r.workspaceSvc.HasEnoughSpaceForByteSize(workspaceID, fh.Size)
if err != nil {
return err
}
if !ok {
return errorpkg.NewStorageLimitExceededError()
}
name := c.Query("name")
if name == "" {
name = fh.Filename
}
file, err := r.fileSvc.Create(service.FileCreateOptions{
Name: name,
Type: model.FileTypeFile,
ParentID: &parentID,
WorkspaceID: workspaceID,
}, userID)
if err != nil {
return err
}
path := filepath.FromSlash(os.TempDir() + "/" + helper.NewID() + filepath.Ext(fh.Filename))
if err := c.SaveFile(fh, path); err != nil {
return err
}
defer func(name string) {
if err := os.Remove(name); err != nil {
log.Error(err)
}
}(path)
file, err = r.fileSvc.Store(file.ID, path, userID)
if err != nil {
return err
}
return c.Status(http.StatusCreated).JSON(file)
}
// Patch godoc
//
// @Summary Patch
// @Description Patch
// @Tags Files
// @Id files_patch
// @Accept x-www-form-urlencoded
// @Produce json
// @Param id path string true "ID"
// @Success 200 {object} service.File
// @Failure 404 {object} errorpkg.ErrorResponse
// @Failure 400 {object} errorpkg.ErrorResponse
// @Failure 500 {object} errorpkg.ErrorResponse
// @Router /files/{id} [patch]
func (r *FileRouter) Patch(c *fiber.Ctx) error {
userID := GetUserID(c)
files, err := r.fileSvc.FindByID([]string{c.Params("id")}, userID)
if err != nil {
return err
}
file := files[0]
fh, err := c.FormFile("file")
if err != nil {
return err
}
ok, err := r.workspaceSvc.HasEnoughSpaceForByteSize(file.WorkspaceID, fh.Size)
if err != nil {
return err
}
if !ok {
return errorpkg.NewStorageLimitExceededError()
}
path := filepath.FromSlash(os.TempDir() + "/" + helper.NewID() + filepath.Ext(fh.Filename))
if err := c.SaveFile(fh, path); err != nil {
return err
}
defer func(name string) {
if err := os.Remove(name); err != nil {
log.Error(err)
}
}(path)
file, err = r.fileSvc.Store(file.ID, path, userID)
if err != nil {
return err
}
return c.JSON(file)
}
// CreateFolder godoc
//
// @Summary Create
// @Description Create
// @Tags Files
// @Id files_create_folder
// @Accept json
// @Produce json
// @Param body body service.FileCreateFolderOptions true "Body"
// @Success 200 {object} service.File
// @Failure 400 {object} errorpkg.ErrorResponse
// @Failure 500 {object} errorpkg.ErrorResponse
// @Router /files/create_folder [post]
func (r *FileRouter) CreateFolder(c *fiber.Ctx) error {
userID := GetUserID(c)
opts := new(service.FileCreateFolderOptions)
if err := c.BodyParser(opts); err != nil {
return err
}
if err := validator.New().Struct(opts); err != nil {
return errorpkg.NewRequestBodyValidationError(err)
}
parentID := opts.ParentID
if parentID == nil {
workspace, err := r.workspaceSvc.Find(opts.WorkspaceID, userID)
if err != nil {
return err
}
parentID = &workspace.RootID
}
res, err := r.fileSvc.Create(service.FileCreateOptions{
Name: opts.Name,
Type: model.FileTypeFolder,
ParentID: parentID,
WorkspaceID: opts.WorkspaceID,
}, userID)
if err != nil {
return err
}
return c.Status(http.StatusCreated).JSON(res)
}
// GetByID godoc
//
// @Summary Get by ID
// @Description Get by ID
// @Tags Files
// @Id files_get_by_id
// @Produce json
// @Param id path string true "ID"
// @Success 200 {object} service.File
// @Failure 404 {object} errorpkg.ErrorResponse
// @Failure 500 {object} errorpkg.ErrorResponse
// @Router /files/{id} [get]
func (r *FileRouter) GetByID(c *fiber.Ctx) error {
userID := GetUserID(c)
res, err := r.fileSvc.FindByID([]string{c.Params("id")}, userID)
if err != nil {
return err
}
if len(res) == 0 {
return errorpkg.NewFileNotFoundError(nil)
}
return c.JSON(res[0])
}
// GetByPath godoc
//
// @Summary Get by Path
// @Description Get by Path
// @Tags Files
// @Id files_get_by_path
// @Produce json
// @Param id path string true "ID"
// @Success 200 {object} service.File
// @Failure 404 {object} errorpkg.ErrorResponse
// @Failure 500 {object} errorpkg.ErrorResponse
// @Router /files/get [get]
func (r *FileRouter) GetByPath(c *fiber.Ctx) error {
userID := GetUserID(c)
if c.Query("path") == "" {
return errorpkg.NewMissingQueryParamError("path")
}
res, err := r.fileSvc.FindByPath(c.Query("path"), userID)
if err != nil {
return err
}
return c.JSON(res)
}
// ListByPath godoc
//
// @Summary List by Path
// @Description List by Path
// @Tags Files
// @Id files_list_by_path
// @Produce json
// @Param path query string true "Path"
// @Success 200 {array} service.File
// @Failure 404 {object} errorpkg.ErrorResponse
// @Failure 500 {object} errorpkg.ErrorResponse
// @Router /files/list [get]
func (r *FileRouter) ListByPath(c *fiber.Ctx) error {
userID := GetUserID(c)
if c.Query("path") == "" {
return errorpkg.NewMissingQueryParamError("path")
}
res, err := r.fileSvc.ListByPath(c.Query("path"), userID)
if err != nil {
return err
}
return c.JSON(res)
}
// List godoc
//
// @Summary List
// @Description List
// @Tags Files
// @Id files_list
// @Produce json
// @Param id path string true "ID"
// @Param type query string false "Type"
// @Param page query string false "Page"
// @Param size query string false "Size"
// @Param sort_by query string false "Sort By"
// @Param sort_order query string false "Sort Order"
// @Param query query string false "Query"
// @Success 200 {object} service.FileList
// @Failure 404 {object} errorpkg.ErrorResponse
// @Failure 500 {object} errorpkg.ErrorResponse
// @Router /files/{id}/list [get]
func (r *FileRouter) List(c *fiber.Ctx) error {
var err error
var res *service.FileList
id := c.Params("id")
userID := GetUserID(c)
var page int64
if c.Query("page") == "" {
page = 1
} else {
page, err = strconv.ParseInt(c.Query("page"), 10, 32)
if err != nil {
page = 1
}
}
var size int64
if c.Query("size") == "" {
size = FileDefaultPageSize
} else {
size, err = strconv.ParseInt(c.Query("size"), 10, 32)
if err != nil {
return err
}
}
sortBy := c.Query("sort_by")
if !IsValidSortBy(sortBy) {
return errorpkg.NewInvalidQueryParamError("sort_by")
}
sortOrder := c.Query("sort_order")
if !IsValidSortOrder(sortOrder) {
return errorpkg.NewInvalidQueryParamError("sort_order")
}
fileType := c.Query("type")
if fileType != model.FileTypeFile && fileType != model.FileTypeFolder && fileType != "" {
return errorpkg.NewInvalidQueryParamError("type")
}
query := c.Query("query")
opts := service.FileListOptions{
Page: uint(page),
Size: uint(size),
SortBy: sortBy,
SortOrder: sortOrder,
}
if query != "" {
bytes, err := base64.StdEncoding.DecodeString(query + strings.Repeat("=", (4-len(query)%4)%4))
if err != nil {
return errorpkg.NewInvalidQueryParamError("query")
}
if err := json.Unmarshal(bytes, &opts.Query); err != nil {
return errorpkg.NewInvalidQueryParamError("query")
}
res, err = r.fileSvc.Search(id, opts, userID)
if err != nil {
return err
}
} else {
if fileType != "" {
opts.Query = &service.FileQuery{
Type: &fileType,
}
}
res, err = r.fileSvc.List(id, opts, userID)
if err != nil {
return err
}
}
return c.JSON(res)
}
// GetIDs godoc
//
// @Summary Get IDs
// @Description Get IDs
// @Tags Files
// @Id files_get_ids
// @Produce json
// @Param id path string true "ID"
// @Success 200 {array} string
// @Failure 404 {object} errorpkg.ErrorResponse
// @Failure 500 {object} errorpkg.ErrorResponse
// @Router /files/{id}/get_ids [get]
func (r *FileRouter) GetIDs(c *fiber.Ctx) error {
userID := GetUserID(c)
res, err := r.fileSvc.GetIDs(c.Params("id"), userID)
if err != nil {
return err
}
return c.JSON(res)
}
// GetPath godoc
//
// @Summary Get path
// @Description Get path
// @Tags Files
// @Id files_get_path
// @Produce json
// @Param id path string true "ID"
// @Success 200 {array} service.File
// @Failure 404 {object} errorpkg.ErrorResponse
// @Failure 500 {object} errorpkg.ErrorResponse
// @Router /files/{id}/get_path [get]
func (r *FileRouter) GetPath(c *fiber.Ctx) error {
userID := GetUserID(c)
res, err := r.fileSvc.GetPath(c.Params("id"), userID)
if err != nil {
return err
}
return c.JSON(res)
}
// Copy godoc
//
// @Summary Copy
// @Description Copy
// @Tags Files
// @Id files_copy
// @Produce json
// @Param id path string true "ID"
// @Param body body service.FileCopyOptions true "Body"
// @Failure 404 {object} errorpkg.ErrorResponse
// @Failure 500 {object} errorpkg.ErrorResponse
// @Router /files/{id}/copy [post]
func (r *FileRouter) Copy(c *fiber.Ctx) error {
userID := GetUserID(c)
opts := new(service.FileCopyOptions)
if err := c.BodyParser(opts); err != nil {
return err
}
if err := validator.New().Struct(opts); err != nil {
return errorpkg.NewRequestBodyValidationError(err)
}
res, err := r.fileSvc.Copy(c.Params("id"), opts.IDs, userID)
if err != nil {
return err
}
return c.JSON(res)
}
// Move godoc
//
// @Summary Move
// @Description Move
// @Tags Files
// @Id files_move
// @Produce json
// @Param id path string true "ID"
// @Param body body service.FileMoveOptions true "Body"
// @Failure 404 {object} errorpkg.ErrorResponse
// @Failure 500 {object} errorpkg.ErrorResponse
// @Router /files/{id}/move [post]
func (r *FileRouter) Move(c *fiber.Ctx) error {
userID := GetUserID(c)
opts := new(service.FileMoveOptions)
if err := c.BodyParser(opts); err != nil {
return err
}
if err := validator.New().Struct(opts); err != nil {
return errorpkg.NewRequestBodyValidationError(err)
}
if _, err := r.fileSvc.Move(c.Params("id"), opts.IDs, userID); err != nil {
return err
}
return c.SendStatus(http.StatusNoContent)
}
// Rename godoc
//
// @Summary Rename
// @Description Rename
// @Tags Files
// @Id files_rename
// @Produce json
// @Param id path string true "ID"
// @Param body body service.FileRenameOptions true "Body"
// @Success 200 {object} service.File
// @Failure 404 {object} errorpkg.ErrorResponse
// @Failure 500 {object} errorpkg.ErrorResponse
// @Router /files/{id}/rename [post]
func (r *FileRouter) Rename(c *fiber.Ctx) error {
userID := GetUserID(c)
opts := new(service.FileRenameOptions)
if err := c.BodyParser(opts); err != nil {
return err
}
if err := validator.New().Struct(opts); err != nil {
return errorpkg.NewRequestBodyValidationError(err)
}
res, err := r.fileSvc.Rename(c.Params("id"), opts.Name, userID)
if err != nil {
return err
}
return c.JSON(res)
}
// Delete godoc
//
// @Summary Delete
// @Description Delete
// @Tags Files
// @Id files_delete
// @Produce json
// @Param id path string true "ID"
// @Failure 404 {object} errorpkg.ErrorResponse
// @Failure 500 {object} errorpkg.ErrorResponse
// @Router /files/{id} [delete]
func (r *FileRouter) Delete(c *fiber.Ctx) error {
userID := GetUserID(c)
_, err := r.fileSvc.Delete([]string{c.Params("id")}, userID)
if err != nil {
return err
}
return c.SendStatus(http.StatusNoContent)
}
// BatchGet godoc
//
// @Summary Batch get
// @Description Batch get
// @Tags Files
// @Id files_batch_get
// @Produce json
// @Param body body service.FileBatchGetOptions true "Body"
// @Success 200 {array} service.File
// @Failure 500 {object} errorpkg.ErrorResponse
// @Router /files/batch_get [post]
func (r *FileRouter) BatchGet(c *fiber.Ctx) error {
userID := GetUserID(c)
opts := new(service.FileBatchGetOptions)
if err := c.BodyParser(opts); err != nil {
return err
}
if err := validator.New().Struct(opts); err != nil {
return errorpkg.NewRequestBodyValidationError(err)
}
res, err := r.fileSvc.FindByID(opts.IDs, userID)
if err != nil {
return err
}
return c.JSON(res)
}
// BatchDelete godoc
//
// @Summary Batch delete
// @Description Batch delete
// @Tags Files
// @Id files_batch_delete
// @Produce json
// @Param body body service.FileBatchDeleteOptions true "Body"
// @Success 200 {array} string
// @Failure 500 {object} errorpkg.ErrorResponse
// @Router /files/batch_delete [post]
func (r *FileRouter) BatchDelete(c *fiber.Ctx) error {
userID := GetUserID(c)
opts := new(service.FileBatchDeleteOptions)
if err := c.BodyParser(opts); err != nil {
return err
}
if err := validator.New().Struct(opts); err != nil {
return errorpkg.NewRequestBodyValidationError(err)
}
res, err := r.fileSvc.Delete(opts.IDs, userID)
if err != nil {
return err
}
return c.JSON(res)
}
// GetSize godoc
//
// @Summary Get size
// @Description Get size
// @Tags Files
// @Id files_get_size
// @Produce json
// @Param id path string true "ID"
// @Success 200 {object} int
// @Failure 404 {object} errorpkg.ErrorResponse
// @Failure 500 {object} errorpkg.ErrorResponse
// @Router /files/{id}/get_size [get]
func (r *FileRouter) GetSize(c *fiber.Ctx) error {
userID := GetUserID(c)
id := c.Params("id")
res, err := r.fileSvc.GetSize(id, userID)
if err != nil {
return err
}
return c.JSON(res)
}
// GetItemCount godoc
//
// @Summary Get children count
// @Description Get children count
// @Tags Files
// @Id files_get_children_count
// @Produce json
// @Param id path string true "ID"
// @Success 200 {object} int
// @Failure 404 {object} errorpkg.ErrorResponse
// @Failure 500 {object} errorpkg.ErrorResponse
// @Router /files/{id}/get_item_count [get]
func (r *FileRouter) GetItemCount(c *fiber.Ctx) error {
userID := GetUserID(c)
res, err := r.fileSvc.GetItemCount(c.Params("id"), userID)
if err != nil {
return err
}
return c.JSON(res)
}
// GrantUserPermission godoc
//
// @Summary Grant user permission
// @Description Grant user permission
// @Tags Files
// @Id files_grant_user_permission
// @Produce json
// @Param id path string true "ID"
// @Param body body service.FileGrantUserPermissionOptions true "Body"
// @Failure 404 {object} errorpkg.ErrorResponse
// @Failure 500 {object} errorpkg.ErrorResponse
// @Router /files/grant_user_permission [post]
func (r *FileRouter) GrantUserPermission(c *fiber.Ctx) error {
userID := GetUserID(c)
opts := new(service.FileGrantUserPermissionOptions)
if err := c.BodyParser(opts); err != nil {
return err
}
if err := validator.New().Struct(opts); err != nil {
return errorpkg.NewRequestBodyValidationError(err)
}
if err := r.fileSvc.GrantUserPermission(opts.IDs, opts.UserID, opts.Permission, userID); err != nil {
return err
}
return c.SendStatus(http.StatusNoContent)
}
// RevokeUserPermission godoc
//
// @Summary Revoke user permission
// @Description Revoke user permission
// @Tags Files
// @Id files_revoke_user_permission
// @Produce json
// @Param id path string true "ID"
// @Param body body service.FileRevokeUserPermissionOptions true "Body"
// @Failure 404 {object} errorpkg.ErrorResponse
// @Failure 500 {object} errorpkg.ErrorResponse
// @Router /files/revoke_user_permission [post]
func (r *FileRouter) RevokeUserPermission(c *fiber.Ctx) error {
userID := GetUserID(c)
opts := new(service.FileRevokeUserPermissionOptions)
if err := c.BodyParser(opts); err != nil {
return err
}
if err := validator.New().Struct(opts); err != nil {
return errorpkg.NewRequestBodyValidationError(err)
}
if err := r.fileSvc.RevokeUserPermission(opts.IDs, opts.UserID, userID); err != nil {
return err
}
return c.SendStatus(http.StatusNoContent)
}
// GrantGroupPermission godoc
//
// @Summary Grant group permission
// @Description Grant group permission
// @Tags Files
// @Id files_grant_group_permission
// @Produce json
// @Param id path string true "ID"
// @Param body body service.FileGrantGroupPermissionOptions true "Body"
// @Failure 404 {object} errorpkg.ErrorResponse
// @Failure 500 {object} errorpkg.ErrorResponse
// @Router /files/grant_group_permission [post]
func (r *FileRouter) GrantGroupPermission(c *fiber.Ctx) error {
userID := GetUserID(c)
opts := new(service.FileGrantGroupPermissionOptions)
if err := c.BodyParser(opts); err != nil {
return err
}
if err := validator.New().Struct(opts); err != nil {
return errorpkg.NewRequestBodyValidationError(err)
}
if err := r.fileSvc.GrantGroupPermission(opts.IDs, opts.GroupID, opts.Permission, userID); err != nil {
return err
}
return c.SendStatus(http.StatusNoContent)
}
// RevokeGroupPermission godoc
//
// @Summary Revoke group permission
// @Description Revoke group permission
// @Tags Files
// @Id files_revoke_group_permission
// @Produce json
// @Param id path string true "ID"
// @Param body body service.FileRevokeGroupPermissionOptions true "Body"
// @Failure 404 {object} errorpkg.ErrorResponse
// @Failure 500 {object} errorpkg.ErrorResponse
// @Router /files/{id}/revoke_group_permission [post]
func (r *FileRouter) RevokeGroupPermission(c *fiber.Ctx) error {
userID := GetUserID(c)
opts := new(service.FileRevokeGroupPermissionOptions)
if err := c.BodyParser(opts); err != nil {
return err
}
if err := validator.New().Struct(opts); err != nil {
return errorpkg.NewRequestBodyValidationError(err)
}
if err := r.fileSvc.RevokeGroupPermission(opts.IDs, opts.GroupID, userID); err != nil {
return err
}
return c.SendStatus(http.StatusNoContent)
}
// GetUserPermissions godoc
//
// @Summary Get user permissions
// @Description Get user permissions
// @Tags Files
// @Id files_get_user_permissions
// @Produce json
// @Param id path string true "ID"
// @Success 200 {array} service.UserPermission
// @Failure 404 {object} errorpkg.ErrorResponse
// @Failure 500 {object} errorpkg.ErrorResponse
// @Router /files/{id}/get_user_permissions [get]
func (r *FileRouter) GetUserPermissions(c *fiber.Ctx) error {
userID := GetUserID(c)
res, err := r.fileSvc.GetUserPermissions(c.Params("id"), userID)
if err != nil {
return err
}
return c.JSON(res)
}
// GetGroupPermissions godoc
//
// @Summary Get group permissions
// @Description Get group permissions
// @Tags Files
// @Id files_get_group_permissions
// @Produce json
// @Param id path string true "ID"
// @Success 200 {array} service.GroupPermission
// @Failure 404 {object} errorpkg.ErrorResponse
// @Failure 500 {object} errorpkg.ErrorResponse
// @Router /files/{id}/get_group_permissions [get]
func (r *FileRouter) GetGroupPermissions(c *fiber.Ctx) error {
userID := GetUserID(c)
res, err := r.fileSvc.GetGroupPermissions(c.Params("id"), userID)
if err != nil {
return err
}
return c.JSON(res)
}
type FileDownloadRouter struct {
fileSvc *service.FileService
accessTokenCookieName string
}
func NewFileDownloadRouter() *FileDownloadRouter {
return &FileDownloadRouter{
fileSvc: service.NewFileService(),
accessTokenCookieName: "voltaserve_access_token",
}
}
func (r *FileDownloadRouter) AppendNonJWTRoutes(g fiber.Router) {
g.Get("/:id/original:ext", r.DownloadOriginal)
g.Get("/:id/preview:ext", r.DownloadPreview)
}
// DownloadOriginal godoc
//
// @Summary Download original
// @Description Download original
// @Tags Files
// @Id files_download_original
// @Produce json
// @Param id path string true "ID"
// @Param access_token query string true "Access Token"
// @Failure 404 {object} errorpkg.ErrorResponse
// @Failure 500 {object} errorpkg.ErrorResponse
// @Router /files/{id}/original{ext} [get]
func (r *FileDownloadRouter) DownloadOriginal(c *fiber.Ctx) error {
accessToken := c.Cookies(r.accessTokenCookieName)
if accessToken == "" {
accessToken = c.Query("access_token")
if accessToken == "" {
return errorpkg.NewFileNotFoundError(nil)
}
}
userID, err := r.getUserID(accessToken)
if err != nil {
return c.SendStatus(http.StatusNotFound)
}
buf, file, snapshot, err := r.fileSvc.DownloadOriginalBuffer(c.Params("id"), userID)
if err != nil {
return err
}
if filepath.Ext(snapshot.GetOriginal().Key) != c.Params("ext") {
return errorpkg.NewS3ObjectNotFoundError(nil)
}
bytes := buf.Bytes()
c.Set("Content-Type", infra.DetectMimeFromBytes(bytes))
c.Set("Content-Disposition", fmt.Sprintf("filename=\"%s\"", file.GetName()))
return c.Send(bytes)
}
// DownloadPreview godoc
//
// @Summary Download preview
// @Description Download preview
// @Tags Files
// @Id files_download_preview
// @Produce json
// @Param id path string true "ID"
// @Param access_token query string true "Access Token"
// @Failure 404 {object} errorpkg.ErrorResponse
// @Failure 500 {object} errorpkg.ErrorResponse
// @Router /files/{id}/preview{ext} [get]
func (r *FileDownloadRouter) DownloadPreview(c *fiber.Ctx) error {
accessToken := c.Cookies(r.accessTokenCookieName)
if accessToken == "" {
accessToken = c.Query("access_token")
if accessToken == "" {
return errorpkg.NewFileNotFoundError(nil)
}
}
userID, err := r.getUserID(accessToken)
if err != nil {
return c.SendStatus(http.StatusNotFound)
}
buf, file, snapshot, err := r.fileSvc.DownloadPreviewBuffer(c.Params("id"), userID)
if err != nil {
return err
}
if filepath.Ext(snapshot.GetPreview().Key) != c.Params("ext") {
return errorpkg.NewS3ObjectNotFoundError(nil)
}
bytes := buf.Bytes()
c.Set("Content-Type", infra.DetectMimeFromBytes(bytes))
c.Set("Content-Disposition", fmt.Sprintf("filename=\"%s\"", file.GetName()))
return c.Send(bytes)
}
func (r *FileDownloadRouter) getUserID(accessToken string) (string, error) {
token, err := jwt.Parse(accessToken, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return []byte(config.GetConfig().Security.JWTSigningKey), nil
})
if err != nil {
return "", err
}
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
return claims["sub"].(string), nil
} else {
return "", errors.New("cannot find sub claim")
}
}
type ConversionWebhookRouter struct {
fileSvc *service.FileService
}
func NewConversionWebhookRouter() *ConversionWebhookRouter {
return &ConversionWebhookRouter{
fileSvc: service.NewFileService(),
}
}
func (r *ConversionWebhookRouter) AppendInternalRoutes(g fiber.Router) {
g.Post("/conversion_webhook/update_snapshot", r.UpdateSnapshot)
}
// UpdateSnapshot godoc
//
// @Summary Update snapshot
// @Description Update snapshot
// @Tags Files
// @Id files_conversion_webhook_update_snapshot
// @Produce json
// @Param body body service.SnapshotUpdateOptions true "Body"
// @Success 201
// @Failure 401 {object} errorpkg.ErrorResponse
// @Failure 500 {object} errorpkg.ErrorResponse
// @Router /files/conversion_webhook/update_snapshot [post]
func (r *ConversionWebhookRouter) UpdateSnapshot(c *fiber.Ctx) error {
apiKey := c.Query("api_key")
if apiKey == "" {
return errorpkg.NewMissingQueryParamError("api_key")
}
opts := new(service.SnapshotUpdateOptions)
if err := c.BodyParser(opts); err != nil {
return err
}
if err := validator.New().Struct(opts); err != nil {
return errorpkg.NewRequestBodyValidationError(err)
}
if err := r.fileSvc.UpdateSnapshot(*opts, apiKey); err != nil {
return err
}
return c.SendStatus(204)
}