Skip to main content
Glama
direct-api-client.js13 kB
#!/usr/bin/env node /** * Direct API Client v1.0.0 * Standalone API client for EuConquisto Composer (no browser automation required) * * Purpose: Enable direct API access without JWT redirect server or browser automation * Status: EXPERIMENTAL - Alternative implementation approach * * @version 1.0.0 (January 15, 2025) */ import fetch from 'node-fetch'; import FormData from 'form-data'; import { DirectAPIConfig } from '../config/direct-api-config.js'; export class DirectAPIClient { constructor(config = null) { this.config = DirectAPIConfig; this.apiLog = []; // Load configuration if provided if (config) { this.config.loadFromConfig(config); } else { // Try to load from environment variables this.config.loadFromEnvironment(); } this.isReady = this.config.isValid(); } /** * Validate API configuration and connectivity */ async validateConnection() { console.log('🔍 Validating direct API connection...'); if (!this.isReady) { const status = this.config.getValidationStatus(); return { success: false, error: 'Configuration invalid', details: status }; } try { // Test user profile endpoint const userResponse = await this.getUserProfile(); if (userResponse.success) { console.log('✅ API connection validated successfully'); return { success: true, userProfile: userResponse.data, config: this.config.getSummary() }; } else { console.log('❌ API connection validation failed'); return { success: false, error: 'User profile request failed', details: userResponse }; } } catch (error) { console.log('❌ API connection validation error:', error.message); return { success: false, error: error.message, networkError: true }; } } /** * Get user profile (test authentication) */ async getUserProfile() { const url = this.config.buildURL(this.config.endpoints.userProfile); const headers = this.config.getHeaders(); this.logAPI('GET_USER_PROFILE', 'Requesting user profile', url); try { const response = await fetch(url, { method: 'GET', headers: headers }); const responseText = await response.text(); let parsedResponse = null; try { parsedResponse = JSON.parse(responseText); } catch (e) { // Response might not be JSON } const result = { success: response.ok, status: response.status, statusText: response.statusText, data: parsedResponse, responseText: responseText }; this.logAPI('GET_USER_PROFILE_RESULT', `Status: ${response.status}`, result.success); return result; } catch (error) { const result = { success: false, error: error.message, networkError: true }; this.logAPI('GET_USER_PROFILE_ERROR', `Network error: ${error.message}`, false); return result; } } /** * Get project information */ async getProjectInfo() { const url = this.config.buildURL(this.config.endpoints.projectInfo, { projectUid: this.config.authentication.projectUid }); const headers = this.config.getHeaders(); this.logAPI('GET_PROJECT_INFO', 'Requesting project information', url); try { const response = await fetch(url, { method: 'GET', headers: headers }); const responseText = await response.text(); let parsedResponse = null; try { parsedResponse = JSON.parse(responseText); } catch (e) { // Response might not be JSON } const result = { success: response.ok, status: response.status, statusText: response.statusText, data: parsedResponse, responseText: responseText }; this.logAPI('GET_PROJECT_INFO_RESULT', `Status: ${response.status}`, result.success); return result; } catch (error) { const result = { success: false, error: error.message, networkError: true }; this.logAPI('GET_PROJECT_INFO_ERROR', `Network error: ${error.message}`, false); return result; } } /** * Save composition directly via API */ async saveComposition(compositionData, title = 'Direct API Composition') { console.log('💾 Saving composition via direct API...'); if (!this.isReady) { return { success: false, error: 'API client not properly configured' }; } try { // Select optimal connector const connector = this.config.selectOptimalConnector(); if (!connector) { return { success: false, error: 'No suitable connector available' }; } console.log(`📡 Using connector: ${connector.name || connector.uid}`); // Build upload URL const uploadUrl = this.config.buildURL(this.config.endpoints.uploadComposition, { connectorUid: connector.uid, projectUid: this.config.authentication.projectUid }); // Prepare composition file const timestamp = Date.now(); const safeName = title.replace(/[^a-zA-Z0-9]/g, '_'); const fileName = `composition_${timestamp}_${safeName}.rdpcomposer`; const compositionJSON = JSON.stringify(compositionData, null, 2); console.log(`📄 File: ${fileName} (${compositionJSON.length} bytes)`); // Prepare FormData const formData = new FormData(); formData.append('file', compositionJSON, { filename: fileName, contentType: 'application/json' }); // Get upload headers (no Content-Type for FormData) const headers = this.config.getUploadHeaders(); this.logAPI('SAVE_COMPOSITION', 'Uploading composition', { url: uploadUrl, fileName: fileName, fileSize: compositionJSON.length, connector: connector.name || connector.uid }); // Execute upload const response = await fetch(uploadUrl, { method: 'POST', headers: headers, body: formData }); const responseText = await response.text(); let parsedResponse = null; try { parsedResponse = JSON.parse(responseText); } catch (e) { // Response might not be JSON } const result = { success: response.ok, status: response.status, statusText: response.statusText, data: parsedResponse, responseText: responseText, uploadDetails: { fileName: fileName, fileSize: compositionJSON.length, connector: connector.name || connector.uid, projectUid: this.config.authentication.projectUid } }; if (result.success) { // Try to extract composition UID const compositionUid = this.extractCompositionUID(parsedResponse); if (compositionUid) { result.compositionUid = compositionUid; console.log(`✅ Composition saved successfully: ${compositionUid}`); } else { console.log(`✅ Composition uploaded successfully (UID extraction failed)`); } } else { console.log(`❌ Composition save failed: ${response.status} ${response.statusText}`); } this.logAPI('SAVE_COMPOSITION_RESULT', `Status: ${response.status}`, result.success); return result; } catch (error) { console.log(`❌ Composition save error: ${error.message}`); const result = { success: false, error: error.message, networkError: true }; this.logAPI('SAVE_COMPOSITION_ERROR', `Network error: ${error.message}`, false); return result; } } /** * Extract composition UID from API response (same logic as current implementation) */ extractCompositionUID(responseData) { if (!responseData) return null; // Array response format if (Array.isArray(responseData) && responseData[0]) { const firstItem = responseData[0]; return firstItem.uid || firstItem.id || firstItem.compositionId || null; } // Direct object response if (typeof responseData === 'object') { return responseData.uid || responseData.id || responseData.compositionId || responseData.composition_uid || responseData.fileId || null; } // String response (direct UID) if (typeof responseData === 'string' && responseData.length > 10) { return responseData; } return null; } /** * Create a complete composition workflow (generate + save) */ async createComposition(prompt, subject = 'General', gradeLevel = '6º ano') { console.log('🎯 Creating composition via direct API workflow...'); console.log(` Prompt: ${prompt}`); console.log(` Subject: ${subject}`); console.log(` Grade Level: ${gradeLevel}\n`); try { // Step 1: Generate composition structure (simplified for testing) const composition = this.generateTestComposition(prompt, subject, gradeLevel); // Step 2: Save via direct API const saveResult = await this.saveComposition(composition, prompt); if (saveResult.success) { return { success: true, composition: composition, saveResult: saveResult, compositionUid: saveResult.compositionUid, message: 'Composition created and saved successfully via direct API' }; } else { return { success: false, error: 'Failed to save composition', details: saveResult }; } } catch (error) { return { success: false, error: error.message, workflowError: true }; } } /** * Generate a test composition (simplified version for API testing) */ generateTestComposition(prompt, subject, gradeLevel) { return { metadata: { title: prompt.substring(0, 50), description: `Educational content about ${subject} for ${gradeLevel}`, subject: subject, gradeLevel: gradeLevel, createdAt: new Date().toISOString(), createdBy: 'Direct API Client v1.0.0' }, structure: [ { type: "head-1", text: prompt.substring(0, 50), level: 1 }, { type: "text-1", text: `<p>Este é um conteúdo educacional sobre <strong>${subject}</strong> criado via API direta.</p><p>Prompt: ${prompt}</p><p>Nível: ${gradeLevel}</p>` }, { type: "quiz-1", questions: [ { question: `Qual é o tema principal deste conteúdo sobre ${subject}?`, options: [ "Conceitos básicos", "Aplicações práticas", "Teoria avançada", "Exercícios" ], correct: 0 } ] } ] }; } /** * Get API logs for debugging */ getAPILogs() { return this.apiLog; } /** * Log API operations */ logAPI(operation, details, success = true) { this.apiLog.push({ timestamp: new Date().toISOString(), operation: operation, details: details, success: success }); } /** * Get configuration status */ getStatus() { return { ready: this.isReady, config: this.config.getSummary(), apiLogs: this.apiLog.length }; } } // CLI testing utility if (import.meta.url === `file://${process.argv[1]}`) { console.log('🔗 Direct API Client v1.0.0'); console.log('============================\n'); const client = new DirectAPIClient(); console.log('📊 Client Status:'); console.log(JSON.stringify(client.getStatus(), null, 2)); if (client.isReady) { console.log('\n🧪 Running API validation test...'); client.validateConnection().then(result => { console.log('\n📋 Validation Result:'); console.log(JSON.stringify(result, null, 2)); if (result.success) { console.log('\n✅ Direct API approach is functional!'); console.log('🎯 Next: Configure environment variables and test composition creation'); } else { console.log('\n❌ Direct API requires configuration or has connectivity issues'); console.log('🎯 Next: Check authentication configuration and network connectivity'); } }).catch(error => { console.error('\n❌ Test execution error:', error.message); }); } else { console.log('\n⚠️ Client not ready - configuration required'); console.log('🎯 Configure authentication via environment variables or config object'); } } // Export already declared at the bottom

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/rkm097git/euconquisto-composer-mcp-poc'

If you have feedback or need assistance with the MCP directory API, please join our Discord server