fix etag caching when run on localhost

(languages were not possible to be selected if run locally, cause of
aggressive caching)
This commit is contained in:
HF 2023-12-11 20:56:26 +01:00
parent 6b0404b66d
commit bcc489eff9
6 changed files with 54 additions and 49 deletions

1
.gitignore vendored
View File

@ -10,6 +10,7 @@ logs
*.log *.log
npm-debug.log* npm-debug.log*
*.tmp *.tmp
records.json
pids pids
*.pid *.pid

View File

@ -57,7 +57,7 @@ export const DEFAULT_CANVASES = {
ranked: true, ranked: true,
req: -1, req: -1,
sd: '2020-01-08', sd: '2020-01-08',
} },
}; };
export const TILE_LOADING_IMAGE = './loading.png'; export const TILE_LOADING_IMAGE = './loading.png';

View File

@ -3,7 +3,6 @@
*/ */
import express from 'express'; import express from 'express';
import etag from 'etag';
import path from 'path'; import path from 'path';
import ranking from './ranking'; import ranking from './ranking';
@ -16,7 +15,6 @@ import captcha from './captcha';
import resetPassword from './reset_password'; import resetPassword from './reset_password';
import api from './api'; import api from './api';
import { getJsAssets } from '../core/assets';
import { expressTTag } from '../core/ttag'; import { expressTTag } from '../core/ttag';
import corsMiddleware from '../utils/corsMiddleware'; import corsMiddleware from '../utils/corsMiddleware';
import generateGlobePage from '../ssr/Globe'; import generateGlobePage from '../ssr/Globe';
@ -72,34 +70,26 @@ router.use(expressTTag);
// //
// 3D Globe (react generated) // 3D Globe (react generated)
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
const globeEtag = etag(
getJsAssets('globe').join('_'),
{ weak: true },
);
router.get('/globe', (req, res) => { router.get('/globe', (req, res) => {
const { html, etag: globeEtag } = generateGlobePage(req);
res.set({ res.set({
'Cache-Control': `private, max-age=${15 * 60}`, // seconds 'Cache-Control': `private, max-age=${15 * 60}`, // seconds
ETag: globeEtag, ETag: globeEtag,
}); });
if (req.headers['if-none-match'] === globeEtag) { if (!html) {
res.status(304).end(); res.status(304).end();
return; return;
} }
res.set('Content-Type', 'text/html; charset=utf-8'); res.set('Content-Type', 'text/html; charset=utf-8');
res.status(200).send(html);
res.status(200).send(generateGlobePage(req.lang));
}); });
// //
// PopUps // PopUps
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
const winEtag = etag(
getJsAssets('popup').join('_'),
{ weak: true },
);
router.use( router.use(
AVAILABLE_POPUPS.map((p) => `/${p.toLowerCase()}`), AVAILABLE_POPUPS.map((p) => `/${p.toLowerCase()}`),
(req, res, next) => { (req, res, next) => {
@ -108,51 +98,43 @@ router.use(
return; return;
} }
const { html, etag: winEtag } = generatePopUpPage(req);
res.set({ res.set({
'Cache-Control': `private, max-age=${15 * 60}`, // seconds 'Cache-Control': `private, max-age=${15 * 60}`, // seconds
ETag: winEtag, ETag: winEtag,
}); });
if (req.headers['if-none-match'] === winEtag) { if (!html) {
res.status(304).end(); res.status(304).end();
return; return;
} }
res.set('Content-Type', 'text/html; charset=utf-8'); res.set('Content-Type', 'text/html; charset=utf-8');
res.status(200).send(html);
res.status(200).send(generatePopUpPage(req));
}, },
); );
// //
// Main Page // Main Page
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
const indexEtag = etag(
getJsAssets('client').join('_'),
{ weak: true },
);
router.get('/', (req, res) => { router.get('/', (req, res) => {
const { html, csp, etag: mainEtag } = generateMainPage(req);
res.set({ res.set({
'Cache-Control': `private, max-age=${15 * 60}`, // seconds 'Cache-Control': `private, max-age=${15 * 60}`, // seconds
// ETag: indexEtag, 'Content-Security-Policy': csp,
ETag: mainEtag,
}); });
/* if (!html) {
* TODO fix this per language
if (req.headers['if-none-match'] === indexEtag) {
res.status(304).end(); res.status(304).end();
return; return;
} }
*/
const [html, csp] = generateMainPage(req);
res.set({ res.set({
'Content-Type': 'text/html; charset=utf-8', 'Content-Type': 'text/html; charset=utf-8',
'Content-Security-Policy': csp,
}); });
res.status(200).send(html); res.status(200).send(html);
}); });

View File

@ -4,6 +4,7 @@
*/ */
/* eslint-disable max-len */ /* eslint-disable max-len */
import etag from 'etag';
import { getTTag } from '../core/ttag'; import { getTTag } from '../core/ttag';
@ -17,9 +18,15 @@ import globeCss from '../styles/globe.css';
* @param lang language code * @param lang language code
* @return html of mainpage * @return html of mainpage
*/ */
function generateGlobePage(lang) { function generateGlobePage(req) {
const { lang } = req;
const scripts = getJsAssets('globe', lang); 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 { t } = getTTag(lang);
const html = ` const html = `
@ -48,7 +55,7 @@ function generateGlobePage(lang) {
</html> </html>
`; `;
return html; return { html, etag: globeEtag };
} }
export default generateGlobePage; export default generateGlobePage;

View File

@ -4,6 +4,7 @@
/* eslint-disable max-len */ /* eslint-disable max-len */
import { createHash } from 'crypto'; import { createHash } from 'crypto';
import etag from 'etag';
import { langCodeToCC } from '../utils/location'; import { langCodeToCC } from '../utils/location';
import ttags, { getTTag } from '../core/ttag'; import ttags, { getTTag } from '../core/ttag';
@ -42,19 +43,25 @@ const bodyScriptHash = createHash('sha256').update(bodyScript).digest('base64');
function generateMainPage(req) { function generateMainPage(req) {
const { lang } = req; const { lang } = req;
const host = getHostFromRequest(req, false); const host = getHostFromRequest(req, false);
const ssvR = { const shard = (host.startsWith(`${socketEvents.thisShard}.`))
? null : socketEvents.getLowestActiveShard();
const ssvR = JSON.stringify({
...ssv, ...ssv,
shard: (host.startsWith(`${socketEvents.thisShard}.`)) shard,
? null : socketEvents.getLowestActiveShard(),
lang: lang === 'default' ? 'en' : lang, lang: lang === 'default' ? 'en' : lang,
}; });
const scripts = getJsAssets('client', 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 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 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 { t } = getTTag(lang);
const html = ` const html = `
@ -81,7 +88,8 @@ function generateMainPage(req) {
</body> </body>
</html> </html>
`; `;
return [html, csp];
return { html, csp, etag: mainEtag };
} }
export default generateMainPage; export default generateMainPage;

View File

@ -4,6 +4,7 @@
*/ */
/* eslint-disable max-len */ /* eslint-disable max-len */
import etag from 'etag';
import { langCodeToCC } from '../utils/location'; import { langCodeToCC } from '../utils/location';
import ttags, { getTTag } from '../core/ttag'; import ttags, { getTTag } from '../core/ttag';
@ -33,18 +34,24 @@ if (BACKUP_URL) {
/* /*
* generates string with html of win page * generates string with html of win page
* @param lang language code * @param lang language code
* @return html of mainpage * @return html and etag of popup page
*/ */
function generatePopUpPage(req) { function generatePopUpPage(req) {
const { lang } = req; const { lang } = req;
const host = getHostFromRequest(req); const host = getHostFromRequest(req);
const ssvR = { const shard = (host.startsWith(`${socketEvents.thisShard}.`))
? null : socketEvents.getLowestActiveShard();
const ssvR = JSON.stringify({
...ssv, ...ssv,
shard: (host.startsWith(`${socketEvents.thisShard}.`)) shard,
? null : socketEvents.getLowestActiveShard(),
lang: lang === 'default' ? 'en' : lang, 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); const { t } = getTTag(lang);
@ -62,18 +69,18 @@ function generatePopUpPage(req) {
/> />
<link rel="icon" href="/favicon.ico" type="image/x-icon" /> <link rel="icon" href="/favicon.ico" type="image/x-icon" />
<link rel="apple-touch-icon" href="apple-touch-icon.png" /> <link rel="apple-touch-icon" href="apple-touch-icon.png" />
<script>window.ssv=JSON.parse('${JSON.stringify(ssvR)}')</script> <script>window.ssv=JSON.parse('${ssvR}')</script>
<link rel="stylesheet" type="text/css" id="globcss" href="${getCssAssets().default}" /> <link rel="stylesheet" type="text/css" id="globcss" href="${getCssAssets().default}" />
</head> </head>
<body> <body>
<div id="app" class="popup"> <div id="app" class="popup">
</div> </div>
<script src="${script}"></script> ${scripts.map((script) => `<script src="${script}"></script>`).join('')}
</body> </body>
</html> </html>
`; `;
return html; return { html, etag: popEtag };
} }
export default generatePopUpPage; export default generatePopUpPage;