| 1 | #!/usr/bin/env node
|
|---|
| 2 | //
|
|---|
| 3 | // Herstel de posts van een verloren Klonkt uit de tijdlijn-cache van instances
|
|---|
| 4 | // die haar volgden (shaer-l1v).
|
|---|
| 5 | //
|
|---|
| 6 | // node scripts/recover-from-cache.mjs \
|
|---|
| 7 | // --actor https://boiert.eu/ap/users/boiert \
|
|---|
| 8 | // --from /pad/naar/sound-fabrics/database.sqlite \
|
|---|
| 9 | // --from /pad/naar/nog-een/database.sqlite \
|
|---|
| 10 | // --media /geredde/storage/media \
|
|---|
| 11 | // --audio /geredde/storage/audio \
|
|---|
| 12 | // --out boiert-herstel.zip
|
|---|
| 13 | //
|
|---|
| 14 | // Het resultaat is een gewoon archief in het formaat uit docs/EXPORT-FORMAT.md.
|
|---|
| 15 | // Terugzetten gaat dus met scripts/import-archive.mjs, met dezelfde droogloop en
|
|---|
| 16 | // dezelfde regel rond het behouden van AP-ids. Er is bewust geen apart
|
|---|
| 17 | // herstelpad: dat zou een tweede implementatie zijn van iets dat al bestaat.
|
|---|
| 18 | //
|
|---|
| 19 | // WAT HIER PRINCIPIEEL NIET IN ZIT:
|
|---|
| 20 | // - antwoorden van de verloren site zelf (die staan niet in een tijdlijn-cache)
|
|---|
| 21 | // - alles van voor het moment dat de bron ging volgen
|
|---|
| 22 | // - concepten (nooit gefedereerd, dus nergens gecachet)
|
|---|
| 23 | // - de plek van afbeeldingen IN de tekst (bij het federeren eruit gehaald)
|
|---|
| 24 |
|
|---|
| 25 | import fs from 'fs';
|
|---|
| 26 | import path from 'path';
|
|---|
| 27 | import { recoverFromCache } from '../src/services/ArchiveRecoveryService.js';
|
|---|
| 28 | import { zipArchive } from '../src/services/ArchiveExportService.js';
|
|---|
| 29 |
|
|---|
| 30 | const args = process.argv.slice(2);
|
|---|
| 31 | const alle = (naam) => args.reduce((uit, a, i) => (a === naam && args[i + 1] ? [...uit, args[i + 1]] : uit), []);
|
|---|
| 32 | const een = (naam) => alle(naam)[0] || null;
|
|---|
| 33 |
|
|---|
| 34 | const actor = een('--actor');
|
|---|
| 35 | const sources = alle('--from');
|
|---|
| 36 | if (!actor || !sources.length) {
|
|---|
| 37 | console.error('gebruik: node scripts/recover-from-cache.mjs --actor <actor-uri> --from <db> [--from <db>] [--media <map>] [--audio <map>] [--out <zip> | --dir <map>] [--houd-titel]');
|
|---|
| 38 | process.exit(1);
|
|---|
| 39 | }
|
|---|
| 40 |
|
|---|
| 41 | let uit;
|
|---|
| 42 | try {
|
|---|
| 43 | uit = recoverFromCache({
|
|---|
| 44 | sources, actorUri: actor, mediaRoot: een('--media'), audioRoot: een('--audio'),
|
|---|
| 45 | houdTitelInTekst: args.includes('--houd-titel'),
|
|---|
| 46 | slug: een('--slug') || '', title: een('--titel') || '',
|
|---|
| 47 | });
|
|---|
| 48 | } catch (e) { console.error(`kan niet herstellen: ${e.message}`); process.exit(1); }
|
|---|
| 49 |
|
|---|
| 50 | const r = uit.rapport;
|
|---|
| 51 | console.log('bronnen:');
|
|---|
| 52 | for (const b of r.bronnen) console.log(` ${b.rijen.toString().padStart(5)} rij(en) ${b.pad}`);
|
|---|
| 53 | console.log(`\nposts gevonden : ${r.posts}`);
|
|---|
| 54 | console.log(`periode : ${r.oudste || '?'} tot ${r.nieuwste || '?'}`);
|
|---|
| 55 | console.log(`media : ${r.media} gered, ${r.mediaMissing} niet gevonden`);
|
|---|
| 56 | console.log(`titels : ${r.titels.length} losgetrokken uit de tekst`);
|
|---|
| 57 |
|
|---|
| 58 | // De titels horen door een MENS nagelopen te worden. Er is geen sluitend signaal
|
|---|
| 59 | // dat een vetgedrukte eerste regel een titel was; de slug is er niet van afgeleid.
|
|---|
| 60 | if (r.titels.length) {
|
|---|
| 61 | console.log('\nteruggevonden titels -- loop deze na, een post die echt met een vetgedrukte');
|
|---|
| 62 | console.log('regel begint raakt die regel kwijt aan zijn titel:');
|
|---|
| 63 | for (const t of r.titels.slice(0, 30)) console.log(` ${t.slug.padEnd(28)} ${t.titel}`);
|
|---|
| 64 | if (r.titels.length > 30) console.log(` ... en nog ${r.titels.length - 30}`);
|
|---|
| 65 | }
|
|---|
| 66 | if (r.gemist.length) {
|
|---|
| 67 | console.log('\nmedia niet gevonden op schijf:');
|
|---|
| 68 | for (const m of r.gemist.slice(0, 20)) console.log(` ${m.slug.padEnd(28)} ${m.url}`);
|
|---|
| 69 | if (r.gemist.length > 20) console.log(` ... en nog ${r.gemist.length - 20}`);
|
|---|
| 70 | }
|
|---|
| 71 | for (const w of r.waarschuwingen) console.log(`\nLET OP: ${w}`);
|
|---|
| 72 |
|
|---|
| 73 | console.log('\nNIET herstelbaar uit een tijdlijn-cache: eigen antwoorden, alles van voor het');
|
|---|
| 74 | console.log('volgmoment, concepten, en de plek van afbeeldingen in de tekst.');
|
|---|
| 75 |
|
|---|
| 76 | if (args.includes('--dry-run')) {
|
|---|
| 77 | console.log(`\n--dry-run: ${uit.files.size} bestand(en) zouden geschreven worden. Niets geschreven.`);
|
|---|
| 78 | process.exit(0);
|
|---|
| 79 | }
|
|---|
| 80 |
|
|---|
| 81 | const dir = een('--dir');
|
|---|
| 82 | if (dir) {
|
|---|
| 83 | for (const pad of [...uit.files.keys()].sort()) {
|
|---|
| 84 | const doel = path.join(dir, pad);
|
|---|
| 85 | fs.mkdirSync(path.dirname(doel), { recursive: true });
|
|---|
| 86 | fs.writeFileSync(doel, uit.files.get(pad));
|
|---|
| 87 | }
|
|---|
| 88 | console.log(`\ngeschreven naar ${path.resolve(dir)}`);
|
|---|
| 89 | process.exit(0);
|
|---|
| 90 | }
|
|---|
| 91 |
|
|---|
| 92 | const out = een('--out') || 'herstel.zip';
|
|---|
| 93 | fs.writeFileSync(out, zipArchive(uit.files));
|
|---|
| 94 | console.log(`\ngeschreven: ${out} (${fs.statSync(out).size} bytes)`);
|
|---|
| 95 | console.log(`\nterugzetten: node scripts/import-archive.mjs <slug> ${out} --dry-run`);
|
|---|