openvibers/openvibe.run Commits

Run: /updates — what shipped on openvibe.run (the shared update log) (#47)

OP OpenVibers committed 29 days ago · 5380d73335 · 5 files, +39 −5

openvibe.run gets `/updates`: what shipped on the site, from the network's shared update log (`openvibe-shared/frame` `updatesBody` + `shipped.js`), inside the Frame like the front page. A site-wide crawl found openvibe.help linking it (404).

- One `<h1>`, a canonical URL, the home's CSP and a public 5-minute cache.
- The footer links it.
- The sitemap now reads `discovery.PAGES`, which gains `/updates`, so the sitemap, llms.txt and the IndexNow batch agree.

`test/site.test.js` checks the page and the sitemap. `test/discovery.test.js` expects `/updates` in the IndexNow batch.

Changes

ddiff --git a/server/app.js b/server/app.js
iindex 30cf3ce..92f0fcb 100644
---- a/server/app.js
++++ b/server/app.js
@@@ -114,12 +114,18 @@ function createApp(opts = {}) {
res.setHeader('Cache-Control', 'public, max-age=300');
res.type('html').send(home.renderHome({ config }));
});
++ // What shipped on this site: the shared update log, as on every OpenVibe site.
++ app.get('/updates', (req, res) => {
++ res.setHeader('Content-Security-Policy', home.HOME_CSP);
++ res.setHeader('Cache-Control', 'public, max-age=300');
++ 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' })); }
app.get('/llms.txt', (req, res) => res.type('text/plain').set('Cache-Control', 'public, max-age=3600').send(discovery.llmsTxt(config)));
app.get('/llms-full.txt', (req, res) => res.type('text/plain').set('Cache-Control', 'public, max-age=3600').send(discovery.llmsFull(config)));
app.get('/robots.txt', (req, res) => res.type('text/plain').send(['User-agent: *', 'Allow: /$', 'Disallow: /api/', 'Disallow: /internal/', `Sitemap: ${config.baseUrl}/sitemap.xml`, ''].join('\n')));
-- app.get('/sitemap.xml', (req, res) => res.type('application/xml').send(require('openvibe-shared/seo').sitemapXml(['/', '/terms', '/privacy', '/dmca'].map((p) => ({ loc: `${config.baseUrl}${p}` })))));
++ app.get('/sitemap.xml', (req, res) => res.type('application/xml').send(require('openvibe-shared/seo').sitemapXml(discovery.PAGES.map((p) => ({ loc: `${config.baseUrl}${p}` })))));
app.get('/favicon.ico', (req, res) => res.type('image/svg+xml').set('Cache-Control', 'public, max-age=86400').send(appIcon.favicon({ site: 'run' })));
app.get('/manifest.webmanifest', (req, res) => res.type('application/manifest+json').send(JSON.stringify(appIcon.manifest({ site: 'run', name: home.SITE_NAME, shortName: 'Run', description: home.DESCRIPTION, startUrl: '/' }))));
ddiff --git a/server/web/discovery.js b/server/web/discovery.js
iindex 44bd0f6..9f50299 100644
---- a/server/web/discovery.js
++++ b/server/web/discovery.js
@@@ -11,12 +11,13 @@ const REPO = 'https://github.com/OpenVibers/OpenVibe.Run';
const NODE_REPO = 'https://github.com/OpenVibers/OpenVibe.Node';
// The public pages, in the order the sitemap lists them.
--const PAGES = ['/', '/terms', '/privacy', '/dmca'];
++const PAGES = ['/', '/updates', '/terms', '/privacy', '/dmca'];
function sections(base) {
return [
{ title: 'Pages', links: [
{ title: 'OpenVibe.Run', url: `${base}/`, note: 'what a job is, how it runs, and the API in brief' },
++ { title: 'What shipped', url: `${base}/updates`, note: 'every change deployed, newest first' },
{ title: 'Terms', url: `${base}/terms` },
{ title: 'Privacy', url: `${base}/privacy` },
{ title: 'DMCA', url: `${base}/dmca` },
ddiff --git a/server/web/home.js b/server/web/home.js
iindex 3b1ae08..837da6f 100644
---- a/server/web/home.js
++++ b/server/web/home.js
@@@ -10,6 +10,7 @@
* HOME_CSP its Content-Security-Policy (the Frame's scripts and calls to the Network)
*/
const shell = require('openvibe-shared/shell');
++const frame = require('openvibe-shared/frame');
const showcase = require('openvibe-shared/showcase');
const appIcon = require('openvibe-shared/app-icon');
const ovServe = require('openvibe-shared/serve');
@@@ -54,13 +55,30 @@ function renderHome({ config }) {
title: `${SITE_NAME}: run a job on a node that can`,
siteName: SITE_NAME, description: DESCRIPTION, summary: DESCRIPTION,
canonical: `${config.baseUrl}/`, robots: 'index, follow',
-- navbar: nav, footer: { service: 'run', variant: 'full', mount: '#ov-footer', brandName: SITE_NAME }, home: '/',
++ navbar: nav, footer: FOOTER, home: '/',
navLinks: nav.links,
head: [appIcon.headTags({ site: 'run' }), '<link rel="manifest" href="/manifest.webmanifest">', `<link rel="stylesheet" href="${ovServe.url(showcase.STYLESHEET)}">`].join('\n'),
body: `<div id="navbar-mount"></div>\n<main id="main">${body()}</main>`,
});
}
++const FOOTER = { service: 'run', variant: 'full', mount: '#ov-footer', brandName: SITE_NAME, updates: '/updates' };
++
++/** /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' }];
++ return shell.page({
++ name: SITE_NAME, service: 'run', lang: 'en',
++ title: `What shipped on ${SITE_NAME}`,
++ siteName: SITE_NAME, description: `Every change deployed to ${SITE_NAME}, newest first.`,
++ canonical: `${config.baseUrl}/updates`, robots: 'index, follow',
++ navbar: { service: 'run', apiBase: NETWORK_URL, links, history: { type: 'page', title: SITE_NAME } },
++ footer: FOOTER, home: '/', navLinks: links,
++ head: [appIcon.headTags({ site: 'run' }), '<link rel="manifest" href="/manifest.webmanifest">'].join('\n'),
++ body: `<div id="navbar-mount"></div>\n<main id="main" class="page">\n${frame.updatesBody({ service: 'run', siteName: SITE_NAME })}\n</main>\n<script src="${ovServe.url('shipped.js')}" defer></script>`,
++ });
++}
++
function body() {
return showcase.hero({
eyebrow: 'OpenVibe.Run · jobs on nodes · alpha',
@@@ -102,4 +120,4 @@ function body() {
</section>`;
}
--module.exports = { renderHome, HOME_CSP, SITE_NAME, DESCRIPTION };
++module.exports = { renderHome, renderUpdates, HOME_CSP, SITE_NAME, DESCRIPTION };
ddiff --git a/test/discovery.test.js b/test/discovery.test.js
iindex 705f5aa..568a21a 100644
---- a/test/discovery.test.js
++++ b/test/discovery.test.js
@@@ -59,7 +59,7 @@ const KEY = require('crypto').randomBytes(16).toString('hex');
assert.strictEqual(sent.length, 1);
assert.strictEqual(sent[0].key, KEY);
assert.strictEqual(sent[0].keyLocation, `https://run.test/${KEY}.txt`);
-- assert.deepStrictEqual(sent[0].urlList.slice(0, 2), ['https://run.test/', 'https://run.test/terms']);
++ assert.deepStrictEqual(sent[0].urlList.slice(0, 3), ['https://run.test/', 'https://run.test/updates', 'https://run.test/terms']);
assert.ok(sent[0].urlList.every((u) => !/\/api\/|\/internal\//.test(u)));
});
} finally {
ddiff --git a/test/site.test.js b/test/site.test.js
iindex 9f2e220..b2f725d 100644
---- a/test/site.test.js
++++ b/test/site.test.js
@@@ -37,6 +37,15 @@ const { boot, check, done } = require('./helpers/app');
const manifest = JSON.parse((await t.call('GET', '/manifest.webmanifest', { token: null })).text);
assert.strictEqual(manifest.name, 'OpenVibe.Run');
});
++ await check('/updates: the shared update log in the Frame, one heading, in the sitemap', async () => {
++ const r = await t.call('GET', '/updates', { token: null });
++ assert.strictEqual(r.status, 200);
++ assert.ok(r.text.includes('<h1>What shipped on OpenVibe.Run</h1>') && r.text.includes('data-ov-shipped="log" data-service="run"'));
++ assert.strictEqual((r.text.match(/<h1[\s>]/g) || []).length, 1);
++ assert.match(r.text, /<script src="\/shared\/shipped\.js\?v=[0-9a-f]+" defer><\/script>/);
++ assert.match(r.headers.get('content-security-policy'), /connect-src 'self' https:\/\/openvibe\.network/);
++ assert.match((await t.call('GET', '/sitemap.xml', { token: null })).text, /<loc>[^<]*\/updates<\/loc>/);
++ });
await check('an unknown API path is still a problem+json 404', async () => {
const r = await t.call('GET', '/api/v1/nothing-here', { token: null });
assert.strictEqual(r.status, 404);