325 lines
9.3 KiB
TypeScript
325 lines
9.3 KiB
TypeScript
import { NotificationAdapter } from './notification-adapter.interface';
|
|
import { getServerSession } from 'next-auth';
|
|
import { authOptions } from "@/app/api/auth/options";
|
|
import { logger } from '@/lib/logger';
|
|
import { Notification, NotificationCount } from '@/lib/types/notification';
|
|
|
|
export class RocketChatAdapter implements NotificationAdapter {
|
|
readonly sourceName = 'rocketchat';
|
|
private baseUrl: string;
|
|
|
|
constructor() {
|
|
this.baseUrl = process.env.NEXT_PUBLIC_IFRAME_PAROLE_URL?.split('/channel')[0] || '';
|
|
|
|
logger.debug('[ROCKETCHAT_ADAPTER] Initialized', {
|
|
hasBaseUrl: !!this.baseUrl,
|
|
});
|
|
}
|
|
|
|
async isConfigured(): Promise<boolean> {
|
|
return !!this.baseUrl &&
|
|
!!process.env.ROCKET_CHAT_TOKEN &&
|
|
!!process.env.ROCKET_CHAT_USER_ID;
|
|
}
|
|
|
|
/**
|
|
* Get user's email from session
|
|
*/
|
|
private async getUserEmail(): Promise<string | null> {
|
|
try {
|
|
const session = await getServerSession(authOptions);
|
|
const email = session?.user?.email || null;
|
|
logger.debug('[ROCKETCHAT_ADAPTER] getUserEmail', {
|
|
hasSession: !!session,
|
|
hasEmail: !!email,
|
|
emailHash: email ? Buffer.from(email.toLowerCase()).toString('base64').slice(0, 12) : null,
|
|
});
|
|
return email;
|
|
} catch (error) {
|
|
logger.error('[ROCKETCHAT_ADAPTER] Error getting user email', {
|
|
error: error instanceof Error ? error.message : String(error),
|
|
});
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get RocketChat user ID from email
|
|
*/
|
|
private async getRocketChatUserId(email: string): Promise<string | null> {
|
|
try {
|
|
const username = email.split('@')[0];
|
|
if (!username) return null;
|
|
|
|
const adminHeaders = {
|
|
'X-Auth-Token': process.env.ROCKET_CHAT_TOKEN!,
|
|
'X-User-Id': process.env.ROCKET_CHAT_USER_ID!,
|
|
'Content-Type': 'application/json'
|
|
};
|
|
|
|
const usersResponse = await fetch(`${this.baseUrl}/api/v1/users.list`, {
|
|
method: 'GET',
|
|
headers: adminHeaders
|
|
});
|
|
|
|
if (!usersResponse.ok) {
|
|
logger.error('[ROCKETCHAT_ADAPTER] Failed to get users list', {
|
|
status: usersResponse.status,
|
|
});
|
|
return null;
|
|
}
|
|
|
|
const usersData = await usersResponse.json();
|
|
if (!usersData.success || !Array.isArray(usersData.users)) {
|
|
logger.error('[ROCKETCHAT_ADAPTER] Invalid users list response', {
|
|
success: usersData.success,
|
|
hasUsers: Array.isArray(usersData.users),
|
|
});
|
|
return null;
|
|
}
|
|
|
|
logger.debug('[ROCKETCHAT_ADAPTER] Searching for user', {
|
|
username,
|
|
totalUsers: usersData.users.length,
|
|
});
|
|
|
|
const currentUser = usersData.users.find((u: any) => u.username === username);
|
|
|
|
if (!currentUser) {
|
|
logger.warn('[ROCKETCHAT_ADAPTER] User not found in RocketChat', {
|
|
username,
|
|
searchedIn: usersData.users.length,
|
|
availableUsernames: usersData.users.slice(0, 5).map((u: any) => u.username),
|
|
});
|
|
return null;
|
|
}
|
|
|
|
logger.debug('[ROCKETCHAT_ADAPTER] Found user', {
|
|
username,
|
|
userId: currentUser._id,
|
|
});
|
|
|
|
return currentUser._id;
|
|
} catch (error) {
|
|
logger.error('[ROCKETCHAT_ADAPTER] Error getting RocketChat user ID', {
|
|
error: error instanceof Error ? error.message : String(error),
|
|
});
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get user token for RocketChat API
|
|
* Creates a token for the specified RocketChat user ID
|
|
*/
|
|
private async getUserToken(rocketChatUserId: string): Promise<{ authToken: string; userId: string } | null> {
|
|
try {
|
|
const adminHeaders = {
|
|
'X-Auth-Token': process.env.ROCKET_CHAT_TOKEN!,
|
|
'X-User-Id': process.env.ROCKET_CHAT_USER_ID!,
|
|
'Content-Type': 'application/json'
|
|
};
|
|
|
|
// Create token for the specific user
|
|
const createTokenResponse = await fetch(`${this.baseUrl}/api/v1/users.createToken`, {
|
|
method: 'POST',
|
|
headers: adminHeaders,
|
|
body: JSON.stringify({
|
|
userId: rocketChatUserId
|
|
})
|
|
});
|
|
|
|
if (!createTokenResponse.ok) {
|
|
logger.error('[ROCKETCHAT_ADAPTER] Failed to create user token', {
|
|
status: createTokenResponse.status,
|
|
rocketChatUserId,
|
|
});
|
|
return null;
|
|
}
|
|
|
|
const tokenData = await createTokenResponse.json();
|
|
|
|
if (!tokenData.success || !tokenData.data) {
|
|
logger.error('[ROCKETCHAT_ADAPTER] Invalid token response', {
|
|
response: tokenData,
|
|
});
|
|
return null;
|
|
}
|
|
|
|
logger.debug('[ROCKETCHAT_ADAPTER] User token created', {
|
|
rocketChatUserId,
|
|
tokenUserId: tokenData.data.userId,
|
|
});
|
|
|
|
return {
|
|
authToken: tokenData.data.authToken,
|
|
userId: tokenData.data.userId
|
|
};
|
|
} catch (error) {
|
|
logger.error('[ROCKETCHAT_ADAPTER] Error getting user token', {
|
|
error: error instanceof Error ? error.message : String(error),
|
|
rocketChatUserId,
|
|
});
|
|
return null;
|
|
}
|
|
}
|
|
|
|
async getNotificationCount(userId: string): Promise<NotificationCount> {
|
|
logger.debug('[ROCKETCHAT_ADAPTER] getNotificationCount called', { userId });
|
|
|
|
try {
|
|
const email = await this.getUserEmail();
|
|
if (!email) {
|
|
logger.error('[ROCKETCHAT_ADAPTER] Could not get user email');
|
|
return {
|
|
total: 0,
|
|
unread: 0,
|
|
sources: {
|
|
rocketchat: {
|
|
total: 0,
|
|
unread: 0
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
const rocketChatUserId = await this.getRocketChatUserId(email);
|
|
if (!rocketChatUserId) {
|
|
logger.debug('[ROCKETCHAT_ADAPTER] User not found in RocketChat', {
|
|
emailHash: email ? Buffer.from(email.toLowerCase()).toString('base64').slice(0, 12) : null,
|
|
});
|
|
return {
|
|
total: 0,
|
|
unread: 0,
|
|
sources: {
|
|
rocketchat: {
|
|
total: 0,
|
|
unread: 0
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
logger.debug('[ROCKETCHAT_ADAPTER] Found RocketChat user', {
|
|
rocketChatUserId,
|
|
emailHash: Buffer.from(email.toLowerCase()).toString('base64').slice(0, 12),
|
|
});
|
|
|
|
const userToken = await this.getUserToken(rocketChatUserId);
|
|
if (!userToken) {
|
|
logger.error('[ROCKETCHAT_ADAPTER] Could not get user token', {
|
|
rocketChatUserId,
|
|
});
|
|
return {
|
|
total: 0,
|
|
unread: 0,
|
|
sources: {
|
|
rocketchat: {
|
|
total: 0,
|
|
unread: 0
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
const userHeaders = {
|
|
'X-Auth-Token': userToken.authToken,
|
|
'X-User-Id': userToken.userId,
|
|
'Content-Type': 'application/json'
|
|
};
|
|
|
|
// Get user's subscriptions
|
|
const subscriptionsResponse = await fetch(`${this.baseUrl}/api/v1/subscriptions.get`, {
|
|
method: 'GET',
|
|
headers: userHeaders
|
|
});
|
|
|
|
if (!subscriptionsResponse.ok) {
|
|
logger.error('[ROCKETCHAT_ADAPTER] Failed to get subscriptions', {
|
|
status: subscriptionsResponse.status,
|
|
});
|
|
return {
|
|
total: 0,
|
|
unread: 0,
|
|
sources: {
|
|
rocketchat: {
|
|
total: 0,
|
|
unread: 0
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
const subscriptionsData = await subscriptionsResponse.json();
|
|
if (!subscriptionsData.success || !Array.isArray(subscriptionsData.update)) {
|
|
logger.error('[ROCKETCHAT_ADAPTER] Invalid subscriptions response');
|
|
return {
|
|
total: 0,
|
|
unread: 0,
|
|
sources: {
|
|
rocketchat: {
|
|
total: 0,
|
|
unread: 0
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
// Filter subscriptions with unread messages
|
|
const userSubscriptions = subscriptionsData.update.filter((sub: any) => {
|
|
return (sub.unread > 0 || sub.alert) && ['d', 'c', 'p'].includes(sub.t);
|
|
});
|
|
|
|
// Calculate total unread count
|
|
const totalUnreadCount = userSubscriptions.reduce((sum: number, sub: any) =>
|
|
sum + (sub.unread || 0), 0);
|
|
|
|
logger.debug('[ROCKETCHAT_ADAPTER] Notification counts', {
|
|
total: userSubscriptions.length,
|
|
unread: totalUnreadCount,
|
|
});
|
|
|
|
return {
|
|
total: userSubscriptions.length,
|
|
unread: totalUnreadCount,
|
|
sources: {
|
|
rocketchat: {
|
|
total: userSubscriptions.length,
|
|
unread: totalUnreadCount
|
|
}
|
|
}
|
|
};
|
|
} catch (error) {
|
|
logger.error('[ROCKETCHAT_ADAPTER] Error getting notification count', {
|
|
error: error instanceof Error ? error.message : String(error),
|
|
});
|
|
return {
|
|
total: 0,
|
|
unread: 0,
|
|
sources: {
|
|
rocketchat: {
|
|
total: 0,
|
|
unread: 0
|
|
}
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
async getNotifications(userId: string, page = 1, limit = 20): Promise<Notification[]> {
|
|
// For now, return empty array - we can implement this later if needed
|
|
// The notification count is what matters for the badge
|
|
return [];
|
|
}
|
|
|
|
async markAsRead(userId: string, notificationId: string): Promise<boolean> {
|
|
// Not implemented yet - RocketChat handles read status automatically
|
|
return false;
|
|
}
|
|
|
|
async markAllAsRead(userId: string): Promise<boolean> {
|
|
// Not implemented yet - RocketChat handles read status automatically
|
|
return false;
|
|
}
|
|
}
|