diff --git a/documentation/99-zmeny.md b/documentation/99-zmeny.md index 6a66876..06b7abd 100644 --- a/documentation/99-zmeny.md +++ b/documentation/99-zmeny.md @@ -2,6 +2,38 @@ Nejnovejsi nahore. +## 2026-09-02 - "Interni chyba serveru" pri zalozeni ticketu + +Formular noveho ticketu koncil na 500 uz pri vyplnenem samotnem predmetu. + +### Cim to bylo + +`apiFetch` prevadi telo na JSON sam: + +```ts +body: body !== undefined ? JSON.stringify(body) : undefined, +``` + +Dialog mu ho ale predaval **uz prevedene**, tedy `body: JSON.stringify({...})`. +Druhy prevod z objektu udelal retezec a na server dorazilo `"{\"subject\":...}"`. +`express.json` je ve vychozim nastaveni `strict`, takze retezec na nejvyssi +urovni odmitne a vyhodi vyjimku. + +Tatáz chyba byla i ve dvou mistech helpdesku: zalozeni pozadavku a komentar. + +### A druha polovina: spatna hlaska + +Vyjimka z parseru tela propadla do centralniho error handleru, ktery z nej +udelal **500 "Interni chyba serveru"**. Ta hlaska rika, ze je neco spatne u nas, +a posila cloveka hledat na spatnou stranu - pritom slo o spatne polozeny dotaz. + +Error handler proto rozpozna chyby parseru tela (`status 400` a `type` zacinajici +`entity.`) a vraci **400 s vetou "Telo pozadavku neni platny JSON objekt."** +Ostatni chyby zustavaji 500, jak byly. + +Overeno: telo zakodovane dvakrat vraci 400 s tou vetou, opravene telo jen +s predmetem vraci ticket. + ## 2026-09-02 - Builder nabizi jen to, co jde zavolat V nabidce kroku byly vsechny sluzby katalogu, i ty, ke kterym firma nema diff --git a/src/index.ts b/src/index.ts index d5688c8..f912d04 100644 --- a/src/index.ts +++ b/src/index.ts @@ -214,7 +214,24 @@ if (hasWebBuild) { } // Centralni error handler - nic nesmi propadnout bez logu. -app.use((err: unknown, _req: Request, res: Response, _next: NextFunction) => { +app.use((err: unknown, req: Request, res: Response, _next: NextFunction) => { + /* + * Rozbite telo pozadavku neni nase chyba, je to spatne polozeny dotaz. + * + * `express.json` na nej vyhodi vyjimku, ta propadla sem a uzivatel videl + * "Interni chyba serveru" - hlasku, ktera rika, ze je neco spatne u nas, + * a poslala ho hledat na spatnou stranu. Stalo to jedno odpoledne. + */ + const status = (err as { status?: number } | null)?.status; + const type = (err as { type?: string } | null)?.type; + if (status === 400 && typeof type === 'string' && type.startsWith('entity.')) { + console.warn(`[error] ${req.method} ${req.path}: neplatne telo pozadavku (${type})`); + return res.status(400).json({ + error: 'bad_request', + message: 'Tělo požadavku není platný JSON objekt.', + }); + } + console.error('[error]', err); const message = err instanceof Error ? err.message : 'Neznama chyba.'; res.status(500).json({ diff --git a/web/src/components/dashboard/NewTicketDialog.tsx b/web/src/components/dashboard/NewTicketDialog.tsx index 2767115..8c7250f 100644 --- a/web/src/components/dashboard/NewTicketDialog.tsx +++ b/web/src/components/dashboard/NewTicketDialog.tsx @@ -123,7 +123,9 @@ export function NewTicketDialog({ try { await apiFetch('/api/dashboard/tickets', { method: 'POST', - body: JSON.stringify({ + // apiFetch telo samo prevede na JSON. Druhy JSON.stringify z nej udelal + // retezec, ktery parser tela odmitne - a to skoncilo jako 500. + body: { subject: subject.trim(), body, priority, @@ -132,7 +134,7 @@ export function NewTicketDialog({ assigneeGroupId: assigneeGroupId === '' ? null : assigneeGroupId, // Prázdné údaje se neposílají: zákazník je nepovinný. ...(company || contact || reply ? { customer: { company, contact, reply } } : {}), - }), + }, }); reset(); onCreated(); diff --git a/web/src/pages/dashboard/Helpdesk.tsx b/web/src/pages/dashboard/Helpdesk.tsx index 7243dc8..553a648 100644 --- a/web/src/pages/dashboard/Helpdesk.tsx +++ b/web/src/pages/dashboard/Helpdesk.tsx @@ -165,7 +165,8 @@ function NewRequestDialog({ try { await apiFetch('/api/dashboard/helpdesk', { method: 'POST', - body: JSON.stringify({ subject: subject.trim(), body, priority }), + // Bez druheho JSON.stringify, ten uz dela apiFetch. + body: { subject: subject.trim(), body, priority }, }); setSubject(''); setBody(''); @@ -289,7 +290,7 @@ function RequestDetail({ try { await apiFetch(`/api/dashboard/helpdesk/${id}/comment`, { method: 'POST', - body: JSON.stringify({ text: text.trim() }), + body: { text: text.trim() }, }); setText(''); const fresh = await apiFetch(`/api/dashboard/helpdesk/${id}`);