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.
This commit is contained in:
Johann150 2022-10-23 13:34:37 +02:00
parent c3c7164dfb
commit 66d7b69377
Signed by untrusted user: Johann150
GPG key ID: 9EE6577A2A06F8F1
2 changed files with 30 additions and 33 deletions

View file

@ -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<void>((res) => {
export async function handler(endpoint: IEndpoint, ctx: Koa.Context): Promise<void> {
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());
}
});
});
};

View file

@ -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';