From 0d62d435d9eb53f3b366136a985ed2874b3cbef9 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Thu, 22 Jan 2026 01:27:40 +0800 Subject: [PATCH] =?UTF-8?q?feat(backup):=20=E6=96=B0=E5=BB=BA/=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E5=A4=87=E4=BB=BD=E8=B4=A6=E5=8F=B7=E6=97=B6=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=20s3=E3=80=81sftp=E3=80=81webdav=20=E5=AD=98=E5=82=A8?= =?UTF-8?q?=E8=BF=9E=E6=8E=A5=20(#1264)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Initial plan * feat(backup): 新建/更新备份账号时验证 s3、sftp、webdav 存储连接 Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com> * refactor(backup): 优化 validateStorage 函数,统一连接验证逻辑 Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com> * feat: 优化备份账号保存 * fix: lint --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com> Co-authored-by: 耗子 --- cmd/ace/wire_gen.go | 2 +- internal/data/backup.go | 12 ++--- internal/service/backup_account.go | 70 +++++++++++++++++++++++++++++- 3 files changed, 76 insertions(+), 8 deletions(-) diff --git a/cmd/ace/wire_gen.go b/cmd/ace/wire_gen.go index 3ca41f0b..973bd358 100644 --- a/cmd/ace/wire_gen.go +++ b/cmd/ace/wire_gen.go @@ -91,7 +91,7 @@ func initWeb() (*app.Web, error) { databaseUserService := service.NewDatabaseUserService(databaseUserRepo) backupService := service.NewBackupService(locale, backupRepo) backupAccountRepo := data.NewBackupAccountRepo(locale, db, logger) - backupAccountService := service.NewBackupAccountService(backupAccountRepo) + backupAccountService := service.NewBackupAccountService(locale, backupAccountRepo) certService := service.NewCertService(locale, certRepo) certDNSRepo := data.NewCertDNSRepo(db, logger) certDNSService := service.NewCertDNSService(certDNSRepo) diff --git a/internal/data/backup.go b/internal/data/backup.go index 0fcf93c6..e0d1f466 100644 --- a/internal/data/backup.go +++ b/internal/data/backup.go @@ -93,11 +93,11 @@ func (r *backupRepo) Create(ctx context.Context, typ biz.BackupType, target stri name := fmt.Sprintf("%s_%s", target, time.Now().Format("20060102150405")) if app.IsCli { fmt.Println(r.hr) - fmt.Println(fmt.Sprintf(r.t.Get("★ Start backup [%s]", time.Now().Format(time.DateTime)))) + fmt.Println(r.t.Get("★ Start backup [%s]", time.Now().Format(time.DateTime))) fmt.Println(r.hr) - fmt.Println(fmt.Sprintf(r.t.Get("|-Backup type: %s", string(typ)))) - fmt.Println(fmt.Sprintf(r.t.Get("|-Backup account: %s", backupAccount.Name))) - fmt.Println(fmt.Sprintf(r.t.Get("|-Backup target: %s", target))) + fmt.Println(r.t.Get("|-Backup type: %s", string(typ))) + fmt.Println(r.t.Get("|-Backup account: %s", backupAccount.Name)) + fmt.Println(r.t.Get("|-Backup target: %s", target)) } switch typ { @@ -122,7 +122,7 @@ func (r *backupRepo) Create(ctx context.Context, typ biz.BackupType, target stri slog.String("target", target), ) if app.IsCli { - fmt.Println(fmt.Sprintf(r.t.Get("☆ Backup failed: %v [%s]", err, time.Now().Format(time.DateTime)))) + fmt.Println(r.t.Get("☆ Backup failed: %v [%s]", err, time.Now().Format(time.DateTime))) } } else { r.log.Info("backup created", @@ -132,7 +132,7 @@ func (r *backupRepo) Create(ctx context.Context, typ biz.BackupType, target stri slog.String("target", target), ) if app.IsCli { - fmt.Println(fmt.Sprintf(r.t.Get("☆ Backup completed [%s]\n", time.Now().Format(time.DateTime)))) + fmt.Println(r.t.Get("☆ Backup completed [%s]\n", time.Now().Format(time.DateTime))) } } diff --git a/internal/service/backup_account.go b/internal/service/backup_account.go index 2f0ee9ce..3d62432a 100644 --- a/internal/service/backup_account.go +++ b/internal/service/backup_account.go @@ -1,20 +1,26 @@ package service import ( + "errors" "net/http" + "github.com/leonelquinteros/gotext" "github.com/libtnb/chix" "github.com/acepanel/panel/internal/biz" "github.com/acepanel/panel/internal/http/request" + "github.com/acepanel/panel/pkg/storage" + "github.com/acepanel/panel/pkg/types" ) type BackupAccountService struct { + t *gotext.Locale backupAccountRepo biz.BackupAccountRepo } -func NewBackupAccountService(backupAccount biz.BackupAccountRepo) *BackupAccountService { +func NewBackupAccountService(t *gotext.Locale, backupAccount biz.BackupAccountRepo) *BackupAccountService { return &BackupAccountService{ + t: t, backupAccountRepo: backupAccount, } } @@ -45,6 +51,11 @@ func (s *BackupAccountService) Create(w http.ResponseWriter, r *http.Request) { return } + if err = s.validateStorage(req.Type, req.Info); err != nil { + Error(w, http.StatusUnprocessableEntity, "%v", err) + return + } + account, err := s.backupAccountRepo.Create(r.Context(), req) if err != nil { Error(w, http.StatusInternalServerError, "%v", err) @@ -61,6 +72,11 @@ func (s *BackupAccountService) Update(w http.ResponseWriter, r *http.Request) { return } + if err = s.validateStorage(req.Type, req.Info); err != nil { + Error(w, http.StatusUnprocessableEntity, "%v", err) + return + } + if err = s.backupAccountRepo.Update(r.Context(), req); err != nil { Error(w, http.StatusInternalServerError, "%v", err) return @@ -99,3 +115,55 @@ func (s *BackupAccountService) Delete(w http.ResponseWriter, r *http.Request) { Success(w, nil) } + +// validateStorage 验证存储账号配置是否正确 +func (s *BackupAccountService) validateStorage(accountType string, info types.BackupAccountInfo) error { + var err error + var client storage.Storage + + switch biz.BackupAccountType(accountType) { + case biz.BackupAccountTypeS3: + client, err = storage.NewS3(storage.S3Config{ + Region: info.Region, + Bucket: info.Bucket, + AccessKeyID: info.AccessKey, + SecretAccessKey: info.SecretKey, + Endpoint: info.Endpoint, + BasePath: info.Path, + AddressingStyle: storage.S3AddressingStyle(info.Style), + }) + if err != nil { + return errors.New(s.t.Get("s3 configuration error: %v", err)) + } + case biz.BackupAccountTypeSFTP: + client, err = storage.NewSFTP(storage.SFTPConfig{ + Host: info.Host, + Port: info.Port, + Username: info.Username, + Password: info.Password, + PrivateKey: info.PrivateKey, + BasePath: info.Path, + }) + if err != nil { + return errors.New(s.t.Get("sftp configuration error: %v", err)) + } + case biz.BackupAccountTypeWebDav: + client, err = storage.NewWebDav(storage.WebDavConfig{ + URL: info.URL, + Username: info.Username, + Password: info.Password, + BasePath: info.Path, + }) + if err != nil { + return errors.New(s.t.Get("webdav configuration error: %v", err)) + } + default: + return nil + } + + if _, err = client.List(""); err != nil { + return errors.New(s.t.Get("storage connection error: %v", err)) + } + + return nil +}