# Architecture Go + htmx (sogoms-admin) Guide pour comprendre et modifier l'interface d'administration SOGOMS. ## Vue d'ensemble ``` Browser (htmx) ←→ Go Server ←→ Services (sockets) │ │ │ HTTP GET/POST │ ▼ ▼ Templates Handlers (HTML) (Go) ``` **Stack :** - **Backend** : Go net/http (pas de framework) - **Frontend** : Go templates + htmx + Pico.css - **Principe** : Le serveur renvoie des fragments HTML, htmx les injecte dans le DOM --- ## Structure des fichiers ``` cmd/sogoms/admin/ ├── main.go # Point d'entrée, routes, config ├── handlers.go # Handlers HTTP (logique métier) ├── middleware.go # Auth, CSRF, logging, rate limiting ├── session.go # Gestion des sessions ├── services.go # Appels vers microservices (sockets) └── templates/ ├── layout.html # Layout commun (head, nav, footer) ├── login.html # Page de connexion ├── dashboard.html # Dashboard principal └── partials/ # Fragments htmx ├── apps_list.html └── services_status.html ``` --- ## Flux de données ### 1. Requête initiale (page complète) ``` GET /admin/ │ ▼ main.go:103 ─────────────────────────────────────────────┐ │ mux.HandleFunc("GET /admin/", AuthMiddleware(...)) │ └───────────────────────────────────────────────────────┘ │ ▼ handlers.go:114-138 ─────────────────────────────────────┐ │ func HandleDashboard(w, r) { │ │ data := map[string]any{ │ │ "Title": "Dashboard", │ │ "Apps": accessibleApps, │ │ ... │ │ } │ │ s.render(w, "dashboard.html", data) │ │ } │ └───────────────────────────────────────────────────────┘ │ ▼ templates/dashboard.html ────────────────────────────────┐ │ {{template "layout" .}} │ │

Dashboard

│ │
│ │ Chargement... │ │
│ └───────────────────────────────────────────────────────┘ ``` ### 2. Requête htmx (fragment) ``` hx-get="/admin/api/services/health" │ ▼ main.go:109 ─────────────────────────────────────────────┐ │ mux.HandleFunc("GET /admin/api/services/health", ...) │ └───────────────────────────────────────────────────────┘ │ ▼ handlers.go:172-193 ─────────────────────────────────────┐ │ func HandleAPIServicesHealth(w, r) { │ │ statuses := s.services.HealthCheck() │ │ data := map[string]any{"Services": statuses} │ │ s.render(w, "partials/services_status.html", data)│ │ } │ └───────────────────────────────────────────────────────┘ │ ▼ templates/partials/services_status.html ─────────────────┐ │ │ └───────────────────────────────────────────────────────┘ │ ▼ htmx remplace le contenu du
avec ce fragment ``` --- ## Attributs htmx essentiels | Attribut | Description | Exemple | |----------|-------------|---------| | `hx-get` | GET AJAX vers URL | `hx-get="/admin/api/apps"` | | `hx-post` | POST AJAX vers URL | `hx-post="/admin/api/apps/create"` | | `hx-trigger` | Quand déclencher | `load`, `click`, `every 30s`, `submit` | | `hx-target` | Où injecter la réponse | `hx-target="#result"` (défaut: élément courant) | | `hx-swap` | Comment injecter | `innerHTML` (défaut), `outerHTML`, `beforeend` | | `hx-indicator` | Spinner pendant chargement | `hx-indicator=".loading"` | | `hx-confirm` | Confirmation avant action | `hx-confirm="Supprimer ?"` | ### Exemples ```html
Chargement...
``` --- ## Templates Go ### Syntaxe de base ```html {{.Title}} {{if .IsSuperAdmin}} Super Admin {{else}} App Admin {{end}} {{range .Apps}}
  • {{.Name}}
  • {{end}} {{template "layout" .}} {{define "content"}} Contenu ici {{end}} ``` ### Structure layout.html ```html {{define "layout"}} {{.Title}} - SOGOMS Admin
    {{template "content" .}}
    {{end}} ``` ### Structure page (dashboard.html) ```html {{define "dashboard.html"}} {{template "layout" .}} {{end}} {{define "content"}}

    Dashboard

    {{end}} ``` --- ## Ajouter une nouvelle fonctionnalité ### Exemple : Liste des jobs cron **1. Créer le partial** `templates/partials/cron_jobs.html` ```html {{define "partials/cron_jobs.html"}} {{range .Jobs}} {{end}}
    Job Schedule Prochain run
    {{.Name}} {{.Schedule}} {{.NextRun}}
    {{end}} ``` **2. Ajouter le handler** dans `handlers.go` ```go func (s *AdminServer) HandleAPICronJobs(w http.ResponseWriter, r *http.Request) { user := GetUserFromContext(r.Context()) if user == nil { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } jobs, err := s.services.GetCronJobs() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } data := map[string]any{ "Jobs": jobs, } s.render(w, "partials/cron_jobs.html", data) } ``` **3. Ajouter la route** dans `main.go` ```go mux.HandleFunc("GET /admin/api/cron/jobs", AuthMiddleware(sessions, adminCfg, server.HandleAPICronJobs)) ``` **4. Utiliser dans le dashboard** `templates/dashboard.html` ```html
    Jobs Cron
    Chargement...
    ``` --- ## Mode développement Le flag `-dev` recharge les templates à chaque requête : ```yaml # config/sogoctl.yaml sogoms-admin: args: - "-templates" - "/config/admin/templates" - "-dev" # ← rechargement auto ``` **Workflow dev :** ```bash # 1. Modifier un template localement vim cmd/sogoms/admin/templates/dashboard.html # 2. Déployer les templates (sans recompilation) ./deploy-admin.sh # 3. Rafraîchir le navigateur → changements visibles ``` **Pour la prod**, retirer `-dev` (templates chargés une fois au démarrage). --- ## Fichiers de configuration | Fichier | Rôle | |---------|------| | `/secrets/admin_users.yaml` | Users, passwords, rôles | | `/secrets/admin_session_secret` | Clé HMAC pour cookies | | `/config/sogoctl.yaml` | Args de sogoms-admin | | `/config/admin/templates/` | Templates HTML (si -templates) | --- ## Ressources - **htmx** : https://htmx.org/docs/ - **Pico.css** : https://picocss.com/docs/ - **Go templates** : https://pkg.go.dev/html/template