swaf/src/Controller.ts

165 lines
5.2 KiB
TypeScript

import express, {IRouter, RequestHandler, Router} from "express";
import {PathParams} from "express-serve-static-core";
import Application from "./Application.js";
import {registerRoute} from "./common/Routing.js";
import FileUploadMiddleware from "./FileUploadMiddleware.js";
import {logger} from "./Logger.js";
import Middleware, {MiddlewareType} from "./Middleware.js";
export default abstract class Controller {
private readonly router: Router = express.Router();
private readonly fileUploadFormRouter: Router = express.Router();
private app?: Application;
public getGlobalMiddlewares(): Middleware[] {
return [];
}
public hasGlobalMiddlewares(): boolean {
return this.getGlobalMiddlewares().length > 0;
}
public setupGlobalHandlers(router: Router): void {
for (const middleware of this.getGlobalMiddlewares()) {
router.use(this.wrap(middleware.getRequestHandler()));
}
}
public getRoutesPrefix(): string {
return '/';
}
public abstract routes(): void;
public setupRoutes(): { mainRouter: Router, fileUploadFormRouter: Router } {
this.routes();
return {
mainRouter: this.router,
fileUploadFormRouter: this.fileUploadFormRouter,
};
}
protected use(handler: RequestHandler): void {
this.router.use(this.wrap(handler));
logger.info('Installed anonymous middleware on ' + this.getRoutesPrefix());
}
protected useMiddleware(...middlewares: MiddlewareType<Middleware>[]): void {
for (const middleware of middlewares) {
const instance = new middleware(this.getApp());
if (instance instanceof FileUploadMiddleware) {
this.fileUploadFormRouter.use(this.wrap(instance.getRequestHandler()));
} else {
this.router.use(this.wrap(instance.getRequestHandler()));
}
logger.info('Installed ' + middleware.name + ' on ' + this.getRoutesPrefix());
}
}
protected get(
path: PathParams,
handler: RequestHandler,
routeName?: string,
...middlewares: (MiddlewareType<Middleware>)[]
): void {
this.handle('get', path, handler, routeName, ...middlewares);
}
protected post(
path: PathParams,
handler: RequestHandler,
routeName?: string,
...middlewares: (MiddlewareType<Middleware>)[]
): void {
this.handle('post', path, handler, routeName, ...middlewares);
}
protected put(
path: PathParams,
handler: RequestHandler,
routeName?: string,
...middlewares: (MiddlewareType<Middleware>)[]
): void {
this.handle('put', path, handler, routeName, ...middlewares);
}
protected delete(
path: PathParams,
handler: RequestHandler,
routeName?: string,
...middlewares: (MiddlewareType<Middleware>)[]
): void {
this.handle('delete', path, handler, routeName, ...middlewares);
}
private handle(
action: Exclude<keyof IRouter, 'stack' | 'param' | 'route' | 'use'>,
path: PathParams,
handler: RequestHandler,
routeName?: string,
...middlewares: (MiddlewareType<Middleware>)[]
): void {
this.registerRoutes(path, handler, routeName);
for (const middleware of middlewares) {
const instance = new middleware(this.getApp());
if (instance instanceof FileUploadMiddleware) {
this.fileUploadFormRouter[action](path, this.wrap(instance.getRequestHandler()));
} else {
this.router[action](path, this.wrap(instance.getRequestHandler()));
}
}
this.router[action](path, this.wrap(handler));
}
private wrap(handler: RequestHandler): RequestHandler {
return async (req, res, next) => {
try {
await handler.call(this, req, res, next);
} catch (e) {
next(e);
}
};
}
private registerRoutes(path: PathParams, handler: RequestHandler, routeName?: string) {
if (typeof routeName !== 'string') {
routeName = handler.name
.replace(/(?:^|\.?)([A-Z])/g, (x, y) => '_' + y.toLowerCase())
.replace(/(^_|get_|post_)/g, '');
}
if (routeName.length === 0) return;
let routePath = null;
if (path instanceof Array && path.length > 0) {
path = path[0];
}
if (typeof path === 'string') {
const prefix = this.getRoutesPrefix();
routePath = (prefix !== '/' ? prefix : '') + path;
}
if (typeof routePath !== 'string') {
logger.warn(`Cannot assign path to route ${routeName}.`);
return;
}
if (registerRoute(routeName, routePath)) {
logger.info(`Route ${routeName} has path ${routePath}`);
} else {
logger.warn(`Couldn't register ${routeName} for path ${routePath}`);
}
}
protected getApp(): Application {
if (!this.app) throw new Error('Application not initialized.');
return this.app;
}
public setApp(app: Application): void {
this.app = app;
}
}