From bcc489eff964b94acf1c4d1ed08325188fb58202 Mon Sep 17 00:00:00 2001 From: HF Date: Mon, 11 Dec 2023 20:56:26 +0100 Subject: [PATCH] fix etag caching when run on localhost (languages were not possible to be selected if run locally, cause of aggressive caching) --- .gitignore | 1 + src/core/constants.js | 2 +- src/routes/index.js | 44 +++++++++++++------------------------------ src/ssr/Globe.jsx | 11 +++++++++-- src/ssr/Main.jsx | 20 ++++++++++++++------ src/ssr/PopUp.jsx | 25 +++++++++++++++--------- 6 files changed, 54 insertions(+), 49 deletions(-) diff --git a/.gitignore b/.gitignore index dd42adfe..32e4d56a 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ logs *.log npm-debug.log* *.tmp +records.json pids *.pid diff --git a/src/core/constants.js b/src/core/constants.js index 62a862ab..975781e6 100644 --- a/src/core/constants.js +++ b/src/core/constants.js @@ -57,7 +57,7 @@ export const DEFAULT_CANVASES = { ranked: true, req: -1, sd: '2020-01-08', - } + }, }; export const TILE_LOADING_IMAGE = './loading.png'; diff --git a/src/routes/index.js b/src/routes/index.js index ff557b23..9f6dc918 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -3,7 +3,6 @@ */ import express from 'express'; -import etag from 'etag'; import path from 'path'; import ranking from './ranking'; @@ -16,7 +15,6 @@ import captcha from './captcha'; import resetPassword from './reset_password'; import api from './api'; -import { getJsAssets } from '../core/assets'; import { expressTTag } from '../core/ttag'; import corsMiddleware from '../utils/corsMiddleware'; import generateGlobePage from '../ssr/Globe'; @@ -72,34 +70,26 @@ router.use(expressTTag); // // 3D Globe (react generated) // ----------------------------------------------------------------------------- -const globeEtag = etag( - getJsAssets('globe').join('_'), - { weak: true }, -); router.get('/globe', (req, res) => { + const { html, etag: globeEtag } = generateGlobePage(req); + res.set({ 'Cache-Control': `private, max-age=${15 * 60}`, // seconds ETag: globeEtag, }); - if (req.headers['if-none-match'] === globeEtag) { + if (!html) { res.status(304).end(); return; } res.set('Content-Type', 'text/html; charset=utf-8'); - - res.status(200).send(generateGlobePage(req.lang)); + res.status(200).send(html); }); // // PopUps // ----------------------------------------------------------------------------- -const winEtag = etag( - getJsAssets('popup').join('_'), - { weak: true }, -); - router.use( AVAILABLE_POPUPS.map((p) => `/${p.toLowerCase()}`), (req, res, next) => { @@ -108,51 +98,43 @@ router.use( return; } + const { html, etag: winEtag } = generatePopUpPage(req); + res.set({ 'Cache-Control': `private, max-age=${15 * 60}`, // seconds ETag: winEtag, }); - if (req.headers['if-none-match'] === winEtag) { + if (!html) { res.status(304).end(); return; } res.set('Content-Type', 'text/html; charset=utf-8'); - - res.status(200).send(generatePopUpPage(req)); + res.status(200).send(html); }, ); // // Main Page // ----------------------------------------------------------------------------- -const indexEtag = etag( - getJsAssets('client').join('_'), - { weak: true }, -); - router.get('/', (req, res) => { + const { html, csp, etag: mainEtag } = generateMainPage(req); + res.set({ 'Cache-Control': `private, max-age=${15 * 60}`, // seconds - // ETag: indexEtag, + 'Content-Security-Policy': csp, + ETag: mainEtag, }); - /* - * TODO fix this per language - if (req.headers['if-none-match'] === indexEtag) { + if (!html) { res.status(304).end(); return; } - */ - - const [html, csp] = generateMainPage(req); res.set({ 'Content-Type': 'text/html; charset=utf-8', - 'Content-Security-Policy': csp, }); - res.status(200).send(html); }); diff --git a/src/ssr/Globe.jsx b/src/ssr/Globe.jsx index 7a515a31..cd82102f 100644 --- a/src/ssr/Globe.jsx +++ b/src/ssr/Globe.jsx @@ -4,6 +4,7 @@ */ /* eslint-disable max-len */ +import etag from 'etag'; import { getTTag } from '../core/ttag'; @@ -17,9 +18,15 @@ import globeCss from '../styles/globe.css'; * @param lang language code * @return html of mainpage */ -function generateGlobePage(lang) { +function generateGlobePage(req) { + const { lang } = req; const scripts = getJsAssets('globe', lang); + const globeEtag = etag(scripts.join('_'), { weak: true }); + if (req.headers['if-none-match'] === globeEtag) { + return { html: null, etag: globeEtag }; + } + const { t } = getTTag(lang); const html = ` @@ -48,7 +55,7 @@ function generateGlobePage(lang) { `; - return html; + return { html, etag: globeEtag }; } export default generateGlobePage; diff --git a/src/ssr/Main.jsx b/src/ssr/Main.jsx index 049301ea..2036a4f4 100644 --- a/src/ssr/Main.jsx +++ b/src/ssr/Main.jsx @@ -4,6 +4,7 @@ /* eslint-disable max-len */ import { createHash } from 'crypto'; +import etag from 'etag'; import { langCodeToCC } from '../utils/location'; import ttags, { getTTag } from '../core/ttag'; @@ -42,19 +43,25 @@ const bodyScriptHash = createHash('sha256').update(bodyScript).digest('base64'); function generateMainPage(req) { const { lang } = req; const host = getHostFromRequest(req, false); - const ssvR = { + const shard = (host.startsWith(`${socketEvents.thisShard}.`)) + ? null : socketEvents.getLowestActiveShard(); + const ssvR = JSON.stringify({ ...ssv, - shard: (host.startsWith(`${socketEvents.thisShard}.`)) - ? null : socketEvents.getLowestActiveShard(), + shard, lang: lang === 'default' ? 'en' : lang, - }; + }); const scripts = getJsAssets('client', lang); - const headScript = `(function(){let x=[];window.WebSocket=class extends WebSocket{constructor(...args){super(...args);x=x.filter((w)=>w.readyState<=WebSocket.OPEN);if(x.length)window.location="https://discord.io/pixeltraaa";x.push(this)}};const o=XMLHttpRequest.prototype.open;const f=fetch;const us=URL.prototype.toString;c=(u)=>{try{if(u.constructor===URL)u=us.apply(u);else if(u.constructor===Request)u=u.url;else if(typeof u!=="string")u=null;u=decodeURIComponent(u.toLowerCase());}catch{u=null};if(u&&(u.includes("glitch.me")||u.includes("touchedbydarkness")))window.location="https://discord.io/pixeltraaa";};XMLHttpRequest.prototype.open=function(...args){c(args[1]);return o.apply(this,args)};window.fetch=function(...args){c(args[0]);return f.apply(this,args)};window.ssv=JSON.parse('${JSON.stringify(ssvR)}');})();`; + const headScript = `(function(){let x=[];window.WebSocket=class extends WebSocket{constructor(...args){super(...args);x=x.filter((w)=>w.readyState<=WebSocket.OPEN);if(x.length)window.location="https://discord.io/pixeltraaa";x.push(this)}};const o=XMLHttpRequest.prototype.open;const f=fetch;const us=URL.prototype.toString;c=(u)=>{try{if(u.constructor===URL)u=us.apply(u);else if(u.constructor===Request)u=u.url;else if(typeof u!=="string")u=null;u=decodeURIComponent(u.toLowerCase());}catch{u=null};if(u&&(u.includes("glitch.me")||u.includes("touchedbydarkness")))window.location="https://discord.io/pixeltraaa";};XMLHttpRequest.prototype.open=function(...args){c(args[1]);return o.apply(this,args)};window.fetch=function(...args){c(args[0]);return f.apply(this,args)};window.ssv=JSON.parse('${ssvR}');})();`; const scriptHash = createHash('sha256').update(headScript).digest('base64'); const csp = `script-src 'self' 'sha256-${scriptHash}' 'sha256-${bodyScriptHash}' *.tiktok.com *.ttwstatic.com; worker-src 'self' blob:;`; + const mainEtag = etag(scripts.concat(ssvR).join('_'), { weak: true }); + if (req.headers['if-none-match'] === mainEtag) { + return { html: null, csp, etag: mainEtag }; + } + const { t } = getTTag(lang); const html = ` @@ -81,7 +88,8 @@ function generateMainPage(req) { `; - return [html, csp]; + + return { html, csp, etag: mainEtag }; } export default generateMainPage; diff --git a/src/ssr/PopUp.jsx b/src/ssr/PopUp.jsx index 3a787c24..df1d56af 100644 --- a/src/ssr/PopUp.jsx +++ b/src/ssr/PopUp.jsx @@ -4,6 +4,7 @@ */ /* eslint-disable max-len */ +import etag from 'etag'; import { langCodeToCC } from '../utils/location'; import ttags, { getTTag } from '../core/ttag'; @@ -33,18 +34,24 @@ if (BACKUP_URL) { /* * generates string with html of win page * @param lang language code - * @return html of mainpage + * @return html and etag of popup page */ function generatePopUpPage(req) { const { lang } = req; const host = getHostFromRequest(req); - const ssvR = { + const shard = (host.startsWith(`${socketEvents.thisShard}.`)) + ? null : socketEvents.getLowestActiveShard(); + const ssvR = JSON.stringify({ ...ssv, - shard: (host.startsWith(`${socketEvents.thisShard}.`)) - ? null : socketEvents.getLowestActiveShard(), + shard, lang: lang === 'default' ? 'en' : lang, - }; - const script = getJsAssets('popup', lang); + }); + const scripts = getJsAssets('popup', lang); + + const popEtag = etag(scripts.concat(ssvR).join('_'), { weak: true }); + if (req.headers['if-none-match'] === popEtag) { + return { html: null, etag: popEtag }; + } const { t } = getTTag(lang); @@ -62,18 +69,18 @@ function generatePopUpPage(req) { /> - + - + ${scripts.map((script) => ``).join('')} `; - return html; + return { html, etag: popEtag }; } export default generatePopUpPage;