🎉 Sync 2026-03-23 03:47:01

This commit is contained in:
github-actions[bot] committed 2026-03-23 03:47:01 +08:00
1 parent 9028beac27
commit 5e79f3a579
78 files changed
+1873 -11153

No files matched your search

@@ -0,0 +1,35 @@
'use strict';
'require view';
'require fs';
'require ui';
const isReadonlyView = !L.hasViewPermission() || null;
return view.extend({
handleSaveApply: null,
handleReset: null,
load() {
return L.resolveDefault(fs.read('/etc/netdata/netdata.conf'), '');
},
handleSave(ev) {
let value = (document.querySelector('textarea').value || '').trim().replace(/\r\n/g, '\n') + '\n';
return fs.write('/etc/netdata/netdata.conf', value).then((rc) => {
document.querySelector('textarea').value = value;
ui.addNotification(null, E('p', _('Contents have been saved.')), 'info');
return fs.exec('/etc/init.d/netdata', [ 'reload' ]);
}).catch((e) => {
ui.addNotification(null, E('p', _('Unable to save contents: %s').format(e.message)));
});
},
render(conf) {
return E([
E('h4', _('Edit Netdata main config: <code>/etc/netdata/netdata.conf</code>')),
E('p', {}, E('textarea', { 'style': 'width:100%', 'rows': 25, 'disabled': isReadonlyView }, [ conf != null ? conf : '' ]))
]);
}
});
@@ -1,183 +0,0 @@
// Copyright (C) 2021-2025 sirpdboy herboy2008@gmail.com https://github.com/sirpdboy/luci-app-netdata
'use strict';
'require form';
'require poll';
'require rpc';
'require uci';
'require ui';
'require view';
'require fs';
const getNetdataVersion = rpc.declare({
object: 'luci.netdata',
method: 'get_version',
expect: { 'version': '' }
});
async function checkProcess() {
try {
const pidofRes = await fs.exec('/bin/pidof', ['netdata']);
if (pidofRes.code === 0) {
return {
running: true,
pid: pidofRes.stdout.trim()
};
}
} catch (err) {
// Ignore error
}
try {
const psRes = await fs.exec('/bin/ps', ['-C', 'netdata', '-o', 'pid=']);
const pid = psRes.stdout.trim();
return {
running: pid !== '',
pid: pid || null
};
} catch (err) {
return { running: false, pid: null };
}
}
function getVersionInfo() {
return L.resolveDefault(getNetdataVersion(), {}).then(function(result) {
return result || {};
}).catch(function(error) {
console.error('Failed to get version:', error);
return {};
});
}
function getServiceStatus() {
return L.resolveDefault(checkProcess(), {}).then(function(res) {
let isRunning = false;
try {
if (res && res.running) {
isRunning = true;
}
} catch (e) {
console.error('Service status error:', e);
}
return isRunning;
}).catch(function(error) {
console.error('Service status check failed:', error);
return false;
});
}
function renderStatus(isRunning, webport, protocol, version) {
let statusText = isRunning ? _('RUNNING') : _('NOT RUNNING');
let color = isRunning ? 'green' : 'red';
let icon = isRunning ? '✓' : '✗';
let versionText = version ? `${version}` : '';
let html = String.format(
'<em><span style="color:%s">%s <strong>%s %s - %s</strong></span></em>',
color, icon, _('Netdata'),versionText, statusText
);
if (isRunning) {
let buttonUrl = String.format('%s//%s:%s/', protocol, window.location.hostname, webport);
html += String.format(
'<input class="cbi-button cbi-button-reload" type="button" style="margin-left: 20px" value="%s" onclick="window.open(\'%s\')">',
_('Open Web UI'),
buttonUrl
);
}
return html;
}
return view.extend({
load: function() {
return Promise.all([
uci.load('netdata')
]);
},
render: function(data) {
let m, s, o;
let webport = uci.get_first('netdata', 'netdata', 'port') || '19999';
let uci_ssl = uci.get_first('netdata', 'netdata', 'enable_ssl') || '0';
let protocol = uci_ssl === '1' ? 'https:' : 'http:';
m = new form.Map('netdata', _('Netdata'),
_('An open-source real-time performance monitoring and visualization tool'));
// 状态显示部分
s = m.section(form.TypedSection);
s.anonymous = true;
s.addremove = false;
s.render = function() {
poll.add(function() {
return Promise.all([
getServiceStatus(),
getVersionInfo()
]).then(function(results) {
var [isRunning, version] = results;
var view = document.getElementById('service_status');
if (view) {
view.innerHTML = renderStatus(isRunning, webport, protocol, version);
}
}).catch(function(error) {
console.error('Poll error:', error);
});
}, 5); // 添加轮询间隔5秒
return E('div', { class: 'cbi-section', id: 'status_bar' }, [
E('div', { id: 'service_status' },
E('p', {}, _('Collecting data...'))
),
E('div', { 'style': 'text-align: right; font-style: italic;' }, [
E('span', {}, [
_('© github '),
E('a', {
'href': 'https://github.com/sirpdboy',
'target': '_blank',
'style': 'text-decoration: none;'
}, 'by sirpdboy')
])
])
]);
};
// 配置部分
s = m.section(form.NamedSection, 'netdata', 'netdata');
o = s.option(form.Flag, 'enabled', _('Enable'));
o.default = o.disabled;
o.rmempty = false;
o = s.option(form.Value, 'port', _('Service port'));
o.datatype = 'port';
o.placeholder = '19999';
o.rmempty = false;
o.validate = function(section_id, value) {
if (value < 1 || value > 65535) {
return _('Port must be between 1 and 65535');
}
return true;
};
o = s.option(form.Flag, 'enable_ssl', _('Enable SSL'));
o.rmempty = true;
o = s.option(form.DummyValue, 'cert_file', _('Cert file'));
o.default = '/etc/ssl/ezopwrt.crt';
o.depends('enable_ssl', '1');
o.cfgvalue = function(section_id) {
return uci.get('netdata', section_id, 'cert_file') || '/etc/ssl/ezopwrt.crt';
};
o = s.option(form.DummyValue, 'key_file', _('Cert Key file'));
o.default = '/etc/ssl/ezopwrt.key';
o.depends('enable_ssl', '1');
o.cfgvalue = function(section_id) {
return uci.get('netdata', section_id, 'key_file') || '/etc/ssl/ezopwrt.key';
};
// o = s.option(form.DummyValue, 'feedback_info', _('feedback info'));
// o.href = 'https://github.com/sirpdboy';
// o.cfgvalue = function() {
// return 'https://github.com/sirpdboy';
// };
return m.render();
}
});
@@ -1,36 +1,162 @@
// Copyright (C) 2021-2025 sirpdboy herboy2008@gmail.com https://github.com/sirpdboy/luci-app-netdata
'use strict';
'require view';
'require fs';
'require uci';
'require ui';
const isReadonlyView = !L.hasViewPermission() || null;
'require form';
return view.extend({
handleSaveApply: null,
handleReset: null,
// handleSaveApply: null,
// handleSave: null,
// handleReset: null,
load() {
return L.resolveDefault(fs.read('/etc/netdata/netdata.conf'), '');
return Promise.all([
L.resolveDefault(fs.stat('/usr/sbin/nginx'), {}),
uci.load('netdata')
]);
},
handleSave(ev) {
let value = (document.querySelector('textarea').value || '').trim().replace(/\r\n/g, '\n') + '\n';
render(res) {
const has_nginx = res[0].path;
return fs.write('/etc/netdata/netdata.conf', value).then((rc) => {
document.querySelector('textarea').value = value;
ui.addNotification(null, E('p', _('Contents have been saved.')), 'info');
let m, s, o;
return fs.exec('/etc/init.d/netdata', [ 'restart' ]);
}).catch((e) => {
ui.addNotification(null, E('p', _('Unable to save contents: %s').format(e.message)));
});
},
m = new form.Map('netdata');
render(conf) {
return E([
E('h4', _('Edit Netdata main config: <code>/etc/netdata/netdata.conf</code>')),
E('p', {}, E('textarea', { 'style': 'width:100%', 'rows': 25, 'disabled': isReadonlyView }, [ conf != null ? conf : '' ]))
]);
s = m.section(form.TypedSection, 'netdata');
s.anonymous = true;
o = s.option(form.Flag, 'enabled', _('Enable'));
o.rmempty = false;
o = s.option(form.Value, 'port', _('Service port'));
o.datatype = 'port';
o.placeholder = '19999';
o.rmempty = false;
o = s.option(form.Flag, 'enable_ssl', _('Enable SSL'));
o.rmempty = true;
o.depends('nginx_support', '0');
o = s.option(form.Value, 'cert_file', _('Cert file'));
o.placeholder = '/etc/netdata/cert.crt';
o.rmempty = false;
o.retain = true;
o.depends('enable_ssl', '1');
o = s.option(form.Value, 'key_file', _('Cert Key file'));
o.placeholder = '/etc/netdata/cert.key';
o.rmempty = false;
o.retain = true;
o.depends('enable_ssl', '1');
o = s.option(form.Flag, 'nginx_support', _('Nginx Support'));
o.rmempty = true;
if (! has_nginx) {
o.description = _('To enable this feature you need install <b>luci-nginx</b> and <b>luci-ssl-nginx</b><br/> first');
o.readonly = true;
}
o.write = function(section, value) {
uci.set('netdata', section, 'nginx_support', has_nginx ? value : null);
};
o = s.option(form.Flag, 'auth', _('Enable Auth'));
o.rmempty = true;
o.depends('nginx_support', '1');
o.write = function(section, value) {
uci.set('netdata', section, 'auth', has_nginx ? value : null);
};
o = s.option(form.Value, 'user_passwd', _('Login Username and Password hash'));
o.placeholder = 'admin:$apr1$t7qQjoqb$YBHtAb7VGSkjIdObMG.Oy0';
o.rmempty = false;
o.retain = true;
o.depends('auth', '1');
o = s.option(form.Button, '_webui', _('Open Web UI'));
o.inputtitle = _('Open');
o.inputstyle = 'apply';
o.onclick = L.bind(function(ev, section_id) {
const port=uci.get('netdata', section_id, 'port') || '19999';
const ssl=uci.get('netdata', section_id, 'enable_ssl') || '0';
const nginx=uci.get('netdata', section_id, 'nginx_support') || '0';
window.open((nginx === '1' ? window.location.protocol : ssl === '1' ? 'https:' : 'http:') + '//' + window.location.hostname + (nginx === '1' ? '/netdata/' : ':' + port));
}, o)
o = s.option(form.Button, '_start', _('Start') + ' ' + _('Netdata'));
o.inputtitle = _('Start');
o.inputstyle = 'apply';
o.onclick = function() {
return fs.exec('/etc/init.d/netdata', ['start'])
.catch((e) => { ui.addNotification(null, E('p', e.message), 'error') });
};
o = s.option(form.Button, '_stop', _('Stop') + ' ' + _('Netdata'));
o.inputtitle = _('Stop');
o.inputstyle = 'reset';
o.onclick = function() {
return fs.exec('/etc/init.d/netdata', ['stop'])
.catch((e) => { ui.addNotification(null, E('p', e.message), 'error') });
};
s = m.section(form.TypedSection, '_utilities');
s.render = L.bind(function(view, section_id) {
return E('div',{ 'class': 'cbi-section' }, [
E('h3', {}, [ _('Password hash generator') ]),
E('div', { 'class': 'generator' }, [
E('div', { 'class': 'control-group' }, [
E('button', {
'class': 'cbi-button cbi-button-neutral',
'click': ui.createHandlerFn(this, () => {
let npwn = this.map.findElement('id', '_passwd_type');
if (npwn.type == 'password') {
npwn.setAttribute('type', 'text');
} else if (npwn.type == 'text') {
npwn.setAttribute('type', 'password');
}
})
}, '*'),
E('input', {
'id': '_passwd_type',
'style': 'margin:5px 0',
'class': 'cbi-input-password',
'type': 'password',
'value': 'type new passwd here'
})
]),
E('button', {
'class': 'cbi-button cbi-button-action',
'click': ui.createHandlerFn(this, () => {
let npw = this.map.findElement('id', '_passwd_type').value,
button = document.querySelector('.generator > .cbi-button'),
out = this.map.findElement('id', '_passwd_hash_out');
button.setAttribute('disabled', 'true');
return fs.exec('/usr/bin/openssl', [ 'passwd', '-apr1', npw ]).then((res) => {
out.value = res.stdout || '';
}).catch(e => {
ui.addNotification(null, E('p', e.message), 'error');
}).finally(() => {
button.removeAttribute('disabled');
});
})
}, [ _('GenNew') ]),
E('input', {
'id': '_passwd_hash_out',
'style': 'margin:5px 0; width:320px',
'type': 'text',
'readonly': ''
})
])
]);
}, o, this);
return m.render();
}
});
@@ -0,0 +1,26 @@
'use strict';
'require view';
'require uci';
return view.extend({
handleSaveApply: null,
handleSave: null,
handleReset: null,
load() {
return uci.load('netdata');
},
render() {
var port = uci.get_first('netdata', 'netdata', 'port') || '19999',
ssl = uci.get_first('netdata', 'netdata', 'enable_ssl') || '0',
nginx = uci.get_first('netdata', 'netdata', 'nginx_support') || '0';
if (port === '0')
return E('div', { class: 'alert-message warning' },
_('Port 0 is not supported.<br />Change to a other port and try again.'));
return E('iframe', {
src: (nginx === '1' ? window.location.protocol : ssl === '1' ? 'https:' : 'http:') + '//' + window.location.hostname + (nginx === '1' ? '/netdata/' : ':' + port),
style: 'width: 100%; min-height: 100vh; border: none; border-radius: 3px;'
});
}
});
@@ -1,73 +0,0 @@
// Copyright (C) 2021-2025 sirpdboy herboy2008@gmail.com https://github.com/sirpdboy/luci-app-netdata
'use strict';
'require view';
'require fs';
'require ui';
'require uci';
'require form';
'require poll';
return view.extend({
load: function() {
return uci.load('netdata');
},
checkRunning: function() {
return fs.exec('/bin/pidof', ['netdata']).then(function(pidRes) {
if (pidRes.code === 0) return { isRunning: true };
return fs.exec('/bin/ash', ['-c', 'ps | grep -q "[n]etdata"']).then(function(grepRes) {
return { isRunning: grepRes.code === 0 };
});
});
},
render: function() {
var self = this;
return this.checkRunning().then(function(checkResult) {
var isRunning = checkResult.isRunning;
let webport = uci.get_first('netdata', 'netdata', 'port') || '19999';
let uci_ssl = uci.get_first('netdata', 'netdata', 'enable_ssl') || '0';
let nginx = uci.get_first('netdata', 'netdata', 'nginx_support') || '0';
let protocol = nginx === '1' ? window.location.protocol : uci_ssl === '1' ? 'https:' : 'http:';
let buttonUrl = String.format('%s//%s:%s/#menu_system', protocol, window.location.hostname, webport);
var container = E('div');
if (!isRunning) {
var message = _('Netdata Service Not Running');
container.appendChild(E('div', {
style: 'text-align: center; padding: 2em;'
}, [
E('img', {
src: 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMjQiIGhlaWdodD0iMTAyNCIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCI+PHBhdGggZmlsbD0iI2RmMDAwMCIgZD0iTTk0Mi40MjEgMjM0LjYyNGw4MC44MTEtODAuODExLTE1My4wNDUtMTUzLjA0NS04MC44MTEgODAuODExYy03OS45NTctNTEuNjI3LTE3NS4xNDctODEuNTc5LTI3Ny4zNzYtODEuNTc5LTI4Mi43NTIgMC01MTIgMjI5LjI0OC01MTIgNTEyIDAgMTAyLjIyOSAyOS45NTIgMTk3LjQxOSA4MS41NzkgMjc3LjM3NmwtODAuODExIDgwLjgxMSAxNTMuMDQ1IDE1My4wNDUgODAuODExLTgwLjgxMWM3OS45NTcgNTEuNjI3IDE3NS4xNDcgODEuNTc5IDI3Ny4zNzYgODEuNTc5IDI4Mi43NTIgMCA1MTItMjI5LjI0OCA1MTItNTEyIDAtMTAyLjIyOS0yOS45NTItMTk3LjQxOS04MS41NzktMjc3LjM3NnpNMTk0Ljk0NCA1MTJjMC0xNzUuMTA0IDE0MS45NTItMzE3LjA1NiAzMTcuMDU2LTMxNy4wNTYgNDggMCA5My40ODMgMTAuNjY3IDEzNC4yMjkgMjkuNzgxbC00MjEuNTQ3IDQyMS41NDdjLTE5LjA3Mi00MC43ODktMjkuNzM5LTg2LjI3Mi0yOS43MzktMTM0LjI3MnpNNTEyIDgyOS4wNTZjLTQ4IDAtOTMuNDgzLTEwLjY2Ny0xMzQuMjI5LTI5Ljc4MWw0MjEuNTQ3LTQyMS41NDdjMTkuMDcyIDQwLjc4OSAyOS43ODEgODYuMjcyIDI5Ljc4MSAxMzQuMjI5LTAuMDQzIDE3NS4xNDctMTQxLjk5NSAzMTcuMDk5LTMxNy4wOTkgMzE3LjA5OXoiLz48L3N2Zz4=',
style: 'width: 100px; height: 100px; margin-bottom: 1em;'
}),
E('h2', {}, message)
]));
} else {
var iframe = E('iframe', {
src: buttonUrl,
style: 'width: 100%; min-height: 100vh; border: none;'
});
container.appendChild(iframe);
}
poll.add(function() {
return self.checkRunning().then(function(checkResult) {
var newStatus = checkResult.isRunning;
if (newStatus !== isRunning) {
window.location.reload();
}
});
}, 5);
poll.start();
return container;
});
},
handleSaveApply: null,
handleSave: null,
handleReset: null
});