forked from FoundKeyGang/FoundKey
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:
parent
c3c7164dfb
commit
66d7b69377
2 changed files with 30 additions and 33 deletions
|
@ -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());
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
|
@ -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';
|
||||
|
|
Loading…
Reference in a new issue