Skip to content

Commit ebb4c0e

Browse files
author
abhinav
committed
Merge branch 'cache-bust-dynamic-configuration-7.6_CLEAN' into cache-bust-dynamic-configuration_contribute-main
2 parents 93b9b46 + d2458e6 commit ebb4c0e

File tree

14 files changed

+367
-14
lines changed

14 files changed

+367
-14
lines changed

config/config.example.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ cache:
8787
# NOTE: When updates are made to compiled *.js files, it will automatically bypass this browser cache, because
8888
# all compiled *.js files include a unique hash in their name which updates when content is modified.
8989
control: max-age=604800 # revalidate browser
90+
# These static files should not be cached (paths relative to dist/browser, including the leading slash)
91+
noCacheFiles:
92+
- '/index.html'
9093
autoSync:
9194
defaultTime: 0
9295
maxBufferSize: 100
@@ -441,6 +444,7 @@ themes:
441444
# - name: BASE_THEME_NAME
442445
#
443446
- name: dspace
447+
prefetch: true
444448
headTags:
445449
- tagName: link
446450
attributes:

package-lock.json

Lines changed: 48 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@
147147
"ngx-pagination": "6.0.3",
148148
"ngx-skeleton-loader": "^11.3.0",
149149
"ngx-ui-switch": "^16.1.0",
150+
"node-html-parser": "^7.0.1",
150151
"nouislider": "^15.7.1",
151152
"orejime": "^2.3.1",
152153
"pem": "1.14.8",

server.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import {
4747
AppConfig,
4848
} from './src/config/app-config.interface';
4949
import { extendEnvironmentWithAppConfig } from './src/config/config.util';
50+
import { ServerHashedFileMapping } from './src/modules/dynamic-hash/hashed-file-mapping.server';
5051
import { logStartupMessage } from './startup-message';
5152
import { TOKENITEM } from '@dspace/core/auth/models/auth-token-info.model';
5253
import { CommonEngine } from '@angular/ssr/node';
@@ -68,7 +69,11 @@ const indexHtml = join(DIST_FOLDER, 'index.html');
6869

6970
const cookieParser = require('cookie-parser');
7071

71-
const appConfig: AppConfig = buildAppConfig(join(DIST_FOLDER, 'assets/config.json'));
72+
const configJson = join(DIST_FOLDER, 'assets/config.json');
73+
const hashedFileMapping = new ServerHashedFileMapping(DIST_FOLDER, 'index.html');
74+
const appConfig: AppConfig = buildAppConfig(configJson, hashedFileMapping);
75+
appConfig.themes.forEach(themeConfig => hashedFileMapping.addThemeStyle(themeConfig.name, themeConfig.prefetch));
76+
hashedFileMapping.save();
7277

7378
// cache of SSR pages for known bots, only enabled in production mode
7479
let botCache: LRUCache<string, any>;
@@ -324,7 +329,7 @@ function clientSideRender(req, res) {
324329
html = html.replace(new RegExp(REST_BASE_URL, 'g'), environment.rest.baseUrl);
325330
}
326331

327-
res.send(html);
332+
res.set('Cache-Control', 'no-cache, no-store').send(html);
328333
}
329334

330335

@@ -335,7 +340,11 @@ function clientSideRender(req, res) {
335340
*/
336341
function addCacheControl(req, res, next) {
337342
// instruct browser to revalidate
338-
res.header('Cache-Control', environment.cache.control || 'max-age=604800');
343+
if (environment.cache.noCacheFiles.includes(req.originalUrl)) {
344+
res.header('Cache-Control', 'no-cache, no-store');
345+
} else {
346+
res.header('Cache-Control', environment.cache.control || 'max-age=604800');
347+
}
339348
next();
340349
}
341350

src/app/shared/theme-support/theme.service.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
Inject,
44
Injectable,
55
Injector,
6+
Optional,
67
} from '@angular/core';
78
import {
89
ActivatedRouteSnapshot,
@@ -62,6 +63,7 @@ import {
6263
} from 'rxjs/operators';
6364

6465
import { environment } from '../../../environments/environment';
66+
import { HashedFileMapping } from '../../../modules/dynamic-hash/hashed-file-mapping';
6567
import { GET_THEME_CONFIG_FOR_FACTORY } from '../object-collection/shared/listable-object/listable-object.decorator';
6668
import {
6769
SetThemeAction,
@@ -105,6 +107,7 @@ export class ThemeService {
105107
@Inject(GET_THEME_CONFIG_FOR_FACTORY) private gtcf: (str) => ThemeConfig,
106108
private router: Router,
107109
@Inject(DOCUMENT) private document: any,
110+
@Optional() private hashedFileMapping: HashedFileMapping,
108111
@Inject(APP_CONFIG) private appConfig: BuildConfig,
109112
) {
110113
// Create objects from the theme configs in the environment file
@@ -228,10 +231,14 @@ export class ThemeService {
228231
// automatically updated if we add nodes later
229232
const currentThemeLinks = Array.from(head.getElementsByClassName('theme-css'));
230233
const link = this.document.createElement('link');
234+
const themeCSS = `${encodeURIComponent(themeName)}-theme.css`;
231235
link.setAttribute('rel', 'stylesheet');
232236
link.setAttribute('type', 'text/css');
233237
link.setAttribute('class', 'theme-css');
234-
link.setAttribute('href', `${encodeURIComponent(themeName)}-theme.css`);
238+
link.setAttribute(
239+
'href',
240+
this.hashedFileMapping?.resolve(themeCSS) ?? themeCSS,
241+
);
235242
// wait for the new css to download before removing the old one to prevent a
236243
// flash of unstyled content
237244
link.onload = () => {

src/config/cache-config.interface.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ export interface CacheConfig extends Config {
77
};
88
// Cache-Control HTTP Header
99
control: string;
10+
// These static files should not be cached (paths relative to dist/browser, including the leading slash)
11+
noCacheFiles: string[]
1012
autoSync: AutoSyncConfig;
1113
// In-memory caches of server-side rendered (SSR) content. These caches can be used to limit the frequency
1214
// of re-generating SSR pages to improve performance.

src/config/config.server.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ import {
1414
} from 'colors';
1515
import { load } from 'js-yaml';
1616

17+
import { ServerHashedFileMapping } from '../modules/dynamic-hash/hashed-file-mapping.server';
1718
import { AppConfig } from './app-config.interface';
19+
import { BuildConfig } from './build-config.interface';
1820
import { Config } from './config.interface';
1921
import { mergeConfig } from './config.util';
2022
import { DefaultAppConfig } from './default-app-config';
@@ -179,7 +181,7 @@ const buildBaseUrl = (config: ServerConfig): void => {
179181
* @param destConfigPath optional path to save config file
180182
* @returns app config
181183
*/
182-
export const buildAppConfig = (destConfigPath?: string): AppConfig => {
184+
export const buildAppConfig = (destConfigPath?: string, mapping?: ServerHashedFileMapping): AppConfig => {
183185
// start with default app config
184186
const appConfig: AppConfig = new DefaultAppConfig();
185187

@@ -247,7 +249,21 @@ export const buildAppConfig = (destConfigPath?: string): AppConfig => {
247249
buildBaseUrl(appConfig.rest);
248250

249251
if (isNotEmpty(destConfigPath)) {
250-
writeFileSync(destConfigPath, JSON.stringify(appConfig, null, 2));
252+
const content = JSON.stringify(appConfig, null, 2);
253+
254+
writeFileSync(destConfigPath, content);
255+
if (mapping !== undefined) {
256+
mapping.add(destConfigPath, content);
257+
if (!(appConfig as BuildConfig).ssr?.enabled) {
258+
// If we're serving for CSR we can retrieve the configuration before JS is loaded/executed
259+
mapping.addHeadLink({
260+
path: destConfigPath,
261+
rel: 'preload',
262+
as: 'fetch',
263+
crossorigin: 'anonymous',
264+
});
265+
}
266+
}
251267

252268
console.log(`Angular ${bold('config.json')} file generated correctly at ${bold(destConfigPath)} \n`);
253269
}

src/config/default-app-config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ export class DefaultAppConfig implements AppConfig {
8181
},
8282
// Cache-Control HTTP Header
8383
control: 'max-age=604800', // revalidate browser
84+
// These static files should not be cached (paths relative to dist/browser, including the leading slash)
85+
noCacheFiles: [
86+
'/index.html', // see https://web.dev/articles/http-cache#unversioned-urls
87+
],
8488
autoSync: {
8589
defaultTime: 0,
8690
maxBufferSize: 100,

src/config/theme.config.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ export interface NamedThemeConfig extends Config {
1313
* A list of HTML tags that should be added to the HEAD section of the document, whenever this theme is active.
1414
*/
1515
headTags?: HeadTagConfig[];
16+
17+
/**
18+
* Whether this theme's CSS should be prefetched in CSR mode
19+
*/
20+
prefetch?: boolean;
1621
}
1722

1823
/**

src/environments/environment.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ export const environment: BuildConfig = {
7474
},
7575
// msToLive: 1000, // 15 minutes
7676
control: 'max-age=60',
77+
// These static files should not be cached (paths relative to dist/browser, including the leading slash)
78+
noCacheFiles: [
79+
'/index.html', // see https://web.dev/articles/http-cache#unversioned-urls
80+
],
7781
autoSync: {
7882
defaultTime: 0,
7983
maxBufferSize: 100,

0 commit comments

Comments
 (0)