mirror of
https://github.com/itdoginfo/podkop.git
synced 2025-12-08 12:36:50 +03:00
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:
@@ -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,13 +1032,15 @@ 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;
|
||||||
});
|
});
|
||||||
|
|
||||||
return map_promise;
|
return map_promise;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user