fix: remove staging chunks after completed uploads

Successful completion moved the assembled file into data/complete but left the
upload staging directory behind, including all chunk files. Remove the upload's
staging directory only after the final file has been renamed into place so
incomplete and failed uploads remain resumable.

A repeat complete request for that old upload id now returns 404 because the
temporary upload record has been retired with its chunks.

Test Plan:
- just check

Refs: none
This commit is contained in:
2026-05-30 17:59:21 +02:00
parent 996ad5c4c8
commit 428af52e2f
3 changed files with 14 additions and 4 deletions
+2 -1
View File
@@ -17,7 +17,8 @@ Keep this file as the reusable verification checklist while implementing
- `PUT /api/uploads/:id/chunks/:index` rejects out-of-range indexes.
- `PUT /api/uploads/:id/chunks/:index` accepts duplicate chunks.
- `GET /api/uploads/:id` reports completed chunks from disk.
- `POST /api/uploads/:id/complete` assembles verified chunks.
- `POST /api/uploads/:id/complete` assembles verified chunks and removes
staging data.
- `POST /api/uploads/:id/complete` rejects incomplete uploads.
- `POST /api/uploads/:id/complete` rejects corrupt chunk files.
- `static/app.js` passes `node --check`.
+9
View File
@@ -171,6 +171,7 @@ impl Storage {
drop(output);
fs::rename(&tmp_path, &final_path).await?;
self.remove_upload_dir(upload_id).await?;
Ok(meta.complete_response(final_path.display().to_string()))
}
@@ -257,6 +258,14 @@ impl Storage {
Ok(())
}
async fn remove_upload_dir(&self, upload_id: &str) -> Result<(), StorageError> {
match fs::remove_dir_all(self.upload_dir(upload_id)).await {
Ok(()) => Ok(()),
Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(()),
Err(error) => Err(error.into()),
}
}
}
#[derive(Debug)]
+3 -3
View File
@@ -48,11 +48,11 @@ async fn assembles_completed_upload() -> Result<(), Box<dyn std::error::Error>>
b"hello world"
);
assert!(
temp_dir
!temp_dir
.path()
.join("staging")
.join(&upload.upload_id)
.is_dir()
.exists()
);
let duplicate = app
@@ -61,7 +61,7 @@ async fn assembles_completed_upload() -> Result<(), Box<dyn std::error::Error>>
&format!("/api/uploads/{}/complete", upload.upload_id),
)?)
.await?;
assert_eq!(duplicate.status(), StatusCode::CONFLICT);
assert_eq!(duplicate.status(), StatusCode::NOT_FOUND);
Ok(())
}