diff --git a/app/api/parse-email/route.ts b/app/api/parse-email/route.ts new file mode 100644 index 00000000..1db100fc --- /dev/null +++ b/app/api/parse-email/route.ts @@ -0,0 +1,55 @@ +import { NextResponse } from 'next/server'; +import { simpleParser } from 'mailparser'; +import DOMPurify from 'dompurify'; +import { JSDOM } from 'jsdom'; + +// Create a window object for DOMPurify +const window = new JSDOM('').window; +const purify = DOMPurify(window); + +function cleanHtml(html: string): string { + try { + return purify.sanitize(html, { + ALLOWED_TAGS: ['p', 'br', 'div', 'span', 'a', 'img', 'strong', 'em', 'u', 'ul', 'ol', 'li', 'blockquote', 'pre', 'code', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'], + ALLOWED_ATTR: ['href', 'src', 'alt', 'title', 'class', 'style'], + ALLOWED_URI_REGEXP: /^(?:(?:(?:f|ht)tps?|mailto|tel):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i + }); + } catch (error) { + console.error('Error cleaning HTML:', error); + return html; + } +} + +export async function POST(request: Request) { + try { + const { emailContent } = await request.json(); + + if (!emailContent) { + return NextResponse.json( + { error: 'Email content is required' }, + { status: 400 } + ); + } + + const parsed = await simpleParser(emailContent); + + return NextResponse.json({ + subject: parsed.subject || null, + from: parsed.from?.text || null, + to: parsed.to?.text || null, + cc: parsed.cc?.text || null, + bcc: parsed.bcc?.text || null, + date: parsed.date || null, + html: parsed.html ? cleanHtml(parsed.html) : null, + text: parsed.text || null, + attachments: parsed.attachments || [], + headers: Object.fromEntries(parsed.headers) + }); + } catch (error) { + console.error('Error parsing email:', error); + return NextResponse.json( + { error: 'Failed to parse email' }, + { status: 500 } + ); + } +} \ No newline at end of file diff --git a/app/courrier/page.tsx b/app/courrier/page.tsx index cbb4a5d0..3292c7f4 100644 --- a/app/courrier/page.tsx +++ b/app/courrier/page.tsx @@ -198,6 +198,17 @@ const initialSidebarItems = [ } ]; +function formatDate(date: Date | null): string { + if (!date) return ''; + return new Intl.DateTimeFormat('fr-FR', { + day: '2-digit', + month: '2-digit', + year: 'numeric', + hour: '2-digit', + minute: '2-digit' + }).format(date); +} + async function getReplyBody(email: Email, type: 'reply' | 'reply-all' | 'forward' = 'reply') { if (!email.body) return ''; @@ -211,26 +222,26 @@ async function getReplyBody(email: Email, type: 'reply' | 'reply-all' | 'forward formattedContent = `
`; } else { formattedContent = ` `; } - return cleanHtml(formattedContent); + return formattedContent; } catch (error) { console.error('Error generating reply body:', error); return ''; @@ -447,18 +458,6 @@ export default function CourrierPage() { loadEmails(); }, [currentView]); - // Format date for display - function formatDate(date: Date | null): string { - if (!date) return ''; - return new Intl.DateTimeFormat('fr-FR', { - day: '2-digit', - month: '2-digit', - year: 'numeric', - hour: '2-digit', - minute: '2-digit' - }).format(date); - } - // Get account color const getAccountColor = (accountId: number) => { const account = accounts.find(acc => acc.id === accountId); diff --git a/lib/mail-parser-wrapper.ts b/lib/mail-parser-wrapper.ts index 7c417b8d..41a5e76d 100644 --- a/lib/mail-parser-wrapper.ts +++ b/lib/mail-parser-wrapper.ts @@ -18,8 +18,12 @@ export interface ParsedEmail { date: Date | null; html: string | null; text: string | null; - attachments: Attachment[]; - headers: Record