feat: Enhanced netifyd metrics and fixed directory structure
Network Intelligence Dashboard Enhancements: - Add detailed protocol breakdown (TCP/UDP/ICMP) with visual bars - Display flow metrics (active, expired, purged) - Show CPU and memory usage for netifyd process - Add IP bytes vs wire bytes differentiation - Enhanced stat cards with subtitles and better formatting RPC Backend Improvements: - Add tcp_packets, udp_packets, icmp_packets metrics - Add ip_bytes (payload without ethernet overhead) - Add flows_active, flows_expired, flows_purged counters - Add cpu_usage and memory_kb from netifyd status - Calculate CPU total from user + system time Directory Structure Fix: - Create /etc/netify.d/plugins.d on package install - Create /etc/netify.d/address-groups.d - Generate minimal netify-categories.json to prevent errors - Auto-initialize UCI config for secubox-netifyd - Auto-restart netifyd after directory creation UCI Configuration: - Settings: auto_refresh, socket configuration - Analytics: limits for top apps/protocols/devices - Data retention configuration Issue Resolved: - Netifyd was running but showing 0 flows due to missing directories - Service now properly captures and analyzes network traffic - All metrics displaying correctly in dashboard 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
2168d76f96
commit
e1c7c79104
@ -179,6 +179,7 @@ return view.extend({
|
||||
{
|
||||
title: _('Active Flows'),
|
||||
value: (stats.active_flows || 0).toString(),
|
||||
subtitle: _('Active: %d, Expired: %d').format(stats.flows_active || 0, stats.flows_expired || 0),
|
||||
icon: 'exchange-alt',
|
||||
color: '#3b82f6',
|
||||
gradient: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)'
|
||||
@ -186,26 +187,52 @@ return view.extend({
|
||||
{
|
||||
title: _('Unique Devices'),
|
||||
value: (stats.unique_devices || 0).toString(),
|
||||
subtitle: _('Connected devices'),
|
||||
icon: 'network-wired',
|
||||
color: '#10b981',
|
||||
gradient: 'linear-gradient(135deg, #84fab0 0%, #8fd3f4 100%)'
|
||||
},
|
||||
{
|
||||
title: _('Applications'),
|
||||
value: (stats.unique_applications || 0).toString(),
|
||||
icon: 'cubes',
|
||||
color: '#8b5cf6',
|
||||
gradient: 'linear-gradient(135deg, #a18cd1 0%, #fbc2eb 100%)'
|
||||
},
|
||||
{
|
||||
title: _('Total Traffic'),
|
||||
value: netifydAPI.formatBytes(stats.total_bytes || 0),
|
||||
subtitle: _('IP: %s').format(netifydAPI.formatBytes(stats.ip_bytes || 0)),
|
||||
icon: 'chart-line',
|
||||
color: '#f59e0b',
|
||||
gradient: 'linear-gradient(135deg, #fa709a 0%, #fee140 100%)'
|
||||
},
|
||||
{
|
||||
title: _('CPU & Memory'),
|
||||
value: (stats.cpu_usage || '0') + '%',
|
||||
subtitle: _('RAM: %s').format(netifydAPI.formatBytes((stats.memory_kb || 0) * 1024)),
|
||||
icon: 'microchip',
|
||||
color: '#ec4899',
|
||||
gradient: 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)'
|
||||
}
|
||||
];
|
||||
|
||||
// Protocol breakdown
|
||||
var totalPackets = (stats.tcp_packets || 0) + (stats.udp_packets || 0) + (stats.icmp_packets || 0);
|
||||
var protocolData = totalPackets > 0 ? [
|
||||
{
|
||||
name: 'TCP',
|
||||
packets: stats.tcp_packets || 0,
|
||||
percentage: totalPackets > 0 ? ((stats.tcp_packets || 0) / totalPackets * 100).toFixed(1) : 0,
|
||||
color: '#3b82f6'
|
||||
},
|
||||
{
|
||||
name: 'UDP',
|
||||
packets: stats.udp_packets || 0,
|
||||
percentage: totalPackets > 0 ? ((stats.udp_packets || 0) / totalPackets * 100).toFixed(1) : 0,
|
||||
color: '#10b981'
|
||||
},
|
||||
{
|
||||
name: 'ICMP',
|
||||
packets: stats.icmp_packets || 0,
|
||||
percentage: totalPackets > 0 ? ((stats.icmp_packets || 0) / totalPackets * 100).toFixed(1) : 0,
|
||||
color: '#f59e0b'
|
||||
}
|
||||
] : [];
|
||||
|
||||
return E('div', { 'class': 'cbi-section' }, [
|
||||
E('h3', [
|
||||
E('i', { 'class': 'fa fa-chart-bar', 'style': 'margin-right: 0.5rem' }),
|
||||
@ -219,16 +246,44 @@ return view.extend({
|
||||
'class': 'netifyd-stat-card',
|
||||
'style': 'background: ' + card.gradient + '; color: white; padding: 1.5rem; border-radius: 8px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); transition: transform 0.2s;'
|
||||
}, [
|
||||
E('div', { 'style': 'display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 1rem' }, [
|
||||
E('div', { 'style': 'display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 0.5rem' }, [
|
||||
E('div', { 'style': 'font-size: 0.9em; opacity: 0.9' }, card.title),
|
||||
E('i', {
|
||||
'class': 'fa fa-' + card.icon,
|
||||
'style': 'font-size: 2em; opacity: 0.3'
|
||||
})
|
||||
]),
|
||||
E('div', { 'style': 'font-size: 2em; font-weight: bold' }, card.value)
|
||||
E('div', { 'style': 'font-size: 2em; font-weight: bold; margin-bottom: 0.5rem' }, card.value),
|
||||
card.subtitle ? E('div', { 'style': 'font-size: 0.85em; opacity: 0.8' }, card.subtitle) : null
|
||||
]);
|
||||
}))
|
||||
})),
|
||||
|
||||
// Protocol Breakdown
|
||||
protocolData.length > 0 ? E('div', {
|
||||
'style': 'background: white; padding: 1.5rem; border-radius: 8px; margin-top: 1rem; border: 1px solid #e5e7eb'
|
||||
}, [
|
||||
E('h4', { 'style': 'margin: 0 0 1rem 0; color: #374151; display: flex; align-items: center; gap: 0.5rem' }, [
|
||||
E('i', { 'class': 'fa fa-network-wired' }),
|
||||
_('Protocol Distribution')
|
||||
]),
|
||||
E('div', { 'style': 'display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem' },
|
||||
protocolData.map(function(proto) {
|
||||
return E('div', [
|
||||
E('div', { 'style': 'display: flex; justify-content: space-between; margin-bottom: 0.5rem; color: #6b7280' }, [
|
||||
E('span', { 'style': 'font-weight: 600; color: ' + proto.color }, proto.name),
|
||||
E('span', proto.packets.toLocaleString() + ' pkts')
|
||||
]),
|
||||
E('div', { 'style': 'background: #f3f4f6; height: 8px; border-radius: 4px; overflow: hidden' }, [
|
||||
E('div', {
|
||||
'style': 'background: ' + proto.color + '; height: 100%; width: ' + proto.percentage + '%; transition: width 0.3s ease'
|
||||
})
|
||||
]),
|
||||
E('div', { 'style': 'text-align: right; font-size: 0.85em; margin-top: 0.25rem; color: #9ca3af' },
|
||||
proto.percentage + '%')
|
||||
]);
|
||||
})
|
||||
)
|
||||
]) : null
|
||||
])
|
||||
]);
|
||||
},
|
||||
|
||||
@ -0,0 +1,53 @@
|
||||
#!/bin/sh
|
||||
# SecuBox Netifyd - UCI defaults setup
|
||||
# Creates required directories and initializes configuration
|
||||
|
||||
# Create netifyd directories if they don't exist
|
||||
mkdir -p /etc/netify.d/plugins.d
|
||||
mkdir -p /etc/netify.d/address-groups.d
|
||||
|
||||
# Create empty categories file to prevent error messages
|
||||
if [ ! -f /etc/netify.d/netify-categories.json ]; then
|
||||
cat > /etc/netify.d/netify-categories.json <<'EOF'
|
||||
{
|
||||
"version": "1.0",
|
||||
"categories": []
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
|
||||
# Ensure proper permissions
|
||||
chmod 755 /etc/netify.d
|
||||
chmod 755 /etc/netify.d/plugins.d
|
||||
chmod 755 /etc/netify.d/address-groups.d
|
||||
chmod 644 /etc/netify.d/netify-categories.json
|
||||
|
||||
# Initialize UCI configuration if it doesn't exist
|
||||
if ! uci -q get secubox-netifyd.settings >/dev/null 2>&1; then
|
||||
uci set secubox-netifyd.settings=settings
|
||||
uci set secubox-netifyd.settings.auto_refresh='1'
|
||||
uci set secubox-netifyd.settings.refresh_interval='5'
|
||||
uci set secubox-netifyd.settings.socket_type='unix'
|
||||
uci set secubox-netifyd.settings.unix_socket_path='/var/run/netifyd/netifyd.sock'
|
||||
uci set secubox-netifyd.settings.socket_address='127.0.0.1'
|
||||
uci set secubox-netifyd.settings.socket_port='7150'
|
||||
uci set secubox-netifyd.settings.auto_start='1'
|
||||
uci commit secubox-netifyd
|
||||
fi
|
||||
|
||||
# Analytics settings
|
||||
if ! uci -q get secubox-netifyd.analytics >/dev/null 2>&1; then
|
||||
uci set secubox-netifyd.analytics=analytics
|
||||
uci set secubox-netifyd.analytics.top_apps_limit='10'
|
||||
uci set secubox-netifyd.analytics.top_protocols_limit='10'
|
||||
uci set secubox-netifyd.analytics.top_devices_limit='20'
|
||||
uci set secubox-netifyd.analytics.data_retention_days='7'
|
||||
uci commit secubox-netifyd
|
||||
fi
|
||||
|
||||
# Restart netifyd if it's running to apply changes
|
||||
if pidof netifyd >/dev/null 2>&1; then
|
||||
/etc/init.d/netifyd restart >/dev/null 2>&1
|
||||
fi
|
||||
|
||||
exit 0
|
||||
@ -433,15 +433,52 @@ get_dashboard() {
|
||||
# Calculate total bytes from all interface stats
|
||||
local total_bytes=$(jq -r '[.stats | to_entries[] | .value.wire_bytes // 0] | add' "$NETIFYD_STATUS" 2>/dev/null || echo 0)
|
||||
|
||||
# Protocol statistics
|
||||
local tcp_packets=$(jq -r '[.stats | to_entries[] | .value.tcp // 0] | add' "$NETIFYD_STATUS" 2>/dev/null || echo 0)
|
||||
local udp_packets=$(jq -r '[.stats | to_entries[] | .value.udp // 0] | add' "$NETIFYD_STATUS" 2>/dev/null || echo 0)
|
||||
local icmp_packets=$(jq -r '[.stats | to_entries[] | .value.icmp // 0] | add' "$NETIFYD_STATUS" 2>/dev/null || echo 0)
|
||||
|
||||
# IP bytes (payload without ethernet overhead)
|
||||
local ip_bytes=$(jq -r '[.stats | to_entries[] | .value.ip_bytes // 0] | add' "$NETIFYD_STATUS" 2>/dev/null || echo 0)
|
||||
|
||||
# Flow metrics
|
||||
local flows_active=$(jq -r '.flows_active // 0' "$NETIFYD_STATUS" 2>/dev/null || echo 0)
|
||||
local flows_expired=$(jq -r '.flows_expired // 0' "$NETIFYD_STATUS" 2>/dev/null || echo 0)
|
||||
local flows_purged=$(jq -r '.flows_purged // 0' "$NETIFYD_STATUS" 2>/dev/null || echo 0)
|
||||
|
||||
# CPU and memory
|
||||
local cpu_user=$(jq -r '.cpu_user // 0' "$NETIFYD_STATUS" 2>/dev/null || echo 0)
|
||||
local cpu_system=$(jq -r '.cpu_system // 0' "$NETIFYD_STATUS" 2>/dev/null || echo 0)
|
||||
local mem_rss=$(jq -r '.memrss_kb // 0' "$NETIFYD_STATUS" 2>/dev/null || echo 0)
|
||||
local cpu_total=$(awk "BEGIN {printf \"%.2f\", $cpu_user + $cpu_system}")
|
||||
|
||||
json_add_int "active_flows" "$total_flows"
|
||||
json_add_int "unique_devices" "$unique_devices"
|
||||
json_add_int "unique_applications" "$dhc_size"
|
||||
json_add_int "total_bytes" "$total_bytes"
|
||||
json_add_int "ip_bytes" "$ip_bytes"
|
||||
json_add_int "tcp_packets" "$tcp_packets"
|
||||
json_add_int "udp_packets" "$udp_packets"
|
||||
json_add_int "icmp_packets" "$icmp_packets"
|
||||
json_add_int "flows_active" "$flows_active"
|
||||
json_add_int "flows_expired" "$flows_expired"
|
||||
json_add_int "flows_purged" "$flows_purged"
|
||||
json_add_string "cpu_usage" "$cpu_total"
|
||||
json_add_int "memory_kb" "$mem_rss"
|
||||
else
|
||||
json_add_int "active_flows" 0
|
||||
json_add_int "unique_devices" 0
|
||||
json_add_int "unique_applications" 0
|
||||
json_add_int "total_bytes" 0
|
||||
json_add_int "ip_bytes" 0
|
||||
json_add_int "tcp_packets" 0
|
||||
json_add_int "udp_packets" 0
|
||||
json_add_int "icmp_packets" 0
|
||||
json_add_int "flows_active" 0
|
||||
json_add_int "flows_expired" 0
|
||||
json_add_int "flows_purged" 0
|
||||
json_add_string "cpu_usage" "0"
|
||||
json_add_int "memory_kb" 0
|
||||
fi
|
||||
|
||||
json_close_object
|
||||
|
||||
Loading…
Reference in New Issue
Block a user