openvibers/openvibe.mediahub Commits

Pics legal pages send Pics' own page list (#417)

OP OpenVibers committed 24 h ago · 75004aa44b · 3 files, +70 −3

Shared's legal template starts the navbar without links, so /terms, /privacy and /dmca on openvibe.pics showed its fallback (Home, My Account, Themes) instead of Explore, Your pictures and Albums. The Pics handler now adds its list to that init call, and the test checks every page and every row's route.

Changes

ddiff --git a/server/pics/layout.js b/server/pics/layout.js
iindex 9bae161..e16ade6 100644
---- a/server/pics/layout.js
++++ b/server/pics/layout.js
@@@ -121,4 +121,4 @@ function send(res, status, o) {
res.status(status).type('html').send(renderPage({ ...o, status }));
}
--module.exports = { renderPage, send, asset, assetVersion, SITE_NAME, TAGLINE, raw, html };
++module.exports = { renderPage, send, asset, assetVersion, SITE_NAME, TAGLINE, raw, html, NAV_LINKS };
ddiff --git a/server/pics/pages.js b/server/pics/pages.js
iindex 4d10582..065253b 100644
---- a/server/pics/pages.js
++++ b/server/pics/pages.js
@@@ -35,7 +35,7 @@ const { formatBytes } = require('../drive/rules');
const { html, raw, notice, time, shareButton } = require('../render/html');
const labelsUi = require('../render/labels-ui');
const { ACCESS_COOKIE, REFRESH_COOKIE } = require('../auth/sso');
--const { send, assetVersion, SITE_NAME } = require('./layout');
++const { send, assetVersion, SITE_NAME, NAV_LINKS } = require('./layout');
const store = require('./store');
const { createPicsApi } = require('./api');
const { createCommentsClient } = require('./comments-client');
@@@ -873,7 +873,14 @@ ${row.state === 'hidden' ? html`<button class="ovp-btn" name="action" value="res
body: raw(frame.updatesBody({ service: 'media-hub', siteName: SITE_NAME }) + `<script src="${ovServe.url('shipped.js')}" defer></script>`),
}));
const legal = require('openvibe-shared/legal');
-- r.get(legal.PATHS, legal.handler({ id: 'media-hub', service: 'media-hub', host, name: SITE_NAME, profile: 'ugc' }));
++ // Shared's legal template starts the navbar without links, so /terms and /privacy showed its fallback
++ // (Home, My Account, Themes) instead of Pics' own pages. Add Pics' list to that init call.
++ const navLinks = JSON.stringify(NAV_LINKS);
++ r.get(legal.PATHS, (req, res, next) => {
++ const sendLegal = res.send.bind(res);
++ res.send = (body) => sendLegal(String(body).replace('try{OpenVibeNavbar.init({service:', `try{OpenVibeNavbar.init({links:${navLinks},service:`));
++ next();
++ }, legal.handler({ id: 'media-hub', service: 'media-hub', host, name: SITE_NAME, profile: 'ugc' }));
r.use((req, res) => notFound(req, res));
// eslint-disable-next-line no-unused-vars
ddiff --git a/test/page-links.test.js b/test/page-links.test.js
nnew file mode 100644
iindex 0000000..e0fa29a
---- /dev/null
++++ b/test/page-links.test.js
@@@ -0,0 +1,60 @@
++'use strict';
++// Every page of openvibe.pics gives the shared navbar Pics' own page list (the owner, 2026-11-12). Shared's legal
++// template used to fall back to Home / My Account / Themes on /terms, /privacy and /dmca.
++const assert = require('assert');
++const http = require('http');
++const { boot, check, done } = require('./helpers/boot');
++const { NAV_LINKS } = require('../server/pics/layout');
++
++function hostClient(t) {
++ return (p) => new Promise((resolve, reject) => {
++ const req = http.request({ hostname: new URL(t.base).hostname, port: new URL(t.base).port, path: p, method: 'GET', headers: { Host: 'openvibe.pics' } }, (res) => {
++ let body = '';
++ res.on('data', (c) => { body += c; });
++ res.on('end', () => resolve({ status: res.statusCode, text: body }));
++ });
++ req.on('error', reject);
++ req.end();
++ });
++}
++
++/** The links inside a page's OpenVibeNavbar.init({...}) call. */
++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();
++ const get = hostClient(t);
++ try {
++ const own = NAV_LINKS.map(({ label, href }) => ({ label, href }));
++ await check('every page sends Pics\' own list, not the shared fallback', async () => {
++ for (const path of ['/', '/updates', ...NAV_LINKS.map((l) => l.href), '/terms', '/privacy', '/dmca']) {
++ const r = await get(path);
++ assert.ok([200, 302, 401, 403].includes(r.status), `${path} answered ${r.status}`);
++ if (r.status !== 200) continue;
++ const links = (initLinks(r.text) || []).map(({ label, href }) => ({ label, href }));
++ assert.deepStrictEqual(links, own, `${path} sends Pics' list`);
++ }
++ });
++ await check('every row is a route of this host', async () => {
++ for (const { href } of NAV_LINKS) {
++ const r = await get(href);
++ assert.notStrictEqual(r.status, 404, `${href} is a route`);
++ assert.ok(r.status < 500, `${href} does not fail (${r.status})`);
++ }
++ });
++ } finally { await t.close(); }
++ done();
++})().catch((e) => { console.error(e); process.exit(1); });