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
JavaScript
;
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