Files
craftvia/src/server/backup/worker.ts
T
msolarczekandClaude Opus 5 c8e6f30a27
CI / build-and-check (push) Canceled after 0s
CI / audit (push) Canceled after 0s
CI / sbom (push) Canceled after 0s
Basis: Certvia dev@a48c5fb als Fundament für Craftvia
Unveränderter Stand von certvia/dev (a48c5fb) plus Craftvia-Spezifikation
und Brandbook unter docs/craftvia/. ISMS-Module werden im Folgecommit entfernt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 11:05:39 +02:00

50 lines
2.0 KiB
TypeScript

// ── BullMQ-Worker der Backup-/DSGVO-Ops-Queue ────────────────────────────────
//
// Analog zum Mail-Worker (SEC1). Nimmt Jobs aus `backup-ops` und führt sie über
// `processBackupJob` aus. Concurrency 1: Restore ist destruktiv und langlaufend
// (Mandant-Sperre, Pre-Restore-Snapshot) — Jobs laufen bewusst seriell, nicht
// parallel. Kein automatisches Retry (attempts=1 in queue.ts): eine Wiederholung
// eines destruktiven Restore ist eine bewusste Betreiber-Entscheidung.
import { Worker, type Job } from "bullmq";
import { BACKUP_QUEUE, type BackupOpsJob } from "./job";
import { closeBackupQueues, getBackupConnection, getBackupDeadLetterQueue } from "./queue";
import { processBackupJob } from "./ops";
export function startBackupWorker(): Worker<BackupOpsJob> {
const connection = getBackupConnection();
if (!connection) {
throw new Error("REDIS_URL ist nicht gesetzt — ohne Redis gibt es keinen Backup-Worker-Betrieb.");
}
const worker = new Worker<BackupOpsJob>(
BACKUP_QUEUE,
async (job: Job<BackupOpsJob>) => {
await processBackupJob(job.data);
},
{ connection, concurrency: 1 },
);
worker.on("failed", async (job, err) => {
if (!job) return;
console.error(`[backup] Job ${job.id} (${job.data.kind}) fehlgeschlagen: ${err.message}`);
// attempts=1 → jeder Fehlschlag ist endgültig: Dead-Letter.
await getBackupDeadLetterQueue()
?.add("dead", { job: job.data, error: err.message })
.catch(() => {});
console.error(`[backup] ALARM — Job ${job.id} in die Dead-Letter-Queue verschoben.`);
});
worker.on("completed", (job) => {
console.info(`[backup] Job ${job.id} (${job.data.kind}) fertig — Mandant ${job.data.tenantId}.`);
});
return worker;
}
/** Sauberes Herunterfahren von Worker und Redis. */
export async function shutdownBackupWorker(worker: Worker | null): Promise<void> {
await worker?.close();
await closeBackupQueues();
}