feat: refactor storage configuration to use dedicated storage package and add new storage types

BREAKING CHANGE: remove deprecated config
This commit is contained in:
krau
2025-03-21 20:52:41 +08:00
parent 8e180006f0
commit 65fee89e14
18 changed files with 246 additions and 417 deletions

38
config/storage/alist.go Normal file
View File

@@ -0,0 +1,38 @@
package storage
import (
"fmt"
"github.com/krau/SaveAny-Bot/types"
)
type AlistStorageConfig struct {
BaseConfig
URL string `toml:"url" mapstructure:"url" json:"url"`
Username string `toml:"username" mapstructure:"username" json:"username"`
Password string `toml:"password" mapstructure:"password" json:"password"`
Token string `toml:"token" mapstructure:"token" json:"token"`
BasePath string `toml:"base_path" mapstructure:"base_path" json:"base_path"`
TokenExp int64 `toml:"token_exp" mapstructure:"token_exp" json:"token_exp"`
}
func (a *AlistStorageConfig) Validate() error {
if a.URL == "" {
return fmt.Errorf("url is required for alist storage")
}
if a.Token == "" && (a.Username == "" || a.Password == "") {
return fmt.Errorf("username and password or token is required for alist storage")
}
if a.BasePath == "" {
return fmt.Errorf("base_path is required for alist storage")
}
return nil
}
func (a *AlistStorageConfig) GetType() types.StorageType {
return types.StorageTypeAlist
}
func (a *AlistStorageConfig) GetName() string {
return a.Name
}

63
config/storage/factory.go Normal file
View File

@@ -0,0 +1,63 @@
package storage
import (
"fmt"
"reflect"
"github.com/krau/SaveAny-Bot/types"
"github.com/mitchellh/mapstructure"
"github.com/spf13/viper"
)
var storageFactories = map[types.StorageType]func(cfg *BaseConfig) (StorageConfig, error){
types.StorageTypeLocal: createStorageConfig(&LocalStorageConfig{}),
types.StorageTypeAlist: createStorageConfig(&AlistStorageConfig{}),
types.StorageTypeWebdav: createStorageConfig(&WebdavStorageConfig{}),
types.StorageTypeMinio: createStorageConfig(&MinioStorageConfig{}),
}
func createStorageConfig(configType StorageConfig) func(cfg *BaseConfig) (StorageConfig, error) {
return func(cfg *BaseConfig) (StorageConfig, error) {
configValue := reflect.New(reflect.TypeOf(configType).Elem()).Interface().(StorageConfig)
reflect.ValueOf(configValue).Elem().FieldByName("BaseConfig").Set(reflect.ValueOf(*cfg))
if err := mapstructure.Decode(cfg.RawConfig, configValue); err != nil {
return nil, fmt.Errorf("failed to decode %s storage config: %w", cfg.Type, err)
}
return configValue, nil
}
}
func LoadStorageConfigs(v *viper.Viper) ([]StorageConfig, error) {
var baseConfigs []BaseConfig
if err := v.UnmarshalKey("storages", &baseConfigs); err != nil {
return nil, fmt.Errorf("failed to unmarshal storage configs: %w", err)
}
var configs []StorageConfig
for _, baseCfg := range baseConfigs {
if !baseCfg.Enable {
continue
}
factory, ok := storageFactories[types.StorageType(baseCfg.Type)]
if !ok {
return nil, fmt.Errorf("unsupported storage type: %s", baseCfg.Type)
}
cfg, err := factory(&baseCfg)
if err != nil {
return nil, fmt.Errorf("failed to create storage config for %s: %w", baseCfg.Name, err)
}
if err := cfg.Validate(); err != nil {
return nil, fmt.Errorf("invalid storage config for %s: %w", baseCfg.Name, err)
}
configs = append(configs, cfg)
}
return configs, nil
}

27
config/storage/local.go Normal file
View File

@@ -0,0 +1,27 @@
package storage
import (
"fmt"
"github.com/krau/SaveAny-Bot/types"
)
type LocalStorageConfig struct {
BaseConfig
BasePath string `toml:"base_path" mapstructure:"base_path" json:"base_path"`
}
func (l *LocalStorageConfig) Validate() error {
if l.BasePath == "" {
return fmt.Errorf("path is required for local storage")
}
return nil
}
func (l *LocalStorageConfig) GetType() types.StorageType {
return types.StorageTypeLocal
}
func (l *LocalStorageConfig) GetName() string {
return l.Name
}

41
config/storage/minio.go Normal file
View File

@@ -0,0 +1,41 @@
package storage
import (
"fmt"
"github.com/krau/SaveAny-Bot/types"
)
type MinioStorageConfig struct {
BaseConfig
Endpoint string `toml:"endpoint" mapstructure:"endpoint" json:"endpoint"`
AccessKeyID string `toml:"access_key_id" mapstructure:"access_key_id" json:"access_key_id"`
SecretAccessKey string `toml:"secret_access_key" mapstructure:"secret_access_key" json:"secret_access_key"`
BucketName string `toml:"bucket_name" mapstructure:"bucket_name" json:"bucket_name"`
UseSSL bool `toml:"use_ssl" mapstructure:"use_ssl" json:"use_ssl"`
BasePath string `toml:"base_path" mapstructure:"base_path" json:"base_path"`
}
func (m *MinioStorageConfig) Validate() error {
if m.Endpoint == "" {
return fmt.Errorf("endpoint is required for minio storage")
}
if m.AccessKeyID == "" || m.SecretAccessKey == "" {
return fmt.Errorf("access_key_id and secret_access_key are required for minio storage")
}
if m.BucketName == "" {
return fmt.Errorf("bucket_name is required for minio storage")
}
if m.BasePath == "" {
return fmt.Errorf("base_path is required for minio storage")
}
return nil
}
func (m *MinioStorageConfig) GetType() types.StorageType {
return types.StorageTypeMinio
}
func (m *MinioStorageConfig) GetName() string {
return m.Name
}

16
config/storage/types.go Normal file
View File

@@ -0,0 +1,16 @@
package storage
import "github.com/krau/SaveAny-Bot/types"
type StorageConfig interface {
Validate() error
GetType() types.StorageType
GetName() string
}
type BaseConfig struct {
Name string `toml:"name" mapstructure:"name" json:"name"`
Type string `toml:"type" mapstructure:"type" json:"type"`
Enable bool `toml:"enable" mapstructure:"enable" json:"enable"`
RawConfig map[string]any `toml:"-" mapstructure:",remain"`
}

36
config/storage/webdav.go Normal file
View File

@@ -0,0 +1,36 @@
package storage
import (
"fmt"
"github.com/krau/SaveAny-Bot/types"
)
type WebdavStorageConfig struct {
BaseConfig
URL string `toml:"url" mapstructure:"url" json:"url"`
Username string `toml:"username" mapstructure:"username" json:"username"`
Password string `toml:"password" mapstructure:"password" json:"password"`
BasePath string `toml:"base_path" mapstructure:"base_path" json:"base_path"`
}
func (w *WebdavStorageConfig) Validate() error {
if w.URL == "" {
return fmt.Errorf("url is required for webdav storage")
}
if w.Username == "" || w.Password == "" {
return fmt.Errorf("username and password is required for webdav storage")
}
if w.BasePath == "" {
return fmt.Errorf("base_path is required for webdav storage")
}
return nil
}
func (w *WebdavStorageConfig) GetType() types.StorageType {
return types.StorageTypeWebdav
}
func (w *WebdavStorageConfig) GetName() string {
return w.Name
}