SignIn
This commit is contained in:
parent
aadc8ebc6b
commit
54802eaa4f
@ -97,17 +97,60 @@ export async function GET(
|
|||||||
if (attachment.content) {
|
if (attachment.content) {
|
||||||
attachmentBuffer = Buffer.from(attachment.content, 'base64');
|
attachmentBuffer = Buffer.from(attachment.content, 'base64');
|
||||||
} else {
|
} else {
|
||||||
// Need to fetch from Graph API - this requires the attachment ID
|
// Need to fetch from Graph API - try to get attachment ID from email metadata
|
||||||
// For now, return error as we need to modify the email fetching to include attachment IDs
|
// First, re-fetch the email to get attachment IDs if available
|
||||||
logger.error('[ATTACHMENT] Graph API attachment requires ID but content not cached', {
|
logger.debug('[ATTACHMENT] Attachment content not cached, fetching from Graph API', {
|
||||||
|
emailId,
|
||||||
|
attachmentIndex: attachmentIdx,
|
||||||
|
mailCredentialIdHash: graphCheck.mailCredentialId ? Buffer.from(graphCheck.mailCredentialId).toString('base64').slice(0, 12) : null,
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Re-fetch the email with full attachment data
|
||||||
|
const { fetchGraphEmail, fetchGraphAttachment } = await import('@/lib/services/microsoft-graph-mail');
|
||||||
|
const graphMessage = await fetchGraphEmail(graphCheck.mailCredentialId, emailId);
|
||||||
|
|
||||||
|
if (!graphMessage.attachments || attachmentIdx >= graphMessage.attachments.length) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: "Attachment not found" },
|
||||||
|
{ status: 404 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const graphAttachment = graphMessage.attachments[attachmentIdx];
|
||||||
|
|
||||||
|
// If still no contentBytes, try fetching by attachment ID
|
||||||
|
if (!graphAttachment.contentBytes && graphAttachment.id) {
|
||||||
|
const attachmentData = await fetchGraphAttachment(
|
||||||
|
graphCheck.mailCredentialId,
|
||||||
|
emailId,
|
||||||
|
graphAttachment.id
|
||||||
|
);
|
||||||
|
attachmentBuffer = Buffer.from(attachmentData.contentBytes, 'base64');
|
||||||
|
} else if (graphAttachment.contentBytes) {
|
||||||
|
attachmentBuffer = Buffer.from(graphAttachment.contentBytes, 'base64');
|
||||||
|
} else {
|
||||||
|
logger.error('[ATTACHMENT] Graph API attachment has no content and no ID', {
|
||||||
emailId,
|
emailId,
|
||||||
attachmentIndex: attachmentIdx,
|
attachmentIndex: attachmentIdx,
|
||||||
});
|
});
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ error: "Attachment content not available. Please refresh the email to load attachment data." },
|
{ error: "Attachment content not available" },
|
||||||
{ status: 404 }
|
{ status: 404 }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} catch (fetchError) {
|
||||||
|
logger.error('[ATTACHMENT] Error fetching attachment from Graph API', {
|
||||||
|
emailId,
|
||||||
|
attachmentIndex: attachmentIdx,
|
||||||
|
error: fetchError instanceof Error ? fetchError.message : String(fetchError),
|
||||||
|
});
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: "Failed to fetch attachment from Microsoft Graph API" },
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('[ATTACHMENT] Error fetching Graph API attachment', {
|
logger.error('[ATTACHMENT] Error fetching Graph API attachment', {
|
||||||
emailId,
|
emailId,
|
||||||
|
|||||||
@ -185,6 +185,7 @@ export async function fetchGraphEmails(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch a single email by ID from Microsoft Graph
|
* Fetch a single email by ID from Microsoft Graph
|
||||||
|
* Automatically fetches attachment content if not included in initial response
|
||||||
*/
|
*/
|
||||||
export async function fetchGraphEmail(
|
export async function fetchGraphEmail(
|
||||||
mailCredentialId: string,
|
mailCredentialId: string,
|
||||||
@ -199,7 +200,45 @@ export async function fetchGraphEmail(
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return response.data;
|
const message = response.data;
|
||||||
|
|
||||||
|
// If email has attachments but they don't have contentBytes, fetch them individually
|
||||||
|
if (message.hasAttachments && message.attachments && Array.isArray(message.attachments)) {
|
||||||
|
const attachmentsWithContent = await Promise.all(
|
||||||
|
message.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;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
message.attachments = attachmentsWithContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return message;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
logger.error('Error fetching email from Microsoft Graph', {
|
logger.error('Error fetching email from Microsoft Graph', {
|
||||||
mailCredentialIdHash: Buffer.from(mailCredentialId).toString('base64').slice(0, 12),
|
mailCredentialIdHash: Buffer.from(mailCredentialId).toString('base64').slice(0, 12),
|
||||||
@ -385,3 +424,39 @@ export async function getGraphUnreadCount(
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch a single attachment by ID from a message using Microsoft Graph API
|
||||||
|
*/
|
||||||
|
export async function fetchGraphAttachment(
|
||||||
|
mailCredentialId: string,
|
||||||
|
messageId: string,
|
||||||
|
attachmentId: string
|
||||||
|
): Promise<{
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
contentType: string;
|
||||||
|
size: number;
|
||||||
|
contentBytes: string; // Base64 encoded content
|
||||||
|
}> {
|
||||||
|
try {
|
||||||
|
const client = await getMicrosoftGraphClient(mailCredentialId);
|
||||||
|
|
||||||
|
const response = await client.get(`/me/messages/${messageId}/attachments/${attachmentId}`);
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
} catch (error: any) {
|
||||||
|
logger.error('Error fetching attachment from Microsoft Graph', {
|
||||||
|
mailCredentialIdHash: Buffer.from(mailCredentialId).toString('base64').slice(0, 12),
|
||||||
|
messageId,
|
||||||
|
attachmentId,
|
||||||
|
error: error instanceof Error ? error.message : String(error),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (error.response?.status === 401 || error.response?.status === 403) {
|
||||||
|
throw new Error('Microsoft Graph API access denied - may need to re-authenticate with Mail.Read permissions');
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user