fix(update): failed download returns to Download, not Install (#26)
A resumable-but-failed download leaves partial component files in update-staging. has_staged_update() treated ANY staged file as "install-ready", so the state self-heal kept update_in_progress=true and the UI showed Install instead of Download (no clean retry). - update.rs: write a .download-complete marker only after EVERY component downloads+verifies; has_staged_update() now checks that marker. Partial/failed downloads (no marker) correctly read as not-staged → self-heal clears update_in_progress → UI shows Download. Resume still works (partial files kept). - SystemUpdate.vue: on a genuine download failure, reset downloaded/in_progress and re-sync, so the user lands back on Download immediately. cargo check + vue-tsc clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
3a9d1db763
commit
82cfc8ccba
@ -538,12 +538,23 @@ pub async fn load_state(data_dir: &Path) -> Result<UpdateState> {
|
|||||||
Ok(state)
|
Ok(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Marker written only after EVERY component has downloaded and verified.
|
||||||
|
/// Distinguishes a complete, install-ready staging from the partial files a
|
||||||
|
/// resumable-but-failed download leaves behind.
|
||||||
|
const STAGED_COMPLETE_MARKER: &str = ".download-complete";
|
||||||
|
|
||||||
async fn has_staged_update(data_dir: &Path) -> bool {
|
async fn has_staged_update(data_dir: &Path) -> bool {
|
||||||
let staging_dir = data_dir.join("update-staging");
|
// A *complete* staged update carries the marker. A partial/failed download
|
||||||
let Ok(mut entries) = fs::read_dir(&staging_dir).await else {
|
// leaves component files (kept for resume) but no marker, so it reads as
|
||||||
return false;
|
// "not staged" — the state self-heal then clears update_in_progress and the
|
||||||
};
|
// UI returns to Download instead of stranding the user on Install.
|
||||||
matches!(entries.next_entry().await, Ok(Some(_)))
|
fs::metadata(
|
||||||
|
data_dir
|
||||||
|
.join("update-staging")
|
||||||
|
.join(STAGED_COMPLETE_MARKER),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.is_ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn save_state(data_dir: &Path, state: &UpdateState) -> Result<()> {
|
pub async fn save_state(data_dir: &Path, state: &UpdateState) -> Result<()> {
|
||||||
@ -801,7 +812,10 @@ pub async fn download_update(data_dir: &Path) -> Result<DownloadProgress> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark update as downloaded
|
// Mark update as downloaded. Write the completion marker FIRST so a crash
|
||||||
|
// between the two can't leave update_in_progress=true without the marker
|
||||||
|
// (which the self-heal would then clear, harmlessly forcing a re-download).
|
||||||
|
let _ = fs::write(staging_dir.join(STAGED_COMPLETE_MARKER), b"1").await;
|
||||||
let mut state = load_state(data_dir).await?;
|
let mut state = load_state(data_dir).await?;
|
||||||
state.update_in_progress = true;
|
state.update_in_progress = true;
|
||||||
save_state(data_dir, &state).await?;
|
save_state(data_dir, &state).await?;
|
||||||
|
|||||||
@ -871,6 +871,13 @@ async function downloadUpdate() {
|
|||||||
await loadStatus()
|
await loadStatus()
|
||||||
showStatus(t('systemUpdate.upToDateMessage'))
|
showStatus(t('systemUpdate.upToDateMessage'))
|
||||||
} else {
|
} else {
|
||||||
|
// A failed download is NOT a staged update — return the UI to the
|
||||||
|
// Download button so the user can retry, instead of stranding them on
|
||||||
|
// Install. Re-sync from the backend (its self-heal clears a stale
|
||||||
|
// update_in_progress once the partial staging is cleaned up).
|
||||||
|
downloaded.value = false
|
||||||
|
updateInProgress.value = false
|
||||||
|
await loadStatus()
|
||||||
showStatus(`${t('systemUpdate.downloadFailed')} ${msg}`, true)
|
showStatus(`${t('systemUpdate.downloadFailed')} ${msg}`, true)
|
||||||
}
|
}
|
||||||
if (import.meta.env.DEV) console.warn('Download failed', e)
|
if (import.meta.env.DEV) console.warn('Download failed', e)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user