import axios from 'axios'; export class OutlineService { private apiUrl: string; private apiToken: string; constructor() { 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); } async createCollection(mission: any): Promise { if (!this.apiToken) { throw new Error('Outline API token is not configured'); } console.log('Mission data received:', JSON.stringify({ id: mission.id, label: mission.label, name: mission.name, description: mission.description }, null, 2)); // Determine the best name to use for the collection // Prioritize mission.name, then mission.label, then default const collectionName = mission.name || mission.label || `Mission ${mission.id}`; console.log(`Using collection name: "${collectionName}"`); try { // Create a collection in Outline based on the mission // Note: According to error message, valid permission values are 'read', 'read_write', 'admin' // We'll use private: true to make it private const payload = { name: collectionName, description: mission.description || 'Mission documentation', color: '#4f46e5', // Indigo color as default permission: "read", // Use read-only permission private: true, // Make it private sharing: false // Disable sharing }; console.log('Sending to Outline API:', JSON.stringify(payload, null, 2)); const response = await axios.post( `${this.apiUrl}/collections.create`, payload, { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${this.apiToken}` } } ); console.log('Outline API response:', JSON.stringify(response.data, null, 2)); // Create a default document in the collection if (response.data && (response.data.data?.id || response.data.id)) { const collectionId = response.data.data?.id || response.data.id; try { // Try to create a welcome document in the collection await this.createWelcomeDocument(collectionId, collectionName); } catch (docError) { console.warn('Failed to create welcome document, but collection was created:', docError); } return collectionId; } else { throw new Error('Failed to get collection ID from Outline API response'); } } catch (error) { if (axios.isAxiosError(error)) { console.error('Full error response:', JSON.stringify(error.response?.data, null, 2)); 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}`); } } throw error; } } // Helper method to create a welcome document in a collection private async createWelcomeDocument(collectionId: string, collectionName: string): Promise { try { const response = await axios.post( `${this.apiUrl}/documents.create`, { title: `Welcome to ${collectionName}`, text: `# Welcome to ${collectionName}\n\nThis is your new private collection in Outline. Here you can store and share documents related to this mission.\n\n## Getting Started\n\n- Use the + button to create new documents\n- Organize documents with headers and lists\n- Share this collection only with team members involved in this mission`, collectionId: collectionId, publish: true }, { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${this.apiToken}` } } ); console.log('Created welcome document with ID:', response.data?.data?.id || response.data?.id); } catch (error) { console.error('Failed to create welcome document:', error); } } async deleteCollection(collectionId: string): Promise { if (!this.apiToken) { throw new Error('Outline API token is not configured'); } try { // Delete the collection in Outline await axios.post( `${this.apiUrl}/collections.delete`, { id: collectionId }, { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${this.apiToken}` } } ); return true; } catch (error) { console.error('Error deleting Outline collection:', error); return false; } } }