fix: Netdata Dashboard improvements and bug fixes
- Improve dashboard rendering and service status display - Fix settings UI layout and validation - Update RPCD backend for better error handling Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
9ef0b6db18
commit
e8e177d655
@ -28,7 +28,9 @@ return view.extend({
|
||||
var logs = (data[3] && data[3].entries) || [];
|
||||
|
||||
var isRunning = netdataStatus.running || false;
|
||||
var netdataUrl = netdataStatus.url || 'http://127.0.0.1:19999';
|
||||
var netdataPort = netdataStatus.port || 19999;
|
||||
// Use current browser hostname for iframe (not 127.0.0.1 which won't work)
|
||||
var netdataUrl = 'http://' + window.location.hostname + ':' + netdataPort;
|
||||
var alarmCount = this.countAlarms(alarms);
|
||||
|
||||
var view = E('div', { 'class': 'netdata-dashboard secubox-netdata' }, [
|
||||
@ -69,28 +71,45 @@ return view.extend({
|
||||
},
|
||||
|
||||
renderHeader: function(status, stats) {
|
||||
return E('div', { 'class': 'sh-page-header sh-page-header-lite' }, [
|
||||
var headerStyle = 'margin-bottom: 1.5rem;';
|
||||
var titleStyle = 'display: flex; align-items: center; gap: 0.5rem; margin: 0 0 0.5rem 0; font-size: 1.5rem;';
|
||||
var subtitleStyle = 'margin: 0; color: #8b949e; font-size: 0.9rem;';
|
||||
var metaStyle = 'display: flex; flex-wrap: wrap; gap: 1rem; margin-top: 1rem;';
|
||||
|
||||
return E('div', { 'style': headerStyle }, [
|
||||
E('div', {}, [
|
||||
E('h2', { 'class': 'sh-page-title' }, [
|
||||
E('span', { 'class': 'sh-page-title-icon' }, '📊'),
|
||||
E('h2', { 'style': titleStyle }, [
|
||||
E('span', {}, '📊'),
|
||||
_('Netdata Monitoring')
|
||||
]),
|
||||
E('p', { 'class': 'sh-page-subtitle' },
|
||||
E('p', { 'style': subtitleStyle },
|
||||
_('Real-time analytics for CPU, memory, disk, and services.'))
|
||||
]),
|
||||
E('div', { 'class': 'sh-header-meta' }, [
|
||||
E('div', { 'style': metaStyle }, [
|
||||
this.renderHeaderChip(_('Status'), status.running ? _('Online') : _('Offline'),
|
||||
status.running ? 'success' : 'warn'),
|
||||
this.renderHeaderChip(_('Version'), status.version || _('Unknown')),
|
||||
this.renderHeaderChip(_('Version'), status.version || _('unknown')),
|
||||
this.renderHeaderChip(_('Uptime'), API.formatUptime(stats.uptime || 0))
|
||||
])
|
||||
]);
|
||||
},
|
||||
|
||||
renderHeaderChip: function(label, value, tone) {
|
||||
return E('div', { 'class': 'sh-header-chip' + (tone ? ' ' + tone : '') }, [
|
||||
E('span', { 'class': 'sh-chip-label' }, label),
|
||||
E('strong', {}, value)
|
||||
var chipStyle = 'display: flex; flex-direction: column; padding: 0.5rem 1rem; background: #161b22; border: 1px solid #30363d; border-radius: 8px; min-width: 100px;';
|
||||
var labelStyle = 'font-size: 0.75rem; color: #8b949e; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 0.25rem;';
|
||||
var valueStyle = 'font-size: 1rem; font-weight: 600;';
|
||||
|
||||
if (tone === 'success') {
|
||||
valueStyle += ' color: #3fb950;';
|
||||
} else if (tone === 'warn') {
|
||||
valueStyle += ' color: #d29922;';
|
||||
} else {
|
||||
valueStyle += ' color: #f0f6fc;';
|
||||
}
|
||||
|
||||
return E('div', { 'style': chipStyle }, [
|
||||
E('span', { 'style': labelStyle }, label),
|
||||
E('strong', { 'style': valueStyle }, value)
|
||||
]);
|
||||
},
|
||||
|
||||
@ -115,20 +134,34 @@ return view.extend({
|
||||
},
|
||||
|
||||
renderQuickStats: function(stats) {
|
||||
return E('div', { 'class': 'nd-quick-stats' }, [
|
||||
this.renderStatCard(_('CPU'), (stats.cpu_percent || 0) + '%'),
|
||||
this.renderStatCard(_('Memory'), (stats.memory_percent || 0) + '%'),
|
||||
this.renderStatCard(_('Disk'), (stats.disk_percent || 0) + '%'),
|
||||
this.renderStatCard(_('Load'), stats.load || '0.00'),
|
||||
this.renderStatCard(_('Temp'), (stats.temperature || 0) + '°C'),
|
||||
this.renderStatCard(_('Clients'), stats.clients || 0)
|
||||
var gridStyle = 'display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 0.75rem; margin-bottom: 1.5rem;';
|
||||
|
||||
return E('div', { 'style': gridStyle }, [
|
||||
this.renderStatCard(_('CPU'), (stats.cpu_percent || 0) + '%', stats.cpu_percent > 80 ? 'danger' : stats.cpu_percent > 50 ? 'warning' : 'good'),
|
||||
this.renderStatCard(_('Memory'), (stats.memory_percent || 0) + '%', stats.memory_percent > 80 ? 'danger' : stats.memory_percent > 50 ? 'warning' : 'good'),
|
||||
this.renderStatCard(_('Disk'), (stats.disk_percent || 0) + '%', stats.disk_percent > 80 ? 'danger' : stats.disk_percent > 50 ? 'warning' : 'info'),
|
||||
this.renderStatCard(_('Load'), stats.load || '0.00', 'info'),
|
||||
this.renderStatCard(_('Temp'), (stats.temperature || 0) + '°C', stats.temperature > 70 ? 'danger' : stats.temperature > 50 ? 'warning' : 'good'),
|
||||
this.renderStatCard(_('Clients'), stats.clients || 0, 'info')
|
||||
]);
|
||||
},
|
||||
|
||||
renderStatCard: function(label, value) {
|
||||
return E('div', { 'class': 'nd-stat-card' }, [
|
||||
E('span', { 'class': 'nd-stat-label' }, label),
|
||||
E('strong', { 'class': 'nd-stat-value' }, value)
|
||||
renderStatCard: function(label, value, tone) {
|
||||
var cardStyle = 'display: flex; flex-direction: column; align-items: center; padding: 1rem; background: #161b22; border: 1px solid #30363d; border-radius: 8px; text-align: center;';
|
||||
var labelStyle = 'font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.5px; color: #6e7681; margin-bottom: 0.5rem;';
|
||||
var valueStyle = 'font-size: 1.5rem; font-weight: 700; font-family: monospace;';
|
||||
|
||||
var colors = {
|
||||
'good': '#3fb950',
|
||||
'warning': '#d29922',
|
||||
'danger': '#f85149',
|
||||
'info': '#58a6ff'
|
||||
};
|
||||
valueStyle += ' color: ' + (colors[tone] || '#f0f6fc') + ';';
|
||||
|
||||
return E('div', { 'style': cardStyle }, [
|
||||
E('span', { 'style': labelStyle }, label),
|
||||
E('strong', { 'style': valueStyle }, String(value))
|
||||
]);
|
||||
},
|
||||
|
||||
|
||||
@ -18,6 +18,15 @@ return view.extend({
|
||||
var info = data[1] || {};
|
||||
var system = data[2] || {};
|
||||
|
||||
// Build URL using browser hostname (not 127.0.0.1 which won't work from browser)
|
||||
var port = status.port || 19999;
|
||||
var bind = status.bind || '0.0.0.0';
|
||||
var dashboardUrl = 'http://' + window.location.hostname + ':' + port;
|
||||
|
||||
var tableStyle = 'width: 100%; border-collapse: collapse;';
|
||||
var thStyle = 'padding: 0.75rem 1rem; text-align: left; font-weight: 600; width: 200px; background: #161b22; border-bottom: 1px solid #30363d;';
|
||||
var tdStyle = 'padding: 0.75rem 1rem; border-bottom: 1px solid #30363d;';
|
||||
|
||||
var view = E('div', { 'class': 'cbi-map' }, [
|
||||
E('link', { 'rel': 'stylesheet', 'href': L.resource('secubox-theme/secubox-theme.css') }),
|
||||
E('h2', {}, _('Netdata Settings')),
|
||||
@ -27,35 +36,37 @@ return view.extend({
|
||||
// Service Information
|
||||
E('div', { 'class': 'cbi-section' }, [
|
||||
E('h3', {}, _('Service Information')),
|
||||
E('div', { 'class': 'table cbi-section-table' }, [
|
||||
E('div', { 'class': 'tr cbi-section-table-row' }, [
|
||||
E('div', { 'class': 'td left', 'style': 'width: 33%; font-weight: bold;' }, _('Service Status')),
|
||||
E('div', { 'class': 'td left' }, [
|
||||
E('span', {
|
||||
'class': 'badge',
|
||||
'style': 'background: ' + (status.running ? '#28a745' : '#dc3545') + '; color: white; padding: 0.25em 0.6em; border-radius: 3px;'
|
||||
}, status.running ? _('Running') : _('Stopped'))
|
||||
])
|
||||
]),
|
||||
E('div', { 'class': 'tr cbi-section-table-row' }, [
|
||||
E('div', { 'class': 'td left', 'style': 'width: 33%; font-weight: bold;' }, _('Version')),
|
||||
E('div', { 'class': 'td left' }, status.version || 'Unknown')
|
||||
]),
|
||||
E('div', { 'class': 'tr cbi-section-table-row' }, [
|
||||
E('div', { 'class': 'td left', 'style': 'width: 33%; font-weight: bold;' }, _('Listen Port')),
|
||||
E('div', { 'class': 'td left' }, (status.port || 19999).toString())
|
||||
]),
|
||||
E('div', { 'class': 'tr cbi-section-table-row' }, [
|
||||
E('div', { 'class': 'td left', 'style': 'width: 33%; font-weight: bold;' }, _('Bind Address')),
|
||||
E('div', { 'class': 'td left' }, status.bind || '127.0.0.1')
|
||||
]),
|
||||
E('div', { 'class': 'tr cbi-section-table-row' }, [
|
||||
E('div', { 'class': 'td left', 'style': 'width: 33%; font-weight: bold;' }, _('Dashboard URL')),
|
||||
E('div', { 'class': 'td left' }, [
|
||||
E('a', {
|
||||
'href': status.url || 'http://127.0.0.1:19999',
|
||||
'target': '_blank'
|
||||
}, status.url || 'http://127.0.0.1:19999')
|
||||
E('table', { 'style': tableStyle }, [
|
||||
E('tbody', {}, [
|
||||
E('tr', {}, [
|
||||
E('th', { 'style': thStyle }, _('Service Status')),
|
||||
E('td', { 'style': tdStyle }, [
|
||||
E('span', {
|
||||
'style': 'display: inline-block; padding: 0.25rem 0.75rem; border-radius: 4px; font-weight: 500; background: ' + (status.running ? '#238636' : '#da3633') + '; color: white;'
|
||||
}, status.running ? _('Running') : _('Stopped'))
|
||||
])
|
||||
]),
|
||||
E('tr', {}, [
|
||||
E('th', { 'style': thStyle }, _('Version')),
|
||||
E('td', { 'style': tdStyle }, status.version || 'unknown')
|
||||
]),
|
||||
E('tr', {}, [
|
||||
E('th', { 'style': thStyle }, _('Listen Port')),
|
||||
E('td', { 'style': tdStyle }, String(port))
|
||||
]),
|
||||
E('tr', {}, [
|
||||
E('th', { 'style': thStyle }, _('Bind Address')),
|
||||
E('td', { 'style': tdStyle }, bind)
|
||||
]),
|
||||
E('tr', {}, [
|
||||
E('th', { 'style': thStyle }, _('Dashboard URL')),
|
||||
E('td', { 'style': tdStyle }, [
|
||||
E('a', {
|
||||
'href': dashboardUrl,
|
||||
'target': '_blank',
|
||||
'style': 'color: #58a6ff;'
|
||||
}, dashboardUrl)
|
||||
])
|
||||
])
|
||||
])
|
||||
])
|
||||
@ -64,31 +75,32 @@ return view.extend({
|
||||
// System Information
|
||||
E('div', { 'class': 'cbi-section', 'style': 'margin-top: 2em;' }, [
|
||||
E('h3', {}, _('System Information')),
|
||||
E('div', { 'class': 'table cbi-section-table' }, [
|
||||
E('div', { 'class': 'tr cbi-section-table-row' }, [
|
||||
E('div', { 'class': 'td left', 'style': 'width: 33%; font-weight: bold;' }, _('Hostname')),
|
||||
E('div', { 'class': 'td left' }, system.hostname || 'Unknown')
|
||||
]),
|
||||
E('div', { 'class': 'tr cbi-section-table-row' }, [
|
||||
E('div', { 'class': 'td left', 'style': 'width: 33%; font-weight: bold;' }, _('Model')),
|
||||
E('div', { 'class': 'td left' }, system.model || 'Unknown')
|
||||
]),
|
||||
E('div', { 'class': 'tr cbi-section-table-row' }, [
|
||||
E('div', { 'class': 'td left', 'style': 'width: 33%; font-weight: bold;' }, _('Kernel')),
|
||||
E('div', { 'class': 'td left' }, system.kernel || 'Unknown')
|
||||
]),
|
||||
E('div', { 'class': 'tr cbi-section-table-row' }, [
|
||||
E('div', { 'class': 'td left', 'style': 'width: 33%; font-weight: bold;' }, _('Architecture')),
|
||||
E('div', { 'class': 'td left' }, system.arch || 'Unknown')
|
||||
]),
|
||||
E('div', { 'class': 'tr cbi-section-table-row' }, [
|
||||
E('div', { 'class': 'td left', 'style': 'width: 33%; font-weight: bold;' }, _('Distribution')),
|
||||
E('div', { 'class': 'td left' },
|
||||
(system.distro || 'OpenWrt') + ' ' + (system.version || ''))
|
||||
]),
|
||||
E('div', { 'class': 'tr cbi-section-table-row' }, [
|
||||
E('div', { 'class': 'td left', 'style': 'width: 33%; font-weight: bold;' }, _('Uptime')),
|
||||
E('div', { 'class': 'td left' }, system.uptime_formatted || '0d 0h 0m')
|
||||
E('table', { 'style': tableStyle }, [
|
||||
E('tbody', {}, [
|
||||
E('tr', {}, [
|
||||
E('th', { 'style': thStyle }, _('Hostname')),
|
||||
E('td', { 'style': tdStyle }, system.hostname || 'Unknown')
|
||||
]),
|
||||
E('tr', {}, [
|
||||
E('th', { 'style': thStyle }, _('Model')),
|
||||
E('td', { 'style': tdStyle }, system.model || 'Unknown')
|
||||
]),
|
||||
E('tr', {}, [
|
||||
E('th', { 'style': thStyle }, _('Kernel')),
|
||||
E('td', { 'style': tdStyle }, system.kernel || 'Unknown')
|
||||
]),
|
||||
E('tr', {}, [
|
||||
E('th', { 'style': thStyle }, _('Architecture')),
|
||||
E('td', { 'style': tdStyle }, system.arch || 'Unknown')
|
||||
]),
|
||||
E('tr', {}, [
|
||||
E('th', { 'style': thStyle }, _('Distribution')),
|
||||
E('td', { 'style': tdStyle }, (system.distro || 'OpenWrt') + ' ' + (system.version || ''))
|
||||
]),
|
||||
E('tr', {}, [
|
||||
E('th', { 'style': thStyle }, _('Uptime')),
|
||||
E('td', { 'style': tdStyle }, system.uptime_formatted || '0d 0h 0m')
|
||||
])
|
||||
])
|
||||
])
|
||||
]),
|
||||
|
||||
@ -439,8 +439,8 @@ get_stats() {
|
||||
get_netdata_status() {
|
||||
json_init
|
||||
|
||||
# Check if netdata is running
|
||||
if pgrep -x netdata >/dev/null 2>&1; then
|
||||
# Check if netdata is running (use pidof for OpenWrt compatibility)
|
||||
if pidof netdata >/dev/null 2>&1; then
|
||||
json_add_string "service" "running"
|
||||
json_add_boolean "running" 1
|
||||
else
|
||||
@ -448,23 +448,27 @@ get_netdata_status() {
|
||||
json_add_boolean "running" 0
|
||||
fi
|
||||
|
||||
# Get configuration
|
||||
local port="19999"
|
||||
local bind="127.0.0.1"
|
||||
# Get configuration with proper defaults
|
||||
local port=""
|
||||
local bind=""
|
||||
|
||||
if [ -f /etc/netdata/netdata.conf ]; then
|
||||
port=$(grep "^\s*default port" /etc/netdata/netdata.conf 2>/dev/null | awk '{print $NF}' || echo "19999")
|
||||
bind=$(grep "^\s*bind to" /etc/netdata/netdata.conf 2>/dev/null | awk '{print $NF}' || echo "127.0.0.1")
|
||||
port=$(grep "^\s*default port" /etc/netdata/netdata.conf 2>/dev/null | awk '{print $NF}')
|
||||
bind=$(grep "^\s*bind to" /etc/netdata/netdata.conf 2>/dev/null | awk '{print $NF}')
|
||||
fi
|
||||
|
||||
# Apply defaults if empty
|
||||
[ -z "$port" ] && port="19999"
|
||||
[ -z "$bind" ] && bind="127.0.0.1"
|
||||
|
||||
json_add_int "port" "$port"
|
||||
json_add_string "bind" "$bind"
|
||||
json_add_string "url" "http://${bind}:${port}"
|
||||
|
||||
# Try to get version from Netdata API
|
||||
local version=""
|
||||
if command -v curl >/dev/null; then
|
||||
version=$(curl -s "http://${bind}:${port}/api/v1/info" 2>/dev/null | jsonfilter -e '@.version' 2>/dev/null || echo "")
|
||||
if command -v curl >/dev/null && pidof netdata >/dev/null 2>&1; then
|
||||
version=$(curl -s --connect-timeout 2 "http://${bind}:${port}/api/v1/info" 2>/dev/null | jsonfilter -e '@.version' 2>/dev/null || echo "")
|
||||
fi
|
||||
json_add_string "version" "${version:-unknown}"
|
||||
|
||||
@ -484,7 +488,7 @@ get_netdata_alarms() {
|
||||
fi
|
||||
|
||||
# Fetch alarms from Netdata API
|
||||
if command -v curl >/dev/null && pgrep -x netdata >/dev/null; then
|
||||
if command -v curl >/dev/null && pidof netdata >/dev/null; then
|
||||
local alarms=$(curl -s "http://${bind}:${port}/api/v1/alarms" 2>/dev/null)
|
||||
if [ -n "$alarms" ] && [ "$alarms" != "null" ]; then
|
||||
echo "$alarms"
|
||||
@ -509,7 +513,7 @@ get_netdata_info() {
|
||||
fi
|
||||
|
||||
# Fetch info from Netdata API
|
||||
if command -v curl >/dev/null && pgrep -x netdata >/dev/null; then
|
||||
if command -v curl >/dev/null && pidof netdata >/dev/null; then
|
||||
local info=$(curl -s "http://${bind}:${port}/api/v1/info" 2>/dev/null)
|
||||
if [ -n "$info" ] && [ "$info" != "null" ]; then
|
||||
echo "$info"
|
||||
@ -529,7 +533,7 @@ restart_netdata() {
|
||||
/etc/init.d/netdata restart >/dev/null 2>&1
|
||||
sleep 2
|
||||
|
||||
if pgrep -x netdata >/dev/null 2>&1; then
|
||||
if pidof netdata >/dev/null 2>&1; then
|
||||
json_add_boolean "success" 1
|
||||
json_add_string "message" "Netdata restarted successfully"
|
||||
secubox_log "Netdata service restarted"
|
||||
@ -549,14 +553,14 @@ restart_netdata() {
|
||||
start_netdata() {
|
||||
json_init
|
||||
|
||||
if pgrep -x netdata >/dev/null 2>&1; then
|
||||
if pidof netdata >/dev/null 2>&1; then
|
||||
json_add_boolean "success" 1
|
||||
json_add_string "message" "Netdata already running"
|
||||
elif [ -x /etc/init.d/netdata ]; then
|
||||
/etc/init.d/netdata start >/dev/null 2>&1
|
||||
sleep 2
|
||||
|
||||
if pgrep -x netdata >/dev/null 2>&1; then
|
||||
if pidof netdata >/dev/null 2>&1; then
|
||||
json_add_boolean "success" 1
|
||||
json_add_string "message" "Netdata started successfully"
|
||||
secubox_log "Netdata service started"
|
||||
@ -576,14 +580,14 @@ start_netdata() {
|
||||
stop_netdata() {
|
||||
json_init
|
||||
|
||||
if ! pgrep -x netdata >/dev/null 2>&1; then
|
||||
if ! pidof netdata >/dev/null 2>&1; then
|
||||
json_add_boolean "success" 1
|
||||
json_add_string "message" "Netdata already stopped"
|
||||
elif [ -x /etc/init.d/netdata ]; then
|
||||
/etc/init.d/netdata stop >/dev/null 2>&1
|
||||
sleep 1
|
||||
|
||||
if ! pgrep -x netdata >/dev/null 2>&1; then
|
||||
if ! pidof netdata >/dev/null 2>&1; then
|
||||
json_add_boolean "success" 1
|
||||
json_add_string "message" "Netdata stopped successfully"
|
||||
secubox_log "Netdata service stopped"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user