/** * ═══════════════════════════════════════════════════════════════ * MAHRAK GEO PLATFORM — API INTEGRATION * ═══════════════════════════════════════════════════════════════ * * This file connects the index.html UI with all backend APIs * Handles: Smart Scan, Audit Form, Dashboard Data */ // ═══ CONFIG ═══ const API_BASE = 'http://localhost:8001/api'; // Point to backend on port 8001 const API_ENDPOINTS = { // Analysis & Crawling jobs: `${API_BASE}/jobs`, jobResults: (jobId) => `${API_BASE}/jobs/${jobId}/results`, jobStatus: (jobId) => `${API_BASE}/jobs/${jobId}`, // Keywords keywords: (jobId) => `${API_BASE}/jobs/${jobId}/keywords`, // Tavily Search (for Smart Scan) tavilySearch: `${API_BASE}/tavily/search`, // Competitor Intelligence competitorIntel: `${API_BASE}/competitor/intelligence`, // Health Check health: `${API_BASE}/health`, // Auth auth: { login: `${API_BASE}/auth/login`, register: `${API_BASE}/auth/register`, me: `${API_BASE}/users/me`, } }; // ═══ UTILITY: Get Auth Token ═══ function getAuthToken() { return localStorage.getItem('token') || null; } function setAuthToken(token) { localStorage.setItem('token', token); } // ═══ UTILITY: API Request Helper ═══ async function apiRequest(endpoint, options = {}) { const token = getAuthToken(); const headers = { 'Content-Type': 'application/json', ...options.headers, }; if (token) { headers['Authorization'] = `Bearer ${token}`; } try { const response = await fetch(endpoint, { ...options, headers, }); const data = await response.json(); if (!response.ok) { console.error(`API Error [${response.status}]:`, data); throw new Error(data.error || `HTTP ${response.status}`); } return data; } catch (error) { console.error('API Request Failed:', error); throw error; } } // ═══════════════════════════════════════════════════════════════ // SMART SCAN — Connect to Real Backend // ═══════════════════════════════════════════════════════════════ async function runSmartScanWithAPI(businessName) { console.log('🚀 Starting Smart Scan for:', businessName); try { // Step 1: Check API Health console.log('📡 Checking API health...'); const health = await apiRequest(API_ENDPOINTS.health); console.log('✓ API Health:', health); // Step 2: Run Tavily Search for AI Visibility console.log('🤖 Scanning AI Search Engines...'); const aiSearchResult = await apiRequest(API_ENDPOINTS.tavilySearch, { method: 'POST', body: JSON.stringify({ query: `"${businessName}" OR ${businessName} best recommendation`, max_results: 5, search_depth: 'advanced' }) }); console.log('✓ AI Search Results:', aiSearchResult); // Step 3: Run Tavily Search for Google Results console.log('🔍 Scanning Google Search...'); const googleSearchResult = await apiRequest(API_ENDPOINTS.tavilySearch, { method: 'POST', body: JSON.stringify({ query: businessName, max_results: 10, search_depth: 'basic' }) }); console.log('✓ Google Search Results:', googleSearchResult); // Step 4: Run Tavily Search for Social console.log('📱 Scanning Social Media...'); const socialSearchResult = await apiRequest(API_ENDPOINTS.tavilySearch, { method: 'POST', body: JSON.stringify({ query: `${businessName} site:tiktok.com OR site:instagram.com OR site:youtube.com`, max_results: 5, search_depth: 'basic' }) }); console.log('✓ Social Search Results:', socialSearchResult); // Step 5: Compile Results const scanResults = { businessName, timestamp: new Date().toISOString(), engines: { aiSearch: { name: 'AI Search (ChatGPT/Gemini/Perplexity)', results: aiSearchResult.result?.results || [], score: calculateScore(aiSearchResult.result?.results || []), status: (aiSearchResult.result?.results || []).length > 0 ? 'found' : 'missing' }, googleSearch: { name: 'Google Search', results: googleSearchResult.result?.results || [], score: calculateScore(googleSearchResult.result?.results || []), status: (googleSearchResult.result?.results || []).length > 0 ? 'found' : 'missing' }, socialSearch: { name: 'Social Media (TikTok/Instagram/YouTube)', results: socialSearchResult.result?.results || [], score: calculateScore(socialSearchResult.result?.results || []), status: (socialSearchResult.result?.results || []).length > 0 ? 'found' : 'missing' } } }; console.log('✓ Scan Complete:', scanResults); return scanResults; } catch (error) { console.error('❌ Smart Scan Failed:', error); throw error; } } function calculateScore(results) { if (!results || results.length === 0) return 0; // Score based on number of results and relevance return Math.min(100, results.length * 15); } // ═══════════════════════════════════════════════════════════════ // AUDIT FORM — Connect to Analysis System // ═══════════════════════════════════════════════════════════════ async function submitAuditForm(formData) { console.log('📋 Submitting Audit Form:', formData); try { // Step 1: Create a new job/analysis console.log('🔄 Creating analysis job...'); const jobResponse = await apiRequest(API_ENDPOINTS.jobs, { method: 'POST', body: JSON.stringify({ url: formData.website, org_name: formData.businessName, org_url: formData.website, max_pages: 10, runs: 1, metadata: { contactName: formData.name, contactEmail: formData.email, contactPhone: formData.phone, sector: formData.sector } }) }); if (!jobResponse.ok) { throw new Error(jobResponse.error || 'Failed to create job'); } const jobId = jobResponse.job_id; console.log('✓ Job Created:', jobId); // Step 2: Poll for job completion console.log('⏳ Waiting for analysis to complete...'); const analysisResult = await pollJobCompletion(jobId); console.log('✓ Analysis Complete:', analysisResult); // Step 3: Get detailed results console.log('📊 Fetching detailed results...'); const detailedResults = await apiRequest(API_ENDPOINTS.jobResults(jobId)); console.log('✓ Detailed Results:', detailedResults); return { jobId, formData, analysisResult, detailedResults }; } catch (error) { console.error('❌ Audit Form Submission Failed:', error); throw error; } } async function pollJobCompletion(jobId, maxAttempts = 40, interval = 3000) { let attempts = 0; return new Promise((resolve, reject) => { const poll = async () => { try { const jobStatus = await apiRequest(API_ENDPOINTS.jobStatus(jobId)); console.log(`[Attempt ${attempts + 1}/${maxAttempts}] Job Status:`, jobStatus.status); if (jobStatus.status === 'completed') { resolve(jobStatus); } else if (jobStatus.status === 'failed') { reject(new Error('Job failed: ' + (jobStatus.error || 'Unknown error'))); } else if (attempts >= maxAttempts) { reject(new Error('Job polling timeout')); } else { attempts++; setTimeout(poll, interval); } } catch (error) { reject(error); } }; poll(); }); } // ═══════════════════════════════════════════════════════════════ // DASHBOARD DATA — Load Real Data // ═══════════════════════════════════════════════════════════════ async function loadDashboardData() { console.log('📊 Loading Dashboard Data...'); try { // Get all jobs const jobsResponse = await apiRequest(API_ENDPOINTS.jobs); const jobs = jobsResponse.jobs || []; console.log('✓ Jobs Loaded:', jobs.length); // Get latest completed job const completedJobs = jobs.filter(j => j.status === 'completed'); if (completedJobs.length === 0) { console.warn('⚠️ No completed jobs found'); return null; } const latestJob = completedJobs[completedJobs.length - 1]; console.log('✓ Latest Job:', latestJob.id); // Get job results const results = await apiRequest(API_ENDPOINTS.jobResults(latestJob.id)); console.log('✓ Job Results:', results); // Get keywords const keywords = await apiRequest(API_ENDPOINTS.keywords(latestJob.id)); console.log('✓ Keywords:', keywords); return { job: latestJob, results, keywords }; } catch (error) { console.error('❌ Dashboard Data Load Failed:', error); return null; } } // ═══════════════════════════════════════════════════════════════ // COMPETITOR INTELLIGENCE // ═══════════════════════════════════════════════════════════════ async function getCompetitorIntelligence(url, industry) { console.log('👥 Fetching Competitor Intelligence...'); try { const result = await apiRequest(API_ENDPOINTS.competitorIntel, { method: 'POST', body: JSON.stringify({ url, industry, count: 5 }) }); console.log('✓ Competitor Intelligence:', result); return result; } catch (error) { console.error('❌ Competitor Intelligence Failed:', error); return null; } } // ═══════════════════════════════════════════════════════════════ // HEALTH CHECK — Test All Connections // ═══════════════════════════════════════════════════════════════ async function testAllConnections() { console.log('🔍 Testing All API Connections...'); const results = { timestamp: new Date().toISOString(), endpoints: {} }; // Test Health try { const health = await apiRequest(API_ENDPOINTS.health); results.endpoints.health = { status: 'ok', data: health }; console.log('✓ Health:', health); } catch (error) { results.endpoints.health = { status: 'error', error: error.message }; console.error('✗ Health:', error.message); } // Test Auth (if token exists) const token = getAuthToken(); if (token) { try { const me = await apiRequest(API_ENDPOINTS.auth.me); results.endpoints.auth = { status: 'ok', data: me }; console.log('✓ Auth:', me); } catch (error) { results.endpoints.auth = { status: 'error', error: error.message }; console.error('✗ Auth:', error.message); } } // Test Jobs try { const jobs = await apiRequest(API_ENDPOINTS.jobs); results.endpoints.jobs = { status: 'ok', count: (jobs.jobs || []).length }; console.log('✓ Jobs:', jobs.jobs?.length || 0); } catch (error) { results.endpoints.jobs = { status: 'error', error: error.message }; console.error('✗ Jobs:', error.message); } console.log('📋 Connection Test Results:', results); return results; } // ═══════════════════════════════════════════════════════════════ // EXPORT FOR USE IN HTML // ═══════════════════════════════════════════════════════════════ window.MahrakAPI = { runSmartScanWithAPI, submitAuditForm, loadDashboardData, getCompetitorIntelligence, testAllConnections, apiRequest, getAuthToken, setAuthToken, API_ENDPOINTS }; console.log('✓ Mahrak API Integration Loaded');