NeahNew/lib/services/rocketchat-service.ts
2025-05-06 15:36:15 +02:00

221 lines
6.7 KiB
TypeScript

import axios from 'axios';
export class RocketChatService {
private apiUrl: string;
private authToken: string;
private userId: string;
constructor() {
// Extract the base URL from the iframe URL
const baseUrl = process.env.NEXT_PUBLIC_IFRAME_PAROLE_URL?.split('/channel')[0] || '';
this.apiUrl = baseUrl;
this.authToken = process.env.ROCKET_CHAT_TOKEN || '';
this.userId = process.env.ROCKET_CHAT_USER_ID || '';
}
/**
* Create a new channel in Rocket.Chat
* @param mission The mission data
* @returns Channel ID or throws error
*/
async createChannel(mission: any): Promise<string> {
try {
// First, get all mission users that need to be added to the channel
const missionUserEmails = mission.missionUsers.map((mu: any) => mu.user.email);
const rocketChatUsernames: string[] = [];
for (const email of missionUserEmails) {
const user = await this.getUserByEmail(email);
if (user) {
rocketChatUsernames.push(user.username);
} else {
console.warn(`User not found in Rocket.Chat: ${email}`);
}
}
// Sanitize the channel name to comply with Rocket.Chat restrictions
const channelName = this.sanitizeChannelName(mission.name);
// Create the channel
const response = await axios.post(
`${this.apiUrl}/api/v1/channels.create`,
{
name: channelName,
members: rocketChatUsernames,
readOnly: false,
},
{
headers: {
'Content-Type': 'application/json',
'X-Auth-Token': this.authToken,
'X-User-Id': this.userId
}
}
);
if (!response.data || !response.data.success) {
throw new Error(`Failed to create Rocket.Chat channel: ${JSON.stringify(response.data)}`);
}
const channelId = response.data.channel._id;
console.log(`Created Rocket.Chat channel with ID: ${channelId}`);
// Make "Gardien de la Parole" users channel admins
await this.setChannelAdmins(channelId, mission.missionUsers);
return channelId;
} catch (error) {
console.error('Error creating Rocket.Chat channel:', error);
throw new Error(`Rocket.Chat integration failed: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Make "Gardien de la Parole" users channel admins
* @param channelId The Rocket.Chat channel ID
* @param missionUsers The mission users with roles
*/
async setChannelAdmins(channelId: string, missionUsers: any[]): Promise<void> {
try {
// Find "Gardien de la Parole" users
const gardienParoleUsers = missionUsers.filter((mu: any) => mu.role === 'gardien-parole');
for (const gardienUser of gardienParoleUsers) {
const user = await this.getUserByEmail(gardienUser.user.email);
if (user) {
await this.makeUserChannelOwner(channelId, user._id);
}
}
} catch (error) {
console.error('Error setting channel admins:', error);
// Don't fail if setting admins fails
}
}
/**
* Make a user a channel owner
* @param channelId The Rocket.Chat channel ID
* @param userId The Rocket.Chat user ID
*/
async makeUserChannelOwner(channelId: string, userId: string): Promise<void> {
try {
const response = await axios.post(
`${this.apiUrl}/api/v1/channels.addOwner`,
{
roomId: channelId,
userId: userId
},
{
headers: {
'Content-Type': 'application/json',
'X-Auth-Token': this.authToken,
'X-User-Id': this.userId
}
}
);
if (!response.data || !response.data.success) {
throw new Error(`Failed to make user channel owner: ${JSON.stringify(response.data)}`);
}
console.log(`Made user ${userId} an owner of channel ${channelId}`);
} catch (error) {
console.error(`Error making user ${userId} channel owner:`, error);
// Don't fail if individual user assignment fails
}
}
/**
* Get a user by email
* @param email The user email to search for
* @returns The user object or null if not found
*/
async getUserByEmail(email: string): Promise<any | null> {
try {
// First try exact email match
const response = await axios.get(
`${this.apiUrl}/api/v1/users.list`,
{
headers: {
'X-Auth-Token': this.authToken,
'X-User-Id': this.userId
}
}
);
if (!response.data || !response.data.success) {
throw new Error(`Failed to get users: ${JSON.stringify(response.data)}`);
}
// First check if user exists by email
const userByEmail = response.data.users.find((user: any) =>
user.emails?.some((e: any) => e.address === email)
);
if (userByEmail) {
return userByEmail;
}
// If not found by email, try by username (username might be the part before @)
const username = email.split('@')[0];
const userByUsername = response.data.users.find((user: any) => user.username === username);
return userByUsername || null;
} catch (error) {
console.error('Error getting user by email:', error);
return null;
}
}
/**
* Sanitize a channel name to comply with Rocket.Chat restrictions
* @param name The original name
* @returns The sanitized name
*/
private sanitizeChannelName(name: string): string {
// Replace spaces and invalid characters with hyphens
// Channel names must match regex [0-9a-zA-Z-_.]+
let sanitized = name.replace(/[^0-9a-zA-Z-_.]/g, '-');
// Ensure no sequential hyphens
sanitized = sanitized.replace(/-+/g, '-');
// Trim hyphens from beginning and end
sanitized = sanitized.replace(/^-+|-+$/g, '');
// Ensure name is not empty after sanitization
if (!sanitized) {
sanitized = 'mission-channel';
}
return sanitized.toLowerCase();
}
/**
* Delete a channel from Rocket.Chat
* @param channelId The Rocket.Chat channel ID to delete
* @returns True if successful, false otherwise
*/
async deleteChannel(channelId: string): Promise<boolean> {
try {
const response = await axios.post(
`${this.apiUrl}/api/v1/channels.delete`,
{
roomId: channelId
},
{
headers: {
'Content-Type': 'application/json',
'X-Auth-Token': this.authToken,
'X-User-Id': this.userId
}
}
);
return response.data && response.data.success === true;
} catch (error) {
console.error(`Error deleting Rocket.Chat channel ${channelId}:`, error);
return false;
}
}
}