@mieweb/wikigdrive
Version:
Google Drive to MarkDown synchronization
332 lines (331 loc) • 11 kB
JavaScript
import process from 'node:process';
import { instrumentAndWrap } from '../../../telemetry.js';
export const HttpStatus = {
OK: 200,
CREATED: 201,
NO_CONTENT: 204
};
function getMethods(obj) {
const res = {};
for (const m of Object.getOwnPropertyNames(obj.constructor.prototype)) {
res[m] = obj[m];
}
return res;
}
export class ErrorHandler {
constructor() {
Object.defineProperty(this, "req", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "res", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "subPath", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "logger", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
}
async catch(err) {
throw err;
}
}
export class ControllerCallContext {
constructor(route, subPath, req, res, logger) {
Object.defineProperty(this, "route", {
enumerable: true,
configurable: true,
writable: true,
value: route
});
Object.defineProperty(this, "subPath", {
enumerable: true,
configurable: true,
writable: true,
value: subPath
});
Object.defineProperty(this, "req", {
enumerable: true,
configurable: true,
writable: true,
value: req
});
Object.defineProperty(this, "res", {
enumerable: true,
configurable: true,
writable: true,
value: res
});
Object.defineProperty(this, "logger", {
enumerable: true,
configurable: true,
writable: true,
value: logger
});
}
async routeParamMethod() {
return this.req.method.toLowerCase();
}
async routeParamPath(name) {
return this.req.params[name];
}
async routeParamBody() {
return this.req.body;
}
async routeParamQuery(name) {
return this.req.query[name];
}
async routeParamUser() {
return this.req.user;
}
}
function addSwaggerRoute(mainPath, route) {
// SwaggerDocService.addRoute(mainPath, route);
}
export class Controller {
constructor(subPath) {
Object.defineProperty(this, "subPath", {
enumerable: true,
configurable: true,
writable: true,
value: subPath
});
}
getRoute(instance, methodFunc) {
const classType = instance.constructor.prototype;
if (!classType.controllerId) {
classType.controllerId = 'controller_' + Controller.counter;
Controller.counter++;
}
const key = classType.controllerId + '.' + methodFunc;
if (!Controller.routes[key]) {
Controller.routes[key] = {
hidden: false,
roles: [],
errorHandlers: [],
methodFunc,
responseObjectType: 'object',
responseStatus: HttpStatus.OK,
responseContentType: 'application/json; charset=utf-8'
};
}
return Controller.routes[key];
}
async getRouter() {
const controllerId = this.constructor.prototype.controllerId;
const { Router } = await import('express');
const router = Router();
for (const key in Controller.routes) {
if (!key.startsWith(controllerId + '.')) {
continue;
}
const route = Controller.routes[key];
if (!route.hidden) {
addSwaggerRoute(this.subPath, route);
}
const handlers = [];
if (route.roles.length > 0) {
handlers.push((req, res, next) => {
if (req.user && route.roles.indexOf(req.user.global_role) > -1) {
next();
}
else {
const logger = req['logger'];
logger.error('User does not have any of those roles: ' +
JSON.stringify(route.roles) +
', only: ' +
req.user.global_role);
// throw Boom.forbidden(req.t('auth.youNeedToBeAuthorized'));
}
});
}
handlers.push(async (req, res, next) => {
try {
const methods = getMethods(this.constructor.prototype);
const bound = this[route.methodFunc].bind({
...methods,
...this,
});
res.header('Content-type', route.responseContentType);
const args = [];
const logger = req['logger'];
const ctx = new ControllerCallContext(route, this.subPath, req, res, logger);
args.push(ctx);
let retVal;
if (process.env.ZIPKIN_URL) {
const spanName = req.originalUrl + '.' + route.methodFunc;
await instrumentAndWrap(spanName, req, res, async () => {
retVal = await bound(...args);
});
}
else {
retVal = await bound(...args);
}
if ('stream' === route.responseObjectType) {
return;
}
if ('void' === route.responseObjectType) {
res.status(HttpStatus.NO_CONTENT).send();
return;
}
if ('html' === route.responseObjectType) {
res.status(route.responseStatus).send(retVal);
return;
}
res.status(route.responseStatus).json(retVal);
}
catch (err) {
let err1 = err;
for (const inputFilter of route.errorHandlers) {
try {
const boundErrorHandler = inputFilter.catch.bind({
...this,
...inputFilter,
subPath: this.subPath,
req,
res
});
await boundErrorHandler(err);
}
catch (subErr) {
err1 = subErr;
}
}
next(err1);
}
});
switch (route.method) {
case 'GET':
router.get(route.routePath, ...handlers);
break;
case 'POST':
router.post(route.routePath, ...handlers);
break;
case 'PUT':
router.put(route.routePath, ...handlers);
break;
case 'DELETE':
router.delete(route.routePath, ...handlers);
break;
case 'USE':
router.use(route.routePath, ...handlers);
break;
}
}
return router;
}
}
Object.defineProperty(Controller, "routes", {
enumerable: true,
configurable: true,
writable: true,
value: {}
});
Object.defineProperty(Controller, "counter", {
enumerable: true,
configurable: true,
writable: true,
value: 1
});
export function RouteUse(routePath, docs = {}) {
return function (_func, ctx) {
ctx.addInitializer(function () {
const route = this.getRoute(this, ctx.name);
route.routePath = routePath;
route.method = 'USE';
route.routeDocs = docs;
});
};
}
export function RouteGet(routePath, docs = {}) {
return function (_func, ctx) {
ctx.addInitializer(function () {
const route = this.getRoute(this, ctx.name);
route.routePath = routePath;
route.method = 'GET';
route.routeDocs = docs;
});
};
}
export function RoutePut(routePath, docs = {}) {
return function (_func, ctx) {
ctx.addInitializer(function () {
const route = this.getRoute(this, ctx.name);
route.routePath = routePath;
route.method = 'PUT';
route.routeDocs = docs;
});
};
}
export function RoutePost(routePath, docs = {}) {
return function (_func, ctx) {
ctx.addInitializer(function () {
const route = this.getRoute(this, ctx.name);
route.routePath = routePath;
route.method = 'POST';
route.routeDocs = docs;
});
};
}
export function RouteDelete(routePath, docs = {}) {
return function (_func, ctx) {
ctx.addInitializer(function () {
const route = this.getRoute(this, ctx.name);
route.routePath = routePath;
route.method = 'DELETE';
route.routeDocs = docs;
});
};
}
export function RouteDocsHidden() {
return function (controller, methodProp) {
const route = controller.getRoute(controller, methodProp);
route.hidden = true;
};
}
export function RouteHasRole(roles) {
return function (controller, methodProp) {
const route = controller.getRoute(controller, methodProp);
route.roles = roles;
};
}
export function RouteResponse(objType = 'object', docs = {}, contentType = 'application/json; charset=utf-8') {
return function (_func, ctx) {
ctx.addInitializer(function () {
const route = this.getRoute(this, ctx.name);
route.responseObjectType = objType;
route.responseContentType = contentType;
route.responseDocs = docs;
});
};
}
// TODO: remove
export function RouteErrorHandler(errorHandler) {
return function (_func, ctx) {
ctx.addInitializer(function () {
const route = this.getRoute(this, ctx.name);
route.errorHandlers.push(errorHandler);
});
};
}
export function RouteResponseStatus(status = HttpStatus.OK) {
return function (_func, ctx) {
ctx.addInitializer(function () {
const route = this.getRoute(this, ctx.name);
route.responseStatus = status;
});
};
}