Agenda Sync refactor Mig Graph
This commit is contained in:
parent
60c5ce8e1a
commit
a6b63d723a
@ -989,12 +989,12 @@ export async function getEmails(
|
||||
folder,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
// Fall back to IMAP if Graph API fails
|
||||
logger.debug('[EMAIL] Falling back to IMAP after Graph API error');
|
||||
// Don't fall back to IMAP for Microsoft accounts - the token doesn't have IMAP permissions
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Use IMAP for non-Microsoft accounts or as fallback
|
||||
// Use IMAP for non-Microsoft accounts only
|
||||
// The getImapConnection function already handles 'default' accountId by finding the first available account
|
||||
const client = await getImapConnection(userId, accountId);
|
||||
|
||||
|
||||
@ -4,6 +4,8 @@ import { Notification, NotificationCount } from '@/lib/types/notification';
|
||||
import { getRedisClient } from '@/lib/redis';
|
||||
import { prisma } from '@/lib/prisma';
|
||||
import { getImapConnection } from '@/lib/services/email-service';
|
||||
import { getGraphUnreadCount, fetchGraphEmails } from '@/lib/services/microsoft-graph-mail';
|
||||
import { prisma } from '@/lib/prisma';
|
||||
|
||||
export class EmailAdapter implements NotificationAdapter {
|
||||
readonly sourceName = 'email';
|
||||
@ -44,53 +46,92 @@ export class EmailAdapter implements NotificationAdapter {
|
||||
for (const account of accounts) {
|
||||
const accountId = account.id;
|
||||
try {
|
||||
// Get IMAP connection for this account
|
||||
logger.debug('[EMAIL_ADAPTER] Processing account', {
|
||||
userId,
|
||||
accountId,
|
||||
email: account.email,
|
||||
});
|
||||
const client = await getImapConnection(userId, accountId);
|
||||
|
||||
// Check if this is a Microsoft account that should use Graph API
|
||||
const mailCredential = await prisma.mailCredentials.findUnique({
|
||||
where: { id: accountId },
|
||||
select: {
|
||||
id: true,
|
||||
host: true,
|
||||
use_oauth: true,
|
||||
refresh_token: true,
|
||||
},
|
||||
});
|
||||
|
||||
const isMicrosoftAccount = mailCredential &&
|
||||
mailCredential.host === 'outlook.office365.com' &&
|
||||
mailCredential.use_oauth &&
|
||||
mailCredential.refresh_token;
|
||||
|
||||
unreadCounts[accountId] = {};
|
||||
|
||||
// Standard folders to check (focus on INBOX for notifications)
|
||||
const standardFolders = ['INBOX'];
|
||||
|
||||
// Get mailboxes for this account to check if folders exist
|
||||
const mailboxes = await client.list();
|
||||
const availableFolders = mailboxes.map(mb => mb.path);
|
||||
|
||||
// Check each standard folder if it exists
|
||||
for (const folder of standardFolders) {
|
||||
// Skip if folder doesn't exist in this account
|
||||
if (!availableFolders.includes(folder) &&
|
||||
!availableFolders.some(f => f.toLowerCase() === folder.toLowerCase())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isMicrosoftAccount) {
|
||||
// Use Graph API for Microsoft accounts
|
||||
try {
|
||||
// Check folder status without opening it (more efficient)
|
||||
const status = await client.status(folder, { unseen: true });
|
||||
const unreadCount = await getGraphUnreadCount(mailCredential.id, 'Inbox');
|
||||
unreadCounts[accountId]['INBOX'] = unreadCount;
|
||||
|
||||
if (status && typeof status.unseen === 'number') {
|
||||
// Store the unread count
|
||||
unreadCounts[accountId][folder] = status.unseen;
|
||||
logger.debug('[EMAIL_ADAPTER] Unread count (Graph API)', {
|
||||
userId,
|
||||
accountId,
|
||||
folder: 'INBOX',
|
||||
unread: unreadCount,
|
||||
});
|
||||
} catch (graphError) {
|
||||
logger.error('[EMAIL_ADAPTER] Error getting unread count via Graph API', {
|
||||
userId,
|
||||
accountId,
|
||||
error: graphError instanceof Error ? graphError.message : String(graphError),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Use IMAP for non-Microsoft accounts
|
||||
const client = await getImapConnection(userId, accountId);
|
||||
|
||||
// Standard folders to check (focus on INBOX for notifications)
|
||||
const standardFolders = ['INBOX'];
|
||||
|
||||
// Get mailboxes for this account to check if folders exist
|
||||
const mailboxes = await client.list();
|
||||
const availableFolders = mailboxes.map(mb => mb.path);
|
||||
|
||||
// Check each standard folder if it exists
|
||||
for (const folder of standardFolders) {
|
||||
// Skip if folder doesn't exist in this account
|
||||
if (!availableFolders.includes(folder) &&
|
||||
!availableFolders.some(f => f.toLowerCase() === folder.toLowerCase())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
// Check folder status without opening it (more efficient)
|
||||
const status = await client.status(folder, { unseen: true });
|
||||
|
||||
logger.debug('[EMAIL_ADAPTER] Unread count', {
|
||||
if (status && typeof status.unseen === 'number') {
|
||||
// Store the unread count
|
||||
unreadCounts[accountId][folder] = status.unseen;
|
||||
|
||||
logger.debug('[EMAIL_ADAPTER] Unread count (IMAP)', {
|
||||
userId,
|
||||
accountId,
|
||||
folder,
|
||||
unread: status.unseen,
|
||||
});
|
||||
}
|
||||
} catch (folderError) {
|
||||
logger.error('[EMAIL_ADAPTER] Error getting unread count for folder', {
|
||||
userId,
|
||||
accountId,
|
||||
folder,
|
||||
unread: status.unseen,
|
||||
error: folderError instanceof Error ? folderError.message : String(folderError),
|
||||
});
|
||||
// Continue to next folder even if this one fails
|
||||
}
|
||||
} catch (folderError) {
|
||||
logger.error('[EMAIL_ADAPTER] Error getting unread count for folder', {
|
||||
userId,
|
||||
accountId,
|
||||
folder,
|
||||
error: folderError instanceof Error ? folderError.message : String(folderError),
|
||||
});
|
||||
// Continue to next folder even if this one fails
|
||||
}
|
||||
}
|
||||
} catch (accountError) {
|
||||
@ -227,6 +268,68 @@ export class EmailAdapter implements NotificationAdapter {
|
||||
// Use the same flow as getEmails() but filter for unread only
|
||||
for (const account of accounts) {
|
||||
try {
|
||||
// Check if this is a Microsoft account that should use Graph API
|
||||
const mailCredential = await prisma.mailCredentials.findUnique({
|
||||
where: { id: account.id },
|
||||
select: {
|
||||
id: true,
|
||||
host: true,
|
||||
use_oauth: true,
|
||||
refresh_token: true,
|
||||
},
|
||||
});
|
||||
|
||||
const isMicrosoftAccount = mailCredential &&
|
||||
mailCredential.host === 'outlook.office365.com' &&
|
||||
mailCredential.use_oauth &&
|
||||
mailCredential.refresh_token;
|
||||
|
||||
if (isMicrosoftAccount) {
|
||||
// Use Graph API for Microsoft accounts
|
||||
try {
|
||||
const graphResult = await fetchGraphEmails(
|
||||
mailCredential.id,
|
||||
'Inbox',
|
||||
limit * 3, // Get more than limit to have enough after filtering
|
||||
0,
|
||||
'isRead eq false' // Filter for unread only
|
||||
);
|
||||
|
||||
// Convert Graph messages to notifications
|
||||
for (const graphMessage of graphResult.value) {
|
||||
if (graphMessage.isRead) continue; // Double-check unread status
|
||||
|
||||
notifications.push({
|
||||
id: `email-${graphMessage.id}`,
|
||||
source: 'email',
|
||||
title: graphMessage.subject || '(No subject)',
|
||||
message: graphMessage.bodyPreview || '',
|
||||
timestamp: new Date(graphMessage.receivedDateTime),
|
||||
read: false,
|
||||
link: `/courrier/${account.id}?email=${graphMessage.id}`,
|
||||
metadata: {
|
||||
accountId: account.id,
|
||||
accountEmail: account.email,
|
||||
emailId: graphMessage.id,
|
||||
folder: 'INBOX',
|
||||
},
|
||||
});
|
||||
|
||||
if (notifications.length >= limit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (graphError) {
|
||||
logger.error('[EMAIL_ADAPTER] Error fetching notifications via Graph API', {
|
||||
userId,
|
||||
accountId: account.id,
|
||||
error: graphError instanceof Error ? graphError.message : String(graphError),
|
||||
});
|
||||
}
|
||||
continue; // Skip IMAP processing for Microsoft accounts
|
||||
}
|
||||
|
||||
// Use IMAP for non-Microsoft accounts
|
||||
const client = await getImapConnection(userId, account.id);
|
||||
|
||||
// Use the same approach as getEmails() - open mailbox first
|
||||
|
||||
Loading…
Reference in New Issue
Block a user