From 1c2818002fd85b2ddef8d0baeec7f86d7432afaa Mon Sep 17 00:00:00 2001 From: William Welling Date: Wed, 19 Jul 2017 12:11:15 -0500 Subject: [PATCH] added CSR fallback and optionally disable SSR --- config/environment.default.js | 8 ++++ nodemon.json | 1 + src/config/global-config.interface.ts | 4 +- src/config/universal-config.interface.ts | 5 +++ src/index.csr.html | 19 +++++++++ src/index.html | 2 + src/main.server.aot.ts | 40 +++++++++++++------ src/main.server.ts | 40 +++++++++++++------ .../transfer-state/browser-transfer-state.ts | 4 +- 9 files changed, 96 insertions(+), 27 deletions(-) create mode 100644 src/config/universal-config.interface.ts create mode 100644 src/index.csr.html diff --git a/config/environment.default.js b/config/environment.default.js index 049f06b1e2..be85df1ce8 100644 --- a/config/environment.default.js +++ b/config/environment.default.js @@ -15,11 +15,19 @@ module.exports = { // NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript nameSpace: '/dspace-spring-rest/api' }, + // Caching settings cache: { // NOTE: how long should objects be cached for by default msToLive: 15 * 60 * 1000, // 15 minute control: 'max-age=60' // revalidate browser }, + // Angular Universal settings + universal: { + preboot: true, + async: true, + time: false + }, + // Log directory logDirectory: '.', // NOTE: rehydrate or replay // rehydrate will transfer prerender state to browser state, actions do not need to replay diff --git a/nodemon.json b/nodemon.json index 7c50f34761..00313fe368 100644 --- a/nodemon.json +++ b/nodemon.json @@ -1,6 +1,7 @@ { "watch": [ "dist", + "config", "src/index.html" ], "ext": "js ts json html" diff --git a/src/config/global-config.interface.ts b/src/config/global-config.interface.ts index 45470c1aab..547169ef0c 100644 --- a/src/config/global-config.interface.ts +++ b/src/config/global-config.interface.ts @@ -1,12 +1,14 @@ import { ServerConfig } from './server-config.interface'; import { CacheConfig } from './cache-config.interface'; +import { UniversalConfig } from './universal-config.interface'; export interface GlobalConfig { ui: ServerConfig; rest: ServerConfig; - prerenderStrategy: string; production: boolean; cache: CacheConfig; + universal: UniversalConfig; logDirectory: string; + prerenderStrategy: string; debug: boolean; } diff --git a/src/config/universal-config.interface.ts b/src/config/universal-config.interface.ts new file mode 100644 index 0000000000..2d92564e52 --- /dev/null +++ b/src/config/universal-config.interface.ts @@ -0,0 +1,5 @@ +export interface UniversalConfig { + preboot: boolean, + async: boolean, + time: boolean +} diff --git a/src/index.csr.html b/src/index.csr.html new file mode 100644 index 0000000000..8c25dfa084 --- /dev/null +++ b/src/index.csr.html @@ -0,0 +1,19 @@ + + + + + + + DSpace + + + + + + + + + + + + diff --git a/src/index.html b/src/index.html index 0fbf27396f..491a2d319c 100644 --- a/src/index.html +++ b/src/index.html @@ -13,4 +13,6 @@ + + diff --git a/src/main.server.aot.ts b/src/main.server.aot.ts index 8a5397da44..d6231ba0ab 100644 --- a/src/main.server.aot.ts +++ b/src/main.server.aot.ts @@ -56,7 +56,7 @@ app.set('view engine', 'html'); app.set('views', 'src'); function cacheControl(req, res, next) { - // instruct browser to revalidate in 60 seconds + // instruct browser to revalidate res.header('Cache-Control', ENV_CONFIG.cache.control || 'max-age=60'); next(); } @@ -67,18 +67,34 @@ app.use('/', cacheControl, express.static('dist', { index: false })); // app.get('/data.json', serverApi); // app.use('/api', createMockApi()); +function ngApp(req, res) { + + function onHandleError(parentZoneDelegate, currentZone, targetZone, error) { + console.warn('Error in SSR, serving for direct CSR'); + res.sendFile('index.csr.html', { root: './src' }); + } + + if (ENV_CONFIG.universal.preboot) { + Zone.current.fork({ name: 'CSR fallback', onHandleError }).run(() => { + res.render('../dist/index', { + req, + res, + preboot: ENV_CONFIG.universal.preboot, + async: ENV_CONFIG.universal.async, + time: ENV_CONFIG.universal.time, + baseUrl: ENV_CONFIG.ui.nameSpace, + originUrl: ENV_CONFIG.ui.baseUrl, + requestUrl: req.originalUrl + }); + }); + } else { + console.log('Universal preboot off, service for direct CSD'); + res.sendFile('index.csr.html', { root: './src' }); + } +} + ROUTES.forEach((route: string) => { - app.get(route, (req, res) => { - res.cookie('ui_origin', ENV_CONFIG.ui.baseUrl, { - maxAge: 1000 * 60 * 15, - httpOnly: true, - signed: false - }); - res.render('../dist/index', { - req: req, - res: res - }); - }); + app.get(route, ngApp); }); function serverStarted() { diff --git a/src/main.server.ts b/src/main.server.ts index 76e3cae932..de41a7dde1 100644 --- a/src/main.server.ts +++ b/src/main.server.ts @@ -44,7 +44,7 @@ app.set('view engine', 'html'); app.set('views', 'src'); function cacheControl(req, res, next) { - // instruct browser to revalidate in 60 seconds + // instruct browser to revalidate res.header('Cache-Control', ENV_CONFIG.cache.control || 'max-age=60'); next(); } @@ -55,18 +55,34 @@ app.use('/', cacheControl, express.static('dist', { index: false })); // app.get('/data.json', serverApi); // app.use('/api', createMockApi()); +function ngApp(req, res) { + + function onHandleError(parentZoneDelegate, currentZone, targetZone, error) { + console.warn('Error in SSR, serving for direct CSR'); + res.sendFile('index.csr.html', { root: './src' }); + } + + if (ENV_CONFIG.universal.preboot) { + Zone.current.fork({ name: 'CSR fallback', onHandleError }).run(() => { + res.render('../dist/index', { + req, + res, + preboot: ENV_CONFIG.universal.preboot, + async: ENV_CONFIG.universal.async, + time: ENV_CONFIG.universal.time, + baseUrl: ENV_CONFIG.ui.nameSpace, + originUrl: ENV_CONFIG.ui.baseUrl, + requestUrl: req.originalUrl + }); + }); + } else { + console.log('Universal preboot off, service for direct CSD'); + res.sendFile('index.csr.html', { root: './src' }); + } +} + ROUTES.forEach((route: string) => { - app.get(route, (req, res) => { - res.cookie('ui_origin', ENV_CONFIG.ui.baseUrl, { - maxAge: 1000 * 60 * 15, - httpOnly: true, - signed: false - }); - res.render('../dist/index', { - req: req, - res: res - }); - }); + app.get(route, ngApp); }); function serverStarted() { diff --git a/src/modules/transfer-state/browser-transfer-state.ts b/src/modules/transfer-state/browser-transfer-state.ts index cd30b64416..d1e0a1ecb0 100644 --- a/src/modules/transfer-state/browser-transfer-state.ts +++ b/src/modules/transfer-state/browser-transfer-state.ts @@ -27,7 +27,7 @@ export class BrowserTransferState extends TransferState { if (this.config.prerenderStrategy === 'replay') { if (cache.actions !== undefined) { if (this.config.debug) { - console.info('Replay:', cache.actions); + console.info('Replay:', (cache.actions !== undefined && cache.actions !== null) ? cache.actions : []); } this.store.dispatch(new StoreAction(StoreActionTypes.REPLAY, cache.actions)); } else { @@ -35,7 +35,7 @@ export class BrowserTransferState extends TransferState { } } else if (this.config.prerenderStrategy === 'rehydrate') { if (this.config.debug) { - console.info('Rehydrate:', cache.state); + console.info('Rehydrate:', (cache.state !== undefined && cache.state !== null) ? cache.state : []); } this.store.dispatch(new StoreAction(StoreActionTypes.REHYDRATE, cache.state)); } else {