refactor: improve error handling and code readability in podkop.js and update init.d script to check sing-box status

This commit is contained in:
Ivan K
2025-02-23 22:56:01 +03:00
parent b806586a5a
commit 171381fa18
2 changed files with 84 additions and 99 deletions

View File

@@ -20,7 +20,7 @@ async function safeExec(command, args = [], timeout = 3000) {
fs.exec(command, args), fs.exec(command, args),
new Promise((_, reject) => { new Promise((_, reject) => {
controller.signal.addEventListener('abort', () => { controller.signal.addEventListener('abort', () => {
reject(new Error(`Command execution timed out after ${timeout}ms`)); reject(new Error('Command execution timed out'));
}); });
}) })
]); ]);
@@ -28,13 +28,8 @@ async function safeExec(command, args = [], timeout = 3000) {
clearTimeout(timeoutId); clearTimeout(timeoutId);
return result; return result;
} catch (error) { } catch (error) {
console.error(`Command execution error: ${error.message}`); console.warn(`Command execution failed or timed out: ${command} ${args.join(' ')}`);
console.error(`Command: ${command} ${args.join(' ')}`); return { stdout: '', stderr: error.message };
return {
stdout: '',
stderr: `Error executing command: ${error.message}`,
error: error
};
} }
} }
@@ -94,10 +89,13 @@ function createConfigSection(section, map, network) {
if (cfgvalue) { if (cfgvalue) {
try { try {
const label = cfgvalue.split('#').pop() || 'unnamed'; const label = cfgvalue.split('#').pop() || 'unnamed';
const descDiv = E('div', { 'class': 'cbi-value-description' }, _('Current config: ') + label); const decodedLabel = decodeURIComponent(label);
const descDiv = E('div', { 'class': 'cbi-value-description' }, _('Current config: ') + decodedLabel);
container.appendChild(descDiv); container.appendChild(descDiv);
} catch (e) { } catch (e) {
console.error('Error parsing config label:', e); console.error('Error parsing config label:', e);
const descDiv = E('div', { 'class': 'cbi-value-description' }, _('Current config: ') + (cfgvalue.split('#').pop() || 'unnamed'));
container.appendChild(descDiv);
} }
} else { } else {
const defaultDesc = E('div', { 'class': 'cbi-value-description' }, const defaultDesc = E('div', { 'class': 'cbi-value-description' },
@@ -718,10 +716,7 @@ return view.extend({
</style> </style>
`); `);
const m = new form.Map('podkop', _('Podkop configuration'), null, ['main', 'extra']); const m = new form.Map('podkop', _(''), null, ['main', 'extra']);
safeExec('/etc/init.d/podkop', ['show_version']).then(res => {
if (res.stdout) m.title = _('Podkop') + ' v' + res.stdout.trim();
});
// Main Section // Main Section
const mainSection = m.section(form.TypedSection, 'main'); const mainSection = m.section(form.TypedSection, 'main');
@@ -879,74 +874,47 @@ return view.extend({
o.cfgvalue = () => E('div', { id: 'diagnostics-status' }, _('Loading diagnostics...')); o.cfgvalue = () => E('div', { id: 'diagnostics-status' }, _('Loading diagnostics...'));
function checkFakeIP() { function checkFakeIP() {
const createStatus = (state, message, color) => ({
state,
message: _(message),
color: STATUS_COLORS[color]
});
return new Promise(async (resolve) => { return new Promise(async (resolve) => {
try { try {
// Check sing-box status and DNS configuration first
const singboxStatusResult = await safeExec('/etc/init.d/podkop', ['get_sing_box_status']); const singboxStatusResult = await safeExec('/etc/init.d/podkop', ['get_sing_box_status']);
const singboxStatus = JSON.parse(singboxStatusResult.stdout || '{"running":0,"dns_configured":0}'); const singboxStatus = JSON.parse(singboxStatusResult.stdout || '{"running":0,"dns_configured":0}');
if (!singboxStatus.running) { if (!singboxStatus.running) {
resolve({ return resolve(createStatus('not_working', 'sing-box not running', 'ERROR'));
state: 'not_working',
message: _('sing-box not running'),
color: STATUS_COLORS.ERROR
});
return;
} }
if (!singboxStatus.dns_configured) { if (!singboxStatus.dns_configured) {
resolve({ return resolve(createStatus('not_working', 'DNS not configured', 'ERROR'));
state: 'not_working',
message: _('DNS not configured'),
color: STATUS_COLORS.ERROR
});
return;
} }
if (singboxStatus.running && singboxStatus.dns_configured) { const controller = new AbortController();
const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 10000);
const timeoutId = setTimeout(() => controller.abort(), 10000);
try { try {
const response = await fetch('http://httpbin.org/ip', { signal: controller.signal }); const response = await fetch('http://httpbin.org/ip', { signal: controller.signal });
const text = await response.text(); const text = await response.text();
clearTimeout(timeoutId); clearTimeout(timeoutId);
if (text.includes('Cannot GET /ip')) { if (text.includes('Cannot GET /ip')) {
resolve({ return resolve(createStatus('working', 'working', 'SUCCESS'));
state: 'working',
message: _('working'),
color: STATUS_COLORS.SUCCESS
});
} else if (text.includes('"origin":')) {
resolve({
state: 'not_working',
message: _('not working'),
color: STATUS_COLORS.ERROR
});
} else {
resolve({
state: 'error',
message: _('check error'),
color: STATUS_COLORS.WARNING
});
}
} catch (fetchError) {
clearTimeout(timeoutId);
resolve({
state: 'error',
message: fetchError.name === 'AbortError' ? _('timeout') : _('check error'),
color: STATUS_COLORS.WARNING
});
} }
if (text.includes('"origin":')) {
return resolve(createStatus('not_working', 'not working', 'ERROR'));
}
return resolve(createStatus('error', 'check error', 'WARNING'));
} catch (fetchError) {
clearTimeout(timeoutId);
const message = fetchError.name === 'AbortError' ? 'timeout' : 'check error';
return resolve(createStatus('error', message, 'WARNING'));
} }
} catch (error) { } catch (error) {
console.error('Error in checkFakeIP:', error); console.error('Error in checkFakeIP:', error);
resolve({ return resolve(createStatus('error', 'check error', 'WARNING'));
state: 'error',
message: _('check error'),
color: STATUS_COLORS.WARNING
});
} }
}); });
} }
@@ -981,7 +949,6 @@ return view.extend({
container.innerHTML = ''; container.innerHTML = '';
container.appendChild(statusSection); container.appendChild(statusSection);
// Update FakeIP status
const fakeipElement = document.getElementById('fakeip-status'); const fakeipElement = document.getElementById('fakeip-status');
if (fakeipElement) { if (fakeipElement) {
fakeipElement.innerHTML = E('span', { 'style': `color: ${fakeipStatus.color}` }, [ fakeipElement.innerHTML = E('span', { 'style': `color: ${fakeipStatus.color}` }, [
@@ -1002,41 +969,57 @@ return view.extend({
} }
} }
// Start periodic updates function startPeriodicUpdates(titleDiv) {
function startPeriodicUpdates() { let updateTimer = null;
let intervalId = null; let isVisible = !document.hidden;
let isVisible = true; let versionText = _('Podkop');
let versionReceived = false;
// Initial update const updateStatus = async () => {
updateDiagnostics(); try {
if (!versionReceived) {
const version = await safeExec('/etc/init.d/podkop', ['show_version'], 2000);
if (version.stdout) {
versionText = _('Podkop') + ' v' + version.stdout.trim();
versionReceived = true;
}
}
// Handle visibility change const singboxStatusResult = await safeExec('/etc/init.d/podkop', ['get_sing_box_status']);
document.addEventListener('visibilitychange', () => { const singboxStatus = JSON.parse(singboxStatusResult.stdout || '{"running":0,"dns_configured":0}');
isVisible = document.visibilityState === 'visible'; const fakeipStatus = await checkFakeIP();
if (isVisible) {
// Tab became visible - do immediate update and restart interval titleDiv.textContent = versionText + (!singboxStatus.running || !singboxStatus.dns_configured || fakeipStatus.state === 'not_working' ? ' (not working)' : '');
updateDiagnostics();
if (intervalId === null) { await updateDiagnostics();
intervalId = setInterval(updateDiagnostics, 10000); } catch (error) {
} console.warn('Failed to update status:', error);
} else { titleDiv.textContent = versionText + ' (not working)';
// Tab hidden - clear interval
if (intervalId !== null) {
clearInterval(intervalId);
intervalId = null;
}
} }
};
const toggleUpdates = (visible) => {
if (visible) {
updateStatus();
if (!updateTimer) {
updateTimer = setInterval(updateStatus, 10000);
}
} else if (updateTimer) {
clearInterval(updateTimer);
updateTimer = null;
}
};
document.addEventListener('visibilitychange', () => {
isVisible = !document.hidden;
toggleUpdates(isVisible);
}); });
// Start interval if page is visible toggleUpdates(isVisible);
if (isVisible) {
intervalId = setInterval(updateDiagnostics, 10000);
}
// Cleanup on page unload
window.addEventListener('unload', () => { window.addEventListener('unload', () => {
if (intervalId !== null) { if (updateTimer) {
clearInterval(intervalId); clearInterval(updateTimer);
} }
}); });
} }
@@ -1049,10 +1032,12 @@ return view.extend({
extraSection.multiple = true; extraSection.multiple = true;
createConfigSection(extraSection, m, network); createConfigSection(extraSection, m, network);
const map_promise = m.render(); const map_promise = m.render().then(node => {
map_promise.then(node => { const titleDiv = E('h2', { 'class': 'cbi-map-title' }, _('Podkop'));
node.insertBefore(titleDiv, node.firstChild);
node.classList.add('fade-in'); node.classList.add('fade-in');
startPeriodicUpdates(); startPeriodicUpdates(titleDiv);
return node; return node;
}); });

View File

@@ -1828,7 +1828,7 @@ get_status() {
fi fi
# Check if service is running # Check if service is running
if pgrep -f "podkop" >/dev/null; then if pgrep -f "sing-box" >/dev/null; then
running=1 running=1
fi fi