fix: stop voice entry posting an unsaved detail id, and name failures right
Review findings on this branch. The lab-case save could be posted against a detail the server has never seen. persistDraft returns a *preview* treatment instead of saving when any detail lacks a treatment type — the blank one the workspace opens with is enough — and a preview's detail id falls back to the client id. Recording straight after opening a visit and confirming a result with a lab or due date would send that id and fail the whole save. It now checks what came back rather than the precondition, so it holds for every early return persistDraft has. stop() optional-chained into a no-op when the recorder was already gone, leaving the bar recording forever with a live timer and only Cancel as a way out. Three "this browser cannot record" paths reported VOICE_MIC_DENIED — no MediaRecorder at all, no container the API accepts, and a recorder that throws after permission was already granted. Telling clinicians their microphone was denied sends them hunting for a permission nothing asked for; they now report VOICE_UNSUPPORTED_FORMAT. The voice route's large-body match stripped every trailing slash while Express ignores exactly one, so '/api/voice/extract//' bought a 10 MB buffer for a request that then 404s. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -93,6 +93,9 @@ describe('createJsonBodyParser', () => {
|
||||
'/api/voice/extract/extra',
|
||||
'/api/voice',
|
||||
'/voice/extract',
|
||||
// Express ignores one trailing slash, not two — this one never routes, so it must
|
||||
// not get the large parser either.
|
||||
'/api/voice/extract//',
|
||||
]) {
|
||||
const res = await request(buildApp()).post(path).send(bodyOfKb(300));
|
||||
expect(res.status).toBe(413);
|
||||
|
||||
@@ -31,7 +31,10 @@ export const VOICE_BODY_LIMIT = '10mb';
|
||||
* recording — a failure that looks like a broken microphone, not a routing detail.
|
||||
*/
|
||||
function isVoiceExtractPath(path: string): boolean {
|
||||
return path.toLowerCase().replace(/\/+$/, '') === VOICE_EXTRACT_PATH;
|
||||
// Exactly one trailing slash, because that is exactly what Express ignores. Stripping
|
||||
// every trailing slash would hand the 10 MB parser to `/api/voice/extract//`, which
|
||||
// buffers the body and then 404s — memory spent on a request that never routes.
|
||||
return path.toLowerCase().replace(/\/$/, '') === VOICE_EXTRACT_PATH;
|
||||
}
|
||||
|
||||
export function createJsonBodyParser(): RequestHandler {
|
||||
|
||||
Reference in New Issue
Block a user