alist/internal/operations/account.go

208 lines
5.9 KiB
Go
Raw Normal View History

2022-06-09 09:11:46 +00:00
package operations
import (
"context"
"github.com/alist-org/alist/v3/internal/driver"
"github.com/alist-org/alist/v3/internal/model"
"github.com/alist-org/alist/v3/internal/store"
"github.com/alist-org/alist/v3/pkg/utils"
"github.com/pkg/errors"
2022-06-10 08:49:52 +00:00
log "github.com/sirupsen/logrus"
2022-06-09 15:05:27 +00:00
"sort"
"strings"
2022-06-10 08:49:52 +00:00
"sync"
2022-06-10 08:51:20 +00:00
"time"
2022-06-09 09:11:46 +00:00
)
// Although the driver type is stored,
// there is an account in each driver,
// so it should actually be an account, just wrapped by the driver
var accountsMap = map[string]driver.Driver{}
func GetAccountByVirtualPath(virtualPath string) (driver.Driver, error) {
accountDriver, ok := accountsMap[virtualPath]
if !ok {
return nil, errors.Errorf("no virtual path for an account is: %s", virtualPath)
}
return accountDriver, nil
}
// CreateAccount Save the account to database so account can get an id
// then instantiate corresponding driver and save it in memory
func CreateAccount(ctx context.Context, account model.Account) error {
2022-06-10 08:51:20 +00:00
account.Modified = time.Now()
2022-06-09 09:11:46 +00:00
err := store.CreateAccount(&account)
if err != nil {
return errors.WithMessage(err, "failed create account in database")
}
// already has an id
driverName := account.Driver
driverNew, err := GetDriverNew(driverName)
if err != nil {
return errors.WithMessage(err, "failed get driver new")
}
accountDriver := driverNew()
err = accountDriver.Init(ctx, account)
if err != nil {
return errors.WithMessage(err, "failed init account")
}
accountsMap[account.VirtualPath] = accountDriver
return nil
}
// UpdateAccount update account
// get old account first
// drop the account then reinitialize
func UpdateAccount(ctx context.Context, account model.Account) error {
oldAccount, err := store.GetAccountById(account.ID)
if err != nil {
return errors.WithMessage(err, "failed get old account")
}
2022-06-10 08:51:20 +00:00
account.Modified = time.Now()
2022-06-09 09:11:46 +00:00
err = store.UpdateAccount(&account)
if err != nil {
return errors.WithMessage(err, "failed update account in database")
}
if oldAccount.VirtualPath != account.VirtualPath {
// virtual path renamed
delete(accountsMap, oldAccount.VirtualPath)
}
accountDriver, err := GetAccountByVirtualPath(oldAccount.VirtualPath)
if err != nil {
return errors.WithMessage(err, "failed get account driver")
}
err = accountDriver.Drop(ctx)
if err != nil {
return errors.WithMessage(err, "failed drop account")
}
err = accountDriver.Init(ctx, account)
if err != nil {
return errors.WithMessage(err, "failed init account")
}
accountsMap[account.VirtualPath] = accountDriver
return nil
}
// SaveDriverAccount call from specific driver
func SaveDriverAccount(driver driver.Driver) error {
account := driver.GetAccount()
addition := driver.GetAddition()
bytes, err := utils.Json.Marshal(addition)
if err != nil {
return errors.Wrap(err, "error while marshal addition")
}
account.Addition = string(bytes)
err = store.UpdateAccount(&account)
if err != nil {
return errors.WithMessage(err, "failed update account in database")
}
return nil
}
2022-06-09 15:05:27 +00:00
// GetAccountsByPath get account by longest match path, contains balance account.
// for example, there is /a/b,/a/c,/a/d/e,/a/d/e.balance
// GetAccountsByPath(/a/d/e/f) => /a/d/e,/a/d/e.balance
func GetAccountsByPath(path string) []driver.Driver {
accounts := make([]driver.Driver, 0)
curSlashCount := 0
for _, v := range accountsMap {
2022-06-10 08:49:52 +00:00
virtualPath := utils.GetActualVirtualPath(v.GetAccount().VirtualPath)
2022-06-09 15:05:27 +00:00
if virtualPath == "/" {
virtualPath = ""
}
// not this
if path != virtualPath && !strings.HasPrefix(path, virtualPath+"/") {
continue
}
slashCount := strings.Count(virtualPath, "/")
// not the longest match
if slashCount < curSlashCount {
continue
}
if slashCount > curSlashCount {
accounts = accounts[:0]
curSlashCount = slashCount
}
accounts = append(accounts, v)
}
// make sure the order is the same for same input
sort.Slice(accounts, func(i, j int) bool {
return accounts[i].GetAccount().VirtualPath < accounts[j].GetAccount().VirtualPath
})
return accounts
}
2022-06-09 15:05:52 +00:00
// GetAccountFilesByPath Obtain the virtual file generated by the account according to the path
// for example, there are: /a/b,/a/c,/a/d/e,/a/b.balance1,/av
// GetAccountFilesByPath(/a) => b,c,d
func GetAccountFilesByPath(prefix string) []driver.FileInfo {
files := make([]driver.FileInfo, 0)
accounts := make([]driver.Driver, len(accountsMap))
i := 0
for _, v := range accountsMap {
accounts[i] = v
i += 1
}
sort.Slice(accounts, func(i, j int) bool {
if accounts[i].GetAccount().Index == accounts[j].GetAccount().Index {
return accounts[i].GetAccount().VirtualPath < accounts[j].GetAccount().VirtualPath
}
return accounts[i].GetAccount().Index < accounts[j].GetAccount().Index
})
prefix = utils.StandardizationPath(prefix)
set := make(map[string]interface{})
for _, v := range accounts {
// balance account
2022-06-10 08:49:52 +00:00
if utils.IsBalance(v.GetAccount().VirtualPath) {
2022-06-09 15:05:52 +00:00
continue
}
full := utils.StandardizationPath(v.GetAccount().VirtualPath)
if len(full) <= len(prefix) {
continue
}
// not prefixed with `prefix`
if !strings.HasPrefix(full, prefix+"/") && prefix != "/" {
continue
}
name := strings.Split(strings.TrimPrefix(strings.TrimPrefix(full, prefix), "/"), "/")[0]
if _, ok := set[name]; ok {
continue
}
files = append(files, model.File{
Name: name,
Size: 0,
Modified: v.GetAccount().Modified,
})
set[name] = nil
}
return files
}
2022-06-10 08:49:52 +00:00
var balanceMap sync.Map
// GetBalancedAccount get account by path
func GetBalancedAccount(path string) driver.Driver {
path = utils.StandardizationPath(path)
accounts := GetAccountsByPath(path)
accountNum := len(accounts)
switch accountNum {
case 0:
return nil
case 1:
return accounts[0]
default:
virtualPath := utils.GetActualVirtualPath(accounts[0].GetAccount().VirtualPath)
cur, ok := balanceMap.Load(virtualPath)
i := 0
if ok {
i = cur.(int)
i = (i + 1) % accountNum
balanceMap.Store(virtualPath, i)
} else {
balanceMap.Store(virtualPath, i)
}
log.Debugln("use: ", i)
return accounts[i]
}
}