From 8440d77fd76ccc883324ffb55a57bfbd33a4b092 Mon Sep 17 00:00:00 2001 From: ssongliu <73214554+ssongliu@users.noreply.github.com> Date: Sat, 8 Feb 2025 14:25:26 +0800 Subject: [PATCH] fix: Fix the issue of backup account synchronization failure (#7821) --- agent/app/api/v2/backup.go | 4 ++-- agent/app/repo/backup.go | 7 ++++++ agent/app/service/backup.go | 16 +++++++++----- agent/init/hook/hook.go | 7 ++++++ agent/router/backup.go | 2 +- core/app/api/v2/backup.go | 12 +++++----- core/app/dto/common.go | 4 ++++ core/app/service/backup.go | 21 +++++++++--------- core/utils/cloud_storage/client/helper.go | 2 +- core/utils/xpack/xpack.go | 2 +- frontend/src/api/modules/backup.ts | 22 +++++++++---------- .../views/setting/backup-account/index.vue | 6 ++--- .../setting/backup-account/operate/index.vue | 14 ++++++++---- 13 files changed, 73 insertions(+), 46 deletions(-) diff --git a/agent/app/api/v2/backup.go b/agent/app/api/v2/backup.go index 920d315d9..abf1885f7 100644 --- a/agent/app/api/v2/backup.go +++ b/agent/app/api/v2/backup.go @@ -11,13 +11,13 @@ import ( ) func (b *BaseApi) CheckBackupUsed(c *gin.Context) { - id, err := helper.GetIntParamByKey(c, "id") + name, err := helper.GetStrParamByKey(c, "name") if err != nil { helper.BadRequest(c, err) return } - if err := backupService.CheckUsed(id); err != nil { + if err := backupService.CheckUsed(name); err != nil { helper.BadRequest(c, err) return } diff --git a/agent/app/repo/backup.go b/agent/app/repo/backup.go index 6c00cbb51..37cd8d18d 100644 --- a/agent/app/repo/backup.go +++ b/agent/app/repo/backup.go @@ -17,6 +17,7 @@ type IBackupRepo interface { Create(backup *model.BackupAccount) error Save(backup *model.BackupAccount) error Delete(opts ...DBOption) error + WithByPublic(isPublic bool) DBOption ListRecord(opts ...DBOption) ([]model.BackupRecord, error) PageRecord(page, size int, opts ...DBOption) (int64, []model.BackupRecord, error) @@ -35,6 +36,12 @@ func NewIBackupRepo() IBackupRepo { return &BackupRepo{} } +func (u *BackupRepo) WithByPublic(isPublic bool) DBOption { + return func(g *gorm.DB) *gorm.DB { + return g.Where("is_public = ?", isPublic) + } +} + func (u *BackupRepo) Get(opts ...DBOption) (model.BackupAccount, error) { var backup model.BackupAccount db := global.DB diff --git a/agent/app/service/backup.go b/agent/app/service/backup.go index a08643804..41ff90f50 100644 --- a/agent/app/service/backup.go +++ b/agent/app/service/backup.go @@ -28,7 +28,7 @@ import ( type BackupService struct{} type IBackupService interface { - CheckUsed(id uint) error + CheckUsed(name string) error Sync(req dto.SyncFromMaster) error LoadBackupOptions() ([]dto.BackupOption, error) @@ -103,8 +103,8 @@ func (u *BackupService) SearchWithPage(req dto.SearchPageWithType) (int64, inter item.Vars = string(itemVars) } } else { - item.AccessKey = base64.StdEncoding.EncodeToString([]byte(item.AccessKey)) - item.Credential = base64.StdEncoding.EncodeToString([]byte(item.Credential)) + item.AccessKey, _ = encrypt.StringDecryptWithBase64(item.AccessKey) + item.Credential, _ = encrypt.StringDecryptWithBase64(item.Credential) } if account.Type == constant.OneDrive || account.Type == constant.ALIYUN || account.Type == constant.GoogleDrive { @@ -394,15 +394,19 @@ func (u *BackupService) LoadBackupOptions() ([]dto.BackupOption, error) { return data, nil } -func (u *BackupService) CheckUsed(id uint) error { +func (u *BackupService) CheckUsed(name string) error { + account, _ := backupRepo.Get(repo.WithByName(name), backupRepo.WithByPublic(true)) + if account.ID == 0 { + return nil + } cronjobs, _ := cronjobRepo.List() for _, job := range cronjobs { - if job.DownloadAccountID == id { + if job.DownloadAccountID == account.ID { return buserr.New("ErrBackupInUsed") } ids := strings.Split(job.SourceAccountIDs, ",") for _, idItem := range ids { - if idItem == fmt.Sprintf("%v", id) { + if idItem == fmt.Sprintf("%v", account.ID) { return buserr.New("ErrBackupInUsed") } } diff --git a/agent/init/hook/hook.go b/agent/init/hook/hook.go index 8f826553c..11bdc275f 100644 --- a/agent/init/hook/hook.go +++ b/agent/init/hook/hook.go @@ -1,6 +1,7 @@ package hook import ( + "os" "path" "github.com/1Panel-dev/1Panel/agent/app/dto" @@ -102,4 +103,10 @@ func loadLocalDir() { return } global.Dir.LocalBackupDir = account.BackupPath + + if _, err := os.Stat(account.BackupPath); err != nil && os.IsNotExist(err) { + if err = os.MkdirAll(account.BackupPath, os.ModePerm); err != nil { + global.LOG.Errorf("mkdir %s failed, err: %v", account.BackupPath, err) + } + } } diff --git a/agent/router/backup.go b/agent/router/backup.go index b239c1e31..16478a98e 100644 --- a/agent/router/backup.go +++ b/agent/router/backup.go @@ -11,7 +11,7 @@ func (s *BackupRouter) InitRouter(Router *gin.RouterGroup) { backupRouter := Router.Group("backups") baseApi := v2.ApiGroupApp.BaseApi { - backupRouter.GET("/check/:id", baseApi.CheckBackupUsed) + backupRouter.GET("/check/:name", baseApi.CheckBackupUsed) backupRouter.POST("/sync", baseApi.SyncBackupAccount) backupRouter.GET("/options", baseApi.LoadBackupOptions) backupRouter.POST("/search", baseApi.SearchBackup) diff --git a/core/app/api/v2/backup.go b/core/app/api/v2/backup.go index d24c5b7db..f4f8d9db9 100644 --- a/core/app/api/v2/backup.go +++ b/core/app/api/v2/backup.go @@ -33,13 +33,13 @@ func (b *BaseApi) CreateBackup(c *gin.Context) { // @Tags Backup Account // @Summary Refresh token // @Accept json -// @Param request body dto.BackupOperate true "request" +// @Param request body dto.OperateByName true "request" // @Success 200 // @Security ApiKeyAuth // @Security Timestamp // @Router /core/backups/refresh/token [post] func (b *BaseApi) RefreshToken(c *gin.Context) { - var req dto.OperateByID + var req dto.OperateByName if err := helper.CheckBindAndValidate(&req, c); err != nil { return } @@ -96,19 +96,19 @@ func (b *BaseApi) LoadBackupClientInfo(c *gin.Context) { // @Tags Backup Account // @Summary Delete backup account // @Accept json -// @Param request body dto.OperateByID true "request" +// @Param request body dto.OperateByName true "request" // @Success 200 // @Security ApiKeyAuth // @Security Timestamp // @Router /core/backups/del [post] -// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"backup_accounts","output_column":"type","output_value":"types"}],"formatZH":"删除备份账号 [types]","formatEN":"delete backup account [types]"} +// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"删除备份账号 [name]","formatEN":"delete backup account [name]"} func (b *BaseApi) DeleteBackup(c *gin.Context) { - var req dto.OperateByID + var req dto.OperateByName if err := helper.CheckBindAndValidate(&req, c); err != nil { return } - if err := backupService.Delete(req.ID); err != nil { + if err := backupService.Delete(req.Name); err != nil { helper.InternalServer(c, err) return } diff --git a/core/app/dto/common.go b/core/app/dto/common.go index 3d8cc1212..acdaa0782 100644 --- a/core/app/dto/common.go +++ b/core/app/dto/common.go @@ -35,6 +35,10 @@ type OperateByType struct { Type string `json:"type"` } +type OperateByName struct { + Name string `json:"name"` +} + type OperateByID struct { ID uint `json:"id"` } diff --git a/core/app/service/backup.go b/core/app/service/backup.go index 72517adb9..83eab8737 100644 --- a/core/app/service/backup.go +++ b/core/app/service/backup.go @@ -33,8 +33,8 @@ type IBackupService interface { Create(backupDto dto.BackupOperate) error GetBuckets(backupDto dto.ForBuckets) ([]interface{}, error) Update(req dto.BackupOperate) error - Delete(id uint) error - RefreshToken(req dto.OperateByID) error + Delete(name string) error + RefreshToken(req dto.OperateByName) error } func NewIBackupService() IBackupService { @@ -156,8 +156,8 @@ func (u *BackupService) GetBuckets(req dto.ForBuckets) ([]interface{}, error) { return client.ListBuckets() } -func (u *BackupService) Delete(id uint) error { - backup, _ := backupRepo.Get(repo.WithByID(id)) +func (u *BackupService) Delete(name string) error { + backup, _ := backupRepo.Get(repo.WithByName(name)) if backup.ID == 0 { return buserr.New("ErrRecordNotFound") } @@ -167,21 +167,21 @@ func (u *BackupService) Delete(id uint) error { if backup.Type == constant.Local { return buserr.New("ErrBackupLocal") } - if _, err := req_helper.NewLocalClient(fmt.Sprintf("/api/v2/backups/check/%v", id), http.MethodGet, nil); err != nil { + if _, err := req_helper.NewLocalClient(fmt.Sprintf("/api/v2/backups/check/%s", name), http.MethodGet, nil); err != nil { global.LOG.Errorf("check used of local cronjob failed, err: %v", err) return buserr.New("ErrBackupInUsed") } - if err := xpack.CheckBackupUsed(id); err != nil { + if err := xpack.CheckBackupUsed(name); err != nil { global.LOG.Errorf("check used of node cronjob failed, err: %v", err) return buserr.New("ErrBackupInUsed") } go syncAccountToAgent(backup, "delete") - return backupRepo.Delete(repo.WithByID(id)) + return backupRepo.Delete(repo.WithByName(name)) } func (u *BackupService) Update(req dto.BackupOperate) error { - backup, _ := backupRepo.Get(repo.WithByID(req.ID)) + backup, _ := backupRepo.Get(repo.WithByName(req.Name)) if backup.ID == 0 { return buserr.New("ErrRecordNotFound") } @@ -198,6 +198,7 @@ func (u *BackupService) Update(req dto.BackupOperate) error { if err := copier.Copy(&newBackup, &req); err != nil { return buserr.WithDetail("ErrStructTransform", err.Error(), nil) } + newBackup.ID = backup.ID itemAccessKey, err := base64.StdEncoding.DecodeString(newBackup.AccessKey) if err != nil { return err @@ -235,8 +236,8 @@ func (u *BackupService) Update(req dto.BackupOperate) error { return nil } -func (u *BackupService) RefreshToken(req dto.OperateByID) error { - backup, _ := backupRepo.Get(repo.WithByID(req.ID)) +func (u *BackupService) RefreshToken(req dto.OperateByName) error { + backup, _ := backupRepo.Get(repo.WithByName(req.Name)) if backup.ID == 0 { return buserr.New("ErrRecordNotFound") } diff --git a/core/utils/cloud_storage/client/helper.go b/core/utils/cloud_storage/client/helper.go index 08134748c..02ef6ee5c 100644 --- a/core/utils/cloud_storage/client/helper.go +++ b/core/utils/cloud_storage/client/helper.go @@ -8,7 +8,7 @@ import ( func loadParamFromVars(key string, vars map[string]interface{}) string { if _, ok := vars[key]; !ok { - if key != "bucket" && key != "port" { + if key != "bucket" && key != "port" && key != "authMode" && key != "passPhrase" { global.LOG.Errorf("load param %s from vars failed, err: not exist!", key) } return "" diff --git a/core/utils/xpack/xpack.go b/core/utils/xpack/xpack.go index d5ed4ae80..6229d8af1 100644 --- a/core/utils/xpack/xpack.go +++ b/core/utils/xpack/xpack.go @@ -16,7 +16,7 @@ func Proxy(c *gin.Context, currentNode string) {} func UpdateGroup(name string, group, newGroup uint) error { return nil } -func CheckBackupUsed(id uint) error { return nil } +func CheckBackupUsed(name string) error { return nil } func RequestToAllAgent(reqUrl, reqMethod string, reqBody io.Reader) error { return nil } diff --git a/frontend/src/api/modules/backup.ts b/frontend/src/api/modules/backup.ts index 60f1875d6..54778eccb 100644 --- a/frontend/src/api/modules/backup.ts +++ b/frontend/src/api/modules/backup.ts @@ -46,12 +46,11 @@ export const getFilesFromBackup = (id: number) => { }; // backup-core -export const refreshToken = (params: { id: number; isPublic: boolean }) => { - let urlItem = '/core/backups/refresh/token'; - if (!params.isPublic || !globalStore.isProductPro) { - urlItem = '/backups/refresh/token'; +export const refreshToken = (params: { id: number; name: string; isPublic: boolean }) => { + if (!params.isPublic) { + return http.post('/backups/refresh/token', { id: params.id }); } - return http.post(urlItem, { id: params.id }); + return http.post('/core/backups/refresh/token', { name: params.name }); }; export const getClientInfo = (clientType: string) => { return http.get(`/core/backups/client/${clientType}`); @@ -65,7 +64,7 @@ export const addBackup = (params: Backup.BackupOperate) => { request.credential = Base64.encode(request.credential); } let urlItem = '/core/backups'; - if (!params.isPublic || !globalStore.isProductPro) { + if (!params.isPublic) { urlItem = '/backups'; } return http.post(urlItem, request, TimeoutEnum.T_60S); @@ -79,17 +78,16 @@ export const editBackup = (params: Backup.BackupOperate) => { request.credential = Base64.encode(request.credential); } let urlItem = '/core/backups/update'; - if (!params.isPublic || !globalStore.isProductPro) { + if (!params.isPublic) { urlItem = '/backups/update'; } return http.post(urlItem, request); }; -export const deleteBackup = (params: { id: number; isPublic: boolean }) => { - let urlItem = '/core/backups/del'; - if (!params.isPublic || !globalStore.isProductPro) { - urlItem = '/backups/del'; +export const deleteBackup = (params: { id: number; name: string; isPublic: boolean }) => { + if (!params.isPublic) { + return http.post('/backups/del', { id: params.id }); } - return http.post(urlItem, { id: params.id }); + return http.post('/core/backups/del', { name: params.name }); }; export const listBucket = (params: Backup.ForBucket) => { let request = deepCopy(params) as Backup.BackupOperate; diff --git a/frontend/src/views/setting/backup-account/index.vue b/frontend/src/views/setting/backup-account/index.vue index 489ccd35c..dcca5bede 100644 --- a/frontend/src/views/setting/backup-account/index.vue +++ b/frontend/src/views/setting/backup-account/index.vue @@ -167,13 +167,13 @@ const loadEndpoint = (row: any) => { const onDelete = async (row: Backup.BackupInfo) => { opRef.value.acceptParams({ title: i18n.global.t('commons.button.delete'), - names: [row.type], + names: ['[ ' + row.type + ' ] ' + row.name], msg: i18n.global.t('commons.msg.operatorHelper', [ i18n.global.t('setting.backupAccount'), i18n.global.t('commons.button.delete'), ]), api: deleteBackup, - params: { id: row.id, isPublic: row.isPublic }, + params: { id: row.id, name: row.name, isPublic: row.isPublic }, }); }; @@ -193,7 +193,7 @@ const onOpenDialog = async ( }; const refreshItemToken = async (row: any) => { - await refreshToken({ id: row.id, isPublic: row.isPublic }); + await refreshToken({ id: row.id, name: row.name, isPublic: row.isPublic }); MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); search(); }; diff --git a/frontend/src/views/setting/backup-account/operate/index.vue b/frontend/src/views/setting/backup-account/operate/index.vue index 3e467c4e5..8f790b453 100644 --- a/frontend/src/views/setting/backup-account/operate/index.vue +++ b/frontend/src/views/setting/backup-account/operate/index.vue @@ -11,13 +11,16 @@ prop="isPublic" :rules="Rules.requiredSelect" > - + + {{ dialogData.rowData!.isPublic ? $t('setting.public') : $t('setting.private') }} + + {{ $t('setting.public') }} {{ $t('setting.private') }} - - {{ dialogData.rowData!.isPublic ? $t('setting.publicHelper') : $t('setting.privateHelper') }} - + + {{ dialogData.rowData!.isPublic ? $t('setting.publicHelper') : $t('setting.privateHelper') }} + {{ $t('setting.' + dialogData.rowData!.type) }} @@ -440,6 +443,9 @@ const dialogData = ref({ }); const acceptParams = (params: DialogProps): void => { dialogData.value = params; + dialogData.value.rowData.varsJson = dialogData.value.rowData!.vars + ? JSON.parse(dialogData.value.rowData!.vars) + : {}; title.value = i18n.global.t('commons.button.' + dialogData.value.title); if (dialogData.value.title === 'create') { dialogData.value.rowData!.type = 'OSS';