openvibers/openvibe.run Commits

Every Run page sends the same page list, addressed from anywhere (#263)

OP OpenVibers committed 1 days ago · 44b281d09d · 3 files, +61 −5

/updates, /terms and /privacy dropped the home row or showed the shared fallback, and the home's bare #anchors did not work from another page. One NAV_LINKS now sends Your machine, How it works and API as /#machine, /#how and /#api on the home, the update log, the legal pages and the 404.

Changes

ddiff --git a/server/app.js b/server/app.js
iindex 61a8997..58c8688 100644
---- a/server/app.js
++++ b/server/app.js
@@@ -177,7 +177,15 @@ function createApp(opts = {}) {
res.type('html').send(home.renderUpdates({ config }));
});
{ const legal = require('openvibe-shared/legal');
-- app.get(legal.PATHS, (req, res, next) => { res.setHeader('Content-Security-Policy', home.HOME_CSP); next(); }, legal.handler({ id: 'run', service: 'run', host: 'openvibe.run', name: home.SITE_NAME, profile: 'hosting' })); }
++ // Shared's legal template starts the navbar without links, so /terms and /privacy showed its fallback
++ // (Home, My Account, Themes) instead of Run's own pages. Add Run's list to that init call.
++ const navLinks = JSON.stringify(home.NAV_LINKS);
++ app.get(legal.PATHS, (req, res, next) => {
++ res.setHeader('Content-Security-Policy', home.HOME_CSP);
++ const send = res.send.bind(res);
++ res.send = (body) => send(String(body).replace('try{OpenVibeNavbar.init({service:', `try{OpenVibeNavbar.init({links:${navLinks},service:`));
++ next();
++ }, legal.handler({ id: 'run', service: 'run', host: 'openvibe.run', name: home.SITE_NAME, profile: 'hosting' })); }
// The agent layer (openvibe-shared/agent-layer): /.well-known/agent.json, and the Markdown twins of the home
// and the update log. Mounted after the page routes, so a browser keeps this site's own page (which names the
// twin) and only a twin request reaches this renderer; before the fallback, so an unknown .md is a 404.
ddiff --git a/server/web/home.js b/server/web/home.js
iindex 53d906e..f2769b9 100644
---- a/server/web/home.js
++++ b/server/web/home.js
@@@ -17,6 +17,9 @@ const appIcon = require('openvibe-shared/app-icon');
const ovServe = require('openvibe-shared/serve');
const SITE_NAME = 'OpenVibe.Run';
++// The site's own places, the same on every page: home's sections, addressed from anywhere (on /updates a bare
++// "#how" went nowhere). The first row is the home itself.
++const NAV_LINKS = [{ label: 'Your machine', href: '/#machine' }, { label: 'How it works', href: '/#how' }, { label: 'API', href: '/#api' }];
// The questions the catalog answers for Run (openvibe-contracts manifests/services/run.json, kept word for word): shown
// on the home and given to search engines as FAQPage data.
const FAQ = ((require('openvibe-contracts/manifests/services/run.json').site || {}).faq || [])
@@@ -73,7 +76,7 @@ function renderHome({ config }) {
const nav = {
service: 'run',
apiBase: NETWORK_URL, sessionUrl: '/auth/me', loginUrl: '/auth/login?next={path}', logoutUrl: '/auth/logout?next={url}', silentLogin: '/auth/login?silent=1&next={url}',
-- links: [{ label: 'Your machine', href: '#machine' }, { label: 'How it works', href: '#how' }, { label: 'API', href: '#api' }],
++ links: NAV_LINKS,
history: { type: 'page', title: SITE_NAME },
};
return shell.page({
@@@ -98,7 +101,7 @@ const UPDATES_FONT = "<style>:where(body){font-family:var(--ov-font,'Inter',-app
/** /updates: what shipped on this site, the network's shared update log (openvibe-shared/frame updatesBody). */
function renderUpdates({ config }) {
-- const links = [{ label: 'How it works', href: '/#how' }, { label: 'API', href: '/#api' }];
++ const links = NAV_LINKS;
return shell.page({
name: SITE_NAME, service: 'run', lang: 'en',
title: `What shipped on ${SITE_NAME}`,
@@@ -115,7 +118,7 @@ function renderUpdates({ config }) {
/** The page a browser gets for a path nothing serves: the site's own Frame, one way back, and no clue about what was asked for. */
function renderNotFound({ config }) {
const host = String(config.baseUrl).replace(/^https?:\/\//, '').replace(/\/.*$/, '');
-- const links = [{ label: 'How it works', href: '/#how' }, { label: 'API', href: '/#api' }];
++ const links = NAV_LINKS;
return shell.page({
name: SITE_NAME, service: 'run', lang: 'en',
title: `Page not found · ${SITE_NAME}`,
@@@ -258,4 +261,4 @@ function body() {
});
}
--module.exports = { renderHome, renderUpdates, renderNotFound, HOME_CSP, SITE_NAME, DESCRIPTION, FAQ };
++module.exports = { renderHome, renderUpdates, renderNotFound, HOME_CSP, SITE_NAME, DESCRIPTION, FAQ, NAV_LINKS };
ddiff --git a/test/page-links.test.js b/test/page-links.test.js
nnew file mode 100644
iindex 0000000..61cdb70
---- /dev/null
++++ b/test/page-links.test.js
@@@ -0,0 +1,45 @@
++'use strict';
++// Every page of openvibe.run gives the shared navbar the site's own page list (the owner, 2026-11-12): the home,
++// /updates, /terms, /privacy and /dmca all send Your machine / How it works / API, addressed from any page.
++const assert = require('assert');
++const { boot, check, done } = require('./helpers/app');
++const { NAV_LINKS } = require('../server/web/home');
++
++function initLinks(html) {
++ const at = html.indexOf('OpenVibeNavbar.init');
++ if (at < 0) return null;
++ const start = html.indexOf('links', at);
++ const open = html.indexOf('[', start);
++ let depth = 0, q = null, esc = false;
++ for (let i = open; i < html.length; i += 1) {
++ const c = html[i];
++ if (q) { if (esc) esc = false; else if (c === '\\') esc = true; else if (c === q) q = null; continue; }
++ if (c === '"' || c === "'") { q = c; continue; }
++ if (c === '[') depth += 1; else if (c === ']') { depth -= 1; if (!depth) return JSON.parse(html.slice(open, i + 1)); }
++ }
++ return null;
++}
++
++(async () => {
++ const t = await boot();
++ try {
++ const own = NAV_LINKS.map(({ label, href }) => ({ label, href }));
++ await check('every page sends Run\'s own list, not the shared fallback', async () => {
++ for (const path of ['/', '/updates', '/terms', '/privacy', '/dmca']) {
++ const r = await t.call('GET', path, { token: null });
++ assert.strictEqual(r.status, 200, `${path} answered ${r.status}`);
++ const links = (initLinks(r.text) || []).map(({ label, href }) => ({ label, href }));
++ assert.deepStrictEqual(links, own, `${path} sends Run's list`);
++ }
++ });
++ await check('every row is a route or a section of the home', async () => {
++ for (const { href } of NAV_LINKS) {
++ const [path, hash] = href.split('#');
++ const r = await t.call('GET', path || '/', { token: null });
++ assert.strictEqual(r.status, 200, `${path} is a route`);
++ if (hash) assert.ok(r.text.includes(`id="${hash}"`), `${href} names a section on the home`);
++ }
++ });
++ } finally { await t.close(); }
++ done();
++})().catch((e) => { console.error(e); process.exit(1); });