Files
cloudron-box/dashboard/src/views/AppArchiveView.vue
T

145 lines
4.2 KiB
Vue
Raw Normal View History

<script setup>
import { useI18n } from 'vue-i18n';
const i18n = useI18n();
const t = i18n.t;
import { ref, onMounted, useTemplateRef } from 'vue';
2025-12-20 09:06:32 +01:00
import { TableView, InputDialog } from '@cloudron/pankow';
import { prettyLongDate } from '@cloudron/pankow/utils';
import { API_ORIGIN } from '../constants.js';
import AppRestoreDialog from '../components/AppRestoreDialog.vue';
2025-12-20 09:06:32 +01:00
import ActionBar from '../components/ActionBar.vue';
import Section from '../components/Section.vue';
import ArchivesModel from '../models/ArchivesModel.js';
import { download } from '../utils.js';
2025-10-06 22:31:17 +02:00
import BackupSitesModel from '../models/BackupSitesModel.js';
const archivesModel = ArchivesModel.create();
2025-10-06 22:31:17 +02:00
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: {
2026-01-13 16:32:47 +01:00
label: t('main.table.created'),
sort: true,
hideMobile: true,
},
actions: {
width: '100px',
}
};
2025-12-20 09:06:32 +01:00
function createActionMenu(archive) {
return [{
icon: 'fa-solid fa-history',
label: t('backups.restoreArchiveDialog.restoreAction'),
2025-12-20 09:06:32 +01:00
quickAction: true,
action: onRestore.bind(null, archive),
}, {
2025-09-18 18:32:06 +02:00
separator: true,
},{
icon: 'fa-solid fa-file-alt',
label: t('backups.listing.tooltipDownloadBackupConfig'),
action: onDownloadConfig.bind(null, archive),
}, {
2025-09-18 18:32:06 +02:00
separator: true,
},{
icon: 'fa-solid fa-trash-alt',
label: t('main.action.remove'),
action: onRemove.bind(null, archive),
}];
}
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({
2025-11-07 18:35:34 +01:00
title: t('backups.deleteArchiveDialog.title'),
message: t('backups.deleteArchiveDialog.description', { appTitle: archive.appConfig?.manifest?.title, appFqdn: archive.appConfig?.fqdn }),
confirmStyle: 'danger',
confirmLabel: t('backups.deleteArchive.deleteAction'),
2025-11-07 18:35:34 +01:00
rejectLabel: t('main.dialog.cancel'),
rejectStyle: 'secondary'
});
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) {
2025-10-06 22:31:17 +02:00
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`;
2025-10-06 22:31:17 +02:00
download(filename, JSON.stringify(backupConfig, null, 4));
}
onMounted(async () => {
await refreshArchives();
busy.value = false;
});
</script>
<template>
<div class="content">
<Section :title="$t('backups.archives.title')">
<InputDialog ref="inputDialog"/>
<AppRestoreDialog ref="restoreDialog"/>
2025-08-06 20:39:10 +02:00
<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">
2025-12-20 09:06:32 +01:00
<ActionBar :actions="createActionMenu(archive)"/>
</template>
</TableView>
</Section>
</div>
</template>