diff --git a/MODULE-ENABLE-DISABLE-DESIGN.md b/MODULE-ENABLE-DISABLE-DESIGN.md
new file mode 100644
index 00000000..9fe681a0
--- /dev/null
+++ b/MODULE-ENABLE-DISABLE-DESIGN.md
@@ -0,0 +1,356 @@
+# Module Enable/Disable Design Document
+
+**Version:** 0.3.1
+**Date:** 2025-12-27
+**Author:** Claude Code + CyberMind
+
+## đŻ Objectif
+
+Remplacer la logique **start/stop** des modules SecuBox par une logique **enable/disable** (activĂ©/dĂ©sactivĂ©), car les modules sont des **plugins installĂ©s** qu'on souhaite activer ou dĂ©sactiver, plutĂŽt que des services qu'on dĂ©marre ou arrĂȘte ponctuellement.
+
+## đ Changements Conceptuels
+
+### Avant (v0.2.x)
+
+```
+Module installĂ© â peut ĂȘtre "Running" ou "Stopped"
+Actions: Start / Stop / Restart
+Ătat affichĂ©: "Running" (vert) ou "Stopped" (gris)
+```
+
+### AprĂšs (v0.3.1+)
+
+```
+Module installĂ© â peut ĂȘtre "Enabled" ou "Disabled"
+Actions: Enable / Disable
+Ătat affichĂ©: "ActivĂ©" (vert) ou "DĂ©sactivĂ©" (gris)
+Info complémentaire: "Service running" (si enabled + running)
+```
+
+## đïž Architecture Technique
+
+### 1. Configuration UCI
+
+Chaque module dans `/etc/config/secubox` aura un champ `enabled`:
+
+```uci
+config module 'crowdsec'
+ option name 'CrowdSec Dashboard'
+ option package 'luci-app-crowdsec-dashboard'
+ option config 'crowdsec'
+ option category 'security'
+ option enabled '1' # NEW: 1 = activé, 0 = désactivé
+ option icon 'đĄïž'
+ option color '#ef4444'
+```
+
+### 2. Méthodes RPCD (`luci.secubox`)
+
+#### Anciennes méthodes (DEPRECATED)
+- â `start_module(module_id)` â dĂ©marre le service
+- â `stop_module(module_id)` â arrĂȘte le service
+- â `restart_module(module_id)` â redĂ©marre le service
+
+#### Nouvelles méthodes (v0.3.1+)
+
+```javascript
+// Active un module (config UCI + démarrage service)
+enable_module(module_id)
+â uci set secubox.${module}.enabled='1'
+â uci commit secubox
+â /etc/init.d/${service} enable
+â /etc/init.d/${service} start
+â return { success: true, message: "Module activĂ©" }
+
+// DĂ©sactive un module (config UCI + arrĂȘt service)
+disable_module(module_id)
+â uci set secubox.${module}.enabled='0'
+â uci commit secubox
+â /etc/init.d/${service} disable
+â /etc/init.d/${service} stop
+â return { success: true, message: "Module dĂ©sactivĂ©" }
+
+// Vérifie si un module est activé
+check_module_enabled(module_id)
+â return uci get secubox.${module}.enabled == '1'
+
+// Vérifie si le service tourne (info complémentaire)
+check_service_running(module_id)
+â return pgrep -f ${service} > /dev/null
+```
+
+### 3. Structure de données retournée
+
+```json
+{
+ "modules": [
+ {
+ "id": "crowdsec",
+ "name": "CrowdSec Dashboard",
+ "category": "security",
+ "installed": true,
+ "enabled": true, // Ătat principal (config UCI)
+ "running": true, // Ătat du service (info)
+ "status": "active", // enabled + running = "active"
+ "icon": "đĄïž",
+ "color": "#ef4444"
+ },
+ {
+ "id": "netdata",
+ "name": "Netdata Monitoring",
+ "category": "monitoring",
+ "installed": true,
+ "enabled": false, // Module désactivé
+ "running": false,
+ "status": "disabled", // Status affiché
+ "icon": "đ",
+ "color": "#22c55e"
+ }
+ ]
+}
+```
+
+### 4. Ătats Possibles
+
+| enabled | running | status | Badge UI | Description |
+|---------|---------|----------|---------------|-------------|
+| `true` | `true` | `active` | â ActivĂ© | Module activĂ© et service tourne |
+| `true` | `false` | `error` | â ïž Erreur | Module activĂ© mais service arrĂȘtĂ© (problĂšme) |
+| `false` | `false` | `disabled` | â DĂ©sactivĂ© | Module dĂ©sactivĂ© (Ă©tat normal) |
+| `false` | `true` | `unknown` | ? Inconnu | Ătat incohĂ©rent (rare) |
+
+## đš Interface Utilisateur
+
+### Dashboard Principal (SecuBox Hub)
+
+**Avant:**
+```
+[CrowdSec Dashboard] â Running [Stop] [Restart]
+[Netdata Monitor] â Stopped [Start]
+```
+
+**AprĂšs:**
+```
+[CrowdSec Dashboard] â ActivĂ© [DĂ©sactiver]
+[Netdata Monitor] â DĂ©sactivĂ© [Activer]
+```
+
+### Module Individual Card
+
+```html
+
+
+
+
+ Service en cours d'exécution
+
+
+
+
+
+```
+
+### Classes CSS
+
+```css
+/* Module states */
+.module-badge.enabled {
+ background: linear-gradient(135deg, #22c55e, #16a34a);
+ color: white;
+}
+
+.module-badge.disabled {
+ background: var(--sh-bg-secondary);
+ color: var(--sh-text-muted);
+}
+
+.module-badge.error {
+ background: linear-gradient(135deg, #f59e0b, #d97706);
+ color: white;
+}
+
+/* Status indicators */
+.status-dot.running {
+ background: #22c55e;
+ animation: pulse 2s infinite;
+}
+
+.status-dot.stopped {
+ background: #94a3b8;
+}
+```
+
+## đ API JavaScript
+
+### Fichier: `secubox/api.js`
+
+```javascript
+// Anciennes méthodes (DEPRECATED - à supprimer)
+startModule: callStartModule, // DEPRECATED
+stopModule: callStopModule, // DEPRECATED
+restartModule: callRestartModule, // DEPRECATED
+
+// Nouvelles méthodes (v0.3.1+)
+enableModule: callEnableModule, // NEW
+disableModule: callDisableModule, // NEW
+
+// Déclarations RPC
+var callEnableModule = rpc.declare({
+ object: 'luci.secubox',
+ method: 'enable_module',
+ params: ['module_id'],
+ expect: { success: false, message: '' }
+});
+
+var callDisableModule = rpc.declare({
+ object: 'luci.secubox',
+ method: 'disable_module',
+ params: ['module_id'],
+ expect: { success: false, message: '' }
+});
+```
+
+## đ Migration des DonnĂ©es
+
+### Script de migration (à exécuter une fois)
+
+```bash
+#!/bin/sh
+# migrate-to-enable-disable.sh
+
+. /lib/functions.sh
+
+migrate_module() {
+ local module="$1"
+ local running=$(pgrep -f "$module" > /dev/null && echo "1" || echo "0")
+
+ # Si le service tourne actuellement, on l'active
+ if [ "$running" = "1" ]; then
+ uci set secubox.${module}.enabled='1'
+ else
+ # Sinon, on le désactive par défaut
+ uci set secubox.${module}.enabled='0'
+ fi
+}
+
+# Migrer tous les modules
+config_load secubox
+config_foreach migrate_module module
+
+uci commit secubox
+echo "Migration completed"
+```
+
+## đ Documentation Utilisateur
+
+### README.md (Ă ajouter)
+
+```markdown
+## Gestion des Modules
+
+Les modules SecuBox sont des plugins installĂ©s qui peuvent ĂȘtre **activĂ©s** ou **dĂ©sactivĂ©s**.
+
+### Activer un module
+- Cliquez sur le bouton **"Activer"** sur la carte du module
+- Le module sera configuré pour démarrer automatiquement au boot
+- Le service associé démarrera immédiatement
+
+### Désactiver un module
+- Cliquez sur le bouton **"Désactiver"** sur la carte du module
+- Le module ne démarrera plus automatiquement au boot
+- Le service associĂ© s'arrĂȘtera immĂ©diatement
+
+### Ătats des modules
+
+| Badge | Signification |
+|-------|---------------|
+| â ActivĂ© | Module activĂ© et service en cours d'exĂ©cution |
+| â ïž Erreur | Module activĂ© mais service arrĂȘtĂ© (vĂ©rifier les logs) |
+| â DĂ©sactivĂ© | Module dĂ©sactivĂ© (normal) |
+
+**Note:** Les modules restent installĂ©s mĂȘme lorsqu'ils sont dĂ©sactivĂ©s. Pour les supprimer complĂštement, utilisez le gestionnaire de paquets APK.
+```
+
+## đ§Ș Tests Ă Effectuer
+
+### Tests Unitaires RPCD
+
+```bash
+# Test enable_module
+ubus call luci.secubox enable_module '{"module_id":"crowdsec"}'
+# Expected: {"success":true,"message":"Module activé"}
+
+# Vérifier config UCI
+uci get secubox.crowdsec.enabled
+# Expected: 1
+
+# Vérifier service
+/etc/init.d/crowdsec enabled && echo "OK" || echo "FAIL"
+pgrep crowdsec && echo "Running" || echo "Not running"
+
+# Test disable_module
+ubus call luci.secubox disable_module '{"module_id":"crowdsec"}'
+# Expected: {"success":true,"message":"Module désactivé"}
+
+# Vérifier
+uci get secubox.crowdsec.enabled
+# Expected: 0
+```
+
+### Tests Interface
+
+1. â
Ouvrir le dashboard SecuBox
+2. â
Vérifier que les modules affichent "Activé" ou "Désactivé"
+3. â
Cliquer sur "DĂ©sactiver" â badge passe Ă "â DĂ©sactivĂ©"
+4. â
Cliquer sur "Activer" â badge passe Ă "â ActivĂ©"
+5. â
VĂ©rifier que le service dĂ©marre/s'arrĂȘte rĂ©ellement
+6. â
RafraĂźchir la page â Ă©tat persiste (UCI)
+
+## đŠ Modules AffectĂ©s
+
+### SecuBox Hub (`luci-app-secubox`)
+
+**Fichiers Ă modifier:**
+- â
`root/usr/libexec/rpcd/luci.secubox` - Backend RPCD
+- â
`htdocs/luci-static/resources/secubox/api.js` - API JS
+- â
`htdocs/luci-static/resources/view/secubox/dashboard.js` - Dashboard
+- â
`htdocs/luci-static/resources/view/secubox/modules.js` - Module list
+- â
`htdocs/luci-static/resources/secubox/dashboard.css` - Styles
+- â
`root/usr/share/rpcd/acl.d/luci-app-secubox.json` - ACL permissions
+- â
`README.md` - Documentation
+
+### System Hub (`luci-app-system-hub`)
+
+**Fichiers Ă modifier:**
+- â
`htdocs/luci-static/resources/view/system-hub/components.js` - Vue composants
+- â
`htdocs/luci-static/resources/view/system-hub/services.js` - Vue services
+- â
`README.md` - Documentation
+
+## đŻ BĂ©nĂ©fices
+
+1. **ClartĂ© conceptuelle**: "Activer/DĂ©sactiver" est plus clair que "DĂ©marrer/ArrĂȘter" pour des plugins
+2. **Persistance**: L'état persiste aprÚs redémarrage (UCI + init.d enable/disable)
+3. **CohĂ©rence**: Tous les modules suivent la mĂȘme logique
+4. **Meilleure UX**: L'utilisateur comprend qu'il active/désactive des fonctionnalités
+5. **Alignement OpenWrt**: Utilise les mécanismes natifs (`/etc/init.d/${service} enable/disable`)
+
+## đ Prochaines Ătapes
+
+- [x] Créer ce document de design
+- [ ] Implémenter les modifications RPCD
+- [ ] Mettre Ă jour l'API JavaScript
+- [ ] Mettre Ă jour les interfaces UI
+- [ ] Mettre Ă jour les ACL permissions
+- [ ] Créer script de migration UCI
+- [ ] Mettre Ă jour la documentation
+- [ ] Tester sur router de test
+- [ ] Déployer en production
+
+---
+
+**Maintainer:** CyberMind
+**License:** Apache-2.0
diff --git a/PERMISSIONS-GUIDE.md b/PERMISSIONS-GUIDE.md
new file mode 100644
index 00000000..88ed4181
--- /dev/null
+++ b/PERMISSIONS-GUIDE.md
@@ -0,0 +1,229 @@
+# OpenWrt Package Permissions Guide
+
+**Version:** 0.3.1
+**Date:** 2025-12-28
+**Author:** CyberMind
+
+## đŻ Objectif
+
+Assurer que tous les fichiers des packages SecuBox ont les **bonnes permissions** dÚs l'installation, sans nécessiter de correction manuelle.
+
+## đ Permissions Requises
+
+### Fichiers Exécutables (755)
+
+Ces fichiers **DOIVENT** avoir les permissions d'exécution:
+
+```
+-rwxr-xr-x (755)
+```
+
+**Liste des fichiers:**
+- `/usr/libexec/rpcd/luci.*` - Scripts RPCD backend
+- `/usr/libexec/secubox/*.sh` - Scripts utilitaires
+- `/etc/init.d/*` - Scripts d'initialisation
+- `/etc/uci-defaults/*` - Scripts de configuration initiale
+
+### Fichiers Non-Exécutables (644)
+
+Ces fichiers **NE DOIVENT PAS** ĂȘtre exĂ©cutables:
+
+```
+-rw-r--r-- (644)
+```
+
+**Liste des fichiers:**
+- `/www/luci-static/resources/**/*.js` - Fichiers JavaScript
+- `/www/luci-static/resources/**/*.css` - Fichiers CSS
+- `/usr/share/rpcd/acl.d/*.json` - Permissions ACL
+- `/usr/share/luci/menu.d/*.json` - Définitions de menu
+- `/etc/config/*` - Fichiers de configuration UCI
+
+## đ§ Configuration dans le Makefile
+
+### Méthode Recommandée: PKG_FILE_MODES
+
+OpenWrt supporte la variable `PKG_FILE_MODES` pour définir les permissions des fichiers lors de l'installation du package.
+
+**Syntaxe:**
+```makefile
+PKG_FILE_MODES:=/path/to/file:permissions
+```
+
+**Exemple complet:**
+```makefile
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=luci-app-example
+PKG_VERSION:=0.3.1
+PKG_RELEASE:=1
+PKG_LICENSE:=Apache-2.0
+PKG_MAINTAINER:=CyberMind
+
+LUCI_TITLE:=LuCI - Example Module
+LUCI_DESCRIPTION:=Example SecuBox module
+LUCI_DEPENDS:=+luci-base +rpcd
+LUCI_PKGARCH:=all
+
+# File permissions (RPCD scripts must be executable)
+PKG_FILE_MODES:=/usr/libexec/rpcd/luci.example:755
+
+include $(TOPDIR)/feeds/luci/luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
+```
+
+### Plusieurs Fichiers Exécutables
+
+Si vous avez plusieurs fichiers exécutables:
+
+```makefile
+PKG_FILE_MODES:=/usr/libexec/rpcd/luci.example:755 \
+ /usr/libexec/example/helper.sh:755 \
+ /etc/init.d/example:755
+```
+
+**Note:** Utilisez `\` pour continuer sur la ligne suivante.
+
+## đŠ Modules SecuBox avec PKG_FILE_MODES
+
+### luci-app-secubox
+```makefile
+PKG_FILE_MODES:=/usr/libexec/rpcd/luci.secubox:755 \
+ /usr/libexec/secubox/fix-permissions.sh:755
+```
+
+### luci-app-system-hub
+```makefile
+PKG_FILE_MODES:=/usr/libexec/rpcd/luci.system-hub:755
+```
+
+### luci-app-network-modes
+```makefile
+PKG_FILE_MODES:=/usr/libexec/rpcd/luci.network-modes:755
+```
+
+## đ§Ș VĂ©rification
+
+### Lors du Développement
+
+Avant de déployer un package, vérifiez les permissions:
+
+```bash
+# Vérifier les scripts RPCD
+ls -l root/usr/libexec/rpcd/luci.*
+
+# Vérifier les scripts helper
+ls -l root/usr/libexec/*/
+
+# Vérifier les fichiers web
+find root/www -type f -name "*.js" -o -name "*.css" | xargs ls -l
+```
+
+### AprĂšs Installation du Package
+
+Vérifiez que les permissions sont correctes sur le routeur:
+
+```bash
+# RPCD scripts doivent ĂȘtre 755
+ls -l /usr/libexec/rpcd/luci.*
+
+# Fichiers web doivent ĂȘtre 644
+ls -l /www/luci-static/resources/secubox/*.js
+ls -l /www/luci-static/resources/secubox/*.css
+```
+
+## đ ïž Script de VĂ©rification Automatique
+
+Un script de vérification est inclus dans `luci-app-secubox`:
+
+```bash
+# Vérifier et corriger toutes les permissions
+/usr/libexec/secubox/fix-permissions.sh
+
+# Via ubus
+ubus call luci.secubox fix_permissions
+
+# Via l'interface web
+Dashboard â Quick Actions â "đ§ Fix Perms"
+```
+
+## â ïž Erreurs Communes
+
+### 1. RPCD Script Non-Exécutable
+
+**SymptĂŽme:**
+```bash
+ubus call luci.example status
+# Command failed: Permission denied
+```
+
+**Cause:** Le script RPCD n'a pas les permissions 755
+
+**Solution:**
+```makefile
+# Ajouter dans le Makefile
+PKG_FILE_MODES:=/usr/libexec/rpcd/luci.example:755
+```
+
+### 2. Fichiers Web Exécutables
+
+**SymptĂŽme:** Fichiers JavaScript/CSS avec permissions 755
+
+**Cause:** Mauvaise manipulation ou script mal configuré
+
+**Solution:** Les fichiers web sont 644 par défaut avec LuCI, pas besoin de les spécifier dans PKG_FILE_MODES
+
+### 3. Script Helper Non-Exécutable
+
+**SymptĂŽme:**
+```bash
+/usr/libexec/example/helper.sh
+# -bash: /usr/libexec/example/helper.sh: Permission denied
+```
+
+**Solution:**
+```makefile
+PKG_FILE_MODES:=/usr/libexec/rpcd/luci.example:755 \
+ /usr/libexec/example/helper.sh:755
+```
+
+## đ RĂ©fĂ©rences
+
+- **LuCI Build System:** `$(TOPDIR)/feeds/luci/luci.mk`
+- **OpenWrt Package Build:** https://openwrt.org/docs/guide-developer/packages
+- **PKG_FILE_MODES:** https://openwrt.org/docs/guide-developer/build-system/use-buildsystem#build_system_variables
+
+## â
Checklist Pré-Déploiement
+
+Avant de créer un package `.ipk` ou `.apk`:
+
+- [ ] Tous les scripts RPCD ont 755 dans PKG_FILE_MODES
+- [ ] Tous les scripts helper ont 755 dans PKG_FILE_MODES
+- [ ] Les fichiers web (JS/CSS) ne sont PAS dans PKG_FILE_MODES (ils sont 644 par défaut)
+- [ ] Les fichiers ACL/Menu ne sont PAS dans PKG_FILE_MODES (ils sont 644 par défaut)
+- [ ] Le Makefile utilise `include $(TOPDIR)/feeds/luci/luci.mk`
+- [ ] PKG_FILE_MODES est défini AVANT le `include $(TOPDIR)/feeds/luci/luci.mk`
+
+## đ Migration des Modules Existants
+
+Pour ajouter PKG_FILE_MODES Ă un module existant:
+
+```bash
+cd luci-app-mymodule
+
+# Ăditer le Makefile
+vi Makefile
+
+# Ajouter avant 'include $(TOPDIR)/feeds/luci/luci.mk'
+PKG_FILE_MODES:=/usr/libexec/rpcd/luci.mymodule:755
+
+# Reconstruire le package
+make package/luci-app-mymodule/clean
+make package/luci-app-mymodule/compile
+```
+
+---
+
+**Maintainer:** CyberMind
+**License:** Apache-2.0
diff --git a/luci-app-auth-guardian/Makefile b/luci-app-auth-guardian/Makefile
index 618a6256..98094083 100644
--- a/luci-app-auth-guardian/Makefile
+++ b/luci-app-auth-guardian/Makefile
@@ -11,6 +11,10 @@ LUCI_DESCRIPTION:=Comprehensive authentication and session management with capti
LUCI_DEPENDS:=+luci-base +rpcd +nodogsplash
LUCI_PKGARCH:=all
+
+# File permissions (RPCD scripts must be executable)
+PKG_FILE_MODES:=/usr/libexec/rpcd/luci.auth-guardian:755
+
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature
diff --git a/luci-app-bandwidth-manager/Makefile b/luci-app-bandwidth-manager/Makefile
index 58bf5ca2..8db4456c 100644
--- a/luci-app-bandwidth-manager/Makefile
+++ b/luci-app-bandwidth-manager/Makefile
@@ -11,6 +11,10 @@ LUCI_DESCRIPTION:=Advanced bandwidth management with QoS rules, client quotas, a
LUCI_DEPENDS:=+luci-base +rpcd +tc +kmod-sched-core +kmod-sched-cake +kmod-ifb +sqm-scripts +iptables +iptables-mod-conntrack-extra +ip-full
LUCI_PKGARCH:=all
+
+# File permissions (RPCD scripts must be executable)
+PKG_FILE_MODES:=/usr/libexec/rpcd/luci.bandwidth-manager:755
+
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature
diff --git a/luci-app-cdn-cache/Makefile b/luci-app-cdn-cache/Makefile
index 3d8fb475..5c855c35 100644
--- a/luci-app-cdn-cache/Makefile
+++ b/luci-app-cdn-cache/Makefile
@@ -11,6 +11,10 @@ LUCI_DESCRIPTION:=Dashboard for managing local CDN caching proxy on OpenWrt
LUCI_DEPENDS:=+luci-base +rpcd
LUCI_PKGARCH:=all
+
+# File permissions (RPCD scripts must be executable)
+PKG_FILE_MODES:=/usr/libexec/rpcd/luci.cdn-cache:755
+
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature
diff --git a/luci-app-client-guardian/Makefile b/luci-app-client-guardian/Makefile
index 9ab4f0f8..1c6202cf 100644
--- a/luci-app-client-guardian/Makefile
+++ b/luci-app-client-guardian/Makefile
@@ -20,6 +20,10 @@ LUCI_DEPENDS:=+luci-base +luci-app-secubox +luci-lib-jsonc +rpcd +rpcd-mod-luci
LUCI_PKGARCH:=all
+
+# File permissions (RPCD scripts must be executable)
+PKG_FILE_MODES:=/usr/libexec/rpcd/luci.client-guardian:755
+
include $(TOPDIR)/feeds/luci/luci.mk
define Package/$(PKG_NAME)/conffiles
diff --git a/luci-app-crowdsec-dashboard/Makefile b/luci-app-crowdsec-dashboard/Makefile
index 75ae8f68..f4524b4f 100644
--- a/luci-app-crowdsec-dashboard/Makefile
+++ b/luci-app-crowdsec-dashboard/Makefile
@@ -20,6 +20,10 @@ LUCI_DEPENDS:=+luci-base +luci-app-secubox +crowdsec +luci-lib-jsonc +rpcd +rpcd
LUCI_PKGARCH:=all
+
+# File permissions (RPCD scripts must be executable)
+PKG_FILE_MODES:=/usr/libexec/rpcd/luci.crowdsec-dashboard:755
+
include $(TOPDIR)/feeds/luci/luci.mk
define Package/$(PKG_NAME)/conffiles
diff --git a/luci-app-ksm-manager/Makefile b/luci-app-ksm-manager/Makefile
index 1fed185b..d4e0fa49 100644
--- a/luci-app-ksm-manager/Makefile
+++ b/luci-app-ksm-manager/Makefile
@@ -16,6 +16,10 @@ LUCI_PKGARCH:=all
PKG_MAINTAINER:=CyberMind
PKG_LICENSE:=Apache-2.0
+
+# File permissions (RPCD scripts must be executable)
+PKG_FILE_MODES:=/usr/libexec/rpcd/luci.ksm-manager:755
+
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature
diff --git a/luci-app-media-flow/Makefile b/luci-app-media-flow/Makefile
index 4dabd72c..a5274c73 100644
--- a/luci-app-media-flow/Makefile
+++ b/luci-app-media-flow/Makefile
@@ -14,6 +14,10 @@ LUCI_DESCRIPTION:=Real-time detection and monitoring of streaming services (Netf
LUCI_DEPENDS:=+luci-base +rpcd +netifyd +luci-app-netifyd-dashboard
LUCI_PKGARCH:=all
+
+# File permissions (RPCD scripts must be executable)
+PKG_FILE_MODES:=/usr/libexec/rpcd/luci.media-flow:755
+
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature
diff --git a/luci-app-netdata-dashboard/Makefile b/luci-app-netdata-dashboard/Makefile
index 3bf7cc3c..b8fb8036 100644
--- a/luci-app-netdata-dashboard/Makefile
+++ b/luci-app-netdata-dashboard/Makefile
@@ -20,6 +20,10 @@ LUCI_DEPENDS:=+luci-base +luci-app-secubox +luci-lib-jsonc +rpcd +rpcd-mod-luci
LUCI_PKGARCH:=all
+
+# File permissions (RPCD scripts must be executable)
+PKG_FILE_MODES:=/usr/libexec/rpcd/luci.netdata-dashboard:755
+
include $(TOPDIR)/feeds/luci/luci.mk
define Package/$(PKG_NAME)/conffiles
diff --git a/luci-app-netifyd-dashboard/Makefile b/luci-app-netifyd-dashboard/Makefile
index 4dfd35fc..abe946f4 100644
--- a/luci-app-netifyd-dashboard/Makefile
+++ b/luci-app-netifyd-dashboard/Makefile
@@ -20,6 +20,10 @@ LUCI_DEPENDS:=+luci-base +luci-app-secubox +luci-lib-jsonc +rpcd +rpcd-mod-luci
LUCI_PKGARCH:=all
+
+# File permissions (RPCD scripts must be executable)
+PKG_FILE_MODES:=/usr/libexec/rpcd/luci.netifyd-dashboard:755
+
include $(TOPDIR)/feeds/luci/luci.mk
define Package/$(PKG_NAME)/conffiles
diff --git a/luci-app-network-modes/Makefile b/luci-app-network-modes/Makefile
index a6b018b9..443332af 100644
--- a/luci-app-network-modes/Makefile
+++ b/luci-app-network-modes/Makefile
@@ -20,6 +20,9 @@ LUCI_DEPENDS:=+luci-base +luci-app-secubox +luci-lib-jsonc +rpcd +rpcd-mod-luci
LUCI_PKGARCH:=all
+# File permissions (RPCD scripts must be executable)
+PKG_FILE_MODES:=/usr/libexec/rpcd/luci.network-modes:755
+
include $(TOPDIR)/feeds/luci/luci.mk
define Package/$(PKG_NAME)/conffiles
diff --git a/luci-app-network-modes/htdocs/luci-static/resources/network-modes/api.js b/luci-app-network-modes/htdocs/luci-static/resources/network-modes/api.js
index 10eea7b5..048dbc3f 100644
--- a/luci-app-network-modes/htdocs/luci-static/resources/network-modes/api.js
+++ b/luci-app-network-modes/htdocs/luci-static/resources/network-modes/api.js
@@ -63,12 +63,102 @@ return baseclass.extend({
callGetAvailableModes(),
callGetInterfaces()
]).then(function(results) {
+ var status = results[0] || {};
+ var currentMode = results[1] || {};
+
+ // Merge current_mode into status for compatibility
+ status.current_mode = currentMode.mode || 'router';
+ status.interfaces = (results[3] || {}).interfaces || [];
+
return {
- status: results[0] || {},
- current_mode: results[1] || { mode: '' },
- available_modes: results[2] || { modes: [] },
- interfaces: results[3] || { interfaces: [] }
+ status: status,
+ modes: results[2] || { modes: [] }
};
});
+ },
+
+ // Get static information about a mode
+ getModeInfo: function(mode) {
+ var modeInfo = {
+ router: {
+ id: 'router',
+ name: 'Router Mode',
+ icon: 'đ ',
+ description: 'Traditional home/office router with NAT, firewall, and DHCP server. Ideal for connecting multiple devices to the internet.',
+ features: [
+ 'NAT and firewall enabled',
+ 'DHCP server for LAN clients',
+ 'Port forwarding and DMZ',
+ 'QoS and traffic shaping'
+ ]
+ },
+ bridge: {
+ id: 'bridge',
+ name: 'Bridge Mode',
+ icon: 'đ',
+ description: 'Transparent layer-2 forwarding without NAT. All devices appear on the same network segment.',
+ features: [
+ 'Layer-2 transparent bridging',
+ 'No NAT or routing',
+ 'STP/RSTP support',
+ 'VLAN tagging support'
+ ]
+ },
+ accesspoint: {
+ id: 'accesspoint',
+ name: 'Access Point',
+ icon: 'đĄ',
+ description: 'WiFi access point with wired uplink. Extends your existing network wirelessly.',
+ features: [
+ 'WiFi hotspot functionality',
+ 'Wired uplink to main router',
+ 'Multiple SSID support',
+ 'Fast roaming (802.11r/k/v)'
+ ]
+ },
+ relay: {
+ id: 'relay',
+ name: 'Repeater/Extender',
+ icon: 'đ',
+ description: 'WiFi to WiFi repeating to extend wireless coverage. Connects wirelessly to upstream network.',
+ features: [
+ 'WiFi range extension',
+ 'Wireless uplink (WDS/Relay)',
+ 'Rebroadcast on same or different SSID',
+ 'Signal amplification'
+ ]
+ },
+ sniffer: {
+ id: 'sniffer',
+ name: 'Sniffer Mode',
+ icon: 'đ',
+ description: 'Network monitoring and packet capture mode for security analysis and troubleshooting.',
+ features: [
+ 'Promiscuous mode capture',
+ 'WiFi monitor mode',
+ 'pcap/pcapng output',
+ 'Integration with Wireshark'
+ ]
+ }
+ };
+
+ return modeInfo[mode] || {
+ id: mode,
+ name: mode.charAt(0).toUpperCase() + mode.slice(1),
+ icon: 'âïž',
+ description: 'Unknown mode',
+ features: []
+ };
+ },
+
+ // Format uptime seconds to human readable
+ formatUptime: function(seconds) {
+ if (!seconds || seconds < 0) return '0d 0h 0m';
+
+ var days = Math.floor(seconds / 86400);
+ var hours = Math.floor((seconds % 86400) / 3600);
+ var minutes = Math.floor((seconds % 3600) / 60);
+
+ return days + 'd ' + hours + 'h ' + minutes + 'm';
}
});
diff --git a/luci-app-secubox/Makefile b/luci-app-secubox/Makefile
index 5045a8cc..d271ba84 100644
--- a/luci-app-secubox/Makefile
+++ b/luci-app-secubox/Makefile
@@ -11,6 +11,10 @@ LUCI_DESCRIPTION:=Central control hub for all SecuBox modules. Provides unified
LUCI_DEPENDS:=+luci-base +rpcd +curl +jq
LUCI_PKGARCH:=all
+# File permissions (RPCD scripts must be executable)
+PKG_FILE_MODES:=/usr/libexec/rpcd/luci.secubox:755 \
+ /usr/libexec/secubox/fix-permissions.sh:755
+
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot
diff --git a/luci-app-secubox/htdocs/luci-static/resources/secubox/api.js b/luci-app-secubox/htdocs/luci-static/resources/secubox/api.js
index da14341f..44f65999 100644
--- a/luci-app-secubox/htdocs/luci-static/resources/secubox/api.js
+++ b/luci-app-secubox/htdocs/luci-static/resources/secubox/api.js
@@ -8,7 +8,7 @@
* RPCD object: luci.secubox
*/
-// Version: 0.2.2
+// Version: 0.3.1
var callStatus = rpc.declare({
object: 'luci.secubox',
@@ -54,6 +54,28 @@ var callRestartModule = rpc.declare({
params: ['module']
});
+// NEW v0.3.1: Enable/Disable module methods
+var callEnableModule = rpc.declare({
+ object: 'luci.secubox',
+ method: 'enable_module',
+ params: ['module'],
+ expect: { success: false, message: '' }
+});
+
+var callDisableModule = rpc.declare({
+ object: 'luci.secubox',
+ method: 'disable_module',
+ params: ['module'],
+ expect: { success: false, message: '' }
+});
+
+var callCheckModuleEnabled = rpc.declare({
+ object: 'luci.secubox',
+ method: 'check_module_enabled',
+ params: ['module'],
+ expect: { enabled: false }
+});
+
var callHealth = rpc.declare({
object: 'luci.secubox',
method: 'health',
@@ -110,6 +132,12 @@ var callClearAlerts = rpc.declare({
expect: { }
});
+var callFixPermissions = rpc.declare({
+ object: 'luci.secubox',
+ method: 'fix_permissions',
+ expect: { success: false, message: '', output: '' }
+});
+
function formatUptime(seconds) {
if (!seconds) return '0s';
var d = Math.floor(seconds / 86400);
@@ -133,9 +161,15 @@ return baseclass.extend({
getModules: callModules,
getModulesByCategory: callModulesByCategory,
getModuleInfo: callModuleInfo,
+ // DEPRECATED: Use enable/disable instead
startModule: callStartModule,
stopModule: callStopModule,
restartModule: callRestartModule,
+ // NEW v0.3.1: Enable/Disable methods
+ enableModule: callEnableModule,
+ disableModule: callDisableModule,
+ checkModuleEnabled: callCheckModuleEnabled,
+ // Health & diagnostics
getHealth: callHealth,
getDiagnostics: callDiagnostics,
getSystemHealth: callSystemHealth,
@@ -145,6 +179,8 @@ return baseclass.extend({
getTheme: callGetTheme,
dismissAlert: callDismissAlert,
clearAlerts: callClearAlerts,
+ fixPermissions: callFixPermissions,
+ // Utilities
formatUptime: formatUptime,
formatBytes: formatBytes
});
diff --git a/luci-app-secubox/htdocs/luci-static/resources/secubox/dashboard.css b/luci-app-secubox/htdocs/luci-static/resources/secubox/dashboard.css
index e77cabcc..9db6bb00 100644
--- a/luci-app-secubox/htdocs/luci-static/resources/secubox/dashboard.css
+++ b/luci-app-secubox/htdocs/luci-static/resources/secubox/dashboard.css
@@ -1,4 +1,5 @@
-/* SecuBox Dashboard Styles * Version: 0.3.0
+/* SecuBox Dashboard Styles
+ * Version: 0.3.1
*/
.secubox-dashboard {
@@ -301,6 +302,28 @@
display: inline-block;
}
+/* Module Status Indicators (v0.3.1) */
+.secubox-status-dot.secubox-status-active {
+ background: #22c55e;
+ box-shadow: 0 0 8px rgba(34, 197, 94, 0.5);
+ animation: pulse 2s infinite;
+}
+
+.secubox-status-dot.secubox-status-disabled {
+ background: #94a3b8;
+}
+
+.secubox-status-dot.secubox-status-error {
+ background: #f59e0b;
+ box-shadow: 0 0 8px rgba(245, 158, 11, 0.5);
+}
+
+.secubox-status-dot.secubox-status-unknown {
+ background: #94a3b8;
+ opacity: 0.5;
+}
+
+/* DEPRECATED: Keeping for backward compatibility */
.secubox-status-dot.secubox-status-running {
background: #22c55e;
box-shadow: 0 0 8px rgba(34, 197, 94, 0.5);
@@ -334,6 +357,27 @@
color: var(--sb-text-muted);
}
+/* Module Status Colors (v0.3.1) */
+.secubox-module-active .secubox-module-mini-status {
+ color: #22c55e;
+ font-weight: 500;
+}
+
+.secubox-module-disabled .secubox-module-mini-status {
+ color: #94a3b8;
+}
+
+.secubox-module-error .secubox-module-mini-status {
+ color: #f59e0b;
+ font-weight: 500;
+}
+
+.secubox-module-unknown .secubox-module-mini-status {
+ color: #94a3b8;
+ opacity: 0.7;
+}
+
+/* DEPRECATED: Keeping for backward compatibility */
.secubox-module-running .secubox-module-mini-status {
color: #22c55e;
}
diff --git a/luci-app-secubox/htdocs/luci-static/resources/view/secubox/dashboard.js b/luci-app-secubox/htdocs/luci-static/resources/view/secubox/dashboard.js
index d7a7eec3..724eb203 100644
--- a/luci-app-secubox/htdocs/luci-static/resources/view/secubox/dashboard.js
+++ b/luci-app-secubox/htdocs/luci-static/resources/view/secubox/dashboard.js
@@ -256,10 +256,20 @@ return view.extend({
};
var moduleCards = filteredModules.map(function(module) {
- var isRunning = module.running;
- var statusClass = isRunning ? 'running' : 'stopped';
+ var status = module.status || 'unknown';
+ var statusClass = status;
var dashboardPath = modulePaths[module.id] || ('admin/secubox/' + module.id);
+ // Status label mapping (v0.3.1)
+ var statusLabels = {
+ 'active': 'â ActivĂ©',
+ 'disabled': 'â DĂ©sactivĂ©',
+ 'error': 'â ïž Erreur',
+ 'unknown': '? Inconnu'
+ };
+
+ var statusLabel = statusLabels[status] || 'â DĂ©sactivĂ©';
+
return E('a', {
'href': L.url(dashboardPath),
'class': 'secubox-module-link secubox-module-' + statusClass
@@ -272,13 +282,12 @@ return view.extend({
E('span', { 'class': 'secubox-module-mini-icon' }, module.icon || 'đŠ'),
E('span', {
'class': 'secubox-status-dot secubox-status-' + statusClass,
- 'title': isRunning ? 'Running' : 'Stopped'
+ 'title': statusLabel
})
]),
E('div', { 'class': 'secubox-module-mini-body' }, [
E('div', { 'class': 'secubox-module-mini-name' }, module.name || module.id),
- E('div', { 'class': 'secubox-module-mini-status' },
- isRunning ? 'â Running' : 'â Stopped')
+ E('div', { 'class': 'secubox-module-mini-status' }, statusLabel)
])
])
]);
@@ -367,10 +376,20 @@ return view.extend({
};
var moduleCards = filteredModules.map(function(module) {
- var isRunning = module.running;
- var statusClass = isRunning ? 'running' : 'stopped';
+ var status = module.status || 'unknown';
+ var statusClass = status;
var dashboardPath = modulePaths[module.id] || ('admin/secubox/' + module.id);
+ // Status label mapping (v0.3.1)
+ var statusLabels = {
+ 'active': 'â ActivĂ©',
+ 'disabled': 'â DĂ©sactivĂ©',
+ 'error': 'â ïž Erreur',
+ 'unknown': '? Inconnu'
+ };
+
+ var statusLabel = statusLabels[status] || 'â DĂ©sactivĂ©';
+
return E('a', {
'href': L.url(dashboardPath),
'class': 'secubox-module-link secubox-module-' + statusClass
@@ -383,13 +402,12 @@ return view.extend({
E('span', { 'class': 'secubox-module-mini-icon' }, module.icon || 'đŠ'),
E('span', {
'class': 'secubox-status-dot secubox-status-' + statusClass,
- 'title': isRunning ? 'Running' : 'Stopped'
+ 'title': statusLabel
})
]),
E('div', { 'class': 'secubox-module-mini-body' }, [
E('div', { 'class': 'secubox-module-mini-name' }, module.name || module.id),
- E('div', { 'class': 'secubox-module-mini-status' },
- isRunning ? 'â Running' : 'â Stopped')
+ E('div', { 'class': 'secubox-module-mini-status' }, statusLabel)
])
])
]);
@@ -443,6 +461,20 @@ return view.extend({
]);
});
+ // Add Fix Permissions button (v0.3.1)
+ buttons.push(
+ E('button', {
+ 'class': 'secubox-action-btn',
+ 'style': 'border-color: #f97316',
+ 'click': function() {
+ self.executeFixPermissions();
+ }
+ }, [
+ E('span', { 'class': 'secubox-action-icon' }, 'đ§'),
+ E('span', { 'class': 'secubox-action-label' }, 'Fix Perms')
+ ])
+ );
+
return E('div', { 'class': 'secubox-card' }, [
E('h3', { 'class': 'secubox-card-title' }, '⥠Quick Actions'),
E('div', { 'class': 'secubox-actions-grid' }, buttons)
@@ -474,6 +506,38 @@ return view.extend({
});
},
+ executeFixPermissions: function() {
+ var self = this;
+ ui.showModal(_('Fixing Permissions'), [
+ E('p', { 'class': 'spinning' }, _('Fixing file permissions and restarting services...'))
+ ]);
+
+ API.fixPermissions().then(function(result) {
+ ui.hideModal();
+ if (result && result.success) {
+ ui.addNotification(null, E('p', 'â Permissions fixed successfully'), 'info');
+ // Show output in console
+ if (result.output) {
+ console.log('Fix Permissions Output:\n' + result.output);
+ }
+ // Refresh data after fixing permissions
+ setTimeout(function() {
+ self.refreshData().then(function() {
+ self.updateDynamicElements();
+ });
+ }, 2000);
+ } else {
+ ui.addNotification(null, E('p', 'â ' + (result.message || 'Failed to fix permissions')), 'error');
+ if (result.output) {
+ console.error('Fix Permissions Error:\n' + result.output);
+ }
+ }
+ }).catch(function(err) {
+ ui.hideModal();
+ ui.addNotification(null, E('p', 'Error: ' + err.message), 'error');
+ });
+ },
+
renderAlerts: function(alerts) {
if (!alerts || alerts.length === 0) {
return E('div', { 'class': 'secubox-card' }, [
diff --git a/luci-app-secubox/htdocs/luci-static/resources/view/secubox/modules.js b/luci-app-secubox/htdocs/luci-static/resources/view/secubox/modules.js
index 85106506..fc408621 100644
--- a/luci-app-secubox/htdocs/luci-static/resources/view/secubox/modules.js
+++ b/luci-app-secubox/htdocs/luci-static/resources/view/secubox/modules.js
@@ -66,8 +66,8 @@ return view.extend({
renderHeader: function(modules) {
var total = modules.length;
var installed = modules.filter(function(m) { return m.installed; }).length;
- var running = modules.filter(function(m) { return m.running; }).length;
- var stopped = installed - running;
+ var enabled = modules.filter(function(m) { return m.enabled; }).length;
+ var disabled = installed - enabled;
return E('div', { 'class': 'secubox-page-header' }, [
E('div', {}, [
@@ -81,12 +81,12 @@ return view.extend({
E('span', { 'class': 'secubox-stat-label' }, 'Total')
]),
E('div', { 'class': 'secubox-stat-badge secubox-stat-success' }, [
- E('span', { 'class': 'secubox-stat-value' }, running),
- E('span', { 'class': 'secubox-stat-label' }, 'Running')
+ 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' }, stopped),
- E('span', { 'class': 'secubox-stat-label' }, 'Stopped')
+ 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),
@@ -144,9 +144,20 @@ return view.extend({
renderModuleCard: function(module) {
var self = this;
- var isRunning = module.running;
+ var status = module.status || 'unknown';
var isInstalled = module.installed;
- var statusClass = isRunning ? 'running' : (isInstalled ? 'stopped' : 'not-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,
@@ -166,7 +177,7 @@ return view.extend({
]),
E('div', {
'class': 'secubox-status-indicator secubox-status-' + statusClass,
- 'title': isRunning ? 'Running' : (isInstalled ? 'Stopped' : 'Not Installed')
+ 'title': statusLabel
})
]),
@@ -184,7 +195,7 @@ return view.extend({
E('span', { 'class': 'secubox-detail-label' }, 'Status:'),
E('span', {
'class': 'secubox-detail-value secubox-status-text-' + statusClass
- }, isRunning ? 'â Running' : (isInstalled ? 'â Stopped' : '- Not Installed'))
+ }, statusLabel)
])
])
]),
@@ -207,36 +218,24 @@ return view.extend({
}, 'đ„ Install')
);
} else {
- // Start/Stop button
- if (module.running) {
+ // 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.stopModule(module);
+ self.disableModule(module);
}
- }, 'âčïž Stop')
+ }, 'âčïž DĂ©sactiver')
);
} else {
actions.push(
E('button', {
'class': 'secubox-btn secubox-btn-success secubox-btn-sm',
'click': function() {
- self.startModule(module);
+ self.enableModule(module);
}
- }, 'â¶ïž Start')
- );
- }
-
- // Restart button (only if running)
- if (module.running) {
- actions.push(
- E('button', {
- 'class': 'secubox-btn secubox-btn-warning secubox-btn-sm',
- 'click': function() {
- self.restartModule(module);
- }
- }, 'đ Restart')
+ }, 'â¶ïž Activer')
);
}
@@ -286,69 +285,65 @@ return view.extend({
return icons[category] || icons['other'];
},
- startModule: function(module) {
+ // Enable module (v0.3.1)
+ enableModule: function(module) {
var self = this;
- ui.showModal(_('Starting Module'), [
- E('p', {}, _('Starting') + ' ' + module.name + '...')
+ ui.showModal(_('Activation du module'), [
+ E('p', {}, 'Activation de ' + module.name + '...')
]);
- API.startModule(module.id).then(function(result) {
+ API.enableModule(module.id).then(function(result) {
ui.hideModal();
if (result && result.success !== false) {
- ui.addNotification(null, E('p', module.name + ' started successfully'), 'info');
+ ui.addNotification(null, E('p', module.name + ' activé avec succÚs'), 'info');
self.refreshData().then(function() {
self.updateModulesGrid();
});
} else {
- ui.addNotification(null, E('p', 'Failed to start ' + module.name), 'error');
+ ui.addNotification(null, E('p', 'Ăchec de l\'activation de ' + module.name), 'error');
}
}).catch(function(err) {
ui.hideModal();
- ui.addNotification(null, E('p', 'Error: ' + err.message), 'error');
+ ui.addNotification(null, E('p', 'Erreur: ' + err.message), 'error');
});
},
- stopModule: function(module) {
+ // Disable module (v0.3.1)
+ disableModule: function(module) {
var self = this;
- ui.showModal(_('Stopping Module'), [
- E('p', {}, _('Stopping') + ' ' + module.name + '...')
+ ui.showModal(_('Désactivation du module'), [
+ E('p', {}, 'Désactivation de ' + module.name + '...')
]);
- API.stopModule(module.id).then(function(result) {
+ API.disableModule(module.id).then(function(result) {
ui.hideModal();
if (result && result.success !== false) {
- ui.addNotification(null, E('p', module.name + ' stopped successfully'), 'info');
+ 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', 'Failed to stop ' + module.name), 'error');
+ ui.addNotification(null, E('p', 'Ăchec de la dĂ©sactivation de ' + module.name), 'error');
}
}).catch(function(err) {
ui.hideModal();
- ui.addNotification(null, E('p', 'Error: ' + err.message), 'error');
+ 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;
- ui.showModal(_('Restarting Module'), [
- E('p', {}, _('Restarting') + ' ' + module.name + '...')
- ]);
-
- API.restartModule(module.id).then(function(result) {
- ui.hideModal();
- if (result && result.success !== false) {
- ui.addNotification(null, E('p', module.name + ' restarted successfully'), 'info');
- self.refreshData().then(function() {
- self.updateModulesGrid();
- });
- } else {
- ui.addNotification(null, E('p', 'Failed to restart ' + module.name), 'error');
- }
- }).catch(function(err) {
- ui.hideModal();
- ui.addNotification(null, E('p', 'Error: ' + err.message), 'error');
+ return this.disableModule(module).then(function() {
+ return self.enableModule(module);
});
},
diff --git a/luci-app-secubox/root/usr/libexec/rpcd/luci.secubox b/luci-app-secubox/root/usr/libexec/rpcd/luci.secubox
index 4cb0e758..c03f3fb2 100755
--- a/luci-app-secubox/root/usr/libexec/rpcd/luci.secubox
+++ b/luci-app-secubox/root/usr/libexec/rpcd/luci.secubox
@@ -44,14 +44,25 @@ check_module_installed() {
fi
}
+# Check if a module is enabled (UCI config)
+check_module_enabled() {
+ local module="$1"
+ local enabled
+
+ config_load secubox
+ config_get enabled "$module" enabled "1"
+
+ echo "$enabled"
+}
+
# Check if a module service is running
check_module_running() {
local module="$1"
local config
-
+
config_load secubox
config_get config "$module" config ""
-
+
case "$module" in
crowdsec)
pgrep -f crowdsec > /dev/null 2>&1 && echo "1" || echo "0"
@@ -79,6 +90,22 @@ check_module_running() {
esac
}
+# Determine module status based on enabled + running
+get_module_status() {
+ local enabled="$1"
+ local running="$2"
+
+ if [ "$enabled" = "1" ] && [ "$running" = "1" ]; then
+ echo "active"
+ elif [ "$enabled" = "1" ] && [ "$running" = "0" ]; then
+ echo "error"
+ elif [ "$enabled" = "0" ] && [ "$running" = "0" ]; then
+ echo "disabled"
+ else
+ echo "unknown"
+ fi
+}
+
# Get overall system status
get_status() {
local total=0
@@ -159,7 +186,9 @@ get_modules() {
config_get version "$module" version "0.0.9"
local is_installed=$(check_module_installed "$module")
+ local is_enabled=$(check_module_enabled "$module")
local is_running=$(check_module_running "$module")
+ local status=$(get_module_status "$is_enabled" "$is_running")
# Get real version from opkg if installed
if [ "$is_installed" = "1" ] && [ -n "$package" ]; then
@@ -178,7 +207,9 @@ get_modules() {
json_add_string "config" "$config"
json_add_string "version" "$version"
json_add_boolean "installed" "$is_installed"
+ json_add_boolean "enabled" "$is_enabled"
json_add_boolean "running" "$is_running"
+ json_add_string "status" "$status"
json_add_boolean "in_uci" "1"
json_close_object
@@ -213,7 +244,9 @@ get_modules() {
esac
local clean_module=$(echo "$module_id" | sed 's/_/-/g')
+ local is_enabled=$(check_module_enabled "$module_id")
local is_running=$(check_module_running "$module_id")
+ local status=$(get_module_status "$is_enabled" "$is_running")
json_add_object ""
json_add_string "id" "$module_id"
@@ -226,7 +259,9 @@ get_modules() {
json_add_string "config" "$clean_module"
json_add_string "version" "$version"
json_add_boolean "installed" "1"
+ json_add_boolean "enabled" "$is_enabled"
json_add_boolean "running" "$is_running"
+ json_add_string "status" "$status"
json_add_boolean "in_uci" "0"
json_close_object
done
@@ -265,7 +300,9 @@ get_modules_by_category() {
config_get version "$module" version "0.0.9"
local is_installed=$(check_module_installed "$module")
+ local is_enabled=$(check_module_enabled "$module")
local is_running=$(check_module_running "$module")
+ local status=$(get_module_status "$is_enabled" "$is_running")
# Get real version from opkg if installed
if [ "$is_installed" = "1" ] && [ -n "$package" ]; then
@@ -282,7 +319,9 @@ get_modules_by_category() {
json_add_string "version" "$version"
json_add_string "package" "$package"
json_add_boolean "installed" "$is_installed"
+ json_add_boolean "enabled" "$is_enabled"
json_add_boolean "running" "$is_running"
+ json_add_string "status" "$status"
json_add_boolean "in_uci" "1"
json_close_object
@@ -321,7 +360,9 @@ get_modules_by_category() {
name=$(echo "$package" | sed 's/^luci-app-//' | sed 's/-dashboard$//' | sed 's/-/ /g' | sed 's/\b\(.\)/\u\1/g')
local is_installed="1" # Must be installed if detected
+ local is_enabled=$(check_module_enabled "$module_id")
local is_running=$(check_module_running "$module_id")
+ local status=$(get_module_status "$is_enabled" "$is_running")
json_add_object ""
json_add_string "id" "$module_id"
@@ -333,7 +374,9 @@ get_modules_by_category() {
json_add_string "package" "$package"
json_add_string "category" "$mod_category"
json_add_boolean "installed" "$is_installed"
+ json_add_boolean "enabled" "$is_enabled"
json_add_boolean "running" "$is_running"
+ json_add_string "status" "$status"
json_add_boolean "in_uci" "0"
json_close_object
fi
@@ -361,7 +404,9 @@ get_module_info() {
config_get version "$module" version "0.0.9"
local is_installed=$(check_module_installed "$module")
+ local is_enabled=$(check_module_enabled "$module")
local is_running=$(check_module_running "$module")
+ local status=$(get_module_status "$is_enabled" "$is_running")
json_init
json_add_string "id" "$module"
@@ -374,7 +419,9 @@ get_module_info() {
json_add_string "config" "$config"
json_add_string "version" "$version"
json_add_boolean "installed" "$is_installed"
+ json_add_boolean "enabled" "$is_enabled"
json_add_boolean "running" "$is_running"
+ json_add_string "status" "$status"
json_dump
}
@@ -422,14 +469,14 @@ stop_module() {
fi
}
-# Restart a module
+# Restart a module (DEPRECATED - use disable/enable instead)
restart_module() {
local module="$1"
local config
-
+
config_load secubox
config_get config "$module" config ""
-
+
if [ -x "/etc/init.d/${config}" ]; then
/etc/init.d/${config} restart
json_init
@@ -444,6 +491,64 @@ restart_module() {
fi
}
+# Enable a module (NEW v0.3.1)
+enable_module() {
+ local module="$1"
+ local config
+
+ config_load secubox
+ config_get config "$module" config ""
+
+ # Set enabled flag in UCI
+ uci set secubox.${module}.enabled='1'
+ uci commit secubox
+
+ # Enable and start service if init script exists
+ if [ -x "/etc/init.d/${config}" ]; then
+ /etc/init.d/${config} enable
+ /etc/init.d/${config} start
+
+ json_init
+ json_add_boolean "success" 1
+ json_add_string "message" "Module activé"
+ json_dump
+ else
+ json_init
+ json_add_boolean "success" 0
+ json_add_string "message" "Init script not found"
+ json_dump
+ fi
+}
+
+# Disable a module (NEW v0.3.1)
+disable_module() {
+ local module="$1"
+ local config
+
+ config_load secubox
+ config_get config "$module" config ""
+
+ # Set disabled flag in UCI
+ uci set secubox.${module}.enabled='0'
+ uci commit secubox
+
+ # Disable and stop service if init script exists
+ if [ -x "/etc/init.d/${config}" ]; then
+ /etc/init.d/${config} stop
+ /etc/init.d/${config} disable
+
+ json_init
+ json_add_boolean "success" 1
+ json_add_string "message" "Module désactivé"
+ json_dump
+ else
+ json_init
+ json_add_boolean "success" 0
+ json_add_string "message" "Init script not found"
+ json_dump
+ fi
+}
+
# Get health report
get_health() {
json_init
@@ -752,12 +857,16 @@ get_dashboard_data() {
config_get color "$module" color "#64748b"
local is_installed=$(check_module_installed "$module")
+ local is_enabled="0"
local is_running="0"
+ local status="disabled"
total=$((total + 1))
if [ "$is_installed" = "1" ]; then
+ is_enabled=$(check_module_enabled "$module")
is_running=$(check_module_running "$module")
+ status=$(get_module_status "$is_enabled" "$is_running")
installed=$((installed + 1))
[ "$is_running" = "1" ] && running=$((running + 1))
fi
@@ -770,7 +879,9 @@ get_dashboard_data() {
json_add_string "icon" "$icon"
json_add_string "color" "$color"
json_add_boolean "installed" "$is_installed"
+ json_add_boolean "enabled" "$is_enabled"
json_add_boolean "running" "$is_running"
+ json_add_string "status" "$status"
json_close_object
done
json_close_array
@@ -844,6 +955,34 @@ clear_alerts() {
json_dump
}
+# Fix permissions (v0.3.1)
+fix_permissions() {
+ local fix_script="/usr/libexec/secubox/fix-permissions.sh"
+
+ if [ ! -x "$fix_script" ]; then
+ json_init
+ json_add_boolean "success" 0
+ json_add_string "message" "Fix script not found or not executable"
+ json_dump
+ return 1
+ fi
+
+ # Run the fix script and capture output
+ local output=$($fix_script 2>&1)
+ local exit_code=$?
+
+ json_init
+ if [ $exit_code -eq 0 ]; then
+ json_add_boolean "success" 1
+ json_add_string "message" "Permissions fixed successfully"
+ else
+ json_add_boolean "success" 0
+ json_add_string "message" "Failed to fix permissions"
+ fi
+ json_add_string "output" "$output"
+ json_dump
+}
+
# Main dispatcher
case "$1" in
list)
@@ -867,6 +1006,15 @@ case "$1" in
json_add_object "restart_module"
json_add_string "module" "string"
json_close_object
+ json_add_object "enable_module"
+ json_add_string "module" "string"
+ json_close_object
+ json_add_object "disable_module"
+ json_add_string "module" "string"
+ json_close_object
+ json_add_object "check_module_enabled"
+ json_add_string "module" "string"
+ json_close_object
json_add_object "health"
json_close_object
json_add_object "diagnostics"
@@ -887,6 +1035,8 @@ case "$1" in
json_close_object
json_add_object "clear_alerts"
json_close_object
+ json_add_object "fix_permissions"
+ json_close_object
json_dump
;;
call)
@@ -927,6 +1077,27 @@ case "$1" in
json_get_var module module ""
restart_module "$module"
;;
+ enable_module)
+ read -r input
+ json_load "$input"
+ json_get_var module module ""
+ enable_module "$module"
+ ;;
+ disable_module)
+ read -r input
+ json_load "$input"
+ json_get_var module module ""
+ disable_module "$module"
+ ;;
+ check_module_enabled)
+ read -r input
+ json_load "$input"
+ json_get_var module module ""
+ local enabled=$(check_module_enabled "$module")
+ json_init
+ json_add_boolean "enabled" "$enabled"
+ json_dump
+ ;;
health)
get_health
;;
@@ -960,6 +1131,9 @@ case "$1" in
clear_alerts)
clear_alerts
;;
+ fix_permissions)
+ fix_permissions
+ ;;
*)
echo '{"error":"Unknown method"}'
;;
diff --git a/luci-app-secubox/root/usr/libexec/secubox/fix-permissions.sh b/luci-app-secubox/root/usr/libexec/secubox/fix-permissions.sh
new file mode 100644
index 00000000..53c79abc
--- /dev/null
+++ b/luci-app-secubox/root/usr/libexec/secubox/fix-permissions.sh
@@ -0,0 +1,169 @@
+#!/bin/sh
+# SPDX-License-Identifier: Apache-2.0
+# SecuBox Permissions Fix Script
+# Automatically fixes file permissions for SecuBox modules
+# Copyright (C) 2025 CyberMind.fr
+
+set -e
+
+LOG_TAG="secubox-fix-perms"
+
+log_info() {
+ logger -t "$LOG_TAG" -p info "$1"
+ echo "[INFO] $1"
+}
+
+log_error() {
+ logger -t "$LOG_TAG" -p err "$1"
+ echo "[ERROR] $1" >&2
+}
+
+fix_rpcd_permissions() {
+ log_info "Fixing RPCD script permissions..."
+
+ local rpcd_scripts="
+ /usr/libexec/rpcd/luci.secubox
+ /usr/libexec/rpcd/luci.system-hub
+ /usr/libexec/rpcd/luci.network-modes
+ /usr/libexec/rpcd/luci.crowdsec-dashboard
+ /usr/libexec/rpcd/luci.netdata-dashboard
+ /usr/libexec/rpcd/luci.netifyd-dashboard
+ /usr/libexec/rpcd/luci.wireguard-dashboard
+ /usr/libexec/rpcd/luci.client-guardian
+ /usr/libexec/rpcd/luci.bandwidth-manager
+ /usr/libexec/rpcd/luci.auth-guardian
+ /usr/libexec/rpcd/luci.media-flow
+ /usr/libexec/rpcd/luci.vhost-manager
+ /usr/libexec/rpcd/luci.traffic-shaper
+ /usr/libexec/rpcd/luci.cdn-cache
+ /usr/libexec/rpcd/luci.ksm-manager
+ "
+
+ local count=0
+ for script in $rpcd_scripts; do
+ if [ -f "$script" ]; then
+ chmod 755 "$script" 2>/dev/null && count=$((count + 1))
+ fi
+ done
+
+ log_info "Fixed $count RPCD scripts (755)"
+}
+
+fix_web_permissions() {
+ log_info "Fixing web resources permissions..."
+
+ # Fix CSS files
+ local css_count=0
+ if [ -d "/www/luci-static/resources" ]; then
+ css_count=$(find /www/luci-static/resources -type f -name "*.css" -exec chmod 644 {} \; -print 2>/dev/null | wc -l)
+ fi
+ log_info "Fixed $css_count CSS files (644)"
+
+ # Fix JS files
+ local js_count=0
+ if [ -d "/www/luci-static/resources" ]; then
+ js_count=$(find /www/luci-static/resources -type f -name "*.js" -exec chmod 644 {} \; -print 2>/dev/null | wc -l)
+ fi
+ log_info "Fixed $js_count JS files (644)"
+}
+
+fix_config_permissions() {
+ log_info "Fixing configuration file permissions..."
+
+ local count=0
+
+ # Fix ACL files
+ for acl in /usr/share/rpcd/acl.d/luci-app-*.json; do
+ if [ -f "$acl" ]; then
+ chmod 644 "$acl" 2>/dev/null && count=$((count + 1))
+ fi
+ done
+
+ # Fix menu files
+ for menu in /usr/share/luci/menu.d/luci-app-*.json; do
+ if [ -f "$menu" ]; then
+ chmod 644 "$menu" 2>/dev/null && count=$((count + 1))
+ fi
+ done
+
+ log_info "Fixed $count config files (644)"
+}
+
+restart_services() {
+ log_info "Restarting services..."
+
+ if /etc/init.d/rpcd restart >/dev/null 2>&1; then
+ log_info "RPCD restarted successfully"
+ else
+ log_error "Failed to restart RPCD"
+ fi
+
+ if /etc/init.d/uhttpd restart >/dev/null 2>&1; then
+ log_info "uHTTPd restarted successfully"
+ else
+ log_error "Failed to restart uHTTPd"
+ fi
+}
+
+verify_permissions() {
+ log_info "Verifying permissions..."
+
+ local errors=0
+
+ # Check RPCD scripts
+ for script in /usr/libexec/rpcd/luci.*; do
+ if [ -f "$script" ]; then
+ local perms=$(ls -l "$script" 2>/dev/null | cut -c1-10)
+ if [ "$perms" != "-rwxr-xr-x" ]; then
+ log_error "Invalid permissions on $script: $perms (expected -rwxr-xr-x)"
+ errors=$((errors + 1))
+ fi
+ fi
+ done
+
+ # Check critical files
+ for file in /www/luci-static/resources/secubox/api.js /www/luci-static/resources/secubox/dashboard.css; do
+ if [ -f "$file" ]; then
+ local perms=$(ls -l "$file" 2>/dev/null | cut -c1-10)
+ if [ "$perms" != "-rw-r--r--" ]; then
+ log_error "Invalid permissions on $file: $perms (expected -rw-r--r--)"
+ errors=$((errors + 1))
+ fi
+ fi
+ done
+
+ if [ $errors -eq 0 ]; then
+ log_info "All permissions verified successfully"
+ return 0
+ else
+ log_error "Found $errors permission errors"
+ return 1
+ fi
+}
+
+main() {
+ echo "================================================"
+ echo " SecuBox Permissions Fix Script v0.3.1"
+ echo "================================================"
+ echo ""
+
+ fix_rpcd_permissions
+ fix_web_permissions
+ fix_config_permissions
+
+ echo ""
+ restart_services
+
+ echo ""
+ verify_permissions
+
+ echo ""
+ echo "================================================"
+ echo " Permissions fix completed!"
+ echo "================================================"
+}
+
+# Run if executed directly
+if [ "${0##*/}" = "fix-permissions.sh" ]; then
+ main "$@"
+fi
diff --git a/luci-app-secubox/root/usr/share/rpcd/acl.d/luci-app-secubox.json b/luci-app-secubox/root/usr/share/rpcd/acl.d/luci-app-secubox.json
index 7f0ab1d2..0c8335f4 100644
--- a/luci-app-secubox/root/usr/share/rpcd/acl.d/luci-app-secubox.json
+++ b/luci-app-secubox/root/usr/share/rpcd/acl.d/luci-app-secubox.json
@@ -8,6 +8,7 @@
"modules",
"modules_by_category",
"module_info",
+ "check_module_enabled",
"health",
"diagnostics",
"get_system_health",
@@ -30,9 +31,12 @@
"start_module",
"stop_module",
"restart_module",
+ "enable_module",
+ "disable_module",
"quick_action",
"dismiss_alert",
- "clear_alerts"
+ "clear_alerts",
+ "fix_permissions"
],
"uci": [
"set",
diff --git a/luci-app-system-hub/Makefile b/luci-app-system-hub/Makefile
index 6cdac4af..ad8f4c73 100644
--- a/luci-app-system-hub/Makefile
+++ b/luci-app-system-hub/Makefile
@@ -11,6 +11,9 @@ LUCI_DESCRIPTION:=Central system control with monitoring, services, logs, and ba
LUCI_DEPENDS:=+luci-base +rpcd +coreutils +coreutils-base64
LUCI_PKGARCH:=all
+# File permissions (RPCD scripts must be executable)
+PKG_FILE_MODES:=/usr/libexec/rpcd/luci.system-hub:755
+
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature
diff --git a/luci-app-traffic-shaper/Makefile b/luci-app-traffic-shaper/Makefile
index 4e844962..2bda9cc2 100644
--- a/luci-app-traffic-shaper/Makefile
+++ b/luci-app-traffic-shaper/Makefile
@@ -10,6 +10,10 @@ LUCI_TITLE:=Traffic Shaper - Advanced QoS Control
LUCI_DESCRIPTION:=Advanced traffic shaping with TC/CAKE for precise bandwidth control
LUCI_DEPENDS:=+luci-base +rpcd +tc +kmod-sched-core +kmod-sched-cake
+
+# File permissions (RPCD scripts must be executable)
+PKG_FILE_MODES:=/usr/libexec/rpcd/luci.traffic-shaper:755
+
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature
diff --git a/luci-app-vhost-manager/Makefile b/luci-app-vhost-manager/Makefile
index 966f6c89..a5c20e86 100644
--- a/luci-app-vhost-manager/Makefile
+++ b/luci-app-vhost-manager/Makefile
@@ -14,6 +14,10 @@ LUCI_DESCRIPTION:=Nginx reverse proxy manager with Let's Encrypt SSL certificate
LUCI_DEPENDS:=+luci-base +rpcd +nginx-ssl +acme +curl
LUCI_PKGARCH:=all
+
+# File permissions (RPCD scripts must be executable)
+PKG_FILE_MODES:=/usr/libexec/rpcd/luci.vhost-manager:755
+
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature
diff --git a/luci-app-wireguard-dashboard/Makefile b/luci-app-wireguard-dashboard/Makefile
index 473c4f2c..41fad86b 100644
--- a/luci-app-wireguard-dashboard/Makefile
+++ b/luci-app-wireguard-dashboard/Makefile
@@ -20,6 +20,10 @@ LUCI_DEPENDS:=+luci-base +luci-app-secubox +luci-lib-jsonc +rpcd +rpcd-mod-luci
LUCI_PKGARCH:=all
+
+# File permissions (RPCD scripts must be executable)
+PKG_FILE_MODES:=/usr/libexec/rpcd/luci.wireguard-dashboard:755
+
include $(TOPDIR)/feeds/luci/luci.mk
define Package/$(PKG_NAME)/conffiles
diff --git a/secubox-tools/add-pkg-file-modes.sh b/secubox-tools/add-pkg-file-modes.sh
new file mode 100755
index 00000000..ecd86794
--- /dev/null
+++ b/secubox-tools/add-pkg-file-modes.sh
@@ -0,0 +1,209 @@
+#!/bin/bash
+# Add PKG_FILE_MODES to OpenWrt Makefiles
+# Automatically detects RPCD scripts and adds correct permissions
+#
+# Usage: ./add-pkg-file-modes.sh [module-path]
+# ./add-pkg-file-modes.sh --all (process all luci-app-* modules)
+
+set -e
+
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
+
+# Colors
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+BLUE='\033[0;34m'
+NC='\033[0m' # No Color
+
+log_info() {
+ echo -e "${BLUE}[INFO]${NC} $1"
+}
+
+log_success() {
+ echo -e "${GREEN}[OK]${NC} $1"
+}
+
+log_warning() {
+ echo -e "${YELLOW}[WARN]${NC} $1"
+}
+
+log_error() {
+ echo -e "${RED}[ERROR]${NC} $1"
+}
+
+# Detect RPCD scripts in a module
+detect_rpcd_scripts() {
+ local module_path="$1"
+ local rpcd_dir="$module_path/root/usr/libexec/rpcd"
+
+ if [ ! -d "$rpcd_dir" ]; then
+ return
+ fi
+
+ find "$rpcd_dir" -type f -name "luci.*" 2>/dev/null | while read script; do
+ basename "$script"
+ done
+}
+
+# Detect helper scripts in a module
+detect_helper_scripts() {
+ local module_path="$1"
+ local helper_dir="$module_path/root/usr/libexec"
+
+ if [ ! -d "$helper_dir" ]; then
+ return
+ fi
+
+ # Find .sh files in subdirectories (not in rpcd/)
+ find "$helper_dir" -type f -name "*.sh" ! -path "*/rpcd/*" 2>/dev/null | while read script; do
+ echo "$script" | sed "s|^$module_path/root||"
+ done
+}
+
+# Generate PKG_FILE_MODES line
+generate_pkg_file_modes() {
+ local module_name="$1"
+ local rpcd_scripts="$2"
+ local helper_scripts="$3"
+
+ local modes=""
+
+ # Add RPCD scripts
+ for script in $rpcd_scripts; do
+ if [ -z "$modes" ]; then
+ modes="/usr/libexec/rpcd/$script:755"
+ else
+ modes="$modes \\\\\n\\t/usr/libexec/rpcd/$script:755"
+ fi
+ done
+
+ # Add helper scripts
+ for script in $helper_scripts; do
+ if [ -z "$modes" ]; then
+ modes="$script:755"
+ else
+ modes="$modes \\\\\n\\t$script:755"
+ fi
+ done
+
+ if [ -n "$modes" ]; then
+ echo -e "# File permissions (RPCD scripts must be executable)\nPKG_FILE_MODES:=$modes"
+ fi
+}
+
+# Check if Makefile already has PKG_FILE_MODES
+has_pkg_file_modes() {
+ local makefile="$1"
+ grep -q "^PKG_FILE_MODES:=" "$makefile" 2>/dev/null
+}
+
+# Add PKG_FILE_MODES to Makefile
+add_to_makefile() {
+ local makefile="$1"
+ local pkg_file_modes="$2"
+
+ if [ ! -f "$makefile" ]; then
+ log_error "Makefile not found: $makefile"
+ return 1
+ fi
+
+ # Check if already exists
+ if has_pkg_file_modes "$makefile"; then
+ log_warning "PKG_FILE_MODES already exists in $makefile"
+ return 0
+ fi
+
+ # Find the line with 'include $(TOPDIR)/feeds/luci/luci.mk'
+ if ! grep -q "include.*luci.mk" "$makefile"; then
+ log_error "Cannot find luci.mk include in $makefile"
+ return 1
+ fi
+
+ # Create backup
+ cp "$makefile" "$makefile.bak"
+
+ # Insert PKG_FILE_MODES before the include line
+ awk -v modes="$pkg_file_modes" '
+ /^include.*luci\.mk/ {
+ print ""
+ print modes
+ print ""
+ }
+ { print }
+ ' "$makefile.bak" > "$makefile"
+
+ log_success "Added PKG_FILE_MODES to $makefile"
+ rm -f "$makefile.bak"
+}
+
+# Process a single module
+process_module() {
+ local module_path="$1"
+ local module_name=$(basename "$module_path")
+
+ log_info "Processing $module_name..."
+
+ # Detect scripts
+ local rpcd_scripts=$(detect_rpcd_scripts "$module_path")
+ local helper_scripts=$(detect_helper_scripts "$module_path")
+
+ if [ -z "$rpcd_scripts" ] && [ -z "$helper_scripts" ]; then
+ log_warning "No executable scripts found in $module_name"
+ return 0
+ fi
+
+ # Generate PKG_FILE_MODES
+ local pkg_file_modes=$(generate_pkg_file_modes "$module_name" "$rpcd_scripts" "$helper_scripts")
+
+ if [ -z "$pkg_file_modes" ]; then
+ log_warning "Could not generate PKG_FILE_MODES for $module_name"
+ return 0
+ fi
+
+ # Add to Makefile
+ local makefile="$module_path/Makefile"
+ add_to_makefile "$makefile" "$pkg_file_modes"
+
+ # Show what was added
+ echo ""
+ echo " Scripts found:"
+ [ -n "$rpcd_scripts" ] && echo " RPCD: $(echo $rpcd_scripts | tr '\n' ' ')"
+ [ -n "$helper_scripts" ] && echo " Helper: $(echo $helper_scripts | tr '\n' ' ')"
+ echo ""
+}
+
+# Main
+main() {
+ echo "================================================"
+ echo " PKG_FILE_MODES Auto-Configurator v0.3.1"
+ echo "================================================"
+ echo ""
+
+ if [ "$1" = "--all" ]; then
+ log_info "Processing all luci-app-* modules..."
+ echo ""
+
+ find "$PROJECT_ROOT" -maxdepth 1 -type d -name "luci-app-*" | sort | while read module_path; do
+ process_module "$module_path"
+ done
+
+ elif [ -n "$1" ] && [ -d "$1" ]; then
+ process_module "$1"
+
+ else
+ log_error "Usage: $0 [module-path] | --all"
+ log_info "Examples:"
+ log_info " $0 ../luci-app-secubox"
+ log_info " $0 --all"
+ exit 1
+ fi
+
+ echo ""
+ echo "================================================"
+ echo " Processing complete!"
+ echo "================================================"
+}
+
+main "$@"
diff --git a/templates/Makefile.template b/templates/Makefile.template
index 104b6f46..e07c6a95 100644
--- a/templates/Makefile.template
+++ b/templates/Makefile.template
@@ -14,9 +14,14 @@ PKG_MAINTAINER:=CyberMind
# LuCI specific
LUCI_TITLE:=LuCI - Package Description
LUCI_DESCRIPTION:=Detailed description of what this package does
-LUCI_DEPENDS:=+luci-base
+LUCI_DEPENDS:=+luci-base +rpcd
LUCI_PKGARCH:=all
+# File permissions (CRITICAL: RPCD scripts MUST be executable)
+# CSS/JS files are 644 by default (correct)
+# Only specify files that need non-default permissions (755 for executables)
+PKG_FILE_MODES:=/usr/libexec/rpcd/luci.PACKAGE_NAME:755
+
# Include LuCI build system
include $(TOPDIR)/feeds/luci/luci.mk