Files
nextcloud-ng_backup/src/components/Bin.vue
T
dillard 36a30732ad Without system cron: warning and a backup that moves on while the page is open (#13); progress of the database phase; Bin tab
- The admin page and backup:status warn when background jobs do not run on a
  system cron, or the cron stopped, with a link to how to set one up.
- Without a working system cron, the open admin page moves a running backup
  forward through /api/tick, a short step per poll.
- The database phase shows the tables dumped ("db: 47/183 tables"); a backup
  that has not moved for 15 minutes says it is waiting for background jobs.
- The trash of restore points is on a tab of its own, Bin.
- New screenshots; changelog and plan for 1.0.2.
2026-11-04 04:08:20 +01:00

94 lines
4.1 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!--
- SPDX-FileCopyrightText: 2026 Dillard Blom
- SPDX-License-Identifier: AGPL-3.0-or-later
-->
<template>
<NcSettingsSection :name="t('ng_backup', 'Bin')"
:description="t('ng_backup', 'Restore points that were removed, most of them by the retention policy. They can be brought back until they are deleted permanently.')">
<NcNoteCard v-if="error" type="error">{{ error }}</NcNoteCard>
<NcLoadingIcon v-if="loading" :size="32" />
<NcNoteCard v-else-if="empty" type="info">{{ t('ng_backup', 'The bin is empty.') }}</NcNoteCard>
<template v-for="target in status.targets" :key="'trash' + target.id">
<div v-if="trash[target.id]?.length" class="ngb-trash">
<h3>{{ status.targets.length > 1 ? target.name : t('ng_backup', 'Removed restore points') }}</h3>
<ul>
<li v-for="item in trash[target.id]" :key="item.id">
{{ item.time ? formatDateTime(item.time) : item.id }}{{ item.label ? ' – ' + item.label : '' }}
<span class="ngb-muted">{{ t('ng_backup', 'removed by {by}, deleted after {date}', { by: item.by, date: formatDate(item.deleteAfter) }) }}</span>
<button v-if="item.by === RETENTION"
class="ngb-info"
:aria-expanded="retentionInfo ? 'true' : 'false'"
:aria-label="t('ng_backup', 'What does this mean?')"
:title="t('ng_backup', 'What does this mean?')"
@click="retentionInfo = !retentionInfo">
ⓘ
</button>
<NcButton variant="tertiary" @click="untrash(target, item)">{{ t('ng_backup', 'Restore') }}</NcButton>
</li>
</ul>
<NcNoteCard v-if="retentionInfo && trash[target.id].some(item => item.by === RETENTION)" type="info">
{{ n('ng_backup',
'The retention policy keeps the newest restore points, and for older days, weeks and months only the last restore point of each. It moves the others to this bin, where they stay for %n day before they are deleted permanently. Until then you can bring one back with "Restore". Nothing was lost by accident.',
'The retention policy keeps the newest restore points, and for older days, weeks and months only the last restore point of each. It moves the others to this bin, where they stay for %n days before they are deleted permanently. Until then you can bring one back with "Restore". Nothing was lost by accident.',
status.key.deleteDelayDays) }}
</NcNoteCard>
</div>
</template>
</NcSettingsSection>
</template>
<script setup>
import { computed, reactive, ref, watch } from 'vue'
import { n, t } from '@nextcloud/l10n'
import { confirmPassword } from '@nextcloud/password-confirmation'
import NcButton from '@nextcloud/vue/components/NcButton'
import NcLoadingIcon from '@nextcloud/vue/components/NcLoadingIcon'
import NcNoteCard from '@nextcloud/vue/components/NcNoteCard'
import NcSettingsSection from '@nextcloud/vue/components/NcSettingsSection'
import api from '../api.js'
import { formatDate, formatDateTime } from '../format.js'
const props = defineProps({ status: { type: Object, required: true } })
const emit = defineEmits(['changed'])
// "by" of a restore point the weekly cleanup removed (PruneService)
const RETENTION = 'retention policy'
const retentionInfo = ref(false)
const trash = reactive({})
const loading = ref(true)
const error = ref('')
const empty = computed(() => props.status.targets.every(target => !trash[target.id]?.length))
async function loadTrash() {
for (const target of props.status.targets) {
try {
trash[target.id] = await api.trash(target.id)
} catch (e) {
trash[target.id] = []
}
}
loading.value = false
}
watch(() => props.status.targets.map(tg => tg.id).join(','), loadTrash, { immediate: true })
async function untrash(target, item) {
error.value = ''
try {
await confirmPassword()
await api.untrash(target.id, item.id)
await loadTrash()
emit('changed')
} catch (e) {
error.value = e?.message || ''
}
}
</script>
<style scoped>
.ngb-info { background: none; border: none; padding: 0 4px; min-height: auto; cursor: pointer; color: var(--color-primary-element); }
.ngb-muted { color: var(--color-text-maxcontrast); }
.ngb-trash { margin: 12px 0; }
.ngb-trash h3 { font-weight: bold; }
</style>