From d0c949070f649591ee666b797e7ac7201ea26f77 Mon Sep 17 00:00:00 2001 From: nmemmert Date: Tue, 28 Jul 2026 12:28:21 -0400 Subject: [PATCH] Fix base64-encoded HTML email bodies not rendering; v1.1.18 Inbound emails from some clients (Outlook, mobile) send htmlBody as base64. Decode it on the server before storing (future emails) and on the client before passing to the iframe srcDoc (existing stored emails). Co-Authored-By: Claude Sonnet 4.6 --- package.json | 2 +- server/routes/inbound-email.js | 14 +++++++++++++- src/EmailPage.tsx | 7 ++++++- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 1f2e0c0..610c51c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "siteforge", "private": true, - "version": "1.1.17", + "version": "1.1.18", "type": "module", "scripts": { "dev": "vite", diff --git a/server/routes/inbound-email.js b/server/routes/inbound-email.js index f8efe93..5bf9d6d 100644 --- a/server/routes/inbound-email.js +++ b/server/routes/inbound-email.js @@ -6,6 +6,18 @@ import { MAX_CONTACT_SUBMISSIONS } from '../config.js' // RFC 5322 msg-id: "<" printable-ASCII-no-whitespace ">" const MESSAGE_ID_RE = /^<[\x21-\x7E]+>$/ +function decodeHtmlBody(raw) { + if (typeof raw !== 'string' || !raw.trim()) return null + const s = raw.trim() + if (s.startsWith('<')) return s // already plain HTML + try { + const decoded = Buffer.from(s.replace(/\s+/g, ''), 'base64').toString('utf8') + return decoded.trimStart().startsWith('<') ? decoded : s + } catch { + return s + } +} + export function register(app) { app.post('/api/inbound-email', (req, res) => { const secret = process.env.INBOUND_EMAIL_SECRET @@ -47,7 +59,7 @@ export function register(app) { name: fromName || fromEmail, email: fromEmail, message: [subject ? `Subject: ${subject}` : '', body ?? ''].filter(Boolean).join('\n\n'), - htmlBody: typeof htmlBody === 'string' && htmlBody.trim() ? htmlBody.trim() : null, + htmlBody: decodeHtmlBody(htmlBody), messageType: 'general', subscribe: false, archived: false, diff --git a/src/EmailPage.tsx b/src/EmailPage.tsx index f922059..68e5999 100644 --- a/src/EmailPage.tsx +++ b/src/EmailPage.tsx @@ -82,6 +82,11 @@ function formatDate(value: string | null | undefined): string { return Number.isNaN(d.getTime()) ? '—' : d.toLocaleString() } +function decodeHtmlBody(raw: string): string { + if (!raw || raw.trimStart().startsWith('<')) return raw + try { return atob(raw.replace(/\s+/g, '')) } catch { return raw } +} + function formatShortDate(value: string | null | undefined): string { if (!value) return '—' const d = new Date(value) @@ -671,7 +676,7 @@ function EmailClient({ onLogout }: { onLogout: () => void }) {
{selected.htmlBody ? (