Agenda Sync refactor Mig Graph

This commit is contained in:
alma 2026-01-14 16:31:46 +01:00
parent dbbc2ff59a
commit 13a2996446

View File

@ -982,7 +982,9 @@ export async function getEmails(
totalPages: Math.ceil(totalEmails / perPage),
folder,
mailboxes,
newestEmailId: emails.length > 0 ? parseInt(emails[0].id) || 0 : 0,
// For Graph API, IDs are strings, so we can't use parseInt
// Use a hash or just use the first email's ID as string, then convert to number if needed
newestEmailId: emails.length > 0 ? (emails[0].id.match(/^\d+$/) ? parseInt(emails[0].id) : 0) : 0,
};
// Cache the result
@ -1219,6 +1221,7 @@ function mapAddresses(addresses: any[] | undefined): Array<{ name: string; addre
/**
* Get a single email with full content
* Supports both IMAP (numeric UIDs) and Microsoft Graph API (string IDs)
*/
export async function getEmailContent(
userId: string,
@ -1231,17 +1234,6 @@ export async function getEmailContent(
throw new Error('Missing required parameters');
}
// Validate UID format
if (!/^\d+$/.test(emailId)) {
throw new Error('Invalid email ID format: must be a numeric UID');
}
// Convert to number for IMAP
const numericId = parseInt(emailId, 10);
if (isNaN(numericId)) {
throw new Error('Email ID must be a number');
}
// Extract account ID from folder name if present and none was explicitly provided
const folderAccountId = folder.includes(':') ? folder.split(':')[0] : accountId;
@ -1258,6 +1250,99 @@ export async function getEmailContent(
accountId: effectiveAccountId,
});
// Check if this is a Microsoft account that should use Graph API
// Graph API uses string IDs (not numeric UIDs), so if emailId is not numeric, it's likely Graph
const isGraphId = !/^\d+$/.test(emailId);
const graphCheck = await shouldUseGraphAPI(userId, effectiveAccountId);
if (isGraphId && graphCheck.useGraph && graphCheck.mailCredentialId) {
// Use Microsoft Graph API for Microsoft accounts with Graph IDs
logger.debug('[EMAIL] Fetching email content via Microsoft Graph API', {
userId,
emailId,
mailCredentialId: graphCheck.mailCredentialId,
});
try {
// Use normalized folder name and effective account ID for cache key
const cachedEmail = await getCachedEmailContent(userId, effectiveAccountId, emailId);
if (cachedEmail) {
logger.debug('[EMAIL] Using cached email content (Graph)', {
userId,
accountId: effectiveAccountId,
emailId,
});
return cachedEmail;
}
// Fetch from Graph API
const { fetchGraphEmail } = await import('./microsoft-graph-mail');
const graphMessage = await fetchGraphEmail(graphCheck.mailCredentialId, emailId);
// Convert Graph message to EmailMessage format
const email: EmailMessage = {
id: graphMessage.id,
from: graphMessage.from ? [{
name: graphMessage.from.emailAddress.name || '',
address: graphMessage.from.emailAddress.address,
}] : [],
to: graphMessage.toRecipients?.map(recipient => ({
name: recipient.emailAddress.name || '',
address: recipient.emailAddress.address,
})) || [],
cc: graphMessage.ccRecipients?.map(recipient => ({
name: recipient.emailAddress.name || '',
address: recipient.emailAddress.address,
})),
subject: graphMessage.subject || '',
date: new Date(graphMessage.receivedDateTime),
flags: {
seen: graphMessage.isRead,
flagged: graphMessage.flag?.flagStatus === 'flagged' || graphMessage.flag?.flagStatus === 'complete',
answered: false,
draft: false,
deleted: false,
},
size: 0,
hasAttachments: graphMessage.hasAttachments || false,
folder: normalizedFolder,
contentFetched: true,
accountId: effectiveAccountId,
content: {
text: graphMessage.body?.contentType === 'text' ? (graphMessage.body.content || '') : '',
html: graphMessage.body?.contentType === 'html' ? (graphMessage.body.content || '') : (graphMessage.bodyPreview || ''),
isHtml: graphMessage.body?.contentType === 'html',
direction: 'ltr',
},
preview: graphMessage.bodyPreview,
};
// Cache the email content
await cacheEmailContent(userId, effectiveAccountId, emailId, email);
return email;
} catch (error) {
logger.error('[EMAIL] Error fetching email content from Graph API', {
userId,
emailId,
error: error instanceof Error ? error.message : String(error),
});
throw error;
}
}
// Use IMAP for non-Microsoft accounts or numeric UIDs
// Validate UID format for IMAP
if (!/^\d+$/.test(emailId)) {
throw new Error('Invalid email ID format: must be a numeric UID for IMAP accounts');
}
// Convert to number for IMAP
const numericId = parseInt(emailId, 10);
if (isNaN(numericId)) {
throw new Error('Email ID must be a number for IMAP accounts');
}
// Use normalized folder name and effective account ID for cache key
const cachedEmail = await getCachedEmailContent(userId, effectiveAccountId, emailId);
if (cachedEmail) {
@ -1441,6 +1526,7 @@ export async function getEmailContent(
/**
* Mark an email as read or unread
* Supports both IMAP (numeric UIDs) and Microsoft Graph API (string IDs)
*/
export async function markEmailReadStatus(
userId: string,
@ -1466,6 +1552,41 @@ export async function markEmailReadStatus(
accountId: effectiveAccountId,
});
// Check if this is a Microsoft account that should use Graph API
// Graph API uses string IDs (not numeric UIDs), so if emailId is not numeric, it's likely Graph
const isGraphId = !/^\d+$/.test(emailId);
const graphCheck = await shouldUseGraphAPI(userId, effectiveAccountId);
if (isGraphId && graphCheck.useGraph && graphCheck.mailCredentialId) {
// Use Microsoft Graph API for Microsoft accounts with Graph IDs
logger.debug('[EMAIL] Marking email as read/unread via Microsoft Graph API', {
userId,
emailId,
isRead,
mailCredentialId: graphCheck.mailCredentialId,
});
try {
const { markGraphEmailAsRead } = await import('./microsoft-graph-mail');
await markGraphEmailAsRead(graphCheck.mailCredentialId, emailId, isRead);
return true;
} catch (error) {
logger.error('[EMAIL] Error marking email as read/unread via Graph API', {
userId,
emailId,
isRead,
error: error instanceof Error ? error.message : String(error),
});
return false;
}
}
// Use IMAP for non-Microsoft accounts or numeric UIDs
// Validate UID format for IMAP
if (!/^\d+$/.test(emailId)) {
throw new Error('Invalid email ID format: must be a numeric UID for IMAP accounts');
}
const client = await getImapConnection(userId, effectiveAccountId);
try {