Fix async file deletion bugs in assets endpoint (#5363) The delete handler had a missing `return` before `sendStatus(400)`, causing execution to fall through to `sendStatus(200)`, a double-send that triggers ERR_HTTP_HEADERS_SENT, which the catch block then compounds by attempting a third `sendStatus(500)`. Both the delete and download handlers used callback-based `fs.unlink()` without awaiting completion. In the download handler, this caused a race with `createWriteStream({ flags: 'wx' })` (which fails if the file still exists). In both handlers, `throw err` inside the callback was an unhandled exception that could never be caught by the outer try/catch. Replace callback-based `fs.unlink()` with `await fs.promises.unlink()` and add missing `return` statements to prevent response cascades.

271c1e22ca3299c21b8d8e7988a22ffd6c1d1a9b

Tony Gies <tgies@tgies.net>

Signed
1 files changed, +8 -14Ignore whitespace
src/endpoints/assets.js+8 -14
@@ -232,9 +232,7 @@ router.post('/download', async (request, response) => {
232 const destination = path.resolve(temp_path);232 const destination = path.resolve(temp_path);
233 // Delete if previous download failed233 // Delete if previous download failed
234 if (fs.existsSync(temp_path)) {234 if (fs.existsSync(temp_path)) {
235 fs.unlink(temp_path, (err) => {235 await fs.promises.unlink(temp_path);
236 if (err) throw err;
237 });
238 }236 }
239 const fileStream = fs.createWriteStream(destination, { flags: 'wx' });237 const fileStream = fs.createWriteStream(destination, { flags: 'wx' });
240 // @ts-ignore238 // @ts-ignore
@@ -291,21 +289,17 @@ router.post('/delete', async (request, response) => {
291 console.info('Request received to delete', category, file_path);289 console.info('Request received to delete', category, file_path);
292290
293 try {291 try {
294 // Delete if previous download failed292 if (!fs.existsSync(file_path)) {
295 if (fs.existsSync(file_path)) {
296 fs.unlink(file_path, (err) => {
297 if (err) throw err;
298 });
299 console.info('Asset deleted.');
300 } else {
301 console.error('Asset not found.');293 console.error('Asset not found.');
302 response.sendStatus(400);294 return response.sendStatus(400);
303 }295 }
304 // Move into asset place296
305 response.sendStatus(200);297 await fs.promises.unlink(file_path);
298 console.info('Asset deleted.');
299 return response.sendStatus(200);
306 } catch (error) {300 } catch (error) {
307 console.error(error);301 console.error(error);
308 response.sendStatus(500);302 return response.sendStatus(500);
309 }303 }
310});304});
311305