From a2fc38be8d8751ea0d1da9fc878b65668258fc20 Mon Sep 17 00:00:00 2001 From: KirCute <951206789@qq.com> Date: Mon, 29 Sep 2025 23:26:56 +0800 Subject: [PATCH] perf(disk-usage): concurrently get details (#1326) --- drivers/115/driver.go | 13 ++++ drivers/123/driver.go | 15 ++++- drivers/123/types.go | 11 ++++ drivers/123/util.go | 13 +++- drivers/123_open/driver.go | 10 +-- drivers/123_open/util.go | 15 ++--- drivers/139/driver.go | 49 ++++++++++++++- drivers/139/meta.go | 1 + drivers/139/types.go | 17 ++++++ drivers/139/util.go | 51 +++++++++++++--- drivers/189/driver.go | 13 ++++ drivers/189/types.go | 18 ++++++ drivers/189/util.go | 23 +++++-- drivers/189_tv/driver.go | 27 +++++++-- drivers/189_tv/types.go | 18 ++++++ drivers/189_tv/utils.go | 27 ++++++--- drivers/189pc/driver.go | 28 +++++++-- drivers/189pc/types.go | 18 ++++++ drivers/189pc/utils.go | 33 ++++++---- drivers/alias/driver.go | 9 ++- drivers/alias/meta.go | 1 + drivers/alias/util.go | 42 ++++++++++++- drivers/aliyundrive/driver.go | 21 ++++++- drivers/baidu_netdisk/driver.go | 6 +- drivers/baidu_netdisk/util.go | 9 ++- drivers/chunk/driver.go | 6 +- drivers/cloudreve_v4/driver.go | 4 +- drivers/crypt/driver.go | 25 +++----- drivers/google_drive/driver.go | 29 +++++++++ drivers/google_drive/meta.go | 17 +++--- drivers/google_drive/types.go | 9 +++ drivers/google_drive/util.go | 31 +++++++--- drivers/ilanzou/driver.go | 29 +++++++-- drivers/onedrive/driver.go | 16 +++++ drivers/onedrive/meta.go | 23 +++---- drivers/onedrive/types.go | 12 ++++ drivers/onedrive/util.go | 18 ++++++ drivers/onedrive_app/driver.go | 16 +++++ drivers/onedrive_app/meta.go | 15 ++--- drivers/onedrive_app/types.go | 12 ++++ drivers/onedrive_app/util.go | 13 ++++ drivers/quark_uc/driver.go | 15 ++++- drivers/quark_uc/types.go | 97 ++++++++++++++++++------------ drivers/quark_uc/util.go | 19 +++++- drivers/smb/driver.go | 22 +++---- drivers/smb/util.go | 20 +++--- go.mod | 14 ++++- go.sum | 29 +++++++-- internal/bootstrap/data/setting.go | 10 ++- internal/conf/const.go | 9 +-- internal/driver/driver.go | 4 +- internal/fs/list.go | 5 +- internal/model/args.go | 7 ++- internal/op/storage.go | 33 +++++++--- server/handles/storage.go | 17 +++++- 55 files changed, 838 insertions(+), 226 deletions(-) diff --git a/drivers/115/driver.go b/drivers/115/driver.go index 62ff1de8..22db74e3 100644 --- a/drivers/115/driver.go +++ b/drivers/115/driver.go @@ -245,4 +245,17 @@ func (d *Pan115) DeleteOfflineTasks(ctx context.Context, hashes []string, delete return d.client.DeleteOfflineTasks(hashes, deleteFiles) } +func (d *Pan115) GetDetails(ctx context.Context) (*model.StorageDetails, error) { + info, err := d.client.GetInfo() + if err != nil { + return nil, err + } + return &model.StorageDetails{ + DiskUsage: model.DiskUsage{ + TotalSpace: uint64(info.SpaceInfo.AllTotal.Size), + FreeSpace: uint64(info.SpaceInfo.AllRemain.Size), + }, + }, nil +} + var _ driver.Driver = (*Pan115)(nil) diff --git a/drivers/123/driver.go b/drivers/123/driver.go index 6e172f67..135ebc51 100644 --- a/drivers/123/driver.go +++ b/drivers/123/driver.go @@ -74,7 +74,6 @@ func (d *Pan123) Link(ctx context.Context, file model.Obj, args model.LinkArgs) "type": f.Type, } resp, err := d.Request(DownloadInfo, http.MethodPost, func(req *resty.Request) { - req.SetBody(data) }, nil) if err != nil { @@ -254,4 +253,18 @@ func (d *Pan123) APIRateLimit(ctx context.Context, api string) error { return limiter.Wait(ctx) } +func (d *Pan123) GetDetails(ctx context.Context) (*model.StorageDetails, error) { + userInfo, err := d.getUserInfo(ctx) + if err != nil { + return nil, err + } + total := userInfo.Data.SpacePermanent + userInfo.Data.SpaceTemp + return &model.StorageDetails{ + DiskUsage: model.DiskUsage{ + TotalSpace: total, + FreeSpace: total - userInfo.Data.SpaceUsed, + }, + }, nil +} + var _ driver.Driver = (*Pan123)(nil) diff --git a/drivers/123/types.go b/drivers/123/types.go index 7e5967d8..dc77b77f 100644 --- a/drivers/123/types.go +++ b/drivers/123/types.go @@ -122,3 +122,14 @@ type S3PreSignedURLs struct { PreSignedUrls map[string]string `json:"presignedUrls"` } `json:"data"` } + +type UserInfoResp struct { + Data struct { + Uid int64 `json:"UID"` + Nickname string `json:"Nickname"` + SpaceUsed uint64 `json:"SpaceUsed"` + SpacePermanent uint64 `json:"SpacePermanent"` + SpaceTemp uint64 `json:"SpaceTemp"` + FileCount int `json:"FileCount"` + } `json:"data"` +} diff --git a/drivers/123/util.go b/drivers/123/util.go index 5d4fa67e..62fbaf90 100644 --- a/drivers/123/util.go +++ b/drivers/123/util.go @@ -43,7 +43,7 @@ const ( S3Auth = MainApi + "/file/s3_upload_object/auth" UploadCompleteV2 = MainApi + "/file/upload_complete/v2" S3Complete = MainApi + "/file/s3_complete_multipart_upload" - //AuthKeySalt = "8-8D$sL8gPjom7bk#cY" + // AuthKeySalt = "8-8D$sL8gPjom7bk#cY" ) func signPath(path string, os string, version string) (k string, v string) { @@ -282,3 +282,14 @@ func (d *Pan123) getFiles(ctx context.Context, parentId string, name string) ([] } return res, nil } + +func (d *Pan123) getUserInfo(ctx context.Context) (*UserInfoResp, error) { + var resp UserInfoResp + _, err := d.Request(UserInfo, http.MethodGet, func(req *resty.Request) { + req.SetContext(ctx) + }, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} diff --git a/drivers/123_open/driver.go b/drivers/123_open/driver.go index ba608bc0..62b4b95c 100644 --- a/drivers/123_open/driver.go +++ b/drivers/123_open/driver.go @@ -84,7 +84,7 @@ func (d *Open123) Link(ctx context.Context, file model.Obj, args model.LinkArgs) }, nil } - uid, err := d.getUID() + uid, err := d.getUID(ctx) if err != nil { return nil, err } @@ -215,7 +215,7 @@ func (d *Open123) Put(ctx context.Context, dstDir model.Obj, file model.FileStre } func (d *Open123) GetDetails(ctx context.Context) (*model.StorageDetails, error) { - userInfo, err := d.getUserInfo() + userInfo, err := d.getUserInfo(ctx) if err != nil { return nil, err } @@ -229,5 +229,7 @@ func (d *Open123) GetDetails(ctx context.Context) (*model.StorageDetails, error) }, nil } -var _ driver.Driver = (*Open123)(nil) -var _ driver.PutResult = (*Open123)(nil) +var ( + _ driver.Driver = (*Open123)(nil) + _ driver.PutResult = (*Open123)(nil) +) diff --git a/drivers/123_open/util.go b/drivers/123_open/util.go index 841e6a16..17da9a66 100644 --- a/drivers/123_open/util.go +++ b/drivers/123_open/util.go @@ -1,6 +1,7 @@ package _123_open import ( + "context" "crypto/md5" "encoding/json" "errors" @@ -18,7 +19,7 @@ import ( log "github.com/sirupsen/logrus" ) -var ( //不同情况下获取的AccessTokenQPS限制不同 如下模块化易于拓展 +var ( // 不同情况下获取的AccessTokenQPS限制不同 如下模块化易于拓展 Api = "https://open-api.123pan.com" AccessToken = InitApiInfo(Api+"/api/v1/access_token", 1) @@ -82,7 +83,6 @@ func (d *Open123) Request(apiInfo *ApiInfo, method string, callback base.ReqCall return nil, errors.New(baseResp.Message) } } - } func (d *Open123) flushAccessToken() error { @@ -148,21 +148,23 @@ func (d *Open123) SignURL(originURL, privateKey string, uid uint64, validDuratio return objURL.String(), nil } -func (d *Open123) getUserInfo() (*UserInfoResp, error) { +func (d *Open123) getUserInfo(ctx context.Context) (*UserInfoResp, error) { var resp UserInfoResp - if _, err := d.Request(UserInfo, http.MethodGet, nil, &resp); err != nil { + if _, err := d.Request(UserInfo, http.MethodGet, func(req *resty.Request) { + req.SetContext(ctx) + }, &resp); err != nil { return nil, err } return &resp, nil } -func (d *Open123) getUID() (uint64, error) { +func (d *Open123) getUID(ctx context.Context) (uint64, error) { if d.UID != 0 { return d.UID, nil } - resp, err := d.getUserInfo() + resp, err := d.getUserInfo(ctx) if err != nil { return 0, err } @@ -184,7 +186,6 @@ func (d *Open123) getFiles(parentFileId int64, limit int, lastFileId int64) (*Fi "searchData": "", }) }, &resp) - if err != nil { return nil, err } diff --git a/drivers/139/driver.go b/drivers/139/driver.go index c8048f1e..fbbc7f8e 100644 --- a/drivers/139/driver.go +++ b/drivers/139/driver.go @@ -54,7 +54,8 @@ func (d *Yun139) Init(ctx context.Context) error { "userInfo": base.Json{ "userType": 1, "accountType": 1, - "accountName": d.Account}, + "accountName": d.Account, + }, "modAddrType": 1, }, &resp) if err != nil { @@ -732,7 +733,7 @@ func (d *Yun139) Put(ctx context.Context, dstDir model.Obj, stream model.FileStr "manualRename": 2, "operation": 0, "path": path.Join(dstDir.GetPath(), dstDir.GetID()), - "seqNo": random.String(32), //序列号不能为空 + "seqNo": random.String(32), // 序列号不能为空 "totalSize": reportSize, "uploadContentList": []base.Json{{ "contentName": stream.GetName(), @@ -834,4 +835,48 @@ func (d *Yun139) Other(ctx context.Context, args model.OtherArgs) (interface{}, } } +func (d *Yun139) GetDetails(ctx context.Context) (*model.StorageDetails, error) { + if d.UserDomainID == "" { + return nil, errs.NotImplement + } + var total, free uint64 + if d.isFamily() { + diskInfo, err := d.getFamilyDiskInfo(ctx) + if err != nil { + return nil, err + } + totalMb, err := strconv.ParseUint(diskInfo.Data.DiskSize, 10, 64) + if err != nil { + return nil, fmt.Errorf("failed convert disk size into integer: %+v", err) + } + usedMb, err := strconv.ParseUint(diskInfo.Data.UsedSize, 10, 64) + if err != nil { + return nil, fmt.Errorf("failed convert used size into integer: %+v", err) + } + total = totalMb * 1024 * 1024 + free = total - (usedMb * 1024 * 1024) + } else { + diskInfo, err := d.getPersonalDiskInfo(ctx) + if err != nil { + return nil, err + } + totalMb, err := strconv.ParseUint(diskInfo.Data.DiskSize, 10, 64) + if err != nil { + return nil, fmt.Errorf("failed convert disk size into integer: %+v", err) + } + freeMb, err := strconv.ParseUint(diskInfo.Data.FreeDiskSize, 10, 64) + if err != nil { + return nil, fmt.Errorf("failed convert free size into integer: %+v", err) + } + total = totalMb * 1024 * 1024 + free = freeMb * 1024 * 1024 + } + return &model.StorageDetails{ + DiskUsage: model.DiskUsage{ + TotalSpace: total, + FreeSpace: free, + }, + }, nil +} + var _ driver.Driver = (*Yun139)(nil) diff --git a/drivers/139/meta.go b/drivers/139/meta.go index 982396b6..b226e240 100644 --- a/drivers/139/meta.go +++ b/drivers/139/meta.go @@ -11,6 +11,7 @@ type Addition struct { driver.RootID Type string `json:"type" type:"select" options:"personal_new,family,group,personal" default:"personal_new"` CloudID string `json:"cloud_id"` + UserDomainID string `json:"user_domain_id" help:"ud_id in Cookie, fill in to show disk usage"` CustomUploadPartSize int64 `json:"custom_upload_part_size" type:"number" default:"0" help:"0 for auto"` ReportRealSize bool `json:"report_real_size" type:"bool" default:"true" help:"Enable to report the real file size during upload"` UseLargeThumbnail bool `json:"use_large_thumbnail" type:"bool" default:"false" help:"Enable to use large thumbnail for images"` diff --git a/drivers/139/types.go b/drivers/139/types.go index d5f025a1..118655de 100644 --- a/drivers/139/types.go +++ b/drivers/139/types.go @@ -312,3 +312,20 @@ type RefreshTokenResp struct { AccessToken string `xml:"accessToken"` Desc string `xml:"desc"` } + +type PersonalDiskInfoResp struct { + BaseResp + Data struct { + FreeDiskSize string `json:"freeDiskSize"` + DiskSize string `json:"diskSize"` + IsInfinitePicStorage *bool `json:"isInfinitePicStorage"` + } `json:"data"` +} + +type FamilyDiskInfoResp struct { + BaseResp + Data struct { + UsedSize string `json:"usedSize"` + DiskSize string `json:"diskSize"` + } `json:"data"` +} diff --git a/drivers/139/util.go b/drivers/139/util.go index 9b7a3c56..9a9d2c6d 100644 --- a/drivers/139/util.go +++ b/drivers/139/util.go @@ -107,8 +107,7 @@ func (d *Yun139) refreshToken() error { return nil } -func (d *Yun139) request(pathname string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) { - url := "https://yun.139.com" + pathname +func (d *Yun139) request(url string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) { req := base.RestyClient.R() randStr := random.String(16) ts := time.Now().Format("2006-01-02 15:04:05") @@ -219,7 +218,7 @@ func (d *Yun139) requestRoute(data interface{}, resp interface{}) ([]byte, error } func (d *Yun139) post(pathname string, data interface{}, resp interface{}) ([]byte, error) { - return d.request(pathname, http.MethodPost, func(req *resty.Request) { + return d.request("https://yun.139.com"+pathname, http.MethodPost, func(req *resty.Request) { req.SetBody(data) }, resp) } @@ -268,7 +267,7 @@ func (d *Yun139) getFiles(catalogID string) ([]model.Obj, error) { HashInfo: utils.NewHashInfo(utils.MD5, content.Digest), }, Thumbnail: model.Thumbnail{Thumbnail: content.ThumbnailURL}, - //Thumbnail: content.BigthumbnailURL, + // Thumbnail: content.BigthumbnailURL, } files = append(files, &f) } @@ -335,7 +334,7 @@ func (d *Yun139) familyGetFiles(catalogID string) ([]model.Obj, error) { Path: path, // 文件所在目录的Path }, Thumbnail: model.Thumbnail{Thumbnail: content.ThumbnailURL}, - //Thumbnail: content.BigthumbnailURL, + // Thumbnail: content.BigthumbnailURL, } files = append(files, &f) } @@ -390,7 +389,7 @@ func (d *Yun139) groupGetFiles(catalogID string) ([]model.Obj, error) { Path: path, // 文件所在目录的Path }, Thumbnail: model.Thumbnail{Thumbnail: content.ThumbnailURL}, - //Thumbnail: content.BigthumbnailURL, + // Thumbnail: content.BigthumbnailURL, } files = append(files, &f) } @@ -418,6 +417,7 @@ func (d *Yun139) getLink(contentId string) (string, error) { } return jsoniter.Get(res, "data", "downloadURL").ToString(), nil } + func (d *Yun139) familyGetLink(contentId string, path string) (string, error) { data := d.newJson(base.Json{ "contentID": contentId, @@ -510,6 +510,7 @@ func (d *Yun139) personalRequest(pathname string, method string, callback base.R } return res.Body(), nil } + func (d *Yun139) personalPost(pathname string, data interface{}, resp interface{}) ([]byte, error) { return d.personalRequest(pathname, http.MethodPost, func(req *resty.Request) { req.SetBody(data) @@ -545,7 +546,7 @@ func (d *Yun139) personalGetFiles(fileId string) ([]model.Obj, error) { } nextPageCursor = resp.Data.NextPageCursor for _, item := range resp.Data.Items { - var isFolder = (item.Type == "folder") + isFolder := (item.Type == "folder") var f model.Obj if isFolder { f = &model.Object{ @@ -557,7 +558,7 @@ func (d *Yun139) personalGetFiles(fileId string) ([]model.Obj, error) { IsFolder: isFolder, } } else { - var Thumbnails = item.Thumbnails + Thumbnails := item.Thumbnails var ThumbnailUrl string if d.UseLargeThumbnail { for _, thumb := range Thumbnails { @@ -600,7 +601,7 @@ func (d *Yun139) personalGetLink(fileId string) (string, error) { if err != nil { return "", err } - var cdnUrl = jsoniter.Get(res, "data", "cdnUrl").ToString() + cdnUrl := jsoniter.Get(res, "data", "cdnUrl").ToString() if cdnUrl != "" { return cdnUrl, nil } else { @@ -614,12 +615,14 @@ func (d *Yun139) getAuthorization() string { } return d.Authorization } + func (d *Yun139) getAccount() string { if d.ref != nil { return d.ref.getAccount() } return d.Account } + func (d *Yun139) getPersonalCloudHost() string { if d.ref != nil { return d.ref.getPersonalCloudHost() @@ -670,3 +673,33 @@ func (d *Yun139) uploadPersonalParts(ctx context.Context, partInfos []PartInfo, } return nil } + +func (d *Yun139) getPersonalDiskInfo(ctx context.Context) (*PersonalDiskInfoResp, error) { + data := map[string]interface{}{ + "userDomainId": d.UserDomainID, + } + var resp PersonalDiskInfoResp + _, err := d.request("https://user-njs.yun.139.com/user/disk/getPersonalDiskInfo", http.MethodPost, func(req *resty.Request) { + req.SetBody(data) + req.SetContext(ctx) + }, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} + +func (d *Yun139) getFamilyDiskInfo(ctx context.Context) (*FamilyDiskInfoResp, error) { + data := map[string]interface{}{ + "userDomainId": d.UserDomainID, + } + var resp FamilyDiskInfoResp + _, err := d.request("https://user-njs.yun.139.com/user/disk/getFamilyDiskInfo", http.MethodPost, func(req *resty.Request) { + req.SetBody(data) + req.SetContext(ctx) + }, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} diff --git a/drivers/189/driver.go b/drivers/189/driver.go index 5e546ef8..c12c010c 100644 --- a/drivers/189/driver.go +++ b/drivers/189/driver.go @@ -194,4 +194,17 @@ func (d *Cloud189) Put(ctx context.Context, dstDir model.Obj, stream model.FileS return d.newUpload(ctx, dstDir, stream, up) } +func (d *Cloud189) GetDetails(ctx context.Context) (*model.StorageDetails, error) { + capacityInfo, err := d.getCapacityInfo(ctx) + if err != nil { + return nil, err + } + return &model.StorageDetails{ + DiskUsage: model.DiskUsage{ + TotalSpace: capacityInfo.CloudCapacityInfo.TotalSize, + FreeSpace: capacityInfo.CloudCapacityInfo.FreeSize, + }, + }, nil +} + var _ driver.Driver = (*Cloud189)(nil) diff --git a/drivers/189/types.go b/drivers/189/types.go index 5354db95..4aee16e1 100644 --- a/drivers/189/types.go +++ b/drivers/189/types.go @@ -66,3 +66,21 @@ type DownResp struct { ResMessage string `json:"res_message"` FileDownloadUrl string `json:"downloadUrl"` } + +type CapacityResp struct { + ResCode int `json:"res_code"` + ResMessage string `json:"res_message"` + Account string `json:"account"` + CloudCapacityInfo struct { + FreeSize uint64 `json:"freeSize"` + MailUsedSize uint64 `json:"mail189UsedSize"` + TotalSize uint64 `json:"totalSize"` + UsedSize uint64 `json:"usedSize"` + } `json:"cloudCapacityInfo"` + FamilyCapacityInfo struct { + FreeSize uint64 `json:"freeSize"` + TotalSize uint64 `json:"totalSize"` + UsedSize uint64 `json:"usedSize"` + } `json:"familyCapacityInfo"` + TotalSize uint64 `json:"totalSize"` +} diff --git a/drivers/189/util.go b/drivers/189/util.go index d10c7e8b..6e0682ea 100644 --- a/drivers/189/util.go +++ b/drivers/189/util.go @@ -157,7 +157,7 @@ func (d *Cloud189) request(url string, method string, callback base.ReqCallback, if err != nil { return nil, err } - //log.Debug(res.String()) + // log.Debug(res.String()) if e.ErrorCode != "" { if e.ErrorCode == "InvalidSessionKey" { err = d.newLogin() @@ -186,8 +186,8 @@ func (d *Cloud189) getFiles(fileId string) ([]model.Obj, error) { "mediaType": "0", "folderId": fileId, "iconOption": "5", - "orderBy": "lastOpTime", //account.OrderBy - "descending": "true", //account.OrderDirection + "orderBy": "lastOpTime", // account.OrderBy + "descending": "true", // account.OrderDirection }) }, &resp) if err != nil { @@ -311,7 +311,7 @@ func (d *Cloud189) newUpload(ctx context.Context, dstDir model.Obj, file model.F } d.sessionKey = sessionKey const DEFAULT int64 = 10485760 - var count = int64(math.Ceil(float64(file.GetSize()) / float64(DEFAULT))) + count := int64(math.Ceil(float64(file.GetSize()) / float64(DEFAULT))) res, err := d.uploadRequest("/person/initMultiUpload", map[string]string{ "parentFolderId": dstDir.GetID(), @@ -340,10 +340,10 @@ func (d *Cloud189) newUpload(ctx context.Context, dstDir model.Obj, file model.F if DEFAULT < byteSize { byteSize = DEFAULT } - //log.Debugf("%d,%d", byteSize, finish) + // log.Debugf("%d,%d", byteSize, finish) byteData := make([]byte, byteSize) n, err := io.ReadFull(file, byteData) - //log.Debug(err, n) + // log.Debug(err, n) if err != nil { return err } @@ -395,3 +395,14 @@ func (d *Cloud189) newUpload(ctx context.Context, dstDir model.Obj, file model.F }, nil) return err } + +func (d *Cloud189) getCapacityInfo(ctx context.Context) (*CapacityResp, error) { + var resp CapacityResp + _, err := d.request("https://cloud.189.cn/api/portal/getUserSizeInfo.action", http.MethodGet, func(req *resty.Request) { + req.SetContext(ctx) + }, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} diff --git a/drivers/189_tv/driver.go b/drivers/189_tv/driver.go index d24ef529..6ee69c7e 100644 --- a/drivers/189_tv/driver.go +++ b/drivers/189_tv/driver.go @@ -69,7 +69,7 @@ func (y *Cloud189TV) Init(ctx context.Context) (err error) { // 避免重复登陆 if !y.isLogin() || y.Addition.AccessToken == "" { if err = y.login(); err != nil { - return + return err } } @@ -83,7 +83,7 @@ func (y *Cloud189TV) Init(ctx context.Context) (err error) { y.cron = cron.NewCron(time.Minute * 5) y.cron.Do(y.keepAlive) - return + return err } func (y *Cloud189TV) Drop(ctx context.Context) error { @@ -244,7 +244,6 @@ func (y *Cloud189TV) Copy(ctx context.Context, srcObj, dstDir model.Obj) error { FileName: srcObj.GetName(), IsFolder: BoolToNumber(srcObj.IsDir()), }) - if err != nil { return err } @@ -278,5 +277,25 @@ func (y *Cloud189TV) Put(ctx context.Context, dstDir model.Obj, stream model.Fil } return y.OldUpload(ctx, dstDir, stream, up, isFamily, overwrite) - +} + +func (y *Cloud189TV) GetDetails(ctx context.Context) (*model.StorageDetails, error) { + capacityInfo, err := y.getCapacityInfo(ctx) + if err != nil { + return nil, err + } + var total, free uint64 + if y.isFamily() { + total = capacityInfo.FamilyCapacityInfo.TotalSize + free = capacityInfo.FamilyCapacityInfo.FreeSize + } else { + total = capacityInfo.CloudCapacityInfo.TotalSize + free = capacityInfo.CloudCapacityInfo.FreeSize + } + return &model.StorageDetails{ + DiskUsage: model.DiskUsage{ + TotalSpace: total, + FreeSpace: free, + }, + }, nil } diff --git a/drivers/189_tv/types.go b/drivers/189_tv/types.go index fbec8016..686a090e 100644 --- a/drivers/189_tv/types.go +++ b/drivers/189_tv/types.go @@ -316,3 +316,21 @@ type BatchTaskConflictTaskInfoResp struct { TaskInfos []BatchTaskInfo TaskType int `json:"taskType"` } + +type CapacityResp struct { + ResCode int `json:"res_code"` + ResMessage string `json:"res_message"` + Account string `json:"account"` + CloudCapacityInfo struct { + FreeSize uint64 `json:"freeSize"` + MailUsedSize uint64 `json:"mail189UsedSize"` + TotalSize uint64 `json:"totalSize"` + UsedSize uint64 `json:"usedSize"` + } `json:"cloudCapacityInfo"` + FamilyCapacityInfo struct { + FreeSize uint64 `json:"freeSize"` + TotalSize uint64 `json:"totalSize"` + UsedSize uint64 `json:"usedSize"` + } `json:"familyCapacityInfo"` + TotalSize uint64 `json:"totalSize"` +} diff --git a/drivers/189_tv/utils.go b/drivers/189_tv/utils.go index 395c5dcd..8678f826 100644 --- a/drivers/189_tv/utils.go +++ b/drivers/189_tv/utils.go @@ -173,6 +173,7 @@ func (y *Cloud189TV) put(ctx context.Context, url string, headers map[string]str } return body, nil } + func (y *Cloud189TV) getFiles(ctx context.Context, fileId string, isFamily bool) ([]model.Obj, error) { fullUrl := ApiUrl if isFamily { @@ -238,9 +239,8 @@ func (y *Cloud189TV) login() (err error) { req.SetHeaders(y.AppKeySignatureHeader(ApiUrl+"/family/manage/getQrCodeUUID.action", http.MethodGet)) _, err = req.Execute(http.MethodGet, ApiUrl+"/family/manage/getQrCodeUUID.action") - if err != nil { - return + return err } if erron.HasError() { return &erron @@ -280,7 +280,7 @@ func (y *Cloud189TV) login() (err error) { req.SetQueryParam("uuid", y.TempUuid) _, err = req.Execute(http.MethodGet, ApiUrl+"/family/manage/qrcodeLoginResult.action") if err != nil { - return + return err } if erron.HasError() { return &erron @@ -300,7 +300,7 @@ func (y *Cloud189TV) login() (err error) { reqb.SetQueryParam("e189AccessToken", y.Addition.AccessToken) _, err = reqb.Execute(http.MethodGet, ApiUrl+"/family/manage/loginFamilyMerge.action") if err != nil { - return + return err } if erron.HasError() { @@ -309,7 +309,7 @@ func (y *Cloud189TV) login() (err error) { y.tokenInfo = &tokenInfo op.MustSaveDriverStorage(y) - return + return err } // refreshSession 尝试使用现有的 AccessToken 刷新会话 @@ -324,7 +324,7 @@ func (y *Cloud189TV) refreshSession() (err error) { reqb.SetQueryParam("e189AccessToken", y.Addition.AccessToken) _, err = reqb.Execute(http.MethodGet, ApiUrl+"/family/manage/loginFamilyMerge.action") if err != nil { - return + return err } if erron.HasError() { @@ -371,7 +371,7 @@ func (y *Cloud189TV) RapidUpload(ctx context.Context, dstDir model.Obj, stream m // 旧版本上传,家庭云不支持覆盖 func (y *Cloud189TV) OldUpload(ctx context.Context, dstDir model.Obj, file model.FileStreamer, up driver.UpdateProgress, isFamily bool, overwrite bool) (model.Obj, error) { fileMd5 := file.GetHash().GetHash(utils.MD5) - var tempFile = file.GetFile() + tempFile := file.GetFile() var err error if len(fileMd5) != utils.MD5.Width { tempFile, fileMd5, err = stream.CacheFullAndHash(file, &up, utils.MD5) @@ -474,7 +474,6 @@ func (y *Cloud189TV) OldUploadCreate(ctx context.Context, parentID string, fileM }) } }, &uploadInfo, isFamily) - if err != nil { return nil, err } @@ -628,3 +627,15 @@ func (y *Cloud189TV) WaitBatchTask(aType string, taskID string, t time.Duration) time.Sleep(t) } } + +func (y *Cloud189TV) getCapacityInfo(ctx context.Context) (*CapacityResp, error) { + fullUrl := ApiUrl + "/portal/getUserSizeInfo.action" + var resp CapacityResp + _, err := y.get(fullUrl, func(req *resty.Request) { + req.SetContext(ctx) + }, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} diff --git a/drivers/189pc/driver.go b/drivers/189pc/driver.go index f7ba93c9..6861f7c2 100644 --- a/drivers/189pc/driver.go +++ b/drivers/189pc/driver.go @@ -90,11 +90,11 @@ func (y *Cloud189PC) Init(ctx context.Context) (err error) { if y.Addition.RefreshToken != "" { y.tokenInfo = &AppSessionResp{RefreshToken: y.Addition.RefreshToken} if err = y.refreshToken(); err != nil { - return + return err } } else { if err = y.login(); err != nil { - return + return err } } @@ -124,7 +124,7 @@ func (y *Cloud189PC) Init(ctx context.Context) (err error) { utils.Log.Errorf("cleanFamilyTransferFolderError:%s", err) } }) - return + return err } func (d *Cloud189PC) InitReference(storage driver.Driver) error { @@ -305,7 +305,6 @@ func (y *Cloud189PC) Copy(ctx context.Context, srcObj, dstDir model.Obj) error { FileName: srcObj.GetName(), IsFolder: BoolToNumber(srcObj.IsDir()), }) - if err != nil { return err } @@ -411,3 +410,24 @@ func (y *Cloud189PC) Put(ctx context.Context, dstDir model.Obj, stream model.Fil return y.StreamUpload(ctx, dstDir, stream, up, isFamily, overwrite) } } + +func (y *Cloud189PC) GetDetails(ctx context.Context) (*model.StorageDetails, error) { + capacityInfo, err := y.getCapacityInfo(ctx) + if err != nil { + return nil, err + } + var total, free uint64 + if y.isFamily() { + total = capacityInfo.FamilyCapacityInfo.TotalSize + free = capacityInfo.FamilyCapacityInfo.FreeSize + } else { + total = capacityInfo.CloudCapacityInfo.TotalSize + free = capacityInfo.CloudCapacityInfo.FreeSize + } + return &model.StorageDetails{ + DiskUsage: model.DiskUsage{ + TotalSpace: total, + FreeSpace: free, + }, + }, nil +} diff --git a/drivers/189pc/types.go b/drivers/189pc/types.go index d629a2ad..fe219d8c 100644 --- a/drivers/189pc/types.go +++ b/drivers/189pc/types.go @@ -409,3 +409,21 @@ func (p Params) Encode() string { } return buf.String() } + +type CapacityResp struct { + ResCode int `json:"res_code"` + ResMessage string `json:"res_message"` + Account string `json:"account"` + CloudCapacityInfo struct { + FreeSize uint64 `json:"freeSize"` + MailUsedSize uint64 `json:"mail189UsedSize"` + TotalSize uint64 `json:"totalSize"` + UsedSize uint64 `json:"usedSize"` + } `json:"cloudCapacityInfo"` + FamilyCapacityInfo struct { + FreeSize uint64 `json:"freeSize"` + TotalSize uint64 `json:"totalSize"` + UsedSize uint64 `json:"usedSize"` + } `json:"familyCapacityInfo"` + TotalSize uint64 `json:"totalSize"` +} diff --git a/drivers/189pc/utils.go b/drivers/189pc/utils.go index 39bc3322..87b607b7 100644 --- a/drivers/189pc/utils.go +++ b/drivers/189pc/utils.go @@ -189,6 +189,7 @@ func (y *Cloud189PC) put(ctx context.Context, url string, headers map[string]str } return body, nil } + func (y *Cloud189PC) getFiles(ctx context.Context, fileId string, isFamily bool) ([]model.Obj, error) { res := make([]model.Obj, 0, 100) for pageNum := 1; ; pageNum++ { @@ -342,7 +343,7 @@ func (y *Cloud189PC) loginByPassword() (err error) { SetQueryParam("redirectURL", loginresp.ToUrl). Post(API_URL + "/getSessionForPC.action") if err != nil { - return + return err } if erron.HasError() { @@ -350,12 +351,12 @@ func (y *Cloud189PC) loginByPassword() (err error) { } if tokenInfo.ResCode != 0 { err = fmt.Errorf(tokenInfo.ResMessage) - return + return err } y.Addition.RefreshToken = tokenInfo.RefreshToken y.tokenInfo = &tokenInfo op.MustSaveDriverStorage(y) - return + return err } func (y *Cloud189PC) loginByQRCode() error { @@ -447,7 +448,6 @@ func (y *Cloud189PC) genQRCode(text string) error { // Create the HTML page qrPage := fmt.Sprintf(qrTemplate, text, qrCodeBase64, y.qrcodeParam.UUID) return fmt.Errorf("need verify: \n%s", qrPage) - } func (y *Cloud189PC) initBaseParams() (*BaseLoginParam, error) { @@ -616,7 +616,7 @@ func (y *Cloud189PC) refreshTokenWithRetry(retryCount int) (err error) { if y.ref != nil { return y.ref.refreshTokenWithRetry(retryCount) } - + // 限制重试次数,避免无限递归 if retryCount >= 3 { if y.Addition.RefreshToken != "" { @@ -625,7 +625,7 @@ func (y *Cloud189PC) refreshTokenWithRetry(retryCount int) (err error) { } return errors.New("refresh token failed after maximum retries") } - + var erron RespErr var tokenInfo AppSessionResp _, err = y.client.R(). @@ -700,7 +700,7 @@ func (y *Cloud189PC) StreamUpload(ctx context.Context, dstDir model.Obj, file mo params.Set("familyId", y.FamilyID) fullUrl += "/family" } else { - //params.Set("extend", `{"opScene":"1","relativepath":"","rootfolderid":""}`) + // params.Set("extend", `{"opScene":"1","relativepath":"","rootfolderid":""}`) fullUrl += "/person" } @@ -876,7 +876,7 @@ func (y *Cloud189PC) FastUpload(ctx context.Context, dstDir model.Obj, file mode lastSliceSize = sliceSize } - //step.1 优先计算所需信息 + // step.1 优先计算所需信息 byteSize := sliceSize fileMd5 := utils.MD5.NewFunc() sliceMd5 := utils.MD5.NewFunc() @@ -927,14 +927,14 @@ func (y *Cloud189PC) FastUpload(ctx context.Context, dstDir model.Obj, file mode if isFamily { fullUrl += "/family" } else { - //params.Set("extend", `{"opScene":"1","relativepath":"","rootfolderid":""}`) + // params.Set("extend", `{"opScene":"1","relativepath":"","rootfolderid":""}`) fullUrl += "/person" } // 尝试恢复进度 uploadProgress, ok := base.GetUploadProgress[*UploadProgress](y, y.getTokenInfo().SessionKey, fileMd5Hex) if !ok { - //step.2 预上传 + // step.2 预上传 params := Params{ "parentFolderId": dstDir.GetID(), "fileName": url.QueryEscape(file.GetName()), @@ -1163,7 +1163,6 @@ func (y *Cloud189PC) OldUploadCreate(ctx context.Context, parentID string, fileM }) } }, &uploadInfo, isFamily) - if err != nil { return nil, err } @@ -1473,3 +1472,15 @@ func (y *Cloud189PC) getClient() *resty.Client { } return y.client } + +func (y *Cloud189PC) getCapacityInfo(ctx context.Context) (*CapacityResp, error) { + fullUrl := API_URL + "/portal/getUserSizeInfo.action" + var resp CapacityResp + _, err := y.get(fullUrl, func(req *resty.Request) { + req.SetContext(ctx) + }, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} diff --git a/drivers/alias/driver.go b/drivers/alias/driver.go index 51ef8f69..cd25123e 100644 --- a/drivers/alias/driver.go +++ b/drivers/alias/driver.go @@ -123,7 +123,7 @@ func (d *Alias) Get(ctx context.Context, path string) (model.Obj, error) { func (d *Alias) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) { path := dir.GetPath() if utils.PathEqual(path, "/") && !d.autoFlatten { - return d.listRoot(), nil + return d.listRoot(ctx, args.WithStorageDetails && d.DetailsPassThrough), nil } root, sub := d.getRootAndPath(path) dsts, ok := d.pathMap[root] @@ -131,9 +131,12 @@ func (d *Alias) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([ return nil, errs.ObjectNotFound } var objs []model.Obj - fsArgs := &fs.ListArgs{NoLog: true, Refresh: args.Refresh} for _, dst := range dsts { - tmp, err := fs.List(ctx, stdpath.Join(dst, sub), fsArgs) + tmp, err := fs.List(ctx, stdpath.Join(dst, sub), &fs.ListArgs{ + NoLog: true, + Refresh: args.Refresh, + WithStorageDetails: args.WithStorageDetails && d.DetailsPassThrough, + }) if err == nil { tmp, err = utils.SliceConvert(tmp, func(obj model.Obj) (model.Obj, error) { thumb, ok := model.GetThumb(obj) diff --git a/drivers/alias/meta.go b/drivers/alias/meta.go index 27c2f8f2..bdab5374 100644 --- a/drivers/alias/meta.go +++ b/drivers/alias/meta.go @@ -16,6 +16,7 @@ type Addition struct { DownloadPartSize int `json:"download_part_size" default:"0" type:"number" required:"false" help:"Need to enable proxy. Unit: KB"` Writable bool `json:"writable" type:"bool" default:"false"` ProviderPassThrough bool `json:"provider_pass_through" type:"bool" default:"false"` + DetailsPassThrough bool `json:"details_pass_through" type:"bool" default:"false"` } var config = driver.Config{ diff --git a/drivers/alias/util.go b/drivers/alias/util.go index 11c299e9..b1b767c3 100644 --- a/drivers/alias/util.go +++ b/drivers/alias/util.go @@ -2,8 +2,10 @@ package alias import ( "context" + "errors" stdpath "path" "strings" + "sync" "github.com/OpenListTeam/OpenList/v4/internal/driver" "github.com/OpenListTeam/OpenList/v4/internal/errs" @@ -11,24 +13,58 @@ import ( "github.com/OpenListTeam/OpenList/v4/internal/model" "github.com/OpenListTeam/OpenList/v4/internal/op" "github.com/OpenListTeam/OpenList/v4/server/common" + log "github.com/sirupsen/logrus" ) -func (d *Alias) listRoot() []model.Obj { +func (d *Alias) listRoot(ctx context.Context, withDetails bool) []model.Obj { var objs []model.Obj - for k := range d.pathMap { + var wg sync.WaitGroup + for k, v := range d.pathMap { obj := model.Object{ Name: k, IsFolder: true, Modified: d.Modified, } + idx := len(objs) objs = append(objs, &obj) + if !withDetails || len(v) != 1 { + continue + } + remoteDriver, err := op.GetStorageByMountPath(v[0]) + if err != nil { + continue + } + _, ok := remoteDriver.(driver.WithDetails) + if !ok { + continue + } + objs[idx] = &model.ObjStorageDetails{ + Obj: objs[idx], + StorageDetailsWithName: model.StorageDetailsWithName{ + StorageDetails: nil, + DriverName: remoteDriver.Config().Name, + }, + } + wg.Add(1) + go func() { + defer wg.Done() + details, e := op.GetStorageDetails(ctx, remoteDriver) + if e != nil { + if !errors.Is(e, errs.NotImplement) { + log.Errorf("failed get %s storage details: %+v", remoteDriver.GetStorage().MountPath, e) + } + return + } + objs[idx].(*model.ObjStorageDetails).StorageDetails = details + }() } + wg.Wait() return objs } // do others that not defined in Driver interface func getPair(path string) (string, string) { - //path = strings.TrimSpace(path) + // path = strings.TrimSpace(path) if strings.Contains(path, ":") { pair := strings.SplitN(path, ":", 2) if !strings.Contains(pair[0], "/") { diff --git a/drivers/aliyundrive/driver.go b/drivers/aliyundrive/driver.go index 92df0319..6fd9bff2 100644 --- a/drivers/aliyundrive/driver.go +++ b/drivers/aliyundrive/driver.go @@ -45,7 +45,7 @@ func (d *AliDrive) GetAddition() driver.Additional { func (d *AliDrive) Init(ctx context.Context) error { // TODO login / refresh token - //op.MustSaveDriverStorage(d) + // op.MustSaveDriverStorage(d) err := d.refreshToken() if err != nil { return err @@ -171,7 +171,7 @@ func (d *AliDrive) Put(ctx context.Context, dstDir model.Obj, streamer model.Fil Mimetype: streamer.GetMimetype(), } const DEFAULT int64 = 10485760 - var count = int(math.Ceil(float64(streamer.GetSize()) / float64(DEFAULT))) + count := int(math.Ceil(float64(streamer.GetSize()) / float64(DEFAULT))) partInfoList := make([]base.Json, 0, count) for i := 1; i <= count; i++ { @@ -327,6 +327,23 @@ func (d *AliDrive) Put(ctx context.Context, dstDir model.Obj, streamer model.Fil return fmt.Errorf("%+v", resp2) } +func (d *AliDrive) GetDetails(ctx context.Context) (*model.StorageDetails, error) { + res, err, _ := d.request("https://api.aliyundrive.com/adrive/v1/user/driveCapacityDetails", http.MethodPost, func(req *resty.Request) { + req.SetContext(ctx) + }, nil) + if err != nil { + return nil, err + } + used := utils.Json.Get(res, "drive_used_size").ToUint64() + total := utils.Json.Get(res, "drive_total_size").ToUint64() + return &model.StorageDetails{ + DiskUsage: model.DiskUsage{ + TotalSpace: total, + FreeSpace: total - used, + }, + }, nil +} + func (d *AliDrive) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) { var resp base.Json var url string diff --git a/drivers/baidu_netdisk/driver.go b/drivers/baidu_netdisk/driver.go index 5a692378..4f8e3446 100644 --- a/drivers/baidu_netdisk/driver.go +++ b/drivers/baidu_netdisk/driver.go @@ -212,7 +212,7 @@ func (d *BaiduNetdisk) Put(ctx context.Context, dstDir model.Obj, stream model.F lastBlockSize = sliceSize } - //cal md5 for first 256k data + // cal md5 for first 256k data const SliceSize int64 = 256 * utils.KB // cal md5 blockList := make([]string, 0, count) @@ -284,7 +284,7 @@ func (d *BaiduNetdisk) Put(ctx context.Context, dstDir model.Obj, stream model.F } log.Debugf("%+v", precreateResp) if precreateResp.ReturnType == 2 { - //rapid upload, since got md5 match from baidu server + // rapid upload, since got md5 match from baidu server // 修复时间,具体原因见 Put 方法注释的 **注意** precreateResp.File.Ctime = ctime precreateResp.File.Mtime = mtime @@ -365,7 +365,7 @@ func (d *BaiduNetdisk) uploadSlice(ctx context.Context, params map[string]string } func (d *BaiduNetdisk) GetDetails(ctx context.Context) (*model.StorageDetails, error) { - du, err := d.quota() + du, err := d.quota(ctx) if err != nil { return nil, err } diff --git a/drivers/baidu_netdisk/util.go b/drivers/baidu_netdisk/util.go index 3f031cc4..6ae911e9 100644 --- a/drivers/baidu_netdisk/util.go +++ b/drivers/baidu_netdisk/util.go @@ -1,6 +1,7 @@ package baidu_netdisk import ( + "context" "encoding/hex" "errors" "fmt" @@ -207,7 +208,7 @@ func (d *BaiduNetdisk) linkOfficial(file model.Obj, _ model.LinkArgs) (*model.Li if err != nil { return nil, err } - //if res.StatusCode() == 302 { + // if res.StatusCode() == 302 { u = res.Header().Get("location") //} @@ -381,9 +382,11 @@ func (d *BaiduNetdisk) getSliceSize(filesize int64) int64 { return maxSliceSize } -func (d *BaiduNetdisk) quota() (*model.DiskUsage, error) { +func (d *BaiduNetdisk) quota(ctx context.Context) (*model.DiskUsage, error) { var resp QuotaResp - _, err := d.request("https://pan.baidu.com/api/quota", http.MethodGet, nil, &resp) + _, err := d.request("https://pan.baidu.com/api/quota", http.MethodGet, func(req *resty.Request) { + req.SetContext(ctx) + }, &resp) if err != nil { return nil, err } diff --git a/drivers/chunk/driver.go b/drivers/chunk/driver.go index 76346974..21772eaf 100644 --- a/drivers/chunk/driver.go +++ b/drivers/chunk/driver.go @@ -472,11 +472,7 @@ func (d *Chunk) GetDetails(ctx context.Context) (*model.StorageDetails, error) { if err != nil { return nil, errs.NotImplement } - wd, ok := remoteStorage.(driver.WithDetails) - if !ok { - return nil, errs.NotImplement - } - remoteDetails, err := wd.GetDetails(ctx) + remoteDetails, err := op.GetStorageDetails(ctx, remoteStorage) if err != nil { return nil, err } diff --git a/drivers/cloudreve_v4/driver.go b/drivers/cloudreve_v4/driver.go index e9ce639e..180ca1bd 100644 --- a/drivers/cloudreve_v4/driver.go +++ b/drivers/cloudreve_v4/driver.go @@ -342,7 +342,9 @@ func (d *CloudreveV4) ArchiveDecompress(ctx context.Context, srcObj, dstDir mode func (d *CloudreveV4) GetDetails(ctx context.Context) (*model.StorageDetails, error) { // TODO return storage details (total space, free space, etc.) var r CapacityResp - err := d.request(http.MethodGet, "/user/capacity", nil, &r) + err := d.request(http.MethodGet, "/user/capacity", func(req *resty.Request) { + req.SetContext(ctx) + }, &r) if err != nil { return nil, err } diff --git a/drivers/crypt/driver.go b/drivers/crypt/driver.go index ac5a7797..b00a2ea0 100644 --- a/drivers/crypt/driver.go +++ b/drivers/crypt/driver.go @@ -44,7 +44,7 @@ func (d *Crypt) GetAddition() driver.Additional { } func (d *Crypt) Init(ctx context.Context) error { - //obfuscate credentials if it's updated or just created + // obfuscate credentials if it's updated or just created err := d.updateObfusParm(&d.Password) if err != nil { return fmt.Errorf("failed to obfuscate password: %w", err) @@ -63,7 +63,7 @@ func (d *Crypt) Init(ctx context.Context) error { op.MustSaveDriverStorage(d) - //need remote storage exist + // need remote storage exist storage, err := fs.GetStorage(d.RemotePath, &fs.GetStoragesArgs{}) if err != nil { return fmt.Errorf("can't find remote storage: %w", err) @@ -109,8 +109,8 @@ func (d *Crypt) Drop(ctx context.Context) error { func (d *Crypt) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) { path := dir.GetPath() - //return d.list(ctx, d.RemotePath, path) - //remoteFull + // return d.list(ctx, d.RemotePath, path) + // remoteFull objs, err := fs.List(ctx, d.getPathForRemote(path, true), &fs.ListArgs{NoLog: true, Refresh: args.Refresh}) // the obj must implement the model.SetPath interface @@ -124,7 +124,7 @@ func (d *Crypt) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([ if obj.IsDir() { name, err := d.cipher.DecryptDirName(obj.GetName()) if err != nil { - //filter illegal files + // filter illegal files continue } if !d.ShowHidden && strings.HasPrefix(name, ".") { @@ -143,12 +143,12 @@ func (d *Crypt) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([ thumb, ok := model.GetThumb(obj) size, err := d.cipher.DecryptedSize(obj.GetSize()) if err != nil { - //filter illegal files + // filter illegal files continue } name, err := d.cipher.DecryptFileName(obj.GetName()) if err != nil { - //filter illegal files + // filter illegal files continue } if !d.ShowHidden && strings.HasPrefix(name, ".") { @@ -202,7 +202,7 @@ func (d *Crypt) Get(ctx context.Context, path string) (model.Obj, error) { remoteObj, err = fs.Get(ctx, remoteFullPath, &fs.GetArgs{NoLog: true}) if err != nil { if errs.IsObjectNotFound(err) && secondTry { - //try the opposite + // try the opposite remoteFullPath = d.getPathForRemote(path, !firstTryIsFolder) remoteObj, err2 = fs.Get(ctx, remoteFullPath, &fs.GetArgs{NoLog: true}) if err2 != nil { @@ -240,7 +240,7 @@ func (d *Crypt) Get(ctx context.Context, path string) (model.Obj, error) { IsFolder: remoteObj.IsDir(), } return obj, nil - //return nil, errs.ObjectNotFound + // return nil, errs.ObjectNotFound } // https://github.com/rclone/rclone/blob/v1.67.0/backend/crypt/cipher.go#L37 @@ -366,7 +366,6 @@ func (d *Crypt) Copy(ctx context.Context, srcObj, dstDir model.Obj) error { return fmt.Errorf("failed to convert path to remote path: %w", err) } return op.Copy(ctx, d.remoteStorage, srcRemoteActualPath, dstRemoteActualPath) - } func (d *Crypt) Remove(ctx context.Context, obj model.Obj) error { @@ -412,11 +411,7 @@ func (d *Crypt) Put(ctx context.Context, dstDir model.Obj, streamer model.FileSt } func (d *Crypt) GetDetails(ctx context.Context) (*model.StorageDetails, error) { - wd, ok := d.remoteStorage.(driver.WithDetails) - if !ok { - return nil, errs.NotImplement - } - remoteDetails, err := wd.GetDetails(ctx) + remoteDetails, err := op.GetStorageDetails(ctx, d.remoteStorage) if err != nil { return nil, err } diff --git a/drivers/google_drive/driver.go b/drivers/google_drive/driver.go index c4dd01af..36b9ff01 100644 --- a/drivers/google_drive/driver.go +++ b/drivers/google_drive/driver.go @@ -167,4 +167,33 @@ func (d *GoogleDrive) Put(ctx context.Context, dstDir model.Obj, stream model.Fi return err } +func (d *GoogleDrive) GetDetails(ctx context.Context) (*model.StorageDetails, error) { + if d.DisableDiskUsage { + return nil, errs.NotImplement + } + about, err := d.getAbout(ctx) + if err != nil { + return nil, err + } + var total, used uint64 + if about.StorageQuota.Limit == nil { + total = 0 + } else { + total, err = strconv.ParseUint(*about.StorageQuota.Limit, 10, 64) + if err != nil { + return nil, err + } + } + used, err = strconv.ParseUint(about.StorageQuota.Usage, 10, 64) + if err != nil { + return nil, err + } + return &model.StorageDetails{ + DiskUsage: model.DiskUsage{ + TotalSpace: total, + FreeSpace: total - used, + }, + }, nil +} + var _ driver.Driver = (*GoogleDrive)(nil) diff --git a/drivers/google_drive/meta.go b/drivers/google_drive/meta.go index 34eeac28..75263a12 100644 --- a/drivers/google_drive/meta.go +++ b/drivers/google_drive/meta.go @@ -7,14 +7,15 @@ import ( type Addition struct { driver.RootID - RefreshToken string `json:"refresh_token" required:"true"` - OrderBy string `json:"order_by" type:"string" help:"such as: folder,name,modifiedTime"` - OrderDirection string `json:"order_direction" type:"select" options:"asc,desc"` - UseOnlineAPI bool `json:"use_online_api" default:"true"` - APIAddress string `json:"api_url_address" default:"https://api.oplist.org/googleui/renewapi"` - ClientID string `json:"client_id"` - ClientSecret string `json:"client_secret"` - ChunkSize int64 `json:"chunk_size" type:"number" default:"5" help:"chunk size while uploading (unit: MB)"` + RefreshToken string `json:"refresh_token" required:"true"` + OrderBy string `json:"order_by" type:"string" help:"such as: folder,name,modifiedTime"` + OrderDirection string `json:"order_direction" type:"select" options:"asc,desc"` + UseOnlineAPI bool `json:"use_online_api" default:"true"` + APIAddress string `json:"api_url_address" default:"https://api.oplist.org/googleui/renewapi"` + ClientID string `json:"client_id"` + ClientSecret string `json:"client_secret"` + ChunkSize int64 `json:"chunk_size" type:"number" default:"5" help:"chunk size while uploading (unit: MB)"` + DisableDiskUsage bool `json:"disable_disk_usage" default:"false"` } var config = driver.Config{ diff --git a/drivers/google_drive/types.go b/drivers/google_drive/types.go index df8bddb9..5e71f305 100644 --- a/drivers/google_drive/types.go +++ b/drivers/google_drive/types.go @@ -78,3 +78,12 @@ type Error struct { Message string `json:"message"` } `json:"error"` } + +type AboutResp struct { + StorageQuota struct { + Limit *string `json:"limit"` + Usage string `json:"usage"` + UsageInDrive string `json:"usageInDrive"` + UsageInDriveTrash string `json:"usageInDriveTrash"` + } +} diff --git a/drivers/google_drive/util.go b/drivers/google_drive/util.go index ff4bb7b9..372b6254 100644 --- a/drivers/google_drive/util.go +++ b/drivers/google_drive/util.go @@ -28,16 +28,16 @@ import ( // do others that not defined in Driver interface type googleDriveServiceAccount struct { - //Type string `json:"type"` - //ProjectID string `json:"project_id"` - //PrivateKeyID string `json:"private_key_id"` + // Type string `json:"type"` + // ProjectID string `json:"project_id"` + // PrivateKeyID string `json:"private_key_id"` PrivateKey string `json:"private_key"` ClientEMail string `json:"client_email"` - //ClientID string `json:"client_id"` - //AuthURI string `json:"auth_uri"` + // ClientID string `json:"client_id"` + // AuthURI string `json:"auth_uri"` TokenURI string `json:"token_uri"` - //AuthProviderX509CertURL string `json:"auth_provider_x509_cert_url"` - //ClientX509CertURL string `json:"client_x509_cert_url"` + // AuthProviderX509CertURL string `json:"auth_provider_x509_cert_url"` + // ClientX509CertURL string `json:"client_x509_cert_url"` } func (d *GoogleDrive) refreshToken() error { @@ -255,7 +255,7 @@ func (d *GoogleDrive) getFiles(id string) ([]File, error) { } func (d *GoogleDrive) chunkUpload(ctx context.Context, file model.FileStreamer, url string, up driver.UpdateProgress) error { - var defaultChunkSize = d.ChunkSize * 1024 * 1024 + defaultChunkSize := d.ChunkSize * 1024 * 1024 ss, err := stream.NewStreamSectionReader(file, int(defaultChunkSize), &up) if err != nil { return err @@ -315,3 +315,18 @@ func (d *GoogleDrive) chunkUpload(ctx context.Context, file model.FileStreamer, } return nil } + +func (d *GoogleDrive) getAbout(ctx context.Context) (*AboutResp, error) { + query := map[string]string{ + "fields": "storageQuota", + } + var resp AboutResp + _, err := d.request("https://www.googleapis.com/drive/v3/about", http.MethodGet, func(req *resty.Request) { + req.SetQueryParams(query) + req.SetContext(ctx) + }, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} diff --git a/drivers/ilanzou/driver.go b/drivers/ilanzou/driver.go index 881c59ed..1c147360 100644 --- a/drivers/ilanzou/driver.go +++ b/drivers/ilanzou/driver.go @@ -97,13 +97,13 @@ func (d *ILanZou) List(ctx context.Context, dir model.Obj, args model.ListArgs) } obj := model.Object{ ID: strconv.FormatInt(f.FileId, 10), - //Path: "", + // Path: "", Name: f.FileName, Size: f.FileSize * 1024, Modified: updTime, Ctime: updTime, IsFolder: false, - //HashInfo: utils.HashInfo{}, + // HashInfo: utils.HashInfo{}, } if f.FileType == 2 { obj.IsFolder = true @@ -185,13 +185,13 @@ func (d *ILanZou) MakeDir(ctx context.Context, parentDir model.Obj, dirName stri } return &model.Object{ ID: utils.Json.Get(res, "list", 0, "id").ToString(), - //Path: "", + // Path: "", Name: dirName, Size: 0, Modified: time.Now(), Ctime: time.Now(), IsFolder: true, - //HashInfo: utils.HashInfo{}, + // HashInfo: utils.HashInfo{}, }, nil } @@ -239,7 +239,7 @@ func (d *ILanZou) Rename(ctx context.Context, srcObj model.Obj, newName string) } return &model.Object{ ID: srcObj.GetID(), - //Path: "", + // Path: "", Name: newName, Size: srcObj.GetSize(), Modified: time.Now(), @@ -392,7 +392,7 @@ func (d *ILanZou) Put(ctx context.Context, dstDir model.Obj, s model.FileStreame } return &model.Object{ ID: strconv.FormatInt(file.FileId, 10), - //Path: , + // Path: , Name: file.FileName, Size: s.GetSize(), Modified: s.ModTime(), @@ -402,6 +402,23 @@ func (d *ILanZou) Put(ctx context.Context, dstDir model.Obj, s model.FileStreame }, nil } +func (d *ILanZou) GetDetails(ctx context.Context) (*model.StorageDetails, error) { + res, err := d.proved("/user/account/map", http.MethodGet, func(req *resty.Request) { + req.SetContext(ctx) + }) + if err != nil { + return nil, err + } + total := utils.Json.Get(res, "map", "totalSize").ToUint64() * 1024 + used := utils.Json.Get(res, "map", "usedSize").ToUint64() * 1024 + return &model.StorageDetails{ + DiskUsage: model.DiskUsage{ + TotalSpace: total, + FreeSpace: total - used, + }, + }, nil +} + //func (d *ILanZou) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) { // return nil, errs.NotSupport //} diff --git a/drivers/onedrive/driver.go b/drivers/onedrive/driver.go index 40adf27c..0af2f837 100644 --- a/drivers/onedrive/driver.go +++ b/drivers/onedrive/driver.go @@ -207,4 +207,20 @@ func (d *Onedrive) Put(ctx context.Context, dstDir model.Obj, stream model.FileS return err } +func (d *Onedrive) GetDetails(ctx context.Context) (*model.StorageDetails, error) { + if d.DisableDiskUsage { + return nil, errs.NotImplement + } + drive, err := d.getDrive(ctx) + if err != nil { + return nil, err + } + return &model.StorageDetails{ + DiskUsage: model.DiskUsage{ + TotalSpace: drive.Quota.Total, + FreeSpace: drive.Quota.Remaining, + }, + }, nil +} + var _ driver.Driver = (*Onedrive)(nil) diff --git a/drivers/onedrive/meta.go b/drivers/onedrive/meta.go index 91ce16b9..239911f5 100644 --- a/drivers/onedrive/meta.go +++ b/drivers/onedrive/meta.go @@ -7,17 +7,18 @@ import ( type Addition struct { driver.RootPath - Region string `json:"region" type:"select" required:"true" options:"global,cn,us,de" default:"global"` - IsSharepoint bool `json:"is_sharepoint"` - UseOnlineAPI bool `json:"use_online_api" default:"true"` - APIAddress string `json:"api_url_address" default:"https://api.oplist.org/onedrive/renewapi"` - ClientID string `json:"client_id"` - ClientSecret string `json:"client_secret"` - RedirectUri string `json:"redirect_uri" required:"true" default:"https://api.oplist.org/onedrive/callback"` - RefreshToken string `json:"refresh_token" required:"true"` - SiteId string `json:"site_id"` - ChunkSize int64 `json:"chunk_size" type:"number" default:"5"` - CustomHost string `json:"custom_host" help:"Custom host for onedrive download link"` + Region string `json:"region" type:"select" required:"true" options:"global,cn,us,de" default:"global"` + IsSharepoint bool `json:"is_sharepoint"` + UseOnlineAPI bool `json:"use_online_api" default:"true"` + APIAddress string `json:"api_url_address" default:"https://api.oplist.org/onedrive/renewapi"` + ClientID string `json:"client_id"` + ClientSecret string `json:"client_secret"` + RedirectUri string `json:"redirect_uri" required:"true" default:"https://api.oplist.org/onedrive/callback"` + RefreshToken string `json:"refresh_token" required:"true"` + SiteId string `json:"site_id"` + ChunkSize int64 `json:"chunk_size" type:"number" default:"5"` + CustomHost string `json:"custom_host" help:"Custom host for onedrive download link"` + DisableDiskUsage bool `json:"disable_disk_usage" default:"false"` } var config = driver.Config{ diff --git a/drivers/onedrive/types.go b/drivers/onedrive/types.go index 2a328ec4..a00241eb 100644 --- a/drivers/onedrive/types.go +++ b/drivers/onedrive/types.go @@ -89,3 +89,15 @@ type FileSystemInfoFacet struct { CreatedDateTime time.Time `json:"createdDateTime,omitempty"` // The UTC date and time the file was created on a client. LastModifiedDateTime time.Time `json:"lastModifiedDateTime,omitempty"` // The UTC date and time the file was last modified on a client. } + +type DriveResp struct { + ID string `json:"id"` + DriveType string `json:"driveType"` + Quota struct { + Deleted uint64 `json:"deleted"` + Remaining uint64 `json:"remaining"` + State string `json:"state"` + Total uint64 `json:"total"` + Used uint64 `json:"used"` + } `json:"quota"` +} diff --git a/drivers/onedrive/util.go b/drivers/onedrive/util.go index 3e853cd1..49b5f074 100644 --- a/drivers/onedrive/util.go +++ b/drivers/onedrive/util.go @@ -295,3 +295,21 @@ func (d *Onedrive) upBig(ctx context.Context, dstDir model.Obj, stream model.Fil } return nil } + +func (d *Onedrive) getDrive(ctx context.Context) (*DriveResp, error) { + var api string + host, _ := onedriveHostMap[d.Region] + if d.IsSharepoint { + api = fmt.Sprintf("%s/v1.0/sites/%s/drive", host.Api, d.SiteId) + } else { + api = fmt.Sprintf("%s/v1.0/me/drive", host.Api) + } + var resp DriveResp + _, err := d.Request(api, http.MethodGet, func(req *resty.Request) { + req.SetContext(ctx) + }, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} diff --git a/drivers/onedrive_app/driver.go b/drivers/onedrive_app/driver.go index 19d0f4cd..f28adde0 100644 --- a/drivers/onedrive_app/driver.go +++ b/drivers/onedrive_app/driver.go @@ -206,4 +206,20 @@ func (d *OnedriveAPP) Put(ctx context.Context, dstDir model.Obj, stream model.Fi return err } +func (d *OnedriveAPP) GetDetails(ctx context.Context) (*model.StorageDetails, error) { + if d.DisableDiskUsage { + return nil, errs.NotImplement + } + drive, err := d.getDrive(ctx) + if err != nil { + return nil, err + } + return &model.StorageDetails{ + DiskUsage: model.DiskUsage{ + TotalSpace: drive.Quota.Total, + FreeSpace: drive.Quota.Remaining, + }, + }, nil +} + var _ driver.Driver = (*OnedriveAPP)(nil) diff --git a/drivers/onedrive_app/meta.go b/drivers/onedrive_app/meta.go index 2e47f6d9..32694e6d 100644 --- a/drivers/onedrive_app/meta.go +++ b/drivers/onedrive_app/meta.go @@ -7,13 +7,14 @@ import ( type Addition struct { driver.RootPath - Region string `json:"region" type:"select" required:"true" options:"global,cn,us,de" default:"global"` - ClientID string `json:"client_id" required:"true"` - ClientSecret string `json:"client_secret" required:"true"` - TenantID string `json:"tenant_id"` - Email string `json:"email"` - ChunkSize int64 `json:"chunk_size" type:"number" default:"5"` - CustomHost string `json:"custom_host" help:"Custom host for onedrive download link"` + Region string `json:"region" type:"select" required:"true" options:"global,cn,us,de" default:"global"` + ClientID string `json:"client_id" required:"true"` + ClientSecret string `json:"client_secret" required:"true"` + TenantID string `json:"tenant_id"` + Email string `json:"email"` + ChunkSize int64 `json:"chunk_size" type:"number" default:"5"` + CustomHost string `json:"custom_host" help:"Custom host for onedrive download link"` + DisableDiskUsage bool `json:"disable_disk_usage" default:"false"` } var config = driver.Config{ diff --git a/drivers/onedrive_app/types.go b/drivers/onedrive_app/types.go index 38e07f32..438eddfb 100644 --- a/drivers/onedrive_app/types.go +++ b/drivers/onedrive_app/types.go @@ -72,3 +72,15 @@ type Files struct { Value []File `json:"value"` NextLink string `json:"@odata.nextLink"` } + +type DriveResp struct { + ID string `json:"id"` + DriveType string `json:"driveType"` + Quota struct { + Deleted uint64 `json:"deleted"` + Remaining uint64 `json:"remaining"` + State string `json:"state"` + Total uint64 `json:"total"` + Used uint64 `json:"used"` + } `json:"quota"` +} diff --git a/drivers/onedrive_app/util.go b/drivers/onedrive_app/util.go index 783760ff..4cc6b9c3 100644 --- a/drivers/onedrive_app/util.go +++ b/drivers/onedrive_app/util.go @@ -209,3 +209,16 @@ func (d *OnedriveAPP) upBig(ctx context.Context, dstDir model.Obj, stream model. } return nil } + +func (d *OnedriveAPP) getDrive(ctx context.Context) (*DriveResp, error) { + host, _ := onedriveHostMap[d.Region] + api := fmt.Sprintf("%s/v1.0/users/%s/drive", host.Api, d.Email) + var resp DriveResp + _, err := d.Request(api, http.MethodGet, func(req *resty.Request) { + req.SetContext(ctx) + }, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} diff --git a/drivers/quark_uc/driver.go b/drivers/quark_uc/driver.go index f5b30d8b..f7ebfc60 100644 --- a/drivers/quark_uc/driver.go +++ b/drivers/quark_uc/driver.go @@ -194,7 +194,7 @@ func (d *QuarkOrUC) Put(ctx context.Context, dstDir model.Obj, stream model.File log.Debugf("left: %d", left) reader := driver.NewLimitedUploadStream(ctx, bytes.NewReader(part)) m, err := d.upPart(ctx, pre, stream.GetMimetype(), partNumber, reader) - //m, err := driver.UpPart(pre, file.GetMIMEType(), partNumber, bytes, account, md5Str, sha1Str) + // m, err := driver.UpPart(pre, file.GetMIMEType(), partNumber, bytes, account, md5Str, sha1Str) if err != nil { return err } @@ -212,4 +212,17 @@ func (d *QuarkOrUC) Put(ctx context.Context, dstDir model.Obj, stream model.File return d.upFinish(pre) } +func (d *QuarkOrUC) GetDetails(ctx context.Context) (*model.StorageDetails, error) { + memberInfo, err := d.memberInfo(ctx) + if err != nil { + return nil, err + } + return &model.StorageDetails{ + DiskUsage: model.DiskUsage{ + TotalSpace: memberInfo.Data.TotalCapacity, + FreeSpace: memberInfo.Data.TotalCapacity - memberInfo.Data.UseCapacity, + }, + }, nil +} + var _ driver.Driver = (*QuarkOrUC)(nil) diff --git a/drivers/quark_uc/types.go b/drivers/quark_uc/types.go index c6064731..cea2b883 100644 --- a/drivers/quark_uc/types.go +++ b/drivers/quark_uc/types.go @@ -12,8 +12,8 @@ type Resp struct { Status int `json:"status"` Code int `json:"code"` Message string `json:"message"` - //ReqId string `json:"req_id"` - //Timestamp int `json:"timestamp"` + // ReqId string `json:"req_id"` + // Timestamp int `json:"timestamp"` } var _ model.Obj = (*File)(nil) @@ -21,27 +21,27 @@ var _ model.Obj = (*File)(nil) type File struct { Fid string `json:"fid"` FileName string `json:"file_name"` - //PdirFid string `json:"pdir_fid"` + // PdirFid string `json:"pdir_fid"` Category int `json:"category"` - //FileType int `json:"file_type"` + // FileType int `json:"file_type"` Size int64 `json:"size"` - //FormatType string `json:"format_type"` - //Status int `json:"status"` - //Tags string `json:"tags,omitempty"` + // FormatType string `json:"format_type"` + // Status int `json:"status"` + // Tags string `json:"tags,omitempty"` LCreatedAt int64 `json:"l_created_at"` LUpdatedAt int64 `json:"l_updated_at"` - //NameSpace int `json:"name_space"` - //IncludeItems int `json:"include_items,omitempty"` - //RiskType int `json:"risk_type"` - //BackupSign int `json:"backup_sign"` - //Duration int `json:"duration"` - //FileSource string `json:"file_source"` + // NameSpace int `json:"name_space"` + // IncludeItems int `json:"include_items,omitempty"` + // RiskType int `json:"risk_type"` + // BackupSign int `json:"backup_sign"` + // Duration int `json:"duration"` + // FileSource string `json:"file_source"` File bool `json:"file"` CreatedAt int64 `json:"created_at"` UpdatedAt int64 `json:"updated_at"` - //PrivateExtra struct {} `json:"_private_extra"` - //ObjCategory string `json:"obj_category,omitempty"` - //Thumbnail string `json:"thumbnail,omitempty"` + // PrivateExtra struct {} `json:"_private_extra"` + // ObjCategory string `json:"obj_category,omitempty"` + // Thumbnail string `json:"thumbnail,omitempty"` } func fileToObj(f File) *model.Object { @@ -104,19 +104,19 @@ type SortResp struct { type DownResp struct { Resp Data []struct { - //Fid string `json:"fid"` - //FileName string `json:"file_name"` - //PdirFid string `json:"pdir_fid"` - //Category int `json:"category"` - //FileType int `json:"file_type"` - //Size int `json:"size"` - //FormatType string `json:"format_type"` - //Status int `json:"status"` - //Tags string `json:"tags"` - //LCreatedAt int64 `json:"l_created_at"` - //LUpdatedAt int64 `json:"l_updated_at"` - //NameSpace int `json:"name_space"` - //Thumbnail string `json:"thumbnail"` + // Fid string `json:"fid"` + // FileName string `json:"file_name"` + // PdirFid string `json:"pdir_fid"` + // Category int `json:"category"` + // FileType int `json:"file_type"` + // Size int `json:"size"` + // FormatType string `json:"format_type"` + // Status int `json:"status"` + // Tags string `json:"tags"` + // LCreatedAt int64 `json:"l_created_at"` + // LUpdatedAt int64 `json:"l_updated_at"` + // NameSpace int `json:"name_space"` + // Thumbnail string `json:"thumbnail"` DownloadUrl string `json:"download_url"` //Md5 string `json:"md5"` //RiskType int `json:"risk_type"` @@ -168,14 +168,14 @@ type TranscodingResp struct { Resoultion string `json:"resoultion"` Success bool `json:"success"` } `json:"video_info,omitempty"` - //Right string `json:"right"` - //MemberRight string `json:"member_right"` - //TransStatus string `json:"trans_status"` - //Accessable bool `json:"accessable"` - //SupportsFormat string `json:"supports_format"` - //VideoFuncType string `json:"video_func_type,omitempty"` + // Right string `json:"right"` + // MemberRight string `json:"member_right"` + // TransStatus string `json:"trans_status"` + // Accessable bool `json:"accessable"` + // SupportsFormat string `json:"supports_format"` + // VideoFuncType string `json:"video_func_type,omitempty"` } `json:"video_list"` - //AudioList []interface{} `json:"audio_list"` + // AudioList []interface{} `json:"audio_list"` FileName string `json:"file_name"` NameSpace int `json:"name_space"` Size int64 `json:"size"` @@ -247,8 +247,7 @@ type HashResp struct { Thumbnail string `json:"thumbnail"` FormatType string `json:"format_type"` } `json:"data"` - Metadata struct { - } `json:"metadata"` + Metadata struct{} `json:"metadata"` } type UpAuthResp struct { @@ -258,6 +257,28 @@ type UpAuthResp struct { Speed int `json:"speed"` Headers []interface{} `json:"headers"` } `json:"data"` + Metadata struct{} `json:"metadata"` +} + +type MemberResp struct { + Resp + Data struct { + MemberType string `json:"member_type"` + CreatedAt uint64 `json:"created_at"` + SecretUseCapacity uint64 `json:"secret_use_capacity"` + UseCapacity uint64 `json:"use_capacity"` + IsNewUser bool `json:"is_new_user"` + MemberStatus struct { + Vip string `json:"VIP"` + ZVip string `json:"Z_VIP"` + MiniVip string `json:"MINI_VIP"` + SuperVip string `json:"SUPER_VIP"` + } `json:"member_status"` + SecretTotalCapacity uint64 `json:"secret_total_capacity"` + TotalCapacity uint64 `json:"total_capacity"` + } `json:"data"` Metadata struct { + RangeSize int `json:"range_size"` + ServerCurTime uint64 `json:"server_cur_time"` } `json:"metadata"` } diff --git a/drivers/quark_uc/util.go b/drivers/quark_uc/util.go index fa448588..01f63ef0 100644 --- a/drivers/quark_uc/util.go +++ b/drivers/quark_uc/util.go @@ -198,7 +198,7 @@ func (d *QuarkOrUC) upHash(md5, sha1, taskId string) (bool, error) { } func (d *QuarkOrUC) upPart(ctx context.Context, pre UpPreResp, mineType string, partNumber int, bytes io.Reader) (string, error) { - //func (driver QuarkOrUC) UpPart(pre UpPreResp, mineType string, partNumber int, bytes []byte, account *model.Account, md5Str, sha1Str string) (string, error) { + // func (driver QuarkOrUC) UpPart(pre UpPreResp, mineType string, partNumber int, bytes []byte, account *model.Account, md5Str, sha1Str string) (string, error) { timeStr := time.Now().UTC().Format(http.TimeFormat) data := base.Json{ "auth_info": pre.Data.AuthInfo, @@ -334,3 +334,20 @@ func (d *QuarkOrUC) upFinish(pre UpPreResp) error { time.Sleep(time.Second) return nil } + +func (d *QuarkOrUC) memberInfo(ctx context.Context) (*MemberResp, error) { + var resp MemberResp + query := map[string]string{ + "fetch_subscribe": "false", + "_ch": "home", + "fetch_identity": "false", + } + _, err := d.request("/member", http.MethodGet, func(req *resty.Request) { + req.SetQueryParams(query) + req.SetContext(ctx) + }, &resp) + if err != nil { + return nil, err + } + return &resp, nil +} diff --git a/drivers/smb/driver.go b/drivers/smb/driver.go index 910394cc..3aeffbeb 100644 --- a/drivers/smb/driver.go +++ b/drivers/smb/driver.go @@ -11,7 +11,7 @@ import ( "github.com/OpenListTeam/OpenList/v4/internal/stream" "github.com/OpenListTeam/OpenList/v4/pkg/utils" - "github.com/hirochachacha/go-smb2" + "github.com/cloudsoda/go-smb2" ) type SMB struct { @@ -33,7 +33,7 @@ func (d *SMB) Init(ctx context.Context) error { if !strings.Contains(d.Addition.Address, ":") { d.Addition.Address = d.Addition.Address + ":445" } - return d._initFS() + return d._initFS(ctx) } func (d *SMB) Drop(ctx context.Context) error { @@ -44,7 +44,7 @@ func (d *SMB) Drop(ctx context.Context) error { } func (d *SMB) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) { - if err := d.checkConn(); err != nil { + if err := d.checkConn(ctx); err != nil { return nil, err } fullPath := dir.GetPath() @@ -71,7 +71,7 @@ func (d *SMB) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]m } func (d *SMB) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { - if err := d.checkConn(); err != nil { + if err := d.checkConn(ctx); err != nil { return nil, err } fullPath := file.GetPath() @@ -99,7 +99,7 @@ func (d *SMB) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*m } func (d *SMB) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error { - if err := d.checkConn(); err != nil { + if err := d.checkConn(ctx); err != nil { return err } fullPath := filepath.Join(parentDir.GetPath(), dirName) @@ -113,7 +113,7 @@ func (d *SMB) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) } func (d *SMB) Move(ctx context.Context, srcObj, dstDir model.Obj) error { - if err := d.checkConn(); err != nil { + if err := d.checkConn(ctx); err != nil { return err } srcPath := srcObj.GetPath() @@ -128,7 +128,7 @@ func (d *SMB) Move(ctx context.Context, srcObj, dstDir model.Obj) error { } func (d *SMB) Rename(ctx context.Context, srcObj model.Obj, newName string) error { - if err := d.checkConn(); err != nil { + if err := d.checkConn(ctx); err != nil { return err } srcPath := srcObj.GetPath() @@ -143,7 +143,7 @@ func (d *SMB) Rename(ctx context.Context, srcObj model.Obj, newName string) erro } func (d *SMB) Copy(ctx context.Context, srcObj, dstDir model.Obj) error { - if err := d.checkConn(); err != nil { + if err := d.checkConn(ctx); err != nil { return err } srcPath := srcObj.GetPath() @@ -163,7 +163,7 @@ func (d *SMB) Copy(ctx context.Context, srcObj, dstDir model.Obj) error { } func (d *SMB) Remove(ctx context.Context, obj model.Obj) error { - if err := d.checkConn(); err != nil { + if err := d.checkConn(ctx); err != nil { return err } var err error @@ -182,7 +182,7 @@ func (d *SMB) Remove(ctx context.Context, obj model.Obj) error { } func (d *SMB) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error { - if err := d.checkConn(); err != nil { + if err := d.checkConn(ctx); err != nil { return err } fullPath := filepath.Join(dstDir.GetPath(), stream.GetName()) @@ -206,7 +206,7 @@ func (d *SMB) Put(ctx context.Context, dstDir model.Obj, stream model.FileStream } func (d *SMB) GetDetails(ctx context.Context) (*model.StorageDetails, error) { - if err := d.checkConn(); err != nil { + if err := d.checkConn(ctx); err != nil { return nil, err } stat, err := d.fs.Statfs(d.RootFolderPath) diff --git a/drivers/smb/util.go b/drivers/smb/util.go index 3e40f813..6ae365f8 100644 --- a/drivers/smb/util.go +++ b/drivers/smb/util.go @@ -1,9 +1,9 @@ package smb import ( + "context" "fmt" "io/fs" - "net" "os" "path/filepath" "sync/atomic" @@ -12,7 +12,7 @@ import ( "github.com/OpenListTeam/OpenList/v4/pkg/singleflight" "github.com/OpenListTeam/OpenList/v4/pkg/utils" - "github.com/hirochachacha/go-smb2" + "github.com/cloudsoda/go-smb2" ) func (d *SMB) updateLastConnTime() { @@ -27,24 +27,20 @@ func (d *SMB) getLastConnTime() time.Time { return time.Unix(atomic.LoadInt64(&d.lastConnTime), 0) } -func (d *SMB) initFS() error { +func (d *SMB) initFS(ctx context.Context) error { _, err, _ := singleflight.AnyGroup.Do(fmt.Sprintf("SMB.initFS:%p", d), func() (any, error) { - return nil, d._initFS() + return nil, d._initFS(ctx) }) return err } -func (d *SMB) _initFS() error { - conn, err := net.Dial("tcp", d.Address) - if err != nil { - return err - } +func (d *SMB) _initFS(ctx context.Context) error { dialer := &smb2.Dialer{ Initiator: &smb2.NTLMInitiator{ User: d.Username, Password: d.Password, }, } - s, err := dialer.Dial(conn) + s, err := dialer.Dial(ctx, d.Address) if err != nil { return err } @@ -56,14 +52,14 @@ func (d *SMB) _initFS() error { return err } -func (d *SMB) checkConn() error { +func (d *SMB) checkConn(ctx context.Context) error { if time.Since(d.getLastConnTime()) < 5*time.Minute { return nil } if d.fs != nil { _ = d.fs.Umount() } - return d.initFS() + return d.initFS(ctx) } // CopyFile File copies a single file from src to dst diff --git a/go.mod b/go.mod index 7d27d07b..78416068 100644 --- a/go.mod +++ b/go.mod @@ -21,6 +21,7 @@ require ( github.com/charmbracelet/bubbletea v1.3.6 github.com/charmbracelet/lipgloss v1.1.0 github.com/city404/v6-public-rpc-proto/go v0.0.0-20240817070657-90f8e24b653e + github.com/cloudsoda/go-smb2 v0.0.0-20250228001242-d4c70e6251cc github.com/coreos/go-oidc v2.3.0+incompatible github.com/deckarep/golang-set/v2 v2.8.0 github.com/dhowden/tag v0.0.0-20240417053706-3d75831295e8 @@ -38,7 +39,6 @@ require ( github.com/google/uuid v1.6.0 github.com/gorilla/websocket v1.5.3 github.com/hekmon/transmissionrpc/v3 v3.0.0 - github.com/hirochachacha/go-smb2 v1.1.0 github.com/ipfs/go-ipfs-api v0.7.0 github.com/itsHenry35/gofakes3 v0.0.8 github.com/jlaffaye/ftp v0.2.1-0.20240918233326-1b970516f5d3 @@ -85,8 +85,17 @@ require ( github.com/RoaringBitmap/roaring/v2 v2.4.5 // indirect github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect + github.com/cloudsoda/sddl v0.0.0-20250224235906-926454e91efc // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/ebitengine/purego v0.8.4 // indirect + github.com/geoffgarside/ber v1.2.0 // indirect + github.com/hashicorp/go-uuid v1.0.3 // indirect + github.com/jcmturner/aescts/v2 v2.0.0 // indirect + github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect + github.com/jcmturner/gofork v1.7.6 // indirect + github.com/jcmturner/goidentity/v6 v6.0.1 // indirect + github.com/jcmturner/gokrb5/v8 v8.4.4 // indirect + github.com/jcmturner/rpc/v2 v2.0.3 // indirect github.com/lanrat/extsort v1.0.2 // indirect github.com/mikelolasagasti/xz v1.0.1 // indirect github.com/minio/minlz v1.0.0 // indirect @@ -169,7 +178,6 @@ require ( github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/gabriel-vasile/mimetype v1.4.9 // indirect - github.com/geoffgarside/ber v1.2.0 // indirect github.com/gin-contrib/sse v1.1.0 // indirect github.com/go-chi/chi/v5 v5.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -256,7 +264,7 @@ require ( go.etcd.io/bbolt v1.4.0 // indirect golang.org/x/arch v0.18.0 // indirect golang.org/x/sync v0.16.0 - golang.org/x/sys v0.34.0 // indirect + golang.org/x/sys v0.34.0 golang.org/x/term v0.33.0 // indirect golang.org/x/text v0.27.0 golang.org/x/tools v0.34.0 // indirect diff --git a/go.sum b/go.sum index d631b5b6..af6b0021 100644 --- a/go.sum +++ b/go.sum @@ -200,6 +200,10 @@ github.com/city404/v6-public-rpc-proto/go v0.0.0-20240817070657-90f8e24b653e/go. github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= +github.com/cloudsoda/go-smb2 v0.0.0-20250228001242-d4c70e6251cc h1:t8YjNUCt1DimB4HCIXBztwWMhgxr5yG5/YaRl9Afdfg= +github.com/cloudsoda/go-smb2 v0.0.0-20250228001242-d4c70e6251cc/go.mod h1:CgWpFCFWzzEA5hVkhAc6DZZzGd3czx+BblvOzjmg6KA= +github.com/cloudsoda/sddl v0.0.0-20250224235906-926454e91efc h1:0xCWmFKBmarCqqqLeM7jFBSw/Or81UEElFqO8MY+GDs= +github.com/cloudsoda/sddl v0.0.0-20250224235906-926454e91efc/go.mod h1:uvR42Hb/t52HQd7x5/ZLzZEK8oihrFpgnodIJ1vte2E= github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= @@ -254,7 +258,6 @@ github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sa github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY= github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok= -github.com/geoffgarside/ber v1.1.0/go.mod h1:jVPKeCbj6MvQZhwLYsGwaGI52oUorHoHKNecGT85ZCc= github.com/geoffgarside/ber v1.2.0 h1:/loowoRcs/MWLYmGX9QtIAbA+V/FrnVLsMMPhwiRm64= github.com/geoffgarside/ber v1.2.0/go.mod h1:jVPKeCbj6MvQZhwLYsGwaGI52oUorHoHKNecGT85ZCc= github.com/gin-contrib/cors v1.7.6 h1:3gQ8GMzs1Ylpf70y8bMw4fVpycXIeX1ZemuSQIsnQQY= @@ -355,6 +358,10 @@ github.com/googleapis/gax-go/v2 v2.14.2 h1:eBLnkZ9635krYIPD+ag1USrOAI0Nr0QYF3+/3 github.com/googleapis/gax-go/v2 v2.14.2/go.mod h1:ON64QhlJkhVtSqp4v1uaK92VyZ2gmvDQsweuyLV+8+w= github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= +github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= +github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -364,6 +371,9 @@ github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9n github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -375,8 +385,6 @@ github.com/hekmon/cunits/v2 v2.1.0 h1:k6wIjc4PlacNOHwKEMBgWV2/c8jyD4eRMs5mR1BBhI github.com/hekmon/cunits/v2 v2.1.0/go.mod h1:9r1TycXYXaTmEWlAIfFV8JT+Xo59U96yUJAYHxzii2M= github.com/hekmon/transmissionrpc/v3 v3.0.0 h1:0Fb11qE0IBh4V4GlOwHNYpqpjcYDp5GouolwrpmcUDQ= github.com/hekmon/transmissionrpc/v3 v3.0.0/go.mod h1:38SlNhFzinVUuY87wGj3acOmRxeYZAZfrj6Re7UgCDg= -github.com/hirochachacha/go-smb2 v1.1.0 h1:b6hs9qKIql9eVXAiN0M2wSFY5xnhbHAQoCwRKbaRTZI= -github.com/hirochachacha/go-smb2 v1.1.0/go.mod h1:8F1A4d5EZzrGu5R7PU163UcMRDJQl4FtcxjBfsY8TZE= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= @@ -396,6 +404,18 @@ github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw= github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= +github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= +github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo= +github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= +github.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg= +github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo= +github.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o= +github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= +github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh687T8= +github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs= +github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY= +github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= @@ -693,8 +713,8 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= @@ -748,6 +768,7 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= diff --git a/internal/bootstrap/data/setting.go b/internal/bootstrap/data/setting.go index f07bbaad..08024695 100644 --- a/internal/bootstrap/data/setting.go +++ b/internal/bootstrap/data/setting.go @@ -114,7 +114,9 @@ func InitialSettings() []model.SettingItem { {Key: "share_icon", Value: "🎁", Type: conf.TypeString, Group: model.STYLE}, {Key: "home_container", Value: "max_980px", Type: conf.TypeSelect, Options: "max_980px,hope_container", Group: model.STYLE}, {Key: "settings_layout", Value: "list", Type: conf.TypeSelect, Options: "list,responsive", Group: model.STYLE}, - {Key: conf.HideStorageDetails, Value: "false", Type: conf.TypeBool, Group: model.STYLE, Flag: model.PRIVATE}, + {Key: conf.HideStorageDetails, Value: "true", Type: conf.TypeBool, Group: model.STYLE, Flag: model.PRIVATE}, + {Key: conf.HideStorageDetailsInManagePage, Value: "true", Type: conf.TypeBool, Group: model.STYLE, Flag: model.PRIVATE}, + {Key: "show_disk_usage_in_plain_text", Value: "false", Type: conf.TypeBool, Group: model.STYLE, Flag: model.PUBLIC}, // preview settings {Key: conf.TextTypes, Value: "txt,htm,html,xml,java,properties,sql,js,md,json,conf,ini,vue,php,py,bat,gitignore,yml,go,sh,c,cpp,h,hpp,tsx,vtt,srt,ass,rs,lrc,strm", Type: conf.TypeText, Group: model.PREVIEW, Flag: model.PRIVATE}, {Key: conf.AudioTypes, Value: "mp3,flac,ogg,m4a,wav,opus,wma", Type: conf.TypeText, Group: model.PREVIEW, Flag: model.PRIVATE}, @@ -156,10 +158,12 @@ func InitialSettings() []model.SettingItem { {Key: conf.CustomizeBody, Type: conf.TypeText, Group: model.GLOBAL, Flag: model.PRIVATE}, {Key: conf.LinkExpiration, Value: "0", Type: conf.TypeNumber, Group: model.GLOBAL, Flag: model.PRIVATE}, {Key: conf.SignAll, Value: "true", Type: conf.TypeBool, Group: model.GLOBAL, Flag: model.PRIVATE}, - {Key: conf.PrivacyRegs, Value: `(?:(?:\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]) + { + Key: conf.PrivacyRegs, Value: `(?:(?:\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]) ([[:xdigit:]]{1,4}(?::[[:xdigit:]]{1,4}){7}|::|:(?::[[:xdigit:]]{1,4}){1,6}|[[:xdigit:]]{1,4}:(?::[[:xdigit:]]{1,4}){1,5}|(?:[[:xdigit:]]{1,4}:){2}(?::[[:xdigit:]]{1,4}){1,4}|(?:[[:xdigit:]]{1,4}:){3}(?::[[:xdigit:]]{1,4}){1,3}|(?:[[:xdigit:]]{1,4}:){4}(?::[[:xdigit:]]{1,4}){1,2}|(?:[[:xdigit:]]{1,4}:){5}:[[:xdigit:]]{1,4}|(?:[[:xdigit:]]{1,4}:){1,6}:) (?U)access_token=(.*)&`, - Type: conf.TypeText, Group: model.GLOBAL, Flag: model.PRIVATE}, + Type: conf.TypeText, Group: model.GLOBAL, Flag: model.PRIVATE, + }, {Key: conf.OcrApi, Value: "https://openlistteam-ocr-api-server.hf.space/ocr/file/json", MigrationValue: "https://api.example.com/ocr/file/json", Type: conf.TypeString, Group: model.GLOBAL}, // TODO: This can be replace by a community-hosted endpoint, see https://github.com/OpenListTeam/ocr_api_server {Key: conf.FilenameCharMapping, Value: `{"/": "|"}`, Type: conf.TypeText, Group: model.GLOBAL}, {Key: conf.ForwardDirectLinkParams, Value: "false", Type: conf.TypeBool, Group: model.GLOBAL}, diff --git a/internal/conf/const.go b/internal/conf/const.go index 94f55e63..5543f608 100644 --- a/internal/conf/const.go +++ b/internal/conf/const.go @@ -17,10 +17,11 @@ const ( AllowMounted = "allow_mounted" RobotsTxt = "robots_txt" - Logo = "logo" // multi-lines text, L1: light, EOL: dark - Favicon = "favicon" - MainColor = "main_color" - HideStorageDetails = "hide_storage_details" + Logo = "logo" // multi-lines text, L1: light, EOL: dark + Favicon = "favicon" + MainColor = "main_color" + HideStorageDetails = "hide_storage_details" + HideStorageDetailsInManagePage = "hide_storage_details_in_manage_page" // preview TextTypes = "text_types" diff --git a/internal/driver/driver.go b/internal/driver/driver.go index 01d89a6e..4a8e8825 100644 --- a/internal/driver/driver.go +++ b/internal/driver/driver.go @@ -9,8 +9,8 @@ import ( type Driver interface { Meta Reader - //Writer - //Other + // Writer + // Other } type Meta interface { diff --git a/internal/fs/list.go b/internal/fs/list.go index aa2f47f0..5245795f 100644 --- a/internal/fs/list.go +++ b/internal/fs/list.go @@ -24,8 +24,9 @@ func list(ctx context.Context, path string, args *ListArgs) ([]model.Obj, error) var _objs []model.Obj if storage != nil { _objs, err = op.List(ctx, storage, actualPath, model.ListArgs{ - ReqPath: path, - Refresh: args.Refresh, + ReqPath: path, + Refresh: args.Refresh, + WithStorageDetails: args.WithStorageDetails, }) if err != nil { if !args.NoLog { diff --git a/internal/model/args.go b/internal/model/args.go index ead19cbb..2ec95f14 100644 --- a/internal/model/args.go +++ b/internal/model/args.go @@ -11,9 +11,10 @@ import ( ) type ListArgs struct { - ReqPath string - S3ShowPlaceholder bool - Refresh bool + ReqPath string + S3ShowPlaceholder bool + Refresh bool + WithStorageDetails bool } type LinkArgs struct { diff --git a/internal/op/storage.go b/internal/op/storage.go index b4daff62..53784802 100644 --- a/internal/op/storage.go +++ b/internal/op/storage.go @@ -7,6 +7,7 @@ import ( "runtime" "sort" "strings" + "sync" "time" "github.com/OpenListTeam/OpenList/v4/internal/db" @@ -351,11 +352,9 @@ func GetStorageVirtualFilesWithDetailsByPath(ctx context.Context, prefix string, DriverName: d.Config().Name, }, } - storage, ok := d.(driver.WithDetails) - if !ok { - return ret - } - details, err := storage.GetDetails(ctx) + timeoutCtx, cancel := context.WithTimeout(ctx, time.Second) + defer cancel() + details, err := GetStorageDetails(timeoutCtx, d) if err != nil { if !errors.Is(err, errs.NotImplement) { log.Errorf("failed get %s storage details: %+v", d.GetStorage().MountPath, err) @@ -379,6 +378,7 @@ func getStorageVirtualFilesByPath(prefix string, rootCallback func(driver.Driver prefix = utils.FixAndCleanPath(prefix) set := make(map[string]int) + var wg sync.WaitGroup for _, v := range storages { mountPath := utils.GetActualMountPath(v.GetStorage().MountPath) // Exclude prefix itself and non prefix @@ -396,14 +396,25 @@ func getStorageVirtualFilesByPath(prefix string, rootCallback func(driver.Driver IsFolder: true, } if len(names) == 1 { - files = append(files, rootCallback(v, obj)) + idx = len(files) + files = append(files, obj) + wg.Add(1) + go func() { + defer wg.Done() + files[idx] = rootCallback(v, files[idx]) + }() } else { files = append(files, obj) } } else if len(names) == 1 { - files[idx] = rootCallback(v, files[idx]) + wg.Add(1) + go func() { + defer wg.Done() + files[idx] = rootCallback(v, files[idx]) + }() } } + wg.Wait() return files } @@ -427,3 +438,11 @@ func GetBalancedStorage(path string) driver.Driver { return storages[i] } } + +func GetStorageDetails(ctx context.Context, storage driver.Driver) (*model.StorageDetails, error) { + wd, ok := storage.(driver.WithDetails) + if !ok { + return nil, errs.NotImplement + } + return wd.GetDetails(ctx) +} diff --git a/server/handles/storage.go b/server/handles/storage.go index c67bbcc0..e802caad 100644 --- a/server/handles/storage.go +++ b/server/handles/storage.go @@ -2,14 +2,18 @@ package handles import ( "context" + "errors" "strconv" "sync" + "time" "github.com/OpenListTeam/OpenList/v4/internal/conf" "github.com/OpenListTeam/OpenList/v4/internal/db" "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/internal/op" + "github.com/OpenListTeam/OpenList/v4/internal/setting" "github.com/OpenListTeam/OpenList/v4/server/common" "github.com/gin-gonic/gin" log "github.com/sirupsen/logrus" @@ -28,20 +32,27 @@ func makeStorageResp(c *gin.Context, storages []model.Storage) []*StorageResp { Storage: s, MountDetails: nil, } + if setting.GetBool(conf.HideStorageDetailsInManagePage) { + continue + } d, err := op.GetStorageByMountPath(s.MountPath) if err != nil { continue } - wd, ok := d.(driver.WithDetails) + _, ok := d.(driver.WithDetails) if !ok { continue } wg.Add(1) go func() { defer wg.Done() - details, err := wd.GetDetails(c) + ctx, cancel := context.WithTimeout(c, time.Second*3) + defer cancel() + details, err := op.GetStorageDetails(ctx, d) if err != nil { - log.Errorf("failed get %s details: %+v", s.MountPath, err) + if !errors.Is(err, errs.NotImplement) { + log.Errorf("failed get %s details: %+v", s.MountPath, err) + } return } ret[i].MountDetails = details