mirror of
https://github.com/itdoginfo/podkop.git
synced 2025-12-09 13:06:52 +03:00
feat: add service status and diagnostic tools to podkop UI
This commit is contained in:
@@ -5,6 +5,14 @@
|
|||||||
'require network';
|
'require network';
|
||||||
'require fs';
|
'require fs';
|
||||||
|
|
||||||
|
function formatDiagnosticOutput(output) {
|
||||||
|
if (!output) return '';
|
||||||
|
return output.trim()
|
||||||
|
.replace(/\x1b\[[0-9;]*m/g, '') // Remove ANSI color codes
|
||||||
|
.replace(/\r\n/g, '\n') // Normalize line endings
|
||||||
|
.replace(/\r/g, '\n');
|
||||||
|
}
|
||||||
|
|
||||||
return view.extend({
|
return view.extend({
|
||||||
async render() {
|
async render() {
|
||||||
document.getElementsByTagName('head')[0].insertAdjacentHTML('beforeend', `
|
document.getElementsByTagName('head')[0].insertAdjacentHTML('beforeend', `
|
||||||
@@ -21,6 +29,7 @@ return view.extend({
|
|||||||
m.title = _('Podkop') + ' v' + res.stdout.trim();
|
m.title = _('Podkop') + ' v' + res.stdout.trim();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
s = m.section(form.TypedSection, 'main');
|
s = m.section(form.TypedSection, 'main');
|
||||||
s.anonymous = true;
|
s.anonymous = true;
|
||||||
|
|
||||||
@@ -637,453 +646,63 @@ return view.extend({
|
|||||||
// Diagnostics tab
|
// Diagnostics tab
|
||||||
o = s.tab('diagnostics', _('Diagnostics'));
|
o = s.tab('diagnostics', _('Diagnostics'));
|
||||||
|
|
||||||
function formatDiagnosticOutput(output) {
|
// Service Status Section
|
||||||
if (!output) return '';
|
let createStatusSection = function (podkopStatus, singboxStatus, podkop, luci, singbox, system) {
|
||||||
|
return E('div', { 'class': 'cbi-section' }, [
|
||||||
return output
|
E('h3', {}, _('Service Status')),
|
||||||
.replace(/\x1B\[[0-9;]*[mK]/g, '')
|
E('div', { 'class': 'table', style: 'display: flex; gap: 20px;' }, [
|
||||||
.replace(/\[[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}\] /g, '')
|
// Podkop Column
|
||||||
.replace(/\n{3,}/g, '\n\n')
|
E('div', { 'style': 'flex: 1; padding: 15px; background: #f8f9fa; border-radius: 8px;' }, [
|
||||||
.replace(/===\s+(.*?)\s+===/g, (_, title) => `\n${title}\n${'─'.repeat(title.length)}`)
|
E('div', { 'style': 'margin-bottom: 15px;' }, [
|
||||||
.replace(/^Checking\s+(.+)\.{3}/gm, '► Checking $1...')
|
E('strong', {}, _('Podkop Status')),
|
||||||
.replace(/:\s+(available|not found)$/gm, (_, status) =>
|
E('br'),
|
||||||
`: ${status === 'available' ? '✓' : '✗'}`);
|
E('span', {
|
||||||
}
|
'style': `color: ${podkopStatus.running ? '#4caf50' : '#f44336'}`
|
||||||
|
|
||||||
// Version Information Section
|
|
||||||
o = s.taboption('diagnostics', form.Button, '_show_versions');
|
|
||||||
o.title = _('Version Information');
|
|
||||||
o.description = _('Show version information for all components');
|
|
||||||
o.inputtitle = _('Show Versions');
|
|
||||||
o.inputstyle = 'apply';
|
|
||||||
o.onclick = function () {
|
|
||||||
Promise.all([
|
|
||||||
fs.exec('/etc/init.d/podkop', ['show_version']),
|
|
||||||
fs.exec('/etc/init.d/podkop', ['show_luci_version']),
|
|
||||||
fs.exec('/etc/init.d/podkop', ['show_sing_box_version']),
|
|
||||||
fs.exec('/etc/init.d/podkop', ['show_system_info'])
|
|
||||||
]).then(function ([podkop, luci, singbox, system]) {
|
|
||||||
const versions = [
|
|
||||||
'=== Podkop Version ===\n' + (podkop.stdout || _('No output')) + '\n',
|
|
||||||
'=== LuCI App ===\n' + (luci.stdout || _('No output')) + '\n',
|
|
||||||
'=== sing-box ===\n' + (singbox.stdout || _('No output')) + '\n',
|
|
||||||
system.stdout || _('No output')
|
|
||||||
].join('\n');
|
|
||||||
|
|
||||||
const formattedOutput = formatDiagnosticOutput(versions);
|
|
||||||
ui.showModal(_('Version Information'), [
|
|
||||||
E('div', {
|
|
||||||
style:
|
|
||||||
'max-height: 70vh;' +
|
|
||||||
'overflow-y: auto;' +
|
|
||||||
'margin: 1em 0;' +
|
|
||||||
'padding: 1.5em;' +
|
|
||||||
'background: #f8f9fa;' +
|
|
||||||
'border: 1px solid #e9ecef;' +
|
|
||||||
'border-radius: 4px;' +
|
|
||||||
'font-family: monospace;' +
|
|
||||||
'white-space: pre-wrap;' +
|
|
||||||
'word-wrap: break-word;' +
|
|
||||||
'line-height: 1.5;' +
|
|
||||||
'font-size: 14px;'
|
|
||||||
}, [
|
}, [
|
||||||
E('pre', { style: 'margin: 0;' }, formattedOutput)
|
podkopStatus.running ? '✔' : '✘',
|
||||||
]),
|
' ',
|
||||||
E('div', {
|
podkopStatus.status
|
||||||
style: 'display: flex; justify-content: space-between; margin-top: 1em;'
|
|
||||||
}, [
|
|
||||||
E('button', {
|
|
||||||
'class': 'btn',
|
|
||||||
'click': function (ev) {
|
|
||||||
const textarea = document.createElement('textarea');
|
|
||||||
textarea.value = '```txt\n' + formattedOutput + '\n```';
|
|
||||||
document.body.appendChild(textarea);
|
|
||||||
textarea.select();
|
|
||||||
try {
|
|
||||||
document.execCommand('copy');
|
|
||||||
ev.target.textContent = _('Copied!');
|
|
||||||
setTimeout(() => {
|
|
||||||
ev.target.textContent = _('Copy to Clipboard');
|
|
||||||
}, 1000);
|
|
||||||
} catch (err) {
|
|
||||||
ui.addNotification(null, E('p', {}, _('Failed to copy: ') + err.message));
|
|
||||||
}
|
|
||||||
document.body.removeChild(textarea);
|
|
||||||
}
|
|
||||||
}, _('Copy to Clipboard')),
|
|
||||||
E('button', {
|
|
||||||
'class': 'btn',
|
|
||||||
'click': ui.hideModal
|
|
||||||
}, _('Close'))
|
|
||||||
])
|
])
|
||||||
]);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Connection Checks Section
|
|
||||||
o = s.taboption('diagnostics', form.Button, '_check_nft');
|
|
||||||
o.title = _('NFT Rules');
|
|
||||||
o.description = _('Show current nftables rules and statistics');
|
|
||||||
o.inputtitle = _('Check Rules');
|
|
||||||
o.inputstyle = 'apply';
|
|
||||||
o.onclick = function () {
|
|
||||||
return fs.exec('/etc/init.d/podkop', ['check_nft'])
|
|
||||||
.then(function (res) {
|
|
||||||
const formattedOutput = formatDiagnosticOutput(res.stdout || _('No output'));
|
|
||||||
ui.showModal(_('NFT Rules'), [
|
|
||||||
E('div', {
|
|
||||||
style:
|
|
||||||
'max-height: 70vh;' +
|
|
||||||
'overflow-y: auto;' +
|
|
||||||
'margin: 1em 0;' +
|
|
||||||
'padding: 1.5em;' +
|
|
||||||
'background: #f8f9fa;' +
|
|
||||||
'border: 1px solid #e9ecef;' +
|
|
||||||
'border-radius: 4px;' +
|
|
||||||
'font-family: monospace;' +
|
|
||||||
'white-space: pre-wrap;' +
|
|
||||||
'word-wrap: break-word;' +
|
|
||||||
'line-height: 1.5;' +
|
|
||||||
'font-size: 14px;'
|
|
||||||
}, [
|
|
||||||
E('pre', { style: 'margin: 0;' }, formattedOutput)
|
|
||||||
]),
|
]),
|
||||||
E('div', {
|
E('div', { 'class': 'btn-group', 'style': 'display: flex; flex-direction: column; gap: 8px;' }, [
|
||||||
style: 'display: flex; justify-content: space-between; margin-top: 1em;'
|
podkopStatus.running ?
|
||||||
}, [
|
E('button', {
|
||||||
|
'class': 'btn cbi-button-remove',
|
||||||
|
'click': function () {
|
||||||
|
return fs.exec('/etc/init.d/podkop', ['stop'])
|
||||||
|
.then(() => location.reload());
|
||||||
|
}
|
||||||
|
}, _('Stop Podkop')) :
|
||||||
|
E('button', {
|
||||||
|
'class': 'btn cbi-button-apply',
|
||||||
|
'click': function () {
|
||||||
|
return fs.exec('/etc/init.d/podkop', ['start'])
|
||||||
|
.then(() => location.reload());
|
||||||
|
}
|
||||||
|
}, _('Start Podkop')),
|
||||||
|
E('button', {
|
||||||
|
'class': 'btn cbi-button-apply',
|
||||||
|
'click': function () {
|
||||||
|
return fs.exec('/etc/init.d/podkop', ['restart'])
|
||||||
|
.then(() => location.reload());
|
||||||
|
}
|
||||||
|
}, _('Restart Podkop')),
|
||||||
|
E('button', {
|
||||||
|
'class': 'btn cbi-button-' + (podkopStatus.enabled ? 'remove' : 'apply'),
|
||||||
|
'click': function () {
|
||||||
|
return fs.exec('/etc/init.d/podkop', [podkopStatus.enabled ? 'disable' : 'enable'])
|
||||||
|
.then(() => location.reload());
|
||||||
|
}
|
||||||
|
}, podkopStatus.enabled ? _('Disable Podkop') : _('Enable Podkop')),
|
||||||
E('button', {
|
E('button', {
|
||||||
'class': 'btn',
|
'class': 'btn',
|
||||||
'click': function (ev) {
|
'click': function () {
|
||||||
const textarea = document.createElement('textarea');
|
|
||||||
textarea.value = '```txt\n' + formattedOutput + '\n```';
|
|
||||||
document.body.appendChild(textarea);
|
|
||||||
textarea.select();
|
|
||||||
try {
|
|
||||||
document.execCommand('copy');
|
|
||||||
ev.target.textContent = _('Copied!');
|
|
||||||
setTimeout(() => {
|
|
||||||
ev.target.textContent = _('Copy to Clipboard');
|
|
||||||
}, 1000);
|
|
||||||
} catch (err) {
|
|
||||||
ui.addNotification(null, E('p', {}, _('Failed to copy: ') + err.message));
|
|
||||||
}
|
|
||||||
document.body.removeChild(textarea);
|
|
||||||
}
|
|
||||||
}, _('Copy to Clipboard')),
|
|
||||||
E('button', {
|
|
||||||
'class': 'btn',
|
|
||||||
'click': ui.hideModal
|
|
||||||
}, _('Close'))
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Logs Section
|
|
||||||
o = s.taboption('diagnostics', form.Button, '_check_sing_box_logs');
|
|
||||||
o.title = _('Sing-Box Logs');
|
|
||||||
o.description = _('View recent sing-box logs from system journal');
|
|
||||||
o.inputtitle = _('View Sing-Box Logs');
|
|
||||||
o.inputstyle = 'apply';
|
|
||||||
o.onclick = function () {
|
|
||||||
return fs.exec('/etc/init.d/podkop', ['check_sing_box_logs'])
|
|
||||||
.then(function (res) {
|
|
||||||
const formattedOutput = formatDiagnosticOutput(res.stdout || _('No output'));
|
|
||||||
ui.showModal(_('Sing-Box Logs'), [
|
|
||||||
E('div', {
|
|
||||||
style:
|
|
||||||
'max-height: 70vh;' +
|
|
||||||
'overflow-y: auto;' +
|
|
||||||
'margin: 1em 0;' +
|
|
||||||
'padding: 1.5em;' +
|
|
||||||
'background: #f8f9fa;' +
|
|
||||||
'border: 1px solid #e9ecef;' +
|
|
||||||
'border-radius: 4px;' +
|
|
||||||
'font-family: monospace;' +
|
|
||||||
'white-space: pre-wrap;' +
|
|
||||||
'word-wrap: break-word;' +
|
|
||||||
'line-height: 1.5;' +
|
|
||||||
'font-size: 14px;'
|
|
||||||
}, [
|
|
||||||
E('pre', { style: 'margin: 0;' }, formattedOutput)
|
|
||||||
]),
|
|
||||||
E('div', {
|
|
||||||
style: 'display: flex; justify-content: space-between; margin-top: 1em;'
|
|
||||||
}, [
|
|
||||||
E('button', {
|
|
||||||
'class': 'btn',
|
|
||||||
'click': function (ev) {
|
|
||||||
const textarea = document.createElement('textarea');
|
|
||||||
textarea.value = '```logs\n' + formattedOutput + '\n```';
|
|
||||||
document.body.appendChild(textarea);
|
|
||||||
textarea.select();
|
|
||||||
try {
|
|
||||||
document.execCommand('copy');
|
|
||||||
ev.target.textContent = _('Copied!');
|
|
||||||
setTimeout(() => {
|
|
||||||
ev.target.textContent = _('Copy to Clipboard');
|
|
||||||
}, 1000);
|
|
||||||
} catch (err) {
|
|
||||||
ui.addNotification(null, E('p', {}, _('Failed to copy logs: ') + err.message));
|
|
||||||
}
|
|
||||||
document.body.removeChild(textarea);
|
|
||||||
}
|
|
||||||
}, _('Copy to Clipboard')),
|
|
||||||
E('button', {
|
|
||||||
'class': 'btn',
|
|
||||||
'click': ui.hideModal
|
|
||||||
}, _('Close'))
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
o = s.taboption('diagnostics', form.Button, '_check_logs');
|
|
||||||
o.title = _('Podkop Logs');
|
|
||||||
o.description = _('View recent podkop logs from system journal');
|
|
||||||
o.inputtitle = _('View Podkop Logs');
|
|
||||||
o.inputstyle = 'apply';
|
|
||||||
o.onclick = function () {
|
|
||||||
return fs.exec('/etc/init.d/podkop', ['check_logs'])
|
|
||||||
.then(function (res) {
|
|
||||||
const formattedOutput = formatDiagnosticOutput(res.stdout || _('No output'));
|
|
||||||
ui.showModal(_('Podkop Logs'), [
|
|
||||||
E('div', {
|
|
||||||
style:
|
|
||||||
'max-height: 70vh;' +
|
|
||||||
'overflow-y: auto;' +
|
|
||||||
'margin: 1em 0;' +
|
|
||||||
'padding: 1.5em;' +
|
|
||||||
'background: #f8f9fa;' +
|
|
||||||
'border: 1px solid #e9ecef;' +
|
|
||||||
'border-radius: 4px;' +
|
|
||||||
'font-family: monospace;' +
|
|
||||||
'white-space: pre-wrap;' +
|
|
||||||
'word-wrap: break-word;' +
|
|
||||||
'line-height: 1.5;' +
|
|
||||||
'font-size: 14px;'
|
|
||||||
}, [
|
|
||||||
E('pre', { style: 'margin: 0;' }, formattedOutput)
|
|
||||||
]),
|
|
||||||
E('div', {
|
|
||||||
style: 'display: flex; justify-content: space-between; margin-top: 1em;'
|
|
||||||
}, [
|
|
||||||
E('button', {
|
|
||||||
'class': 'btn',
|
|
||||||
'click': function (ev) {
|
|
||||||
const textarea = document.createElement('textarea');
|
|
||||||
textarea.value = '```logs\n' + formattedOutput + '\n```';
|
|
||||||
document.body.appendChild(textarea);
|
|
||||||
textarea.select();
|
|
||||||
try {
|
|
||||||
document.execCommand('copy');
|
|
||||||
ev.target.textContent = _('Copied!');
|
|
||||||
setTimeout(() => {
|
|
||||||
ev.target.textContent = _('Copy to Clipboard');
|
|
||||||
}, 1000);
|
|
||||||
} catch (err) {
|
|
||||||
ui.addNotification(null, E('p', {}, _('Failed to copy logs: ') + err.message));
|
|
||||||
}
|
|
||||||
document.body.removeChild(textarea);
|
|
||||||
}
|
|
||||||
}, _('Copy to Clipboard')),
|
|
||||||
E('button', {
|
|
||||||
'class': 'btn',
|
|
||||||
'click': ui.hideModal
|
|
||||||
}, _('Close'))
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Configurations Section
|
|
||||||
o = s.taboption('diagnostics', form.Button, '_check_sing_box_connections');
|
|
||||||
o.title = _('Active Connections');
|
|
||||||
o.description = _('View active sing-box network connections');
|
|
||||||
o.inputtitle = _('Check Connections');
|
|
||||||
o.inputstyle = 'apply';
|
|
||||||
o.onclick = function () {
|
|
||||||
return fs.exec('/etc/init.d/podkop', ['check_sing_box_connections'])
|
|
||||||
.then(function (res) {
|
|
||||||
const formattedOutput = formatDiagnosticOutput(res.stdout || _('No output'));
|
|
||||||
ui.showModal(_('Active Connections'), [
|
|
||||||
E('div', {
|
|
||||||
style:
|
|
||||||
'max-height: 70vh;' +
|
|
||||||
'overflow-y: auto;' +
|
|
||||||
'margin: 1em 0;' +
|
|
||||||
'padding: 1.5em;' +
|
|
||||||
'background: #f8f9fa;' +
|
|
||||||
'border: 1px solid #e9ecef;' +
|
|
||||||
'border-radius: 4px;' +
|
|
||||||
'font-family: monospace;' +
|
|
||||||
'white-space: pre-wrap;' +
|
|
||||||
'word-wrap: break-word;' +
|
|
||||||
'line-height: 1.5;' +
|
|
||||||
'font-size: 14px;'
|
|
||||||
}, [
|
|
||||||
E('pre', { style: 'margin: 0;' }, formattedOutput)
|
|
||||||
]),
|
|
||||||
E('div', {
|
|
||||||
style: 'display: flex; justify-content: space-between; margin-top: 1em;'
|
|
||||||
}, [
|
|
||||||
E('button', {
|
|
||||||
'class': 'btn',
|
|
||||||
'click': function (ev) {
|
|
||||||
const textarea = document.createElement('textarea');
|
|
||||||
textarea.value = '```txt\n' + formattedOutput + '\n```';
|
|
||||||
document.body.appendChild(textarea);
|
|
||||||
textarea.select();
|
|
||||||
try {
|
|
||||||
document.execCommand('copy');
|
|
||||||
ev.target.textContent = _('Copied!');
|
|
||||||
setTimeout(() => {
|
|
||||||
ev.target.textContent = _('Copy to Clipboard');
|
|
||||||
}, 1000);
|
|
||||||
} catch (err) {
|
|
||||||
ui.addNotification(null, E('p', {}, _('Failed to copy: ') + err.message));
|
|
||||||
}
|
|
||||||
document.body.removeChild(textarea);
|
|
||||||
}
|
|
||||||
}, _('Copy to Clipboard')),
|
|
||||||
E('button', {
|
|
||||||
'class': 'btn',
|
|
||||||
'click': ui.hideModal
|
|
||||||
}, _('Close'))
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
o = s.taboption('diagnostics', form.Button, '_check_dnsmasq');
|
|
||||||
o.title = _('DNSMasq Configuration');
|
|
||||||
o.description = _('View current DNSMasq configuration settings');
|
|
||||||
o.inputtitle = _('Check DNSMasq');
|
|
||||||
o.inputstyle = 'apply';
|
|
||||||
o.onclick = function () {
|
|
||||||
return fs.exec('/etc/init.d/podkop', ['check_dnsmasq'])
|
|
||||||
.then(function (res) {
|
|
||||||
const formattedOutput = formatDiagnosticOutput(res.stdout || _('No output'));
|
|
||||||
ui.showModal(_('DNSMasq Configuration'), [
|
|
||||||
E('div', {
|
|
||||||
style:
|
|
||||||
'max-height: 70vh;' +
|
|
||||||
'overflow-y: auto;' +
|
|
||||||
'margin: 1em 0;' +
|
|
||||||
'padding: 1.5em;' +
|
|
||||||
'background: #f8f9fa;' +
|
|
||||||
'border: 1px solid #e9ecef;' +
|
|
||||||
'border-radius: 4px;' +
|
|
||||||
'font-family: monospace;' +
|
|
||||||
'white-space: pre-wrap;' +
|
|
||||||
'word-wrap: break-word;' +
|
|
||||||
'line-height: 1.5;' +
|
|
||||||
'font-size: 14px;'
|
|
||||||
}, [
|
|
||||||
E('pre', { style: 'margin: 0;' }, formattedOutput)
|
|
||||||
]),
|
|
||||||
E('div', {
|
|
||||||
style: 'display: flex; justify-content: space-between; margin-top: 1em;'
|
|
||||||
}, [
|
|
||||||
E('button', {
|
|
||||||
'class': 'btn',
|
|
||||||
'click': function (ev) {
|
|
||||||
const textarea = document.createElement('textarea');
|
|
||||||
textarea.value = '```txt\n' + formattedOutput + '\n```';
|
|
||||||
document.body.appendChild(textarea);
|
|
||||||
textarea.select();
|
|
||||||
try {
|
|
||||||
document.execCommand('copy');
|
|
||||||
ev.target.textContent = _('Copied!');
|
|
||||||
setTimeout(() => {
|
|
||||||
ev.target.textContent = _('Copy to Clipboard');
|
|
||||||
}, 1000);
|
|
||||||
} catch (err) {
|
|
||||||
ui.addNotification(null, E('p', {}, _('Failed to copy: ') + err.message));
|
|
||||||
}
|
|
||||||
document.body.removeChild(textarea);
|
|
||||||
}
|
|
||||||
}, _('Copy to Clipboard')),
|
|
||||||
E('button', {
|
|
||||||
'class': 'btn',
|
|
||||||
'click': ui.hideModal
|
|
||||||
}, _('Close'))
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
o = s.taboption('diagnostics', form.Button, '_show_sing_box_config');
|
|
||||||
o.title = _('Sing-Box Configuration');
|
|
||||||
o.description = _('Show current sing-box configuration');
|
|
||||||
o.inputtitle = _('Show Sing-Box Config');
|
|
||||||
o.inputstyle = 'apply';
|
|
||||||
o.onclick = function () {
|
|
||||||
return fs.exec('/etc/init.d/podkop', ['show_sing_box_config'])
|
|
||||||
.then(function (res) {
|
|
||||||
const formattedOutput = formatDiagnosticOutput(res.stdout || _('No output'));
|
|
||||||
ui.showModal(_('Sing-Box Configuration'), [
|
|
||||||
E('div', {
|
|
||||||
style:
|
|
||||||
'max-height: 70vh;' +
|
|
||||||
'overflow-y: auto;' +
|
|
||||||
'margin: 1em 0;' +
|
|
||||||
'padding: 1.5em;' +
|
|
||||||
'background: #f8f9fa;' +
|
|
||||||
'border: 1px solid #e9ecef;' +
|
|
||||||
'border-radius: 4px;' +
|
|
||||||
'font-family: monospace;' +
|
|
||||||
'white-space: pre-wrap;' +
|
|
||||||
'word-wrap: break-word;' +
|
|
||||||
'line-height: 1.5;' +
|
|
||||||
'font-size: 14px;'
|
|
||||||
}, [
|
|
||||||
E('pre', { style: 'margin: 0;' }, formattedOutput)
|
|
||||||
]),
|
|
||||||
E('div', {
|
|
||||||
style: 'display: flex; justify-content: space-between; margin-top: 1em;'
|
|
||||||
}, [
|
|
||||||
E('button', {
|
|
||||||
'class': 'btn',
|
|
||||||
'click': function (ev) {
|
|
||||||
const textarea = document.createElement('textarea');
|
|
||||||
textarea.value = '```json\n' + formattedOutput + '\n```';
|
|
||||||
document.body.appendChild(textarea);
|
|
||||||
textarea.select();
|
|
||||||
try {
|
|
||||||
document.execCommand('copy');
|
|
||||||
ev.target.textContent = _('Copied!');
|
|
||||||
setTimeout(() => {
|
|
||||||
ev.target.textContent = _('Copy to Clipboard');
|
|
||||||
}, 1000);
|
|
||||||
} catch (err) {
|
|
||||||
ui.addNotification(null, E('p', {}, _('Failed to copy: ') + err.message));
|
|
||||||
}
|
|
||||||
document.body.removeChild(textarea);
|
|
||||||
}
|
|
||||||
}, _('Copy to Clipboard')),
|
|
||||||
E('button', {
|
|
||||||
'class': 'btn',
|
|
||||||
'click': ui.hideModal
|
|
||||||
}, _('Close'))
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
o = s.taboption('diagnostics', form.Button, '_show_config');
|
|
||||||
o.title = _('Podkop Configuration');
|
|
||||||
o.description = _('Show current podkop configuration with masked sensitive data');
|
|
||||||
o.inputtitle = _('Show Config');
|
|
||||||
o.inputstyle = 'apply';
|
|
||||||
o.onclick = function () {
|
|
||||||
return fs.exec('/etc/init.d/podkop', ['show_config'])
|
return fs.exec('/etc/init.d/podkop', ['show_config'])
|
||||||
.then(function (res) {
|
.then(function (res) {
|
||||||
const formattedOutput = formatDiagnosticOutput(res.stdout || _('No output'));
|
const formattedOutput = formatDiagnosticOutput(res.stdout || _('No output'));
|
||||||
ui.showModal(_('Podkop Configuration'), [
|
ui.showModal(_('Podkop Configuration'), [
|
||||||
E('div', {
|
E('div', {
|
||||||
style:
|
style: 'max-height: 70vh;' +
|
||||||
'max-height: 70vh;' +
|
|
||||||
'overflow-y: auto;' +
|
'overflow-y: auto;' +
|
||||||
'margin: 1em 0;' +
|
'margin: 1em 0;' +
|
||||||
'padding: 1.5em;' +
|
'padding: 1.5em;' +
|
||||||
@@ -1105,7 +724,7 @@ return view.extend({
|
|||||||
'class': 'btn',
|
'class': 'btn',
|
||||||
'click': function (ev) {
|
'click': function (ev) {
|
||||||
const textarea = document.createElement('textarea');
|
const textarea = document.createElement('textarea');
|
||||||
textarea.value = '```json\n' + formattedOutput + '\n```';
|
textarea.value = '```txt\n' + formattedOutput + '\n```';
|
||||||
document.body.appendChild(textarea);
|
document.body.appendChild(textarea);
|
||||||
textarea.select();
|
textarea.select();
|
||||||
try {
|
try {
|
||||||
@@ -1127,27 +746,456 @@ return view.extend({
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
}, _('Show Config')),
|
||||||
o = s.taboption('diagnostics', form.Button, '_list_update');
|
E('button', {
|
||||||
o.title = _('Update Lists');
|
'class': 'btn',
|
||||||
o.description = _('Update all lists in config');
|
'click': function () {
|
||||||
o.inputtitle = _('Update Lists');
|
return fs.exec('/etc/init.d/podkop', ['check_logs'])
|
||||||
o.inputstyle = 'apply';
|
.then(function (res) {
|
||||||
o.onclick = function () {
|
const formattedOutput = formatDiagnosticOutput(res.stdout || _('No output'));
|
||||||
|
ui.showModal(_('Podkop Logs'), [
|
||||||
|
E('div', {
|
||||||
|
style: 'max-height: 70vh;' +
|
||||||
|
'overflow-y: auto;' +
|
||||||
|
'margin: 1em 0;' +
|
||||||
|
'padding: 1.5em;' +
|
||||||
|
'background: #f8f9fa;' +
|
||||||
|
'border: 1px solid #e9ecef;' +
|
||||||
|
'border-radius: 4px;' +
|
||||||
|
'font-family: monospace;' +
|
||||||
|
'white-space: pre-wrap;' +
|
||||||
|
'word-wrap: break-word;' +
|
||||||
|
'line-height: 1.5;' +
|
||||||
|
'font-size: 14px;'
|
||||||
|
}, [
|
||||||
|
E('pre', { style: 'margin: 0;' }, formattedOutput)
|
||||||
|
]),
|
||||||
|
E('div', {
|
||||||
|
style: 'display: flex; justify-content: space-between; margin-top: 1em;'
|
||||||
|
}, [
|
||||||
|
E('button', {
|
||||||
|
'class': 'btn',
|
||||||
|
'click': function (ev) {
|
||||||
|
const textarea = document.createElement('textarea');
|
||||||
|
textarea.value = '```txt\n' + formattedOutput + '\n```';
|
||||||
|
document.body.appendChild(textarea);
|
||||||
|
textarea.select();
|
||||||
|
try {
|
||||||
|
document.execCommand('copy');
|
||||||
|
ev.target.textContent = _('Copied!');
|
||||||
|
setTimeout(() => {
|
||||||
|
ev.target.textContent = _('Copy to Clipboard');
|
||||||
|
}, 1000);
|
||||||
|
} catch (err) {
|
||||||
|
ui.addNotification(null, E('p', {}, _('Failed to copy: ') + err.message));
|
||||||
|
}
|
||||||
|
document.body.removeChild(textarea);
|
||||||
|
}
|
||||||
|
}, _('Copy to Clipboard')),
|
||||||
|
E('button', {
|
||||||
|
'class': 'btn',
|
||||||
|
'click': ui.hideModal
|
||||||
|
}, _('Close'))
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, _('View Logs'))
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
// Sing-box Column
|
||||||
|
E('div', { 'style': 'flex: 1; padding: 15px; background: #f8f9fa; border-radius: 8px;' }, [
|
||||||
|
E('div', { 'style': 'margin-bottom: 15px;' }, [
|
||||||
|
E('strong', {}, _('Sing-box Status')),
|
||||||
|
E('br'),
|
||||||
|
E('span', {
|
||||||
|
'style': `color: ${singboxStatus.running ? '#4caf50' : '#f44336'}`
|
||||||
|
}, [
|
||||||
|
singboxStatus.running ? '✔' : '✘',
|
||||||
|
' ',
|
||||||
|
`${singboxStatus.status}`
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
E('div', { 'class': 'btn-group', 'style': 'display: flex; flex-direction: column; gap: 8px;' }, [
|
||||||
|
E('button', {
|
||||||
|
'class': 'btn',
|
||||||
|
'click': function () {
|
||||||
|
return fs.exec('/etc/init.d/podkop', ['show_sing_box_config'])
|
||||||
|
.then(function (res) {
|
||||||
|
const formattedOutput = formatDiagnosticOutput(res.stdout || _('No output'));
|
||||||
|
ui.showModal(_('Sing-box Configuration'), [
|
||||||
|
E('div', {
|
||||||
|
style: 'max-height: 70vh;' +
|
||||||
|
'overflow-y: auto;' +
|
||||||
|
'margin: 1em 0;' +
|
||||||
|
'padding: 1.5em;' +
|
||||||
|
'background: #f8f9fa;' +
|
||||||
|
'border: 1px solid #e9ecef;' +
|
||||||
|
'border-radius: 4px;' +
|
||||||
|
'font-family: monospace;' +
|
||||||
|
'white-space: pre-wrap;' +
|
||||||
|
'word-wrap: break-word;' +
|
||||||
|
'line-height: 1.5;' +
|
||||||
|
'font-size: 14px;'
|
||||||
|
}, [
|
||||||
|
E('pre', { style: 'margin: 0;' }, formattedOutput)
|
||||||
|
]),
|
||||||
|
E('div', {
|
||||||
|
style: 'display: flex; justify-content: space-between; margin-top: 1em;'
|
||||||
|
}, [
|
||||||
|
E('button', {
|
||||||
|
'class': 'btn',
|
||||||
|
'click': function (ev) {
|
||||||
|
const textarea = document.createElement('textarea');
|
||||||
|
textarea.value = '```txt\n' + formattedOutput + '\n```';
|
||||||
|
document.body.appendChild(textarea);
|
||||||
|
textarea.select();
|
||||||
|
try {
|
||||||
|
document.execCommand('copy');
|
||||||
|
ev.target.textContent = _('Copied!');
|
||||||
|
setTimeout(() => {
|
||||||
|
ev.target.textContent = _('Copy to Clipboard');
|
||||||
|
}, 1000);
|
||||||
|
} catch (err) {
|
||||||
|
ui.addNotification(null, E('p', {}, _('Failed to copy: ') + err.message));
|
||||||
|
}
|
||||||
|
document.body.removeChild(textarea);
|
||||||
|
}
|
||||||
|
}, _('Copy to Clipboard')),
|
||||||
|
E('button', {
|
||||||
|
'class': 'btn',
|
||||||
|
'click': ui.hideModal
|
||||||
|
}, _('Close'))
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, _('Show Config')),
|
||||||
|
E('button', {
|
||||||
|
'class': 'btn',
|
||||||
|
'click': function () {
|
||||||
|
return fs.exec('/etc/init.d/podkop', ['check_sing_box_logs'])
|
||||||
|
.then(function (res) {
|
||||||
|
const formattedOutput = formatDiagnosticOutput(res.stdout || _('No output'));
|
||||||
|
ui.showModal(_('Sing-box Logs'), [
|
||||||
|
E('div', {
|
||||||
|
style: 'max-height: 70vh;' +
|
||||||
|
'overflow-y: auto;' +
|
||||||
|
'margin: 1em 0;' +
|
||||||
|
'padding: 1.5em;' +
|
||||||
|
'background: #f8f9fa;' +
|
||||||
|
'border: 1px solid #e9ecef;' +
|
||||||
|
'border-radius: 4px;' +
|
||||||
|
'font-family: monospace;' +
|
||||||
|
'white-space: pre-wrap;' +
|
||||||
|
'word-wrap: break-word;' +
|
||||||
|
'line-height: 1.5;' +
|
||||||
|
'font-size: 14px;'
|
||||||
|
}, [
|
||||||
|
E('pre', { style: 'margin: 0;' }, formattedOutput)
|
||||||
|
]),
|
||||||
|
E('div', {
|
||||||
|
style: 'display: flex; justify-content: space-between; margin-top: 1em;'
|
||||||
|
}, [
|
||||||
|
E('button', {
|
||||||
|
'class': 'btn',
|
||||||
|
'click': function (ev) {
|
||||||
|
const textarea = document.createElement('textarea');
|
||||||
|
textarea.value = '```txt\n' + formattedOutput + '\n```';
|
||||||
|
document.body.appendChild(textarea);
|
||||||
|
textarea.select();
|
||||||
|
try {
|
||||||
|
document.execCommand('copy');
|
||||||
|
ev.target.textContent = _('Copied!');
|
||||||
|
setTimeout(() => {
|
||||||
|
ev.target.textContent = _('Copy to Clipboard');
|
||||||
|
}, 1000);
|
||||||
|
} catch (err) {
|
||||||
|
ui.addNotification(null, E('p', {}, _('Failed to copy: ') + err.message));
|
||||||
|
}
|
||||||
|
document.body.removeChild(textarea);
|
||||||
|
}
|
||||||
|
}, _('Copy to Clipboard')),
|
||||||
|
E('button', {
|
||||||
|
'class': 'btn',
|
||||||
|
'click': ui.hideModal
|
||||||
|
}, _('Close'))
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, _('View Logs')),
|
||||||
|
E('button', {
|
||||||
|
'class': 'btn',
|
||||||
|
'click': function () {
|
||||||
|
return fs.exec('/etc/init.d/podkop', ['check_sing_box_connections'])
|
||||||
|
.then(function (res) {
|
||||||
|
const formattedOutput = formatDiagnosticOutput(res.stdout || _('No output'));
|
||||||
|
ui.showModal(_('Active Connections'), [
|
||||||
|
E('div', {
|
||||||
|
style: 'max-height: 70vh;' +
|
||||||
|
'overflow-y: auto;' +
|
||||||
|
'margin: 1em 0;' +
|
||||||
|
'padding: 1.5em;' +
|
||||||
|
'background: #f8f9fa;' +
|
||||||
|
'border: 1px solid #e9ecef;' +
|
||||||
|
'border-radius: 4px;' +
|
||||||
|
'font-family: monospace;' +
|
||||||
|
'white-space: pre-wrap;' +
|
||||||
|
'word-wrap: break-word;' +
|
||||||
|
'line-height: 1.5;' +
|
||||||
|
'font-size: 14px;'
|
||||||
|
}, [
|
||||||
|
E('pre', { style: 'margin: 0;' }, formattedOutput)
|
||||||
|
]),
|
||||||
|
E('div', {
|
||||||
|
style: 'display: flex; justify-content: space-between; margin-top: 1em;'
|
||||||
|
}, [
|
||||||
|
E('button', {
|
||||||
|
'class': 'btn',
|
||||||
|
'click': function (ev) {
|
||||||
|
const textarea = document.createElement('textarea');
|
||||||
|
textarea.value = '```txt\n' + formattedOutput + '\n```';
|
||||||
|
document.body.appendChild(textarea);
|
||||||
|
textarea.select();
|
||||||
|
try {
|
||||||
|
document.execCommand('copy');
|
||||||
|
ev.target.textContent = _('Copied!');
|
||||||
|
setTimeout(() => {
|
||||||
|
ev.target.textContent = _('Copy to Clipboard');
|
||||||
|
}, 1000);
|
||||||
|
} catch (err) {
|
||||||
|
ui.addNotification(null, E('p', {}, _('Failed to copy: ') + err.message));
|
||||||
|
}
|
||||||
|
document.body.removeChild(textarea);
|
||||||
|
}
|
||||||
|
}, _('Copy to Clipboard')),
|
||||||
|
E('button', {
|
||||||
|
'class': 'btn',
|
||||||
|
'click': ui.hideModal
|
||||||
|
}, _('Close'))
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, _('Check Connections'))
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
// Diagnostics Column
|
||||||
|
E('div', { 'style': 'flex: 1; padding: 15px; background: #f8f9fa; border-radius: 8px;' }, [
|
||||||
|
E('div', { 'style': 'margin-bottom: 15px;' }, [
|
||||||
|
E('strong', {}, _('Diagnostic Tools'))
|
||||||
|
]),
|
||||||
|
E('div', { 'class': 'btn-group', 'style': 'display: flex; flex-direction: column; gap: 8px;' }, [
|
||||||
|
E('button', {
|
||||||
|
'class': 'btn',
|
||||||
|
'click': function () {
|
||||||
|
return fs.exec('/etc/init.d/podkop', ['check_nft'])
|
||||||
|
.then(function (res) {
|
||||||
|
const formattedOutput = formatDiagnosticOutput(res.stdout || _('No output'));
|
||||||
|
ui.showModal(_('NFT Rules'), [
|
||||||
|
E('div', {
|
||||||
|
style: 'max-height: 70vh;' +
|
||||||
|
'overflow-y: auto;' +
|
||||||
|
'margin: 1em 0;' +
|
||||||
|
'padding: 1.5em;' +
|
||||||
|
'background: #f8f9fa;' +
|
||||||
|
'border: 1px solid #e9ecef;' +
|
||||||
|
'border-radius: 4px;' +
|
||||||
|
'font-family: monospace;' +
|
||||||
|
'white-space: pre-wrap;' +
|
||||||
|
'word-wrap: break-word;' +
|
||||||
|
'line-height: 1.5;' +
|
||||||
|
'font-size: 14px;'
|
||||||
|
}, [
|
||||||
|
E('pre', { style: 'margin: 0;' }, formattedOutput)
|
||||||
|
]),
|
||||||
|
E('div', {
|
||||||
|
style: 'display: flex; justify-content: space-between; margin-top: 1em;'
|
||||||
|
}, [
|
||||||
|
E('button', {
|
||||||
|
'class': 'btn',
|
||||||
|
'click': function (ev) {
|
||||||
|
const textarea = document.createElement('textarea');
|
||||||
|
textarea.value = '```txt\n' + formattedOutput + '\n```';
|
||||||
|
document.body.appendChild(textarea);
|
||||||
|
textarea.select();
|
||||||
|
try {
|
||||||
|
document.execCommand('copy');
|
||||||
|
ev.target.textContent = _('Copied!');
|
||||||
|
setTimeout(() => {
|
||||||
|
ev.target.textContent = _('Copy to Clipboard');
|
||||||
|
}, 1000);
|
||||||
|
} catch (err) {
|
||||||
|
ui.addNotification(null, E('p', {}, _('Failed to copy: ') + err.message));
|
||||||
|
}
|
||||||
|
document.body.removeChild(textarea);
|
||||||
|
}
|
||||||
|
}, _('Copy to Clipboard')),
|
||||||
|
E('button', {
|
||||||
|
'class': 'btn',
|
||||||
|
'click': ui.hideModal
|
||||||
|
}, _('Close'))
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, _('Check NFT Rules')),
|
||||||
|
E('button', {
|
||||||
|
'class': 'btn',
|
||||||
|
'click': function () {
|
||||||
|
return fs.exec('/etc/init.d/podkop', ['check_dnsmasq'])
|
||||||
|
.then(function (res) {
|
||||||
|
const formattedOutput = formatDiagnosticOutput(res.stdout || _('No output'));
|
||||||
|
ui.showModal(_('DNSMasq Configuration'), [
|
||||||
|
E('div', {
|
||||||
|
style: 'max-height: 70vh;' +
|
||||||
|
'overflow-y: auto;' +
|
||||||
|
'margin: 1em 0;' +
|
||||||
|
'padding: 1.5em;' +
|
||||||
|
'background: #f8f9fa;' +
|
||||||
|
'border: 1px solid #e9ecef;' +
|
||||||
|
'border-radius: 4px;' +
|
||||||
|
'font-family: monospace;' +
|
||||||
|
'white-space: pre-wrap;' +
|
||||||
|
'word-wrap: break-word;' +
|
||||||
|
'line-height: 1.5;' +
|
||||||
|
'font-size: 14px;'
|
||||||
|
}, [
|
||||||
|
E('pre', { style: 'margin: 0;' }, formattedOutput)
|
||||||
|
]),
|
||||||
|
E('div', {
|
||||||
|
style: 'display: flex; justify-content: space-between; margin-top: 1em;'
|
||||||
|
}, [
|
||||||
|
E('button', {
|
||||||
|
'class': 'btn',
|
||||||
|
'click': function (ev) {
|
||||||
|
const textarea = document.createElement('textarea');
|
||||||
|
textarea.value = '```txt\n' + formattedOutput + '\n```';
|
||||||
|
document.body.appendChild(textarea);
|
||||||
|
textarea.select();
|
||||||
|
try {
|
||||||
|
document.execCommand('copy');
|
||||||
|
ev.target.textContent = _('Copied!');
|
||||||
|
setTimeout(() => {
|
||||||
|
ev.target.textContent = _('Copy to Clipboard');
|
||||||
|
}, 1000);
|
||||||
|
} catch (err) {
|
||||||
|
ui.addNotification(null, E('p', {}, _('Failed to copy: ') + err.message));
|
||||||
|
}
|
||||||
|
document.body.removeChild(textarea);
|
||||||
|
}
|
||||||
|
}, _('Copy to Clipboard')),
|
||||||
|
E('button', {
|
||||||
|
'class': 'btn',
|
||||||
|
'click': ui.hideModal
|
||||||
|
}, _('Close'))
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, _('Check DNSMasq')),
|
||||||
|
E('button', {
|
||||||
|
'class': 'btn',
|
||||||
|
'click': function () {
|
||||||
return fs.exec('/etc/init.d/podkop', ['list_update'])
|
return fs.exec('/etc/init.d/podkop', ['list_update'])
|
||||||
.then(function (res) {
|
.then(function (res) {
|
||||||
const formattedOutput = formatDiagnosticOutput(res.stdout || _('No output'));
|
const formattedOutput = formatDiagnosticOutput(res.stdout || _('No output'));
|
||||||
ui.showModal(_('Lists Update Results'), [
|
ui.showModal(_('Lists Update Results'), [
|
||||||
E('div', { style: 'white-space:pre-wrap;padding:5px' }, formattedOutput),
|
E('div', {
|
||||||
E('div', { class: 'right' }, E('button', {
|
style: 'max-height: 70vh;' +
|
||||||
class: 'btn',
|
'overflow-y: auto;' +
|
||||||
click: ui.hideModal
|
'margin: 1em 0;' +
|
||||||
}, _('Close')))
|
'padding: 1.5em;' +
|
||||||
|
'background: #f8f9fa;' +
|
||||||
|
'border: 1px solid #e9ecef;' +
|
||||||
|
'border-radius: 4px;' +
|
||||||
|
'font-family: monospace;' +
|
||||||
|
'white-space: pre-wrap;' +
|
||||||
|
'word-wrap: break-word;' +
|
||||||
|
'line-height: 1.5;' +
|
||||||
|
'font-size: 14px;'
|
||||||
|
}, [
|
||||||
|
E('pre', { style: 'margin: 0;' }, formattedOutput)
|
||||||
|
]),
|
||||||
|
E('div', {
|
||||||
|
style: 'display: flex; justify-content: space-between; margin-top: 1em;'
|
||||||
|
}, [
|
||||||
|
E('button', {
|
||||||
|
'class': 'btn',
|
||||||
|
'click': function (ev) {
|
||||||
|
const textarea = document.createElement('textarea');
|
||||||
|
textarea.value = '```txt\n' + formattedOutput + '\n```';
|
||||||
|
document.body.appendChild(textarea);
|
||||||
|
textarea.select();
|
||||||
|
try {
|
||||||
|
document.execCommand('copy');
|
||||||
|
ev.target.textContent = _('Copied!');
|
||||||
|
setTimeout(() => {
|
||||||
|
ev.target.textContent = _('Copy to Clipboard');
|
||||||
|
}, 1000);
|
||||||
|
} catch (err) {
|
||||||
|
ui.addNotification(null, E('p', {}, _('Failed to copy: ') + err.message));
|
||||||
|
}
|
||||||
|
document.body.removeChild(textarea);
|
||||||
|
}
|
||||||
|
}, _('Copy to Clipboard')),
|
||||||
|
E('button', {
|
||||||
|
'class': 'btn',
|
||||||
|
'click': ui.hideModal
|
||||||
|
}, _('Close'))
|
||||||
|
])
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
}, _('Update Lists'))
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
// Version Information Column
|
||||||
|
E('div', { 'style': 'flex: 1; padding: 15px; background: #f8f9fa; border-radius: 8px;' }, [
|
||||||
|
E('div', { 'style': 'margin-bottom: 15px;' }, [
|
||||||
|
E('strong', {}, _('Version Information')),
|
||||||
|
E('br'),
|
||||||
|
E('div', { 'style': 'margin-top: 10px; font-family: monospace; white-space: pre-wrap;' }, [
|
||||||
|
E('strong', {}, 'Podkop: '), podkop.stdout ? podkop.stdout.trim() : _('Unknown'), '\n',
|
||||||
|
E('strong', {}, 'LuCI App: '), luci.stdout ? luci.stdout.trim() : _('Unknown'), '\n',
|
||||||
|
E('strong', {}, 'Sing-box: '), singbox.stdout ? singbox.stdout.trim() : _('Unknown'), '\n',
|
||||||
|
E('strong', {}, 'OpenWrt Version: '), system.stdout ? system.stdout.split('\n')[1].trim() : _('Unknown'), '\n',
|
||||||
|
E('strong', {}, 'Device Model: '), system.stdout ? system.stdout.split('\n')[4].trim() : _('Unknown')
|
||||||
|
])
|
||||||
|
])
|
||||||
|
])
|
||||||
|
])
|
||||||
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
o = s.taboption('diagnostics', form.DummyValue, '_status');
|
||||||
|
o.rawhtml = true;
|
||||||
|
o.cfgvalue = function () {
|
||||||
|
return Promise.all([
|
||||||
|
fs.exec('/etc/init.d/podkop', ['get_status']),
|
||||||
|
fs.exec('/etc/init.d/podkop', ['get_sing_box_status']),
|
||||||
|
fs.exec('/etc/init.d/podkop', ['show_version']),
|
||||||
|
fs.exec('/etc/init.d/podkop', ['show_luci_version']),
|
||||||
|
fs.exec('/etc/init.d/podkop', ['show_sing_box_version']),
|
||||||
|
fs.exec('/etc/init.d/podkop', ['show_system_info'])
|
||||||
|
]).then(function ([podkopStatus, singboxStatus, podkop, luci, singbox, system]) {
|
||||||
|
try {
|
||||||
|
const parsedPodkopStatus = JSON.parse(podkopStatus.stdout || '{"running":0,"enabled":0,"status":"unknown"}');
|
||||||
|
const parsedSingboxStatus = JSON.parse(singboxStatus.stdout || '{"running":0,"enabled":0,"status":"unknown"}');
|
||||||
|
return createStatusSection(parsedPodkopStatus, parsedSingboxStatus, podkop, luci, singbox, system);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error parsing status:', e);
|
||||||
|
return E('div', { 'class': 'alert-message warning' }, [
|
||||||
|
E('strong', {}, _('Error loading status')), E('br'),
|
||||||
|
E('pre', {}, e.toString())
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// Add new section 'extra'
|
// Add new section 'extra'
|
||||||
var s = m.section(form.TypedSection, 'extra', _('Extra configurations'));
|
var s = m.section(form.TypedSection, 'extra', _('Extra configurations'));
|
||||||
s.anonymous = false;
|
s.anonymous = false;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ script=$(readlink "$initscript")
|
|||||||
NAME="$(basename ${script:-$initscript})"
|
NAME="$(basename ${script:-$initscript})"
|
||||||
config_load "$NAME"
|
config_load "$NAME"
|
||||||
|
|
||||||
EXTRA_COMMANDS="main list_update check_proxy check_nft check_github check_logs check_sing_box_connections check_sing_box_logs check_dnsmasq show_config show_version show_sing_box_config show_luci_version show_sing_box_version show_system_info"
|
EXTRA_COMMANDS="main list_update check_proxy check_nft check_github check_logs check_sing_box_connections check_sing_box_logs check_dnsmasq show_config show_version show_sing_box_config show_luci_version show_sing_box_version show_system_info get_status get_sing_box_status"
|
||||||
EXTRA_HELP=" list_update Updating domain and subnet lists
|
EXTRA_HELP=" list_update Updating domain and subnet lists
|
||||||
check_proxy Check if sing-box proxy works correctly
|
check_proxy Check if sing-box proxy works correctly
|
||||||
check_nft Show PodkopTable nftables rules
|
check_nft Show PodkopTable nftables rules
|
||||||
@@ -21,7 +21,8 @@ EXTRA_HELP=" list_update Updating domain and subnet lists
|
|||||||
show_sing_box_config Show current sing-box configuration
|
show_sing_box_config Show current sing-box configuration
|
||||||
show_luci_version Show LuCI app version
|
show_luci_version Show LuCI app version
|
||||||
show_sing_box_version Show sing-box version
|
show_sing_box_version Show sing-box version
|
||||||
show_system_info Show OpenWrt version and device model"
|
show_system_info Show OpenWrt version and device model
|
||||||
|
get_sing_box_status Get sing-box status"
|
||||||
|
|
||||||
[ ! -L /usr/sbin/podkop ] && ln -s /etc/init.d/podkop /usr/sbin/podkop
|
[ ! -L /usr/sbin/podkop ] && ln -s /etc/init.d/podkop /usr/sbin/podkop
|
||||||
|
|
||||||
@@ -1630,3 +1631,71 @@ show_system_info() {
|
|||||||
echo "=== Device Model ==="
|
echo "=== Device Model ==="
|
||||||
cat /tmp/sysinfo/model
|
cat /tmp/sysinfo/model
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get_sing_box_status() {
|
||||||
|
local running=0
|
||||||
|
local enabled=0
|
||||||
|
local status=""
|
||||||
|
local version=""
|
||||||
|
|
||||||
|
# Check if service is enabled
|
||||||
|
if [ -x /etc/rc.d/S99sing-box ]; then
|
||||||
|
enabled=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if service is running
|
||||||
|
if pgrep -f "sing-box" >/dev/null; then
|
||||||
|
running=1
|
||||||
|
version=$(sing-box version | head -n 1 | awk '{print $3}')
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Format status message
|
||||||
|
if [ $running -eq 1 ]; then
|
||||||
|
if [ $enabled -eq 1 ]; then
|
||||||
|
status="running & enabled"
|
||||||
|
else
|
||||||
|
status="running but disabled"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [ $enabled -eq 1 ]; then
|
||||||
|
status="stopped but enabled"
|
||||||
|
else
|
||||||
|
status="stopped & disabled"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "{\"running\":$running,\"enabled\":$enabled,\"status\":\"$status\"}"
|
||||||
|
}
|
||||||
|
|
||||||
|
get_status() {
|
||||||
|
local running=0
|
||||||
|
local enabled=0
|
||||||
|
local status=""
|
||||||
|
|
||||||
|
# Check if service is enabled
|
||||||
|
if [ -x /etc/rc.d/S99podkop ]; then
|
||||||
|
enabled=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if service is running
|
||||||
|
if pgrep -f "sing-box" >/dev/null; then
|
||||||
|
running=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Format status message
|
||||||
|
if [ $running -eq 1 ]; then
|
||||||
|
if [ $enabled -eq 1 ]; then
|
||||||
|
status="running & enabled"
|
||||||
|
else
|
||||||
|
status="running but disabled"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [ $enabled -eq 1 ]; then
|
||||||
|
status="stopped but enabled"
|
||||||
|
else
|
||||||
|
status="stopped & disabled"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "{\"running\":$running,\"enabled\":$enabled,\"status\":\"$status\"}"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user