SignIn
This commit is contained in:
parent
54802eaa4f
commit
a7153c3b26
@ -1314,10 +1314,33 @@ export async function getEmailContent(
|
|||||||
const { fetchGraphEmail } = await import('./microsoft-graph-mail');
|
const { fetchGraphEmail } = await import('./microsoft-graph-mail');
|
||||||
const graphMessage = await fetchGraphEmail(graphCheck.mailCredentialId, emailId);
|
const graphMessage = await fetchGraphEmail(graphCheck.mailCredentialId, emailId);
|
||||||
|
|
||||||
|
logger.debug('[EMAIL] Graph message received', {
|
||||||
|
userId,
|
||||||
|
emailId,
|
||||||
|
hasAttachments: graphMessage.hasAttachments,
|
||||||
|
attachmentsCount: graphMessage.attachments?.length || 0,
|
||||||
|
attachmentsPresent: !!graphMessage.attachments,
|
||||||
|
});
|
||||||
|
|
||||||
// Convert Graph attachments to EmailAttachment format
|
// Convert Graph attachments to EmailAttachment format
|
||||||
const attachments: EmailAttachment[] = [];
|
const attachments: EmailAttachment[] = [];
|
||||||
if (graphMessage.attachments && Array.isArray(graphMessage.attachments)) {
|
if (graphMessage.attachments && Array.isArray(graphMessage.attachments)) {
|
||||||
|
logger.debug('[EMAIL] Processing attachments from Graph API', {
|
||||||
|
userId,
|
||||||
|
emailId,
|
||||||
|
count: graphMessage.attachments.length,
|
||||||
|
});
|
||||||
|
|
||||||
for (const graphAtt of graphMessage.attachments) {
|
for (const graphAtt of graphMessage.attachments) {
|
||||||
|
logger.debug('[EMAIL] Processing attachment', {
|
||||||
|
userId,
|
||||||
|
emailId,
|
||||||
|
attachmentName: graphAtt.name,
|
||||||
|
hasContentBytes: !!graphAtt.contentBytes,
|
||||||
|
attachmentId: graphAtt.id,
|
||||||
|
size: graphAtt.size,
|
||||||
|
});
|
||||||
|
|
||||||
// Graph API attachments have contentBytes in base64
|
// Graph API attachments have contentBytes in base64
|
||||||
if (graphAtt.contentBytes) {
|
if (graphAtt.contentBytes) {
|
||||||
attachments.push({
|
attachments.push({
|
||||||
@ -1327,17 +1350,34 @@ export async function getEmailContent(
|
|||||||
content: graphAtt.contentBytes, // Already base64 from Graph API
|
content: graphAtt.contentBytes, // Already base64 from Graph API
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// If no contentBytes, it might be a reference attachment - store metadata only
|
// If no contentBytes, store metadata anyway - content will be fetched on demand
|
||||||
attachments.push({
|
attachments.push({
|
||||||
filename: graphAtt.name || 'attachment',
|
filename: graphAtt.name || 'attachment',
|
||||||
contentType: graphAtt.contentType || 'application/octet-stream',
|
contentType: graphAtt.contentType || 'application/octet-stream',
|
||||||
size: graphAtt.size || 0,
|
size: graphAtt.size || 0,
|
||||||
|
// Store attachment ID if available for later fetching
|
||||||
|
attachmentId: graphAtt.id,
|
||||||
// No content - will need to fetch separately via attachment ID
|
// No content - will need to fetch separately via attachment ID
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (graphMessage.hasAttachments) {
|
||||||
|
// Email has attachments but they weren't included in the response
|
||||||
|
// This can happen if the $select doesn't include attachments or if they need to be fetched separately
|
||||||
|
logger.warn('[EMAIL] Email has attachments but attachments array is missing', {
|
||||||
|
userId,
|
||||||
|
emailId,
|
||||||
|
hasAttachments: graphMessage.hasAttachments,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.debug('[EMAIL] Converted attachments', {
|
||||||
|
userId,
|
||||||
|
emailId,
|
||||||
|
attachmentsCount: attachments.length,
|
||||||
|
attachmentsWithContent: attachments.filter(a => a.content).length,
|
||||||
|
});
|
||||||
|
|
||||||
// Convert Graph message to EmailMessage format
|
// Convert Graph message to EmailMessage format
|
||||||
const email: EmailMessage = {
|
const email: EmailMessage = {
|
||||||
id: graphMessage.id,
|
id: graphMessage.id,
|
||||||
|
|||||||
@ -194,48 +194,97 @@ export async function fetchGraphEmail(
|
|||||||
try {
|
try {
|
||||||
const client = await getMicrosoftGraphClient(mailCredentialId);
|
const client = await getMicrosoftGraphClient(mailCredentialId);
|
||||||
|
|
||||||
|
// First, get the message without attachments to check if it has attachments
|
||||||
const response = await client.get(`/me/messages/${messageId}`, {
|
const response = await client.get(`/me/messages/${messageId}`, {
|
||||||
params: {
|
params: {
|
||||||
'$select': 'id,subject,from,toRecipients,ccRecipients,bccRecipients,body,bodyPreview,receivedDateTime,sentDateTime,isRead,hasAttachments,importance,flag,attachments',
|
'$select': 'id,subject,from,toRecipients,ccRecipients,bccRecipients,body,bodyPreview,receivedDateTime,sentDateTime,isRead,hasAttachments,importance,flag',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const message = response.data;
|
const message = response.data;
|
||||||
|
|
||||||
// If email has attachments but they don't have contentBytes, fetch them individually
|
logger.debug('Fetched email from Graph API', {
|
||||||
if (message.hasAttachments && message.attachments && Array.isArray(message.attachments)) {
|
messageId,
|
||||||
const attachmentsWithContent = await Promise.all(
|
hasAttachments: message.hasAttachments,
|
||||||
message.attachments.map(async (attachment: any) => {
|
mailCredentialIdHash: Buffer.from(mailCredentialId).toString('base64').slice(0, 12),
|
||||||
// If contentBytes is missing, fetch the attachment content
|
});
|
||||||
if (!attachment.contentBytes && attachment.id) {
|
|
||||||
try {
|
// If email has attachments, fetch them separately
|
||||||
logger.debug('Fetching attachment content from Graph API', {
|
// Microsoft Graph API sometimes doesn't include attachments in the initial response
|
||||||
messageId,
|
if (message.hasAttachments) {
|
||||||
attachmentId: attachment.id,
|
try {
|
||||||
attachmentName: attachment.name,
|
logger.debug('Fetching attachments list from Graph API', {
|
||||||
});
|
messageId,
|
||||||
|
mailCredentialIdHash: Buffer.from(mailCredentialId).toString('base64').slice(0, 12),
|
||||||
const attachmentData = await fetchGraphAttachment(mailCredentialId, messageId, attachment.id);
|
});
|
||||||
return {
|
|
||||||
...attachment,
|
// Fetch attachments separately
|
||||||
contentBytes: attachmentData.contentBytes,
|
const attachmentsResponse = await client.get(`/me/messages/${messageId}/attachments`, {
|
||||||
};
|
params: {
|
||||||
} catch (error) {
|
'$select': 'id,name,contentType,size,contentBytes,isInline',
|
||||||
logger.error('Error fetching attachment content', {
|
},
|
||||||
messageId,
|
});
|
||||||
attachmentId: attachment.id,
|
|
||||||
error: error instanceof Error ? error.message : String(error),
|
const attachments = attachmentsResponse.data.value || [];
|
||||||
});
|
|
||||||
// Return attachment without content if fetch fails
|
logger.debug('Fetched attachments from Graph API', {
|
||||||
|
messageId,
|
||||||
|
attachmentsCount: attachments.length,
|
||||||
|
mailCredentialIdHash: Buffer.from(mailCredentialId).toString('base64').slice(0, 12),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Process attachments - fetch content if not included
|
||||||
|
if (attachments.length > 0) {
|
||||||
|
const attachmentsWithContent = await Promise.all(
|
||||||
|
attachments.map(async (attachment: any) => {
|
||||||
|
// If contentBytes is missing, fetch the attachment content
|
||||||
|
if (!attachment.contentBytes && attachment.id) {
|
||||||
|
try {
|
||||||
|
logger.debug('Fetching attachment content from Graph API', {
|
||||||
|
messageId,
|
||||||
|
attachmentId: attachment.id,
|
||||||
|
attachmentName: attachment.name,
|
||||||
|
});
|
||||||
|
|
||||||
|
const attachmentData = await fetchGraphAttachment(mailCredentialId, messageId, attachment.id);
|
||||||
|
return {
|
||||||
|
...attachment,
|
||||||
|
contentBytes: attachmentData.contentBytes,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Error fetching attachment content', {
|
||||||
|
messageId,
|
||||||
|
attachmentId: attachment.id,
|
||||||
|
error: error instanceof Error ? error.message : String(error),
|
||||||
|
});
|
||||||
|
// Return attachment without content if fetch fails
|
||||||
|
return attachment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Already has contentBytes, return as-is
|
||||||
return attachment;
|
return attachment;
|
||||||
}
|
})
|
||||||
}
|
);
|
||||||
// Already has contentBytes, return as-is
|
|
||||||
return attachment;
|
message.attachments = attachmentsWithContent;
|
||||||
})
|
} else {
|
||||||
);
|
logger.warn('Email has hasAttachments=true but no attachments returned', {
|
||||||
|
messageId,
|
||||||
message.attachments = attachmentsWithContent;
|
mailCredentialIdHash: Buffer.from(mailCredentialId).toString('base64').slice(0, 12),
|
||||||
|
});
|
||||||
|
message.attachments = [];
|
||||||
|
}
|
||||||
|
} catch (attachmentsError: any) {
|
||||||
|
logger.error('Error fetching attachments from Graph API', {
|
||||||
|
messageId,
|
||||||
|
error: attachmentsError instanceof Error ? attachmentsError.message : String(attachmentsError),
|
||||||
|
status: attachmentsError.response?.status,
|
||||||
|
});
|
||||||
|
// Continue without attachments if fetch fails
|
||||||
|
message.attachments = [];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message.attachments = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return message;
|
return message;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user