Update SSR caching to only work when unauthenticated. Enhance config comments

This commit is contained in:
Tim Donohue
2023-01-09 16:20:06 -06:00
parent b0696a404d
commit 0d4cf5e468
2 changed files with 31 additions and 26 deletions

View File

@@ -39,11 +39,16 @@ cache:
maxBufferSize: 100
timePerMethod:
PATCH: 3 # time in seconds
# In-memory cache of server-side rendered content
# In-memory cache of server-side rendered pages. This cache stores the most recently accessed public pages.
# Pages are automatically added/dropped from this cache based on how recently they have been used.
serverSide:
# Maximum number of pages (rendered via SSR) to cache. Set to zero to disable server side caching.
# Maximum number of pages to cache. Set to zero (0) to disable server side caching. Default is 100, which means
# the 100 most recently accessed public pages will be cached. As all pages are cached in server memory,
# increasing this value will increase memory needs. Individual cached pages are usually small (<100KB),
# so max=100 should only require a maximum of 9-10MB of memory. Restarting the app clears this page cache.
max: 100
# Amount of time after which cached pages are considered stale (in ms)
# Amount of time after which cached pages are considered stale (in ms). After becoming stale, the cached
# copy is automatically refreshed on the next request.
timeToLive: 900000 # 15 minutes
# Authentication settings

View File

@@ -54,6 +54,8 @@ import { buildAppConfig } from './src/config/config.server';
import { APP_CONFIG, AppConfig } from './src/config/app-config.interface';
import { extendEnvironmentWithAppConfig } from './src/config/config.util';
import { logStartupMessage } from './startup-message';
import { TOKENITEM } from 'src/app/core/auth/models/auth-token-info.model';
import { isAuthenticated } from 'src/app/core/auth/selectors';
/*
@@ -113,13 +115,13 @@ export function app() {
/*
* Add cookie parser middleware
* See [morgan](https://github.com/expressjs/cookie-parser)
* See [cookie-parser](https://github.com/expressjs/cookie-parser)
*/
server.use(cookieParser());
/*
* Add parser for request bodies
* See [morgan](https://github.com/expressjs/body-parser)
* Add JSON parser for request bodies
* See [body-parser](https://github.com/expressjs/body-parser)
*/
server.use(json());
@@ -258,7 +260,7 @@ function serverSideRender(req, res, sendToUser: boolean = true) {
if (hasNoValue(err) && hasValue(data)) {
res.locals.ssr = true; // mark response as SSR (enables text compression)
// save server side rendered data to cache
saveToCache(getCacheKey(req), data);
saveToCache(req, data);
if (sendToUser) {
// send rendered page to user
res.send(data);
@@ -313,7 +315,7 @@ function addCacheControl(req, res, next) {
*/
function enableCache() {
if (cacheEnabled()) {
// Initialize a new "least-recently-used" item cache.
// Initialize a new "least-recently-used" item cache (where least recently used items are removed first)
// See https://www.npmjs.com/package/lru-cache
cache = new LRU( {
max: environment.cache.serverSide.max || 100, // 100 items in cache maximum
@@ -340,8 +342,10 @@ function cacheCheck(req, res, next) {
let cacheHit = false;
let debug = false; // Enable to see cache hits & re-rendering logs
// Only check cache if cache enabled & SSR is enabled
if (cacheEnabled()) {
// Only check cache if cache enabled & NOT authenticated.
// NOTE: Authenticated users cannot use the SSR cache. Cached pages only show data available to anonymous users.
// Only public pages can currently be cached, as the cached data is not user-specific.
if (cacheEnabled() && !isUserAuthenticated(req)) {
const key = getCacheKey(req);
// Check if this page is in our cache
@@ -387,29 +391,25 @@ function getCacheKey(req): string {
}
/**
* Save data to server side cache, if enabled. If caching is not enabled, this is a noop
* @param key page key
* Save data to server side cache, if enabled. If caching is not enabled or user is authenticated, this is a noop
* @param req current page request
* @param data page data to save to cache
*/
function saveToCache(key: string, data: any) {
// Only cache if caching is enabled and this path is allowed to be cached
if (cacheEnabled() && canCachePage(key)) {
cache.set(key, data);
function saveToCache(req, data: any) {
// Only cache if caching is enabled and no one is currently authenticated. This means ONLY public pages can be cached.
// NOTE: It's not safe to save page data to the cache when a user is authenticated. In that situation,
// the page may include sensitive or user-specific materials. As the cache is shared across all users, it can only contain public info.
if (cacheEnabled() && !isUserAuthenticated(req)) {
cache.set(getCacheKey(req), data);
}
}
/**
* Whether this path is allowed to be cached. Only public paths can be cached as the cache is shared across all users.
* @param key page key (corresponds to path of page)
* @returns true if allowed to be cached, false otherwise.
* Whether a user is authenticated or not
*/
function canCachePage(key: string) {
// Only these publicly accessible pages can be cached.
// NOTE: Caching pages which require authentication is NOT ALLOWED. The same cache is used by all users & user-specific data must NEVER appear in cache.
const allowedPages = [/^\/$/, /^\/home$/, /^\/items\//, /^\/entities\//, /^\/collections\//, /^\/communities\//, /^\/search[\/?]?/, /\/browse\//, /^\/community-list$/];
// Check whether any of these regexs match with the passed in key
return allowedPages.some(regex => regex.test(key));
function isUserAuthenticated(req): boolean {
// Check whether our authentication Cookie exists or not
return req.cookies[TOKENITEM];
}
/*