2022-05-13 13:26:45 -04:00
package api
import (
"net/http"
2022-06-03 11:21:52 -04:00
"path/filepath"
2022-05-13 13:26:45 -04:00
"sort"
2022-06-23 16:13:58 -04:00
"github.com/datarhei/core/v16/http/api"
"github.com/datarhei/core/v16/http/cache"
"github.com/datarhei/core/v16/http/handler"
"github.com/datarhei/core/v16/http/handler/util"
"github.com/datarhei/core/v16/io/fs"
2022-05-13 13:26:45 -04:00
"github.com/labstack/echo/v4"
)
// The DiskFSHandler type provides handlers for manipulating a filesystem
type DiskFSHandler struct {
cache cache . Cacher
filesystem fs . Filesystem
handler * handler . DiskFSHandler
}
// NewDiskFS return a new DiskFS type. You have to provide a filesystem to act on and optionally
// a Cacher where files will be purged from if the Cacher is related to the filesystem.
func NewDiskFS ( fs fs . Filesystem , cache cache . Cacher ) * DiskFSHandler {
return & DiskFSHandler {
cache : cache ,
filesystem : fs ,
handler : handler . NewDiskFS ( fs , cache ) ,
}
}
// GetFile returns the file at the given path
// @Summary Fetch a file from the filesystem
// @Description Fetch a file from the filesystem. The contents of that file are returned.
// @ID diskfs-3-get-file
// @Produce application/data
// @Produce json
// @Param path path string true "Path to file"
// @Success 200 {file} byte
// @Success 301 {string} string
// @Failure 404 {object} api.Error
// @Security ApiKeyAuth
// @Router /api/v3/fs/disk/{path} [get]
func ( h * DiskFSHandler ) GetFile ( c echo . Context ) error {
path := util . PathWildcardParam ( c )
mimeType := c . Response ( ) . Header ( ) . Get ( echo . HeaderContentType )
c . Response ( ) . Header ( ) . Del ( echo . HeaderContentType )
file := h . filesystem . Open ( path )
if file == nil {
return api . Err ( http . StatusNotFound , "File not found" , path )
}
stat , _ := file . Stat ( )
if stat . IsDir ( ) {
return api . Err ( http . StatusNotFound , "File not found" , path )
}
defer file . Close ( )
c . Response ( ) . Header ( ) . Set ( "Last-Modified" , stat . ModTime ( ) . UTC ( ) . Format ( "Mon, 02 Jan 2006 15:04:05 GMT" ) )
if path , ok := stat . IsLink ( ) ; ok {
2022-06-03 11:21:52 -04:00
path = filepath . Clean ( "/" + path )
2022-05-13 13:26:45 -04:00
if path [ 0 ] == '/' {
path = path [ 1 : ]
}
return c . Redirect ( http . StatusMovedPermanently , path )
}
c . Response ( ) . Header ( ) . Set ( echo . HeaderContentType , mimeType )
if c . Request ( ) . Method == "HEAD" {
return c . Blob ( http . StatusOK , "application/data" , nil )
}
return c . Stream ( http . StatusOK , "application/data" , file )
}
// PutFile adds or overwrites a file at the given path
// @Summary Add a file to the filesystem
// @Description Writes or overwrites a file on the filesystem
// @ID diskfs-3-put-file
// @Accept application/data
// @Produce text/plain
// @Produce json
// @Param path path string true "Path to file"
// @Param data body []byte true "File data"
// @Success 201 {string} string
// @Success 204 {string} string
// @Failure 507 {object} api.Error
// @Security ApiKeyAuth
// @Router /api/v3/fs/disk/{path} [put]
func ( h * DiskFSHandler ) PutFile ( c echo . Context ) error {
path := util . PathWildcardParam ( c )
c . Response ( ) . Header ( ) . Del ( echo . HeaderContentType )
req := c . Request ( )
_ , created , err := h . filesystem . Store ( path , req . Body )
if err != nil {
return api . Err ( http . StatusBadRequest , "%s" , err )
}
if h . cache != nil {
h . cache . Delete ( path )
}
c . Response ( ) . Header ( ) . Set ( "Content-Location" , req . URL . RequestURI ( ) )
if created {
return c . String ( http . StatusCreated , path )
}
return c . NoContent ( http . StatusNoContent )
}
// DeleteFile removes a file from the filesystem
// @Summary Remove a file from the filesystem
// @Description Remove a file from the filesystem
// @ID diskfs-3-delete-file
// @Produce text/plain
// @Param path path string true "Path to file"
// @Success 200 {string} string
// @Failure 404 {object} api.Error
// @Security ApiKeyAuth
// @Router /api/v3/fs/disk/{path} [delete]
func ( h * DiskFSHandler ) DeleteFile ( c echo . Context ) error {
path := util . PathWildcardParam ( c )
c . Response ( ) . Header ( ) . Del ( echo . HeaderContentType )
size := h . filesystem . Delete ( path )
if size < 0 {
return api . Err ( http . StatusNotFound , "File not found" , path )
}
if h . cache != nil {
h . cache . Delete ( path )
}
return c . String ( http . StatusOK , "OK" )
}
// ListFiles lists all files on the filesystem
// @Summary List all files on the filesystem
// @Description List all files on the filesystem. The listing can be ordered by name, size, or date of last modification in ascending or descending order.
// @ID diskfs-3-list-files
// @Produce json
// @Param glob query string false "glob pattern for file names"
// @Param sort query string false "none, name, size, lastmod"
// @Param order query string false "asc, desc"
// @Success 200 {array} api.FileInfo
// @Security ApiKeyAuth
2022-08-18 03:13:00 -04:00
// @Router /api/v3/fs/disk [get]
2022-05-13 13:26:45 -04:00
func ( h * DiskFSHandler ) ListFiles ( c echo . Context ) error {
pattern := util . DefaultQuery ( c , "glob" , "" )
sortby := util . DefaultQuery ( c , "sort" , "none" )
order := util . DefaultQuery ( c , "order" , "asc" )
files := h . filesystem . List ( pattern )
var sortFunc func ( i , j int ) bool
switch sortby {
case "name" :
if order == "desc" {
sortFunc = func ( i , j int ) bool { return files [ i ] . Name ( ) > files [ j ] . Name ( ) }
} else {
sortFunc = func ( i , j int ) bool { return files [ i ] . Name ( ) < files [ j ] . Name ( ) }
}
case "size" :
if order == "desc" {
sortFunc = func ( i , j int ) bool { return files [ i ] . Size ( ) > files [ j ] . Size ( ) }
} else {
sortFunc = func ( i , j int ) bool { return files [ i ] . Size ( ) < files [ j ] . Size ( ) }
}
default :
if order == "asc" {
sortFunc = func ( i , j int ) bool { return files [ i ] . ModTime ( ) . Before ( files [ j ] . ModTime ( ) ) }
} else {
sortFunc = func ( i , j int ) bool { return files [ i ] . ModTime ( ) . After ( files [ j ] . ModTime ( ) ) }
}
}
sort . Slice ( files , sortFunc )
2022-09-08 09:00:09 -04:00
fileinfos := [ ] api . FileInfo { }
2022-05-13 13:26:45 -04:00
2022-09-08 09:00:09 -04:00
for _ , f := range files {
if f . IsDir ( ) {
continue
}
fileinfos = append ( fileinfos , api . FileInfo {
2022-05-13 13:26:45 -04:00
Name : f . Name ( ) ,
Size : f . Size ( ) ,
LastMod : f . ModTime ( ) . Unix ( ) ,
2022-09-08 09:00:09 -04:00
} )
2022-05-13 13:26:45 -04:00
}
return c . JSON ( http . StatusOK , fileinfos )
}