feat: Support the retention of remote backup files when deleting sche… (#8296)

Refs #8236
This commit is contained in:
ssongliu 2025-04-02 17:03:55 +08:00 committed by GitHub
parent bba8aab18c
commit 90be74504e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 70 additions and 25 deletions

View file

@ -73,11 +73,13 @@ type CronjobDownload struct {
type CronjobClean struct { type CronjobClean struct {
IsDelete bool `json:"isDelete"` IsDelete bool `json:"isDelete"`
CleanData bool `json:"cleanData"` CleanData bool `json:"cleanData"`
CleanRemoteData bool `json:"cleanRemoteData"`
CronjobID uint `json:"cronjobID" validate:"required"` CronjobID uint `json:"cronjobID" validate:"required"`
} }
type CronjobBatchDelete struct { type CronjobBatchDelete struct {
CleanData bool `json:"cleanData"` CleanData bool `json:"cleanData"`
CleanRemoteData bool `json:"cleanRemoteData"`
IDs []uint `json:"ids" validate:"required"` IDs []uint `json:"ids" validate:"required"`
} }

View file

@ -114,6 +114,13 @@ func (u *CronjobService) CleanRecord(req dto.CronjobClean) error {
if err != nil { if err != nil {
return err return err
} }
if !req.CleanRemoteData {
for key := range accountMap {
if key != constant.Local {
delete(accountMap, key)
}
}
}
cronjob.RetainCopies = 0 cronjob.RetainCopies = 0
u.removeExpiredBackup(cronjob, accountMap, model.BackupRecord{}) u.removeExpiredBackup(cronjob, accountMap, model.BackupRecord{})
} else { } else {
@ -249,7 +256,7 @@ func (u *CronjobService) Delete(req dto.CronjobBatchDelete) error {
global.Cron.Remove(cron.EntryID(idItem)) global.Cron.Remove(cron.EntryID(idItem))
} }
global.LOG.Infof("stop cronjob entryID: %s", cronjob.EntryIDs) global.LOG.Infof("stop cronjob entryID: %s", cronjob.EntryIDs)
if err := u.CleanRecord(dto.CronjobClean{CronjobID: id, CleanData: req.CleanData, IsDelete: true}); err != nil { if err := u.CleanRecord(dto.CronjobClean{CronjobID: id, CleanData: req.CleanData, CleanRemoteData: req.CleanRemoteData, IsDelete: true}); err != nil {
return err return err
} }
if err := cronjobRepo.Delete(commonRepo.WithByID(id)); err != nil { if err := cronjobRepo.Delete(commonRepo.WithByID(id)); err != nil {

View file

@ -81,6 +81,7 @@ export namespace Cronjob {
export interface CronjobDelete { export interface CronjobDelete {
ids: Array<number>; ids: Array<number>;
cleanData: boolean; cleanData: boolean;
cleanRemoteData: boolean;
} }
export interface UpdateStatus { export interface UpdateStatus {
id: number; id: number;

View file

@ -27,8 +27,12 @@ export const searchRecords = (params: Cronjob.SearchRecord) => {
return http.post<ResPage<Cronjob.Record>>(`cronjobs/search/records`, params); return http.post<ResPage<Cronjob.Record>>(`cronjobs/search/records`, params);
}; };
export const cleanRecords = (id: number, cleanData: boolean) => { export const cleanRecords = (id: number, cleanData: boolean, cleanRemoteData: boolean) => {
return http.post(`cronjobs/records/clean`, { cronjobID: id, cleanData: cleanData }); return http.post(`cronjobs/records/clean`, {
cronjobID: id,
cleanData: cleanData,
cleanRemoteData: cleanRemoteData,
});
}; };
export const getRecordDetail = (params: string) => { export const getRecordDetail = (params: string) => {

View file

@ -1013,6 +1013,7 @@ const message = {
errHandle: 'Cronjob execution failure', errHandle: 'Cronjob execution failure',
noRecord: 'Trigger the Cron Job, and you will see the records here.', noRecord: 'Trigger the Cron Job, and you will see the records here.',
cleanData: 'Clean data', cleanData: 'Clean data',
cleanRemoteData: 'Delete remote data',
cleanDataHelper: 'Delete the backup file generated during this task.', cleanDataHelper: 'Delete the backup file generated during this task.',
noLogs: 'No task output yet...', noLogs: 'No task output yet...',
errPath: 'Backup path [{0}] error, cannot download!', errPath: 'Backup path [{0}] error, cannot download!',

View file

@ -998,6 +998,7 @@ const message = {
errHandle: 'cronjob実行障害', errHandle: 'cronjob実行障害',
noRecord: 'Cronジョブをトリガーするとここにレコードが表示されます', noRecord: 'Cronジョブをトリガーするとここにレコードが表示されます',
cleanData: 'クリーンデータ', cleanData: 'クリーンデータ',
cleanRemoteData: 'リモートデータを削除',
cleanDataHelper: 'このタスク中に生成されたバックアップファイルを削除します', cleanDataHelper: 'このタスク中に生成されたバックアップファイルを削除します',
noLogs: 'タスク出力はまだありません...', noLogs: 'タスク出力はまだありません...',
errPath: 'バックアップパス[{0}]エラーダウンロードできません', errPath: 'バックアップパス[{0}]エラーダウンロードできません',

View file

@ -992,6 +992,7 @@ const message = {
errHandle: '크론 작업 실행 실패', errHandle: '크론 작업 실행 실패',
noRecord: '크론 작업을 트리거하고 나면 여기에 레코드가 표시됩니다.', noRecord: '크론 작업을 트리거하고 나면 여기에 레코드가 표시됩니다.',
cleanData: '데이터 정리', cleanData: '데이터 정리',
cleanRemoteData: '원격 데이터 삭제',
cleanDataHelper: ' 작업에서 생성된 백업 파일을 삭제합니다.', cleanDataHelper: ' 작업에서 생성된 백업 파일을 삭제합니다.',
noLogs: '작업 출력이 아직 없습니다...', noLogs: '작업 출력이 아직 없습니다...',
errPath: '백업 경로 [{0}] 오류, 다운로드할 없습니다!', errPath: '백업 경로 [{0}] 오류, 다운로드할 없습니다!',

View file

@ -1027,6 +1027,7 @@ const message = {
errHandle: 'Kegagalan pelaksanaan tugas cron', errHandle: 'Kegagalan pelaksanaan tugas cron',
noRecord: 'Picu Tugas Cron, dan anda akan melihat rekod di sini.', noRecord: 'Picu Tugas Cron, dan anda akan melihat rekod di sini.',
cleanData: 'Bersihkan data', cleanData: 'Bersihkan data',
cleanRemoteData: 'Padam data jarak jauh',
cleanDataHelper: 'Padam fail sandaran yang dijana semasa tugas ini.', cleanDataHelper: 'Padam fail sandaran yang dijana semasa tugas ini.',
noLogs: 'Tiada keluaran tugas lagi...', noLogs: 'Tiada keluaran tugas lagi...',
errPath: 'Laluan sandaran [{0}] salah, tidak boleh dimuat turun!', errPath: 'Laluan sandaran [{0}] salah, tidak boleh dimuat turun!',

View file

@ -1018,6 +1018,7 @@ const message = {
errHandle: 'Falha na execução do Cronjob', errHandle: 'Falha na execução do Cronjob',
noRecord: 'Acione a tarefa Cron e você verá os registros aqui.', noRecord: 'Acione a tarefa Cron e você verá os registros aqui.',
cleanData: 'Limpar dados', cleanData: 'Limpar dados',
cleanRemoteData: 'Excluir dados remotos',
cleanDataHelper: 'Excluir o arquivo de backup gerado durante esta tarefa.', cleanDataHelper: 'Excluir o arquivo de backup gerado durante esta tarefa.',
noLogs: 'Ainda não saída de tarefa...', noLogs: 'Ainda não saída de tarefa...',
errPath: 'Caminho de backup [{0}] com erro, não é possível fazer o download!', errPath: 'Caminho de backup [{0}] com erro, não é possível fazer o download!',

View file

@ -1021,6 +1021,7 @@ const message = {
errHandle: 'Сбой выполнения задачи Cron', errHandle: 'Сбой выполнения задачи Cron',
noRecord: 'Запустите задачу Cron, и вы увидите записи здесь.', noRecord: 'Запустите задачу Cron, и вы увидите записи здесь.',
cleanData: 'Очистить данные', cleanData: 'Очистить данные',
cleanRemoteData: 'Удалить удалённые данные',
cleanDataHelper: 'Удалить файл резервной копии, созданный во время этой задачи.', cleanDataHelper: 'Удалить файл резервной копии, созданный во время этой задачи.',
noLogs: 'Пока нет вывода задачи...', noLogs: 'Пока нет вывода задачи...',
errPath: 'Ошибка пути резервной копии [{0}], невозможно скачать!', errPath: 'Ошибка пути резервной копии [{0}], невозможно скачать!',

View file

@ -965,6 +965,7 @@ const message = {
errHandle: '任務執行失敗', errHandle: '任務執行失敗',
noRecord: '目前計劃任務暫未產生記錄', noRecord: '目前計劃任務暫未產生記錄',
cleanData: '刪除備份檔案', cleanData: '刪除備份檔案',
cleanRemoteData: '刪除遠端備份檔案',
cleanDataHelper: '刪除該任務執行過程中產生的備份檔案', cleanDataHelper: '刪除該任務執行過程中產生的備份檔案',
noLogs: '暫無任務輸出...', noLogs: '暫無任務輸出...',
errPath: '備份路徑 [{0}] 錯誤無法下載', errPath: '備份路徑 [{0}] 錯誤無法下載',

View file

@ -966,6 +966,7 @@ const message = {
errHandle: '任务执行失败', errHandle: '任务执行失败',
noRecord: '当前计划任务暂未产生记录', noRecord: '当前计划任务暂未产生记录',
cleanData: '删除备份文件', cleanData: '删除备份文件',
cleanRemoteData: '删除远程备份文件',
cleanDataHelper: '删除该任务执行过程中产生的备份文件', cleanDataHelper: '删除该任务执行过程中产生的备份文件',
noLogs: '暂无任务输出...', noLogs: '暂无任务输出...',
errPath: '备份路径 [{0}] 错误无法下载', errPath: '备份路径 [{0}] 错误无法下载',

View file

@ -163,6 +163,11 @@
<el-form class="mt-4 mb-1" v-if="showClean" ref="deleteForm" label-position="left"> <el-form class="mt-4 mb-1" v-if="showClean" ref="deleteForm" label-position="left">
<el-form-item> <el-form-item>
<el-checkbox v-model="cleanData" :label="$t('cronjob.cleanData')" /> <el-checkbox v-model="cleanData" :label="$t('cronjob.cleanData')" />
<el-checkbox
v-if="cleanData"
v-model="cleanRemoteData"
:label="$t('cronjob.cleanRemoteData')"
/>
<span class="input-help"> <span class="input-help">
{{ $t('cronjob.cleanDataHelper') }} {{ $t('cronjob.cleanDataHelper') }}
</span> </span>
@ -197,6 +202,7 @@ const operateIDs = ref();
const opRef = ref(); const opRef = ref();
const showClean = ref(); const showClean = ref();
const cleanData = ref(); const cleanData = ref();
const cleanRemoteData = ref(true);
const data = ref(); const data = ref();
const paginationConfig = reactive({ const paginationConfig = reactive({
@ -312,7 +318,7 @@ const onDelete = async (row: Cronjob.CronjobInfo | null) => {
const onSubmitDelete = async () => { const onSubmitDelete = async () => {
loading.value = true; loading.value = true;
await deleteCronjob({ ids: operateIDs.value, cleanData: cleanData.value }) await deleteCronjob({ ids: operateIDs.value, cleanData: cleanData.value, cleanRemoteData: cleanRemoteData.value })
.then(() => { .then(() => {
loading.value = false; loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.deleteSuccess')); MsgSuccess(i18n.global.t('commons.msg.deleteSuccess'));

View file

@ -223,6 +223,7 @@
<el-form ref="deleteForm" label-position="left" v-loading="delLoading"> <el-form ref="deleteForm" label-position="left" v-loading="delLoading">
<el-form-item> <el-form-item>
<el-checkbox v-model="cleanData" :label="$t('cronjob.cleanData')" /> <el-checkbox v-model="cleanData" :label="$t('cronjob.cleanData')" />
<el-checkbox v-if="cleanData" v-model="cleanRemoteData" :label="$t('cronjob.cleanRemoteData')" />
<span class="input-help"> <span class="input-help">
{{ $t('cronjob.cleanDataHelper') }} {{ $t('cronjob.cleanDataHelper') }}
</span> </span>
@ -282,6 +283,7 @@ const currentRecordDetail = ref<string>('');
const deleteVisible = ref(); const deleteVisible = ref();
const delLoading = ref(); const delLoading = ref();
const cleanData = ref(); const cleanData = ref();
const cleanRemoteData = ref(true);
const acceptParams = async (params: DialogProps): Promise<void> => { const acceptParams = async (params: DialogProps): Promise<void> => {
let itemSize = Number(localStorage.getItem(searchInfo.cacheSizeKey)); let itemSize = Number(localStorage.getItem(searchInfo.cacheSizeKey));
@ -433,12 +435,13 @@ const loadRecord = async (row: Cronjob.Record) => {
}; };
const onClean = async () => { const onClean = async () => {
if (!isBackup()) {
ElMessageBox.confirm(i18n.global.t('commons.msg.clean'), i18n.global.t('commons.msg.deleteTitle'), { ElMessageBox.confirm(i18n.global.t('commons.msg.clean'), i18n.global.t('commons.msg.deleteTitle'), {
confirmButtonText: i18n.global.t('commons.button.confirm'), confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'), cancelButtonText: i18n.global.t('commons.button.cancel'),
type: 'warning', type: 'warning',
}).then(async () => { }).then(async () => {
await cleanRecords(dialogData.value.rowData.id, cleanData.value) await cleanRecords(dialogData.value.rowData.id, cleanData.value, cleanRemoteData.value)
.then(() => { .then(() => {
delLoading.value = false; delLoading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
@ -448,11 +451,14 @@ const onClean = async () => {
delLoading.value = false; delLoading.value = false;
}); });
}); });
} else {
deleteVisible.value = true;
}
}; };
const cleanRecord = async () => { const cleanRecord = async () => {
delLoading.value = true; delLoading.value = true;
await cleanRecords(dialogData.value.rowData.id, cleanData.value) await cleanRecords(dialogData.value.rowData.id, cleanData.value, cleanRemoteData.value)
.then(() => { .then(() => {
delLoading.value = false; delLoading.value = false;
deleteVisible.value = false; deleteVisible.value = false;
@ -464,6 +470,17 @@ const cleanRecord = async () => {
}); });
}; };
function isBackup() {
return (
dialogData.value.rowData!.type === 'app' ||
dialogData.value.rowData!.type === 'website' ||
dialogData.value.rowData!.type === 'database' ||
dialogData.value.rowData!.type === 'directory' ||
dialogData.value.rowData!.type === 'snapshot' ||
dialogData.value.rowData!.type === 'log'
);
}
onBeforeUnmount(() => { onBeforeUnmount(() => {
clearInterval(Number(timer)); clearInterval(Number(timer));
timer = null; timer = null;