swaf/src/ApplicationComponent.ts

90 lines
2.7 KiB
TypeScript

import config from "config";
import {Express, Router} from "express";
import Application from "./Application.js";
import {logger} from "./Logger.js";
import Middleware, {MiddlewareType} from "./Middleware.js";
import SecurityError from "./SecurityError.js";
import {sleep} from "./Utils.js";
export default abstract class ApplicationComponent {
private currentRouter?: Router;
private app?: Application;
public async checkSecuritySettings?(): Promise<void>;
public async init?(): Promise<void>;
public async initRoutes?(router: Router): Promise<void>;
public async handleRoutes?(router: Router): Promise<void>;
public async start?(expressApp: Express): Promise<void>;
public async stop?(): Promise<void>;
public isReady(): boolean {
return true;
}
protected async prepare(name: string, prepare: () => Promise<void>): Promise<void> {
let err;
do {
try {
await prepare();
err = null;
} catch (e) {
err = e;
logger.error(err, `${name} failed to prepare; retrying in 5s...`);
await sleep(5000);
}
} while (err);
logger.info(`${name} ready!`);
}
protected async close(thingName: string, fn: (callback: (err?: Error | null) => void) => void): Promise<void> {
try {
await new Promise<void>((resolve, reject) => fn((err?: Error | null) => {
if (err) reject(err);
else resolve();
}));
logger.info(`${thingName} closed.`);
} catch (e) {
logger.error(e, `An error occurred while closing the ${thingName}.`);
}
}
protected checkSecurityConfigField(field: string): void {
if (!config.has(field) || config.get<string>(field) === 'default') {
throw new SecurityError(`${field} field not configured.`);
}
}
protected use<M extends Middleware>(middleware: MiddlewareType<M>): void {
if (!this.currentRouter) throw new Error('Cannot call this method outside init() and handle().');
const instance = new middleware(this.getApp());
this.currentRouter.use(async (req, res, next) => {
try {
await instance.getRequestHandler()(req, res, next);
} catch (e) {
next(e);
}
});
}
public setCurrentRouter(router: Router | null): void {
this.currentRouter = router || undefined;
}
protected getApp(): Application {
if (!this.app) throw new Error('app field not initialized.');
return this.app;
}
public setApp(app: Application): void {
this.app = app;
}
}