diff --git a/drivers/115_open/driver.go b/drivers/115_open/driver.go index 1ded971e..edab65ab 100644 --- a/drivers/115_open/driver.go +++ b/drivers/115_open/driver.go @@ -337,6 +337,27 @@ func (d *Open115) OfflineList(ctx context.Context) (*sdk.OfflineTaskListResp, er return resp, nil } +func (d *Open115) GetDetails(ctx context.Context) (*model.StorageDetails, error) { + userInfo, err := d.client.UserInfo(ctx) + if err != nil { + return nil, err + } + total, err := userInfo.RtSpaceInfo.AllTotal.Size.Int64() + if err != nil { + return nil, err + } + free, err := userInfo.RtSpaceInfo.AllRemain.Size.Int64() + if err != nil { + return nil, err + } + return &model.StorageDetails{ + DiskUsage: model.DiskUsage{ + TotalSpace: uint64(total), + FreeSpace: uint64(free), + }, + }, nil +} + // func (d *Open115) GetArchiveMeta(ctx context.Context, obj model.Obj, args model.ArchiveArgs) (model.ArchiveMeta, error) { // // TODO get archive file meta-info, return errs.NotImplement to use an internal archive tool, optional // return nil, errs.NotImplement diff --git a/drivers/123_open/driver.go b/drivers/123_open/driver.go index 480d0f51..ba608bc0 100644 --- a/drivers/123_open/driver.go +++ b/drivers/123_open/driver.go @@ -214,5 +214,20 @@ func (d *Open123) Put(ctx context.Context, dstDir model.Obj, file model.FileStre return nil, fmt.Errorf("upload complete timeout") } +func (d *Open123) GetDetails(ctx context.Context) (*model.StorageDetails, error) { + userInfo, err := d.getUserInfo() + if err != nil { + return nil, err + } + total := userInfo.Data.SpacePermanent + userInfo.Data.SpaceTemp + free := total - userInfo.Data.SpaceUsed + return &model.StorageDetails{ + DiskUsage: model.DiskUsage{ + TotalSpace: total, + FreeSpace: free, + }, + }, nil +} + var _ driver.Driver = (*Open123)(nil) var _ driver.PutResult = (*Open123)(nil) diff --git a/drivers/123_open/types.go b/drivers/123_open/types.go index c1d64d2f..70257d84 100644 --- a/drivers/123_open/types.go +++ b/drivers/123_open/types.go @@ -133,9 +133,9 @@ type UserInfoResp struct { // HeadImage string `json:"headImage"` // Passport string `json:"passport"` // Mail string `json:"mail"` - // SpaceUsed int64 `json:"spaceUsed"` - // SpacePermanent int64 `json:"spacePermanent"` - // SpaceTemp int64 `json:"spaceTemp"` + SpaceUsed uint64 `json:"spaceUsed"` + SpacePermanent uint64 `json:"spacePermanent"` + SpaceTemp uint64 `json:"spaceTemp"` // SpaceTempExpr int64 `json:"spaceTempExpr"` // Vip bool `json:"vip"` // DirectTraffic int64 `json:"directTraffic"` diff --git a/drivers/aliyundrive_open/driver.go b/drivers/aliyundrive_open/driver.go index 4695f411..20dd92dc 100644 --- a/drivers/aliyundrive_open/driver.go +++ b/drivers/aliyundrive_open/driver.go @@ -291,6 +291,21 @@ func (d *AliyundriveOpen) Other(ctx context.Context, args model.OtherArgs) (inte return resp, nil } +func (d *AliyundriveOpen) GetDetails(ctx context.Context) (*model.StorageDetails, error) { + res, err := d.request(ctx, limiterOther, "/adrive/v1.0/user/getSpaceInfo", http.MethodPost, nil) + if err != nil { + return nil, err + } + total := utils.Json.Get(res, "personal_space_info", "total_size").ToUint64() + used := utils.Json.Get(res, "personal_space_info", "used_size").ToUint64() + return &model.StorageDetails{ + DiskUsage: model.DiskUsage{ + TotalSpace: total, + FreeSpace: total - used, + }, + }, nil +} + var _ driver.Driver = (*AliyundriveOpen)(nil) var _ driver.MkdirResult = (*AliyundriveOpen)(nil) var _ driver.MoveResult = (*AliyundriveOpen)(nil) diff --git a/drivers/baidu_netdisk/driver.go b/drivers/baidu_netdisk/driver.go index 0fa94e88..5a692378 100644 --- a/drivers/baidu_netdisk/driver.go +++ b/drivers/baidu_netdisk/driver.go @@ -364,4 +364,12 @@ func (d *BaiduNetdisk) uploadSlice(ctx context.Context, params map[string]string return nil } +func (d *BaiduNetdisk) GetDetails(ctx context.Context) (*model.StorageDetails, error) { + du, err := d.quota() + if err != nil { + return nil, err + } + return &model.StorageDetails{DiskUsage: *du}, nil +} + var _ driver.Driver = (*BaiduNetdisk)(nil) diff --git a/drivers/baidu_netdisk/types.go b/drivers/baidu_netdisk/types.go index ec8ceabc..0e9ee443 100644 --- a/drivers/baidu_netdisk/types.go +++ b/drivers/baidu_netdisk/types.go @@ -189,3 +189,12 @@ type PrecreateResp struct { // return_type=2 File File `json:"info"` } + +type QuotaResp struct { + Errno int `json:"errno"` + RequestId int64 `json:"request_id"` + Total uint64 `json:"total"` + Used uint64 `json:"used"` + //Free uint64 `json:"free"` + //Expire bool `json:"expire"` +} diff --git a/drivers/baidu_netdisk/util.go b/drivers/baidu_netdisk/util.go index 6a51d9b9..3f031cc4 100644 --- a/drivers/baidu_netdisk/util.go +++ b/drivers/baidu_netdisk/util.go @@ -381,6 +381,18 @@ func (d *BaiduNetdisk) getSliceSize(filesize int64) int64 { return maxSliceSize } +func (d *BaiduNetdisk) quota() (*model.DiskUsage, error) { + var resp QuotaResp + _, err := d.request("https://pan.baidu.com/api/quota", http.MethodGet, nil, &resp) + if err != nil { + return nil, err + } + return &model.DiskUsage{ + TotalSpace: resp.Total, + FreeSpace: resp.Total - resp.Used, + }, nil +} + // func encodeURIComponent(str string) string { // r := url.QueryEscape(str) // r = strings.ReplaceAll(r, "+", "%20") diff --git a/drivers/cloudreve_v4/driver.go b/drivers/cloudreve_v4/driver.go index 45549cbc..e9ce639e 100644 --- a/drivers/cloudreve_v4/driver.go +++ b/drivers/cloudreve_v4/driver.go @@ -339,6 +339,21 @@ func (d *CloudreveV4) ArchiveDecompress(ctx context.Context, srcObj, dstDir mode return nil, errs.NotImplement } +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) + if err != nil { + return nil, err + } + return &model.StorageDetails{ + DiskUsage: model.DiskUsage{ + TotalSpace: r.Total, + FreeSpace: r.Total - r.Used, + }, + }, nil +} + //func (d *CloudreveV4) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) { // return nil, errs.NotSupport //} diff --git a/drivers/cloudreve_v4/types.go b/drivers/cloudreve_v4/types.go index 7c178211..a10f9fe1 100644 --- a/drivers/cloudreve_v4/types.go +++ b/drivers/cloudreve_v4/types.go @@ -204,3 +204,9 @@ type FolderSummaryResp struct { CalculatedAt time.Time `json:"calculated_at"` } `json:"folder_summary"` } + +type CapacityResp struct { + Total uint64 `json:"total"` + Used uint64 `json:"used"` + // StoragePackTotal uint64 `json:"storage_pack_total"` +} diff --git a/drivers/crypt/driver.go b/drivers/crypt/driver.go index 704c70cb..ac5a7797 100644 --- a/drivers/crypt/driver.go +++ b/drivers/crypt/driver.go @@ -411,6 +411,20 @@ func (d *Crypt) Put(ctx context.Context, dstDir model.Obj, streamer model.FileSt return nil } +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) + if err != nil { + return nil, err + } + return &model.StorageDetails{ + DiskUsage: remoteDetails.DiskUsage, + }, nil +} + //func (d *Safe) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) { // return nil, errs.NotSupport //} diff --git a/drivers/local/driver.go b/drivers/local/driver.go index 0a78e12d..1a55ae01 100644 --- a/drivers/local/driver.go +++ b/drivers/local/driver.go @@ -434,4 +434,14 @@ func (d *Local) Put(ctx context.Context, dstDir model.Obj, stream model.FileStre return nil } +func (d *Local) GetDetails(ctx context.Context) (*model.StorageDetails, error) { + du, err := getDiskUsage(d.RootFolderPath) + if err != nil { + return nil, err + } + return &model.StorageDetails{ + DiskUsage: du, + }, nil +} + var _ driver.Driver = (*Local)(nil) diff --git a/drivers/local/util_unix.go b/drivers/local/util_unix.go index ddb4879f..3362df34 100644 --- a/drivers/local/util_unix.go +++ b/drivers/local/util_unix.go @@ -5,8 +5,25 @@ package local import ( "io/fs" "strings" + "syscall" + + "github.com/OpenListTeam/OpenList/v4/internal/model" ) func isHidden(f fs.FileInfo, _ string) bool { return strings.HasPrefix(f.Name(), ".") } + +func getDiskUsage(path string) (model.DiskUsage, error) { + var stat syscall.Statfs_t + err := syscall.Statfs(path, &stat) + if err != nil { + return model.DiskUsage{}, err + } + total := stat.Blocks * uint64(stat.Bsize) + free := stat.Bfree * uint64(stat.Bsize) + return model.DiskUsage{ + TotalSpace: total, + FreeSpace: free, + }, nil +} diff --git a/drivers/local/util_windows.go b/drivers/local/util_windows.go index 8df191cb..37064009 100644 --- a/drivers/local/util_windows.go +++ b/drivers/local/util_windows.go @@ -1,22 +1,51 @@ -//go:build windows - -package local - -import ( - "io/fs" - "path/filepath" - "syscall" -) - -func isHidden(f fs.FileInfo, fullPath string) bool { - filePath := filepath.Join(fullPath, f.Name()) - namePtr, err := syscall.UTF16PtrFromString(filePath) - if err != nil { - return false - } - attrs, err := syscall.GetFileAttributes(namePtr) - if err != nil { - return false - } - return attrs&syscall.FILE_ATTRIBUTE_HIDDEN != 0 -} +//go:build windows + +package local + +import ( + "errors" + "io/fs" + "path/filepath" + "syscall" + + "github.com/OpenListTeam/OpenList/v4/internal/model" + "golang.org/x/sys/windows" +) + +func isHidden(f fs.FileInfo, fullPath string) bool { + filePath := filepath.Join(fullPath, f.Name()) + namePtr, err := syscall.UTF16PtrFromString(filePath) + if err != nil { + return false + } + attrs, err := syscall.GetFileAttributes(namePtr) + if err != nil { + return false + } + return attrs&syscall.FILE_ATTRIBUTE_HIDDEN != 0 +} + +func getDiskUsage(path string) (model.DiskUsage, error) { + abs, err := filepath.Abs(path) + if err != nil { + return model.DiskUsage{}, err + } + root := filepath.VolumeName(abs) + if len(root) != 2 || root[1] != ':' { + return model.DiskUsage{}, errors.New("cannot get disk label") + } + var freeBytes, totalBytes, totalFreeBytes uint64 + err = windows.GetDiskFreeSpaceEx( + windows.StringToUTF16Ptr(root), + &freeBytes, + &totalBytes, + &totalFreeBytes, + ) + if err != nil { + return model.DiskUsage{}, err + } + return model.DiskUsage{ + TotalSpace: totalBytes, + FreeSpace: freeBytes, + }, nil +} diff --git a/drivers/sftp/driver.go b/drivers/sftp/driver.go index 7de24248..17db4038 100644 --- a/drivers/sftp/driver.go +++ b/drivers/sftp/driver.go @@ -4,6 +4,7 @@ import ( "context" "os" "path" + "strings" "github.com/OpenListTeam/OpenList/v4/internal/driver" "github.com/OpenListTeam/OpenList/v4/internal/errs" @@ -127,4 +128,22 @@ func (d *SFTP) Put(ctx context.Context, dstDir model.Obj, stream model.FileStrea return err } +func (d *SFTP) GetDetails(ctx context.Context) (*model.StorageDetails, error) { + stat, err := d.client.StatVFS(d.RootFolderPath) + if err != nil { + if strings.Contains(err.Error(), "unimplemented") { + return nil, errs.NotImplement + } + return nil, err + } + total := stat.Blocks * stat.Bsize + free := stat.Bfree * stat.Bsize + return &model.StorageDetails{ + DiskUsage: model.DiskUsage{ + TotalSpace: total, + FreeSpace: free, + }, + }, nil +} + var _ driver.Driver = (*SFTP)(nil) diff --git a/drivers/smb/driver.go b/drivers/smb/driver.go index 3e12f122..910394cc 100644 --- a/drivers/smb/driver.go +++ b/drivers/smb/driver.go @@ -205,6 +205,22 @@ func (d *SMB) Put(ctx context.Context, dstDir model.Obj, stream model.FileStream return nil } +func (d *SMB) GetDetails(ctx context.Context) (*model.StorageDetails, error) { + if err := d.checkConn(); err != nil { + return nil, err + } + stat, err := d.fs.Statfs(d.RootFolderPath) + if err != nil { + return nil, err + } + return &model.StorageDetails{ + DiskUsage: model.DiskUsage{ + TotalSpace: stat.BlockSize() * stat.TotalBlockCount(), + FreeSpace: stat.BlockSize() * stat.AvailableBlockCount(), + }, + }, nil +} + //func (d *SMB) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) { // return nil, errs.NotSupport //} diff --git a/drivers/template/driver.go b/drivers/template/driver.go index 5587dfea..477ca7f7 100644 --- a/drivers/template/driver.go +++ b/drivers/template/driver.go @@ -93,6 +93,11 @@ func (d *Template) ArchiveDecompress(ctx context.Context, srcObj, dstDir model.O return nil, errs.NotImplement } +func (d *Template) GetDetails(ctx context.Context) (*model.StorageDetails, error) { + // TODO return storage details (total space, free space, etc.) + return nil, errs.NotImplement +} + //func (d *Template) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) { // return nil, errs.NotSupport //} diff --git a/internal/bootstrap/data/setting.go b/internal/bootstrap/data/setting.go index f0030511..e5ef0407 100644 --- a/internal/bootstrap/data/setting.go +++ b/internal/bootstrap/data/setting.go @@ -114,6 +114,7 @@ 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}, // 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}, diff --git a/internal/conf/const.go b/internal/conf/const.go index fd0e1610..f46b0d80 100644 --- a/internal/conf/const.go +++ b/internal/conf/const.go @@ -17,9 +17,10 @@ const ( AllowMounted = "allow_mounted" RobotsTxt = "robots_txt" - Logo = "logo" // multi-lines text, L1: light, EOL: dark - Favicon = "favicon" - MainColor = "main_color" + Logo = "logo" // multi-lines text, L1: light, EOL: dark + Favicon = "favicon" + MainColor = "main_color" + HideStorageDetails = "hide_storage_details" // preview TextTypes = "text_types" diff --git a/internal/driver/driver.go b/internal/driver/driver.go index 2884b543..01d89a6e 100644 --- a/internal/driver/driver.go +++ b/internal/driver/driver.go @@ -210,6 +210,11 @@ type ArchiveDecompressResult interface { ArchiveDecompress(ctx context.Context, srcObj, dstDir model.Obj, args model.ArchiveDecompressArgs) ([]model.Obj, error) } +type WithDetails interface { + // GetDetails get storage details (total space, free space, etc.) + GetDetails(ctx context.Context) (*model.StorageDetails, error) +} + type Reference interface { InitReference(storage Driver) error } diff --git a/internal/fs/fs.go b/internal/fs/fs.go index 8c1f646b..ca199ed4 100644 --- a/internal/fs/fs.go +++ b/internal/fs/fs.go @@ -19,8 +19,9 @@ import ( // then pass the actual path to the op package type ListArgs struct { - Refresh bool - NoLog bool + Refresh bool + NoLog bool + WithStorageDetails bool } func List(ctx context.Context, path string, args *ListArgs) ([]model.Obj, error) { @@ -35,11 +36,12 @@ func List(ctx context.Context, path string, args *ListArgs) ([]model.Obj, error) } type GetArgs struct { - NoLog bool + NoLog bool + WithStorageDetails bool } func Get(ctx context.Context, path string, args *GetArgs) (model.Obj, error) { - res, err := get(ctx, path) + res, err := get(ctx, path, args) if err != nil { if !args.NoLog { log.Warnf("failed get %s: %s", path, err) diff --git a/internal/fs/get.go b/internal/fs/get.go index 2761322d..4e91c5bd 100644 --- a/internal/fs/get.go +++ b/internal/fs/get.go @@ -11,11 +11,11 @@ import ( "github.com/pkg/errors" ) -func get(ctx context.Context, path string) (model.Obj, error) { +func get(ctx context.Context, path string, args *GetArgs) (model.Obj, error) { path = utils.FixAndCleanPath(path) // maybe a virtual file if path != "/" { - virtualFiles := op.GetStorageVirtualFilesByPath(stdpath.Dir(path)) + virtualFiles := op.GetStorageVirtualFilesWithDetailsByPath(ctx, stdpath.Dir(path), !args.WithStorageDetails) for _, f := range virtualFiles { if f.GetName() == stdpath.Base(path) { return f, nil diff --git a/internal/fs/list.go b/internal/fs/list.go index cfc13229..aa2f47f0 100644 --- a/internal/fs/list.go +++ b/internal/fs/list.go @@ -15,7 +15,7 @@ import ( func list(ctx context.Context, path string, args *ListArgs) ([]model.Obj, error) { meta, _ := ctx.Value(conf.MetaKey).(*model.Meta) user, _ := ctx.Value(conf.UserKey).(*model.User) - virtualFiles := op.GetStorageVirtualFilesByPath(path) + virtualFiles := op.GetStorageVirtualFilesWithDetailsByPath(ctx, path, !args.WithStorageDetails) storage, actualPath, err := op.GetStorageAndActualPath(path) if err != nil && len(virtualFiles) == 0 { return nil, errors.WithMessage(err, "failed get storage") diff --git a/internal/model/storage.go b/internal/model/storage.go index 1f60667e..8c754e0f 100644 --- a/internal/model/storage.go +++ b/internal/model/storage.go @@ -55,3 +55,40 @@ func (p Proxy) Webdav302() bool { func (p Proxy) WebdavProxyURL() bool { return p.WebdavPolicy == "use_proxy_url" } + +type DiskUsage struct { + TotalSpace uint64 `json:"total_space"` + FreeSpace uint64 `json:"free_space"` +} + +type StorageDetails struct { + DiskUsage +} + +type StorageDetailsWithName struct { + *StorageDetails + DriverName string `json:"driver_name"` +} + +type ObjWithStorageDetails interface { + GetStorageDetails() *StorageDetailsWithName +} + +type ObjStorageDetails struct { + Obj + StorageDetailsWithName +} + +func (o ObjStorageDetails) GetStorageDetails() *StorageDetailsWithName { + return &o.StorageDetailsWithName +} + +func GetStorageDetails(obj Obj) (*StorageDetailsWithName, bool) { + if obj, ok := obj.(ObjWithStorageDetails); ok { + return obj.GetStorageDetails(), true + } + if unwrap, ok := obj.(ObjUnwrap); ok { + return GetStorageDetails(unwrap.Unwrap()) + } + return nil, false +} diff --git a/internal/op/storage.go b/internal/op/storage.go index f24a098d..b4daff62 100644 --- a/internal/op/storage.go +++ b/internal/op/storage.go @@ -15,7 +15,6 @@ import ( "github.com/OpenListTeam/OpenList/v4/internal/model" "github.com/OpenListTeam/OpenList/v4/pkg/generic_sync" "github.com/OpenListTeam/OpenList/v4/pkg/utils" - mapset "github.com/deckarep/golang-set/v2" "github.com/pkg/errors" log "github.com/sirupsen/logrus" ) @@ -335,6 +334,40 @@ func getStoragesByPath(path string) []driver.Driver { // for example, there are: /a/b,/a/c,/a/d/e,/a/b.balance1,/av // GetStorageVirtualFilesByPath(/a) => b,c,d func GetStorageVirtualFilesByPath(prefix string) []model.Obj { + return getStorageVirtualFilesByPath(prefix, func(_ driver.Driver, obj model.Obj) model.Obj { + return obj + }) +} + +func GetStorageVirtualFilesWithDetailsByPath(ctx context.Context, prefix string, hideDetails ...bool) []model.Obj { + if utils.IsBool(hideDetails...) { + return GetStorageVirtualFilesByPath(prefix) + } + return getStorageVirtualFilesByPath(prefix, func(d driver.Driver, obj model.Obj) model.Obj { + ret := &model.ObjStorageDetails{ + Obj: obj, + StorageDetailsWithName: model.StorageDetailsWithName{ + StorageDetails: nil, + DriverName: d.Config().Name, + }, + } + storage, ok := d.(driver.WithDetails) + if !ok { + return ret + } + details, err := storage.GetDetails(ctx) + if err != nil { + if !errors.Is(err, errs.NotImplement) { + log.Errorf("failed get %s storage details: %+v", d.GetStorage().MountPath, err) + } + return ret + } + ret.StorageDetails = details + return ret + }) +} + +func getStorageVirtualFilesByPath(prefix string, rootCallback func(driver.Driver, model.Obj) model.Obj) []model.Obj { files := make([]model.Obj, 0) storages := storagesMap.Values() sort.Slice(storages, func(i, j int) bool { @@ -345,21 +378,30 @@ func GetStorageVirtualFilesByPath(prefix string) []model.Obj { }) prefix = utils.FixAndCleanPath(prefix) - set := mapset.NewSet[string]() + set := make(map[string]int) for _, v := range storages { mountPath := utils.GetActualMountPath(v.GetStorage().MountPath) // Exclude prefix itself and non prefix if len(prefix) >= len(mountPath) || !utils.IsSubPath(prefix, mountPath) { continue } - name := strings.SplitN(strings.TrimPrefix(mountPath[len(prefix):], "/"), "/", 2)[0] - if set.Add(name) { - files = append(files, &model.Object{ - Name: name, + names := strings.SplitN(strings.TrimPrefix(mountPath[len(prefix):], "/"), "/", 2) + idx, ok := set[names[0]] + if !ok { + set[names[0]] = len(files) + obj := &model.Object{ + Name: names[0], Size: 0, Modified: v.GetStorage().Modified, IsFolder: true, - }) + } + if len(names) == 1 { + files = append(files, rootCallback(v, obj)) + } else { + files = append(files, obj) + } + } else if len(names) == 1 { + files[idx] = rootCallback(v, files[idx]) } } return files diff --git a/server/handles/fsread.go b/server/handles/fsread.go index 88a9b62b..6665094c 100644 --- a/server/handles/fsread.go +++ b/server/handles/fsread.go @@ -33,18 +33,19 @@ type DirReq struct { } type ObjResp struct { - Id string `json:"id"` - Path string `json:"path"` - Name string `json:"name"` - Size int64 `json:"size"` - IsDir bool `json:"is_dir"` - Modified time.Time `json:"modified"` - Created time.Time `json:"created"` - Sign string `json:"sign"` - Thumb string `json:"thumb"` - Type int `json:"type"` - HashInfoStr string `json:"hashinfo"` - HashInfo map[*utils.HashType]string `json:"hash_info"` + Id string `json:"id"` + Path string `json:"path"` + Name string `json:"name"` + Size int64 `json:"size"` + IsDir bool `json:"is_dir"` + Modified time.Time `json:"modified"` + Created time.Time `json:"created"` + Sign string `json:"sign"` + Thumb string `json:"thumb"` + Type int `json:"type"` + HashInfoStr string `json:"hashinfo"` + HashInfo map[*utils.HashType]string `json:"hash_info"` + MountDetails *model.StorageDetailsWithName `json:"mount_details,omitempty"` } type FsListResp struct { @@ -98,7 +99,10 @@ func FsList(c *gin.Context, req *ListReq, user *model.User) { common.ErrorStrResp(c, "Refresh without permission", 403) return } - objs, err := fs.List(c.Request.Context(), reqPath, &fs.ListArgs{Refresh: req.Refresh}) + objs, err := fs.List(c.Request.Context(), reqPath, &fs.ListArgs{ + Refresh: req.Refresh, + WithStorageDetails: !user.IsGuest() && !setting.GetBool(conf.HideStorageDetails), + }) if err != nil { common.ErrorResp(c, err, 500) return @@ -224,19 +228,21 @@ func toObjsResp(objs []model.Obj, parent string, encrypt bool) []ObjResp { var resp []ObjResp for _, obj := range objs { thumb, _ := model.GetThumb(obj) + mountDetails, _ := model.GetStorageDetails(obj) resp = append(resp, ObjResp{ - Id: obj.GetID(), - Path: obj.GetPath(), - Name: obj.GetName(), - Size: obj.GetSize(), - IsDir: obj.IsDir(), - Modified: obj.ModTime(), - Created: obj.CreateTime(), - HashInfoStr: obj.GetHash().String(), - HashInfo: obj.GetHash().Export(), - Sign: common.Sign(obj, parent, encrypt), - Thumb: thumb, - Type: utils.GetObjType(obj.GetName(), obj.IsDir()), + Id: obj.GetID(), + Path: obj.GetPath(), + Name: obj.GetName(), + Size: obj.GetSize(), + IsDir: obj.IsDir(), + Modified: obj.ModTime(), + Created: obj.CreateTime(), + HashInfoStr: obj.GetHash().String(), + HashInfo: obj.GetHash().Export(), + Sign: common.Sign(obj, parent, encrypt), + Thumb: thumb, + Type: utils.GetObjType(obj.GetName(), obj.IsDir()), + MountDetails: mountDetails, }) } return resp @@ -293,7 +299,9 @@ func FsGet(c *gin.Context, req *FsGetReq, user *model.User) { common.ErrorStrResp(c, "password is incorrect or you have no permission", 403) return } - obj, err := fs.Get(c.Request.Context(), reqPath, &fs.GetArgs{}) + obj, err := fs.Get(c.Request.Context(), reqPath, &fs.GetArgs{ + WithStorageDetails: !user.IsGuest() && !setting.GetBool(conf.HideStorageDetails), + }) if err != nil { common.ErrorResp(c, err, 500) return @@ -350,20 +358,22 @@ func FsGet(c *gin.Context, req *FsGetReq, user *model.User) { } parentMeta, _ := op.GetNearestMeta(parentPath) thumb, _ := model.GetThumb(obj) + mountDetails, _ := model.GetStorageDetails(obj) common.SuccessResp(c, FsGetResp{ ObjResp: ObjResp{ - Id: obj.GetID(), - Path: obj.GetPath(), - Name: obj.GetName(), - Size: obj.GetSize(), - IsDir: obj.IsDir(), - Modified: obj.ModTime(), - Created: obj.CreateTime(), - HashInfoStr: obj.GetHash().String(), - HashInfo: obj.GetHash().Export(), - Sign: common.Sign(obj, parentPath, isEncrypt(meta, reqPath)), - Type: utils.GetFileType(obj.GetName()), - Thumb: thumb, + Id: obj.GetID(), + Path: obj.GetPath(), + Name: obj.GetName(), + Size: obj.GetSize(), + IsDir: obj.IsDir(), + Modified: obj.ModTime(), + Created: obj.CreateTime(), + HashInfoStr: obj.GetHash().String(), + HashInfo: obj.GetHash().Export(), + Sign: common.Sign(obj, parentPath, isEncrypt(meta, reqPath)), + Type: utils.GetFileType(obj.GetName()), + Thumb: thumb, + MountDetails: mountDetails, }, RawURL: rawURL, Readme: getReadme(meta, reqPath), diff --git a/server/handles/storage.go b/server/handles/storage.go index 70b9e1ad..c67bbcc0 100644 --- a/server/handles/storage.go +++ b/server/handles/storage.go @@ -3,9 +3,11 @@ package handles import ( "context" "strconv" + "sync" "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/model" "github.com/OpenListTeam/OpenList/v4/internal/op" "github.com/OpenListTeam/OpenList/v4/server/common" @@ -13,6 +15,42 @@ import ( log "github.com/sirupsen/logrus" ) +type StorageResp struct { + model.Storage + MountDetails *model.StorageDetails `json:"mount_details,omitempty"` +} + +func makeStorageResp(c *gin.Context, storages []model.Storage) []*StorageResp { + ret := make([]*StorageResp, len(storages)) + var wg sync.WaitGroup + for i, s := range storages { + ret[i] = &StorageResp{ + Storage: s, + MountDetails: nil, + } + d, err := op.GetStorageByMountPath(s.MountPath) + if err != nil { + continue + } + wd, ok := d.(driver.WithDetails) + if !ok { + continue + } + wg.Add(1) + go func() { + defer wg.Done() + details, err := wd.GetDetails(c) + if err != nil { + log.Errorf("failed get %s details: %+v", s.MountPath, err) + return + } + ret[i].MountDetails = details + }() + } + wg.Wait() + return ret +} + func ListStorages(c *gin.Context) { var req model.PageReq if err := c.ShouldBind(&req); err != nil { @@ -27,7 +65,7 @@ func ListStorages(c *gin.Context) { return } common.SuccessResp(c, common.PageResp{ - Content: storages, + Content: makeStorageResp(c, storages), Total: total, }) }