mirror of
https://github.com/Since-3/Kuma.git
synced 2026-10-12 19:51:17 +00:00
100 lines
3.9 KiB
TypeScript
100 lines
3.9 KiB
TypeScript
/**
|
|
* Gemeinsame HTML-Shell für alle E-Mail-Templates.
|
|
*
|
|
* Tabellen-basiertes Layout mit ausschließlich inline-Styles (Outlook-Kompatibilität,
|
|
* kein <style>-Block, kein Flexbox/Grid, keine CSS-Gradients).
|
|
*/
|
|
|
|
import { escapeHtml, sanitizeColor, sanitizeUrl } from "./utils";
|
|
import { EMAIL_TOKENS, KUMASY_BRANDING, type EmailBranding } from "./tokens";
|
|
|
|
export interface RenderEmailLayoutParams {
|
|
title: string;
|
|
/** H1-Text im Header-Band. Vom Aufrufer bereits escaped/vertrauenswürdig. */
|
|
headerHeading: string;
|
|
/** Optionale volle Reihe ÜBER dem Header-Band (z.B. Cover-Bild). */
|
|
bannerHtml?: string;
|
|
/** Fertiger Inner-Content, wird unverändert in die Content-Zelle eingesetzt. */
|
|
bodyHtml: string;
|
|
branding?: EmailBranding;
|
|
/**
|
|
* Ersetzt den Hinweis "automatisch generiert, bitte nicht antworten" im
|
|
* Footer - z.B. bei Werbe-Mails, auf die Kunden antworten dürfen und die
|
|
* einen Abmeldelink brauchen. Vom Aufrufer bereits escaped.
|
|
*/
|
|
footerNoteHtml?: string;
|
|
}
|
|
|
|
export function renderEmailLayout({
|
|
title,
|
|
headerHeading,
|
|
bannerHtml,
|
|
bodyHtml,
|
|
branding = KUMASY_BRANDING,
|
|
footerNoteHtml,
|
|
}: RenderEmailLayoutParams): string {
|
|
const safeTitle = escapeHtml(title);
|
|
const safeCompanyName = escapeHtml(branding.companyName);
|
|
const safeLogoAlt = escapeHtml(branding.logoAlt);
|
|
const safePrimaryColor = sanitizeColor(branding.primaryColor, KUMASY_BRANDING.primaryColor);
|
|
|
|
const headerLogoHtml = branding.logoUrl
|
|
? `<img src="${sanitizeUrl(branding.logoUrl)}" alt="${safeLogoAlt}" width="160" style="display: block; margin: 0 auto 16px; max-width: 160px; height: auto;" />`
|
|
: `<p style="margin: 0 0 12px; color: #ffffff; font-size: 15px; font-weight: 700; letter-spacing: 0.5px; text-transform: uppercase;">${safeCompanyName}</p>`;
|
|
|
|
return `
|
|
<!DOCTYPE html>
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>${safeTitle}</title>
|
|
</head>
|
|
<body style="margin: 0; padding: 0; font-family: ${EMAIL_TOKENS.fontFamily}; background-color: ${EMAIL_TOKENS.colors.bodyBackground};">
|
|
<table width="100%" cellpadding="0" cellspacing="0" style="background-color: ${EMAIL_TOKENS.colors.bodyBackground}; padding: ${EMAIL_TOKENS.spacing.pagePadding}px 0;">
|
|
<tr>
|
|
<td align="center">
|
|
<table width="600" cellpadding="0" cellspacing="0" style="background-color: ${EMAIL_TOKENS.colors.cardBackground}; border-radius: ${EMAIL_TOKENS.radius.card}; overflow: hidden; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
|
|
|
|
${bannerHtml ?? ""}
|
|
|
|
<!-- Header -->
|
|
<tr>
|
|
<td style="background-color: ${safePrimaryColor}; padding: 40px; text-align: center;">
|
|
${headerLogoHtml}
|
|
<h1 style="margin: 0; color: #ffffff; font-size: 28px; font-weight: 600;">
|
|
${headerHeading}
|
|
</h1>
|
|
</td>
|
|
</tr>
|
|
|
|
<!-- Content -->
|
|
<tr>
|
|
<td style="padding: ${EMAIL_TOKENS.spacing.cardPadding}px;">
|
|
${bodyHtml}
|
|
</td>
|
|
</tr>
|
|
|
|
<!-- Footer -->
|
|
<tr>
|
|
<td style="background-color: ${EMAIL_TOKENS.colors.footerBackground}; padding: ${EMAIL_TOKENS.spacing.footerPadding}px; text-align: center; border-top: 1px solid ${EMAIL_TOKENS.colors.border};">
|
|
<p style="margin: 0 0 10px; color: ${EMAIL_TOKENS.colors.textSecondary}; font-size: 14px;">
|
|
${safeCompanyName}
|
|
</p>
|
|
<p style="margin: 0 0 10px; color: ${EMAIL_TOKENS.colors.textMuted}; font-size: 12px;">
|
|
${footerNoteHtml ?? "Diese E-Mail wurde automatisch generiert. Bitte antworten Sie nicht auf diese E-Mail."}
|
|
</p>
|
|
<p style="margin: 0; color: ${EMAIL_TOKENS.colors.textMuted}; font-size: 11px;">
|
|
Powered by Kumasy
|
|
</p>
|
|
</td>
|
|
</tr>
|
|
|
|
</table>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</body>
|
|
</html>
|
|
`;
|
|
}
|