TypeScript 97.4%
SQL 1%
JavaScript 0.9%
CSS 0.6%
1/**2 * Hand-written HTML emails (no React Email dependency): small, deterministic, and3 * renderable everywhere. Each template returns { subject, html, text }.4 */56export interface EmailContent {7 subject: string;8 html: string;9 text: string;10}1112const BRAND = {13 name: "PolyLLM",14 tagline: "Your models. Your keys. One workspace.",15 bg: "#0b0c10",16 card: "#12141a",17 fg: "#f4f5f7",18 muted: "#9aa3b2",19 accent: "#7c8cff",20 accentDark: "#5b6cf0",21 border: "#232734",22};2324function escapeHtml(s: string): string {25 return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);26}2728function layout(opts: { siteUrl: string; preheader: string; title: string; body: string; cta?: { label: string; url: string }; footnote?: string }): string {29 const cta = opts.cta30 ? `<table role="presentation" cellspacing="0" cellpadding="0" style="margin:28px 0 8px"><tr><td style="border-radius:10px;background:${BRAND.accent}">31 <a href="${opts.cta.url}" style="display:inline-block;padding:13px 22px;font:600 15px/1 -apple-system,BlinkMacSystemFont,'Segoe UI',Inter,Roboto,sans-serif;color:#0b0c10;text-decoration:none;border-radius:10px">${escapeHtml(opts.cta.label)}</a>32 </td></tr></table>33 <p style="margin:12px 0 0;font:13px/1.6 -apple-system,BlinkMacSystemFont,'Segoe UI',Inter,Roboto,sans-serif;color:${BRAND.muted}">Or paste this link in your browser:<br><a href="${opts.cta.url}" style="color:${BRAND.accent};word-break:break-all">${opts.cta.url}</a></p>`34 : "";35 return `<!doctype html>36<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>${escapeHtml(opts.title)}</title>37<meta name="color-scheme" content="dark light"><meta name="supported-color-schemes" content="dark light"></head>38<body style="margin:0;padding:0;background:${BRAND.bg}">39<span style="display:none!important;visibility:hidden;opacity:0;color:transparent;height:0;width:0;overflow:hidden">${escapeHtml(opts.preheader)}</span>40<table role="presentation" width="100%" cellspacing="0" cellpadding="0" style="background:${BRAND.bg};padding:40px 16px">41<tr><td align="center">42<table role="presentation" width="100%" cellspacing="0" cellpadding="0" style="max-width:560px">43 <tr><td style="padding:0 8px 20px">44 <a href="${opts.siteUrl}" style="text-decoration:none;display:inline-flex;align-items:center;gap:10px">45 <span style="display:inline-block;width:28px;height:28px;border-radius:8px;background:linear-gradient(135deg,${BRAND.accent},#b58cff);vertical-align:middle"></span>46 <span style="font:700 18px/28px -apple-system,BlinkMacSystemFont,'Segoe UI',Inter,Roboto,sans-serif;color:${BRAND.fg};vertical-align:middle;margin-left:10px">PolyLLM</span>47 </a>48 </td></tr>49 <tr><td style="background:${BRAND.card};border:1px solid ${BRAND.border};border-radius:16px;padding:36px 32px">50 <h1 style="margin:0 0 16px;font:700 22px/1.3 -apple-system,BlinkMacSystemFont,'Segoe UI',Inter,Roboto,sans-serif;color:${BRAND.fg};letter-spacing:-0.01em">${escapeHtml(opts.title)}</h1>51 <div style="font:15px/1.65 -apple-system,BlinkMacSystemFont,'Segoe UI',Inter,Roboto,sans-serif;color:${BRAND.fg}">${opts.body}</div>52 ${cta}53 ${opts.footnote ? `<p style="margin:24px 0 0;padding-top:20px;border-top:1px solid ${BRAND.border};font:13px/1.6 -apple-system,BlinkMacSystemFont,'Segoe UI',Inter,Roboto,sans-serif;color:${BRAND.muted}">${opts.footnote}</p>` : ""}54 </td></tr>55 <tr><td style="padding:20px 8px 0;font:12px/1.6 -apple-system,BlinkMacSystemFont,'Segoe UI',Inter,Roboto,sans-serif;color:${BRAND.muted}">56 ${BRAND.name} — ${BRAND.tagline}<br>57 <a href="${opts.siteUrl}" style="color:${BRAND.muted}">${opts.siteUrl.replace(/^https?:\/\//, "")}</a>58 </td></tr>59</table></td></tr></table></body></html>`;60}6162function p(text: string) {63 return `<p style="margin:0 0 14px">${text}</p>`;64}6566const textFooter = (siteUrl: string) => `\n\n—\n${BRAND.name} — ${BRAND.tagline}\n${siteUrl}\n`;6768export function verificationEmail(siteUrl: string, url: string): EmailContent {69 return {70 subject: "Verify your email — PolyLLM",71 html: layout({72 siteUrl,73 preheader: "Confirm your email address to activate your PolyLLM account.",74 title: "Confirm your email",75 body: p("Welcome to PolyLLM. Click the button below to verify your email address and activate your account.") + p("This link expires in 24 hours."),76 cta: { label: "Verify email", url },77 footnote: "If you didn't create a PolyLLM account, you can safely ignore this email.",78 }),79 text: `Confirm your email\n\nWelcome to PolyLLM. Open the link below to verify your email address and activate your account (expires in 24 hours):\n\n${url}\n\nIf you didn't create a PolyLLM account, you can ignore this email.${textFooter(siteUrl)}`,80 };81}8283export function welcomeEmail(siteUrl: string, name: string): EmailContent {84 const hi = name ? `Hi ${escapeHtml(name)},` : "Hi,";85 return {86 subject: "Welcome to PolyLLM",87 html: layout({88 siteUrl,89 preheader: "Your account is verified. Connect a provider to start chatting.",90 title: "Welcome to PolyLLM",91 body:92 p(hi) +93 p("Your email is verified. PolyLLM is a universal control center for AI models: connect your own API keys, discover the latest models, configure them precisely, and chat with every provider through one interface.") +94 p("<strong>Next step:</strong> connect at least one provider (OpenAI, Anthropic, Gemini, xAI, Mistral, DeepSeek, Kimi, OpenRouter or Cerebras). Your keys are encrypted at rest and only decrypted on the server immediately before a request."),95 cta: { label: "Connect a provider", url: `${siteUrl}/app/settings/providers` },96 }),97 text: `${name ? `Hi ${name},` : "Hi,"}\n\nYour email is verified. Connect at least one provider to start chatting:\n${siteUrl}/app/settings/providers${textFooter(siteUrl)}`,98 };99}100101export function passwordResetEmail(siteUrl: string, url: string): EmailContent {102 return {103 subject: "Reset your PolyLLM password",104 html: layout({105 siteUrl,106 preheader: "Use this link to choose a new password.",107 title: "Reset your password",108 body: p("We received a request to reset the password of your PolyLLM account. Click below to choose a new one.") + p("This link expires in 1 hour and can be used once."),109 cta: { label: "Choose a new password", url },110 footnote: "If you didn't request a password reset, no action is needed — your password stays unchanged.",111 }),112 text: `Reset your password\n\nOpen this link to choose a new password (expires in 1 hour):\n\n${url}\n\nIf you didn't request this, ignore this email.${textFooter(siteUrl)}`,113 };114}115116export function passwordChangedEmail(siteUrl: string, when: string, ip?: string | null): EmailContent {117 const where = ip ? ` from IP ${escapeHtml(ip)}` : "";118 return {119 subject: "Your PolyLLM password was changed",120 html: layout({121 siteUrl,122 preheader: "Security notice: your password was changed.",123 title: "Password changed",124 body: p(`The password of your PolyLLM account was changed on ${escapeHtml(when)}${where}.`) + p("If this was you, no action is needed. Other sessions have been signed out."),125 cta: { label: "Review security settings", url: `${siteUrl}/app/settings/security` },126 footnote: "If you did not make this change, reset your password immediately and review your active sessions.",127 }),128 text: `Password changed\n\nThe password of your PolyLLM account was changed on ${when}${ip ? ` from IP ${ip}` : ""}. If this wasn't you, reset your password immediately: ${siteUrl}/forgot-password${textFooter(siteUrl)}`,129 };130}131132export function emailChangeApprovalEmail(siteUrl: string, url: string, newEmail: string): EmailContent {133 return {134 subject: "Approve your email change — PolyLLM",135 html: layout({136 siteUrl,137 preheader: "Confirm that you want to change the email on your account.",138 title: "Approve email change",139 body: p(`You asked to change the email of your PolyLLM account to <strong>${escapeHtml(newEmail)}</strong>.`) + p("Confirm this change from your current address by clicking below."),140 cta: { label: "Approve change", url },141 footnote: "If you didn't request this, ignore this email — nothing changes without your approval.",142 }),143 text: `Approve email change\n\nYou asked to change your PolyLLM email to ${newEmail}. Confirm here:\n\n${url}${textFooter(siteUrl)}`,144 };145}146147export function emailChangedEmail(siteUrl: string, newEmail: string): EmailContent {148 return {149 subject: "Your PolyLLM email was changed",150 html: layout({151 siteUrl,152 preheader: "Security notice: the email on your account changed.",153 title: "Email changed",154 body: p(`The email address of your PolyLLM account is now <strong>${escapeHtml(newEmail)}</strong>.`),155 footnote: "If you did not make this change, contact us immediately by replying to this email.",156 }),157 text: `Email changed\n\nThe email of your PolyLLM account is now ${newEmail}. If this wasn't you, reply to this email immediately.${textFooter(siteUrl)}`,158 };159}160161export function accountDeletionEmail(siteUrl: string, url: string): EmailContent {162 return {163 subject: "Confirm account deletion — PolyLLM",164 html: layout({165 siteUrl,166 preheader: "Confirm that you want to permanently delete your account.",167 title: "Delete your account?",168 body: p("You asked to permanently delete your PolyLLM account, including your conversations, presets, usage history and encrypted provider keys.") + p("<strong>This cannot be undone.</strong> Confirm by clicking below."),169 cta: { label: "Permanently delete my account", url },170 footnote: "If you didn't request this, ignore this email — your account stays untouched.",171 }),172 text: `Delete your account?\n\nConfirm permanent deletion of your PolyLLM account (irreversible):\n\n${url}${textFooter(siteUrl)}`,173 };174}175176export function newLoginEmail(siteUrl: string, when: string, ip?: string | null, userAgent?: string | null): EmailContent {177 return {178 subject: "New sign-in to PolyLLM",179 html: layout({180 siteUrl,181 preheader: "A new sign-in to your account was detected.",182 title: "New sign-in detected",183 body:184 p(`Your PolyLLM account was signed in on ${escapeHtml(when)}.`) +185 `<ul style="margin:0 0 14px;padding-left:18px">${ip ? `<li>IP address: ${escapeHtml(ip)}</li>` : ""}${userAgent ? `<li>Device: ${escapeHtml(userAgent.slice(0, 120))}</li>` : ""}</ul>` +186 p("If this was you, no action is needed."),187 cta: { label: "Review sessions", url: `${siteUrl}/app/settings/security` },188 footnote: "If you don't recognize this sign-in, change your password and sign out of all devices.",189 }),190 text: `New sign-in detected on ${when}${ip ? ` from ${ip}` : ""}. Review your sessions: ${siteUrl}/app/settings/security${textFooter(siteUrl)}`,191 };192}193