--- title: Notification Hub url: https://blog.guigpap.com/fr/workflows/notification-hub/ url_md: https://blog.guigpap.com/fr/workflows/notification-hub.md category: automation date: '2026-01-20' maturite: production techno: - n8n - telegram application: - monitoring - operations --- # Notification Hub > Workflow central de routage intelligent des notifications avec déduplication, quiet hours et sub-workflow callback ## 1. Quoi ? — Définition et contexte Le **Notification Hub** est le workflow N8N qui centralise toutes les notifications de l'infrastructure. Il dédoublonne les alertes, applique les quiet hours, route vers les bons canaux, et délègue les callbacks à un sub-workflow dédié extrait depuis le refactoring #276. > **Note - Hub central** > > Sans hub central, chaque service envoie ses propres notifications. Résultat : spam, doublons, alertes à 3h du matin pour des warnings non critiques. Le Notification Hub résout ces problèmes en étant l'unique point de sortie pour toutes les alertes. ### Composants | Workflow | Nodes | Rôle | |----------|-------|------| | **Notification Hub** (parent) | 41 | Dedup + quiet hours + format + send multi-canal | | **NH Callback Handler** (`9Xb3mplmzE5994bi`) | 18 | Ack, approve, retry, ignore, delivery callbacks | ### Sources de notifications | Source | Type d'événements | |--------|-------------------| | **Docker DIUN** | Nouvelles versions d'images, demandes d'approbation | | **Alertmanager** | Alertes Prometheus (CPU, RAM, disk) | | **Health Check** | Status des services | | **Global Error Handler** | Erreurs N8N classifiées avec actions [Retry] [Details] [Ignore] [Fix] | | **Security CVE Watch** | Alertes CRITICAL + digest hebdomadaire | | **Workflows N8N** | Résultats d'exécution, événements business | ### Canaux de sortie | Canal | Usage | |-------|-------| | **Telegram** | Principal, notifications interactives avec boutons | | **ntfy** | Backup push mobile (canal CRITICAL) | | **Discord** | Optionnel, pour équipes | ### Architecture visuelle ```mermaid flowchart TD subgraph Sources["Sources"] direction TB DIUN["Docker DIUN"] AM["Alertmanager"] HC["Health Check"] GEH["Global Error Handler"] CVE["Security CVE Watch"] WFs["Workflows N8N divers"] end subgraph Hub["Notification Hub · 41 nodes"] direction TB Receive["Webhook / Execute Workflow"] IsCB["Is Callback?"] Validate["Validate Payload"] Dedup["Deduplication · Redis"] Quiet["Quiet Hours"] Format["Format per Channel"] Send["Send Multi-canal"] History["Insert History DT"] end subgraph CB["NH Callback Handler · 18 nodes"] direction TB Parse["Parse Callback"] AckBtn["Ack"] ApproveBtn["Approve"] RetryBtn["Retry"] IgnoreBtn["Ignore"] end subgraph Out["Canaux"] direction TB TG["Telegram · boutons inline"] NTFY["ntfy · push mobile"] Discord["Discord"] end Sources --> Receive --> IsCB IsCB -->|Yes| CB IsCB -->|No| Validate --> Dedup --> Quiet --> Format --> Send Send --> TG Send --> NTFY Send --> Discord Send --> History ``` --- ## 2. Pourquoi ? — Enjeux et motivations ### Problèmes résolus | Problème | Sans hub | Avec hub | |----------|----------|----------| | **Spam de notifications** | Chaque service notifie indépendamment | Point unique de sortie contrôlé | | **Doublons** | Même alerte envoyée plusieurs fois | Déduplication par clé + TTL | | **Alertes nocturnes** | Réveillé pour un warning | Quiet hours pour non-critiques | | **Pas d'audit** | Notifications perdues | Historique complet en base | | **Boutons cassés** | Logique callback dispersée | Sub-workflow dédié réutilisable | ### Pourquoi extraire un Callback Handler (#276) ? Le hub initial mélangeait l'ingestion de notifications et la gestion des callbacks Telegram dans un seul workflow de 57 nodes. Trois symptômes ont déclenché l'extraction : | Symptôme | Cause | |----------|-------| | **Double-trigger ambigu** | Webhook entrant pouvait être une nouvelle notif OU un callback | | **Dedup bypass mal géré** | Les callbacks ne devaient pas passer par dedup mais le code le faisait parfois | | **Tests difficiles** | Tester un callback impliquait d'envoyer une vraie notif d'abord | L'extraction en sub-workflow `NH Callback Handler` (18 nodes, ID `9Xb3mplmzE5994bi`) résout ces tensions : le parent route en début de chaîne via `Is Callback?`, et les callbacks bypassent dedup + quiet hours. ### Fonctionnalités clés | Fonction | Bénéfice | |----------|----------| | **Déduplication** | Évite les notifications en double sur une fenêtre de temps | | **Quiet Hours** | Notifications non-critiques différées 22h-8h | | **Multi-canal** | Telegram + ntfy + Discord selon la sévérité | | **Historique** | Toutes les notifications stockées pour audit | | **Callbacks routés** | Bypass dedup pour boutons Telegram, sub-workflow extraite | --- ## 3. Comment ? — Mise en œuvre technique ### Le parcours d'une notification **1. Réception** — Le hub reçoit via webhook `/webhook/notification-hub` (interne uniquement) ou via Execute Workflow. **2. `Is Callback?`** — Détection précoce : si le payload contient un `callback_data` Telegram, route vers NH Callback Handler. Bypasse dedup + quiet hours. **3. Validation** — Type requis, severity valide (`info` / `warning` / `critical`), format JSON conforme. **4. Déduplication** — Calcul d'une clé `::`, lookup Redis, skip si présent. **5. Quiet Hours** — Si l'heure est entre 22h et 8h ET la severity n'est pas critical, marquer `deferred=true` et différer. **6. Format** — Template par type et canal (Telegram HTML, Discord embed, ntfy texte). **7. Send multi-canal** — Telegram toujours, ntfy si `critical`, Discord si configuré. **8. Historisation** — Insert dans `notification_history` avec dedup_key, channels, timestamp. ### Déduplication ```javascript // Clé de déduplication const dedupeKey = `${type}:${container}:${severity}`; // TTL variable selon la sévérité const ttl = { info: 3600, // 1 heure warning: 1800, // 30 min critical: 300 // 5 min }; const existing = await redis.get(dedupeKey); if (existing) { return { skipped: true, reason: 'duplicate' }; } await redis.set(dedupeKey, Date.now(), { EX: ttl[severity] }); ``` ### Quiet Hours | Heure | Info | Warning | Critical | |-------|------|---------|----------| | 08h-22h | Immédiat | Immédiat | Immédiat | | 22h-08h | Queue | Queue | Immédiat | Les notifications différées sont stockées dans une Data Table `pending_notifications` et envoyées au prochain passage à 8h via le workflow `Deferred Sender`. ### Bypass callbacks Les callbacks Telegram (boutons inline) reviennent au hub via le même webhook. Le node `Is Callback?` (IF en début de chaîne) les détecte par la présence de `source='telegram_callback'` ou de `callback_query` dans le payload : ```mermaid flowchart TD Receive["Webhook"] IsCB{"source = telegram_callback ?"} Direct["NH Callback Handler · 18n"] Normal["Validate → Dedup → Quiet → Format → Send"] Receive --> IsCB IsCB -->|Yes| Direct IsCB -->|No| Normal ``` Sans ce bypass, un clic sur `[Retry]` à 23h serait dédupliqué avec la notification originale ou différé jusqu'au matin — ce qui casserait l'UX. ### Format d'entrée ```json { "type": "update_success", "severity": "info", "title": "Docker Update réussi", "message": "n8n-stack mis à jour vers 1.73.0", "container": "n8n", "image": "n8nio/n8n:1.73.0", "project": "n8n-stack", "timestamp": "2026-01-20T10:30:00.000Z", "callback_actions": [ { "label": "Détails", "callback": "notif_details_" }, { "label": "Ignorer", "callback": "notif_ignore_" } ], "metadata": { "duration_seconds": 45, "previous_version": "1.72.0" } } ``` ### Types supportés | Type | Sévérité par défaut | Origine | |------|---------------------|---------| | `update_success` / `update_failed` / `update_queued` | info / critical / info | Docker Updates | | `approval_request` | warning | Docker Updates, GEH Fix Applier | | `alert_prometheus` | variable | Alertmanager | | `container_down` | critical | Health Check | | `error_classified` | variable | Global Error Handler | | `cve_alert` | critical | Security CVE Watch | ### Appel du hub **Depuis un workflow N8N (Execute Workflow) :** ```javascript { "workflowId": { "__rl": true, "value": "", "mode": "id" }, "inputData": { "type": "update_success", "severity": "info", "title": "...", "message": "..." } } ``` **Via webhook interne :** ```bash curl -X POST http://n8n:5678/webhook/notification-hub \ -H "Content-Type: application/json" \ -d '{"type":"custom","severity":"info","title":"Test","message":"Hello"}' ``` > **Caution - Webhook interne uniquement** > > Le webhook `/webhook/notification-hub` est bloqué par Caddy depuis l'extérieur. Il n'est accessible que depuis le réseau Docker `webproxy` interne. Toute exposition publique introduirait un vecteur d'abus. ### Historisation Table `notification_history` (PostgreSQL N8N) : | Colonne | Type | Description | |---------|------|-------------| | `id` | UUID | Identifiant unique | | `type` | Text | Type de notification | | `severity` | Text | info / warning / critical | | `payload` | JSONB | Contenu complet | | `channels` | Array | Canaux utilisés | | `sent_at` | Timestamp | Date d'envoi effective | | `deferred` | Boolean | Si différé pendant quiet hours | | `dedupe_key` | Text | Clé de déduplication | | `callback_results` | JSONB | Réponses aux boutons (cumulatif) | --- ## 4. Et si ? — Perspectives et limites ### Limites actuelles | Limite | Impact | Mitigation | |--------|--------|------------| | **Redis single-instance** | Perte du cache si Redis down | Notification envoyée (pas de dédupe) — fail-open | | **Pas d'escalade automatique** | Alertes non acquittées restent warning | Prévu mais non implémenté | | **Quiet hours fixes** | Pas de configuration par utilisateur | Suffisant pour usage solo | | **Callback timeout** | Si l'utilisateur ne clique jamais, le state reste pending | TTL 7j sur les callbacks dans la DT | ### Scénarios d'évolution **Si besoin d'escalade automatique** : - Tracker les alertes non acquittées dans `pending_notifications` - Après délai configurable, passer en `critical` → ntfy + appel Twilio optionnel - Dashboard Grafana avec heatmap des alertes ignorées **Si volume de notifications augmente** : - Agrégation par batch (résumé toutes les heures pour les `info`) - Digest quotidien pour les notifications non urgentes - Filtrage par abonnement (opt-in par type via Data Table) **Si équipe multi-utilisateurs** : - Configuration quiet hours par utilisateur (Data Table `notification_routing`) - Routage selon l'utilisateur de garde - Acquittements partagés (un acquit suffit) --- ## Pages liées ### Infrastructure - [N8N en mode Queue](/fr/infrastructure/n8n-queue-mode/) — Backend automation - [Monitoring Stack](/fr/infrastructure/monitoring-stack/) — Source Prometheus - [Notify Stack](/fr/infrastructure/notify-stack/) — DIUN et ntfy ### Workflows - [Telegram Orchestrator](/fr/workflows/telegram-orchestrator/) — Réception des callbacks - [Docker Updates](/fr/workflows/docker-updates/) — Source DIUN + approval - [Error Handler](/fr/workflows/error-handler/) — Notifie via Hub avec boutons d'action ### Référence - [Glossaire](/fr/reference/glossary/) — Webhook, Deduplication, Quiet Hours ## Metadonnees agent - Cet article est issu du blog GuiGPaP Lab. - Contexte global du blog: https://blog.guigpap.com/llms.txt - Contact auteur: https://odoo.guigpap.com/mon-cv - Licence: CC-BY-SA 4.0