// Global variables let integrityCheckInterval = null; let directivesLoading = false; // Integrity Check Functions function startIntegrityCheck() { console.log("Start button clicked"); // Debug log const startBtn = document.getElementById('startCheckBtn'); const progressBar = document.getElementById('integrityProgressBar'); const progressText = document.getElementById('integrityProgressText'); startBtn.disabled = true; progressText.textContent = 'Starting check...'; progressBar.style.width = '0%'; // Show a notification for debugging showNotification('Attempting to start integrity check...', 'info'); fetch('integrity_check_handler.php', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: 'action=start_integrity_check' }) .then(response => { console.log("Response status:", response.status); // Debug log if (!response.ok) { throw new Error(`Server error: ${response.status}`); } return response.text().then(text => { console.log("Raw response:", text); // Log the raw response try { return JSON.parse(text); } catch (e) { console.error("JSON parse error:", e); throw new Error("Invalid JSON response: " + text.substring(0, 100)); } }); }) .then(data => { console.log("Parsed data:", data); // Debug log if (data.success) { updateIntegrityCheckUI(true); progressBar.classList.add('processing'); startProgressMonitoring(); showNotification('Integrity check started successfully', 'success'); } else { throw new Error(data.message || 'Failed to start check'); } }) .catch(error => { console.error('Error starting check:', error); progressText.textContent = 'Error: ' + error.message; startBtn.disabled = false; progressBar.classList.remove('processing'); showNotification('Error: ' + error.message, 'error'); }); } function startProgressMonitoring() { const progressBar = document.getElementById('integrityProgressBar'); const progressText = document.getElementById('integrityProgressText'); let failedAttempts = 0; const maxFailedAttempts = 10; const retryDelay = 3000; // 3 seconds between retries - increased to reduce server load let isPhase1 = true; // Track which phase we're in if (integrityCheckInterval) { clearInterval(integrityCheckInterval); } // Add processing animation class progressBar.classList.add('processing'); const checkProgress = async () => { try { const response = await fetch('integrity_check_handler.php', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Cache-Control': 'no-cache' }, body: 'action=get_check_status' }); if (response.status === 503) { failedAttempts += 0.5; // Count 503s as half failures console.log('Server busy, attempt: ' + failedAttempts); return; // Continue monitoring without throwing error } if (!response.ok) { throw new Error(`Server returned ${response.status}: ${response.statusText}`); } // Get raw text and try to parse const text = await response.text(); let data; try { data = JSON.parse(text); } catch (e) { console.error("Failed to parse JSON response:", text); failedAttempts++; return; // Skip this update } if (data.success && data.status) { // Reset failed attempts on success failedAttempts = 0; const status = data.status; // Check for phase transition if (data.phase_change || (status.status && (status.status.includes('Phase 2') || status.status.includes('Starting phase 2')))) { if (isPhase1) { isPhase1 = false; // Phase transition notification showNotification('Phase 1 complete. Starting Phase 2: Grouping anomalies into directives.', 'info'); // Reset progress bar for phase 2 progressBar.style.width = '0%'; progressText.textContent = 'Starting Phase 2: Grouping anomalies...'; } } const progress = (parseInt(status.processed_count) / parseInt(status.total_count) * 100) || 0; // Update progress bar and text progressBar.style.width = `${progress}%`; progressText.textContent = status.status || `Processing: ${progress.toFixed(1)}%`; // Check if the process is complete or stopped if (!status.is_running || progress >= 100) { clearInterval(integrityCheckInterval); progressBar.classList.remove('processing'); if (progress >= 100) { progressText.textContent = 'Check completed'; showNotification('Integrity check completed successfully.', 'success'); // Process Phase 2 before switching tabs setTimeout(() => { checkPhase2Status(); }, 1500); } } // Update UI based on status if (status.is_paused == '1') { progressBar.classList.add('paused'); progressText.classList.add('paused'); updateIntegrityCheckUI(true, true); } else if (status.status && status.status.includes('cooling')) { progressBar.classList.add('cooling'); progressText.classList.add('cooling'); progressBar.classList.remove('paused'); progressText.classList.remove('paused'); } else { progressBar.classList.remove('cooling'); progressText.classList.remove('cooling'); progressBar.classList.remove('paused'); progressText.classList.remove('paused'); } } } catch (error) { console.error('Progress monitoring error:', error); failedAttempts++; if (failedAttempts >= maxFailedAttempts) { // Reset counter and show warning but keep monitoring failedAttempts = maxFailedAttempts / 2; showNotification('Server is busy. Progress will continue in background.', 'warning'); } } }; // Initial check checkProgress(); // Set up interval - check every 3 seconds integrityCheckInterval = setInterval(checkProgress, retryDelay); } function checkPhase2Status() { console.log("Checking Phase 2 status"); // Add a timeout counter if (!window.phase2TimeoutCounter) { window.phase2TimeoutCounter = 0; } window.phase2TimeoutCounter++; // If we've been checking for too long (20 attempts), force completion if (window.phase2TimeoutCounter > 20) { console.log("Phase 2 taking too long, forcing completion"); window.phase2TimeoutCounter = 0; switchTab('directives'); return; } fetch('integrity_check_handler.php', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: 'action=process_anomaly_batch' }) .then(response => { if (!response.ok) { console.log("Phase 2 status check response not OK:", response.status); throw new Error(`Server returned ${response.status}`); } return response.text().then(text => { console.log("Raw response:", text); // Log the raw response try { return JSON.parse(text); } catch (e) { console.error("JSON parse error:", e); throw new Error("Invalid JSON response: " + text.substring(0, 100)); } }); }) .then(data => { console.log("Phase 2 processing response:", data); if (data.success) { if (data.status === 'completed') { // All anomalies processed, load directives console.log("Phase 2 complete, switching to directives tab"); window.phase2TimeoutCounter = 0; // Reset counter switchTab('directives'); } else if (data.status === 'processing') { // Update progress bar const progressBar = document.getElementById('integrityProgressBar'); const progressText = document.getElementById('integrityProgressText'); if (progressBar && data.progress !== undefined) { progressBar.style.width = `${data.progress}%`; progressText.textContent = `Processing: ${data.processed} of ${data.total} anomalies (${data.progress}%)`; } // Continue processing setTimeout(checkPhase2Status, 2000); } else { // Unknown status but successful, assume complete console.log("Phase 2 unknown status, assuming complete"); window.phase2TimeoutCounter = 0; // Reset counter switchTab('directives'); } } else { showNotification('Error processing anomalies: ' + (data.message || 'Unknown error'), 'error'); // Still continue after a delay setTimeout(checkPhase2Status, 5000); } }) .catch(error => { console.error("Error checking Phase 2 status:", error); // Retry after a delay setTimeout(checkPhase2Status, 5000); }); } function stopIntegrityCheck() { console.log("Stop button clicked"); if (!confirm('Are you sure you want to stop the integrity check? This will terminate the process.')) { return; } showNotification('Attempting to stop check...', 'info'); fetch('integrity_check_handler.php', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: 'action=stop' }) .then(response => { console.log("Stop response status:", response.status); if (!response.ok) { throw new Error(`Server error: ${response.status}`); } return response.text().then(text => { console.log("Raw response:", text); // Log the raw response try { return JSON.parse(text); } catch (e) { console.error("JSON parse error:", e); throw new Error("Invalid JSON response: " + text.substring(0, 100)); } }); }) .then(data => { console.log("Stop response data:", data); if (data.success) { if (integrityCheckInterval) { clearInterval(integrityCheckInterval); } const progressBar = document.getElementById('integrityProgressBar'); progressBar.classList.remove('processing'); progressBar.classList.remove('cooling'); updateIntegrityCheckUI(false); showNotification('Integrity check stopped.', 'info'); } else { throw new Error(data.message || 'Failed to stop integrity check'); } }) .catch(error => { console.error('Error stopping check:', error); showNotification('Failed to stop check: ' + error.message, 'error'); }); } // Pause and Resume Functions function pauseIntegrityCheck() { console.log("Pause button clicked"); const pauseBtn = document.getElementById('pauseCheckBtn'); // If already paused, this is a resume action if (pauseBtn.textContent.trim() === 'Resume') { resumeIntegrityCheck(); return; } showNotification('Attempting to pause check...', 'info'); fetch('integrity_check_handler.php', { method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded'}, body: 'action=pause' }) .then(response => { console.log("Pause response status:", response.status); if (!response.ok) { throw new Error(`Server error: ${response.status}`); } return response.text().then(text => { console.log("Raw response:", text); // Log the raw response try { return JSON.parse(text); } catch (e) { console.error("JSON parse error:", e); throw new Error("Invalid JSON response: " + text.substring(0, 100)); } }); }) .then(data => { console.log("Pause response data:", data); if (data.success) { const progressBar = document.getElementById('integrityProgressBar'); const progressText = document.getElementById('integrityProgressText'); progressBar.classList.add('paused'); progressText.classList.add('paused'); progressText.textContent = 'Check paused'; pauseBtn.textContent = 'Resume'; showNotification('Integrity check paused.', 'info'); } else { throw new Error(data.message || 'Failed to pause integrity check'); } }) .catch(error => { console.error('Error pausing check:', error); showNotification('Failed to pause check: ' + error.message, 'error'); }); } function resumeIntegrityCheck() { console.log("Resume button clicked"); const pauseBtn = document.getElementById('pauseCheckBtn'); showNotification('Attempting to resume check...', 'info'); fetch('integrity_check_handler.php', { method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded'}, body: 'action=resume' }) .then(response => { console.log("Resume response status:", response.status); if (!response.ok) { throw new Error(`Server error: ${response.status}`); } return response.text().then(text => { console.log("Raw response:", text); // Log the raw response try { return JSON.parse(text); } catch (e) { console.error("JSON parse error:", e); throw new Error("Invalid JSON response: " + text.substring(0, 100)); } }); }) .then(data => { console.log("Resume response data:", data); if (data.success) { const progressBar = document.getElementById('integrityProgressBar'); const progressText = document.getElementById('integrityProgressText'); progressBar.classList.remove('paused'); progressText.classList.remove('paused'); progressText.textContent = 'Check resumed'; pauseBtn.textContent = 'Pause'; showNotification('Integrity check resumed.', 'info'); } else { throw new Error(data.message || 'Failed to resume integrity check'); } }) .catch(error => { console.error('Error resuming check:', error); showNotification('Failed to resume check: ' + error.message, 'error'); }); } function updateIntegrityCheckUI(running, paused = false) { const startBtn = document.getElementById('startCheckBtn'); const pauseBtn = document.getElementById('pauseCheckBtn'); const stopBtn = document.getElementById('stopCheckBtn'); const progressText = document.getElementById('integrityProgressText'); const progressBar = document.getElementById('integrityProgressBar'); if (running) { startBtn.style.display = 'none'; pauseBtn.style.display = 'inline-block'; stopBtn.style.display = 'inline-block'; if (paused) { pauseBtn.textContent = 'Resume'; progressText.textContent = 'Check paused'; progressBar.classList.add('paused'); progressText.classList.add('paused'); } else { pauseBtn.textContent = 'Pause'; progressBar.classList.remove('paused'); progressText.classList.remove('paused'); if (!progressText.textContent.includes('%')) { progressText.textContent = 'Processing...'; } } } else { startBtn.style.display = 'inline-block'; pauseBtn.style.display = 'none'; stopBtn.style.display = 'none'; progressText.textContent = 'Ready to start integrity check'; } } // Directives Functions function loadDirectives() { // Prevent multiple simultaneous requests if (directivesLoading) { console.log("Already loading directives, request ignored"); return; } directivesLoading = true; console.log("Loading directives"); const tableBody = document.getElementById('directivesTableBody'); if (!tableBody) { console.error('Directives table body not found'); directivesLoading = false; return; } tableBody.innerHTML = '
${data.description}
This action will delete ${data.affected_count} panel members with this anomaly.