diff --git a/app/api/parse-email/route.ts b/app/api/parse-email/route.ts index ad03828d..de4f7b0a 100644 --- a/app/api/parse-email/route.ts +++ b/app/api/parse-email/route.ts @@ -9,6 +9,26 @@ function getEmailAddress(address: AddressObject | AddressObject[] | undefined): return address.text; } +// Clean up the HTML to make it safe but preserve styles +function processHtml(html: string | null): string | null { + if (!html) return null; + + try { + // Make the content display well in the email context + return html + // Fix self-closing tags that might break React + .replace(/<(br|hr|img|input|link|meta|area|base|col|embed|keygen|param|source|track|wbr)([^>]*)>/gi, '<$1$2 />') + // Keep style tags but ensure they're closed properly + .replace(/]*)>([\s\S]*?)<\/style>/gi, (match) => { + // Just return the matched style tag as-is + return match; + }); + } catch (error) { + console.error('Error processing HTML:', error); + return html; + } +} + export async function POST(request: Request) { try { const body = await request.json(); @@ -23,6 +43,10 @@ export async function POST(request: Request) { const parsed = await simpleParser(email); + // Process the HTML to preserve styling but make it safe + // Handle the case where parsed.html could be a boolean + const processedHtml = typeof parsed.html === 'string' ? processHtml(parsed.html) : null; + return NextResponse.json({ subject: parsed.subject || null, from: getEmailAddress(parsed.from), @@ -30,7 +54,7 @@ export async function POST(request: Request) { cc: getEmailAddress(parsed.cc), bcc: getEmailAddress(parsed.bcc), date: parsed.date || null, - html: parsed.html || null, + html: processedHtml, text: parsed.textAsHtml || parsed.text || null, attachments: parsed.attachments?.map(att => ({ filename: att.filename, diff --git a/components/email/ComposeEmail.tsx b/components/email/ComposeEmail.tsx index da7006a4..d3b168fe 100644 --- a/components/email/ComposeEmail.tsx +++ b/components/email/ComposeEmail.tsx @@ -1,7 +1,7 @@ 'use client'; import { useState, useRef, useEffect } from 'react'; -import { formatEmailForReplyOrForward, EmailMessage } from '@/lib/services/email-service'; +import { formatEmailForReplyOrForward, EmailMessage, EmailAddress } from '@/lib/services/email-service'; import { X, Paperclip, ChevronDown, ChevronUp, SendHorizontal, Loader2 } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; @@ -139,82 +139,72 @@ export default function ComposeEmail({ setSending(true); // Use sending state to show loading // Format subject with Fwd: prefix if needed - const cleanSubject = initialEmail.subject.replace(/^(Fwd|FW|Forward):\s*/i, '').trim(); - const formattedSubject = initialEmail.subject.match(/^(Fwd|FW|Forward):/i) + const cleanSubject = initialEmail.subject?.replace(/^(Fwd|FW|Forward):\s*/i, '').trim() || ''; + const formattedSubject = initialEmail.subject?.match(/^(Fwd|FW|Forward):/i) ? initialEmail.subject : `Fwd: ${cleanSubject}`; setSubject(formattedSubject); - // Process the email content + // Store the email content directly without complex parsing const emailContent = initialEmail.content || ''; - let processedContent = ''; - // Only attempt to parse if we have content - if (emailContent.trim()) { - try { - // Parse email on the server - const response = await fetch('/api/parse-email', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ email: emailContent }), - }); + // Create a forwarding header with proper types + const fromDisplay = Array.isArray(initialEmail.from) && initialEmail.from.length > 0 + ? initialEmail.from.map(f => f.name ? `${f.name} <${f.address}>` : f.address).join(', ') + : 'Unknown Sender'; - const data = await response.json(); - if (!response.ok) { - throw new Error(data.error || 'Failed to parse email'); - } - - // Use DOMPurify to sanitize the HTML but preserve style tags - const htmlContent = data.html || data.text || ''; - - // Configure DOMPurify to keep certain tags including style - const sanitizedContent = DOMPurify.sanitize(htmlContent, { - ADD_TAGS: ['style', 'meta', 'link'], - ADD_ATTR: ['id', 'class', 'style'], - WHOLE_DOCUMENT: false - }); - - processedContent = sanitizedContent; - } catch (error) { - console.error('Error parsing email content:', error); - processedContent = '
Error processing original content
'; - } - } else { - processedContent = '
No content available
'; - } + const toDisplay = Array.isArray(initialEmail.to) && initialEmail.to.length > 0 + ? initialEmail.to.map(t => t.name ? `${t.name} <${t.address}>` : t.address).join(', ') + : ''; - // Create a clean, well-formatted forwarded message with the original styles preserved + const ccDisplay = initialEmail.cc && Array.isArray(initialEmail.cc) && initialEmail.cc.length > 0 + ? initialEmail.cc.map(c => c.name ? `${c.name} <${c.address}>` : c.address).join(', ') + : ''; + + const dateDisplay = initialEmail.date + ? initialEmail.date instanceof Date + ? initialEmail.date.toLocaleString() + : new Date(initialEmail.date as any).toLocaleString() + : ''; + + const subjectDisplay = initialEmail.subject || '(No subject)'; + + // Don't try to parse the content, just wrap it in a container + const contentDisplay = emailContent.trim() + ? `
${emailContent}
` + : '
No content available
'; + + // Construct the forwarded message with direct header information const forwardedContent = ` -
- ---------- Forwarded message ---------
- From: ${formatSender(initialEmail.from)}
- Date: ${formatDate(initialEmail.date)}
- Subject: ${initialEmail.subject || ''}
- To: ${formatRecipients(initialEmail.to)}
- ${initialEmail.cc && initialEmail.cc.length ? `Cc: ${formatRecipients(initialEmail.cc)}
` : ''} -
-
- ${processedContent} -
-`; +
+
+ ---------- Forwarded message ---------
+ From: ${fromDisplay}
+ Date: ${dateDisplay}
+ Subject: ${subjectDisplay}
+ To: ${toDisplay}
+ ${ccDisplay ? `Cc: ${ccDisplay}
` : ''} +
+
+ ${contentDisplay} +
+
`; // Set the formatted content setBody(forwardedContent); } catch (error) { console.error('Error initializing forwarded email:', error); - const errorContent = ` + // Fallback content for error case + setBody(`
---------- Forwarded message ---------
- From: ${initialEmail.from ? formatSender(initialEmail.from) : 'Unknown'}
- Date: ${initialEmail.date ? formatDate(initialEmail.date) : ''}
- Subject: ${initialEmail.subject || ''}
- To: ${initialEmail.to ? formatRecipients(initialEmail.to) : ''}
+ From: ${initialEmail.from ? JSON.stringify(initialEmail.from) : 'Unknown'}
+ Date: ${initialEmail.date ? new Date(initialEmail.date as any).toLocaleString() : ''}
+ Subject: ${initialEmail.subject || ''}
+ To: ${initialEmail.to ? JSON.stringify(initialEmail.to) : ''}
-
Error loading forwarded content
`; - setBody(errorContent); +
Error loading forwarded content
`); } finally { setSending(false); }