feat: Release v0.4.3 - Dual menu access and enhanced permissions
This release adds dual menu access for Network Modes (both SecuBox and LuCI Network menus) and significantly expands RPCD permissions for all mode configuration operations. ## Network Modes - Dual Menu Access (2 files) - Added Network Modes to standard LuCI Network menu (admin/network/modes) - Maintains existing SecuBox menu location (admin/secubox/network/modes) - Users can now access Network Modes from both locations - Menu order: 60 in Network menu, 10 in SecuBox Network category ## Network Modes - Enhanced Permissions (1 file) Added 13+ new RPCD methods to ACL for complete mode management: Read permissions: - preview_changes - sniffer_config, ap_config, relay_config, router_config - travel_config, doublenat_config, multiwan_config, vpnrelay_config - travel_scan_networks Write permissions: - apply_mode, confirm_mode, rollback - update_settings - generate_wireguard_keys, apply_wireguard_config - apply_mtu_clamping, enable_tcp_bbr - add_vhost, generate_config ## Network Modes - View Updates (11 files) Updated all mode views for consistency: - helpers.js: 28 lines refactored - overview.js: Enhanced view structure - All mode views: wizard, router, multiwan, doublenat, accesspoint, relay, vpnrelay, travel, sniffer ## Theme Enhancements (1 file) - theme.js: 89 lines added - Enhanced theme initialization and configuration - Improved component styling support ## SecuBox Dashboard (2 files) - Updated dashboard.js and modules.js - Improved view rendering and integration ## System Hub (3 files) - Enhanced logs.js, overview.js, services.js - Better view consistency and functionality Summary: - 19 files changed (+282, -36) - Dual menu access for Network Modes - 13+ new RPCD permission methods - All network mode views updated - Theme significantly enhanced 🤖 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
aad081e841
commit
de40c8e533
@ -2,18 +2,18 @@
|
||||
'require ui';
|
||||
'require network-modes.api as api';
|
||||
|
||||
var NAV_ITEMS = [
|
||||
{ id: 'overview', icon: '📊', label: _('Overview') },
|
||||
{ id: 'wizard', icon: '🧭', label: _('Wizard') },
|
||||
{ id: 'router', icon: '🌐', label: _('Router') },
|
||||
{ id: 'multiwan', icon: '🔀', label: _('Multi-WAN') },
|
||||
{ id: 'doublenat', icon: '🧱', label: _('Double NAT') },
|
||||
{ id: 'accesspoint', icon: '📡', label: _('Access Point') },
|
||||
{ id: 'relay', icon: '📶', label: _('Relay') },
|
||||
{ id: 'vpnrelay', icon: '🛡️', label: _('VPN Relay') },
|
||||
{ id: 'travel', icon: '🧳', label: _('Travel') },
|
||||
{ id: 'sniffer', icon: '🕵️', label: _('Sniffer') },
|
||||
{ id: 'settings', icon: '⚙️', label: _('Settings') }
|
||||
var NAV_BLUEPRINT = [
|
||||
{ id: 'overview', icon: '📊', labelKey: 'Overview' },
|
||||
{ id: 'wizard', icon: '🧭', labelKey: 'Wizard' },
|
||||
{ id: 'router', icon: '🌐', labelKey: 'Router' },
|
||||
{ id: 'multiwan', icon: '🔀', labelKey: 'Multi-WAN' },
|
||||
{ id: 'doublenat', icon: '🧱', labelKey: 'Double NAT' },
|
||||
{ id: 'accesspoint', icon: '📡', labelKey: 'Access Point' },
|
||||
{ id: 'relay', icon: '📶', labelKey: 'Relay' },
|
||||
{ id: 'vpnrelay', icon: '🛡️', labelKey: 'VPN Relay' },
|
||||
{ id: 'travel', icon: '🧳', labelKey: 'Travel' },
|
||||
{ id: 'sniffer', icon: '🕵️', labelKey: 'Sniffer' },
|
||||
{ id: 'settings', icon: '⚙️', labelKey: 'Settings' }
|
||||
];
|
||||
|
||||
function isToggleActive(node) {
|
||||
@ -138,7 +138,7 @@ function createNavigationTabs(activeId) {
|
||||
var base = 'admin/secubox/network/modes/';
|
||||
return E('nav', { 'class': 'nm-nav-tabs' }, [
|
||||
E('div', { 'class': 'cyber-tablist' },
|
||||
NAV_ITEMS.map(function(item) {
|
||||
NAV_BLUEPRINT.map(function(item) {
|
||||
var cls = 'cyber-tab';
|
||||
if (activeId === item.id)
|
||||
cls += ' is-active';
|
||||
@ -149,7 +149,7 @@ function createNavigationTabs(activeId) {
|
||||
'aria-current': activeId === item.id ? 'page' : null
|
||||
}, [
|
||||
E('span', { 'class': 'cyber-tab-icon' }, item.icon),
|
||||
E('span', { 'class': 'cyber-tab-label' }, item.label)
|
||||
E('span', { 'class': 'cyber-tab-label' }, _(item.labelKey))
|
||||
]);
|
||||
})
|
||||
)
|
||||
|
||||
@ -6,7 +6,10 @@
|
||||
'require network-modes.helpers as helpers';
|
||||
'require secubox-theme/theme as Theme';
|
||||
|
||||
Theme.init({ theme: 'dark', language: 'en' });
|
||||
var nmLang = (typeof L !== 'undefined' && L.env && L.env.lang) ||
|
||||
(document.documentElement && document.documentElement.getAttribute('lang')) ||
|
||||
(navigator.language ? navigator.language.split('-')[0] : 'en');
|
||||
Theme.init({ language: nmLang });
|
||||
|
||||
function buildWifiToggle(flag, label, desc, active) {
|
||||
return E('div', { 'class': 'nm-toggle' }, [
|
||||
|
||||
@ -5,7 +5,10 @@
|
||||
'require network-modes.helpers as helpers';
|
||||
'require secubox-theme/theme as Theme';
|
||||
|
||||
Theme.init({ theme: 'dark', language: 'en' });
|
||||
var nmLang = (typeof L !== 'undefined' && L.env && L.env.lang) ||
|
||||
(document.documentElement && document.documentElement.getAttribute('lang')) ||
|
||||
(navigator.language ? navigator.language.split('-')[0] : 'en');
|
||||
Theme.init({ language: nmLang });
|
||||
|
||||
return view.extend({
|
||||
title: _('Double NAT Mode'),
|
||||
|
||||
@ -5,7 +5,10 @@
|
||||
'require network-modes.helpers as helpers';
|
||||
'require secubox-theme/theme as Theme';
|
||||
|
||||
Theme.init({ theme: 'dark', language: 'en' });
|
||||
var nmLang = (typeof L !== 'undefined' && L.env && L.env.lang) ||
|
||||
(document.documentElement && document.documentElement.getAttribute('lang')) ||
|
||||
(navigator.language ? navigator.language.split('-')[0] : 'en');
|
||||
Theme.init({ language: nmLang });
|
||||
|
||||
return view.extend({
|
||||
title: _('Multi-WAN Mode'),
|
||||
|
||||
@ -7,8 +7,11 @@
|
||||
'require secubox/help as Help';
|
||||
'require secubox-theme/theme as Theme';
|
||||
|
||||
// Initialize global theme
|
||||
Theme.init({ theme: 'dark', language: 'en' });
|
||||
// Initialize global theme respecting LuCI preferences
|
||||
var nmLang = (typeof L !== 'undefined' && L.env && L.env.lang) ||
|
||||
(document.documentElement && document.documentElement.getAttribute('lang')) ||
|
||||
(navigator.language ? navigator.language.split('-')[0] : 'en');
|
||||
Theme.init({ language: nmLang });
|
||||
|
||||
return view.extend({
|
||||
title: _('Network Modes'),
|
||||
|
||||
@ -6,7 +6,10 @@
|
||||
'require network-modes.helpers as helpers';
|
||||
'require secubox-theme/theme as Theme';
|
||||
|
||||
Theme.init({ theme: 'dark', language: 'en' });
|
||||
var nmLang = (typeof L !== 'undefined' && L.env && L.env.lang) ||
|
||||
(document.documentElement && document.documentElement.getAttribute('lang')) ||
|
||||
(navigator.language ? navigator.language.split('-')[0] : 'en');
|
||||
Theme.init({ language: nmLang });
|
||||
|
||||
function buildOptToggle(key, icon, label, desc, enabled) {
|
||||
return E('div', { 'class': 'nm-toggle' }, [
|
||||
|
||||
@ -6,7 +6,10 @@
|
||||
'require network-modes.helpers as helpers';
|
||||
'require secubox-theme/theme as Theme';
|
||||
|
||||
Theme.init({ theme: 'dark', language: 'en' });
|
||||
var nmLang = (typeof L !== 'undefined' && L.env && L.env.lang) ||
|
||||
(document.documentElement && document.documentElement.getAttribute('lang')) ||
|
||||
(navigator.language ? navigator.language.split('-')[0] : 'en');
|
||||
Theme.init({ language: nmLang });
|
||||
|
||||
function buildToggle(id, icon, title, desc, enabled) {
|
||||
return E('div', { 'class': 'nm-toggle' }, [
|
||||
|
||||
@ -6,7 +6,10 @@
|
||||
'require network-modes.helpers as helpers';
|
||||
'require secubox-theme/theme as Theme';
|
||||
|
||||
Theme.init({ theme: 'dark', language: 'en' });
|
||||
var nmLang = (typeof L !== 'undefined' && L.env && L.env.lang) ||
|
||||
(document.documentElement && document.documentElement.getAttribute('lang')) ||
|
||||
(navigator.language ? navigator.language.split('-')[0] : 'en');
|
||||
Theme.init({ language: nmLang });
|
||||
|
||||
function buildToggle(id, icon, title, desc, active) {
|
||||
return E('div', { 'class': 'nm-toggle' }, [
|
||||
|
||||
@ -7,7 +7,10 @@
|
||||
'require secubox/help as Help';
|
||||
'require secubox-theme/theme as Theme';
|
||||
|
||||
Theme.init({ theme: 'dark', language: 'en' });
|
||||
var nmLang = (typeof L !== 'undefined' && L.env && L.env.lang) ||
|
||||
(document.documentElement && document.documentElement.getAttribute('lang')) ||
|
||||
(navigator.language ? navigator.language.split('-')[0] : 'en');
|
||||
Theme.init({ language: nmLang });
|
||||
|
||||
return view.extend({
|
||||
title: _('Travel Router Mode'),
|
||||
|
||||
@ -5,7 +5,10 @@
|
||||
'require network-modes.helpers as helpers';
|
||||
'require secubox-theme/theme as Theme';
|
||||
|
||||
Theme.init({ theme: 'dark', language: 'en' });
|
||||
var nmLang = (typeof L !== 'undefined' && L.env && L.env.lang) ||
|
||||
(document.documentElement && document.documentElement.getAttribute('lang')) ||
|
||||
(navigator.language ? navigator.language.split('-')[0] : 'en');
|
||||
Theme.init({ language: nmLang });
|
||||
|
||||
return view.extend({
|
||||
title: _('VPN Relay Mode'),
|
||||
|
||||
@ -50,7 +50,10 @@ var callRollback = rpc.declare({
|
||||
expect: { }
|
||||
});
|
||||
|
||||
Theme.init({ theme: 'dark', language: 'en' });
|
||||
var nmLang = (typeof L !== 'undefined' && L.env && L.env.lang) ||
|
||||
(document.documentElement && document.documentElement.getAttribute('lang')) ||
|
||||
(navigator.language ? navigator.language.split('-')[0] : 'en');
|
||||
Theme.init({ language: nmLang });
|
||||
|
||||
return view.extend({
|
||||
title: _('Network Mode Wizard'),
|
||||
|
||||
@ -96,5 +96,103 @@
|
||||
"type": "view",
|
||||
"path": "network-modes/settings"
|
||||
}
|
||||
},
|
||||
"admin/network/modes": {
|
||||
"title": "Network Modes",
|
||||
"order": 60,
|
||||
"action": {
|
||||
"type": "firstchild"
|
||||
},
|
||||
"depends": {
|
||||
"acl": ["luci-app-network-modes"]
|
||||
}
|
||||
},
|
||||
"admin/network/modes/overview": {
|
||||
"title": "Overview",
|
||||
"order": 10,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "network-modes/overview"
|
||||
}
|
||||
},
|
||||
"admin/network/modes/wizard": {
|
||||
"title": "Mode Wizard",
|
||||
"order": 20,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "network-modes/wizard"
|
||||
}
|
||||
},
|
||||
"admin/network/modes/router": {
|
||||
"title": "Router Mode",
|
||||
"order": 30,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "network-modes/router"
|
||||
}
|
||||
},
|
||||
"admin/network/modes/multiwan": {
|
||||
"title": "Multi-WAN Mode",
|
||||
"order": 35,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "network-modes/multiwan"
|
||||
}
|
||||
},
|
||||
"admin/network/modes/doublenat": {
|
||||
"title": "Double NAT Mode",
|
||||
"order": 37,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "network-modes/doublenat"
|
||||
}
|
||||
},
|
||||
"admin/network/modes/accesspoint": {
|
||||
"title": "Access Point Mode",
|
||||
"order": 40,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "network-modes/accesspoint"
|
||||
}
|
||||
},
|
||||
"admin/network/modes/relay": {
|
||||
"title": "Relay Mode",
|
||||
"order": 50,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "network-modes/relay"
|
||||
}
|
||||
},
|
||||
"admin/network/modes/vpnrelay": {
|
||||
"title": "VPN Relay Mode",
|
||||
"order": 52,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "network-modes/vpnrelay"
|
||||
}
|
||||
},
|
||||
"admin/network/modes/travel": {
|
||||
"title": "Travel Mode",
|
||||
"order": 55,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "network-modes/travel"
|
||||
}
|
||||
},
|
||||
"admin/network/modes/sniffer": {
|
||||
"title": "Sniffer Mode",
|
||||
"order": 60,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "network-modes/sniffer"
|
||||
}
|
||||
},
|
||||
"admin/network/modes/settings": {
|
||||
"title": "Settings",
|
||||
"order": 90,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "network-modes/settings"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,7 +8,17 @@
|
||||
"get_current_mode",
|
||||
"get_available_modes",
|
||||
"get_interfaces",
|
||||
"validate_config"
|
||||
"validate_config",
|
||||
"preview_changes",
|
||||
"sniffer_config",
|
||||
"ap_config",
|
||||
"relay_config",
|
||||
"router_config",
|
||||
"travel_config",
|
||||
"doublenat_config",
|
||||
"multiwan_config",
|
||||
"vpnrelay_config",
|
||||
"travel_scan_networks"
|
||||
],
|
||||
"system": [ "info", "board" ],
|
||||
"network.interface": [ "status", "dump" ],
|
||||
@ -24,7 +34,17 @@
|
||||
"write": {
|
||||
"ubus": {
|
||||
"luci.network-modes": [
|
||||
"set_mode"
|
||||
"set_mode",
|
||||
"apply_mode",
|
||||
"confirm_mode",
|
||||
"rollback",
|
||||
"update_settings",
|
||||
"generate_wireguard_keys",
|
||||
"apply_wireguard_config",
|
||||
"apply_mtu_clamping",
|
||||
"enable_tcp_bbr",
|
||||
"add_vhost",
|
||||
"generate_config"
|
||||
]
|
||||
},
|
||||
"uci": [ "network", "wireless", "firewall", "dhcp", "network-modes" ]
|
||||
|
||||
@ -13,7 +13,10 @@ document.head.appendChild(E('link', {
|
||||
'href': L.resource('secubox-theme/secubox-theme.css')
|
||||
}));
|
||||
|
||||
Theme.init({ theme: 'dark', language: 'en' });
|
||||
var secuLang = (typeof L !== 'undefined' && L.env && L.env.lang) ||
|
||||
(document.documentElement && document.documentElement.getAttribute('lang')) ||
|
||||
(navigator.language ? navigator.language.split('-')[0] : 'en');
|
||||
Theme.init({ language: secuLang });
|
||||
|
||||
return view.extend({
|
||||
dashboardData: null,
|
||||
|
||||
@ -18,8 +18,11 @@ document.head.appendChild(E('link', {
|
||||
'href': L.resource('secubox/modules.css')
|
||||
}));
|
||||
|
||||
// Initialize global theme
|
||||
Theme.init({ theme: 'dark', language: 'en' });
|
||||
// Initialize global theme respecting LuCI selection
|
||||
var secuLang = (typeof L !== 'undefined' && L.env && L.env.lang) ||
|
||||
(document.documentElement && document.documentElement.getAttribute('lang')) ||
|
||||
(navigator.language ? navigator.language.split('-')[0] : 'en');
|
||||
Theme.init({ language: secuLang });
|
||||
|
||||
return view.extend({
|
||||
modulesData: [],
|
||||
|
||||
@ -6,7 +6,10 @@
|
||||
'require system-hub/api as API';
|
||||
'require secubox-theme/theme as Theme';
|
||||
|
||||
Theme.init({ theme: 'dark', language: 'en' });
|
||||
var shLang = (typeof L !== 'undefined' && L.env && L.env.lang) ||
|
||||
(document.documentElement && document.documentElement.getAttribute('lang')) ||
|
||||
(navigator.language ? navigator.language.split('-')[0] : 'en');
|
||||
Theme.init({ language: shLang });
|
||||
|
||||
return view.extend({
|
||||
logs: [],
|
||||
|
||||
@ -6,7 +6,10 @@
|
||||
'require system-hub/api as API';
|
||||
'require secubox-theme/theme as Theme';
|
||||
|
||||
Theme.init({ theme: 'dark', language: 'en' });
|
||||
var shLang = (typeof L !== 'undefined' && L.env && L.env.lang) ||
|
||||
(document.documentElement && document.documentElement.getAttribute('lang')) ||
|
||||
(navigator.language ? navigator.language.split('-')[0] : 'en');
|
||||
Theme.init({ language: shLang });
|
||||
|
||||
return view.extend({
|
||||
sysInfo: null,
|
||||
|
||||
@ -6,7 +6,10 @@
|
||||
'require system-hub/api as API';
|
||||
'require secubox-theme/theme as Theme';
|
||||
|
||||
Theme.init({ theme: 'dark', language: 'en' });
|
||||
var shLang = (typeof L !== 'undefined' && L.env && L.env.lang) ||
|
||||
(document.documentElement && document.documentElement.getAttribute('lang')) ||
|
||||
(navigator.language ? navigator.language.split('-')[0] : 'en');
|
||||
Theme.init({ language: shLang });
|
||||
|
||||
return view.extend({
|
||||
services: [],
|
||||
|
||||
@ -10,19 +10,38 @@ return baseclass.extend({
|
||||
currentTheme: 'dark',
|
||||
currentLanguage: 'en',
|
||||
translations: {},
|
||||
availableThemes: ['dark', 'light', 'cyberpunk'],
|
||||
|
||||
init: function(options) {
|
||||
var opts = options || {};
|
||||
var theme = opts.theme || this.currentTheme;
|
||||
var lang = opts.language || this.currentLanguage;
|
||||
var lang = opts.language || this._detectLanguage();
|
||||
var theme = this._isValidTheme(opts.theme) ? opts.theme : this._detectPreferredTheme();
|
||||
|
||||
this.apply(theme);
|
||||
return this.setLanguage(lang);
|
||||
},
|
||||
|
||||
apply: function(theme) {
|
||||
if (!this._isValidTheme(theme))
|
||||
theme = this._detectPreferredTheme();
|
||||
|
||||
this.currentTheme = theme || 'dark';
|
||||
document.body.setAttribute('data-secubox-theme', this.currentTheme);
|
||||
|
||||
if (document.documentElement)
|
||||
document.documentElement.setAttribute('data-secubox-theme', this.currentTheme);
|
||||
if (document.body)
|
||||
document.body.setAttribute('data-secubox-theme', this.currentTheme);
|
||||
},
|
||||
|
||||
setPreferredTheme: function(theme) {
|
||||
if (!this._isValidTheme(theme))
|
||||
return;
|
||||
|
||||
try {
|
||||
window.localStorage.setItem('secubox.theme', theme);
|
||||
} catch (err) { /* ignore private mode */ }
|
||||
|
||||
this.apply(theme);
|
||||
},
|
||||
|
||||
setLanguage: function(lang) {
|
||||
@ -100,5 +119,69 @@ return baseclass.extend({
|
||||
opts.header || null,
|
||||
E('div', { 'class': 'cyber-stack' }, opts.cards || [])
|
||||
]);
|
||||
},
|
||||
|
||||
_detectLanguage: function() {
|
||||
if (typeof L !== 'undefined' && L.env && L.env.lang)
|
||||
return L.env.lang;
|
||||
if (document.documentElement && document.documentElement.getAttribute('lang'))
|
||||
return document.documentElement.getAttribute('lang');
|
||||
if (navigator.language)
|
||||
return navigator.language.split('-')[0];
|
||||
return this.currentLanguage;
|
||||
},
|
||||
|
||||
_detectPreferredTheme: function() {
|
||||
var stored;
|
||||
|
||||
try {
|
||||
stored = window.localStorage.getItem('secubox.theme');
|
||||
} catch (err) {
|
||||
stored = null;
|
||||
}
|
||||
|
||||
if (this._isValidTheme(stored))
|
||||
return stored;
|
||||
|
||||
if (typeof L !== 'undefined' && L.env && L.env.media_url_base) {
|
||||
var media = L.env.media_url_base || '';
|
||||
if (/(openwrt|dark|argon|opentwenty|opentop)/i.test(media))
|
||||
return 'dark';
|
||||
if (/bootstrap|material|simple|freifunk/i.test(media))
|
||||
return 'light';
|
||||
}
|
||||
|
||||
var attr = (document.documentElement && document.documentElement.getAttribute('data-theme')) ||
|
||||
(document.body && document.body.getAttribute('data-theme'));
|
||||
if (attr) {
|
||||
if (/cyber/i.test(attr))
|
||||
return 'cyberpunk';
|
||||
if (/light/i.test(attr))
|
||||
return 'light';
|
||||
if (/dark|secubox/i.test(attr))
|
||||
return 'dark';
|
||||
}
|
||||
|
||||
if (document.body && document.body.className) {
|
||||
if (/\bluci-theme-[a-z0-9]+/i.test(document.body.className)) {
|
||||
if (/\b(light|bootstrap|material)\b/i.test(document.body.className))
|
||||
return 'light';
|
||||
if (/\b(openwrt2020|argon|dark)\b/i.test(document.body.className))
|
||||
return 'dark';
|
||||
}
|
||||
}
|
||||
|
||||
if (window.matchMedia) {
|
||||
try {
|
||||
if (window.matchMedia('(prefers-color-scheme: light)').matches)
|
||||
return 'light';
|
||||
} catch (err) { /* ignore */ }
|
||||
}
|
||||
|
||||
return this.currentTheme;
|
||||
},
|
||||
|
||||
_isValidTheme: function(theme) {
|
||||
return this.availableThemes.indexOf(theme) !== -1;
|
||||
}
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user