Fondation
This commit is contained in:
parent
c341468996
commit
c4267a0eeb
@ -527,7 +527,7 @@ export const authOptions: NextAuthOptions = {
|
|||||||
session.refreshToken = token.refreshToken as string | undefined;
|
session.refreshToken = token.refreshToken as string | undefined;
|
||||||
|
|
||||||
logger.debug('[SESSION_CALLBACK] Session created', {
|
logger.debug('[SESSION_CALLBACK] Session created', {
|
||||||
userId: session.user.id,
|
userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
|
||||||
hasEmail: !!session.user.email,
|
hasEmail: !!session.user.email,
|
||||||
rolesCount: session.user.role.length,
|
rolesCount: session.user.role.length,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -36,7 +36,7 @@ export async function GET(req: NextRequest) {
|
|||||||
const cachedData = await getCachedCalendarData(session.user.id);
|
const cachedData = await getCachedCalendarData(session.user.id);
|
||||||
if (cachedData) {
|
if (cachedData) {
|
||||||
logger.debug('[CALENDAR] Using cached calendar data', {
|
logger.debug('[CALENDAR] Using cached calendar data', {
|
||||||
userId: session.user.id,
|
userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
|
||||||
calendarCount: cachedData.length,
|
calendarCount: cachedData.length,
|
||||||
});
|
});
|
||||||
return NextResponse.json(cachedData);
|
return NextResponse.json(cachedData);
|
||||||
@ -45,7 +45,7 @@ export async function GET(req: NextRequest) {
|
|||||||
|
|
||||||
// If no cache or forcing refresh, fetch from database
|
// If no cache or forcing refresh, fetch from database
|
||||||
logger.debug('[CALENDAR] Fetching calendar data from database', {
|
logger.debug('[CALENDAR] Fetching calendar data from database', {
|
||||||
userId: session.user.id,
|
userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Ensure user has a default private calendar (created automatically if missing)
|
// Ensure user has a default private calendar (created automatically if missing)
|
||||||
@ -70,7 +70,7 @@ export async function GET(req: NextRequest) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
logger.debug('[CALENDAR] Created default private calendar', {
|
logger.debug('[CALENDAR] Created default private calendar', {
|
||||||
userId: session.user.id,
|
userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,7 +201,7 @@ export async function GET(req: NextRequest) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
logger.debug('[CALENDAR] Fetched calendars with events', {
|
logger.debug('[CALENDAR] Fetched calendars with events', {
|
||||||
userId: session.user.id,
|
userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
|
||||||
personalCount: filteredPersonalCalendars.length,
|
personalCount: filteredPersonalCalendars.length,
|
||||||
missionCount: missionCalendars.length,
|
missionCount: missionCalendars.length,
|
||||||
totalCount: sortedCalendars.length,
|
totalCount: sortedCalendars.length,
|
||||||
|
|||||||
@ -58,12 +58,17 @@ async function ensureUserExists(session: any): Promise<void> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (existingUser) {
|
if (existingUser) {
|
||||||
logger.debug('[COURRIER_ACCOUNT] User already exists', { userId });
|
logger.debug('[COURRIER_ACCOUNT] User already exists', {
|
||||||
|
userIdHash: Buffer.from(userId).toString('base64').slice(0, 12),
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// User doesn't exist, create it
|
// User doesn't exist, create it
|
||||||
logger.debug('[COURRIER_ACCOUNT] User not found, creating from session data', { userId, email: userEmail.substring(0, 5) + '***' });
|
logger.debug('[COURRIER_ACCOUNT] User not found, creating from session data', {
|
||||||
|
userIdHash: Buffer.from(userId).toString('base64').slice(0, 12),
|
||||||
|
emailHash: Buffer.from(userEmail.toLowerCase()).toString('base64').slice(0, 12),
|
||||||
|
});
|
||||||
|
|
||||||
// Generate a temporary random password (not used for auth, Keycloak handles that)
|
// Generate a temporary random password (not used for auth, Keycloak handles that)
|
||||||
const tempPassword = await bcrypt.hash(Math.random().toString(36).slice(-10), 10);
|
const tempPassword = await bcrypt.hash(Math.random().toString(36).slice(-10), 10);
|
||||||
@ -78,15 +83,20 @@ async function ensureUserExists(session: any): Promise<void> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.debug('[COURRIER_ACCOUNT] Successfully created user', { userId, email: userEmail.substring(0, 5) + '***' });
|
logger.debug('[COURRIER_ACCOUNT] Successfully created user', {
|
||||||
|
userIdHash: Buffer.from(userId).toString('base64').slice(0, 12),
|
||||||
|
emailHash: Buffer.from(userEmail.toLowerCase()).toString('base64').slice(0, 12),
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('[COURRIER_ACCOUNT] Error ensuring user exists', {
|
logger.error('[COURRIER_ACCOUNT] Error ensuring user exists', {
|
||||||
userId,
|
userIdHash: Buffer.from(userId).toString('base64').slice(0, 12),
|
||||||
error: error instanceof Error ? error.message : String(error)
|
error: error instanceof Error ? error.message : String(error)
|
||||||
});
|
});
|
||||||
// If it's a unique constraint error, user might have been created by another request
|
// If it's a unique constraint error, user might have been created by another request
|
||||||
if (error instanceof Error && error.message.includes('Unique constraint')) {
|
if (error instanceof Error && error.message.includes('Unique constraint')) {
|
||||||
logger.debug('[COURRIER_ACCOUNT] User may have been created by concurrent request', { userId });
|
logger.debug('[COURRIER_ACCOUNT] User may have been created by concurrent request', {
|
||||||
|
userIdHash: Buffer.from(userId).toString('base64').slice(0, 12),
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw error;
|
throw error;
|
||||||
|
|||||||
@ -46,11 +46,11 @@ export async function GET(request: Request) {
|
|||||||
// CRITICAL FIX: Log exact parameters received by the API
|
// CRITICAL FIX: Log exact parameters received by the API
|
||||||
logger.debug('[COURRIER_API] Received request', {
|
logger.debug('[COURRIER_API] Received request', {
|
||||||
folder,
|
folder,
|
||||||
accountId,
|
accountIdHash: accountId ? Buffer.from(accountId).toString('base64').slice(0, 12) : null,
|
||||||
page,
|
page,
|
||||||
checkOnly,
|
checkOnly,
|
||||||
refresh,
|
refresh,
|
||||||
userId: session.user.id
|
userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
|
||||||
});
|
});
|
||||||
|
|
||||||
// CRITICAL FIX: More robust parameter normalization
|
// CRITICAL FIX: More robust parameter normalization
|
||||||
@ -74,15 +74,15 @@ export async function GET(request: Request) {
|
|||||||
// CRITICAL FIX: Enhanced logging for parameter resolution
|
// CRITICAL FIX: Enhanced logging for parameter resolution
|
||||||
logger.debug('[COURRIER_API] Using normalized parameters', {
|
logger.debug('[COURRIER_API] Using normalized parameters', {
|
||||||
folder: normalizedFolder,
|
folder: normalizedFolder,
|
||||||
accountId: effectiveAccountId,
|
accountIdHash: Buffer.from(effectiveAccountId).toString('base64').slice(0, 12),
|
||||||
userId: session.user.id
|
userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
|
||||||
});
|
});
|
||||||
|
|
||||||
// If refresh=true, invalidate cache before fetching
|
// If refresh=true, invalidate cache before fetching
|
||||||
if (refresh) {
|
if (refresh) {
|
||||||
logger.debug('[COURRIER_API] Refresh requested - invalidating cache', {
|
logger.debug('[COURRIER_API] Refresh requested - invalidating cache', {
|
||||||
userId: session.user.id,
|
userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
|
||||||
accountId: effectiveAccountId,
|
accountIdHash: Buffer.from(effectiveAccountId).toString('base64').slice(0, 12),
|
||||||
folder: normalizedFolder
|
folder: normalizedFolder
|
||||||
});
|
});
|
||||||
await invalidateFolderCache(session.user.id, effectiveAccountId, normalizedFolder);
|
await invalidateFolderCache(session.user.id, effectiveAccountId, normalizedFolder);
|
||||||
@ -92,8 +92,8 @@ export async function GET(request: Request) {
|
|||||||
if (!searchQuery && !checkOnly && !refresh) {
|
if (!searchQuery && !checkOnly && !refresh) {
|
||||||
// CRITICAL FIX: Use consistent cache key format with the correct account ID
|
// CRITICAL FIX: Use consistent cache key format with the correct account ID
|
||||||
logger.debug('[COURRIER_API] Checking Redis cache', {
|
logger.debug('[COURRIER_API] Checking Redis cache', {
|
||||||
userId: session.user.id,
|
userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
|
||||||
accountId: effectiveAccountId,
|
accountIdHash: Buffer.from(effectiveAccountId).toString('base64').slice(0, 12),
|
||||||
folder: normalizedFolder,
|
folder: normalizedFolder,
|
||||||
page,
|
page,
|
||||||
perPage
|
perPage
|
||||||
@ -107,8 +107,8 @@ export async function GET(request: Request) {
|
|||||||
);
|
);
|
||||||
if (cachedEmails) {
|
if (cachedEmails) {
|
||||||
logger.debug('[COURRIER_API] Using Redis cached emails', {
|
logger.debug('[COURRIER_API] Using Redis cached emails', {
|
||||||
userId: session.user.id,
|
userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
|
||||||
accountId: effectiveAccountId,
|
accountIdHash: Buffer.from(effectiveAccountId).toString('base64').slice(0, 12),
|
||||||
folder: normalizedFolder,
|
folder: normalizedFolder,
|
||||||
page,
|
page,
|
||||||
perPage
|
perPage
|
||||||
@ -118,8 +118,8 @@ export async function GET(request: Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.debug('[COURRIER_API] Redis cache miss, fetching from IMAP', {
|
logger.debug('[COURRIER_API] Redis cache miss, fetching from IMAP', {
|
||||||
userId: session.user.id,
|
userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
|
||||||
accountId: effectiveAccountId,
|
accountIdHash: Buffer.from(effectiveAccountId).toString('base64').slice(0, 12),
|
||||||
folder: normalizedFolder,
|
folder: normalizedFolder,
|
||||||
page,
|
page,
|
||||||
perPage
|
perPage
|
||||||
@ -139,8 +139,8 @@ export async function GET(request: Request) {
|
|||||||
// CRITICAL FIX: Log when emails are returned from IMAP
|
// CRITICAL FIX: Log when emails are returned from IMAP
|
||||||
logger.debug('[COURRIER_API] Successfully fetched emails from IMAP', {
|
logger.debug('[COURRIER_API] Successfully fetched emails from IMAP', {
|
||||||
count: emailsResult.emails.length,
|
count: emailsResult.emails.length,
|
||||||
accountId: effectiveAccountId,
|
accountIdHash: Buffer.from(effectiveAccountId).toString('base64').slice(0, 12),
|
||||||
userId: session.user.id
|
userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
|
||||||
});
|
});
|
||||||
|
|
||||||
// The result is already cached in the getEmails function (if not checkOnly)
|
// The result is already cached in the getEmails function (if not checkOnly)
|
||||||
@ -148,7 +148,7 @@ export async function GET(request: Request) {
|
|||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
logger.error('[COURRIER_API] Error fetching emails', {
|
logger.error('[COURRIER_API] Error fetching emails', {
|
||||||
error: error instanceof Error ? error.message : String(error),
|
error: error instanceof Error ? error.message : String(error),
|
||||||
userId: session?.user?.id
|
userIdHash: session?.user?.id ? Buffer.from(session.user.id).toString('base64').slice(0, 12) : null,
|
||||||
});
|
});
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ error: "Failed to fetch emails", message: error.message },
|
{ error: "Failed to fetch emails", message: error.message },
|
||||||
@ -180,8 +180,8 @@ export async function POST(request: Request) {
|
|||||||
|
|
||||||
// Log the cache invalidation operation
|
// Log the cache invalidation operation
|
||||||
logger.debug('[COURRIER_API] Invalidating cache', {
|
logger.debug('[COURRIER_API] Invalidating cache', {
|
||||||
userId: session.user.id,
|
userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
|
||||||
accountId: effectiveAccountId,
|
accountIdHash: Buffer.from(effectiveAccountId).toString('base64').slice(0, 12),
|
||||||
folder: normalizedFolder || 'all folders'
|
folder: normalizedFolder || 'all folders'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -151,7 +151,7 @@ export async function GET() {
|
|||||||
|
|
||||||
// Get user with their accounts
|
// Get user with their accounts
|
||||||
logger.debug('[COURRIER_SESSION] Fetching user with ID', {
|
logger.debug('[COURRIER_SESSION] Fetching user with ID', {
|
||||||
userId: session.user.id,
|
userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
|
||||||
});
|
});
|
||||||
const user = await prisma.user.findUnique({
|
const user = await prisma.user.findUnique({
|
||||||
where: { id: session.user.id },
|
where: { id: session.user.id },
|
||||||
@ -171,7 +171,7 @@ export async function GET() {
|
|||||||
const accounts = (user.mailCredentials || []) as MailCredentials[];
|
const accounts = (user.mailCredentials || []) as MailCredentials[];
|
||||||
if (accounts.length === 0) {
|
if (accounts.length === 0) {
|
||||||
logger.debug('[COURRIER_SESSION] No email accounts found for user', {
|
logger.debug('[COURRIER_SESSION] No email accounts found for user', {
|
||||||
userId: session.user.id,
|
userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
|
||||||
});
|
});
|
||||||
return NextResponse.json({
|
return NextResponse.json({
|
||||||
authenticated: true,
|
authenticated: true,
|
||||||
@ -182,9 +182,8 @@ export async function GET() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.debug('[COURRIER_SESSION] Found accounts for user', {
|
logger.debug('[COURRIER_SESSION] Found accounts for user', {
|
||||||
userId: session.user.id,
|
userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
|
||||||
count: accounts.length,
|
count: accounts.length,
|
||||||
emails: accounts.map(a => a.email),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Fetch folders for each account
|
// Fetch folders for each account
|
||||||
|
|||||||
@ -92,7 +92,7 @@ export async function GET(request: Request) {
|
|||||||
const username = session.user.email.split('@')[0];
|
const username = session.user.email.split('@')[0];
|
||||||
if (!username) {
|
if (!username) {
|
||||||
logger.error('[ROCKET_CHAT] No username found in session email', {
|
logger.error('[ROCKET_CHAT] No username found in session email', {
|
||||||
email: session.user.email,
|
emailHash: Buffer.from(session.user.email.toLowerCase()).toString('base64').slice(0, 12),
|
||||||
});
|
});
|
||||||
return NextResponse.json({ messages: [] }, { status: 200 });
|
return NextResponse.json({ messages: [] }, { status: 200 });
|
||||||
}
|
}
|
||||||
@ -123,15 +123,13 @@ export async function GET(request: Request) {
|
|||||||
|
|
||||||
if (!currentUser) {
|
if (!currentUser) {
|
||||||
logger.error('[ROCKET_CHAT] User not found in users list', {
|
logger.error('[ROCKET_CHAT] User not found in users list', {
|
||||||
username,
|
emailHash: Buffer.from(session.user.email.toLowerCase()).toString('base64').slice(0, 12),
|
||||||
email: session.user.email,
|
|
||||||
});
|
});
|
||||||
return NextResponse.json({ messages: [] }, { status: 200 });
|
return NextResponse.json({ messages: [] }, { status: 200 });
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug('[ROCKET_CHAT] Found user', {
|
logger.debug('[ROCKET_CHAT] Found user', {
|
||||||
username: currentUser.username,
|
emailHash: Buffer.from(session.user.email.toLowerCase()).toString('base64').slice(0, 12),
|
||||||
id: currentUser._id,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Step 3: Create a token for the current user
|
// Step 3: Create a token for the current user
|
||||||
@ -207,8 +205,7 @@ export async function GET(request: Request) {
|
|||||||
sum + (sub.unread || 0), 0);
|
sum + (sub.unread || 0), 0);
|
||||||
|
|
||||||
logger.debug('[ROCKET_CHAT] Filtered user subscriptions', {
|
logger.debug('[ROCKET_CHAT] Filtered user subscriptions', {
|
||||||
userId: currentUser._id,
|
emailHash: Buffer.from(session.user.email.toLowerCase()).toString('base64').slice(0, 12),
|
||||||
username: currentUser.username,
|
|
||||||
totalSubscriptions: userSubscriptions.length,
|
totalSubscriptions: userSubscriptions.length,
|
||||||
totalUnreadCount: totalUnreadCount,
|
totalUnreadCount: totalUnreadCount,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -190,21 +190,23 @@ export async function getImapConnection(
|
|||||||
totalConnectionRequests++;
|
totalConnectionRequests++;
|
||||||
|
|
||||||
logger.debug('[IMAP] getImapConnection called', {
|
logger.debug('[IMAP] getImapConnection called', {
|
||||||
userId,
|
userIdHash: Buffer.from(userId).toString('base64').slice(0, 12),
|
||||||
accountId: accountId ?? 'default',
|
accountIdHash: accountId ? Buffer.from(accountId).toString('base64').slice(0, 12) : 'default',
|
||||||
});
|
});
|
||||||
|
|
||||||
// Special handling for 'default' accountId - find the first available account
|
// Special handling for 'default' accountId - find the first available account
|
||||||
if (!accountId || accountId === 'default') {
|
if (!accountId || accountId === 'default') {
|
||||||
logger.debug('[IMAP] Resolving default accountId', { userId });
|
logger.debug('[IMAP] Resolving default accountId', {
|
||||||
|
userIdHash: Buffer.from(userId).toString('base64').slice(0, 12),
|
||||||
|
});
|
||||||
|
|
||||||
// Try getting the account ID from cache to avoid database query
|
// Try getting the account ID from cache to avoid database query
|
||||||
const sessionData = await getCachedImapSession(userId);
|
const sessionData = await getCachedImapSession(userId);
|
||||||
if (sessionData && sessionData.defaultAccountId) {
|
if (sessionData && sessionData.defaultAccountId) {
|
||||||
accountId = sessionData.defaultAccountId;
|
accountId = sessionData.defaultAccountId;
|
||||||
logger.debug('[IMAP] Using cached default account ID', {
|
logger.debug('[IMAP] Using cached default account ID', {
|
||||||
userId,
|
userIdHash: Buffer.from(userId).toString('base64').slice(0, 12),
|
||||||
accountId,
|
accountIdHash: Buffer.from(accountId).toString('base64').slice(0, 12),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Query to find all accounts for this user
|
// Query to find all accounts for this user
|
||||||
@ -217,9 +219,8 @@ export async function getImapConnection(
|
|||||||
if (accounts && accounts.length > 0) {
|
if (accounts && accounts.length > 0) {
|
||||||
const firstAccount = accounts[0];
|
const firstAccount = accounts[0];
|
||||||
logger.debug('[IMAP] Using first available account from DB', {
|
logger.debug('[IMAP] Using first available account from DB', {
|
||||||
userId,
|
userIdHash: Buffer.from(userId).toString('base64').slice(0, 12),
|
||||||
accountId: firstAccount.id,
|
accountIdHash: Buffer.from(firstAccount.id).toString('base64').slice(0, 12),
|
||||||
email: firstAccount.email,
|
|
||||||
});
|
});
|
||||||
accountId = firstAccount.id;
|
accountId = firstAccount.id;
|
||||||
|
|
||||||
@ -304,21 +305,23 @@ export async function getImapConnection(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If we get here, we need a new connection
|
// If we get here, we need a new connection
|
||||||
logger.debug('[IMAP] Creating new connection', { connectionKey });
|
logger.debug('[IMAP] Creating new connection', {
|
||||||
|
connectionKeyHash: Buffer.from(connectionKey).toString('base64').slice(0, 12),
|
||||||
|
});
|
||||||
|
|
||||||
// First try to get credentials from Redis cache
|
// First try to get credentials from Redis cache
|
||||||
let credentials = await getCachedEmailCredentials(userId, accountId);
|
let credentials = await getCachedEmailCredentials(userId, accountId);
|
||||||
logger.debug('[IMAP] Retrieved credentials from Redis cache', {
|
logger.debug('[IMAP] Retrieved credentials from Redis cache', {
|
||||||
userId,
|
userIdHash: Buffer.from(userId).toString('base64').slice(0, 12),
|
||||||
accountId,
|
accountIdHash: accountId ? Buffer.from(accountId).toString('base64').slice(0, 12) : 'default',
|
||||||
found: !!credentials,
|
found: !!credentials,
|
||||||
});
|
});
|
||||||
|
|
||||||
// If not in cache, get from database and cache them
|
// If not in cache, get from database and cache them
|
||||||
if (!credentials) {
|
if (!credentials) {
|
||||||
logger.debug('[IMAP] Credentials not found in cache, querying database', {
|
logger.debug('[IMAP] Credentials not found in cache, querying database', {
|
||||||
userId,
|
userIdHash: Buffer.from(userId).toString('base64').slice(0, 12),
|
||||||
accountId,
|
accountIdHash: accountId ? Buffer.from(accountId).toString('base64').slice(0, 12) : 'default',
|
||||||
});
|
});
|
||||||
|
|
||||||
// Fetch directly from database
|
// Fetch directly from database
|
||||||
@ -333,17 +336,16 @@ export async function getImapConnection(
|
|||||||
|
|
||||||
if (!dbCredentials) {
|
if (!dbCredentials) {
|
||||||
logger.error('[IMAP] No credentials found for user', {
|
logger.error('[IMAP] No credentials found for user', {
|
||||||
userId,
|
userIdHash: Buffer.from(userId).toString('base64').slice(0, 12),
|
||||||
accountId,
|
accountIdHash: accountId ? Buffer.from(accountId).toString('base64').slice(0, 12) : 'default',
|
||||||
});
|
});
|
||||||
totalConnectionErrors++;
|
totalConnectionErrors++;
|
||||||
throw new Error('Email account credentials not found');
|
throw new Error('Email account credentials not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug('[IMAP] Database lookup returned credentials', {
|
logger.debug('[IMAP] Database lookup returned credentials', {
|
||||||
userId,
|
userIdHash: Buffer.from(userId).toString('base64').slice(0, 12),
|
||||||
accountId,
|
accountIdHash: accountId ? Buffer.from(accountId).toString('base64').slice(0, 12) : 'default',
|
||||||
email: dbCredentials.email,
|
|
||||||
hasPassword: !!dbCredentials.password,
|
hasPassword: !!dbCredentials.password,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -523,7 +525,7 @@ async function createImapConnection(credentials: EmailCredentials, connectionKey
|
|||||||
const extendedCreds = credentials as EmailCredentialsExtended;
|
const extendedCreds = credentials as EmailCredentialsExtended;
|
||||||
|
|
||||||
logger.debug('[IMAP] Creating ImapFlow client with credentials metadata', {
|
logger.debug('[IMAP] Creating ImapFlow client with credentials metadata', {
|
||||||
email: extendedCreds.email,
|
emailHash: Buffer.from(extendedCreds.email || '').toString('base64').slice(0, 12),
|
||||||
host: extendedCreds.host,
|
host: extendedCreds.host,
|
||||||
port: extendedCreds.port,
|
port: extendedCreds.port,
|
||||||
hasPassword: !!extendedCreds.password,
|
hasPassword: !!extendedCreds.password,
|
||||||
@ -537,7 +539,9 @@ async function createImapConnection(credentials: EmailCredentials, connectionKey
|
|||||||
|
|
||||||
// Check if we have valid OAuth tokens
|
// Check if we have valid OAuth tokens
|
||||||
if (extendedCreds.useOAuth && extendedCreds.accessToken) {
|
if (extendedCreds.useOAuth && extendedCreds.accessToken) {
|
||||||
logger.debug('[IMAP] Using XOAUTH2 authentication', { connectionKey });
|
logger.debug('[IMAP] Using XOAUTH2 authentication', {
|
||||||
|
connectionKeyHash: Buffer.from(connectionKey).toString('base64').slice(0, 12),
|
||||||
|
});
|
||||||
|
|
||||||
// Set auth parameters for ImapFlow
|
// Set auth parameters for ImapFlow
|
||||||
authParams = {
|
authParams = {
|
||||||
@ -545,10 +549,14 @@ async function createImapConnection(credentials: EmailCredentials, connectionKey
|
|||||||
accessToken: extendedCreds.accessToken
|
accessToken: extendedCreds.accessToken
|
||||||
};
|
};
|
||||||
|
|
||||||
logger.debug('[IMAP] XOAUTH2 auth configured', { connectionKey });
|
logger.debug('[IMAP] XOAUTH2 auth configured', {
|
||||||
|
connectionKeyHash: Buffer.from(connectionKey).toString('base64').slice(0, 12),
|
||||||
|
});
|
||||||
} else if (extendedCreds.password) {
|
} else if (extendedCreds.password) {
|
||||||
// Use regular password authentication
|
// Use regular password authentication
|
||||||
logger.debug('[IMAP] Using password authentication', { connectionKey });
|
logger.debug('[IMAP] Using password authentication', {
|
||||||
|
connectionKeyHash: Buffer.from(connectionKey).toString('base64').slice(0, 12),
|
||||||
|
});
|
||||||
authParams = {
|
authParams = {
|
||||||
user: extendedCreds.email,
|
user: extendedCreds.email,
|
||||||
pass: extendedCreds.password
|
pass: extendedCreds.password
|
||||||
@ -928,11 +936,11 @@ export async function getEmails(
|
|||||||
): Promise<EmailListResult> {
|
): Promise<EmailListResult> {
|
||||||
// Normalize folder name and handle account ID
|
// Normalize folder name and handle account ID
|
||||||
logger.debug('[EMAIL] getEmails called', {
|
logger.debug('[EMAIL] getEmails called', {
|
||||||
userId,
|
userIdHash: Buffer.from(userId).toString('base64').slice(0, 12),
|
||||||
folder,
|
folder,
|
||||||
page,
|
page,
|
||||||
perPage,
|
perPage,
|
||||||
accountId: accountId || 'default',
|
accountIdHash: accountId ? Buffer.from(accountId).toString('base64').slice(0, 12) : 'default',
|
||||||
checkOnly,
|
checkOnly,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -20,7 +20,9 @@ export async function ensureFreshToken(
|
|||||||
): Promise<{ accessToken: string; success: boolean }> {
|
): Promise<{ accessToken: string; success: boolean }> {
|
||||||
try {
|
try {
|
||||||
// Try Redis first (fast path)
|
// Try Redis first (fast path)
|
||||||
logger.debug('[TOKEN_REFRESH] Checking if token refresh is needed', { email: email.substring(0, 5) + '***' });
|
logger.debug('[TOKEN_REFRESH] Checking if token refresh is needed', {
|
||||||
|
emailHash: Buffer.from(email.toLowerCase()).toString('base64').slice(0, 12),
|
||||||
|
});
|
||||||
const redis = getRedisClient();
|
const redis = getRedisClient();
|
||||||
const key = KEYS.CREDENTIALS(userId, email);
|
const key = KEYS.CREDENTIALS(userId, email);
|
||||||
let credStr = await redis.get(key);
|
let credStr = await redis.get(key);
|
||||||
@ -30,7 +32,9 @@ export async function ensureFreshToken(
|
|||||||
creds = JSON.parse(credStr);
|
creds = JSON.parse(credStr);
|
||||||
} else {
|
} else {
|
||||||
// Redis cache miss - fallback to Prisma database
|
// Redis cache miss - fallback to Prisma database
|
||||||
logger.debug('[TOKEN_REFRESH] No credentials in Redis, checking Prisma', { email: email.substring(0, 5) + '***' });
|
logger.debug('[TOKEN_REFRESH] No credentials in Redis, checking Prisma', {
|
||||||
|
emailHash: Buffer.from(email.toLowerCase()).toString('base64').slice(0, 12),
|
||||||
|
});
|
||||||
const account = await prisma.mailCredentials.findFirst({
|
const account = await prisma.mailCredentials.findFirst({
|
||||||
where: {
|
where: {
|
||||||
userId: userId,
|
userId: userId,
|
||||||
@ -54,16 +58,22 @@ export async function ensureFreshToken(
|
|||||||
|
|
||||||
// Re-populate Redis cache
|
// Re-populate Redis cache
|
||||||
await redis.set(key, JSON.stringify(creds), 'EX', 86400);
|
await redis.set(key, JSON.stringify(creds), 'EX', 86400);
|
||||||
logger.debug('[TOKEN_REFRESH] Recovered credentials from Prisma and cached in Redis', { email: email.substring(0, 5) + '***' });
|
logger.debug('[TOKEN_REFRESH] Recovered credentials from Prisma and cached in Redis', {
|
||||||
|
emailHash: Buffer.from(email.toLowerCase()).toString('base64').slice(0, 12),
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
logger.debug('[TOKEN_REFRESH] No OAuth credentials found in database', { email: email.substring(0, 5) + '***' });
|
logger.debug('[TOKEN_REFRESH] No OAuth credentials found in database', {
|
||||||
|
emailHash: Buffer.from(email.toLowerCase()).toString('base64').slice(0, 12),
|
||||||
|
});
|
||||||
return { accessToken: '', success: false };
|
return { accessToken: '', success: false };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If not OAuth or missing refresh token, return failure
|
// If not OAuth or missing refresh token, return failure
|
||||||
if (!creds.useOAuth || !creds.refreshToken) {
|
if (!creds.useOAuth || !creds.refreshToken) {
|
||||||
logger.debug('[TOKEN_REFRESH] Account not using OAuth or missing refresh token', { email: email.substring(0, 5) + '***' });
|
logger.debug('[TOKEN_REFRESH] Account not using OAuth or missing refresh token', {
|
||||||
|
emailHash: Buffer.from(email.toLowerCase()).toString('base64').slice(0, 12),
|
||||||
|
});
|
||||||
return { accessToken: '', success: false };
|
return { accessToken: '', success: false };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user