integrity: add integrity check fields and initial UI
This commit is contained in:
@@ -4,8 +4,8 @@ import { useI18n } from 'vue-i18n';
|
||||
const i18n = useI18n();
|
||||
const t = i18n.t;
|
||||
|
||||
import { ref, onMounted, useTemplateRef } from 'vue';
|
||||
import { Button, Switch, Checkbox, FormGroup, TextInput, TableView, Dialog, ProgressBar } from '@cloudron/pankow';
|
||||
import { ref, onMounted, onUnmounted, useTemplateRef } from 'vue';
|
||||
import { Button, Switch, Checkbox, FormGroup, TextInput, TableView, Dialog, ProgressBar, Spinner } from '@cloudron/pankow';
|
||||
import { prettyLongDate, prettyFileSize } from '@cloudron/pankow/utils';
|
||||
import { API_ORIGIN, RSTATES } from '../../constants.js';
|
||||
import { download } from '../../utils.js';
|
||||
@@ -14,6 +14,7 @@ import AppRestoreDialog from '../AppRestoreDialog.vue';
|
||||
import SettingsItem from '../SettingsItem.vue';
|
||||
import AppsModel from '../../models/AppsModel.js';
|
||||
import BackupSitesModel from '../../models/BackupSitesModel.js';
|
||||
import BackupsModel from '../../models/BackupsModel.js';
|
||||
import TasksModel from '../../models/TasksModel.js';
|
||||
import { TASK_TYPES } from '../../constants.js';
|
||||
import BackupInfoDialog from '../BackupInfoDialog.vue';
|
||||
@@ -21,6 +22,7 @@ import ActionBar from '../../components/ActionBar.vue';
|
||||
|
||||
const appsModel = AppsModel.create();
|
||||
const backupSitesModel = BackupSitesModel.create();
|
||||
const backupsModel = BackupsModel.create();
|
||||
const tasksModel = TasksModel.create();
|
||||
|
||||
const props = defineProps([ 'app' ]);
|
||||
@@ -47,6 +49,11 @@ const columns = ref({
|
||||
label: t('main.table.version'),
|
||||
sort: true,
|
||||
},
|
||||
integrity: {
|
||||
label: 'Integrity',
|
||||
sort: false,
|
||||
width: '100px',
|
||||
},
|
||||
actions: {
|
||||
label: '',
|
||||
sort: false,
|
||||
@@ -90,13 +97,13 @@ function createActionMenu(backup) {
|
||||
disabled: !!props.app.taskId || props.app.runState === 'stopped',
|
||||
action: onRestore.bind(null, backup),
|
||||
quickAction: true
|
||||
// }, {
|
||||
// separator: true,
|
||||
// }, {
|
||||
// icon: 'fa-solid fa-key',
|
||||
// label: t('app.backups.backups.checkIntegrity'),
|
||||
// visible: props.app.accessLevel === 'admin',
|
||||
// action: onCheckIntegrity.bind(null, backup),
|
||||
}, {
|
||||
separator: true,
|
||||
}, {
|
||||
icon: 'fa-solid fa-key',
|
||||
label: t('backups.checkIntegrity'),
|
||||
visible: props.app.accessLevel === 'admin',
|
||||
action: onCheckIntegrity.bind(null, backup),
|
||||
}];
|
||||
}
|
||||
|
||||
@@ -233,11 +240,31 @@ async function onRestore(backup) {
|
||||
restoreDialog.value.open();
|
||||
}
|
||||
|
||||
// const backupsModel = BackupsModel.create();
|
||||
const integrityTasks = ref({});
|
||||
let integrityPollTimer = null;
|
||||
|
||||
// async function onCheckIntegrity(backup) {
|
||||
// await backupsModel.checkIntegrity(backup.id);
|
||||
// }
|
||||
async function refreshIntegrityTasks() {
|
||||
for (const taskId of Object.keys(integrityTasks.value)) {
|
||||
const [error, result] = await tasksModel.get(taskId);
|
||||
if (error) continue;
|
||||
integrityTasks.value[taskId] = result;
|
||||
if (!result.active) delete integrityTasks.value[taskId];
|
||||
}
|
||||
|
||||
const stillActive = Object.keys(integrityTasks).length !== 0;
|
||||
if (stillActive) {
|
||||
integrityPollTimer = setTimeout(refreshIntegrityTasks, 10000);
|
||||
} else {
|
||||
integrityPollTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
async function onCheckIntegrity(backup) {
|
||||
const [error, taskId] = await backupsModel.checkIntegrity(backup.id);
|
||||
if (error) return window.cloudron.onError(error);
|
||||
integrityTasks.value[taskId] = {};
|
||||
await refreshIntegrityTasks();
|
||||
}
|
||||
|
||||
async function onRestoreSubmit() {
|
||||
restoreBusy.value = true;
|
||||
@@ -265,9 +292,10 @@ async function refreshBackupList() {
|
||||
const [error, result] = await appsModel.backups(props.app.id);
|
||||
if (error) return console.error(error);
|
||||
|
||||
result.forEach(backup => {
|
||||
for (const backup of result) {
|
||||
backup.site = backupSites.value.find(t => t.id === backup.siteId);
|
||||
});
|
||||
if (backup.integrityCheckTaskId) integrityTasks.value[backup.integrityCheckTaskId] = {};
|
||||
}
|
||||
backups.value = result;
|
||||
}
|
||||
|
||||
@@ -287,10 +315,15 @@ onMounted(async () => {
|
||||
|
||||
await refreshBackupList();
|
||||
await refreshTasks();
|
||||
await refreshIntegrityTasks();
|
||||
|
||||
busy.value = false;
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (integrityPollTimer) clearTimeout(integrityPollTimer);
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -401,6 +434,14 @@ onMounted(async () => {
|
||||
<template #size="backup">
|
||||
<span v-if="backup.stats?.upload">{{ prettyFileSize(backup.stats.upload.size) }} - {{ backup.stats.upload.fileCount }} file(s)</span>
|
||||
</template>
|
||||
<template #integrity="backup">
|
||||
<Spinner v-if="backup.integrityCheckTaskId && integrityTasks[backup.integrityCheckTaskId]" style="min-width: 0;"/>
|
||||
<div v-else-if="backup.lastIntegrityCheckTime" style="display: flex; align-items: center;">
|
||||
<i v-if="backup.integrityCheckStatus === 'passed'" class="fa-solid fa-check-circle" v-tooltip="prettyLongDate(backup.lastIntegrityCheckTime)"></i>
|
||||
<i v-else class="fa-solid fa-times-circle" v-tooltip="prettyLongDate(backup.lastIntegrityCheckTime)"></i>
|
||||
</div>
|
||||
<div v-else>-</div>
|
||||
</template>
|
||||
<template #actions="backup">
|
||||
<ActionBar :actions="createActionMenu(backup)"/>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user