import crypto from "node:crypto"; export const EMAIL_VERIFICATION_PURPOSE_REGISTER = "register"; export type ResendConfig = { apiKey: string; fromEmail: string; }; function getVerificationSecret(): string { return ( process.env.EMAIL_VERIFICATION_SECRET || process.env.AUTH_SECRET || "email-verification-secret" ); } export function hashEmailVerificationCode(params: { email: string; purpose: string; code: string; }): string { const payload = `${params.email.toLowerCase()}:${params.purpose}:${params.code}:${getVerificationSecret()}`; return crypto.createHash("sha256").update(payload).digest("hex"); } export function createEmailVerificationCode(): string { return String(crypto.randomInt(0, 1000000)).padStart(6, "0"); } export function isResendConfigured(): boolean { return Boolean(process.env.RESEND_API_KEY && process.env.RESEND_FROM_EMAIL); } export function resolveResendConfig(config?: { apiKey?: string | null; fromEmail?: string | null; }): ResendConfig | null { const apiKey = String(config?.apiKey || process.env.RESEND_API_KEY || "").trim(); const fromEmail = String(config?.fromEmail || process.env.RESEND_FROM_EMAIL || "").trim(); if (!apiKey || !fromEmail) { return null; } return { apiKey, fromEmail }; } export async function sendVerificationCodeByResend(params: { to: string; code: string; siteName: string; expiresInMinutes: number; config?: ResendConfig; }): Promise { const resolvedConfig = params.config || resolveResendConfig(); if (!resolvedConfig) { throw new Error("Resend is not configured"); } const subject = `【${params.siteName}】邮箱验证码`; const html = `

邮箱验证码

你正在注册 ${params.siteName},验证码如下:

${params.code}

验证码 ${params.expiresInMinutes} 分钟内有效,请勿泄露给他人。

`; const text = `你正在注册 ${params.siteName},验证码:${params.code}。验证码 ${params.expiresInMinutes} 分钟内有效。`; const response = await fetch("https://api.resend.com/emails", { method: "POST", headers: { Authorization: `Bearer ${resolvedConfig.apiKey}`, "Content-Type": "application/json", }, body: JSON.stringify({ from: resolvedConfig.fromEmail, to: params.to, subject, html, text, }), }); if (!response.ok) { const detail = await response.text(); throw new Error(`Resend request failed: ${response.status} ${detail}`); } }