openvibers/openvibe.events Commits

A missing page answers the site's 404 page in the Frame (#194)

OP OpenVibers committed 12 days ago · d700d81f3a · 3 files, +38 −9

A browser that asks for a path Events does not serve gets the site's own 404 page (the OpenVibe Frame, noindex, one line naming the host, links home and to the developer guide). An API path or a request whose Accept is not text/html keeps the problem document.

Changes

ddiff --git a/server/app.js b/server/app.js
iindex f3c01ff..9a1b520 100644
---- a/server/app.js
++++ b/server/app.js
@@@ -185,7 +185,8 @@ function createApp({ config, store, auth, keys, worker, realtime, metrics, dnsLo
// A browser asking for a page that is not here gets a page; an API client the problem document.
app.use((req, res) => {
if (req.method === 'GET' && !req.path.startsWith('/api/') && req.accepts(['application/json', 'text/html']) === 'text/html') {
-- return res.status(404).type('html').set('Content-Security-Policy', HOME_CSP).set('Cache-Control', 'no-store').send(renderNotFound());
++ return res.status(404).type('html').set('Content-Security-Policy', HOME_CSP).set('Cache-Control', 'no-store')
++ .set('X-Robots-Tag', 'noindex').send(renderNotFound({ siteUrl: config.siteUrl }));
}
return http.sendProblem(res, 404, 'events.not_found', { detail: `no route ${req.method} ${req.path}`, ctx: req.ov });
});
ddiff --git a/server/home.js b/server/home.js
iindex b733a7d..66ff67c 100644
---- a/server/home.js
++++ b/server/home.js
@@@ -494,12 +494,35 @@ ${sections({ apiUrl, stats })}
});
}
--/** The page a browser gets for a path nothing serves: a real document (language, title, the app icon), not JSON. */
--function renderNotFound() {
-- return ['<!doctype html>', '<html lang="en">', '<head>', '<meta charset="utf-8">', '<meta name="viewport" content="width=device-width, initial-scale=1">',
-- `<title>Not found · ${SITE_NAME}</title>`, '<meta name="robots" content="noindex">', appIcon.headTags({ site: 'events' }), '</head>',
-- '<body style="font-family:system-ui,sans-serif;margin:3rem auto;max-width:36rem;padding:0 1rem;line-height:1.5">',
-- '<h1>Not found</h1>', `<p>Nothing is here. <a href="/">${SITE_NAME}</a> · <a href="${NETWORK_URL}">OpenVibe</a></p>`, '</body>', '</html>'].join('\n');
++/**
++ * The page a browser gets for a path nothing serves: the site's own Frame, one way back, and no clue
++ * about what was asked for. An API client keeps the problem document (server/app.js).
++ */
++function renderNotFound({ siteUrl = 'https://openvibe.events' } = {}) {
++ const host = String(siteUrl).replace(/^https?:\/\//, '').replace(/\/.*$/, '');
++ const links = [{ label: 'Guide', href: `${SOURCE}#readme` }];
++ return shell.page({
++ name: SITE_NAME, service: 'events', lang: 'en',
++ title: `Page not found · ${SITE_NAME}`,
++ siteName: SITE_NAME,
++ description: `There is no page at this address on ${host}.`,
++ robots: 'noindex, follow',
++ navbar: { service: 'events', apiBase: NETWORK_URL, links, history: { type: 'page', title: SITE_NAME } },
++ footer: FOOTER, home: '/', navLinks: links,
++ head: [
++ appIcon.headTags({ site: 'events' }),
++ `<link rel="stylesheet" href="${ovServe.url(showcase.STYLESHEET)}">`,
++ ].join('\n'),
++ body: `<div id="navbar-mount"></div>
++<main id="main" class="page">
++<h1>Page not found</h1>
++<p>There is no page at this address on ${esc(host)}. The link may be old or mistyped.</p>
++<div class="sc-actions">
++<a class="sc-btn sc-primary" href="/">${SITE_NAME} home</a>
++<a class="sc-btn" href="${SOURCE}#readme">Developer guide</a>
++</div>
++</main>`,
++ });
}
module.exports = { renderHome, renderUpdates, renderNotFound, HOME_CSP, DESCRIPTION, sections };
ddiff --git a/test/home.test.js b/test/home.test.js
iindex 052615a..89d304d 100644
---- a/test/home.test.js
++++ b/test/home.test.js
@@@ -112,11 +112,16 @@ t('the home CSP lets Cloudflare Web Analytics load and report (it injects its be
assert.match(HOME_CSP, /connect-src [^;]*https:\/\/cloudflareinsights\.com/);
});
--t('a browser gets a page for a path nothing serves (lang, title, icon); an API client the problem document', async () => {
++t('a browser that asks for a missing page gets the site 404 page; an API client the problem document', async () => {
const page = await raw(h.base, '/no-such-page', { Accept: 'text/html,*/*;q=0.8' });
assert.strictEqual(page.status, 404);
assert.match(page.headers['content-type'], /^text\/html/);
-- assert.ok(page.body.includes('<html lang="en">') && page.body.includes('<title>Not found') && page.body.includes('rel="icon"'), page.body.slice(0, 300));
++ assert.ok(page.body.includes('<h1>Page not found</h1>'), page.body.slice(0, 300));
++ assert.ok(page.body.includes('There is no page at this address on openvibe.events. The link may be old or mistyped.'));
++ assert.ok(page.body.includes('id="navbar-mount"'), 'the page is in the site Frame');
++ assert.ok(page.body.includes('<meta name="robots" content="noindex, follow">'));
++ assert.strictEqual(page.headers['x-robots-tag'], 'noindex');
++ assert.ok(page.body.includes('class="sc-btn sc-primary" href="/"'), 'home is the first way back');
const api = await raw(h.base, '/no-such-page', { Accept: 'application/json' });
assert.strictEqual(api.status, 404);
assert.match(api.headers['content-type'], /problem\+json|json/);