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

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

Export en import kiezen de juiste instance, en tijdstempels zijn echt UTC

Twee fouten, allebei op productie gevonden en allebei van het soort dat SUCCES
meldt.

## 1. Het script opende de verkeerde database

Op een split install deelt elke Klonkt de code in /opt/klonkt maar staat zijn data
onder /var/lib/klonkt/<slug>/ met een eigen .env. De scripts lazen die .env niet,
dus database.js viel terug op storage/database.sqlite IN DE CODE-MAP. Op een
server die ooit de enkelvoudige opzet draaide ligt daar een achtergebleven oude
database.

Waargenomen: export-archive.mjs boiert schreef een archief van 444 bytes met
"posts: 0" en meldde dat het gelukt was, terwijl het in een oude lege database
keek. liz en sood gaven "onbekende site". Het script raakte die oude database
ook nog aan (boot-migraties), terwijl het alleen hoort te lezen.

Nu leest scripts/instance-env.mjs de .env van de instance (standaard
/var/lib/klonkt/<slug>/.env, of --data-root / --env) VOORDAT de service geladen
wordt -- database.js opent de database namelijk bij import. De uitvoer noemt
voortaan welke .env en welke database gebruikt zijn.

En zonder PUBLIC_BASE_URL stopt hij nu HARD in plaats van het als bijzin te
melden: zonder origin krijgt het archief een lege origin, en dan maakt een import
nieuwe AP-ids. Dan is het een kopie van de tekst en geen herstel -- precies de
belofte waar het formaat om draait.

Voor de importer weegt dit zwaarder dan voor de exporter: in de verkeerde
database schrijven draai je niet terug.

## 2. Tijdstempels schoven met de tijdzone van de machine

Vraag van Bart: normaliseert de export wel naar UTC? Nee.

SQLite schrijft CURRENT_TIMESTAMP als "2026-07-01 12:56:10" -- in UTC, maar zonder
zone. Date.parse leest die vorm als LOKALE tijd. Op een machine in Amsterdam komt
daar 10:56:10Z uit: twee uur verschoven, in elk archief.

Geen enkele test kon dat zien, want de testmachine draait op UTC. En mijn eigen
rondgang-vergelijking plakte er zelf een Z achter, waarmee ik er precies overheen
keek.

toISO leest een zoneloze vorm nu expliciet als UTC. Nieuwe test die alleen onder
een niet-UTC zone iets bewijst; gecontroleerd dat hij onder Europe/Amsterdam
omvalt zonder de fix. Hele suite 577/577 onder zowel UTC als Europe/Amsterdam.

  • Property mode set to 100644
File size: 3.7 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// 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
24import fs from 'fs';
25import path from 'path';
26import { kiesInstance, eisOrigin } from './instance-env.mjs';
27
28const args = process.argv.slice(2);
29const slug = args.find((a) => !a.startsWith('-'));
30const vlag = (naam) => { const i = args.indexOf(naam); return i >= 0 ? (args[i + 1] || true) : null; };
31
32if (!slug) {
33 console.error('gebruik: node scripts/export-archive.mjs <slug> [--data-root <map>] [--env <pad>] [--dry-run | --out <zip> | --dir <map>]');
34 process.exit(1);
35}
36
37// EERST de instance kiezen, DAN pas de service laden: src/config/database.js
38// leest DATABASE_PATH bij import en opent de database meteen.
39let gekozen;
40try {
41 gekozen = kiesInstance(slug, {
42 dataRoot: typeof vlag('--data-root') === 'string' ? vlag('--data-root') : null,
43 envPad: typeof vlag('--env') === 'string' ? vlag('--env') : null,
44 });
45} catch (e) { console.error(e.message); process.exit(1); }
46eisOrigin(gekozen.bron);
47
48const { buildArchive, zipArchive, writeArchiveDir } = await import('../src/services/ArchiveExportService.js');
49
50let uit;
51try { uit = buildArchive(slug); }
52catch (e) {
53 console.error(`${e.message}`);
54 if (gekozen.bron) console.error(`(gelezen uit ${gekozen.bron})`);
55 else console.error('(geen instance-.env gevonden -- op een split install: --data-root /var/lib/klonkt)');
56 process.exit(1);
57}
58const t = uit.counts;
59console.log(`site : ${uit.manifest.site.slug} (${uit.manifest.origin})`);
60console.log(`instellingen uit: ${gekozen.bron || 'de omgeving'}`);
61console.log(`database : ${process.env.DATABASE_PATH || '(standaard in de code-map)'}`);
62console.log(`posts : ${t.posts}`);
63console.log(`antwoorden: ${t.replies} (alleen-lezen archief)`);
64console.log(`media : ${t.media} meegenomen, ${t.mediaMissing} ontbrekend`);
65
66// Ontbrekende media worden GETELD en GEMELD, nooit stil overgeslagen: een post
67// waarvan het plaatje verdwenen is hoort dat te zeggen.
68if (uit.missing.length) {
69 console.log('\nontbrekende media:');
70 for (const m of uit.missing.slice(0, 20)) console.log(` ${m.post} ${m.url}`);
71 if (uit.missing.length > 20) console.log(` ... en nog ${uit.missing.length - 20}`);
72}
73
74if (args.includes('--dry-run')) {
75 console.log(`\n--dry-run: ${uit.files.size} bestand(en) zouden geschreven worden. Niets geschreven.`);
76 process.exit(0);
77}
78
79const dir = vlag('--dir');
80if (dir && typeof dir === 'string') {
81 writeArchiveDir(uit.files, dir);
82 console.log(`\ngeschreven naar ${path.resolve(dir)}`);
83 process.exit(0);
84}
85
86const out = (typeof vlag('--out') === 'string' ? vlag('--out') : null) || `${slug}-archief.zip`;
87fs.writeFileSync(out, zipArchive(uit.files));
88console.log(`\ngeschreven: ${out} (${fs.statSync(out).size} bytes)`);
Note: See TracBrowser for help on using the repository browser.