openvibers/openvibe.vip Commits

Take VIP cache headers from openvibe-shared/cache-policy (#3)

OP OpenVibers committed 2 Oct 2026 · 0c509647ab · 3 files, +55 −3

checks run by GitHub CI (test, test-pg) · CI ✓ 3/3 · review SHIP by DeepSeek harness

https://github.com/OpenVibers/OpenVibe.VIP/pull/3

Changes

ddiff --git a/server/app.js b/server/app.js
iindex 5be666b..0d7751d 100644
---- a/server/app.js
++++ b/server/app.js
@@@ -19,6 +19,7 @@ const path = require('path');
const express = require('express');
const cookieParser = require('cookie-parser');
const { http } = require('openvibe-contracts');
++const cache = require('openvibe-shared/cache-policy');
const { loadConfig } = require('./config');
const { openDb } = require('./db');
const { createKeyProvider, createUserAuth } = require('./network');
@@@ -112,7 +113,7 @@ async function createApp(opts = {}) {
setHeaders(res, filePath) {
const rel = path.relative(PUBLIC_DIR, filePath).split(path.sep).join('/');
const v = res.req && res.req.query && res.req.query.v;
-- res.setHeader('Cache-Control', v && v === assetVersion(rel) ? 'public, max-age=31536000, immutable' : 'no-cache');
++ res.setHeader('Cache-Control', cache.assetHeaders(rel, { hashed: v && v === assetVersion(rel) }));
},
}));
app.use(createWebRoutes({ domain, config, layout, userAuth }));
ddiff --git a/server/web/embeds.js b/server/web/embeds.js
iindex 660ccd5..141d91f 100644
---- a/server/web/embeds.js
++++ b/server/web/embeds.js
@@@ -21,6 +21,7 @@
*/
const crypto = require('crypto');
const express = require('express');
++const cache = require('openvibe-shared/cache-policy');
const { esc } = require('../util');
const USERNAME_RE = /^[A-Za-z0-9_][A-Za-z0-9_.-]{0,39}$/;
@@@ -120,10 +121,10 @@ function createEmbedRoutes({ domain, config }) {
});
res.removeHeader('X-Frame-Options');
if (!found) {
-- return res.status(404).set('Cache-Control', 'public, max-age=60').type('html')
++ return res.status(404).set('Cache-Control', cache.htmlHeaders({ maxAge: 60 })).type('html')
.send(`<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="robots" content="noindex"><title>OpenVibe.VIP</title><style>${STYLES[theme]}</style></head><body><main class="w"><p class="k">OpenVibe.VIP</p><p class="n">No memberships to show.</p></main></body></html>\n`);
}
-- return res.status(200).set('Cache-Control', 'public, max-age=60').type('html').send(widgetHtml({ card: found.card, theme }));
++ return res.status(200).set('Cache-Control', cache.htmlHeaders({ maxAge: 60 })).type('html').send(widgetHtml({ card: found.card, theme }));
} catch (e) { return next(e); }
});
ddiff --git a/test/asset-cache.test.js b/test/asset-cache.test.js
nnew file mode 100644
iindex 0000000..85506cd
---- /dev/null
++++ b/test/asset-cache.test.js
@@@ -0,0 +1,50 @@
++'use strict';
++/**
++ * One cache rule for every header (openvibe-shared/cache-policy, plan T11 lane D): a static asset
++ * served with the version it was built at (`?v=<hash>`) is immutable for a year; one served without
++ * a matching ?v= gets the short public window with stale-while-revalidate. An HTML page takes the
++ * HTML policy, and an API/JSON response keeps its own value.
++ */
++const assert = require('assert');
++const { boot, harness } = require('./helpers/app');
++const { assetVersion } = require('../server/web/layout');
++
++const { test, run } = harness('asset-cache');
++
++(async () => {
++ const t = await boot();
++ const get = async (p) => {
++ const r = await fetch(t.base + p, { redirect: 'manual' });
++ return { status: r.status, headers: r.headers, text: await r.text() };
++ };
++
++ test('a static asset under its current ?v= is immutable for a year', async () => {
++ const v = assetVersion('css/vip.css');
++ const r = await get(`/css/vip.css?v=${v}`);
++ assert.strictEqual(r.status, 200, r.text);
++ assert.strictEqual(r.headers.get('cache-control'), 'public, max-age=31536000, immutable');
++ });
++
++ test('a static asset with a wrong or missing ?v= gets the short public window', async () => {
++ const wrong = await get('/css/vip.css?v=deadbeefdeadbeef');
++ assert.strictEqual(wrong.status, 200, wrong.text);
++ assert.strictEqual(wrong.headers.get('cache-control'), 'public, max-age=300, stale-while-revalidate=86400');
++ const none = await get('/css/vip.css');
++ assert.strictEqual(none.status, 200, none.text);
++ assert.strictEqual(none.headers.get('cache-control'), 'public, max-age=300, stale-while-revalidate=86400');
++ });
++
++ test('an HTML widget takes the HTML policy', async () => {
++ const r = await get('/embed/nobody/widget');
++ assert.strictEqual(r.status, 404, r.text);
++ assert.strictEqual(r.headers.get('cache-control'), 'public, max-age=60, stale-while-revalidate=3600');
++ });
++
++ test('card.json stays a public API window', async () => {
++ const r = await get('/embed/nobody/card.json');
++ assert.strictEqual(r.status, 404, r.text);
++ assert.strictEqual(r.headers.get('cache-control'), 'public, max-age=60');
++ });
++
++ await run();
++})().catch((e) => { console.error(e); process.exit(1); });