🎨 Design System v0.3.0 (Demo-inspired) - New dark palette: #0a0a0f, #6366f1→#8b5cf6 gradients - Typography: Inter + JetBrains Mono - Compact stats grid (130px min) - Gradient text effects with background-clip - Sticky navigation tabs - Enhanced card borders and hover effects 📚 Comprehensive Documentation Suite - DEVELOPMENT-GUIDELINES.md (33KB, 900+ lines) - 9 major sections: Design, Architecture, RPCD, ACL, JS, CSS, Errors, Validation, Deployment - Complete code templates and best practices - Common error diagnostics and solutions - QUICK-START.md (6.4KB) - 8 critical rules for immediate reference - Quick code templates - Error quick fixes table - deploy-module-template.sh (8.1KB) - Standardized deployment with automatic backup - Permission fixes, cache clearing, verification - Updated CLAUDE.md, README.md with documentation index - Updated .claude/README.md to v2.0 🔄 Version Updates - luci-app-secubox: 0.1.2 → 0.2.2 - luci-app-system-hub: 0.1.1 → 0.2.2 - Updated all version strings (api.js, overview.js, CSS files) 🎯 CSS Enhancements - common.css: Complete rewrite with demo palette - overview.css: Dashboard header with gradient - services.css: Updated version to 0.2.2 - components.css: Updated version to 0.2.2 🔧 Critical Rules Documented 1. RPCD naming: file = ubus object (luci. prefix) 2. Menu path = view file location 3. Permissions: 755 (RPCD), 644 (CSS/JS) 4. ALWAYS run validate-modules.sh 5. CSS variables only (no hardcode) 6. Dark mode mandatory 7. Typography: Inter + JetBrains Mono 8. Gradients: --sh-primary → --sh-primary-end 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
33 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
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