diff --git a/app/api/parse-email/route.ts b/app/api/parse-email/route.ts index ad03828d..613b89ce 100644 --- a/app/api/parse-email/route.ts +++ b/app/api/parse-email/route.ts @@ -1,48 +1,29 @@ import { NextResponse } from 'next/server'; -import { simpleParser, AddressObject } from 'mailparser'; - -function getEmailAddress(address: AddressObject | AddressObject[] | undefined): string | null { - if (!address) return null; - if (Array.isArray(address)) { - return address.map(a => a.text).join(', '); - } - return address.text; -} +import { parseEmail } from '@/lib/server/email-parser'; export async function POST(request: Request) { try { const body = await request.json(); - const { email } = body; + console.log('Received request body:', body); - if (!email || typeof email !== 'string') { + const { emailContent } = body; + console.log('Email content type:', typeof emailContent); + console.log('Email content length:', emailContent?.length); + + if (!emailContent || typeof emailContent !== 'string') { + console.log('Invalid email content:', { emailContent, type: typeof emailContent }); return NextResponse.json( - { error: 'Invalid email content' }, + { error: 'Invalid email content. Expected a string.', received: { type: typeof emailContent, length: emailContent?.length } }, { status: 400 } ); } - const parsed = await simpleParser(email); - - 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 || {} - }); + const parsed = await parseEmail(emailContent); + return NextResponse.json(parsed); } catch (error) { console.error('Error parsing email:', error); return NextResponse.json( - { error: 'Failed to parse email' }, + { error: 'Failed to parse email', details: error instanceof Error ? error.message : 'Unknown error' }, { status: 500 } ); } diff --git a/app/courrier/page.tsx b/app/courrier/page.tsx index 12e71f67..baafd4cc 100644 --- a/app/courrier/page.tsx +++ b/app/courrier/page.tsx @@ -103,30 +103,20 @@ function splitEmailHeadersAndBody(emailBody: string): { headers: string; body: s function EmailContent({ email }: { email: Email }) { const [content, setContent] = useState(null); const [error, setError] = useState(null); - const [isLoading, setIsLoading] = useState(false); useEffect(() => { let mounted = true; async function loadContent() { - if (!email) return; - - setIsLoading(true); try { if (!email.body) { - if (mounted) { - setContent(
No content available
); - setIsLoading(false); - } + if (mounted) setContent(null); return; } const formattedEmail = email.body.trim(); if (!formattedEmail) { - if (mounted) { - setContent(
No content available
); - setIsLoading(false); - } + if (mounted) setContent(null); return; } @@ -137,7 +127,7 @@ function EmailContent({ email }: { email: Email }) { setContent(
); } else if (parsedEmail.text) { @@ -147,17 +137,15 @@ function EmailContent({ email }: { email: Email }) {
); } else { - setContent(
No content available
); + setContent(null); } setError(null); - setIsLoading(false); } } catch (err) { console.error('Error rendering email content:', err); if (mounted) { setError('Error rendering email content. Please try again.'); setContent(null); - setIsLoading(false); } } } @@ -167,21 +155,13 @@ function EmailContent({ email }: { email: Email }) { return () => { mounted = false; }; - }, [email?.body]); - - if (isLoading) { - return ( -
-
-
- ); - } + }, [email.body]); if (error) { return
{error}
; } - return content ||
No content available
; + return content; } function renderEmailContent(email: Email) { @@ -330,29 +310,15 @@ function getReplyBody(email: Email, type: 'reply' | 'reply-all' | 'forward' = 'r function EmailPreview({ email }: { email: Email }) { const [preview, setPreview] = useState(''); const [error, setError] = useState(null); - const [isLoading, setIsLoading] = useState(false); useEffect(() => { let mounted = true; async function loadPreview() { - if (!email?.body) { - if (mounted) setPreview('No content available'); - return; - } - - setIsLoading(true); try { const decoded = await decodeEmail(email.body); if (mounted) { - if (decoded.text) { - setPreview(decoded.text.substring(0, 150) + '...'); - } else if (decoded.html) { - const cleanText = decoded.html.replace(/<[^>]*>/g, ' ').trim(); - setPreview(cleanText.substring(0, 150) + '...'); - } else { - setPreview('No preview available'); - } + setPreview(decoded.text || cleanHtml(decoded.html || '')); setError(null); } } catch (err) { @@ -361,8 +327,6 @@ function EmailPreview({ email }: { email: Email }) { setError('Error generating preview'); setPreview(''); } - } finally { - if (mounted) setIsLoading(false); } } @@ -371,11 +335,7 @@ function EmailPreview({ email }: { email: Email }) { return () => { mounted = false; }; - }, [email?.body]); - - if (isLoading) { - return Loading preview...; - } + }, [email.body]); if (error) { return {error}; @@ -547,55 +507,38 @@ export default function CourrierPage() { setAvailableFolders(data.folders); } - // Process emails keeping exact folder names and sort by date - const processedEmails = (data.emails || []) - .map((email: any) => ({ - id: Number(email.id), - accountId: 1, - from: email.from || '', - fromName: email.fromName || email.from?.split('@')[0] || '', - to: email.to || '', - subject: email.subject || '(No subject)', - body: email.body || '', - date: email.date || new Date().toISOString(), - read: email.read || false, - starred: email.starred || false, - folder: email.folder || currentView, - cc: email.cc, - bcc: email.bcc, - flags: email.flags || [], - raw: email.body || '' - })); - - // Sort emails by date, ensuring most recent first - const sortedEmails = processedEmails.sort((a: Email, b: Email) => { - const dateA = new Date(a.date).getTime(); - const dateB = new Date(b.date).getTime(); - return dateB - dateA; // Most recent first - }); + // Process emails keeping exact folder names + const processedEmails = (data.emails || []).map((email: any) => ({ + id: Number(email.id), + accountId: 1, + from: email.from || '', + fromName: email.fromName || email.from?.split('@')[0] || '', + to: email.to || '', + subject: email.subject || '(No subject)', + body: email.body || '', + date: email.date || new Date().toISOString(), + read: email.read || false, + starred: email.starred || false, + folder: email.folder || currentView, + cc: email.cc, + bcc: email.bcc, + flags: email.flags || [], + raw: email.body || '' + })); // Only update unread count if we're in the Inbox folder if (currentView === 'INBOX') { - const unreadInboxEmails = sortedEmails.filter( + const unreadInboxEmails = processedEmails.filter( (email: Email) => !email.read && email.folder === 'INBOX' ).length; setUnreadCount(unreadInboxEmails); } if (isLoadMore) { - // When loading more, merge with existing emails and re-sort - setEmails(prev => { - const combined = [...prev, ...sortedEmails]; - return combined.sort((a: Email, b: Email) => { - const dateA = new Date(a.date).getTime(); - const dateB = new Date(b.date).getTime(); - return dateB - dateA; // Most recent first - }); - }); + setEmails(prev => [...prev, ...processedEmails]); setPage(prev => prev + 1); } else { - // For initial load or refresh, just use the sorted emails - setEmails(sortedEmails); + setEmails(processedEmails); setPage(1); } @@ -629,60 +572,53 @@ export default function CourrierPage() { return; } + // Set the selected email first to show preview immediately + setSelectedEmail(email); + + // Fetch the full email content + const response = await fetch(`/api/mail/${emailId}`); + if (!response.ok) { + throw new Error('Failed to fetch full email content'); + } + + const fullEmail = await response.json(); + + // Update the email in the list and selected email with full content + setEmails(prevEmails => prevEmails.map(email => + email.id === emailId + ? { ...email, body: fullEmail.body } + : email + )); + + setSelectedEmail(prev => prev ? { ...prev, body: fullEmail.body } : prev); + + // Try to mark as read in the background try { - // Set the selected email first to show preview immediately - setSelectedEmail(email); - setContentLoading(true); - - // Fetch the full email content - const response = await fetch(`/api/mail/${emailId}`); - if (!response.ok) { - throw new Error('Failed to fetch full email content'); - } - - const fullEmail = await response.json(); - - // Update the email in the list and selected email with full content - setEmails(prevEmails => prevEmails.map(email => - email.id === emailId - ? { ...email, body: fullEmail.body || email.body } - : email - )); - - setSelectedEmail(prev => prev ? { ...prev, body: fullEmail.body || prev.body } : prev); - setContentLoading(false); + const markReadResponse = await fetch(`/api/mail/mark-read`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + emailId, + isRead: true, + }), + }); - // Try to mark as read in the background - try { - const markReadResponse = await fetch(`/api/mail/mark-read`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - emailId, - isRead: true, - }), - }); - - if (markReadResponse.ok) { - // Only update the emails list if the API call was successful - setEmails((prevEmails: Email[]) => - prevEmails.map((email: Email): Email => - email.id === emailId - ? { ...email, read: true } - : email - ) - ); - } else { - console.error('Failed to mark email as read:', await markReadResponse.text()); - } - } catch (error) { - console.error('Error marking email as read:', error); + if (markReadResponse.ok) { + // Only update the emails list if the API call was successful + setEmails((prevEmails: Email[]) => + prevEmails.map((email: Email): Email => + email.id === emailId + ? { ...email, read: true } + : email + ) + ); + } else { + console.error('Failed to mark email as read:', await markReadResponse.text()); } } catch (error) { - console.error('Error fetching email content:', error); - setContentLoading(false); + console.error('Error marking email as read:', error); } }; @@ -1217,7 +1153,7 @@ export default function CourrierPage() { ); // Add back the handleReply function - const handleReply = async (type: 'reply' | 'reply-all' | 'forward') => { + const handleReply = (type: 'reply' | 'reply-all' | 'forward') => { if (!selectedEmail) return; const getReplyTo = () => { @@ -1238,51 +1174,45 @@ export default function CourrierPage() { return subject.startsWith('Re:') ? subject : `Re: ${subject}`; }; - try { - // Get the decoded content first - const decoded = await decodeEmail(selectedEmail.body); - - // Create a clean structure with clear separation - const formattedContent = ` -
-
- ${type === 'forward' ? ` -
- ---------- Forwarded message ---------
- From: ${selectedEmail.from}
- Date: ${new Date(selectedEmail.date).toLocaleString()}
- Subject: ${selectedEmail.subject}
- To: ${selectedEmail.to}
- ${selectedEmail.cc ? `Cc: ${selectedEmail.cc}
` : ''} -
- ${decoded.html || decoded.text || ''} -
- ` : ` -
- On ${new Date(selectedEmail.date).toLocaleString()}, ${selectedEmail.from} wrote: -
-
- ${decoded.html || decoded.text || ''} -
- `} -
- `; + // Get the formatted original email content + const originalContent = getReplyBody(selectedEmail, type); + + // Create a clean structure with clear separation + const formattedContent = ` +
+
+ ${type === 'forward' ? ` +
+ ---------- Forwarded message ---------
+ From: ${selectedEmail.from}
+ Date: ${new Date(selectedEmail.date).toLocaleString()}
+ Subject: ${selectedEmail.subject}
+ To: ${selectedEmail.to}
+ ${selectedEmail.cc ? `Cc: ${selectedEmail.cc}
` : ''} +
+ ` : ` +
+ On ${new Date(selectedEmail.date).toLocaleString()}, ${selectedEmail.from} wrote: +
+ `} +
+ ${originalContent} +
+
+ `; - // Update the compose form - setComposeTo(getReplyTo()); - setComposeCc(getReplyCc()); - setComposeSubject(getReplySubject()); - setComposeBody(formattedContent); - setComposeBcc(''); + // Update the compose form + setComposeTo(getReplyTo()); + setComposeCc(getReplyCc()); + setComposeSubject(getReplySubject()); + setComposeBody(formattedContent); + setComposeBcc(''); - // Show the compose form and CC field for Reply All - setShowCompose(true); - setShowCc(type === 'reply-all'); - setShowBcc(false); - setAttachments([]); - } catch (error) { - console.error('Error preparing reply:', error); - } + // Show the compose form and CC field for Reply All + setShowCompose(true); + setShowCc(type === 'reply-all'); + setShowBcc(false); + setAttachments([]); }; // Add back the toggleStarred function diff --git a/components/ComposeEmail.tsx b/components/ComposeEmail.tsx index 92847375..ea64142f 100644 --- a/components/ComposeEmail.tsx +++ b/components/ComposeEmail.tsx @@ -81,145 +81,157 @@ export default function ComposeEmail({ }: ComposeEmailProps) { const composeBodyRef = useRef(null); const [localContent, setLocalContent] = useState(''); - const [isLoading, setIsLoading] = useState(false); + const [isInitialized, setIsInitialized] = useState(false); useEffect(() => { - if (replyTo || forwardFrom) { - const initializeContent = async () => { - if (!composeBodyRef.current) return; + if (composeBodyRef.current && !isInitialized) { + let content = ''; + + if (replyTo || forwardFrom) { + const originalContent = replyTo?.body || forwardFrom?.body || ''; - try { - const emailToProcess = replyTo || forwardFrom; - if (!emailToProcess?.body) { - console.error('No email body found to process'); - return; - } - - // Set initial loading state - composeBodyRef.current.innerHTML = ` -
-
-
Loading original message...
-
- `; - - // 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.body }), - }); - - const data = await response.json(); - if (!response.ok) { - throw new Error(data.error || 'Failed to parse email'); - } - - const emailContent = data.html || data.text || ''; - - // Format the reply/forward content - const quotedContent = forwardFrom ? ` -
- ---------- Forwarded message ---------
- From: ${emailToProcess.from}
- Date: ${new Date(emailToProcess.date).toLocaleString()}
- Subject: ${emailToProcess.subject}
- To: ${emailToProcess.to}
- ${emailToProcess.cc ? `Cc: ${emailToProcess.cc}
` : ''} -
-
- ${emailContent} -
- ` : ` -
- On ${new Date(emailToProcess.date).toLocaleString()}, ${emailToProcess.from} wrote: -
-
- ${emailContent} -
- `; - - // Set the content in the compose area with proper structure - const formattedContent = ` -
-

- ${quotedContent} + fetch('/api/parse-email', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ emailContent: originalContent }), + }) + .then(response => response.json()) + .then(parsed => { + content = ` +
+


+ ${forwardFrom ? ` +
+ ---------- Forwarded message ---------
+ From: ${forwardFrom.from}
+ Date: ${new Date(forwardFrom.date).toLocaleString()}
+ Subject: ${forwardFrom.subject}
+ To: ${forwardFrom.to}
+ ${forwardFrom.cc ? `Cc: ${forwardFrom.cc}
` : ''} +
+ ${parsed.html || parsed.text} +
+ ` : ` +
+ On ${new Date(replyTo?.date || '').toLocaleString()}, ${replyTo?.from} wrote: +
+
+ ${parsed.html || parsed.text} +
+ `}
`; 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); + composeBodyRef.current.innerHTML = content; + setIsInitialized(true); + + // Place cursor at the beginning of the compose area + const composeArea = composeBodyRef.current.querySelector('.compose-area'); + if (composeArea) { + const range = document.createRange(); + const sel = window.getSelection(); + range.setStart(composeArea, 0); range.collapse(true); - selection?.removeAllRanges(); - selection?.addRange(range); - (firstDiv as HTMLElement).focus(); + sel?.removeAllRanges(); + sel?.addRange(range); + (composeArea as HTMLElement).focus(); } - - // Update compose state - setComposeBody(formattedContent); - setLocalContent(formattedContent); - } - } catch (error) { - console.error('Error initializing compose content:', error); - if (composeBodyRef.current) { - const errorContent = ` -
-
-
Error loading original message.
-
- `; - composeBodyRef.current.innerHTML = errorContent; - setComposeBody(errorContent); - setLocalContent(errorContent); } + }) + .catch(error => { + console.error('Error parsing email:', error); + }); + } else { + content = `
`; + composeBodyRef.current.innerHTML = content; + setIsInitialized(true); + + const composeArea = composeBodyRef.current.querySelector('.compose-area'); + if (composeArea) { + const range = document.createRange(); + const sel = window.getSelection(); + range.setStart(composeArea, 0); + range.collapse(true); + sel?.removeAllRanges(); + sel?.addRange(range); + (composeArea as HTMLElement).focus(); } - }; - - initializeContent(); + } } - }, [replyTo, forwardFrom]); + }, [composeBody, replyTo, forwardFrom, isInitialized]); + // Modified input handler to work with the single contentEditable area const handleInput = (e: React.FormEvent) => { if (!composeBodyRef.current) return; - const content = composeBodyRef.current.innerHTML; + // Get the compose area content + const composeArea = composeBodyRef.current.querySelector('.compose-area'); + if (!composeArea) return; + + const content = composeArea.innerHTML; + if (!content.trim()) { - setLocalContent(''); - setComposeBody(''); - } else { - setLocalContent(content); - setComposeBody(content); + console.warn('Email content is empty'); + return; } + // Create MIME headers + const mimeHeaders = { + 'MIME-Version': '1.0', + 'Content-Type': 'text/html; charset="utf-8"', + 'Content-Transfer-Encoding': 'quoted-printable' + }; + + // Combine headers and content + const mimeContent = Object.entries(mimeHeaders) + .map(([key, value]) => `${key}: ${value}`) + .join('\n') + '\n\n' + content; + + setComposeBody(mimeContent); + if (onBodyChange) { - onBodyChange(content); + onBodyChange(mimeContent); } }; const handleSendEmail = async () => { - if (!composeBodyRef.current) return; + // Ensure we have content before sending + if (!composeBodyRef.current) { + console.error('Compose body ref is not available'); + return; + } const composeArea = composeBodyRef.current.querySelector('.compose-area'); - if (!composeArea) return; + if (!composeArea) { + console.error('Compose area not found'); + return; + } + // Get the current content const content = composeArea.innerHTML; if (!content.trim()) { console.error('Email content is empty'); return; } + // Create MIME headers + const mimeHeaders = { + 'MIME-Version': '1.0', + 'Content-Type': 'text/html; charset="utf-8"', + 'Content-Transfer-Encoding': 'quoted-printable' + }; + + // Combine headers and content + const mimeContent = Object.entries(mimeHeaders) + .map(([key, value]) => `${key}: ${value}`) + .join('\n') + '\n\n' + content; + + setComposeBody(mimeContent); + try { - const encodedContent = await encodeComposeContent(content); - setComposeBody(encodedContent); await handleSend(); setShowCompose(false); } catch (error) { diff --git a/lib/compose-mime-decoder.ts b/lib/compose-mime-decoder.ts index 04bc1fd5..96b4c140 100644 --- a/lib/compose-mime-decoder.ts +++ b/lib/compose-mime-decoder.ts @@ -3,58 +3,73 @@ * Handles basic email content without creating nested structures */ -interface ParsedContent { - html: string | null; - text: string | null; +export function decodeComposeContent(content: string): string { + if (!content) return ''; + + // Basic HTML cleaning without creating nested structures + let cleaned = content + // Remove script and style tags + .replace(/]*>[\s\S]*?<\/script>/gi, '') + .replace(/]*>[\s\S]*?<\/style>/gi, '') + // Remove meta tags + .replace(/]*>/gi, '') + // Remove head and title + .replace(/]*>[\s\S]*?<\/head>/gi, '') + .replace(/]*>[\s\S]*?<\/title>/gi, '') + // Remove body tags + .replace(/]*>/gi, '') + .replace(/<\/body>/gi, '') + // Remove html tags + .replace(/]*>/gi, '') + .replace(/<\/html>/gi, '') + // Handle basic formatting + .replace(//gi, '\n') + .replace(/]*>/gi, '\n') + .replace(/<\/p>/gi, '\n') + // Handle lists + .replace(/]*>/gi, '\n') + .replace(/<\/ul>/gi, '\n') + .replace(/]*>/gi, '\n') + .replace(/<\/ol>/gi, '\n') + .replace(/]*>/gi, '• ') + .replace(/<\/li>/gi, '\n') + // Handle basic text formatting + .replace(/]*>/gi, '**') + .replace(/<\/strong>/gi, '**') + .replace(/]*>/gi, '**') + .replace(/<\/b>/gi, '**') + .replace(/]*>/gi, '*') + .replace(/<\/em>/gi, '*') + .replace(/]*>/gi, '*') + .replace(/<\/i>/gi, '*') + // Handle links + .replace(/]*href="([^"]*)"[^>]*>(.*?)<\/a>/gi, '$2 ($1)') + // Handle basic entities + .replace(/ /g, ' ') + .replace(/&/g, '&') + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(/"/g, '"') + .replace(/'/g, "'") + // Clean up whitespace + .replace(/\s+/g, ' ') + .trim(); + + // Do NOT wrap in additional divs + return cleaned; } -export async function decodeComposeContent(content: string): Promise { - if (!content.trim()) { - return { html: null, text: null }; - } - - try { - const response = await fetch('/api/parse-email', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ emailContent: content }), - }); - - if (!response.ok) { - throw new Error('Failed to parse email'); - } - - const parsed = await response.json(); - return { - html: parsed.html || null, - text: parsed.text || null - }; - } catch (error) { - console.error('Error parsing email content:', error); - // Fallback to basic content handling - return { - html: content, - text: content - }; - } -} - -export async function encodeComposeContent(content: string): Promise { - if (!content.trim()) { - throw new Error('Email content is empty'); - } - - // Create MIME headers - const mimeHeaders = { - 'MIME-Version': '1.0', - 'Content-Type': 'text/html; charset="utf-8"', - 'Content-Transfer-Encoding': 'quoted-printable' - }; - - // Combine headers and content - return Object.entries(mimeHeaders) - .map(([key, value]) => `${key}: ${value}`) - .join('\n') + '\n\n' + content; +export function encodeComposeContent(content: string): string { + if (!content) return ''; + + // Basic HTML encoding without adding structure + const encoded = content + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, ''') + .replace(/\n/g, '
'); + + return encoded; } \ No newline at end of file diff --git a/lib/mail-parser-wrapper.ts b/lib/mail-parser-wrapper.ts index ea78b01b..c400a011 100644 --- a/lib/mail-parser-wrapper.ts +++ b/lib/mail-parser-wrapper.ts @@ -22,20 +22,9 @@ export interface ParsedEmail { export async function decodeEmail(emailContent: string): Promise { try { // Ensure the email content is properly formatted - const formattedContent = emailContent?.trim(); + const formattedContent = emailContent.trim(); if (!formattedContent) { - return { - subject: null, - from: null, - to: null, - cc: null, - bcc: null, - date: null, - html: null, - text: 'No content available', - attachments: [], - headers: {} - }; + throw new Error('Email content is empty'); } const response = await fetch('/api/parse-email', { @@ -43,61 +32,22 @@ export async function decodeEmail(emailContent: string): Promise { headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify({ email: formattedContent }), + body: JSON.stringify({ emailContent: formattedContent }), }); - const data = await response.json(); - if (!response.ok) { - console.error('API Error:', data); - return { - subject: null, - from: null, - to: null, - cc: null, - bcc: null, - date: null, - html: null, - text: data.error || 'Failed to parse email', - attachments: [], - headers: {} - }; - } - - // If we have a successful response but no content - if (!data.html && !data.text) { - return { - ...data, - date: data.date ? new Date(data.date) : null, - html: null, - text: 'No content available', - attachments: data.attachments || [], - headers: data.headers || {} - }; + const errorData = await response.json(); + throw new Error(errorData.error || 'Failed to parse email'); } + const data = await response.json(); return { ...data, - date: data.date ? new Date(data.date) : null, - text: data.text || null, - html: data.html || null, - attachments: data.attachments || [], - headers: data.headers || {} + date: data.date ? new Date(data.date) : null }; } catch (error) { console.error('Error parsing email:', error); - return { - subject: null, - from: null, - to: null, - cc: null, - bcc: null, - date: null, - html: null, - text: 'Error parsing email content', - attachments: [], - headers: {} - }; + throw error; } } diff --git a/next.config.js b/next.config.js index d48bee4a..93c15381 100644 --- a/next.config.js +++ b/next.config.js @@ -1,13 +1,16 @@ /** @type {import('next').NextConfig} */ const nextConfig = { webpack: (config, { isServer }) => { - // Handle node: protocol imports if (!isServer) { config.resolve.fallback = { ...config.resolve.fallback, - buffer: require.resolve('buffer/'), - stream: require.resolve('stream-browserify'), - util: require.resolve('util/'), + net: false, + tls: false, + fs: false, + dns: false, + child_process: false, + http2: false, + module: false, }; } return config;