mirror of
https://github.com/datarhei/core.git
synced 2025-10-05 16:07:07 +08:00
Add PUT /api/v3/fs endpoint for file operations
This commit is contained in:
86
docs/docs.go
86
docs/docs.go
@@ -321,6 +321,9 @@ const docTemplate = `{
|
|||||||
"produces": [
|
"produces": [
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
|
"tags": [
|
||||||
|
"v16.12.0"
|
||||||
|
],
|
||||||
"summary": "List all registered filesystems",
|
"summary": "List all registered filesystems",
|
||||||
"operationId": "filesystem-3-list",
|
"operationId": "filesystem-3-list",
|
||||||
"responses": {
|
"responses": {
|
||||||
@@ -334,6 +337,56 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"put": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Execute file operations (copy or move) between registered filesystems",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"v16.?.?"
|
||||||
|
],
|
||||||
|
"summary": "File operations between filesystems",
|
||||||
|
"operationId": "filesystem-3-file-operation",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "Filesystem operation",
|
||||||
|
"name": "config",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/api.FilesystemOperation"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/api.Error"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Not Found",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/api.Error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/api/v3/fs/{name}": {
|
"/api/v3/fs/{name}": {
|
||||||
@@ -347,6 +400,9 @@ const docTemplate = `{
|
|||||||
"produces": [
|
"produces": [
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
|
"tags": [
|
||||||
|
"v16.7.2"
|
||||||
|
],
|
||||||
"summary": "List all files on a filesystem",
|
"summary": "List all files on a filesystem",
|
||||||
"operationId": "filesystem-3-list-files",
|
"operationId": "filesystem-3-list-files",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -401,6 +457,9 @@ const docTemplate = `{
|
|||||||
"application/data",
|
"application/data",
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
|
"tags": [
|
||||||
|
"v16.7.2"
|
||||||
|
],
|
||||||
"summary": "Fetch a file from a filesystem",
|
"summary": "Fetch a file from a filesystem",
|
||||||
"operationId": "filesystem-3-get-file",
|
"operationId": "filesystem-3-get-file",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -454,6 +513,9 @@ const docTemplate = `{
|
|||||||
"text/plain",
|
"text/plain",
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
|
"tags": [
|
||||||
|
"v16.7.2"
|
||||||
|
],
|
||||||
"summary": "Add a file to a filesystem",
|
"summary": "Add a file to a filesystem",
|
||||||
"operationId": "filesystem-3-put-file",
|
"operationId": "filesystem-3-put-file",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -515,6 +577,9 @@ const docTemplate = `{
|
|||||||
"produces": [
|
"produces": [
|
||||||
"text/plain"
|
"text/plain"
|
||||||
],
|
],
|
||||||
|
"tags": [
|
||||||
|
"v16.7.2"
|
||||||
|
],
|
||||||
"summary": "Remove a file from a filesystem",
|
"summary": "Remove a file from a filesystem",
|
||||||
"operationId": "filesystem-3-delete-file",
|
"operationId": "filesystem-3-delete-file",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -2785,6 +2850,27 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"api.FilesystemOperation": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"operation"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"from": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"operation": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"copy",
|
||||||
|
"move"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"to": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"api.GraphQuery": {
|
"api.GraphQuery": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@@ -314,6 +314,9 @@
|
|||||||
"produces": [
|
"produces": [
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
|
"tags": [
|
||||||
|
"v16.12.0"
|
||||||
|
],
|
||||||
"summary": "List all registered filesystems",
|
"summary": "List all registered filesystems",
|
||||||
"operationId": "filesystem-3-list",
|
"operationId": "filesystem-3-list",
|
||||||
"responses": {
|
"responses": {
|
||||||
@@ -327,6 +330,56 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"put": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Execute file operations (copy or move) between registered filesystems",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"v16.?.?"
|
||||||
|
],
|
||||||
|
"summary": "File operations between filesystems",
|
||||||
|
"operationId": "filesystem-3-file-operation",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "Filesystem operation",
|
||||||
|
"name": "config",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/api.FilesystemOperation"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/api.Error"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Not Found",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/api.Error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/api/v3/fs/{name}": {
|
"/api/v3/fs/{name}": {
|
||||||
@@ -340,6 +393,9 @@
|
|||||||
"produces": [
|
"produces": [
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
|
"tags": [
|
||||||
|
"v16.7.2"
|
||||||
|
],
|
||||||
"summary": "List all files on a filesystem",
|
"summary": "List all files on a filesystem",
|
||||||
"operationId": "filesystem-3-list-files",
|
"operationId": "filesystem-3-list-files",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -394,6 +450,9 @@
|
|||||||
"application/data",
|
"application/data",
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
|
"tags": [
|
||||||
|
"v16.7.2"
|
||||||
|
],
|
||||||
"summary": "Fetch a file from a filesystem",
|
"summary": "Fetch a file from a filesystem",
|
||||||
"operationId": "filesystem-3-get-file",
|
"operationId": "filesystem-3-get-file",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -447,6 +506,9 @@
|
|||||||
"text/plain",
|
"text/plain",
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
|
"tags": [
|
||||||
|
"v16.7.2"
|
||||||
|
],
|
||||||
"summary": "Add a file to a filesystem",
|
"summary": "Add a file to a filesystem",
|
||||||
"operationId": "filesystem-3-put-file",
|
"operationId": "filesystem-3-put-file",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -508,6 +570,9 @@
|
|||||||
"produces": [
|
"produces": [
|
||||||
"text/plain"
|
"text/plain"
|
||||||
],
|
],
|
||||||
|
"tags": [
|
||||||
|
"v16.7.2"
|
||||||
|
],
|
||||||
"summary": "Remove a file from a filesystem",
|
"summary": "Remove a file from a filesystem",
|
||||||
"operationId": "filesystem-3-delete-file",
|
"operationId": "filesystem-3-delete-file",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -2778,6 +2843,27 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"api.FilesystemOperation": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"operation"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"from": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"operation": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"copy",
|
||||||
|
"move"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"to": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"api.GraphQuery": {
|
"api.GraphQuery": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@@ -456,6 +456,20 @@ definitions:
|
|||||||
type:
|
type:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
|
api.FilesystemOperation:
|
||||||
|
properties:
|
||||||
|
from:
|
||||||
|
type: string
|
||||||
|
operation:
|
||||||
|
enum:
|
||||||
|
- copy
|
||||||
|
- move
|
||||||
|
type: string
|
||||||
|
to:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- operation
|
||||||
|
type: object
|
||||||
api.GraphQuery:
|
api.GraphQuery:
|
||||||
properties:
|
properties:
|
||||||
query:
|
query:
|
||||||
@@ -2142,6 +2156,40 @@ paths:
|
|||||||
security:
|
security:
|
||||||
- ApiKeyAuth: []
|
- ApiKeyAuth: []
|
||||||
summary: List all registered filesystems
|
summary: List all registered filesystems
|
||||||
|
tags:
|
||||||
|
- v16.12.0
|
||||||
|
put:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: Execute file operations (copy or move) between registered filesystems
|
||||||
|
operationId: filesystem-3-file-operation
|
||||||
|
parameters:
|
||||||
|
- description: Filesystem operation
|
||||||
|
in: body
|
||||||
|
name: config
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/api.FilesystemOperation'
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/api.Error'
|
||||||
|
"404":
|
||||||
|
description: Not Found
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/api.Error'
|
||||||
|
security:
|
||||||
|
- ApiKeyAuth: []
|
||||||
|
summary: File operations between filesystems
|
||||||
|
tags:
|
||||||
|
- v16.?.?
|
||||||
/api/v3/fs/{name}:
|
/api/v3/fs/{name}:
|
||||||
get:
|
get:
|
||||||
description: List all files on a filesystem. The listing can be ordered by name,
|
description: List all files on a filesystem. The listing can be ordered by name,
|
||||||
@@ -2177,6 +2225,8 @@ paths:
|
|||||||
security:
|
security:
|
||||||
- ApiKeyAuth: []
|
- ApiKeyAuth: []
|
||||||
summary: List all files on a filesystem
|
summary: List all files on a filesystem
|
||||||
|
tags:
|
||||||
|
- v16.7.2
|
||||||
/api/v3/fs/{name}/{path}:
|
/api/v3/fs/{name}/{path}:
|
||||||
delete:
|
delete:
|
||||||
description: Remove a file from a filesystem
|
description: Remove a file from a filesystem
|
||||||
@@ -2206,6 +2256,8 @@ paths:
|
|||||||
security:
|
security:
|
||||||
- ApiKeyAuth: []
|
- ApiKeyAuth: []
|
||||||
summary: Remove a file from a filesystem
|
summary: Remove a file from a filesystem
|
||||||
|
tags:
|
||||||
|
- v16.7.2
|
||||||
get:
|
get:
|
||||||
description: Fetch a file from a filesystem
|
description: Fetch a file from a filesystem
|
||||||
operationId: filesystem-3-get-file
|
operationId: filesystem-3-get-file
|
||||||
@@ -2239,6 +2291,8 @@ paths:
|
|||||||
security:
|
security:
|
||||||
- ApiKeyAuth: []
|
- ApiKeyAuth: []
|
||||||
summary: Fetch a file from a filesystem
|
summary: Fetch a file from a filesystem
|
||||||
|
tags:
|
||||||
|
- v16.7.2
|
||||||
put:
|
put:
|
||||||
consumes:
|
consumes:
|
||||||
- application/data
|
- application/data
|
||||||
@@ -2282,6 +2336,8 @@ paths:
|
|||||||
security:
|
security:
|
||||||
- ApiKeyAuth: []
|
- ApiKeyAuth: []
|
||||||
summary: Add a file to a filesystem
|
summary: Add a file to a filesystem
|
||||||
|
tags:
|
||||||
|
- v16.7.2
|
||||||
/api/v3/log:
|
/api/v3/log:
|
||||||
get:
|
get:
|
||||||
description: Get the last log lines of the Restreamer application
|
description: Get the last log lines of the Restreamer application
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
// FileInfo represents informatiion about a file on a filesystem
|
// FileInfo represents information about a file on a filesystem
|
||||||
type FileInfo struct {
|
type FileInfo struct {
|
||||||
Name string `json:"name" jsonschema:"minLength=1"`
|
Name string `json:"name" jsonschema:"minLength=1"`
|
||||||
Size int64 `json:"size_bytes" jsonschema:"minimum=0" format:"int64"`
|
Size int64 `json:"size_bytes" jsonschema:"minimum=0" format:"int64"`
|
||||||
@@ -13,3 +13,10 @@ type FilesystemInfo struct {
|
|||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Mount string `json:"mount"`
|
Mount string `json:"mount"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FilesystemOperation represents a file operation on one or more filesystems
|
||||||
|
type FilesystemOperation struct {
|
||||||
|
Operation string `json:"operation" validate:"required" enums:"copy,move" jsonschema:"enum=copy,enum=move"`
|
||||||
|
From string `json:"from"`
|
||||||
|
To string `json:"to"`
|
||||||
|
}
|
||||||
|
@@ -2,6 +2,8 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/datarhei/core/v16/http/api"
|
"github.com/datarhei/core/v16/http/api"
|
||||||
"github.com/datarhei/core/v16/http/handler"
|
"github.com/datarhei/core/v16/http/handler"
|
||||||
@@ -32,6 +34,7 @@ func NewFS(filesystems map[string]FSConfig) *FSHandler {
|
|||||||
// @Summary Fetch a file from a filesystem
|
// @Summary Fetch a file from a filesystem
|
||||||
// @Description Fetch a file from a filesystem
|
// @Description Fetch a file from a filesystem
|
||||||
// @ID filesystem-3-get-file
|
// @ID filesystem-3-get-file
|
||||||
|
// @Tags v16.7.2
|
||||||
// @Produce application/data
|
// @Produce application/data
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param name path string true "Name of the filesystem"
|
// @Param name path string true "Name of the filesystem"
|
||||||
@@ -56,6 +59,7 @@ func (h *FSHandler) GetFile(c echo.Context) error {
|
|||||||
// @Summary Add a file to a filesystem
|
// @Summary Add a file to a filesystem
|
||||||
// @Description Writes or overwrites a file on a filesystem
|
// @Description Writes or overwrites a file on a filesystem
|
||||||
// @ID filesystem-3-put-file
|
// @ID filesystem-3-put-file
|
||||||
|
// @Tags v16.7.2
|
||||||
// @Accept application/data
|
// @Accept application/data
|
||||||
// @Produce text/plain
|
// @Produce text/plain
|
||||||
// @Produce json
|
// @Produce json
|
||||||
@@ -82,6 +86,7 @@ func (h *FSHandler) PutFile(c echo.Context) error {
|
|||||||
// @Summary Remove a file from a filesystem
|
// @Summary Remove a file from a filesystem
|
||||||
// @Description Remove a file from a filesystem
|
// @Description Remove a file from a filesystem
|
||||||
// @ID filesystem-3-delete-file
|
// @ID filesystem-3-delete-file
|
||||||
|
// @Tags v16.7.2
|
||||||
// @Produce text/plain
|
// @Produce text/plain
|
||||||
// @Param name path string true "Name of the filesystem"
|
// @Param name path string true "Name of the filesystem"
|
||||||
// @Param path path string true "Path to file"
|
// @Param path path string true "Path to file"
|
||||||
@@ -104,6 +109,7 @@ func (h *FSHandler) DeleteFile(c echo.Context) error {
|
|||||||
// @Summary List all files on a filesystem
|
// @Summary List all files on a filesystem
|
||||||
// @Description List all files on a filesystem. The listing can be ordered by name, size, or date of last modification in ascending or descending order.
|
// @Description List all files on a filesystem. The listing can be ordered by name, size, or date of last modification in ascending or descending order.
|
||||||
// @ID filesystem-3-list-files
|
// @ID filesystem-3-list-files
|
||||||
|
// @Tags v16.7.2
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param name path string true "Name of the filesystem"
|
// @Param name path string true "Name of the filesystem"
|
||||||
// @Param glob query string false "glob pattern for file names"
|
// @Param glob query string false "glob pattern for file names"
|
||||||
@@ -127,6 +133,7 @@ func (h *FSHandler) ListFiles(c echo.Context) error {
|
|||||||
// @Summary List all registered filesystems
|
// @Summary List all registered filesystems
|
||||||
// @Description Listall registered filesystems
|
// @Description Listall registered filesystems
|
||||||
// @ID filesystem-3-list
|
// @ID filesystem-3-list
|
||||||
|
// @Tags v16.12.0
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {array} api.FilesystemInfo
|
// @Success 200 {array} api.FilesystemInfo
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
@@ -144,3 +151,73 @@ func (h *FSHandler) List(c echo.Context) error {
|
|||||||
|
|
||||||
return c.JSON(http.StatusOK, fss)
|
return c.JSON(http.StatusOK, fss)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FileOperation executes file operations between filesystems
|
||||||
|
// @Summary File operations between filesystems
|
||||||
|
// @Description Execute file operations (copy or move) between registered filesystems
|
||||||
|
// @ID filesystem-3-file-operation
|
||||||
|
// @Tags v16.?.?
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param config body api.FilesystemOperation true "Filesystem operation"
|
||||||
|
// @Success 200 {string} string
|
||||||
|
// @Failure 400 {object} api.Error
|
||||||
|
// @Failure 404 {object} api.Error
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Router /api/v3/fs [put]
|
||||||
|
func (h *FSHandler) FileOperation(c echo.Context) error {
|
||||||
|
operation := api.FilesystemOperation{}
|
||||||
|
|
||||||
|
if err := util.ShouldBindJSON(c, &operation); err != nil {
|
||||||
|
return api.Err(http.StatusBadRequest, "Invalid JSON", "%s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if operation.Operation != "copy" && operation.Operation != "move" {
|
||||||
|
return api.Err(http.StatusBadRequest, "Invalid operation", "%s", operation.Operation)
|
||||||
|
}
|
||||||
|
|
||||||
|
from := strings.Split(filepath.Join("/", operation.From), "/")
|
||||||
|
if len(from) < 2 {
|
||||||
|
return api.Err(http.StatusBadRequest, "Invalid source path", "%s", operation.From)
|
||||||
|
}
|
||||||
|
fromFSName := from[1]
|
||||||
|
fromPath := strings.Join(from[2:], "/")
|
||||||
|
fromFS, ok := h.filesystems[fromFSName]
|
||||||
|
if !ok {
|
||||||
|
return api.Err(http.StatusBadRequest, "Source filesystem not found", "%s", fromFSName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if operation.From == operation.To {
|
||||||
|
return c.JSON(http.StatusOK, "OK")
|
||||||
|
}
|
||||||
|
|
||||||
|
to := strings.Split(filepath.Join("/", operation.To), "/")
|
||||||
|
if len(to) < 2 {
|
||||||
|
return api.Err(http.StatusBadRequest, "Invalid target path", "%s", operation.To)
|
||||||
|
}
|
||||||
|
toFSName := to[1]
|
||||||
|
toPath := strings.Join(to[2:], "/")
|
||||||
|
toFS, ok := h.filesystems[toFSName]
|
||||||
|
if !ok {
|
||||||
|
return api.Err(http.StatusBadRequest, "Target filesystem not found", "%s", toFSName)
|
||||||
|
}
|
||||||
|
|
||||||
|
fromFile := fromFS.Handler.FS.Filesystem.Open(fromPath)
|
||||||
|
if fromFile == nil {
|
||||||
|
return api.Err(http.StatusNotFound, "File not found", "%s:%s", fromFSName, fromPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer fromFile.Close()
|
||||||
|
|
||||||
|
_, _, err := toFS.Handler.FS.Filesystem.WriteFileReader(toPath, fromFile)
|
||||||
|
if err != nil {
|
||||||
|
toFS.Handler.FS.Filesystem.Remove(toPath)
|
||||||
|
return api.Err(http.StatusBadRequest, "Writing target file failed", "%s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if operation.Operation == "move" {
|
||||||
|
fromFS.Handler.FS.Filesystem.Remove(fromPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(http.StatusOK, "OK")
|
||||||
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -16,36 +17,25 @@ import (
|
|||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getDummyFilesystemsHandler(filesystem httpfs.FS) (*FSHandler, error) {
|
func getDummyFilesystemsHandler(filesystems []httpfs.FS) (*FSHandler, error) {
|
||||||
handler := NewFS(map[string]FSConfig{
|
config := map[string]FSConfig{}
|
||||||
filesystem.Name: {
|
|
||||||
Type: filesystem.Filesystem.Type(),
|
for _, fs := range filesystems {
|
||||||
Mountpoint: filesystem.Mountpoint,
|
config[fs.Name] = FSConfig{
|
||||||
Handler: handler.NewFS(filesystem),
|
Type: fs.Filesystem.Type(),
|
||||||
},
|
Mountpoint: fs.Mountpoint,
|
||||||
})
|
Handler: handler.NewFS(fs),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handler := NewFS(config)
|
||||||
|
|
||||||
return handler, nil
|
return handler, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDummyFilesystemsRouter(filesystem fs.Filesystem) (*echo.Echo, error) {
|
func getDummyFilesystemsRouter(filesystems []httpfs.FS) (*echo.Echo, error) {
|
||||||
router := mock.DummyEcho()
|
router := mock.DummyEcho()
|
||||||
|
|
||||||
fs := httpfs.FS{
|
handler, err := getDummyFilesystemsHandler(filesystems)
|
||||||
Name: "foo",
|
|
||||||
Mountpoint: "/",
|
|
||||||
AllowWrite: true,
|
|
||||||
EnableAuth: false,
|
|
||||||
Username: "",
|
|
||||||
Password: "",
|
|
||||||
DefaultFile: "",
|
|
||||||
DefaultContentType: "text/html",
|
|
||||||
Gzip: false,
|
|
||||||
Filesystem: filesystem,
|
|
||||||
Cache: nil,
|
|
||||||
}
|
|
||||||
|
|
||||||
handler, err := getDummyFilesystemsHandler(fs)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -55,6 +45,7 @@ func getDummyFilesystemsRouter(filesystem fs.Filesystem) (*echo.Echo, error) {
|
|||||||
router.DELETE("/:name/*", handler.DeleteFile)
|
router.DELETE("/:name/*", handler.DeleteFile)
|
||||||
router.GET("/:name", handler.ListFiles)
|
router.GET("/:name", handler.ListFiles)
|
||||||
router.GET("/", handler.List)
|
router.GET("/", handler.List)
|
||||||
|
router.PUT("/", handler.FileOperation)
|
||||||
|
|
||||||
return router, nil
|
return router, nil
|
||||||
}
|
}
|
||||||
@@ -63,7 +54,16 @@ func TestFilesystems(t *testing.T) {
|
|||||||
memfs, err := fs.NewMemFilesystem(fs.MemConfig{})
|
memfs, err := fs.NewMemFilesystem(fs.MemConfig{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
router, err := getDummyFilesystemsRouter(memfs)
|
filesystems := []httpfs.FS{
|
||||||
|
{
|
||||||
|
Name: "foo",
|
||||||
|
Mountpoint: "/",
|
||||||
|
AllowWrite: true,
|
||||||
|
Filesystem: memfs,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
router, err := getDummyFilesystemsRouter(filesystems)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
response := mock.Request(t, http.StatusOK, router, "GET", "/", nil)
|
response := mock.Request(t, http.StatusOK, router, "GET", "/", nil)
|
||||||
@@ -139,3 +139,125 @@ func TestFilesystems(t *testing.T) {
|
|||||||
|
|
||||||
require.Equal(t, 0, len(l))
|
require.Equal(t, 0, len(l))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFileOperation(t *testing.T) {
|
||||||
|
memfs1, err := fs.NewMemFilesystem(fs.MemConfig{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
memfs2, err := fs.NewMemFilesystem(fs.MemConfig{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
filesystems := []httpfs.FS{
|
||||||
|
{
|
||||||
|
Name: "foo",
|
||||||
|
Mountpoint: "/foo",
|
||||||
|
AllowWrite: true,
|
||||||
|
Filesystem: memfs1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "bar",
|
||||||
|
Mountpoint: "/bar",
|
||||||
|
AllowWrite: true,
|
||||||
|
Filesystem: memfs2,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
router, err := getDummyFilesystemsRouter(filesystems)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
mock.Request(t, http.StatusNotFound, router, "GET", "/foo/file", nil)
|
||||||
|
mock.Request(t, http.StatusNotFound, router, "GET", "/bar/file", nil)
|
||||||
|
|
||||||
|
data := mock.Read(t, "./fixtures/addProcess.json")
|
||||||
|
require.NotNil(t, data)
|
||||||
|
|
||||||
|
mock.Request(t, http.StatusCreated, router, "PUT", "/foo/file", data)
|
||||||
|
mock.Request(t, http.StatusOK, router, "GET", "/foo/file", nil)
|
||||||
|
mock.Request(t, http.StatusNotFound, router, "GET", "/bar/file", nil)
|
||||||
|
|
||||||
|
op := api.FilesystemOperation{}
|
||||||
|
|
||||||
|
jsondata, err := json.Marshal(op)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
mock.Request(t, http.StatusBadRequest, router, "PUT", "/", bytes.NewReader(jsondata))
|
||||||
|
|
||||||
|
op = api.FilesystemOperation{
|
||||||
|
Operation: "copy",
|
||||||
|
}
|
||||||
|
|
||||||
|
jsondata, err = json.Marshal(op)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
mock.Request(t, http.StatusBadRequest, router, "PUT", "/", bytes.NewReader(jsondata))
|
||||||
|
|
||||||
|
op = api.FilesystemOperation{
|
||||||
|
Operation: "copy",
|
||||||
|
From: "foo/elif",
|
||||||
|
}
|
||||||
|
|
||||||
|
jsondata, err = json.Marshal(op)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
mock.Request(t, http.StatusBadRequest, router, "PUT", "/", bytes.NewReader(jsondata))
|
||||||
|
|
||||||
|
op = api.FilesystemOperation{
|
||||||
|
Operation: "copy",
|
||||||
|
From: "foo/elif",
|
||||||
|
To: "/bar",
|
||||||
|
}
|
||||||
|
|
||||||
|
jsondata, err = json.Marshal(op)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
mock.Request(t, http.StatusNotFound, router, "PUT", "/", bytes.NewReader(jsondata))
|
||||||
|
|
||||||
|
op = api.FilesystemOperation{
|
||||||
|
Operation: "copy",
|
||||||
|
From: "foo/file",
|
||||||
|
To: "/bar",
|
||||||
|
}
|
||||||
|
|
||||||
|
jsondata, err = json.Marshal(op)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
mock.Request(t, http.StatusBadRequest, router, "PUT", "/", bytes.NewReader(jsondata))
|
||||||
|
|
||||||
|
op = api.FilesystemOperation{
|
||||||
|
Operation: "copy",
|
||||||
|
From: "foo/file",
|
||||||
|
To: "/bar/file",
|
||||||
|
}
|
||||||
|
|
||||||
|
jsondata, err = json.Marshal(op)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
mock.Request(t, http.StatusOK, router, "PUT", "/", bytes.NewReader(jsondata))
|
||||||
|
|
||||||
|
mock.Request(t, http.StatusOK, router, "GET", "/foo/file", nil)
|
||||||
|
response := mock.Request(t, http.StatusOK, router, "GET", "/bar/file", nil)
|
||||||
|
|
||||||
|
filedata, err := io.ReadAll(mock.Read(t, "./fixtures/addProcess.json"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Equal(t, filedata, response.Raw)
|
||||||
|
|
||||||
|
op = api.FilesystemOperation{
|
||||||
|
Operation: "move",
|
||||||
|
From: "foo/file",
|
||||||
|
To: "/bar/file",
|
||||||
|
}
|
||||||
|
|
||||||
|
jsondata, err = json.Marshal(op)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
mock.Request(t, http.StatusOK, router, "PUT", "/", bytes.NewReader(jsondata))
|
||||||
|
|
||||||
|
mock.Request(t, http.StatusNotFound, router, "GET", "/foo/file", nil)
|
||||||
|
response = mock.Request(t, http.StatusOK, router, "GET", "/bar/file", nil)
|
||||||
|
|
||||||
|
filedata, err = io.ReadAll(mock.Read(t, "./fixtures/addProcess.json"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Equal(t, filedata, response.Raw)
|
||||||
|
}
|
||||||
|
@@ -14,13 +14,13 @@ import (
|
|||||||
|
|
||||||
// The FSHandler type provides handlers for manipulating a filesystem
|
// The FSHandler type provides handlers for manipulating a filesystem
|
||||||
type FSHandler struct {
|
type FSHandler struct {
|
||||||
fs fs.FS
|
FS fs.FS
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFS return a new FSHandler type. You have to provide a filesystem to act on.
|
// NewFS return a new FSHandler type. You have to provide a filesystem to act on.
|
||||||
func NewFS(fs fs.FS) *FSHandler {
|
func NewFS(fs fs.FS) *FSHandler {
|
||||||
return &FSHandler{
|
return &FSHandler{
|
||||||
fs: fs,
|
FS: fs,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,20 +30,20 @@ func (h *FSHandler) GetFile(c echo.Context) error {
|
|||||||
mimeType := c.Response().Header().Get(echo.HeaderContentType)
|
mimeType := c.Response().Header().Get(echo.HeaderContentType)
|
||||||
c.Response().Header().Del(echo.HeaderContentType)
|
c.Response().Header().Del(echo.HeaderContentType)
|
||||||
|
|
||||||
file := h.fs.Filesystem.Open(path)
|
file := h.FS.Filesystem.Open(path)
|
||||||
if file == nil {
|
if file == nil {
|
||||||
return api.Err(http.StatusNotFound, "File not found", path)
|
return api.Err(http.StatusNotFound, "File not found", path)
|
||||||
}
|
}
|
||||||
|
|
||||||
stat, _ := file.Stat()
|
stat, _ := file.Stat()
|
||||||
|
|
||||||
if len(h.fs.DefaultFile) != 0 {
|
if len(h.FS.DefaultFile) != 0 {
|
||||||
if stat.IsDir() {
|
if stat.IsDir() {
|
||||||
path = filepath.Join(path, h.fs.DefaultFile)
|
path = filepath.Join(path, h.FS.DefaultFile)
|
||||||
|
|
||||||
file.Close()
|
file.Close()
|
||||||
|
|
||||||
file = h.fs.Filesystem.Open(path)
|
file = h.FS.Filesystem.Open(path)
|
||||||
if file == nil {
|
if file == nil {
|
||||||
return api.Err(http.StatusNotFound, "File not found", path)
|
return api.Err(http.StatusNotFound, "File not found", path)
|
||||||
}
|
}
|
||||||
@@ -82,13 +82,13 @@ func (h *FSHandler) PutFile(c echo.Context) error {
|
|||||||
|
|
||||||
req := c.Request()
|
req := c.Request()
|
||||||
|
|
||||||
_, created, err := h.fs.Filesystem.WriteFileReader(path, req.Body)
|
_, created, err := h.FS.Filesystem.WriteFileReader(path, req.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return api.Err(http.StatusBadRequest, "Bad request", "%s", err)
|
return api.Err(http.StatusBadRequest, "Bad request", "%s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if h.fs.Cache != nil {
|
if h.FS.Cache != nil {
|
||||||
h.fs.Cache.Delete(path)
|
h.FS.Cache.Delete(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Response().Header().Set("Content-Location", req.URL.RequestURI())
|
c.Response().Header().Set("Content-Location", req.URL.RequestURI())
|
||||||
@@ -105,14 +105,14 @@ func (h *FSHandler) DeleteFile(c echo.Context) error {
|
|||||||
|
|
||||||
c.Response().Header().Del(echo.HeaderContentType)
|
c.Response().Header().Del(echo.HeaderContentType)
|
||||||
|
|
||||||
size := h.fs.Filesystem.Remove(path)
|
size := h.FS.Filesystem.Remove(path)
|
||||||
|
|
||||||
if size < 0 {
|
if size < 0 {
|
||||||
return api.Err(http.StatusNotFound, "File not found", path)
|
return api.Err(http.StatusNotFound, "File not found", path)
|
||||||
}
|
}
|
||||||
|
|
||||||
if h.fs.Cache != nil {
|
if h.FS.Cache != nil {
|
||||||
h.fs.Cache.Delete(path)
|
h.FS.Cache.Delete(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.String(http.StatusOK, "Deleted: "+path)
|
return c.String(http.StatusOK, "Deleted: "+path)
|
||||||
@@ -123,7 +123,7 @@ func (h *FSHandler) ListFiles(c echo.Context) error {
|
|||||||
sortby := util.DefaultQuery(c, "sort", "none")
|
sortby := util.DefaultQuery(c, "sort", "none")
|
||||||
order := util.DefaultQuery(c, "order", "asc")
|
order := util.DefaultQuery(c, "order", "asc")
|
||||||
|
|
||||||
files := h.fs.Filesystem.List("/", pattern)
|
files := h.FS.Filesystem.List("/", pattern)
|
||||||
|
|
||||||
var sortFunc func(i, j int) bool
|
var sortFunc func(i, j int) bool
|
||||||
|
|
||||||
|
@@ -605,6 +605,7 @@ func (s *server) setRoutesV3(v3 *echo.Group) {
|
|||||||
handler := api.NewFS(fshandlers)
|
handler := api.NewFS(fshandlers)
|
||||||
|
|
||||||
v3.GET("/fs", handler.List)
|
v3.GET("/fs", handler.List)
|
||||||
|
v3.PUT("/fs", handler.FileOperation)
|
||||||
|
|
||||||
v3.GET("/fs/:name", handler.ListFiles)
|
v3.GET("/fs/:name", handler.ListFiles)
|
||||||
v3.GET("/fs/:name/*", handler.GetFile, mwmime.NewWithConfig(mwmime.Config{
|
v3.GET("/fs/:name/*", handler.GetFile, mwmime.NewWithConfig(mwmime.Config{
|
||||||
|
@@ -87,6 +87,7 @@ func TestFilesystem(t *testing.T) {
|
|||||||
"writeFile": testWriteFile,
|
"writeFile": testWriteFile,
|
||||||
"writeFileSafe": testWriteFileSafe,
|
"writeFileSafe": testWriteFileSafe,
|
||||||
"writeFileReader": testWriteFileReader,
|
"writeFileReader": testWriteFileReader,
|
||||||
|
"writeFileDir": testWriteFileDir,
|
||||||
"delete": testDelete,
|
"delete": testDelete,
|
||||||
"files": testFiles,
|
"files": testFiles,
|
||||||
"replace": testReplace,
|
"replace": testReplace,
|
||||||
@@ -198,6 +199,11 @@ func testWriteFileReader(t *testing.T, fs Filesystem) {
|
|||||||
require.Equal(t, int64(1), cur)
|
require.Equal(t, int64(1), cur)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testWriteFileDir(t *testing.T, fs Filesystem) {
|
||||||
|
_, _, err := fs.WriteFile("/", []byte("xxxxx"))
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
func testOpen(t *testing.T, fs Filesystem) {
|
func testOpen(t *testing.T, fs Filesystem) {
|
||||||
file := fs.Open("/foobar")
|
file := fs.Open("/foobar")
|
||||||
require.Nil(t, file)
|
require.Nil(t, file)
|
||||||
|
@@ -342,6 +342,10 @@ func (fs *memFilesystem) Symlink(oldname, newname string) error {
|
|||||||
func (fs *memFilesystem) WriteFileReader(path string, r io.Reader) (int64, bool, error) {
|
func (fs *memFilesystem) WriteFileReader(path string, r io.Reader) (int64, bool, error) {
|
||||||
path = fs.cleanPath(path)
|
path = fs.cleanPath(path)
|
||||||
|
|
||||||
|
if fs.isDir(path) {
|
||||||
|
return -1, false, fmt.Errorf("path not writeable")
|
||||||
|
}
|
||||||
|
|
||||||
newFile := &memFile{
|
newFile := &memFile{
|
||||||
memFileInfo: memFileInfo{
|
memFileInfo: memFileInfo{
|
||||||
name: path,
|
name: path,
|
||||||
@@ -360,6 +364,8 @@ func (fs *memFilesystem) WriteFileReader(path string, r io.Reader) (int64, bool,
|
|||||||
"filesize_bytes": size,
|
"filesize_bytes": size,
|
||||||
"error": err,
|
"error": err,
|
||||||
}).Warn().Log("Incomplete file")
|
}).Warn().Log("Incomplete file")
|
||||||
|
|
||||||
|
return -1, false, fmt.Errorf("incomplete file")
|
||||||
}
|
}
|
||||||
|
|
||||||
newFile.size = size
|
newFile.size = size
|
||||||
|
Reference in New Issue
Block a user