From 66d7b69377ac4ec82fbc1f84cee3a1a00e4dae66 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sun, 23 Oct 2022 13:34:37 +0200 Subject: [PATCH] server: refactor API handler and returning errors This refactors the API handler to not use default exports, be async instead of constructing a promise and modify how errors are returned. --- .../backend/src/server/api/api-handler.ts | 61 +++++++++---------- packages/backend/src/server/api/index.ts | 2 +- 2 files changed, 30 insertions(+), 33 deletions(-) diff --git a/packages/backend/src/server/api/api-handler.ts b/packages/backend/src/server/api/api-handler.ts index 956096367..f389ab8da 100644 --- a/packages/backend/src/server/api/api-handler.ts +++ b/packages/backend/src/server/api/api-handler.ts @@ -5,59 +5,56 @@ import authenticate, { AuthenticationError } from './authenticate.js'; import call from './call.js'; import { ApiError } from './error.js'; -export default (endpoint: IEndpoint, ctx: Koa.Context) => new Promise((res) => { +export async function handler(endpoint: IEndpoint, ctx: Koa.Context): Promise { const body = ctx.is('multipart/form-data') ? (ctx.request as any).body : ctx.method === 'GET' ? ctx.query : ctx.request.body; - const reply = (x?: any, y?: ApiError) => { - if (x == null) { - ctx.status = 204; - } else if (typeof x === 'number' && y) { - ctx.status = x; - ctx.body = { - error: { - message: y!.message, - code: y!.code, - id: y!.id, - kind: y!.kind, - ...(y!.info ? { info: y!.info } : {}), - }, - }; - } else { - // 文字列を返す場合は、JSON.stringify通さないとJSONと認識されない - ctx.body = typeof x === 'string' ? JSON.stringify(x) : x; + const error = (e: ApiError): void => { + ctx.status = e.httpStatusCode ?? 500; + if (e.httpStatusCode === 401) { + ctx.response.set('WWW-Authenticate', 'Bearer'); } - res(); - }; + ctx.body = { + error: { + message: e!.message, + code: e!.code, + ...(e!.info ? { info: e!.info } : {}), + endpoint: endpoint.name, + }, + }; + } // Authentication // for GET requests, do not even pass on the body parameter as it is considered unsafe - authenticate(ctx.headers.authorization, ctx.method === 'GET' ? null : body['i']).then(([user, app]) => { + await authenticate(ctx.headers.authorization, ctx.method === 'GET' ? null : body['i']).then(async ([user, app]) => { // API invoking - call(endpoint.name, user, app, body, ctx).then((res: any) => { + await call(endpoint.name, user, app, body, ctx).then((res: any) => { if (ctx.method === 'GET' && endpoint.meta.cacheSec && !body['i'] && !user) { ctx.set('Cache-Control', `public, max-age=${endpoint.meta.cacheSec}`); } - reply(res); + if (res == null) { + ctx.status = 204; + } else { + ctx.status = 200; + // If a string is returned, it must be passed through JSON.stringify to be recognized as JSON. + ctx.body = typeof res === 'string' ? JSON.stringify(res) : res; + } }).catch((e: ApiError) => { - reply(e.httpStatusCode ? e.httpStatusCode : e.kind === 'client' ? 400 : 500, e); + error(e); }); }).catch(e => { if (e instanceof AuthenticationError) { - ctx.response.status = 403; - ctx.response.set('WWW-Authenticate', 'Bearer'); - ctx.response.body = { + error({ message: 'Authentication failed: ' + e.message, code: 'AUTHENTICATION_FAILED', id: 'b0a7f5f8-dc2f-4171-b91f-de88ad238e14', - kind: 'client', - }; - res(); + httpStatusCode: 401, + }); } else { - reply(500, new ApiError()); + error(new ApiError()); } }); -}); +}; diff --git a/packages/backend/src/server/api/index.ts b/packages/backend/src/server/api/index.ts index 83ece51f5..3f5708022 100644 --- a/packages/backend/src/server/api/index.ts +++ b/packages/backend/src/server/api/index.ts @@ -11,7 +11,7 @@ import cors from '@koa/cors'; import { Instances, AccessTokens, Users } from '@/models/index.js'; import config from '@/config/index.js'; import endpoints from './endpoints.js'; -import handler from './api-handler.js'; +import { handler } from './api-handler.js'; import signup from './private/signup.js'; import signin from './private/signin.js'; import signupPending from './private/signup-pending.js';