openvibers/openvibe.chat Commits

Chat: indexable pages share the site card (og:image) (#74)

OP OpenVibers committed 11 Oct 2026 · 2eb9b279f7 · 2 files, +9 −0

Indexable Chat pages (global chat and public rooms) now carry `og:image`/`twitter:image` from the site's share card (openvibe-shared 4.0.0 `social.url('chat')`, served from Chat's own `/shared/`). Private and noindex pages, including DMs and the 404, advertise none.

Test: `web.test.js` checks that global chat shares the card, that the card URL answers `image/jpeg`, and that a noindex page has no `og:image`.

Changes

ddiff --git a/server/web/layout.js b/server/web/layout.js
iindex 82c3102..a252141 100644
---- a/server/web/layout.js
++++ b/server/web/layout.js
@@@ -9,6 +9,7 @@ const fs = require('fs');
const path = require('path');
const ovServe = require('openvibe-shared/serve');
const sharedSeo = require('openvibe-shared/seo');
++const social = require('openvibe-shared/social');
const appIcon = require('openvibe-shared/app-icon');
const frame = require('openvibe-shared/frame');
@@@ -71,6 +72,8 @@ function renderPage(o) {
const head = sharedSeo.headTags({
title, description: o.description || DEFAULT_DESCRIPTION, canonical: `${config.web.baseUrl}${o.path || '/'}`,
robots: o.robots, siteName: SITE_NAME, type: 'website', jsonLd: o.jsonLd,
++ // Indexable pages share the site's card (openvibe-shared/social); private ones advertise nothing.
++ image: /^index/.test(o.robots) ? social.url('chat') : undefined, imageAlt: /^index/.test(o.robots) ? SITE_NAME : undefined,
});
const actor = o.actor || { kind: 'anonymous' };
const who = actor.kind === 'user'
ddiff --git a/test/web.test.js b/test/web.test.js
iindex 44c9b2c..3de497b 100644
---- a/test/web.test.js
++++ b/test/web.test.js
@@@ -35,6 +35,11 @@ t('global chat reads without JavaScript; signing in shows the composer', async (
assert.match(r.text, /Sign in with OpenVibe/);
assert.match(r.text, /<meta name="robots" content="index, follow">/);
assert.match(r.text, /\/shared\/navbar\.js\?v=/, 'the Frame from this site\'s own /shared/');
++ const card = /<meta property="og:image" content="([^"]+)">/.exec(r.text);
++ assert.ok(card && /\/shared\/social\/chat\.jpg\?v=/.test(card[1]), 'global chat shares the site card');
++ const img = await req('GET', new URL(card[1]).pathname + new URL(card[1]).search);
++ assert.strictEqual(img.status, 200);
++ assert.strictEqual(img.headers.get('content-type'), 'image/jpeg');
assert.ok(!/id="oc-compose"/.test(r.text), 'no composer for a guest');
const me = await req('GET', '/', { headers: cookie(alice) });
assert.match(me.text, /id="oc-compose"/);
@@@ -167,6 +172,7 @@ t('unknown pages: a browser gets the site\'s HTML 404 page, the API and other cl
assert.match(r.text, /<h1>Page not found<\/h1>/);
assert.match(r.text, /<meta name="robots" content="noindex">/);
assert.match(r.text, /<code>\/__ovcheck-404<\/code>/);
++ assert.ok(!/og:image/.test(r.text), 'a noindex page advertises no card');
assert.match(r.text, /\/shared\/navbar\.js\?v=/, 'inside the Frame, like every page');
const x = await req('GET', '/%3Cscript%3Ex', { headers: html });
assert.strictEqual(x.status, 404);