Commit Graph
5498 Commits
Author SHA1 Message Date
zacharyandClaude Opus 5 fbebe31ef3 chore(ops): fail early on a stale Node and compare migrations per dialect
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 0a94110a67 chore(ops): add LXC deploy script and fork maintenance notes
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 04069610fa refactor(widgets): let the UniFi OS chart fill the tile
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 f2572bdeef fix(integrations): end the health window on the last measured hour
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 d08bc942a2 feat(widgets): show WAN uptime in the UniFi site table
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 e505e94644 feat(integrations): fill the recent hours from the 5-minute ISP metrics
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 e32b956385 fix(widgets): make the UniFi site table sortable and fill the tile
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zachary 5e02ad4e78 Revert "fix(integrations): label sites with the console name Site Manager shows"
This reverts commit 12db2c0677.
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 8bdbc30376 fix(integrations): label sites with the console name Site Manager shows
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 44e9c22092 docs(integrations): say what a null SD-WAN status does not tell us
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 d2912c5e31 fix(integrations): treat an unstated console update state as unknown
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 e3e077fcb8 fix(widgets): hide the decorative health bar from assistive technology
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 ca298cd688 fix(widgets): report every health state in the UniFi site summary
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 ce886f7ca1 fix(widgets): omit packet loss when the cloud did not measure it
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 b39e27fd3b feat(widgets): add UniFi Site Manager overview widget
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 74ff68993e fix(widgets): remove out-of-scope UniFi Site Manager widget stub
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 9a7915d230 feat(api): expose unifiSiteManager overview query
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 4b7b7c5d1c fix(integrations): type the Site Manager transport boundary and guard it
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 c3633e7834 docs: plan — annotate test fixture arrays and require typecheck per task
Le fixture déclarait warnings: [], inféré never[], alors que le test lui
assigne un tableau de chaînes. tsc refuse ; vitest ne le voit pas.

Ajouté à la vérification de chaque tâche : pnpm turbo typecheck, pas
seulement pnpm test.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 3d31ce8eec feat(integrations): add UnifiSiteManagerIntegration
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 58e7fe730a docs: plan — the SD-WAN dash means unknown, not 'no configuration'
Constat de revue : un échec total des appels de statut produit le même
tableau vide qu'un compte sans configuration. Le rendu reste honnête —
le tiret n'affirme rien — mais le commentaire, lui, tranchait à tort.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 92b949c47f feat(integrations): add UniFi Site Manager cloud client
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 3ef0b68112 feat(integrations): add UniFi Site Manager schemas and pure mappers
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 ba3a060b06 feat(definitions): declare unifiSiteManager integration and widget kinds
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 974bf83bde docs: plan — ESM-safe guard test and the .spec.ts inclusion constraint
Scan pré-vol. Le test de garde du bundle client lisait son source via
__dirname, qui n'existe pas en ESM et n'est utilisé par aucun autre spec
du dépôt. Remplacé par import.meta.url, avec la directive d'environnement
node puisqu'il lit le disque et que le défaut est jsdom.

Ajouté aux contraintes globales : vitest n'inclut que **/*.spec.ts, donc
un fichier .spec.tsx ne serait jamais exécuté et son absence passerait
pour un succès.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 896c60df55 docs: plan — add per-site SD-WAN tunnel status
L'état des tunnels entre dans quatre des sept tâches : schémas et index
par site (tâche 2), deux méthodes de client bornées (tâche 3),
orchestration en enrichissement non fatal (tâche 4), colonne et
traductions (tâche 6), et une étape de vérification dédiée (tâche 7).

Deux points de justesse portés par des tests :
- `ping: 0` devient `pingMs: null`. Relevé sur un tunnel connecté entre
  deux sites distants, où une latence nulle est physiquement
  impossible : le champ veut dire « non mesuré ».
- le nombre de configurations interrogées est borné à cinq. Le statut
  étant indexé par configuration, un compte à dix produirait onze
  requêtes par rafraîchissement.

La topologie reste hors périmètre : une liste de quinze réseaux n'a pas
sa place dans une ligne de tableau.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 9d1465c4fc docs: spec — add per-site SD-WAN tunnel status
Ajout demandé après validation : l'état des tunnels, indexé par siteId,
se greffe sur les lignes existantes. La topologie reste hors v1 — une
liste de quinze réseaux n'a pas sa place dans une ligne de tableau.

Relevé au passage : ping vaut 0 sur un tunnel connecté entre deux sites
distants, où une latence nulle est physiquement impossible. Le champ
signifie « non mesuré » et le contrat le traduit en null, plutôt que de
propager un zéro que l'interface afficherait comme une performance.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 7b443c9da3 docs: spec — health states are a status palette, with a mandatory text summary
Le validateur de palette ne couvre que les palettes catégorielles. Sain,
dégradé et coupé sont des statuts : palette réservée et fixe, assortie de
la règle qu'ils ne voyagent jamais sans icône ni libellé.

Une icône par segment étant impossible sur vingt-quatre segments, la
bande est doublée d'un résumé textuel. Mesuré : un vert/orange/rouge
classique donne un ΔE de 2,9 entre orange et vert en vision protanope.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 64beae817d docs: spec — back to a table, health bar as a column
L'utilisateur revient sur la forme liste et retient la proposition
initiale : tableau HomarrDataTable avec la bande de santé en colonne.

La bande reste dans la v1 (D4 inchangée) — c'est la forme qui change,
pas le périmètre. Le tableau rend le tri et la visibilité responsive,
absorbe la croissance du nombre de sites, et correspond à ce que fait
l'interface Ubiquiti.

Contrepartie consignée : dans une colonne la bande est étroite, ce qui
fait de l'infobulle par segment le moyen principal de lire une tranche
plutôt qu'un confort.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 a3b7ca9a8c docs: spec — merge ISP metrics into the site health list
Proposition de l'utilisateur, retenue : fusionner les métriques ISP avec
la barre « Internet Health » pour une liste d'état rapide des sites.

Elle inverse la décision D4. La barre avait été écartée au motif que
`internetIssues` est un tableau creux dont il faudrait reconstituer les
périodes saines — vrai de cette source, mais `isp-metrics` fournit la
même information déjà dense, une entrée par site, vérifié. La barre
coûte alors une boucle de coloration, pas un moteur de graphe.

D3 change aussi : liste d'état plutôt que tableau, la bande au centre
plutôt qu'en colonne. HomarrDataTable n'a donc plus lieu d'être, et le
tri disparaît. Limite assumée et consignée : au-delà de cinq ou six
sites la forme tableau redeviendrait préférable.

Point de justesse ajouté au contrat : une tranche horaire sans mesure
vaut "unknown" et jamais "healthy". Un site enregistré depuis douze
heures n'a que douze tranches sur vingt-quatre ; les peindre en vert
affirmerait un bon fonctionnement jamais mesuré.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 1c602a305b docs: design for UniFi Site Manager integration
Nouvelle intégration cloud-only, distincte de unifiOs, remontant une vue
multi-sites. Premier des trois projets identifiés à partir des captures
du Site Manager ; il construit aussi le socle des deux suivants.

Constat notable : il n'existe pas d'API cachée. /ea/* est un alias
complet de /v1/*, et une trentaine d'autres chemins plausibles
renvoient 404. La surface documentée est toute la surface.

La séparation types / parse est imposée dès la conception par le défaut
rencontré sur l'intégration précédente, où un import serveur dans un
module réexporté côté client empêchait l'application de démarrer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 ff24a5b009 feat(widgets): 5s refresh, download first, chart from the first reading
Trois demandes utilisateur, plus une adaptation rendue nécessaire par la
première.

- Ordre inversé : descendant avant montant.
- Rafraîchissement porté de 60 s à 5 s. Il fallait le faire sur les DEUX
  couches locales : l'intervalle client seul aurait relu la même entrée
  de cache serveur douze fois. Le cache cloud reste à 5 min, ses
  métriques n'évoluant que par tranches de 5 min.
- Le graphe est rendu dès la première mesure : vide plutôt qu'absent,
  ce qui évite un saut de mise en page et n'affirme rien de faux.

Adaptation : la console ne rafraîchit ses statistiques que toutes les
30 s (mesuré — `lastHeartbeatAt` avance par pas de 30 s exacts, valeurs
identiques entre deux battements). Interroger toutes les 5 s renvoie
donc six fois la même mesure. Sans filtrage, le graphe deviendrait un
escalier et 60 points ne couvriraient plus que 5 minutes. Une lecture
identique à la précédente n'est donc pas enregistrée.

Seuil de péremption locale recalibré de 150 s à 30 s : l'ancien était
dimensionné pour 60 s + 60 s et ne signalait plus rien d'utile.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 d5ae666c41 feat(widgets): chart WAN throughput instead of static figures
L'API UniFi ne fournit que l'instantané et Homarr ne stocke aucun
historique : la série est accumulée côté client, comme le fait déjà le
widget systemResources. Elle repart donc vide à chaque rechargement.

Deux décisions d'honnêteté, dans la ligne du reste du widget :
- une lecture en échec est ignorée, jamais enregistrée comme un zéro,
  qui dessinerait une chute à la ligne de base n'ayant pas eu lieu ;
- sous deux points, on affiche les chiffres seuls : une ligne plate
  tracée sur un point unique se lit comme un réseau au repos alors
  qu'on n'a rien mesuré.

Couleurs validées par le validateur de palette (bande de luminosité,
chroma, séparation daltonisme et vision normale, contraste) en thème
clair ET sombre. La paire orange.5/yellow.5 du widget systemResources
n'a délibérément pas été reprise : son ΔE en vision normale est de
12,1, sous le plancher de lisibilité de 15.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 b29d4f5445 fix(nextjs): register the unifiOs category translation key
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 4c04f91c17 fix(integrations): keep unifi-os-types client-safe
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 d0865c3b15 fix: address final review — cloud TLS, MAC guard, honest ISP metrics
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 367eb82db5 docs: spec — make ISP metrics nullable, they are absent during a WAN outage
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 731d63c19c fix(widgets): stop asserting a gateway that does not exist, label the ISP gap
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 a81894dd8a feat(widgets): add UniFi OS widget
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 99282742c6 fix(widgets): remove out-of-scope UniFi OS widget stub from task 6
The widget stub (packages/widgets/src/unifi-os/) was incorrectly added in
Task 6 and belongs to Task 7. Its removal confirms the type system does not
require it, and prevents inconsistencies: the stub declares IconWifi while
Task 7 specifies IconRouter, and it is not registered in manifest.ts,
catalog.ts or refetch-intervals.ts.

- Delete packages/widgets/src/unifi-os/ directory entirely
- Revert packages/widgets/src/index.tsx to commit 57286d83d

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 31781d25ad feat(api): expose unifiOs widget summary query through tRPC router and request handler
Create unifiOs request handler with 60s cache and stale-on-error fallback, and tRPC router exposing clientApi.widget.unifiOs.summary query. Include minimal widget stub for type system.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 c9b3611b77 fix(integrations): make client/device counts nullable and test real degradation
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zachary d156d70b31 docs: spec — drop unused VPN servers call, make client/device counts nullable 2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 67c3cba167 feat(integrations): add UnifiOsIntegration with optional cloud enrichment
Introduces UnifiOsIntegration, orchestrating UnifiOsApi (local) and
UnifiSmApi (optional cloud) to build the UnifiOsSummary. Local endpoint
calls degrade independently via Promise.allSettled; the cloud client is
never instantiated without a configured siteManagerApiKey secret.
Registers the unifiOs kind in the integration creator, index, and types
barrels, resolving the previously-failing typecheck.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 67ec3cab3d fix(integrations): select newest ISP metric period explicitly
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 eed8c5fe91 fix(integrations): type unifi-os test fetch mocks for tsc
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 3ce3eefa5e feat(integrations): add optional UniFi Site Manager client with 5min cache
Task 4 of the UniFi OS integration: an optional cloud client for
Ubiquiti's Site Manager API that enriches the local console with ISP
latency data. Instantiated only when a cloud API key is configured
(Task 5). Every failure path (bad key, non-ok status, parse error,
unmatched gateway MAC) returns null rather than throwing, since the
cloud is a pure enrichment. Metrics are cached 5 minutes, keyed by
gateway MAC, to respect the ~27 min publication lag and avoid rate
limiting; the query window is 3 hours because a 15-minute window
returns an empty array on the real API.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 6f9a30799e feat(integrations): add UniFi OS local API client
Implements HTTP client for UniFi OS console integration API with support for
fetching site information, client counts, device statistics, and VPN server
configs. Includes pagination handling with filter-based fallback when client
count exceeds API limit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zacharyandClaude Opus 5 d33f2155a7 fix(integrations): make unifi-os test invocation-independent with vi.hoisted()
Guarded environment validation with SKIP_ENV_VALIDATION and SECRET_ENCRYPTION_KEY
set in vi.hoisted() so that tests pass both with 'pnpm test' (NODE_ENV=development)
and bare 'pnpm vitest run' (no env override).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:21:43 +02:00
zachary 957211164f docs: fix test command in plan (pnpm test, not pnpm vitest run) 2026-09-13 22:21:43 +02:00