mirror of
https://github.com/krau/SaveAny-Bot.git
synced 2026-05-11 23:19:47 +08:00
feat!: (WIP) switched back to using config files config storages because the conversation handling is shit
This commit is contained in:
95
config/deprecated.go
Normal file
95
config/deprecated.go
Normal file
@@ -0,0 +1,95 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/krau/SaveAny-Bot/types"
|
||||
"gorm.io/datatypes"
|
||||
)
|
||||
|
||||
// for compatibility
|
||||
type deprecatedStorageConfig struct {
|
||||
Alist alistConfig `toml:"alist" mapstructure:"alist"`
|
||||
Local localConfig `toml:"local" mapstructure:"local"`
|
||||
Webdav webdavConfig `toml:"webdav" mapstructure:"webdav"`
|
||||
}
|
||||
|
||||
type alistConfig struct {
|
||||
Enable bool `toml:"enable" mapstructure:"enable" json:"enable"`
|
||||
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 *alistConfig) ToJSON() datatypes.JSON {
|
||||
tokenExp := strconv.FormatInt(a.TokenExp, 10)
|
||||
return datatypes.JSON([]byte(`{"url":"` + a.URL + `","username":"` + a.Username + `","password":"` + a.Password + `","token":"` + a.Token + `","base_path":"` + a.BasePath + `","token_exp":` + tokenExp + `}`))
|
||||
}
|
||||
|
||||
type localConfig struct {
|
||||
Enable bool `toml:"enable" mapstructure:"enable" json:"enable"`
|
||||
BasePath string `toml:"base_path" mapstructure:"base_path" json:"base_path"`
|
||||
}
|
||||
|
||||
func (l *localConfig) ToJSON() datatypes.JSON {
|
||||
return datatypes.JSON([]byte(`{"base_path":"` + l.BasePath + `"}`))
|
||||
}
|
||||
|
||||
type webdavConfig struct {
|
||||
Enable bool `toml:"enable" mapstructure:"enable" json:"enable"`
|
||||
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 *webdavConfig) ToJSON() datatypes.JSON {
|
||||
return datatypes.JSON([]byte(`{"url":"` + w.URL + `","username":"` + w.Username + `","password":"` + w.Password + `","base_path":"` + w.BasePath + `"}`))
|
||||
}
|
||||
|
||||
func transformDeprecatedStorageConfig() {
|
||||
if Cfg.DeprecatedStorage.Alist.Enable {
|
||||
alistStorage := &AlistStorageConfig{
|
||||
NewStorageConfig: NewStorageConfig{
|
||||
Name: "Alist",
|
||||
Enable: true,
|
||||
Type: string(types.StorageTypeAlist),
|
||||
},
|
||||
URL: Cfg.DeprecatedStorage.Alist.URL,
|
||||
Username: Cfg.DeprecatedStorage.Alist.Username,
|
||||
Password: Cfg.DeprecatedStorage.Alist.Password,
|
||||
Token: Cfg.DeprecatedStorage.Alist.Token,
|
||||
BasePath: Cfg.DeprecatedStorage.Alist.BasePath,
|
||||
TokenExp: Cfg.DeprecatedStorage.Alist.TokenExp,
|
||||
}
|
||||
Cfg.Storages = append(Cfg.Storages, alistStorage)
|
||||
}
|
||||
if Cfg.DeprecatedStorage.Local.Enable {
|
||||
localStorage := &LocalStorageConfig{
|
||||
NewStorageConfig: NewStorageConfig{
|
||||
Name: "Local",
|
||||
Enable: true,
|
||||
Type: string(types.StorageTypeLocal),
|
||||
},
|
||||
BasePath: Cfg.DeprecatedStorage.Local.BasePath,
|
||||
}
|
||||
Cfg.Storages = append(Cfg.Storages, localStorage)
|
||||
}
|
||||
if Cfg.DeprecatedStorage.Webdav.Enable {
|
||||
webdavStorage := &WebdavStorageConfig{
|
||||
NewStorageConfig: NewStorageConfig{
|
||||
Name: "Webdav",
|
||||
Enable: true,
|
||||
Type: string(types.StorageTypeWebdav),
|
||||
},
|
||||
URL: Cfg.DeprecatedStorage.Webdav.URL,
|
||||
Username: Cfg.DeprecatedStorage.Webdav.Username,
|
||||
Password: Cfg.DeprecatedStorage.Webdav.Password,
|
||||
BasePath: Cfg.DeprecatedStorage.Webdav.BasePath,
|
||||
}
|
||||
Cfg.Storages = append(Cfg.Storages, webdavStorage)
|
||||
}
|
||||
}
|
||||
104
config/storage_factory.go
Normal file
104
config/storage_factory.go
Normal file
@@ -0,0 +1,104 @@
|
||||
// storage_config.go
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/krau/SaveAny-Bot/types"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type StorageConfig interface {
|
||||
Validate() error
|
||||
GetType() types.StorageType
|
||||
GetName() string
|
||||
}
|
||||
|
||||
// Base storage config
|
||||
type NewStorageConfig 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]interface{} `toml:"-" mapstructure:",remain"`
|
||||
}
|
||||
|
||||
type StorageConfigFactory func(cfg *NewStorageConfig) (StorageConfig, error)
|
||||
|
||||
var storageFactories = make(map[string]StorageConfigFactory)
|
||||
|
||||
func RegisterStorageFactory(storageType string, factory StorageConfigFactory) {
|
||||
storageFactories[storageType] = factory
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterStorageFactory(string(types.StorageTypeLocal), newLocalStorageConfig)
|
||||
RegisterStorageFactory(string(types.StorageTypeAlist), newAlistStorageConfig)
|
||||
RegisterStorageFactory(string(types.StorageTypeWebdav), newWebdavStorageConfig)
|
||||
}
|
||||
|
||||
func newLocalStorageConfig(cfg *NewStorageConfig) (StorageConfig, error) {
|
||||
var localCfg LocalStorageConfig
|
||||
localCfg.NewStorageConfig = *cfg
|
||||
|
||||
if err := mapstructure.Decode(cfg.RawConfig, &localCfg); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode local storage config: %w", err)
|
||||
}
|
||||
|
||||
return &localCfg, nil
|
||||
}
|
||||
|
||||
func newAlistStorageConfig(cfg *NewStorageConfig) (StorageConfig, error) {
|
||||
var alistCfg AlistStorageConfig
|
||||
alistCfg.NewStorageConfig = *cfg
|
||||
|
||||
if err := mapstructure.Decode(cfg.RawConfig, &alistCfg); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode alist storage config: %w", err)
|
||||
}
|
||||
|
||||
return &alistCfg, nil
|
||||
}
|
||||
|
||||
func newWebdavStorageConfig(cfg *NewStorageConfig) (StorageConfig, error) {
|
||||
var webdavCfg WebdavStorageConfig
|
||||
webdavCfg.NewStorageConfig = *cfg
|
||||
|
||||
if err := mapstructure.Decode(cfg.RawConfig, &webdavCfg); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode webdav storage config: %w", err)
|
||||
}
|
||||
|
||||
return &webdavCfg, nil
|
||||
}
|
||||
|
||||
func LoadStorageConfigs(v *viper.Viper) ([]StorageConfig, error) {
|
||||
var baseConfigs []NewStorageConfig
|
||||
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[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
|
||||
}
|
||||
106
config/storages.go
Normal file
106
config/storages.go
Normal file
@@ -0,0 +1,106 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/krau/SaveAny-Bot/types"
|
||||
)
|
||||
|
||||
func (c *Config) GetStoragesByType(storageType types.StorageType) []StorageConfig {
|
||||
var storages []StorageConfig
|
||||
for _, storage := range c.Storages {
|
||||
if storage.GetType() == storageType {
|
||||
storages = append(storages, storage)
|
||||
}
|
||||
}
|
||||
return storages
|
||||
}
|
||||
|
||||
func (c *Config) GetStorageByName(name string) StorageConfig {
|
||||
for _, storage := range c.Storages {
|
||||
if storage.GetName() == name {
|
||||
return storage
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type LocalStorageConfig struct {
|
||||
NewStorageConfig
|
||||
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
|
||||
}
|
||||
|
||||
type AlistStorageConfig struct {
|
||||
NewStorageConfig
|
||||
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
|
||||
}
|
||||
|
||||
type WebdavStorageConfig struct {
|
||||
NewStorageConfig
|
||||
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
|
||||
}
|
||||
28
config/user.go
Normal file
28
config/user.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
)
|
||||
|
||||
type userConfig struct {
|
||||
ID int64 `toml:"id" mapstructure:"id" json:"id"` // telegram user id
|
||||
Storages []string `toml:"storages" mapstructure:"storages" json:"storages"` // storage names
|
||||
Blacklist bool `toml:"blacklist" mapstructure:"blacklist" json:"blacklist"` // 黑名单模式, storage names 中的存储将不会被使用, 默认为白名单模式
|
||||
}
|
||||
|
||||
func (c *Config) GetStorageNamesByUserID(userID int64) []string {
|
||||
for _, user := range c.Users {
|
||||
if user.ID == userID {
|
||||
if user.Blacklist {
|
||||
allStorages := make([]string, 0, len(c.Storages))
|
||||
for _, storage := range c.Storages {
|
||||
allStorages = append(allStorages, storage.GetName())
|
||||
}
|
||||
return slice.Compact(slice.Difference(allStorages, user.Storages))
|
||||
} else {
|
||||
return user.Storages
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
132
config/viper.go
132
config/viper.go
@@ -3,23 +3,24 @@ package config
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"gorm.io/datatypes"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Workers int `toml:"workers" mapstructure:"workers"`
|
||||
Retry int `toml:"retry" mapstructure:"retry"`
|
||||
NoCleanCache bool `toml:"no_clean_cache" mapstructure:"no_clean_cache" json:"no_clean_cache"`
|
||||
Workers int `toml:"workers" mapstructure:"workers"`
|
||||
Retry int `toml:"retry" mapstructure:"retry"`
|
||||
NoCleanCache bool `toml:"no_clean_cache" mapstructure:"no_clean_cache" json:"no_clean_cache"`
|
||||
Users []userConfig `toml:"users" mapstructure:"users" json:"users"`
|
||||
|
||||
Temp tempConfig `toml:"temp" mapstructure:"temp"`
|
||||
Log logConfig `toml:"log" mapstructure:"log"`
|
||||
DB dbConfig `toml:"db" mapstructure:"db"`
|
||||
Telegram telegramConfig `toml:"telegram" mapstructure:"telegram"`
|
||||
Storage storageConfig `toml:"storage" mapstructure:"storage"`
|
||||
Temp tempConfig `toml:"temp" mapstructure:"temp"`
|
||||
Log logConfig `toml:"log" mapstructure:"log"`
|
||||
DB dbConfig `toml:"db" mapstructure:"db"`
|
||||
Telegram telegramConfig `toml:"telegram" mapstructure:"telegram"`
|
||||
Storages []StorageConfig `toml:"-" mapstructure:"-" json:"storages"`
|
||||
// Deprecated
|
||||
DeprecatedStorage deprecatedStorageConfig `toml:"storage" mapstructure:"storage"`
|
||||
}
|
||||
|
||||
type tempConfig struct {
|
||||
@@ -38,12 +39,13 @@ type dbConfig struct {
|
||||
}
|
||||
|
||||
type telegramConfig struct {
|
||||
Token string `toml:"token" mapstructure:"token"`
|
||||
AppID int `toml:"app_id" mapstructure:"app_id" json:"app_id"`
|
||||
AppHash string `toml:"app_hash" mapstructure:"app_hash" json:"app_hash"`
|
||||
// 白名单用户
|
||||
Admins []int64 `toml:"admins" mapstructure:"admins"` // Whitelisted users
|
||||
Proxy proxyConfig `toml:"proxy" mapstructure:"proxy"`
|
||||
Token string `toml:"token" mapstructure:"token"`
|
||||
AppID int `toml:"app_id" mapstructure:"app_id" json:"app_id"`
|
||||
AppHash string `toml:"app_hash" mapstructure:"app_hash" json:"app_hash"`
|
||||
Proxy proxyConfig `toml:"proxy" mapstructure:"proxy"`
|
||||
|
||||
// Deprecated
|
||||
Admins []int64 `toml:"admins" mapstructure:"admins"`
|
||||
}
|
||||
|
||||
type proxyConfig struct {
|
||||
@@ -51,56 +53,9 @@ type proxyConfig struct {
|
||||
URL string `toml:"url" mapstructure:"url"`
|
||||
}
|
||||
|
||||
// pre-defined storages, for compatibility.
|
||||
/*
|
||||
在配置文件中定义的存储将会为telegram.admins中的每个用户创建一个存储模型
|
||||
*/
|
||||
// these config will be removed in the future.
|
||||
type storageConfig struct {
|
||||
Alist AlistConfig `toml:"alist" mapstructure:"alist"`
|
||||
Local LocalConfig `toml:"local" mapstructure:"local"`
|
||||
Webdav WebdavConfig `toml:"webdav" mapstructure:"webdav"`
|
||||
}
|
||||
|
||||
type AlistConfig struct {
|
||||
Enable bool `toml:"enable" mapstructure:"enable" json:"enable"`
|
||||
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 *AlistConfig) ToJSON() datatypes.JSON {
|
||||
tokenExp := strconv.FormatInt(a.TokenExp, 10)
|
||||
return datatypes.JSON([]byte(`{"url":"` + a.URL + `","username":"` + a.Username + `","password":"` + a.Password + `","token":"` + a.Token + `","base_path":"` + a.BasePath + `","token_exp":` + tokenExp + `}`))
|
||||
}
|
||||
|
||||
type LocalConfig struct {
|
||||
Enable bool `toml:"enable" mapstructure:"enable" json:"enable"`
|
||||
BasePath string `toml:"base_path" mapstructure:"base_path" json:"base_path"`
|
||||
}
|
||||
|
||||
func (l *LocalConfig) ToJSON() datatypes.JSON {
|
||||
return datatypes.JSON([]byte(`{"base_path":"` + l.BasePath + `"}`))
|
||||
}
|
||||
|
||||
type WebdavConfig struct {
|
||||
Enable bool `toml:"enable" mapstructure:"enable" json:"enable"`
|
||||
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 *WebdavConfig) ToJSON() datatypes.JSON {
|
||||
return datatypes.JSON([]byte(`{"url":"` + w.URL + `","username":"` + w.Username + `","password":"` + w.Password + `","base_path":"` + w.BasePath + `"}`))
|
||||
}
|
||||
|
||||
var Cfg *Config
|
||||
|
||||
func Init() {
|
||||
func Init() error {
|
||||
viper.SetConfigName("config")
|
||||
viper.AddConfigPath(".")
|
||||
viper.AddConfigPath("/etc/saveany/")
|
||||
@@ -125,7 +80,11 @@ func Init() {
|
||||
|
||||
viper.SetDefault("db.path", "data/saveany.db")
|
||||
|
||||
viper.SafeWriteConfigAs("config.toml")
|
||||
if err := viper.SafeWriteConfigAs("config.toml"); err != nil {
|
||||
if _, ok := err.(viper.ConfigFileAlreadyExistsError); !ok {
|
||||
return fmt.Errorf("error saving default config: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := viper.ReadInConfig(); err != nil {
|
||||
fmt.Println("Error reading config file, ", err)
|
||||
@@ -133,17 +92,52 @@ func Init() {
|
||||
}
|
||||
|
||||
Cfg = &Config{}
|
||||
|
||||
if err := viper.Unmarshal(Cfg); err != nil {
|
||||
fmt.Println("Error unmarshalling config file, ", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if Cfg.Storage != (storageConfig{}) {
|
||||
fmt.Println("警告: 存储配置已经废弃, 未来版本将会移除.\n请直接使用 Bot 命令添加存储.")
|
||||
|
||||
if Cfg.Telegram.Admins != nil {
|
||||
fmt.Println("警告: 你正在使用旧版 Telegram 管理员配置, 该配置下的用户将可用所有存储.\ntelegram.admins 未来版本将会被废弃, 请参考新的配置文件模板, 使用 users 配置替代.")
|
||||
for _, admin := range Cfg.Telegram.Admins {
|
||||
Cfg.Users = append(Cfg.Users, userConfig{
|
||||
ID: admin,
|
||||
Storages: []string{},
|
||||
Blacklist: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
storagesConfig, err := LoadStorageConfigs(viper.GetViper())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error loading storage configs: %w", err)
|
||||
}
|
||||
Cfg.Storages = storagesConfig
|
||||
|
||||
if Cfg.DeprecatedStorage != (deprecatedStorageConfig{}) {
|
||||
fmt.Println("\n警告: 你正在使用旧版存储配置, 未来版本将会被废弃.\n请参考新的配置文件模板.")
|
||||
transformDeprecatedStorageConfig()
|
||||
}
|
||||
|
||||
storageNames := make(map[string]struct{})
|
||||
for _, storage := range Cfg.Storages {
|
||||
if _, ok := storageNames[storage.GetName()]; ok {
|
||||
return fmt.Errorf("重复的存储名: %s", storage.GetName())
|
||||
}
|
||||
storageNames[storage.GetName()] = struct{}{}
|
||||
}
|
||||
|
||||
fmt.Printf("已加载 %d 个存储:\n", len(Cfg.Storages))
|
||||
for _, storage := range Cfg.Storages {
|
||||
fmt.Printf(" - %s (%s)\n", storage.GetName(), storage.GetType())
|
||||
}
|
||||
|
||||
if Cfg.Workers < 1 || Cfg.Retry < 1 {
|
||||
fmt.Println("Invalid workers or retry value")
|
||||
os.Exit(1)
|
||||
return fmt.Errorf("workers 和 retry 必须大于 0, 当前值: workers=%d, retry=%d", Cfg.Workers, Cfg.Retry)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Set(key string, value any) {
|
||||
|
||||
Reference in New Issue
Block a user