feat(log): improve logging for emails

This commit is contained in:
2026-03-11 14:23:39 +01:00
parent 5e5efcaf6f
commit 17aa8956a7
2 changed files with 299 additions and 93 deletions

View File

@@ -19,6 +19,7 @@ import {
} from "drizzle-orm";
import { z } from "zod";
import {
emailLog,
sendCancellationEmail,
sendConfirmationEmail,
sendPaymentReminderEmail,
@@ -324,7 +325,10 @@ export const appRouter = {
giftAmount: input.giftAmount,
drinkCardValue: isPerformer ? 0 : drinkCardEuros(guests.length),
}).catch((err) =>
console.error("Failed to send confirmation email:", err),
emailLog("error", "email.catch", {
type: "confirmation",
error: String(err),
}),
);
return { success: true, managementToken };
@@ -412,7 +416,12 @@ export const appRouter = {
artForm: input.artForm,
giftAmount: input.giftAmount,
drinkCardValue: isPerformer ? 0 : drinkCardEuros(guests.length),
}).catch((err) => console.error("Failed to send update email:", err));
}).catch((err) =>
emailLog("error", "email.catch", {
type: "update",
error: String(err),
}),
);
return { success: true };
}),
@@ -431,7 +440,10 @@ export const appRouter = {
to: row.email,
firstName: row.firstName,
}).catch((err) =>
console.error("Failed to send cancellation email:", err),
emailLog("error", "email.catch", {
type: "cancellation",
error: String(err),
}),
);
return { success: true };
@@ -869,7 +881,11 @@ export const appRouter = {
// Fire-and-forget — don't let a mail failure block the response
sendSubscriptionConfirmationEmail({ to: input.email }).catch((err) =>
console.error("Failed to send subscription confirmation email:", err),
emailLog("error", "email.catch", {
type: "subscription_confirmation",
to: input.email,
error: String(err),
}),
);
return { ok: true };
@@ -901,6 +917,9 @@ export const appRouter = {
now >= REMINDER_1H_WINDOW_START.getTime() &&
now < REGISTRATION_OPENS_AT.getTime();
const activeWindow = in24hWindow ? "24h" : in1hWindow ? "1h" : null;
emailLog("info", "reminders.cron.tick", { activeWindow });
if (!in24hWindow && !in1hWindow) {
return { sent: 0, skipped: true, reason: "outside_window" as const };
}
@@ -915,6 +934,8 @@ export const appRouter = {
.from(reminder)
.where(isNull(reminder.sent24hAt));
emailLog("info", "reminders.24h.pending", { count: pending.length });
for (const row of pending) {
try {
await sendReminder24hEmail({ to: row.email });
@@ -925,7 +946,11 @@ export const appRouter = {
sent++;
} catch (err) {
errors.push(`24h ${row.email}: ${String(err)}`);
console.error(`Failed to send 24h reminder to ${row.email}:`, err);
emailLog("error", "email.catch", {
type: "reminder_24h",
to: row.email,
error: String(err),
});
}
}
}
@@ -937,6 +962,8 @@ export const appRouter = {
.from(reminder)
.where(isNull(reminder.sentAt));
emailLog("info", "reminders.1h.pending", { count: pending.length });
for (const row of pending) {
try {
await sendReminderEmail({ to: row.email });
@@ -947,11 +974,20 @@ export const appRouter = {
sent++;
} catch (err) {
errors.push(`1h ${row.email}: ${String(err)}`);
console.error(`Failed to send 1h reminder to ${row.email}:`, err);
emailLog("error", "email.catch", {
type: "reminder_1h",
to: row.email,
error: String(err),
});
}
}
}
emailLog("info", "reminders.cron.done", {
sent,
errorCount: errors.length,
});
return { sent, skipped: false, errors };
}),
@@ -1081,6 +1117,9 @@ export async function runSendReminders(): Promise<{
now >= REMINDER_1H_WINDOW_START.getTime() &&
now < REGISTRATION_OPENS_AT.getTime();
const activeWindow = in24hWindow ? "24h" : in1hWindow ? "1h" : null;
emailLog("info", "reminders.cron.tick", { activeWindow });
if (!in24hWindow && !in1hWindow) {
return { sent: 0, skipped: true, reason: "outside_window", errors: [] };
}
@@ -1094,6 +1133,8 @@ export async function runSendReminders(): Promise<{
.from(reminder)
.where(isNull(reminder.sent24hAt));
emailLog("info", "reminders.24h.pending", { count: pending.length });
for (const row of pending) {
try {
await sendReminder24hEmail({ to: row.email });
@@ -1104,7 +1145,11 @@ export async function runSendReminders(): Promise<{
sent++;
} catch (err) {
errors.push(`24h ${row.email}: ${String(err)}`);
console.error(`Failed to send 24h reminder to ${row.email}:`, err);
emailLog("error", "email.catch", {
type: "reminder_24h",
to: row.email,
error: String(err),
});
}
}
}
@@ -1115,6 +1160,8 @@ export async function runSendReminders(): Promise<{
.from(reminder)
.where(isNull(reminder.sentAt));
emailLog("info", "reminders.1h.pending", { count: pending.length });
for (const row of pending) {
try {
await sendReminderEmail({ to: row.email });
@@ -1125,7 +1172,11 @@ export async function runSendReminders(): Promise<{
sent++;
} catch (err) {
errors.push(`1h ${row.email}: ${String(err)}`);
console.error(`Failed to send 1h reminder to ${row.email}:`, err);
emailLog("error", "email.catch", {
type: "reminder_1h",
to: row.email,
error: String(err),
});
}
}
}
@@ -1144,6 +1195,10 @@ export async function runSendReminders(): Promise<{
),
);
emailLog("info", "reminders.payment.pending", {
count: unpaidWatchers.length,
});
for (const reg of unpaidWatchers) {
if (!reg.managementToken) continue;
try {
@@ -1161,9 +1216,15 @@ export async function runSendReminders(): Promise<{
sent++;
} catch (err) {
errors.push(`payment-reminder ${reg.email}: ${String(err)}`);
console.error(`Failed to send payment reminder to ${reg.email}:`, err);
emailLog("error", "email.catch", {
type: "payment_reminder",
to: reg.email,
error: String(err),
});
}
}
emailLog("info", "reminders.cron.done", { sent, errorCount: errors.length });
return { sent, skipped: false, errors };
}