UNPKG

lynx-framework

Version:

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

331 lines (329 loc) 21 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 }; } }; Object.defineProperty(exports, "__esModule", { value: true }); exports.BaseController = exports.FlashType = void 0; var render_response_1 = require("./render.response"); var redirect_response_1 = require("./redirect.response"); var skip_response_1 = require("./skip.response"); var unauthorized_response_1 = require("./unauthorized.response"); var file_response_1 = require("./file.response"); var media_entity_1 = require("./entities/media.entity"); var status_error_1 = require("./status-error"); var logger_1 = require("./logger"); var xml_response_1 = require("./xml.response"); var FlashType; (function (FlashType) { FlashType[FlashType["primary"] = 0] = "primary"; FlashType[FlashType["secondary"] = 1] = "secondary"; FlashType[FlashType["success"] = 2] = "success"; FlashType[FlashType["danger"] = 3] = "danger"; FlashType[FlashType["warning"] = 4] = "warning"; FlashType[FlashType["info"] = 5] = "info"; FlashType[FlashType["light"] = 6] = "light"; FlashType[FlashType["dark"] = 7] = "dark"; })(FlashType = exports.FlashType || (exports.FlashType = {})); function mapFlashTypeToString(type) { switch (type) { case FlashType.primary: return 'primary'; case FlashType.secondary: return 'secondary'; case FlashType.success: return 'success'; case FlashType.danger: return 'danger'; case FlashType.warning: return 'warning'; case FlashType.info: return 'info'; case FlashType.light: return 'light'; case FlashType.dark: return 'dark'; } } /** * This class defines the basic class for any controllers. It implements a lot * of utility methods in order to correctly generate any response. */ var BaseController = /** @class */ (function () { function BaseController(app) { this.logger = logger_1.logger; this.app = app; } Object.defineProperty(BaseController.prototype, "metadata", { get: function () { return this._metadata; }, enumerable: false, configurable: true }); /** * This method is called only when the constructed has been completed. * Since this method is async, it can be used to perform some initialization * that needed the use of the await keyword. */ BaseController.prototype.postConstructor = function () { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/]; }); }); }; /** * Add a value to the current request context. * Any variable added with this method will available in the template context * thought the @method render method. * @param req the current Request * @param key the key of the value to add * @param value the value to add */ BaseController.prototype.addToContext = function (req, key, value) { if (!req.lynxContext) { req.lynxContext = {}; } req.lynxContext[key] = value; }; /** * Utility method to generate an error with a status code. * This method should be used instead of the usual throw new Error(msg). * In this way, a proper HTTP status code can be used (for example, 404 or 500), * instead of the default 400. * @param status the http status code to return * @param message the error message * @return a new @type StatusError object */ BaseController.prototype.error = function (status, message) { var err = new status_error_1.default(message); err.statusCode = status; return err; }; /** * This method generates a url to a route starting from the route name and * optionally its parameters. * If a parameter is not used to generate the route url, it will be appended * as a query parameter. * @param name the name of the route * @param parameters a plain object containing the parameters for the route. */ BaseController.prototype.route = function (name, parameters) { return this.app.route(name, parameters); }; /** * Generate a web page starting from a template and using a generated context. * @param view the name of the view * @param req the request object * @param context a plain object containing any necessary data needed by the view */ BaseController.prototype.render = function (view, req, context) { if (!view.endsWith('.njk')) { view = view + '.njk'; } if (!context) { context = {}; } context.req = req; context.flash = req.session.sessionFlash; for (var key in req.lynxContext) { context[key] = req.lynxContext[key]; } delete req.session.sessionFlash; return new render_response_1.default(view, context); }; /** * Redirect the current route to another * @param routeName the new of the target route * @param routeParams a plain object containing the parameters for the route. */ BaseController.prototype.redirect = function (routeName, routeParams) { return new redirect_response_1.default(this.route(routeName, routeParams)); }; /** * Add a flash message in the current request. * @param msg the FlashMessage to be included * @param req the request */ BaseController.prototype.addFlashMessage = function (msg, req) { var session = req.session; if (!session.sessionFlash) { session.sessionFlash = []; } session.sessionFlash.push({ type: mapFlashTypeToString(msg.type), message: this.tr(msg.message, req), }); }; /** * Add a success flash message in the current request. * @param msg the string (can be localized) of the message * @param req the request */ BaseController.prototype.addSuccessMessage = function (msg, req) { this.addFlashMessage({ type: FlashType.success, message: msg }, req); }; /** * Add an error flash message in the current request. * @param msg the string (can be localized) of the message * @param req the request */ BaseController.prototype.addErrorMessage = function (msg, req) { this.addFlashMessage({ type: FlashType.danger, message: msg }, req); }; /** * Generate a response suitable to file download. This method can also be * used to generate images of specific dimensions. * @param path the string path of the file, or a Media object to be downloaded * @param options options to correctly generate the output file */ BaseController.prototype.download = function (path, options) { var f; if (path instanceof media_entity_1.default) { if (path.isDirectory) { throw new Error('unable to download a directory'); } f = new file_response_1.default(path.fileName); f.contentType = path.mimetype; if (path.originalName) { f.fileName = path.originalName; } } else { f = new file_response_1.default(path); } if (options) { f.options = options; } return f; }; /** * @deprecated use the `throw this.error(401, 'unauthorized') instead. * Generate an unauthorized response. */ BaseController.prototype.unauthorized = function () { return new unauthorized_response_1.default(); }; /** * Generate a skip response. In this particular case, the original Express `next()` * will be executed, causing the controller chain to continue its execution. */ BaseController.prototype.next = function () { return new skip_response_1.default(); }; /** * Generate a response as an Xml file, but starting from a standard Nunjucks template. * This response is very similar to the standard render response. The main difference is * the `contentType`, set to `application/xml`. * Moreover, the flash messages are ignored. * @param view the name of the view * @param req the request object * @param context a plain object containing any necessary data needed by the view */ BaseController.prototype.xml = function (view, req, context) { if (!view.endsWith('.njk')) { view = view + '.njk'; } if (!context) { context = {}; } context.req = req; for (var key in req.lynxContext) { context[key] = req.lynxContext[key]; } return new xml_response_1.default(view, context); }; /** * Utility method to send emails from a controller. * This method is similar to the `sendMail` method, but define a lower level API. * Indeed, it directly accepts the text and the html of the email, and not the templates urls. * @param dest the email destination (can also be an array of addresses) * @param subject the subject of the email * @param text the text version of the email * @param html the html version of the email */ BaseController.prototype.sendRawMail = function (dest, subject, text, html) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, this.app.mailClient.sendRawMail(dest, subject, text, html)]; }); }); }; /** * Utility method to send an email from a controller. This method is async, * so use the await keyword (or eventually a promise) to correctly read the * return value. * This method uses the template engine to compile the email. * NOTE: internally, this method uses the `sendRawMail` method. * @param req the current request * @param dest the email destination (can also be an array of addresses) * @param subjectTemplateString the subject of the email, that can also be a string template * @param textTemplate the text version of the email, referencing a path in the view folders * @param htmlTemplate the html version of the email, referencing a path in the view folders * @param context a plain object containing any necessary data needed by the view */ BaseController.prototype.sendMail = function (req, dest, subjectTemplateString, textTemplate, htmlTemplate, context) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, this.app.mailClient.sendMail(req, dest, subjectTemplateString, textTemplate, htmlTemplate, context)]; }); }); }; /** * Utility method to obtain a translated string. * @param str the string key to be translated * @param req the original request * @param language optionally, the language can be forced using this variable */ BaseController.prototype.tr = function (str, req, language) { return this.app.translate(str, req, language); }; /** * Utility method to obtain a translated 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 */ BaseController.prototype.trFormat = function (str, req, language) { var args = []; for (var _i = 3; _i < arguments.length; _i++) { args[_i - 3] = arguments[_i]; } return this.app.translateFormat(str, req, language, args); }; return BaseController; }()); exports.BaseController = BaseController; //# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL2x5bngvYmFzZS5jb250cm9sbGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUdBLHFEQUErQztBQUMvQyx5REFBbUQ7QUFDbkQsaURBQTJDO0FBQzNDLGlFQUEyRDtBQUMzRCxpREFBMkM7QUFHM0Msd0RBQTRDO0FBQzVDLCtDQUF5QztBQUV6QyxtQ0FBa0M7QUFFbEMsK0NBQXlDO0FBRXpDLElBQVksU0FTWDtBQVRELFdBQVksU0FBUztJQUNqQiwrQ0FBTyxDQUFBO0lBQ1AsbURBQVMsQ0FBQTtJQUNULCtDQUFPLENBQUE7SUFDUCw2Q0FBTSxDQUFBO0lBQ04sK0NBQU8sQ0FBQTtJQUNQLHlDQUFJLENBQUE7SUFDSiwyQ0FBSyxDQUFBO0lBQ0wseUNBQUksQ0FBQTtBQUNSLENBQUMsRUFUVyxTQUFTLEdBQVQsaUJBQVMsS0FBVCxpQkFBUyxRQVNwQjtBQUVELFNBQVMsb0JBQW9CLENBQUMsSUFBZTtJQUN6QyxRQUFRLElBQUksRUFBRTtRQUNWLEtBQUssU0FBUyxDQUFDLE9BQU87WUFDbEIsT0FBTyxTQUFTLENBQUM7UUFDckIsS0FBSyxTQUFTLENBQUMsU0FBUztZQUNwQixPQUFPLFdBQVcsQ0FBQztRQUN2QixLQUFLLFNBQVMsQ0FBQyxPQUFPO1lBQ2xCLE9BQU8sU0FBUyxDQUFDO1FBQ3JCLEtBQUssU0FBUyxDQUFDLE1BQU07WUFDakIsT0FBTyxRQUFRLENBQUM7UUFDcEIsS0FBSyxTQUFTLENBQUMsT0FBTztZQUNsQixPQUFPLFNBQVMsQ0FBQztRQUNyQixLQUFLLFNBQVMsQ0FBQyxJQUFJO1lBQ2YsT0FBTyxNQUFNLENBQUM7UUFDbEIsS0FBSyxTQUFTLENBQUMsS0FBSztZQUNoQixPQUFPLE9BQU8sQ0FBQztRQUNuQixLQUFLLFNBQVMsQ0FBQyxJQUFJO1lBQ2YsT0FBTyxNQUFNLENBQUM7S0FDckI7QUFDTCxDQUFDO0FBT0Q7OztHQUdHO0FBQ0g7SUFTSSx3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QztJQUVEOzs7Ozs7OztPQVFHO0lBQ0ksNEJBQUcsR0FBVixVQUFXLElBQVksRUFBRSxHQUFZLEVBQUUsT0FBYTtRQUNoRCxJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsRUFBRTtZQUN4QixJQUFJLEdBQUcsSUFBSSxHQUFHLE1BQU0sQ0FBQztTQUN4QjtRQUNELElBQUksQ0FBQyxPQUFPLEVBQUU7WUFDVixPQUFPLEdBQUcsRUFBRSxDQUFDO1NBQ2hCO1FBQ0QsT0FBTyxDQUFDLEdBQUcsR0FBRyxHQUFHLENBQUM7UUFDbEIsS0FBSyxJQUFJLEdBQUcsSUFBSSxHQUFHLENBQUMsV0FBVyxFQUFFO1lBQzdCLE9BQU8sQ0FBQyxHQUFHLENBQUMsR0FBRyxHQUFHLENBQUMsV0FBVyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1NBQ3ZDO1FBQ0QsT0FBTyxJQUFJLHNCQUFXLENBQUMsSUFBSSxFQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQzFDLENBQUM7SUFFRDs7Ozs7Ozs7T0FRRztJQUNVLG9DQUFXLEdBQXhCLFVBQ0ksSUFBdUIsRUFDdkIsT0FBZSxFQUNmLElBQVksRUFDWixJQUFZOzs7Z0JBRVosc0JBQU8sSUFBSSxDQUFDLEdBQUcsQ0FBQyxVQUFVLENBQUMsV0FBVyxDQUFDLElBQUksRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLElBQUksQ0FBQyxFQUFDOzs7S0FDckU7SUFFRDs7Ozs7Ozs7Ozs7O09BWUc7SUFDVSxpQ0FBUSxHQUFyQixVQUNJLEdBQW9CLEVBQ3BCLElBQXVCLEVBQ3ZCLHFCQUE2QixFQUM3QixZQUFvQixFQUNwQixZQUFvQixFQUNwQixPQUFZOzs7Z0JBRVosc0JBQU8sSUFBSSxDQUFDLEdBQUcsQ0FBQyxVQUFVLENBQUMsUUFBUSxDQUMvQixHQUFHLEVBQ0gsSUFBSSxFQUNKLHFCQUFxQixFQUNyQixZQUFZLEVBQ1osWUFBWSxFQUNaLE9BQU8sQ0FDVixFQUFDOzs7S0FDTDtJQUVEOzs7OztPQUtHO0lBQ0ksMkJBQUUsR0FBVCxVQUFVLEdBQVcsRUFBRSxHQUFZLEVBQUUsUUFBaUI7UUFDbEQsT0FBTyxJQUFJLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLFFBQVEsQ0FBQyxDQUFDO0lBQ2xELENBQUM7SUFFRDs7Ozs7OztPQU9HO0lBQ0ksaUNBQVEsR0FBZixVQUNJLEdBQVcsRUFDWCxHQUFZLEVBQ1osUUFBNEI7UUFDNUIsY0FBWTthQUFaLFVBQVksRUFBWixxQkFBWSxFQUFaLElBQVk7WUFBWiw2QkFBWTs7UUFFWixPQUFPLElBQUksQ0FBQyxHQUFHLENBQUMsZUFBZSxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsUUFBUSxFQUFFLElBQUksQ0FBQyxDQUFDO0lBQzlELENBQUM7SUFDTCxxQkFBQztBQUFELENBMVFBLEFBMFFDLElBQUE7QUExUVksd0NBQWMiLCJmaWxlIjoiYmFzZS5jb250cm9sbGVyLmpzIiwic291cmNlUm9vdCI6IiJ9