UNPKG

@zerooneit/expressive-tea

Version:
196 lines (195 loc) 8.71 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.resolveProxy = void 0; const $P = require("bluebird"); // tslint:disable-next-line:no-duplicate-imports const express = require("express"); const fs = require("fs"); const http = require("http"); const https = require("https"); const MetaData_1 = require("../classes/MetaData"); const Settings_1 = require("../classes/Settings"); const BootLoaderExceptions_1 = require("../exceptions/BootLoaderExceptions"); const object_helper_1 = require("../helpers/object-helper"); const constants_1 = require("../libs/constants"); const WebSocket = require("ws"); const WebsocketService_1 = require("../services/WebsocketService"); /** * Expressive Tea Application interface is the response from an started application, contains the express application * and a node http server instance. * @typedef {Object} ExpressiveTeaApplication * @property {Express} application - Express Application Instance * @property {HTTPServer} server - HTTP Server Object * @summary Application Interface */ /** * <b>Bootstrap Server Engine Class</b> is an abstract class to provide the Expressive Tea engine and bootstraps tools. * This is containing the logic and full functionality of Expressive Tea and only can be extended. * * @abstract * @class Boot * @summary Bootstrap Engine Class */ class Boot { constructor() { /** * Maintain a reference to Singleton instance of Settings, if settings still does not initialized it will created * automatically when extended class create a new instance. * * @type {Settings} * @public * @summary Server Settings instance reference */ this.settings = new Settings_1.default(); /** * Automatically create an Express application instance which will be user to configure over all the boot stages. * @type {Express} * @private * @readonly * @summary Express Application instance internal property. */ this.server = express(); this.settings.set('application', this.server); } /** * Bootstrap and verify that all the required plugins are correctly configured and proceed to attach all the * registered modules. <b>Remember</b> this is the unique method that must be decorated for the Register Module * decorator. * @summary Initialize and Bootstrap Server. * @returns {Promise<ExpressiveTeaApplication>} */ async start() { return new $P(async (resolver, rejector) => { try { const privateKey = this.settings.get('privateKey'); const certificate = this.settings.get('certificate'); const startWebsocket = this.settings.get('startWebsocket'); const detachWebsocket = this.settings.get('detachWebsocket'); const serverConfigQueue = []; // tslint:disable-next-line:one-variable-per-declaration let ws, wss; const server = http.createServer(this.server); const secureServer = privateKey && certificate && https.createServer({ cert: fs.readFileSync(certificate).toString('utf-8'), key: fs.readFileSync(privateKey).toString('utf-8') }, this.server); if (startWebsocket) { ws = new WebSocket.Server(detachWebsocket ? { noServer: true } : { server }); if (secureServer) { wss = new WebSocket.Server(detachWebsocket ? { noServer: true } : { server: secureServer }); } WebsocketService_1.default.init(ws, wss); WebsocketService_1.default.getInstance().setHttpServer(server); WebsocketService_1.default.getInstance().setHttpServer(secureServer); } await resolveProxyContainers(this); await resolveDirectives(this, this.server); await resolveStatic(this, this.server); for (const stage of constants_1.BOOT_ORDER) { await resolveStage(stage, this, this.server); } await resolveStage(constants_1.BOOT_STAGES.APPLICATION, this, this.server); serverConfigQueue.push(new $P(resolve => server.listen(this.settings.get('port'), () => { console.log(`Running HTTP Server on [${this.settings.get('port')}]`); resolve(); }))); if (secureServer) { serverConfigQueue.push(new $P(resolve => secureServer.listen(this.settings.get('securePort'), () => { console.log(`Running Secure HTTP Server on [${this.settings.get('securePort')}]`); resolve(); }))); } await $P.all([ resolveStage(constants_1.BOOT_STAGES.AFTER_APPLICATION_MIDDLEWARES, this, this.server), resolveStage(constants_1.BOOT_STAGES.ON_HTTP_CREATION, this, this.server, server, secureServer) ]); $P.all(serverConfigQueue) .then(() => { return resolveStage(constants_1.BOOT_STAGES.START, this, this.server, server, secureServer); }) .then(() => resolver({ application: this.server, server, secureServer })); } catch (e) { return rejector(e); } }); } } async function resolveStage(stage, ctx, server, ...extraArgs) { try { await bootloaderResolve(stage, server, ctx, ...extraArgs); if (stage === constants_1.BOOT_STAGES.APPLICATION) { await resolveModules(ctx, server); } } catch (e) { checkIfStageFails(e); } } async function resolveDirectives(instance, server) { const registeredDirectives = MetaData_1.default.get(constants_1.REGISTERED_DIRECTIVES_KEY, (0, object_helper_1.getClass)(instance)) || []; registeredDirectives.forEach((options) => { server.set.call(server, options.name, ...options.settings); }); } async function resolveStatic(instance, server) { const registeredStatic = MetaData_1.default.get(constants_1.REGISTERED_STATIC_KEY, (0, object_helper_1.getClass)(instance)) || []; registeredStatic.forEach((staticOptions) => { if (staticOptions.virtual) { server.use(staticOptions.virtual, express.static(staticOptions.root, staticOptions.options)); } else { server.use(express.static(staticOptions.root, staticOptions.options)); } }); } async function resolveModules(instance, server) { const registeredModules = MetaData_1.default.get(constants_1.REGISTERED_MODULE_KEY, instance, 'start') || []; registeredModules.forEach(Module => { const moduleInstance = new Module(); moduleInstance.__register(server); }); } async function bootloaderResolve(STAGE, server, instance, ...args) { const bootLoader = MetaData_1.default.get(constants_1.BOOT_STAGES_KEY, (0, object_helper_1.getClass)(instance)) || constants_1.STAGES_INIT; for (const loader of bootLoader[STAGE] || []) { try { await selectLoaderType(loader, server, ...args); } catch (e) { shouldFailIfRequire(e, loader); } } } async function resolveProxyContainers(context) { const ProxyContainers = MetaData_1.default.get(constants_1.ROUTER_PROXIES_KEY, (0, object_helper_1.getClass)(context)) || []; for (const Container of ProxyContainers) { resolveProxy(Container, context.server); } } async function resolveProxy(ProxyContainer, server) { const proxyContainer = new ProxyContainer(); proxyContainer.__register(server); } exports.resolveProxy = resolveProxy; async function selectLoaderType(loader, server, ...args) { return loader.method(server, ...args); } function checkIfStageFails(e) { if (e instanceof BootLoaderExceptions_1.BootLoaderSoftExceptions) { console.info(e.message); } else { console.error(e.message); // Re Throwing Error to Get it a top level. throw e; } } function shouldFailIfRequire(e, loader) { const failMessage = `Failed [${loader.name}]: ${e.message}`; if (!loader || loader.required) { throw new BootLoaderExceptions_1.BootLoaderRequiredExceptions(failMessage); } throw new BootLoaderExceptions_1.BootLoaderSoftExceptions(`${failMessage} and will be not enabled`); } exports.default = Boot;