secubox-openwrt/luci-app-network-modes/htdocs/luci-static/resources/view/network-modes/multiwan.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

242 lines
8.3 KiB
JavaScript

'use strict';
'require view';
'require ui';
'require network-modes.api as api';
'require network-modes.helpers as helpers';
'require secubox-theme/theme as Theme';
Theme.init({ theme: 'dark', language: 'en' });
return view.extend({
title: _('Multi-WAN Mode'),
load: function() {
return api.getMultiWanConfig();
},
render: function(data) {
var cfg = data || {};
var policy = cfg.policy || {};
var links = cfg.links || {};
var candidates = cfg.wan_candidates || [];
policy.primary = links.primary || policy.primary;
policy.secondary = links.secondary || policy.secondary;
policy.mwan3_enabled = links.mwan3_enabled;
this.lastCandidates = candidates;
var container = E('div', { 'class': 'network-modes-dashboard multiwan-mode' }, [
E('link', { 'rel': 'stylesheet', 'href': L.resource('network-modes/dashboard.css') }),
helpers.createHero({
icon: '⚡',
title: _('Multi-WAN Gateway'),
subtitle: _('Balanced load sharing and automatic failover between two uplinks.'),
actions: [
E('button', {
'class': 'nm-btn nm-btn-primary',
'data-action': 'multiwan-save',
'type': 'button'
}, '💾 ' + _('Save Settings')),
E('button', {
'class': 'nm-btn',
'type': 'button',
'click': function() {
window.location.hash = '#admin/secubox/network/network-modes/wizard';
}
}, '🧭 ' + _('Mode Wizard'))
]
}),
this.renderLinkStatus(links, candidates),
this.renderPolicySection(policy),
this.renderCandidates(candidates)
]);
container.querySelectorAll('.nm-toggle-switch').forEach(function(toggle) {
toggle.addEventListener('click', function() {
this.classList.toggle('active');
});
});
this.bindActions(container);
return container;
},
renderLinkStatus: function(links, candidates) {
var primaryInfo = candidates.find(function(item) {
return item.name === links.primary;
}) || {};
var secondaryInfo = candidates.find(function(item) {
return item.name === links.secondary;
}) || {};
return helpers.createSection({
icon: '🛰️',
title: _('WAN Links Status'),
body: [
E('div', { 'class': 'nm-interfaces-grid' }, [
this.renderLinkCard(_('Primary Link'), links.primary, primaryInfo.up),
this.renderLinkCard(_('Secondary Link'), links.secondary, secondaryInfo.up),
E('div', { 'class': 'nm-interface-card' }, [
E('div', { 'class': 'nm-interface-icon' }, '⚙️'),
E('div', { 'class': 'nm-interface-info' }, [
E('div', { 'class': 'nm-interface-name' }, _('mwan3 integration')),
E('div', { 'class': 'nm-interface-ip' }, links.mwan3_enabled ? _('Enabled') : _('Disabled'))
]),
E('div', { 'class': 'nm-interface-status ' + (links.mwan3_enabled ? 'up' : 'down') })
])
])
]
});
},
renderLinkCard: function(label, iface, up) {
return E('div', { 'class': 'nm-interface-card' }, [
E('div', { 'class': 'nm-interface-icon' }, up ? '🟢' : '🔴'),
E('div', { 'class': 'nm-interface-info' }, [
E('div', { 'class': 'nm-interface-name' }, label),
E('div', { 'class': 'nm-interface-ip' }, iface || _('Not set'))
]),
E('div', { 'class': 'nm-interface-status ' + (up ? 'up' : 'down') })
]);
},
renderPolicySection: function(policy) {
var view = helpers.createSection({
icon: '🧠',
title: _('Failover & Balancing Policy'),
body: [
E('div', { 'class': 'nm-form-grid' }, [
E('div', { 'class': 'nm-form-group' }, [
E('label', { 'class': 'nm-form-label' }, _('Policy Profile')),
E('select', { 'class': 'nm-select', 'id': 'multiwan-policy' }, [
E('option', { 'value': 'balanced', 'selected': policy.profile === 'balanced' }, _('Balanced')),
E('option', { 'value': 'failover', 'selected': policy.profile === 'failover' }, _('Failover')),
E('option', { 'value': 'aggregate', 'selected': policy.profile === 'aggregate' }, _('Aggregate'))
])
]),
E('div', { 'class': 'nm-form-group' }, [
E('label', { 'class': 'nm-form-label' }, _('Health Check Host')),
E('input', {
'class': 'nm-input',
'id': 'multiwan-tracking-host',
'value': policy.tracking_host || '8.8.8.8'
})
]),
E('div', { 'class': 'nm-form-group' }, [
E('label', { 'class': 'nm-form-label' }, _('Check Interval (s)')),
E('input', {
'class': 'nm-input',
'type': 'number',
'id': 'multiwan-tracking-interval',
'value': policy.tracking_interval || 30,
'min': '5'
})
]),
E('div', { 'class': 'nm-form-group' }, [
E('label', { 'class': 'nm-form-label' }, _('Failover Hold Time (s)')),
E('input', {
'class': 'nm-input',
'type': 'number',
'id': 'multiwan-failover-hold',
'value': policy.failover_hold || 45,
'min': '10'
})
])
]),
E('div', { 'class': 'nm-form-grid' }, [
E('div', { 'class': 'nm-form-group' }, [
E('label', { 'class': 'nm-form-label' }, _('Primary Interface')),
this.createInterfaceSelect('multiwan-primary', policy.primary)
]),
E('div', { 'class': 'nm-form-group' }, [
E('label', { 'class': 'nm-form-label' }, _('Secondary Interface')),
this.createInterfaceSelect('multiwan-secondary', policy.secondary)
])
]),
E('div', { 'class': 'nm-toggle-list' }, [
this.renderToggle(_('Load balancing'), _('Use ECMP load sharing across WAN links'), 'multiwan-load-balance', policy.load_balance !== '0'),
this.renderToggle(_('Enable mwan3 integration'), _('Apply mwan3 policies and tracking'), 'multiwan-mwan3', policy.mwan3_enabled === true || policy.mwan3_enabled === 1 || policy.mwan3_enabled === '1')
])
]
});
return view;
},
renderCandidates: function(candidates) {
if (!candidates.length)
return helpers.createSection({
icon: '📋',
title: _('Available Interfaces'),
body: [E('p', { 'class': 'nm-text-muted' }, _('No interfaces detected'))]
});
return helpers.createSection({
icon: '📋',
title: _('Available Interfaces'),
body: [
E('div', { 'class': 'nm-interfaces-grid' },
candidates.map(function(iface) {
return E('div', { 'class': 'nm-interface-card' }, [
E('div', { 'class': 'nm-interface-icon' }, iface.up ? '🟢' : '⚫'),
E('div', { 'class': 'nm-interface-info' }, [
E('div', { 'class': 'nm-interface-name' }, iface.name),
E('div', { 'class': 'nm-interface-ip' }, iface.mac || '')
]),
E('div', { 'class': 'nm-interface-status ' + (iface.up ? 'up' : 'down') })
]);
}))
]
});
},
renderToggle: function(title, desc, id, active) {
return E('div', { 'class': 'nm-toggle' }, [
E('div', { 'class': 'nm-toggle-info' }, [
E('span', { 'class': 'nm-toggle-icon' }, '⚙️'),
E('div', {}, [
E('div', { 'class': 'nm-toggle-label' }, title),
E('div', { 'class': 'nm-toggle-desc' }, desc)
])
]),
E('div', {
'class': 'nm-toggle-switch' + (active ? ' active' : ''),
'id': id
})
]);
},
createInterfaceSelect: function(id, selected) {
var options = this.lastCandidates && this.lastCandidates.length ? this.lastCandidates : [{ name: 'eth1' }];
return E('select', { 'class': 'nm-select', 'id': id },
options.map(function(iface) {
return E('option', { 'value': iface.name, 'selected': iface.name === selected }, iface.name);
}));
},
bindActions: function(container) {
var saveBtn = container.querySelector('[data-action="multiwan-save"]');
if (saveBtn)
saveBtn.addEventListener('click', ui.createHandlerFn(this, 'saveMultiWanSettings', container));
},
saveMultiWanSettings: function(container) {
var payload = {
wan_primary: (container.querySelector('#multiwan-primary') || {}).value,
wan_secondary: (container.querySelector('#multiwan-secondary') || {}).value,
policy: (container.querySelector('#multiwan-policy') || {}).value,
tracking_host: (container.querySelector('#multiwan-tracking-host') || {}).value,
tracking_interval: (container.querySelector('#multiwan-tracking-interval') || {}).value,
failover_hold: (container.querySelector('#multiwan-failover-hold') || {}).value,
load_balance: helpers.isToggleActive(container.querySelector('#multiwan-load-balance')) ? 1 : 0,
use_mwan3: helpers.isToggleActive(container.querySelector('#multiwan-mwan3')) ? 1 : 0
};
return helpers.persistSettings('multiwan', payload);
},
lastCandidates: null,
handleSaveApply: null,
handleSave: null,
handleReset: null
});