Changeset d7d2ede in Klonkt for src


Ignore:
Timestamp:
08/24/2026 03:08:53 PM (2 weeks ago)
Author:
Robin <roboburr@…>
Branches:
main
Children:
df7de6e
Parents:
9a22776
Message:

C2S: een note met een name krijgt echt een titel (shaer-uply)

Een post die over C2S binnenkwam kon nooit een titel hebben: c2sCreatePost
schreef in posts.title en in posts_fts en las object.name nooit. Een
client die er een zette zag hem geruisloos verdwijnen -- accepteren en
weggooien, het slechtste van de drie mogelijke gedragingen. De API was
daarmee de enige publicatieweg die iets niet kan wat het web wel kan.
Gevonden door R9999, dat alleen over C2S publiceert.

Alle drie de schrijfplekken, want een halve reparatie laat een gat:

  • posts.title krijgt de titel, dus het web toont hem;
  • posts_fts krijgt hem ook, dus hij is op zijn titel te VINDEN;
  • deliverCreate krijgt hem mee, dus de gefedereerde note krijgt zijn vetgedrukte eerste regel van buildNote, precies als een webpost (Mastodon negeert name op een Note).

De titel is platte tekst (toPlainText), per AS2 en per wat de kolom overal
verwacht, getrimd en begrensd op 200 -- de huisregel voor korte vrije tekst
(content warning, sitetitel); de posteditor op het web heeft geen eigen
grens.

Vier tests in test/c2s-title.test.js, met tegenbewijs gedraaid: tegen de
oude code falen de twee titeltests en slagen de twee die ongewijzigd gedrag
vastleggen (geen name = lege titel, witruimte = geen titel). Suite: 1226
groen.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/services/ActivityPubService.js

    r9a22776 rd7d2ede  
    38073807    return `<p><video controls playsinline preload="metadata"${poster} src="${a.url}"></video></p>`;
    38083808  }).join('');
     3809  // De titel (shaer-uply): AS2 zet hem in `name`, en die werd hier nooit
     3810  // gelezen -- een client kon hem zetten en hij verdween geruisloos, het
     3811  // slechtste van de drie mogelijke gedragingen. Platte tekst, want dat is wat
     3812  // `name` per AS2 is en wat de titelkolom overal verwacht; wie er toch HTML
     3813  // in stopt houdt de tekst over. De grens van 200 is de huisregel voor korte
     3814  // vrije tekst hier (content warning, sitetitel) -- de posteditor op het web
     3815  // heeft geen eigen grens, dus strenger dan het web zijn we hiermee niet
     3816  // op een manier die iemand merkt.
     3817  // Vanaf de kolom doet de bestaande machinerie de rest: het web toont hem,
     3818  // en buildNote vouwt hem als vetgedrukte eerste regel in de content
     3819  // (Mastodon negeert `name` op een Note).
     3820  const title = HtmlSanitizerService.toPlainText(typeof object.name === 'string' ? object.name : '').trim().slice(0, 200);
    38093821  const postId = crypto.randomUUID();
    38103822  const slug = 'n-' + postId.slice(0, 8);
     
    38223834  db.prepare(`INSERT INTO posts (id, site_id, slug, author_id, title, content, excerpt, status, type, language, fan_only, ap_visibility, created_at, updated_at, published_at)
    38233835              VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`)
    3824     .run(postId, site.id, slug, user.id, '', html + mediaHtml, '', 'published', 'post', object.language || 'nl', fanOnly, vis, now, now, now);
     3836    .run(postId, site.id, slug, user.id, title, html + mediaHtml, '', 'published', 'post', object.language || 'nl', fanOnly, vis, now, now, now);
    38253837  if (media.length) { try { db.prepare('UPDATE posts SET c2s_attachments = ? WHERE id = ?').run(JSON.stringify(media), postId); } catch { /* column exists via ensureColumn */ } }
    38263838  try { db.prepare('UPDATE posts SET content_rendered = ? WHERE id = ?').run(bakePostContent(html + mediaHtml), postId); } catch { /* render fallback covers it */ }
    38273839  bakePostContentWithMentions(html + mediaHtml).then((h) => { try { db.prepare('UPDATE posts SET content_rendered = ? WHERE id = ?').run(h, postId); } catch { /* keep sync bake */ } }).catch(() => {});
    3828   try { db.prepare('INSERT INTO posts_fts(content, title, author, post_id) VALUES (?,?,?,?)').run(HtmlSanitizerService.toPlainText(html), '', user.username || '', postId); } catch { /* FTS non-fatal */ }
     3840  // Ook in de zoekindex, en niet alleen in de kolom (shaer-uply): anders is
     3841  // een getitelde C2S-post wel te zien maar niet op zijn titel te vinden.
     3842  try { db.prepare('INSERT INTO posts_fts(content, title, author, post_id) VALUES (?,?,?,?)').run(HtmlSanitizerService.toPlainText(html), title, user.username || '', postId); } catch { /* FTS non-fatal */ }
    38293843  if (vis !== 'direct') {
    3830     deliverCreate(site, { id: postId, slug, title: '', content: html + mediaHtml, published_at: now, created_at: now, fan_only: fanOnly, ap_visibility: vis, c2s_attachments: media.length ? JSON.stringify(media) : null }).catch(() => { /* best-effort */ });
     3844    deliverCreate(site, { id: postId, slug, title, content: html + mediaHtml, published_at: now, created_at: now, fan_only: fanOnly, ap_visibility: vis, c2s_attachments: media.length ? JSON.stringify(media) : null }).catch(() => { /* best-effort */ });
    38313845  }
    38323846  return { status: 201, id: postId, url: `${base}/ap/notes/${postId}` };
Note: See TracChangeset for help on using the changeset viewer.