courrier clean 2$

This commit is contained in:
alma 2025-04-26 20:44:11 +02:00
parent 26e72f4f73
commit ad9999fdd5
8 changed files with 105 additions and 312 deletions

View File

@ -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 - **Replacement**: Use `lib/utils/email-formatter.ts` instead
- **Reason**: Consolidated email formatting to a single source of truth - **Reason**: Consolidated email formatting to a single source of truth
### 2. `cleanHtml` in `lib/mail-parser-wrapper.ts` ### 2. `lib/mail-parser-wrapper.ts` (REMOVED)
- **Status**: Deprecated for email composition - **Status**: Removed
- **Replacement**: Use `cleanHtmlContent` from `lib/utils/email-formatter.ts` instead - **Replacement**: Use functions from `lib/utils/email-formatter.ts` instead
- **Reason**: Better handling of text direction and HTML sanitization - **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 ## 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 1. `formatForwardedEmail`: Format emails for forwarding
2. `formatReplyEmail`: Format emails for replying or replying to all 2. `formatReplyEmail`: Format emails for replying or replying to all
3. `formatEmailForReplyOrForward`: Compatibility function that maps to the above two 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. Use these functions for all email formatting needs.
## Email Parsing and Processing Functions ## Email Parsing and Processing Functions
### 1. `splitEmailHeadersAndBody` ### 1. `splitEmailHeadersAndBody` (REMOVED)
- **Location**: `app/courrier/page.tsx` - **Location**: Removed
- **Reason**: Email parsing has been centralized in `lib/mail-parser-wrapper.ts` and the API endpoint. - **Reason**: Email parsing has been centralized in `lib/server/email-parser.ts` and the API endpoint.
- **Replacement**: Use the `decodeEmail` function from `lib/mail-parser-wrapper.ts` which provides a more comprehensive parsing solution. - **Replacement**: Use the `parseEmail` function from `lib/server/email-parser.ts` which provides a comprehensive parsing solution.
- **Status**: Currently marked with `@deprecated` comment, no usages found.
### 2. `getReplyBody` ### 2. `getReplyBody`
- **Location**: `app/courrier/page.tsx` - **Location**: `app/courrier/page.tsx`
@ -62,17 +71,15 @@ Use these functions for all email formatting needs.
- **Replacement**: Use `<EmailPreview email={email} />` directly. - **Replacement**: Use `<EmailPreview email={email} />` directly.
- **Status**: Currently marked with `@deprecated` comment, no usages found. - **Status**: Currently marked with `@deprecated` comment, no usages found.
### 4. `cleanHtml` (in server/email-parser.ts) ### 4. `cleanHtml` (REMOVED)
- **Location**: `lib/server/email-parser.ts` - **Location**: Removed from `lib/server/email-parser.ts`
- **Reason**: This functionality has been centralized in `lib/mail-parser-wrapper.ts`. - **Reason**: HTML sanitization has been consolidated in `lib/utils/email-formatter.ts`.
- **Replacement**: Use `cleanHtml` from `lib/mail-parser-wrapper.ts`. - **Replacement**: Use `sanitizeHtml` from `lib/utils/email-formatter.ts`.
- **Status**: Currently marked with `@deprecated` comment, used in `parseEmail` function.
### 5. `processHtml` (in parse-email/route.ts) ### 5. `processHtml` (REMOVED)
- **Location**: `app/api/parse-email/route.ts` - **Location**: Removed from `app/api/parse-email/route.ts`
- **Reason**: HTML processing has been centralized in `lib/mail-parser-wrapper.ts`. - **Reason**: HTML processing has been consolidated in `lib/utils/email-formatter.ts`.
- **Replacement**: Use `cleanHtml` from `lib/mail-parser-wrapper.ts`. - **Replacement**: Use `sanitizeHtml` from `lib/utils/email-formatter.ts`.
- **Status**: Currently marked with `@deprecated` comment, still used in the API route.
## Deprecated API Routes ## Deprecated API Routes
@ -112,15 +119,16 @@ A compatibility layer has been added to the new component to ensure backward com
## Migration Plan ## Migration Plan
### Phase 1: Deprecation (Current) ### Phase 1: Deprecation (Completed)
- Mark all deprecated functions with `@deprecated` comments - Mark all deprecated functions with `@deprecated` comments
- Add console warnings to deprecated functions - Add console warnings to deprecated functions
- Document alternatives - Document alternatives
### Phase 2: Removal (Future) ### Phase 2: Removal (Completed)
- Remove deprecated functions after ensuring no code uses them - Remove deprecated files: `lib/email-parser.ts` and `lib/mail-parser-wrapper.ts`
- Ensure proper migration path for any code that might have been using these functions - Consolidate all email formatting in `lib/utils/email-formatter.ts`
- Update documentation to remove references to deprecated code - All email parsing now in `lib/server/email-parser.ts`
- Update documentation to point to the centralized utilities
## Server-Client Code Separation ## Server-Client Code Separation

View File

@ -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. 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: 2. **Email Parsing**: Raw email content is parsed using:
- Server-side: `simpleParser` from `mailparser` library via `/api/parse-email` API route - Server-side: `parseEmail` function from `lib/server/email-parser.ts` (which uses `simpleParser` from the `mailparser` library)
- Client-side: `decodeEmail` function in `lib/mail-parser-wrapper.ts` - API route: `/api/parse-email` provides a REST interface to the parser
3. **HTML Sanitization**: Email HTML content is sanitized and processed using: 3. **HTML Sanitization**: Email HTML content is sanitized and processed using:
- `cleanHtml` function in `lib/mail-parser-wrapper.ts` (centralized implementation) - `sanitizeHtml` function in `lib/utils/email-formatter.ts` (centralized implementation)
- CSS styles are optionally preserved and scoped to prevent leakage - 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 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 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 - Email is sent through the `/api/courrier/send` endpoint
## Deprecated Functions ## 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. - 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 - `/lib` - Utility functions and services
- `/services` - Domain-specific services, including email service - `/services` - Domain-specific services, including email service
- `/server` - Server-side utilities - `/server` - Server-side utilities
- `/utils` - Utility functions including the centralized email formatter
## Dependencies ## Dependencies
@ -72,7 +73,7 @@ All email formatting is now handled by a centralized formatter in `lib/utils/ema
- `formatForwardedEmail`: Format emails for forwarding - `formatForwardedEmail`: Format emails for forwarding
- `formatReplyEmail`: Format emails for replying - `formatReplyEmail`: Format emails for replying
- `cleanHtmlContent`: Sanitize HTML while preserving direction attributes - `sanitizeHtml`: Sanitize HTML while preserving direction attributes
- `formatEmailForReplyOrForward`: Compatibility function for both - `formatEmailForReplyOrForward`: Compatibility function for both
This centralized approach prevents formatting inconsistencies and direction problems when dealing with emails in different languages. This centralized approach prevents formatting inconsistencies and direction problems when dealing with emails in different languages.

View File

@ -124,7 +124,7 @@ interface ParsedEmailMetadata {
/** /**
* @deprecated This function is deprecated and will be removed in future versions. * @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 } { function splitEmailHeadersAndBody(emailBody: string): { headers: string; body: string } {
const [headers, ...bodyParts] = emailBody.split('\r\n\r\n'); const [headers, ...bodyParts] = emailBody.split('\r\n\r\n');

View File

@ -5,7 +5,7 @@ import DOMPurify from 'isomorphic-dompurify';
import { Loader2, Paperclip, Download } from 'lucide-react'; import { Loader2, Paperclip, Download } from 'lucide-react';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge'; import { Badge } from '@/components/ui/badge';
import { cleanHtml } from '@/lib/mail-parser-wrapper'; import { sanitizeHtml } from '@/lib/utils/email-formatter';
interface EmailAddress { interface EmailAddress {
name: string; name: string;

View File

@ -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<ParsedContent> {
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<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;
}

View File

@ -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(/<script[^>]*>[\s\S]*?<\/script>/gi, '')
.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, '');
// Convert <br> and <p> to newlines
html = html.replace(/<br[^>]*>/gi, '\n')
.replace(/<p[^>]*>/gi, '\n')
.replace(/<\/p>/gi, '\n');
// Remove all other HTML tags
html = html.replace(/<[^>]+>/g, '');
// Decode HTML entities
html = html.replace(/&nbsp;/g, ' ')
.replace(/&amp;/g, '&')
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>')
.replace(/&quot;/g, '"');
// Clean up whitespace
return html.replace(/\n\s*\n/g, '\n\n').trim();
}

View File

@ -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<string, any>;
}
export async function decodeEmail(emailContent: string): Promise<ParsedEmail> {
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: {}
};
}
}

View File

@ -268,4 +268,66 @@ export function formatEmailForReplyOrForward(
body: content 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;
} }