From 2d5674108bd4a546dc6b71b749a60dc0e9551b54 Mon Sep 17 00:00:00 2001 From: librelad Date: Fri, 28 Aug 2026 13:09:43 +0100 Subject: [PATCH] setup: registering a drive made it disappear from the Storage step MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit storage.json carries two lists: `candidates`, drives that could be added, and `locations`, the ones already registered. The wizard read only the first. So a drive vanished from Storage the moment it was registered — the step fell back to "Only one drive found, so everything goes here" on a box with three, and because the two root dropdowns only render when there is more than one option, the choice they exist to offer disappeared with it. A registered location is the clearest case of a usable drive there is. Read both lists, deduplicated by path since one can appear in both while a registration settles. The generator's location entries carried no size or free figures either, so those cards rendered as "free of" with both numbers missing next to a system disk that had them. They now carry size, free, fstype and used_pct like the system entry, and the card shows the name the user chose rather than the raw path. Found by the flow test: three registered locations, three apps placed across them, and a Storage step insisting there was one drive. Co-Authored-By: Claude Opus 5 --- .../frontend/core/setup/js/setup-wizard.js | 18 ++++++++++++++++-- .../system/webui_storage_candidates.sh | 14 +++++++++++++- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/containers/libreportal/frontend/core/setup/js/setup-wizard.js b/containers/libreportal/frontend/core/setup/js/setup-wizard.js index 9836123..c63626a 100755 --- a/containers/libreportal/frontend/core/setup/js/setup-wizard.js +++ b/containers/libreportal/frontend/core/setup/js/setup-wizard.js @@ -478,7 +478,19 @@ class SetupWizard { const res = await fetch('/data/system/storage.json', { cache: 'no-store' }); if (!res.ok) throw new Error(`HTTP ${res.status}`); const data = await res.json(); - this.storageCandidates = Array.isArray(data.candidates) ? data.candidates : []; + // Both halves of the feed. `candidates` is drives that could be added; + // `locations` is the ones already registered — which the step showed as + // nothing at all, so registering a drive made it VANISH from Storage and + // the note went back to claiming only one drive was found. A registered + // location is the clearest case of a usable drive there is. + const registered = (Array.isArray(data.locations) ? data.locations : []) + .filter(l => l && l.path && l.path !== (data.primary || '')) + .map(l => Object.assign({ verdict: l.state === 'ok' ? 'ok' : 'warn', registered: true }, l)); + const unregistered = Array.isArray(data.candidates) ? data.candidates : []; + // A path can legitimately appear in both while a registration settles. + const seen = new Set(registered.map(l => (l.path || '').replace(/\/$/, ''))); + this.storageCandidates = registered.concat( + unregistered.filter(c => !seen.has((c.path || '').replace(/\/$/, '')))); this.storageSystem = data.system || null; } catch (e) { console.log('[setup] storage scan unavailable:', e.message); @@ -601,7 +613,9 @@ class SetupWizard { : (refused ? 'can\u2019t be used' : (c.verdict === 'warn' ? 'needs care' : '')); - const title = o.system ? this._primaryLabel() : c.path; + // A registered location has a name the user chose; an unregistered + // candidate only has a path. + const title = o.system ? this._primaryLabel() : (c.name || c.path); return `
diff --git a/scripts/webui/data/generators/system/webui_storage_candidates.sh b/scripts/webui/data/generators/system/webui_storage_candidates.sh index ec926b6..dc02c9a 100644 --- a/scripts/webui/data/generators/system/webui_storage_candidates.sh +++ b/scripts/webui/data/generators/system/webui_storage_candidates.sh @@ -41,7 +41,19 @@ webuiGenerateStorageCandidates() apps="$(storageAppsOnRoot "$path" 2>/dev/null | paste -sd, -)" (( first )) || locations+="," first=0 - locations+="{\"id\":\"$(_lpJsonEsc "$id")\",\"name\":\"$(_lpJsonEsc "$name")\",\"path\":\"$(_lpJsonEsc "$path")\",\"state\":\"$(_lpJsonEsc "$state")\",\"apps\":\"$(_lpJsonEsc "$apps")\"}" + # Size and free too. Without them the setup wizard renders a registered + # location as a card reading "free of" with both numbers missing — it + # shows the same card as the system disk, which does carry them. + local loc_size="" loc_free="" loc_fs="" loc_pct="" + if command -v findmnt >/dev/null 2>&1 && [[ -d "$path" ]]; then + loc_size=$(findmnt -no SIZE --target "$path" 2>/dev/null | tail -1) + loc_free=$(findmnt -no AVAIL --target "$path" 2>/dev/null | tail -1) + loc_fs=$(findmnt -no FSTYPE --target "$path" 2>/dev/null | tail -1) + fi + loc_pct=$(df -Pk "$path" 2>/dev/null | awk 'NR==2 {gsub("%","",$5); print $5}') + locations+="{\"id\":\"$(_lpJsonEsc "$id")\",\"name\":\"$(_lpJsonEsc "$name")\",\"path\":\"$(_lpJsonEsc "$path")\",\"state\":\"$(_lpJsonEsc "$state")\",\"apps\":\"$(_lpJsonEsc "$apps")\"" + locations+=",\"size\":\"$(_lpJsonEsc "$loc_size")\",\"free\":\"$(_lpJsonEsc "$loc_free")\"" + locations+=",\"fstype\":\"$(_lpJsonEsc "$loc_fs")\",\"used_pct\":${loc_pct:-0},\"removable\":false}" done < <(runStorage verify 2>/dev/null) locations+="]"