| 1 | //
|
|---|
| 2 | // Welke instance bedoel je?
|
|---|
| 3 | //
|
|---|
| 4 | // Op een split install (deploy/MULTI-INSTANCE.md) deelt elke Klonkt dezelfde code
|
|---|
| 5 | // in /opt/klonkt, maar staat zijn DATA onder /var/lib/klonkt/<slug>/ met een eigen
|
|---|
| 6 | // .env. Een script dat vanuit de code-map draait zonder die .env te lezen opent
|
|---|
| 7 | // dus de VERKEERDE database -- en op een machine die ooit de oude enkelvoudige
|
|---|
| 8 | // opzet draaide is dat een achtergebleven storage/database.sqlite in de checkout.
|
|---|
| 9 | //
|
|---|
| 10 | // Dat ging in de praktijk mis en op de ergste manier: het meldde SUCCES. Een
|
|---|
| 11 | // export van een site met zeven jaar aan posten schreef een archief van 444 bytes
|
|---|
| 12 | // met "posts: 0", omdat hij in een oude lege database keek. Een leeg archief dat
|
|---|
| 13 | // zegt dat het gelukt is, is erger dan een foutmelding.
|
|---|
| 14 | //
|
|---|
| 15 | // Vandaar dit: los, want de importer en het herstel hebben hem net zo hard nodig.
|
|---|
| 16 |
|
|---|
| 17 | import fs from 'fs';
|
|---|
| 18 | import path from 'path';
|
|---|
| 19 |
|
|---|
| 20 | /** Lees een .env zonder afhankelijkheden. Alleen KEY=value, # is commentaar. */
|
|---|
| 21 | export function leesEnv(pad) {
|
|---|
| 22 | const uit = {};
|
|---|
| 23 | for (const regel of fs.readFileSync(pad, 'utf8').split('\n')) {
|
|---|
| 24 | const s = regel.trim();
|
|---|
| 25 | if (!s || s.startsWith('#')) continue;
|
|---|
| 26 | const i = s.indexOf('=');
|
|---|
| 27 | if (i < 1) continue;
|
|---|
| 28 | let v = s.slice(i + 1).trim();
|
|---|
| 29 | if ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'"))) v = v.slice(1, -1);
|
|---|
| 30 | uit[s.slice(0, i).trim()] = v;
|
|---|
| 31 | }
|
|---|
| 32 | return uit;
|
|---|
| 33 | }
|
|---|
| 34 |
|
|---|
| 35 | /**
|
|---|
| 36 | * Zet de omgeving voor EEN instance, en zeg hardop welke.
|
|---|
| 37 | *
|
|---|
| 38 | * Moet AANGEROEPEN WORDEN VOORDAT src/config/database.js geladen wordt: die leest
|
|---|
| 39 | * DATABASE_PATH bij import en opent de database meteen. Vandaar dat de scripts
|
|---|
| 40 | * hun service-import uitstellen tot na deze aanroep.
|
|---|
| 41 | *
|
|---|
| 42 | * @returns {{bron: string, env: object}} waar de instellingen vandaan kwamen
|
|---|
| 43 | */
|
|---|
| 44 | export function kiesInstance(slug, opts = {}) {
|
|---|
| 45 | const dataRoot = opts.dataRoot || process.env.KLONKT_DATA_ROOT || '/var/lib/klonkt';
|
|---|
| 46 | const kandidaat = opts.envPad || path.join(dataRoot, slug, '.env');
|
|---|
| 47 |
|
|---|
| 48 | if (fs.existsSync(kandidaat)) {
|
|---|
| 49 | const env = leesEnv(kandidaat);
|
|---|
| 50 | // De data-map van de instance is de wortel voor relatieve paden in zijn .env,
|
|---|
| 51 | // precies zoals de unit hem draait (WorkingDirectory is de code, DATA_DIR de data).
|
|---|
| 52 | const basis = env.DATA_DIR || path.dirname(kandidaat);
|
|---|
| 53 | const absoluut = (p) => (p && !path.isAbsolute(p) ? path.resolve(basis, p) : p);
|
|---|
| 54 | for (const k of ['DATABASE_PATH', 'MEDIA_PATH', 'AUDIO_PATH', 'AVATAR_PATH', 'COVER_PATH']) {
|
|---|
| 55 | if (env[k]) process.env[k] = absoluut(env[k]);
|
|---|
| 56 | }
|
|---|
| 57 | if (env.PUBLIC_BASE_URL) process.env.PUBLIC_BASE_URL = env.PUBLIC_BASE_URL;
|
|---|
| 58 | if (env.DATA_DIR) process.env.DATA_DIR = env.DATA_DIR;
|
|---|
| 59 | return { bron: kandidaat, env };
|
|---|
| 60 | }
|
|---|
| 61 |
|
|---|
| 62 | if (opts.envPad) {
|
|---|
| 63 | // Expliciet meegegeven en niet gevonden: dat is een vergissing en geen reden
|
|---|
| 64 | // om stilletjes iets anders te openen.
|
|---|
| 65 | throw new Error(`geen .env op ${opts.envPad}`);
|
|---|
| 66 | }
|
|---|
| 67 | return { bron: null, env: {} };
|
|---|
| 68 | }
|
|---|
| 69 |
|
|---|
| 70 | /**
|
|---|
| 71 | * Weiger te beginnen als we niet kunnen weten wiens gegevens dit zijn.
|
|---|
| 72 | *
|
|---|
| 73 | * Zonder PUBLIC_BASE_URL komt er een archief uit met een lege `origin`, en dan
|
|---|
| 74 | * weigert de importer de AP-ids te behouden -- hij maakt nieuwe. Daarmee valt
|
|---|
| 75 | * precies de belofte weg waar het formaat om draait: een herstel waarna de
|
|---|
| 76 | * boosts en antwoorden elders hun post terugvinden. Dat mag geen voetnoot in de
|
|---|
| 77 | * uitvoer zijn.
|
|---|
| 78 | */
|
|---|
| 79 | export function eisOrigin(bron) {
|
|---|
| 80 | if (process.env.PUBLIC_BASE_URL) return;
|
|---|
| 81 | console.error([
|
|---|
| 82 | 'GESTOPT: er is geen PUBLIC_BASE_URL.',
|
|---|
| 83 | '',
|
|---|
| 84 | 'Zonder die waarde krijgt het archief een lege origin, en dan maakt een import',
|
|---|
| 85 | 'NIEUWE ActivityPub-ids in plaats van de oude te behouden. Boosts, likes en',
|
|---|
| 86 | 'antwoorden elders vinden hun post dan niet meer terug -- het archief is dan',
|
|---|
| 87 | 'een kopie van de tekst en geen herstel.',
|
|---|
| 88 | '',
|
|---|
| 89 | bron
|
|---|
| 90 | ? `Gelezen uit ${bron}, maar PUBLIC_BASE_URL staat daar niet in.`
|
|---|
| 91 | : 'Geen .env van een instance gevonden. Op een split install:\n'
|
|---|
| 92 | + ' node scripts/export-archive.mjs <slug> --data-root /var/lib/klonkt',
|
|---|
| 93 | ].join('\n'));
|
|---|
| 94 | process.exit(1);
|
|---|
| 95 | }
|
|---|