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; public async init?(): Promise; public async initRoutes?(router: Router): Promise; public async handleRoutes?(router: Router): Promise; public async start?(expressApp: Express): Promise; public async stop?(): Promise; public isReady(): boolean { return true; } protected async prepare(name: string, prepare: () => Promise): Promise { 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 { try { await new Promise((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(field) === 'default') { throw new SecurityError(`${field} field not configured.`); } } protected use(middleware: MiddlewareType): 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; } }