2024-08-07 13:52:34 +08:00
package v2
2024-07-23 14:48:37 +08:00
import (
"errors"
"fmt"
"io"
"net/http"
"net/url"
"os"
"path"
"path/filepath"
"strconv"
"strings"
2024-08-07 13:52:34 +08:00
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
2024-07-23 14:48:37 +08:00
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
"github.com/1Panel-dev/1Panel/agent/app/dto/response"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/files"
websocket2 "github.com/1Panel-dev/1Panel/agent/utils/websocket"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
)
// @Tags File
// @Summary List files
// @Description 获取文件列表
// @Accept json
// @Param request body request.FileOption true "request"
// @Success 200 {object} response.FileInfo
// @Security ApiKeyAuth
// @Router /files/search [post]
func ( b * BaseApi ) ListFiles ( c * gin . Context ) {
var req request . FileOption
if err := helper . CheckBindAndValidate ( & req , c ) ; err != nil {
return
}
files , err := fileService . GetFileList ( req )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
helper . SuccessWithData ( c , files )
}
// @Tags File
// @Summary Page file
// @Description 分页获取上传文件
// @Accept json
// @Param request body request.SearchUploadWithPage true "request"
// @Success 200 {array} response.FileInfo
// @Security ApiKeyAuth
// @Router /files/upload/search [post]
func ( b * BaseApi ) SearchUploadWithPage ( c * gin . Context ) {
var req request . SearchUploadWithPage
if err := helper . CheckBindAndValidate ( & req , c ) ; err != nil {
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 ,
} )
}
// @Tags File
// @Summary Load files tree
// @Description 加载文件树
// @Accept json
// @Param request body request.FileOption true "request"
// @Success 200 {array} response.FileTree
// @Security ApiKeyAuth
// @Router /files/tree [post]
func ( b * BaseApi ) GetFileTree ( c * gin . Context ) {
var req request . FileOption
if err := helper . CheckBindAndValidate ( & req , c ) ; err != nil {
return
}
tree , err := fileService . GetFileTree ( req )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
helper . SuccessWithData ( c , tree )
}
// @Tags File
// @Summary Create file
// @Description 创建文件/文件夹
// @Accept json
// @Param request body request.FileCreate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /files [post]
// @x-panel-log {"bodyKeys":["path"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"创建文件/文件夹 [path]","formatEN":"Create dir or file [path]"}
func ( b * BaseApi ) CreateFile ( c * gin . Context ) {
var req request . FileCreate
if err := helper . CheckBindAndValidate ( & req , c ) ; err != nil {
return
}
err := fileService . Create ( req )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
helper . SuccessWithData ( c , nil )
}
// @Tags File
// @Summary Delete file
// @Description 删除文件/文件夹
// @Accept json
// @Param request body request.FileDelete true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /files/del [post]
// @x-panel-log {"bodyKeys":["path"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"删除文件/文件夹 [path]","formatEN":"Delete dir or file [path]"}
func ( b * BaseApi ) DeleteFile ( c * gin . Context ) {
var req request . FileDelete
if err := helper . CheckBindAndValidate ( & req , c ) ; err != nil {
return
}
err := fileService . Delete ( req )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
helper . SuccessWithData ( c , nil )
}
// @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]
// @x-panel-log {"bodyKeys":["paths"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"批量删除文件/文件夹 [paths]","formatEN":"Batch delete dir or file [paths]"}
func ( b * BaseApi ) BatchDeleteFile ( c * gin . Context ) {
var req request . FileBatchDelete
if err := helper . CheckBindAndValidate ( & req , c ) ; err != nil {
return
}
err := fileService . BatchDelete ( req )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
helper . SuccessWithData ( c , nil )
}
// @Tags File
// @Summary Change file mode
// @Description 修改文件权限
// @Accept json
// @Param request body request.FileCreate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /files/mode [post]
// @x-panel-log {"bodyKeys":["path","mode"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改权限 [paths] => [mode]","formatEN":"Change mode [paths] => [mode]"}
func ( b * BaseApi ) ChangeFileMode ( c * gin . Context ) {
var req request . FileCreate
if err := helper . CheckBindAndValidate ( & req , c ) ; err != nil {
return
}
err := fileService . ChangeMode ( req )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
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]
// @x-panel-log {"bodyKeys":["path","user","group"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改用户/组 [paths] => [user]/[group]","formatEN":"Change owner [paths] => [user]/[group]"}
func ( b * BaseApi ) ChangeFileOwner ( c * gin . Context ) {
var req request . FileRoleUpdate
if err := helper . CheckBindAndValidate ( & req , c ) ; err != nil {
return
}
if err := fileService . ChangeOwner ( req ) ; err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
helper . SuccessWithOutData ( c )
}
// @Tags File
// @Summary Compress file
// @Description 压缩文件
// @Accept json
// @Param request body request.FileCompress true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /files/compress [post]
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"压缩文件 [name]","formatEN":"Compress file [name]"}
func ( b * BaseApi ) CompressFile ( c * gin . Context ) {
var req request . FileCompress
if err := helper . CheckBindAndValidate ( & req , c ) ; err != nil {
return
}
err := fileService . Compress ( req )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
helper . SuccessWithData ( c , nil )
}
// @Tags File
// @Summary Decompress file
// @Description 解压文件
// @Accept json
// @Param request body request.FileDeCompress true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /files/decompress [post]
// @x-panel-log {"bodyKeys":["path"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"解压 [path]","formatEN":"Decompress file [path]"}
func ( b * BaseApi ) DeCompressFile ( c * gin . Context ) {
var req request . FileDeCompress
if err := helper . CheckBindAndValidate ( & req , c ) ; err != nil {
return
}
err := fileService . DeCompress ( req )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
helper . SuccessWithData ( c , nil )
}
// @Tags File
// @Summary Load file content
// @Description 获取文件内容
// @Accept json
// @Param request body request.FileContentReq true "request"
// @Success 200 {object} response.FileInfo
// @Security ApiKeyAuth
// @Router /files/content [post]
// @x-panel-log {"bodyKeys":["path"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"获取文件内容 [path]","formatEN":"Load file content [path]"}
func ( b * BaseApi ) GetContent ( c * gin . Context ) {
var req request . FileContentReq
if err := helper . CheckBindAndValidate ( & req , c ) ; err != nil {
return
}
info , err := fileService . GetContent ( req )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
helper . SuccessWithData ( c , info )
}
// @Tags File
// @Summary Update file content
// @Description 更新文件内容
// @Accept json
// @Param request body request.FileEdit true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /files/save [post]
// @x-panel-log {"bodyKeys":["path"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新文件内容 [path]","formatEN":"Update file content [path]"}
func ( b * BaseApi ) SaveContent ( c * gin . Context ) {
var req request . FileEdit
if err := helper . CheckBindAndValidate ( & req , c ) ; err != nil {
return
}
if err := fileService . SaveContent ( req ) ; err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
helper . SuccessWithData ( c , nil )
}
// @Tags File
// @Summary Upload file
// @Description 上传文件
// @Param file formData file true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /files/upload [post]
// @x-panel-log {"bodyKeys":["path"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"上传文件 [path]","formatEN":"Upload file [path]"}
func ( b * BaseApi ) UploadFiles ( c * gin . Context ) {
form , err := c . MultipartForm ( )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrBadRequest , constant . ErrTypeInvalidParams , err )
return
}
uploadFiles := form . File [ "file" ]
paths := form . Value [ "path" ]
overwrite := true
if ow , ok := form . Value [ "overwrite" ] ; ok {
if len ( ow ) != 0 {
parseBool , _ := strconv . ParseBool ( ow [ 0 ] )
overwrite = parseBool
}
}
if len ( paths ) == 0 || ! strings . Contains ( paths [ 0 ] , "/" ) {
helper . ErrorWithDetail ( c , constant . CodeErrBadRequest , constant . ErrTypeInvalidParams , errors . New ( "error paths in request" ) )
return
}
dir := path . Dir ( paths [ 0 ] )
_ , 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 {
helper . ErrorWithDetail ( c , constant . CodeErrBadRequest , constant . ErrTypeInvalidParams , fmt . Errorf ( "mkdir %s failed, err: %v" , dir , err ) )
return
}
}
info , err := os . Stat ( dir )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
mode := info . Mode ( )
fileOp := files . NewFileOp ( )
success := 0
failures := make ( buserr . MultiErr )
for _ , file := range uploadFiles {
dstFilename := path . Join ( paths [ 0 ] , file . Filename )
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
}
}
tmpFilename := dstFilename + ".tmp"
if err := c . SaveUploadedFile ( file , tmpFilename ) ; 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
}
dstInfo , statErr := os . Stat ( dstFilename )
if overwrite {
_ = os . Remove ( dstFilename )
}
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 {
_ = os . Chmod ( dstFilename , dstInfo . Mode ( ) )
} else {
_ = os . Chmod ( dstFilename , mode )
}
success ++
}
if success == 0 {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , failures )
} else {
helper . SuccessWithMsg ( c , fmt . Sprintf ( "%d files upload success" , success ) )
}
}
// @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
if err := helper . CheckBindAndValidate ( & req , c ) ; err != nil {
return
}
if _ , err := os . Stat ( req . Path ) ; err != nil {
helper . SuccessWithData ( c , false )
return
}
helper . SuccessWithData ( c , true )
}
// @Tags File
// @Summary Change file name
// @Description 修改文件名称
// @Accept json
// @Param request body request.FileRename true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /files/rename [post]
// @x-panel-log {"bodyKeys":["oldName","newName"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"重命名 [oldName] => [newName]","formatEN":"Rename [oldName] => [newName]"}
func ( b * BaseApi ) ChangeFileName ( c * gin . Context ) {
var req request . FileRename
if err := helper . CheckBindAndValidate ( & req , c ) ; err != nil {
return
}
if err := fileService . ChangeName ( req ) ; err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
helper . SuccessWithData ( c , nil )
}
// @Tags File
// @Summary Wget file
// @Description 下载远端文件
// @Accept json
// @Param request body request.FileWget true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /files/wget [post]
// @x-panel-log {"bodyKeys":["url","path","name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"下载 url => [path]/[name]","formatEN":"Download url => [path]/[name]"}
func ( b * BaseApi ) WgetFile ( c * gin . Context ) {
var req request . FileWget
if err := helper . CheckBindAndValidate ( & req , c ) ; err != nil {
return
}
key , err := fileService . Wget ( req )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
helper . SuccessWithData ( c , response . FileWgetRes {
Key : key ,
} )
}
// @Tags File
// @Summary Move file
// @Description 移动文件
// @Accept json
// @Param request body request.FileMove true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /files/move [post]
// @x-panel-log {"bodyKeys":["oldPaths","newPath"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"移动文件 [oldPaths] => [newPath]","formatEN":"Move [oldPaths] => [newPath]"}
func ( b * BaseApi ) MoveFile ( c * gin . Context ) {
var req request . FileMove
if err := helper . CheckBindAndValidate ( & req , c ) ; err != nil {
return
}
if err := fileService . MvFile ( req ) ; err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
helper . SuccessWithData ( c , nil )
}
// @Tags File
// @Summary Download file
// @Description 下载文件
// @Accept json
// @Success 200
// @Security ApiKeyAuth
// @Router /files/download [get]
func ( b * BaseApi ) Download ( c * gin . Context ) {
filePath := c . Query ( "path" )
file , err := os . Open ( filePath )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
}
defer file . Close ( )
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]
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"下载文件 [name]","formatEN":"Download file [name]"}
func ( b * BaseApi ) DownloadChunkFiles ( c * gin . Context ) {
var req request . FileChunkDownload
if err := helper . CheckBindAndValidate ( & req , c ) ; err != nil {
return
}
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 )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
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
}
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" )
if c . Request . Header . Get ( "Range" ) != "" {
rangeHeader := c . Request . Header . Get ( "Range" )
rangeArr := strings . Split ( rangeHeader , "=" ) [ 1 ]
rangeParts := strings . Split ( rangeArr , "-" )
startPos , _ := strconv . ParseInt ( rangeParts [ 0 ] , 10 , 64 )
var endPos int64
if rangeParts [ 1 ] == "" {
endPos = info . Size ( ) - 1
} else {
endPos , _ = strconv . ParseInt ( rangeParts [ 1 ] , 10 , 64 )
}
c . Writer . Header ( ) . Set ( "Content-Range" , fmt . Sprintf ( "bytes %d-%d/%d" , startPos , endPos , info . Size ( ) ) )
c . Writer . WriteHeader ( http . StatusPartialContent )
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 ( )
_ , _ = file . Seek ( startPos , 0 )
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 )
}
}
// @Tags File
// @Summary Load file size
// @Description 获取文件夹大小
// @Accept json
// @Param request body request.DirSizeReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /files/size [post]
// @x-panel-log {"bodyKeys":["path"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"获取文件夹大小 [path]","formatEN":"Load file size [path]"}
func ( b * BaseApi ) Size ( c * gin . Context ) {
var req request . DirSizeReq
if err := helper . CheckBindAndValidate ( & req , c ) ; err != nil {
return
}
res , err := fileService . DirSize ( req )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
helper . SuccessWithData ( c , res )
}
func mergeChunks ( fileName string , fileDir string , dstDir string , chunkCount int , overwrite bool ) error {
defer func ( ) {
_ = os . RemoveAll ( fileDir )
} ( )
op := files . NewFileOp ( )
dstDir = strings . TrimSpace ( dstDir )
mode , _ := files . GetParentMode ( dstDir )
if mode == 0 {
mode = 0755
}
if _ , err := os . Stat ( dstDir ) ; err != nil && os . IsNotExist ( err ) {
if err = op . CreateDir ( dstDir , mode ) ; err != nil {
return err
}
}
dstFileName := filepath . Join ( dstDir , fileName )
dstInfo , statErr := os . Stat ( dstFileName )
if statErr == nil {
mode = dstInfo . Mode ( )
} else {
mode = 0644
}
if overwrite {
_ = os . Remove ( dstFileName )
}
targetFile , err := os . OpenFile ( dstFileName , os . O_RDWR | os . O_CREATE , mode )
if err != nil {
return err
}
defer targetFile . Close ( )
for i := 0 ; i < chunkCount ; i ++ {
chunkPath := filepath . Join ( fileDir , fmt . Sprintf ( "%s.%d" , fileName , i ) )
chunkData , err := os . ReadFile ( chunkPath )
if err != nil {
return err
}
_ , err = targetFile . Write ( chunkData )
if err != nil {
return err
}
_ = os . Remove ( chunkPath )
}
return nil
}
// @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 ) {
var err error
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
}
defer uploadFile . Close ( )
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 ( )
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
}
}
filename := c . PostForm ( "filename" )
fileDir := filepath . Join ( tmpDir , filename )
if chunkIndex == 0 {
if fileOp . Stat ( fileDir ) {
_ = fileOp . DeleteDir ( fileDir )
}
_ = os . MkdirAll ( fileDir , 0755 )
}
filePath := filepath . Join ( fileDir , filename )
defer func ( ) {
if err != nil {
_ = os . Remove ( fileDir )
}
} ( )
var (
emptyFile * os . File
chunkData [ ] byte
)
emptyFile , err = os . Create ( filePath )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrBadRequest , constant . ErrTypeInvalidParams , err )
return
}
defer emptyFile . Close ( )
chunkData , err = io . ReadAll ( uploadFile )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , buserr . WithMap ( constant . ErrFileUpload , map [ string ] interface { } { "name" : filename , "detail" : err . Error ( ) } , err ) )
return
}
chunkPath := filepath . Join ( fileDir , fmt . Sprintf ( "%s.%d" , filename , chunkIndex ) )
err = os . WriteFile ( chunkPath , chunkData , 0644 )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , buserr . WithMap ( constant . ErrFileUpload , map [ string ] interface { } { "name" : filename , "detail" : err . Error ( ) } , err ) )
return
}
if chunkIndex + 1 == chunkCount {
overwrite := true
if ow := c . PostForm ( "overwrite" ) ; ow != "" {
overwrite , _ = strconv . ParseBool ( ow )
}
err = mergeChunks ( filename , fileDir , c . PostForm ( "path" ) , chunkCount , overwrite )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , buserr . WithMap ( constant . ErrFileUpload , map [ string ] interface { } { "name" : filename , "detail" : err . Error ( ) } , err ) )
return
}
helper . SuccessWithData ( c , true )
} else {
return
}
}
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
}
wsClient := websocket2 . NewWsClient ( "fileClient" , ws )
go wsClient . Read ( )
go wsClient . Write ( )
}
func ( b * BaseApi ) Keys ( c * gin . Context ) {
res := & response . FileProcessKeys { }
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 )
}
// @Tags File
// @Summary Read file by Line
// @Description 按行读取日志文件
// @Param request body request.FileReadByLineReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /files/read [post]
func ( b * BaseApi ) ReadFileByLine ( c * gin . Context ) {
var req request . FileReadByLineReq
if err := helper . CheckBindAndValidate ( & req , c ) ; err != nil {
return
}
res , err := fileService . ReadLogByLine ( req )
if err != nil {
helper . ErrorWithDetail ( c , constant . CodeErrInternalServer , constant . ErrTypeInternalServer , err )
return
}
helper . SuccessWithData ( c , res )
}
// @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 )
}