source: Klonkt/scripts/export-archive.mjs@ a6d703a

main
Last change on this file since a6d703a was 712b161, checked in by roboburr <roboburr@…>, 5 weeks ago

De exporter (shaer-1a6)

ArchiveExportService bouwt het archief uit docs/EXPORT-FORMAT.md, plus een CLI
(scripts/export-archive.mjs) met een droogloop.

Alleen de eigen posts van de site, inclusief concepten. Geen sleutel, sessie,
wachtwoordhash of DM van een ander -- dit is nadrukkelijk niet de storage-zip uit
shaer-190t.

Media worden gesleuteld op de sha256 van hun bytes, zodat twee exports dezelfde
namen opleveren en dubbelen vanzelf samenvallen. Een verdwenen bestand krijgt de
derde staat uit het formaat -- 'missing', met de oorspronkelijke plek erbij -- en
wordt geteld en gemeld, nooit stil weggelaten.

Zip zonder nieuwe afhankelijkheid: store-only, gesorteerde ingangen, vaste mtime.
Zonder compressie is byte-gelijkheid geen kwestie van vertrouwen in de
instellingen van een bibliotheek. Gecontroleerd dat gewone unzip hem uitpakt en
dat de bytes er identiek uitkomen; een zelfgeschreven zip die alleen zichzelf kan
lezen is geen uitwisselformaat.

Twee dingen kwamen pas boven bij het draaien tegen echte data (beta):

  • een audio-bijlage in c2s_attachments draagt naast url ook een POSTER, en die viel er stil uit. Nu meegenomen; media op beta gingen daarmee van 2 naar 3.
  • het [[track:]]-pad raakt op beta geen enkele rij (nul audio_tracks), dus dat heeft nu een eigen test in plaats van de aanname dat het meeliep.

Een verzonnen pad in oude inhoud kan geen willekeurig bestand van de schijf het
archief in trekken: media-URL's worden binnen MEDIA_ROOT gehouden en een URL naar
een andere host wordt nooit van onze eigen schijf gelezen.

De reproduceerbaarheidseis in het formaat sloot exportedAt niet uit en sprak
zichzelf daarmee tegen; dat is rechtgezet.

15 tests, waaronder de acceptatietest uit het document: de moeilijkste post die
Klonkt kan maken (poll, bijlagen, quote, content warning, betaald). Suite 493/493.

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

  • Property mode set to 100644
File size: 2.3 KB
Line 
1#!/usr/bin/env node
2//
3// Exporteer het inhoudsarchief van een site (shaer-1a6).
4//
5// node scripts/export-archive.mjs <slug> --dry-run
6// node scripts/export-archive.mjs <slug> --out boiert.zip
7// node scripts/export-archive.mjs <slug> --dir ./archief
8//
9// Het formaat staat in docs/EXPORT-FORMAT.md.
10//
11// Dit is NIET de storage-zip: er zit geen sleutel, sessie, wachtwoordhash of
12// DM van een ander in. Wel je eigen concepten, en -- als je die hebt -- de
13// volledige inhoud van betaalde posts. Behandel het bestand als de inhoud zelf.
14
15import fs from 'fs';
16import path from 'path';
17import { buildArchive, zipArchive, writeArchiveDir } from '../src/services/ArchiveExportService.js';
18
19const args = process.argv.slice(2);
20const slug = args.find((a) => !a.startsWith('-'));
21const vlag = (naam) => { const i = args.indexOf(naam); return i >= 0 ? (args[i + 1] || true) : null; };
22
23if (!slug) {
24 console.error('gebruik: node scripts/export-archive.mjs <slug> [--dry-run | --out <zip> | --dir <map>]');
25 process.exit(1);
26}
27
28const uit = buildArchive(slug);
29const t = uit.counts;
30console.log(`site : ${uit.manifest.site.slug} (${uit.manifest.origin || 'geen PUBLIC_BASE_URL'})`);
31console.log(`posts : ${t.posts}`);
32console.log(`antwoorden: ${t.replies} (alleen-lezen archief)`);
33console.log(`media : ${t.media} meegenomen, ${t.mediaMissing} ontbrekend`);
34
35// Ontbrekende media worden GETELD en GEMELD, nooit stil overgeslagen: een post
36// waarvan het plaatje verdwenen is hoort dat te zeggen.
37if (uit.missing.length) {
38 console.log('\nontbrekende media:');
39 for (const m of uit.missing.slice(0, 20)) console.log(` ${m.post} ${m.url}`);
40 if (uit.missing.length > 20) console.log(` ... en nog ${uit.missing.length - 20}`);
41}
42
43if (args.includes('--dry-run')) {
44 console.log(`\n--dry-run: ${uit.files.size} bestand(en) zouden geschreven worden. Niets geschreven.`);
45 process.exit(0);
46}
47
48const dir = vlag('--dir');
49if (dir && typeof dir === 'string') {
50 writeArchiveDir(uit.files, dir);
51 console.log(`\ngeschreven naar ${path.resolve(dir)}`);
52 process.exit(0);
53}
54
55const out = (typeof vlag('--out') === 'string' ? vlag('--out') : null) || `${slug}-archief.zip`;
56fs.writeFileSync(out, zipArchive(uit.files));
57console.log(`\ngeschreven: ${out} (${fs.statSync(out).size} bytes)`);
Note: See TracBrowser for help on using the repository browser.