2022-08-24 11:10:50 +08:00
package v1
import (
2022-12-01 10:36:49 +08:00
"errors"
2022-09-03 18:41:52 +08:00
"fmt"
2023-04-07 11:30:10 +08:00
"io"
2023-03-15 12:58:56 +08:00
"net/http"
2023-05-30 00:00:56 +08:00
"net/url"
2023-03-15 12:58:56 +08:00
"os"
"path"
"path/filepath"
"strconv"
"strings"
2024-04-09 16:36:09 +08:00
2023-03-11 19:55:37 +08:00
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/backend/app/dto"
2023-01-04 22:31:51 +08:00
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
"github.com/1Panel-dev/1Panel/backend/app/dto/response"
"github.com/1Panel-dev/1Panel/backend/buserr"
2022-10-17 16:32:31 +08:00
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
2023-03-13 16:44:39 +08:00
"github.com/1Panel-dev/1Panel/backend/utils/files"
2022-10-17 16:32:31 +08:00
websocket2 "github.com/1Panel-dev/1Panel/backend/utils/websocket"
2022-08-24 11:10:50 +08:00
"github.com/gin-gonic/gin"
2022-09-14 19:09:39 +08:00
"github.com/gorilla/websocket"
2022-08-24 11:10:50 +08:00
)
2023-01-04 22:31:51 +08:00
// @Tags File
2023-01-05 11:57:03 +08:00
// @Summary List files
2023-01-04 22:31:51 +08:00
// @Description 获取文件列表
// @Accept json
// @Param request body request.FileOption true "request"
// @Success 200 {object} response.FileInfo
// @Security ApiKeyAuth
// @Router /files/search [post]
2022-08-24 11:10:50 +08:00
func ( b * BaseApi ) ListFiles ( c * gin . Context ) {
2022-12-14 15:39:13 +08:00
var req request . FileOption
2023-10-27 12:04:11 +08:00
if err := helper . CheckBindAndValidate ( & req , c ) ; err != nil {
2022-08-24 11:10:50 +08:00
return
}
files , err := fileService . GetFileList ( req )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
helper . SuccessWithData ( c , files )
}
2022-08-24 17:34:21 +08:00
2023-03-02 13:54:07 +08:00
// @Tags File
// @Summary Page file
// @Description 分页获取上传文件
// @Accept json
// @Param request body request.SearchUploadWithPage true "request"
2023-07-06 18:04:22 +08:00
// @Success 200 {array} response.FileInfo
2023-03-02 13:54:07 +08:00
// @Security ApiKeyAuth
// @Router /files/upload/search [post]
func ( b * BaseApi ) SearchUploadWithPage ( c * gin . Context ) {
var req request . SearchUploadWithPage
2023-10-27 12:04:11 +08:00
if err := helper . CheckBindAndValidate ( & req , c ) ; err != nil {
2023-03-02 13:54:07 +08:00
return
}
total , files , err := fileService . SearchUploadWithPage ( req )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
helper . SuccessWithData ( c , dto . PageResult {
Items : files ,
Total : total ,
} )
}
2023-01-04 22:31:51 +08:00
// @Tags File
// @Summary Load files tree
// @Description 加载文件树
// @Accept json
// @Param request body request.FileOption true "request"
2023-07-06 18:04:22 +08:00
// @Success 200 {array} response.FileTree
2023-01-04 22:31:51 +08:00
// @Security ApiKeyAuth
// @Router /files/tree [post]
2022-08-24 17:34:21 +08:00
func ( b * BaseApi ) GetFileTree ( c * gin . Context ) {
2022-12-14 15:39:13 +08:00
var req request . FileOption
2023-10-27 12:04:11 +08:00
if err := helper . CheckBindAndValidate ( & req , c ) ; err != nil {
2022-08-24 17:34:21 +08:00
return
}
tree , err := fileService . GetFileTree ( req )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
helper . SuccessWithData ( c , tree )
}
2022-08-25 17:54:52 +08:00
2023-01-04 22:31:51 +08:00
// @Tags File
// @Summary Create file
// @Description 创建文件/文件夹
// @Accept json
// @Param request body request.FileCreate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /files [post]
2023-10-07 15:46:44 +08:00
// @x-panel-log {"bodyKeys":["path"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"创建文件/文件夹 [path]","formatEN":"Create dir or file [path]"}
2022-08-25 17:54:52 +08:00
func ( b * BaseApi ) CreateFile ( c * gin . Context ) {
2022-12-14 15:39:13 +08:00
var req request . FileCreate
2023-10-27 12:04:11 +08:00
if err := helper . CheckBindAndValidate ( & req , c ) ; err != nil {
2022-08-25 17:54:52 +08:00
return
}
err := fileService . Create ( req )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
helper . SuccessWithData ( c , nil )
}
2022-08-25 18:48:03 +08:00
2023-01-04 22:31:51 +08:00
// @Tags File
// @Summary Delete file
// @Description 删除文件/文件夹
// @Accept json
// @Param request body request.FileDelete true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /files/del [post]
2023-10-07 15:46:44 +08:00
// @x-panel-log {"bodyKeys":["path"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"删除文件/文件夹 [path]","formatEN":"Delete dir or file [path]"}
2022-08-25 18:48:03 +08:00
func ( b * BaseApi ) DeleteFile ( c * gin . Context ) {
2022-12-14 15:39:13 +08:00
var req request . FileDelete
2023-10-27 12:04:11 +08:00
if err := helper . CheckBindAndValidate ( & req , c ) ; err != nil {
2022-08-25 18:48:03 +08:00
return
}
err := fileService . Delete ( req )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
helper . SuccessWithData ( c , nil )
}
2022-08-29 15:26:36 +08:00
2023-01-04 22:31:51 +08:00
// @Tags File
// @Summary Batch delete file
// @Description 批量删除文件/文件夹
// @Accept json
// @Param request body request.FileBatchDelete true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /files/batch/del [post]
2023-10-07 15:46:44 +08:00
// @x-panel-log {"bodyKeys":["paths"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"批量删除文件/文件夹 [paths]","formatEN":"Batch delete dir or file [paths]"}
2022-12-01 10:36:49 +08:00
func ( b * BaseApi ) BatchDeleteFile ( c * gin . Context ) {
2022-12-14 15:39:13 +08:00
var req request . FileBatchDelete
2023-10-27 12:04:11 +08:00
if err := helper . CheckBindAndValidate ( & req , c ) ; err != nil {
2022-12-01 10:36:49 +08:00
return
}
err := fileService . BatchDelete ( req )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
helper . SuccessWithData ( c , nil )
}
2023-01-04 22:31:51 +08:00
// @Tags File
// @Summary Change file mode
// @Description 修改文件权限
// @Accept json
// @Param request body request.FileCreate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /files/mode [post]
2023-10-07 15:46:44 +08:00
// @x-panel-log {"bodyKeys":["path","mode"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改权限 [paths] => [mode]","formatEN":"Change mode [paths] => [mode]"}
2022-08-29 15:26:36 +08:00
func ( b * BaseApi ) ChangeFileMode ( c * gin . Context ) {
2022-12-14 15:39:13 +08:00
var req request . FileCreate
2023-10-27 12:04:11 +08:00
if err := helper . CheckBindAndValidate ( & req , c ) ; err != nil {
2022-08-29 15:26:36 +08:00
return
}
err := fileService . ChangeMode ( req )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
2023-04-26 22:18:16 +08:00
helper . SuccessWithOutData ( c )
}
// @Tags File
// @Summary Change file owner
// @Description 修改文件用户/组
// @Accept json
// @Param request body request.FileRoleUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /files/owner [post]
2023-10-07 15:46:44 +08:00
// @x-panel-log {"bodyKeys":["path","user","group"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改用户/组 [paths] => [user]/[group]","formatEN":"Change owner [paths] => [user]/[group]"}
2023-04-26 22:18:16 +08:00
func ( b * BaseApi ) ChangeFileOwner ( c * gin . Context ) {
var req request . FileRoleUpdate
2023-10-27 12:04:11 +08:00
if err := helper . CheckBindAndValidate ( & req , c ) ; err != nil {
2023-04-26 22:18:16 +08:00
return
}
if err := fileService . ChangeOwner ( req ) ; err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
helper . SuccessWithOutData ( c )
2022-08-29 15:26:36 +08:00
}
2022-08-30 17:59:59 +08:00
2023-01-04 22:31:51 +08:00
// @Tags File
// @Summary Compress file
// @Description 压缩文件
// @Accept json
// @Param request body request.FileCompress true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /files/compress [post]
2023-10-07 15:46:44 +08:00
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"压缩文件 [name]","formatEN":"Compress file [name]"}
2022-08-30 17:59:59 +08:00
func ( b * BaseApi ) CompressFile ( c * gin . Context ) {
2022-12-14 15:39:13 +08:00
var req request . FileCompress
2023-10-27 12:04:11 +08:00
if err := helper . CheckBindAndValidate ( & req , c ) ; err != nil {
2022-08-30 17:59:59 +08:00
return
}
err := fileService . Compress ( req )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
helper . SuccessWithData ( c , nil )
}
2022-08-31 13:59:02 +08:00
2023-01-04 22:31:51 +08:00
// @Tags File
// @Summary Decompress file
// @Description 解压文件
// @Accept json
// @Param request body request.FileDeCompress true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /files/decompress [post]
2023-10-07 15:46:44 +08:00
// @x-panel-log {"bodyKeys":["path"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"解压 [path]","formatEN":"Decompress file [path]"}
2022-08-31 13:59:02 +08:00
func ( b * BaseApi ) DeCompressFile ( c * gin . Context ) {
2022-12-14 15:39:13 +08:00
var req request . FileDeCompress
2023-10-27 12:04:11 +08:00
if err := helper . CheckBindAndValidate ( & req , c ) ; err != nil {
2022-08-31 13:59:02 +08:00
return
}
err := fileService . DeCompress ( req )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
helper . SuccessWithData ( c , nil )
}
2022-09-01 19:02:33 +08:00
2023-01-04 22:31:51 +08:00
// @Tags File
// @Summary Load file content
// @Description 获取文件内容
// @Accept json
2023-10-27 15:27:25 +08:00
// @Param request body request.FileContentReq true "request"
2023-01-04 22:31:51 +08:00
// @Success 200 {object} response.FileInfo
// @Security ApiKeyAuth
// @Router /files/content [post]
2023-10-07 15:46:44 +08:00
// @x-panel-log {"bodyKeys":["path"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"获取文件内容 [path]","formatEN":"Load file content [path]"}
2022-09-01 19:02:33 +08:00
func ( b * BaseApi ) GetContent ( c * gin . Context ) {
2023-10-27 15:27:25 +08:00
var req request . FileContentReq
2023-10-27 12:04:11 +08:00
if err := helper . CheckBindAndValidate ( & req , c ) ; err != nil {
2022-09-01 19:02:33 +08:00
return
}
info , err := fileService . GetContent ( req )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
helper . SuccessWithData ( c , info )
}
2023-01-04 22:31:51 +08:00
// @Tags File
// @Summary Update file content
// @Description 更新文件内容
// @Accept json
// @Param request body request.FileEdit true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /files/save [post]
2023-10-07 15:46:44 +08:00
// @x-panel-log {"bodyKeys":["path"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新文件内容 [path]","formatEN":"Update file content [path]"}
2022-09-01 19:02:33 +08:00
func ( b * BaseApi ) SaveContent ( c * gin . Context ) {
2022-12-14 15:39:13 +08:00
var req request . FileEdit
2023-10-27 12:04:11 +08:00
if err := helper . CheckBindAndValidate ( & req , c ) ; err != nil {
2022-09-01 19:02:33 +08:00
return
}
if err := fileService . SaveContent ( req ) ; err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
helper . SuccessWithData ( c , nil )
}
2022-09-03 18:41:52 +08:00
2023-01-04 22:31:51 +08:00
// @Tags File
// @Summary Upload file
// @Description 上传文件
// @Param file formData file true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /files/upload [post]
2023-10-07 15:46:44 +08:00
// @x-panel-log {"bodyKeys":["path"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"上传文件 [path]","formatEN":"Upload file [path]"}
2022-09-03 18:41:52 +08:00
func ( b * BaseApi ) UploadFiles ( c * gin . Context ) {
form , err := c . MultipartForm ( )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrBadRequest , constant . ErrTypeInvalidParams , err )
return
}
2024-04-09 16:36:09 +08:00
uploadFiles := form . File [ "file" ]
2022-09-03 18:41:52 +08:00
paths := form . Value [ "path" ]
2024-04-09 16:36:09 +08:00
2024-04-03 14:37:22 +08:00
overwrite := true
if ow , ok := form . Value [ "overwrite" ] ; ok {
if len ( ow ) != 0 {
parseBool , _ := strconv . ParseBool ( ow [ 0 ] )
overwrite = parseBool
}
}
2024-04-09 16:36:09 +08:00
2022-12-01 10:36:49 +08:00
if len ( paths ) == 0 || ! strings . Contains ( paths [ 0 ] , "/" ) {
helper . ErrorWithDetail ( c , constant . CodeErrBadRequest , constant . ErrTypeInvalidParams , errors . New ( "error paths in request" ) )
return
}
2022-12-16 16:43:07 +08:00
dir := path . Dir ( paths [ 0 ] )
2024-04-09 16:36:09 +08:00
info , err := os . Stat ( dir )
if err != nil && os . IsNotExist ( err ) {
mode , err := files . GetParentMode ( dir )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
if err = os . MkdirAll ( dir , mode ) ; err != nil {
2023-02-21 19:06:24 +08:00
helper . ErrorWithDetail ( c , constant . CodeErrBadRequest , constant . ErrTypeInvalidParams , fmt . Errorf ( "mkdir %s failed, err: %v" , dir , err ) )
return
2022-12-01 10:36:49 +08:00
}
}
2024-04-09 16:36:09 +08:00
info , err = os . Stat ( dir )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
mode := info . Mode ( )
fileOp := files . NewFileOp ( )
2022-09-03 18:41:52 +08:00
success := 0
2022-12-16 16:43:07 +08:00
failures := make ( buserr . MultiErr )
2024-04-09 16:36:09 +08:00
for _ , file := range uploadFiles {
2024-04-03 14:37:22 +08:00
dstFilename := path . Join ( paths [ 0 ] , file . Filename )
2024-04-09 16:36:09 +08:00
dstDir := path . Dir ( dstFilename )
if ! fileOp . Stat ( dstDir ) {
if err = fileOp . CreateDir ( dstDir , mode ) ; err != nil {
e := fmt . Errorf ( "create dir [%s] failed, err: %v" , path . Dir ( dstFilename ) , err )
failures [ file . Filename ] = e
global . LOG . Error ( e )
continue
}
}
2024-04-03 14:37:22 +08:00
tmpFilename := dstFilename + ".tmp"
if err := c . SaveUploadedFile ( file , tmpFilename ) ; err != nil {
_ = os . Remove ( tmpFilename )
2022-12-16 16:43:07 +08:00
e := fmt . Errorf ( "upload [%s] file failed, err: %v" , file . Filename , err )
failures [ file . Filename ] = e
global . LOG . Error ( e )
2022-09-03 18:41:52 +08:00
continue
}
2024-04-09 16:36:09 +08:00
dstInfo , statErr := os . Stat ( dstFilename )
2024-04-03 14:37:22 +08:00
if overwrite {
_ = os . Remove ( dstFilename )
}
2024-04-09 16:36:09 +08:00
2024-04-03 14:37:22 +08:00
err = os . Rename ( tmpFilename , dstFilename )
if err != nil {
_ = os . Remove ( tmpFilename )
e := fmt . Errorf ( "upload [%s] file failed, err: %v" , file . Filename , err )
failures [ file . Filename ] = e
global . LOG . Error ( e )
continue
}
if statErr == nil {
2024-04-09 16:36:09 +08:00
_ = os . Chmod ( dstFilename , dstInfo . Mode ( ) )
} else {
_ = os . Chmod ( dstFilename , mode )
2024-04-03 14:37:22 +08:00
}
2022-09-03 18:41:52 +08:00
success ++
}
2022-12-16 16:43:07 +08:00
if success == 0 {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , failures )
} else {
helper . SuccessWithMsg ( c , fmt . Sprintf ( "%d files upload success" , success ) )
}
2022-09-03 18:41:52 +08:00
}
2022-09-03 22:22:40 +08:00
2023-03-08 15:33:43 +08:00
// @Tags File
// @Summary Check file exist
// @Description 检测文件是否存在
// @Accept json
// @Param request body request.FilePathCheck true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /files/check [post]
func ( b * BaseApi ) CheckFile ( c * gin . Context ) {
var req request . FilePathCheck
2023-10-27 12:04:11 +08:00
if err := helper . CheckBindAndValidate ( & req , c ) ; err != nil {
2023-03-08 15:33:43 +08:00
return
}
2023-11-01 18:27:19 +08:00
if _ , err := os . Stat ( req . Path ) ; err != nil {
helper . SuccessWithData ( c , false )
2023-03-08 15:33:43 +08:00
return
}
2023-11-01 18:27:19 +08:00
helper . SuccessWithData ( c , true )
2023-03-08 15:33:43 +08:00
}
2023-01-04 22:31:51 +08:00
// @Tags File
// @Summary Change file name
// @Description 修改文件名称
// @Accept json
// @Param request body request.FileRename true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /files/rename [post]
2023-10-07 15:46:44 +08:00
// @x-panel-log {"bodyKeys":["oldName","newName"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"重命名 [oldName] => [newName]","formatEN":"Rename [oldName] => [newName]"}
2022-09-06 17:48:49 +08:00
func ( b * BaseApi ) ChangeFileName ( c * gin . Context ) {
2022-12-14 15:39:13 +08:00
var req request . FileRename
2023-10-27 12:04:11 +08:00
if err := helper . CheckBindAndValidate ( & req , c ) ; err != nil {
2022-09-03 22:22:40 +08:00
return
}
if err := fileService . ChangeName ( req ) ; err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
helper . SuccessWithData ( c , nil )
}
2022-09-05 16:25:26 +08:00
2023-01-04 22:31:51 +08:00
// @Tags File
// @Summary Wget file
// @Description 下载远端文件
// @Accept json
// @Param request body request.FileWget true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /files/wget [post]
2023-10-07 15:46:44 +08:00
// @x-panel-log {"bodyKeys":["url","path","name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"下载 url => [path]/[name]","formatEN":"Download url => [path]/[name]"}
2022-09-06 17:48:49 +08:00
func ( b * BaseApi ) WgetFile ( c * gin . Context ) {
2022-12-14 15:39:13 +08:00
var req request . FileWget
2023-10-27 12:04:11 +08:00
if err := helper . CheckBindAndValidate ( & req , c ) ; err != nil {
2022-09-05 16:25:26 +08:00
return
}
2022-09-14 19:09:39 +08:00
key , err := fileService . Wget ( req )
if err != nil {
2022-09-05 16:25:26 +08:00
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
2022-12-14 15:39:13 +08:00
helper . SuccessWithData ( c , response . FileWgetRes {
2022-09-14 19:09:39 +08:00
Key : key ,
} )
2022-09-05 16:25:26 +08:00
}
2022-09-06 10:35:35 +08:00
2023-01-04 22:31:51 +08:00
// @Tags File
// @Summary Move file
// @Description 移动文件
// @Accept json
// @Param request body request.FileMove true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /files/move [post]
2023-10-07 15:46:44 +08:00
// @x-panel-log {"bodyKeys":["oldPaths","newPath"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"移动文件 [oldPaths] => [newPath]","formatEN":"Move [oldPaths] => [newPath]"}
2022-09-06 17:48:49 +08:00
func ( b * BaseApi ) MoveFile ( c * gin . Context ) {
2022-12-14 15:39:13 +08:00
var req request . FileMove
2023-10-27 12:04:11 +08:00
if err := helper . CheckBindAndValidate ( & req , c ) ; err != nil {
2022-09-06 10:35:35 +08:00
return
}
if err := fileService . MvFile ( req ) ; err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
helper . SuccessWithData ( c , nil )
}
2022-09-06 17:48:49 +08:00
2023-01-04 22:31:51 +08:00
// @Tags File
// @Summary Download file
// @Description 下载文件
// @Accept json
// @Success 200
// @Security ApiKeyAuth
2024-01-31 20:23:59 +08:00
// @Router /files/download [get]
2022-09-06 17:48:49 +08:00
func ( b * BaseApi ) Download ( c * gin . Context ) {
2023-05-30 00:00:56 +08:00
filePath := c . Query ( "path" )
file , err := os . Open ( filePath )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
}
info , _ := file . Stat ( )
c . Header ( "Content-Length" , strconv . FormatInt ( info . Size ( ) , 10 ) )
c . Header ( "Content-Disposition" , "attachment; filename*=utf-8''" + url . PathEscape ( info . Name ( ) ) )
http . ServeContent ( c . Writer , c . Request , info . Name ( ) , info . ModTime ( ) , file )
}
// @Tags File
// @Summary Chunk Download file
// @Description 分片下载下载文件
// @Accept json
// @Param request body request.FileDownload true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /files/chunkdownload [post]
2023-10-07 15:46:44 +08:00
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"下载文件 [name]","formatEN":"Download file [name]"}
2023-05-30 00:00:56 +08:00
func ( b * BaseApi ) DownloadChunkFiles ( c * gin . Context ) {
var req request . FileChunkDownload
2023-10-27 12:04:11 +08:00
if err := helper . CheckBindAndValidate ( & req , c ) ; err != nil {
2022-09-06 17:48:49 +08:00
return
}
2023-05-30 00:00:56 +08:00
fileOp := files . NewFileOp ( )
if ! fileOp . Stat ( req . Path ) {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrPathNotFound , nil )
return
}
filePath := req . Path
fstFile , err := fileOp . OpenFile ( filePath )
2022-09-06 17:48:49 +08:00
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
2023-05-30 00:00:56 +08:00
info , err := fstFile . Stat ( )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
if info . IsDir ( ) {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrFileDownloadDir , err )
return
}
2024-04-09 16:36:09 +08:00
2023-05-30 00:00:56 +08:00
c . Writer . Header ( ) . Set ( "Content-Disposition" , fmt . Sprintf ( "attachment; filename=%s" , req . Name ) )
c . Writer . Header ( ) . Set ( "Content-Type" , "application/octet-stream" )
c . Writer . Header ( ) . Set ( "Content-Length" , strconv . FormatInt ( info . Size ( ) , 10 ) )
c . Writer . Header ( ) . Set ( "Accept-Ranges" , "bytes" )
2024-04-09 16:36:09 +08:00
2023-05-30 00:00:56 +08:00
if c . Request . Header . Get ( "Range" ) != "" {
rangeHeader := c . Request . Header . Get ( "Range" )
rangeArr := strings . Split ( rangeHeader , "=" ) [ 1 ]
rangeParts := strings . Split ( rangeArr , "-" )
2024-04-09 16:36:09 +08:00
2023-05-30 00:00:56 +08:00
startPos , _ := strconv . ParseInt ( rangeParts [ 0 ] , 10 , 64 )
2024-04-09 16:36:09 +08:00
2023-05-30 00:00:56 +08:00
var endPos int64
if rangeParts [ 1 ] == "" {
endPos = info . Size ( ) - 1
} else {
endPos , _ = strconv . ParseInt ( rangeParts [ 1 ] , 10 , 64 )
}
2024-04-09 16:36:09 +08:00
2023-05-30 00:00:56 +08:00
c . Writer . Header ( ) . Set ( "Content-Range" , fmt . Sprintf ( "bytes %d-%d/%d" , startPos , endPos , info . Size ( ) ) )
c . Writer . WriteHeader ( http . StatusPartialContent )
2024-04-09 16:36:09 +08:00
2023-05-30 00:00:56 +08:00
buffer := make ( [ ] byte , 1024 * 1024 )
file , err := os . Open ( filePath )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
defer file . Close ( )
2024-04-09 16:36:09 +08:00
2023-07-04 11:38:10 +08:00
_ , _ = file . Seek ( startPos , 0 )
2023-05-30 00:00:56 +08:00
reader := io . LimitReader ( file , endPos - startPos + 1 )
_ , err = io . CopyBuffer ( c . Writer , reader , buffer )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
} else {
c . File ( filePath )
}
2022-09-06 17:48:49 +08:00
}
2022-09-09 18:10:41 +08:00
2023-01-04 22:31:51 +08:00
// @Tags File
// @Summary Load file size
// @Description 获取文件夹大小
// @Accept json
// @Param request body request.DirSizeReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /files/size [post]
2023-10-07 15:46:44 +08:00
// @x-panel-log {"bodyKeys":["path"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"获取文件夹大小 [path]","formatEN":"Load file size [path]"}
2022-09-09 18:10:41 +08:00
func ( b * BaseApi ) Size ( c * gin . Context ) {
2022-12-14 15:39:13 +08:00
var req request . DirSizeReq
2023-10-27 12:04:11 +08:00
if err := helper . CheckBindAndValidate ( & req , c ) ; err != nil {
2022-09-09 18:10:41 +08:00
return
}
res , err := fileService . DirSize ( req )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
helper . SuccessWithData ( c , res )
}
2022-09-14 19:09:39 +08:00
2023-03-13 16:44:39 +08:00
func mergeChunks ( fileName string , fileDir string , dstDir string , chunkCount int ) error {
2023-10-24 22:27:32 +08:00
op := files . NewFileOp ( )
dstDir = strings . TrimSpace ( dstDir )
2024-04-09 16:36:09 +08:00
mode , _ := files . GetParentMode ( dstDir )
if mode == 0 {
mode = os . ModePerm
}
2023-10-24 22:27:32 +08:00
if _ , err := os . Stat ( dstDir ) ; err != nil && os . IsNotExist ( err ) {
2024-04-09 16:36:09 +08:00
if err = op . CreateDir ( dstDir , mode ) ; err != nil {
2023-03-22 15:20:30 +08:00
return err
}
}
2024-04-09 16:36:09 +08:00
targetFile , err := os . OpenFile ( filepath . Join ( dstDir , fileName ) , os . O_RDWR | os . O_CREATE , mode )
2023-03-13 16:44:39 +08:00
if err != nil {
return err
}
2024-04-09 16:36:09 +08:00
2023-03-13 16:44:39 +08:00
for i := 0 ; i < chunkCount ; i ++ {
chunkPath := filepath . Join ( fileDir , fmt . Sprintf ( "%s.%d" , fileName , i ) )
2023-04-07 11:30:10 +08:00
chunkData , err := os . ReadFile ( chunkPath )
2023-03-13 16:44:39 +08:00
if err != nil {
return err
}
_ , err = targetFile . Write ( chunkData )
if err != nil {
return err
}
}
2024-04-09 16:36:09 +08:00
2023-03-13 16:44:39 +08:00
return files . NewFileOp ( ) . DeleteDir ( fileDir )
}
// @Tags File
// @Summary ChunkUpload file
// @Description 分片上传文件
// @Param file formData file true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /files/chunkupload [post]
func ( b * BaseApi ) UploadChunkFiles ( c * gin . Context ) {
2023-05-27 23:15:30 +08:00
var err error
2023-03-13 16:44:39 +08:00
fileForm , err := c . FormFile ( "chunk" )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrBadRequest , constant . ErrTypeInvalidParams , err )
return
}
uploadFile , err := fileForm . Open ( )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrBadRequest , constant . ErrTypeInvalidParams , err )
return
}
chunkIndex , err := strconv . Atoi ( c . PostForm ( "chunkIndex" ) )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrBadRequest , constant . ErrTypeInvalidParams , err )
return
}
chunkCount , err := strconv . Atoi ( c . PostForm ( "chunkCount" ) )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrBadRequest , constant . ErrTypeInvalidParams , err )
return
}
fileOp := files . NewFileOp ( )
2023-03-22 18:34:29 +08:00
tmpDir := path . Join ( global . CONF . System . TmpDir , "upload" )
if ! fileOp . Stat ( tmpDir ) {
if err := fileOp . CreateDir ( tmpDir , 0755 ) ; err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrBadRequest , constant . ErrTypeInvalidParams , err )
return
}
2023-03-13 16:44:39 +08:00
}
filename := c . PostForm ( "filename" )
2023-03-22 18:34:29 +08:00
fileDir := filepath . Join ( tmpDir , filename )
2023-07-11 18:19:20 +08:00
if chunkIndex == 0 {
if fileOp . Stat ( fileDir ) {
_ = fileOp . DeleteDir ( fileDir )
}
_ = os . MkdirAll ( fileDir , 0755 )
}
2023-03-13 16:44:39 +08:00
filePath := filepath . Join ( fileDir , filename )
2024-04-09 16:36:09 +08:00
2023-05-27 23:15:30 +08:00
defer func ( ) {
if err != nil {
_ = os . Remove ( fileDir )
}
} ( )
var (
emptyFile * os . File
chunkData [ ] byte
)
2024-04-09 16:36:09 +08:00
2023-05-27 23:15:30 +08:00
emptyFile , err = os . Create ( filePath )
2023-03-13 16:44:39 +08:00
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrBadRequest , constant . ErrTypeInvalidParams , err )
return
}
2023-03-22 18:34:29 +08:00
defer emptyFile . Close ( )
2024-04-09 16:36:09 +08:00
2023-05-27 23:15:30 +08:00
chunkData , err = io . ReadAll ( uploadFile )
2023-03-13 16:44:39 +08:00
if err != nil {
2023-05-27 23:15:30 +08:00
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , buserr . WithMap ( constant . ErrFileUpload , map [ string ] interface { } { "name" : filename , "detail" : err . Error ( ) } , err ) )
2023-03-13 16:44:39 +08:00
return
}
2024-04-09 16:36:09 +08:00
2023-03-13 16:44:39 +08:00
chunkPath := filepath . Join ( fileDir , fmt . Sprintf ( "%s.%d" , filename , chunkIndex ) )
2023-04-07 11:30:10 +08:00
err = os . WriteFile ( chunkPath , chunkData , 0644 )
2023-03-13 16:44:39 +08:00
if err != nil {
2023-05-27 23:15:30 +08:00
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , buserr . WithMap ( constant . ErrFileUpload , map [ string ] interface { } { "name" : filename , "detail" : err . Error ( ) } , err ) )
2023-03-13 16:44:39 +08:00
return
}
2024-04-09 16:36:09 +08:00
2023-03-13 16:44:39 +08:00
if chunkIndex + 1 == chunkCount {
err = mergeChunks ( filename , fileDir , c . PostForm ( "path" ) , chunkCount )
if err != nil {
2023-05-27 23:15:30 +08:00
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , buserr . WithMap ( constant . ErrFileUpload , map [ string ] interface { } { "name" : filename , "detail" : err . Error ( ) } , err ) )
2023-03-13 16:44:39 +08:00
return
}
helper . SuccessWithData ( c , true )
} else {
return
}
}
2022-09-14 19:09:39 +08:00
var wsUpgrade = websocket . Upgrader {
CheckOrigin : func ( r * http . Request ) bool {
return true
} ,
}
func ( b * BaseApi ) Ws ( c * gin . Context ) {
ws , err := wsUpgrade . Upgrade ( c . Writer , c . Request , nil )
if err != nil {
return
}
2023-06-28 14:50:10 +08:00
wsClient := websocket2 . NewWsClient ( "fileClient" , ws )
2022-09-14 19:09:39 +08:00
go wsClient . Read ( )
go wsClient . Write ( )
}
func ( b * BaseApi ) Keys ( c * gin . Context ) {
2022-12-14 15:39:13 +08:00
res := & response . FileProcessKeys { }
2022-09-14 19:09:39 +08:00
keys , err := global . CACHE . PrefixScanKey ( "file-wget-" )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
res . Keys = keys
helper . SuccessWithData ( c , res )
}
2023-11-01 18:30:56 +08:00
// @Tags File
// @Summary Read file by Line
2023-11-23 11:00:08 +08:00
// @Description 按行读取日志文件
2023-11-01 18:30:56 +08:00
// @Param request body request.FileReadByLineReq true "request"
// @Success 200
// @Security ApiKeyAuth
2024-01-25 18:18:26 +08:00
// @Router /files/read [post]
2023-11-01 18:30:56 +08:00
func ( b * BaseApi ) ReadFileByLine ( c * gin . Context ) {
var req request . FileReadByLineReq
if err := helper . CheckBindAndValidate ( & req , c ) ; err != nil {
return
}
2023-11-23 11:00:08 +08:00
res , err := fileService . ReadLogByLine ( req )
2023-11-01 18:30:56 +08:00
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
2023-11-23 11:00:08 +08:00
return
2023-11-01 18:30:56 +08:00
}
helper . SuccessWithData ( c , res )
}
2023-11-02 18:38:50 +08:00
// @Tags File
// @Summary Batch change file mode and owner
// @Description 批量修改文件权限和用户/组
// @Accept json
// @Param request body request.FileRoleReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /files/batch/role [post]
// @x-panel-log {"bodyKeys":["paths","mode","user","group"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"批量修改文件权限和用户/组 [paths] => [mode]/[user]/[group]","formatEN":"Batch change file mode and owner [paths] => [mode]/[user]/[group]"}
func ( b * BaseApi ) BatchChangeModeAndOwner ( c * gin . Context ) {
var req request . FileRoleReq
if err := helper . CheckBindAndValidate ( & req , c ) ; err != nil {
return
}
if err := fileService . BatchChangeModeAndOwner ( req ) ; err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
}
helper . SuccessWithOutData ( c )
}