From 1043d4057b443e19cb47f90d7237d00d327235db Mon Sep 17 00:00:00 2001 From: ReaperOfVeriod Date: Sun, 25 Oct 2026 05:54:58 +0100 Subject: [PATCH] Add authenticated backup restore control --- src/App.svelte | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/App.svelte b/src/App.svelte index b51a9a0..d1fee48 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -81,6 +81,7 @@ let backups: Array<{ filename: string; size_bytes: number; modified_at: string | null }> = []; let backupError = ''; let backupBusy = false; + let restoreBusy = false; let activeView = 'Live dashboard'; let sessionToken = ''; let authenticated = false; @@ -320,6 +321,26 @@ } } + async function restoreBackup(filename: string) { + if (!window.confirm(`Restore ${filename}? This replaces the current database and cannot be undone.`)) return; + restoreBusy = true; + backupError = ''; + try { + const response = await fetch(`${serverAddress}/api/v1/admin/backups/restore`, { + method: 'POST', + headers: { 'content-type': 'application/json', authorization: `Bearer ${sessionToken}` }, + body: JSON.stringify({ filename, confirmation: 'RESTORE' }), + }); + const result: { status?: string; reason?: string | null } = await response.json(); + if (!response.ok || result.status !== 'ok') throw new Error(result.reason || 'Database restore failed.'); + backupError = 'Database restored. Restart the server before continuing to use the dashboard.'; + } catch (error) { + backupError = error instanceof Error ? error.message : 'Database restore failed.'; + } finally { + restoreBusy = false; + } + } + async function replaceCredentials() { authBusy = true; authError = ''; @@ -1236,7 +1257,7 @@ {:else if activeView === 'Overlays'}
OVERLAYS
{#if selectedOverlayId || overlayName}
LIVE PREVIEW
{#if overlayError}
{overlayError}
{/if}{:else}
Select or create an overlayDrafts autosave when explicitly saved; publishing controls the browser source.
{/if}
{:else if activeView === 'Settings'} -
SERVER SETTINGS
DATABASE BACKUPS

Daily backups use the server's local schedule. Restore remains a destructive recovery workflow and is not started from this list.

{#if backupError}
{backupError}
{/if}{#if backups.length}
{#each backups as backup}
{backup.filename}{Math.round(backup.size_bytes / 1024)} KiB{backup.modified_at ? new Date(backup.modified_at).toLocaleString() : 'UNKNOWN TIME'}
{/each}
{:else}
No backup archives are available.
{/if}
+
SERVER SETTINGS
DATABASE BACKUPS

Daily backups use the server's local schedule. Restore replaces the current database and is permanently destructive.

{#if backupError}
{backupError}
{/if}{#if backups.length}
{#each backups as backup}
{backup.filename}{Math.round(backup.size_bytes / 1024)} KiB{backup.modified_at ? new Date(backup.modified_at).toLocaleString() : 'UNKNOWN TIME'}
{/each}
{:else}
No backup archives are available.
{/if}
{:else}
STREAM PREVIEW⋮
{#if playerSrc}{:else}
STREAM OFFLINE
CONNECT TWITCH TO BEGIN
{/if}