mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-08-14 12:47:10 +00:00
fix(career): bound the prepare request; validate the setlist (PR #971 review)
Both CodeRabbit findings were right. 1. A HUNG PREPARE COULD BLOCK THE GIG FOREVER. `await fetch(...)` only rejects on a network ERROR. A server that accepts the connection and then never answers hangs indefinitely — and the gig would never start. That makes this optimisation the exact thing the PR promises it can never be: the reason you cannot play. The request is now bounded by an AbortController (PREPARE_TIMEOUT_MS, generous because unpacking a setlist is real work — but a CEILING, not a wait). Past it we start the gig and let the first play extract lazily, as it always did. The Play button is restored in a `finally`, so a timeout cannot strand the poster on "Preparing set…" with Play disabled — which would have been the same bug wearing a different hat. 2. THE `songs` BODY WAS UNVALIDATED. A str is iterable: "abc" would have prepared three one-character "songs". And the endpoint unpacks zips, so an arbitrary caller could ask for unbounded work. Now list-only, string entries, blanks dropped, capped at MAX_GIG_SONGS. Tests: the fetch is abortable and the button is re-enabled on EVERY path including the abort; non-list bodies, non-string/blank entries, and an oversized setlist. 50 career tests, JS 5/5, eslint clean.
This commit is contained in:
@@ -12,6 +12,10 @@
|
||||
'use strict';
|
||||
|
||||
const API = '/api/plugins/career';
|
||||
// Unpacking a setlist is real work (zips, possibly on a slow/network drive),
|
||||
// so this is generous — but it is a CEILING, not a wait. Past it we start the
|
||||
// gig and let the first play extract lazily, as it always did.
|
||||
const PREPARE_TIMEOUT_MS = 60000;
|
||||
const VENUE_OVERRIDE_KEY = 'feedBack-career-venue';
|
||||
const NO_VENUE = '__none__';
|
||||
const PREV_VIZ_KEY = 'feedBack-career-prev-viz';
|
||||
@@ -1136,14 +1140,26 @@
|
||||
async function prepareGigSongs(prop, btn) {
|
||||
const label = btn && btn.textContent;
|
||||
if (btn) { btn.disabled = true; btn.textContent = 'Preparing set…'; }
|
||||
// A bare `await fetch(...)` only rejects on a network ERROR — a server
|
||||
// that accepts the connection and then never answers hangs forever, and
|
||||
// the gig would never start. That would make this optimisation the very
|
||||
// thing it promises never to be: the reason you cannot play. Give up
|
||||
// waiting and let the first play extract lazily, exactly as before.
|
||||
const ctrl = new AbortController();
|
||||
const timer = setTimeout(() => ctrl.abort(), PREPARE_TIMEOUT_MS);
|
||||
try {
|
||||
await fetch(`${API}/gigs/prepare`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ songs: prop.songs.map((s) => s.filename) }),
|
||||
signal: ctrl.signal,
|
||||
});
|
||||
} catch (_) { /* start anyway — first play will extract as it always did */ }
|
||||
if (btn) { btn.disabled = false; if (label) btn.textContent = label; }
|
||||
} catch (_) {
|
||||
// abort, offline, non-2xx — all the same: start the gig anyway.
|
||||
} finally {
|
||||
clearTimeout(timer);
|
||||
if (btn) { btn.disabled = false; if (label) btn.textContent = label; }
|
||||
}
|
||||
}
|
||||
|
||||
async function startGig(btn) {
|
||||
|
||||
Reference in New Issue
Block a user