|
package op |
|
|
|
import ( |
|
"context" |
|
"encoding/json" |
|
"sort" |
|
"strings" |
|
"time" |
|
|
|
"github.com/alist-org/alist/v3/internal/conf" |
|
"github.com/alist-org/alist/v3/internal/db" |
|
"github.com/alist-org/alist/v3/internal/driver" |
|
"github.com/alist-org/alist/v3/internal/model" |
|
"github.com/alist-org/alist/v3/pkg/generic_sync" |
|
"github.com/alist-org/alist/v3/pkg/utils" |
|
mapset "github.com/deckarep/golang-set/v2" |
|
"github.com/pkg/errors" |
|
log "github.com/sirupsen/logrus" |
|
) |
|
|
|
|
|
|
|
|
|
var storagesMap generic_sync.MapOf[string, driver.Driver] |
|
|
|
func GetAllStorages() []driver.Driver { |
|
return storagesMap.Values() |
|
} |
|
|
|
func HasStorage(mountPath string) bool { |
|
return storagesMap.Has(utils.FixAndCleanPath(mountPath)) |
|
} |
|
|
|
func GetStorageByMountPath(mountPath string) (driver.Driver, error) { |
|
mountPath = utils.FixAndCleanPath(mountPath) |
|
storageDriver, ok := storagesMap.Load(mountPath) |
|
if !ok { |
|
return nil, errors.Errorf("no mount path for an storage is: %s", mountPath) |
|
} |
|
return storageDriver, nil |
|
} |
|
|
|
|
|
|
|
func CreateStorage(ctx context.Context, storage model.Storage) (uint, error) { |
|
storage.Modified = time.Now() |
|
storage.MountPath = utils.FixAndCleanPath(storage.MountPath) |
|
var err error |
|
|
|
driverName := storage.Driver |
|
driverNew, err := GetDriver(driverName) |
|
if err != nil { |
|
return 0, errors.WithMessage(err, "failed get driver new") |
|
} |
|
storageDriver := driverNew() |
|
|
|
err = db.CreateStorage(&storage) |
|
if err != nil { |
|
return storage.ID, errors.WithMessage(err, "failed create storage in database") |
|
} |
|
|
|
err = initStorage(ctx, storage, storageDriver) |
|
go callStorageHooks("add", storageDriver) |
|
if err != nil { |
|
return storage.ID, errors.Wrap(err, "failed init storage but storage is already created") |
|
} |
|
log.Debugf("storage %+v is created", storageDriver) |
|
return storage.ID, nil |
|
} |
|
|
|
|
|
func CopyStorageById(ctx context.Context, id uint) (uint, error) { |
|
storage, err := db.GetStorageById(id) |
|
if err != nil { |
|
return 0, errors.WithMessage(err, "copied get storage") |
|
} |
|
|
|
jsonData, _ := json.Marshal(storage) |
|
storage_json := string(jsonData) |
|
var data map[string]interface{} |
|
err = json.Unmarshal([]byte(storage_json), &data) |
|
if err != nil { |
|
return 0, errors.WithMessage(err, "解析存储失败") |
|
} |
|
|
|
delete(data, "id") |
|
|
|
result, err := json.Marshal(data) |
|
if err != nil { |
|
return 0, errors.WithMessage(err, "解析存储失败") |
|
} |
|
var new_storage model.Storage |
|
err = json.Unmarshal([]byte(result), &new_storage) |
|
if err != nil { |
|
return 0, errors.WithMessage(err, "解析新存储失败") |
|
} |
|
|
|
|
|
new_storage.MountPath = storage.MountPath + "_copyed2" |
|
new_storage.Modified = time.Now() |
|
new_storage.MountPath = utils.FixAndCleanPath(new_storage.MountPath) |
|
driverName := new_storage.Driver |
|
driverNew, err := GetDriver(driverName) |
|
if err != nil { |
|
return 0, errors.WithMessage(err, "failed get driver new") |
|
} |
|
storageDriver := driverNew() |
|
|
|
err = db.CreateStorage(&new_storage) |
|
if err != nil { |
|
return new_storage.ID, errors.WithMessage(err, "failed create storage in database") |
|
} |
|
|
|
err = initStorage(ctx, new_storage, storageDriver) |
|
go callStorageHooks("add", storageDriver) |
|
if err != nil { |
|
return new_storage.ID, errors.Wrap(err, "failed init storage but storage is already created") |
|
} |
|
log.Debugf("storage %+v is created", storageDriver) |
|
return new_storage.ID, nil |
|
} |
|
|
|
|
|
func LoadStorage(ctx context.Context, storage model.Storage) error { |
|
storage.MountPath = utils.FixAndCleanPath(storage.MountPath) |
|
|
|
driverName := storage.Driver |
|
driverNew, err := GetDriver(driverName) |
|
if err != nil { |
|
return errors.WithMessage(err, "failed get driver new") |
|
} |
|
storageDriver := driverNew() |
|
|
|
err = initStorage(ctx, storage, storageDriver) |
|
go callStorageHooks("add", storageDriver) |
|
log.Debugf("storage %+v is created", storageDriver) |
|
return err |
|
} |
|
|
|
|
|
func initStorage(ctx context.Context, storage model.Storage, storageDriver driver.Driver) (err error) { |
|
storageDriver.SetStorage(storage) |
|
driverStorage := storageDriver.GetStorage() |
|
|
|
|
|
err = utils.Json.UnmarshalFromString(driverStorage.Addition, storageDriver.GetAddition()) |
|
if err == nil { |
|
err = storageDriver.Init(ctx) |
|
} |
|
storagesMap.Store(driverStorage.MountPath, storageDriver) |
|
if err != nil { |
|
driverStorage.SetStatus(err.Error()) |
|
err = errors.Wrap(err, "failed init storage") |
|
} else { |
|
driverStorage.SetStatus(WORK) |
|
} |
|
MustSaveDriverStorage(storageDriver) |
|
return err |
|
} |
|
|
|
func EnableStorage(ctx context.Context, id uint) error { |
|
storage, err := db.GetStorageById(id) |
|
if err != nil { |
|
return errors.WithMessage(err, "failed get storage") |
|
} |
|
if !storage.Disabled { |
|
return errors.Errorf("this storage have enabled") |
|
} |
|
storage.Disabled = false |
|
err = db.UpdateStorage(storage) |
|
if err != nil { |
|
return errors.WithMessage(err, "failed update storage in db") |
|
} |
|
err = LoadStorage(ctx, *storage) |
|
if err != nil { |
|
return errors.WithMessage(err, "failed load storage") |
|
} |
|
return nil |
|
} |
|
|
|
func DisableStorage(ctx context.Context, id uint) error { |
|
storage, err := db.GetStorageById(id) |
|
if err != nil { |
|
return errors.WithMessage(err, "failed get storage") |
|
} |
|
if storage.Disabled { |
|
return errors.Errorf("this storage have disabled") |
|
} |
|
storageDriver, err := GetStorageByMountPath(storage.MountPath) |
|
if err != nil { |
|
return errors.WithMessage(err, "failed get storage driver") |
|
} |
|
|
|
if err := storageDriver.Drop(ctx); err != nil { |
|
return errors.Wrap(err, "failed drop storage") |
|
} |
|
|
|
storage.Disabled = true |
|
storage.SetStatus(DISABLED) |
|
err = db.UpdateStorage(storage) |
|
if err != nil { |
|
return errors.WithMessage(err, "failed update storage in db") |
|
} |
|
storagesMap.Delete(storage.MountPath) |
|
go callStorageHooks("del", storageDriver) |
|
return nil |
|
} |
|
|
|
|
|
|
|
|
|
func UpdateStorage(ctx context.Context, storage model.Storage) error { |
|
oldStorage, err := db.GetStorageById(storage.ID) |
|
if err != nil { |
|
return errors.WithMessage(err, "failed get old storage") |
|
} |
|
if oldStorage.Driver != storage.Driver { |
|
return errors.Errorf("driver cannot be changed") |
|
} |
|
|
|
if storage.SyncGroup { |
|
storage.Modified = time.Now() |
|
storage.SyncGroup = false |
|
storage.MountPath = utils.FixAndCleanPath(storage.MountPath) |
|
|
|
|
|
|
|
var changeMap = make(map[string]interface{}) |
|
var storageMap map[string]interface{} |
|
storageAdditionStr := storage.Addition |
|
err := json.Unmarshal([]byte(storageAdditionStr), &storageMap) |
|
if err != nil { |
|
return errors.Errorf("反序列化新存储失败") |
|
} |
|
|
|
var oldStorageMap map[string]interface{} |
|
oldStorageAdditionStr := oldStorage.Addition |
|
err = json.Unmarshal([]byte(oldStorageAdditionStr), &oldStorageMap) |
|
if err != nil { |
|
return errors.Errorf("反序列化旧存储失败") |
|
} |
|
|
|
for key, value := range storageMap { |
|
oldValue := oldStorageMap[key] |
|
if oldValue != value { |
|
|
|
changeMap[key] = value |
|
} |
|
} |
|
|
|
if len(changeMap) == 0 { |
|
return errors.Errorf("Addition信息未发生变化,如需修改请关闭同步组存储选项!!!") |
|
} |
|
|
|
update_err := db.UpdateGroupStorages(storage.Group, changeMap) |
|
if update_err != nil { |
|
return errors.WithMessage(err, "更新同组存储数据失败") |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
if storage.Disabled { |
|
return nil |
|
} |
|
if oldStorage.MountPath != storage.MountPath { |
|
|
|
storagesMap.Delete(oldStorage.MountPath) |
|
} |
|
|
|
storages, err := db.GetGroupStorages(storage.Group) |
|
go func(storages []model.Storage) { |
|
for _, storage := range storages { |
|
storageDriver, err := GetStorageByMountPath(storage.MountPath) |
|
if err != nil { |
|
log.Errorf("failed get storage driver: %+v", err) |
|
continue |
|
} |
|
|
|
if err := storageDriver.Drop(context.Background()); err != nil { |
|
log.Errorf("failed drop storage: %+v", err) |
|
continue |
|
} |
|
if err := LoadStorage(context.Background(), storage); err != nil { |
|
log.Errorf("failed get enabled storages: %+v", err) |
|
continue |
|
} |
|
log.Infof("success load storage: [%s], driver: [%s]", |
|
storage.MountPath, storage.Driver) |
|
} |
|
conf.StoragesLoaded = true |
|
}(storages) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return err |
|
} else { |
|
storage.Modified = time.Now() |
|
storage.MountPath = utils.FixAndCleanPath(storage.MountPath) |
|
storage.SyncGroup = false |
|
err = db.UpdateStorage(&storage) |
|
if err != nil { |
|
return errors.WithMessage(err, "failed update storage in database") |
|
} |
|
if storage.Disabled { |
|
return nil |
|
} |
|
storageDriver, err := GetStorageByMountPath(oldStorage.MountPath) |
|
if oldStorage.MountPath != storage.MountPath { |
|
|
|
storagesMap.Delete(oldStorage.MountPath) |
|
} |
|
if err != nil { |
|
return errors.WithMessage(err, "failed get storage driver") |
|
} |
|
err = storageDriver.Drop(ctx) |
|
if err != nil { |
|
return errors.Wrapf(err, "failed drop storage") |
|
} |
|
|
|
err = initStorage(ctx, storage, storageDriver) |
|
go callStorageHooks("update", storageDriver) |
|
log.Debugf("storage %+v is update", storageDriver) |
|
return err |
|
} |
|
|
|
} |
|
|
|
func DeleteStorageById(ctx context.Context, id uint) error { |
|
storage, err := db.GetStorageById(id) |
|
if err != nil { |
|
return errors.WithMessage(err, "failed get storage") |
|
} |
|
if !storage.Disabled { |
|
storageDriver, err := GetStorageByMountPath(storage.MountPath) |
|
if err != nil { |
|
return errors.WithMessage(err, "failed get storage driver") |
|
} |
|
|
|
if err := storageDriver.Drop(ctx); err != nil { |
|
return errors.Wrapf(err, "failed drop storage") |
|
} |
|
|
|
storagesMap.Delete(storage.MountPath) |
|
go callStorageHooks("del", storageDriver) |
|
} |
|
|
|
if err := db.DeleteStorageById(id); err != nil { |
|
return errors.WithMessage(err, "failed delete storage in database") |
|
} |
|
return nil |
|
} |
|
|
|
|
|
func MustSaveDriverStorage(driver driver.Driver) { |
|
err := saveDriverStorage(driver) |
|
if err != nil { |
|
log.Errorf("failed save driver storage: %s", err) |
|
} |
|
} |
|
|
|
func saveDriverStorage(driver driver.Driver) error { |
|
storage := driver.GetStorage() |
|
addition := driver.GetAddition() |
|
str, err := utils.Json.MarshalToString(addition) |
|
if err != nil { |
|
return errors.Wrap(err, "error while marshal addition") |
|
} |
|
storage.Addition = str |
|
err = db.UpdateStorage(storage) |
|
if err != nil { |
|
return errors.WithMessage(err, "failed update storage in database") |
|
} |
|
return nil |
|
} |
|
|
|
|
|
|
|
|
|
func getStoragesByPath(path string) []driver.Driver { |
|
storages := make([]driver.Driver, 0) |
|
curSlashCount := 0 |
|
storagesMap.Range(func(mountPath string, value driver.Driver) bool { |
|
mountPath = utils.GetActualMountPath(mountPath) |
|
|
|
if utils.IsSubPath(mountPath, path) { |
|
slashCount := strings.Count(utils.PathAddSeparatorSuffix(mountPath), "/") |
|
|
|
if slashCount > curSlashCount { |
|
storages = storages[:0] |
|
curSlashCount = slashCount |
|
} |
|
if slashCount == curSlashCount { |
|
storages = append(storages, value) |
|
} |
|
} |
|
return true |
|
}) |
|
|
|
sort.Slice(storages, func(i, j int) bool { |
|
return storages[i].GetStorage().MountPath < storages[j].GetStorage().MountPath |
|
}) |
|
return storages |
|
} |
|
|
|
|
|
|
|
|
|
func GetStorageVirtualFilesByPath(prefix string) []model.Obj { |
|
files := make([]model.Obj, 0) |
|
storages := storagesMap.Values() |
|
sort.Slice(storages, func(i, j int) bool { |
|
if storages[i].GetStorage().Order == storages[j].GetStorage().Order { |
|
return storages[i].GetStorage().MountPath < storages[j].GetStorage().MountPath |
|
} |
|
return storages[i].GetStorage().Order < storages[j].GetStorage().Order |
|
}) |
|
|
|
prefix = utils.FixAndCleanPath(prefix) |
|
set := mapset.NewSet[string]() |
|
for _, v := range storages { |
|
mountPath := utils.GetActualMountPath(v.GetStorage().MountPath) |
|
|
|
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, |
|
Size: 0, |
|
Modified: v.GetStorage().Modified, |
|
IsFolder: true, |
|
}) |
|
} |
|
} |
|
return files |
|
} |
|
|
|
var balanceMap generic_sync.MapOf[string, int] |
|
|
|
|
|
func GetBalancedStorage(path string) driver.Driver { |
|
path = utils.FixAndCleanPath(path) |
|
storages := getStoragesByPath(path) |
|
storageNum := len(storages) |
|
switch storageNum { |
|
case 0: |
|
return nil |
|
case 1: |
|
return storages[0] |
|
default: |
|
virtualPath := utils.GetActualMountPath(storages[0].GetStorage().MountPath) |
|
i, _ := balanceMap.LoadOrStore(virtualPath, 0) |
|
i = (i + 1) % storageNum |
|
balanceMap.Store(virtualPath, i) |
|
return storages[i] |
|
} |
|
} |
|
|