Files
recap/server/license-ready-email.js
T
Keysat 0ae59f3550 Add multi-tenant cloud mode: self-serve purchase, credit metering, core-decoupling
Introduces RECAP_MODE=multi alongside single-mode self-host:
- Tenant auth + accounts (magic-link via System SMTP), per-tenant credit pool,
  anonymous trial minting with per-IP/-64 caps
- Self-serve Pro/Max purchase: inline Lightning (BTCPay) + card (Zaprite),
  prepaid 30-day periods, expiry-reminder emails
- Core-decoupling: relay owns cloud tier/expiry keyed by Recaps user-id
- SQLite (better-sqlite3) schema for multi-mode; filesystem unchanged for single
- StartOS actions/versions through 0.2.155
2026-06-13 14:25:05 -05:00

94 lines
3.8 KiB
JavaScript

// "Your Recap [Pro|Max] account is ready" email — sent after an
// anon buyer's BTCPay invoice settles. Distinct from the standard
// sign-in magic-link email because the framing is celebratory ("your
// purchase is confirmed, click here to access") rather than the
// transactional "click here to sign in." Both share the underlying
// magic-link mechanism — only the copy and subject differ.
//
// Returns { subject, text, html } in the nodemailer shape.
export function renderLicenseReadyEmail({
verifyUrl,
tierLabel = "Pro",
brandName = "Recaps",
expiresInMinutes = 15,
}) {
const subject = `Your ${brandName} ${tierLabel} account is ready`;
const text = [
`Thanks for upgrading to ${brandName} ${tierLabel} — your payment is confirmed.`,
"",
`Click the link below to sign in to your new account. ${tierLabel} is already active and your license is attached.`,
"",
verifyUrl,
"",
`This link expires in ${expiresInMinutes} minutes and can only be used once. If it expires, just go back to ${brandName} and request a fresh sign-in link from the same email.`,
"",
"Welcome aboard.",
].join("\n");
const html = `<!doctype html>
<html>
<body style="margin:0;padding:0;background:#fafafa;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:#fafafa;padding:32px 0;">
<tr>
<td align="center">
<table role="presentation" width="480" cellpadding="0" cellspacing="0" style="background:#ffffff;border-radius:8px;padding:32px;max-width:90%;">
<tr>
<td style="font-size:18px;font-weight:600;color:#111;padding-bottom:8px;">
Your ${escapeHtml(brandName)} ${escapeHtml(tierLabel)} account is ready
</td>
</tr>
<tr>
<td style="font-size:14px;line-height:1.55;color:#444;padding-bottom:8px;">
Thanks for upgrading — your payment is confirmed and your <strong>${escapeHtml(tierLabel)}</strong> license is attached to your new account.
</td>
</tr>
<tr>
<td style="font-size:14px;line-height:1.55;color:#444;padding-bottom:24px;">
Click the button below to sign in. The link expires in ${expiresInMinutes} minutes.
</td>
</tr>
<tr>
<td align="center" style="padding-bottom:24px;">
<a href="${escapeAttr(verifyUrl)}" style="display:inline-block;background:#3b82f6;color:#fff;text-decoration:none;font-size:15px;font-weight:600;padding:12px 24px;border-radius:6px;">Sign in to ${escapeHtml(brandName)}</a>
</td>
</tr>
<tr>
<td style="font-size:13px;line-height:1.5;color:#888;padding-bottom:8px;">
Or copy and paste this URL into your browser:
</td>
</tr>
<tr>
<td style="font-size:12px;color:#888;word-break:break-all;padding-bottom:24px;">
${escapeHtml(verifyUrl)}
</td>
</tr>
<tr>
<td style="font-size:12px;line-height:1.5;color:#888;border-top:1px solid #eee;padding-top:16px;">
If the link expires before you click it, you can request a fresh sign-in link from ${escapeHtml(brandName)} using this same email. Your ${escapeHtml(tierLabel)} license will already be on the account when you sign in.
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>`;
return { subject, text, html };
}
function escapeHtml(s) {
return String(s)
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#39;");
}
function escapeAttr(s) {
return escapeHtml(s);
}