1
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
# Dossier technique pour le deuxième widget Glance de monitoring d’agents Rust
|
||||
|
||||
## Conclusion opérationnelle
|
||||
|
||||
Oui, ton deuxième widget est **réalisable**, et il est même cohérent avec l’architecture de Glance à condition de le traiter comme **une application externe intégrée dans Glance**, pas comme un widget natif compilé “dans” Glance en Rust. Le dépôt principal de Glance est un projet **Go** avec un `go.mod`, et sa mécanique de widgets personnalisés repose aujourd’hui sur quatre portes d’entrée documentées par le projet lui-même : `iframe`, `html`, `extension` et `custom-api`. Glance précise aussi que les pages **ne se mettent pas à jour automatiquement en arrière-plan** et qu’un rechargement de page est normalement nécessaire pour récupérer les nouvelles données. Pour ton besoin — tuiles live, popup latéral, auto-découverte, installation d’agent, mise à jour, métriques à fréquences différentes et états de veille/arrêt/actif — l’option la plus robuste est donc : **backend Rust + petite UI web servie par ce backend + intégration dans Glance via `iframe`**. citeturn36view0turn33view1turn35view0turn34view0
|
||||
|
||||
Il y a aussi une nuance importante de vocabulaire : le projet étudié ici est **Glance**, pas **Glances**. La documentation de Glance dit d’ailleurs explicitement que le widget `server-stats` est encore “under development”, qu’il peut s’appuyer sur le **Glance Agent** pour des serveurs distants, et que le support d’autres providers “such as Glances” viendra plus tard. Autrement dit, ton besoin avancé dépasse clairement ce que le widget natif `server-stats` couvre aujourd’hui. citeturn3view0
|
||||
|
||||
Mon verdict est donc simple : **oui pour Rust**, mais **en service externe** consommé par Glance, pas comme module interne du binaire Go de Glance. Si tu veux un rendu vraiment vivant et administrable, je te recommande de bâtir un petit produit séparé, par exemple `fleet-collector` et `fleet-agent`, puis de l’afficher dans une page Glance dédiée. Cette approche servira d’ailleurs très bien ton **premier widget IP réseau** et ton **deuxième widget agents** avec un même modèle d’inventaire. citeturn33view1turn34view0turn31view0turn35view0
|
||||
|
||||
## Ce que Glance permet réellement pour ce besoin
|
||||
|
||||
Glance organise l’interface autour de **pages** et de **colonnes**. Une page peut jouer le rôle de “tab” au sens fonctionnel, avec un layout `wide` et des colonnes `full` ou `small`. Si tu veux réserver un onglet complet à ton monitoring, Glance le permet nativement côté configuration, sans toucher au code source du projet. citeturn3view0
|
||||
|
||||
Pour ton cas, les trois mécanismes utiles sont les suivants. D’abord, `custom-api` sait appeler une API JSON avec `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, gérer des headers, un body, des `subrequests`, puis rendre le résultat avec un template basé sur **Go `html/template`** et `gjson`. C’est puissant pour des widgets “server-rendered” relativement riches, mais la doc de Glance dit aussi explicitement que ce mode demande des notions de programmation, HTML, CSS et templates Go. Ensuite, `extension` est un protocole HTTP simple où un service externe renvoie du HTML et quelques headers spécifiques ; Glance documente néanmoins que cette API est **work in progress**, que le cache par défaut d’une extension est de **30 minutes**, qu’il est configurable, et que le seul type de contenu supporté actuellement est `html`. Enfin, `iframe` embarque simplement une application externe à partir d’une URL et d’une hauteur donnée. Pour un écran de supervision avec popups, tuiles dynamiques, tri intelligent, interaction fine et mises à jour live, `iframe` est de loin la meilleure option. citeturn35view0turn31view0turn34view0turn33view1
|
||||
|
||||
La principale raison est le modèle d’actualisation de Glance. Le README officiel dit que les widgets ne font **pas de requêtes périodiques en arrière-plan** et que les données sont récupérées au chargement puis mises en cache ; il faut normalement recharger la page pour voir les changements. Il existe bien une pull request ouverte ajoutant des “live-events” avec **polling serveur toutes les 15 secondes** et SSE pour `monitor` et `custom-api`, mais elle est **encore ouverte** au 19 mai 2026 et n’appartient donc pas au comportement stable documenté du `main` actuel. Ton besoin de CPU/RAM à 1 seconde ne doit pas dépendre d’une PR non mergée. citeturn33view1turn33view0
|
||||
|
||||
Glance reste toutefois très bon comme **conteneur**, car tu peux lui faire servir des assets via `assets-path`, injecter du HTML dans le `<head>` avec `document.head`, ajouter un `custom-css-file`, et cibler les widgets par `widget-type-*` ou `css-class`. Autrement dit, même si le vrai widget vit dans un service Rust séparé, tu peux le “fondre” visuellement dans l’univers Glance. citeturn32view0turn32view1turn32view2turn32view3
|
||||
|
||||
Un squelette de configuration Glance pour ce deuxième widget ressemblerait à ceci :
|
||||
|
||||
```yaml
|
||||
pages:
|
||||
- name: Infrastructure
|
||||
width: wide
|
||||
columns:
|
||||
- size: full
|
||||
widgets:
|
||||
- type: iframe
|
||||
title: Fleet Monitor
|
||||
source: https://dash.example.net/widgets/agents
|
||||
height: 820
|
||||
css-class: fleet-monitor
|
||||
```
|
||||
|
||||
Si tu veux garder un mode “dégradé” purement Glance, tu peux aussi fournir un **petit widget `custom-api`** qui n’affiche qu’un résumé global : nombre d’agents, nombre offline, charge CPU moyenne, machines en alerte, lien d’installation et lien “ouvrir la vue détaillée”. Mais la vue principale riche doit rester dans l’`iframe`. citeturn35view0turn34view0turn32view3
|
||||
|
||||
## Architecture recommandée pour le widget agents
|
||||
|
||||
Je te recommande une architecture en trois briques. **Première brique** : un `fleet-agent` Rust installé sur chaque VM ou machine physique. **Deuxième brique** : un `fleet-collector` Rust central qui reçoit les enregistrements, stocke l’inventaire, expose une API et un flux temps réel. **Troisième brique** : une UI web légère, servie par le collector, affichée dans Glance via `iframe`. Cette séparation colle au modèle de Glance, contourne proprement l’absence d’auto-refresh généralisé, et te laisse toute latitude pour le placement intelligent des tuiles, les popups, les liens personnalisés et les workflows d’installation/mise à jour. citeturn33view1turn34view0turn35view0turn31view0
|
||||
|
||||
```text
|
||||
[ VM / Bare metal ]
|
||||
└─ fleet-agent (Rust)
|
||||
├─ collecte CPU / RAM / disque / process
|
||||
├─ inventaire matériel
|
||||
├─ écoute veille / reboot / shutdown
|
||||
└─ push JSON vers collector
|
||||
|
||||
[ Serveur central ]
|
||||
└─ fleet-collector (Rust)
|
||||
├─ API d’enregistrement des agents
|
||||
├─ stockage inventaire + séries courtes
|
||||
├─ stream SSE / WebSocket pour l’UI
|
||||
├─ génération commandes install / update
|
||||
└─ UI web du widget
|
||||
|
||||
[ Glance ]
|
||||
└─ page "Infrastructure"
|
||||
└─ widget iframe -> UI du collector
|
||||
```
|
||||
|
||||
Je te conseille un modèle **push** depuis les agents vers le collector, parce que ton cahier des charges inclut des événements qui se prêtent mal à un modèle purement pull : arrêt propre, passage en veille, reprise, changement d’état, heartbeat, auto-enregistrement au boot, et publication de métriques avec des fréquences hétérogènes. Comme Glance n’est pas un moteur de polling continu des widgets, et comme `server-stats` est encore explicitement en développement, vouloir faire ça directement “dans Glance” te mettrait rapidement à contre-courant du produit. citeturn33view1turn3view0
|
||||
|
||||
La bonne granularité, à mon sens, est la suivante. **Inventaire statique** au démarrage puis deux fois par jour : hostname, OS, carte mère, modèle, vendor, serial, interfaces réseau, icône, emplacement, groupe, parent, liens. **Métriques rapides** toutes les 1 à 5 secondes : CPU, RAM, état de disponibilité, éventuellement charge GPU si la machine en a une. **Métriques lentes** toutes les 30 minutes : capacité disque, occupation des volumes, état SMART synthétique. **Événements** à la volée : démarrage, arrêt, veille, reprise, changement d’IP, agent obsolète, échec d’un module. Cette séparation colle bien aux capacités des APIs système disponibles et évite de transmettre trop de bruit. citeturn22view0turn24search7turn8search0turn8search4turn12search0turn11search0turn10search0turn30search6
|
||||
|
||||
Pour le **volet secondaire** de tes tuiles, je te conseille de ne pas tout remonter à 1 seconde. Les données de popup — SMBIOS/DMI, services, icône, emplacement, relations de parenté, liste des top processus, état détaillé des disques — peuvent être stockées côté collector et réactualisées plus lentement, voire à la demande. Les docs de `sysinfo` montrent explicitement que le CPU s’appuie sur des différences temporelles, qu’il faut garder une même instance `System`, et qu’il vaut mieux utiliser des refresh ciblés pour les performances. Cette philosophie va exactement dans le sens de ton besoin “fréquences différentes selon les familles de données”. citeturn22view0turn26view0
|
||||
|
||||
## Stack Rust recommandée
|
||||
|
||||
Pour le **runtime** et l’API réseau, la combinaison la plus naturelle est **Tokio + Axum**. Tokio est le runtime asynchrone standard de fait pour les applications réseau Rust ; sa primitive `interval` est adaptée à des boucles planifiées de collecte. Axum est conçu pour fonctionner avec Tokio et Hyper, sait gérer des APIs HTTP, les WebSockets, et fournit nativement des réponses **SSE**. Pour ton widget, SSE suffit souvent très bien : le collector pousse les changements de tuiles, et l’UI met à jour la grille sans refresh. citeturn16search22turn20search2turn20search5turn16search2turn20search0turn20search4
|
||||
|
||||
Pour la **collecte système générale**, je recommande **`sysinfo`** comme base. La doc officielle montre que la crate couvre processus, mémoire, CPU, disques, réseaux, températures de composants, et qu’elle dispose de `RefreshKind`, `ProcessRefreshKind`, `DiskRefreshKind`, ainsi que d’un `MINIMUM_CPU_UPDATE_INTERVAL`. Elle expose aussi des informations de carte mère, par exemple nom, vendor, version, numéro de série et asset tag via `Motherboard`. Pour les top processus, elle fournit notamment `cpu_usage()`, `memory()`, `status()`, `parent()`, `exe()` et `cmd()`. C’est une excellente base pour un agent Rust Linux-first. citeturn22view0turn25view0turn25view1turn26view0turn26view1
|
||||
|
||||
Pour le **matériel** et les identifiants, tu as deux étages. D’abord `sysinfo`, qui sait remonter une partie de l’inventaire matériel. Ensuite, pour les informations plus complètes ou plus “admin” — SMBIOS/DMI, châssis, BIOS, vendor, références — tu peux compléter avec `dmidecode` ou directement les fichiers sysfs. La doc de `dmidecode` rappelle toutefois que ces informations viennent du firmware SMBIOS/DMI et peuvent être **rapides et sûres à lire** mais **parfois peu fiables**, car elles dépendent de ce que le firmware expose. C’est important pour ton design : il faut afficher ces champs comme de l’inventaire déclaratif, pas comme une vérité absolue. citeturn25view0turn9search12turn9search6
|
||||
|
||||
Pour les **disques**, je te conseille de séparer “occupation des volumes” et “santé physique”. L’occupation se fait très bien avec `sysinfo::Disks`. La santé disque et les attributs SMART doivent venir de **`smartctl`**, car la documentation officielle indique que `smartctl` contrôle et surveille le système SMART des disques ATA/SATA, SCSI/SAS et SSD, et son code source expose un mode `--json`/`-j` pour une sortie JSON ou YAML. C’est le meilleur compromis Rust aujourd’hui : exécuter `smartctl`, parser le JSON, et normaliser un petit résumé par disque pour ton popup ou tes alertes. citeturn24search7turn8search0turn8search4
|
||||
|
||||
Pour les **GPU**, je te recommande un design par provider optionnel. Côté NVIDIA, la référence est clairement **NVML** ; NVIDIA documente NVML comme une API C de monitoring et management des GPU, thread-safe, et base de `nvidia-smi`. En Rust, `nvml-wrapper` permet d’y accéder proprement. Côté AMD, la doc ROCm indique que **AMD SMI** est l’interface unifiée vers laquelle AMD pousse désormais les usages de monitoring/management GPU et qu’elle est la successeure de ROCm SMI. Traduction pratique : **phase 1 NVIDIA si tu veux aller vite**, **phase 2 AMD SMI si ton parc le justifie**. citeturn10search0turn10search1turn6search4turn30search1turn30search6
|
||||
|
||||
Pour la **base de données centrale**, tu as deux bons choix. Si ton parc reste modeste et si tu veux un déploiement très simple, **SQLite en WAL** est parfaitement défendable avec un **collector unique** et un modèle “une écriture logique à la fois”. La doc SQLite rappelle comment activer WAL et souligne que ce mode permet, en règle générale, que les écrivains ne bloquent pas les lecteurs et inversement, tout en expliquant le rôle des checkpoints. En revanche, la doc SQLite signale aussi un **bug rare WAL-reset** corrigé en **3.51.3** le 13 mars 2026, ainsi que dans certains backports. Donc si tu pars sur SQLite/WAL, veille à utiliser une version corrigée. Si tu veux monter en charge, multi-utilisateur, ou faire de la rétention longue avec analytics, **PostgreSQL** devient plus confortable. Côté Rust, **SQLx** est adapté aux deux mondes : crate async, support Tokio, support SQLite/PostgreSQL, requêtes vérifiables à la compilation. citeturn27view0turn19search0turn19search1
|
||||
|
||||
Pour les **mises à jour de l’agent**, deux options sont sérieuses. Soit tu fais du **self-update** in-place avec une crate dédiée ; la doc de `self_update` précise qu’elle sait mettre à jour des exécutables Rust en place à partir de plusieurs backends de distribution. Soit, ce que je préfère dans ton contexte, tu gardes un **installeur shell** et des **binaires versionnés**, ce qui te donne un chemin d’upgrade plus transparent, plus compatible avec systemd et plus facile à signer/vérifier. Pour la signature, **Minisign** est une option légère et robuste, officiellement documentée comme un outil simple de signature/vérification basé sur **Ed25519**. citeturn16search1turn17search0
|
||||
|
||||
## Déploiement, découverte et mises à jour
|
||||
|
||||
Pour la **distribution** des artefacts, Gitea est suffisant. La documentation officielle du **Generic Package Registry** explique qu’il peut publier des fichiers génériques comme des binaires de release, via `PUT`, et les télécharger via `GET` sur des URLs versionnées. Gitea documente aussi l’authentification API par Basic auth, query token, ou surtout `Authorization: token ...`. Concrètement, tu peux stocker dans Gitea : le binaire par plateforme, le fichier `.minisig`, un `manifest.json`, et le `install.sh`. citeturn28view0turn28view2
|
||||
|
||||
Dans ce modèle, ton widget central peut générer la commande d’installation exactement dans l’esprit que tu veux, par exemple :
|
||||
|
||||
```bash
|
||||
curl -fsSL https://git.example.net/api/packages/infra/generic/fleet-agent/1.2.0/install.sh \
|
||||
| bash -s -- \
|
||||
--collector https://collector.example.net \
|
||||
--token AGENT_REG_TOKEN \
|
||||
--group vm \
|
||||
--icon server \
|
||||
--link https://proxmox.example.net
|
||||
```
|
||||
|
||||
Je te conseille toutefois que ce script **ne télécharge pas directement un binaire non vérifié** : il doit récupérer le manifest, télécharger l’artefact pour l’OS/arch courants, vérifier la signature Minisign, installer le binaire, créer l’unité systemd et démarrer le service. C’est plus sûr que le simple `curl | bash` aveugle, tout en restant simple à opérer. citeturn28view0turn17search0
|
||||
|
||||
Pour l’**auto-découverte**, je te recommande de faire de la **self-registration** d’abord, et du **mDNS/DNS-SD** seulement en option locale. Les RFC IETF disent clairement que **mDNS** fonctionne sur le **local link** en absence de DNS conventionnel, et que **DNS-SD** sert à découvrir les instances d’un service à partir de son type. Cela veut dire que mDNS est pratique sur un LAN plat, mais pas un mécanisme principal pour des segments séparés, des VLANs ou des routes plus complexes. En outre, les travaux IETF sur la privacy de DNS-SD rappellent que la découverte de services divulgue typiquement des noms d’hôtes et des paramètres réseau. En pratique : **annonce mDNS facultative pour le confort local**, **enregistrement explicite au collector pour le vrai fonctionnement**. citeturn14search3turn14search1turn14search7
|
||||
|
||||
Pour la **détection des états système**, le plus propre est de mélanger hooks systemd et écoute D-Bus. La documentation systemd indique que `ExecStop=` et `ExecStopPost=` s’exécutent lors d’une opération d’arrêt/restart du service, ce qui te donne un bon point de sortie “propre” pour publier un dernier événement. En parallèle, l’interface `org.freedesktop.login1` documente les signaux `PrepareForShutdown()` et `PrepareForSleep()` envoyés juste avant et juste après extinction/suspend, avec un booléen indiquant l’entrée ou la sortie de l’état. C’est exactement ce qu’il te faut pour les états “veille”, “arrêt”, “actif”. citeturn11search0turn12search0
|
||||
|
||||
Enfin, pour la **mise à jour par mini-modules**, le collector peut exposer une notion de “catalogue de modules” par agent : par exemple `smart`, `gpu-nvidia`, `gpu-amd`, `services-extra`, `netinfo-advanced`. Le widget n’a pas besoin d’exécuter lui-même le code distant ; il peut afficher la commande exacte à lancer, ou bien l’agent peut **poller un manifest signé** et proposer/appliquer l’update localement selon une politique. Cette seconde approche est plus sûre et plus industrialisable si tu comptes dépasser quelques machines. citeturn28view0turn16search1turn17search0
|
||||
|
||||
## Plan de réalisation recommandé
|
||||
|
||||
Je te recommande un **MVP Linux-first** en trois étapes. **Étape une** : collector Rust + UI iframe + agent Rust qui remonte en push l’enregistrement, le heartbeat, CPU, RAM, volumes, état de base, et un top 5 processus rafraîchi toutes les 10 à 15 secondes. Tu obtiens déjà les tuiles, l’état online/offline, les popups secondaires et la génération de commandes d’installation. Cette première étape s’appuie presque entièrement sur `tokio`, `axum`, `sysinfo`, `systemd` et la mécanique Glance `iframe`. citeturn20search2turn16search2turn22view0turn26view0turn34view0
|
||||
|
||||
**Étape deux** : enrichissement matériel. Tu ajoutes DMI/SMBIOS, cartes réseau, disques SMART, capteurs/temperatures disponibles, et éventuellement données GPU NVIDIA. Cette couche est surtout utile pour ton volet latéral “hardware, icon, services, lien, emplacement, parent”. C’est aussi l’étape où tu peux stabiliser ton modèle d’inventaire pour qu’il serve **en commun** ton premier widget “cartographie IP réseau” et ton deuxième widget “agents”. citeturn25view0turn9search12turn8search0turn10search0
|
||||
|
||||
**Étape trois** : exploitation avancée. Tu ajoutes auto-discovery optionnelle, agent modules, politiques d’update, AMD SMI si nécessaire, et éventuellement une petite vue Glance `custom-api` de synthèse pour les KPIs globaux. À ce stade, Glance reste le portail, mais la logique métier vit clairement dans ton service Rust, ce qui est précisément ce que l’architecture de Glance favorise déjà avec `iframe`, `extension` et `custom-api`. citeturn35view0turn31view0turn34view0
|
||||
|
||||
## Limites et points ouverts
|
||||
|
||||
La principale limite technique est que ton besoin est **beaucoup plus proche d’un mini-produit de fleet monitoring** que d’un simple widget statique. Ce n’est pas un défaut de Glance ; c’est juste que Glance documente aujourd’hui un modèle de données principalement **fetch-on-load + cache**, avec des extensions HTML et des custom APIs orientées rendu, et non un framework de widgets riches temps réel comparable à une SPA supervisée par un bus d’événements. L’existence d’une PR SSE non mergée va dans ce sens : le besoin de live updates existe, mais il n’est pas encore un socle stable du projet. citeturn33view1turn33view0turn31view0turn35view0
|
||||
|
||||
Le deuxième point ouvert est le **périmètre OS**. La stack que je te propose est excellente pour un parc **Linux / VMs / bare metal sous systemd**, parce qu’elle repose sur `smartctl`, login1/systemd, SMBIOS/DMI, NVML/AMD SMI et `sysinfo`. Si tu veux couvrir sérieusement Windows, BSD ou macOS avec le même niveau de détails matériels et d’événements système, il faudra des providers spécifiques ou un niveau de service un peu moins riche. `sysinfo` est multi-OS, mais tout ce qui touche au shutdown propre, à SMART, à SMBIOS détaillé et à la télémétrie GPU dépend fortement de l’OS et du vendor. citeturn22view0turn9search12turn8search0turn12search0turn10search0turn30search6
|
||||
|
||||
Le troisième point ouvert est la **taille de parc** visée. Si tu parles de quelques dizaines de machines, SQLite en WAL avec un collector unique, un cache mémoire court et des agrégats récents est probablement suffisant. Si tu parles de centaines de nœuds, d’historiques longs ou de multiples utilisateurs simultanés du dashboard, je partirais plus vite sur PostgreSQL et un découplage plus net entre ingestion temps réel et UI. Et si tu restes sur SQLite, je te recommande explicitement de vérifier la version déployée au regard des correctifs WAL mentionnés par la documentation SQLite de mars 2026. citeturn27view0turn19search1
|
||||
|
||||
En synthèse finale : **oui, tu peux ajouter ce widget avec du Rust** ; **non, je ne te recommande pas de vouloir l’ajouter “dans Glance” comme widget natif Rust** ; **oui, je te recommande fortement un service Rust externe affiché par `iframe` dans une page Glance**, avec un agent Rust en push, un collector temps réel, un stockage léger, une distribution via Gitea, et une vérification de signature des binaires. C’est l’architecture la plus propre, la plus maintenable, et la plus fidèle à la manière dont Glance expose aujourd’hui ses points d’extension. citeturn36view0turn35view0turn34view0turn31view0turn33view1
|
||||
@@ -0,0 +1,141 @@
|
||||
# Dossier technique sur Glance et l’ajout d’un onglet IP réseau
|
||||
|
||||
## Périmètre de l’analyse
|
||||
|
||||
Le dépôt que tu as donné est bien **`glanceapp/glance`**, c’est-à-dire **Glance**, le dashboard self-hosted de l’organisation glanceapp. Ce n’est **pas** **Glances** de nicolargo, qui est un outil de monitoring système différent. La nuance compte beaucoup pour ton projet, parce que Glance est d’abord un **framework de tableau de bord configurable en YAML**, pensé pour agréger et afficher des données, pas un moteur natif de supervision réseau temps réel. citeturn2view0turn26search2turn19view0
|
||||
|
||||
La conséquence la plus importante pour ton besoin est celle-ci : **Glance doit être traité comme la couche d’interface**, tandis que le **scan de ton réseau 10.0.0.0/22** et l’inventaire détaillé des IP doivent vivre dans **un service séparé**. Le README officiel précise en effet qu’une **actualisation de page est nécessaire** pour mettre à jour les informations et qu’**aucune requête périodique n’est effectuée en arrière-plan** par les widgets ; les données sont chargées au rendu puis mises en cache suivant la durée définie pour chaque widget. citeturn19view0
|
||||
|
||||
Autre point utile : le widget natif **`monitor`** existe déjà, mais il est conçu pour **une liste de sites/URLs** avec `title`, `url`, éventuel `check-url`, `icon`, timeouts et variantes d’affichage. Il ne correspond pas à un **inventaire CIDR**, à un découpage par sous-réseaux, ni à un **volet détaillé par IP**. Ton besoin dépasse donc nettement le périmètre des widgets intégrés actuels. citeturn12view2
|
||||
|
||||
## Langages et technologies employés
|
||||
|
||||
D’après la répartition des langages affichée sur le dépôt GitHub, Glance est majoritairement écrit en **Go**, avec une part significative de **HTML**, **JavaScript** et **CSS**, puis un peu de **Dockerfile**. La page du dépôt affiche précisément **Go 55,5 %**, **HTML 16,2 %**, **JavaScript 15,0 %**, **CSS 13,2 %** et **Dockerfile 0,1 %**. Cette répartition correspond bien à ce qu’on voit dans l’arborescence : un backend Go, des templates HTML embarqués, des assets CSS/JS locaux et un packaging Docker très léger. citeturn19view0
|
||||
|
||||
Le cœur applicatif est un module Go nommé `github.com/glanceapp/glance`, et le `go.mod` déclare aujourd’hui **Go 1.24.3**. Les dépendances directes sont révélatrices : `fsnotify` pour la surveillance et le rechargement de config, `gofeed` pour les flux, `gopsutil/v4` pour les métriques système, `gjson` pour le parsing JSON dans les widgets custom, `x/crypto` pour l’authentification et `yaml.v3` pour toute la configuration. À noter qu’il y a un léger écart documentaire : le `README` parle encore d’un build avec Go **>= 1.23**, mais le `go.mod` du dépôt `main` pointe déjà sur **1.24.3** ; si tu recompiles, c’est le `go.mod` qu’il faut considérer comme source de vérité. citeturn4view0turn19view0
|
||||
|
||||
Le point d’entrée est volontairement minuscule : `main.go` ne fait qu’appeler `glance.Main()` depuis `internal/glance`. Le `Dockerfile` confirme le modèle de distribution : build en **Go sur Alpine**, avec `CGO_ENABLED=0`, puis copie du binaire dans une image Alpine finale. En pratique, Glance est donc pensé comme **un binaire Go autonome**, sans runtime Node, sans bundle frontend externe et avec une surface de build très simple. citeturn4view2turn4view1
|
||||
|
||||
Le frontend n’emploie pas de framework type React/Vue/Svelte. Le README insiste sur du **“minimal vanilla JS”**, et les guidelines de contribution ajoutent même explicitement **“No `package.json`”** et la consigne d’**éviter les nouvelles dépendances**. Pour un développeur, c’est un signal très clair : Glance privilégie du **server-side rendering** avec templates Go, des assets statiques maison, et un JavaScript utilitaire limité. citeturn19view0
|
||||
|
||||
Techniquement, les assets sont **embarqués dans le binaire** via `//go:embed` pour `static` et `templates`. Le code montre aussi que le CSS est **bundlé au runtime** à partir de `internal/glance/static/css`, tandis que `internal/glance/static/js` contient des scripts comme `animations.js`, `calendar.js`, `login.js`, `masonry.js`, `page.js`, `popover.js`, `templating.js`, `todo.js` et `utils.js`. Côté templates, on trouve les vues page/document/footer ainsi qu’un template HTML dédié par widget ou style de widget. citeturn9view4turn13view0turn13view1turn14view0
|
||||
|
||||
## Structure interne de l’application
|
||||
|
||||
L’arborescence de haut niveau est sobre et lisible : à la racine, on a surtout `docs/`, `internal/glance/`, `pkg/sysinfo/`, `main.go`, `go.mod`, `Dockerfile` et la config de release. Pour une lecture développeur, **tout le produit** est concentré dans `internal/glance`, tandis que `docs/` sert de documentation fonctionnelle et `pkg/sysinfo/` isole une partie des métriques système. citeturn2view0
|
||||
|
||||
La structure de configuration est également très claire dans `config.go`. On y voit les blocs top-level **`server`**, **`auth`**, **`document`**, **`theme`**, **`branding`** et surtout **`pages`**. Chaque page possède un `name`, un `slug`, une largeur (`width`), des `head-widgets` et des `columns`; chaque colonne porte une `size` et une liste de `widgets`. La documentation confirme que la **première page devient la page d’accueil**, que les pages apparaissent **automatiquement dans la navigation** dans l’ordre de déclaration, et qu’une page peut avoir **jusqu’à 3 colonnes**, avec **1 ou 2 colonnes `full`** au minimum. citeturn16view0turn11view0
|
||||
|
||||
Autrement dit, dans le vocabulaire de Glance, ton futur “tab” n’est pas une entité spéciale : c’est simplement **une nouvelle page** Glance. C’est une bonne nouvelle, parce que ton onglet réseau peut donc être ajouté proprement comme **page dédiée**, avec une largeur `wide` et un ou plusieurs widgets de présentation. La doc donne d’ailleurs des largeurs `default`, `slim` et `wide`, et associe `wide` à **1920 px max**, ce qui est pertinent pour ton souhait d’afficher un très grand nombre de petites cases IP. citeturn11view0
|
||||
|
||||
Le système de widgets natifs est, lui aussi, très explicite. Dans `internal/glance`, on voit une série de fichiers `widget-*.go` et, côté templates, des fichiers `*.html` correspondants. La fonction `newWidget` dans `widget.go` enregistre les types connus comme `calendar`, `clock`, `weather`, `monitor`, `extension`, `custom-api`, `iframe`, `html`, `server-stats`, etc. Le même fichier définit une interface `widget` avec notamment `Render()`, `initialize()`, `requiresUpdate()`, `update()`, `handleRequest()` et les méthodes d’identification/configuration. En pratique, cela veut dire qu’un **widget natif** Glance est un **type Go compilé**, pas un plugin chargé dynamiquement. citeturn3view0turn24view0
|
||||
|
||||
Le couplage avec le rendu HTML est fort, mais propre : les widgets vivent en Go, le rendu passe par des templates de `html/template`, et les assets/template sont embarqués dans le binaire. J’en déduis qu’ajouter un widget natif implique toujours **des modifications du code source Go**, **un template HTML** et souvent **une recompilation complète** de l’application. Ce n’est pas une architecture à plugins dynamiques. citeturn24view0turn9view4turn4view1
|
||||
|
||||
## Les options réalistes pour ajouter ton module
|
||||
|
||||
Officiellement, Glance documente **quatre façons** de créer des widgets personnalisés : **`iframe`**, **`html`**, **`extension`** et **`custom-api`**. Pour ton cas, les deux plus sérieuses sont `custom-api`, `extension` et, très franchement, `iframe` si tu veux une vraie mini-application interactive. Le mode `html` reste trop statique pour un module IPAM réseau un peu avancé. citeturn19view0turn12view0turn12view1turn12view4
|
||||
|
||||
Le widget **`custom-api`** est la voie la plus “Glance-compatible” quand on dispose déjà d’une API JSON. La documentation officielle dit explicitement que sa configuration demande des notions de **programmation**, **HTML**, **CSS**, **langage de templates Go** et concepts propres à Glance. Elle précise aussi que le rendu repose sur `html/template` et `gjson`. C’est très puissant si ton backend de scan publie un JSON structuré et si tu acceptes un modèle d’affichage plutôt **déclaratif**. Le dépôt `glanceapp/community-widgets` est d’ailleurs un excellent indicateur de la stratégie réelle du projet : il précise que la plupart des widgets communautaires sont faits avec `custom-api`, et que cette approche est **beaucoup plus simple à déployer** qu’une extension. citeturn12view0turn9view1turn18search2turn20search0
|
||||
|
||||
Le widget **`extension`** est plus souple côté langage, parce qu’il ne demande qu’**un serveur HTTP** renvoyant du contenu et quelques en-têtes spéciaux comme `Widget-Title`, `Widget-Title-URL`, `Widget-Content-Type` et `Widget-Content-Frameless`. Mais la doc officielle prévient de deux choses importantes : **l’API d’extension est WIP** et **seul le type de contenu `html` est officiellement supporté pour l’instant**. Le dépôt communautaire confirme aussi que les extensions sont **plus impliquées**, car elles demandent un **serveur ou conteneur séparé**. Autrement dit : oui, c’est faisable et élégant pour du Rust, mais c’est moins stable et plus coûteux à maintenir qu’un `custom-api`. citeturn9view0turn12view1turn18search2
|
||||
|
||||
Le widget **`iframe`** est paradoxalement très intéressant pour toi. La doc officielle le décrit simplement comme un widget capable d’embarquer une source externe avec une hauteur configurable. Ça paraît basique, mais pour un **plan IP cliquable**, avec **volet de détails**, **rafraîchissement indépendant** et éventuellement un peu de JS applicatif, c’est en pratique **l’intégration la plus simple et la plus robuste** : tu gardes Glance comme shell de navigation et tu exécutes ton mini-dashboard réseau comme une petite app web autonome. citeturn12view4turn19view0
|
||||
|
||||
Enfin, si tu veux modifier **le cœur de Glance** pour ajouter un widget “officiel” à ton fork, les technologies à employer sont sans ambiguïté : **Go** pour la logique, **templates HTML Go** pour le rendu, éventuellement un peu de **JS/CSS statiques** dans `internal/glance/static`, et la **documentation YAML** dans `docs/configuration.md`. C’est la voie la plus intégrée, mais aussi celle qui te lie le plus au codebase et aux conventions du projet. citeturn3view0turn13view1turn14view0turn24view0
|
||||
|
||||
Glance fournit néanmoins quelques points d’extension utiles pour “hybrider” une approche custom : le serveur peut exposer un répertoire **`/assets/`**, le document global peut recevoir du **HTML custom dans `<head>`**, et le thème supporte un **fichier CSS custom**. En plus, chaque widget peut recevoir un `css-class`, et la doc explique que Glance utilise beaucoup de classes utilitaires, avec une classe `widget-type-{name}` pour cibler précisément un widget. En clair, tu peux très bien injecter **ton propre CSS** et même **ton propre JS** global si tu veux enrichir un `custom-api` avec un comportement plus dynamique. citeturn23view0turn23view2
|
||||
|
||||
## Architecture recommandée pour ton onglet IP
|
||||
|
||||
Pour ton besoin précis, je recommande une **page Glance dédiée** nommée par exemple **“Réseau”**, en `width: wide`, avec **une seule colonne `full`**. La raison est simple : une page Glance ne peut avoir que **trois colonnes maximum**, alors que ton découpage logique contient déjà **quatre sous-réseaux** (`10.0.0.x`, `10.0.1.x`, `10.0.2.x`, `10.0.3.x`), sans compter le volet de détail. En plus, un bloc `/22` représente **1024 adresses IPv4** ; à cette échelle, il vaut mieux afficher **un seul grand module full-width** avec quatre sections internes, plutôt que disperser l’UI dans les colonnes natives de Glance. citeturn11view0turn25calculator0
|
||||
|
||||
Je déconseille fortement d’embarquer le **moteur de ping** directement dans Glance. Le produit n’est pas conçu pour exécuter des sondes réseau en tâche de fond : il rafraîchit ses données **au chargement de page**, puis s’appuie sur le **cache de widget**. Ton service de scan doit donc être **un daemon séparé** qui fait le travail lourd à la fréquence de ton choix et qui publie un **état matérialisé** que Glance ne fait qu’afficher. C’est le découpage le plus propre techniquement, mais aussi celui qui colle le mieux à l’architecture officielle du projet. citeturn19view0
|
||||
|
||||
Je te conseille aussi de **ne pas assimiler “IP libre” à “ne répond pas au ping”**. La documentation officielle de Nmap rappelle qu’un hôte actif peut être **manqué** si un firewall filtre les probes ou leurs réponses, et qu’un échec de découverte ICMP ne permet pas de conclure qu’une machine est réellement absente. Pour ton dashboard, il vaut mieux distinguer au moins **`alive`**, **`seen`/`used`**, **`reserved`** et **`free`**, où `free` ne devient vrai qu’en absence de réponse **et** en absence d’occupation connue dans ton inventaire/DHCP/ARP. citeturn29search0turn29search1
|
||||
|
||||
Si tu veux le faire en **Rust**, le stack le plus cohérent pour le service externe est un backend de type **Axum + Tokio**, avec **SQLite via SQLx** si tu veux persister tes métadonnées riches (`hardware`, `icon`, `services`, `link`, `location`, `parent`, notes, dernières vues, etc.). Axum se présente comme une bibliothèque de routage HTTP et de handling ergonomique, Tokio comme le runtime asynchrone de référence pour I/O, timers et scheduling, et SQLx comme un toolkit SQL async “pure Rust” compatible SQLite. Pour ton besoin, c’est un trio très logique. citeturn27search0turn27search5turn27search2
|
||||
|
||||
L’API de ce service peut rester simple. Je te recommande un endpoint de synthèse pour le rendu global, et éventuellement un endpoint fin pour le détail d’une IP. Par exemple :
|
||||
|
||||
```json
|
||||
{
|
||||
"last_scan_at": "2026-05-19T12:34:56Z",
|
||||
"sections": [
|
||||
{
|
||||
"name": "VM",
|
||||
"cidr": "10.0.0.0/24",
|
||||
"items": [
|
||||
{
|
||||
"ip": "10.0.0.12",
|
||||
"state": "alive",
|
||||
"hostname": "vm-proxy-01",
|
||||
"icon": "mdi:server",
|
||||
"link": "https://vm-proxy-01.local",
|
||||
"services": ["ssh", "https"],
|
||||
"location": "rack-a",
|
||||
"parent": "pve01"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Ce schéma n’est pas imposé par Glance ; c’est le bon niveau d’abstraction pour séparer proprement **scan**, **inventaire** et **présentation**.
|
||||
|
||||
Pour l’intégration visuelle dans Glance, tu as à mon avis **deux architectures vraiment pertinentes**. La première, que je considère comme la meilleure pour ton cas, est **une page Glance + un widget `iframe` plein écran** pointant vers une petite app web Rust. Tu gagnes tout de suite la liberté de faire une vraie grille, un **volet latéral** au clic, un polling indépendant, du tri, du filtrage, voire de la navigation clavier, sans dépendre du cycle de rendu de Glance. C’est la voie la plus simple si tu veux une UX riche. citeturn12view4turn19view0
|
||||
|
||||
La seconde est une approche **plus native visuellement** : un widget **`custom-api`** qui tire un JSON de ton service Rust, rend les cases via template HTML, puis s’appuie sur **`css-class`**, **`custom-css-file`**, `/assets/` et éventuellement un script injecté dans `document.head` pour animer un panneau de détails ou appliquer un peu de comportement côté client. Cette voie demande plus de bricolage frontend, mais elle garde un rendu plus “Glance”. Elle a aussi des précédents crédibles : le dépôt communautaire propose déjà des widgets **Tailscale devices** et **NetAlertX Device Status** qui affichent justement des équipements, leur statut et leurs IP à partir d’une API externe. citeturn12view0turn23view0turn23view2turn21search0turn22search0turn22search2
|
||||
|
||||
Comme ton futur widget risque vite d’être long, ne mets pas tout en vrac dans `glance.yml`. La documentation Glance supporte les **includes YAML** avec `$include`, et le dépôt communautaire recommande justement de sortir les widgets complexes dans des fichiers dédiés. Pour ton cas, un fichier `ipam.yml` séparé est la bonne pratique. citeturn11view0turn20search0
|
||||
|
||||
Voici le squelette Glance que je recommanderais pour la solution la plus robuste, c’est-à-dire **une page dédiée + iframe** :
|
||||
|
||||
```yaml
|
||||
pages:
|
||||
- name: Réseau
|
||||
width: wide
|
||||
columns:
|
||||
- size: full
|
||||
widgets:
|
||||
- type: iframe
|
||||
title: Plan IP
|
||||
source: http://ipam-ui:8088/
|
||||
height: 1200
|
||||
```
|
||||
|
||||
Et si tu veux tenter le mode “plus natif Glance”, voici le principe hybride :
|
||||
|
||||
```yaml
|
||||
server:
|
||||
assets-path: /app/assets
|
||||
|
||||
document:
|
||||
head: |
|
||||
<script defer src="/assets/ipam.js"></script>
|
||||
|
||||
pages:
|
||||
- name: Réseau
|
||||
width: wide
|
||||
columns:
|
||||
- size: full
|
||||
widgets:
|
||||
- type: custom-api
|
||||
title: Plan IP
|
||||
cache: 15s
|
||||
css-class: ipam-grid
|
||||
url: http://ipam-api:8088/api/network/summary
|
||||
template: |
|
||||
<!-- grille + sous-sections + drawer -->
|
||||
```
|
||||
|
||||
Le second modèle demande plus de finesse, mais il reste compatible avec les mécanismes officiellement documentés par Glance. citeturn12view0turn23view0turn23view2
|
||||
|
||||
## Peut-on ajouter des widgets en Rust
|
||||
|
||||
**Oui, mais pas de la même manière selon ce que tu appelles “ajouter un widget”.** Si tu entends par là **fournir les données et/ou le rendu depuis un service externe**, alors **oui, Rust convient très bien**. C’est même une très bonne option, parce que Glance supporte officiellement un widget `custom-api` qui consomme du JSON, un widget `extension` basé sur un serveur HTTP externe, et un widget `iframe` qui peut embarquer n’importe quelle application web. Ces trois voies sont **compatibles avec un backend écrit en Rust**. citeturn12view0turn12view1turn12view4turn9view0
|
||||
|
||||
En revanche, si tu veux dire **“ajouter un widget natif à Glance lui-même, dans le code de l’application”**, alors la réponse réaliste est **non, pas en Rust dans le cadre normal du projet**. Le code source montre que les widgets natifs sont **des types Go enregistrés dans `newWidget`**, que l’application est construite comme **un binaire Go unique**, et que les templates/assets sont **embarqués par `go:embed`**. Le dépôt ne montre pas de système de plugins dynamiques ou de pont officiel vers Rust. Tu pourrais toujours bricoler une solution maison via FFI, sidecar ou fork très personnalisé, mais ce ne serait ni idiomatique pour Glance, ni dans l’esprit “peu de dépendances / binaire simple” affiché par le projet. citeturn24view0turn4view1turn9view4turn19view0
|
||||
|
||||
Ma conclusion est donc nette : **si ton vrai objectif est d’obtenir rapidement un onglet IP réseau puissant et maintenable, fais-le en Rust à l’extérieur de Glance**, puis intègre-le dans Glance via **`iframe`** si tu veux la meilleure interactivité, ou via **`custom-api`** si tu veux un rendu plus natif et que tu acceptes davantage de contraintes côté UI. **Si ton objectif est d’ajouter un widget “first-class” au codebase Glance**, alors il faut le faire en **Go**, avec les templates et assets du projet. citeturn12view4turn12view0turn9view0turn24view0turn4view1
|
||||
Reference in New Issue
Block a user