diff --git a/.env b/.env index 5a562b3f..439b5763 100644 --- a/.env +++ b/.env @@ -11,7 +11,8 @@ KEYCLOAK_ADMIN_PASSWORD=PW5jfqX00m DOLIBARR_API_URL=https://mediations.slm-lab.net/api/index.php/ DOLIBARR_API_KEY=2znq976PzZz1q2JSe9DG2A3hmbNMGIh8 - +OUTLINE_API_KEY="ol_api_tlLlANBfcoJ4l7zA8GOcpduAeL6QyBTcYvEnlN" +OUTLINE_API_URL="https://chapitre.slm-lab.net/api" # MinIO S3 configuration MINIO_S3_UPLOAD_BUCKET_URL="https://dome-api.slm-lab.net/" MINIO_AWS_REGION="eu-east-1" diff --git a/lib/services/outline-service.ts b/lib/services/outline-service.ts index f01a12ac..05712175 100644 --- a/lib/services/outline-service.ts +++ b/lib/services/outline-service.ts @@ -5,28 +5,33 @@ export class OutlineService { private apiToken: string; constructor() { - this.apiUrl = process.env.OUTLINE_API_URL || ''; + this.apiUrl = process.env.OUTLINE_API_URL || 'https://chapitre.slm-lab.net/api'; this.apiToken = process.env.OUTLINE_API_KEY || ''; + console.log('OutlineService initialized with URL:', this.apiUrl); + console.log('Creating Outline collection with token length:', this.apiToken.length); } - /** - * Create a new collection in Outline - * @param mission The mission data - * @returns Collection ID or throws error - */ async createCollection(mission: any): Promise { + if (!this.apiToken) { + throw new Error('Outline API token is not configured'); + } + 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); - + // Create a collection in Outline based on the mission const response = await axios.post( - `${this.apiUrl}/collections.create`, + `${this.apiUrl}/collections`, { - name: mission.name, - description: mission.intention || '', - permission: 'read', - private: true + name: mission.label, + description: mission.description || 'Mission documentation', + color: '#4f46e5', // Indigo color as default + permission: 'read_write', + // Added extra fields that might be useful for identifying the collection + private: false, + external: true, + meta: { + missionId: mission.id, + createdAt: new Date().toISOString() + } }, { headers: { @@ -36,136 +41,37 @@ export class OutlineService { } ); - if (!response.data || !response.data.data || !response.data.data.id) { - throw new Error(`Failed to create Outline collection: ${JSON.stringify(response.data)}`); + if (response.data && response.data.data && response.data.data.id) { + return response.data.data.id; + } else { + throw new Error('Failed to get collection ID from Outline API response'); } - - 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 { - 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; + if (axios.isAxiosError(error)) { + if (error.response?.status === 401) { + console.error('Outline authentication error:', error.response.data); + throw new Error('Outline API authentication failed - check your token'); + } else if (error.response?.status === 429) { + console.error('Outline rate limit error:', error.response.data); + throw new Error('Outline API rate limit exceeded - try again later'); + } else { + console.error('Outline API error:', error.response?.data || error.message); + throw new Error(`Outline API error: ${error.response?.status} - ${error.message}`); } - - // 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 + throw error; } } - /** - * 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 { - 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 { - 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 { + if (!this.apiToken) { + throw new Error('Outline API token is not configured'); + } + try { - const response = await axios.post( - `${this.apiUrl}/collections.delete`, - { - id: collectionId - }, + // Delete the collection in Outline + await axios.delete( + `${this.apiUrl}/collections/${collectionId}`, { headers: { 'Content-Type': 'application/json', @@ -174,9 +80,9 @@ export class OutlineService { } ); - return response.data && response.data.success === true; + return true; } catch (error) { - console.error(`Error deleting Outline collection ${collectionId}:`, error); + console.error('Error deleting Outline collection:', error); return false; } }