- Add BullMQ to backend; mail jobs (verification, password reset) now enqueued instead of sent inline - Mail worker processes jobs with 3-attempt exponential backoff retry - Admin GraphQL resolvers: adminQueues, adminQueueJobs, adminRetryJob, adminRemoveJob, adminPauseQueue, adminResumeQueue - Admin frontend page at /admin/queues: queue cards with counts, job table with status filter, retry/remove/pause actions Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
46 lines
1.5 KiB
TypeScript
46 lines
1.5 KiB
TypeScript
import nodemailer from "nodemailer";
|
|
import { mailQueue } from "../queues/index.js";
|
|
|
|
const transporter = nodemailer.createTransport({
|
|
host: process.env.SMTP_HOST || "localhost",
|
|
port: parseInt(process.env.SMTP_PORT || "587"),
|
|
secure: process.env.SMTP_SECURE === "true",
|
|
auth: process.env.SMTP_USER
|
|
? {
|
|
user: process.env.SMTP_USER,
|
|
pass: process.env.SMTP_PASS,
|
|
}
|
|
: undefined,
|
|
});
|
|
|
|
const FROM = process.env.EMAIL_FROM || "noreply@sexy.pivoine.art";
|
|
const BASE_URL = process.env.PUBLIC_URL || "http://localhost:3000";
|
|
|
|
export async function sendVerification(email: string, token: string): Promise<void> {
|
|
await transporter.sendMail({
|
|
from: FROM,
|
|
to: email,
|
|
subject: "Verify your email",
|
|
html: `<p>Click <a href="${BASE_URL}/signup/verify?token=${token}">here</a> to verify your email.</p>`,
|
|
});
|
|
}
|
|
|
|
export async function sendPasswordReset(email: string, token: string): Promise<void> {
|
|
await transporter.sendMail({
|
|
from: FROM,
|
|
to: email,
|
|
subject: "Reset your password",
|
|
html: `<p>Click <a href="${BASE_URL}/password/reset?token=${token}">here</a> to reset your password.</p>`,
|
|
});
|
|
}
|
|
|
|
const jobOpts = { attempts: 3, backoff: { type: "exponential" as const, delay: 5000 } };
|
|
|
|
export async function enqueueVerification(email: string, token: string): Promise<void> {
|
|
await mailQueue.add("sendVerification", { email, token }, jobOpts);
|
|
}
|
|
|
|
export async function enqueuePasswordReset(email: string, token: string): Promise<void> {
|
|
await mailQueue.add("sendPasswordReset", { email, token }, jobOpts);
|
|
}
|