From e6b37f81334313f1f63de507279d4694246bc1d2 Mon Sep 17 00:00:00 2001 From: Khoray Date: Wed, 21 Jan 2026 23:15:43 +0800 Subject: [PATCH 1/5] fix(driver/seafile): object not found when RootFolderPath != "/" --- drivers/seafile/driver.go | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/seafile/driver.go b/drivers/seafile/driver.go index 646d68055..05b6e3693 100644 --- a/drivers/seafile/driver.go +++ b/drivers/seafile/driver.go @@ -43,20 +43,22 @@ func (d *Seafile) Drop(ctx context.Context) error { func (d *Seafile) List(ctx context.Context, dir model.Obj, args model.ListArgs) (result []model.Obj, err error) { path := dir.GetPath() - if path == "/" && d.RepoId == "" { + if path == d.RootFolderPath { libraries, err := d.listLibraries() if err != nil { return nil, err } - return utils.SliceConvert(libraries, func(f LibraryItemResp) (model.Obj, error) { - return &model.Object{ - Path: stdpath.Join(path, f.Name), - Name: f.Name, - Modified: time.Unix(f.Modified, 0), - Size: f.Size, - IsFolder: true, - }, nil - }) + if path == "/" && d.RepoId == "" { + return utils.SliceConvert(libraries, func(f LibraryItemResp) (model.Obj, error) { + return &model.Object{ + Path: stdpath.Join(path, f.Name), + Name: f.Name, + Modified: time.Unix(f.Modified, 0), + Size: f.Size, + IsFolder: true, + }, nil + }) + } } var repo *LibraryInfo repo, path, err = d.getRepoAndPath(path) From 5a3270de373b0ca634c8999d3b7cf967d8766140 Mon Sep 17 00:00:00 2001 From: j2rong4cn Date: Thu, 22 Jan 2026 20:03:43 +0800 Subject: [PATCH 2/5] refactor(seafile): restructure Seafile driver for improved library handling and error management --- drivers/seafile/driver.go | 177 +++++++++++++++++--------------------- drivers/seafile/types.go | 43 +++++++-- drivers/seafile/util.go | 76 ++-------------- 3 files changed, 123 insertions(+), 173 deletions(-) diff --git a/drivers/seafile/driver.go b/drivers/seafile/driver.go index 05b6e3693..59704bae8 100644 --- a/drivers/seafile/driver.go +++ b/drivers/seafile/driver.go @@ -6,9 +6,9 @@ import ( "net/http" stdpath "path" "strings" - "time" "github.com/OpenListTeam/OpenList/v4/internal/driver" + "github.com/OpenListTeam/OpenList/v4/internal/errs" "github.com/OpenListTeam/OpenList/v4/internal/model" "github.com/OpenListTeam/OpenList/v4/pkg/utils" "github.com/go-resty/resty/v2" @@ -19,7 +19,7 @@ type Seafile struct { Addition authorization string - libraryMap map[string]*LibraryInfo + root model.Obj } func (d *Seafile) Config() driver.Config { @@ -33,46 +33,71 @@ func (d *Seafile) GetAddition() driver.Additional { func (d *Seafile) Init(ctx context.Context) error { d.Address = strings.TrimSuffix(d.Address, "/") d.RootFolderPath = utils.FixAndCleanPath(d.RootFolderPath) - d.libraryMap = make(map[string]*LibraryInfo) - return d.getToken() + err := d.getToken() + if err != nil { + return err + } + if d.RepoId != "" { + library, err := d.getLibraryInfo(d.RepoId) + if err != nil { + return err + } + library.path = d.RootFolderPath + library.ObjMask = model.Locked + d.root = &LibraryInfo{ + LibraryItemResp: library, + } + } else { + d.root = &model.Object{ + Name: "root", + Path: d.RootFolderPath, + IsFolder: true, + Modified: d.Modified, + Mask: model.Locked, + } + } + return nil } func (d *Seafile) Drop(ctx context.Context) error { + d.root = nil return nil } -func (d *Seafile) List(ctx context.Context, dir model.Obj, args model.ListArgs) (result []model.Obj, err error) { - path := dir.GetPath() - if path == d.RootFolderPath { - libraries, err := d.listLibraries() - if err != nil { - return nil, err - } - if path == "/" && d.RepoId == "" { - return utils.SliceConvert(libraries, func(f LibraryItemResp) (model.Obj, error) { - return &model.Object{ - Path: stdpath.Join(path, f.Name), - Name: f.Name, - Modified: time.Unix(f.Modified, 0), - Size: f.Size, - IsFolder: true, - }, nil - }) - } +func (d *Seafile) GetRoot(ctx context.Context) (model.Obj, error) { + if d.root == nil { + return nil, errs.StorageNotInit } - var repo *LibraryInfo - repo, path, err = d.getRepoAndPath(path) - if err != nil { - return nil, err - } - if repo.Encrypted { - err = d.decryptLibrary(repo) - if err != nil { - return nil, err + return d.root, nil +} + +func (d *Seafile) List(ctx context.Context, dir model.Obj, args model.ListArgs) (result []model.Obj, err error) { + switch o := dir.(type) { + default: + var resp []LibraryItemResp + _, err = d.request(http.MethodGet, "/api2/repos/", func(req *resty.Request) { + req.SetResult(&resp) + }) + return utils.SliceConvert(resp, func(f LibraryItemResp) (model.Obj, error) { + f.path = d.RootFolderPath + return &LibraryInfo{ + LibraryItemResp: f, + }, nil + }) + case *LibraryInfo: + if o.Encrypted { + err = d.decryptLibrary(o) + if err != nil { + return nil, err + } } + case *RepoItemResp: + // do nothing } - var resp []RepoDirItemResp - _, err = d.request(http.MethodGet, fmt.Sprintf("/api2/repos/%s/dir/", repo.Id), func(req *resty.Request) { + + path := dir.GetPath() + var resp []RepoItemResp + _, err = d.request(http.MethodGet, fmt.Sprintf("/api2/repos/%s/dir/", dir.GetID()), func(req *resty.Request) { req.SetResult(&resp).SetQueryParams(map[string]string{ "p": path, }) @@ -80,25 +105,16 @@ func (d *Seafile) List(ctx context.Context, dir model.Obj, args model.ListArgs) if err != nil { return nil, err } - return utils.SliceConvert(resp, func(f RepoDirItemResp) (model.Obj, error) { - return &model.Object{ - Path: stdpath.Join(dir.GetPath(), f.Name), - Name: f.Name, - Modified: time.Unix(f.Modified, 0), - Size: f.Size, - IsFolder: f.Type == "dir", - }, nil + return utils.SliceConvert(resp, func(f RepoItemResp) (model.Obj, error) { + f.path = stdpath.Join(path, f.Name) + return &f, nil }) } func (d *Seafile) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { - repo, path, err := d.getRepoAndPath(file.GetPath()) - if err != nil { - return nil, err - } - res, err := d.request(http.MethodGet, fmt.Sprintf("/api2/repos/%s/file/", repo.Id), func(req *resty.Request) { + res, err := d.request(http.MethodGet, fmt.Sprintf("/api2/repos/%s/file/", file.GetID()), func(req *resty.Request) { req.SetQueryParams(map[string]string{ - "p": path, + "p": file.GetPath(), "reuse": "1", }) }) @@ -111,14 +127,9 @@ func (d *Seafile) Link(ctx context.Context, file model.Obj, args model.LinkArgs) } func (d *Seafile) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error { - repo, path, err := d.getRepoAndPath(parentDir.GetPath()) - if err != nil { - return err - } - path, _ = utils.JoinBasePath(path, dirName) - _, err = d.request(http.MethodPost, fmt.Sprintf("/api2/repos/%s/dir/", repo.Id), func(req *resty.Request) { + _, err := d.request(http.MethodPost, fmt.Sprintf("/api2/repos/%s/dir/", parentDir.GetID()), func(req *resty.Request) { req.SetQueryParams(map[string]string{ - "p": path, + "p": stdpath.Join(parentDir.GetPath(), dirName), }).SetFormData(map[string]string{ "operation": "mkdir", }) @@ -127,34 +138,22 @@ func (d *Seafile) MakeDir(ctx context.Context, parentDir model.Obj, dirName stri } func (d *Seafile) Move(ctx context.Context, srcObj, dstDir model.Obj) error { - repo, path, err := d.getRepoAndPath(srcObj.GetPath()) - if err != nil { - return err - } - dstRepo, dstPath, err := d.getRepoAndPath(dstDir.GetPath()) - if err != nil { - return err - } - _, err = d.request(http.MethodPost, fmt.Sprintf("/api2/repos/%s/file/", repo.Id), func(req *resty.Request) { + _, err := d.request(http.MethodPost, fmt.Sprintf("/api2/repos/%s/file/", srcObj.GetID()), func(req *resty.Request) { req.SetQueryParams(map[string]string{ - "p": path, + "p": srcObj.GetPath(), }).SetFormData(map[string]string{ "operation": "move", - "dst_repo": dstRepo.Id, - "dst_dir": dstPath, + "dst_repo": dstDir.GetID(), + "dst_dir": dstDir.GetPath(), }) }, true) return err } func (d *Seafile) Rename(ctx context.Context, srcObj model.Obj, newName string) error { - repo, path, err := d.getRepoAndPath(srcObj.GetPath()) - if err != nil { - return err - } - _, err = d.request(http.MethodPost, fmt.Sprintf("/api2/repos/%s/file/", repo.Id), func(req *resty.Request) { + _, err := d.request(http.MethodPost, fmt.Sprintf("/api2/repos/%s/file/", srcObj.GetID()), func(req *resty.Request) { req.SetQueryParams(map[string]string{ - "p": path, + "p": srcObj.GetPath(), }).SetFormData(map[string]string{ "operation": "rename", "newname": newName, @@ -164,47 +163,31 @@ func (d *Seafile) Rename(ctx context.Context, srcObj model.Obj, newName string) } func (d *Seafile) Copy(ctx context.Context, srcObj, dstDir model.Obj) error { - repo, path, err := d.getRepoAndPath(srcObj.GetPath()) - if err != nil { - return err - } - dstRepo, dstPath, err := d.getRepoAndPath(dstDir.GetPath()) - if err != nil { - return err - } - _, err = d.request(http.MethodPost, fmt.Sprintf("/api2/repos/%s/file/", repo.Id), func(req *resty.Request) { + _, err := d.request(http.MethodPost, fmt.Sprintf("/api2/repos/%s/file/", srcObj.GetID()), func(req *resty.Request) { req.SetQueryParams(map[string]string{ - "p": path, + "p": srcObj.GetPath(), }).SetFormData(map[string]string{ "operation": "copy", - "dst_repo": dstRepo.Id, - "dst_dir": dstPath, + "dst_repo": dstDir.GetID(), + "dst_dir": dstDir.GetPath(), }) }) return err } func (d *Seafile) Remove(ctx context.Context, obj model.Obj) error { - repo, path, err := d.getRepoAndPath(obj.GetPath()) - if err != nil { - return err - } - _, err = d.request(http.MethodDelete, fmt.Sprintf("/api2/repos/%s/file/", repo.Id), func(req *resty.Request) { + _, err := d.request(http.MethodDelete, fmt.Sprintf("/api2/repos/%s/file/", obj.GetID()), func(req *resty.Request) { req.SetQueryParams(map[string]string{ - "p": path, + "p": obj.GetPath(), }) }) return err } func (d *Seafile) Put(ctx context.Context, dstDir model.Obj, s model.FileStreamer, up driver.UpdateProgress) error { - repo, path, err := d.getRepoAndPath(dstDir.GetPath()) - if err != nil { - return err - } - res, err := d.request(http.MethodGet, fmt.Sprintf("/api2/repos/%s/upload-link/", repo.Id), func(req *resty.Request) { + res, err := d.request(http.MethodGet, fmt.Sprintf("/api2/repos/%s/upload-link/", dstDir.GetID()), func(req *resty.Request) { req.SetQueryParams(map[string]string{ - "p": path, + "p": dstDir.GetPath(), }) }) if err != nil { @@ -220,7 +203,7 @@ func (d *Seafile) Put(ctx context.Context, dstDir model.Obj, s model.FileStreame }) req.SetFileReader("file", s.GetName(), r). SetFormData(map[string]string{ - "parent_dir": path, + "parent_dir": dstDir.GetPath(), "replace": "1", }). SetContext(ctx) diff --git a/drivers/seafile/types.go b/drivers/seafile/types.go index 47cb322df..58ddcb26a 100644 --- a/drivers/seafile/types.go +++ b/drivers/seafile/types.go @@ -1,6 +1,11 @@ package seafile -import "time" +import ( + "time" + + "github.com/OpenListTeam/OpenList/v4/internal/model" + "github.com/OpenListTeam/OpenList/v4/pkg/utils" +) type AuthTokenResp struct { Token string `json:"token"` @@ -13,7 +18,37 @@ type RepoItemResp struct { Size int64 `json:"size"` Modified int64 `json:"mtime"` Permission string `json:"permission"` + + path string + model.ObjMask +} + +func (l *RepoItemResp) IsDir() bool { + return l.Type == "dir" +} +func (l *RepoItemResp) GetPath() string { + return l.path +} +func (l *RepoItemResp) GetName() string { + return l.Name +} +func (l *RepoItemResp) ModTime() time.Time { + return time.Unix(l.Modified, 0) +} +func (l *RepoItemResp) CreateTime() time.Time { + return l.ModTime() } +func (l *RepoItemResp) GetSize() int64 { + return l.Size +} +func (l *RepoItemResp) GetID() string { + return l.Id +} +func (l *RepoItemResp) GetHash() utils.HashInfo { + return utils.HashInfo{} +} + +var _ model.Obj = (*RepoItemResp)(nil) type LibraryItemResp struct { RepoItemResp @@ -33,12 +68,8 @@ type LibraryItemResp struct { SizeFormatted string `json:"size_formatted"` } -type RepoDirItemResp struct { - RepoItemResp -} - type LibraryInfo struct { LibraryItemResp decryptedTime time.Time decryptedSuccess bool -} \ No newline at end of file +} diff --git a/drivers/seafile/util.go b/drivers/seafile/util.go index 6b5d09939..f2c0b6659 100644 --- a/drivers/seafile/util.go +++ b/drivers/seafile/util.go @@ -7,9 +7,6 @@ import ( "strings" "time" - "github.com/OpenListTeam/OpenList/v4/internal/errs" - "github.com/OpenListTeam/OpenList/v4/pkg/utils" - "github.com/OpenListTeam/OpenList/v4/drivers/base" "github.com/go-resty/resty/v2" ) @@ -71,73 +68,12 @@ func (d *Seafile) request(method string, pathname string, callback base.ReqCallb return res.Body(), nil } -func (d *Seafile) getRepoAndPath(fullPath string) (repo *LibraryInfo, path string, err error) { - libraryMap := d.libraryMap - repoId := d.Addition.RepoId - if repoId != "" { - if len(repoId) == 36 /* uuid */ { - for _, library := range libraryMap { - if library.Id == repoId { - return library, fullPath, nil - } - } - } - } else { - var repoName string - str := fullPath[1:] - pos := strings.IndexRune(str, '/') - if pos == -1 { - repoName = str - } else { - repoName = str[:pos] - } - path = utils.FixAndCleanPath(fullPath[1+len(repoName):]) - if library, ok := libraryMap[repoName]; ok { - return library, path, nil - } - } - return nil, "", errs.ObjectNotFound -} - -func (d *Seafile) listLibraries() (resp []LibraryItemResp, err error) { - repoId := d.Addition.RepoId - if repoId == "" { - _, err = d.request(http.MethodGet, "/api2/repos/", func(req *resty.Request) { - req.SetResult(&resp) - }) - } else { - var oneResp LibraryItemResp - _, err = d.request(http.MethodGet, fmt.Sprintf("/api2/repos/%s/", repoId), func(req *resty.Request) { - req.SetResult(&oneResp) - }) - if err == nil { - resp = append(resp, oneResp) - } - } - if err != nil { - return nil, err - } - libraryMap := make(map[string]*LibraryInfo) - var putLibraryMap func(library LibraryItemResp, index int) - putLibraryMap = func(library LibraryItemResp, index int) { - name := library.Name - if index > 0 { - name = fmt.Sprintf("%s (%d)", name, index) - } - if _, exist := libraryMap[name]; exist { - putLibraryMap(library, index+1) - } else { - libraryInfo := LibraryInfo{} - data, _ := utils.Json.Marshal(library) - _ = utils.Json.Unmarshal(data, &libraryInfo) - libraryMap[name] = &libraryInfo - } - } - for _, library := range resp { - putLibraryMap(library, 0) - } - d.libraryMap = libraryMap - return resp, nil +func (d *Seafile) getLibraryInfo(repoId string) (LibraryItemResp, error) { + var oneResp LibraryItemResp + _, err := d.request(http.MethodGet, fmt.Sprintf("/api2/repos/%s/", repoId), func(req *resty.Request) { + req.SetResult(&oneResp) + }) + return oneResp, err } var repoPwdNotConfigured = errors.New("library password not configured") From 45fccf40ab98df4ea6fed1b8af145924c0417621 Mon Sep 17 00:00:00 2001 From: j2rong4cn Date: Thu, 22 Jan 2026 20:09:10 +0800 Subject: [PATCH 3/5] add IsDir method to LibraryInfo type --- drivers/seafile/types.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/seafile/types.go b/drivers/seafile/types.go index 58ddcb26a..70d80d34f 100644 --- a/drivers/seafile/types.go +++ b/drivers/seafile/types.go @@ -73,3 +73,7 @@ type LibraryInfo struct { decryptedTime time.Time decryptedSuccess bool } + +func (l *LibraryInfo) IsDir() bool { + return true +} From ab1ccf6fa2a6be897b7e442e466b211032000678 Mon Sep 17 00:00:00 2001 From: j2rong4cn Date: Thu, 22 Jan 2026 20:52:50 +0800 Subject: [PATCH 4/5] improve initialization --- drivers/seafile/driver.go | 41 ++++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/drivers/seafile/driver.go b/drivers/seafile/driver.go index 59704bae8..041eaacc4 100644 --- a/drivers/seafile/driver.go +++ b/drivers/seafile/driver.go @@ -32,11 +32,11 @@ func (d *Seafile) GetAddition() driver.Additional { func (d *Seafile) Init(ctx context.Context) error { d.Address = strings.TrimSuffix(d.Address, "/") - d.RootFolderPath = utils.FixAndCleanPath(d.RootFolderPath) err := d.getToken() if err != nil { return err } + d.RootFolderPath = utils.FixAndCleanPath(d.RootFolderPath) if d.RepoId != "" { library, err := d.getLibraryInfo(d.RepoId) if err != nil { @@ -47,7 +47,9 @@ func (d *Seafile) Init(ctx context.Context) error { d.root = &LibraryInfo{ LibraryItemResp: library, } - } else { + return nil + } + if len(d.RootFolderPath) <= 1 { d.root = &model.Object{ Name: "root", Path: d.RootFolderPath, @@ -55,8 +57,37 @@ func (d *Seafile) Init(ctx context.Context) error { Modified: d.Modified, Mask: model.Locked, } + return nil } - return nil + + var resp []LibraryItemResp + _, err = d.request(http.MethodGet, "/api2/repos/", func(req *resty.Request) { + req.SetResult(&resp) + }) + if err != nil { + return err + } + for _, library := range resp { + p, found := strings.CutPrefix(d.RootFolderPath[1:], library.Name) + if !found { + continue + } + if p == "" { + p = "/" + } else if p[0] != '/' { + continue + } + // d.RepoId = library.Id + // d.RootFolderPath = p + + library.path = p + library.ObjMask = model.Locked + d.root = &LibraryInfo{ + LibraryItemResp: library, + } + return nil + } + return fmt.Errorf("Library for root folder path %q not found", d.RootFolderPath) } func (d *Seafile) Drop(ctx context.Context) error { @@ -72,6 +103,7 @@ func (d *Seafile) GetRoot(ctx context.Context) (model.Obj, error) { } func (d *Seafile) List(ctx context.Context, dir model.Obj, args model.ListArgs) (result []model.Obj, err error) { + path := dir.GetPath() switch o := dir.(type) { default: var resp []LibraryItemResp @@ -79,7 +111,7 @@ func (d *Seafile) List(ctx context.Context, dir model.Obj, args model.ListArgs) req.SetResult(&resp) }) return utils.SliceConvert(resp, func(f LibraryItemResp) (model.Obj, error) { - f.path = d.RootFolderPath + f.path = path return &LibraryInfo{ LibraryItemResp: f, }, nil @@ -95,7 +127,6 @@ func (d *Seafile) List(ctx context.Context, dir model.Obj, args model.ListArgs) // do nothing } - path := dir.GetPath() var resp []RepoItemResp _, err = d.request(http.MethodGet, fmt.Sprintf("/api2/repos/%s/dir/", dir.GetID()), func(req *resty.Request) { req.SetResult(&resp).SetQueryParams(map[string]string{ From 2a69a7c734f7ba8a9442680ef8f4c6ab981b9d5a Mon Sep 17 00:00:00 2001 From: j2rong4cn Date: Thu, 22 Jan 2026 21:32:00 +0800 Subject: [PATCH 5/5] add repoID to RepoItemResp and update List method to set repoID --- drivers/seafile/driver.go | 1 + drivers/seafile/types.go | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/drivers/seafile/driver.go b/drivers/seafile/driver.go index 041eaacc4..221ac4361 100644 --- a/drivers/seafile/driver.go +++ b/drivers/seafile/driver.go @@ -138,6 +138,7 @@ func (d *Seafile) List(ctx context.Context, dir model.Obj, args model.ListArgs) } return utils.SliceConvert(resp, func(f RepoItemResp) (model.Obj, error) { f.path = stdpath.Join(path, f.Name) + f.repoID = dir.GetID() return &f, nil }) } diff --git a/drivers/seafile/types.go b/drivers/seafile/types.go index 70d80d34f..29a51ce84 100644 --- a/drivers/seafile/types.go +++ b/drivers/seafile/types.go @@ -21,6 +21,7 @@ type RepoItemResp struct { path string model.ObjMask + repoID string } func (l *RepoItemResp) IsDir() bool { @@ -42,6 +43,9 @@ func (l *RepoItemResp) GetSize() int64 { return l.Size } func (l *RepoItemResp) GetID() string { + if l.repoID != "" { + return l.repoID + } return l.Id } func (l *RepoItemResp) GetHash() utils.HashInfo {