148 lines
4.5 KiB
Vue
148 lines
4.5 KiB
Vue
<script setup>
|
|
|
|
import { useI18n } from 'vue-i18n';
|
|
const i18n = useI18n();
|
|
const t = i18n.t;
|
|
|
|
import { ref, onMounted, useTemplateRef } from 'vue';
|
|
import { Button, Menu, TableView, InputDialog } from '@cloudron/pankow';
|
|
import { prettyLongDate } from '@cloudron/pankow/utils';
|
|
import { API_ORIGIN } from '../constants.js';
|
|
import AppRestoreDialog from '../components/AppRestoreDialog.vue';
|
|
import Section from '../components/Section.vue';
|
|
import ArchivesModel from '../models/ArchivesModel.js';
|
|
import { download } from '../utils.js';
|
|
import BackupSitesModel from '../models/BackupSitesModel.js';
|
|
|
|
const archivesModel = ArchivesModel.create();
|
|
const backupSitesModel = BackupSitesModel.create();
|
|
|
|
const columns = {
|
|
icon: {}, // archived
|
|
location: {
|
|
label: t('app.location.location'),
|
|
sort: true
|
|
},
|
|
info: {
|
|
label: t('backups.archives.info'),
|
|
sort: false,
|
|
hideMobile: true,
|
|
},
|
|
creationTime: {
|
|
label: t('main.table.date'),
|
|
sort: true,
|
|
hideMobile: true,
|
|
},
|
|
actions: {}
|
|
};
|
|
|
|
const actionMenuModel = ref([]);
|
|
const actionMenuElement = useTemplateRef('actionMenuElement');
|
|
function onActionMenu(archive, event) {
|
|
actionMenuModel.value = [{
|
|
icon: 'fa-solid fa-history',
|
|
label: t('backups.restoreArchiveDialog.restoreAction'),
|
|
action: onRestore.bind(null, archive),
|
|
}, {
|
|
separator: true,
|
|
},{
|
|
icon: 'fa-solid fa-file-alt',
|
|
label: t('backups.listing.tooltipDownloadBackupConfig'),
|
|
action: onDownloadConfig.bind(null, archive),
|
|
}, {
|
|
separator: true,
|
|
},{
|
|
icon: 'fa-solid fa-trash-alt',
|
|
label: t('main.action.remove'),
|
|
action: onRemove.bind(null, archive),
|
|
}];
|
|
|
|
actionMenuElement.value.open(event, event.currentTarget);
|
|
}
|
|
|
|
const busy = ref(true);
|
|
const archives = ref([]);
|
|
|
|
async function refreshArchives() {
|
|
const [error, result] = await archivesModel.list();
|
|
if (error) return console.error(error);
|
|
|
|
// ensure we use the full api oprigin
|
|
result.forEach(a => {
|
|
a.iconUrl = API_ORIGIN + a.iconUrl;
|
|
});
|
|
|
|
archives.value = result;
|
|
}
|
|
|
|
const inputDialog = useTemplateRef('inputDialog');
|
|
async function onRemove(archive) {
|
|
const yes = await inputDialog.value.confirm({
|
|
title: t('backups.deleteArchiveDialog.title', { appTitle: archive.appConfig?.manifest?.title, fqdn: archive.appConfig?.fqdn }),
|
|
message: t('backups.deleteArchiveDialog.description'),
|
|
confirmStyle: 'danger',
|
|
confirmLabel: t('backups.deleteArchive.deleteAction'),
|
|
rejectLabel: t('main.dialog.cancel')
|
|
});
|
|
|
|
if (!yes) return;
|
|
|
|
const [error] = await archivesModel.remove(archive.id);
|
|
if (error) return console.error(error);
|
|
|
|
await refreshArchives();
|
|
}
|
|
|
|
const restoreDialog = useTemplateRef('restoreDialog');
|
|
|
|
async function onRestore(archive) {
|
|
restoreDialog.value.open(archive);
|
|
}
|
|
|
|
async function onDownloadConfig(archive) {
|
|
const [backupConfigError, backupConfig] = await backupSitesModel.generateBackupConfig(archive);
|
|
if (backupConfigError) return console.error(backupConfigError);
|
|
|
|
const filename = `${archive.appConfig.fqdn}-archive-config-${(new Date(archive.creationTime)).toISOString().split('T')[0]}.json`;
|
|
download(filename, JSON.stringify(backupConfig, null, 4));
|
|
}
|
|
|
|
onMounted(async () => {
|
|
await refreshArchives();
|
|
busy.value = false;
|
|
});
|
|
|
|
</script>
|
|
|
|
<template>
|
|
<div class="content">
|
|
<Menu ref="actionMenuElement" :model="actionMenuModel" />
|
|
|
|
<Section :title="$t('backups.archives.title')">
|
|
<InputDialog ref="inputDialog"/>
|
|
<AppRestoreDialog ref="restoreDialog"/>
|
|
|
|
<TableView :columns="columns" :model="archives" :busy="busy" :placeholder="$t('archives.listing.placeholder')">
|
|
<template #icon="archive">
|
|
<img :src="archive.iconUrl || 'img/appicon_fallback.png'" v-fallback-image="API_ORIGIN + '/img/appicon_fallback.png'" height="24" width="24"/>
|
|
</template>
|
|
|
|
<!-- for pre-8.2 backups, appConfig can be null -->
|
|
<template #location="archive">{{ archive.appConfig ? archive.appConfig.fqdn : '-' }}</template>
|
|
|
|
<template #info="archive">
|
|
<span v-tooltip="`${archive.manifest.id}@${archive.manifest.version}`">{{ archive.manifest.title }}</span>
|
|
</template>
|
|
|
|
<template #creationTime="archive">{{ prettyLongDate(archive.creationTime) }}</template>
|
|
|
|
<template #actions="archive">
|
|
<div style="text-align: right;">
|
|
<Button tool plain secondary @click.capture="onActionMenu(archive, $event)" icon="fa-solid fa-ellipsis" />
|
|
</div>
|
|
</template>
|
|
</TableView>
|
|
</Section>
|
|
</div>
|
|
</template>
|