secubox-openwrt/luci-app-secubox/htdocs/luci-static/resources/view/secubox/modules.js
CyberMind-FR a53e5f7068 feat: Add comprehensive permission management system (v0.3.1)
Implement three-tier permission management across all SecuBox modules:

**1. Package-Level Permissions (PKG_FILE_MODES)**
- Add PKG_FILE_MODES to all 15 module Makefiles
- RPCD scripts: 755 (executable)
- CSS/JS/JSON files: 644 (default, no config needed)
- Ensures correct permissions at installation time

**2. Runtime Permission Fix**
- New script: /usr/libexec/secubox/fix-permissions.sh
- RPCD method: luci.secubox fix_permissions
- UI control: "🔧 Fix Perms" button in Quick Actions
- Fixes all permissions and restarts services

**3. Automation & Documentation**
- secubox-tools/add-pkg-file-modes.sh: Auto-configure PKG_FILE_MODES
- PERMISSIONS-GUIDE.md: Comprehensive permissions guide
- MODULE-ENABLE-DISABLE-DESIGN.md: Enable/disable system design doc
- Updated Makefile template with PKG_FILE_MODES pattern

**Modules Updated:**
- luci-app-auth-guardian
- luci-app-bandwidth-manager
- luci-app-cdn-cache
- luci-app-client-guardian
- luci-app-crowdsec-dashboard
- luci-app-ksm-manager
- luci-app-media-flow
- luci-app-netdata-dashboard
- luci-app-netifyd-dashboard
- luci-app-network-modes
- luci-app-secubox (+ fix-permissions.sh script)
- luci-app-system-hub
- luci-app-traffic-shaper
- luci-app-vhost-manager
- luci-app-wireguard-dashboard

**Benefits:**
- No more manual permission fixes after installation
- Users can fix permissions from UI without SSH access
- Proper OpenWrt package management compliance
- Automated detection and configuration

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

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

374 lines
11 KiB
JavaScript

'use strict';
'require view';
'require ui';
'require dom';
'require secubox/api as API';
'require secubox/theme as Theme';
'require poll';
// Load CSS
document.head.appendChild(E('link', {
'rel': 'stylesheet',
'type': 'text/css',
'href': L.resource('secubox/secubox.css')
}));
document.head.appendChild(E('link', {
'rel': 'stylesheet',
'type': 'text/css',
'href': L.resource('secubox/modules.css')
}));
// Initialize theme
Theme.init();
return view.extend({
modulesData: [],
load: function() {
return this.refreshData();
},
refreshData: function() {
var self = this;
return API.getModules().then(function(data) {
self.modulesData = data.modules || [];
return data;
});
},
render: function(data) {
var self = this;
var modules = this.modulesData;
var container = E('div', { 'class': 'secubox-modules-page' });
// Header with stats
container.appendChild(this.renderHeader(modules));
// Filter tabs
container.appendChild(this.renderFilterTabs());
// Modules grid
container.appendChild(E('div', { 'id': 'modules-grid', 'class': 'secubox-modules-grid' },
this.renderModuleCards(modules, 'all')
));
// Auto-refresh
poll.add(function() {
return self.refreshData().then(function() {
self.updateModulesGrid();
});
}, 30);
return container;
},
renderHeader: function(modules) {
var total = modules.length;
var installed = modules.filter(function(m) { return m.installed; }).length;
var enabled = modules.filter(function(m) { return m.enabled; }).length;
var disabled = installed - enabled;
return E('div', { 'class': 'secubox-page-header' }, [
E('div', {}, [
E('h2', {}, '📦 SecuBox Modules'),
E('p', { 'class': 'secubox-page-subtitle' },
'Manage and monitor all SecuBox modules')
]),
E('div', { 'class': 'secubox-modules-stats' }, [
E('div', { 'class': 'secubox-stat-badge' }, [
E('span', { 'class': 'secubox-stat-value' }, total),
E('span', { 'class': 'secubox-stat-label' }, 'Total')
]),
E('div', { 'class': 'secubox-stat-badge secubox-stat-success' }, [
E('span', { 'class': 'secubox-stat-value' }, enabled),
E('span', { 'class': 'secubox-stat-label' }, 'Activés')
]),
E('div', { 'class': 'secubox-stat-badge secubox-stat-warning' }, [
E('span', { 'class': 'secubox-stat-value' }, disabled),
E('span', { 'class': 'secubox-stat-label' }, 'Désactivés')
]),
E('div', { 'class': 'secubox-stat-badge secubox-stat-muted' }, [
E('span', { 'class': 'secubox-stat-value' }, total - installed),
E('span', { 'class': 'secubox-stat-label' }, 'Available')
])
])
]);
},
renderFilterTabs: function() {
var self = this;
var tabs = [
{ id: 'all', label: 'All Modules', icon: '📦' },
{ id: 'security', label: 'Security', icon: '🛡️' },
{ id: 'monitoring', label: 'Monitoring', icon: '📊' },
{ id: 'network', label: 'Network', icon: '🌐' },
{ id: 'system', label: 'System', icon: '⚙️' }
];
return E('div', { 'class': 'secubox-filter-tabs' },
tabs.map(function(tab) {
return E('button', {
'class': 'secubox-filter-tab' + (tab.id === 'all' ? ' active' : ''),
'data-filter': tab.id,
'click': function(ev) {
document.querySelectorAll('.secubox-filter-tab').forEach(function(el) {
el.classList.remove('active');
});
ev.target.classList.add('active');
self.filterModules(tab.id);
}
}, tab.icon + ' ' + tab.label);
})
);
},
renderModuleCards: function(modules, filter) {
var self = this;
var filtered = filter === 'all' ? modules :
modules.filter(function(m) { return m.category === filter; });
if (filtered.length === 0) {
return E('div', { 'class': 'secubox-empty-state' }, [
E('div', { 'class': 'secubox-empty-icon' }, '📭'),
E('div', { 'class': 'secubox-empty-title' }, 'No modules found'),
E('div', { 'class': 'secubox-empty-text' }, 'Try selecting a different category')
]);
}
return filtered.map(function(module) {
return self.renderModuleCard(module);
});
},
renderModuleCard: function(module) {
var self = this;
var status = module.status || 'unknown';
var isInstalled = module.installed;
var statusClass = isInstalled ? status : 'not-installed';
// Status label mapping (v0.3.1)
var statusLabels = {
'active': '✓ Activé',
'disabled': '○ Désactivé',
'error': '⚠️ Erreur',
'unknown': '? Inconnu',
'not-installed': '- Not Installed'
};
var statusLabel = isInstalled ? (statusLabels[status] || '○ Désactivé') : statusLabels['not-installed'];
return E('div', {
'class': 'secubox-module-card secubox-module-' + statusClass,
'style': 'border-left: 4px solid ' + (module.color || '#64748b')
}, [
// Card Header
E('div', { 'class': 'secubox-module-card-header' }, [
E('div', { 'class': 'secubox-module-icon' }, module.icon || '📦'),
E('div', { 'class': 'secubox-module-info' }, [
E('h3', { 'class': 'secubox-module-name' }, module.name || module.id),
E('div', { 'class': 'secubox-module-meta' }, [
E('span', { 'class': 'secubox-module-category' },
this.getCategoryIcon(module.category) + ' ' + (module.category || 'other')),
E('span', { 'class': 'secubox-module-version' },
'v' + (module.version || '0.0.9'))
])
]),
E('div', {
'class': 'secubox-status-indicator secubox-status-' + statusClass,
'title': statusLabel
})
]),
// Card Body
E('div', { 'class': 'secubox-module-card-body' }, [
E('p', { 'class': 'secubox-module-description' },
module.description || 'No description available'),
E('div', { 'class': 'secubox-module-details' }, [
E('div', { 'class': 'secubox-module-detail' }, [
E('span', { 'class': 'secubox-detail-label' }, 'Package:'),
E('code', { 'class': 'secubox-detail-value' }, module.package || 'N/A')
]),
E('div', { 'class': 'secubox-module-detail' }, [
E('span', { 'class': 'secubox-detail-label' }, 'Status:'),
E('span', {
'class': 'secubox-detail-value secubox-status-text-' + statusClass
}, statusLabel)
])
])
]),
// Card Actions
E('div', { 'class': 'secubox-module-card-actions' },
this.renderModuleActions(module))
]);
},
renderModuleActions: function(module) {
var self = this;
var actions = [];
if (!module.installed) {
actions.push(
E('button', {
'class': 'secubox-btn secubox-btn-secondary secubox-btn-sm',
'disabled': true
}, '📥 Install')
);
} else {
// Enable/Disable button (v0.3.1)
if (module.enabled) {
actions.push(
E('button', {
'class': 'secubox-btn secubox-btn-danger secubox-btn-sm',
'click': function() {
self.disableModule(module);
}
}, '⏹️ Désactiver')
);
} else {
actions.push(
E('button', {
'class': 'secubox-btn secubox-btn-success secubox-btn-sm',
'click': function() {
self.enableModule(module);
}
}, '▶️ Activer')
);
}
// Dashboard link
var dashboardPath = this.getModuleDashboardPath(module.id);
if (dashboardPath) {
actions.push(
E('a', {
'href': L.url(dashboardPath),
'class': 'secubox-btn secubox-btn-primary secubox-btn-sm'
}, '📊 Dashboard')
);
}
}
return actions;
},
getModuleDashboardPath: function(moduleId) {
var paths = {
'crowdsec': 'admin/secubox/security/crowdsec',
'netdata': 'admin/secubox/monitoring/netdata',
'netifyd': 'admin/secubox/security/netifyd',
'wireguard': 'admin/secubox/network/wireguard',
'network_modes': 'admin/secubox/network/modes',
'client_guardian': 'admin/secubox/security/guardian',
'system_hub': 'admin/secubox/system/system-hub',
'bandwidth_manager': 'admin/secubox/network/bandwidth',
'auth_guardian': 'admin/secubox/security/auth',
'media_flow': 'admin/secubox/network/media',
'vhost_manager': 'admin/secubox/system/vhost',
'traffic_shaper': 'admin/secubox/network/shaper',
'cdn_cache': 'admin/secubox/network/cdn',
'ksm_manager': 'admin/secubox/security/ksm'
};
return paths[moduleId] || null;
},
getCategoryIcon: function(category) {
var icons = {
'security': '🛡️',
'monitoring': '📊',
'network': '🌐',
'system': '⚙️',
'other': '📦'
};
return icons[category] || icons['other'];
},
// Enable module (v0.3.1)
enableModule: function(module) {
var self = this;
ui.showModal(_('Activation du module'), [
E('p', {}, 'Activation de ' + module.name + '...')
]);
API.enableModule(module.id).then(function(result) {
ui.hideModal();
if (result && result.success !== false) {
ui.addNotification(null, E('p', module.name + ' activé avec succès'), 'info');
self.refreshData().then(function() {
self.updateModulesGrid();
});
} else {
ui.addNotification(null, E('p', 'Échec de l\'activation de ' + module.name), 'error');
}
}).catch(function(err) {
ui.hideModal();
ui.addNotification(null, E('p', 'Erreur: ' + err.message), 'error');
});
},
// Disable module (v0.3.1)
disableModule: function(module) {
var self = this;
ui.showModal(_('Désactivation du module'), [
E('p', {}, 'Désactivation de ' + module.name + '...')
]);
API.disableModule(module.id).then(function(result) {
ui.hideModal();
if (result && result.success !== false) {
ui.addNotification(null, E('p', module.name + ' désactivé avec succès'), 'info');
self.refreshData().then(function() {
self.updateModulesGrid();
});
} else {
ui.addNotification(null, E('p', 'Échec de la désactivation de ' + module.name), 'error');
}
}).catch(function(err) {
ui.hideModal();
ui.addNotification(null, E('p', 'Erreur: ' + err.message), 'error');
});
},
// DEPRECATED: Keeping for backward compatibility
startModule: function(module) {
return this.enableModule(module);
},
stopModule: function(module) {
return this.disableModule(module);
},
restartModule: function(module) {
var self = this;
return this.disableModule(module).then(function() {
return self.enableModule(module);
});
},
filterModules: function(category) {
var grid = document.getElementById('modules-grid');
if (grid) {
dom.content(grid, this.renderModuleCards(this.modulesData, category));
}
},
updateModulesGrid: function() {
var activeTab = document.querySelector('.secubox-filter-tab.active');
var filter = activeTab ? activeTab.getAttribute('data-filter') : 'all';
this.filterModules(filter);
// Update header stats
var header = document.querySelector('.secubox-modules-stats');
if (header && header.parentNode) {
var newHeader = this.renderHeader(this.modulesData);
header.parentNode.replaceChild(newHeader.querySelector('.secubox-modules-stats'), header);
}
},
handleSaveApply: null,
handleSave: null,
handleReset: null
});