diff --git a/package/secubox/luci-app-netdata-dashboard/htdocs/luci-static/resources/view/netdata-dashboard/dashboard.js b/package/secubox/luci-app-netdata-dashboard/htdocs/luci-static/resources/view/netdata-dashboard/dashboard.js index 47442a52..e25c6b14 100644 --- a/package/secubox/luci-app-netdata-dashboard/htdocs/luci-static/resources/view/netdata-dashboard/dashboard.js +++ b/package/secubox/luci-app-netdata-dashboard/htdocs/luci-static/resources/view/netdata-dashboard/dashboard.js @@ -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)) ]); }, diff --git a/package/secubox/luci-app-netdata-dashboard/htdocs/luci-static/resources/view/netdata-dashboard/settings.js b/package/secubox/luci-app-netdata-dashboard/htdocs/luci-static/resources/view/netdata-dashboard/settings.js index d413c5b5..cbbb5e50 100644 --- a/package/secubox/luci-app-netdata-dashboard/htdocs/luci-static/resources/view/netdata-dashboard/settings.js +++ b/package/secubox/luci-app-netdata-dashboard/htdocs/luci-static/resources/view/netdata-dashboard/settings.js @@ -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') + ]) ]) ]) ]), diff --git a/package/secubox/luci-app-netdata-dashboard/root/usr/libexec/rpcd/luci.netdata-dashboard b/package/secubox/luci-app-netdata-dashboard/root/usr/libexec/rpcd/luci.netdata-dashboard index 5f0632ac..e7ff63b8 100755 --- a/package/secubox/luci-app-netdata-dashboard/root/usr/libexec/rpcd/luci.netdata-dashboard +++ b/package/secubox/luci-app-netdata-dashboard/root/usr/libexec/rpcd/luci.netdata-dashboard @@ -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"