missions api2
This commit is contained in:
parent
ec0adbf24a
commit
32367c4a9c
@ -4,7 +4,7 @@ import { authOptions } from "@/app/api/auth/options";
|
||||
import { prisma } from '@/lib/prisma';
|
||||
import { getPublicUrl } from '@/lib/s3';
|
||||
import { S3_CONFIG } from '@/lib/s3';
|
||||
import { IntegrationService } from '@/lib/services/integration-service-fixed';
|
||||
import { IntegrationService } from '@/lib/services/integration-service';
|
||||
|
||||
// Helper function to check authentication
|
||||
async function checkAuth(request: Request) {
|
||||
|
||||
@ -1,277 +0,0 @@
|
||||
import { prisma } from '@/lib/prisma';
|
||||
import { LeantimeService } from './leantime-service';
|
||||
import { OutlineService } from './outline-service-fixed';
|
||||
import { RocketChatService } from './rocketchat-service-fixed';
|
||||
import axios from 'axios';
|
||||
|
||||
interface IntegrationResult {
|
||||
success: boolean;
|
||||
error?: string;
|
||||
data?: {
|
||||
leantimeProjectId?: number;
|
||||
outlineCollectionId?: string;
|
||||
rocketChatChannelId?: string;
|
||||
giteaRepositoryUrl?: string;
|
||||
penpotProjectId?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export class IntegrationService {
|
||||
private leantimeService: LeantimeService;
|
||||
private outlineService: OutlineService;
|
||||
private rocketChatService: RocketChatService;
|
||||
|
||||
constructor() {
|
||||
this.leantimeService = new LeantimeService();
|
||||
this.outlineService = new OutlineService();
|
||||
this.rocketChatService = new RocketChatService();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up all integrations for a mission
|
||||
* @param missionId The mission ID
|
||||
* @returns Integration result
|
||||
*/
|
||||
async setupIntegrationsForMission(missionId: string): Promise<IntegrationResult> {
|
||||
try {
|
||||
// Get complete mission data with users
|
||||
const mission = await prisma.mission.findUnique({
|
||||
where: { id: missionId },
|
||||
include: {
|
||||
missionUsers: {
|
||||
include: {
|
||||
user: true
|
||||
}
|
||||
},
|
||||
attachments: true
|
||||
}
|
||||
});
|
||||
|
||||
if (!mission) {
|
||||
throw new Error(`Mission not found: ${missionId}`);
|
||||
}
|
||||
|
||||
// These fields will store the IDs of created resources
|
||||
let leantimeProjectId: number | undefined;
|
||||
let outlineCollectionId: string | undefined;
|
||||
let rocketChatChannelId: string | undefined;
|
||||
|
||||
// Track which integrations succeeded and which failed
|
||||
const integrationStatus = {
|
||||
leantime: { success: false, id: undefined as number | undefined, error: undefined as string | undefined },
|
||||
outline: { success: false, id: undefined as string | undefined, error: undefined as string | undefined },
|
||||
rocketchat: { success: false, id: undefined as string | undefined, error: undefined as string | undefined }
|
||||
};
|
||||
|
||||
// A flag to determine if we should consider this a success or failure overall
|
||||
let criticalFailure = false;
|
||||
|
||||
try {
|
||||
// Step 1: Create Leantime project (Consider this a critical integration)
|
||||
try {
|
||||
console.log('Starting Leantime project creation...');
|
||||
leantimeProjectId = await this.leantimeService.createProject(mission);
|
||||
console.log(`Leantime project created with ID: ${leantimeProjectId}`);
|
||||
integrationStatus.leantime.success = true;
|
||||
integrationStatus.leantime.id = leantimeProjectId;
|
||||
} catch (leantimeError) {
|
||||
console.error('Error creating Leantime project:', leantimeError);
|
||||
integrationStatus.leantime.success = false;
|
||||
integrationStatus.leantime.error = leantimeError instanceof Error ? leantimeError.message : String(leantimeError);
|
||||
criticalFailure = true;
|
||||
throw leantimeError; // Leantime is critical, so we rethrow
|
||||
}
|
||||
|
||||
// Add a delay to avoid rate limits (extended to 3 seconds)
|
||||
console.log('Waiting 3 seconds before proceeding to Outline integration...');
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
|
||||
// Step 2: Create Outline collection (Consider this non-critical)
|
||||
try {
|
||||
console.log('Starting Outline collection creation...');
|
||||
outlineCollectionId = await this.outlineService.createCollection(mission);
|
||||
console.log(`Outline collection created with ID: ${outlineCollectionId}`);
|
||||
integrationStatus.outline.success = true;
|
||||
integrationStatus.outline.id = outlineCollectionId;
|
||||
} catch (outlineError) {
|
||||
console.error('Error creating Outline collection:', outlineError);
|
||||
integrationStatus.outline.success = false;
|
||||
integrationStatus.outline.error = outlineError instanceof Error ? outlineError.message : String(outlineError);
|
||||
|
||||
// Check if it's an authentication error (401)
|
||||
if (axios.isAxiosError(outlineError) && outlineError.response?.status === 401) {
|
||||
console.log('⚠️ Outline authentication error. Please check your API credentials.');
|
||||
} else if (axios.isAxiosError(outlineError) && outlineError.response?.status === 429) {
|
||||
console.log('⚠️ Outline rate limiting error (429). The integration will be skipped for now.');
|
||||
}
|
||||
|
||||
// Don't set criticalFailure - Outline is non-critical
|
||||
}
|
||||
|
||||
// Add a delay to avoid rate limits (extended to 3 seconds)
|
||||
console.log('Waiting 3 seconds before proceeding to Rocket.Chat integration...');
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
|
||||
// Step 3: Create Rocket.Chat channel (Consider this non-critical)
|
||||
try {
|
||||
console.log('Starting Rocket.Chat channel creation...');
|
||||
rocketChatChannelId = await this.rocketChatService.createChannel(mission);
|
||||
console.log(`Rocket.Chat channel created with ID: ${rocketChatChannelId}`);
|
||||
integrationStatus.rocketchat.success = true;
|
||||
integrationStatus.rocketchat.id = rocketChatChannelId;
|
||||
} catch (rocketChatError) {
|
||||
console.error('Error creating Rocket.Chat channel:', rocketChatError);
|
||||
integrationStatus.rocketchat.success = false;
|
||||
integrationStatus.rocketchat.error = rocketChatError instanceof Error ? rocketChatError.message : String(rocketChatError);
|
||||
|
||||
// Check for rate limiting
|
||||
if (axios.isAxiosError(rocketChatError) && rocketChatError.response?.status === 429) {
|
||||
console.log('⚠️ Rocket.Chat rate limiting error (429). The integration will be skipped for now.');
|
||||
}
|
||||
// Don't set criticalFailure - Rocket.Chat is non-critical
|
||||
}
|
||||
|
||||
// Update the mission with the integration IDs (only for successful integrations)
|
||||
try {
|
||||
// Use raw SQL or updateMany if the Prisma client doesn't recognize the fields
|
||||
await prisma.$executeRaw`
|
||||
UPDATE "Mission"
|
||||
SET "leantimeProjectId" = ${integrationStatus.leantime.success ? leantimeProjectId?.toString() : null},
|
||||
"outlineCollectionId" = ${integrationStatus.outline.success ? outlineCollectionId : null},
|
||||
"rocketChatChannelId" = ${integrationStatus.rocketchat.success ? rocketChatChannelId : null}
|
||||
WHERE "id" = ${missionId}
|
||||
`;
|
||||
|
||||
console.log('Successfully updated mission with integration IDs');
|
||||
} catch (updateError) {
|
||||
console.error('Error updating mission with integration IDs:', updateError);
|
||||
// This isn't critical, so don't fail the overall process
|
||||
}
|
||||
|
||||
// Output a summary of integration results
|
||||
console.log('Integration results:', JSON.stringify(integrationStatus, null, 2));
|
||||
|
||||
// If we get here without a critical failure, we consider it a success
|
||||
// even if some non-critical integrations failed
|
||||
return {
|
||||
success: !criticalFailure,
|
||||
data: {
|
||||
leantimeProjectId,
|
||||
outlineCollectionId,
|
||||
rocketChatChannelId
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error setting up integrations:', error);
|
||||
|
||||
// Rollback any created resources
|
||||
await this.rollbackIntegrations(leantimeProjectId, outlineCollectionId, rocketChatChannelId);
|
||||
|
||||
// Delete the mission itself since a critical integration failed
|
||||
try {
|
||||
await prisma.mission.delete({
|
||||
where: { id: missionId }
|
||||
});
|
||||
console.log(`Mission ${missionId} deleted due to critical integration failure.`);
|
||||
} catch (deleteError) {
|
||||
console.error('Error deleting mission after integration failure:', deleteError);
|
||||
}
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: `Integration failed: ${error instanceof Error ? error.message : String(error)}`
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error in integration setup:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: `Integration setup failed: ${error instanceof Error ? error.message : String(error)}`
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rollback integrations if any step fails
|
||||
* @param leantimeProjectId The Leantime project ID to delete
|
||||
* @param outlineCollectionId The Outline collection ID to delete
|
||||
* @param rocketChatChannelId The Rocket.Chat channel ID to delete
|
||||
*/
|
||||
private async rollbackIntegrations(
|
||||
leantimeProjectId?: number,
|
||||
outlineCollectionId?: string,
|
||||
rocketChatChannelId?: string
|
||||
): Promise<void> {
|
||||
console.log('⚠️ Rolling back integrations due to an error...');
|
||||
|
||||
// Track what we've successfully rolled back
|
||||
const rollbackStatuses = {
|
||||
leantime: false,
|
||||
outline: false,
|
||||
rocketchat: false
|
||||
};
|
||||
|
||||
try {
|
||||
// Attempt to delete Leantime project
|
||||
if (leantimeProjectId) {
|
||||
try {
|
||||
console.log(`Attempting to delete Leantime project: ${leantimeProjectId}`);
|
||||
const leantimeSuccess = await this.leantimeService.deleteProject(leantimeProjectId);
|
||||
rollbackStatuses.leantime = leantimeSuccess;
|
||||
console.log(`Leantime project deletion ${leantimeSuccess ? 'successful' : 'failed'}: ${leantimeProjectId}`);
|
||||
|
||||
// Add a longer delay to avoid rate limiting (3 seconds)
|
||||
console.log('Waiting 3 seconds before next rollback operation...');
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
} catch (leantimeError) {
|
||||
console.error('Error during Leantime rollback:', leantimeError);
|
||||
console.log(`⚠️ Note: Leantime project ${leantimeProjectId} may need to be deleted manually`);
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to delete Outline collection
|
||||
if (outlineCollectionId) {
|
||||
try {
|
||||
console.log(`Attempting to delete Outline collection: ${outlineCollectionId}`);
|
||||
const outlineSuccess = await this.outlineService.deleteCollection(outlineCollectionId);
|
||||
rollbackStatuses.outline = outlineSuccess;
|
||||
console.log(`Outline collection deletion ${outlineSuccess ? 'successful' : 'failed'}: ${outlineCollectionId}`);
|
||||
|
||||
// Add a longer delay to avoid rate limiting (3 seconds)
|
||||
console.log('Waiting 3 seconds before next rollback operation...');
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
} catch (outlineError) {
|
||||
console.error('Error during Outline rollback:', outlineError);
|
||||
console.log(`⚠️ Note: Outline collection ${outlineCollectionId} may need to be deleted manually`);
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to delete Rocket.Chat channel
|
||||
if (rocketChatChannelId) {
|
||||
try {
|
||||
console.log(`Attempting to delete Rocket.Chat channel: ${rocketChatChannelId}`);
|
||||
const rocketChatSuccess = await this.rocketChatService.deleteChannel(rocketChatChannelId);
|
||||
rollbackStatuses.rocketchat = rocketChatSuccess;
|
||||
console.log(`Rocket.Chat channel deletion ${rocketChatSuccess ? 'successful' : 'failed'}: ${rocketChatChannelId}`);
|
||||
} catch (rocketChatError) {
|
||||
console.error('Error during Rocket.Chat rollback:', rocketChatError);
|
||||
console.log(`⚠️ Note: Rocket.Chat channel ${rocketChatChannelId} may need to be deleted manually`);
|
||||
}
|
||||
}
|
||||
|
||||
// Provide a summary of rollback operations
|
||||
console.log('Rollback summary:', JSON.stringify(rollbackStatuses));
|
||||
|
||||
// If any rollbacks failed, provide a note
|
||||
if (!rollbackStatuses.leantime || !rollbackStatuses.outline || !rollbackStatuses.rocketchat) {
|
||||
console.log('⚠️ Some resources may need to be deleted manually.');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error during rollback:', error);
|
||||
console.log('⚠️ Resources may need to be deleted manually:');
|
||||
if (leantimeProjectId) console.log(`- Leantime project: ${leantimeProjectId}`);
|
||||
if (outlineCollectionId) console.log(`- Outline collection: ${outlineCollectionId}`);
|
||||
if (rocketChatChannelId) console.log(`- Rocket.Chat channel: ${rocketChatChannelId}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,183 +0,0 @@
|
||||
import axios from 'axios';
|
||||
|
||||
export class OutlineService {
|
||||
private apiUrl: string;
|
||||
private apiToken: string;
|
||||
|
||||
constructor() {
|
||||
this.apiUrl = process.env.OUTLINE_API_URL || 'https://app.getoutline.com/api';
|
||||
this.apiToken = process.env.OUTLINE_API_KEY || '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new collection in Outline
|
||||
* @param mission The mission data
|
||||
* @returns Collection ID or throws error
|
||||
*/
|
||||
async createCollection(mission: any): Promise<string> {
|
||||
try {
|
||||
// Log the API details for debugging
|
||||
console.log('Creating Outline collection with token length:', this.apiToken ? this.apiToken.length : 0);
|
||||
console.log('Outline API URL:', this.apiUrl);
|
||||
|
||||
const response = await axios.post(
|
||||
`${this.apiUrl}/collections.create`,
|
||||
{
|
||||
name: mission.name,
|
||||
description: mission.intention || '',
|
||||
permission: 'read',
|
||||
private: true
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${this.apiToken}`
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.data || !response.data.data || !response.data.data.id) {
|
||||
throw new Error(`Failed to create Outline collection: ${JSON.stringify(response.data)}`);
|
||||
}
|
||||
|
||||
const collectionId = response.data.data.id;
|
||||
console.log(`Created Outline collection with ID: ${collectionId}`);
|
||||
|
||||
// Assign users to the collection
|
||||
if (mission.missionUsers && mission.missionUsers.length > 0) {
|
||||
await this.assignUsersToCollection(collectionId, mission.missionUsers);
|
||||
}
|
||||
|
||||
return collectionId;
|
||||
} catch (error) {
|
||||
if (axios.isAxiosError(error) && error.response) {
|
||||
console.error('Outline API Error Details:', {
|
||||
status: error.response.status,
|
||||
statusText: error.response.statusText,
|
||||
data: error.response.data
|
||||
});
|
||||
}
|
||||
console.error('Error creating Outline collection:', error);
|
||||
throw new Error(`Outline integration failed: ${error instanceof Error ? error.message : String(error)}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign mission users to the Outline collection
|
||||
* @param collectionId The Outline collection ID
|
||||
* @param missionUsers The mission users with roles
|
||||
*/
|
||||
async assignUsersToCollection(collectionId: string, missionUsers: any[]): Promise<void> {
|
||||
try {
|
||||
for (const missionUser of missionUsers) {
|
||||
// Get the user in Outline
|
||||
const outlineUserId = await this.getUserByEmail(missionUser.user.email);
|
||||
if (!outlineUserId) {
|
||||
console.warn(`User not found in Outline: ${missionUser.user.email}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Determine permission (Gardien de la Mémoire gets admin, others get read)
|
||||
const permission = missionUser.role === 'gardien-memoire' ? 'admin' : 'read';
|
||||
|
||||
// Add the user to the collection
|
||||
await this.addUserToCollection(collectionId, outlineUserId, permission);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error assigning users to collection:', error);
|
||||
// Continue even if some user assignments fail
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a user to a collection with the specified permission
|
||||
* @param collectionId The Outline collection ID
|
||||
* @param userId The Outline user ID
|
||||
* @param permission The permission to assign
|
||||
*/
|
||||
async addUserToCollection(collectionId: string, userId: string, permission: 'read' | 'admin'): Promise<void> {
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${this.apiUrl}/collections.add_user`,
|
||||
{
|
||||
id: collectionId,
|
||||
userId: userId,
|
||||
permission: permission
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${this.apiToken}`
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.data || !response.data.success) {
|
||||
throw new Error(`Failed to add user to collection: ${JSON.stringify(response.data)}`);
|
||||
}
|
||||
|
||||
console.log(`Added user ${userId} to collection ${collectionId} with permission ${permission}`);
|
||||
} catch (error) {
|
||||
console.error(`Error adding user ${userId} to collection ${collectionId}:`, error);
|
||||
// Don't fail if individual user assignment fails
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a user ID by email
|
||||
* @param email The user email to search for
|
||||
* @returns The user ID or null if not found
|
||||
*/
|
||||
async getUserByEmail(email: string): Promise<string | null> {
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${this.apiUrl}/users.info`,
|
||||
{
|
||||
email: email
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${this.apiToken}`
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.data || !response.data.data || !response.data.data.id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return response.data.data.id;
|
||||
} catch (error) {
|
||||
console.error('Error getting user by email:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a collection from Outline
|
||||
* @param collectionId The Outline collection ID to delete
|
||||
* @returns True if successful, false otherwise
|
||||
*/
|
||||
async deleteCollection(collectionId: string): Promise<boolean> {
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${this.apiUrl}/collections.delete`,
|
||||
{
|
||||
id: collectionId
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${this.apiToken}`
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return response.data && response.data.success === true;
|
||||
} catch (error) {
|
||||
console.error(`Error deleting Outline collection ${collectionId}:`, error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,221 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user