panel 2 courier api restore

This commit is contained in:
alma 2025-04-26 10:28:20 +02:00
parent 42615957ec
commit 1dc167ec97
2 changed files with 74 additions and 60 deletions

View File

@ -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(/<style([^>]*)>([\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,

View File

@ -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');
}
const toDisplay = Array.isArray(initialEmail.to) && initialEmail.to.length > 0
? initialEmail.to.map(t => t.name ? `${t.name} <${t.address}>` : t.address).join(', ')
: '';
// Use DOMPurify to sanitize the HTML but preserve style tags
const htmlContent = data.html || data.text || '';
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(', ')
: '';
// 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
});
const dateDisplay = initialEmail.date
? initialEmail.date instanceof Date
? initialEmail.date.toLocaleString()
: new Date(initialEmail.date as any).toLocaleString()
: '';
processedContent = sanitizedContent;
} catch (error) {
console.error('Error parsing email content:', error);
processedContent = '<div style="color: #666; font-style: italic;">Error processing original content</div>';
}
} else {
processedContent = '<div style="color: #666; font-style: italic;">No content available</div>';
}
const subjectDisplay = initialEmail.subject || '(No subject)';
// Create a clean, well-formatted forwarded message with the original styles preserved
// Don't try to parse the content, just wrap it in a container
const contentDisplay = emailContent.trim()
? `<div class="forwarded-content">${emailContent}</div>`
: '<div style="color: #666; font-style: italic;">No content available</div>';
// Construct the forwarded message with direct header information
const forwardedContent = `
<div style="border-top: 1px solid #e5e7eb; padding-top: 20px; margin-top: 20px; color: #6b7280; font-size: 0.875rem;">
<div style="border-top: 1px solid #e5e7eb; padding-top: 20px; margin-top: 20px;">
<div style="color: #6b7280; font-size: 0.875rem; margin-bottom: 15px;">
---------- Forwarded message ---------<br/>
From: ${formatSender(initialEmail.from)}<br/>
Date: ${formatDate(initialEmail.date)}<br/>
Subject: ${initialEmail.subject || ''}<br/>
To: ${formatRecipients(initialEmail.to)}<br/>
${initialEmail.cc && initialEmail.cc.length ? `Cc: ${formatRecipients(initialEmail.cc)}<br/>` : ''}
</div>
<div style="margin-top: 10px;">
${processedContent}
</div>
`;
<strong>From:</strong> ${fromDisplay}<br/>
<strong>Date:</strong> ${dateDisplay}<br/>
<strong>Subject:</strong> ${subjectDisplay}<br/>
<strong>To:</strong> ${toDisplay}<br/>
${ccDisplay ? `<strong>Cc:</strong> ${ccDisplay}<br/>` : ''}
</div>
<div style="margin-top: 15px; border-left: 2px solid #e5e7eb; padding-left: 10px;">
${contentDisplay}
</div>
</div>`;
// Set the formatted content
setBody(forwardedContent);
} catch (error) {
console.error('Error initializing forwarded email:', error);
const errorContent = `
// Fallback content for error case
setBody(`
<div style="border-top: 1px solid #e5e7eb; padding-top: 20px; margin-top: 20px; color: #6b7280; font-size: 0.875rem;">
---------- Forwarded message ---------<br/>
From: ${initialEmail.from ? formatSender(initialEmail.from) : 'Unknown'}<br/>
Date: ${initialEmail.date ? formatDate(initialEmail.date) : ''}<br/>
Subject: ${initialEmail.subject || ''}<br/>
To: ${initialEmail.to ? formatRecipients(initialEmail.to) : ''}<br/>
<strong>From:</strong> ${initialEmail.from ? JSON.stringify(initialEmail.from) : 'Unknown'}<br/>
<strong>Date:</strong> ${initialEmail.date ? new Date(initialEmail.date as any).toLocaleString() : ''}<br/>
<strong>Subject:</strong> ${initialEmail.subject || ''}<br/>
<strong>To:</strong> ${initialEmail.to ? JSON.stringify(initialEmail.to) : ''}<br/>
</div>
<div style="color: #ef4444; font-style: italic; margin-top: 10px;">Error loading forwarded content</div>`;
setBody(errorContent);
<div style="color: #ef4444; font-style: italic; margin-top: 10px;">Error loading forwarded content</div>`);
} finally {
setSending(false);
}