UNPKG

lynx-framework

Version:

lynx is a NodeJS framework for Web Development, based on decorators and the async/await support.

855 lines (854 loc) 59 kB
"use strict"; 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 __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __spreadArrays = (this && this.__spreadArrays) || function () { for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; for (var r = Array(s), k = 0, i = 0; i < il; i++) for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) r[k] = a[j]; return r; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.app = exports.getLanguageFromRequest = exports.isProduction = void 0; var express = require("express"); var http = require('http'); var nunjucks = require("nunjucks"); var fs = require("fs"); require("reflect-metadata"); var typeorm_1 = require("typeorm"); var session = require("express-session"); var bodyParser = require("body-parser"); var multer = require("multer"); var moment = require("moment"); var flash = require('express-flash'); var migration_entity_1 = require("./entities/migration.entity"); var error_controller_1 = require("./error.controller"); var expressGenerator = require("./express-generator"); var setup_1 = require("./entities/setup"); var jsonwebtoken_1 = require("jsonwebtoken"); var translations = {}; var routes = {}; var logger_1 = require("./logger"); var api_response_wrapper_1 = require("./api-response-wrapper"); var decorators_1 = require("./templating/decorators"); /** * Utility function to check if we are in the production environment. * @return true if the NODE_ENV is set to "production", false otherwise */ function isProduction() { return process.env.NODE_ENV === 'production'; } exports.isProduction = isProduction; /** * Retrieve the preferred language from an express request, using the accepts-languages * header value. * @param req the express request * @return the two letter lower-cased language, or "*" (wildcard), or null */ function getLanguageFromRequest(req) { var lang = req.acceptsLanguages()[0]; if (lang.indexOf('-') !== -1) { lang = lang.split('-')[0]; } if (lang) { lang = lang.trim().toLowerCase(); } return lang; } exports.getLanguageFromRequest = getLanguageFromRequest; /** * This function shall be called with the nunjucks environment as self parameter! * It retrieve the language of the current request, using the default * language set in the app as fallback. */ function retrieveLanguage(self) { var lang = null; try { var req = self.ctx.req; lang = getLanguageFromRequest(req); if (lang === '*') { lang = null; } } catch (e) { } try { if (!lang) { lang = self.getVariables()['lang']; } if (!lang) { var app_1 = self.ctx.req.app.get('app'); lang = app_1.config.defaultLanguage; } } catch (e) { lang = exports.app.config.defaultLanguage; } return lang; } /** * Implementation of the tr filer for the nunjucks engine. * It trying to understand the current language from the request. The fallback * uses the defaultLanguage set on the app. */ function translate(str) { try { var lang = retrieveLanguage(this); return performTranslation(str, translations[lang]); } catch (e) { logger_1.logger.info(e); logger_1.logger.info(this); } return str; } function performTranslation(str, translations) { var translation = translations[str]; if (translation !== null && translation !== undefined) { return translation; } var start = str.indexOf('{{'); var end = str.indexOf('}}'); if (start != -1 && end != -1) { var key = str.substring(start + 2, end); translation = translations[key.trim()]; return str.replace('{{' + key + '}}', translation); } return str; } /** * Implementation of the date filter using moment. * The default implementation uses the "lll" string format, resulting in * Feb 19, 2018 4:57 PM in English. * @param d the date to format * @param format the string to format the date, default to lll * @return the formatted date */ function date(d, format) { if (!d) { return ''; } var lang = retrieveLanguage(this); var m = moment(d).locale(lang); if (!format) { format = 'lll'; } return m.format(format); } /** * Apply parameters to an URL. If a parameter is not found as path parameter, * it is added as query parameter. * @param url the url to compile * @param parameters a plain object containing the parameters * @return the compiled url */ function applyParametersToUrl(url, parameters) { if (!parameters) { return url; } if (url.indexOf('?') == -1) { url += '?'; } else { url += '&'; } for (var key in parameters) { if (url.indexOf(':' + key) == -1) { url += key + "=" + parameters[key] + "&"; } else { url = url.replace(':' + key, parameters[key]); } } if (url.endsWith('?') || url.endsWith('&')) { url = url.substring(0, url.length - 1); } return url; } /** * Transform the route name to a URL and compile it with the given parameters. * @param name the route name (or, eventually, the path) * @param parameters a plain object containing the parameters * @return the compiled url */ function route(name, parameters) { var url = name; if (routes[name]) { url = routes[name]; } return applyParametersToUrl(url, parameters); } /** * Implementation of the old filter function. This function returns the previous * value of the input form. Fallback to the defaultValue. * @param name the name used in the form * @param defaultValue a fallback value * @return the previous value or defaultValue */ function old(name, defaultValue) { var req = this.ctx.req; if (req.body && req.body[name]) { return req.body[name]; } if (req.query[name]) { return req.query[name]; } return defaultValue; } /** * Implementation of the format filter function to format a float number. * By default, the number is formatted with 2 decimal numbers. * @param val the number to format * @param decimal the number of decimal number * @return the formatted number as a string */ function format(val, decimal) { if (decimal === void 0) { decimal = 2; } return Number(val).toFixed(decimal); } /** * Implementation of the resolvePath global function. Using this function, it is * possible to refer to any views with a virtual folder containing all the available * views. * @param path the virtual absolute path of the view * @return the absolute path of the view if resolved, or the original path otherwise */ function resolvePath(path) { var normalizedPath = path; if (normalizedPath.endsWith('.njk')) { normalizedPath = normalizedPath.substring(0, normalizedPath.length - 4); } var _app; try { _app = this.ctx.req.app.get('app'); } catch (e) { _app = exports.app; } var resolved = _app.templateMap[path]; if (resolved) { return resolved; } return path; } /** * This function returns the current server url, with the used protocol and port. * This function is available as global function on nunjucks. */ function currentHost() { var req = this.ctx.req; return req.protocol + '://' + req.get('host'); } /** * The App class contains the initialization code for a Lynx application. */ var App = /** @class */ (function () { function App(config, modules) { var _this = this; this._modules = new Set(); this.apiResponseWrapper = new api_response_wrapper_1.DefaultAPIResponseWrapper(); this._config = config; exports.app = this; if (!config.onlyModules) { logger_1.logger.warn('LEGACY MODE: loading both modules and a current application context is now deprecated.'); logger_1.logger.warn('LEGACY MODE: please reorganize your project folder in order to have ONLY modules, also for your current application context!'); } if (modules) { this._modules = new Set(modules); this._modules.forEach(function (module) { return module.mount(_this._config); }); } config.db.entities.unshift(__dirname + '/entities/*.entity.js'); config.middlewaresFolders.unshift(__dirname + '/middlewares'); config.viewFolders.unshift(__dirname + '/views'); if (!config.disabledDb) { typeorm_1.createConnection(config.db) .then(function (_) { // here you can start to work with your entities logger_1.logger.info('Connection to the db established!'); setup_1.setup(config.db.entities) .then(function (_) { _this._modules.forEach(function (module) { return module.onDatabaseConnected(); }); if (!config.disableMigrations) { _this.executeMigrations() .catch(function (err) { logger_1.logger.error(err); process.exit(1); }) .then(function () { if (_this._config.onDatabaseInit) { _this._config.onDatabaseInit(); } }); } else if (_this._config.onDatabaseInit) { _this._config.onDatabaseInit(); } }) .catch(function (error) { logger_1.logger.error(error); process.exit(1); }); }) .catch(function (error) { logger_1.logger.error(error); process.exit(1); }); } else { logger_1.logger.debug('The DB service is disabled'); } this.express = express(); this.httpServer = http.createServer(this.express); this.express.set('app', this); this.express.use(function (_, res, next) { res.setHeader('X-Powered-By', 'lynx-framework/express'); next(); }); for (var _i = 0, _a = this.config.globalInterceptors; _i < _a.length; _i++) { var interceptor = _a[_i]; if (interceptor.onlyFor) { this.express.use(interceptor.onlyFor, interceptor.cb); } else { this.express.use(interceptor.cb); } } if (this.config.jsonLimit) { this.express.use(bodyParser.json({ limit: this.config.jsonLimit })); } else { this.express.use(bodyParser.json()); } this.express.use(bodyParser.urlencoded({ extended: true })); var app_session_options = { secret: config.sessionSecret, resave: false, saveUninitialized: true, }; if (config.sessionStore) { app_session_options.store = config.sessionStore; } this._appSession = session(app_session_options); this.express.use(this._appSession); this.express.use(flash()); this._upload = multer({ dest: config.uploadPath }); fs.exists(config.cachePath, function (exists) { if (!exists) { fs.mkdir(config.cachePath, function (err) { if (err) { logger_1.logger.error('Error creating the local cache directory', err); } }); } }); for (var _b = 0, _c = config.publicFolders; _b < _c.length; _b++) { var folder = _c[_b]; this.express.use(express.static(folder)); } this.generateTemplateMap(config.viewFolders); this._nunjucksEnvironment = nunjucks.configure(config.viewFolders, { autoescape: true, watch: true, express: this.express, }); this._nunjucksEnvironment.addFilter('tr', translate); this._nunjucksEnvironment.addFilter('json', JSON.stringify); this._nunjucksEnvironment.addFilter('format', format); this._nunjucksEnvironment.addFilter('date', date); this.loadTranslations(config.translationFolders); this._nunjucksEnvironment.addGlobal('route', route); this._nunjucksEnvironment.addGlobal('old', old); this._nunjucksEnvironment.addGlobal('resolvePath', resolvePath); this._nunjucksEnvironment.addGlobal('currentHost', currentHost); for (var _d = 0, _e = config.templatingFolders; _d < _e.length; _d++) { var path = _e[_d]; this.loadTemplating(path); } decorators_1.initializeTemplating(this); for (var _f = 0, _g = config.middlewaresFolders; _f < _g.length; _f++) { var path = _g[_f]; this.loadMiddlewares(path); } for (var _h = 0, _j = config.controllersFolders; _h < _j.length; _h++) { var path = _j[_h]; this.loadControllers(path); } this._errorController = new error_controller_1.default(this); this._mailClient = config.mailFactoryConstructor(); this._mailClient.init().catch(function (err) { logger_1.logger.warn('Error trying to initialize the mailClient', err); }); this._modules.forEach(function (module) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, module.onAppReady(this)]; case 1: return [2 /*return*/, _a.sent()]; } }); }); }); } Object.defineProperty(App.prototype, "config", { get: function () { return this._config; }, enumerable: false, configurable: true }); Object.defineProperty(App.prototype, "templateMap", { get: function () { return this._templateMap; }, enumerable: false, configurable: true }); Object.defineProperty(App.prototype, "nunjucksEnvironment", { get: function () { return this._nunjucksEnvironment; }, enumerable: false, configurable: true }); Object.defineProperty(App.prototype, "upload", { get: function () { return this._upload; }, enumerable: false, configurable: true }); Object.defineProperty(App.prototype, "mailClient", { get: function () { return this._mailClient; }, enumerable: false, configurable: true }); Object.defineProperty(App.prototype, "appSession", { get: function () { return this._appSession; }, enumerable: false, configurable: true }); Object.defineProperty(App.prototype, "customResizeFunction", { /** * If not null, contains a custom function that perform the resizing of an image */ get: function () { return this._customResizeFunction; }, /** * Set a new image resizing function, that will be executed automatically when a new file is downloaded. * The cache system will automatically work; this function shall only execute the image resizing. */ set: function (resizeFunction) { this._customResizeFunction = resizeFunction; }, enumerable: false, configurable: true }); Object.defineProperty(App.prototype, "customErrorController", { /** * This property allow the customization of the standard error controller. * You need to create the controller using its standard constructor: * new MyCustomErrorController(app) */ set: function (ctrl) { this._errorController = ctrl; }, enumerable: false, configurable: true }); App.prototype.recursiveGenerateTemplateMap = function (path, currentPath) { var files = fs.readdirSync(path); for (var index in files) { var currentFilePath = path + '/' + files[index]; if (fs.lstatSync(currentFilePath).isDirectory()) { this.recursiveGenerateTemplateMap(currentFilePath, currentPath + files[index] + '/'); continue; } var name = files[index].replace('.njk', ''); this._templateMap[currentPath + name] = currentFilePath; } }; App.prototype.generateTemplateMap = function (paths) { this._templateMap = {}; for (var _i = 0, paths_1 = paths; _i < paths_1.length; _i++) { var path = paths_1[_i]; this.recursiveGenerateTemplateMap(path, '/'); } }; App.prototype.recursiveExecuteMigrations = function (path) { return __awaiter(this, void 0, void 0, function () { var files, _a, _b, _i, index, currentFilePath, m, entity, migration, e_1; return __generator(this, function (_c) { switch (_c.label) { case 0: if (!fs.existsSync(path)) { logger_1.logger.warn('The migration folder ' + path + " doesn't exists!"); return [2 /*return*/]; } files = fs.readdirSync(path).sort(function (a, b) { return a.localeCompare(b); }); _a = []; for (_b in files) _a.push(_b); _i = 0; _c.label = 1; case 1: if (!(_i < _a.length)) return [3 /*break*/, 13]; index = _a[_i]; currentFilePath = path + '/' + files[index]; if (!fs.lstatSync(currentFilePath).isDirectory()) return [3 /*break*/, 3]; return [4 /*yield*/, this.recursiveExecuteMigrations(currentFilePath)]; case 2: _c.sent(); return [3 /*break*/, 12]; case 3: if (currentFilePath.endsWith('ts')) return [3 /*break*/, 12]; m = require(currentFilePath); if (!m.default) { throw new Error('Please define the migration as the export default class in file ' + currentFilePath + '.'); } return [4 /*yield*/, migration_entity_1.default.findByName(currentFilePath)]; case 4: entity = _c.sent(); if (entity && entity.wasExecuted()) { return [3 /*break*/, 12]; } if (!!entity) return [3 /*break*/, 6]; entity = new migration_entity_1.default(); entity.name = currentFilePath; return [4 /*yield*/, entity.save()]; case 5: _c.sent(); _c.label = 6; case 6: migration = new m.default(); _c.label = 7; case 7: _c.trys.push([7, 10, , 12]); return [4 /*yield*/, migration.up()]; case 8: _c.sent(); entity.setExecuted(); return [4 /*yield*/, entity.save()]; case 9: _c.sent(); logger_1.logger.info('Migration ' + currentFilePath + ' executed!'); return [3 /*break*/, 12]; case 10: e_1 = _c.sent(); entity.setFailed(); return [4 /*yield*/, entity.save()]; case 11: _c.sent(); logger_1.logger.error('Error executing the migration ' + currentFilePath); throw e_1; case 12: _i++; return [3 /*break*/, 1]; case 13: return [2 /*return*/]; } }); }); }; /** * This method will execute the migrations. * By default, this method will be executed automatically during the app * startup. In some scenario, like hight-scalability, this behaviour could * be unwanted. Thus, it is possibly otherwise to explicitly call this method * in some other way (for example, connecting it to a standard http route). */ App.prototype.executeMigrations = function () { return __awaiter(this, void 0, void 0, function () { var _i, _a, path; return __generator(this, function (_b) { switch (_b.label) { case 0: _i = 0, _a = this._config.migrationsFolders; _b.label = 1; case 1: if (!(_i < _a.length)) return [3 /*break*/, 4]; path = _a[_i]; return [4 /*yield*/, this.recursiveExecuteMigrations(path)]; case 2: _b.sent(); _b.label = 3; case 3: _i++; return [3 /*break*/, 1]; case 4: return [2 /*return*/]; } }); }); }; App.prototype.loadTranslations = function (paths) { for (var _i = 0, paths_2 = paths; _i < paths_2.length; _i++) { var path = paths_2[_i]; var files = fs.readdirSync(path); for (var index in files) { var nameWithExtension = files[index]; if (!nameWithExtension.endsWith('json')) continue; var name = nameWithExtension.substring(0, nameWithExtension.indexOf('.')); var tmp = JSON.parse(fs.readFileSync(path + '/' + nameWithExtension, 'utf8')); if (!translations[name]) { translations[name] = {}; } for (var key in tmp) { translations[name][key] = tmp[key]; } } } }; App.prototype.loadMiddlewares = function (path) { if (!fs.existsSync(path)) { logger_1.logger.warn('The middleares folder ' + path + " doesn't exists!"); return; } var middlewares = fs.readdirSync(path); for (var index in middlewares) { var currentFilePath = path + '/' + middlewares[index]; if (fs.lstatSync(currentFilePath).isDirectory()) { this.loadMiddlewares(currentFilePath); continue; } if (middlewares[index].endsWith('ts')) continue; var midd = require(currentFilePath); if (!midd.default) { throw new Error('Please define the middleware as the export default class in file ' + currentFilePath + '.'); } expressGenerator.useMiddleware(this, midd.default); } }; App.prototype.loadControllers = function (path) { var files = fs.readdirSync(path); for (var index in files) { var currentFilePath = path + '/' + files[index]; if (fs.lstatSync(currentFilePath).isDirectory()) { this.loadControllers(currentFilePath); continue; } if (files[index].endsWith('ts')) continue; var ctrl = require(currentFilePath); if (!ctrl.default) { throw new Error('Please define the controller as the export default class in file ' + currentFilePath + '.'); } expressGenerator.useController(this, ctrl.default, routes); } }; App.prototype.loadTemplating = function (path) { if (!fs.existsSync(path)) { return; } var files = fs.readdirSync(path); for (var index in files) { var currentFilePath = path + '/' + files[index]; if (fs.lstatSync(currentFilePath).isDirectory()) { this.loadTemplating(currentFilePath); continue; } if (files[index].endsWith('ts')) continue; require(currentFilePath); } }; App.prototype.startServer = function (port) { var _this = this; this.express.use(function (req, res) { _this._errorController .onNotFound(req) .then(function (r) { if (!res.headersSent) { res.status(404); } r.performResponse(req, res); }) .catch(function (err) { if (!res.headersSent) { res.status(404); } res.send(err); }); }); this.express.use(function (error, req, res, _) { _this._errorController .onError(error, req) .then(function (r) { if (!res.headersSent) { res.status(500); } r.performResponse(req, res); }) .catch(function (err) { if (!res.headersSent) { res.status(500); } res.send(err); }); }); this.httpServer.listen(port, function () { logger_1.logger.info("server is listening on " + port); }); }; /** * Proxy method to the usual `route` method available from the controller * @param name the name of the route * @param parameters an object containing the parameters of the route * @returns the compiled url */ App.prototype.route = function (name, parameters) { return route(name, parameters); }; /** * Request the translation of a string * @param str the string to be translated * @param req the request from which infer the language * @param language optionally, the language can be forced using this variable * @returns the translated string */ App.prototype.translate = function (str, req, language) { try { var lang = language !== null && language !== void 0 ? language : getLanguageFromRequest(req); if (!lang) { lang = this._config.defaultLanguage; } return performTranslation(str, translations[lang]); } catch (e) { logger_1.logger.info(e); } return str; }; /** * Request the translation of a string, formatted with parameters. * Each parameter should be encoded as {0}, {1}, etc... * @param str the string key to be translated * @param req the original request * @param language optionally, the language can be forced using this variable * @param args the arguments to format the string */ App.prototype.translateFormat = function (str, req, language) { var args = []; for (var _i = 3; _i < arguments.length; _i++) { args[_i - 3] = arguments[_i]; } var translated = this.translate(str, req, language); return this.format.apply(this, __spreadArrays([translated], args)); }; App.prototype.format = function (fmt) { var args = []; for (var _i = 1; _i < arguments.length; _i++) { args[_i - 1] = arguments[_i]; } if (!fmt.match(/^(?:(?:(?:[^{}]|(?:\{\{)|(?:\}\}))+)|(?:\{[0-9]+\}))+$/)) { throw new Error('invalid format string.'); } return fmt.replace(/((?:[^{}]|(?:\{\{)|(?:\}\}))+)|(?:\{([0-9]+)\})/g, function (_, str, index) { if (str) { return str.replace(/(?:{{)|(?:}})/g, function (m) { return m[0]; }); } else { if (index >= args.length) { throw new Error('argument index is out of range in format'); } return args[index]; } }); }; /** * Utility method to generate an user token * @param user the user * @returns the token for the user */ App.prototype.generateTokenForUser = function (user) { return jsonwebtoken_1.sign({ id: user.id }, this._config.tokenSecret, { expiresIn: '1y', }); }; return App; }()); exports.default = App; Object.defineProperty(Array.prototype, 'serialize', { enumerable: false, configurable: true, value: function () { var r = []; for (var _i = 0, _a = this; _i < _a.length; _i++) { var el = _a[_i]; if (el.serialize) { r.push(el.serialize()); } else { r.push(el); } } return r; }, }); Object.defineProperty(Array.prototype, 'addHiddenField', { enumerable: false, configurable: true, value: function (field) { for (var _i = 0, _a = this; _i < _a.length; _i++) { var el = _a[_i]; if (el.addHiddenField) { el.addHiddenField(field); } } }, }); Object.defineProperty(Array.prototype, 'removeHiddenField', { enumerable: false, configurable: true, value: function (field) { for (var _i = 0, _a = this; _i < _a.length; _i++) { var el = _a[_i]; if (el.removeHiddenField) { el.removeHiddenField(field); } } }, }); //# sourceMappingURL=data:application/json;charset=utf8;base64,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