123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661 |
- "use strict";
- Object.defineProperty(exports, "__esModule", {
- value: true
- });
- exports.default = exports.PagesRouter = void 0;
- var _PromiseRouter = _interopRequireDefault(require("../PromiseRouter"));
- var _Config = _interopRequireDefault(require("../Config"));
- var _express = _interopRequireDefault(require("express"));
- var _path = _interopRequireDefault(require("path"));
- var _fs = require("fs");
- var _node = require("parse/node");
- var _Utils = _interopRequireDefault(require("../Utils"));
- var _mustache = _interopRequireDefault(require("mustache"));
- var _Page = _interopRequireDefault(require("../Page"));
- function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
- const pages = Object.freeze({
- passwordReset: new _Page.default({
- id: 'passwordReset',
- defaultFile: 'password_reset.html'
- }),
- passwordResetSuccess: new _Page.default({
- id: 'passwordResetSuccess',
- defaultFile: 'password_reset_success.html'
- }),
- passwordResetLinkInvalid: new _Page.default({
- id: 'passwordResetLinkInvalid',
- defaultFile: 'password_reset_link_invalid.html'
- }),
- emailVerificationSuccess: new _Page.default({
- id: 'emailVerificationSuccess',
- defaultFile: 'email_verification_success.html'
- }),
- emailVerificationSendFail: new _Page.default({
- id: 'emailVerificationSendFail',
- defaultFile: 'email_verification_send_fail.html'
- }),
- emailVerificationSendSuccess: new _Page.default({
- id: 'emailVerificationSendSuccess',
- defaultFile: 'email_verification_send_success.html'
- }),
- emailVerificationLinkInvalid: new _Page.default({
- id: 'emailVerificationLinkInvalid',
- defaultFile: 'email_verification_link_invalid.html'
- }),
- emailVerificationLinkExpired: new _Page.default({
- id: 'emailVerificationLinkExpired',
- defaultFile: 'email_verification_link_expired.html'
- })
- });
- const pageParams = Object.freeze({
- appName: 'appName',
- appId: 'appId',
- token: 'token',
- username: 'username',
- error: 'error',
- locale: 'locale',
- publicServerUrl: 'publicServerUrl'
- });
- const pageParamHeaderPrefix = 'x-parse-page-param-';
- const errors = Object.freeze({
- jsonFailedFileLoading: 'failed to load JSON file',
- fileOutsideAllowedScope: 'not allowed to read file outside of pages directory'
- });
- class PagesRouter extends _PromiseRouter.default {
-
- constructor(pages = {}) {
- super();
-
- this.pagesConfig = pages;
- this.pagesEndpoint = pages.pagesEndpoint ? pages.pagesEndpoint : 'apps';
- this.pagesPath = pages.pagesPath ? _path.default.resolve('./', pages.pagesPath) : _path.default.resolve(__dirname, '../../public');
- this.loadJsonResource();
- this.mountPagesRoutes();
- this.mountCustomRoutes();
- this.mountStaticRoute();
- }
- verifyEmail(req) {
- const config = req.config;
- const {
- username,
- token: rawToken
- } = req.query;
- const token = rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken;
- if (!config) {
- this.invalidRequest();
- }
- if (!token || !username) {
- return this.goToPage(req, pages.emailVerificationLinkInvalid);
- }
- const userController = config.userController;
- return userController.verifyEmail(username, token).then(() => {
- const params = {
- [pageParams.username]: username
- };
- return this.goToPage(req, pages.emailVerificationSuccess, params);
- }, () => {
- const params = {
- [pageParams.username]: username
- };
- return this.goToPage(req, pages.emailVerificationLinkExpired, params);
- });
- }
- resendVerificationEmail(req) {
- const config = req.config;
- const username = req.body.username;
- if (!config) {
- this.invalidRequest();
- }
- if (!username) {
- return this.goToPage(req, pages.emailVerificationLinkInvalid);
- }
- const userController = config.userController;
- return userController.resendVerificationEmail(username, req).then(() => {
- return this.goToPage(req, pages.emailVerificationSendSuccess);
- }, () => {
- return this.goToPage(req, pages.emailVerificationSendFail);
- });
- }
- passwordReset(req) {
- const config = req.config;
- const params = {
- [pageParams.appId]: req.params.appId,
- [pageParams.appName]: config.appName,
- [pageParams.token]: req.query.token,
- [pageParams.username]: req.query.username,
- [pageParams.publicServerUrl]: config.publicServerURL
- };
- return this.goToPage(req, pages.passwordReset, params);
- }
- requestResetPassword(req) {
- const config = req.config;
- if (!config) {
- this.invalidRequest();
- }
- const {
- username,
- token: rawToken
- } = req.query;
- const token = rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken;
- if (!username || !token) {
- return this.goToPage(req, pages.passwordResetLinkInvalid);
- }
- return config.userController.checkResetTokenValidity(username, token).then(() => {
- const params = {
- [pageParams.token]: token,
- [pageParams.username]: username,
- [pageParams.appId]: config.applicationId,
- [pageParams.appName]: config.appName
- };
- return this.goToPage(req, pages.passwordReset, params);
- }, () => {
- const params = {
- [pageParams.username]: username
- };
- return this.goToPage(req, pages.passwordResetLinkInvalid, params);
- });
- }
- resetPassword(req) {
- const config = req.config;
- if (!config) {
- this.invalidRequest();
- }
- const {
- username,
- new_password,
- token: rawToken
- } = req.body;
- const token = rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken;
- if ((!username || !token || !new_password) && req.xhr === false) {
- return this.goToPage(req, pages.passwordResetLinkInvalid);
- }
- if (!username) {
- throw new _node.Parse.Error(_node.Parse.Error.USERNAME_MISSING, 'Missing username');
- }
- if (!token) {
- throw new _node.Parse.Error(_node.Parse.Error.OTHER_CAUSE, 'Missing token');
- }
- if (!new_password) {
- throw new _node.Parse.Error(_node.Parse.Error.PASSWORD_MISSING, 'Missing password');
- }
- return config.userController.updatePassword(username, token, new_password).then(() => {
- return Promise.resolve({
- success: true
- });
- }, err => {
- return Promise.resolve({
- success: false,
- err
- });
- }).then(result => {
- if (req.xhr) {
- if (result.success) {
- return Promise.resolve({
- status: 200,
- response: 'Password successfully reset'
- });
- }
- if (result.err) {
- throw new _node.Parse.Error(_node.Parse.Error.OTHER_CAUSE, `${result.err}`);
- }
- }
- const query = result.success ? {
- [pageParams.username]: username
- } : {
- [pageParams.username]: username,
- [pageParams.token]: token,
- [pageParams.appId]: config.applicationId,
- [pageParams.error]: result.err,
- [pageParams.appName]: config.appName
- };
- const page = result.success ? pages.passwordResetSuccess : pages.passwordReset;
- return this.goToPage(req, page, query, false);
- });
- }
-
- goToPage(req, page, params = {}, responseType) {
- const config = req.config;
-
- const redirect = config.pages.forceRedirect ? true : responseType !== undefined ? responseType : req.method == 'POST';
-
- const defaultParams = this.getDefaultParams(config);
- if (Object.values(defaultParams).includes(undefined)) {
- return this.notFound();
- }
- params = Object.assign(params, defaultParams);
-
-
-
- const locale = this.getLocale(req);
- params[pageParams.locale] = locale;
-
- const defaultFile = page.defaultFile;
- const defaultPath = this.defaultPagePath(defaultFile);
- const defaultUrl = this.composePageUrl(defaultFile, config.publicServerURL);
-
- const customUrl = config.pages.customUrls[page.id];
- if (customUrl && !_Utils.default.isPath(customUrl)) {
- return this.redirectResponse(customUrl, params);
- }
-
- let placeholders = {};
- if (config.pages.enableLocalization && config.pages.localizationJsonPath) {
- placeholders = this.getJsonPlaceholders(locale, params);
- }
-
- if (config.pages.enableLocalization && locale) {
- return _Utils.default.getLocalizedPath(defaultPath, locale).then(({
- path,
- subdir
- }) => redirect ? this.redirectResponse(this.composePageUrl(defaultFile, config.publicServerURL, subdir), params) : this.pageResponse(path, params, placeholders));
- } else {
- return redirect ? this.redirectResponse(defaultUrl, params) : this.pageResponse(defaultPath, params, placeholders);
- }
- }
-
- staticRoute(req) {
-
- const relativePath = req.params[0];
-
- const absolutePath = _path.default.resolve(this.pagesPath, relativePath);
-
- if (!absolutePath || !absolutePath.endsWith('.html')) {
- return this.fileResponse(absolutePath);
- }
-
- const params = this.getDefaultParams(req.config);
- const locale = this.getLocale(req);
- if (locale) {
- params.locale = locale;
- }
-
- const placeholders = this.getJsonPlaceholders(locale, params);
- return this.pageResponse(absolutePath, params, placeholders);
- }
-
- getJsonTranslation(locale) {
-
- if (this.jsonParameters === undefined) {
- return {};
- }
-
- locale = locale || this.pagesConfig.localizationFallbackLocale;
-
- const language = locale.split('-')[0];
- const resource = this.jsonParameters[locale] || this.jsonParameters[language] || this.jsonParameters[this.pagesConfig.localizationFallbackLocale] || {};
- const translation = resource.translation || {};
- return translation;
- }
-
- getJsonPlaceholders(locale, params = {}) {
-
- if (!this.pagesConfig.enableLocalization || !this.pagesConfig.localizationJsonPath) {
- return {};
- }
-
- let placeholders = this.getJsonTranslation(locale);
-
-
- placeholders = JSON.stringify(placeholders);
- placeholders = _mustache.default.render(placeholders, params);
- placeholders = JSON.parse(placeholders);
- return placeholders;
- }
-
- async pageResponse(path, params = {}, placeholders = {}) {
-
- let data;
- try {
- data = await this.readFile(path);
- } catch (e) {
- return this.notFound();
- }
-
- let configPlaceholders = typeof this.pagesConfig.placeholders === 'function' ? this.pagesConfig.placeholders(params) : Object.prototype.toString.call(this.pagesConfig.placeholders) === '[object Object]' ? this.pagesConfig.placeholders : {};
- if (configPlaceholders instanceof Promise) {
- configPlaceholders = await configPlaceholders;
- }
-
- const allPlaceholders = Object.assign({}, configPlaceholders, placeholders);
- const paramsAndPlaceholders = Object.assign({}, params, allPlaceholders);
- data = _mustache.default.render(data, paramsAndPlaceholders);
-
-
- const headers = Object.entries(params).reduce((m, p) => {
- if (p[1] !== undefined) {
- m[`${pageParamHeaderPrefix}${p[0].toLowerCase()}`] = p[1];
- }
- return m;
- }, {});
- return {
- text: data,
- headers: headers
- };
- }
-
- async fileResponse(path) {
-
- let data;
- try {
- data = await this.readFile(path);
- } catch (e) {
- return this.notFound();
- }
- return {
- text: data
- };
- }
-
- async readFile(filePath) {
-
-
-
-
- const normalizedPath = _path.default.normalize(filePath);
-
- if (!normalizedPath.startsWith(this.pagesPath)) {
- throw errors.fileOutsideAllowedScope;
- }
- return await _fs.promises.readFile(normalizedPath, 'utf-8');
- }
-
- loadJsonResource() {
- if (this.pagesConfig.localizationJsonPath === undefined) {
- return;
- }
- try {
- const json = require(_path.default.resolve('./', this.pagesConfig.localizationJsonPath));
- this.jsonParameters = json;
- } catch (e) {
- throw errors.jsonFailedFileLoading;
- }
- }
-
- getDefaultParams(config) {
- return config ? {
- [pageParams.appId]: config.appId,
- [pageParams.appName]: config.appName,
- [pageParams.publicServerUrl]: config.publicServerURL
- } : {};
- }
-
- getLocale(req) {
- const locale = (req.query || {})[pageParams.locale] || (req.body || {})[pageParams.locale] || (req.params || {})[pageParams.locale] || (req.headers || {})[pageParamHeaderPrefix + pageParams.locale];
- return locale;
- }
-
- async redirectResponse(url, params) {
-
- params = Object.entries(params).reduce((m, p) => {
- if (p[1] !== undefined) {
- m[p[0]] = p[1];
- }
- return m;
- }, {});
-
- const location = new URL(url);
- Object.entries(params).forEach(p => location.searchParams.set(p[0], p[1]));
- const locationString = location.toString();
-
-
- const headers = Object.entries(params).reduce((m, p) => {
- if (p[1] !== undefined) {
- m[`${pageParamHeaderPrefix}${p[0].toLowerCase()}`] = p[1];
- }
- return m;
- }, {});
- return {
- status: 303,
- location: locationString,
- headers: headers
- };
- }
- defaultPagePath(file) {
- return _path.default.join(this.pagesPath, file);
- }
- composePageUrl(file, publicServerUrl, locale) {
- let url = publicServerUrl;
- url += url.endsWith('/') ? '' : '/';
- url += this.pagesEndpoint + '/';
- url += locale === undefined ? '' : locale + '/';
- url += file;
- return url;
- }
- notFound() {
- return {
- text: 'Not found.',
- status: 404
- };
- }
- invalidRequest() {
- const error = new Error();
- error.status = 403;
- error.message = 'unauthorized';
- throw error;
- }
-
- setConfig(req, failGracefully = false) {
- req.config = _Config.default.get(req.params.appId || req.query.appId);
- if (!req.config && !failGracefully) {
- this.invalidRequest();
- }
- return Promise.resolve();
- }
- mountPagesRoutes() {
- this.route('GET', `/${this.pagesEndpoint}/:appId/verify_email`, req => {
- this.setConfig(req);
- }, req => {
- return this.verifyEmail(req);
- });
- this.route('POST', `/${this.pagesEndpoint}/:appId/resend_verification_email`, req => {
- this.setConfig(req);
- }, req => {
- return this.resendVerificationEmail(req);
- });
- this.route('GET', `/${this.pagesEndpoint}/choose_password`, req => {
- this.setConfig(req);
- }, req => {
- return this.passwordReset(req);
- });
- this.route('POST', `/${this.pagesEndpoint}/:appId/request_password_reset`, req => {
- this.setConfig(req);
- }, req => {
- return this.resetPassword(req);
- });
- this.route('GET', `/${this.pagesEndpoint}/:appId/request_password_reset`, req => {
- this.setConfig(req);
- }, req => {
- return this.requestResetPassword(req);
- });
- }
- mountCustomRoutes() {
- for (const route of this.pagesConfig.customRoutes || []) {
- this.route(route.method, `/${this.pagesEndpoint}/:appId/${route.path}`, req => {
- this.setConfig(req);
- }, async req => {
- const {
- file,
- query = {}
- } = (await route.handler(req)) || {};
-
- if (!file) {
- return this.notFound();
- }
-
- const page = new _Page.default({
- id: file,
- defaultFile: file
- });
- return this.goToPage(req, page, query, false);
- });
- }
- }
- mountStaticRoute() {
- this.route('GET', `/${this.pagesEndpoint}/(*)?`, req => {
- this.setConfig(req, true);
- }, req => {
- return this.staticRoute(req);
- });
- }
- expressRouter() {
- const router = _express.default.Router();
- router.use('/', super.expressRouter());
- return router;
- }
- }
- exports.PagesRouter = PagesRouter;
- var _default = exports.default = PagesRouter;
- module.exports = {
- PagesRouter,
- pageParamHeaderPrefix,
- pageParams,
- pages
- };
|