secubox-openwrt/luci-app-network-modes/htdocs/luci-static/resources/view/network-modes/overview.js
CyberMind-FR a6477b8710 feat: Version 0.4.1 - Enhanced network modes and system improvements
Major Enhancements:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Network Modes Module:
- Added 3 new network modes:
  * Double NAT mode (doublenat.js) - Cascaded router configuration
  * Multi-WAN mode (multiwan.js) - Load balancing and failover
  * VPN Relay mode (vpnrelay.js) - VPN gateway configuration
- Enhanced existing modes:
  * Access Point improvements
  * Travel mode refinements
  * Router mode enhancements
  * Relay mode updates
  * Sniffer mode optimizations
- Updated wizard with new mode options
- Enhanced API with new mode support
- Improved dashboard CSS styling
- Updated helpers for new modes
- Extended RPCD backend functionality
- Updated menu structure for new modes
- Enhanced UCI configuration

System Hub Module:
- Added dedicated logs.css stylesheet
- Enhanced logs.js view with better styling
- Improved overview.css responsive design
- Enhanced services.css for better UX
- Updated overview.js with theme integration
- Improved services.js layout

SecuBox Dashboard:
- Enhanced dashboard.css with theme variables
- Improved dashboard.js responsiveness
- Better integration with global theme

Files Changed:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Network Modes (17 files):
  Modified: api.js, dashboard.css, helpers.js, menu, config, RPCD backend
  Modified Views: accesspoint, overview, relay, router, sniffer, travel, wizard
  New Views: doublenat, multiwan, vpnrelay

System Hub (6 files):
  New: logs.css
  Modified: overview.css, services.css, logs.js, overview.js, services.js

SecuBox (2 files):
  Modified: dashboard.css, dashboard.js

Total: 25 files changed (21 modified, 4 new)

Technical Improvements:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
- Global theme CSS variable usage
- Responsive design enhancements
- Improved error handling
- Better mode validation
- Enhanced user feedback
- Optimized CSS performance
- Improved accessibility

Network Mode Capabilities:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. Router Mode - Standard routing
2. Access Point Mode - WiFi AP with bridge
3. Relay Mode - WiFi repeater/extender
4. Travel Mode - Portable router configuration
5. Sniffer Mode - Network monitoring
6. Double NAT Mode - Cascaded NAT for network isolation (NEW)
7. Multi-WAN Mode - Multiple uplinks with load balancing (NEW)
8. VPN Relay Mode - VPN gateway and tunnel endpoint (NEW)

Breaking Changes:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
None - All changes are backward compatible

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-28 18:12:28 +01:00

380 lines
12 KiB
JavaScript

'use strict';
'require view';
'require dom';
'require ui';
'require network-modes.api as api';
'require secubox/help as Help';
'require secubox-theme/theme as Theme';
// Initialize global theme
Theme.init({ theme: 'dark', language: 'en' });
return view.extend({
title: _('Network Modes'),
load: function() {
return api.getAllData();
},
handleModeSwitch: function(mode) {
var self = this;
ui.showModal(_('Switch Mode'), [
E('p', {}, _('Are you sure you want to switch to ') + mode + _(' mode?')),
E('p', { 'class': 'nm-alert nm-alert-warning' }, [
E('span', { 'class': 'nm-alert-icon' }, '⚠️'),
E('div', {}, [
E('div', { 'class': 'nm-alert-title' }, _('Warning')),
E('div', { 'class': 'nm-alert-text' }, _('This will change network configuration. A backup will be created.'))
])
]),
E('div', { 'class': 'right' }, [
E('button', {
'class': 'btn',
'click': ui.hideModal
}, _('Cancel')),
' ',
E('button', {
'class': 'btn cbi-button-positive',
'click': function() {
ui.hideModal();
return api.applyMode(mode).then(function(result) {
if (result.success) {
ui.addNotification(null, E('p', {}, result.message), 'success');
window.location.reload();
} else {
ui.addNotification(null, E('p', {}, 'Error: ' + result.error), 'error');
}
});
}
}, _('Switch Mode'))
])
]);
},
render: function(data) {
var self = this;
var status = data.status || {};
var modesData = (data.modes || {}).modes || [];
var currentMode = status.current_mode || 'router';
// Build a full mode map using backend data + fallbacks
var baseOrder = ['router', 'doublenat', 'multiwan', 'vpnrelay', 'bridge', 'accesspoint', 'relay', 'travel', 'sniffer'];
var modeInfos = {};
// Prime with RPC payload so description/icon/features stay in sync
modesData.forEach(function(mode) {
var fallback = api.getModeInfo(mode.id || '');
modeInfos[mode.id] = Object.assign({}, fallback, {
id: mode.id,
name: mode.name || (fallback && fallback.name) || mode.id,
icon: mode.icon || (fallback && fallback.icon) || '🌐',
description: mode.description || (fallback && fallback.description) || '',
features: Array.isArray(mode.features) && mode.features.length
? mode.features
: (fallback && fallback.features) || []
});
});
// Ensure every known mode has a definition, even if RPC omitted it
baseOrder.concat(['sniffer']).forEach(function(mode) {
if (!modeInfos[mode]) {
modeInfos[mode] = api.getModeInfo(mode);
}
});
// Preserve RPC ordering but guarantee canonical fallback + sniffer tab
var modeOrder = modesData.map(function(mode) { return mode.id; });
baseOrder.forEach(function(mode) {
if (modeOrder.indexOf(mode) === -1)
modeOrder.push(mode);
});
if (modeOrder.indexOf('sniffer') === -1)
modeOrder.push('sniffer');
var currentModeInfo = modeInfos[currentMode] || api.getModeInfo(currentMode);
var view = E('div', { 'class': 'network-modes-dashboard' }, [
// Load global theme CSS
E('link', { 'rel': 'stylesheet', 'href': L.resource('secubox-theme/secubox-theme.css') }),
E('link', { 'rel': 'stylesheet', 'href': L.resource('secubox/help.css') }),
E('link', { 'rel': 'stylesheet', 'href': L.resource('network-modes/dashboard.css') }),
// Header
E('div', { 'class': 'nm-header' }, [
E('div', { 'class': 'nm-logo' }, [
E('div', { 'class': 'nm-logo-icon' }, '🌐'),
E('div', { 'class': 'nm-logo-text' }, ['Network ', E('span', {}, 'Configuration')])
]),
E('div', { 'class': 'nm-mode-badge ' + currentMode }, [
E('span', { 'class': 'nm-mode-dot' }),
currentModeInfo ? currentModeInfo.name : currentMode
]),
Help.createHelpButton('network-modes', 'header', {
icon: '📖',
label: _('Help')
})
]),
// Current Mode Display Card
E('div', { 'class': 'nm-current-mode-card' }, [
E('div', { 'class': 'nm-current-mode-header' }, [
E('div', { 'class': 'nm-current-mode-icon' }, currentModeInfo ? currentModeInfo.icon : '🌐'),
E('div', { 'class': 'nm-current-mode-info' }, [
E('div', { 'class': 'nm-current-mode-label' }, 'Current Network Mode'),
E('h2', { 'class': 'nm-current-mode-name' }, currentModeInfo ? currentModeInfo.name : currentMode)
])
]),
E('div', { 'class': 'nm-current-mode-description' },
currentModeInfo ? currentModeInfo.description : 'Unknown mode'),
E('div', { 'class': 'nm-current-mode-config' }, [
E('div', { 'class': 'nm-config-item' }, [
E('span', { 'class': 'nm-config-label' }, 'WAN IP:'),
E('span', { 'class': 'nm-config-value' }, status.wan_ip || 'N/A')
]),
E('div', { 'class': 'nm-config-item' }, [
E('span', { 'class': 'nm-config-label' }, 'LAN IP:'),
E('span', { 'class': 'nm-config-value' }, status.lan_ip || 'N/A')
]),
E('div', { 'class': 'nm-config-item' }, [
E('span', { 'class': 'nm-config-label' }, 'DHCP Server:'),
E('span', { 'class': 'nm-config-value' }, status.dhcp_enabled ? 'Enabled' : 'Disabled')
])
]),
E('button', {
'class': 'nm-change-mode-btn',
'click': function() {
window.location.hash = '#admin/secubox/network/network-modes/wizard';
}
}, '🔄 Change Mode')
]),
// Mode Comparison Table
E('div', { 'class': 'nm-comparison-card' }, [
E('h3', { 'class': 'nm-comparison-title' }, 'Mode Comparison Table'),
E('div', { 'class': 'nm-comparison-table-wrapper' }, [
E('table', { 'class': 'nm-comparison-table' }, [
E('thead', {}, [
E('tr', {}, [
E('th', {}, 'Feature')
].concat(baseOrder.map(function(modeId) {
var info = modeInfos[modeId] || api.getModeInfo(modeId);
return E('th', {
'class': currentMode === modeId ? 'active-mode' : ''
}, (info.icon || '') + ' ' + (info.name || modeId));
})))
]),
E('tbody', {}, (function() {
var comparisonRows = [
{
label: 'Use Case',
values: {
router: 'Home/Office',
doublenat: 'Behind ISP box',
multiwan: 'Dual uplinks',
vpnrelay: 'VPN gateway',
bridge: 'Layer 2 passthrough',
accesspoint: 'WiFi Hotspot',
relay: 'WiFi Extender',
travel: 'Hotel / Travel kit',
sniffer: 'Packet capture / TAP'
}
},
{
label: 'WAN Ports',
values: {
router: '1 port',
doublenat: 'WAN DHCP',
multiwan: '2 uplinks',
vpnrelay: 'VPN tunnel',
bridge: 'All bridged',
accesspoint: '1 uplink',
relay: 'WiFi uplink',
travel: 'WiFi or USB',
sniffer: 'Monitor source port'
}
},
{
label: 'LAN Ports',
values: {
router: 'Multiple',
doublenat: 'LAN + Guest',
multiwan: 'All ports',
vpnrelay: 'Policy-based',
bridge: 'All ports',
accesspoint: 'All ports',
relay: 'All ports',
travel: 'All ports',
sniffer: 'Mirror to capture'
}
},
{
label: 'WiFi Role',
values: {
router: 'Access Point',
doublenat: 'Router',
multiwan: 'Router',
vpnrelay: 'Router',
bridge: 'Optional AP',
accesspoint: 'AP only',
relay: 'Client + AP',
travel: 'Client + AP',
sniffer: 'Monitor mode'
}
},
{
label: 'DHCP Server',
values: {
router: 'Yes',
doublenat: 'Yes',
multiwan: 'Yes',
vpnrelay: 'Optional',
bridge: 'No',
accesspoint: 'No',
relay: 'Yes',
travel: 'Yes',
sniffer: 'No'
}
},
{
label: 'NAT',
values: {
router: 'Enabled',
doublenat: 'Double layer',
multiwan: 'Enabled',
vpnrelay: 'VPN NAT',
bridge: 'Disabled',
accesspoint: 'Disabled',
relay: 'Enabled',
travel: 'Enabled',
sniffer: 'Disabled'
}
}
];
return comparisonRows.map(function(row) {
return E('tr', {}, [
E('td', { 'class': 'feature-label' }, row.label)
].concat(baseOrder.map(function(modeId) {
return E('td', {
'class': currentMode === modeId ? 'active-mode' : ''
}, row.values[modeId] || '—');
})));
});
})())
])
])
]),
// Mode Selection Grid
E('div', { 'class': 'nm-modes-grid' },
modeOrder.map(function(modeId) {
var info = modeInfos[modeId];
if (!info)
return null;
var isActive = modeId === currentMode;
return E('div', {
'class': 'nm-mode-card ' + modeId + (isActive ? ' active' : ''),
'click': function() {
if (!isActive) {
self.handleModeSwitch(modeId);
}
}
}, [
isActive ? E('div', { 'class': 'nm-mode-active-indicator' }, 'Active') : '',
E('div', { 'class': 'nm-mode-header' }, [
E('div', { 'class': 'nm-mode-icon' }, info.icon),
E('div', { 'class': 'nm-mode-title' }, [
E('h3', {}, info.name),
E('p', {}, modeId.charAt(0).toUpperCase() + modeId.slice(1) + ' Mode')
])
]),
E('div', { 'class': 'nm-mode-description' }, info.description),
E('div', { 'class': 'nm-mode-features' },
(info.features || []).map(function(f) {
return E('span', { 'class': 'nm-mode-feature' }, [
E('span', { 'class': 'nm-mode-feature-icon' }, '✓'),
f
]);
})
)
]);
}).filter(function(card) { return !!card; })
),
// Interfaces Status
E('div', { 'class': 'nm-card' }, [
E('div', { 'class': 'nm-card-header' }, [
E('div', { 'class': 'nm-card-title' }, [
E('span', { 'class': 'nm-card-title-icon' }, '🔌'),
'Network Interfaces'
]),
E('div', { 'class': 'nm-card-badge' }, (status.interfaces || []).length + ' interfaces')
]),
E('div', { 'class': 'nm-card-body' }, [
E('div', { 'class': 'nm-interfaces-grid' },
(status.interfaces || []).map(function(iface) {
var icon = '🔌';
if (iface.name.startsWith('wlan') || iface.name.startsWith('wl')) icon = '📶';
else if (iface.name.startsWith('wg')) icon = '🔐';
else if (iface.name.startsWith('br')) icon = '🌉';
else if (iface.name.startsWith('eth')) icon = '🔗';
return E('div', { 'class': 'nm-interface-card' }, [
E('div', { 'class': 'nm-interface-icon' }, icon),
E('div', { 'class': 'nm-interface-info' }, [
E('div', { 'class': 'nm-interface-name' }, iface.name),
E('div', { 'class': 'nm-interface-ip' }, iface.ip || 'No IP')
]),
E('div', { 'class': 'nm-interface-status ' + iface.state })
]);
})
)
])
]),
// Services Status
E('div', { 'class': 'nm-card' }, [
E('div', { 'class': 'nm-card-header' }, [
E('div', { 'class': 'nm-card-title' }, [
E('span', { 'class': 'nm-card-title-icon' }, '🔧'),
'Services Status'
])
]),
E('div', { 'class': 'nm-card-body' }, [
E('div', { 'class': 'nm-interfaces-grid' },
[
{ name: 'Firewall', key: 'firewall', icon: '🛡️' },
{ name: 'DHCP/DNS', key: 'dnsmasq', icon: '📡' },
{ name: 'Netifyd', key: 'netifyd', icon: '🔍' },
{ name: 'Nginx', key: 'nginx', icon: '🌐' },
{ name: 'Squid', key: 'squid', icon: '🦑' }
].map(function(svc) {
var running = status.services && status.services[svc.key];
return E('div', { 'class': 'nm-interface-card' }, [
E('div', { 'class': 'nm-interface-icon' }, svc.icon),
E('div', { 'class': 'nm-interface-info' }, [
E('div', { 'class': 'nm-interface-name' }, svc.name),
E('div', { 'class': 'nm-interface-ip' }, running ? 'Running' : 'Stopped')
]),
E('div', { 'class': 'nm-interface-status ' + (running ? 'up' : 'down') })
]);
})
)
])
])
]);
// Include CSS
var cssLink = E('link', { 'rel': 'stylesheet', 'href': L.resource('network-modes/dashboard.css') });
document.head.appendChild(cssLink);
return view;
},
handleSaveApply: null,
handleSave: null,
handleReset: null
});