Added comprehensive automation for file permissions management to prevent HTTP 403 errors caused by incorrect permissions (600 instead of 644). 🆕 New Tool: fix-permissions.sh ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Automated script to fix and verify file permissions: Features: - Fixes local source permissions (--local) - Fixes remote router permissions (--remote) - Default: fixes both local and remote - Auto-verifies RPCD scripts (755) - Auto-verifies CSS files (644) - Auto-verifies JS files (644) - Clears cache and restarts services (remote) - Reports all changes made Usage: ./secubox-tools/fix-permissions.sh --local # Before commit ./secubox-tools/fix-permissions.sh --remote # After deploy ./secubox-tools/fix-permissions.sh # Both ✨ Enhanced: validate-modules.sh - Check 7 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Added comprehensive permission validation: Check 7: htdocs file permissions - Validates all CSS files have 644 permissions - Validates all JS files have 644 permissions - Reports files with wrong permissions - Suggests fix-permissions.sh for auto-correction - Counts permission errors in summary Total validation checks: 7 1. RPCD script names vs ubus objects 2. Menu paths vs view file locations 3. View files have menu entries 4. RPCD script permissions (755) 5. JSON syntax validation 6. ubus object naming convention 7. htdocs file permissions (644) ← NEW 📚 Documentation Updates ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ DEVELOPMENT-GUIDELINES.md: - Added "Correction Automatique" section with fix-permissions.sh - Added "Validation Automatique des Permissions" section - Added recommended workflow: fix → validate → commit → deploy → fix remote QUICK-START.md: - Updated Validation section with fix-permissions.sh - Updated Common Errors Quick Fix with automated script - Updated Pre-Commit Checklist with automated tools - Marked permissions as "auto-verified" in checklist CLAUDE.md: - Updated critical rules with auto-fix commands - Added 7 validation checks list - Enhanced Validation section with detailed check descriptions - Added fix-permissions.sh to workflow 🔧 Files Modified ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ New: + secubox-tools/fix-permissions.sh (executable) Modified: * secubox-tools/validate-modules.sh (Check 7 added) * DEVELOPMENT-GUIDELINES.md (~50 lines added) * QUICK-START.md (~15 lines added) * CLAUDE.md (~25 lines added) 🎯 Problem Solved ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Root cause: Files created/deployed with umask 0077 result in 600 permissions Symptom: HTTP 403 Forbidden errors on CSS/JS resources Impact: Modules fail to load in browser Recent examples: - secubox: 10 files with 600 permissions (monitoring.js, theme.js, etc.) - netdata-dashboard: 3 files with 600 permissions Solution: Automated detection and correction tools now prevent this issue Workflow integration: ✅ Pre-commit: fix-permissions.sh --local ✅ Validation: validate-modules.sh (Check 7) ✅ Post-deploy: fix-permissions.sh --remote 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
40 KiB
SecuBox & System Hub - Development Guidelines
Version: 1.0.0 Date: 2025-12-26 Audience: Développeurs, IA assistants, mainteneurs
Ce document définit les standards, bonnes pratiques et validations obligatoires pour le développement de modules SecuBox et System Hub dans l'écosystème OpenWrt LuCI.
Table des matières
- Design System & UI Guidelines
- Architecture & Naming Conventions
- RPCD & ubus Best Practices
- ACL & Permissions
- JavaScript Patterns
- CSS/Styling Standards
- Common Errors & Solutions
- Validation Checklist
- Deployment Procedures
Design System & UI Guidelines
Color Palette (Demo-inspired)
IMPORTANT: Toujours utiliser la palette définie dans system-hub/common.css
Dark Mode (Primary - Recommended)
--sh-text-primary: #fafafa;
--sh-text-secondary: #a0a0b0;
--sh-bg-primary: #0a0a0f; /* Fond principal (noir profond) */
--sh-bg-secondary: #12121a; /* Fond cartes/sections */
--sh-bg-tertiary: #1a1a24; /* Fond hover/actif */
--sh-bg-card: #12121a;
--sh-border: #2a2a35;
--sh-primary: #6366f1; /* Indigo */
--sh-primary-end: #8b5cf6; /* Violet (pour dégradés) */
--sh-success: #22c55e; /* Vert */
--sh-danger: #ef4444; /* Rouge */
--sh-warning: #f59e0b; /* Orange */
Light Mode (Secondary)
--sh-text-primary: #0f172a;
--sh-text-secondary: #475569;
--sh-bg-primary: #ffffff;
--sh-bg-secondary: #f8fafc;
--sh-bg-tertiary: #f1f5f9;
--sh-bg-card: #ffffff;
--sh-border: #e2e8f0;
✅ TOUJOURS utiliser les CSS variables - Ne JAMAIS hardcoder les couleurs.
Typography
Fonts Stack
/* Texte général */
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
/* Valeurs numériques, IDs, code */
font-family: 'JetBrains Mono', 'Courier New', monospace;
Import requis (ajouté dans common.css):
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&family=Inter:wght@400;500;600;700&display=swap');
Font Sizes
/* Titres */
--sh-title-xl: 28px; /* Page titles */
--sh-title-lg: 20px; /* Card titles */
--sh-title-md: 16px; /* Section headers */
/* Texte */
--sh-text-base: 14px; /* Body text */
--sh-text-sm: 13px; /* Labels, meta */
--sh-text-xs: 11px; /* Uppercase labels */
/* Valeurs */
--sh-value-xl: 40px; /* Large metrics */
--sh-value-lg: 32px; /* Stats overview */
--sh-value-md: 28px; /* Badges */
Component Patterns
1. Page Header (Standard)
HTML Structure:
E('div', { 'class': 'sh-page-header' }, [
E('div', {}, [
E('h2', { 'class': 'sh-page-title' }, [
E('span', { 'class': 'sh-page-title-icon' }, '🎯'),
'Page Title'
]),
E('p', { 'class': 'sh-page-subtitle' }, 'Description of the page')
]),
E('div', { 'class': 'sh-stats-grid' }, [
// Stats badges here
])
])
CSS Classes:
.sh-page-header- Container with flex layout.sh-page-title- Gradient text effect.sh-page-title-icon- Icon (no gradient).sh-page-subtitle- Secondary text.sh-stats-grid- Grid pour badges (130px min)
2. Stats Badges
RÈGLE: Minimum 130px, police monospace pour valeurs
E('div', { 'class': 'sh-stat-badge' }, [
E('div', { 'class': 'sh-stat-value' }, '92'),
E('div', { 'class': 'sh-stat-label' }, 'CPU %')
])
Grid Layout:
.sh-stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
gap: 12px;
}
3. Cards avec bordure colorée
OBLIGATOIRE: Toutes les cards doivent avoir une bordure top de 3px
E('div', { 'class': 'sh-card sh-card-success' }, [
E('div', { 'class': 'sh-card-header' }, [
E('h3', { 'class': 'sh-card-title' }, [
E('span', { 'class': 'sh-card-title-icon' }, '⚙️'),
'Card Title'
])
]),
E('div', { 'class': 'sh-card-body' }, [
// Content
])
])
Variants de bordure:
.sh-card- Bordure gradient (visible au hover).sh-card-success- Bordure verte permanente.sh-card-danger- Bordure rouge permanente.sh-card-warning- Bordure orange permanente
4. Buttons
Gradient buttons (preferred):
E('button', { 'class': 'sh-btn sh-btn-primary' }, 'Primary Action')
E('button', { 'class': 'sh-btn sh-btn-success' }, 'Success Action')
E('button', { 'class': 'sh-btn sh-btn-danger' }, 'Danger Action')
E('button', { 'class': 'sh-btn sh-btn-secondary' }, 'Secondary Action')
Tous les buttons doivent avoir:
- Shadow effect (déjà dans CSS)
- Hover animation (translateY(-2px))
- Transition smooth (0.3s cubic-bezier)
5. Filter Tabs
E('div', { 'class': 'sh-filter-tabs' }, [
E('div', {
'class': 'sh-filter-tab active',
'data-filter': 'all'
}, [
E('span', { 'class': 'sh-tab-icon' }, '📋'),
E('span', { 'class': 'sh-tab-label' }, 'All')
])
])
Active tab styling:
- Background: gradient indigo-violet
- Color: white
- Box-shadow avec glow
Grid Systems
Stats Overview (Compact)
grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
gap: 16px;
Metric Cards (Medium)
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 20px;
Info Cards (Large)
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
Gradient Effects
Gradient Text (Titles)
background: linear-gradient(135deg, var(--sh-primary), var(--sh-primary-end));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
Utiliser: .sh-gradient-text class ou .sh-page-title
Gradient Backgrounds (Buttons, Badges)
background: linear-gradient(135deg, var(--sh-primary), var(--sh-primary-end));
Gradient Borders (Top)
/* 3px top border avec dégradé */
.element::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 3px;
background: linear-gradient(90deg, var(--sh-primary), var(--sh-primary-end));
}
Animation Standards
Hover Effects
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
transform: translateY(-3px); /* Cards */
transform: translateY(-2px); /* Buttons, badges */
Shadow Progression
/* Default */
box-shadow: none;
/* Hover - Subtle */
box-shadow: 0 8px 20px var(--sh-shadow);
/* Hover - Pronounced */
box-shadow: 0 12px 28px var(--sh-hover-shadow);
/* Button Hover */
box-shadow: 0 8px 20px rgba(99, 102, 241, 0.5);
Architecture & Naming Conventions
CRITICAL: RPCD Script Naming
RÈGLE ABSOLUE: Le nom du fichier RPCD DOIT correspondre EXACTEMENT au nom de l'objet ubus dans JavaScript.
✅ CORRECT:
JavaScript:
var callStatus = rpc.declare({
object: 'luci.system-hub', // ← Nom objet
method: 'getHealth'
});
Fichier RPCD:
root/usr/libexec/rpcd/luci.system-hub # ← EXACT MATCH
❌ INCORRECT (Causes d'erreur -32000):
# Mauvais - manque le préfixe
root/usr/libexec/rpcd/system-hub
# Mauvais - underscore au lieu de tiret
root/usr/libexec/rpcd/luci.system_hub
# Mauvais - nom différent
root/usr/libexec/rpcd/systemhub
Menu Path Conventions
RÈGLE: Les chemins dans menu.d/*.json doivent correspondre EXACTEMENT aux fichiers de vue.
✅ CORRECT:
Menu JSON:
{
"action": {
"type": "view",
"path": "system-hub/overview"
}
}
Fichier de vue:
htdocs/luci-static/resources/view/system-hub/overview.js
❌ INCORRECT (Causes 404):
Menu: "path": "system-hub/overview" mais fichier: view/systemhub/overview.js
Prefixes Standards
| Type | Prefix | Exemple |
|---|---|---|
| ubus objects | luci. |
luci.system-hub |
| CSS classes | sh- (System Hub) ou sb- (SecuBox) |
.sh-page-header |
| CSS variables | --sh- |
--sh-primary |
| JavaScript modules | Nom du module | system-hub/api.js |
File Structure Template
luci-app-<module-name>/
├── Makefile
├── README.md
├── htdocs/luci-static/resources/
│ ├── view/<module-name>/
│ │ ├── overview.js # Page principale
│ │ ├── settings.js # Configuration
│ │ └── *.js # Autres vues
│ └── <module-name>/
│ ├── api.js # RPC client
│ ├── theme.js # Theme helpers (optionnel)
│ ├── common.css # Styles partagés
│ └── *.css # Styles spécifiques
└── root/
├── usr/
│ ├── libexec/rpcd/
│ │ └── luci.<module-name> # ⚠️ MUST match ubus object
│ └── share/
│ ├── luci/menu.d/
│ │ └── luci-app-<module-name>.json
│ └── rpcd/acl.d/
│ └── luci-app-<module-name>.json
└── etc/config/<module-name> (optionnel)
RPCD & ubus Best Practices
RPCD Script Template (Shell)
Fichier: root/usr/libexec/rpcd/luci.<module-name>
#!/bin/sh
# RPCD backend for <module-name>
# ubus object: luci.<module-name>
case "$1" in
list)
# Liste des méthodes disponibles
echo '{
"getStatus": {},
"getHealth": {},
"getServices": {}
}'
;;
call)
case "$2" in
getStatus)
# TOUJOURS retourner du JSON valide
printf '{"enabled": true, "version": "1.0.0"}\n'
;;
getHealth)
# Lire les métriques système
cpu_usage=$(top -bn1 | grep "CPU:" | awk '{print $2}' | sed 's/%//')
mem_total=$(free | grep Mem | awk '{print $2}')
mem_used=$(free | grep Mem | awk '{print $3}')
printf '{
"cpu": {"usage": %s},
"memory": {"total_kb": %s, "used_kb": %s}
}\n' "$cpu_usage" "$mem_total" "$mem_used"
;;
getServices)
# Exemple avec services
services='[]'
for service in /etc/init.d/*; do
# Build JSON array
:
done
echo "$services"
;;
*)
echo '{"error": "Method not found"}'
exit 1
;;
esac
;;
esac
RPCD Script Validation
CHECKLIST OBLIGATOIRE:
- ✅ Fichier exécutable:
chmod +x root/usr/libexec/rpcd/luci.<module-name> - ✅ Shebang présent:
#!/bin/sh - ✅ Structure case/esac correcte
- ✅ Méthode
listretourne JSON avec toutes les méthodes - ✅ Méthode
callgère tous les cas - ✅ Toujours retourner du JSON valide
- ✅ Pas de
echode debug (commentés en prod) - ✅ Gestion d'erreur pour méthodes inconnues
Testing RPCD Scripts
Sur le routeur:
# Test direct
/usr/libexec/rpcd/luci.system-hub list
# Via ubus
ubus list luci.system-hub
ubus call luci.system-hub getStatus
# Restart RPCD après modification
/etc/init.d/rpcd restart
Common RPCD Errors
Error: "Object not found" (-32000)
Cause: Nom du fichier RPCD ne correspond pas à l'objet ubus
Solution:
# Vérifier le nom dans JS
grep -r "object:" htdocs/luci-static/resources/view/ --include="*.js"
# Renommer le fichier RPCD pour correspondre
mv root/usr/libexec/rpcd/wrong-name root/usr/libexec/rpcd/luci.correct-name
Error: "Method not found" (-32601)
Cause: Méthode non déclarée dans list ou non implémentée dans call
Solution:
# Vérifier que la méthode est dans les deux blocs
grep "getStatus" root/usr/libexec/rpcd/luci.*
Error: Invalid JSON returned
Cause: Output RPCD n'est pas du JSON valide
Solution:
# Tester le JSON
/usr/libexec/rpcd/luci.module-name call getStatus | jsonlint
# Utiliser printf au lieu de echo pour le JSON
printf '{"key": "%s"}\n' "$value"
ACL & Permissions
ACL File Template
Fichier: root/usr/share/rpcd/acl.d/luci-app-<module-name>.json
{
"luci-app-<module-name>": {
"description": "Grant access to <Module Name>",
"read": {
"ubus": {
"luci.<module-name>": [
"getStatus",
"getHealth",
"getServices"
]
},
"uci": [
"<module-name>"
]
},
"write": {
"ubus": {
"luci.<module-name>": [
"setConfig",
"restartService"
]
},
"uci": [
"<module-name>"
]
}
}
}
ACL Best Practices
- Séparation read/write: Ne donnez que les permissions nécessaires
- Liste explicite: Listez toutes les méthodes ubus utilisées
- UCI access: Ajoutez les configs UCI dans
readetwrite - Validation JSON: Toujours valider avec
jsonlint
Common ACL Errors
Error: "Access denied"
Cause: Méthode ubus pas dans ACL
Solution:
{
"read": {
"ubus": {
"luci.system-hub": [
"getHealth" // ← Ajouter la méthode manquante
]
}
}
}
Error: "UCI config not accessible"
Cause: Config UCI pas dans ACL
Solution:
{
"read": {
"uci": [
"system-hub" // ← Ajouter le config
]
}
}
JavaScript Patterns
API Module Template
Fichier: htdocs/luci-static/resources/<module-name>/api.js
'use strict';
'require rpc';
'require uci';
return L.Class.extend({
// Déclarer les appels RPC
callGetStatus: rpc.declare({
object: 'luci.<module-name>',
method: 'getStatus',
expect: { }
}),
callGetHealth: rpc.declare({
object: 'luci.<module-name>',
method: 'getHealth',
expect: { }
}),
// Méthodes wrapper avec gestion d'erreur
getStatus: function() {
return this.callGetStatus().catch(function(err) {
console.error('Failed to get status:', err);
return { enabled: false, error: err.message };
});
},
getHealth: function() {
return this.callGetHealth().catch(function(err) {
console.error('Failed to get health:', err);
return {
cpu: { usage: 0 },
memory: { usage: 0 },
error: err.message
};
});
},
// Utilitaires
formatBytes: function(bytes) {
if (bytes === 0) return '0 B';
var k = 1024;
var sizes = ['B', 'KB', 'MB', 'GB'];
var i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
});
View Template
Fichier: htdocs/luci-static/resources/view/<module-name>/overview.js
'use strict';
'require view';
'require ui';
'require dom';
'require poll';
'require <module-name>/api as API';
return view.extend({
// State
healthData: null,
sysInfo: null,
// Load data
load: function() {
return Promise.all([
API.getStatus(),
API.getHealth()
]);
},
// Render UI
render: function(data) {
var self = this;
this.sysInfo = data[0] || {};
this.healthData = data[1] || {};
var container = E('div', { 'class': '<module>-dashboard' }, [
// Link CSS files
E('link', { 'rel': 'stylesheet', 'href': L.resource('<module>/common.css') }),
E('link', { 'rel': 'stylesheet', 'href': L.resource('<module>/overview.css') }),
// Header
this.renderHeader(),
// Content
this.renderContent()
]);
// Setup auto-refresh
poll.add(L.bind(function() {
return Promise.all([
API.getStatus(),
API.getHealth()
]).then(L.bind(function(refreshData) {
this.sysInfo = refreshData[0] || {};
this.healthData = refreshData[1] || {};
this.updateDashboard();
}, this));
}, this), 30); // Refresh every 30s
return container;
},
renderHeader: function() {
return E('div', { 'class': 'sh-page-header' }, [
// Header content
]);
},
renderContent: function() {
return E('div', { 'class': 'sh-content' }, [
// Main content
]);
},
updateDashboard: function() {
// Update existing DOM elements
var element = document.querySelector('.my-element');
if (element) {
dom.content(element, this.renderContent());
}
},
// Required stubs for LuCI
handleSaveApply: null,
handleSave: null,
handleReset: null
});
Event Handling Pattern
// ✅ CORRECT: Bind events après render
render: function(data) {
var container = E('div', {}, [
E('button', {
'id': 'my-button',
'class': 'sh-btn sh-btn-primary'
}, 'Click Me')
]);
// Ajouter l'événement après le container est créé
container.addEventListener('click', function(ev) {
if (ev.target && ev.target.id === 'my-button') {
self.handleButtonClick();
}
});
return container;
},
handleButtonClick: function() {
ui.addNotification(null, E('p', 'Button clicked!'), 'info');
}
Common JavaScript Errors
Error: "[object HTMLButtonElement]" affiché
Cause: Array imbriqué quand E() attend un array simple
// ❌ INCORRECT
E('div', {}, [
this.renderButtons() // renderButtons retourne déjà un array
])
// ✅ CORRECT
E('div', {},
this.renderButtons() // Pas de [ ] supplémentaire
)
Error: "Cannot read property of undefined"
Cause: Données API non disponibles
// ❌ INCORRECT
var cpuUsage = this.healthData.cpu.usage;
// ✅ CORRECT (avec optional chaining)
var cpuUsage = (this.healthData.cpu && this.healthData.cpu.usage) || 0;
// ou
var cpuUsage = this.healthData.cpu?.usage || 0; // ES2020
Error: "poll callback failed"
Cause: Promise non retournée dans poll.add
// ❌ INCORRECT
poll.add(function() {
API.getHealth(); // Pas de return!
}, 30);
// ✅ CORRECT
poll.add(function() {
return API.getHealth().then(function(data) {
// Update UI
});
}, 30);
CSS/Styling Standards
File Organization
<module-name>/
├── common.css # Shared components (headers, buttons, cards, tabs)
├── overview.css # Overview page specific
├── services.css # Services page specific
└── *.css # Other page-specific styles
CSS File Template
/**
* Module Name - Page/Component Styles
* Description of what this file styles
* Version: X.Y.Z
*/
/* === Import shared styles (if needed) === */
/* Not required if loaded in HTML */
/* === Page-specific variables (if needed) === */
:root {
--page-specific-var: value;
}
/* === Layout === */
.module-page-container {
/* Layout styles */
}
/* === Components === */
.module-component {
/* Component styles */
}
/* === Responsive === */
@media (max-width: 768px) {
/* Mobile styles */
}
/* === Dark Mode Overrides === */
[data-theme="dark"] .module-component {
/* Dark mode specific */
}
CSS Best Practices
1. TOUJOURS utiliser les variables CSS
/* ❌ INCORRECT */
.my-card {
background: #12121a;
color: #fafafa;
}
/* ✅ CORRECT */
.my-card {
background: var(--sh-bg-card);
color: var(--sh-text-primary);
}
2. Prefix classes par module
/* System Hub */
.sh-page-header { }
.sh-card { }
.sh-btn { }
/* SecuBox */
.sb-module-grid { }
.sb-dashboard { }
/* Module spécifique */
.netdata-chart { }
.crowdsec-alert { }
3. Transitions cohérentes
/* Standard transition */
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
/* Quick transition (hover states) */
transition: all 0.2s ease;
/* Smooth transition (large movements) */
transition: all 0.5s ease;
4. Responsive breakpoints
/* Mobile */
@media (max-width: 768px) {
.sh-stats-grid {
grid-template-columns: repeat(2, 1fr);
}
}
/* Tablet */
@media (min-width: 769px) and (max-width: 1024px) {
/* Tablet specific */
}
/* Desktop */
@media (min-width: 1025px) {
/* Desktop specific */
}
5. Dark mode OBLIGATOIRE
Toujours fournir des styles dark mode:
/* Light mode (default) */
.my-component {
background: var(--sh-bg-card);
border: 1px solid var(--sh-border);
}
/* Dark mode override */
[data-theme="dark"] .my-component {
background: var(--sh-bg-card);
border-color: var(--sh-border);
}
Z-index Scale
Respecter cette échelle:
--z-base: 0;
--z-dropdown: 100;
--z-sticky: 200;
--z-fixed: 300;
--z-modal-backdrop: 400;
--z-modal: 500;
--z-popover: 600;
--z-tooltip: 700;
Common Errors & Solutions
1. RPCD Object Not Found (-32000)
Erreur complète:
RPC call to luci.system-hub/getHealth failed with error -32000: Object not found
Diagnostic:
# 1. Vérifier que le fichier RPCD existe
ls -la /usr/libexec/rpcd/luci.system-hub
# 2. Vérifier qu'il est exécutable
chmod +x /usr/libexec/rpcd/luci.system-hub
# 3. Lister les objets ubus
ubus list | grep system-hub
# 4. Si absent, redémarrer RPCD
/etc/init.d/rpcd restart
ubus list | grep system-hub
Solutions:
- Renommer le fichier RPCD pour correspondre exactement
- Vérifier permissions (755 ou rwxr-xr-x)
- Redémarrer rpcd
2. View Not Found (404)
Erreur:
HTTP error 404 while loading class file '/luci-static/resources/view/system-hub/overview.js'
Diagnostic:
# 1. Vérifier que le fichier existe
ls -la /www/luci-static/resources/view/system-hub/overview.js
# 2. Vérifier le chemin dans menu.d
grep "path" /usr/share/luci/menu.d/luci-app-system-hub.json
Solutions:
- Vérifier que le path dans menu JSON correspond au fichier
- Vérifier permissions du fichier (644)
- Nettoyer cache:
rm -f /tmp/luci-indexcache /tmp/luci-modulecache/*
3. CSS Not Loading (403 Forbidden)
Erreur:
GET /luci-static/resources/system-hub/common.css 403 Forbidden
Diagnostic:
# Vérifier permissions
ls -la /www/luci-static/resources/system-hub/common.css
Solution:
# Corriger permissions
chmod 644 /www/luci-static/resources/system-hub/*.css
4. Invalid JSON from RPCD
Erreur dans browser console:
SyntaxError: Unexpected token in JSON at position X
Diagnostic:
# Tester le JSON directement
/usr/libexec/rpcd/luci.system-hub call getHealth | jsonlint
# Ou avec jq
/usr/libexec/rpcd/luci.system-hub call getHealth | jq .
Solutions courantes:
# ❌ INCORRECT - Quote simple non échappée
echo '{"error": "can't process"}'
# ✅ CORRECT - Utiliser printf et doubles quotes
printf '{"error": "cannot process"}\n'
# ❌ INCORRECT - Variable non quotée
echo "{\"value\": $var}"
# ✅ CORRECT - Variable quotée
printf '{"value": "%s"}\n' "$var"
5. Browser Cache Issues
Symptômes:
- Changements CSS/JS non visibles
- Anciennes données affichées
- Code mis à jour mais interface identique
Solutions:
# 1. Côté serveur - nettoyer cache LuCI
ssh root@router "rm -f /tmp/luci-indexcache /tmp/luci-modulecache/* && /etc/init.d/uhttpd restart"
# 2. Côté client - hard refresh
Ctrl + Shift + R (Chrome/Firefox)
Ctrl + F5 (Windows)
Cmd + Shift + R (Mac)
# 3. Mode privé/incognito pour test
Ctrl + Shift + N (Chrome)
Ctrl + Shift + P (Firefox)
6. ACL Access Denied
Erreur:
Access to path '/admin/secubox/system/system-hub' denied
Diagnostic:
# Vérifier ACL
cat /usr/share/rpcd/acl.d/luci-app-system-hub.json | jq .
# Vérifier que méthodes ubus sont listées
grep "getHealth" /usr/share/rpcd/acl.d/luci-app-system-hub.json
Solution: Ajouter la méthode manquante dans ACL et redémarrer rpcd.
Validation Checklist
Pre-Commit Checklist
Avant chaque commit, vérifier:
-
RPCD Script:
- Nom fichier correspond à objet ubus
- Exécutable (chmod +x)
- Structure list/call correcte
- Retourne JSON valide
- Toutes méthodes implémentées
-
Menu & ACL:
- Path menu correspond au fichier vue
- ACL liste toutes les méthodes ubus
- JSON valide (jsonlint)
-
JavaScript:
- 'use strict' en première ligne
- Imports requis présents
- Pas de console.log en prod
- Gestion d'erreur sur API calls
- Event handlers bindés correctement
-
CSS:
- Variables CSS utilisées (pas de hardcode)
- Classes prefixées (sh-, sb-, module-)
- Dark mode supporté
- Responsive (max-width: 768px)
- Transitions cohérentes
-
Makefile:
- PKG_VERSION incrémenté
- LUCI_DEPENDS correct
- Include path correct (../../luci.mk)
Pre-Deploy Checklist
Avant déploiement sur routeur:
-
Validation scripts:
./secubox-tools/validate-modules.sh -
Test RPCD local:
/usr/libexec/rpcd/luci.module-name list /usr/libexec/rpcd/luci.module-name call getStatus -
Test JSON:
find . -name "*.json" -exec jsonlint {} \; -
Shellcheck:
shellcheck root/usr/libexec/rpcd/* -
Permissions:
# RPCD scripts chmod 755 root/usr/libexec/rpcd/* # CSS/JS files chmod 644 htdocs/luci-static/resources/**/*
Post-Deploy Checklist
Après déploiement:
-
Services:
/etc/init.d/rpcd status /etc/init.d/uhttpd status -
ubus objects:
ubus list | grep luci.module-name -
Fichiers présents:
ls -la /www/luci-static/resources/view/module-name/ ls -la /www/luci-static/resources/module-name/ -
Permissions correctes:
ls -la /usr/libexec/rpcd/luci.module-name ls -la /www/luci-static/resources/module-name/*.css -
Test navigateur:
- Ouvrir en mode privé
- Vérifier console (F12) - pas d'erreurs
- Vérifier Network tab - tous les fichiers chargent (200)
- Tester dark/light mode
- Tester responsive (mobile view)
Deployment Procedures
⚠️ Pre-Deployment Checks (CRITICAL)
TOUJOURS exécuter ces vérifications AVANT tout déploiement:
1. Vérification de l'Espace Disque
# Sur le routeur cible
ssh root@192.168.8.191 "df -h | grep overlay"
# Vérifier que l'utilisation est < 90%
# Exemple OK:
# /dev/loop0 98.8M 45.2M 53.6M 46% /overlay
# Exemple CRITIQUE (STOP deployment):
# /dev/loop0 98.8M 98.8M 0 100% /overlay ← PLEIN!
Si l'overlay est plein (≥95%):
# Libérer de l'espace avant déploiement
ssh root@192.168.8.191 << 'EOF'
# Supprimer fichiers temporaires
rm -rf /tmp/*.ipk /tmp/luci-* 2>/dev/null
# Supprimer anciens backups (>7 jours)
find /root -name '*.backup-*' -type f -mtime +7 -delete 2>/dev/null
# Vérifier packages inutilisés
opkg list-installed | grep -E 'netdata|unused'
# Après nettoyage, vérifier l'espace libéré
df -h | grep overlay
EOF
Tailles typiques à surveiller:
- Netdata web UI: ~22MB (considérer suppression si non utilisé)
- Modules LuCI: ~1-2MB chacun
- Fichiers CSS/JS: ~10-50KB chacun
2. Vérification des Permissions (Critique pour Éviter Erreurs 403)
Permissions OBLIGATOIRES:
| Type | Permission | Octal | Raison |
|---|---|---|---|
| RPCD scripts | rwxr-xr-x |
755 |
Exécutable par system |
| CSS files | rw-r--r-- |
644 |
Lecture web server |
| JS files | rw-r--r-- |
644 |
Lecture web server |
| JSON files | rw-r--r-- |
644 |
Lecture rpcd |
Erreur commune: Fichiers créés avec 600 (rw-------) au lieu de 644
Symptôme: HTTP 403 Forbidden lors du chargement de fichiers JS/CSS
Exemple d'erreur:
NetworkError: HTTP error 403 while loading class file
"/luci-static/resources/view/netdata-dashboard/dashboard.js"
Diagnostic rapide:
# Vérifier permissions des fichiers déployés
ssh root@192.168.8.191 "ls -la /www/luci-static/resources/view/MODULE_NAME/"
# Chercher fichiers avec permissions incorrectes (600)
ssh root@192.168.8.191 "find /www/luci-static/resources/view/ -type f -name '*.js' -perm 600"
# MAUVAIS (cause 403):
# -rw------- 1 root root 9763 dashboard.js ← 600 = pas lisible par web!
# BON:
# -rw-r--r-- 1 root root 9763 dashboard.js ← 644 = OK
Correction immédiate:
# Corriger TOUS les fichiers CSS/JS
ssh root@192.168.8.191 << 'EOF'
find /www/luci-static/resources/ -name '*.css' -exec chmod 644 {} \;
find /www/luci-static/resources/ -name '*.js' -exec chmod 644 {} \;
find /usr/libexec/rpcd/ -name 'luci.*' -exec chmod 755 {} \;
EOF
⚡ Correction Automatique (Recommandé):
Utiliser le script automatique qui vérifie et corrige toutes les permissions:
# Corriger permissions locales (source code)
./secubox-tools/fix-permissions.sh --local
# Corriger permissions sur routeur
./secubox-tools/fix-permissions.sh --remote
# Corriger les deux (local + remote)
./secubox-tools/fix-permissions.sh
Le script fix-permissions.sh effectue automatiquement:
- ✅ Fixe tous les RPCD scripts à 755
- ✅ Fixe tous les CSS à 644
- ✅ Fixe tous les JS à 644
- ✅ Vérifie qu'aucun fichier 600 ne reste
- ✅ Clear cache et restart services (remote mode)
- ✅ Affiche un rapport complet des changements
🔍 Validation Automatique des Permissions:
Le script validate-modules.sh inclut maintenant un Check 7 qui vérifie automatiquement les permissions:
./secubox-tools/validate-modules.sh
# Check 7 validera:
# ✓ Tous les RPCD sont 755
# ✓ Tous les CSS sont 644
# ✓ Tous les JS sont 644
# ❌ Affichera erreurs si permissions incorrectes
Workflow recommandé:
- Développer/modifier code
./secubox-tools/fix-permissions.sh --local(avant commit)./secubox-tools/validate-modules.sh(vérifier tout)- Commit & push
- Deploy sur routeur
./secubox-tools/fix-permissions.sh --remote(après deploy)
3. Post-Deployment Verification
Checklist après déploiement:
#!/bin/bash
ROUTER="root@192.168.8.191"
MODULE="module-name"
echo "🔍 Post-Deployment Verification"
echo ""
# 1. Vérifier espace disque
echo "1. Espace disque restant:"
ssh "$ROUTER" "df -h | grep overlay | awk '{print \$5}'" || echo "❌ FAIL"
# 2. Vérifier permissions CSS/JS
echo "2. Permissions CSS/JS:"
ssh "$ROUTER" "find /www/luci-static/resources/$MODULE -type f \( -name '*.css' -o -name '*.js' \) ! -perm 644" | \
if [ -z "$(cat)" ]; then echo "✅ OK"; else echo "❌ FAIL - Permissions incorrectes"; fi
# 3. Vérifier permissions RPCD
echo "3. Permissions RPCD:"
ssh "$ROUTER" "ls -l /usr/libexec/rpcd/luci.$MODULE | grep -q rwxr-xr-x" && echo "✅ OK" || echo "❌ FAIL"
# 4. Vérifier ubus object
echo "4. ubus object disponible:"
ssh "$ROUTER" "ubus list | grep -q luci.$MODULE" && echo "✅ OK" || echo "❌ FAIL"
# 5. Vérifier fichiers accessibles (HTTP)
echo "5. Fichiers web accessibles:"
ssh "$ROUTER" "test -r /www/luci-static/resources/$MODULE/common.css" && echo "✅ OK" || echo "⚠️ common.css non trouvé"
# 6. Vérifier cache cleared
echo "6. Cache LuCI cleared:"
ssh "$ROUTER" "test ! -f /tmp/luci-indexcache" && echo "✅ OK" || echo "⚠️ Cache encore présent"
echo ""
echo "✅ Vérification terminée"
4. Common Deployment Errors
| Erreur | Cause | Solution Rapide |
|---|---|---|
| HTTP 403 Forbidden | Permissions 600 au lieu de 644 | chmod 644 *.js *.css |
| No space left on device | Overlay plein | Nettoyer /tmp, supprimer anciens backups |
| Object not found -32000 | RPCD pas exécutable ou mal nommé | chmod 755 rpcd/luci.* + vérifier nom |
| Module not appearing | Cache LuCI pas cleared | rm /tmp/luci-* + restart services |
| Changes not visible | Cache navigateur | Mode privé + Ctrl+Shift+R |
5. Emergency Disk Space Recovery
Si le déploiement échoue avec "No space left on device":
#!/bin/bash
ROUTER="root@192.168.8.191"
echo "🚨 Emergency Disk Space Recovery"
echo ""
# 1. Analyser l'utilisation
echo "Top 10 consumers:"
ssh "$ROUTER" "du -k /overlay/upper 2>/dev/null | sort -rn | head -10"
# 2. Nettoyer temporaires
echo ""
echo "Cleaning temp files..."
ssh "$ROUTER" "rm -rf /tmp/*.ipk /tmp/luci-* /root/*.ipk 2>/dev/null"
# 3. Supprimer anciens backups
echo "Removing old backups (>7 days)..."
ssh "$ROUTER" "find /root -name '*.backup-*' -mtime +7 -delete 2>/dev/null"
# 4. Option: Supprimer Netdata Web UI (libère ~22MB)
echo ""
echo "⚠️ Option: Remove Netdata Web UI (saves ~22MB)?"
read -p "Continue? (y/N) " -n 1 -r
if [[ $REPLY =~ ^[Yy]$ ]]; then
ssh "$ROUTER" "opkg remove netdata-web 2>/dev/null || rm -rf /usr/share/netdata/web/*"
fi
# 5. Vérifier espace libéré
echo ""
echo "Space after cleanup:"
ssh "$ROUTER" "df -h | grep overlay"
Standard Deployment Script Template
#!/bin/bash
# Deploy <Module Name>
ROUTER="root@192.168.8.191"
MODULE="<module-name>"
LOCAL_DIR="/path/to/luci-app-$MODULE/htdocs/luci-static/resources"
REMOTE_DIR="/www/luci-static/resources"
echo "📦 Déploiement $MODULE"
echo ""
# 1. Deploy JS files
echo "1. Copie des fichiers JS..."
scp "$LOCAL_DIR/view/$MODULE/"*.js "$ROUTER:$REMOTE_DIR/view/$MODULE/"
scp "$LOCAL_DIR/$MODULE/api.js" "$ROUTER:$REMOTE_DIR/$MODULE/"
# 2. Deploy CSS files
echo "2. Copie des fichiers CSS..."
scp "$LOCAL_DIR/$MODULE/"*.css "$ROUTER:$REMOTE_DIR/$MODULE/"
# 3. Deploy RPCD backend
echo "3. Copie du backend RPCD..."
scp "root/usr/libexec/rpcd/luci.$MODULE" "$ROUTER:/usr/libexec/rpcd/"
# 4. Fix permissions
echo "4. Correction des permissions..."
ssh "$ROUTER" "chmod 755 /usr/libexec/rpcd/luci.$MODULE"
ssh "$ROUTER" "chmod 644 $REMOTE_DIR/$MODULE/*.css"
ssh "$ROUTER" "chmod 644 $REMOTE_DIR/view/$MODULE/*.js"
# 5. Clear cache
echo "5. Nettoyage du cache..."
ssh "$ROUTER" "rm -f /tmp/luci-indexcache /tmp/luci-modulecache/* 2>/dev/null"
# 6. Restart services
echo "6. Redémarrage des services..."
ssh "$ROUTER" "/etc/init.d/rpcd restart"
ssh "$ROUTER" "/etc/init.d/uhttpd restart"
# 7. Verify
echo ""
echo "7. Vérification..."
ssh "$ROUTER" "ubus list | grep luci.$MODULE"
echo ""
echo "✅ Déploiement terminé!"
echo ""
echo "🌐 Testez (mode privé):"
echo " https://192.168.8.191/cgi-bin/luci/admin/secubox/path/to/$MODULE"
Rollback Procedure
En cas de problème:
#!/bin/bash
# Rollback to previous version
ROUTER="root@192.168.8.191"
BACKUP_DIR="/root/luci-backups/$(date +%Y%m%d)"
# 1. Créer backup avant deploy
ssh "$ROUTER" "mkdir -p $BACKUP_DIR"
ssh "$ROUTER" "cp -r /www/luci-static/resources/module-name $BACKUP_DIR/"
ssh "$ROUTER" "cp /usr/libexec/rpcd/luci.module-name $BACKUP_DIR/"
# 2. En cas de problème, restore
ssh "$ROUTER" "cp -r $BACKUP_DIR/module-name /www/luci-static/resources/"
ssh "$ROUTER" "cp $BACKUP_DIR/luci.module-name /usr/libexec/rpcd/"
ssh "$ROUTER" "/etc/init.d/rpcd restart && /etc/init.d/uhttpd restart"
Version Control
Toujours incrémenter les versions:
# Makefile
PKG_VERSION:=0.3.0
PKG_RELEASE:=1
/* CSS files */
/**
* Module - Styles
* Version: 0.3.0
*/
// JavaScript
// Version: 0.3.0
Semantic Versioning:
- MAJOR.MINOR.PATCH (1.2.3)
- MAJOR: Breaking changes
- MINOR: New features (backward compatible)
- PATCH: Bug fixes
Quick Reference
Essential Commands
# Validation
./secubox-tools/validate-modules.sh
# Build (local)
./secubox-tools/local-build.sh build luci-app-module-name
# Deploy files
scp file.js root@router:/www/luci-static/resources/
# Fix permissions
ssh root@router "chmod 644 /www/luci-static/resources/**/*.css"
ssh root@router "chmod 755 /usr/libexec/rpcd/luci.*"
# Clear cache
ssh root@router "rm -f /tmp/luci-indexcache /tmp/luci-modulecache/*"
# Restart services
ssh root@router "/etc/init.d/rpcd restart && /etc/init.d/uhttpd restart"
# Test ubus
ssh root@router "ubus list | grep luci"
ssh root@router "ubus call luci.module-name getStatus"
# Validate JSON
jsonlint file.json
jq . file.json
CSS Classes Quick Reference
/* Layout */
.sh-page-header /* Page header container */
.sh-page-title /* Page title (gradient text) */
.sh-page-subtitle /* Page subtitle */
/* Stats */
.sh-stats-grid /* Grid for stat badges (130px min) */
.sh-stat-badge /* Stat badge container */
.sh-stat-value /* Stat value (monospace) */
.sh-stat-label /* Stat label (uppercase) */
/* Cards */
.sh-card /* Card container (with gradient border on hover) */
.sh-card-success /* Card with green border */
.sh-card-danger /* Card with red border */
.sh-card-warning /* Card with orange border */
.sh-card-header /* Card header */
.sh-card-title /* Card title */
.sh-card-body /* Card content */
/* Buttons */
.sh-btn /* Base button */
.sh-btn-primary /* Primary button (gradient) */
.sh-btn-success /* Success button (green) */
.sh-btn-danger /* Danger button (red) */
.sh-btn-secondary /* Secondary button (outline) */
/* Tabs */
.sh-filter-tabs /* Filter tabs container */
.sh-filter-tab /* Filter tab */
.sh-filter-tab.active /* Active filter tab (gradient) */
.sh-nav-tabs /* Navigation tabs (sticky) */
.sh-nav-tab /* Navigation tab */
.sh-nav-tab.active /* Active nav tab (underline) */
/* Utilities */
.sh-gradient-text /* Gradient text effect */
.sh-id-display /* Monospace ID display */
.sh-empty-state /* Empty state placeholder */
Color Variables Quick Reference
/* Text */
var(--sh-text-primary) /* Main text */
var(--sh-text-secondary) /* Secondary text */
/* Backgrounds */
var(--sh-bg-primary) /* Main background */
var(--sh-bg-secondary) /* Secondary background */
var(--sh-bg-tertiary) /* Tertiary background */
var(--sh-bg-card) /* Card background */
/* Borders */
var(--sh-border) /* Border color */
/* Colors */
var(--sh-primary) /* Indigo #6366f1 */
var(--sh-primary-end) /* Violet #8b5cf6 */
var(--sh-success) /* Green #22c55e */
var(--sh-danger) /* Red #ef4444 */
var(--sh-warning) /* Orange #f59e0b */
/* Effects */
var(--sh-shadow) /* Box shadow */
var(--sh-hover-shadow) /* Hover shadow */
var(--sh-hover-bg) /* Hover background */
Conclusion
Ce guide doit être consulté AVANT de:
- Créer un nouveau module
- Modifier des styles existants
- Ajouter des méthodes RPCD
- Déployer sur un routeur
- Débugger des erreurs
En cas de doute, TOUJOURS:
- Consulter ce guide
- Exécuter validate-modules.sh
- Tester en mode privé
- Vérifier la console browser (F12)
Ressources supplémentaires:
- CLAUDE.md - Architecture et build
- secubox-tools/validate-modules.sh - Validation automatique
- Templates/ - Templates de code
Dernière mise à jour: 2025-12-26 Maintenu par: CyberMind Studio Version du guide: 1.0.0