2021-09-26 10:35:02 +08:00
package v1
import (
2023-02-02 03:36:59 +00:00
"errors"
2022-08-15 11:37:21 +08:00
"fmt"
2021-09-26 10:35:02 +08:00
"io"
"io/ioutil"
2022-06-08 18:19:45 +08:00
"log"
2021-09-26 10:35:02 +08:00
"net/http"
2022-06-08 18:19:45 +08:00
"net/url"
2022-04-06 12:10:51 +08:00
url2 "net/url"
2021-09-26 10:35:02 +08:00
"os"
"path"
2022-06-08 18:19:45 +08:00
"path/filepath"
2022-03-09 16:37:03 +08:00
"strconv"
2022-02-17 18:43:25 +08:00
"strings"
2022-06-08 19:31:01 +08:00
"sync"
2021-10-15 11:43:41 +08:00
2022-11-29 12:17:14 -05:00
"github.com/IceWhaleTech/CasaOS-Common/utils/logger"
2023-02-02 03:36:59 +00:00
"github.com/IceWhaleTech/CasaOS/internal/conf"
"github.com/IceWhaleTech/CasaOS/internal/driver"
2021-10-15 11:43:41 +08:00
"github.com/IceWhaleTech/CasaOS/model"
2023-02-02 03:36:59 +00:00
"github.com/IceWhaleTech/CasaOS/pkg/utils"
2022-06-29 11:09:58 +08:00
"github.com/IceWhaleTech/CasaOS/pkg/utils/common_err"
2021-10-15 11:43:41 +08:00
"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
"github.com/IceWhaleTech/CasaOS/service"
2023-02-02 03:36:59 +00:00
"github.com/IceWhaleTech/CasaOS/internal/sign"
2021-10-15 11:43:41 +08:00
"github.com/gin-gonic/gin"
2022-06-08 18:19:45 +08:00
uuid "github.com/satori/go.uuid"
2022-10-20 02:25:26 -04:00
"go.uber.org/zap"
2021-09-26 10:35:02 +08:00
)
// @Summary 读取文件
// @Produce application/json
// @Accept application/json
// @Tags file
// @Security ApiKeyAuth
// @Param path query string true "路径"
// @Success 200 {string} string "ok"
// @Router /file/read [get]
func GetFilerContent ( c * gin . Context ) {
filePath := c . Query ( "path" )
if len ( filePath ) == 0 {
2022-07-22 11:02:11 +08:00
c . JSON ( common_err . CLIENT_ERROR , model . Result {
2022-06-29 11:09:58 +08:00
Success : common_err . INVALID_PARAMS ,
Message : common_err . GetMsg ( common_err . INVALID_PARAMS ) ,
2021-09-26 10:35:02 +08:00
} )
return
}
if ! file . Exists ( filePath ) {
2022-07-22 11:02:11 +08:00
c . JSON ( common_err . SERVICE_ERROR , model . Result {
2022-06-29 11:09:58 +08:00
Success : common_err . FILE_DOES_NOT_EXIST ,
Message : common_err . GetMsg ( common_err . FILE_DOES_NOT_EXIST ) ,
2021-09-26 10:35:02 +08:00
} )
return
}
2022-10-20 02:25:26 -04:00
// 文件读取任务是将文件内容读取到内存中。
2021-09-26 10:35:02 +08:00
info , err := ioutil . ReadFile ( filePath )
if err != nil {
2022-07-22 11:02:11 +08:00
c . JSON ( common_err . SERVICE_ERROR , model . Result {
2022-06-29 11:09:58 +08:00
Success : common_err . FILE_READ_ERROR ,
Message : common_err . GetMsg ( common_err . FILE_READ_ERROR ) ,
2021-09-26 10:35:02 +08:00
Data : err . Error ( ) ,
} )
return
}
result := string ( info )
2022-07-22 11:02:11 +08:00
c . JSON ( common_err . SUCCESS , model . Result {
2022-06-29 11:09:58 +08:00
Success : common_err . SUCCESS ,
Message : common_err . GetMsg ( common_err . SUCCESS ) ,
2021-09-26 10:35:02 +08:00
Data : result ,
} )
}
func GetLocalFile ( c * gin . Context ) {
path := c . Query ( "path" )
if len ( path ) == 0 {
c . JSON ( http . StatusOK , model . Result {
2022-06-29 11:09:58 +08:00
Success : common_err . INVALID_PARAMS ,
Message : common_err . GetMsg ( common_err . INVALID_PARAMS ) ,
2021-09-26 10:35:02 +08:00
} )
return
}
if ! file . Exists ( path ) {
c . JSON ( http . StatusOK , model . Result {
2022-06-29 11:09:58 +08:00
Success : common_err . FILE_DOES_NOT_EXIST ,
Message : common_err . GetMsg ( common_err . FILE_DOES_NOT_EXIST ) ,
2021-09-26 10:35:02 +08:00
} )
return
}
c . File ( path )
}
2022-02-17 18:43:25 +08:00
// @Summary download
2021-09-26 10:35:02 +08:00
// @Produce application/json
// @Accept application/json
// @Tags file
// @Security ApiKeyAuth
2022-07-22 11:02:11 +08:00
// @Param format query string false "Compression format" Enums(zip,tar,targz)
2022-06-08 18:19:45 +08:00
// @Param files query string true "file list eg: filename1,filename2,filename3 "
2021-09-26 10:35:02 +08:00
// @Success 200 {string} string "ok"
// @Router /file/download [get]
func GetDownloadFile ( c * gin . Context ) {
2022-07-22 11:02:11 +08:00
t := c . Query ( "format" )
2022-06-08 18:19:45 +08:00
files := c . Query ( "files" )
if len ( files ) == 0 {
2022-07-22 11:02:11 +08:00
c . JSON ( common_err . CLIENT_ERROR , model . Result {
2022-06-29 11:09:58 +08:00
Success : common_err . INVALID_PARAMS ,
Message : common_err . GetMsg ( common_err . INVALID_PARAMS ) ,
2021-09-26 10:35:02 +08:00
} )
return
}
2022-06-08 18:19:45 +08:00
list := strings . Split ( files , "," )
for _ , v := range list {
if ! file . Exists ( v ) {
2022-07-22 11:02:11 +08:00
c . JSON ( common_err . SERVICE_ERROR , model . Result {
2022-06-29 11:09:58 +08:00
Success : common_err . FILE_DOES_NOT_EXIST ,
Message : common_err . GetMsg ( common_err . FILE_DOES_NOT_EXIST ) ,
2022-06-08 18:19:45 +08:00
} )
return
}
2021-09-26 10:35:02 +08:00
}
c . Header ( "Content-Type" , "application/octet-stream" )
c . Header ( "Content-Transfer-Encoding" , "binary" )
c . Header ( "Cache-Control" , "no-cache" )
2022-06-08 18:19:45 +08:00
// handles only single files not folders and multiple files
if len ( list ) == 1 {
2021-09-26 10:35:02 +08:00
2022-06-08 18:19:45 +08:00
filePath := list [ 0 ]
info , err := os . Stat ( filePath )
if err != nil {
c . JSON ( http . StatusOK , model . Result {
2022-06-29 11:09:58 +08:00
Success : common_err . FILE_DOES_NOT_EXIST ,
Message : common_err . GetMsg ( common_err . FILE_DOES_NOT_EXIST ) ,
2022-06-08 18:19:45 +08:00
} )
return
}
if ! info . IsDir ( ) {
2021-09-26 10:35:02 +08:00
2022-10-20 02:25:26 -04:00
// 打开文件
2022-06-08 18:19:45 +08:00
fileTmp , _ := os . Open ( filePath )
defer fileTmp . Close ( )
2022-10-20 02:25:26 -04:00
// 获取文件的名称
2022-06-08 18:19:45 +08:00
fileName := path . Base ( filePath )
c . Header ( "Content-Disposition" , "attachment; filename*=utf-8''" + url2 . PathEscape ( fileName ) )
c . File ( filePath )
return
}
}
extension , ar , err := file . GetCompressionAlgorithm ( t )
if err != nil {
2022-07-22 11:02:11 +08:00
c . JSON ( common_err . CLIENT_ERROR , model . Result {
2022-06-29 11:09:58 +08:00
Success : common_err . INVALID_PARAMS ,
Message : common_err . GetMsg ( common_err . INVALID_PARAMS ) ,
2022-04-06 12:10:51 +08:00
} )
return
}
2022-06-08 18:19:45 +08:00
err = ar . Create ( c . Writer )
if err != nil {
2022-07-22 11:02:11 +08:00
c . JSON ( common_err . SERVICE_ERROR , model . Result {
Success : common_err . SERVICE_ERROR ,
Message : common_err . GetMsg ( common_err . SERVICE_ERROR ) ,
2022-06-08 18:19:45 +08:00
Data : err . Error ( ) ,
2022-04-06 12:10:51 +08:00
} )
return
}
2022-06-08 18:19:45 +08:00
defer ar . Close ( )
commonDir := file . CommonPrefix ( filepath . Separator , list ... )
2022-04-06 12:10:51 +08:00
2022-06-08 18:19:45 +08:00
currentPath := filepath . Base ( commonDir )
name := "_" + currentPath
name += extension
c . Header ( "Content-Disposition" , "attachment; filename*=utf-8''" + url . PathEscape ( name ) )
for _ , fname := range list {
err = file . AddFile ( ar , fname , commonDir )
if err != nil {
log . Printf ( "Failed to archive %s: %v" , fname , err )
}
}
}
func GetDownloadSingleFile ( c * gin . Context ) {
2022-07-22 11:02:11 +08:00
filePath := c . Query ( "path" )
if len ( filePath ) == 0 {
2022-12-20 14:05:16 +08:00
c . JSON ( common_err . CLIENT_ERROR , model . Result {
2022-07-22 11:02:11 +08:00
Success : common_err . INVALID_PARAMS ,
Message : common_err . GetMsg ( common_err . INVALID_PARAMS ) ,
} )
return
}
2023-02-02 03:36:59 +00:00
fileName := path . Base ( filePath )
// c.Header("Content-Disposition", "inline")
c . Header ( "Content-Disposition" , "attachment; filename*=utf-8''" + url2 . PathEscape ( fileName ) )
storage , _ := service . MyService . FsService ( ) . GetStorage ( filePath )
if storage != nil {
if shouldProxy ( storage , fileName ) {
Proxy ( c )
return
} else {
link , _ , err := service . MyService . FsService ( ) . Link ( c , filePath , model . LinkArgs {
IP : c . ClientIP ( ) ,
Header : c . Request . Header ,
Type : c . Query ( "type" ) ,
} )
if err != nil {
c . JSON ( common_err . SERVICE_ERROR , model . Result {
Success : common_err . SERVICE_ERROR ,
Message : common_err . GetMsg ( common_err . SERVICE_ERROR ) ,
Data : err . Error ( ) ,
} )
return
}
c . Header ( "Referrer-Policy" , "no-referrer" )
c . Header ( "Cache-Control" , "max-age=0, no-cache, no-store, must-revalidate" )
c . Redirect ( 302 , link . URL )
return
}
}
2022-07-22 11:02:11 +08:00
fileTmp , err := os . Open ( filePath )
if err != nil {
c . JSON ( common_err . SERVICE_ERROR , model . Result {
Success : common_err . FILE_DOES_NOT_EXIST ,
Message : common_err . GetMsg ( common_err . FILE_DOES_NOT_EXIST ) ,
} )
return
}
2022-06-08 18:19:45 +08:00
defer fileTmp . Close ( )
2022-04-06 12:10:51 +08:00
2022-06-08 18:19:45 +08:00
c . File ( filePath )
2022-04-06 12:10:51 +08:00
}
2021-09-26 10:35:02 +08:00
// @Summary 获取目录列表
// @Produce application/json
// @Accept application/json
// @Tags file
// @Security ApiKeyAuth
// @Param path query string false "路径"
// @Success 200 {string} string "ok"
// @Router /file/dirpath [get]
func DirPath ( c * gin . Context ) {
2023-01-16 05:54:44 +00:00
var req ListReq
if err := c . ShouldBind ( & req ) ; err != nil {
c . JSON ( common_err . SUCCESS , model . Result { Success : common_err . CLIENT_ERROR , Message : common_err . GetMsg ( common_err . CLIENT_ERROR ) , Data : err . Error ( ) } )
return
}
req . Validate ( )
info := service . MyService . System ( ) . GetDirPath ( req . Path )
2022-08-15 11:37:21 +08:00
shares := service . MyService . Shares ( ) . GetSharesList ( )
sharesMap := make ( map [ string ] string )
for _ , v := range shares {
sharesMap [ v . Path ] = fmt . Sprint ( v . ID )
}
2023-02-02 15:59:33 +00:00
// if len(info) <= (req.Page-1)*req.Size {
// c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.CLIENT_ERROR, Message: common_err.GetMsg(common_err.INVALID_PARAMS), Data: "page out of range"})
// return
// }
forEnd := req . Index * req . Size
if forEnd > len ( info ) {
forEnd = len ( info )
}
for i := ( req . Index - 1 ) * req . Size ; i < forEnd ; i ++ {
2022-08-15 11:37:21 +08:00
if v , ok := sharesMap [ info [ i ] . Path ] ; ok {
ex := make ( map [ string ] interface { } )
shareEx := make ( map [ string ] string )
shareEx [ "shared" ] = "true"
shareEx [ "id" ] = v
ex [ "share" ] = shareEx
info [ i ] . Extensions = ex
}
}
2022-10-20 02:25:26 -04:00
// Hide the files or folders in operation
2022-06-08 18:19:45 +08:00
fileQueue := make ( map [ string ] string )
2022-06-10 14:47:50 +08:00
if len ( service . OpStrArr ) > 0 {
for _ , v := range service . OpStrArr {
v , ok := service . FileQueue . Load ( v )
if ! ok {
continue
}
vt := v . ( model . FileOperate )
for _ , i := range vt . Item {
lastPath := i . From [ strings . LastIndex ( i . From , "/" ) + 1 : ]
fileQueue [ vt . To + "/" + lastPath ] = i . From
}
2022-06-08 18:19:45 +08:00
}
}
2022-06-10 14:47:50 +08:00
2023-01-16 05:54:44 +00:00
pathList := [ ] ObjResp { }
2023-02-02 15:59:33 +00:00
for i := ( req . Index - 1 ) * req . Size ; i < forEnd ; i ++ {
2022-10-20 02:25:26 -04:00
if info [ i ] . Name == ".temp" && info [ i ] . IsDir {
continue
}
2022-06-08 18:19:45 +08:00
if _ , ok := fileQueue [ info [ i ] . Path ] ; ! ok {
2023-01-16 05:54:44 +00:00
t := ObjResp { }
t . IsDir = info [ i ] . IsDir
t . Name = info [ i ] . Name
t . Modified = info [ i ] . Date
t . Size = info [ i ] . Size
t . Path = info [ i ] . Path
2023-02-02 15:59:33 +00:00
t . Extensions = info [ i ] . Extensions
2023-01-16 05:54:44 +00:00
pathList = append ( pathList , t )
2023-02-02 15:59:33 +00:00
2022-06-08 18:19:45 +08:00
}
}
2023-01-16 05:54:44 +00:00
flist := FsListResp {
2023-02-02 15:59:33 +00:00
Content : pathList ,
Total : int64 ( len ( info ) ) ,
// Readme: "",
// Write: true,
// Provider: "local",
Index : req . Index ,
Size : req . Size ,
2023-01-16 05:54:44 +00:00
}
c . JSON ( common_err . SUCCESS , model . Result { Success : common_err . SUCCESS , Message : common_err . GetMsg ( common_err . SUCCESS ) , Data : flist } )
2021-09-26 10:35:02 +08:00
}
2022-02-17 18:43:25 +08:00
// @Summary rename file or dir
2021-09-26 10:35:02 +08:00
// @Produce application/json
// @Accept application/json
// @Tags file
// @Security ApiKeyAuth
2022-06-08 18:19:45 +08:00
// @Param oldpath body string true "path of old"
// @Param newpath body string true "path of new"
2021-09-26 10:35:02 +08:00
// @Success 200 {string} string "ok"
// @Router /file/rename [put]
func RenamePath ( c * gin . Context ) {
2022-06-08 18:19:45 +08:00
json := make ( map [ string ] string )
2022-07-22 11:02:11 +08:00
c . ShouldBind ( & json )
op := json [ "old_path" ]
np := json [ "new_path" ]
2021-09-26 10:35:02 +08:00
if len ( op ) == 0 || len ( np ) == 0 {
2022-07-22 11:02:11 +08:00
c . JSON ( common_err . CLIENT_ERROR , model . Result { Success : common_err . INVALID_PARAMS , Message : common_err . GetMsg ( common_err . INVALID_PARAMS ) } )
2021-09-26 10:35:02 +08:00
return
}
2022-06-29 11:09:58 +08:00
success , err := service . MyService . System ( ) . RenameFile ( op , np )
2022-07-22 11:02:11 +08:00
c . JSON ( common_err . SUCCESS , model . Result { Success : success , Message : common_err . GetMsg ( success ) , Data : err } )
2021-09-26 10:35:02 +08:00
}
2022-02-17 18:43:25 +08:00
// @Summary create folder
2021-09-26 10:35:02 +08:00
// @Produce application/json
2022-06-08 18:19:45 +08:00
// @Accept application/json
2021-09-26 10:35:02 +08:00
// @Tags file
// @Security ApiKeyAuth
2022-06-08 18:19:45 +08:00
// @Param path body string true "path of folder"
2021-09-26 10:35:02 +08:00
// @Success 200 {string} string "ok"
// @Router /file/mkdir [post]
func MkdirAll ( c * gin . Context ) {
2022-06-08 18:19:45 +08:00
json := make ( map [ string ] string )
2022-07-22 11:02:11 +08:00
c . ShouldBind ( & json )
2022-06-08 18:19:45 +08:00
path := json [ "path" ]
2021-09-26 10:35:02 +08:00
var code int
if len ( path ) == 0 {
2022-07-22 11:02:11 +08:00
c . JSON ( common_err . CLIENT_ERROR , model . Result { Success : common_err . INVALID_PARAMS , Message : common_err . GetMsg ( common_err . INVALID_PARAMS ) } )
2021-09-26 10:35:02 +08:00
return
}
2022-06-08 18:19:45 +08:00
// decodedPath, err := url.QueryUnescape(path)
// if err != nil {
2022-06-29 11:09:58 +08:00
// c.JSON(http.StatusOK, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)})
2022-06-08 18:19:45 +08:00
// return
// }
2022-06-29 11:09:58 +08:00
code , _ = service . MyService . System ( ) . MkdirAll ( path )
2022-07-22 11:02:11 +08:00
c . JSON ( common_err . SUCCESS , model . Result { Success : code , Message : common_err . GetMsg ( code ) } )
2021-09-26 10:35:02 +08:00
}
2022-03-09 16:37:03 +08:00
// @Summary create file
2021-10-15 11:43:41 +08:00
// @Produce application/json
2022-06-08 18:19:45 +08:00
// @Accept application/json
2021-10-15 11:43:41 +08:00
// @Tags file
// @Security ApiKeyAuth
2022-06-08 18:19:45 +08:00
// @Param path body string true "path of folder (path need to url encode)"
2021-10-15 11:43:41 +08:00
// @Success 200 {string} string "ok"
// @Router /file/create [post]
func PostCreateFile ( c * gin . Context ) {
2022-06-08 18:19:45 +08:00
json := make ( map [ string ] string )
2022-07-22 11:02:11 +08:00
c . ShouldBind ( & json )
2022-06-08 18:19:45 +08:00
path := json [ "path" ]
2021-10-15 11:43:41 +08:00
var code int
if len ( path ) == 0 {
2022-07-22 11:02:11 +08:00
c . JSON ( common_err . CLIENT_ERROR , model . Result { Success : common_err . INVALID_PARAMS , Message : common_err . GetMsg ( common_err . INVALID_PARAMS ) } )
2021-10-15 11:43:41 +08:00
return
}
2022-06-08 18:19:45 +08:00
// decodedPath, err := url.QueryUnescape(path)
// if err != nil {
2022-06-29 11:09:58 +08:00
// c.JSON(http.StatusOK, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)})
2022-06-08 18:19:45 +08:00
// return
// }
2022-06-29 11:09:58 +08:00
code , _ = service . MyService . System ( ) . CreateFile ( path )
2022-07-22 11:02:11 +08:00
c . JSON ( common_err . SUCCESS , model . Result { Success : code , Message : common_err . GetMsg ( code ) } )
2021-10-15 11:43:41 +08:00
}
2022-03-09 16:37:03 +08:00
// @Summary upload file
// @Produce application/json
2022-06-08 18:19:45 +08:00
// @Accept application/json
2022-03-09 16:37:03 +08:00
// @Tags file
// @Security ApiKeyAuth
// @Param path formData string false "file path"
// @Param file formData file true "file"
// @Success 200 {string} string "ok"
// @Router /file/upload [get]
func GetFileUpload ( c * gin . Context ) {
relative := c . Query ( "relativePath" )
fileName := c . Query ( "filename" )
2022-03-09 19:44:08 +08:00
chunkNumber := c . Query ( "chunkNumber" )
2022-03-10 13:40:42 +08:00
totalChunks , _ := strconv . Atoi ( c . DefaultQuery ( "totalChunks" , "0" ) )
2022-03-09 16:37:03 +08:00
path := c . Query ( "path" )
2022-03-09 19:44:08 +08:00
dirPath := ""
2022-03-10 16:25:33 +08:00
hash := file . GetHashByContent ( [ ] byte ( fileName ) )
2022-10-20 02:25:26 -04:00
tempDir := filepath . Join ( path , ".temp" , hash + strconv . Itoa ( totalChunks ) ) + "/"
2022-03-09 16:37:03 +08:00
if fileName != relative {
2022-03-09 19:44:08 +08:00
dirPath = strings . TrimSuffix ( relative , fileName )
2022-03-10 16:25:33 +08:00
tempDir += dirPath
2022-03-09 16:37:03 +08:00
file . MkDir ( path + "/" + dirPath )
}
2022-03-10 16:25:33 +08:00
tempDir += chunkNumber
2022-03-09 19:44:08 +08:00
if ! file . CheckNotExist ( tempDir ) {
2022-06-29 11:09:58 +08:00
c . JSON ( 200 , model . Result { Success : 200 , Message : common_err . GetMsg ( common_err . FILE_ALREADY_EXISTS ) } )
2022-03-09 16:37:03 +08:00
return
}
2022-06-29 11:09:58 +08:00
c . JSON ( 204 , model . Result { Success : 204 , Message : common_err . GetMsg ( common_err . SUCCESS ) } )
2022-03-09 16:37:03 +08:00
}
2022-02-17 18:43:25 +08:00
// @Summary upload file
2021-09-26 10:35:02 +08:00
// @Produce application/json
// @Accept multipart/form-data
// @Tags file
// @Security ApiKeyAuth
2022-02-17 18:43:25 +08:00
// @Param path formData string false "file path"
// @Param file formData file true "file"
2021-09-26 10:35:02 +08:00
// @Success 200 {string} string "ok"
2022-02-17 18:43:25 +08:00
// @Router /file/upload [post]
2021-09-26 10:35:02 +08:00
func PostFileUpload ( c * gin . Context ) {
2022-02-17 18:43:25 +08:00
f , _ , _ := c . Request . FormFile ( "file" )
2022-03-09 16:37:03 +08:00
relative := c . PostForm ( "relativePath" )
fileName := c . PostForm ( "filename" )
2022-03-09 19:44:08 +08:00
totalChunks , _ := strconv . Atoi ( c . DefaultPostForm ( "totalChunks" , "0" ) )
chunkNumber := c . PostForm ( "chunkNumber" )
dirPath := ""
2022-03-09 16:37:03 +08:00
path := c . PostForm ( "path" )
2022-03-09 19:44:08 +08:00
hash := file . GetHashByContent ( [ ] byte ( fileName ) )
2022-02-17 18:43:25 +08:00
if len ( path ) == 0 {
2022-11-29 12:17:14 -05:00
logger . Error ( "path should not be empty" )
2022-10-20 02:25:26 -04:00
c . JSON ( http . StatusBadRequest , model . Result { Success : common_err . INVALID_PARAMS , Message : common_err . GetMsg ( common_err . INVALID_PARAMS ) } )
2022-02-17 18:43:25 +08:00
return
}
2022-10-20 02:25:26 -04:00
tempDir := filepath . Join ( path , ".temp" , hash + strconv . Itoa ( totalChunks ) ) + "/"
2022-03-09 16:37:03 +08:00
if fileName != relative {
2022-03-09 19:44:08 +08:00
dirPath = strings . TrimSuffix ( relative , fileName )
2022-03-10 16:25:33 +08:00
tempDir += dirPath
2022-10-20 02:25:26 -04:00
if err := file . MkDir ( path + "/" + dirPath ) ; err != nil {
2022-11-29 12:17:14 -05:00
logger . Error ( "error when trying to create `" + path + "/" + dirPath + "`" , zap . Error ( err ) )
2022-10-20 02:25:26 -04:00
c . JSON ( http . StatusInternalServerError , model . Result { Success : common_err . SERVICE_ERROR , Message : err . Error ( ) } )
return
}
2022-03-09 16:37:03 +08:00
}
2022-03-10 13:40:42 +08:00
2022-03-09 16:37:03 +08:00
path += "/" + relative
2022-03-10 16:25:33 +08:00
if ! file . CheckNotExist ( tempDir + chunkNumber ) {
2022-10-20 02:25:26 -04:00
if err := file . RMDir ( tempDir + chunkNumber ) ; err != nil {
2022-11-29 12:17:14 -05:00
logger . Error ( "error when trying to remove existing `" + tempDir + chunkNumber + "`" , zap . Error ( err ) )
2022-10-20 02:25:26 -04:00
c . JSON ( http . StatusInternalServerError , model . Result { Success : common_err . SERVICE_ERROR , Message : err . Error ( ) } )
return
}
2022-02-17 18:43:25 +08:00
}
2022-03-09 16:37:03 +08:00
2022-03-09 19:44:08 +08:00
if totalChunks > 1 {
2022-10-20 02:25:26 -04:00
if err := file . IsNotExistMkDir ( tempDir ) ; err != nil {
2022-11-29 12:17:14 -05:00
logger . Error ( "error when trying to create `" + tempDir + "`" , zap . Error ( err ) )
2022-10-20 02:25:26 -04:00
c . JSON ( http . StatusInternalServerError , model . Result { Success : common_err . SERVICE_ERROR , Message : err . Error ( ) } )
return
}
out , err := os . OpenFile ( tempDir + chunkNumber , os . O_WRONLY | os . O_CREATE , 0 o644 )
if err != nil {
2022-11-29 12:17:14 -05:00
logger . Error ( "error when trying to open `" + tempDir + chunkNumber + "` for creation" , zap . Error ( err ) )
2022-10-20 02:25:26 -04:00
c . JSON ( http . StatusInternalServerError , model . Result { Success : common_err . SERVICE_ERROR , Message : err . Error ( ) } )
return
}
2022-03-09 19:44:08 +08:00
defer out . Close ( )
2022-10-20 02:25:26 -04:00
if _ , err := io . Copy ( out , f ) ; err != nil { // recommend to use https://github.com/iceber/iouring-go for faster copy
2022-11-29 12:17:14 -05:00
logger . Error ( "error when trying to write to `" + tempDir + chunkNumber + "`" , zap . Error ( err ) )
2022-10-20 02:25:26 -04:00
c . JSON ( http . StatusInternalServerError , model . Result { Success : common_err . SERVICE_ERROR , Message : err . Error ( ) } )
return
}
fileNum , err := ioutil . ReadDir ( tempDir )
2022-03-09 19:44:08 +08:00
if err != nil {
2022-11-29 12:17:14 -05:00
logger . Error ( "error when trying to read number of files under `" + tempDir + "`" , zap . Error ( err ) )
2022-10-20 02:25:26 -04:00
c . JSON ( http . StatusInternalServerError , model . Result { Success : common_err . SERVICE_ERROR , Message : err . Error ( ) } )
2022-03-09 19:44:08 +08:00
return
}
2022-10-20 02:25:26 -04:00
if totalChunks == len ( fileNum ) {
if err := file . SpliceFiles ( tempDir , path , totalChunks , 1 ) ; err != nil {
2022-11-29 12:17:14 -05:00
logger . Error ( "error when trying to splice files under `" + tempDir + "`" , zap . Error ( err ) )
2022-10-20 02:25:26 -04:00
c . JSON ( http . StatusInternalServerError , model . Result { Success : common_err . SERVICE_ERROR , Message : err . Error ( ) } )
return
}
if err := file . RMDir ( tempDir ) ; err != nil {
2022-11-29 12:17:14 -05:00
logger . Error ( "error when trying to remove `" + tempDir + "`" , zap . Error ( err ) )
2022-10-20 02:25:26 -04:00
c . JSON ( http . StatusInternalServerError , model . Result { Success : common_err . SERVICE_ERROR , Message : err . Error ( ) } )
return
}
}
2022-03-09 19:44:08 +08:00
} else {
2022-10-20 02:25:26 -04:00
out , err := os . OpenFile ( path , os . O_WRONLY | os . O_CREATE , 0 o644 )
2022-03-09 19:44:08 +08:00
if err != nil {
2022-11-29 12:17:14 -05:00
logger . Error ( "error when trying to open `" + path + "` for creation" , zap . Error ( err ) )
2022-10-20 02:25:26 -04:00
c . JSON ( http . StatusInternalServerError , model . Result { Success : common_err . SERVICE_ERROR , Message : err . Error ( ) } )
2022-03-09 19:44:08 +08:00
return
}
2022-10-20 02:25:26 -04:00
defer out . Close ( )
if _ , err := io . Copy ( out , f ) ; err != nil { // recommend to use https://github.com/iceber/iouring-go for faster copy
2022-11-29 12:17:14 -05:00
logger . Error ( "error when trying to write to `" + path + "`" , zap . Error ( err ) )
2022-10-20 02:25:26 -04:00
c . JSON ( http . StatusInternalServerError , model . Result { Success : common_err . SERVICE_ERROR , Message : common_err . GetMsg ( common_err . SERVICE_ERROR ) , Data : err . Error ( ) } )
return
}
}
c . JSON ( http . StatusOK , model . Result { Success : common_err . SUCCESS , Message : common_err . GetMsg ( common_err . SUCCESS ) } )
2021-09-26 10:35:02 +08:00
}
2021-12-29 16:42:20 +08:00
2022-02-17 18:43:25 +08:00
// @Summary copy or move file
// @Produce application/json
2022-06-08 18:19:45 +08:00
// @Accept application/json
2022-02-17 18:43:25 +08:00
// @Tags file
// @Security ApiKeyAuth
2022-06-08 18:19:45 +08:00
// @Param body body model.FileOperate true "type:move,copy"
2022-02-17 18:43:25 +08:00
// @Success 200 {string} string "ok"
// @Router /file/operate [post]
func PostOperateFileOrDir ( c * gin . Context ) {
2022-06-08 18:19:45 +08:00
list := model . FileOperate { }
2022-07-22 11:02:11 +08:00
c . ShouldBind ( & list )
2022-06-08 18:19:45 +08:00
if len ( list . Item ) == 0 {
2022-07-22 11:02:11 +08:00
c . JSON ( common_err . CLIENT_ERROR , model . Result { Success : common_err . INVALID_PARAMS , Message : common_err . GetMsg ( common_err . INVALID_PARAMS ) } )
2022-02-17 18:43:25 +08:00
return
2021-12-29 16:42:20 +08:00
}
2022-06-10 13:33:53 +08:00
if list . To == list . Item [ 0 ] . From [ : strings . LastIndex ( list . Item [ 0 ] . From , "/" ) ] {
2022-07-22 11:02:11 +08:00
c . JSON ( common_err . SERVICE_ERROR , model . Result { Success : common_err . SOURCE_DES_SAME , Message : common_err . GetMsg ( common_err . SOURCE_DES_SAME ) } )
2022-06-10 13:33:53 +08:00
return
}
2022-06-08 18:19:45 +08:00
var total int64 = 0
for i := 0 ; i < len ( list . Item ) ; i ++ {
size , err := file . GetFileOrDirSize ( list . Item [ i ] . From )
2022-02-17 18:43:25 +08:00
if err != nil {
2022-06-08 18:19:45 +08:00
continue
2022-02-17 18:43:25 +08:00
}
2022-06-08 18:19:45 +08:00
list . Item [ i ] . Size = size
total += size
}
list . TotalSize = total
list . ProcessedSize = 0
uid := uuid . NewV4 ( ) . String ( )
service . FileQueue . Store ( uid , list )
service . OpStrArr = append ( service . OpStrArr , uid )
if len ( service . OpStrArr ) == 1 {
go service . ExecOpFile ( )
go service . CheckFileStatus ( )
2022-06-08 19:31:01 +08:00
go service . MyService . Notify ( ) . SendFileOperateNotify ( false )
2021-12-29 16:42:20 +08:00
}
2022-06-08 18:19:45 +08:00
2022-07-22 11:02:11 +08:00
c . JSON ( common_err . SUCCESS , model . Result { Success : common_err . SUCCESS , Message : common_err . GetMsg ( common_err . SUCCESS ) } )
2021-12-29 16:42:20 +08:00
}
2022-02-28 14:14:39 +08:00
// @Summary delete file
// @Produce application/json
2022-06-08 18:19:45 +08:00
// @Accept application/json
2022-02-28 14:14:39 +08:00
// @Tags file
// @Security ApiKeyAuth
2022-06-08 18:19:45 +08:00
// @Param body body string true "paths eg ["/a/b/c","/d/e/f"]"
2022-02-28 14:14:39 +08:00
// @Success 200 {string} string "ok"
// @Router /file/delete [delete]
2022-02-17 18:43:25 +08:00
func DeleteFile ( c * gin . Context ) {
2022-06-08 18:19:45 +08:00
paths := [ ] string { }
2022-07-22 11:02:11 +08:00
c . ShouldBind ( & paths )
2022-06-08 18:19:45 +08:00
if len ( paths ) == 0 {
2022-07-22 11:02:11 +08:00
c . JSON ( common_err . CLIENT_ERROR , model . Result { Success : common_err . INVALID_PARAMS , Message : common_err . GetMsg ( common_err . INVALID_PARAMS ) } )
2022-02-17 18:43:25 +08:00
return
2021-12-29 16:42:20 +08:00
}
2022-06-08 18:19:45 +08:00
// path := c.Query("path")
// paths := strings.Split(path, ",")
for _ , v := range paths {
err := os . RemoveAll ( v )
if err != nil {
2022-07-22 11:02:11 +08:00
c . JSON ( common_err . SERVICE_ERROR , model . Result { Success : common_err . FILE_DELETE_ERROR , Message : common_err . GetMsg ( common_err . FILE_DELETE_ERROR ) , Data : err } )
2022-06-08 18:19:45 +08:00
return
}
}
2022-07-22 11:02:11 +08:00
c . JSON ( common_err . SUCCESS , model . Result { Success : common_err . SUCCESS , Message : common_err . GetMsg ( common_err . SUCCESS ) } )
2021-12-29 16:42:20 +08:00
}
2022-03-09 16:37:03 +08:00
// @Summary update file
// @Produce application/json
2022-06-08 18:19:45 +08:00
// @Accept application/json
2022-03-09 16:37:03 +08:00
// @Tags file
// @Security ApiKeyAuth
2022-06-08 18:19:45 +08:00
// @Param path body string true "path"
// @Param content body string true "content"
2022-03-09 16:37:03 +08:00
// @Success 200 {string} string "ok"
// @Router /file/update [put]
func PutFileContent ( c * gin . Context ) {
2022-06-08 18:19:45 +08:00
fi := model . FileUpdate { }
2022-07-22 11:02:11 +08:00
c . ShouldBind ( & fi )
2022-06-08 18:19:45 +08:00
// path := c.PostForm("path")
// content := c.PostForm("content")
if ! file . Exists ( fi . FilePath ) {
2022-07-22 11:02:11 +08:00
c . JSON ( common_err . SERVICE_ERROR , model . Result { Success : common_err . FILE_ALREADY_EXISTS , Message : common_err . GetMsg ( common_err . FILE_ALREADY_EXISTS ) } )
2022-03-09 16:37:03 +08:00
return
}
2022-10-20 02:25:26 -04:00
// err := os.Remove(path)
2022-06-08 18:19:45 +08:00
err := os . RemoveAll ( fi . FilePath )
2022-03-09 16:37:03 +08:00
if err != nil {
2022-07-22 11:02:11 +08:00
c . JSON ( common_err . SERVICE_ERROR , model . Result { Success : common_err . FILE_DELETE_ERROR , Message : common_err . GetMsg ( common_err . FILE_DELETE_ERROR ) , Data : err } )
2022-03-09 16:37:03 +08:00
return
}
2022-06-08 18:19:45 +08:00
err = file . CreateFileAndWriteContent ( fi . FilePath , fi . FileContent )
2022-03-09 16:37:03 +08:00
if err != nil {
2022-07-22 11:02:11 +08:00
c . JSON ( common_err . SERVICE_ERROR , model . Result { Success : common_err . SERVICE_ERROR , Message : common_err . GetMsg ( common_err . SERVICE_ERROR ) , Data : err . Error ( ) } )
2022-03-09 16:37:03 +08:00
return
}
2022-07-22 11:02:11 +08:00
c . JSON ( common_err . SUCCESS , model . Result { Success : common_err . SUCCESS , Message : common_err . GetMsg ( common_err . SUCCESS ) } )
2022-03-09 16:37:03 +08:00
}
2022-05-05 13:46:55 +08:00
// @Summary image thumbnail/original image
// @Produce application/json
// @Accept application/json
// @Tags file
// @Security ApiKeyAuth
// @Param path query string true "path"
// @Param type query string false "original,thumbnail" Enums(original,thumbnail)
// @Success 200 {string} string "ok"
// @Router /file/image [get]
func GetFileImage ( c * gin . Context ) {
t := c . Query ( "type" )
path := c . Query ( "path" )
if ! file . Exists ( path ) {
2022-07-22 11:02:11 +08:00
c . JSON ( common_err . SERVICE_ERROR , model . Result { Success : common_err . FILE_ALREADY_EXISTS , Message : common_err . GetMsg ( common_err . FILE_ALREADY_EXISTS ) } )
2022-05-05 13:46:55 +08:00
return
}
if t == "thumbnail" {
f , err := file . GetImage ( path , 100 , 0 )
if err != nil {
2022-07-22 11:02:11 +08:00
c . JSON ( common_err . SERVICE_ERROR , model . Result { Success : common_err . SERVICE_ERROR , Message : common_err . GetMsg ( common_err . SERVICE_ERROR ) , Data : err . Error ( ) } )
2022-05-05 13:46:55 +08:00
return
}
c . Writer . WriteString ( string ( f ) )
return
}
f , err := os . Open ( path )
if err != nil {
2022-07-22 11:02:11 +08:00
c . JSON ( common_err . SERVICE_ERROR , model . Result { Success : common_err . SERVICE_ERROR , Message : common_err . GetMsg ( common_err . SERVICE_ERROR ) , Data : err . Error ( ) } )
2022-05-05 13:46:55 +08:00
return
}
defer f . Close ( )
data , err := ioutil . ReadAll ( f )
if err != nil {
2022-07-22 11:02:11 +08:00
c . JSON ( common_err . SERVICE_ERROR , model . Result { Success : common_err . SERVICE_ERROR , Message : common_err . GetMsg ( common_err . SERVICE_ERROR ) , Data : err . Error ( ) } )
2022-05-05 13:46:55 +08:00
return
}
c . Writer . WriteString ( string ( data ) )
}
2022-06-08 19:31:01 +08:00
func DeleteOperateFileOrDir ( c * gin . Context ) {
id := c . Param ( "id" )
if id == "0" {
service . FileQueue = sync . Map { }
service . OpStrArr = [ ] string { }
} else {
service . FileQueue . Delete ( id )
tempList := [ ] string { }
for _ , v := range service . OpStrArr {
if v != id {
tempList = append ( tempList , v )
}
}
service . OpStrArr = tempList
}
go service . MyService . Notify ( ) . SendFileOperateNotify ( true )
2022-07-22 11:02:11 +08:00
c . JSON ( common_err . SUCCESS , model . Result { Success : common_err . SUCCESS , Message : common_err . GetMsg ( common_err . SUCCESS ) } )
2022-06-08 19:31:01 +08:00
}
2022-12-20 14:05:16 +08:00
func GetSize ( c * gin . Context ) {
json := make ( map [ string ] string )
c . ShouldBind ( & json )
path := json [ "path" ]
size , err := file . GetFileOrDirSize ( path )
if err != nil {
c . JSON ( common_err . SERVICE_ERROR , model . Result { Success : common_err . SERVICE_ERROR , Message : common_err . GetMsg ( common_err . SERVICE_ERROR ) , Data : err . Error ( ) } )
return
}
c . JSON ( common_err . SUCCESS , model . Result { Success : common_err . SUCCESS , Message : common_err . GetMsg ( common_err . SUCCESS ) , Data : size } )
}
2023-02-02 03:36:59 +00:00
func Proxy ( c * gin . Context ) {
rawPath := c . Query ( "path" )
filename := filepath . Base ( rawPath )
storage , err := service . MyService . FsService ( ) . GetStorage ( rawPath )
if err != nil {
c . JSON ( 500 , model . Result { Success : common_err . SERVICE_ERROR , Message : common_err . GetMsg ( common_err . SERVICE_ERROR ) , Data : err . Error ( ) } )
return
}
if canProxy ( storage , filename ) {
downProxyUrl := storage . GetStorage ( ) . DownProxyUrl
if downProxyUrl != "" {
_ , ok := c . GetQuery ( "d" )
if ! ok {
URL := fmt . Sprintf ( "%s%s?sign=%s" ,
strings . Split ( downProxyUrl , "\n" ) [ 0 ] ,
utils . EncodePath ( rawPath , true ) ,
sign . Sign ( rawPath ) )
c . Redirect ( 302 , URL )
return
}
}
link , file , err := service . MyService . FsService ( ) . Link ( c , rawPath , model . LinkArgs {
Header : c . Request . Header ,
Type : c . Query ( "type" ) ,
} )
if err != nil {
c . JSON ( common_err . SERVICE_ERROR , model . Result { Success : common_err . SERVICE_ERROR , Message : common_err . GetMsg ( common_err . SERVICE_ERROR ) , Data : err . Error ( ) } )
return
}
err = CommonProxy ( c . Writer , c . Request , link , file )
if err != nil {
c . JSON ( common_err . SERVICE_ERROR , model . Result { Success : common_err . SERVICE_ERROR , Message : common_err . GetMsg ( common_err . SERVICE_ERROR ) , Data : err . Error ( ) } )
return
}
} else {
c . JSON ( common_err . SERVICE_ERROR , model . Result { Success : common_err . SERVICE_ERROR , Message : common_err . GetMsg ( common_err . SERVICE_ERROR ) , Data : "proxy not allowed" } )
return
}
}
// TODO need optimize
// when should be proxy?
// 1. config.MustProxy()
// 2. storage.WebProxy
// 3. proxy_types
func shouldProxy ( storage driver . Driver , filename string ) bool {
if storage . Config ( ) . MustProxy ( ) || storage . GetStorage ( ) . WebProxy {
return true
}
if utils . SliceContains ( conf . SlicesMap [ conf . ProxyTypes ] , utils . Ext ( filename ) ) {
return true
}
return false
}
// TODO need optimize
// when can be proxy?
// 1. text file
// 2. config.MustProxy()
// 3. storage.WebProxy
// 4. proxy_types
// solution: text_file + shouldProxy()
func canProxy ( storage driver . Driver , filename string ) bool {
if storage . Config ( ) . MustProxy ( ) || storage . GetStorage ( ) . WebProxy || storage . GetStorage ( ) . WebdavProxy ( ) {
return true
}
if utils . SliceContains ( conf . SlicesMap [ conf . ProxyTypes ] , utils . Ext ( filename ) ) {
return true
}
if utils . SliceContains ( conf . SlicesMap [ conf . TextTypes ] , utils . Ext ( filename ) ) {
return true
}
return false
}
var HttpClient = & http . Client { }
func CommonProxy ( w http . ResponseWriter , r * http . Request , link * model . Link , file model . Obj ) error {
// read data with native
var err error
if link . Data != nil {
defer func ( ) {
_ = link . Data . Close ( )
} ( )
w . Header ( ) . Set ( "Content-Type" , "application/octet-stream" )
w . Header ( ) . Set ( "Content-Disposition" , fmt . Sprintf ( ` attachment; filename="%s"; filename*=UTF-8''%s ` , file . GetName ( ) , url . QueryEscape ( file . GetName ( ) ) ) )
w . Header ( ) . Set ( "Content-Length" , strconv . FormatInt ( file . GetSize ( ) , 10 ) )
if link . Header != nil {
// TODO clean header with blacklist or whitelist
link . Header . Del ( "set-cookie" )
for h , val := range link . Header {
w . Header ( ) [ h ] = val
}
}
if link . Status == 0 {
w . WriteHeader ( http . StatusOK )
} else {
w . WriteHeader ( link . Status )
}
_ , err = io . Copy ( w , link . Data )
if err != nil {
return err
}
return nil
}
// local file
if link . FilePath != nil && * link . FilePath != "" {
f , err := os . Open ( * link . FilePath )
if err != nil {
return err
}
defer func ( ) {
_ = f . Close ( )
} ( )
fileStat , err := os . Stat ( * link . FilePath )
if err != nil {
return err
}
w . Header ( ) . Set ( "Content-Disposition" , fmt . Sprintf ( ` attachment; filename="%s"; filename*=UTF-8''%s ` , file . GetName ( ) , url . QueryEscape ( file . GetName ( ) ) ) )
http . ServeContent ( w , r , file . GetName ( ) , fileStat . ModTime ( ) , f )
return nil
} else {
req , err := http . NewRequest ( link . Method , link . URL , nil )
if err != nil {
return err
}
for h , val := range r . Header {
if utils . SliceContains ( conf . SlicesMap [ conf . ProxyIgnoreHeaders ] , strings . ToLower ( h ) ) {
continue
}
req . Header [ h ] = val
}
for h , val := range link . Header {
req . Header [ h ] = val
}
res , err := HttpClient . Do ( req )
if err != nil {
return err
}
defer func ( ) {
_ = res . Body . Close ( )
} ( )
logger . Info ( "proxy status" , zap . Any ( "status" , res . StatusCode ) )
// TODO clean header with blacklist or whitelist
res . Header . Del ( "set-cookie" )
for h , v := range res . Header {
w . Header ( ) [ h ] = v
}
w . WriteHeader ( res . StatusCode )
w . Header ( ) . Set ( "Content-Disposition" , fmt . Sprintf ( ` attachment; filename="%s"; filename*=UTF-8''%s ` , file . GetName ( ) , url . QueryEscape ( file . GetName ( ) ) ) )
if res . StatusCode >= 400 {
all , _ := ioutil . ReadAll ( res . Body )
msg := string ( all )
logger . Info ( "msg" , zap . Any ( "msg" , msg ) )
return errors . New ( msg )
}
_ , err = io . Copy ( w , res . Body )
if err != nil {
return err
}
return nil
}
}