返回 DeepSeek-Reasonix
index.ts
根目录 / workers / accounts / src / email / index.ts
1 import type { Bindings } from "../env";
2 import type { EmailMessage, Mailer } from "./types";
3 import { ResendMailer } from "./resend";
4
5 export type { EmailMessage, Mailer } from "./types";
6
7 // Dev-time fallback: write the message (and any link inside it) to the worker
8 // console so flows can be exercised without configuring a mail provider.
9 class ConsoleMailer implements Mailer {
10 constructor(private readonly from: string) {}
11 async send(msg: EmailMessage): Promise<void> {
12 console.log(`[email:stub] from=${this.from} to=${msg.to}\nsubject: ${msg.subject}\n${msg.text}`);
13 }
14 }
15
16 export function buildMailer(env: Bindings): Mailer {
17 if (env.EMAIL_PROVIDER?.toLowerCase() === "resend" && env.RESEND_API_KEY) {
18 return new ResendMailer(env.RESEND_API_KEY, env.MAIL_FROM);
19 }
20 return new ConsoleMailer(env.MAIL_FROM);
21 }
22
23 function layout(heading: string, intro: string, ctaLabel: string, ctaUrl: string, footer: string): string {
24 return `<!doctype html>
25 <html><body style="margin:0;background:#0b0d12;font-family:-apple-system,Segoe UI,Roboto,sans-serif;color:#e6e8ee;padding:32px">
26 <div style="max-width:480px;margin:0 auto;background:#12151c;border:1px solid #242938;border-radius:14px;padding:28px">
27 <h1 style="margin:0 0 12px;font-size:18px">${heading}</h1>
28 <p style="margin:0 0 20px;line-height:1.6;color:#aeb4c2">${intro}</p>
29 <a href="${ctaUrl}" style="display:inline-block;background:#5b8cff;color:#fff;text-decoration:none;padding:11px 18px;border-radius:9px;font-weight:600">${ctaLabel}</a>
30 <p style="margin:20px 0 0;font-size:12px;color:#717892;word-break:break-all">${footer}</p>
31 </div>
32 </body></html>`;
33 }
34
35 export function verifyEmail(link: string): Omit<EmailMessage, "to"> {
36 return {
37 subject: "Confirm your Reasonix email",
38 text: `Welcome to Reasonix! Confirm your email to activate your account:\n\n${link}\n\nThis link expires in 24 hours. If you didn't sign up, ignore this email.`,
39 html: layout(
40 "Confirm your email",
41 "Welcome to Reasonix! Confirm your email address to activate your account. This link expires in 24 hours.",
42 "Confirm email",
43 link,
44 `Or paste this link: ${link}`,
45 ),
46 };
47 }
48
49 export function resetEmail(link: string): Omit<EmailMessage, "to"> {
50 return {
51 subject: "Reset your Reasonix password",
52 text: `Someone requested a password reset for your Reasonix account. Set a new password here:\n\n${link}\n\nThis link expires in 1 hour. If it wasn't you, you can safely ignore this email.`,
53 html: layout(
54 "Reset your password",
55 "Someone requested a password reset for your Reasonix account. This link expires in 1 hour. If it wasn't you, ignore this email.",
56 "Reset password",
57 link,
58 `Or paste this link: ${link}`,
59 ),
60 };
61 }
62
63 export function accountExistsEmail(loginUrl: string, resetUrl: string): Omit<EmailMessage, "to"> {
64 return {
65 subject: "You already have a Reasonix account",
66 text: `Someone tried to register with this email, but an account already exists.\n\nSign in: ${loginUrl}\nForgot your password? ${resetUrl}\n\nIf this wasn't you, no action is needed.`,
67 html: layout(
68 "You already have an account",
69 "Someone tried to register with this email, but an account already exists. You can sign in, or reset your password if you've forgotten it.",
70 "Sign in",
71 loginUrl,
72 `Forgot your password? ${resetUrl}`,
73 ),
74 };
75 }
76
76 lines TYPESCRIPT