fix: lokale Dump-Datei immer löschen (try/finally)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-05 16:23:05 +02:00
parent 43ddbbcf72
commit 00a3f02d80
+51 -48
View File
@@ -77,57 +77,60 @@ async function runBackup(): Promise<void> {
await dumpToFile(localPath); await dumpToFile(localPath);
console.log(`[backup] Dump geschrieben: ${localPath}`); console.log(`[backup] Dump geschrieben: ${localPath}`);
if (!sshUrl) return; try {
if (!sshUrl) return;
const match = sshUrl.match(/^([^:]+):(.+)$/); const match = sshUrl.match(/^([^:]+):(.+)$/);
if (!match) { if (!match) {
console.error('[backup] BACKUP_SSH_URL muss das Format user@host:/pfad haben'); console.error('[backup] BACKUP_SSH_URL muss das Format user@host:/pfad haben');
return; return;
} }
const [, sshHost, remotePath] = match; const [, sshHost, remotePath] = match;
const rawKeyPath = process.env.BACKUP_SSH_KEY_PATH || ''; const rawKeyPath = process.env.BACKUP_SSH_KEY_PATH || '';
const keyPath = rawKeyPath.startsWith('~') const keyPath = rawKeyPath.startsWith('~')
? rawKeyPath.replace('~', process.env.HOME || '/root') ? rawKeyPath.replace('~', process.env.HOME || '/root')
: rawKeyPath; : rawKeyPath;
const sshOpts = [ const sshOpts = [
...(keyPath ? ['-i', keyPath] : []), ...(keyPath ? ['-i', keyPath] : []),
'-o', 'StrictHostKeyChecking=no', '-o', 'StrictHostKeyChecking=no',
'-o', 'BatchMode=yes', '-o', 'BatchMode=yes',
'-o', 'ConnectTimeout=15', '-o', 'ConnectTimeout=15',
]; ];
// Zielverzeichnis auf Remote anlegen falls nicht vorhanden // Zielverzeichnis auf Remote anlegen falls nicht vorhanden
await new Promise<void>((resolve, reject) => { await new Promise<void>((resolve, reject) => {
const ssh = spawn('ssh', [...sshOpts, sshHost, `mkdir -p ${remotePath}`]); const ssh = spawn('ssh', [...sshOpts, sshHost, `mkdir -p ${remotePath}`]);
ssh.on('error', reject); ssh.on('error', reject);
ssh.on('close', (code) => code === 0 ? resolve() : reject(new Error(`mkdir -p exit ${code}`))); ssh.on('close', (code) => code === 0 ? resolve() : reject(new Error(`mkdir -p exit ${code}`)));
});
await new Promise<void>((resolve, reject) => {
const scp = spawn('scp', [...sshOpts, localPath, `${sshHost}:${remotePath}/${filename}`]);
let scpErr = '';
scp.stderr.on('data', (d: Buffer) => { scpErr += d.toString(); });
scp.on('error', reject);
scp.on('close', (code) =>
code === 0 ? resolve() : reject(new Error(`scp exit ${code}${scpErr ? ': ' + scpErr.trim() : ''}`))
);
});
unlinkSync(localPath);
console.log(`[backup] ${filename}${sshHost}:${remotePath}`);
// Backups älter als 30 Tage auf Remote löschen
await new Promise<void>((resolve) => {
const ssh = spawn('ssh', [
...sshOpts, sshHost,
`find ${remotePath} -name 'sternwarte_*.sql.gz' -mtime +30 -delete`,
]);
ssh.on('error', (e) => { console.error('[backup] Cleanup spawn-Fehler:', e.message); resolve(); });
ssh.on('close', (code) => {
if (code !== 0) console.error('[backup] Cleanup fehlgeschlagen (exit ' + code + ')');
resolve();
}); });
});
await new Promise<void>((resolve, reject) => {
const scp = spawn('scp', [...sshOpts, localPath, `${sshHost}:${remotePath}/${filename}`]);
let scpErr = '';
scp.stderr.on('data', (d: Buffer) => { scpErr += d.toString(); });
scp.on('error', reject);
scp.on('close', (code) =>
code === 0 ? resolve() : reject(new Error(`scp exit ${code}${scpErr ? ': ' + scpErr.trim() : ''}`))
);
});
console.log(`[backup] ${filename}${sshHost}:${remotePath}`);
// Backups älter als 30 Tage auf Remote löschen
await new Promise<void>((resolve) => {
const ssh = spawn('ssh', [
...sshOpts, sshHost,
`find ${remotePath} -name 'sternwarte_*.sql.gz' -mtime +30 -delete`,
]);
ssh.on('error', (e) => { console.error('[backup] Cleanup spawn-Fehler:', e.message); resolve(); });
ssh.on('close', (code) => {
if (code !== 0) console.error('[backup] Cleanup fehlgeschlagen (exit ' + code + ')');
resolve();
});
});
} finally {
try { unlinkSync(localPath); } catch { /* bereits gelöscht oder nie angelegt */ }
}
} }