From a8b83cf4ed3d03260d3946b548ad7753f772956a Mon Sep 17 00:00:00 2001 From: ssongliu <73214554+ssongliu@users.noreply.github.com> Date: Tue, 11 Apr 2023 18:48:28 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=AE=A1=E5=88=92=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=97=B6=E5=A2=9E=E5=8A=A0=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E6=8F=90=E7=A4=BA=20(#580)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/api/v1/cronjob.go | 12 ++-- backend/app/dto/cronjob.go | 10 ++++ backend/app/service/cornjob.go | 61 ++++++++++++++------ backend/app/service/cronjob_helper.go | 20 ------- frontend/src/api/modules/cronjob.ts | 8 +-- frontend/src/lang/modules/en.ts | 3 + frontend/src/lang/modules/zh.ts | 2 + frontend/src/views/cronjob/index.vue | 64 ++++++++++++++++++--- frontend/src/views/cronjob/record/index.vue | 53 +++++++++++++---- 9 files changed, 167 insertions(+), 66 deletions(-) diff --git a/backend/app/api/v1/cronjob.go b/backend/app/api/v1/cronjob.go index 2fcbdc335..433654d50 100644 --- a/backend/app/api/v1/cronjob.go +++ b/backend/app/api/v1/cronjob.go @@ -96,19 +96,19 @@ func (b *BaseApi) SearchJobRecords(c *gin.Context) { // @Summary Clean job records // @Description 清空计划任务记录 // @Accept json -// @Param request body dto.OperateByID true "request" +// @Param request body dto.CronjobClean true "request" // @Success 200 // @Security ApiKeyAuth // @Router /cronjobs/records/clean [post] // @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"cronjobs","output_colume":"name","output_value":"name"}],"formatZH":"清空计划任务记录 [name]","formatEN":"clean cronjob [name] records"} func (b *BaseApi) CleanRecord(c *gin.Context) { - var req dto.OperateByID + var req dto.CronjobClean if err := c.ShouldBindJSON(&req); err != nil { helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) return } - if err := cronjobService.CleanRecord(req.ID); err != nil { + if err := cronjobService.CleanRecord(req); err != nil { helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) return } @@ -120,13 +120,13 @@ func (b *BaseApi) CleanRecord(c *gin.Context) { // @Summary Delete cronjob // @Description 删除计划任务 // @Accept json -// @Param request body dto.BatchDeleteReq true "request" +// @Param request body dto.CronjobBatchDelete true "request" // @Success 200 // @Security ApiKeyAuth // @Router /cronjobs/del [post] // @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"ids","isList":true,"db":"cronjobs","output_colume":"name","output_value":"names"}],"formatZH":"删除计划任务 [names]","formatEN":"delete cronjob [names]"} func (b *BaseApi) DeleteCronjob(c *gin.Context) { - var req dto.BatchDeleteReq + var req dto.CronjobBatchDelete if err := c.ShouldBindJSON(&req); err != nil { helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) return @@ -136,7 +136,7 @@ func (b *BaseApi) DeleteCronjob(c *gin.Context) { return } - if err := cronjobService.Delete(req.Ids); err != nil { + if err := cronjobService.Delete(req); err != nil { helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) return } diff --git a/backend/app/dto/cronjob.go b/backend/app/dto/cronjob.go index 8739a8197..e72efb983 100644 --- a/backend/app/dto/cronjob.go +++ b/backend/app/dto/cronjob.go @@ -52,6 +52,16 @@ type CronjobDownload struct { BackupAccountID uint `json:"backupAccountID" validate:"required"` } +type CronjobClean struct { + CleanData bool `json:"cleanData"` + CronjobID uint `json:"cronjobID" validate:"required"` +} + +type CronjobBatchDelete struct { + CleanData bool `json:"cleanData"` + IDs []uint `json:"ids"` +} + type CronjobInfo struct { ID uint `json:"id"` Name string `json:"name"` diff --git a/backend/app/service/cornjob.go b/backend/app/service/cornjob.go index bb1dda287..fabc07dfd 100644 --- a/backend/app/service/cornjob.go +++ b/backend/app/service/cornjob.go @@ -24,10 +24,10 @@ type ICronjobService interface { HandleOnce(id uint) error Update(id uint, req dto.CronjobUpdate) error UpdateStatus(id uint, status string) error - Delete(ids []uint) error + Delete(req dto.CronjobBatchDelete) error Download(down dto.CronjobDownload) (string, error) StartJob(cronjob *model.Cronjob) (int, error) - CleanRecord(id uint) error + CleanRecord(req dto.CronjobClean) error } func NewICronjobService() ICronjobService { @@ -79,16 +79,39 @@ func (u *CronjobService) SearchRecords(search dto.SearchRecord) (int64, interfac return total, dtoCronjobs, err } -func (u *CronjobService) CleanRecord(id uint) error { - delRecords, err := cronjobRepo.ListRecord(cronjobRepo.WithByJobID(int(id))) +func (u *CronjobService) CleanRecord(req dto.CronjobClean) error { + if req.CleanData { + cronjob, err := cronjobRepo.Get(commonRepo.WithByID(req.CronjobID)) + if err != nil { + return err + } + cronjob.RetainCopies = 0 + backup, err := backupRepo.Get(commonRepo.WithByID(uint(cronjob.TargetDirID))) + if err != nil { + return err + } + if backup.Type != "LOCAL" { + localDir, err := loadLocalDir() + if err != nil { + return err + } + client, err := NewIBackupService().NewClient(&backup) + if err != nil { + return err + } + u.HandleRmExpired(backup.Type, localDir, &cronjob, client) + } else { + u.HandleRmExpired(backup.Type, "", &cronjob, nil) + } + } + delRecords, err := cronjobRepo.ListRecord(cronjobRepo.WithByJobID(int(req.CronjobID))) if err != nil { return err } for _, del := range delRecords { - _ = os.RemoveAll(del.File) _ = os.RemoveAll(del.Records) } - if err := cronjobRepo.DeleteRecord(cronjobRepo.WithByJobID(int(id))); err != nil { + if err := cronjobRepo.DeleteRecord(cronjobRepo.WithByJobID(int(req.CronjobID))); err != nil { return err } return nil @@ -175,21 +198,23 @@ func (u *CronjobService) StartJob(cronjob *model.Cronjob) (int, error) { return entryID, nil } -func (u *CronjobService) Delete(ids []uint) error { - if len(ids) == 1 { - if err := u.HandleDelete(ids[0]); err != nil { +func (u *CronjobService) Delete(req dto.CronjobBatchDelete) error { + for _, id := range req.IDs { + cronjob, _ := cronjobRepo.Get(commonRepo.WithByID(id)) + if cronjob.ID == 0 { + return errors.New("find cronjob in db failed") + } + global.Cron.Remove(cron.EntryID(cronjob.EntryID)) + global.LOG.Infof("stop cronjob entryID: %d", cronjob.EntryID) + if err := u.CleanRecord(dto.CronjobClean{CronjobID: id, CleanData: req.CleanData}); err != nil { + return err + } + if err := cronjobRepo.Delete(commonRepo.WithByID(id)); err != nil { return err } - return cronjobRepo.Delete(commonRepo.WithByID(ids[0])) } - cronjobs, err := cronjobRepo.List(commonRepo.WithIdsIn(ids)) - if err != nil { - return err - } - for i := range cronjobs { - _ = u.HandleDelete(ids[i]) - } - return cronjobRepo.Delete(commonRepo.WithIdsIn(ids)) + + return nil } func (u *CronjobService) Update(id uint, req dto.CronjobUpdate) error { diff --git a/backend/app/service/cronjob_helper.go b/backend/app/service/cronjob_helper.go index 43b15164f..c11c6505c 100644 --- a/backend/app/service/cronjob_helper.go +++ b/backend/app/service/cronjob_helper.go @@ -15,7 +15,6 @@ import ( "github.com/1Panel-dev/1Panel/backend/utils/cloud_storage" "github.com/1Panel-dev/1Panel/backend/utils/cmd" "github.com/pkg/errors" - "github.com/robfig/cron/v3" ) func (u *CronjobService) HandleJob(cronjob *model.Cronjob) { @@ -123,25 +122,6 @@ func (u *CronjobService) HandleBackup(cronjob *model.Cronjob, startTime time.Tim } } -func (u *CronjobService) HandleDelete(id uint) error { - cronjob, _ := cronjobRepo.Get(commonRepo.WithByID(id)) - if cronjob.ID == 0 { - return errors.New("find cronjob in db failed") - } - commonDir := fmt.Sprintf("%s/%s/", cronjob.Type, cronjob.Name) - global.Cron.Remove(cron.EntryID(cronjob.EntryID)) - global.LOG.Infof("stop cronjob entryID: %d", cronjob.EntryID) - _ = cronjobRepo.DeleteRecord(cronjobRepo.WithByJobID(int(id))) - - dir := fmt.Sprintf("%s/task/%s/%s", constant.DataDir, cronjob.Type, cronjob.Name) - if _, err := os.Stat(dir); err == nil { - if err := os.RemoveAll(dir); err != nil { - global.LOG.Errorf("rm file %s/task/%s failed, err: %v", constant.DataDir, commonDir, err) - } - } - return nil -} - func (u *CronjobService) HandleRmExpired(backType, localDir string, cronjob *model.Cronjob, backClient cloud_storage.CloudStorageClient) { global.LOG.Infof("start to handle remove expired, retain copies: %d", cronjob.RetainCopies) records, _ := cronjobRepo.ListRecord(cronjobRepo.WithByJobID(int(cronjob.ID)), commonRepo.WithOrderBy("created_at desc")) diff --git a/frontend/src/api/modules/cronjob.ts b/frontend/src/api/modules/cronjob.ts index 95904b0a4..29303ec02 100644 --- a/frontend/src/api/modules/cronjob.ts +++ b/frontend/src/api/modules/cronjob.ts @@ -14,16 +14,16 @@ export const editCronjob = (params: Cronjob.CronjobUpdate) => { return http.post(`/cronjobs/update`, params); }; -export const deleteCronjob = (params: { ids: number[] }) => { - return http.post(`/cronjobs/del`, params); +export const deleteCronjob = (ids: number[], cleanData: boolean) => { + return http.post(`/cronjobs/del`, { ids: ids, cleanData: cleanData }); }; export const searchRecords = (params: Cronjob.SearchRecord) => { return http.post>(`cronjobs/search/records`, params); }; -export const cleanRecords = (id: number) => { - return http.post(`cronjobs/records/clean`, { id: id }); +export const cleanRecords = (id: number, cleanData: boolean) => { + return http.post(`cronjobs/records/clean`, { cronjobID: id, cleanData: cleanData }); }; export const getRecordDetail = (params: string) => { diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 87c33c408..a1a6e1858 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -627,6 +627,9 @@ const message = { errRecord: 'Incorrect logging', errHandle: 'Task execution failure', noRecord: 'The execution did not generate any logs', + cleanData: 'Clean data', + cleanDataHelper: + 'Clean data will remove all data generated by this scheduled task, including backup files, execution records, and log files.', noLogs: 'No task output yet...', errPath: 'Backup path [{0}] error, cannot download!', }, diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index ac0016030..83b2cf97b 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -628,6 +628,8 @@ const message = { errRecord: '错误的日志记录', errHandle: '任务执行失败', noRecord: '当前计划任务暂未产生记录', + cleanData: '删除数据', + cleanDataHelper: '删除数据,将删除该计划任务产生的所有数据,包括备份文件、执行记录以及日志文件', noLogs: '暂无任务输出...', errPath: '备份路径 [{0}] 错误,无法下载!', }, diff --git a/frontend/src/views/cronjob/index.vue b/frontend/src/views/cronjob/index.vue index ddeb7330d..b7a45506a 100644 --- a/frontend/src/views/cronjob/index.vue +++ b/frontend/src/views/cronjob/index.vue @@ -15,7 +15,7 @@ {{ $t('commons.button.create') }}{{ $t('cronjob.cronTask') }} - + {{ $t('commons.button.delete') }} @@ -114,6 +114,32 @@ + + + + + + {{ $t('cronjob.cleanDataHelper') }} + + + + + + @@ -132,7 +158,6 @@ import RouterButton from '@/components/router-button/index.vue'; import { deleteCronjob, getCronjobPage, handleOnce, updateStatus } from '@/api/modules/cronjob'; import i18n from '@/lang'; import { Cronjob } from '@/api/interface/cronjob'; -import { useDeleteData } from '@/hooks/use-delete-data'; import { ElMessageBox } from 'element-plus'; import { MsgSuccess } from '@/utils/message'; @@ -148,6 +173,11 @@ const paginationConfig = reactive({ }); const searchName = ref(); +const deleteVisiable = ref(); +const deleteCronjobID = ref(); +const delLoading = ref(); +const cleanData = ref(); + const weekOptions = [ { label: i18n.global.t('cronjob.monday'), value: 1 }, { label: i18n.global.t('cronjob.tuesday'), value: 2 }, @@ -204,17 +234,35 @@ const onOpenDialog = async ( dialogRef.value!.acceptParams(params); }; -const onBatchDelete = async (row: Cronjob.CronjobInfo | null) => { - let ids: Array = []; +const onDelete = async (row: Cronjob.CronjobInfo | null) => { if (row) { - ids.push(row.id); + deleteCronjobID.value = row.id; + } else { + deleteCronjobID.value = 0; + } + deleteVisiable.value = true; +}; + +const onSubmitDelete = async () => { + let ids: Array = []; + if (deleteCronjobID.value) { + ids.push(deleteCronjobID.value); } else { selects.value.forEach((item: Cronjob.CronjobInfo) => { ids.push(item.id); }); } - await useDeleteData(deleteCronjob, { ids: ids }, 'commons.msg.delete'); - search(); + delLoading.value = true; + await deleteCronjob(ids, cleanData.value) + .then(() => { + delLoading.value = false; + deleteVisiable.value = false; + MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); + search(); + }) + .catch(() => { + delLoading.value = false; + }); }; const onChangeStatus = async (id: number, status: string) => { @@ -272,7 +320,7 @@ const buttons = [ { label: i18n.global.t('commons.button.delete'), click: (row: Cronjob.CronjobInfo) => { - onBatchDelete(row); + onDelete(row); }, }, ]; diff --git a/frontend/src/views/cronjob/record/index.vue b/frontend/src/views/cronjob/record/index.vue index 1df02908d..d7e804481 100644 --- a/frontend/src/views/cronjob/record/index.vue +++ b/frontend/src/views/cronjob/record/index.vue @@ -70,7 +70,7 @@ > {{ $t('commons.button.enable') }} - + {{ $t('commons.button.clean') }} @@ -282,6 +282,32 @@ + + + + + + + {{ $t('cronjob.cleanDataHelper') }} + + + + + @@ -319,6 +345,10 @@ const currentRecord = ref(); const currentRecordDetail = ref(''); const currentRecordIndex = ref(); +const deleteVisiable = ref(); +const delLoading = ref(); +const cleanData = ref(); + const acceptParams = async (params: DialogProps): Promise => { dialogData.value = params; recordShow.value = true; @@ -547,15 +577,18 @@ const loadRecord = async (row: Cronjob.Record) => { } }; const cleanRecord = async () => { - ElMessageBox.confirm(i18n.global.t('cronjob.cleanHelper'), i18n.global.t('commons.button.clean'), { - confirmButtonText: i18n.global.t('commons.button.confirm'), - cancelButtonText: i18n.global.t('commons.button.cancel'), - type: 'info', - }).then(async () => { - await cleanRecords(dialogData.value.rowData.id); - MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); - search(true); - }); + delLoading.value = true; + console.log(dialogData.value.rowData); + await cleanRecords(dialogData.value.rowData.id, cleanData.value) + .then(() => { + delLoading.value = false; + deleteVisiable.value = false; + MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); + search(true); + }) + .catch(() => { + delLoading.value = false; + }); }; function isBackup() {