Compare commits

...

6 commits

5 changed files with 56 additions and 5 deletions

View file

@ -5,12 +5,23 @@ import authenticate, { AuthenticationError } from './authenticate.js';
import call from './call.js';
import { ApiError } from './error.js';
function getRequestArguments(ctx: Koa.Context): any {
const args = {
...(ctx.params || {}),
...ctx.query,
...(ctx.request.body || {}),
};
// For security reasons, we drop the i parameter if it's a GET request
if (ctx.method === 'GET') {
delete args['i'];
}
return args;
}
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 body = getRequestArguments(ctx);
const error = (e: ApiError): void => {
ctx.status = e.httpStatusCode;

View file

@ -702,6 +702,24 @@ export interface IEndpointMeta {
* (Cache-Control: public)
*/
readonly cacheSec?: number;
/**
* API v2 options
*/
readonly v2?: {
/**
* HTTP verb this endpoint supports
*/
readonly method: string;
/**
* Path alias for v2 endpoint
*
* @example (v0) /api/notes/create -> /api/v2/notes
*/
readonly alias?: string;
};
}
export interface IEndpoint {

View file

@ -10,6 +10,10 @@ export const meta = {
requireCredential: false,
v2: {
method: 'get',
},
allowGet: true,
cacheSec: 60,

View file

@ -69,6 +69,11 @@ for (const endpoint of endpoints) {
} else {
router.get(`/${endpoint.name}`, async ctx => { ctx.status = 405; });
}
if (endpoint.meta.v2) {
const path = endpoint.meta.v2.alias ?? endpoint.name.replace(/-/g, '_');
router[endpoint.meta.v2.method](`/v2/${path}`, handler.bind(null, endpoint));
}
}
}

View file

@ -207,6 +207,19 @@ export function genOpenapiSpec() {
}
spec.paths['/' + endpoint.name] = path;
if (endpoint.meta.v2) {
// we need a clone of the API endpoint info because otherwise we change it by reference
const infoClone = JSON.parse(JSON.stringify(info));
const route = `/v2/${endpoint.meta.v2.alias ?? endpoint.name.replace(/-/g, '_')}`;
infoClone['operationId'] = infoClone['summary'] = route;
spec.paths[route] = {
...spec.paths[route],
[endpoint.meta.v2.method]: infoClone,
};
}
}
return spec;