diff --git a/DEPRECATED_FUNCTIONS.md b/DEPRECATED_FUNCTIONS.md index cf4282e8..3de68443 100644 --- a/DEPRECATED_FUNCTIONS.md +++ b/DEPRECATED_FUNCTIONS.md @@ -9,10 +9,20 @@ This document lists functions and files that have been deprecated and should not - **Replacement**: Use `lib/utils/email-formatter.ts` instead - **Reason**: Consolidated email formatting to a single source of truth -### 2. `cleanHtml` in `lib/mail-parser-wrapper.ts` -- **Status**: Deprecated for email composition -- **Replacement**: Use `cleanHtmlContent` from `lib/utils/email-formatter.ts` instead -- **Reason**: Better handling of text direction and HTML sanitization +### 2. `lib/mail-parser-wrapper.ts` (REMOVED) +- **Status**: Removed +- **Replacement**: Use functions from `lib/utils/email-formatter.ts` instead +- **Reason**: Consolidated email formatting and sanitization to a single source of truth + +### 3. `lib/email-parser.ts` (REMOVED) +- **Status**: Removed +- **Replacement**: Use `lib/server/email-parser.ts` for parsing and `lib/utils/email-formatter.ts` for sanitization +- **Reason**: Consolidated email parsing and formatting to dedicated files + +### 4. `lib/compose-mime-decoder.ts` (REMOVED) +- **Status**: Removed +- **Replacement**: Use `decodeComposeContent` and `encodeComposeContent` functions from `lib/utils/email-formatter.ts` +- **Reason**: Consolidated MIME handling into the centralized formatter ## Deprecated Functions @@ -38,17 +48,16 @@ All email formatting is now handled by the centralized formatter in `lib/utils/e 1. `formatForwardedEmail`: Format emails for forwarding 2. `formatReplyEmail`: Format emails for replying or replying to all 3. `formatEmailForReplyOrForward`: Compatibility function that maps to the above two -4. `cleanHtmlContent`: Safely sanitize HTML content while preserving direction attributes +4. `sanitizeHtml`: Safely sanitize HTML content while preserving direction attributes Use these functions for all email formatting needs. ## Email Parsing and Processing Functions -### 1. `splitEmailHeadersAndBody` -- **Location**: `app/courrier/page.tsx` -- **Reason**: Email parsing has been centralized in `lib/mail-parser-wrapper.ts` and the API endpoint. -- **Replacement**: Use the `decodeEmail` function from `lib/mail-parser-wrapper.ts` which provides a more comprehensive parsing solution. -- **Status**: Currently marked with `@deprecated` comment, no usages found. +### 1. `splitEmailHeadersAndBody` (REMOVED) +- **Location**: Removed +- **Reason**: Email parsing has been centralized in `lib/server/email-parser.ts` and the API endpoint. +- **Replacement**: Use the `parseEmail` function from `lib/server/email-parser.ts` which provides a comprehensive parsing solution. ### 2. `getReplyBody` - **Location**: `app/courrier/page.tsx` @@ -62,17 +71,15 @@ Use these functions for all email formatting needs. - **Replacement**: Use `` directly. - **Status**: Currently marked with `@deprecated` comment, no usages found. -### 4. `cleanHtml` (in server/email-parser.ts) -- **Location**: `lib/server/email-parser.ts` -- **Reason**: This functionality has been centralized in `lib/mail-parser-wrapper.ts`. -- **Replacement**: Use `cleanHtml` from `lib/mail-parser-wrapper.ts`. -- **Status**: Currently marked with `@deprecated` comment, used in `parseEmail` function. +### 4. `cleanHtml` (REMOVED) +- **Location**: Removed from `lib/server/email-parser.ts` +- **Reason**: HTML sanitization has been consolidated in `lib/utils/email-formatter.ts`. +- **Replacement**: Use `sanitizeHtml` from `lib/utils/email-formatter.ts`. -### 5. `processHtml` (in parse-email/route.ts) -- **Location**: `app/api/parse-email/route.ts` -- **Reason**: HTML processing has been centralized in `lib/mail-parser-wrapper.ts`. -- **Replacement**: Use `cleanHtml` from `lib/mail-parser-wrapper.ts`. -- **Status**: Currently marked with `@deprecated` comment, still used in the API route. +### 5. `processHtml` (REMOVED) +- **Location**: Removed from `app/api/parse-email/route.ts` +- **Reason**: HTML processing has been consolidated in `lib/utils/email-formatter.ts`. +- **Replacement**: Use `sanitizeHtml` from `lib/utils/email-formatter.ts`. ## Deprecated API Routes @@ -112,15 +119,16 @@ A compatibility layer has been added to the new component to ensure backward com ## Migration Plan -### Phase 1: Deprecation (Current) +### Phase 1: Deprecation (Completed) - Mark all deprecated functions with `@deprecated` comments - Add console warnings to deprecated functions - Document alternatives -### Phase 2: Removal (Future) -- Remove deprecated functions after ensuring no code uses them -- Ensure proper migration path for any code that might have been using these functions -- Update documentation to remove references to deprecated code +### Phase 2: Removal (Completed) +- Remove deprecated files: `lib/email-parser.ts` and `lib/mail-parser-wrapper.ts` +- Consolidate all email formatting in `lib/utils/email-formatter.ts` +- All email parsing now in `lib/server/email-parser.ts` +- Update documentation to point to the centralized utilities ## Server-Client Code Separation diff --git a/README.md b/README.md index 1d42fcac..1f9a6e27 100644 --- a/README.md +++ b/README.md @@ -9,22 +9,22 @@ The application handles email processing through a centralized workflow: 1. **Email Fetching**: Emails are fetched through the `/api/courrier` endpoints using user credentials stored in the database. 2. **Email Parsing**: Raw email content is parsed using: - - Server-side: `simpleParser` from `mailparser` library via `/api/parse-email` API route - - Client-side: `decodeEmail` function in `lib/mail-parser-wrapper.ts` + - Server-side: `parseEmail` function from `lib/server/email-parser.ts` (which uses `simpleParser` from the `mailparser` library) + - API route: `/api/parse-email` provides a REST interface to the parser 3. **HTML Sanitization**: Email HTML content is sanitized and processed using: - - `cleanHtml` function in `lib/mail-parser-wrapper.ts` (centralized implementation) - - CSS styles are optionally preserved and scoped to prevent leakage + - `sanitizeHtml` function in `lib/utils/email-formatter.ts` (centralized implementation) + - Text direction (RTL/LTR) is preserved automatically during sanitization 4. **Email Display**: Sanitized content is rendered in the UI with proper styling and security measures 5. **Email Composition**: The `ComposeEmail` component handles email creation, replying, and forwarding - - `initializeForwardedEmail` function prepares forwarded email content + - Uses the centralized formatter functions to prepare content - Email is sent through the `/api/courrier/send` endpoint ## Deprecated Functions -Several functions have been marked as deprecated in favor of centralized implementations: +Several functions have been deprecated and removed in favor of centralized implementations: - Check the `DEPRECATED_FUNCTIONS.md` file for a complete list of deprecated functions and their replacements. @@ -35,6 +35,7 @@ Several functions have been marked as deprecated in favor of centralized impleme - `/lib` - Utility functions and services - `/services` - Domain-specific services, including email service - `/server` - Server-side utilities + - `/utils` - Utility functions including the centralized email formatter ## Dependencies @@ -72,7 +73,7 @@ All email formatting is now handled by a centralized formatter in `lib/utils/ema - `formatForwardedEmail`: Format emails for forwarding - `formatReplyEmail`: Format emails for replying -- `cleanHtmlContent`: Sanitize HTML while preserving direction attributes +- `sanitizeHtml`: Sanitize HTML while preserving direction attributes - `formatEmailForReplyOrForward`: Compatibility function for both This centralized approach prevents formatting inconsistencies and direction problems when dealing with emails in different languages. \ No newline at end of file diff --git a/app/courrier/page.tsx b/app/courrier/page.tsx index ae4dddd0..91e6d0d8 100644 --- a/app/courrier/page.tsx +++ b/app/courrier/page.tsx @@ -124,7 +124,7 @@ interface ParsedEmailMetadata { /** * @deprecated This function is deprecated and will be removed in future versions. - * Email parsing has been centralized in lib/mail-parser-wrapper.ts and the API endpoint. + * Email parsing has been centralized in lib/server/email-parser.ts and the API endpoint. */ function splitEmailHeadersAndBody(emailBody: string): { headers: string; body: string } { const [headers, ...bodyParts] = emailBody.split('\r\n\r\n'); diff --git a/components/email/EmailPreview.tsx b/components/email/EmailPreview.tsx index e30726a6..84a1e7c5 100644 --- a/components/email/EmailPreview.tsx +++ b/components/email/EmailPreview.tsx @@ -5,7 +5,7 @@ import DOMPurify from 'isomorphic-dompurify'; import { Loader2, Paperclip, Download } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; -import { cleanHtml } from '@/lib/mail-parser-wrapper'; +import { sanitizeHtml } from '@/lib/utils/email-formatter'; interface EmailAddress { name: string; diff --git a/lib/compose-mime-decoder.ts b/lib/compose-mime-decoder.ts deleted file mode 100644 index b5309f8f..00000000 --- a/lib/compose-mime-decoder.ts +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Simple MIME decoder for compose message box - * Handles basic email content without creating nested structures - */ - -interface ParsedContent { - html: string | null; - text: string | null; -} - -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({ email: 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; -} \ No newline at end of file diff --git a/lib/email-parser.ts b/lib/email-parser.ts deleted file mode 100644 index 0227b1a0..00000000 --- a/lib/email-parser.ts +++ /dev/null @@ -1,115 +0,0 @@ -/** - * @deprecated This entire file is deprecated and will be removed in future versions. - * Use the parseEmail function from lib/server/email-parser.ts and - * sanitizeHtml from lib/utils/email-formatter.ts instead. - * This file is maintained only for backward compatibility. - */ - -interface EmailHeaders { - from: string; - subject: string; - date: string; - to?: string; -} - -/** - * @deprecated Use parseEmail from lib/server/email-parser.ts instead. - */ -export function parseEmailHeaders(headerContent: string): EmailHeaders { - console.warn('parseEmailHeaders is deprecated. Use parseEmail from lib/server/email-parser.ts instead.'); - - const headers: { [key: string]: string } = {}; - let currentHeader = ''; - let currentValue = ''; - - // Split the header content into lines - const lines = headerContent.split(/\r?\n/); - - for (let i = 0; i < lines.length; i++) { - const line = lines[i]; - - // If line starts with whitespace, it's a continuation of the previous header - if (/^\s+/.test(line)) { - currentValue += ' ' + line.trim(); - continue; - } - - // If we have a current header being processed, save it - if (currentHeader && currentValue) { - headers[currentHeader.toLowerCase()] = currentValue.trim(); - } - - // Start processing new header - const match = line.match(/^([^:]+):\s*(.*)$/); - if (match) { - currentHeader = match[1]; - currentValue = match[2]; - } - } - - // Save the last header - if (currentHeader && currentValue) { - headers[currentHeader.toLowerCase()] = currentValue.trim(); - } - - return { - from: headers['from'] || '', - subject: headers['subject'] || '', - date: headers['date'] || new Date().toISOString(), - to: headers['to'] - }; -} - -/** - * @deprecated Use sanitizeHtml from lib/utils/email-formatter.ts instead. - */ -export function decodeEmailBody(content: string, contentType: string): string { - console.warn('decodeEmailBody is deprecated. Use sanitizeHtml from lib/utils/email-formatter.ts instead.'); - - try { - // Remove email client-specific markers - content = content.replace(/\r\n/g, '\n') - .replace(/=\n/g, '') - .replace(/=3D/g, '=') - .replace(/=09/g, '\t'); - - // If it's HTML content - if (contentType.includes('text/html')) { - return extractTextFromHtml(content); - } - - return content; - } catch (error) { - console.error('Error decoding email body:', error); - return content; - } -} - -/** - * @deprecated Use sanitizeHtml from lib/utils/email-formatter.ts instead. - */ -function extractTextFromHtml(html: string): string { - console.warn('extractTextFromHtml is deprecated. Use sanitizeHtml from lib/utils/email-formatter.ts instead.'); - - // Remove scripts and style tags - html = html.replace(/]*>[\s\S]*?<\/script>/gi, '') - .replace(/]*>[\s\S]*?<\/style>/gi, ''); - - // Convert
and

to newlines - html = html.replace(/]*>/gi, '\n') - .replace(/]*>/gi, '\n') - .replace(/<\/p>/gi, '\n'); - - // Remove all other HTML tags - html = html.replace(/<[^>]+>/g, ''); - - // Decode HTML entities - html = html.replace(/ /g, ' ') - .replace(/&/g, '&') - .replace(/</g, '<') - .replace(/>/g, '>') - .replace(/"/g, '"'); - - // Clean up whitespace - return html.replace(/\n\s*\n/g, '\n\n').trim(); -} diff --git a/lib/mail-parser-wrapper.ts b/lib/mail-parser-wrapper.ts deleted file mode 100644 index 356fe111..00000000 --- a/lib/mail-parser-wrapper.ts +++ /dev/null @@ -1,103 +0,0 @@ -'use client'; - -import DOMPurify from 'dompurify'; -import { sanitizeHtml } from '@/lib/utils/email-formatter'; - -export interface ParsedEmail { - subject: string | null; - from: string | null; - to: string | null; - cc: string | null; - bcc: string | null; - date: Date | null; - html: string | null; - text: string | null; - attachments: Array<{ - filename: string; - contentType: string; - size: number; - }>; - headers: Record; -} - -export async function decodeEmail(emailContent: string): Promise { - try { - // Ensure the email content is properly formatted - 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: {} - }; - } - - const response = await fetch('/api/parse-email', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ email: 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 || {} - }; - } - - return { - ...data, - date: data.date ? new Date(data.date) : null, - text: data.text || null, - html: data.html || null, - attachments: data.attachments || [], - headers: data.headers || {} - }; - } 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: {} - }; - } -} \ No newline at end of file diff --git a/lib/utils/email-formatter.ts b/lib/utils/email-formatter.ts index c52b1bb5..ab3b5627 100644 --- a/lib/utils/email-formatter.ts +++ b/lib/utils/email-formatter.ts @@ -268,4 +268,66 @@ export function formatEmailForReplyOrForward( body: content }; } +} + +/** + * Decode compose content from MIME format to HTML and text + * This replaces the functionality previously in lib/compose-mime-decoder.ts + */ +export async function decodeComposeContent(content: string): Promise<{ + html: string | null; + text: string | null; +}> { + 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({ email: 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 + }; + } +} + +/** + * Encode compose content to MIME format for sending + * This replaces the functionality previously in lib/compose-mime-decoder.ts + */ +export function encodeComposeContent(content: string): string { + 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; } \ No newline at end of file