notifications
This commit is contained in:
parent
509f9ea0aa
commit
fc1dd882b6
83
app/api/debug/notifications/route.ts
Normal file
83
app/api/debug/notifications/route.ts
Normal file
@ -0,0 +1,83 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { getServerSession } from 'next-auth';
|
||||
import { authOptions } from '@/app/api/auth/[...nextauth]/route';
|
||||
import { NotificationService } from '@/lib/services/notifications/notification-service';
|
||||
|
||||
// GET /api/debug/notifications
|
||||
export async function GET(request: Request) {
|
||||
try {
|
||||
// Authenticate user
|
||||
const session = await getServerSession(authOptions);
|
||||
if (!session || !session.user?.id) {
|
||||
return NextResponse.json(
|
||||
{ error: "Not authenticated" },
|
||||
{ status: 401 }
|
||||
);
|
||||
}
|
||||
|
||||
const userId = session.user.id;
|
||||
console.log(`[DEBUG] Testing notifications for user ${userId}`);
|
||||
|
||||
// Get environment variables status
|
||||
const envStatus = {
|
||||
LEANTIME_API_URL: process.env.LEANTIME_API_URL ? 'Set' : 'Not set',
|
||||
LEANTIME_TOKEN: process.env.LEANTIME_TOKEN ? `Set (length: ${process.env.LEANTIME_TOKEN.length})` : 'Not set',
|
||||
LEANTIME_API_KEY: process.env.LEANTIME_API_KEY ? `Set (length: ${process.env.LEANTIME_API_KEY.length})` : 'Not set',
|
||||
KEYCLOAK_BASE_URL: process.env.KEYCLOAK_BASE_URL ? 'Set' : 'Not set',
|
||||
KEYCLOAK_REALM: process.env.KEYCLOAK_REALM || 'Not set',
|
||||
KEYCLOAK_ADMIN_CLIENT_ID: process.env.KEYCLOAK_ADMIN_CLIENT_ID ? 'Set' : 'Not set',
|
||||
KEYCLOAK_ADMIN_CLIENT_SECRET: process.env.KEYCLOAK_ADMIN_CLIENT_SECRET ? 'Set (masked)' : 'Not set',
|
||||
};
|
||||
|
||||
// Get user information
|
||||
console.log(`[DEBUG] Getting user info for ${userId}`);
|
||||
let userInfo = {
|
||||
id: userId,
|
||||
email: session.user.email || 'Unknown'
|
||||
};
|
||||
|
||||
// Test notification service
|
||||
console.log(`[DEBUG] Testing notification service`);
|
||||
const notificationService = NotificationService.getInstance();
|
||||
|
||||
// Get notification count
|
||||
console.log(`[DEBUG] Getting notification count`);
|
||||
const startTimeCount = Date.now();
|
||||
const notificationCount = await notificationService.getNotificationCount(userId);
|
||||
const timeForCount = Date.now() - startTimeCount;
|
||||
|
||||
// Get notifications
|
||||
console.log(`[DEBUG] Getting notifications`);
|
||||
const startTimeNotifications = Date.now();
|
||||
const notifications = await notificationService.getNotifications(userId, 1, 10);
|
||||
const timeForNotifications = Date.now() - startTimeNotifications;
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
timestamp: new Date().toISOString(),
|
||||
userInfo,
|
||||
environmentVariables: envStatus,
|
||||
notificationServiceTest: {
|
||||
count: {
|
||||
result: notificationCount,
|
||||
timeMs: timeForCount
|
||||
},
|
||||
notifications: {
|
||||
count: notifications.length,
|
||||
timeMs: timeForNotifications,
|
||||
samples: notifications.slice(0, 3) // Only return first 3 notifications as samples
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (error: any) {
|
||||
console.error('[DEBUG] Error in debug notifications API:', error);
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: "Internal server error",
|
||||
message: error.message,
|
||||
stack: process.env.NODE_ENV === 'development' ? error.stack : undefined
|
||||
},
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -23,20 +23,33 @@ export class LeantimeAdapter implements NotificationAdapter {
|
||||
// Load from environment variables, matching the pattern used in other Leantime integrations
|
||||
this.apiUrl = process.env.LEANTIME_API_URL || '';
|
||||
this.apiToken = process.env.LEANTIME_TOKEN || '';
|
||||
|
||||
// Log configuration on initialization
|
||||
console.log('[LEANTIME_ADAPTER] Initialized with:', {
|
||||
apiUrlConfigured: !!this.apiUrl,
|
||||
apiTokenConfigured: !!this.apiToken,
|
||||
apiUrlPrefix: this.apiUrl ? this.apiUrl.substring(0, 30) + '...' : 'not set',
|
||||
});
|
||||
}
|
||||
|
||||
async getNotifications(userId: string, page = 1, limit = 20): Promise<Notification[]> {
|
||||
console.log(`[LEANTIME_ADAPTER] getNotifications called for userId: ${userId}, page: ${page}, limit: ${limit}`);
|
||||
|
||||
try {
|
||||
// First get the Leantime user ID from email
|
||||
const email = await this.getUserEmail(userId);
|
||||
console.log(`[LEANTIME_ADAPTER] Retrieved email for userId ${userId}:`, email || 'null');
|
||||
|
||||
if (!email) {
|
||||
console.error('Could not get user email for userId:', userId);
|
||||
console.error('[LEANTIME_ADAPTER] Could not get user email for userId:', userId);
|
||||
return [];
|
||||
}
|
||||
|
||||
const leantimeUserId = await this.getLeantimeUserId(email);
|
||||
console.log(`[LEANTIME_ADAPTER] Retrieved Leantime userId for email ${email}:`, leantimeUserId || 'null');
|
||||
|
||||
if (!leantimeUserId) {
|
||||
console.error('User not found in Leantime:', email);
|
||||
console.error('[LEANTIME_ADAPTER] User not found in Leantime:', email);
|
||||
return [];
|
||||
}
|
||||
|
||||
@ -44,88 +57,137 @@ export class LeantimeAdapter implements NotificationAdapter {
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
// Make request to Leantime API using jsonrpc
|
||||
console.log('[LEANTIME_ADAPTER] Sending request to Leantime API for notifications');
|
||||
const jsonRpcBody = {
|
||||
jsonrpc: '2.0',
|
||||
method: 'leantime.rpc.notifications.getAll',
|
||||
params: {
|
||||
userId: leantimeUserId,
|
||||
limit: limit,
|
||||
offset: offset
|
||||
},
|
||||
id: 1
|
||||
};
|
||||
console.log('[LEANTIME_ADAPTER] Request body:', JSON.stringify(jsonRpcBody));
|
||||
|
||||
const response = await fetch(`${this.apiUrl}/api/jsonrpc`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-API-Key': this.apiToken
|
||||
},
|
||||
body: JSON.stringify({
|
||||
jsonrpc: '2.0',
|
||||
method: 'leantime.rpc.notifications.getAll',
|
||||
params: {
|
||||
userId: leantimeUserId,
|
||||
limit: limit,
|
||||
offset: offset
|
||||
},
|
||||
id: 1
|
||||
})
|
||||
body: JSON.stringify(jsonRpcBody)
|
||||
});
|
||||
|
||||
console.log('[LEANTIME_ADAPTER] Response status:', response.status);
|
||||
|
||||
if (!response.ok) {
|
||||
console.error(`Failed to fetch Leantime notifications: ${response.status}`);
|
||||
const errorText = await response.text();
|
||||
console.error('[LEANTIME_ADAPTER] Failed to fetch Leantime notifications:', {
|
||||
status: response.status,
|
||||
body: errorText.substring(0, 200) + (errorText.length > 200 ? '...' : '')
|
||||
});
|
||||
return [];
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
const responseText = await response.text();
|
||||
console.log('[LEANTIME_ADAPTER] Raw response:', responseText.substring(0, 200) + (responseText.length > 200 ? '...' : ''));
|
||||
|
||||
const data = JSON.parse(responseText);
|
||||
console.log('[LEANTIME_ADAPTER] Parsed response data:', {
|
||||
hasResult: !!data.result,
|
||||
resultIsArray: Array.isArray(data.result),
|
||||
resultLength: Array.isArray(data.result) ? data.result.length : 'n/a',
|
||||
error: data.error
|
||||
});
|
||||
|
||||
if (!data.result || !Array.isArray(data.result)) {
|
||||
console.error('Invalid response format from Leantime notifications API');
|
||||
console.error('[LEANTIME_ADAPTER] Invalid response format from Leantime notifications API');
|
||||
return [];
|
||||
}
|
||||
|
||||
return this.transformNotifications(data.result, userId);
|
||||
const notifications = this.transformNotifications(data.result, userId);
|
||||
console.log('[LEANTIME_ADAPTER] Transformed notifications count:', notifications.length);
|
||||
return notifications;
|
||||
} catch (error) {
|
||||
console.error('Error fetching Leantime notifications:', error);
|
||||
console.error('[LEANTIME_ADAPTER] Error fetching Leantime notifications:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async getNotificationCount(userId: string): Promise<NotificationCount> {
|
||||
console.log(`[LEANTIME_ADAPTER] getNotificationCount called for userId: ${userId}`);
|
||||
|
||||
try {
|
||||
// First get the Leantime user ID from email
|
||||
const email = await this.getUserEmail(userId);
|
||||
console.log(`[LEANTIME_ADAPTER] Retrieved email for userId ${userId}:`, email || 'null');
|
||||
|
||||
if (!email) {
|
||||
console.error('Could not get user email for userId:', userId);
|
||||
console.error('[LEANTIME_ADAPTER] Could not get user email for userId:', userId);
|
||||
return this.getEmptyCount();
|
||||
}
|
||||
|
||||
const leantimeUserId = await this.getLeantimeUserId(email);
|
||||
console.log(`[LEANTIME_ADAPTER] Retrieved Leantime userId for email ${email}:`, leantimeUserId || 'null');
|
||||
|
||||
if (!leantimeUserId) {
|
||||
console.error('User not found in Leantime:', email);
|
||||
console.error('[LEANTIME_ADAPTER] User not found in Leantime:', email);
|
||||
return this.getEmptyCount();
|
||||
}
|
||||
|
||||
// Make request to Leantime API using jsonrpc
|
||||
console.log('[LEANTIME_ADAPTER] Sending request to Leantime API for notification count');
|
||||
const jsonRpcBody = {
|
||||
jsonrpc: '2.0',
|
||||
method: 'leantime.rpc.notifications.getNotificationCount',
|
||||
params: {
|
||||
userId: leantimeUserId
|
||||
},
|
||||
id: 1
|
||||
};
|
||||
console.log('[LEANTIME_ADAPTER] Request body:', JSON.stringify(jsonRpcBody));
|
||||
|
||||
const response = await fetch(`${this.apiUrl}/api/jsonrpc`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-API-Key': this.apiToken
|
||||
},
|
||||
body: JSON.stringify({
|
||||
jsonrpc: '2.0',
|
||||
method: 'leantime.rpc.notifications.getNotificationCount',
|
||||
params: {
|
||||
userId: leantimeUserId
|
||||
},
|
||||
id: 1
|
||||
})
|
||||
body: JSON.stringify(jsonRpcBody)
|
||||
});
|
||||
|
||||
console.log('[LEANTIME_ADAPTER] Response status:', response.status);
|
||||
|
||||
if (!response.ok) {
|
||||
console.error(`Failed to fetch Leantime notification count: ${response.status}`);
|
||||
const errorText = await response.text();
|
||||
console.error('[LEANTIME_ADAPTER] Failed to fetch Leantime notification count:', {
|
||||
status: response.status,
|
||||
body: errorText.substring(0, 200) + (errorText.length > 200 ? '...' : '')
|
||||
});
|
||||
return this.getEmptyCount();
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
const responseText = await response.text();
|
||||
console.log('[LEANTIME_ADAPTER] Raw response:', responseText);
|
||||
|
||||
const data = JSON.parse(responseText);
|
||||
console.log('[LEANTIME_ADAPTER] Parsed response data:', {
|
||||
hasResult: !!data.result,
|
||||
resultData: data.result,
|
||||
error: data.error
|
||||
});
|
||||
|
||||
if (!data.result) {
|
||||
console.error('Invalid response format from Leantime notification count API');
|
||||
console.error('[LEANTIME_ADAPTER] Invalid response format from Leantime notification count API');
|
||||
return this.getEmptyCount();
|
||||
}
|
||||
|
||||
const unreadCount = data.result.unread || 0;
|
||||
const totalCount = data.result.total || 0;
|
||||
|
||||
console.log('[LEANTIME_ADAPTER] Notification counts:', { unread: unreadCount, total: totalCount });
|
||||
|
||||
return {
|
||||
total: totalCount,
|
||||
unread: unreadCount,
|
||||
@ -137,7 +199,7 @@ export class LeantimeAdapter implements NotificationAdapter {
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error fetching Leantime notification count:', error);
|
||||
console.error('[LEANTIME_ADAPTER] Error fetching Leantime notification count:', error);
|
||||
return this.getEmptyCount();
|
||||
}
|
||||
}
|
||||
@ -267,36 +329,69 @@ export class LeantimeAdapter implements NotificationAdapter {
|
||||
|
||||
// Helper function to get user's email from ID (using existing method from your system)
|
||||
private async getUserEmail(userId: string): Promise<string | null> {
|
||||
console.log(`[LEANTIME_ADAPTER] Getting email for userId: ${userId}`);
|
||||
|
||||
try {
|
||||
// Fetch from Keycloak similar to how other components do
|
||||
const response = await fetch(`${process.env.KEYCLOAK_BASE_URL}/admin/realms/${process.env.KEYCLOAK_REALM}/users/${userId}`, {
|
||||
const adminToken = await this.getAdminToken();
|
||||
console.log(`[LEANTIME_ADAPTER] Got admin token for Keycloak:`, adminToken ? 'token obtained' : 'failed to get token');
|
||||
|
||||
if (!adminToken) {
|
||||
console.error('[LEANTIME_ADAPTER] Failed to get admin token for Keycloak');
|
||||
return null;
|
||||
}
|
||||
|
||||
const keycloakUrl = `${process.env.KEYCLOAK_BASE_URL}/admin/realms/${process.env.KEYCLOAK_REALM}/users/${userId}`;
|
||||
console.log(`[LEANTIME_ADAPTER] Fetching user from Keycloak:`, keycloakUrl);
|
||||
|
||||
const response = await fetch(keycloakUrl, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${await this.getAdminToken()}`
|
||||
'Authorization': `Bearer ${adminToken}`
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`[LEANTIME_ADAPTER] Keycloak response status:`, response.status);
|
||||
|
||||
if (!response.ok) {
|
||||
console.error('Failed to get user from Keycloak');
|
||||
const errorText = await response.text();
|
||||
console.error('[LEANTIME_ADAPTER] Failed to get user from Keycloak:', {
|
||||
status: response.status,
|
||||
body: errorText.substring(0, 200) + (errorText.length > 200 ? '...' : '')
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
const userData = await response.json();
|
||||
console.log(`[LEANTIME_ADAPTER] Got user data from Keycloak:`, {
|
||||
id: userData.id,
|
||||
email: userData.email,
|
||||
username: userData.username
|
||||
});
|
||||
|
||||
return userData.email || null;
|
||||
} catch (error) {
|
||||
console.error('Error getting user email:', error);
|
||||
console.error('[LEANTIME_ADAPTER] Error getting user email:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to get Leantime user ID by email (using similar pattern to the tasks API)
|
||||
private async getLeantimeUserId(email: string): Promise<number | null> {
|
||||
console.log(`[LEANTIME_ADAPTER] Getting Leantime userId for email: ${email}`);
|
||||
|
||||
try {
|
||||
if (!this.apiToken) {
|
||||
console.error('LEANTIME_TOKEN is not set in environment variables');
|
||||
console.error('[LEANTIME_ADAPTER] LEANTIME_TOKEN is not set in environment variables');
|
||||
return null;
|
||||
}
|
||||
|
||||
console.log('Fetching Leantime user for email:', email);
|
||||
console.log('[LEANTIME_ADAPTER] Sending request to get all Leantime users');
|
||||
const jsonRpcBody = {
|
||||
jsonrpc: '2.0',
|
||||
method: 'leantime.rpc.users.getAll',
|
||||
id: 1
|
||||
};
|
||||
console.log('[LEANTIME_ADAPTER] Request body:', JSON.stringify(jsonRpcBody));
|
||||
|
||||
const response = await fetch(`${this.apiUrl}/api/jsonrpc`, {
|
||||
method: 'POST',
|
||||
@ -304,46 +399,85 @@ export class LeantimeAdapter implements NotificationAdapter {
|
||||
'Content-Type': 'application/json',
|
||||
'X-API-Key': this.apiToken
|
||||
},
|
||||
body: JSON.stringify({
|
||||
jsonrpc: '2.0',
|
||||
method: 'leantime.rpc.users.getAll',
|
||||
id: 1
|
||||
}),
|
||||
body: JSON.stringify(jsonRpcBody),
|
||||
});
|
||||
|
||||
console.log('[LEANTIME_ADAPTER] Response status:', response.status);
|
||||
|
||||
if (!response.ok) {
|
||||
console.error('Failed to fetch Leantime users');
|
||||
const errorText = await response.text();
|
||||
console.error('[LEANTIME_ADAPTER] Failed to fetch Leantime users:', {
|
||||
status: response.status,
|
||||
body: errorText.substring(0, 200) + (errorText.length > 200 ? '...' : '')
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
const responseText = await response.text();
|
||||
console.log('[LEANTIME_ADAPTER] Raw response (truncated):', responseText.substring(0, 200) + (responseText.length > 200 ? '...' : ''));
|
||||
|
||||
const data = JSON.parse(responseText);
|
||||
console.log('[LEANTIME_ADAPTER] Parsed response data:', {
|
||||
hasResult: !!data.result,
|
||||
resultIsArray: Array.isArray(data.result),
|
||||
resultLength: Array.isArray(data.result) ? data.result.length : 'n/a',
|
||||
error: data.error
|
||||
});
|
||||
|
||||
if (!data.result || !Array.isArray(data.result)) {
|
||||
console.error('Invalid response format from Leantime users API');
|
||||
console.error('[LEANTIME_ADAPTER] Invalid response format from Leantime users API');
|
||||
return null;
|
||||
}
|
||||
|
||||
const users = data.result;
|
||||
console.log(`[LEANTIME_ADAPTER] Searching for user with email ${email} among ${users.length} users`);
|
||||
|
||||
// Log a few user objects to check structure
|
||||
if (users.length > 0) {
|
||||
console.log('[LEANTIME_ADAPTER] Sample user object:', JSON.stringify(users[0]));
|
||||
}
|
||||
|
||||
const user = users.find((u: any) => u.username === email);
|
||||
|
||||
if (user) {
|
||||
console.log('Found Leantime user:', { id: user.id, username: user.username });
|
||||
console.log('[LEANTIME_ADAPTER] Found Leantime user:', { id: user.id, username: user.username });
|
||||
return user.id;
|
||||
} else {
|
||||
console.log('No Leantime user found for email:', email);
|
||||
console.log('[LEANTIME_ADAPTER] No Leantime user found for email:', email);
|
||||
// Try alternative property names that might contain the email
|
||||
const alternativeUser = users.find((u: any) =>
|
||||
u.email === email ||
|
||||
u.mail === email ||
|
||||
(typeof u.username === 'string' && u.username.toLowerCase() === email.toLowerCase())
|
||||
);
|
||||
|
||||
if (alternativeUser) {
|
||||
console.log('[LEANTIME_ADAPTER] Found Leantime user with alternative property match:', {
|
||||
id: alternativeUser.id,
|
||||
username: alternativeUser.username,
|
||||
email: alternativeUser.email || alternativeUser.mail
|
||||
});
|
||||
return alternativeUser.id;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error getting Leantime user ID:', error);
|
||||
console.error('[LEANTIME_ADAPTER] Error getting Leantime user ID:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to get admin token (similar to the user management API)
|
||||
private async getAdminToken(): Promise<string | null> {
|
||||
console.log('[LEANTIME_ADAPTER] Getting admin token from Keycloak');
|
||||
|
||||
try {
|
||||
const keycloakTokenUrl = `${process.env.KEYCLOAK_BASE_URL}/realms/master/protocol/openid-connect/token`;
|
||||
console.log('[LEANTIME_ADAPTER] Keycloak token URL:', keycloakTokenUrl);
|
||||
|
||||
const response = await fetch(
|
||||
`${process.env.KEYCLOAK_BASE_URL}/realms/master/protocol/openid-connect/token`,
|
||||
keycloakTokenUrl,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
@ -357,15 +491,23 @@ export class LeantimeAdapter implements NotificationAdapter {
|
||||
}
|
||||
);
|
||||
|
||||
console.log('[LEANTIME_ADAPTER] Keycloak token response status:', response.status);
|
||||
|
||||
if (!response.ok) {
|
||||
console.error('Failed to get admin token');
|
||||
const errorText = await response.text();
|
||||
console.error('[LEANTIME_ADAPTER] Failed to get admin token:', {
|
||||
status: response.status,
|
||||
body: errorText.substring(0, 200) + (errorText.length > 200 ? '...' : '')
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
const tokenData = await response.json();
|
||||
console.log('[LEANTIME_ADAPTER] Successfully obtained admin token');
|
||||
|
||||
return tokenData.access_token;
|
||||
} catch (error) {
|
||||
console.error('Error getting admin token:', error);
|
||||
console.error('[LEANTIME_ADAPTER] Error getting admin token:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,6 +17,8 @@ export class NotificationService {
|
||||
private static REFRESH_LOCK_TTL = 30; // 30 seconds
|
||||
|
||||
constructor() {
|
||||
console.log('[NOTIFICATION_SERVICE] Initializing notification service');
|
||||
|
||||
// Register adapters
|
||||
this.registerAdapter(new LeantimeAdapter());
|
||||
|
||||
@ -25,6 +27,8 @@ export class NotificationService {
|
||||
// this.registerAdapter(new GiteaAdapter());
|
||||
// this.registerAdapter(new DolibarrAdapter());
|
||||
// this.registerAdapter(new MoodleAdapter());
|
||||
|
||||
console.log('[NOTIFICATION_SERVICE] Registered adapters:', Array.from(this.adapters.keys()));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -32,6 +36,7 @@ export class NotificationService {
|
||||
*/
|
||||
public static getInstance(): NotificationService {
|
||||
if (!NotificationService.instance) {
|
||||
console.log('[NOTIFICATION_SERVICE] Creating new notification service instance');
|
||||
NotificationService.instance = new NotificationService();
|
||||
}
|
||||
return NotificationService.instance;
|
||||
@ -42,13 +47,14 @@ export class NotificationService {
|
||||
*/
|
||||
private registerAdapter(adapter: NotificationAdapter): void {
|
||||
this.adapters.set(adapter.sourceName, adapter);
|
||||
console.log(`Registered notification adapter: ${adapter.sourceName}`);
|
||||
console.log(`[NOTIFICATION_SERVICE] Registered notification adapter: ${adapter.sourceName}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all notifications for a user from all configured sources
|
||||
*/
|
||||
async getNotifications(userId: string, page = 1, limit = 20): Promise<Notification[]> {
|
||||
console.log(`[NOTIFICATION_SERVICE] getNotifications called for user ${userId}, page ${page}, limit ${limit}`);
|
||||
const redis = getRedisClient();
|
||||
const cacheKey = NotificationService.NOTIFICATIONS_LIST_CACHE_KEY(userId, page, limit);
|
||||
|
||||
@ -67,31 +73,49 @@ export class NotificationService {
|
||||
return JSON.parse(cachedData);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error retrieving notifications from cache:', error);
|
||||
console.error('[NOTIFICATION_SERVICE] Error retrieving notifications from cache:', error);
|
||||
}
|
||||
|
||||
// No cached data, fetch from all adapters
|
||||
console.log(`[NOTIFICATION_SERVICE] Fetching notifications for user ${userId}`);
|
||||
console.log(`[NOTIFICATION_SERVICE] Fetching notifications for user ${userId} from ${this.adapters.size} adapters`);
|
||||
|
||||
const allNotifications: Notification[] = [];
|
||||
const promises = Array.from(this.adapters.values())
|
||||
.map(adapter => adapter.isConfigured()
|
||||
.then(configured => configured ? adapter.getNotifications(userId, page, limit) : [])
|
||||
.catch(error => {
|
||||
console.error(`Error fetching notifications from ${adapter.sourceName}:`, error);
|
||||
const adapterEntries = Array.from(this.adapters.entries());
|
||||
console.log(`[NOTIFICATION_SERVICE] Available adapters: ${adapterEntries.map(([name]) => name).join(', ')}`);
|
||||
|
||||
const promises = adapterEntries.map(async ([name, adapter]) => {
|
||||
console.log(`[NOTIFICATION_SERVICE] Checking if adapter ${name} is configured`);
|
||||
try {
|
||||
const configured = await adapter.isConfigured();
|
||||
console.log(`[NOTIFICATION_SERVICE] Adapter ${name} is configured: ${configured}`);
|
||||
|
||||
if (configured) {
|
||||
console.log(`[NOTIFICATION_SERVICE] Fetching notifications from ${name} for user ${userId}`);
|
||||
const notifications = await adapter.getNotifications(userId, page, limit);
|
||||
console.log(`[NOTIFICATION_SERVICE] Got ${notifications.length} notifications from ${name}`);
|
||||
return notifications;
|
||||
} else {
|
||||
console.log(`[NOTIFICATION_SERVICE] Skipping adapter ${name} as it is not configured`);
|
||||
return [];
|
||||
})
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`[NOTIFICATION_SERVICE] Error fetching notifications from ${name}:`, error);
|
||||
return [];
|
||||
}
|
||||
});
|
||||
|
||||
const results = await Promise.all(promises);
|
||||
|
||||
// Combine all notifications
|
||||
results.forEach(notifications => {
|
||||
results.forEach((notifications, index) => {
|
||||
const adapterName = adapterEntries[index][0];
|
||||
console.log(`[NOTIFICATION_SERVICE] Adding ${notifications.length} notifications from ${adapterName}`);
|
||||
allNotifications.push(...notifications);
|
||||
});
|
||||
|
||||
// Sort by timestamp (newest first)
|
||||
allNotifications.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
|
||||
console.log(`[NOTIFICATION_SERVICE] Total notifications after sorting: ${allNotifications.length}`);
|
||||
|
||||
// Store in cache
|
||||
try {
|
||||
@ -101,8 +125,9 @@ export class NotificationService {
|
||||
'EX',
|
||||
NotificationService.LIST_CACHE_TTL
|
||||
);
|
||||
console.log(`[NOTIFICATION_SERVICE] Cached ${allNotifications.length} notifications for user ${userId}`);
|
||||
} catch (error) {
|
||||
console.error('Error caching notifications:', error);
|
||||
console.error('[NOTIFICATION_SERVICE] Error caching notifications:', error);
|
||||
}
|
||||
|
||||
return allNotifications;
|
||||
@ -112,6 +137,7 @@ export class NotificationService {
|
||||
* Get notification counts for a user
|
||||
*/
|
||||
async getNotificationCount(userId: string): Promise<NotificationCount> {
|
||||
console.log(`[NOTIFICATION_SERVICE] getNotificationCount called for user ${userId}`);
|
||||
const redis = getRedisClient();
|
||||
const cacheKey = NotificationService.NOTIFICATION_COUNT_CACHE_KEY(userId);
|
||||
|
||||
@ -130,11 +156,11 @@ export class NotificationService {
|
||||
return JSON.parse(cachedData);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error retrieving notification counts from cache:', error);
|
||||
console.error('[NOTIFICATION_SERVICE] Error retrieving notification counts from cache:', error);
|
||||
}
|
||||
|
||||
// No cached data, fetch counts from all adapters
|
||||
console.log(`[NOTIFICATION_SERVICE] Fetching notification counts for user ${userId}`);
|
||||
console.log(`[NOTIFICATION_SERVICE] Fetching notification counts for user ${userId} from ${this.adapters.size} adapters`);
|
||||
|
||||
const aggregatedCount: NotificationCount = {
|
||||
total: 0,
|
||||
@ -142,21 +168,39 @@ export class NotificationService {
|
||||
sources: {}
|
||||
};
|
||||
|
||||
const promises = Array.from(this.adapters.values())
|
||||
.map(adapter => adapter.isConfigured()
|
||||
.then(configured => configured ? adapter.getNotificationCount(userId) : null)
|
||||
.catch(error => {
|
||||
console.error(`Error fetching notification count from ${adapter.sourceName}:`, error);
|
||||
const adapterEntries = Array.from(this.adapters.entries());
|
||||
console.log(`[NOTIFICATION_SERVICE] Available adapters for count: ${adapterEntries.map(([name]) => name).join(', ')}`);
|
||||
|
||||
const promises = adapterEntries.map(async ([name, adapter]) => {
|
||||
console.log(`[NOTIFICATION_SERVICE] Checking if adapter ${name} is configured for count`);
|
||||
try {
|
||||
const configured = await adapter.isConfigured();
|
||||
console.log(`[NOTIFICATION_SERVICE] Adapter ${name} is configured for count: ${configured}`);
|
||||
|
||||
if (configured) {
|
||||
console.log(`[NOTIFICATION_SERVICE] Fetching notification count from ${name} for user ${userId}`);
|
||||
const count = await adapter.getNotificationCount(userId);
|
||||
console.log(`[NOTIFICATION_SERVICE] Got count from ${name}:`, count);
|
||||
return count;
|
||||
} else {
|
||||
console.log(`[NOTIFICATION_SERVICE] Skipping adapter ${name} for count as it is not configured`);
|
||||
return null;
|
||||
})
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`[NOTIFICATION_SERVICE] Error fetching notification count from ${name}:`, error);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
const results = await Promise.all(promises);
|
||||
|
||||
// Combine all counts
|
||||
results.forEach(count => {
|
||||
results.forEach((count, index) => {
|
||||
if (!count) return;
|
||||
|
||||
const adapterName = adapterEntries[index][0];
|
||||
console.log(`[NOTIFICATION_SERVICE] Adding counts from ${adapterName}: total=${count.total}, unread=${count.unread}`);
|
||||
|
||||
aggregatedCount.total += count.total;
|
||||
aggregatedCount.unread += count.unread;
|
||||
|
||||
@ -166,6 +210,8 @@ export class NotificationService {
|
||||
});
|
||||
});
|
||||
|
||||
console.log(`[NOTIFICATION_SERVICE] Aggregated counts for user ${userId}:`, aggregatedCount);
|
||||
|
||||
// Store in cache
|
||||
try {
|
||||
await redis.set(
|
||||
@ -174,8 +220,9 @@ export class NotificationService {
|
||||
'EX',
|
||||
NotificationService.COUNT_CACHE_TTL
|
||||
);
|
||||
console.log(`[NOTIFICATION_SERVICE] Cached notification counts for user ${userId}`);
|
||||
} catch (error) {
|
||||
console.error('Error caching notification counts:', error);
|
||||
console.error('[NOTIFICATION_SERVICE] Error caching notification counts:', error);
|
||||
}
|
||||
|
||||
return aggregatedCount;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user