panel 2 courier api restore
This commit is contained in:
parent
f56304775a
commit
c1b1e74519
@ -11,81 +11,38 @@ function getEmailAddress(address: AddressObject | AddressObject[] | undefined):
|
||||
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
console.log('[DEBUG] Parse-email API called');
|
||||
|
||||
const body = await request.json();
|
||||
const emailContent = body.email || body.emailContent;
|
||||
const { email } = body;
|
||||
|
||||
if (!emailContent || typeof emailContent !== 'string') {
|
||||
console.error('[DEBUG] Parse-email API error: Invalid email content');
|
||||
if (!email || typeof email !== 'string') {
|
||||
return NextResponse.json(
|
||||
{ error: 'Invalid email content' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
console.log('[DEBUG] Parse-email API processing email content, length:', emailContent.length);
|
||||
console.log('[DEBUG] Content sample:', emailContent.substring(0, 100) + '...');
|
||||
const parsed = await simpleParser(email);
|
||||
|
||||
try {
|
||||
const parsed = await simpleParser(emailContent);
|
||||
|
||||
console.log('[DEBUG] Parse-email API successfully parsed email:', {
|
||||
hasSubject: !!parsed.subject,
|
||||
hasHtml: !!parsed.html,
|
||||
hasText: !!parsed.text,
|
||||
hasTextAsHtml: !!parsed.textAsHtml,
|
||||
fromCount: parsed.from ? (Array.isArray(parsed.from) ? parsed.from.length : 1) : 0,
|
||||
attachmentCount: parsed.attachments?.length || 0
|
||||
});
|
||||
|
||||
return NextResponse.json({
|
||||
subject: parsed.subject || null,
|
||||
from: getEmailAddress(parsed.from),
|
||||
to: getEmailAddress(parsed.to),
|
||||
cc: getEmailAddress(parsed.cc),
|
||||
bcc: getEmailAddress(parsed.bcc),
|
||||
date: parsed.date || null,
|
||||
html: parsed.html || parsed.textAsHtml || null,
|
||||
text: parsed.text || null,
|
||||
attachments: parsed.attachments?.map(att => ({
|
||||
filename: att.filename,
|
||||
contentType: att.contentType,
|
||||
size: att.size
|
||||
})) || [],
|
||||
headers: parsed.headers || {}
|
||||
});
|
||||
} catch (parseError) {
|
||||
console.error('[DEBUG] Parse-email API error parsing email:', parseError);
|
||||
|
||||
// Try simpler parsing method for more resilience
|
||||
try {
|
||||
console.log('[DEBUG] Attempting fallback parsing method');
|
||||
const resultObj: any = { text: emailContent, html: null };
|
||||
|
||||
// Simple check if it might be HTML
|
||||
if (emailContent.includes('<html') || emailContent.includes('<body')) {
|
||||
resultObj.html = emailContent;
|
||||
} else {
|
||||
// Convert plain text to HTML
|
||||
resultObj.html = emailContent
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/\n/g, '<br>');
|
||||
}
|
||||
|
||||
console.log('[DEBUG] Fallback parsing generated simple result');
|
||||
return NextResponse.json(resultObj);
|
||||
} catch (fallbackError) {
|
||||
console.error('[DEBUG] Even fallback parsing failed:', fallbackError);
|
||||
throw parseError; // Throw the original error
|
||||
}
|
||||
}
|
||||
return NextResponse.json({
|
||||
subject: parsed.subject || null,
|
||||
from: getEmailAddress(parsed.from),
|
||||
to: getEmailAddress(parsed.to),
|
||||
cc: getEmailAddress(parsed.cc),
|
||||
bcc: getEmailAddress(parsed.bcc),
|
||||
date: parsed.date || null,
|
||||
html: parsed.html || null,
|
||||
text: parsed.textAsHtml || parsed.text || null,
|
||||
attachments: parsed.attachments?.map(att => ({
|
||||
filename: att.filename,
|
||||
contentType: att.contentType,
|
||||
size: att.size
|
||||
})) || [],
|
||||
headers: parsed.headers || {}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('[DEBUG] Parse-email API unhandled error:', error);
|
||||
console.error('Error parsing email:', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to parse email', details: error instanceof Error ? error.message : 'Unknown error' },
|
||||
{ error: 'Failed to parse email' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
|
||||
@ -125,7 +125,7 @@ export default function ComposeEmail({
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we need to fetch full content first - same as panel 3
|
||||
// Check if we need to fetch full content first
|
||||
if (!emailToProcess.content || emailToProcess.content.length === 0) {
|
||||
console.log('[DEBUG] Need to fetch content before composing reply/forward');
|
||||
|
||||
@ -156,144 +156,78 @@ export default function ComposeEmail({
|
||||
}
|
||||
}
|
||||
|
||||
// Now proceed with the usual decoding
|
||||
const type = replyTo ? 'reply' : 'forward';
|
||||
// Parse the original email using the API
|
||||
const response = await fetch('/api/parse-email', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ email: emailToProcess.content }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to parse email: ${response.status}`);
|
||||
}
|
||||
|
||||
// DIRECTLY MATCH PANEL 3 IMPLEMENTATION
|
||||
try {
|
||||
const decoded = await decodeEmail(emailToProcess.content);
|
||||
console.log('[DEBUG] Decoded email for compose:', {
|
||||
hasHtml: !!decoded.html,
|
||||
hasText: !!decoded.text,
|
||||
from: decoded.from,
|
||||
subject: decoded.subject
|
||||
});
|
||||
|
||||
// Create the email container with header information
|
||||
let emailHeader = '';
|
||||
if (type === 'forward') {
|
||||
emailHeader = `
|
||||
<div style="border-top: 1px solid #e5e7eb; margin-top: 20px; padding-top: 20px; color: #6b7280; font-size: 14px;">
|
||||
<p>---------- Forwarded message ---------</p>
|
||||
<p>From: ${decoded.from || ''}</p>
|
||||
<p>Date: ${formatDate(decoded.date ? new Date(decoded.date) : null)}</p>
|
||||
<p>Subject: ${decoded.subject || ''}</p>
|
||||
<p>To: ${decoded.to || ''}</p>
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
emailHeader = `
|
||||
<div style="border-top: 1px solid #e5e7eb; margin-top: 20px; padding-top: 20px; color: #6b7280; font-size: 14px;">
|
||||
<p>On ${formatDate(decoded.date ? new Date(decoded.date) : null)}, ${decoded.from || ''} wrote:</p>
|
||||
</div>
|
||||
`;
|
||||
const data = await response.json();
|
||||
console.log('[DEBUG] Parsed email data:', {
|
||||
hasHtml: !!data.html,
|
||||
hasText: !!data.text,
|
||||
from: data.from,
|
||||
subject: data.subject
|
||||
});
|
||||
|
||||
const emailContent = data.html || data.text || '';
|
||||
|
||||
// Format the reply/forward content
|
||||
const quotedContent = forwardFrom ? `
|
||||
<div style="border-top: 1px solid #e5e7eb; padding-top: 20px; margin-top: 20px; color: #6b7280; font-size: 0.875rem;">
|
||||
---------- Forwarded message ---------<br/>
|
||||
From: ${emailToProcess.from}<br/>
|
||||
Date: ${new Date(emailToProcess.date).toLocaleString()}<br/>
|
||||
Subject: ${emailToProcess.subject}<br/>
|
||||
To: ${emailToProcess.to}<br/>
|
||||
${emailToProcess.cc ? `Cc: ${emailToProcess.cc}<br/>` : ''}
|
||||
</div>
|
||||
<div style="margin-top: 10px; color: #374151;">
|
||||
${emailContent}
|
||||
</div>
|
||||
` : `
|
||||
<div style="border-top: 1px solid #e5e7eb; padding-top: 20px; margin-top: 20px; color: #6b7280; font-size: 0.875rem;">
|
||||
On ${new Date(emailToProcess.date).toLocaleString()}, ${emailToProcess.from} wrote:
|
||||
</div>
|
||||
<blockquote style="margin: 10px 0 0 10px; padding-left: 1em; border-left: 2px solid #e5e7eb; color: #374151;">
|
||||
${emailContent}
|
||||
</blockquote>
|
||||
`;
|
||||
|
||||
// Set the content in the compose area with proper structure
|
||||
const formattedContent = `
|
||||
<div class="compose-area" contenteditable="true" style="min-height: 100px; padding: 10px;">
|
||||
<div style="min-height: 20px;"><br/></div>
|
||||
${quotedContent}
|
||||
</div>
|
||||
`;
|
||||
|
||||
if (composeBodyRef.current) {
|
||||
composeBodyRef.current.innerHTML = formattedContent;
|
||||
|
||||
// Place cursor at the beginning before the quoted content
|
||||
const selection = window.getSelection();
|
||||
const range = document.createRange();
|
||||
const firstDiv = composeBodyRef.current.querySelector('div[style*="min-height: 20px;"]');
|
||||
if (firstDiv) {
|
||||
range.setStart(firstDiv, 0);
|
||||
range.collapse(true);
|
||||
selection?.removeAllRanges();
|
||||
selection?.addRange(range);
|
||||
(firstDiv as HTMLElement).focus();
|
||||
}
|
||||
|
||||
// Get the proper content with minimal sanitization to preserve structure
|
||||
let emailContent = '';
|
||||
if (decoded.html) {
|
||||
// Allow ALL HTML elements and attributes to fully preserve formatting
|
||||
emailContent = DOMPurify.sanitize(decoded.html, {
|
||||
ADD_TAGS: ['style', 'meta', 'link', 'script'],
|
||||
ADD_ATTR: ['*', 'style', 'class', 'id', 'src', 'href', 'target', 'rel', 'align', 'valign', 'border', 'cellpadding', 'cellspacing', 'bgcolor', 'width', 'height'],
|
||||
ALLOW_UNKNOWN_PROTOCOLS: true,
|
||||
WHOLE_DOCUMENT: true,
|
||||
RETURN_DOM: false,
|
||||
KEEP_CONTENT: true
|
||||
});
|
||||
} else if (decoded.text) {
|
||||
emailContent = `<pre style="white-space: pre-wrap; font-family: inherit;">${decoded.text}</pre>`;
|
||||
} else {
|
||||
emailContent = '<div>No content available</div>';
|
||||
}
|
||||
|
||||
// Set the content in the compose area with proper structure
|
||||
const wrappedContent = `
|
||||
<div class="compose-area" contenteditable="true" style="min-height: 100px; padding: 10px;">
|
||||
<div class="cursor-position" style="min-height: 20px; cursor: text;"><br/></div>
|
||||
${emailHeader}
|
||||
<div class="email-content" style="margin-top: 10px; border: 1px solid #e5e7eb; padding: 15px; border-radius: 4px; max-height: 500px; overflow-y: auto;">
|
||||
${emailContent}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
if (composeBodyRef.current) {
|
||||
composeBodyRef.current.innerHTML = wrappedContent;
|
||||
|
||||
// Place cursor at the beginning before the quoted content
|
||||
const selection = window.getSelection();
|
||||
const range = document.createRange();
|
||||
const firstDiv = composeBodyRef.current.querySelector('.cursor-position');
|
||||
if (firstDiv) {
|
||||
range.setStart(firstDiv, 0);
|
||||
range.collapse(true);
|
||||
selection?.removeAllRanges();
|
||||
selection?.addRange(range);
|
||||
(firstDiv as HTMLElement).focus();
|
||||
}
|
||||
|
||||
// After setting the HTML content, add event listeners for scrolling
|
||||
const messageContents = composeBodyRef.current.querySelectorAll('.email-content');
|
||||
messageContents.forEach(container => {
|
||||
// Make sure the container is properly styled for scrolling
|
||||
(container as HTMLElement).style.maxHeight = '400px';
|
||||
(container as HTMLElement).style.overflowY = 'auto';
|
||||
(container as HTMLElement).style.border = '1px solid #e5e7eb';
|
||||
(container as HTMLElement).style.borderRadius = '4px';
|
||||
(container as HTMLElement).style.padding = '15px';
|
||||
|
||||
// Ensure wheel events are properly handled
|
||||
if (!(container as HTMLElement).hasAttribute('data-scroll-handler-attached')) {
|
||||
container.addEventListener('wheel', (e: Event) => {
|
||||
const wheelEvent = e as WheelEvent;
|
||||
const target = e.currentTarget as HTMLElement;
|
||||
|
||||
// Check if we're at the boundary of the scrollable area
|
||||
const isAtBottom = target.scrollHeight - target.scrollTop <= target.clientHeight + 1;
|
||||
const isAtTop = target.scrollTop <= 0;
|
||||
|
||||
// Only prevent default if we're not at the boundaries in the direction of scrolling
|
||||
if ((wheelEvent.deltaY > 0 && !isAtBottom) || (wheelEvent.deltaY < 0 && !isAtTop)) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault(); // Prevent the parent container from scrolling
|
||||
}
|
||||
}, { passive: false });
|
||||
|
||||
// Mark this element as having a scroll handler attached
|
||||
(container as HTMLElement).setAttribute('data-scroll-handler-attached', 'true');
|
||||
}
|
||||
});
|
||||
|
||||
// Update compose state
|
||||
setComposeBody(wrappedContent);
|
||||
setLocalContent(wrappedContent);
|
||||
console.log('[DEBUG] Successfully set compose content with scrollable message area');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[DEBUG] Error parsing email for compose:', error);
|
||||
|
||||
// Fallback to basic content display
|
||||
const errorContent = `
|
||||
<div class="compose-area" contenteditable="true">
|
||||
<br/>
|
||||
<div style="color: #64748b;">
|
||||
---------- Original Message ---------<br/>
|
||||
${emailToProcess.subject ? `Subject: ${emailToProcess.subject}<br/>` : ''}
|
||||
${emailToProcess.from ? `From: ${emailToProcess.from}<br/>` : ''}
|
||||
${emailToProcess.date ? `Date: ${new Date(emailToProcess.date).toLocaleString()}<br/>` : ''}
|
||||
</div>
|
||||
<div style="color: #64748b; border-left: 2px solid #e5e7eb; padding-left: 10px; margin: 10px 0;">
|
||||
${emailToProcess.preview || 'No content available'}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
if (composeBodyRef.current) {
|
||||
composeBodyRef.current.innerHTML = errorContent;
|
||||
setComposeBody(errorContent);
|
||||
setLocalContent(errorContent);
|
||||
}
|
||||
// Update compose state
|
||||
setComposeBody(formattedContent);
|
||||
setLocalContent(formattedContent);
|
||||
console.log('[DEBUG] Successfully set compose content');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[DEBUG] Error initializing compose content:', error);
|
||||
|
||||
@ -19,7 +19,7 @@ export async function decodeComposeContent(content: string): Promise<ParsedConte
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ emailContent: content }),
|
||||
body: JSON.stringify({ email: content }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user