fix(worker): endgueltig abgelehnte Mails nicht wiederholen, Adressen nicht loggen
Docker-Images bauen und veroeffentlichen / build-and-push (push) Canceled after 0s
Docker-Images bauen und veroeffentlichen / build-and-push (push) Canceled after 0s
Eine Mail an eine Adresse ohne Mailserver (SMTP 556, nullMX) wurde bis zu 8-mal erneut versendet und liess jeden Worker-Lauf mit Exit-Code 1 enden; das SMTP-Fehlerlog enthielt dabei die Empfaengeradresse. - SMTP-Antworten 5xx markieren die Mail sofort als FAILED, 4xx und Verbindungsfehler bleiben wiederholbar. - Adressen werden aus Log und lastError entfernt. - Worker-CronJob: backoffLimit 0 (kein zweiter Pod je Fehlschlag) und ttlSecondsAfterFinished 1 Tag, damit Fehler-Pods nicht stehen bleiben. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
f2b548cb98
commit
c3fdfbdc77
3 files changed
+108
-4
No files matched your search
+20
-3
@@ -85,6 +85,21 @@ async function deliver(message: { id: string; toAddress: string; subject: string
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* SMTP-Antworten 5xx sind endgueltig (z. B. Empfaenger abgelehnt, Domain ohne
|
||||
* Mailserver): erneutes Senden aendert nichts und liesse jeden Worker-Lauf
|
||||
* scheitern. 4xx und Verbindungsfehler bleiben wiederholbar.
|
||||
*/
|
||||
export function isPermanentMailError(error: unknown): boolean {
|
||||
const code = typeof error === "object" && error !== null ? (error as { responseCode?: unknown }).responseCode : undefined;
|
||||
return typeof code === "number" && code >= 500 && code < 600;
|
||||
}
|
||||
|
||||
/** SMTP-Fehlermeldungen nennen den Empfaenger; ins Log und in lastError kommt er nicht. */
|
||||
export function redactAddresses(text: string): string {
|
||||
return text.replace(/[^\s<>"',;:()]+@[^\s<>"',;:()]+/g, "[Adresse]");
|
||||
}
|
||||
|
||||
/** Versendet offene Mails. Fehler werden gezaehlt, nie mit Adresse geloggt. */
|
||||
export async function flushEmails(db: Db, params: { bookingId?: string; limit?: number } = {}): Promise<{ sent: number; failed: number }> {
|
||||
const pending = await db.emailMessage.findMany({
|
||||
@@ -111,15 +126,17 @@ export async function flushEmails(db: Db, params: { bookingId?: string; limit?:
|
||||
sent++;
|
||||
} catch (error) {
|
||||
failed++;
|
||||
const reason = error instanceof Error ? error.message.slice(0, 300) : "unbekannter Fehler";
|
||||
const permanent = isPermanentMailError(error);
|
||||
const reason = redactAddresses(error instanceof Error ? error.message : "unbekannter Fehler").slice(0, 300);
|
||||
await db.emailMessage.update({
|
||||
where: { id: message.id },
|
||||
data: {
|
||||
lastError: reason,
|
||||
status: message.attempts + 1 >= MAX_EMAIL_ATTEMPTS ? "FAILED" : "PENDING",
|
||||
status: permanent || message.attempts + 1 >= MAX_EMAIL_ATTEMPTS ? "FAILED" : "PENDING",
|
||||
},
|
||||
});
|
||||
console.error(`[mail] Versand fehlgeschlagen (Nachricht ${message.id}): ${reason}`);
|
||||
const final = permanent ? ", endgueltig abgelehnt" : "";
|
||||
console.error(`[mail] Versand fehlgeschlagen (Nachricht ${message.id}${final}): ${reason}`);
|
||||
}
|
||||
}
|
||||
return { sent, failed };
|
||||
|
||||
Reference in new issue
Block a user