better api error handling

Massimo Melina committed Feb 13, 2023 at 10:04 UTC 43c2e779beb1ed1e341dab971ec34436129acce3
4 files changed +17 -7
frontend/src/api.ts
+3 -3
@@ -15,12 +15,12 @@ export function apiCall(cmd: string, params?: Dict, options: ApiCallOptions={})
15 method: 'POST',
16 headers: { 'content-type': 'application/json' },
17 body: params && JSON.stringify(params),
18 - }).then(res => {
18 + }).then(async res => {
19 stop?.()
20 if (res.ok)
21 return res.json()
22 - const msg = `Failed API ${cmd}: ${res.statusText}`
23 - console.warn(msg + (params ? ' ' + JSON.stringify(params) : ''))
22 + const msg = await res.text() || `Failed API ${cmd}: ${res.statusText}`
23 + console.warn(msg, params ? ' ' + JSON.stringify(params) : '')
24 throw new ApiError(res.status, msg)
25 }, err => {
26 stop?.()
frontend/src/dialog.ts
+1 -1
@@ -60,7 +60,7 @@ export async function alertDialog(msg: ReactElement | string | Error, type:Alert
60 type = 'error'
61 }
62 return new Promise(resolve => newDialog({
63 - className: 'dialog-alert-'+type,
63 + className: 'dialog-alert dialog-alert-'+type,
64 title: _.capitalize(type),
65 icon: '!',
66 onClose: resolve,
frontend/src/index.scss
+4
@@ -272,6 +272,10 @@ button label {
272 .dialog-content {
273 padding: .2em; /* give space for focus outline */
274 }
275 +.dialog-alert .dialog-content {
276 + text-Align: center;
277 + & p { text-Align: left; display: inline-block; }
278 +}
279
280 .dialog {
281 min-width: 10em;
src/apiMiddleware.ts
+9 -3
@@ -28,8 +28,14 @@ export function apiMiddleware(apis: ApiHandlers) : Koa.Middleware {
28 }
29 const csrf = ctx.cookies.get('csrf')
30 // we don't rely on SameSite cookie option because it's https-only
31 - let res = csrf && csrf !== ctx.params.csrf ? new ApiError(HTTP_UNAUTHORIZED, 'csrf')
32 - : await apiFun(ctx.params || {}, ctx)
31 + let res
32 + try {
33 + res = csrf && csrf !== ctx.params.csrf ? new ApiError(HTTP_UNAUTHORIZED, 'csrf')
34 + : await apiFun(ctx.params || {}, ctx)
35 + }
36 + catch(e) {
37 + res = e
38 + }
39 if (isAsyncGenerator(res))
40 res = asyncGeneratorToReadable(res)
41 if (res instanceof Readable) { // Readable, we'll go SSE-mode
@@ -44,7 +50,7 @@ export function apiMiddleware(apis: ApiHandlers) : Koa.Middleware {
50 return ctx.status = res.status
51 }
52 if (res instanceof Error) { // generic exception
47 - ctx.body = String(res)
53 + ctx.body = res.message || String(res)
54 return ctx.status = HTTP_BAD_REQUEST
55 }
56 ctx.body = res