diff --git a/go.mod b/go.mod index 472156f..d32a7c7 100644 --- a/go.mod +++ b/go.mod @@ -173,3 +173,5 @@ require ( sigs.k8s.io/yaml v1.2.0 // indirect ) + +replace github.com/gomodule/redigo v2.0.0+incompatible => github.com/gomodule/redigo v1.8.9 diff --git a/go.sum b/go.sum index 17024ef..64a345d 100644 --- a/go.sum +++ b/go.sum @@ -362,8 +362,8 @@ github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= -github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= +github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws= +github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= diff --git a/models/folder.go b/models/folder.go index ebc1069..80f712c 100644 --- a/models/folder.go +++ b/models/folder.go @@ -18,7 +18,8 @@ type Folder struct { OwnerID uint `gorm:"index:owner_id"` // 数据库忽略字段 - Position string `gorm:"-"` + Position string `gorm:"-"` + WebdavDstName string `gorm:"-"` } // Create 创建目录 @@ -169,6 +170,11 @@ func (folder *Folder) MoveOrCopyFileTo(files []uint, dstFolder *Folder, isCopy b oldFile.FolderID = dstFolder.ID oldFile.UserID = dstFolder.OwnerID + // webdav目标名重置 + if dstFolder.WebdavDstName != "" { + oldFile.Name = dstFolder.WebdavDstName + } + if err := DB.Create(&oldFile).Error; err != nil { return copiedSize, err } @@ -177,6 +183,14 @@ func (folder *Folder) MoveOrCopyFileTo(files []uint, dstFolder *Folder, isCopy b } } else { + var updates = map[string]interface{}{ + "folder_id": dstFolder.ID, + } + // webdav目标名重置 + if dstFolder.WebdavDstName != "" { + updates["name"] = dstFolder.WebdavDstName + } + // 更改顶级要移动文件的父目录指向 err := DB.Model(File{}).Where( "id in (?) and user_id = ? and folder_id = ?", @@ -184,9 +198,7 @@ func (folder *Folder) MoveOrCopyFileTo(files []uint, dstFolder *Folder, isCopy b folder.OwnerID, folder.ID, ). - Update(map[string]interface{}{ - "folder_id": dstFolder.ID, - }). + Update(updates). Error if err != nil { return 0, err @@ -221,6 +233,10 @@ func (folder *Folder) CopyFolderTo(folderID uint, dstFolder *Folder) (size uint6 // 顶级目录直接指向新的目的目录 if folder.ID == folderID { newID = dstFolder.ID + // webdav目标名重置 + if dstFolder.WebdavDstName != "" { + folder.Name = dstFolder.WebdavDstName + } } else if IDCache, ok := newIDCache[*folder.ParentID]; ok { newID = IDCache } else { @@ -282,15 +298,21 @@ func (folder *Folder) MoveFolderTo(dirs []uint, dstFolder *Folder) error { return errors.New("cannot move a folder into itself") } + var updates = map[string]interface{}{ + "parent_id": dstFolder.ID, + } + // webdav目标名重置 + if dstFolder.WebdavDstName != "" { + updates["name"] = dstFolder.WebdavDstName + } + // 更改顶级要移动目录的父目录指向 err := DB.Model(Folder{}).Where( "id in (?) and owner_id = ? and parent_id = ?", dirs, folder.OwnerID, folder.ID, - ).Update(map[string]interface{}{ - "parent_id": dstFolder.ID, - }).Error + ).Update(updates).Error return err diff --git a/pkg/cache/driver.go b/pkg/cache/driver.go index bf49408..1a3e652 100644 --- a/pkg/cache/driver.go +++ b/pkg/cache/driver.go @@ -21,6 +21,7 @@ func Init() { 10, conf.RedisConfig.Network, conf.RedisConfig.Server, + conf.RedisConfig.User, conf.RedisConfig.Password, conf.RedisConfig.DB, ) diff --git a/pkg/cache/redis.go b/pkg/cache/redis.go index e02bb48..5c776a0 100644 --- a/pkg/cache/redis.go +++ b/pkg/cache/redis.go @@ -44,7 +44,7 @@ func deserializer(value []byte) (interface{}, error) { } // NewRedisStore 创建新的redis存储 -func NewRedisStore(size int, network, address, password, database string) *RedisStore { +func NewRedisStore(size int, network, address, user, password, database string) *RedisStore { return &RedisStore{ pool: &redis.Pool{ MaxIdle: size, @@ -63,6 +63,7 @@ func NewRedisStore(size int, network, address, password, database string) *Redis network, address, redis.DialDatabase(db), + redis.DialUsername(user), redis.DialPassword(password), ) if err != nil { diff --git a/pkg/cache/redis_test.go b/pkg/cache/redis_test.go index 79dc6fc..c9f1692 100644 --- a/pkg/cache/redis_test.go +++ b/pkg/cache/redis_test.go @@ -13,7 +13,7 @@ import ( func TestNewRedisStore(t *testing.T) { asserts := assert.New(t) - store := NewRedisStore(10, "tcp", "", "", "0") + store := NewRedisStore(10, "tcp", "", "", "", "0") asserts.NotNil(store) asserts.Panics(func() { diff --git a/pkg/conf/conf.go b/pkg/conf/conf.go index 207e4ac..b0a4ea4 100644 --- a/pkg/conf/conf.go +++ b/pkg/conf/conf.go @@ -53,6 +53,7 @@ type slave struct { type redis struct { Network string Server string + User string Password string DB string } diff --git a/pkg/filesystem/fsctx/context.go b/pkg/filesystem/fsctx/context.go index 942551c..3c6445d 100644 --- a/pkg/filesystem/fsctx/context.go +++ b/pkg/filesystem/fsctx/context.go @@ -35,4 +35,6 @@ const ( CancelFuncCtx // 文件在从机节点中的路径 SlaveSrcPath + // Webdav目标名称 + WebdavDstName ) diff --git a/pkg/filesystem/manage.go b/pkg/filesystem/manage.go index acf739c..c77c9d9 100644 --- a/pkg/filesystem/manage.go +++ b/pkg/filesystem/manage.go @@ -69,6 +69,11 @@ func (fs *FileSystem) Copy(ctx context.Context, dirs, files []uint, src, dst str // 记录复制的文件的总容量 var newUsedStorage uint64 + // 设置webdav目标名 + if dstName, ok := ctx.Value(fsctx.WebdavDstName).(string); ok { + dstFolder.WebdavDstName = dstName + } + // 复制目录 if len(dirs) > 0 { subFileSizes, err := srcFolder.CopyFolderTo(dirs[0], dstFolder) @@ -103,6 +108,11 @@ func (fs *FileSystem) Move(ctx context.Context, dirs, files []uint, src, dst str return ErrPathNotExist } + // 设置webdav目标名 + if dstName, ok := ctx.Value(fsctx.WebdavDstName).(string); ok { + dstFolder.WebdavDstName = dstName + } + // 处理目录及子文件移动 err := srcFolder.MoveFolderTo(dirs, dstFolder) if err != nil { diff --git a/pkg/thumb/ffmpeg.go b/pkg/thumb/ffmpeg.go index 5ad9944..d98f107 100644 --- a/pkg/thumb/ffmpeg.go +++ b/pkg/thumb/ffmpeg.go @@ -67,9 +67,8 @@ func (f *FfmpegGenerator) Generate(ctx context.Context, file io.Reader, src, nam // Invoke ffmpeg scaleOpt := fmt.Sprintf("scale=%s:%s:force_original_aspect_ratio=decrease", options["thumb_width"], options["thumb_height"]) - inputFormat := filepath.Ext(name)[1:] cmd := exec.CommandContext(ctx, - ffmpegOpts["thumb_ffmpeg_path"], "-ss", ffmpegOpts["thumb_ffmpeg_seek"], "-f", inputFormat, "-i", tempInputPath, + ffmpegOpts["thumb_ffmpeg_path"], "-ss", ffmpegOpts["thumb_ffmpeg_seek"], "-i", tempInputPath, "-vf", scaleOpt, "-vframes", "1", tempOutputPath) // Redirect IO diff --git a/pkg/webdav/file.go b/pkg/webdav/file.go index 5076a63..a0e589b 100644 --- a/pkg/webdav/file.go +++ b/pkg/webdav/file.go @@ -9,9 +9,12 @@ import ( "net/http" "path" "path/filepath" + "strconv" + "time" model "github.com/cloudreve/Cloudreve/v3/models" "github.com/cloudreve/Cloudreve/v3/pkg/filesystem" + "github.com/cloudreve/Cloudreve/v3/pkg/filesystem/fsctx" ) // slashClean is equivalent to but slightly more efficient than @@ -23,6 +26,31 @@ func slashClean(name string) string { return path.Clean(name) } +// 更新Copy或Move后的修改时间 +func updateCopyMoveModtime(req *http.Request, fs *filesystem.FileSystem, dst string) error { + var modtime time.Time + if timeVal := req.Header.Get("X-OC-Mtime"); timeVal != "" { + timeUnix, err := strconv.ParseInt(timeVal, 10, 64) + if err == nil { + modtime = time.Unix(timeUnix, 0) + } + } + + if modtime.IsZero() { + return nil + } + + ok, fi := isPathExist(req.Context(), fs, dst) + if !ok { + return nil + } + + if fi.IsDir() { + return model.DB.Model(fi.(*model.Folder)).UpdateColumn("updated_at", modtime).Error + } + return model.DB.Model(fi.(*model.File)).UpdateColumn("updated_at", modtime).Error +} + // moveFiles moves files and/or directories from src to dst. // // See section 9.9.4 for when various HTTP status codes apply. @@ -44,20 +72,17 @@ func moveFiles(ctx context.Context, fs *filesystem.FileSystem, src FileInfo, dst } } - // 判断是否需要移动 if src.GetPosition() != path.Dir(dst) { err = fs.Move( - ctx, + context.WithValue(ctx, fsctx.WebdavDstName, path.Base(dst)), folderIDs, fileIDs, src.GetPosition(), path.Dir(dst), ) - } - - // 判断是否需要重命名 - if err == nil && src.GetName() != path.Base(dst) { + } else if src.GetName() != path.Base(dst) { + // 判断是否需要重命名 err = fs.Rename( ctx, folderIDs, @@ -81,7 +106,6 @@ func copyFiles(ctx context.Context, fs *filesystem.FileSystem, src FileInfo, dst } recursion++ - var ( fileIDs []uint folderIDs []uint @@ -100,7 +124,7 @@ func copyFiles(ctx context.Context, fs *filesystem.FileSystem, src FileInfo, dst } err = fs.Copy( - ctx, + context.WithValue(ctx, fsctx.WebdavDstName, path.Base(dst)), folderIDs, fileIDs, src.GetPosition(), diff --git a/pkg/webdav/prop.go b/pkg/webdav/prop.go index e60775c..fa5e76e 100644 --- a/pkg/webdav/prop.go +++ b/pkg/webdav/prop.go @@ -49,7 +49,7 @@ func (file *FileDeadProps) Patch(proppatches []Proppatch) ([]Propstat, error) { var modtimeUnix int64 modtimeUnix, err = strconv.ParseInt(string(prop.InnerXML), 10, 64) if err == nil { - err = model.DB.Model(file).UpdateColumn("updated_at", time.Unix(modtimeUnix, 0)).Error + err = model.DB.Model(file.File).UpdateColumn("updated_at", time.Unix(modtimeUnix, 0)).Error } } } @@ -78,7 +78,7 @@ func (folder *FolderDeadProps) Patch(proppatches []Proppatch) ([]Propstat, error var modtimeUnix int64 modtimeUnix, err = strconv.ParseInt(string(prop.InnerXML), 10, 64) if err == nil { - err = model.DB.Model(folder).UpdateColumn("updated_at", time.Unix(modtimeUnix, 0)).Error + err = model.DB.Model(folder.Folder).UpdateColumn("updated_at", time.Unix(modtimeUnix, 0)).Error } } } diff --git a/pkg/webdav/webdav.go b/pkg/webdav/webdav.go index 9b2ff1b..3c72f11 100644 --- a/pkg/webdav/webdav.go +++ b/pkg/webdav/webdav.go @@ -496,7 +496,16 @@ func (h *Handler) handleCopyMove(w http.ResponseWriter, r *http.Request, fs *fil return http.StatusBadRequest, errInvalidDepth } } - return copyFiles(ctx, fs, target, dst, r.Header.Get("Overwrite") != "F", depth, 0) + status, err = copyFiles(ctx, fs, target, dst, r.Header.Get("Overwrite") != "F", depth, 0) + if err != nil { + return status, err + } + + err = updateCopyMoveModtime(r, fs, dst) + if err != nil { + return http.StatusInternalServerError, err + } + return status, nil } // windows下,某些情况下(网盘根目录下)Office保存文件时附带的锁token只包含源文件, @@ -515,7 +524,16 @@ func (h *Handler) handleCopyMove(w http.ResponseWriter, r *http.Request, fs *fil return http.StatusBadRequest, errInvalidDepth } } - return moveFiles(ctx, fs, target, dst, r.Header.Get("Overwrite") == "T") + status, err = moveFiles(ctx, fs, target, dst, r.Header.Get("Overwrite") == "T") + if err != nil { + return status, err + } + + err = updateCopyMoveModtime(r, fs, dst) + if err != nil { + return http.StatusInternalServerError, err + } + return status, nil } // OK