210 lines
6.3 KiB
TypeScript
210 lines
6.3 KiB
TypeScript
/**
|
|
* Email Adapter Utility
|
|
*
|
|
* This utility provides adapter functions to convert legacy email
|
|
* formats to the standardized EmailMessage format.
|
|
*
|
|
* Use these functions to migrate code gradually without breaking changes.
|
|
*/
|
|
|
|
import {
|
|
EmailMessage,
|
|
EmailAddress,
|
|
EmailAttachment
|
|
} from '@/lib/types';
|
|
|
|
// Define a local interface for email flags structure
|
|
interface EmailFlags {
|
|
seen: boolean;
|
|
flagged: boolean;
|
|
answered: boolean;
|
|
deleted: boolean;
|
|
draft: boolean;
|
|
}
|
|
|
|
/**
|
|
* Normalize email content from various formats to our standard structure
|
|
*/
|
|
function normalizeEmailContent(email: any): { text: string; html: string; isHtml: boolean; direction: string } {
|
|
// Initialize with default values
|
|
let text = '';
|
|
let html = '';
|
|
let isHtml = false;
|
|
|
|
// Extract content from various possible formats
|
|
if (email.content) {
|
|
if (typeof email.content === 'string') {
|
|
// If content is a string, determine if it's HTML or plain text
|
|
if (email.content.includes('<html') || email.content.includes('<body') || email.content.includes('<div')) {
|
|
html = email.content;
|
|
text = email.content.replace(/<[^>]*>/g, ''); // Basic HTML stripping
|
|
isHtml = true;
|
|
} else {
|
|
text = email.content;
|
|
html = '';
|
|
}
|
|
} else if (typeof email.content === 'object') {
|
|
// If content is already an object, try to read text/html properties
|
|
text = email.content.text || email.content.plainText || '';
|
|
html = email.content.html || '';
|
|
isHtml = !!email.content.isHtml || !!html;
|
|
}
|
|
} else {
|
|
// Try to find content in other common properties
|
|
if (email.html) {
|
|
html = email.html;
|
|
isHtml = true;
|
|
}
|
|
|
|
if (email.text) {
|
|
text = email.text;
|
|
} else if (email.plainText) {
|
|
text = email.plainText;
|
|
}
|
|
|
|
// If we have HTML but no text, create a basic text version
|
|
if (html && !text) {
|
|
text = html.replace(/<[^>]*>/g, '');
|
|
}
|
|
}
|
|
|
|
// Return standardized content object
|
|
return {
|
|
text,
|
|
html,
|
|
isHtml,
|
|
direction: 'ltr' // Default to left-to-right
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Convert a legacy email format to our standardized EmailMessage format
|
|
*
|
|
* This adapter function handles all the various ways email data might be structured
|
|
* in the legacy codebase and converts it to our new standardized format.
|
|
*/
|
|
export function adaptLegacyEmail(legacyEmail: any): EmailMessage {
|
|
if (!legacyEmail) {
|
|
throw new Error('Cannot adapt a null or undefined email');
|
|
}
|
|
|
|
// Handle case where it's already in the right format
|
|
if (
|
|
legacyEmail.content &&
|
|
typeof legacyEmail.content === 'object' &&
|
|
'isHtml' in legacyEmail.content &&
|
|
'text' in legacyEmail.content
|
|
) {
|
|
return legacyEmail as EmailMessage;
|
|
}
|
|
|
|
// Create normalized content
|
|
const normalizedContent = normalizeEmailContent(legacyEmail);
|
|
|
|
// Normalize flags to standard format
|
|
let normalizedFlags: EmailFlags = {
|
|
seen: false,
|
|
flagged: false,
|
|
answered: false,
|
|
deleted: false,
|
|
draft: false
|
|
};
|
|
|
|
// Handle different possible formats for flags
|
|
if (legacyEmail.flags) {
|
|
if (typeof legacyEmail.flags === 'object' && !Array.isArray(legacyEmail.flags)) {
|
|
// Object format: { seen: true, flagged: false, ... }
|
|
normalizedFlags = {
|
|
seen: !!legacyEmail.flags.seen,
|
|
flagged: !!legacyEmail.flags.flagged,
|
|
answered: !!legacyEmail.flags.answered,
|
|
deleted: !!legacyEmail.flags.deleted,
|
|
draft: !!legacyEmail.flags.draft
|
|
};
|
|
} else if (Array.isArray(legacyEmail.flags)) {
|
|
// Array format: ['\\Seen', '\\Flagged', ...]
|
|
normalizedFlags.seen = legacyEmail.flags.includes('\\Seen');
|
|
normalizedFlags.flagged = legacyEmail.flags.includes('\\Flagged');
|
|
normalizedFlags.answered = legacyEmail.flags.includes('\\Answered');
|
|
normalizedFlags.deleted = legacyEmail.flags.includes('\\Deleted');
|
|
normalizedFlags.draft = legacyEmail.flags.includes('\\Draft');
|
|
}
|
|
}
|
|
|
|
// Normalize attachments to standard format
|
|
const normalizedAttachments: EmailAttachment[] = Array.isArray(legacyEmail.attachments)
|
|
? legacyEmail.attachments.map((att: any) => ({
|
|
filename: att.filename || att.name || 'attachment',
|
|
contentType: att.contentType || att.type || 'application/octet-stream',
|
|
content: att.content || att.data || undefined,
|
|
size: att.size || 0,
|
|
contentId: att.contentId || att.cid || undefined
|
|
}))
|
|
: [];
|
|
|
|
// Return a normalized EmailMessage
|
|
return {
|
|
id: legacyEmail.id || legacyEmail.uid?.toString() || `email-${Date.now()}`,
|
|
messageId: legacyEmail.messageId,
|
|
subject: legacyEmail.subject || '(No subject)',
|
|
from: Array.isArray(legacyEmail.from) ? legacyEmail.from : [],
|
|
to: Array.isArray(legacyEmail.to) ? legacyEmail.to : [],
|
|
cc: Array.isArray(legacyEmail.cc) ? legacyEmail.cc : undefined,
|
|
bcc: Array.isArray(legacyEmail.bcc) ? legacyEmail.bcc : undefined,
|
|
date: legacyEmail.date || new Date(),
|
|
flags: normalizedFlags,
|
|
preview: legacyEmail.preview || '',
|
|
content: normalizedContent,
|
|
attachments: normalizedAttachments,
|
|
folder: legacyEmail.folder || 'INBOX',
|
|
contentFetched: true,
|
|
size: typeof legacyEmail.size === 'number' ? legacyEmail.size : 0,
|
|
hasAttachments: normalizedAttachments.length > 0,
|
|
accountId: legacyEmail.accountId
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Helper function to detect if an object is an EmailAddress
|
|
*/
|
|
export function isEmailAddress(obj: any): obj is EmailAddress {
|
|
return obj &&
|
|
typeof obj === 'object' &&
|
|
'address' in obj &&
|
|
typeof obj.address === 'string';
|
|
}
|
|
|
|
/**
|
|
* Convert legacy email address format to standardized EmailAddress format
|
|
*/
|
|
export function adaptEmailAddress(address: any): EmailAddress {
|
|
if (isEmailAddress(address)) {
|
|
return {
|
|
name: address.name || '',
|
|
address: address.address
|
|
};
|
|
}
|
|
|
|
if (typeof address === 'string') {
|
|
// Try to extract name and address from string like "Name <email@example.com>"
|
|
const match = address.match(/^(?:"?([^"]*)"?\s)?<?([^\s>]+@[^\s>]+)>?$/);
|
|
if (match) {
|
|
return {
|
|
name: match[1] || '',
|
|
address: match[2]
|
|
};
|
|
}
|
|
|
|
// If no match, assume it's just an email address
|
|
return {
|
|
name: '',
|
|
address
|
|
};
|
|
}
|
|
|
|
// Return a placeholder if we can't parse the address
|
|
return {
|
|
name: '',
|
|
address: 'unknown@example.com'
|
|
};
|
|
}
|