openvibers/openvibe.mediahub Commits
Pics legal pages send Pics' own page list (#417)
Changes
@@@ -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 };@@@ -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="resbody: 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-varsnnew file mode 100644@@@ -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); });
@@ -121,4 +121,4 @@ function send(res, status, o) {
@@ -121,4 +121,4 @@ function send(res, status, o) {
res.status(status).type('html').send(renderPage({ ...o, status }));
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 };
@@ -35,7 +35,7 @@ const { formatBytes } = require('../drive/rules');
@@ -35,7 +35,7 @@ const { formatBytes } = require('../drive/rules');
const { html, raw, notice, time, shareButton } = require('../render/html');
const { html, raw, notice, time, shareButton } = require('../render/html');
const labelsUi = require('../render/labels-ui');
const labelsUi = require('../render/labels-ui');
const { ACCESS_COOKIE, REFRESH_COOKIE } = require('../auth/sso');
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 store = require('./store');
const { createPicsApi } = require('./api');
const { createPicsApi } = require('./api');
const { createCommentsClient } = require('./comments-client');
const { createCommentsClient } = require('./comments-client');
@@ -873,7 +873,14 @@ ${row.state === 'hidden' ? html`<button class="ovp-btn" name="action" value="res
@@ -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>`),
body: raw(frame.updatesBody({ service: 'media-hub', siteName: SITE_NAME }) + `<script src="${ovServe.url('shipped.js')}" defer></script>`),
}));
}));
const legal = require('openvibe-shared/legal');
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));
r.use((req, res) => notFound(req, res));
// eslint-disable-next-line no-unused-vars
// eslint-disable-next-line no-unused-vars
new file mode 100644
new file mode 100644
@@ -0,0 +1,60 @@
@@ -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); });