Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions server/internal/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ func New(ctx context.Context, cfg config.Config, version string) (*Application,
storageRclone.NewFTPFactory(),
storageRclone.NewRcloneFactory(),
)
// 将全部 rclone 后端注册为独立存储类型(sftp、azureblob、dropbox 等与 s3、ftp 完全平级)
storageRclone.RegisterAllBackends(storageRegistry)
storageTargetService := service.NewStorageTargetService(storageTargetRepo, oauthSessionRepo, storageRegistry, configCipher)
storageTargetService.SetBackupTaskRepository(backupTaskRepo)
storageTargetService.SetBackupRecordRepository(backupRecordRepo)
Expand Down
2 changes: 1 addition & 1 deletion server/internal/service/storage_target_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (

type StorageTargetUpsertInput struct {
Name string `json:"name" binding:"required,min=1,max=128"`
Type string `json:"type" binding:"required,oneof=local_disk google_drive s3 webdav aliyun_oss tencent_cos qiniu_kodo ftp rclone"`
Type string `json:"type" binding:"required,min=1"`
Description string `json:"description" binding:"max=255"`
Enabled bool `json:"enabled"`
Config map[string]any `json:"config" binding:"required"`
Expand Down
72 changes: 72 additions & 0 deletions server/internal/storage/rclone/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -434,3 +434,75 @@ type BackendOption struct {
Required bool `json:"required"`
IsPassword bool `json:"isPassword"`
}

// ---------------------------------------------------------------------------
// 通用 BackendFactory — 为任意 rclone 后端自动生成独立 Factory
// ---------------------------------------------------------------------------

// GenericBackendFactory 为单个 rclone 后端创建独立的 ProviderFactory。
// 用户存储目标的 type 直接是后端名(如 "sftp"),与 "s3"、"ftp" 完全平级。
type GenericBackendFactory struct {
backendType string
sensitive []string
}

// NewBackendFactory 为指定 rclone 后端创建一个 Factory。
func NewBackendFactory(backendType string) GenericBackendFactory {
var sensitive []string
for _, ri := range fs.Registry {
if ri.Name == backendType {
for _, opt := range ri.Options {
if opt.IsPassword {
sensitive = append(sensitive, opt.Name)
}
}
break
}
}
return GenericBackendFactory{backendType: backendType, sensitive: sensitive}
}

func (f GenericBackendFactory) Type() storage.ProviderType { return storage.ProviderType(f.backendType) }
func (f GenericBackendFactory) SensitiveFields() []string { return f.sensitive }

func (f GenericBackendFactory) New(ctx context.Context, rawConfig map[string]any) (storage.StorageProvider, error) {
root, _ := rawConfig["root"].(string)
root = strings.TrimSpace(root)

var b strings.Builder
b.WriteString(":")
b.WriteString(f.backendType)
for key, val := range rawConfig {
if key == "root" {
continue
}
strVal := fmt.Sprintf("%v", val)
if strings.TrimSpace(strVal) == "" {
continue
}
b.WriteString(",")
b.WriteString(key)
b.WriteString("=")
b.WriteString(quoteParam(strVal))
}
b.WriteString(":")
b.WriteString(root)

return newFs(ctx, storage.ProviderType(f.backendType), b.String())
}

// RegisterAllBackends 将所有 rclone 后端注册为独立 Factory 到 Registry。
// 已存在的内置类型(s3, ftp 等)不会被覆盖。
func RegisterAllBackends(registry *storage.Registry) {
builtinTypes := map[string]bool{
"local_disk": true, "s3": true, "webdav": true, "google_drive": true,
"ftp": true, "aliyun_oss": true, "tencent_cos": true, "qiniu_kodo": true,
"rclone": true, "local": true,
}
for _, info := range ListBackends() {
if builtinTypes[info.Name] {
continue
}
registry.Register(NewBackendFactory(info.Name))
}
}
Loading
Loading