Changeset 7842ca1 in Klonkt for scripts/backfill-reactions.mjs
- Timestamp:
- 08/06/2026 09:33:58 AM (5 weeks ago)
- Branches:
- main
- Children:
- 25f2e64
- Parents:
- 6bc5ebd
- git-author:
- Robin <roboburr@…> (08/06/2026 09:33:57 AM)
- git-committer:
- Claude (agent) <aiclaude@…> (08/06/2026 09:33:58 AM)
- File:
-
- 1 edited
-
scripts/backfill-reactions.mjs (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
scripts/backfill-reactions.mjs
r6bc5ebd r7842ca1 1 1 #!/usr/bin/env node 2 2 // 3 // Backfill: reacties uit de afgeleide kolommen naar de tussentabel(shaer-9e9).3 // Reactie-migratie handmatig draaien (shaer-9e9). 4 4 // 5 // "Heb ik hierop gereageerd" stond in Klonkt op twee plekken die door 6 // verschillende routes werden gevuld: ap_timeline.liked/boosted (web-tijdlijn) 7 // en ap_my_reactions (interact-pagina). Sinds fase 1 schrijft setReaction ze 8 // allebei, maar alles van VOOR die wijziging staat nog maar in een van de twee. 9 // 10 // Dit script vult de tussentabel aan met wat alleen in de kolommen staat. Het 11 // moet draaien VOORDAT de lezers naar de tussentabel wijzen (fase 2), anders 12 // verdwijnen die reacties uit beeld -- stil, want een ontbrekende rij is geen 13 // fout. 5 // Normaal hoef je dit NIET: migrateReactions() draait bij boot, één keer per 6 // REACTIONS_MIGRATION_VERSION-bump, net als de self-heal. Dit script is er om 7 // vooraf te kijken wat er zou gebeuren, of om het gericht op één instance te 8 // forceren. 14 9 // 15 10 // node scripts/backfill-reactions.mjs --dry-run 16 // node scripts/backfill-reactions.mjs 11 // node scripts/backfill-reactions.mjs # respecteert de versievlag 12 // node scripts/backfill-reactions.mjs --force # ook als de vlag al staat 17 13 // 18 // Veilig om opnieuw te draaien: INSERT OR IGNORE plus de UNIQUE op19 // (site_slug, target_uri, kind) maakt het idempotent.14 // Bewust een schil om dezelfde functie die bij boot draait: twee implementaties 15 // van een migratie lopen uiteen, en dan repareert de ene wat de andere niet ziet. 20 16 // 21 // Wat het NIET kan: de oorspronkelijke reactiedatum herstellen. Wanneer je 22 // reageerde is nergens vastgelegd, dus de backfill-rijen krijgen de datum van 23 // nu. Dat is geen verlies dat te repareren valt, wel iets om te weten als je 24 // ooit op created_at gaat sorteren. 17 // Wat het doet, en waarom allebei nodig is: 18 // 19 // HERSLEUTELEN De oude interact-route bewaarde de URI waarmee je binnenkwam, 20 // en de bookmarklet geeft de permalink door. Sinds de reacties 21 // op de canonieke object-URI gezocht worden, zouden die rijen 22 // wees zijn. De created_at reist mee. 23 // AANVULLEN Alles wat op oude code via de Krant is gegeven staat alleen in 24 // ap_timeline.liked/boosted. Zonder deze stap toont het als 25 // niet-gereageerd -- en klikt iemand opnieuw, met een tweede 26 // Like de fediverse in als gevolg. 27 // 28 // Wat het NIET kan: bij AANVULLEN de oorspronkelijke reactiedatum herstellen. 29 // Wanneer je reageerde is nergens vastgelegd, dus die rijen krijgen de datum van 30 // nu. Bij hersleutelen blijft de datum wel behouden. 25 31 26 32 import db from '../src/config/database.js'; 33 import { migrateReactions } from '../src/services/ActivityPubService.js'; 27 34 28 35 const dryRun = process.argv.includes('--dry-run'); 36 const force = process.argv.includes('--force'); 29 37 30 const scheef = (kind, kolom) => db.prepare(` 31 SELECT t.slug, t.id FROM ap_timeline t 32 WHERE t.${kolom} = 1 33 AND NOT EXISTS (SELECT 1 FROM ap_my_reactions r 34 WHERE r.site_slug = t.slug AND r.target_uri = t.id AND r.kind = ?) 35 `).all(kind); 36 37 const voor = { 38 const meet = () => ({ 38 39 tussentabel: db.prepare('SELECT COUNT(*) AS n FROM ap_my_reactions').get().n, 39 40 liked: db.prepare('SELECT COUNT(*) AS n FROM ap_timeline WHERE liked = 1').get().n, 40 41 boosted: db.prepare('SELECT COUNT(*) AS n FROM ap_timeline WHERE boosted = 1').get().n, 41 };42 const teDoen = { like: scheef('like', 'liked'), boost: scheef('boost', 'boosted') };43 44 console.log('vooraf :', JSON.stringify(voor));45 console.log('aan te vullen: like =', teDoen.like.length, ', boost =', teDoen.boost.length);46 47 if (dryRun) {48 for (const [kind, rijen] of Object.entries(teDoen)) {49 for (const r of rijen.slice(0, 10)) console.log(` [dry-run] ${kind} ${r.slug} ${r.id}`);50 if (rijen.length > 10) console.log(` ... en nog ${rijen.length - 10}`);51 }52 console.log('\n--dry-run: niets geschreven.');53 process.exit(0);54 }55 56 const ins = db.prepare('INSERT OR IGNORE INTO ap_my_reactions (site_slug, target_uri, kind) VALUES (?,?,?)');57 let toegevoegd = 0;58 db.transaction(() => {59 for (const [kind, rijen] of Object.entries(teDoen)) {60 for (const r of rijen) toegevoegd += ins.run(r.slug, r.id, kind).changes;61 }62 })();63 64 const na = {65 tussentabel: db.prepare('SELECT COUNT(*) AS n FROM ap_my_reactions').get().n,66 42 scheef: db.prepare(` 67 43 SELECT COUNT(*) AS n FROM ap_timeline t … … 69 45 AND NOT EXISTS (SELECT 1 FROM ap_my_reactions r 70 46 WHERE r.site_slug = t.slug AND r.target_uri = t.id)`).get().n, 71 }; 47 wees: db.prepare(` 48 SELECT COUNT(*) AS n FROM ap_my_reactions r 49 WHERE NOT EXISTS (SELECT 1 FROM ap_timeline t 50 WHERE t.slug = r.site_slug AND t.id = r.target_uri)`).get().n, 51 }); 72 52 73 cons ole.log('toegevoegd:', toegevoegd);74 console.log(' achteraf :', JSON.stringify(na));53 const voor = meet(); 54 console.log('vooraf :', JSON.stringify(voor)); 75 55 76 // De controle die telt: is er nog een vlag zonder tegenhanger? Zo ja, dan is de 77 // tussentabel nog niet veilig als bron en mag fase 2 niet. 56 const uit = migrateReactions({ dryRun, force: force || dryRun }); 57 if (uit.overgeslagen) { 58 console.log('\novergeslagen: de versievlag staat al. Gebruik --force om toch te draaien.'); 59 process.exit(0); 60 } 61 if (dryRun) { 62 console.log(`\n--dry-run: zou ${uit.hersleuteld} rij(en) hersleutelen en ${uit.aangevuld} aanvullen. Niets geschreven.`); 63 process.exit(0); 64 } 65 66 const na = meet(); 67 console.log('hersleuteld:', uit.hersleuteld, ' aangevuld:', uit.aangevuld); 68 console.log('achteraf :', JSON.stringify(na)); 69 70 // De twee controles die tellen. Blijft er een vlag zonder tegenhanger, dan is de 71 // tussentabel niet compleet en tonen reacties als niet-gegeven. Blijft er een 72 // tussentabel-rij zonder tijdlijnrij, dan is die op iets buiten je tijdlijn 73 // gericht (legitiem) OF nog op een permalink (niet legitiem) -- vandaar de 74 // waarschuwing in plaats van een fout. 78 75 if (na.scheef !== 0) { 79 console.error(`\n LET OP: nog ${na.scheef} rij(en) met een vlag zonder tegenhanger. Fase 2 mag NIET.`);76 console.error(`\nFOUT: nog ${na.scheef} rij(en) met een vlag zonder tegenhanger.`); 80 77 process.exit(1); 81 78 } 82 if ( toegevoegd !== teDoen.like.length + teDoen.boost.length) {83 console.error('\n LET OP: het aantal toegevoegde rijen wijkt af van de meting vooraf.');79 if (na.wees > voor.wees) { 80 console.error('\nFOUT: er zijn tussentabel-rijen bijgekomen die nergens op slaan.'); 84 81 process.exit(1); 85 82 } 86 console.log('\nOK: elke vlag heeft nu een tegenhanger in de tussentabel.'); 83 if (na.wees) { 84 console.warn(`\nLET OP: ${na.wees} tussentabel-rij(en) zonder tijdlijnrij. Dat mag (een reactie op iets\n` 85 + 'buiten je tijdlijn), maar controleer of er geen permalinks tussen zitten van een post die je\n' 86 + 'wel kent -- die zouden hersleuteld moeten zijn.'); 87 } 88 console.log('\nOK: elke vlag heeft een tegenhanger in de tussentabel.');
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)