Repository URL to install this package:
|
Version:
0.13.1 ▾
|
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
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) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const async_middleware_1 = require("async-middleware");
const compression = require("compression");
const continuation_local_storage_1 = require("continuation-local-storage");
const express = require("express");
const HttpStatus = require("http-status-codes");
require("log-timestamp");
const Mustache = require("mustache");
const raynor_1 = require("raynor");
const React = require("react");
const ReactDOMServer = require("react-dom/server");
const react_helmet_1 = require("react-helmet");
const react_redux_1 = require("react-redux");
const react_router_dom_1 = require("react-router-dom");
const webpack = require("webpack");
const theWebpackDevMiddleware = require("webpack-dev-middleware");
const serializeJavascript = require("serialize-javascript");
const business_rules_js_1 = require("@truesparrow/business-rules-js");
const common_js_1 = require("@truesparrow/common-js");
const common_server_js_1 = require("@truesparrow/common-server-js");
const content_sdk_js_1 = require("@truesparrow/content-sdk-js");
const identity_sdk_js_1 = require("@truesparrow/identity-sdk-js");
const server_1 = require("@truesparrow/identity-sdk-js/server");
const bundles_1 = require("./bundles");
const app_frame_1 = require("../shared/app-frame");
const config = require("../shared/config");
const client_data_1 = require("../shared/client-data");
const store_1 = require("../shared/store");
function main() {
return __awaiter(this, void 0, void 0, function* () {
// Global setup, but hidden inside main()
// ********************
const webpackConfig = require('../../webpack.config.js');
const clientConfigMarshaller = new (raynor_1.MarshalFrom(client_data_1.ClientConfig))();
const clientInitialStateMarshaller = new (raynor_1.MarshalFrom(client_data_1.ClientInitialState))();
const internalWebFetcher = new common_js_1.InternalWebFetcher();
const identityClient = identity_sdk_js_1.newIdentityClient(config.INTERNAL_ORIGIN, `${config.IDENTITY_SERVICE_HOST}:${config.IDENTITY_SERVICE_PORT}`, internalWebFetcher);
const contentPrivateClient = content_sdk_js_1.newContentPrivateClient(config.INTERNAL_ORIGIN, `${config.CONTENT_SERVICE_HOST}:${config.CONTENT_SERVICE_PORT}`, internalWebFetcher);
const bundles = common_js_1.isLocal(config.ENV)
? new bundles_1.WebpackDevBundles(theWebpackDevMiddleware(webpack(webpackConfig), {
//Different because we're mounting on /real/client to boot webpackConfig.output.publicPath,
publicPath: '/',
serverSideRender: false
}))
: new bundles_1.CompiledBundles();
const namespace = continuation_local_storage_1.createNamespace(config.CLS_NAMESPACE_NAME);
console.log('Starting up');
function serverSideRender(url, session, clientInitialState) {
const language = business_rules_js_1.inferLanguage(session);
const store = store_1.createStoreFromInitialState(store_1.reducers, clientInitialState);
const clientConfig = {
allowedPaths: config.ALLOWED_PATHS,
env: config.ENV,
internalOrigin: config.INTERNAL_ORIGIN,
externalOrigin: config.EXTERNAL_ORIGIN,
contactEmail: config.CONTACT_EMAIL,
styleApplicationName: config.STYLE_APPLICATION_NAME,
styleLogoUri: config.STYLE_LOGO_URI,
stylePrimaryColor: config.STYLE_PRIMARY_COLOR,
siteFeExternalHost: config.SITEFE_EXTERNAL_HOST,
contentServiceHost: config.CONTENT_SERVICE_HOST,
contentServicePort: config.CONTENT_SERVICE_PORT,
auth0ClientId: config.AUTH0_CLIENT_CONFIG.clientId,
auth0Domain: config.AUTH0_CLIENT_CONFIG.domain,
auth0LoginCallbackUri: config.AUTH0_CLIENT_CONFIG.loginCallbackUri,
logoutRoutePath: config.LOGOUT_ROUTE_PATH,
fileStackApiKey: config.FILESTACK_API_KEY,
rollbarClientToken: null,
session: session,
language: language
};
namespace.set('SESSION', session);
namespace.set('LANG', language);
const staticContext = {};
const appHtml = ReactDOMServer.renderToString(React.createElement(react_redux_1.Provider, { store: store },
React.createElement(react_router_dom_1.StaticRouter, { location: url, context: staticContext },
React.createElement(app_frame_1.AppFrame, null))));
const specialStatus = staticContext.status == HttpStatus.NOT_FOUND ? HttpStatus.NOT_FOUND : null;
const helmetData = react_helmet_1.Helmet.renderStatic();
return [Mustache.render(bundles.getHtmlIndexTemplate(), {
GOOGLE_MAPS_API_KEY: config.GOOGLE_MAPS_API_KEY,
PAGE_TITLE_HTML: helmetData.title,
PAGE_META_HTML: helmetData.meta,
PAGE_LINK_HTML: helmetData.link,
STYLE_PRIMARY_COLOR: config.STYLE_PRIMARY_COLOR,
STYLE_GRAY_COLOR: config.STYLE_GRAY_COLOR,
APP_HTML: appHtml,
CLIENT_CONFIG: serializeJavascript(clientConfigMarshaller.pack(clientConfig), { isJSON: true }),
CLIENT_INITIAL_STATE: serializeJavascript(clientInitialStateMarshaller.pack(clientInitialState), { isJSON: true }),
WEBPACK_MANIFEST_JS: bundles.getManifestJs(),
}), specialStatus];
}
console.log('Starting web server');
const app = express();
// Setup global properties and behaviours of the application
// ********************
app.disable('x-powered-by');
app.use(common_server_js_1.newNamespaceMiddleware(namespace));
if (true || common_js_1.isNotOnServer(config.ENV)) {
app.use(common_server_js_1.newLocalCommonServerMiddleware(config.NAME, config.ENV, false));
}
else {
// app.use(newCommonServerMiddleware(
// config.NAME,
// config.ENV,
// config.LOGGLY_TOKEN as string,
// config.LOGGLY_SUBDOMAIN as string,
// config.ROLLBAR_SERVER_TOKEN as string));
}
app.use(common_server_js_1.newCommonFrontendServerMiddleware(config.ENV, ['/status/check']));
app.use(compression({ threshold: 0 }));
// Setup the /real portion of the path-space. Here are things which don't belong to the client-side
// interaction, but rather to the server-side one, callbacks from other services etc.
// ********************
// Setup the auth0 authentication flow. Has a complex dance wrt sessions.
app.use('/real/auth0-auth-flow', server_1.newAuth0AuthFlowRouter(config.ENV, config.ALLOWED_PATHS, config.AUTH0_SERVER_CONFIG, internalWebFetcher, identityClient));
// An API gateway for the client side code. Needs session to exist in the request.
app.use('/real/api-gateway', server_1.newApiGatewayRouter(config.INTERNAL_ORIGIN, internalWebFetcher));
// Static serving of the client side code assets (index.html, vendor.js etc). No session. Derived
// from the bundles.
app.use('/real/client', bundles.getOtherBundlesRouter());
// Still a service after all and this will allow health checks & metrics etc.
app.use('/status', common_server_js_1.newHealthCheckRouter());
// Setup serving of a bunch of files for interacting with the web at large, such as robots.txt,
// sitemaps etc. These are derived from the bundles, with some extra data baked in. No session.
// ********************
const siteInfoRouter = express.Router();
siteInfoRouter.get('/robots.txt', (_req, res) => {
res.status(HttpStatus.OK);
res.type('.txt');
res.write(Mustache.render(bundles.getRobotsTxt(), { HOME_URI: config.EXTERNAL_ORIGIN }));
res.end();
});
siteInfoRouter.get('/humans.txt', (_req, res) => {
res.status(HttpStatus.OK);
res.type('.txt');
res.write(bundles.getHumansTxt());
res.end();
});
siteInfoRouter.get('/sitemap.xml', (_req, res) => {
res.status(HttpStatus.OK);
res.type('application/xml; charset=utf-8');
res.write(Mustache.render(bundles.getSitemapXml(), {
HOME_URI: config.EXTERNAL_ORIGIN,
HOME_LAST_MOD: new Date().toISOString()
}));
res.end();
});
siteInfoRouter.get('/browserconfig.xml', (_req, res) => {
res.status(HttpStatus.OK);
res.type('application/xml; charset=utf-8');
res.write(Mustache.render(bundles.getBrowserConfigXml(), {
STYLE_PRIMARY_COLOR: config.STYLE_PRIMARY_COLOR
}));
res.end();
});
siteInfoRouter.get('/site.webmanifest', (_req, res) => {
res.status(HttpStatus.OK);
res.type('.txt');
res.write(Mustache.render(bundles.getSiteWebManifest(), {
EXTERNAL_ORIGIN: config.EXTERNAL_ORIGIN,
STYLE_APPLICATION_NAME: config.STYLE_APPLICATION_NAME,
STYLE_PRIMARY_COLOR: config.STYLE_PRIMARY_COLOR
}));
res.end();
});
app.use('/', siteInfoRouter);
// Setup serving for all all client application level routes. Any path a user enters, which
// doesn't match the ones from above (so /real ones or standard web ones) will be handled
// by serving the "client application". This translated to doing a server-side render of the
// application and serving that embedded into {@link src/shared/static/index.html}, which
// will reference /real/client/client.js and other static resources in order to boot it up.
// ********************
const appRouter = express.Router();
appRouter.use(server_1.newSessionMiddleware(server_1.SessionLevel.None, server_1.SessionInfoSource.Cookie, config.ENV, identityClient));
appRouter.get('*', async_middleware_1.wrap((req, res) => __awaiter(this, void 0, void 0, function* () {
let event = null;
if (req.session.hasUser()) {
try {
event = yield contentPrivateClient.withContext(req.sessionToken).getEvent();
}
catch (e) {
if (e.name == 'EventNotFoundError') {
try {
event = yield contentPrivateClient.withContext(req.sessionToken).createEvent(req.session);
}
catch (e) {
// Nothing happens here. We'll try again on the client. But we do log the error.
req.log.warn(e);
req.errorLog.warn(e);
}
}
else {
// Nothing happens here. We'll try again on the client. But we do log the error.
req.log.warn(e);
req.errorLog.warn(e);
}
}
}
const initialState = {
event: event
};
const [content, specialStatus] = serverSideRender(req.url, req.session, initialState);
res.status(specialStatus != null ? specialStatus : HttpStatus.OK);
res.type('html');
res.write(content);
res.end();
})));
app.use('/', appRouter);
// Start serving
// ********************
app.listen(config.PORT, '0.0.0.0', () => {
console.log(`Started ${config.NAME} service on ${config.PORT}`);
});
});
}
main();
//# sourceMappingURL=index.js.map