panel 2 courier api restore
This commit is contained in:
parent
c81a74aaec
commit
fb36344577
@ -7,6 +7,7 @@ import { Button } from '@/components/ui/button';
|
|||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { Card, CardContent, CardHeader, CardTitle, CardFooter } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle, CardFooter } from '@/components/ui/card';
|
||||||
import { decodeEmail } from '@/lib/mail-parser-wrapper';
|
import { decodeEmail } from '@/lib/mail-parser-wrapper';
|
||||||
|
import DOMPurify from 'isomorphic-dompurify';
|
||||||
|
|
||||||
interface ComposeEmailProps {
|
interface ComposeEmailProps {
|
||||||
initialEmail?: EmailMessage | null;
|
initialEmail?: EmailMessage | null;
|
||||||
@ -94,9 +95,9 @@ export default function ComposeEmail({
|
|||||||
|
|
||||||
// New function to initialize forwarded email using same approach as Panel 3
|
// New function to initialize forwarded email using same approach as Panel 3
|
||||||
const initializeForwardedEmail = async () => {
|
const initializeForwardedEmail = async () => {
|
||||||
if (!initialEmail || !initialEmail.content) {
|
if (!initialEmail) {
|
||||||
console.error('No email content available for forwarding');
|
console.error('No email available for forwarding');
|
||||||
setBody('<div>No content available</div>');
|
setBody('<div style="color: #666; font-style: italic;">No email available for forwarding</div>');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,25 +129,65 @@ export default function ComposeEmail({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create a simple forwarded message header with proper formatting
|
// Create a forwarded message header with proper formatting
|
||||||
const headerContent = `
|
const headerContent = `
|
||||||
<div>
|
<div style="border-bottom: 1px solid #e2e2e2; margin-bottom: 15px; padding-bottom: 15px; font-family: Arial, sans-serif; color: #333;">
|
||||||
<br>
|
<p style="margin: 5px 0; font-size: 14px;">---------- Forwarded message ---------</p>
|
||||||
---------- Forwarded message ---------<br>
|
<p style="margin: 5px 0; font-size: 14px;"><b>From:</b> ${initialEmail.from || ''}</p>
|
||||||
From: ${initialEmail.from || ''}<br>
|
<p style="margin: 5px 0; font-size: 14px;"><b>Date:</b> ${formatDate(initialEmail.date)}</p>
|
||||||
Date: ${formatDate(initialEmail.date)}<br>
|
<p style="margin: 5px 0; font-size: 14px;"><b>Subject:</b> ${initialEmail.subject || ''}</p>
|
||||||
Subject: ${initialEmail.subject || ''}<br>
|
<p style="margin: 5px 0; font-size: 14px;"><b>To:</b> ${initialEmail.to || ''}</p>
|
||||||
To: ${initialEmail.to || ''}<br>
|
</div>`;
|
||||||
<br>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
// Instead of trying to parse and clean the HTML, directly use the raw content
|
// Process content based on its type
|
||||||
// This preserves all original formatting, CSS, and HTML structure
|
let contentBody = '';
|
||||||
setBody(headerContent + initialEmail.content);
|
|
||||||
|
// Check if email content exists
|
||||||
|
if (!initialEmail.content || initialEmail.content.trim() === '') {
|
||||||
|
contentBody = '<div style="color: #666; font-style: italic; margin-top: 10px;">No content available</div>';
|
||||||
|
} else if (initialEmail.content.trim().startsWith('<') && initialEmail.content.includes('</')) {
|
||||||
|
// It's probably HTML, sanitize it before using
|
||||||
|
try {
|
||||||
|
// Use DOMPurify to sanitize HTML content
|
||||||
|
const sanitizedContent = DOMPurify.sanitize(initialEmail.content, {
|
||||||
|
ADD_TAGS: ['style', 'meta', 'link', 'table', 'thead', 'tbody', 'tr', 'td', 'th', 'hr', 'font', 'div', 'span', 'a', 'img', 'b', 'strong', 'i', 'em', 'u', 'br', 'p', 'ul', 'ol', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'pre', 'code', 'center', 'section', 'header', 'footer', 'article', 'nav'],
|
||||||
|
ADD_ATTR: ['colspan', 'rowspan', 'cellpadding', 'cellspacing', 'border', 'bgcolor', 'width', 'height', 'align', 'valign', 'class', 'style', 'color', 'face', 'size', 'background', 'src', 'href', 'target', 'rel', 'alt', 'title'],
|
||||||
|
ALLOW_UNKNOWN_PROTOCOLS: true,
|
||||||
|
WHOLE_DOCUMENT: false,
|
||||||
|
RETURN_DOM: false,
|
||||||
|
FORBID_TAGS: ['script', 'iframe', 'object', 'embed', 'form', 'input', 'button', 'select', 'option', 'textarea', 'canvas', 'video', 'audio'],
|
||||||
|
FORBID_ATTR: ['onerror', 'onload', 'onclick', 'onmouseover', 'onmouseout', 'onchange', 'onsubmit'],
|
||||||
|
});
|
||||||
|
contentBody = sanitizedContent;
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error sanitizing HTML content:', e);
|
||||||
|
contentBody = '<div style="color: #666; font-style: italic; margin-top: 10px;">Error processing original content</div>';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// It's plain text, convert newlines to <br> tags and wrap in a div
|
||||||
|
contentBody = `<div style="font-family: Arial, sans-serif; white-space: pre-wrap;">${initialEmail.content.replace(/\n/g, '<br>')}</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If content seems empty or invalid after processing, provide a fallback
|
||||||
|
if (!contentBody.trim() || contentBody.trim() === '<div></div>') {
|
||||||
|
contentBody = '<div style="color: #666; font-style: italic; margin-top: 10px;">No content available</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the complete forwarded email
|
||||||
|
setBody(headerContent + contentBody);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error initializing forwarded email:', error);
|
console.error('Error initializing forwarded email:', error);
|
||||||
setBody('<div>Error loading forwarded content</div>');
|
// Still provide the headers even if there's an error with the content
|
||||||
|
const errorHeaderContent = `
|
||||||
|
<div style="border-bottom: 1px solid #e2e2e2; margin-bottom: 15px; padding-bottom: 15px; font-family: Arial, sans-serif; color: #333;">
|
||||||
|
<p style="margin: 5px 0; font-size: 14px;">---------- Forwarded message ---------</p>
|
||||||
|
<p style="margin: 5px 0; font-size: 14px;"><b>From:</b> ${initialEmail.from || ''}</p>
|
||||||
|
<p style="margin: 5px 0; font-size: 14px;"><b>Date:</b> ${formatDate(initialEmail.date)}</p>
|
||||||
|
<p style="margin: 5px 0; font-size: 14px;"><b>Subject:</b> ${initialEmail.subject || ''}</p>
|
||||||
|
<p style="margin: 5px 0; font-size: 14px;"><b>To:</b> ${initialEmail.to || ''}</p>
|
||||||
|
</div>
|
||||||
|
<div style="color: #ef4444; font-style: italic; margin-top: 10px;">Error loading forwarded content</div>`;
|
||||||
|
setBody(errorHeaderContent);
|
||||||
} finally {
|
} finally {
|
||||||
setSending(false);
|
setSending(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { EmailMessage } from '@/lib/services/email-service';
|
|||||||
import { Loader2, Paperclip, Download } from 'lucide-react';
|
import { Loader2, Paperclip, Download } from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
|
import { cleanHtml } from '@/lib/mail-parser-wrapper';
|
||||||
|
|
||||||
interface EmailPreviewProps {
|
interface EmailPreviewProps {
|
||||||
email: EmailMessage | null;
|
email: EmailMessage | null;
|
||||||
@ -20,18 +21,31 @@ export default function EmailPreview({ email, loading = false, onReply }: EmailP
|
|||||||
const renderContent = () => {
|
const renderContent = () => {
|
||||||
if (!email?.content) return <p>No content available</p>;
|
if (!email?.content) return <p>No content available</p>;
|
||||||
|
|
||||||
// Sanitize HTML content
|
try {
|
||||||
const sanitizedContent = DOMPurify.sanitize(email.content, {
|
// Use DOMPurify directly with enhanced sanitization options
|
||||||
ADD_TAGS: ['style', 'table', 'thead', 'tbody', 'tr', 'td', 'th'],
|
const sanitizedContent = DOMPurify.sanitize(email.content, {
|
||||||
ADD_ATTR: ['colspan', 'rowspan', 'style', 'width', 'height']
|
ADD_TAGS: ['style', 'meta', 'link', 'table', 'thead', 'tbody', 'tr', 'td', 'th', 'hr', 'font', 'div', 'span', 'a', 'img', 'b', 'strong', 'i', 'em', 'u', 'br', 'p', 'ul', 'ol', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'pre', 'code', 'center', 'section', 'header', 'footer', 'article', 'nav', 'keyframes'],
|
||||||
});
|
ADD_ATTR: ['*', 'colspan', 'rowspan', 'cellpadding', 'cellspacing', 'border', 'bgcolor', 'width', 'height', 'align', 'valign', 'class', 'id', 'style', 'color', 'face', 'size', 'background', 'src', 'href', 'target', 'rel', 'alt', 'title', 'name', 'animation', 'animation-name', 'animation-duration', 'animation-fill-mode'],
|
||||||
|
ALLOW_UNKNOWN_PROTOCOLS: true,
|
||||||
return (
|
WHOLE_DOCUMENT: true,
|
||||||
<div
|
KEEP_CONTENT: true,
|
||||||
className="email-content prose max-w-none dark:prose-invert"
|
RETURN_DOM: false,
|
||||||
dangerouslySetInnerHTML={{ __html: sanitizedContent }}
|
FORBID_TAGS: ['script', 'iframe', 'object', 'embed', 'form', 'input', 'button', 'select', 'option', 'textarea', 'canvas', 'video', 'audio'],
|
||||||
/>
|
FORBID_ATTR: ['onerror', 'onload', 'onclick', 'onmouseover', 'onmouseout', 'onchange', 'onsubmit'],
|
||||||
);
|
USE_PROFILES: { html: true, svg: false, svgFilters: false, mathMl: false },
|
||||||
|
FORCE_BODY: true
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="email-content prose max-w-none dark:prose-invert"
|
||||||
|
dangerouslySetInnerHTML={{ __html: sanitizedContent }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error rendering email content:', error);
|
||||||
|
return <p>Error displaying email content</p>;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Format the date
|
// Format the date
|
||||||
|
|||||||
@ -105,14 +105,16 @@ export function cleanHtml(html: string): string {
|
|||||||
try {
|
try {
|
||||||
// Enhanced configuration to preserve more HTML elements for complex emails
|
// Enhanced configuration to preserve more HTML elements for complex emails
|
||||||
return DOMPurify.sanitize(html, {
|
return DOMPurify.sanitize(html, {
|
||||||
ADD_TAGS: ['style', 'meta', 'link', 'table', 'thead', 'tbody', 'tr', 'td', 'th', 'hr', 'font', 'div', 'span', 'a', 'img', 'b', 'strong', 'i', 'em', 'u', 'br', 'p', 'ul', 'ol', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'pre', 'code', 'center', 'section', 'header', 'footer', 'article', 'nav'],
|
ADD_TAGS: ['style', 'meta', 'link', 'table', 'thead', 'tbody', 'tr', 'td', 'th', 'hr', 'font', 'div', 'span', 'a', 'img', 'b', 'strong', 'i', 'em', 'u', 'br', 'p', 'ul', 'ol', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'pre', 'code', 'center', 'section', 'header', 'footer', 'article', 'nav', 'keyframes'],
|
||||||
ADD_ATTR: ['*', 'colspan', 'rowspan', 'cellpadding', 'cellspacing', 'border', 'bgcolor', 'width', 'height', 'align', 'valign', 'class', 'id', 'style', 'color', 'face', 'size', 'background', 'src', 'href', 'target', 'rel', 'alt', 'title', 'name'],
|
ADD_ATTR: ['*', 'colspan', 'rowspan', 'cellpadding', 'cellspacing', 'border', 'bgcolor', 'width', 'height', 'align', 'valign', 'class', 'id', 'style', 'color', 'face', 'size', 'background', 'src', 'href', 'target', 'rel', 'alt', 'title', 'name', 'animation', 'animation-name', 'animation-duration', 'animation-fill-mode'],
|
||||||
ALLOW_UNKNOWN_PROTOCOLS: true,
|
ALLOW_UNKNOWN_PROTOCOLS: true,
|
||||||
WHOLE_DOCUMENT: true,
|
WHOLE_DOCUMENT: true,
|
||||||
KEEP_CONTENT: true,
|
KEEP_CONTENT: true,
|
||||||
RETURN_DOM: false,
|
RETURN_DOM: false,
|
||||||
FORBID_TAGS: ['script', 'iframe', 'object', 'embed', 'form', 'input', 'button', 'select', 'option', 'textarea', 'canvas', 'video', 'audio'],
|
FORBID_TAGS: ['script', 'iframe', 'object', 'embed', 'form', 'input', 'button', 'select', 'option', 'textarea', 'canvas', 'video', 'audio'],
|
||||||
FORBID_ATTR: ['onerror', 'onload', 'onclick', 'onmouseover', 'onmouseout', 'onchange', 'onsubmit']
|
FORBID_ATTR: ['onerror', 'onload', 'onclick', 'onmouseover', 'onmouseout', 'onchange', 'onsubmit'],
|
||||||
|
USE_PROFILES: { html: true, svg: false, svgFilters: false, mathMl: false },
|
||||||
|
FORCE_BODY: true
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error cleaning HTML:', error);
|
console.error('Error cleaning HTML:', error);
|
||||||
|
|||||||
@ -1,20 +1,16 @@
|
|||||||
import { simpleParser } from 'mailparser';
|
import { simpleParser } from 'mailparser';
|
||||||
|
|
||||||
function cleanHtml(html: string): string {
|
export function cleanHtml(html: string): string {
|
||||||
try {
|
try {
|
||||||
// Basic HTML cleaning without DOMPurify
|
// More permissive cleaning that preserves styling but removes potentially harmful elements
|
||||||
return html
|
return html
|
||||||
.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '') // Remove script tags
|
.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
|
||||||
.replace(/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi, '') // Remove style tags
|
.replace(/<iframe\b[^<]*(?:(?!<\/iframe>)<[^<]*)*<\/iframe>/gi, '')
|
||||||
.replace(/<meta[^>]*>/gi, '') // Remove meta tags
|
.replace(/<object\b[^<]*(?:(?!<\/object>)<[^<]*)*<\/object>/gi, '')
|
||||||
.replace(/<head[^>]*>[\s\S]*?<\/head>/gi, '') // Remove head
|
.replace(/<embed\b[^<]*(?:(?!<\/embed>)<[^<]*)*<\/embed>/gi, '')
|
||||||
.replace(/<title[^>]*>[\s\S]*?<\/title>/gi, '') // Remove title
|
.replace(/<form\b[^<]*(?:(?!<\/form>)<[^<]*)*<\/form>/gi, '')
|
||||||
.replace(/<body[^>]*>/gi, '') // Remove body opening tag
|
.replace(/on\w+="[^"]*"/gi, '') // Remove inline event handlers (onclick, onload, etc.)
|
||||||
.replace(/<\/body>/gi, '') // Remove body closing tag
|
.replace(/on\w+='[^']*'/gi, '');
|
||||||
.replace(/<html[^>]*>/gi, '') // Remove html opening tag
|
|
||||||
.replace(/<\/html>/gi, '') // Remove html closing tag
|
|
||||||
.replace(/\s+/g, ' ') // Clean up whitespace
|
|
||||||
.trim();
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error cleaning HTML:', error);
|
console.error('Error cleaning HTML:', error);
|
||||||
return html;
|
return html;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user