Admin UI: restore point dates and a settings/daily-use split
Show the date of the restore point a restore run used, both in the backup history table and when confirming a restore. Split the admin page into a "Backup and restore" tab (locations, backups, restore) and a "Settings" tab (key, slots, recovery kit), so daily work no longer sits behind the key setup at the top of the page. Signed-off-by: Dillard Blom <dillard.blom@ensembia.com>
This commit is contained in:
+1
-1
@@ -5,7 +5,7 @@
|
||||
<name>NG Backup</name>
|
||||
<summary>Encrypted, incremental backups of your Nextcloud with restore from the web interface</summary>
|
||||
<description><![CDATA[Work in progress, not ready for use.]]></description>
|
||||
<version>0.0.18</version>
|
||||
<version>0.0.19</version>
|
||||
<licence>agpl</licence>
|
||||
<author>Dillard Blom</author>
|
||||
<namespace>NgBackup</namespace>
|
||||
|
||||
@@ -270,8 +270,17 @@ class AdminApiController extends Controller {
|
||||
}
|
||||
|
||||
private function runJson(Run $r): array {
|
||||
$snapshot = null;
|
||||
if ($r->getSnapshotId() !== null) {
|
||||
try {
|
||||
$s = $this->snapshots->findOne($r->getTargetId(), $r->getSnapshotId());
|
||||
$snapshot = ['createdAt' => $s->getCreatedAt(), 'label' => $s->getLabel()];
|
||||
} catch (\Throwable) {
|
||||
// Snapshot already pruned or trashed: the run keeps its id, just not the date.
|
||||
}
|
||||
}
|
||||
return ['id' => $r->getId(), 'targetId' => $r->getTargetId(), 'kind' => $r->getKind(), 'status' => $r->getStatus(),
|
||||
'phase' => $r->getPhase(), 'snapshotId' => $r->getSnapshotId(), 'startedAt' => $r->getStartedAt(),
|
||||
'phase' => $r->getPhase(), 'snapshotId' => $r->getSnapshotId(), 'snapshot' => $snapshot, 'startedAt' => $r->getStartedAt(),
|
||||
'updatedAt' => $r->getUpdatedAt(), 'finishedAt' => $r->getFinishedAt(), 'error' => $r->getError(),
|
||||
'stats' => json_decode($r->getStats() ?? 'null', true)];
|
||||
}
|
||||
|
||||
+24
-2
@@ -11,12 +11,20 @@
|
||||
</NcSettingsSection>
|
||||
|
||||
<template v-if="status">
|
||||
<KeySetup :status="status" @changed="refresh" />
|
||||
<template v-if="status.key.everConfirmed">
|
||||
<div class="ngb-tabs">
|
||||
<NcButton :variant="tab === 'backup' ? 'primary' : 'secondary'" @click="tab = 'backup'">{{ t('ng_backup', 'Backup and restore') }}</NcButton>
|
||||
<NcButton :variant="tab === 'settings' ? 'primary' : 'secondary'" @click="tab = 'settings'">{{ t('ng_backup', 'Settings') }}</NcButton>
|
||||
</div>
|
||||
|
||||
<template v-if="tab === 'settings'">
|
||||
<KeySetup :status="status" @changed="refresh" />
|
||||
</template>
|
||||
<template v-else-if="status.key.everConfirmed">
|
||||
<Locations :status="status" @changed="refresh" />
|
||||
<Backups v-if="status.targets.length" :status="status" @changed="refresh" />
|
||||
<RestoreBrowser v-if="status.targets.length" :status="status" @changed="refresh" />
|
||||
</template>
|
||||
<NcNoteCard v-else type="warning">{{ t('ng_backup', 'Set up the backup key under "Settings" first.') }}</NcNoteCard>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
@@ -24,6 +32,7 @@
|
||||
<script setup>
|
||||
import { onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
import { t } from '@nextcloud/l10n'
|
||||
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'
|
||||
@@ -35,12 +44,20 @@ import RestoreBrowser from './components/RestoreBrowser.vue'
|
||||
|
||||
const status = ref(null)
|
||||
const error = ref('')
|
||||
const tab = ref('settings')
|
||||
let tabInitialized = false
|
||||
let timer = null
|
||||
|
||||
async function refresh() {
|
||||
try {
|
||||
status.value = await api.status()
|
||||
error.value = ''
|
||||
// Default to the daily-use tab once the key is set up; stay on Settings otherwise.
|
||||
// Only decided on first load, so switching tabs ourselves isn't undone by polling.
|
||||
if (!tabInitialized) {
|
||||
tab.value = status.value.key.everConfirmed ? 'backup' : 'settings'
|
||||
tabInitialized = true
|
||||
}
|
||||
} catch (e) {
|
||||
error.value = e.message
|
||||
}
|
||||
@@ -59,4 +76,9 @@ onBeforeUnmount(() => clearTimeout(timer))
|
||||
.ng-backup :deep(.settings-section) {
|
||||
max-width: 900px;
|
||||
}
|
||||
.ngb-tabs {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin: 0 0 12px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
<th>{{ t('ng_backup', 'Started') }}</th>
|
||||
<th>{{ t('ng_backup', 'Location') }}</th>
|
||||
<th>{{ t('ng_backup', 'Kind') }}</th>
|
||||
<th>{{ t('ng_backup', 'Restore point') }}</th>
|
||||
<th>{{ t('ng_backup', 'Status') }}</th>
|
||||
<th>{{ t('ng_backup', 'Files') }}</th>
|
||||
<th>{{ t('ng_backup', 'Uploaded') }}</th>
|
||||
@@ -46,6 +47,7 @@
|
||||
<td>{{ new Date(run.startedAt * 1000).toLocaleString() }}</td>
|
||||
<td>{{ targetName(run.targetId) }}</td>
|
||||
<td>{{ run.kind }}</td>
|
||||
<td>{{ run.snapshot ? new Date(run.snapshot.createdAt * 1000).toLocaleString() + (run.snapshot.label ? ' – ' + run.snapshot.label : '') : '' }}</td>
|
||||
<td :class="'ngb-' + run.status">{{ run.status }}{{ run.status === 'running' ? ' (' + run.phase + ')' : '' }}{{ run.error ? ': ' + run.error : '' }}</td>
|
||||
<td>{{ run.stats?.files?.files ?? run.stats?.restored ?? '' }}</td>
|
||||
<td>{{ run.stats?.files ? formatSize(run.stats.files.uploaded) : '' }}</td>
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
<NcButton v-if="restorable(path) && path !== selected" variant="tertiary" @click="selected = path">{{ t('ng_backup', 'Select this folder') }}</NcButton>
|
||||
|
||||
<div v-if="selected" class="ngb-restore">
|
||||
<p>{{ t('ng_backup', 'Selected: {path}', { path: selected }) }}</p>
|
||||
<p>{{ t('ng_backup', 'Selected: {path}, from the restore point of {date}', { path: selected, date: snapshotLabel(snapshot) }) }}</p>
|
||||
<NcCheckboxRadioSwitch v-for="m in modes" :key="m.id" v-model="mode" :value="m.id" type="radio" name="ngb-mode">{{ m.label }}</NcCheckboxRadioSwitch>
|
||||
<NcButton variant="primary" :disabled="busy" @click="restore">{{ t('ng_backup', 'Restore') }}</NcButton>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user