diff --git a/dist/backupper-2026.06.14.1-x86_64-1.txz.sha256 b/dist/backupper-2026.06.14.1-x86_64-1.txz.sha256 deleted file mode 100644 index 566d67c..0000000 --- a/dist/backupper-2026.06.14.1-x86_64-1.txz.sha256 +++ /dev/null @@ -1 +0,0 @@ -29655e71df818c71341058d031570f204d423eea63de4e199190c44b217343cc backupper-2026.06.14.1-x86_64-1.txz diff --git a/dist/backupper-2026.06.14.1-x86_64-1.txz b/dist/backupper-2026.06.14.2-x86_64-1.txz similarity index 55% rename from dist/backupper-2026.06.14.1-x86_64-1.txz rename to dist/backupper-2026.06.14.2-x86_64-1.txz index 60d1d8e..953146a 100644 Binary files a/dist/backupper-2026.06.14.1-x86_64-1.txz and b/dist/backupper-2026.06.14.2-x86_64-1.txz differ diff --git a/dist/backupper-2026.06.14.2-x86_64-1.txz.sha256 b/dist/backupper-2026.06.14.2-x86_64-1.txz.sha256 new file mode 100644 index 0000000..1fe5655 --- /dev/null +++ b/dist/backupper-2026.06.14.2-x86_64-1.txz.sha256 @@ -0,0 +1 @@ +5da36c3d720851d8ebd5e905399d1a9deca056128d2e01ee3f17a61f7145c807 backupper-2026.06.14.2-x86_64-1.txz diff --git a/dist/backupper.plg b/dist/backupper.plg index bd06b3d..3854d97 100644 --- a/dist/backupper.plg +++ b/dist/backupper.plg @@ -2,13 +2,16 @@ - + - + ]> +### 2026.06.14.2 +- Always show backup browser roots, display folder loading errors inline, and expose the running daemon version. + ### 2026.06.14.1 - Match the backup source browser layout and controls to the snapshot file browser. diff --git a/plugin/backupper.plg b/plugin/backupper.plg index 03c02d2..9b05a55 100644 --- a/plugin/backupper.plg +++ b/plugin/backupper.plg @@ -2,13 +2,16 @@ - + ]> +### 2026.06.14.2 +- Always show backup browser roots, display folder loading errors inline, and expose the running daemon version. + ### 2026.06.14.1 - Match the backup source browser layout and controls to the snapshot file browser. diff --git a/webgui/Backupper.page b/webgui/Backupper.page index f4b9a22..771fa5d 100644 --- a/webgui/Backupper.page +++ b/webgui/Backupper.page @@ -9,10 +9,10 @@ Tag="backup restic snapshots restore" - +
-

Backupper 2026.06.14.1

Encrypted Restic backups for Unraid

+

Backupper 2026.06.14.2

Encrypted Restic backups for Unraid

Connecting...
- + diff --git a/webgui/assets/backupper.css b/webgui/assets/backupper.css index 4d51f25..d3c2e56 100644 --- a/webgui/assets/backupper.css +++ b/webgui/assets/backupper.css @@ -69,6 +69,7 @@ .bu-selection { background: var(--bu-panel-alt); border: 1px solid var(--bu-border); border-radius: 999px; color: var(--bu-text); font-size: 12px; padding: 4px 8px; } .bu-browser-card input[type="checkbox"], .bu-table input[type="checkbox"] { accent-color: var(--bu-accent); height: 20px; width: 20px; } .bu-count { align-items: center; border: 1px solid var(--bu-border); border-radius: 999px; display: inline-flex; font-size: 13px; font-weight: 700; padding: 7px 11px; } +.bu-inline-error { background: rgba(180,35,47,.08); border: 1px solid var(--bu-bad); border-radius: 5px; color: var(--bu-bad); font-weight: 600; margin: 10px 0; padding: 10px 12px; } #backupper-app .bu-button { appearance: none; diff --git a/webgui/assets/backupper.js b/webgui/assets/backupper.js index 9ad1954..d77f98b 100644 --- a/webgui/assets/backupper.js +++ b/webgui/assets/backupper.js @@ -6,7 +6,8 @@ const health = document.getElementById('bu-health'); const toast = document.getElementById('bu-toast'); const tooltip = document.getElementById('bu-tooltip'); - const state = { config: null, runs: [], view: 'dashboard', snapshots: [], selectedSnapshot: null, snapshotRepo: null, files: [], browserPath: '/', restoreIncludes: [], backupSelections: [], sourceBrowserPath: '/', sourceDirectories: [] }; + const sourceRoots = ['/mnt/user','/mnt/disks','/mnt/remotes','/boot'].map(path=>({name:path,path})); + const state = { config: null, runs: [], view: 'dashboard', snapshots: [], selectedSnapshot: null, snapshotRepo: null, files: [], browserPath: '/', restoreIncludes: [], backupSelections: [], sourceBrowserPath: '/', sourceDirectories: sourceRoots, sourceBrowserError: '' }; const fieldHelp = { name: 'Frei wählbarer Anzeigename. Beispiel: Appdata täglich oder VM Home Assistant.', @@ -183,9 +184,9 @@ async function load() { try { - const [config, runs] = await Promise.all([api('/v1/config'), api('/v1/runs')]); + const [config, runs, daemon] = await Promise.all([api('/v1/config'), api('/v1/runs'), api('/v1/health')]); state.config = config; state.runs = runs; - health.textContent = 'Daemon online'; health.className = 'bu-health ok'; render(); + health.textContent = `Daemon online (${daemon.version || 'unknown'})`; health.className = 'bu-health ok'; render(); } catch (error) { health.textContent = 'Daemon unavailable'; health.className = 'bu-health bad'; content.innerHTML = `

Connection failed

${esc(error.message)}

`; @@ -219,7 +220,7 @@ function jobForm(job = {}) { state.backupSelections = (job.sources || []).filter(s=>s.path).map(s=>s.path); const source = (job.sources || []).filter(s=>s.workloadId).map(s=>`@${s.workloadId}`).join('\n'); - state.sourceBrowserPath = '/'; state.sourceDirectories = []; + state.sourceBrowserPath = '/'; state.sourceDirectories = sourceRoots; state.sourceBrowserError = ''; const options = state.config.repositories.map(r => ``).join(''); content.innerHTML = `

${job.id ? 'Edit' : 'New'} backup job

@@ -234,7 +235,7 @@
${button('Save','submit-job','primary')}${button('Cancel','cancel-form')}
`; enhanceTooltips(content); - loadSourceDirectories('/'); + renderSourceBrowser(); } function renderSourceBrowser() { @@ -243,17 +244,23 @@ const selected = new Set(state.backupSelections); const rows = state.sourceDirectories.map(item => `dir${esc(item.name)}${esc(item.path)}${button('Open',`open-source-dir:${encodeURIComponent(item.path)}`)}`).join(''); const chosen = state.backupSelections.length ? state.backupSelections.map(path=>`${esc(path)}`).join('') : 'No folders selected.'; - host.innerHTML = `

Backup source browser

${esc(state.sourceBrowserPath)}
${state.sourceBrowserPath!=='/'?button('Select current folder','select-current-source'):''}${state.sourceBrowserPath!=='/'?button('Up','source-up'):''}Selected: ${state.backupSelections.length}
${chosen}
${rows||''}
SelectTypeNamePath
No subfolders found.
`; + const error = state.sourceBrowserError ? `
${esc(state.sourceBrowserError)}
` : ''; + host.innerHTML = `

Backup source browser

${esc(state.sourceBrowserPath)}
${state.sourceBrowserPath!=='/'?button('Select current folder','select-current-source'):''}${state.sourceBrowserPath!=='/'?button('Up','source-up'):''}Selected: ${state.backupSelections.length}
${error}
${chosen}
${rows||''}
SelectTypeNamePath
No subfolders found.
`; enhanceTooltips(host); } async function loadSourceDirectories(path) { + if (path === '/') { + state.sourceDirectories = sourceRoots; state.sourceBrowserPath = '/'; state.sourceBrowserError = ''; renderSourceBrowser(); return; + } try { state.sourceDirectories = await api(`/v1/filesystem/directories?path=${encodeURIComponent(path)}`); state.sourceBrowserPath = path; + state.sourceBrowserError = ''; renderSourceBrowser(); } catch (error) { - notify(error.message, true); + state.sourceBrowserError = `Folder could not be loaded: ${error.message}`; + renderSourceBrowser(); } }