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;
|
||||
|
||||
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,
|
||||
rolesCount: session.user.role.length,
|
||||
});
|
||||
|
||||
@ -36,7 +36,7 @@ export async function GET(req: NextRequest) {
|
||||
const cachedData = await getCachedCalendarData(session.user.id);
|
||||
if (cachedData) {
|
||||
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,
|
||||
});
|
||||
return NextResponse.json(cachedData);
|
||||
@ -45,7 +45,7 @@ export async function GET(req: NextRequest) {
|
||||
|
||||
// If no cache or forcing refresh, fetch 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)
|
||||
@ -70,7 +70,7 @@ export async function GET(req: NextRequest) {
|
||||
}
|
||||
});
|
||||
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', {
|
||||
userId: session.user.id,
|
||||
userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
|
||||
personalCount: filteredPersonalCalendars.length,
|
||||
missionCount: missionCalendars.length,
|
||||
totalCount: sortedCalendars.length,
|
||||
|
||||
@ -58,12 +58,17 @@ async function ensureUserExists(session: any): Promise<void> {
|
||||
});
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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)
|
||||
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) {
|
||||
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)
|
||||
});
|
||||
// If it's a unique constraint error, user might have been created by another request
|
||||
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;
|
||||
}
|
||||
throw error;
|
||||
|
||||
@ -46,11 +46,11 @@ export async function GET(request: Request) {
|
||||
// CRITICAL FIX: Log exact parameters received by the API
|
||||
logger.debug('[COURRIER_API] Received request', {
|
||||
folder,
|
||||
accountId,
|
||||
accountIdHash: accountId ? Buffer.from(accountId).toString('base64').slice(0, 12) : null,
|
||||
page,
|
||||
checkOnly,
|
||||
refresh,
|
||||
userId: session.user.id
|
||||
userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
|
||||
});
|
||||
|
||||
// CRITICAL FIX: More robust parameter normalization
|
||||
@ -74,15 +74,15 @@ export async function GET(request: Request) {
|
||||
// CRITICAL FIX: Enhanced logging for parameter resolution
|
||||
logger.debug('[COURRIER_API] Using normalized parameters', {
|
||||
folder: normalizedFolder,
|
||||
accountId: effectiveAccountId,
|
||||
userId: session.user.id
|
||||
accountIdHash: Buffer.from(effectiveAccountId).toString('base64').slice(0, 12),
|
||||
userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
|
||||
});
|
||||
|
||||
// If refresh=true, invalidate cache before fetching
|
||||
if (refresh) {
|
||||
logger.debug('[COURRIER_API] Refresh requested - invalidating cache', {
|
||||
userId: session.user.id,
|
||||
accountId: effectiveAccountId,
|
||||
userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
|
||||
accountIdHash: Buffer.from(effectiveAccountId).toString('base64').slice(0, 12),
|
||||
folder: normalizedFolder
|
||||
});
|
||||
await invalidateFolderCache(session.user.id, effectiveAccountId, normalizedFolder);
|
||||
@ -92,8 +92,8 @@ export async function GET(request: Request) {
|
||||
if (!searchQuery && !checkOnly && !refresh) {
|
||||
// CRITICAL FIX: Use consistent cache key format with the correct account ID
|
||||
logger.debug('[COURRIER_API] Checking Redis cache', {
|
||||
userId: session.user.id,
|
||||
accountId: effectiveAccountId,
|
||||
userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
|
||||
accountIdHash: Buffer.from(effectiveAccountId).toString('base64').slice(0, 12),
|
||||
folder: normalizedFolder,
|
||||
page,
|
||||
perPage
|
||||
@ -107,8 +107,8 @@ export async function GET(request: Request) {
|
||||
);
|
||||
if (cachedEmails) {
|
||||
logger.debug('[COURRIER_API] Using Redis cached emails', {
|
||||
userId: session.user.id,
|
||||
accountId: effectiveAccountId,
|
||||
userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
|
||||
accountIdHash: Buffer.from(effectiveAccountId).toString('base64').slice(0, 12),
|
||||
folder: normalizedFolder,
|
||||
page,
|
||||
perPage
|
||||
@ -118,8 +118,8 @@ export async function GET(request: Request) {
|
||||
}
|
||||
|
||||
logger.debug('[COURRIER_API] Redis cache miss, fetching from IMAP', {
|
||||
userId: session.user.id,
|
||||
accountId: effectiveAccountId,
|
||||
userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
|
||||
accountIdHash: Buffer.from(effectiveAccountId).toString('base64').slice(0, 12),
|
||||
folder: normalizedFolder,
|
||||
page,
|
||||
perPage
|
||||
@ -139,8 +139,8 @@ export async function GET(request: Request) {
|
||||
// CRITICAL FIX: Log when emails are returned from IMAP
|
||||
logger.debug('[COURRIER_API] Successfully fetched emails from IMAP', {
|
||||
count: emailsResult.emails.length,
|
||||
accountId: effectiveAccountId,
|
||||
userId: session.user.id
|
||||
accountIdHash: Buffer.from(effectiveAccountId).toString('base64').slice(0, 12),
|
||||
userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
|
||||
});
|
||||
|
||||
// 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) {
|
||||
logger.error('[COURRIER_API] Error fetching emails', {
|
||||
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(
|
||||
{ error: "Failed to fetch emails", message: error.message },
|
||||
@ -180,8 +180,8 @@ export async function POST(request: Request) {
|
||||
|
||||
// Log the cache invalidation operation
|
||||
logger.debug('[COURRIER_API] Invalidating cache', {
|
||||
userId: session.user.id,
|
||||
accountId: effectiveAccountId,
|
||||
userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
|
||||
accountIdHash: Buffer.from(effectiveAccountId).toString('base64').slice(0, 12),
|
||||
folder: normalizedFolder || 'all folders'
|
||||
});
|
||||
|
||||
|
||||
@ -151,7 +151,7 @@ export async function GET() {
|
||||
|
||||
// Get user with their accounts
|
||||
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({
|
||||
where: { id: session.user.id },
|
||||
@ -171,7 +171,7 @@ export async function GET() {
|
||||
const accounts = (user.mailCredentials || []) as MailCredentials[];
|
||||
if (accounts.length === 0) {
|
||||
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({
|
||||
authenticated: true,
|
||||
@ -182,9 +182,8 @@ export async function GET() {
|
||||
}
|
||||
|
||||
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,
|
||||
emails: accounts.map(a => a.email),
|
||||
});
|
||||
|
||||
// Fetch folders for each account
|
||||
|
||||
@ -92,7 +92,7 @@ export async function GET(request: Request) {
|
||||
const username = session.user.email.split('@')[0];
|
||||
if (!username) {
|
||||
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 });
|
||||
}
|
||||
@ -123,15 +123,13 @@ export async function GET(request: Request) {
|
||||
|
||||
if (!currentUser) {
|
||||
logger.error('[ROCKET_CHAT] User not found in users list', {
|
||||
username,
|
||||
email: session.user.email,
|
||||
emailHash: Buffer.from(session.user.email.toLowerCase()).toString('base64').slice(0, 12),
|
||||
});
|
||||
return NextResponse.json({ messages: [] }, { status: 200 });
|
||||
}
|
||||
|
||||
logger.debug('[ROCKET_CHAT] Found user', {
|
||||
username: currentUser.username,
|
||||
id: currentUser._id,
|
||||
emailHash: Buffer.from(session.user.email.toLowerCase()).toString('base64').slice(0, 12),
|
||||
});
|
||||
|
||||
// Step 3: Create a token for the current user
|
||||
@ -207,8 +205,7 @@ export async function GET(request: Request) {
|
||||
sum + (sub.unread || 0), 0);
|
||||
|
||||
logger.debug('[ROCKET_CHAT] Filtered user subscriptions', {
|
||||
userId: currentUser._id,
|
||||
username: currentUser.username,
|
||||
emailHash: Buffer.from(session.user.email.toLowerCase()).toString('base64').slice(0, 12),
|
||||
totalSubscriptions: userSubscriptions.length,
|
||||
totalUnreadCount: totalUnreadCount,
|
||||
});
|
||||
|
||||
@ -190,21 +190,23 @@ export async function getImapConnection(
|
||||
totalConnectionRequests++;
|
||||
|
||||
logger.debug('[IMAP] getImapConnection called', {
|
||||
userId,
|
||||
accountId: accountId ?? 'default',
|
||||
userIdHash: Buffer.from(userId).toString('base64').slice(0, 12),
|
||||
accountIdHash: accountId ? Buffer.from(accountId).toString('base64').slice(0, 12) : 'default',
|
||||
});
|
||||
|
||||
// Special handling for 'default' accountId - find the first available account
|
||||
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
|
||||
const sessionData = await getCachedImapSession(userId);
|
||||
if (sessionData && sessionData.defaultAccountId) {
|
||||
accountId = sessionData.defaultAccountId;
|
||||
logger.debug('[IMAP] Using cached default account ID', {
|
||||
userId,
|
||||
accountId,
|
||||
userIdHash: Buffer.from(userId).toString('base64').slice(0, 12),
|
||||
accountIdHash: Buffer.from(accountId).toString('base64').slice(0, 12),
|
||||
});
|
||||
} else {
|
||||
// Query to find all accounts for this user
|
||||
@ -217,9 +219,8 @@ export async function getImapConnection(
|
||||
if (accounts && accounts.length > 0) {
|
||||
const firstAccount = accounts[0];
|
||||
logger.debug('[IMAP] Using first available account from DB', {
|
||||
userId,
|
||||
accountId: firstAccount.id,
|
||||
email: firstAccount.email,
|
||||
userIdHash: Buffer.from(userId).toString('base64').slice(0, 12),
|
||||
accountIdHash: Buffer.from(firstAccount.id).toString('base64').slice(0, 12),
|
||||
});
|
||||
accountId = firstAccount.id;
|
||||
|
||||
@ -304,21 +305,23 @@ export async function getImapConnection(
|
||||
}
|
||||
|
||||
// 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
|
||||
let credentials = await getCachedEmailCredentials(userId, accountId);
|
||||
logger.debug('[IMAP] Retrieved credentials from Redis cache', {
|
||||
userId,
|
||||
accountId,
|
||||
userIdHash: Buffer.from(userId).toString('base64').slice(0, 12),
|
||||
accountIdHash: accountId ? Buffer.from(accountId).toString('base64').slice(0, 12) : 'default',
|
||||
found: !!credentials,
|
||||
});
|
||||
|
||||
// If not in cache, get from database and cache them
|
||||
if (!credentials) {
|
||||
logger.debug('[IMAP] Credentials not found in cache, querying database', {
|
||||
userId,
|
||||
accountId,
|
||||
userIdHash: Buffer.from(userId).toString('base64').slice(0, 12),
|
||||
accountIdHash: accountId ? Buffer.from(accountId).toString('base64').slice(0, 12) : 'default',
|
||||
});
|
||||
|
||||
// Fetch directly from database
|
||||
@ -333,17 +336,16 @@ export async function getImapConnection(
|
||||
|
||||
if (!dbCredentials) {
|
||||
logger.error('[IMAP] No credentials found for user', {
|
||||
userId,
|
||||
accountId,
|
||||
userIdHash: Buffer.from(userId).toString('base64').slice(0, 12),
|
||||
accountIdHash: accountId ? Buffer.from(accountId).toString('base64').slice(0, 12) : 'default',
|
||||
});
|
||||
totalConnectionErrors++;
|
||||
throw new Error('Email account credentials not found');
|
||||
}
|
||||
|
||||
logger.debug('[IMAP] Database lookup returned credentials', {
|
||||
userId,
|
||||
accountId,
|
||||
email: dbCredentials.email,
|
||||
userIdHash: Buffer.from(userId).toString('base64').slice(0, 12),
|
||||
accountIdHash: accountId ? Buffer.from(accountId).toString('base64').slice(0, 12) : 'default',
|
||||
hasPassword: !!dbCredentials.password,
|
||||
});
|
||||
|
||||
@ -523,7 +525,7 @@ async function createImapConnection(credentials: EmailCredentials, connectionKey
|
||||
const extendedCreds = credentials as EmailCredentialsExtended;
|
||||
|
||||
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,
|
||||
port: extendedCreds.port,
|
||||
hasPassword: !!extendedCreds.password,
|
||||
@ -537,7 +539,9 @@ async function createImapConnection(credentials: EmailCredentials, connectionKey
|
||||
|
||||
// Check if we have valid OAuth tokens
|
||||
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
|
||||
authParams = {
|
||||
@ -545,10 +549,14 @@ async function createImapConnection(credentials: EmailCredentials, connectionKey
|
||||
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) {
|
||||
// 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 = {
|
||||
user: extendedCreds.email,
|
||||
pass: extendedCreds.password
|
||||
@ -928,11 +936,11 @@ export async function getEmails(
|
||||
): Promise<EmailListResult> {
|
||||
// Normalize folder name and handle account ID
|
||||
logger.debug('[EMAIL] getEmails called', {
|
||||
userId,
|
||||
userIdHash: Buffer.from(userId).toString('base64').slice(0, 12),
|
||||
folder,
|
||||
page,
|
||||
perPage,
|
||||
accountId: accountId || 'default',
|
||||
accountIdHash: accountId ? Buffer.from(accountId).toString('base64').slice(0, 12) : 'default',
|
||||
checkOnly,
|
||||
});
|
||||
|
||||
|
||||
@ -20,7 +20,9 @@ export async function ensureFreshToken(
|
||||
): Promise<{ accessToken: string; success: boolean }> {
|
||||
try {
|
||||
// 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 key = KEYS.CREDENTIALS(userId, email);
|
||||
let credStr = await redis.get(key);
|
||||
@ -30,7 +32,9 @@ export async function ensureFreshToken(
|
||||
creds = JSON.parse(credStr);
|
||||
} else {
|
||||
// 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({
|
||||
where: {
|
||||
userId: userId,
|
||||
@ -54,16 +58,22 @@ export async function ensureFreshToken(
|
||||
|
||||
// Re-populate Redis cache
|
||||
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 {
|
||||
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 };
|
||||
}
|
||||
}
|
||||
|
||||
// If not OAuth or missing refresh token, return failure
|
||||
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 };
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user