From c3f44bd12472b56b539c8f0b59bf11bcede95f98 Mon Sep 17 00:00:00 2001 From: Ivan K Date: Wed, 21 May 2025 14:18:23 +0300 Subject: [PATCH 1/3] =?UTF-8?q?=E2=9C=A8=20fix(diagnosticTab):=20add=20err?= =?UTF-8?q?or=20polling=20and=20notification=20system?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/view/podkop/diagnosticTab.js | 86 +++++++++++++++++-- 1 file changed, 81 insertions(+), 5 deletions(-) diff --git a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/diagnosticTab.js b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/diagnosticTab.js index 524eea0..b649878 100644 --- a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/diagnosticTab.js +++ b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/diagnosticTab.js @@ -326,14 +326,23 @@ async function checkBypass() { async function getPodkopErrors() { return new Promise(resolve => { safeExec('/usr/bin/podkop', ['check_logs'], 0, result => { - if (!result || !result.stdout) return resolve([]); + if (!result || !result.stdout) { + console.error('No logs received from check_logs command'); + return resolve([]); + } const logs = result.stdout.split('\n'); - const errors = logs.filter(log => - log.includes('[critical]') - ); + console.log('Got logs from check_logs command, total lines:', logs.length); - console.log('Found errors:', errors); + const errors = logs.filter(log => { + const hasCritical = log.includes('[critical]'); + if (hasCritical) { + console.log('Found critical log:', log); + } + return hasCritical; + }); + + console.log('Found errors:', errors.length, errors); resolve(errors); }); }); @@ -711,6 +720,73 @@ function stopDiagnosticsUpdates() { } } +// Error polling functions +function startErrorPolling() { + console.log('Starting error polling'); + if (errorPollTimer) { + console.log('Clearing existing error poll timer'); + clearInterval(errorPollTimer); + } + + // Reset initial check flag to make sure we show errors + isInitialCheck = false; + + // Immediately check for errors on start + console.log('Running immediate check for errors'); + checkForCriticalErrors(); + + // Then set up periodic checks + console.log('Setting up periodic error checks with interval:', constants.ERROR_POLL_INTERVAL); + errorPollTimer = setInterval(checkForCriticalErrors, constants.ERROR_POLL_INTERVAL); +} + +function stopErrorPolling() { + if (errorPollTimer) { + clearInterval(errorPollTimer); + errorPollTimer = null; + } +} + +async function checkForCriticalErrors() { + try { + console.log('Checking for critical errors, isInitialCheck =', isInitialCheck); + const errors = await getPodkopErrors(); + console.log('Got errors from getPodkopErrors:', errors.length); + + if (errors && errors.length > 0) { + // Filter out errors we've already seen + const newErrors = errors.filter(error => !lastErrorsSet.has(error)); + console.log('New errors not seen before:', newErrors.length); + + if (newErrors.length > 0) { + // On initial check, just store errors without showing notifications + if (!isInitialCheck) { + console.log('Showing notifications for errors:', newErrors.length); + // Show each new error as a notification + newErrors.forEach(error => { + console.log('Showing notification for error:', error); + showErrorNotification(error, newErrors.length > 1); + }); + } else { + console.log('Initial check, not showing notifications'); + } + + // Add new errors to our set of seen errors + newErrors.forEach(error => lastErrorsSet.add(error)); + console.log('Updated lastErrorsSet, size =', lastErrorsSet.size); + } + } + + // After first check, mark as no longer initial + if (isInitialCheck) { + console.log('Setting isInitialCheck to false'); + isInitialCheck = false; + } + } catch (error) { + console.error('Error checking for critical messages:', error); + } +} + // Update individual text element with new content function updateTextElement(elementId, content) { const element = document.getElementById(elementId); From 8573bd99b53126599e082c4b3ea2b6a8844a70ca Mon Sep 17 00:00:00 2001 From: Ivan K Date: Wed, 21 May 2025 14:21:37 +0300 Subject: [PATCH 2/3] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(diagnosticTab?= =?UTF-8?q?):=20remove=20unused=20debug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/view/podkop/diagnosticTab.js | 35 +++---------------- 1 file changed, 5 insertions(+), 30 deletions(-) diff --git a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/diagnosticTab.js b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/diagnosticTab.js index b649878..06fd05c 100644 --- a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/diagnosticTab.js +++ b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/diagnosticTab.js @@ -326,23 +326,13 @@ async function checkBypass() { async function getPodkopErrors() { return new Promise(resolve => { safeExec('/usr/bin/podkop', ['check_logs'], 0, result => { - if (!result || !result.stdout) { - console.error('No logs received from check_logs command'); - return resolve([]); - } + if (!result || !result.stdout) return resolve([]); const logs = result.stdout.split('\n'); - console.log('Got logs from check_logs command, total lines:', logs.length); + const errors = logs.filter(log => + log.includes('[critical]') + ); - const errors = logs.filter(log => { - const hasCritical = log.includes('[critical]'); - if (hasCritical) { - console.log('Found critical log:', log); - } - return hasCritical; - }); - - console.log('Found errors:', errors.length, errors); resolve(errors); }); }); @@ -722,9 +712,7 @@ function stopDiagnosticsUpdates() { // Error polling functions function startErrorPolling() { - console.log('Starting error polling'); if (errorPollTimer) { - console.log('Clearing existing error poll timer'); clearInterval(errorPollTimer); } @@ -732,11 +720,9 @@ function startErrorPolling() { isInitialCheck = false; // Immediately check for errors on start - console.log('Running immediate check for errors'); checkForCriticalErrors(); // Then set up periodic checks - console.log('Setting up periodic error checks with interval:', constants.ERROR_POLL_INTERVAL); errorPollTimer = setInterval(checkForCriticalErrors, constants.ERROR_POLL_INTERVAL); } @@ -749,39 +735,28 @@ function stopErrorPolling() { async function checkForCriticalErrors() { try { - console.log('Checking for critical errors, isInitialCheck =', isInitialCheck); const errors = await getPodkopErrors(); - console.log('Got errors from getPodkopErrors:', errors.length); if (errors && errors.length > 0) { // Filter out errors we've already seen const newErrors = errors.filter(error => !lastErrorsSet.has(error)); - console.log('New errors not seen before:', newErrors.length); if (newErrors.length > 0) { // On initial check, just store errors without showing notifications if (!isInitialCheck) { - console.log('Showing notifications for errors:', newErrors.length); // Show each new error as a notification newErrors.forEach(error => { - console.log('Showing notification for error:', error); showErrorNotification(error, newErrors.length > 1); }); - } else { - console.log('Initial check, not showing notifications'); } // Add new errors to our set of seen errors newErrors.forEach(error => lastErrorsSet.add(error)); - console.log('Updated lastErrorsSet, size =', lastErrorsSet.size); } } // After first check, mark as no longer initial - if (isInitialCheck) { - console.log('Setting isInitialCheck to false'); - isInitialCheck = false; - } + isInitialCheck = false; } catch (error) { console.error('Error checking for critical messages:', error); } From f1954df83b4fd6a4bfbb6e3339eeae931f8d1fa7 Mon Sep 17 00:00:00 2001 From: Ivan K Date: Wed, 21 May 2025 15:09:42 +0300 Subject: [PATCH 3/3] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(diagnosticTab?= =?UTF-8?q?):=20move=20command=20execution=20helpers=20to=20utils.js?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/view/podkop/diagnosticTab.js | 299 ++++-------------- .../resources/view/podkop/podkop.js | 32 +- .../resources/view/podkop/utils.js | 146 +++++++++ 3 files changed, 245 insertions(+), 232 deletions(-) create mode 100644 luci-app-podkop/htdocs/luci-static/resources/view/podkop/utils.js diff --git a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/diagnosticTab.js b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/diagnosticTab.js index 06fd05c..e2887c7 100644 --- a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/diagnosticTab.js +++ b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/diagnosticTab.js @@ -5,6 +5,7 @@ 'require uci'; 'require fs'; 'require view.podkop.constants as constants'; +'require view.podkop.utils as utils'; // Cache system for network requests const fetchCache = {}; @@ -36,52 +37,12 @@ async function cachedFetch(url, options = {}) { } } -// Helper functions for command execution with prioritization +// Helper functions for command execution with prioritization - Using from utils.js now function safeExec(command, args, priority, callback, timeout = constants.COMMAND_TIMEOUT) { - priority = (typeof priority === 'number') ? priority : 0; - - const executeCommand = async () => { - try { - const controller = new AbortController(); - const timeoutId = setTimeout(() => controller.abort(), timeout); - - const result = await Promise.race([ - fs.exec(command, args), - new Promise((_, reject) => { - controller.signal.addEventListener('abort', () => { - reject(new Error('Command execution timed out')); - }); - }) - ]); - - clearTimeout(timeoutId); - - if (callback && typeof callback === 'function') { - callback(result); - } - - return result; - } catch (error) { - console.warn(`Command execution failed or timed out: ${command} ${args.join(' ')}`); - const errorResult = { stdout: '', stderr: error.message, error: error }; - - if (callback && typeof callback === 'function') { - callback(errorResult); - } - - return errorResult; - } - }; - - if (callback && typeof callback === 'function') { - setTimeout(executeCommand, constants.RUN_PRIORITY[priority]); - return; - } - else { - return executeCommand(); - } + return utils.safeExec(command, args, priority, callback, timeout); } +// Helper functions for handling checks function runCheck(checkFunction, priority, callback) { priority = (typeof priority === 'number') ? priority : 0; @@ -255,95 +216,36 @@ function checkDNSAvailability() { } async function checkBypass() { - return new Promise(async (resolve) => { + try { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), constants.FETCH_TIMEOUT); + try { - let configMode = 'proxy'; // Default fallback - try { - const data = await uci.load('podkop'); - configMode = uci.get('podkop', 'main', 'mode') || 'proxy'; - } catch (e) { - console.error('Error getting mode from UCI:', e); + const response1 = await cachedFetch(`https://${constants.FAKEIP_CHECK_DOMAIN}/check`, { signal: controller.signal }); + const data1 = await response1.json(); + + const response2 = await cachedFetch(`https://${constants.IP_CHECK_DOMAIN}/check`, { signal: controller.signal }); + const data2 = await response2.json(); + + clearTimeout(timeoutId); + + if (data1.IP && data2.IP) { + if (data1.IP !== data2.IP) { + return createStatus('working', 'working', 'SUCCESS'); + } else { + return createStatus('not_working', 'same IP for both domains', 'ERROR'); + } + } else { + return createStatus('error', 'check error (no IP)', 'WARNING'); } - - safeExec('/usr/bin/podkop', ['get_sing_box_status'], 0, singboxStatusResult => { - const singboxStatus = JSON.parse(singboxStatusResult.stdout || '{"running":0,"dns_configured":0}'); - - if (!singboxStatus.running) { - return resolve(createStatus('not_working', `${configMode} not running`, 'ERROR')); - } - - // Fetch IP from first endpoint - let ip1 = null; - try { - const controller1 = new AbortController(); - const timeoutId1 = setTimeout(() => controller1.abort(), constants.FETCH_TIMEOUT); - - cachedFetch(`https://${constants.FAKEIP_CHECK_DOMAIN}/check`, { signal: controller1.signal }) - .then(response1 => response1.json()) - .then(data1 => { - clearTimeout(timeoutId1); - ip1 = data1.IP; - - // Fetch IP from second endpoint - const controller2 = new AbortController(); - const timeoutId2 = setTimeout(() => controller2.abort(), constants.FETCH_TIMEOUT); - - cachedFetch(`https://${constants.IP_CHECK_DOMAIN}/check`, { signal: controller2.signal }) - .then(response2 => response2.json()) - .then(data2 => { - clearTimeout(timeoutId2); - const ip2 = data2.IP; - - // Compare IPs - if (ip1 && ip2) { - if (ip1 !== ip2) { - return resolve(createStatus('working', `${configMode} working correctly`, 'SUCCESS')); - } else { - return resolve(createStatus('not_working', `${configMode} routing incorrect`, 'ERROR')); - } - } else { - return resolve(createStatus('error', 'IP comparison failed', 'WARNING')); - } - }) - .catch(error => { - return resolve(createStatus('not_working', `${configMode} not working`, 'ERROR')); - }); - }) - .catch(error => { - return resolve(createStatus('error', 'First endpoint check failed', 'WARNING')); - }); - } catch (error) { - return resolve(createStatus('error', 'Bypass check error', 'WARNING')); - } - }); - } catch (error) { - return resolve(createStatus('error', 'Bypass check error', 'WARNING')); + } catch (fetchError) { + clearTimeout(timeoutId); + const message = fetchError.name === 'AbortError' ? 'timeout' : 'check error'; + return createStatus('error', message, 'WARNING'); } - }); -} - -// Error Handling -async function getPodkopErrors() { - return new Promise(resolve => { - safeExec('/usr/bin/podkop', ['check_logs'], 0, result => { - if (!result || !result.stdout) return resolve([]); - - const logs = result.stdout.split('\n'); - const errors = logs.filter(log => - log.includes('[critical]') - ); - - resolve(errors); - }); - }); -} - -function showErrorNotification(error, isMultiple = false) { - const notificationContent = E('div', { 'class': 'alert-message error' }, [ - E('pre', { 'class': 'error-log' }, error) - ]); - - ui.addNotification(null, notificationContent); + } catch (error) { + return createStatus('error', 'check error', 'WARNING'); + } } // Modal Functions @@ -523,6 +425,7 @@ const ButtonFactory = { return this.createButton({ label: config.label, onClick: () => showConfigModal(config.command, config.title), + additionalClass: `cbi-button-${config.type || ''}`, style: config.style }); } @@ -682,18 +585,20 @@ let createStatusSection = async function () { ]); }; -// Diagnostics Update Functions +// Global variables for tracking state let diagnosticsUpdateTimer = null; -let errorPollTimer = null; -let lastErrorsSet = new Set(); let isInitialCheck = true; +showConfigModal.busy = false; function startDiagnosticsUpdates() { if (diagnosticsUpdateTimer) { clearInterval(diagnosticsUpdateTimer); } + // Immediately update when started updateDiagnostics(); + + // Then set up periodic updates diagnosticsUpdateTimer = setInterval(updateDiagnostics, constants.DIAGNOSTICS_UPDATE_INTERVAL); } @@ -702,64 +607,6 @@ function stopDiagnosticsUpdates() { clearInterval(diagnosticsUpdateTimer); diagnosticsUpdateTimer = null; } - - // Reset the loading state when stopping updates - const container = document.getElementById('diagnostics-status'); - if (container) { - container.removeAttribute('data-loading'); - } -} - -// Error polling functions -function startErrorPolling() { - if (errorPollTimer) { - clearInterval(errorPollTimer); - } - - // Reset initial check flag to make sure we show errors - isInitialCheck = false; - - // Immediately check for errors on start - checkForCriticalErrors(); - - // Then set up periodic checks - errorPollTimer = setInterval(checkForCriticalErrors, constants.ERROR_POLL_INTERVAL); -} - -function stopErrorPolling() { - if (errorPollTimer) { - clearInterval(errorPollTimer); - errorPollTimer = null; - } -} - -async function checkForCriticalErrors() { - try { - const errors = await getPodkopErrors(); - - if (errors && errors.length > 0) { - // Filter out errors we've already seen - const newErrors = errors.filter(error => !lastErrorsSet.has(error)); - - if (newErrors.length > 0) { - // On initial check, just store errors without showing notifications - if (!isInitialCheck) { - // Show each new error as a notification - newErrors.forEach(error => { - showErrorNotification(error, newErrors.length > 1); - }); - } - - // Add new errors to our set of seen errors - newErrors.forEach(error => lastErrorsSet.add(error)); - } - } - - // After first check, mark as no longer initial - isInitialCheck = false; - } catch (error) { - console.error('Error checking for critical messages:', error); - } } // Update individual text element with new content @@ -968,29 +815,44 @@ function setupDiagnosticsEventHandlers(node) { const titleDiv = E('h2', { 'class': 'cbi-map-title' }, _('Podkop')); node.insertBefore(titleDiv, node.firstChild); + // Function to initialize diagnostics + function initDiagnostics(container) { + if (container && container.hasAttribute('data-loading')) { + container.innerHTML = ''; + showConfigModal.busy = false; + createStatusSection().then(section => { + container.appendChild(section); + startDiagnosticsUpdates(); + // Start error polling when diagnostics tab is active + utils.startErrorPolling(); + }); + } + } + document.addEventListener('visibilitychange', function () { const diagnosticsContainer = document.getElementById('diagnostics-status'); - if (document.hidden) { + const diagnosticsTab = document.querySelector('.cbi-tab[data-tab="diagnostics"]'); + + if (document.hidden || !diagnosticsTab || !diagnosticsTab.classList.contains('cbi-tab-active')) { stopDiagnosticsUpdates(); - stopErrorPolling(); + // Don't stop error polling here - it's managed in podkop.js for all tabs } else if (diagnosticsContainer && diagnosticsContainer.hasAttribute('data-loading')) { startDiagnosticsUpdates(); - startErrorPolling(); + // Ensure error polling is running when diagnostics tab is active + utils.startErrorPolling(); } }); setTimeout(() => { const diagnosticsContainer = document.getElementById('diagnostics-status'); - if (diagnosticsContainer) { - if (diagnosticsContainer.hasAttribute('data-loading')) { - diagnosticsContainer.innerHTML = ''; - showConfigModal.busy = false; - createStatusSection().then(section => { - diagnosticsContainer.appendChild(section); - startDiagnosticsUpdates(); - startErrorPolling(); - }); - } + const diagnosticsTab = document.querySelector('.cbi-tab[data-tab="diagnostics"]'); + const otherTabs = document.querySelectorAll('.cbi-tab:not([data-tab="diagnostics"])'); + + // Check for direct page load case + const noActiveTabsExist = !Array.from(otherTabs).some(tab => tab.classList.contains('cbi-tab-active')); + + if (diagnosticsContainer && diagnosticsTab && (diagnosticsTab.classList.contains('cbi-tab-active') || noActiveTabsExist)) { + initDiagnostics(diagnosticsContainer); } const tabs = node.querySelectorAll('.cbi-tabmenu'); @@ -1001,39 +863,14 @@ function setupDiagnosticsEventHandlers(node) { const tabName = tab.getAttribute('data-tab'); if (tabName === 'diagnostics') { const container = document.getElementById('diagnostics-status'); - if (container && !container.hasAttribute('data-loading')) { - container.setAttribute('data-loading', 'true'); - - // Render UI structure immediately - container.innerHTML = ''; - createStatusSection().then(section => { - container.appendChild(section); - startDiagnosticsUpdates(); - startErrorPolling(); - }); - } + container.setAttribute('data-loading', 'true'); + initDiagnostics(container); } else { stopDiagnosticsUpdates(); - stopErrorPolling(); + // Don't stop error polling - it should continue on all tabs } } }); - - const activeTab = tabs[0].querySelector('.cbi-tab[data-tab="diagnostics"]'); - if (activeTab) { - const container = document.getElementById('diagnostics-status'); - if (container && !container.hasAttribute('data-loading')) { - container.setAttribute('data-loading', 'true'); - - // Render UI structure immediately - container.innerHTML = ''; - createStatusSection().then(section => { - container.appendChild(section); - startDiagnosticsUpdates(); - startErrorPolling(); - }); - } - } } }, constants.DIAGNOSTICS_INITIAL_DELAY); diff --git a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js index 4373b31..a296f32 100644 --- a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js +++ b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js @@ -5,6 +5,7 @@ 'require view.podkop.configSection as configSection'; 'require view.podkop.diagnosticTab as diagnosticTab'; 'require view.podkop.additionalTab as additionalTab'; +'require view.podkop.utils as utils'; return view.extend({ async render() { @@ -44,7 +45,36 @@ return view.extend({ // Diagnostics Tab (main section) diagnosticTab.createDiagnosticsSection(mainSection); - const map_promise = m.render().then(node => diagnosticTab.setupDiagnosticsEventHandlers(node)); + const map_promise = m.render().then(node => { + // Set up diagnostics event handlers + diagnosticTab.setupDiagnosticsEventHandlers(node); + + // Start critical error polling for all tabs + utils.startErrorPolling(); + + // Add event listener to keep error polling active when switching tabs + const tabs = node.querySelectorAll('.cbi-tabmenu'); + if (tabs.length > 0) { + tabs[0].addEventListener('click', function (e) { + const tab = e.target.closest('.cbi-tab'); + if (tab) { + // Ensure error polling continues when switching tabs + utils.startErrorPolling(); + } + }); + } + + // Add visibility change handler to manage error polling + document.addEventListener('visibilitychange', function () { + if (document.hidden) { + utils.stopErrorPolling(); + } else { + utils.startErrorPolling(); + } + }); + + return node; + }); // Extra Section const extraSection = m.section(form.TypedSection, 'extra', _('Extra configurations')); diff --git a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/utils.js b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/utils.js new file mode 100644 index 0000000..2b066a9 --- /dev/null +++ b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/utils.js @@ -0,0 +1,146 @@ +'use strict'; +'require baseclass'; +'require ui'; +'require fs'; +'require view.podkop.constants as constants'; + +// Flag to track if this is the first error check +let isInitialCheck = true; + +// Set to track which errors we've already seen +const lastErrorsSet = new Set(); + +// Timer for periodic error polling +let errorPollTimer = null; + +// Helper function to fetch errors from the podkop command +async function getPodkopErrors() { + return new Promise(resolve => { + safeExec('/usr/bin/podkop', ['check_logs'], 0, result => { + if (!result || !result.stdout) return resolve([]); + + const logs = result.stdout.split('\n'); + const errors = logs.filter(log => + log.includes('[critical]') + ); + + resolve(errors); + }); + }); +} + +// Show error notification to the user +function showErrorNotification(error, isMultiple = false) { + const notificationContent = E('div', { 'class': 'alert-message error' }, [ + E('pre', { 'class': 'error-log' }, error) + ]); + + ui.addNotification(null, notificationContent); +} + +// Helper function for command execution with prioritization +function safeExec(command, args, priority, callback, timeout = constants.COMMAND_TIMEOUT) { + priority = (typeof priority === 'number') ? priority : 0; + + const executeCommand = async () => { + try { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), timeout); + + const result = await Promise.race([ + fs.exec(command, args), + new Promise((_, reject) => { + controller.signal.addEventListener('abort', () => { + reject(new Error('Command execution timed out')); + }); + }) + ]); + + clearTimeout(timeoutId); + + if (callback && typeof callback === 'function') { + callback(result); + } + + return result; + } catch (error) { + console.warn(`Command execution failed or timed out: ${command} ${args.join(' ')}`); + const errorResult = { stdout: '', stderr: error.message, error: error }; + + if (callback && typeof callback === 'function') { + callback(errorResult); + } + + return errorResult; + } + }; + + if (callback && typeof callback === 'function') { + setTimeout(executeCommand, constants.RUN_PRIORITY[priority]); + return; + } + else { + return executeCommand(); + } +} + +// Check for critical errors and show notifications +async function checkForCriticalErrors() { + try { + const errors = await getPodkopErrors(); + + if (errors && errors.length > 0) { + // Filter out errors we've already seen + const newErrors = errors.filter(error => !lastErrorsSet.has(error)); + + if (newErrors.length > 0) { + // On initial check, just store errors without showing notifications + if (!isInitialCheck) { + // Show each new error as a notification + newErrors.forEach(error => { + showErrorNotification(error, newErrors.length > 1); + }); + } + + // Add new errors to our set of seen errors + newErrors.forEach(error => lastErrorsSet.add(error)); + } + } + + // After first check, mark as no longer initial + isInitialCheck = false; + } catch (error) { + console.error('Error checking for critical messages:', error); + } +} + +// Start polling for errors at regular intervals +function startErrorPolling() { + if (errorPollTimer) { + clearInterval(errorPollTimer); + } + + // Reset initial check flag to make sure we show errors + isInitialCheck = false; + + // Immediately check for errors on start + checkForCriticalErrors(); + + // Then set up periodic checks + errorPollTimer = setInterval(checkForCriticalErrors, constants.ERROR_POLL_INTERVAL); +} + +// Stop polling for errors +function stopErrorPolling() { + if (errorPollTimer) { + clearInterval(errorPollTimer); + errorPollTimer = null; + } +} + +return baseclass.extend({ + startErrorPolling, + stopErrorPolling, + checkForCriticalErrors, + safeExec +}); \ No newline at end of file