| 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 | // Op een machine met meerdere instances leest hij de .env van die instance zelf
|
|---|
| 10 | // (standaard /var/lib/klonkt/<slug>/.env). Dat is niet netjesheid maar noodzaak:
|
|---|
| 11 | // zonder die .env opent hij de database die toevallig in de code-map ligt, en op
|
|---|
| 12 | // een server die ooit de enkelvoudige opzet draaide is dat een oude lege. Dan
|
|---|
| 13 | // komt er een archief uit dat MELDT dat het gelukt is en nul posts bevat.
|
|---|
| 14 | //
|
|---|
| 15 | // --data-root <map> waar de instances staan (standaard /var/lib/klonkt)
|
|---|
| 16 | // --env <pad> een .env rechtstreeks aanwijzen
|
|---|
| 17 | //
|
|---|
| 18 | // Het formaat staat in docs/EXPORT-FORMAT.md.
|
|---|
| 19 | //
|
|---|
| 20 | // Dit is NIET de storage-zip: er zit geen sleutel, sessie, wachtwoordhash of
|
|---|
| 21 | // DM van een ander in. Wel je eigen concepten, en -- als je die hebt -- de
|
|---|
| 22 | // volledige inhoud van betaalde posts. Behandel het bestand als de inhoud zelf.
|
|---|
| 23 |
|
|---|
| 24 | import fs from 'fs';
|
|---|
| 25 | import path from 'path';
|
|---|
| 26 | import { kiesInstance, eisOrigin, splitsArgs } from './instance-env.mjs';
|
|---|
| 27 |
|
|---|
| 28 | const args = process.argv.slice(2);
|
|---|
| 29 | // Twee namen, en ze zijn niet hetzelfde. De eerste is de INSTANCE (de map onder
|
|---|
| 30 | // de data-root, de systemd-unit); de tweede, optioneel, is de slug van de SITE in
|
|---|
| 31 | // die database. Laat je hem weg en er staat er precies een, dan is de keuze niet
|
|---|
| 32 | // dubbelzinnig en hoef je hem niet te weten.
|
|---|
| 33 | const { vrij, vlaggen } = splitsArgs(args, ['--data-root', '--env', '--out', '--dir']);
|
|---|
| 34 | const instance = vrij[0];
|
|---|
| 35 | let slug = vrij[1] || null;
|
|---|
| 36 | const vlag = (naam) => vlaggen[naam] ?? null;
|
|---|
| 37 |
|
|---|
| 38 | if (!instance) {
|
|---|
| 39 | console.error('gebruik: node scripts/export-archive.mjs <instance> [site-slug] [--data-root <map>] [--env <pad>] [--dry-run | --out <zip> | --dir <map>]');
|
|---|
| 40 | process.exit(1);
|
|---|
| 41 | }
|
|---|
| 42 |
|
|---|
| 43 | // EERST de instance kiezen, DAN pas de service laden: src/config/database.js
|
|---|
| 44 | // leest DATABASE_PATH bij import en opent de database meteen.
|
|---|
| 45 | let gekozen;
|
|---|
| 46 | try {
|
|---|
| 47 | gekozen = kiesInstance(instance, {
|
|---|
| 48 | dataRoot: typeof vlag('--data-root') === 'string' ? vlag('--data-root') : null,
|
|---|
| 49 | envPad: typeof vlag('--env') === 'string' ? vlag('--env') : null,
|
|---|
| 50 | });
|
|---|
| 51 | } catch (e) { console.error(e.message); process.exit(1); }
|
|---|
| 52 | eisOrigin(gekozen.bron);
|
|---|
| 53 |
|
|---|
| 54 | const { buildArchive, zipArchive, writeArchiveDir } = await import('../src/services/ArchiveExportService.js');
|
|---|
| 55 | const { default: db } = await import('../src/config/database.js');
|
|---|
| 56 |
|
|---|
| 57 | // Geen slug gegeven? Dan mag de database het zeggen, mits het antwoord eenduidig is.
|
|---|
| 58 | if (!slug) {
|
|---|
| 59 | let sites = [];
|
|---|
| 60 | try { sites = db.prepare('SELECT slug FROM sites ORDER BY rowid').all().map((r) => r.slug); } catch { /* geen tabel */ }
|
|---|
| 61 | if (sites.length === 1) {
|
|---|
| 62 | [slug] = sites;
|
|---|
| 63 | console.log(`site niet opgegeven, en er staat er precies een: ${slug}`);
|
|---|
| 64 | } else if (sites.length === 0) {
|
|---|
| 65 | console.error(`in ${process.env.DATABASE_PATH || 'deze database'} staat geen enkele site.`);
|
|---|
| 66 | process.exit(1);
|
|---|
| 67 | } else {
|
|---|
| 68 | console.error(`meerdere sites in deze database: ${sites.join(', ')}\nGeef er een op: node scripts/export-archive.mjs ${instance} <site-slug>`);
|
|---|
| 69 | process.exit(1);
|
|---|
| 70 | }
|
|---|
| 71 | }
|
|---|
| 72 |
|
|---|
| 73 | let uit;
|
|---|
| 74 | try { uit = buildArchive(slug); }
|
|---|
| 75 | catch (e) {
|
|---|
| 76 | console.error(`${e.message}`);
|
|---|
| 77 | if (gekozen.bron) console.error(`(gelezen uit ${gekozen.bron})`);
|
|---|
| 78 | else console.error('(geen instance-.env gevonden -- op een split install: --data-root /var/lib/klonkt)');
|
|---|
| 79 | process.exit(1);
|
|---|
| 80 | }
|
|---|
| 81 | const t = uit.counts;
|
|---|
| 82 | console.log(`site : ${uit.manifest.site.slug} (${uit.manifest.origin})`);
|
|---|
| 83 | console.log(`instellingen uit: ${gekozen.bron || 'de omgeving'}`);
|
|---|
| 84 | console.log(`database : ${process.env.DATABASE_PATH || '(standaard in de code-map)'}`);
|
|---|
| 85 | console.log(`posts : ${t.posts}`);
|
|---|
| 86 | console.log(`antwoorden: ${t.replies} (alleen-lezen archief)`);
|
|---|
| 87 | console.log(`media : ${t.media} meegenomen, ${t.mediaMissing} ontbrekend`);
|
|---|
| 88 |
|
|---|
| 89 | // Ontbrekende media worden GETELD en GEMELD, nooit stil overgeslagen: een post
|
|---|
| 90 | // waarvan het plaatje verdwenen is hoort dat te zeggen.
|
|---|
| 91 | if (uit.missing.length) {
|
|---|
| 92 | console.log('\nontbrekende media:');
|
|---|
| 93 | for (const m of uit.missing.slice(0, 20)) console.log(` ${m.post} ${m.url}`);
|
|---|
| 94 | if (uit.missing.length > 20) console.log(` ... en nog ${uit.missing.length - 20}`);
|
|---|
| 95 | }
|
|---|
| 96 |
|
|---|
| 97 | if (args.includes('--dry-run')) {
|
|---|
| 98 | console.log(`\n--dry-run: ${uit.files.size} bestand(en) zouden geschreven worden. Niets geschreven.`);
|
|---|
| 99 | process.exit(0);
|
|---|
| 100 | }
|
|---|
| 101 |
|
|---|
| 102 | const dir = vlag('--dir');
|
|---|
| 103 | if (dir && typeof dir === 'string') {
|
|---|
| 104 | writeArchiveDir(uit.files, dir);
|
|---|
| 105 | console.log(`\ngeschreven naar ${path.resolve(dir)}`);
|
|---|
| 106 | process.exit(0);
|
|---|
| 107 | }
|
|---|
| 108 |
|
|---|
| 109 | const out = (typeof vlag('--out') === 'string' ? vlag('--out') : null) || `${slug}-archief.zip`;
|
|---|
| 110 | fs.writeFileSync(out, zipArchive(uit.files));
|
|---|
| 111 | console.log(`\ngeschreven: ${out} (${fs.statSync(out).size} bytes)`);
|
|---|