From 684c0cf4effa659d2d7da31aee8c17081b9fb1a5 Mon Sep 17 00:00:00 2001 From: alma Date: Sat, 26 Apr 2025 10:45:19 +0200 Subject: [PATCH] panel 2 courier api restore --- app/api/debug-email/route.ts | 89 +++++++++++++++++++ components/email/ComposeEmail.tsx | 142 ++++++++++++++++++++---------- lib/services/email-service.ts | 16 +++- 3 files changed, 195 insertions(+), 52 deletions(-) create mode 100644 app/api/debug-email/route.ts diff --git a/app/api/debug-email/route.ts b/app/api/debug-email/route.ts new file mode 100644 index 00000000..a6dc91bf --- /dev/null +++ b/app/api/debug-email/route.ts @@ -0,0 +1,89 @@ +import { NextResponse } from 'next/server'; +import { getServerSession } from 'next-auth'; +import { authOptions } from '@/app/api/auth/[...nextauth]/route'; +import { getImapConnection } from '@/lib/services/email-service'; +import { simpleParser } from 'mailparser'; + +export async function GET(request: Request) { + // Verify authentication + const session = await getServerSession(authOptions); + if (!session?.user?.id) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); + } + + try { + const { searchParams } = new URL(request.url); + const emailId = searchParams.get('id'); + const folder = searchParams.get('folder') || 'INBOX'; + + if (!emailId) { + return NextResponse.json({ error: 'Email ID is required' }, { status: 400 }); + } + + // Connect to IMAP + const client = await getImapConnection(session.user.id); + + try { + await client.mailboxOpen(folder); + + // Fetch raw email + const message = await client.fetchOne(emailId, { + source: true, + envelope: true, + flags: true + }); + + if (!message) { + return NextResponse.json({ error: 'Email not found' }, { status: 404 }); + } + + // Get raw source content + const { source, envelope } = message; + const rawSource = source.toString(); + + // Parse the email with multiple options to debug + const parsedEmail = await simpleParser(rawSource, { + skipHtmlToText: true, + keepCidLinks: true + }); + + // Extract all available data + return NextResponse.json({ + id: emailId, + folder, + date: envelope.date, + subject: envelope.subject, + from: envelope.from, + to: envelope.to, + // Raw content (truncated to prevent massive responses) + sourcePreview: rawSource.substring(0, 1000) + (rawSource.length > 1000 ? '...' : ''), + sourceLength: rawSource.length, + // Parsed content + htmlPreview: parsedEmail.html ? parsedEmail.html.substring(0, 1000) + (parsedEmail.html.length > 1000 ? '...' : '') : null, + htmlLength: parsedEmail.html ? parsedEmail.html.length : 0, + textPreview: parsedEmail.text ? parsedEmail.text.substring(0, 1000) + (parsedEmail.text.length > 1000 ? '...' : '') : null, + textLength: parsedEmail.text ? parsedEmail.text.length : 0, + // Detect if there's HTML and what type of content it contains + hasHtml: !!parsedEmail.html, + hasStyleTags: typeof parsedEmail.html === 'string' && parsedEmail.html.includes(' { + console.log('Starting initializeForwardedEmail'); if (!initialEmail) { console.error('No email available for forwarding'); setBody('
No email available for forwarding
'); return; } - // Debug log to see the structure of the email object - console.log('Forwarding email object:', JSON.stringify({ + // Debug the email object structure + console.log('Forwarding email object:', { id: initialEmail.id, subject: initialEmail.subject, + fromLength: initialEmail.from?.length, from: initialEmail.from, to: initialEmail.to, date: initialEmail.date, - hasContent: !!initialEmail.content, - hasHTML: !!initialEmail.html, - hasText: !!initialEmail.text, - contentFetched: initialEmail.contentFetched - }, null, 2)); + hasContent: Boolean(initialEmail.content), + contentLength: initialEmail.content?.length, + hasHtml: Boolean(initialEmail.html), + htmlLength: initialEmail.html?.length + }); try { // Format subject with Fwd: prefix if needed - const subject = initialEmail.subject || "(No subject)"; - if (!subject.match(/^(Fwd|FW|Forward):/i)) { - setSubject(`Fwd: ${subject}`); - } else { - setSubject(subject); + const subjectBase = initialEmail.subject || '(No subject)'; + const subjectRegex = /^(Fwd|FW|Forward):\s*/i; + const subject = subjectRegex.test(subjectBase) + ? subjectBase + : `Fwd: ${subjectBase}`; + + setSubject(subject); + + // Format the forwarded message with a well-structured header + const fromString = Array.isArray(initialEmail.from) && initialEmail.from.length > 0 + ? initialEmail.from.map(addr => addr.name + ? `${addr.name} <${addr.address}>` + : addr.address).join(', ') + : 'Unknown'; + + const toString = Array.isArray(initialEmail.to) && initialEmail.to.length > 0 + ? initialEmail.to.map(addr => addr.name + ? `${addr.name} <${addr.address}>` + : addr.address).join(', ') + : ''; + + const dateString = initialEmail.date + ? typeof initialEmail.date === 'string' + ? new Date(initialEmail.date).toLocaleString() + : initialEmail.date.toLocaleString() + : new Date().toLocaleString(); + + // Create a clean wrapper that won't interfere with the original email's styling + const headerHtml = ` +
+
+
---------- Forwarded message ---------
+
From: ${fromString}
+
Date: ${dateString}
+
Subject: ${subjectBase}
+
To: ${toString}
+
+
+ `; + + // Get the content with proper fallbacks + let originalContent = ''; + + // First try the html field which should contain the raw HTML + if (initialEmail.html && initialEmail.html.trim()) { + console.log('Using HTML content for forward'); + originalContent = initialEmail.html; + } + // Then try the content field + else if (initialEmail.content && initialEmail.content.trim()) { + console.log('Using content field for forward'); + originalContent = initialEmail.content; + } + // Fall back to text with styling if available + else if (initialEmail.text && initialEmail.text.trim()) { + console.log('Using text content for forward'); + originalContent = `
${initialEmail.text}
`; + } + // Last resort - no content available + else { + console.log('No content available for forward'); + originalContent = '
No content available
'; } - // Get email content - use whatever data we have available - const content = initialEmail.content || initialEmail.html || initialEmail.text || ''; - - // Get from info - handle various possible formats - let from = 'Unknown Sender'; - if (initialEmail.from) { - if (Array.isArray(initialEmail.from) && initialEmail.from.length > 0) { - from = initialEmail.from[0].address || 'Unknown'; - } else if (typeof initialEmail.from === 'string') { - from = initialEmail.from; - } - } - - // Hard-code a simple format that definitely works + // Preserve all original structure by wrapping, not modifying the original content const forwardedContent = ` -
-

---------- Forwarded message ---------

-

From: ${from}

-

Date: ${new Date().toLocaleString()}

-

Subject: ${subject}

-

To: ${Array.isArray(initialEmail.to) && initialEmail.to.length > 0 ? initialEmail.to[0].address : ''}

-
- -
- ${content ? content : '

No content available

'} -
`; + ${headerHtml} + + `; - // Just set the HTML directly + console.log('Setting body with forwarded content'); setBody(forwardedContent); } catch (error) { console.error('Error initializing forwarded email:', error); - // Super simple fallback + + // Even in error case, provide a usable template setBody(` -
-

---------- Forwarded message ---------

-

From: ${initialEmail.from ? (Array.isArray(initialEmail.from) ? initialEmail.from[0].address : initialEmail.from) : 'Unknown'}

-

Date: ${new Date().toLocaleString()}

-

Subject: ${initialEmail.subject || ''}

-

To: ${initialEmail.to ? (Array.isArray(initialEmail.to) ? initialEmail.to[0].address : initialEmail.to) : ''}

-
-

Content could not be displayed

`); +
+
+
---------- Forwarded message ---------
+
From: ${initialEmail.from ? JSON.stringify(initialEmail.from) : 'Unknown'}
+
Date: ${new Date().toLocaleString()}
+
Subject: ${initialEmail.subject || '(No subject)'}
+
To: ${initialEmail.to ? JSON.stringify(initialEmail.to) : ''}
+
+
+
+ Error loading original message content. The original message may still be viewable in your inbox. +
+ `); } }; diff --git a/lib/services/email-service.ts b/lib/services/email-service.ts index 59fd971a..72e3ea20 100644 --- a/lib/services/email-service.ts +++ b/lib/services/email-service.ts @@ -405,12 +405,18 @@ export async function getEmailContent( const { source, envelope, flags } = message; - // Parse the email content - const parsedEmail = await simpleParser(source.toString()); + // Parse the email content, ensuring all styles and structure are preserved + const parsedEmail = await simpleParser(source.toString(), { + skipHtmlToText: true, // Don't convert HTML to plain text + keepCidLinks: true // Keep Content-ID references for inline images + }); // Convert flags from Set to boolean checks const flagsArray = Array.from(flags as Set); + // Preserve the raw HTML exactly as it was in the original email + const rawHtml = parsedEmail.html || ''; + return { id: emailId, messageId: envelope.messageId, @@ -445,9 +451,11 @@ export async function getEmailContent( contentType: att.contentType, size: att.size || 0 })), - html: parsedEmail.html || undefined, + // Preserve the exact raw HTML to maintain all styling + html: rawHtml, text: parsedEmail.text || undefined, - content: parsedEmail.html || parsedEmail.textAsHtml || parsedEmail.text || '', + // For content field, prioritize using the raw HTML to preserve all styling + content: rawHtml || parsedEmail.text || '', folder, contentFetched: true };