feat(style): add driver icons and disk usage (#1274)

* feat(style): add driver icons and disk usage

* feat(driver): add disk usage for 115_open, 123_open, aliyundrive_open and baidu_netdisk

* feat(driver): add disk usage for crypt, sftp and smb

* chore: clean unused variable

* feat(driver): add disk usage for cloudreve_v4

Signed-off-by: MadDogOwner <xiaoran@xrgzs.top>

* fix(local): disk label check when getting disk usage

* feat(style): return details when accessing the manage page

---------

Signed-off-by: MadDogOwner <xiaoran@xrgzs.top>
Co-authored-by: MadDogOwner <xiaoran@xrgzs.top>
This commit is contained in:
KirCute
2025-09-19 11:59:11 +08:00
committed by GitHub
parent d3bc6321f4
commit cc16cb35bf
26 changed files with 428 additions and 81 deletions

View File

@ -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

View File

@ -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)

View File

@ -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"`

View File

@ -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)

View File

@ -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)

View File

@ -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"`
}

View File

@ -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")

View File

@ -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
//}

View File

@ -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"`
}

View File

@ -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
//}

View File

@ -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)

View File

@ -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
}

View File

@ -3,9 +3,13 @@
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 {
@ -20,3 +24,28 @@ func isHidden(f fs.FileInfo, fullPath string) bool {
}
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
}

View File

@ -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)

View File

@ -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
//}

View File

@ -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
//}

View File

@ -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},

View File

@ -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"

View File

@ -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
}

View File

@ -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)

View File

@ -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

View File

@ -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")

View File

@ -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
}

View File

@ -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

View File

@ -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),

View File

@ -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,
})
}