From e1a2ed043648a0883a4748c7a2561bdcea48c789 Mon Sep 17 00:00:00 2001 From: Noah Hsu Date: Thu, 9 Jun 2022 17:11:46 +0800 Subject: [PATCH] feat: driver and account operate --- drivers/local/driver.go | 10 +- drivers/local/meta.go | 7 +- go.mod | 3 + go.sum | 7 ++ internal/driver/driver.go | 5 +- internal/model/account.go | 2 +- internal/operations/account.go | 93 +++++++++++++++++++ .../manage.go => operations/driver.go} | 60 +++++++----- internal/operations/error.go | 3 + internal/operations/{operate.go => fs.go} | 0 internal/store/account.go | 45 +++++++-- internal/store/store.go | 7 ++ internal/store/util.go | 13 +++ 13 files changed, 210 insertions(+), 45 deletions(-) create mode 100644 internal/operations/account.go rename internal/{driver/manage.go => operations/driver.go} (56%) create mode 100644 internal/operations/error.go rename internal/operations/{operate.go => fs.go} (100%) create mode 100644 internal/store/store.go create mode 100644 internal/store/util.go diff --git a/drivers/local/driver.go b/drivers/local/driver.go index 48f529b30f8..7beda4309ef 100644 --- a/drivers/local/driver.go +++ b/drivers/local/driver.go @@ -2,10 +2,10 @@ package local import ( "context" - "fmt" "github.com/alist-org/alist/v3/internal/driver" "github.com/alist-org/alist/v3/internal/model" "github.com/alist-org/alist/v3/pkg/utils" + "github.com/pkg/errors" "os" "path/filepath" ) @@ -24,15 +24,11 @@ func (d *Driver) Init(ctx context.Context, account model.Account) error { addition := d.Account.Addition err := utils.Json.UnmarshalFromString(addition, d.Addition) if err != nil { - return fmt.Errorf("error while unmarshal addition: %w", err) + return errors.Wrap(err, "error while unmarshal addition") } return nil } -func (d *Driver) Update(ctx context.Context, account model.Account) error { - return d.Init(ctx, account) -} - func (d *Driver) Drop(ctx context.Context) error { return nil } @@ -44,7 +40,7 @@ func (d *Driver) GetAddition() driver.Additional { func (d *Driver) File(ctx context.Context, path string) (driver.FileInfo, error) { fullPath := filepath.Join(d.RootFolder, path) if !utils.Exists(fullPath) { - return nil, driver.ErrorObjectNotFound + return nil, errors.WithStack(driver.ErrorObjectNotFound) } f, err := os.Stat(fullPath) if err != nil { diff --git a/drivers/local/meta.go b/drivers/local/meta.go index a9a67606f35..39efdf4be3a 100644 --- a/drivers/local/meta.go +++ b/drivers/local/meta.go @@ -1,6 +1,9 @@ package local -import "github.com/alist-org/alist/v3/internal/driver" +import ( + "github.com/alist-org/alist/v3/internal/driver" + "github.com/alist-org/alist/v3/internal/operations" +) type Addition struct { RootFolder string `json:"root_folder" help:"root folder path" default:"/"` @@ -17,5 +20,5 @@ func New() driver.Driver { } func init() { - driver.RegisterDriver(config, New) + operations.RegisterDriver(config, New) } diff --git a/go.mod b/go.mod index d0cf700e11a..3cd2e7168c3 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,8 @@ require ( github.com/go-playground/universal-translator v0.18.0 // indirect github.com/go-playground/validator/v10 v10.11.0 // indirect github.com/goccy/go-json v0.9.7 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/leodido/go-urn v1.2.1 // indirect github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible // indirect @@ -27,4 +29,5 @@ require ( golang.org/x/text v0.3.7 // indirect google.golang.org/protobuf v1.28.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect + gorm.io/gorm v1.23.5 // indirect ) diff --git a/go.sum b/go.sum index a05d036e3e8..eda160e9667 100644 --- a/go.sum +++ b/go.sum @@ -19,6 +19,11 @@ github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGF github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -93,3 +98,5 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/gorm v1.23.5 h1:TnlF26wScKSvknUC/Rn8t0NLLM22fypYBlvj1+aH6dM= +gorm.io/gorm v1.23.5/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= diff --git a/internal/driver/driver.go b/internal/driver/driver.go index c77bb8adc44..9eec0c87bc4 100644 --- a/internal/driver/driver.go +++ b/internal/driver/driver.go @@ -14,10 +14,11 @@ type Driver interface { type Meta interface { Config() Config + // Init If already initialized, drop first + // need to unmarshal string to addition first Init(ctx context.Context, account model.Account) error - Update(ctx context.Context, account model.Account) error Drop(ctx context.Context) error - // GetAccount transform additional field to string and assign to account's addition + // GetAccount just get raw account GetAccount() model.Account GetAddition() Additional } diff --git a/internal/model/account.go b/internal/model/account.go index c82e4f3f38e..fafdb37ebfc 100644 --- a/internal/model/account.go +++ b/internal/model/account.go @@ -2,7 +2,7 @@ package model type Account struct { ID uint `json:"id" gorm:"primaryKey"` - VirtualPath string `json:"virtual_path"` + VirtualPath string `json:"virtual_path" gorm:"unique" binding:"required"` Index int `json:"index"` Driver string `json:"driver"` Status string `json:"status"` diff --git a/internal/operations/account.go b/internal/operations/account.go new file mode 100644 index 00000000000..407521202e3 --- /dev/null +++ b/internal/operations/account.go @@ -0,0 +1,93 @@ +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" +) + +// 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 { + 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") + } + 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 +} diff --git a/internal/driver/manage.go b/internal/operations/driver.go similarity index 56% rename from internal/driver/manage.go rename to internal/operations/driver.go index 4541fa1d8c3..27da4321796 100644 --- a/internal/driver/manage.go +++ b/internal/operations/driver.go @@ -1,78 +1,88 @@ -package driver +package operations import ( + "github.com/alist-org/alist/v3/internal/driver" + "github.com/pkg/errors" log "github.com/sirupsen/logrus" "reflect" "strings" ) -type New func() Driver +type New func() driver.Driver -var driversMap = map[string]New{} -var driverItemsMap = map[string]Items{} +var driverNewMap = map[string]New{} +var driverItemsMap = map[string]driver.Items{} -func RegisterDriver(config Config, driver New) { +func RegisterDriver(config driver.Config, driver New) { log.Infof("register driver: [%s]", config.Name) registerDriverItems(config, driver().GetAddition()) - driversMap[config.Name] = driver + driverNewMap[config.Name] = driver } -func registerDriverItems(config Config, addition Additional) { +func GetDriverNew(name string) (New, error) { + n, ok := driverNewMap[name] + if !ok { + return nil, errors.Errorf("no driver named: %s", name) + } + return n, nil +} + +func registerDriverItems(config driver.Config, addition driver.Additional) { tAddition := reflect.TypeOf(addition) mainItems := getMainItems(config) additionalItems := getAdditionalItems(tAddition) - driverItemsMap[config.Name] = Items{mainItems, additionalItems} + driverItemsMap[config.Name] = driver.Items{mainItems, additionalItems} } -func getMainItems(config Config) []Item { - items := []Item{{ +func getMainItems(config driver.Config) []driver.Item { + items := []driver.Item{{ Name: "virtual_path", - Type: TypeString, + Type: driver.TypeString, Required: true, Help: "", }, { Name: "index", - Type: TypeNumber, + Type: driver.TypeNumber, Help: "use to sort", }, { Name: "down_proxy_url", - Type: TypeText, + Type: driver.TypeText, }, { Name: "webdav_direct", - Type: TypeBool, + Type: driver.TypeBool, Help: "Transfer the WebDAV of this account through the native without redirect", }} if !config.OnlyProxy && !config.OnlyLocal { - items = append(items, []Item{{ + items = append(items, []driver.Item{{ Name: "web_proxy", - Type: TypeBool, + Type: driver.TypeBool, }, { Name: "webdav_proxy", - Type: TypeBool, + Type: driver.TypeBool, }, }...) } if config.LocalSort { - items = append(items, []Item{{ + items = append(items, []driver.Item{{ Name: "order_by", - Type: TypeSelect, + Type: driver.TypeSelect, Values: "name,size,modified", }, { Name: "order_direction", - Type: TypeSelect, + Type: driver.TypeSelect, Values: "ASC,DESC", }}...) } - items = append(items, Item{ + items = append(items, driver.Item{ Name: "extract_folder", - Type: TypeSelect, + Type: driver.TypeSelect, Values: "front,back", }) return items } -func getAdditionalItems(t reflect.Type) []Item { - var items []Item +func getAdditionalItems(t reflect.Type) []driver.Item { + var items []driver.Item for i := 0; i < t.NumField(); i++ { field := t.Field(i) tag := field.Tag @@ -80,7 +90,7 @@ func getAdditionalItems(t reflect.Type) []Item { if !ok || ignore == "false" { continue } - item := Item{ + item := driver.Item{ Name: tag.Get("json"), Type: strings.ToLower(field.Type.Name()), Default: tag.Get("default"), diff --git a/internal/operations/error.go b/internal/operations/error.go new file mode 100644 index 00000000000..4c3a78ac76f --- /dev/null +++ b/internal/operations/error.go @@ -0,0 +1,3 @@ +package operations + +var () diff --git a/internal/operations/operate.go b/internal/operations/fs.go similarity index 100% rename from internal/operations/operate.go rename to internal/operations/fs.go diff --git a/internal/store/account.go b/internal/store/account.go index c21d93c3e2a..58f482a8e47 100644 --- a/internal/store/account.go +++ b/internal/store/account.go @@ -1,11 +1,40 @@ package store -import "github.com/alist-org/alist/v3/internal/model" - -type Account interface { - Create(account model.Account) error - Update(account model.Account) error - Delete(id uint) error - GetByID(id uint) (*model.Account, error) - List() ([]model.Account, error) +import ( + "github.com/alist-org/alist/v3/internal/model" + "github.com/pkg/errors" +) + +// CreateAccount just insert account to database +func CreateAccount(account *model.Account) error { + return errors.WithStack(db.Create(account).Error) +} + +// UpdateAccount just update account in database +func UpdateAccount(account *model.Account) error { + return errors.WithStack(db.Save(account).Error) +} + +// DeleteAccountById just delete account from database by id +func DeleteAccountById(id uint) error { + return errors.WithStack(db.Delete(&model.Account{}, id).Error) +} + +// GetAccounts Get all accounts from database +func GetAccounts() ([]model.Account, error) { + var accounts []model.Account + if err := db.Order(columnName("index")).Find(&accounts).Error; err != nil { + return nil, errors.WithStack(err) + } + return accounts, nil +} + +// GetAccountById Get Account by id, used to update account usually +func GetAccountById(id uint) (*model.Account, error) { + var account model.Account + account.ID = id + if err := db.First(&account).Error; err != nil { + return nil, errors.WithStack(err) + } + return &account, nil } diff --git a/internal/store/store.go b/internal/store/store.go new file mode 100644 index 00000000000..bd43c0f69c1 --- /dev/null +++ b/internal/store/store.go @@ -0,0 +1,7 @@ +package store + +import ( + "gorm.io/gorm" +) + +var db gorm.DB diff --git a/internal/store/util.go b/internal/store/util.go new file mode 100644 index 00000000000..6ee4007903d --- /dev/null +++ b/internal/store/util.go @@ -0,0 +1,13 @@ +package store + +import ( + "fmt" + "github.com/alist-org/alist/v3/conf" +) + +func columnName(name string) string { + if conf.Conf.Database.Type == "postgres" { + return fmt.Sprintf(`"%s"`, name) + } + return fmt.Sprintf("`%s`", name) +}