Compare commits

..

9 Commits

23 changed files with 79 additions and 56 deletions

View File

@ -2,7 +2,7 @@
[English Version](https://tank-doc.eyeblue.cn/en) [English Version](https://tank-doc.eyeblue.cn/en)
# 蓝眼云盘3.1.5 # 蓝眼云盘3.1.6
[在线Demo](https://tanker.eyeblue.cn) (体验账号: demo 密码123456) [在线Demo](https://tanker.eyeblue.cn) (体验账号: demo 密码123456)
蓝眼云盘是蓝眼开源系列代表作品之一,致力于打造精致,优雅,简约的云盘。核心功能如下: 蓝眼云盘是蓝眼开源系列代表作品之一,致力于打造精致,优雅,简约的云盘。核心功能如下:
@ -42,11 +42,11 @@
如果您也想参与进来请尽情的fork, star, post issue, pull requests 如果您也想参与进来请尽情的fork, star, post issue, pull requests
当然你可以加入钉钉群一起直接交流在钉钉群中可以获取最新beta版本。 当然你可以加入钉钉群(一群已满员)一起直接交流在钉钉群中可以获取最新beta版本。
![](./build/doc/img/dingding.jpg) ![](./build/doc/img/dingding.jpg)
群号:23156361 群号:44754005
### Support ### Support

Binary file not shown.

Before

Width:  |  Height:  |  Size: 208 KiB

After

Width:  |  Height:  |  Size: 136 KiB

View File

@ -1,7 +1,7 @@
{ {
"files": { "files": {
"main.css": "/static/css/main.8994a208.css", "main.css": "/static/css/main.b03c51fb.css",
"main.js": "/static/js/main.7a2a6dcd.js", "main.js": "/static/js/main.b94b1947.js",
"static/media/logo.png": "/static/media/logo.847e54ef7fb4b744fad4.png", "static/media/logo.png": "/static/media/logo.847e54ef7fb4b744fad4.png",
"static/media/empty.svg": "/static/media/empty.20ceb38d310075aa4c3cdaae210afb65.svg", "static/media/empty.svg": "/static/media/empty.20ceb38d310075aa4c3cdaae210afb65.svg",
"static/media/psd.svg": "/static/media/psd.bf2ac5411c0132f292787f45855934f2.svg", "static/media/psd.svg": "/static/media/psd.bf2ac5411c0132f292787f45855934f2.svg",
@ -18,11 +18,11 @@
"static/media/folder.svg": "/static/media/folder.0bdd7430280c98bf4970b6af5a061c2c.svg", "static/media/folder.svg": "/static/media/folder.0bdd7430280c98bf4970b6af5a061c2c.svg",
"static/media/archive.svg": "/static/media/archive.684c1e42f233aa9d53a8910d4fee091e.svg", "static/media/archive.svg": "/static/media/archive.684c1e42f233aa9d53a8910d4fee091e.svg",
"index.html": "/index.html", "index.html": "/index.html",
"main.8994a208.css.map": "/static/css/main.8994a208.css.map", "main.b03c51fb.css.map": "/static/css/main.b03c51fb.css.map",
"main.7a2a6dcd.js.map": "/static/js/main.7a2a6dcd.js.map" "main.b94b1947.js.map": "/static/js/main.b94b1947.js.map"
}, },
"entrypoints": [ "entrypoints": [
"static/css/main.8994a208.css", "static/css/main.b03c51fb.css",
"static/js/main.7a2a6dcd.js" "static/js/main.b94b1947.js"
] ]
} }

View File

@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="eyeblue tank"/><title>EyeblueTank</title><script defer="defer" src="/static/js/main.7a2a6dcd.js"></script><link href="/static/css/main.8994a208.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run EyeblueTank.</noscript><div id="root"></div></body></html> <!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="eyeblue tank"/><title>EyeblueTank</title><script defer="defer" src="/static/js/main.b94b1947.js"></script><link href="/static/css/main.b03c51fb.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run EyeblueTank.</noscript><div id="root"></div></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -11,7 +11,7 @@
@REM prepare the variables. @REM prepare the variables.
@REM version name @REM version name
SET VERSION_NAME=tank-3.1.5 SET VERSION_NAME=tank-3.1.6
ECHO VERSION_NAME: %VERSION_NAME% ECHO VERSION_NAME: %VERSION_NAME%
@REM golang proxy @REM golang proxy
SET GOPROXY=https://goproxy.cn SET GOPROXY=https://goproxy.cn

View File

@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
set -e
########################################################################### ###########################################################################
# #
@ -9,7 +10,7 @@
#prepare the variables. #prepare the variables.
# version name # version name
VERSION_NAME=tank-3.1.5 VERSION_NAME=tank-3.1.6
echo "VERSION_NAME: ${VERSION_NAME}" echo "VERSION_NAME: ${VERSION_NAME}"
# golang proxy # golang proxy
GOPROXY=https://goproxy.cn GOPROXY=https://goproxy.cn

View File

@ -14,7 +14,7 @@ const (
//db table's prefix. tank31_ means current version is tank:3.1.x //db table's prefix. tank31_ means current version is tank:3.1.x
TABLE_PREFIX = "tank31_" TABLE_PREFIX = "tank31_"
VERSION = "3.1.5" VERSION = "3.1.6"
) )
type Config interface { type Config interface {

View File

@ -412,23 +412,35 @@ func (this *MatterDao) SizeByPuuidAndUserUuid(matterUuid string, userUuid string
} }
//delete a file from db and disk. //delete a file from db and disk.
func (this *MatterDao) Delete(matter *Matter) { func (this *MatterDao) Delete(matter *Matter, symbolLinkDir bool, symbolLevel int) {
// recursive if dir // recursive if dir
if matter.Dir { if matter.Dir {
stat, err := os.Lstat(matter.AbsolutePath())
if err == nil && stat.Mode()&os.ModeSymlink > 0 {
symbolLinkDir = true
}
if symbolLinkDir {
symbolLevel++
}
matters := this.FindByPuuidAndUserUuid(matter.Uuid, matter.UserUuid, nil) matters := this.FindByPuuidAndUserUuid(matter.Uuid, matter.UserUuid, nil)
for _, f := range matters { for _, f := range matters {
this.Delete(f) this.Delete(f, symbolLinkDir, symbolLevel)
} }
//delete from db. //delete from db.
db := core.CONTEXT.GetDB().Delete(&matter) db := core.CONTEXT.GetDB().Delete(&matter)
this.PanicError(db.Error) this.PanicError(db.Error)
if symbolLinkDir {
if symbolLevel == 0 {
os.Remove(matter.AbsolutePath())
}
} else {
//delete dir from disk. //delete dir from disk.
util.DeleteEmptyDir(matter.AbsolutePath()) util.DeleteEmptyDir(matter.AbsolutePath())
}
} else { } else {
//delete from db. //delete from db.
@ -442,11 +454,12 @@ func (this *MatterDao) Delete(matter *Matter) {
this.bridgeDao.DeleteByMatterUuid(matter.Uuid) this.bridgeDao.DeleteByMatterUuid(matter.Uuid)
//delete from disk. //delete from disk.
if !symbolLinkDir {
err := os.Remove(matter.AbsolutePath()) err := os.Remove(matter.AbsolutePath())
if err != nil { if err != nil {
this.logger.Error("occur error when deleting file. %v", err) this.logger.Error("occur error when deleting file. %v", err)
} }
}
} }
} }

View File

@ -250,7 +250,7 @@ func (this *MatterService) Delete(request *http.Request, matter *Matter, user *U
panic(result.BadRequest("matter cannot be nil")) panic(result.BadRequest("matter cannot be nil"))
} }
this.matterDao.Delete(matter) this.matterDao.Delete(matter, false, -1)
//re compute the size of Route. //re compute the size of Route.
this.ComputeRouteSize(matter.Puuid, user) this.ComputeRouteSize(matter.Puuid, user)
@ -987,7 +987,7 @@ func (this *MatterService) mirror(request *http.Request, srcPath string, destDir
this.logger.Info("mirror srcPath = %s destPath = %s", srcPath, destDirMatter.Path) this.logger.Info("mirror srcPath = %s destPath = %s", srcPath, destDirMatter.Path)
if fileStat.IsDir() { if fileStat.IsDir() || (fileStat.Mode()&os.ModeSymlink > 0) {
//判断当前文件夹下,文件是否已经存在了。 //判断当前文件夹下,文件是否已经存在了。
srcDirMatter := this.matterDao.FindByUserUuidAndPuuidAndDirAndName(user.Uuid, destDirMatter.Uuid, TRUE, fileStat.Name()) srcDirMatter := this.matterDao.FindByUserUuidAndPuuidAndDirAndName(user.Uuid, destDirMatter.Uuid, TRUE, fileStat.Name())
@ -1231,7 +1231,7 @@ func (this *MatterService) ScanPhysics(request *http.Request, user *User) {
} }
func (this *MatterService) scanPhysicsFolder(request *http.Request, dirInfo os.FileInfo, dirMatter *Matter, user *User) { func (this *MatterService) scanPhysicsFolder(request *http.Request, dirInfo os.FileInfo, dirMatter *Matter, user *User) {
if !dirInfo.IsDir() { if !dirInfo.IsDir() && !(dirInfo.Mode()&os.ModeSymlink > 0) {
return return
} }
@ -1277,7 +1277,7 @@ func (this *MatterService) scanPhysicsFolder(request *http.Request, dirInfo os.F
} else { } else {
if fileInfo.IsDir() { if fileInfo.IsDir() || (fileInfo.Mode()&os.ModeSymlink > 0) {
//create folder. //create folder.
matter = this.createDirectory(request, dirMatter, name, user) matter = this.createDirectory(request, dirMatter, name, user)

View File

@ -278,6 +278,10 @@ func (this *ShareController) Browse(writer http.ResponseWriter, request *http.Re
user := this.findUser(request) user := this.findUser(request)
share := this.shareService.CheckShare(request, shareUuid, code, user) share := this.shareService.CheckShare(request, shareUuid, code, user)
bridges := this.bridgeDao.FindByShareUuid(share.Uuid) bridges := this.bridgeDao.FindByShareUuid(share.Uuid)
shareOwner := this.userDao.FindByUuid(share.UserUuid)
if shareOwner.Status == USER_STATUS_DISABLED {
panic(result.BadRequestI18n(request, i18n.UserDisabled))
}
if puuid == MATTER_ROOT { if puuid == MATTER_ROOT {

View File

@ -92,6 +92,11 @@ func (this *ShareService) ValidateMatter(request *http.Request, shareUuid string
share := this.CheckShare(request, shareUuid, code, user) share := this.CheckShare(request, shareUuid, code, user)
shareOwner := this.userDao.FindByUuid(share.UserUuid)
if shareOwner.Status == USER_STATUS_DISABLED {
panic(result.BadRequestI18n(request, i18n.UserDisabled))
}
//if shareRootUuid is root. Bridge must has record. //if shareRootUuid is root. Bridge must has record.
if shareRootUuid == MATTER_ROOT { if shareRootUuid == MATTER_ROOT {

View File

@ -53,7 +53,7 @@ func Zip(srcPath string, destPath string) error {
fileHeader.Name = strings.TrimPrefix(path, baseDirPath) fileHeader.Name = strings.TrimPrefix(path, baseDirPath)
// directory need / // directory need /
if fileInfo.IsDir() { if fileInfo.IsDir() || (fileInfo.Mode()&os.ModeSymlink > 0) {
fileHeader.Name += "/" fileHeader.Name += "/"
} }

View File

@ -203,7 +203,7 @@ func (fs *memFS) walk(op, fullname string, f func(dir *memFSNode, frag string, f
Err: os.ErrNotExist, Err: os.ErrNotExist,
} }
} }
if !child.mode.IsDir() { if !child.mode.IsDir() && !(child.mode&os.ModeSymlink > 0) {
return &os.PathError{ return &os.PathError{
Op: op, Op: op,
Path: original, Path: original,
@ -468,7 +468,7 @@ func (f *memFileInfo) Name() string { return f.name }
func (f *memFileInfo) Size() int64 { return f.size } func (f *memFileInfo) Size() int64 { return f.size }
func (f *memFileInfo) Mode() os.FileMode { return f.mode } func (f *memFileInfo) Mode() os.FileMode { return f.mode }
func (f *memFileInfo) ModTime() time.Time { return f.modTime } func (f *memFileInfo) ModTime() time.Time { return f.modTime }
func (f *memFileInfo) IsDir() bool { return f.mode.IsDir() } func (f *memFileInfo) IsDir() bool { return f.mode.IsDir() || (f.mode&os.ModeSymlink > 0) }
func (f *memFileInfo) Sys() interface{} { return nil } func (f *memFileInfo) Sys() interface{} { return nil }
// A memFile is a File implementation for a memFSNode. It is a per-file (not // A memFile is a File implementation for a memFSNode. It is a per-file (not
@ -495,7 +495,7 @@ func (f *memFile) Close() error {
func (f *memFile) Read(p []byte) (int, error) { func (f *memFile) Read(p []byte) (int, error) {
f.n.mu.Lock() f.n.mu.Lock()
defer f.n.mu.Unlock() defer f.n.mu.Unlock()
if f.n.mode.IsDir() { if f.n.mode.IsDir() || (f.n.mode&os.ModeSymlink > 0) {
return 0, os.ErrInvalid return 0, os.ErrInvalid
} }
if f.pos >= len(f.n.data) { if f.pos >= len(f.n.data) {
@ -509,7 +509,7 @@ func (f *memFile) Read(p []byte) (int, error) {
func (f *memFile) Readdir(count int) ([]os.FileInfo, error) { func (f *memFile) Readdir(count int) ([]os.FileInfo, error) {
f.n.mu.Lock() f.n.mu.Lock()
defer f.n.mu.Unlock() defer f.n.mu.Unlock()
if !f.n.mode.IsDir() { if !f.n.mode.IsDir() && !(f.n.mode&os.ModeSymlink > 0) {
return nil, os.ErrInvalid return nil, os.ErrInvalid
} }
old := f.pos old := f.pos
@ -564,7 +564,7 @@ func (f *memFile) Write(p []byte) (int, error) {
f.n.mu.Lock() f.n.mu.Lock()
defer f.n.mu.Unlock() defer f.n.mu.Unlock()
if f.n.mode.IsDir() { if f.n.mode.IsDir() || (f.n.mode&os.ModeSymlink > 0) {
return 0, os.ErrInvalid return 0, os.ErrInvalid
} }
if f.pos < len(f.n.data) { if f.pos < len(f.n.data) {
@ -693,7 +693,7 @@ func copyFiles(ctx context.Context, fs FileSystem, src, dst string, overwrite bo
} }
} }
if srcStat.IsDir() { if srcStat.IsDir() || (srcStat.Mode()&os.ModeSymlink > 0) {
if err := fs.Mkdir(ctx, dst, srcPerm); err != nil { if err := fs.Mkdir(ctx, dst, srcPerm); err != nil {
return http.StatusForbidden, err return http.StatusForbidden, err
} }
@ -752,12 +752,12 @@ func walkFS(ctx context.Context, fs FileSystem, depth int, name string, info os.
// This implementation is based on Walk's code in the standard path/filepath package. // This implementation is based on Walk's code in the standard path/filepath package.
err := walkFn(name, info, nil) err := walkFn(name, info, nil)
if err != nil { if err != nil {
if info.IsDir() && err == filepath.SkipDir { if (info.IsDir() || (info.Mode()&os.ModeSymlink > 0)) && err == filepath.SkipDir {
return nil return nil
} }
return err return err
} }
if !info.IsDir() || depth == 0 { if (!info.IsDir() && !(info.Mode()&os.ModeSymlink > 0)) || depth == 0 {
return nil return nil
} }
if depth == 1 { if depth == 1 {
@ -785,7 +785,7 @@ func walkFS(ctx context.Context, fs FileSystem, depth int, name string, info os.
} else { } else {
err = walkFS(ctx, fs, depth, filename, fileInfo, walkFn) err = walkFS(ctx, fs, depth, filename, fileInfo, walkFn)
if err != nil { if err != nil {
if !fileInfo.IsDir() || err != filepath.SkipDir { if (!fileInfo.IsDir() && !(fileInfo.Mode()&os.ModeSymlink > 0)) || err != filepath.SkipDir {
return err return err
} }
} }

View File

@ -240,7 +240,7 @@ func find(ctx context.Context, ss []string, fs FileSystem, name string) ([]strin
return nil, err return nil, err
} }
ss = append(ss, name) ss = append(ss, name)
if stat.IsDir() { if stat.IsDir() || (stat.Mode()&os.ModeSymlink > 0) {
f, err := fs.OpenFile(ctx, name, os.O_RDONLY, 0) f, err := fs.OpenFile(ctx, name, os.O_RDONLY, 0)
if err != nil { if err != nil {
return nil, err return nil, err
@ -480,7 +480,7 @@ func testFS(t *testing.T, fs FileSystem) {
var stat os.FileInfo var stat os.FileInfo
fileName := parts[0] fileName := parts[0]
if stat, opErr = fs.Stat(ctx, fileName); opErr == nil { if stat, opErr = fs.Stat(ctx, fileName); opErr == nil {
if stat.IsDir() { if stat.IsDir() || (stat.Mode()&os.ModeSymlink > 0) {
got = "dir" got = "dir"
} else { } else {
got = strconv.Itoa(int(stat.Size())) got = strconv.Itoa(int(stat.Size()))
@ -538,7 +538,7 @@ func TestMemFSRoot(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("i=%d: Stat: %v", i, err) t.Fatalf("i=%d: Stat: %v", i, err)
} }
if !stat.IsDir() { if !stat.IsDir() && !(stat.Mode()&os.ModeSymlink > 0) {
t.Fatalf("i=%d: Stat.IsDir is false, want true", i) t.Fatalf("i=%d: Stat.IsDir is false, want true", i)
} }

View File

@ -176,7 +176,7 @@ func Props(ctx context.Context, fs FileSystem, ls LockSystem, name string, pname
if err != nil { if err != nil {
return nil, err return nil, err
} }
isDir := fi.IsDir() isDir := fi.IsDir() || (fi.Mode()&os.ModeSymlink > 0)
var deadProps map[xml.Name]Property var deadProps map[xml.Name]Property
if dph, ok := f.(DeadPropsHolder); ok { if dph, ok := f.(DeadPropsHolder); ok {

View File

@ -177,7 +177,7 @@ func (h *Handler) handleOptions(w http.ResponseWriter, r *http.Request) (status
ctx := r.Context() ctx := r.Context()
allow := "OPTIONS, LOCK, PUT, MKCOL" allow := "OPTIONS, LOCK, PUT, MKCOL"
if fi, err := h.FileSystem.Stat(ctx, reqPath); err == nil { if fi, err := h.FileSystem.Stat(ctx, reqPath); err == nil {
if fi.IsDir() { if fi.IsDir() || (fi.Mode()&os.ModeSymlink > 0) {
allow = "OPTIONS, LOCK, DELETE, PROPPATCH, COPY, MOVE, UNLOCK, PROPFIND" allow = "OPTIONS, LOCK, DELETE, PROPPATCH, COPY, MOVE, UNLOCK, PROPFIND"
} else { } else {
allow = "OPTIONS, LOCK, GET, HEAD, POST, DELETE, PROPPATCH, COPY, MOVE, UNLOCK, PROPFIND, PUT" allow = "OPTIONS, LOCK, GET, HEAD, POST, DELETE, PROPPATCH, COPY, MOVE, UNLOCK, PROPFIND, PUT"
@ -207,7 +207,7 @@ func (h *Handler) handleGetHeadPost(w http.ResponseWriter, r *http.Request) (sta
if err != nil { if err != nil {
return http.StatusNotFound, err return http.StatusNotFound, err
} }
if fi.IsDir() { if fi.IsDir() || (fi.Mode()&os.ModeSymlink > 0) {
return http.StatusMethodNotAllowed, nil return http.StatusMethodNotAllowed, nil
} }
etag, err := findETag(ctx, h.FileSystem, h.LockSystem, reqPath, fi) etag, err := findETag(ctx, h.FileSystem, h.LockSystem, reqPath, fi)
@ -557,7 +557,7 @@ func (h *Handler) handlePropfind(w http.ResponseWriter, r *http.Request) (status
return err return err
} }
href := path.Join(h.Prefix, reqPath) href := path.Join(h.Prefix, reqPath)
if href != "/" && info.IsDir() { if href != "/" && (info.IsDir() || (info.Mode()&os.ModeSymlink > 0)) {
href += "/" href += "/"
} }
return mw.write(MakePropstatResponse(href, pstats)) return mw.write(MakePropstatResponse(href, pstats))