Ignore:
Timestamp:
08/06/2026 02:29:20 PM (5 weeks ago)
Author:
roboburr <roboburr@…>
Branches:
main
Children:
5861849
Parents:
812fea9
git-author:
Robin <roboburr@…> (08/06/2026 02:29:09 PM)
git-committer:
roboburr <roboburr@…> (08/06/2026 02:29:20 PM)
Message:

Herstel uit andermans tijdlijn-cache (shaer-l1v)

De aanleiding: boiert.eu verloor zijn database. De posts staan nog in de
ap_timeline van instances die boiert volgen, en die tabel sleutelt op de
OORSPRONKELIJKE AP-object-URI. De identiteiten overleven dus, en dat is het
verschil tussen herstellen en opnieuw posten -- boosts, likes en antwoorden
elders wijzen naar die ids.

Er is bewust GEEN apart herstelpad. recover-from-cache.mjs maakt een gewoon
archief in het formaat uit docs/EXPORT-FORMAT.md, dat door dezelfde importer gaat
als een export: droogloop, versiecontrole, en dezelfde regel rond het behouden
van AP-ids. Een tweede implementatie zou uiteenlopen met de eerste.

Meerdere bronnen mogen samen: verschillende volgers dekken verschillende
periodes, en per AP-id wint de rijkste versie.

Titels moeten terug uit de TEKST, want buildNote bakt ze daar in (Mastodon
negeert name). Er is geen sluitend signaal: op echte data heet een post "Back
to 1987!" met slug "waiting-on-you", dus de slug is niet van de titel afgeleid.
We gaan op de vorm af -- een openende alinea met alleen vetgedrukte platte tekst
-- en het script drukt AF wat het lostrok, zodat een mens het naloopt. --houd-titel
laat alles staan.

Gevonden door tegen echte cachedata te draaien (24 posts van sound-fabrics uit de
beta-cache): een deel van de bijlagen loopt niet via /media/ maar via
/audio/stream/<bestandsnaam>, en dat is AUDIO_DIR, een andere map. Zonder dat pad
blijft op een muzieksite de halve catalogus liggen. De importer had hetzelfde
gat aan zijn kant: zo'n bestand werd wel weggeschreven maar verdween uit de
kolommen, omdat er geen bestemming onder /media voor was. Nu krijgt het er een.

Het manifest draagt shaer:recovered met de bronnen, het tijdvenster en wat er
principieel ontbreekt -- eigen antwoorden, alles van voor het volgmoment, en
concepten. Anders ziet iemand dit over een jaar aan voor een gewone export en
denkt hij dat de site compleet is.

14 tests, waaronder de hele ketting: cache -> archief -> importer -> posts met
behouden ids, titel, cover en gesloten poll. Suite 528/528.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/services/ArchiveImportService.js

    r812fea9 r7aa3140  
    106106
    107107/**
     108 * Waar deze bijlage komt te staan, als site-relatief pad.
     109 *
     110 * Meestal zijn oorspronkelijke plek en bestemming gelijk. Maar een bestand dat
     111 * ELDERS werd geserveerd -- gehoste audio ging via /audio/stream/ -- heeft geen
     112 * plek onder /media. Zonder een bestemming zou het bestand wel worden
     113 * weggeschreven en toch uit de kolommen verdwijnen. Nu krijgt het een eigen hoek,
     114 * en verwijzen de kolommen daarheen.
     115 */
     116function bestemming(a) {
     117  return padVanOrigineel(a && a['shaer:originalUrl']) || `/media/archief/${path.basename(String((a && a.url) || ''))}`;
     118}
     119
     120/**
    108121 * Zet een archief terug in een site.
    109122 *
     
    189202        continue;
    190203      }
    191       const doel = veiligMediaPad(padVanOrigineel(a['shaer:originalUrl']))
    192         || veiligMediaPad(`/media/archief/${path.basename(a.url)}`);
     204      const doel = veiligMediaPad(bestemming(a));
    193205      if (!doel) { rapport.waarschuwingen.push(`${a.url}: onbruikbaar doelpad, overgeslagen`); continue; }
    194206      schrijf.push({ soort: 'media', doel, bytes });
     
    249261      // en dat zie je pas als je alle kolommen vergelijkt.
    250262      const bijlagen = Array.isArray(o.attachment) ? o.attachment : [];
    251       const padVan = (a) => (a ? padVanOrigineel(a['shaer:originalUrl']) : null);
     263      const padVan = (a) => (a && a['shaer:availability'] !== 'missing' ? bestemming(a) : (a ? padVanOrigineel(a['shaer:originalUrl']) : null));
    252264      const metRol = (r) => bijlagen.find((a) => a['shaer:role'] === r);
    253265      const c2s = bijlagen.filter((a) => a['shaer:role'] === 'c2s').map((a) => {
     
    284296        let mediaId = null;
    285297        const bij = (o.attachment || []).find((a) => a.url === t['shaer:media']);
    286         const doel = bij && veiligMediaPad(padVanOrigineel(bij['shaer:originalUrl']));
     298        const doel = bij && veiligMediaPad(bestemming(bij));
    287299        if (doel) {
    288300          mediaId = randomUUID();
Note: See TracChangeset for help on using the changeset viewer.