openvibers/openvibe.host Commits
The shared update system and one account UI, on the OpenVibe Frame (openvibe-shared 1.11.0, openvibe/frame): the home shows what shipped, /updates is the shared log and the footer links it; the shared navbar handles sign in and out (logoutUrl ends this site's session) and carries this site's account links in its menu, while the page's own account bar is only for no-JS visitors
Changes
@@@ -18,7 +18,7 @@"express-rate-limit": "^7.5.0","openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.33.0","openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.5.0",-- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.5.1"++ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.11.0"},"bin": {"ovhost": "bin/ovhost"@@@ -1437,9 +1437,9 @@}},"node_modules/openvibe-shared": {-- "version": "1.5.1",-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.5.1",-- "integrity": "sha512-1XWRFwN8br3iiy2Z8fmnXQMgR76VlfMpHjP1xr5uKLCJmtvtNSWO1BgA1kQmVtyBY7g+1n6YFIjahaD4qWbD/Q==",++ "version": "1.11.0",++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.11.0",++ "integrity": "sha512-edMW0I25HNFReLcfZ9ak+4q5sy2t7qq7EPG52tOerTBV2fU994C5we/02AD/9i2Vuq0+ayNBBisW0cg60L6DLQ==","license": "MIT","dependencies": {"jsonwebtoken": "^9.0.0"@@@ -33,7 +33,7 @@"express-rate-limit": "^7.5.0","openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.33.0","openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.5.0",-- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.5.1"++ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.11.0"},"engines": {"node": ">=22.0.0"@@@ -46,7 +46,7 @@ const VERSION = require('../package.json').version;const DASHBOARD_CSP = {'default-src': ["'self'"],-- // The shared chrome (theme-loader, navbar) comes from the Network; the inline init is ours.++ // The OpenVibe Frame (theme-loader, navbar, footer) comes from the Network; the inline init is ours.'script-src': ["'self'", "'unsafe-inline'", 'https://openvibe.network'],'style-src': ["'self'", "'unsafe-inline'", 'https://openvibe.network', 'https://fonts.googleapis.com', 'https://cdnjs.cloudflare.com'],'font-src': ["'self'", 'data:', 'https://fonts.gstatic.com', 'https://cdnjs.cloudflare.com'],@@@ -6,6 +6,7 @@* enough on openvibe.host). Every response is private and never cached by a shared cache.*/const express = require('express');++const frame = require('openvibe-shared/frame');const { renderPage } = require('../render/layout');const pages = require('../render/pages');const { csrfToken, checkCsrf, sameOrigin } = require('../auth/forms');@@@ -61,6 +62,8 @@ function createDashboard(ctx) {const back = (path, text, kind) => `${path}?notice=${encodeURIComponent(text)}${kind ? `&kind=${kind}` : ''}`;// ── Pages ───────────────────────────────────────────────++ // What shipped on OpenVibe.Host: the shared update log every OpenVibe site has.++ router.get('/updates', view((req, res) => page(req, res, 200, 'What shipped on OpenVibe.Host', frame.updatesBody({ service: 'host', siteName: 'OpenVibe.Host' }) + frame.shippedScript(), null, { indexable: true })));router.get('/', view((req, res) => {// The signed-out front page is the public page of openvibe.host (sitemap.xml): indexable.// Everything behind sign-in stays noindex and private.@@@ -2,14 +2,14 @@/*** Dashboard page shell. Every page is server-rendered and complete without JavaScript (all actions-- * are plain forms); the shared chrome (navbar.js + theme-loader.js from the Network) is progressive.++ * are plain forms); the OpenVibe Frame (navbar.js + theme-loader.js from the Network) is progressive.* Dashboard pages are private: noindex, private caching.*/const crypto = require('crypto');const fs = require('fs');const path = require('path');const appIcon = require('openvibe-shared/app-icon');--const chrome = require('openvibe-shared/chrome-ssr');++const frame = require('openvibe-shared/frame');const NETWORK_URL = 'https://openvibe.network';const SITE_NAME = 'OpenVibe.Host';@@@ -40,8 +40,13 @@ function renderPage(o) {silentLogin: `${o.config.baseUrl}/auth/login?silent=1&next={url}`,sessionUrl: '/auth/me',loginUrl: `/auth/login?next=${loginNext}`,++ logoutUrl: '/auth/logout?next={path}', // Sign out in the shared navbar ends this site's session too};const who = signedIn ? esc((viewer.user && (viewer.user.display_name || viewer.user.username)) || viewer.subject) : '';++ // This site's own account links live in the shared navbar's account menu (the page's account++ // bar below is only for visitors without JavaScript).++ if (signedIn) nav.menu = { before: [] };++ const footer = { service: 'host', variant: 'full', mount: '#ov-footer', brandName: SITE_NAME, updates: '/updates' };const account = signedIn? `Signed in as ${who} · <a href="/auth/logout?next=%2F">Sign out</a>`: `<a href="/auth/login?next=${loginNext}">Sign in with OpenVibe</a>`;@@@ -59,21 +64,24 @@ ${appIcon.headTags({ site: 'host' })}<link rel="stylesheet" href="${asset('css/host.css')}"><script src="${NETWORK_URL}/shared/theme-loader.js" defer></script><script src="${NETWORK_URL}/shared/navbar.js" defer></script>++<script src="${NETWORK_URL}/shared/footer.js" defer></script></head><body><a class="skip" href="#main">Skip to content</a><div id="navbar-mount"></div>--${chrome.noscriptNav({ name: SITE_NAME, home: '/', links: [{ label: 'Projects', href: '/' }] })}--<div class="account-bar" role="navigation" aria-label="Account">${account}</div>++${frame.noscriptNav({ name: SITE_NAME, home: '/', links: [{ label: 'Projects', href: '/' }] })}++<noscript><div class="account-bar" role="navigation" aria-label="Account">${account}</div></noscript><main id="main" class="page">${notice}${o.body || ''}++${o.path === '/' ? frame.shipped({ service: 'host', title: `Recently shipped on ${SITE_NAME}` }) : ''}</main>--${chrome.footer({ service: 'host', variant: 'full' })}++${frame.footer(footer)}<script>--window.__OV_PAGE = ${JSON.stringify({ navbar: nav }).replace(/</g, '\\u003c')};++window.__OV_PAGE = ${JSON.stringify({ navbar: nav, footer }).replace(/</g, '\\u003c')};document.addEventListener('DOMContentLoaded', function () {-- try { if (window.OpenVibeNavbar) OpenVibeNavbar.init(window.__OV_PAGE.navbar); } catch (e) { /* the chrome is optional */ }++ try { if (window.OpenVibeNavbar) OpenVibeNavbar.init(window.__OV_PAGE.navbar); } catch (e) { /* the Frame is optional */ }++ try { if (window.OpenVibeFooter) OpenVibeFooter.init(window.__OV_PAGE.footer); } catch (e) { /* */ }});</script></body>
@@ -18,7 +18,7 @@
@@ -18,7 +18,7 @@
"express-rate-limit": "^7.5.0",
"express-rate-limit": "^7.5.0",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.33.0",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.33.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.5.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.5.0",
- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.5.1"
+ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.11.0"
},
},
"bin": {
"bin": {
"ovhost": "bin/ovhost"
"ovhost": "bin/ovhost"
@@ -1437,9 +1437,9 @@
@@ -1437,9 +1437,9 @@
}
}
},
},
"node_modules/openvibe-shared": {
"node_modules/openvibe-shared": {
- "version": "1.5.1",
- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.5.1",
- "integrity": "sha512-1XWRFwN8br3iiy2Z8fmnXQMgR76VlfMpHjP1xr5uKLCJmtvtNSWO1BgA1kQmVtyBY7g+1n6YFIjahaD4qWbD/Q==",
+ "version": "1.11.0",
+ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.11.0",
+ "integrity": "sha512-edMW0I25HNFReLcfZ9ak+4q5sy2t7qq7EPG52tOerTBV2fU994C5we/02AD/9i2Vuq0+ayNBBisW0cg60L6DLQ==",
"license": "MIT",
"license": "MIT",
"dependencies": {
"dependencies": {
"jsonwebtoken": "^9.0.0"
"jsonwebtoken": "^9.0.0"
@@ -33,7 +33,7 @@
@@ -33,7 +33,7 @@
"express-rate-limit": "^7.5.0",
"express-rate-limit": "^7.5.0",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.33.0",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.33.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.5.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.5.0",
- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.5.1"
+ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.11.0"
},
},
"engines": {
"engines": {
"node": ">=22.0.0"
"node": ">=22.0.0"
@@ -46,7 +46,7 @@ const VERSION = require('../package.json').version;
@@ -46,7 +46,7 @@ const VERSION = require('../package.json').version;
const DASHBOARD_CSP = {
const DASHBOARD_CSP = {
'default-src': ["'self'"],
'default-src': ["'self'"],
- // The shared chrome (theme-loader, navbar) comes from the Network; the inline init is ours.
+ // The OpenVibe Frame (theme-loader, navbar, footer) comes from the Network; the inline init is ours.
'script-src': ["'self'", "'unsafe-inline'", 'https://openvibe.network'],
'script-src': ["'self'", "'unsafe-inline'", 'https://openvibe.network'],
'style-src': ["'self'", "'unsafe-inline'", 'https://openvibe.network', 'https://fonts.googleapis.com', 'https://cdnjs.cloudflare.com'],
'style-src': ["'self'", "'unsafe-inline'", 'https://openvibe.network', 'https://fonts.googleapis.com', 'https://cdnjs.cloudflare.com'],
'font-src': ["'self'", 'data:', 'https://fonts.gstatic.com', 'https://cdnjs.cloudflare.com'],
'font-src': ["'self'", 'data:', 'https://fonts.gstatic.com', 'https://cdnjs.cloudflare.com'],
@@ -6,6 +6,7 @@
@@ -6,6 +6,7 @@
* enough on openvibe.host). Every response is private and never cached by a shared cache.
* enough on openvibe.host). Every response is private and never cached by a shared cache.
*/
*/
const express = require('express');
const express = require('express');
+const frame = require('openvibe-shared/frame');
const { renderPage } = require('../render/layout');
const { renderPage } = require('../render/layout');
const pages = require('../render/pages');
const pages = require('../render/pages');
const { csrfToken, checkCsrf, sameOrigin } = require('../auth/forms');
const { csrfToken, checkCsrf, sameOrigin } = require('../auth/forms');
@@ -61,6 +62,8 @@ function createDashboard(ctx) {
@@ -61,6 +62,8 @@ function createDashboard(ctx) {
const back = (path, text, kind) => `${path}?notice=${encodeURIComponent(text)}${kind ? `&kind=${kind}` : ''}`;
const back = (path, text, kind) => `${path}?notice=${encodeURIComponent(text)}${kind ? `&kind=${kind}` : ''}`;
// ── Pages ───────────────────────────────────────────────
// ── Pages ───────────────────────────────────────────────
+ // What shipped on OpenVibe.Host: the shared update log every OpenVibe site has.
+ router.get('/updates', view((req, res) => page(req, res, 200, 'What shipped on OpenVibe.Host', frame.updatesBody({ service: 'host', siteName: 'OpenVibe.Host' }) + frame.shippedScript(), null, { indexable: true })));
router.get('/', view((req, res) => {
router.get('/', view((req, res) => {
// The signed-out front page is the public page of openvibe.host (sitemap.xml): indexable.
// The signed-out front page is the public page of openvibe.host (sitemap.xml): indexable.
// Everything behind sign-in stays noindex and private.
// Everything behind sign-in stays noindex and private.
@@ -2,14 +2,14 @@
@@ -2,14 +2,14 @@
/**
/**
* Dashboard page shell. Every page is server-rendered and complete without JavaScript (all actions
* Dashboard page shell. Every page is server-rendered and complete without JavaScript (all actions
- * are plain forms); the shared chrome (navbar.js + theme-loader.js from the Network) is progressive.
+ * are plain forms); the OpenVibe Frame (navbar.js + theme-loader.js from the Network) is progressive.
* Dashboard pages are private: noindex, private caching.
* Dashboard pages are private: noindex, private caching.
*/
*/
const crypto = require('crypto');
const crypto = require('crypto');
const fs = require('fs');
const fs = require('fs');
const path = require('path');
const path = require('path');
const appIcon = require('openvibe-shared/app-icon');
const appIcon = require('openvibe-shared/app-icon');
-const chrome = require('openvibe-shared/chrome-ssr');
+const frame = require('openvibe-shared/frame');
const NETWORK_URL = 'https://openvibe.network';
const NETWORK_URL = 'https://openvibe.network';
const SITE_NAME = 'OpenVibe.Host';
const SITE_NAME = 'OpenVibe.Host';
@@ -40,8 +40,13 @@ function renderPage(o) {
@@ -40,8 +40,13 @@ function renderPage(o) {
silentLogin: `${o.config.baseUrl}/auth/login?silent=1&next={url}`,
silentLogin: `${o.config.baseUrl}/auth/login?silent=1&next={url}`,
sessionUrl: '/auth/me',
sessionUrl: '/auth/me',
loginUrl: `/auth/login?next=${loginNext}`,
loginUrl: `/auth/login?next=${loginNext}`,
+ logoutUrl: '/auth/logout?next={path}', // Sign out in the shared navbar ends this site's session too
};
};
const who = signedIn ? esc((viewer.user && (viewer.user.display_name || viewer.user.username)) || viewer.subject) : '';
const who = signedIn ? esc((viewer.user && (viewer.user.display_name || viewer.user.username)) || viewer.subject) : '';
+ // This site's own account links live in the shared navbar's account menu (the page's account
+ // bar below is only for visitors without JavaScript).
+ if (signedIn) nav.menu = { before: [] };
+ const footer = { service: 'host', variant: 'full', mount: '#ov-footer', brandName: SITE_NAME, updates: '/updates' };
const account = signedIn
const account = signedIn
? `Signed in as ${who} · <a href="/auth/logout?next=%2F">Sign out</a>`
? `Signed in as ${who} · <a href="/auth/logout?next=%2F">Sign out</a>`
: `<a href="/auth/login?next=${loginNext}">Sign in with OpenVibe</a>`;
: `<a href="/auth/login?next=${loginNext}">Sign in with OpenVibe</a>`;
@@ -59,21 +64,24 @@ ${appIcon.headTags({ site: 'host' })}
@@ -59,21 +64,24 @@ ${appIcon.headTags({ site: 'host' })}
<link rel="stylesheet" href="${asset('css/host.css')}">
<link rel="stylesheet" href="${asset('css/host.css')}">
<script src="${NETWORK_URL}/shared/theme-loader.js" defer></script>
<script src="${NETWORK_URL}/shared/theme-loader.js" defer></script>
<script src="${NETWORK_URL}/shared/navbar.js" defer></script>
<script src="${NETWORK_URL}/shared/navbar.js" defer></script>
+<script src="${NETWORK_URL}/shared/footer.js" defer></script>
</head>
</head>
<body>
<body>
<a class="skip" href="#main">Skip to content</a>
<a class="skip" href="#main">Skip to content</a>
<div id="navbar-mount"></div>
<div id="navbar-mount"></div>
-${chrome.noscriptNav({ name: SITE_NAME, home: '/', links: [{ label: 'Projects', href: '/' }] })}
-<div class="account-bar" role="navigation" aria-label="Account">${account}</div>
+${frame.noscriptNav({ name: SITE_NAME, home: '/', links: [{ label: 'Projects', href: '/' }] })}
+<noscript><div class="account-bar" role="navigation" aria-label="Account">${account}</div></noscript>
<main id="main" class="page">
<main id="main" class="page">
${notice}
${notice}
${o.body || ''}
${o.body || ''}
+${o.path === '/' ? frame.shipped({ service: 'host', title: `Recently shipped on ${SITE_NAME}` }) : ''}
</main>
</main>
-${chrome.footer({ service: 'host', variant: 'full' })}
+${frame.footer(footer)}
<script>
<script>
-window.__OV_PAGE = ${JSON.stringify({ navbar: nav }).replace(/</g, '\\u003c')};
+window.__OV_PAGE = ${JSON.stringify({ navbar: nav, footer }).replace(/</g, '\\u003c')};
document.addEventListener('DOMContentLoaded', function () {
document.addEventListener('DOMContentLoaded', function () {
- try { if (window.OpenVibeNavbar) OpenVibeNavbar.init(window.__OV_PAGE.navbar); } catch (e) { /* the chrome is optional */ }
+ try { if (window.OpenVibeNavbar) OpenVibeNavbar.init(window.__OV_PAGE.navbar); } catch (e) { /* the Frame is optional */ }
+ try { if (window.OpenVibeFooter) OpenVibeFooter.init(window.__OV_PAGE.footer); } catch (e) { /* */ }
});
});
</script>
</script>
</body>
</body>