Changeset 0101d0a in Klonkt for test


Ignore:
Timestamp:
07/28/2026 08:22:37 AM (6 weeks ago)
Author:
Robin Genis <roboburr@…>
Branches:
main
Children:
c52dc82
Parents:
b258a79
Message:

Embeds toonden niets: OpenGraph erbij en een backfill

Twee oorzaken, allebei mijn eigen gat.

OPENGRAPH ONTBRAK. Ik had oEmbed gebouwd als de generieke weg, maar het grootste
deel van het web levert dat helemaal niet. Link-previews met een thumbnail
draaien in de praktijk op OpenGraph, en dat is ook wat Mastodon leest. Gecheckt
op een URL uit onze eigen feed: og:image en og:title wel, oEmbed niet. Zonder die
stap loste vrijwel elke link op naar kind=link en werd er dus niets opgeslagen,
en bleef de kaart leeg. OpenGraph zit nu in dezelfde keten, na oEmbed en voor de
kale link, en gebruikt dezelfde pagina-fetch: het kost geen extra request. De
fetch stuurt nu ook een User-Agent mee, want een deel van het web geeft z-n
og-tags alleen aan iets dat op een browser lijkt.

GEEN BACKFILL. embed_json werd alleen gevuld bij nieuwe binnenkomende posts, dus
alle 359 bestaande rijen bleven leeg: precies wat je ziet als je de app opent.
Self-heal v16 haalt het alsnog op, alleen voor rijen zonder quote en zonder
embed, dus een pagina-fetch per kandidaat en nooit opnieuw.

Changed files:
src/services/EmbedResolver.js

  • findOpenGraph (og:image/title/site_name, met twitter:-fallback)
  • resolveEmbed: OpenGraph na oEmbed, een pagina-fetch voor beide
  • liveIO: User-Agent op de pagina-fetch

src/services/ActivityPubService.js

  • SELFHEAL_VERSION 15 -> 16: embed_json backfillen

test/embed-resolver.test.js

  • 4 tests: og lezen + entities, twitter-fallback en non-http image geweigerd, oEmbed wint nog steeds van OpenGraph, en een og-only pagina levert een kaart

remarks: 213 tests groen. End-to-end gedraaid op een echte feed-URL: titel,
provider en thumbnail komen eruit.

-robo
Co-Authored-By: Claude Opus 4.8 <noreply@…>

File:
1 edited

Legend:

Unmodified
Added
Removed
  • test/embed-resolver.test.js

    rb258a79 r0101d0a  
    120120  assert.ok(calls.some((c) => c[1].includes('activity+json')), 'AP asks for activity+json');
    121121});
     122
     123// OpenGraph: the one that actually carries link previews on the open web.
     124// oEmbed is richer, but most sites simply do not ship it, which is why cards
     125// stayed empty until this fallback existed.
     126const OG_PAGE = '<html><head><meta property="og:title" content="Linux f&amp;uuml;r Einsteiger">'
     127  + '<meta property="og:site_name" content="Linux Guides">'
     128  + '<meta property="og:image" content="https://lg.example/tux.png"></head></html>';
     129
     130test('findOpenGraph reads og:image/title/site and decodes entities', async () => {
     131  const { findOpenGraph } = await import('../src/services/EmbedResolver.js');
     132  const og = findOpenGraph(OG_PAGE);
     133  assert.equal(og.image, 'https://lg.example/tux.png');
     134  assert.equal(og.site, 'Linux Guides');
     135  assert.ok(og.title.startsWith('Linux f'));
     136  assert.equal(findOpenGraph('<html><head><title>x</title></head></html>'), null);
     137  assert.equal(findOpenGraph(null), null);
     138});
     139
     140test('findOpenGraph falls back to twitter:image and refuses a non-http image', async () => {
     141  const { findOpenGraph } = await import('../src/services/EmbedResolver.js');
     142  const tw = findOpenGraph('<meta name="twitter:image" content="https://x/y.png"><meta name="twitter:title" content="T">');
     143  assert.equal(tw.image, 'https://x/y.png');
     144  const bad = findOpenGraph('<meta property="og:image" content="javascript:alert(1)"><meta property="og:title" content="T">');
     145  assert.equal(bad.image, null, 'a non-http image is dropped, the title survives');
     146  assert.equal(bad.title, 'T');
     147});
     148
     149test('oEmbed still wins over OpenGraph when a page offers both', async () => {
     150  const both = OEMBED_PAGE.replace('</head>', '<meta property="og:title" content="OG"></head>');
     151  const r = await resolveEmbed('https://v.example/1', io({
     152    getPage: async () => both, getJSON: async () => OEMBED_JSON,
     153  }));
     154  assert.equal(r.kind, 'oembed');
     155  assert.equal(r.title, 'A talk');
     156});
     157
     158test('a page with only OpenGraph yields a thumbnail card', async () => {
     159  const r = await resolveEmbed('https://lg.example/artikel', io({ getPage: async () => OG_PAGE }));
     160  assert.equal(r.kind, 'opengraph');
     161  assert.equal(r.media[0].url, 'https://lg.example/tux.png');
     162  assert.equal(r.provider, 'Linux Guides');
     163  assert.equal(r.url, 'https://lg.example/artikel');
     164});
Note: See TracChangeset for help on using the changeset viewer.