openvibers/openvibe.live Commits

Links to Live's own pages given as full addresses work (the navbar's My channel opened /https://openvibe.live/@name): navigate() takes their path, and a glued own address 301s to it (#422)

OP OpenVibers committed 16 days ago · 6b304dc163 · 3 files, +37 −3

Changes

ddiff --git a/public/js/app.js b/public/js/app.js
iindex 21bdf54b..2cdfe161 100644
---- a/public/js/app.js
++++ b/public/js/app.js
@@@ -945,8 +945,15 @@ function _ovHoldsMedia() {
/** A URL that leaves Live (an OpenVibe.Video watch page, another OpenVibe site): the browser goes there, not the SPA. */
const isOffSite = (url) => /^https?:\/\//i.test(String(url || '')) && !String(url).startsWith(`${location.origin}/`);
++/** A full address on Live itself (the shared navbar's "My channel" is https://openvibe.live/@name) as its path. */
++function sameSitePath(url) {
++ if (!/^https?:\/\//i.test(String(url || ''))) return url;
++ try { const u = new URL(url); return u.origin === location.origin ? `${u.pathname}${u.search}${u.hash}` : url; } catch { return url; }
++}
++
function navigate(urlPath, replace = false) {
if (isOffSite(urlPath)) { if (replace) location.replace(urlPath); else location.href = urlPath; return; }
++ urlPath = sameSitePath(urlPath);
if (_ovRelease.stale && !_ovHoldsMedia()) {
const to = urlPath.startsWith('/') ? urlPath : `/${urlPath}`;
try { sessionStorage.setItem('ov-release-load', _ovRelease.stale); } catch { /* */ }
ddiff --git a/server/web/video-handover.js b/server/web/video-handover.js
iindex e0c001fc..68179e75 100644
---- a/server/web/video-handover.js
++++ b/server/web/video-handover.js
@@@ -28,11 +28,14 @@ function secondsOf(t) {
// which put any link it was handed under '/', and the API now hands it these absolute links.
const GLUED_RE = /^\/https?:\/\/?([^/?#]+)(\/[^?#]*)?$/i;
--/** Where a glued path really points, on one of the two sites Live hands pages to; null for anything else. */
--function unglue(pathname, search = '') {
++/** Where a glued path really points, on one of the two sites Live hands pages to, or Live's own page (its path, when
++ * ownHost names the host it was glued onto: a router that put a full openvibe.live link under '/'); null otherwise. */
++function unglue(pathname, search = '', ownHost = null) {
const m = GLUED_RE.exec(String(pathname || ''));
if (!m) return null;
const host = m[1].toLowerCase();
++ // One leading slash: //evil.example/x would leave the site.
++ if (ownHost && host === String(ownHost).toLowerCase()) return `/${(m[2] || '/').replace(/^\/+/, '')}${search}`;
const origin = [VIDEO_BASE, communityUrl()].find((o) => { try { return new URL(o).host === host; } catch { return false; } });
return origin ? `${origin}${m[2] || '/'}${search}` : null;
}
@@@ -42,7 +45,7 @@ function unglue(pathname, search = '') {
function register(app) {
app.get(/^\/https?:\//i, (req, res, next) => {
const q = req.originalUrl.indexOf('?');
-- const to = unglue(req.path, q >= 0 ? req.originalUrl.slice(q) : '');
++ const to = unglue(req.path, q >= 0 ? req.originalUrl.slice(q) : '', req.get('host'));
return to ? res.redirect(301, to) : next();
});
for (const [prefix, kind] of [['vod', 'vod'], ['clip', 'clip']]) {
ddiff --git a/test/video-handover.test.js b/test/video-handover.test.js
iindex 76926726..d7175943 100644
---- a/test/video-handover.test.js
++++ b/test/video-handover.test.js
@@@ -47,6 +47,30 @@ process.env.NODE_ENV = 'test';
assert.strictEqual((await get('/https:openvibe.video/v/1')).status, 404);
});
++ await check('Live\'s own full address glued onto Live\'s (the navbar\'s "My channel" through the old router) goes to its path, never off the site', async () => {
++ const { unglue } = require('../server/web/video-handover');
++ assert.strictEqual(unglue('/https://openvibe.live/@goosely', '', 'openvibe.live'), '/@goosely');
++ assert.strictEqual(unglue('/https:/openvibe.live/@goosely', '?tab=videos', 'openvibe.live'), '/@goosely?tab=videos');
++ assert.strictEqual(unglue('/https://openvibe.live', '', 'openvibe.live'), '/');
++ assert.strictEqual(unglue('/https://openvibe.live//evil.example/x', '', 'openvibe.live'), '/evil.example/x', 'one leading slash: it stays on Live');
++ assert.strictEqual(unglue('/https://openvibe.live/@goosely', '', null), null, 'no own host: not a redirect');
++ assert.strictEqual(unglue('/https://evil.example/@x', '', 'openvibe.live'), null);
++ const host = `127.0.0.1:${server.address().port}`;
++ assert.deepStrictEqual(await get(`/https://${host}/@goosely`), { status: 301, location: '/@goosely' });
++ });
++
++ await check('the SPA router takes a full address on Live as its path (public/js/app.js sameSitePath)', async () => {
++ const src = fs.readFileSync(path.join(__dirname, '..', 'public', 'js', 'app.js'), 'utf8');
++ const fn = /function sameSitePath\(url\) \{[\s\S]*?\n\}/.exec(src);
++ assert.ok(fn, 'sameSitePath is in app.js');
++ assert.match(src, /urlPath = sameSitePath\(urlPath\);/, 'navigate() uses it');
++ const sameSitePath = new Function('location', `${fn[0]}; return sameSitePath;`)({ origin: 'https://openvibe.live' });
++ assert.strictEqual(sameSitePath('https://openvibe.live/@goosely'), '/@goosely');
++ assert.strictEqual(sameSitePath('https://openvibe.live/@goosely?tab=videos#top'), '/@goosely?tab=videos#top');
++ assert.strictEqual(sameSitePath('/content'), '/content');
++ assert.strictEqual(sameSitePath('https://openvibe.video/v/1'), 'https://openvibe.video/v/1', 'another site is left alone');
++ });
++
await check('what is not an id stays Live\'s 404', async () => {
assert.strictEqual((await get('/vod/abc')).status, 404);
assert.strictEqual((await get('/vod/0')).status, 404);