mirror of https://github.com/1Panel-dev/1Panel
feat: 计划任务删除时增加删除数据提示 (#580)
parent
38725097a6
commit
a8b83cf4ed
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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"`
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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"))
|
||||
|
|
|
@ -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<ResPage<Cronjob.Record>>(`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) => {
|
||||
|
|
|
@ -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!',
|
||||
},
|
||||
|
|
|
@ -628,6 +628,8 @@ const message = {
|
|||
errRecord: '错误的日志记录',
|
||||
errHandle: '任务执行失败',
|
||||
noRecord: '当前计划任务暂未产生记录',
|
||||
cleanData: '删除数据',
|
||||
cleanDataHelper: '删除数据,将删除该计划任务产生的所有数据,包括备份文件、执行记录以及日志文件',
|
||||
noLogs: '暂无任务输出...',
|
||||
errPath: '备份路径 [{0}] 错误,无法下载!',
|
||||
},
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<el-button type="primary" @click="onOpenDialog('create')">
|
||||
{{ $t('commons.button.create') }}{{ $t('cronjob.cronTask') }}
|
||||
</el-button>
|
||||
<el-button plain :disabled="selects.length === 0" @click="onBatchDelete(null)">
|
||||
<el-button plain :disabled="selects.length === 0" @click="onDelete(null)">
|
||||
{{ $t('commons.button.delete') }}
|
||||
</el-button>
|
||||
</el-col>
|
||||
|
@ -114,6 +114,32 @@
|
|||
</template>
|
||||
</LayoutContent>
|
||||
|
||||
<el-dialog
|
||||
v-model="deleteVisiable"
|
||||
:title="$t('commons.button.clean')"
|
||||
width="30%"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<el-form ref="deleteForm" label-position="left" v-loading="delLoading">
|
||||
<el-form-item>
|
||||
<el-checkbox v-model="cleanData" :label="$t('cronjob.cleanData')" />
|
||||
<span class="input-help">
|
||||
{{ $t('cronjob.cleanDataHelper') }}
|
||||
</span>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="deleteVisiable = false" :disabled="delLoading">
|
||||
{{ $t('commons.button.cancel') }}
|
||||
</el-button>
|
||||
<el-button type="primary" @click="onSubmitDelete">
|
||||
{{ $t('commons.button.confirm') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<OperatrDialog @search="search" ref="dialogRef" />
|
||||
<Records @search="search()" ref="dialogRecordRef" />
|
||||
</div>
|
||||
|
@ -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<number> = [];
|
||||
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<number> = [];
|
||||
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);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
|
|
@ -70,7 +70,7 @@
|
|||
>
|
||||
{{ $t('commons.button.enable') }}
|
||||
</el-button>
|
||||
<el-button type="primary" @click="cleanRecord()" link>
|
||||
<el-button type="primary" @click="deleteVisiable = true" link>
|
||||
{{ $t('commons.button.clean') }}
|
||||
</el-button>
|
||||
</span>
|
||||
|
@ -282,6 +282,32 @@
|
|||
</div>
|
||||
</template>
|
||||
</LayoutContent>
|
||||
|
||||
<el-dialog
|
||||
v-model="deleteVisiable"
|
||||
:title="$t('commons.button.clean')"
|
||||
width="30%"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<el-form ref="deleteForm" label-position="left" v-loading="delLoading">
|
||||
<el-form-item>
|
||||
<el-checkbox v-model="cleanData" :label="$t('cronjob.cleanData')" />
|
||||
<span class="input-help">
|
||||
{{ $t('cronjob.cleanDataHelper') }}
|
||||
</span>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="deleteVisiable = false" :disabled="delLoading">
|
||||
{{ $t('commons.button.cancel') }}
|
||||
</el-button>
|
||||
<el-button type="primary" @click="cleanRecord">
|
||||
{{ $t('commons.button.confirm') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -319,6 +345,10 @@ const currentRecord = ref<Cronjob.Record>();
|
|||
const currentRecordDetail = ref<string>('');
|
||||
const currentRecordIndex = ref();
|
||||
|
||||
const deleteVisiable = ref();
|
||||
const delLoading = ref();
|
||||
const cleanData = ref();
|
||||
|
||||
const acceptParams = async (params: DialogProps): Promise<void> => {
|
||||
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() {
|
||||
|
|
Loading…
Reference in New Issue