openvibers/openrestream Commits

Take robots.txt Cache-Control from openvibe-shared cache-policy (#7)

OP OpenVibers committed 2 Oct 2026 · 29f2c7e620 · 2 files, +47 −1

checks run by GitHub CI (test, test-pg) · CI ✓ 3/3 · review SHIP by Codex GPT-6 Sol

https://github.com/OpenVibers/OpenRe.Stream/pull/7

Changes

ddiff --git a/server/app.js b/server/app.js
iindex 4d37312..3f2d7f8 100644
---- a/server/app.js
++++ b/server/app.js
@@@ -4,6 +4,7 @@ const http = require('http');
const express = require('express');
const cookieParser = require('cookie-parser');
const contracts = require('openvibe-contracts');
++const cache = require('openvibe-shared/cache-policy');
const { createV1Router } = require('./api/v1');
const { createSsoRoutes } = require('./auth/sso');
const { createUiRouter } = require('./ui/routes');
@@@ -139,7 +140,7 @@ function createApp({ rt, auth, keys, log = console, fetchImpl }) {
});
app.use('/auth', createSsoRoutes({ config, auth, fetchImpl }));
-- app.get('/robots.txt', (_req, res) => res.type('text/plain').send('User-agent: *\nAllow: /$\nDisallow: /streams\nDisallow: /sessions\nDisallow: /destinations\nDisallow: /api/\nDisallow: /play/\n'));
++ app.get('/robots.txt', (_req, res) => res.type('text/plain').set('Cache-Control', cache.htmlHeaders()).send('User-agent: *\nAllow: /$\nDisallow: /streams\nDisallow: /sessions\nDisallow: /destinations\nDisallow: /api/\nDisallow: /play/\n'));
app.use('/', createUiRouter({ rt, auth }));
app.use((req, res) => contracts.http.sendProblem(res, 404, 'openre.not_found', { detail: `no route ${req.method} ${req.path}`, ctx: req.ov }));
ddiff --git a/test/asset-cache.test.js b/test/asset-cache.test.js
nnew file mode 100644
iindex 0000000..cbf08d9
---- /dev/null
++++ b/test/asset-cache.test.js
@@@ -0,0 +1,45 @@
++'use strict';
++// Cache-Control comes from openvibe-shared/cache-policy (plan T11), never a hand-written string:
++// robots.txt takes the shared HTML policy, the /shared browser assets are content-addressed and
++// immutable for a year (openvibe-shared/serve already uses the module), and the API's own
++// no-store values are left to openvibe-shared/serve and the media/API routes that need them.
++const assert = require('assert');
++const cache = require('openvibe-shared/cache-policy');
++const { bootApi, request, suite } = require('./helpers');
++
++const t = suite('asset-cache');
++
++t('the module is the only source of the asset/HTML policy values', () => {
++ assert.strictEqual(cache.assetHeaders('theme-loader.js?v=deadbeefdeadbeef', { hashed: true }), 'public, max-age=31536000, immutable');
++ assert.strictEqual(cache.assetHeaders('theme-loader.js?v=deadbeefdeadbeef', { hashed: false }), 'public, max-age=300, stale-while-revalidate=86400');
++ assert.strictEqual(cache.htmlHeaders({ private: true }), 'private, no-store');
++ assert.strictEqual(cache.htmlHeaders(), 'public, max-age=120, stale-while-revalidate=3600');
++});
++
++t('robots.txt carries the shared HTML policy', async () => {
++ const api = await bootApi({});
++ try {
++ const r = await request(api.base, 'GET', '/robots.txt');
++ assert.strictEqual(r.status, 200);
++ assert.strictEqual(r.headers.get('cache-control'), cache.htmlHeaders());
++ } finally {
++ await api.close();
++ }
++});
++
++t('content-addressed /shared assets are immutable for a year', async () => {
++ const api = await bootApi({});
++ try {
++ const serve = require('openvibe-shared/serve');
++ const name = 'theme-loader.js';
++ const hit = await request(api.base, 'GET', `/shared/${name}?v=${serve.hashOf(name)}`);
++ assert.strictEqual(hit.status, 200);
++ assert.strictEqual(hit.headers.get('cache-control'), cache.IMMUTABLE);
++ const miss = await request(api.base, 'GET', `/shared/${name}?v=deadbeefdeadbeef`);
++ assert.strictEqual(miss.headers.get('cache-control'), cache.assetHeaders(name, { hashed: false, swr: 60 }));
++ } finally {
++ await api.close();
++ }
++});
++
++t.run();