"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Server = void 0; const express_1 = __importDefault(require("express")); const http_1 = require("../base/http"); const exceptions_1 = require("../base/exceptions"); const exceptions_2 = require("../base/exceptions"); const logger_1 = require("../base/logger"); const i18n_1 = require("../base/i18n"); const path_1 = __importDefault(require("path")); const fs_1 = __importDefault(require("fs")); const http_2 = __importDefault(require("http")); const ws_1 = require("ws"); const exceptions_3 = require("../base/exceptions"); const exceptions_4 = require("../base/exceptions"); const exceptions_5 = require("../base/exceptions"); const websocket_1 = require("../base/websocket"); const swagger_jsdoc_1 = __importDefault(require("swagger-jsdoc")); const swagger_ui_express_1 = __importDefault(require("swagger-ui-express")); /** @sealed */ class Server { constructor(properties) { var _a, _b, _c, _d, _e; this.instance = (0, express_1.default)(); this.httpServer = http_2.default.createServer(this.instance); this.port = properties.port; this.host = properties.host || `http://localhost:${this.port}`; this.i18n = i18n_1.i18nLoader.getInstance(); if (properties.locale) this.i18n.setLocale(properties.locale); this.logger = new logger_1.Logger(); this.httpHandlers = {}; this.wsHandlers = properties.wsHandlers || {}; this.wsServers = {}; this.i18nPath = properties.i18nPath; this.middlewaresPath = properties.middlewaresPath; this.routesPath = properties.routesPath; this.viewEngine = properties.viewEngine; this.viewsPath = properties.viewsPath; this.options = properties.options; if (properties.swagger) { this.swaggerDocsPath = (_a = properties.swagger) === null || _a === void 0 ? void 0 : _a.docsPath; this.swaggerTitle = ((_b = properties.swagger) === null || _b === void 0 ? void 0 : _b.title) || 'API Documentation'; this.swaggerDescription = ((_c = properties.swagger) === null || _c === void 0 ? void 0 : _c.description) || 'API Documentation'; this.swaggerApiVersion = ((_d = properties.swagger) === null || _d === void 0 ? void 0 : _d.version) || '1.0.0'; this.swaggerRoute = ((_e = properties.swagger) === null || _e === void 0 ? void 0 : _e.route) || '/api-docs'; } this.initialized = false; } init() { return __awaiter(this, void 0, void 0, function* () { if (this.viewEngine) this.instance.set('view engine', this.viewEngine); if (this.viewsPath) this.instance.set('views', this.viewsPath); this.i18n.load(path_1.default.resolve(__dirname, Server.i18nDefaultPath)); yield this.registerMiddlewares(path_1.default.resolve(__dirname, Server.defaultMiddlewaresPath)); yield this.registerRoutes(path_1.default.resolve(__dirname, Server.defaultRoutesPath)); yield this.postInit(); this.initialized = true; return this; }); } postInit() { return __awaiter(this, void 0, void 0, function* () { if (this.i18nPath) this.i18n.load(this.i18nPath); if (this.middlewaresPath) yield this.registerMiddlewares(this.middlewaresPath); if (this.routesPath) yield this.registerRoutes(this.routesPath); if (this.swaggerDocsPath) fs_1.default.writeFileSync(this.swaggerDocsPath, ''); if (Object.keys(this.wsHandlers).length > 0) { this.registerWsServers(); this.applyWsHandlers(); } }); } processHttpHandlers() { const handlers = []; for (const key in this.httpHandlers) handlers.push(this.httpHandlers[key]); handlers.sort((a, b) => a.getOrder() - b.getOrder()); for (const handler of handlers) { if (handler instanceof http_1.Middleware) this.addMiddleware(handler); else if (handler instanceof http_1.Route) this.addRoute(handler); } } addMiddleware(middleware) { if (middleware.getRoute() != null) this.instance.use(middleware.getRoute(), middleware.getAction()); else this.instance.use(middleware.getAction()); return this; } addRoute(route) { if (route.getRoute() == null) throw new exceptions_1.RouteNotSetException(); switch (route.getMethod()) { case http_1.HttpMethod.GET: return this.get(route); case http_1.HttpMethod.POST: return this.post(route); default: throw new exceptions_2.IncorrectMethodException(); } } registerRoutesDocumentation() { for (const routeName in this.httpHandlers) { const route = this.httpHandlers[routeName]; if (!(route instanceof http_1.Route)) continue; if (route.getRoute() == null) throw new exceptions_1.RouteNotSetException(); if (![http_1.HttpMethod.GET, http_1.HttpMethod.POST].includes(route.getMethod())) throw new exceptions_2.IncorrectMethodException(); const docs = route.getDocumentation(); if (this.swaggerDocsPath && docs.length > 0) { fs_1.default.appendFileSync(this.swaggerDocsPath, `${docs}\n`); this.logInfo(`Swagger documentation for route '${route.getRoute()}' generated!`); } } return this; } registerWsServers() { for (const url of Object.keys(this.wsHandlers)) { const wsServer = this.wsServers[url] = new ws_1.Server({ noServer: true }); const wsHandler = this.wsHandlers[url]; wsServer.on(websocket_1.WebSocketHandler.Event.CONNECTION, (ws) => { wsHandler.onConnect(ws); ws.on(websocket_1.WebSocketHandler.Event.MESSAGE, wsHandler.onMessage); ws.on(websocket_1.WebSocketHandler.Event.ERROR, wsHandler.onError); ws.on(websocket_1.WebSocketHandler.Event.CLOSE, wsHandler.onClose); }); } return this; } applyWsHandlers() { this.httpServer.on('upgrade', (request, socket, head) => { var _a; const url = (_a = request.url) === null || _a === void 0 ? void 0 : _a.split('?')[0]; if (url && this.wsHandlers[url] && this.wsServers[url]) { const wsServer = this.wsServers[url]; wsServer.handleUpgrade(request, socket, head, (ws) => { wsServer.emit(websocket_1.WebSocketHandler.Event.CONNECTION, ws, request); }); } else { socket.destroy(); } }); return this; } getWsConnections(url) { const wsServer = this.wsServers[url]; return wsServer ? wsServer.clients : null; } logInfo(message) { this.logger.getLogger().info(message); } logError(message) { this.logger.getLogger().error(message); } logWarn(message) { this.logger.getLogger().warn(message); } log(message) { switch (message.type) { case logger_1.MessageTypes.WARNING: return this.logWarn(message.text); case logger_1.MessageTypes.ERROR: return this.logError(message.text); default: return this.logInfo(message.text); } } get(route) { this.instance.get(route.getRoute(), route.getAction()); return this; } post(route) { this.instance.post(route.getRoute(), route.getAction()); return this; } registerSwaggerMiddleware() { if (!this.swaggerDocsPath) return this; const swaggerOptions = { swaggerDefinition: { openapi: '3.0.0', info: { title: this.swaggerTitle, version: this.swaggerApiVersion, description: this.swaggerDescription, }, servers: [ { url: this.host } ], }, apis: [this.swaggerDocsPath], }; const swaggerDocs = (0, swagger_jsdoc_1.default)(swaggerOptions); this.instance.use(this.swaggerRoute, swagger_ui_express_1.default.serve, swagger_ui_express_1.default.setup(swaggerDocs)); return this; } registerRoutes(dir) { return __awaiter(this, void 0, void 0, function* () { const files = fs_1.default.readdirSync(dir, { recursive: true, encoding: 'utf8' }); for (const file of files) { if (/\.js$/.test(file)) { const { default: RouteClass } = yield Promise.resolve(`${path_1.default.join(dir, file)}`).then(s => __importStar(require(s))); if (RouteClass.prototype instanceof http_1.Route) { this.httpHandlers[RouteClass.name] = new RouteClass(this); } else throw new exceptions_4.InvalidRouteException(file); } } return this; }); } registerMiddlewares(dir) { return __awaiter(this, void 0, void 0, function* () { const files = fs_1.default.readdirSync(dir, { recursive: true, encoding: 'utf8' }); for (const file of files) { if (/\.js$/.test(file)) { const { default: MiddlewareClass } = yield Promise.resolve(`${path_1.default.join(dir, file)}`).then(s => __importStar(require(s))); if (MiddlewareClass.prototype instanceof http_1.Middleware) { this.httpHandlers[MiddlewareClass.name] = new MiddlewareClass(this); } else throw new exceptions_3.InvalidMiddlewareException(file); } } return this; }); } getLogger() { return this.logger; } i18nLoad(path) { this.i18n.load(path); return this; } getHost() { return this.host; } getOption(key) { return this.options ? this.options[key] || null : null; } start(callback) { if (!this.initialized) throw new exceptions_5.ServerNotInitializedException(); this.registerRoutesDocumentation(); this.registerSwaggerMiddleware(); this.processHttpHandlers(); const cb = () => { this.logInfo((0, i18n_1.$$)('org.crazydoctor.expressts.start', { 'port': this.port })); if (callback) callback(); }; this.httpServer.listen(this.port, cb); } } exports.Server = Server; Server.i18nDefaultPath = '../resources/i18n.json'; Server.defaultMiddlewaresPath = '../middlewares'; Server.defaultRoutesPath = '../routes'; //# sourceMappingURL=Server.js.map