#!/usr/bin/env node /** * Entschlüsselt ein Lageplan-Backup (.tar.gz.enc) → .tar.gz * * Verwendung: * node scripts/decrypt-backup.js [passphrase] * (ohne Passphrase-Argument wird sie interaktiv abgefragt bzw. aus BACKUP_PASSPHRASE gelesen) * * Danach: * tar xzf output.tar.gz → enthält database.dump + files/ * pg_restore --clean --if-exists -d database.dump * files/ zurück in MinIO spielen (siehe docs/BACKUP.md) * * Format der .enc-Datei: [salt(16)][iv(12)][ciphertext][tag(16)], AES-256-GCM, Key = scrypt(passphrase, salt). */ const fs = require('fs') const crypto = require('crypto') const [inPath, outPath] = process.argv.slice(2) let passphrase = process.argv[4] || process.env.BACKUP_PASSPHRASE if (!inPath || !outPath) { console.error('Verwendung: node decrypt-backup.js [passphrase]') process.exit(1) } if (!passphrase) { console.error('Passphrase fehlt. Als 3. Argument oder via BACKUP_PASSPHRASE übergeben.') process.exit(1) } const size = fs.statSync(inPath).size if (size < 44) { console.error('Datei zu klein / kein gültiges Backup.'); process.exit(1) } const fd = fs.openSync(inPath, 'r') const head = Buffer.alloc(28) fs.readSync(fd, head, 0, 28, 0) const tag = Buffer.alloc(16) fs.readSync(fd, tag, 0, 16, size - 16) fs.closeSync(fd) const salt = head.subarray(0, 16) const iv = head.subarray(16, 28) const key = crypto.scryptSync(passphrase, salt, 32) const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv) decipher.setAuthTag(tag) const input = fs.createReadStream(inPath, { start: 28, end: size - 17 }) const output = fs.createWriteStream(outPath) input.pipe(decipher).pipe(output) output.on('finish', () => console.log('OK →', outPath)) decipher.on('error', (e) => { console.error('Entschlüsselung fehlgeschlagen (falsche Passphrase?):', e.message); process.exit(1) })