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

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