openvibers/openvibe.ai Commits
/stats shares with the site's card: Open Graph and Twitter tags on the console layout's one public page (#132)
Changes
@@@ -8,6 +8,7 @@* and whether it resolves (configured | missing | not_required), never a value; base URLs lose any* user info and query string. Run inputs and outputs arrive already bounded by queries.preview.*/++const social = require('openvibe-shared/social');const crypto = require('crypto');class Raw { constructor(s) { this.s = s; } }@@@ -120,10 +121,14 @@ const NAV = [];// The console's pages are noindex; the one public page drawn with this layout (/stats) passes robots, a description and--// its canonical.++// its canonical, and gets the share tags (the site's card from openvibe-shared/social) a shared link needs.++function shareTags({ title, description, canonical }) {++ const image = new URL(social.url('ai') || '/shared/social/ai.jpg', canonical).href;++ return html`<meta property="og:type" content="website"><meta property="og:site_name" content="OpenVibe.AI"><meta property="og:title" content="${title}">${description ? html`<meta property="og:description" content="${description}">` : ''}<meta property="og:url" content="${canonical}"><meta property="og:image" content="${image}"><meta name="twitter:card" content="summary_large_image">`;++}function layout({ title, section, staff, csrf, notice, error, body, site = 'operator console', robots = 'noindex,nofollow', description = null, canonical = null }) {return `<!doctype html>${render(html`<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">--<meta name="robots" content="${robots}">${description ? html`<meta name="description" content="${description}">` : ''}${canonical ? html`<link rel="canonical" href="${canonical}">` : ''}<meta name="referrer" content="no-referrer"><title>${title} · AI ${site}</title><style>${new Raw(CSS)}</style></head>++<meta name="robots" content="${robots}">${description ? html`<meta name="description" content="${description}">` : ''}${canonical ? html`<link rel="canonical" href="${canonical}">${shareTags({ title: `${title} · AI ${site}`, description, canonical })}` : ''}<meta name="referrer" content="no-referrer"><title>${title} · AI ${site}</title><style>${new Raw(CSS)}</style></head><body><a class="skip" href="#main">Skip to content</a><header><div class="bar"><span class="brand">OpenVibe.AI · ${site}</span>${staff ? html`<nav aria-label="Console"><ul>${NAV.map(([href, label, id]) => html`<li><a href="${href}"${id === section ? html` aria-current="page"` : ''}>${label}</a></li>`)}</ul></nav><div class="who"><span>${staff.username ? `@${staff.username} ` : ''}<code>${staff.subject}</code> · ${staff.role}</span>@@@ -84,6 +84,9 @@ t.test('/stats (price and latency) is in search: index,follow, its own descriptiassert.ok(r.text.includes('<meta name="robots" content="index,follow">'), 'indexable');assert.match(r.text, /<meta name="description" content="Price and latency of every AI model OpenVibe\.AI called in the last 7 days: [^"]+">/);assert.ok(r.text.includes(`<link rel="canonical" href="${SITE}/stats">`), 'canonical');++ assert.ok(r.text.includes(`<meta property="og:url" content="${SITE}/stats">`), 'a shared link has its tags');++ assert.match(r.text, new RegExp(`<meta property="og:image" content="${SITE.replace(/\./g, '\\.')}/shared/social/ai\\.jpg\\?v=[0-9a-f]+">`));++ assert.ok(r.text.includes('<meta name="twitter:card" content="summary_large_image">'));});t.test('robots.txt, sitemap.xml and llms.txt name the public origin', async () => {
@@ -8,6 +8,7 @@
@@ -8,6 +8,7 @@
* and whether it resolves (configured | missing | not_required), never a value; base URLs lose any
* and whether it resolves (configured | missing | not_required), never a value; base URLs lose any
* user info and query string. Run inputs and outputs arrive already bounded by queries.preview.
* user info and query string. Run inputs and outputs arrive already bounded by queries.preview.
*/
*/
+const social = require('openvibe-shared/social');
const crypto = require('crypto');
const crypto = require('crypto');
class Raw { constructor(s) { this.s = s; } }
class Raw { constructor(s) { this.s = s; } }
@@ -120,10 +121,14 @@ const NAV = [
@@ -120,10 +121,14 @@ const NAV = [
];
];
// The console's pages are noindex; the one public page drawn with this layout (/stats) passes robots, a description and
// The console's pages are noindex; the one public page drawn with this layout (/stats) passes robots, a description and
-// its canonical.
+// its canonical, and gets the share tags (the site's card from openvibe-shared/social) a shared link needs.
+function shareTags({ title, description, canonical }) {
+ const image = new URL(social.url('ai') || '/shared/social/ai.jpg', canonical).href;
+ return html`<meta property="og:type" content="website"><meta property="og:site_name" content="OpenVibe.AI"><meta property="og:title" content="${title}">${description ? html`<meta property="og:description" content="${description}">` : ''}<meta property="og:url" content="${canonical}"><meta property="og:image" content="${image}"><meta name="twitter:card" content="summary_large_image">`;
+}
function layout({ title, section, staff, csrf, notice, error, body, site = 'operator console', robots = 'noindex,nofollow', description = null, canonical = null }) {
function layout({ title, section, staff, csrf, notice, error, body, site = 'operator console', robots = 'noindex,nofollow', description = null, canonical = null }) {
return `<!doctype html>${render(html`<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
return `<!doctype html>${render(html`<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
-<meta name="robots" content="${robots}">${description ? html`<meta name="description" content="${description}">` : ''}${canonical ? html`<link rel="canonical" href="${canonical}">` : ''}<meta name="referrer" content="no-referrer"><title>${title} · AI ${site}</title><style>${new Raw(CSS)}</style></head>
+<meta name="robots" content="${robots}">${description ? html`<meta name="description" content="${description}">` : ''}${canonical ? html`<link rel="canonical" href="${canonical}">${shareTags({ title: `${title} · AI ${site}`, description, canonical })}` : ''}<meta name="referrer" content="no-referrer"><title>${title} · AI ${site}</title><style>${new Raw(CSS)}</style></head>
<body><a class="skip" href="#main">Skip to content</a><header><div class="bar"><span class="brand">OpenVibe.AI · ${site}</span>
<body><a class="skip" href="#main">Skip to content</a><header><div class="bar"><span class="brand">OpenVibe.AI · ${site}</span>
${staff ? html`<nav aria-label="Console"><ul>${NAV.map(([href, label, id]) => html`<li><a href="${href}"${id === section ? html` aria-current="page"` : ''}>${label}</a></li>`)}</ul></nav>
${staff ? html`<nav aria-label="Console"><ul>${NAV.map(([href, label, id]) => html`<li><a href="${href}"${id === section ? html` aria-current="page"` : ''}>${label}</a></li>`)}</ul></nav>
<div class="who"><span>${staff.username ? `@${staff.username} ` : ''}<code>${staff.subject}</code> · ${staff.role}</span>
<div class="who"><span>${staff.username ? `@${staff.username} ` : ''}<code>${staff.subject}</code> · ${staff.role}</span>
@@ -84,6 +84,9 @@ t.test('/stats (price and latency) is in search: index,follow, its own descripti
@@ -84,6 +84,9 @@ t.test('/stats (price and latency) is in search: index,follow, its own descripti
assert.ok(r.text.includes('<meta name="robots" content="index,follow">'), 'indexable');
assert.ok(r.text.includes('<meta name="robots" content="index,follow">'), 'indexable');
assert.match(r.text, /<meta name="description" content="Price and latency of every AI model OpenVibe\.AI called in the last 7 days: [^"]+">/);
assert.match(r.text, /<meta name="description" content="Price and latency of every AI model OpenVibe\.AI called in the last 7 days: [^"]+">/);
assert.ok(r.text.includes(`<link rel="canonical" href="${SITE}/stats">`), 'canonical');
assert.ok(r.text.includes(`<link rel="canonical" href="${SITE}/stats">`), 'canonical');
+ assert.ok(r.text.includes(`<meta property="og:url" content="${SITE}/stats">`), 'a shared link has its tags');
+ assert.match(r.text, new RegExp(`<meta property="og:image" content="${SITE.replace(/\./g, '\\.')}/shared/social/ai\\.jpg\\?v=[0-9a-f]+">`));
+ assert.ok(r.text.includes('<meta name="twitter:card" content="summary_large_image">'));
});
});
t.test('robots.txt, sitemap.xml and llms.txt name the public origin', async () => {
t.test('robots.txt, sitemap.xml and llms.txt name the public origin', async () => {