refactor: architecture modules indépendants — nettoyage CORE, registry enrichi, page modules dynamique

- Supprimer les modules services et logs du CORE (déplacés dans viewServices et viewLogs)
- Enrichir modules/module.go : interface Registry avec NavItemDef, RunOnTarget, StreamOnTarget
- Réécrire modules/loader.go : NewLoader accepte *db.DB, *sshpool.Pool, *crypto.Encryptor
- Ajouter migration 005 : colonnes nav_* sur la table modules + suppression services/logs DB
- Mettre à jour db.go (repairSchema) pour ajout idempotent des colonnes nav_*
- Mettre à jour settings.go : GetModules retourne les champs nav, ajout GetRegistryModules et InstallRegistryModule
- Mettre à jour main.go : NewLoader avec les bons arguments, ajout routes /api/registry/modules
- Mettre à jour modules.html : section Store avec liste des modules Forgejo
- Mettre à jour app.js : sidebar dynamique (nav_href depuis DB), modulesPage avec store
- Mettre à jour pages.css : styles pour store de modules

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
enzo 2026-03-22 03:34:17 +01:00
parent 91cf788221
commit ec7d120ef6
15 changed files with 460 additions and 997 deletions

View file

@ -1,5 +1,4 @@
// Package modules définit le contrat d'interface pour les modules ProxmoxPanel.
// Chaque module implémente l'interface Module et s'enregistre auprès du ModuleRegistry.
package modules
import (
@ -9,36 +8,49 @@ import (
// Module est l'interface que chaque module doit implémenter.
type Module interface {
// ID retourne l'identifiant unique du module (doit correspondre à la table modules en DB).
ID() string
// Register est appelé au chargement du module actif.
// Il reçoit le registry pour enregistrer ses routes, widgets, etc.
Register(registry Registry) error
}
// NavItemDef décrit l'entrée de navigation d'un module dans la sidebar.
type NavItemDef struct {
ID string `json:"id"`
Href string `json:"href"`
Icon string `json:"icon"`
Color string `json:"color"`
LabelKey string `json:"label_key"`
}
// Registry est l'interface exposée aux modules pour s'enregistrer dans le CORE.
// Seuls des types de la bibliothèque standard sont exposés — aucun type internal.
type Registry interface {
// RegisterRoute enregistre une route HTTP dans le router principal.
// Enregistrement de routes HTTP
RegisterRoute(method, path string, handler http.HandlerFunc, requireAdmin bool)
// RegisterWSChannel enregistre un handler WebSocket pour un channel nommé.
// Enregistrement du canal WebSocket
RegisterWSChannel(channel string, handler WSHandler)
// RegisterWidget déclare un type de widget disponible pour le dashboard.
// Widgets et onglets
RegisterWidget(widget WidgetDef)
// RegisterSettingsTab ajoute un onglet dans la page paramètres.
RegisterSettingsTab(tab SettingsTabDef)
// RegisterTranslations fusionne des clés de traduction pour une langue donnée.
// Traductions et migrations
RegisterTranslations(lang string, keys map[string]string)
// RegisterMigration déclare une migration de base de données propre au module.
RegisterMigration(version int, sql string, fn MigrationFn)
// DB retourne un accès à SQLite avec isolation par module (préfixe de tables).
// Entrée de navigation dans la sidebar
RegisterNavItem(item NavItemDef)
// Accès à la base SQLite (isolation par module possible via préfixe)
DB() *sql.DB
// Service SSH — exécute une commande sur la cible (host ou lxc:VMID)
// La cible "host" exécute directement, "lxc:101" wrappe via pct exec
RunOnTarget(target, command string) (string, error)
// Service SSH — streaming de la sortie ligne par ligne
// Le channel est fermé à la fin de la commande
StreamOnTarget(target, command string, output chan<- string) error
}
// WSHandler est un handler WebSocket pour un channel nommé.
@ -55,12 +67,11 @@ type WidgetDef struct {
// SettingsTabDef décrit un onglet de paramètres fourni par un module.
type SettingsTabDef struct {
ID string `json:"id"`
Label string `json:"label"`
Icon string `json:"icon"`
// Path est le chemin frontend du composant Vue à charger (lazy import).
ID string `json:"id"`
Label string `json:"label"`
Icon string `json:"icon"`
ComponentPath string `json:"component_path"`
}
// MigrationFn est une fonction de migration optionnelle (pour les migrations non-SQL).
// MigrationFn est une fonction de migration optionnelle.
type MigrationFn func(db *sql.DB) error