refactor: Remove debug features from NetIfyd dashboard
Removed Features: - Debug mode toggle button - Debug panel and log display - Update indicator (count and time since last update) - Debug logging functions (debug, toggleDebug) - Debug state variables (debugMode, updateCount, errorCount, lastUpdate) - REFRESH-DEBUG.md documentation Preserved Features: ✅ Race condition fix (containers created before poll.add) ✅ Auto-refresh functionality (5-second polling) ✅ Data caching (latestDashboardData, latestTopApps, latestTopProtocols) ✅ Application aggregation function ✅ All core rendering functions ✅ Service control and statistics display Benefits: - Cleaner, production-ready code - Reduced code complexity (~120 lines removed) - Maintains all critical functionality - Better performance (no debug overhead) The dashboard now provides a streamlined interface focused on displaying network intelligence data without development debug features. 🤖 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
8d5e4275f6
commit
8ffd693a85
@ -1,344 +0,0 @@
|
||||
# Netifyd Dashboard - Refresh & Debug Analysis
|
||||
|
||||
## Refresh Problem Identified
|
||||
|
||||
### Root Cause
|
||||
The dashboard polling system had a **race condition** where:
|
||||
|
||||
1. `poll.add()` was called **before** containers were created
|
||||
2. When the first poll callback executed, `self.statusContainer`, `self.statsContainer`, etc. were `undefined`
|
||||
3. DOM updates failed silently because containers didn't exist yet
|
||||
4. No error handling or logging to detect the issue
|
||||
|
||||
### Code Flow Issue (Before Fix)
|
||||
|
||||
```javascript
|
||||
// WRONG ORDER - This was the problem:
|
||||
poll.add(function() {
|
||||
// Try to update containers
|
||||
if (self.statusContainer) { ... } // undefined!
|
||||
});
|
||||
|
||||
// Containers created AFTER poll setup
|
||||
self.statusContainer = E('div');
|
||||
self.statsContainer = E('div');
|
||||
```
|
||||
|
||||
### The Fix
|
||||
|
||||
```javascript
|
||||
// CORRECT ORDER - Fixed version:
|
||||
|
||||
// 1. Create containers FIRST
|
||||
self.statusContainer = E('div');
|
||||
self.statsContainer = E('div');
|
||||
self.appsContainer = E('div');
|
||||
self.protosContainer = E('div');
|
||||
|
||||
// 2. THEN set up polling
|
||||
poll.add(function() {
|
||||
// Now containers exist!
|
||||
if (self.statusContainer) {
|
||||
dom.content(self.statusContainer, newContent);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Debug Features Added
|
||||
|
||||
### 1. Debug Mode Toggle
|
||||
|
||||
**Button in Header**: "Enable Debug" / "Disable Debug"
|
||||
- Toggles debug logging to browser console
|
||||
- Shows/hides debug panel with live log entries
|
||||
- Changes button color (secondary → danger when enabled)
|
||||
|
||||
### 2. Debug Panel
|
||||
|
||||
**Visual Log Display**:
|
||||
- Shows last 20 log entries
|
||||
- Timestamp + message + JSON data
|
||||
- Auto-scrolling, newest entries at top
|
||||
- Monospaced font for readability
|
||||
- Hidden by default, shown when debug enabled
|
||||
|
||||
### 3. Console Logging
|
||||
|
||||
**Browser Console Output**:
|
||||
```
|
||||
[NetifydDashboard 2026-01-06T17:30:00.123Z] Loading dashboard data... {...}
|
||||
[NetifydDashboard 2026-01-06T17:30:00.456Z] Dashboard data loaded {...}
|
||||
[NetifydDashboard 2026-01-06T17:30:00.789Z] Rendering dashboard {...}
|
||||
[NetifydDashboard 2026-01-06T17:30:05.123Z] Polling for updates... (interval: 5s)
|
||||
[NetifydDashboard 2026-01-06T17:30:05.456Z] Poll update #1 {...}
|
||||
```
|
||||
|
||||
### 4. Update Indicator
|
||||
|
||||
**Live Status Display**:
|
||||
- Shows in header: "Updates: 5 | Last: 3s ago"
|
||||
- Updates every second
|
||||
- Counts total poll updates
|
||||
- Shows seconds since last update
|
||||
- Visual feedback that polling is working
|
||||
|
||||
### 5. Error Tracking
|
||||
|
||||
**Error Handling**:
|
||||
- Catches poll errors with try/catch
|
||||
- Logs errors to debug panel
|
||||
- Counts total errors: `self.errorCount`
|
||||
- Includes error message and stack trace
|
||||
- Doesn't break polling on error
|
||||
|
||||
## Debug Data Logged
|
||||
|
||||
### On Load
|
||||
```json
|
||||
{
|
||||
"dashboard": {
|
||||
"service": { "running": true, "uptime": 123 },
|
||||
"stats": { "active_flows": 25, "unique_devices": 6 }
|
||||
},
|
||||
"status": { "running": true, "version": "5.2.1" },
|
||||
"apps": 3,
|
||||
"protocols": 3
|
||||
}
|
||||
```
|
||||
|
||||
### On Poll Update
|
||||
```json
|
||||
{
|
||||
"flows": 25,
|
||||
"devices": 6,
|
||||
"apps": 3
|
||||
}
|
||||
```
|
||||
|
||||
### On Error
|
||||
```json
|
||||
{
|
||||
"error": "RPC call failed: Connection timeout",
|
||||
"stack": "Error: RPC call failed...\n at ..."
|
||||
}
|
||||
```
|
||||
|
||||
## Metrics Tracked
|
||||
|
||||
- `updateCount`: Total number of successful polls
|
||||
- `errorCount`: Total number of failed polls
|
||||
- `lastUpdate`: Timestamp of last successful update
|
||||
- `refreshInterval`: Polling interval in seconds (default: 5)
|
||||
|
||||
## How to Use Debug Mode
|
||||
|
||||
### Enable Debug
|
||||
|
||||
1. Open Netifyd Dashboard
|
||||
2. Click "Enable Debug" button in top-right
|
||||
3. Debug panel appears below description
|
||||
4. Console logs start appearing
|
||||
|
||||
### What to Look For
|
||||
|
||||
**Healthy Dashboard**:
|
||||
```
|
||||
Updates: 12 | Last: 2s ago
|
||||
```
|
||||
- Update count increasing every 5 seconds
|
||||
- Last update time stays under 5 seconds
|
||||
|
||||
**Problem Indicators**:
|
||||
```
|
||||
Updates: 0 | Last: never
|
||||
```
|
||||
- No updates happening
|
||||
- Check console for errors
|
||||
|
||||
```
|
||||
Updates: 5 | Last: 45s ago
|
||||
```
|
||||
- Polling stopped or stalled
|
||||
- Check for JavaScript errors in console
|
||||
|
||||
### Debug Log Entries
|
||||
|
||||
**Normal Operation**:
|
||||
```
|
||||
2026-01-06T17:30:00.123Z Loading dashboard data...
|
||||
2026-01-06T17:30:00.456Z Dashboard data loaded
|
||||
2026-01-06T17:30:00.789Z Rendering dashboard
|
||||
2026-01-06T17:30:05.123Z Polling for updates... (interval: 5s)
|
||||
2026-01-06T17:30:05.456Z Poll update #1
|
||||
2026-01-06T17:30:10.456Z Poll update #2
|
||||
```
|
||||
|
||||
**With Errors**:
|
||||
```
|
||||
2026-01-06T17:30:05.123Z Polling for updates... (interval: 5s)
|
||||
2026-01-06T17:30:05.456Z Poll error #1
|
||||
{
|
||||
"error": "RPC call failed",
|
||||
"stack": "Error: ..."
|
||||
}
|
||||
```
|
||||
|
||||
## Polling Configuration
|
||||
|
||||
### Current Settings
|
||||
|
||||
- **Interval**: 5 seconds (`refreshInterval: 5`)
|
||||
- **Data Sources**:
|
||||
- Dashboard stats (flows, devices, traffic)
|
||||
- Service status (running, uptime, version)
|
||||
- Top applications (DPI detected apps)
|
||||
- Top protocols (TCP/UDP/ICMP breakdown)
|
||||
|
||||
### Changing Poll Interval
|
||||
|
||||
Edit `dashboard.js`:
|
||||
```javascript
|
||||
return view.extend({
|
||||
refreshInterval: 10, // Change to 10 seconds
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
Valid range: 1-60 seconds (recommended: 5-10)
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Dashboard Not Updating
|
||||
|
||||
1. Enable debug mode
|
||||
2. Check update indicator
|
||||
3. Look for errors in console
|
||||
4. Check RPC backend is responding:
|
||||
```bash
|
||||
ssh root@router 'ubus call luci.secubox-netifyd get_dashboard'
|
||||
```
|
||||
|
||||
### High Update Count, No Data Changes
|
||||
|
||||
**Possible causes**:
|
||||
- Netifyd service running but no network traffic
|
||||
- Network interfaces not being monitored
|
||||
- Flow data not being captured
|
||||
|
||||
**Solutions**:
|
||||
- Check `netifyd -s` output on router
|
||||
- Verify interfaces are configured
|
||||
- Generate some network traffic (ping, wget, etc.)
|
||||
|
||||
### Errors in Debug Log
|
||||
|
||||
**Common errors**:
|
||||
- "RPC call failed" → Backend not responding
|
||||
- "Connection timeout" → Network issue
|
||||
- "Method not found" → RPC method missing
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
### Debug Mode Impact
|
||||
|
||||
- **Console logging**: Minimal (<1% CPU)
|
||||
- **Debug panel**: Small DOM updates (~0.5 KB per entry)
|
||||
- **Memory**: Max 20 entries × ~200 bytes = 4 KB
|
||||
|
||||
### Polling Impact
|
||||
|
||||
- **Network**: ~1 KB per poll (4 RPC calls)
|
||||
- **CPU**: <1% (JSON parsing + DOM updates)
|
||||
- **Recommended for**: Production use
|
||||
|
||||
### Reducing Impact
|
||||
|
||||
If needed, increase interval:
|
||||
```javascript
|
||||
refreshInterval: 10, // Less frequent updates
|
||||
```
|
||||
|
||||
## Code Architecture
|
||||
|
||||
### Component Structure
|
||||
|
||||
```
|
||||
dashboard.js
|
||||
├── State Management
|
||||
│ ├── debugMode (boolean)
|
||||
│ ├── updateCount (number)
|
||||
│ ├── errorCount (number)
|
||||
│ └── lastUpdate (Date)
|
||||
│
|
||||
├── Debug Functions
|
||||
│ ├── debug(message, data) - Log entry
|
||||
│ └── toggleDebug(ev) - Toggle on/off
|
||||
│
|
||||
├── Data Loading
|
||||
│ ├── load() - Initial load with debug
|
||||
│ └── poll callback - Auto-refresh with debug
|
||||
│
|
||||
└── Rendering
|
||||
├── render() - Main layout + debug panel
|
||||
├── renderServiceStatus() - Status card
|
||||
├── renderStatistics() - Metrics cards
|
||||
├── renderTopApplications() - App chart
|
||||
└── renderTopProtocols() - Protocol chart
|
||||
```
|
||||
|
||||
### Polling Flow
|
||||
|
||||
```
|
||||
[Page Load]
|
||||
↓
|
||||
load() - Fetch initial data
|
||||
↓
|
||||
render() - Create DOM + containers
|
||||
↓
|
||||
poll.add() - Register polling callback
|
||||
↓
|
||||
[Every 5 seconds]
|
||||
↓
|
||||
Poll callback executes
|
||||
↓
|
||||
Fetch fresh data (4 RPC calls)
|
||||
↓
|
||||
Update containers via dom.content()
|
||||
↓
|
||||
Update debug log & indicator
|
||||
↓
|
||||
[Repeat]
|
||||
```
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
### Potential Additions
|
||||
|
||||
1. **Export Debug Log** - Download as JSON/text file
|
||||
2. **Pause/Resume Polling** - Manual control
|
||||
3. **Poll Interval Slider** - UI control (1-60s)
|
||||
4. **Health Score** - Green/yellow/red indicator
|
||||
5. **Network Stats Graph** - Real-time chart
|
||||
6. **Alert Thresholds** - Notify on high traffic
|
||||
7. **Debug Filters** - Show only errors/warnings
|
||||
8. **Performance Metrics** - RPC response times
|
||||
|
||||
### API Enhancement Ideas
|
||||
|
||||
1. **Batch RPC Calls** - Single call for all data
|
||||
2. **Delta Updates** - Only send changed data
|
||||
3. **Compression** - Reduce network overhead
|
||||
4. **Caching** - Client-side data retention
|
||||
|
||||
## Summary
|
||||
|
||||
The refresh problem was caused by a race condition where polling started before DOM containers existed. The fix ensures containers are created first, then polling is initialized. Debug mode provides comprehensive visibility into the polling system, making future issues easy to diagnose.
|
||||
|
||||
**Key Improvements**:
|
||||
- ✅ Fixed container creation order
|
||||
- ✅ Added error handling to polls
|
||||
- ✅ Added visual debug panel
|
||||
- ✅ Added console logging
|
||||
- ✅ Added update indicator
|
||||
- ✅ Added error counting
|
||||
- ✅ Zero production impact when debug disabled
|
||||
@ -11,52 +11,10 @@ return view.extend({
|
||||
statsContainer: null,
|
||||
appsContainer: null,
|
||||
protosContainer: null,
|
||||
debugContainer: null,
|
||||
debugMode: false,
|
||||
lastUpdate: null,
|
||||
updateCount: 0,
|
||||
errorCount: 0,
|
||||
latestDashboardData: null,
|
||||
latestTopApps: null,
|
||||
latestTopProtocols: null,
|
||||
|
||||
debug: function(message, data) {
|
||||
if (!this.debugMode) return;
|
||||
|
||||
var timestamp = new Date().toISOString();
|
||||
console.log('[NetifydDashboard ' + timestamp + '] ' + message, data || '');
|
||||
|
||||
if (this.debugContainer) {
|
||||
var logEntry = E('div', {
|
||||
'style': 'padding: 0.25rem; border-bottom: 1px solid #e5e7eb; font-family: monospace; font-size: 0.85em'
|
||||
}, [
|
||||
E('span', { 'style': 'color: #6b7280' }, timestamp + ' '),
|
||||
E('span', { 'style': 'color: #059669; font-weight: 600' }, message),
|
||||
data ? E('pre', { 'style': 'margin: 0.25rem 0 0 0; color: #374151; font-size: 0.8em' },
|
||||
JSON.stringify(data, null, 2)) : null
|
||||
]);
|
||||
this.debugContainer.insertBefore(logEntry, this.debugContainer.firstChild);
|
||||
|
||||
// Keep only last 20 entries
|
||||
while (this.debugContainer.childNodes.length > 20) {
|
||||
this.debugContainer.removeChild(this.debugContainer.lastChild);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toggleDebug: function(ev) {
|
||||
this.debugMode = !this.debugMode;
|
||||
if (ev && ev.target) {
|
||||
ev.target.textContent = this.debugMode ? 'Disable Debug' : 'Enable Debug';
|
||||
ev.target.className = 'btn ' + (this.debugMode ? 'btn-danger' : 'btn-secondary');
|
||||
}
|
||||
this.debug('Debug mode ' + (this.debugMode ? 'enabled' : 'disabled'));
|
||||
|
||||
if (this.debugContainer) {
|
||||
this.debugContainer.style.display = this.debugMode ? 'block' : 'none';
|
||||
}
|
||||
},
|
||||
|
||||
aggregateApplications: function(apps) {
|
||||
var totals = {
|
||||
flows: 0,
|
||||
@ -79,27 +37,16 @@ return view.extend({
|
||||
},
|
||||
|
||||
load: function() {
|
||||
this.debug('Loading dashboard data...');
|
||||
return Promise.all([
|
||||
netifydAPI.getDashboard(),
|
||||
netifydAPI.getServiceStatus(),
|
||||
netifydAPI.getTopApplications(),
|
||||
netifydAPI.getTopProtocols()
|
||||
]).then(L.bind(function(result) {
|
||||
this.debug('Dashboard data loaded', {
|
||||
dashboard: result[0],
|
||||
status: result[1],
|
||||
apps: result[2] ? result[2].applications.length : 0,
|
||||
protocols: result[3] ? result[3].protocols.length : 0
|
||||
});
|
||||
this.latestDashboardData = result[0] || {};
|
||||
this.latestTopApps = result[2] || {};
|
||||
this.latestTopProtocols = result[3] || {};
|
||||
return result;
|
||||
}, this)).catch(L.bind(function(err) {
|
||||
this.debug('Error loading dashboard data', { error: err.message });
|
||||
this.errorCount++;
|
||||
throw err;
|
||||
]).then(L.bind(function(result) {
|
||||
this.latestDashboardData = result[0] || {};
|
||||
this.latestTopApps = result[2] || {};
|
||||
this.latestTopProtocols = result[3] || {};
|
||||
return result;
|
||||
}, this));
|
||||
},
|
||||
|
||||
@ -595,41 +542,22 @@ return view.extend({
|
||||
this.latestTopApps = topApps;
|
||||
this.latestTopProtocols = topProtos;
|
||||
|
||||
// Store container references
|
||||
var self = this;
|
||||
|
||||
this.debug('Rendering dashboard', { dashboard: dashboard, status: status });
|
||||
|
||||
// Create containers first
|
||||
self.statusContainer = E('div');
|
||||
self.statsContainer = E('div');
|
||||
self.appsContainer = E('div');
|
||||
self.protosContainer = E('div');
|
||||
|
||||
// Debug panel (hidden by default)
|
||||
self.debugContainer = E('div', {
|
||||
'class': 'cbi-section',
|
||||
'style': 'display: none; max-height: 400px; overflow-y: auto; background: #f9fafb; border: 1px solid #e5e7eb; border-radius: 0.5rem; padding: 1rem; margin-top: 1rem'
|
||||
});
|
||||
|
||||
// Set up polling for real-time updates AFTER containers are created
|
||||
poll.add(L.bind(function() {
|
||||
self.debug('Polling for updates... (interval: ' + self.refreshInterval + 's)');
|
||||
return Promise.all([
|
||||
netifydAPI.getDashboard(),
|
||||
netifydAPI.getServiceStatus(),
|
||||
netifydAPI.getTopApplications(),
|
||||
netifydAPI.getTopProtocols()
|
||||
]).then(L.bind(function(result) {
|
||||
self.updateCount++;
|
||||
self.lastUpdate = new Date();
|
||||
|
||||
self.debug('Poll update #' + self.updateCount, {
|
||||
flows: result[0] ? result[0].stats.active_flows : 0,
|
||||
devices: result[0] ? result[0].stats.unique_devices : 0,
|
||||
apps: result[2] ? result[2].applications.length : 0
|
||||
});
|
||||
|
||||
// Update containers if they exist
|
||||
if (self.statusContainer && result[1]) {
|
||||
dom.content(self.statusContainer, self.renderServiceStatus(result[1]));
|
||||
@ -644,45 +572,18 @@ return view.extend({
|
||||
dom.content(self.protosContainer, self.renderTopProtocols(result[3]));
|
||||
}
|
||||
}, this)).catch(L.bind(function(err) {
|
||||
self.errorCount++;
|
||||
self.debug('Poll error #' + self.errorCount, { error: err.message, stack: err.stack });
|
||||
console.error('Netifyd dashboard poll error:', err);
|
||||
}, this));
|
||||
}, this), this.refreshInterval);
|
||||
|
||||
var pageContent = E('div', { 'class': 'cbi-map' }, [
|
||||
E('div', { 'style': 'display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem' }, [
|
||||
E('h2', { 'name': 'content', 'style': 'margin: 0' }, [
|
||||
E('i', { 'class': 'fa fa-chart-pie', 'style': 'margin-right: 0.5rem' }),
|
||||
_('Network Intelligence Dashboard')
|
||||
]),
|
||||
E('div', { 'style': 'display: flex; gap: 0.5rem' }, [
|
||||
E('button', {
|
||||
'class': 'btn btn-secondary',
|
||||
'click': ui.createHandlerFn(this, 'toggleDebug')
|
||||
}, _('Enable Debug')),
|
||||
E('span', {
|
||||
'id': 'netifyd-update-indicator',
|
||||
'style': 'padding: 0.5rem 1rem; background: #f3f4f6; border-radius: 0.5rem; font-size: 0.85em; color: #6b7280'
|
||||
}, [
|
||||
E('i', { 'class': 'fa fa-clock' }),
|
||||
' ',
|
||||
E('span', {}, _('Updates: 0'))
|
||||
])
|
||||
])
|
||||
E('h2', { 'name': 'content' }, [
|
||||
E('i', { 'class': 'fa fa-chart-pie', 'style': 'margin-right: 0.5rem' }),
|
||||
_('Network Intelligence Dashboard')
|
||||
]),
|
||||
E('div', { 'class': 'cbi-map-descr' },
|
||||
_('Real-time deep packet inspection, application detection, and network analytics powered by Netifyd DPI engine')),
|
||||
|
||||
// Debug panel
|
||||
E('div', {}, [
|
||||
E('h3', { 'style': 'margin: 1rem 0 0.5rem 0; color: #374151; display: ' + (self.debugMode ? 'block' : 'none') }, [
|
||||
E('i', { 'class': 'fa fa-bug', 'style': 'margin-right: 0.5rem' }),
|
||||
_('Debug Log')
|
||||
]),
|
||||
self.debugContainer
|
||||
]),
|
||||
|
||||
// Service Status
|
||||
self.statusContainer,
|
||||
|
||||
@ -705,21 +606,6 @@ return view.extend({
|
||||
dom.content(self.appsContainer, self.renderTopApplications(topApps));
|
||||
dom.content(self.protosContainer, self.renderTopProtocols(topProtos));
|
||||
|
||||
// Update indicator with polling
|
||||
var updateIndicator = function() {
|
||||
var indicator = document.getElementById('netifyd-update-indicator');
|
||||
if (indicator && self.lastUpdate) {
|
||||
var elapsed = Math.floor((new Date() - self.lastUpdate) / 1000);
|
||||
var span = indicator.querySelector('span');
|
||||
if (span) {
|
||||
span.textContent = _('Updates: %d | Last: %ds ago').format(self.updateCount, elapsed);
|
||||
}
|
||||
}
|
||||
};
|
||||
setInterval(updateIndicator, 1000);
|
||||
|
||||
this.debug('Dashboard rendered successfully');
|
||||
|
||||
return pageContent;
|
||||
},
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user