From 3f48912ad7a77f38714d8ec4c4920a4d66dc16cf Mon Sep 17 00:00:00 2001 From: bruno Date: Sat, 1 Aug 2026 10:56:32 -0400 Subject: [PATCH] docs: initial architecture, roadmap, readme for ntfy-bridge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - README.md: project overview, quickstart, sources summary - ARCHITECTURE.md: complete architecture, data flow, project structure - ROADMAP.md: 5-phase development plan (v0.1.0 → v0.5.0+) - CONTRIBUTING.md: dev setup, conventions, how to add a source - ntfy-bridge.example.yaml: fully documented configuration template - scripts/example-cron-disk.sh: example cron script → ntfy-bridge --- .gitignore | 28 ++++ ARCHITECTURE.md | 269 +++++++++++++++++++++++++++++++++++ CONTRIBUTING.md | 90 ++++++++++++ README.md | 76 ++++++++++ ROADMAP.md | 95 +++++++++++++ ntfy-bridge.example.yaml | 128 +++++++++++++++++ scripts/example-cron-disk.sh | 25 ++++ 7 files changed, 711 insertions(+) create mode 100644 .gitignore create mode 100644 ARCHITECTURE.md create mode 100644 CONTRIBUTING.md create mode 100644 README.md create mode 100644 ROADMAP.md create mode 100644 ntfy-bridge.example.yaml create mode 100755 scripts/example-cron-disk.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9a78dde --- /dev/null +++ b/.gitignore @@ -0,0 +1,28 @@ +# ntfy-bridge + +# Binary +ntfy-bridge +ntfy-bridge.exe +ntfy-bridge-mac + +# V build artifacts +.vmodules/ +*.o +*.obj +*.exe +*.so +*.dylib + +# Config (contains secrets — use example as template) +ntfy-bridge.yaml +*.local.yaml + +# IDE +.vscode/ +.idea/ +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 0000000..07a238a --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,269 @@ +# ntfy-bridge — Architecture + +## Philosophie + +ntfy-bridge est un **daemon HTTP léger** écrit en V (~500 lignes) qui compile en un seul binaire natif statique. Il écoute des webhooks, se connecte au socket Docker, et poll des endpoints HTTP — puis transforme chaque événement en notification Ntfy formatée. + +**Principes :** +- **Un binaire, zéro runtime** — pas de Node, Python, JVM ou conteneur obligatoire +- **Config-driven** — tout passe par le fichier YAML, pas de recompilation +- **Fail-safe** — un échec réseau vers Ntfy ne crashe pas le daemon +- **Silencieux par défaut** — ne spamme pas, utilise les bons niveaux de priorité Ntfy (1-5) +- **Extensible** — ajouter une source = implémenter un module dans `sources/` + +## Architecture globale + +``` +┌─────────────────────────────────────────────────────────────┐ +│ ntfy-bridge │ +│ │ +│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────────┐ │ +│ │ HTTP │ │ Docker │ │ HTTP │ │ Cron │ │ +│ │ Server │ │ Watcher │ │ Poller │ │ Receiver │ │ +│ │ :9090 │ │ goroutine│ │ goroutine│ │ /webhooks │ │ +│ └────┬─────┘ └────┬─────┘ └────┬─────┘ └─────┬──────┘ │ +│ │ │ │ │ │ +│ └──────────────┼──────────────┼──────────────┘ │ +│ ▼ ▼ │ +│ ┌──────────────────────────┐ │ +│ │ Event Pipeline │ │ +│ │ ┌────────┐ ┌────────┐ │ │ +│ │ │Parse & │ │Dedup & │ │ │ +│ │ │Format │──│RateLim │ │ │ +│ │ └────────┘ └────────┘ │ │ +│ └────────────┬─────────────┘ │ +│ ▼ │ +│ ┌──────────────────────────┐ │ +│ │ Ntfy Publisher │ │ +│ │ POST / │ │ +│ └──────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +## Flux de données + +### 1. Webhook entrant (Gitea, Uptime Kuma, generic) +``` +POST /webhooks/gitea-flowdeck + │ + ▼ +1. Verify signature (optionnel) +2. Parse JSON → Event struct +3. Route vers le transformer approprié +4. Appliquer priority_map +5. Appliquer template de message +6. POST vers Ntfy +``` + +### 2. Docker watcher (socket) +``` +goroutine DockerWatcher + │ + ▼ +1. Connexion au socket Docker (/var/run/docker.sock) +2. Stream events via GET /events?filters=... +3. Filtrer par événements configurés (die, oom, health_status) +4. Extraire container_name, image, exit_code +5. Appliquer template +6. POST vers Ntfy +``` + +### 3. HTTP Poller +``` +goroutine HttpPoller (toutes les N secondes) + │ + ▼ +1. GET avec timeout configuré +2. Si status ≠ expected → Event(priority=5) +3. Si OK et précédemment DOWN → Event(priority=1, tags="white_check_mark") +4. Sinon → rien (pas de notification si tout va bien) +``` + +### 4. Cron receiver +``` +POST /webhooks/cron-disk + │ + ▼ +1. Body = texte brut du script cron +2. Pass-through vers Ntfy +3. Le script cron contrôle son propre format +``` + +## Modèle de données + +```v +struct Config { + server ServerConfig + defaults DefaultConfig + sources SourcesConfig +} + +struct ServerConfig { + url string // URL du serveur Ntfy + auth_token string // Token d'auth (optionnel) + listen string // Adresse d'écoute HTTP (:9090) +} + +struct DefaultConfig { + priority int // 1-5 + tags []string // ["loudspeaker"] +} + +struct Source { + name string + webhook_path string + topic string + template string // Go-like template + priority_map map[string]int + tags []string +} + +struct Event { + source string // "gitea", "docker", "uptime_kuma", "http_poll", "cron" + name string // nom de la source configurée + topic string // topic Ntfy cible + priority int // 1-5 + tags []string + message string // message formaté final + raw string // payload brut (pour debug) +} +``` + +## Pipeline de transformation + +Chaque source implémente l'interface : + +```v +interface Transformer { + transform(raw string, source &Source) ?Event +} +``` + +### Transformers inclus + +| Transformer | Entrée | Sortie | +|---|---|---| +| `gitea_transformer` | JSON webhook Gitea | `🔨 [FlowDeck] bruno pushed to main: "Fix bug" (a3f2c1d)` | +| `uptime_kuma_transformer` | JSON webhook Kuma | `🚨 Gitea is DOWN — 503 — since 14:32` | +| `docker_transformer` | Docker event JSON | `🐳 flowdeck exited OOMKilled on docker-prod-1` | +| `http_poll_transformer` | HTTP response | `❌ obsigate.dracodev.net/health → timeout 30s` | +| `generic_transformer` | Raw body | Pass-through, pas de transformation | + +### Priorités Ntfy + +| Niveau | Usage | Exemple | +|---|---|---| +| 5 (urgent) | Service critique down, container OOM | Uptime Kuma DOWN | +| 4 (high) | PR ouverte, container crash | Gitea pull_request, Docker die | +| 3 (default) | Push, issue, activité normale | Gitea push | +| 2 (low) | Info, succès | Service back UP | +| 1 (min) | Debug, heartbeat | Health check OK (si configuré) | + +## Déduplication et rate limiting + +- **Déduplication** : même source + même type d'événement + même cible → cooldown 5 minutes +- **Rate limiting** : max 10 notifs/minute/topic (configurable) +- **Uptime Kuma spécifique** : les "down" répétés dans un intervalle de 2 min sont dédupliqués + +## Structure du projet + +``` +ntfy-bridge/ +├── README.md # Vue d'ensemble, quickstart +├── ARCHITECTURE.md # Ce document +├── ROADMAP.md # Phases de développement +├── CONTRIBUTING.md # Guide de contribution +├── ntfy-bridge.example.yaml # Exemple de configuration complet +├── src/ +│ ├── main.v # Entrypoint, parsing CLI +│ ├── config.v # Chargement + validation YAML +│ ├── server.v # HTTP server + routing webhooks +│ ├── event.v # Types Event, pipeline +│ ├── ntfy.v # Client HTTP Ntfy (POST) +│ ├── dedup.v # Déduplication + rate limiting +│ ├── template.v # Mini moteur de template +│ ├── sources/ +│ │ ├── gitea.v # Transformer Gitea +│ │ ├── uptime_kuma.v # Transformer Uptime Kuma +│ │ ├── docker.v # Watcher Docker socket +│ │ ├── http_poll.v # Poller HTTP périodique +│ │ └── generic.v # Webhook passe-partout +│ └── log.v # Logging structuré +├── tests/ +│ ├── config_test.v # Tests validation config +│ ├── gitea_test.v # Tests transformer Gitea +│ ├── uptime_kuma_test.v # Tests transformer Kuma +│ └── dedup_test.v # Tests déduplication +├── scripts/ +│ └── example-cron-disk.sh # Exemple script cron → Ntfy +└── v.mod # Dépendances V +``` + +## Dépendances V (v.mod) + +```v +Module { + name: 'ntfy-bridge' + description: 'Homelab notification hub — aggregates Gitea, Docker, Uptime Kuma → Ntfy' + version: '0.1.0' + deps: [ + 'ui-driver.cua' + ] +} +``` + +Dépendances externes minimales : +- `vsl` — parsing YAML (sinon implémenter un parseur minimal) +- `x.net` — client HTTP (ou lib standard V `net`) +- `json` — stdlib V pour le parsing JSON + +En réalité, V a tout dans sa stdlib : `net.http`, `json`, `time`, `os`, `flag`. Le projet vise **zéro dépendance externe**. + +## Déploiement + +### Option A : Binaire nu + systemd +```ini +# /etc/systemd/system/ntfy-bridge.service +[Unit] +Description=ntfy-bridge notification hub +After=network.target + +[Service] +ExecStart=/usr/local/bin/ntfy-bridge --config /etc/ntfy-bridge.yaml +Restart=always +RestartSec=10 + +[Install] +WantedBy=multi-user.target +``` + +### Option B : Conteneur Docker +```dockerfile +FROM alpine:latest +COPY ntfy-bridge /usr/local/bin/ +COPY ntfy-bridge.yaml /etc/ +EXPOSE 9090 +CMD ["/usr/local/bin/ntfy-bridge", "--config", "/etc/ntfy-bridge.yaml"] +``` + +### Option C : Compilation croisée +```bash +v -os windows . -o ntfy-bridge.exe # Pour Windows +v -os linux . -o ntfy-bridge # Pour Linux +v -os macos . -o ntfy-bridge-mac # Pour macOS +``` + +## Sécurité + +- **Signature webhooks** : vérification HMAC-SHA256 optionnelle pour Gitea +- **Auth token** : token optionnel pour le serveur Ntfy (si auth activée) +- **Pas d'exposition externe** : le serveur HTTP écoute sur localhost par défaut (:9090) +- **Rate limiting interne** : max 10 notifs/minute/source pour éviter le spam +- **Input validation** : tout JSON entrant est validé avant processing +- **No secrets in config** : le token Ntfy peut être passé via variable d'environnement `NTFY_TOKEN` + +## Métriques et observabilité + +- **Health endpoint** : `GET /health` → 200 OK + uptime + compteur de notifs +- **Stats endpoint** : `GET /stats` → compteurs par source, erreurs, latence Ntfy +- **Logging** : stdout en JSON structuré (timestamp, level, source, event, error) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..a0feabe --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,90 @@ +# Contributing to ntfy-bridge + +## Stack + +- **Langage** : [V](https://vlang.io/) — compilé, rapide, syntaxe proche de Go +- **Dépendances** : Zéro dépendance externe (stdlib V uniquement) +- **Build** : `v .` → binaire natif statique + +## Setup développement + +```bash +# Installer V +curl -s https://github.com/vlang/v/releases/latest/download/v_linux.zip -o /tmp/v.zip +unzip /tmp/v.zip -d /tmp && sudo /tmp/v/v symlink + +# Cloner +git clone https://git.dracodev.net/bruno/ntfy-bridge.git +cd ntfy-bridge + +# Compiler (mode dev avec hot reload) +v watch run . + +# Tests +v test . + +# Build production +v -prod . +``` + +## Structure du code + +``` +src/ +├── main.v # Entrypoint, flag parsing, orchestration +├── config.v # Config loading + validation +├── server.v # HTTP server, webhook routing +├── event.v # Event types, pipeline +├── ntfy.v # Ntfy HTTP client +├── dedup.v # Deduplication + rate limiting +├── template.v # Mini template engine +├── log.v # Structured logging +└── sources/ # Source-specific transformers + ├── gitea.v + ├── uptime_kuma.v + ├── docker.v + ├── http_poll.v + └── generic.v +``` + +## Ajouter une nouvelle source + +1. Créer `src/sources/ma_source.v` +2. Implémenter la fonction `transform(raw string, source &Source) ?Event` +3. Déclarer le type dans `sources` du `config.v` +4. Router l'endpoint dans `server.v` +5. Ajouter un test dans `tests/ma_source_test.v` + +## Conventions de code + +- **Nommage** : snake_case pour les fonctions/variables, PascalCase pour les structs +- **Gestion d'erreurs** : utiliser le type `?` (Option/Result) de V +- **Logging** : `log.info()`, `log.warn()`, `log.error()` avec contexte +- **Messages de commit** : [Conventional Commits](https://www.conventionalcommits.org/) + - `feat: add Gitea webhook transformer` + - `fix: handle Docker socket disconnect` + - `docs: update ARCHITECTURE.md` + +## Tests + +```bash +# Tous les tests +v test . + +# Test spécifique +v test tests/gitea_test.v + +# Avec verbose +v test . -stats +``` + +## CI/CD + +Le repo utilise Gitea Actions. Le workflow dans `.gitea/workflows/ci.yml` : +- Compile en mode `-prod` +- Lance `v test .` +- Vérifie le formatting (`v fmt -verify`) + +## Questions ? + +Ouvrir une issue sur https://git.dracodev.net/bruno/ntfy-bridge/issues diff --git a/README.md b/README.md new file mode 100644 index 0000000..1c942be --- /dev/null +++ b/README.md @@ -0,0 +1,76 @@ +# ntfy-bridge + +Hub central de notifications pour homelab — agrège des sources multiples (Gitea, Docker, Uptime Kuma, health checks HTTP, scripts cron) et les transforme en notifications [Ntfy](https://ntfy.sh/) intelligentes, formatées, avec priorités et contexte. + +``` +┌─────────────────┐ +│ Gitea webhook │──┐ +├─────────────────┤ │ +│ Uptime Kuma │──┤ +├─────────────────┤ │ ┌──────────────┐ ┌───────────┐ ┌──────────────┐ +│ Docker events │──┤────→│ ntfy-bridge │─────→│ Ntfy Srv │─────→│ Ton phone │ +├─────────────────┤ │ └──────────────┘ └───────────┘ └──────────────┘ +│ HTTP health │──┤ +├─────────────────┤ │ +│ Scripts cron │──┘ +└─────────────────┘ +``` + +## Pourquoi ntfy-bridge ? + +Dans un homelab avec 8+ hôtes Docker, Gitea, Uptime Kuma et des dizaines de services, les alertes arrivent de partout. `ntfy-bridge` centralise tout dans une seule file de notifications intelligentes — avec le bon niveau de priorité, le bon format, et le bon topic Ntfy. + +| Sans ntfy-bridge | Avec ntfy-bridge | +|---|---| +| Webhook Gitea → email (noyé) | Push = notif Ntfy formatée avec auteur, commit, message | +| Uptime Kuma → alerte brute | "🚨 Gitea DOWN — 503 — since 14:32" avec priority=5 | +| Container qui crashe → logs Docker | "🐳 flowdeck exited OOMKilled on docker-prod-1" | +| Script cron → email perdu | Résumé quotidien disque à 9h dans ta poche | + +## Quick Start + +```bash +# Installer V (si pas déjà fait) +curl -s https://github.com/vlang/v/releases/latest/download/v_linux.zip -o /tmp/v.zip +unzip /tmp/v.zip -d /tmp && sudo /tmp/v/v symlink + +# Compiler +git clone https://git.dracodev.net/bruno/ntfy-bridge.git +cd ntfy-bridge +v . + +# Configurer +cp ntfy-bridge.example.yaml ntfy-bridge.yaml +vim ntfy-bridge.yaml + +# Lancer +./ntfy-bridge --config ntfy-bridge.yaml +``` + +## Sources supportées + +| Source | Type | Description | +|--------|------|-------------| +| **Gitea** | Webhook | Push, PR, issues, releases → notifs formatées | +| **Uptime Kuma** | Webhook | Statut up/down avec priorité critique | +| **Docker** | Socket | Container start/die/oom/health_status | +| **HTTP Poll** | Polling | Health checks périodiques | +| **Cron** | Webhook | Reçoit des notifs depuis des scripts shell | +| **Generic** | Webhook | Endpoint passe-partout pour tout script custom | + +## Exemple de config minimale + +```yaml +server: + url: https://ntfy.dracodev.net + listen: ":9090" + +sources: + gitea: + - webhook_path: /webhooks/gitea + topic: dev-notifs +``` + +## Licence + +MIT diff --git a/ROADMAP.md b/ROADMAP.md new file mode 100644 index 0000000..b7d1903 --- /dev/null +++ b/ROADMAP.md @@ -0,0 +1,95 @@ +# ntfy-bridge — Roadmap + +## Phase 1 : MVP (core engine) — v0.1.0 + +Objectif : daemon qui compile, lit une config, écoute des webhooks et envoie vers Ntfy. + +- [ ] `main.v` — CLI args (`--config`, `--version`, `--validate`) +- [ ] `config.v` — Chargement + validation du YAML +- [ ] `server.v` — HTTP server sur `:9090` avec routing basique +- [ ] `event.v` — Types de données : `Event`, `Config`, `Source` +- [ ] `ntfy.v` — Client HTTP POST vers Ntfy (topic, message, priority, tags) +- [ ] `sources/generic.v` — Webhook passe-partout (POST raw → Ntfy) +- [ ] `log.v` — Logging structuré (timestamp, level, message) +- [ ] `ntfy-bridge.example.yaml` — Config d'exemple complète et commentée + +**Sortie** : `ntfy-bridge` compile, accepte un webhook générique et l'envoie vers Ntfy. + +## Phase 2 : Sources spécialisées — v0.2.0 + +- [ ] `sources/gitea.v` — Transformer Gitea (push, PR, issues, releases) + - Parse le JSON webhook Gitea + - Format : `🔨 [repo] auteur action: "message" (sha)` + - Priority map par type d'événement +- [ ] `sources/uptime_kuma.v` — Transformer Uptime Kuma + - Parse le JSON webhook Kuma (heartbeat) + - Format : `🚨 monitor is DOWN — status — since time` + - Priority 5 pour DOWN, 1 pour UP +- [ ] `sources/docker.v` — Watcher socket Docker + - Connexion au socket Docker (local + distant TCP) + - Filtrage par événements (die, health_status, oom) + - Template de message configurable + - Multi-hôtes (plusieurs sockets dans la config) +- [ ] `sources/http_poll.v` — Poller HTTP + - Goroutine de polling périodique (intervalle configurable) + - Vérification status code + timeout + - State machine up/down avec notification uniquement au changement +- [ ] `dedup.v` — Déduplication basique + - Cache LRU avec TTL (5 min par défaut) + - Clé de déduplication : source + type + cible + +**Sortie** : Toutes les sources majeures sont intégrées et fonctionnelles. + +## Phase 3 : Robustesse — v0.3.0 + +- [ ] `dedup.v` amélioré — Rate limiting global par topic +- [ ] Signature webhooks — Vérification HMAC-SHA256 optionnelle (Gitea) +- [ ] Retry logic — Retry avec backoff exponentiel vers Ntfy (3 tentatives) +- [ ] Graceful shutdown — SIGTERM → drain des événements en cours +- [ ] Health endpoint — `GET /health` → 200 + uptime + stats basiques +- [ ] Stats endpoint — `GET /stats` → compteurs par source +- [ ] Config reload — `SIGHUP` → reload config sans redémarrage +- [ ] Tests unitaires — `config_test.v`, `gitea_test.v`, `dedup_test.v` + +**Sortie** : Le daemon est prêt pour la production homelab. + +## Phase 4 : Qualité de vie — v0.4.0 + +- [ ] Template engine avancé — Support des variables dans les messages + ``` + "🐳 {container_name} → {status} (exit: {exit_code}) on {host}" + ``` +- [ ] Silence rules — `POST /api/silence?duration=30m` mute temporaire +- [ ] Grouping — Regrouper N notifications similaires en une seule +- [ ] Webhook secret validation — HMAC pour tous les webhooks +- [ ] Docker Compose example — `docker-compose.yml` prêt à l'emploi +- [ ] systemd unit file — `ntfy-bridge.service` +- [ ] CI/CD via Gitea Actions — build + test automatique + +**Sortie** : Expérience utilisateur complète et agréable. + +## Phase 5 : Futures idées — v0.5.0+ + +- [ ] Web UI minimale — Page de statut des sources + historique récent +- [ ] Filtres avancés — Expressions conditionnelles par source + ```yaml + filters: + - match: { container_name: "test-*" } + action: drop + ``` +- [ ] Notifications structurées — Support des actions Ntfy (boutons click) +- [ ] Intégration Prometheus — Métriques exposées au format OpenMetrics +- [ ] Webhook sortant — Forwarder les événements vers un autre webhook (Slack, Discord) +- [ ] Support multi-utilisateurs — ACLs par webhook_path +- [ ] Fichier d'état — Persistance de l'état up/down des health checks +- [ ] Plugin system — Sources customisables via dll/.so (futur lointain) + +## Suivi des versions + +| Version | Date cible | Contenu | +|---------|-----------|---------| +| v0.1.0 | — | Moteur core + webhook générique | +| v0.2.0 | — | Sources Gitea, Uptime Kuma, Docker, HTTP poll | +| v0.3.0 | — | Robustesse, tests, health check, rate limiting | +| v0.4.0 | — | Templates, silence rules, grouping, CI/CD | +| v0.5.0+ | — | Web UI, Prometheus, plugins | diff --git a/ntfy-bridge.example.yaml b/ntfy-bridge.example.yaml new file mode 100644 index 0000000..a00bcfa --- /dev/null +++ b/ntfy-bridge.example.yaml @@ -0,0 +1,128 @@ +# ntfy-bridge configuration +# ============================ + +# ── Server ──────────────────────────────────────────── +server: + # URL de ton serveur Ntfy + url: https://ntfy.dracodev.net + + # Token d'authentification (optionnel, si ton serveur Ntfy a auth activée) + # Peut aussi être passé via variable d'environnement NTFY_TOKEN + # auth_token: tk_xxxxxxxxxxxx + + # Adresse d'écoute du serveur HTTP interne + # Mettre "0.0.0.0:9090" pour exposer réseau, "127.0.0.1:9090" pour localhost + listen: "127.0.0.1:9090" + +# ── Defaults ────────────────────────────────────────── +# Appliqués à toutes les sources sauf override explicite +defaults: + priority: 3 # 1=min, 2=low, 3=default, 4=high, 5=urgent + tags: ["loudspeaker"] # Tags/emojis Ntfy + +# ── Sources ─────────────────────────────────────────── +sources: + + # ═══════════════════════════════════════════════════ + # GITEA — webhooks depuis git.dracodev.net + # ═══════════════════════════════════════════════════ + gitea: + - name: "FlowDeck activity" + webhook_path: /webhooks/gitea-flowdeck + repo: bruno/flowdeck + topic: dev-notifs + priority_map: + pull_request: 4 + issue: 3 + push: 2 + # Template de message (variables dispo: {repo}, {user}, {action}, {title}, {sha}) + template: | + 🔨 **[{repo}]** {user} {action}: "{title}" + `{sha}` + + - name: "ObsiGate activity" + webhook_path: /webhooks/gitea-obsigate + repo: bruno/obsigate + topic: dev-notifs + + # ═══════════════════════════════════════════════════ + # UPTIME KUMA — alertes de monitoring + # ═══════════════════════════════════════════════════ + uptime_kuma: + - name: "Services critiques" + webhook_path: /webhooks/kuma-critical + topic: alerts + state_map: + down: { priority: 5, tags: ["rotating_light", "x"] } + up: { priority: 1, tags: ["white_check_mark"] } + + # ═══════════════════════════════════════════════════ + # DOCKER — surveillance des conteneurs + # ═══════════════════════════════════════════════════ + docker: + - name: "Production containers" + hosts: + - unix:///var/run/docker.sock + # Hôtes distants: + # - tcp://192.168.30.101:2375 + # - tcp://192.168.30.20:2375 + events: [die, health_status, oom] + topic: infra + template: | + 🐳 **{container_name}** → {status} + Image: `{image}` + Exit code: {exit_code} + Host: {host} + + # ═══════════════════════════════════════════════════ + # HTTP POLL — health checks périodiques + # ═══════════════════════════════════════════════════ + http_poll: + - name: "Health endpoints" + interval: 60 # secondes entre chaque check + checks: + - url: https://obsigate.dracodev.net/health + topic: alerts + priority: 5 # priorité si DOWN + expect_status: 200 + timeout: 10 # secondes + + - url: https://flowdeck.dracodev.net/health + topic: alerts + priority: 5 + expect_status: 200 + timeout: 10 + + - url: https://git.dracodev.net/api/v1/version + topic: alerts + priority: 5 + expect_status: 200 + timeout: 10 + + # ═══════════════════════════════════════════════════ + # CRON — reçoit des notifs depuis des scripts shell + # ═══════════════════════════════════════════════════ + cron: + - name: "Disk usage quotidien" + webhook_path: /webhooks/cron-disk + topic: daily + + - name: "Backup report" + webhook_path: /webhooks/cron-backup + topic: daily + + # ═══════════════════════════════════════════════════ + # GENERIC — webhook passe-partout + # ═══════════════════════════════════════════════════ + generic: + - name: "Custom alerts" + webhook_path: /webhooks/generic + topic: custom + +# ── Déduplication ────────────────────────────────────── +dedup: + enabled: true + ttl_seconds: 300 # 5 minutes — durée du cache de déduplication + rate_limit: + max_per_minute: 10 # max notifications/minute par topic + max_per_source: 30 # max notifications/minute toute source confondue diff --git a/scripts/example-cron-disk.sh b/scripts/example-cron-disk.sh new file mode 100755 index 0000000..d893841 --- /dev/null +++ b/scripts/example-cron-disk.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# example-cron-disk.sh +# Exemple de script cron qui envoie un résumé d'espace disque vers ntfy-bridge. +# À appeler depuis cron : 0 9 * * * /path/to/example-cron-disk.sh + +NTFY_BRIDGE_URL="${NTFY_BRIDGE_URL:-http://127.0.0.1:9090}" +WEBHOOK_PATH="${WEBHOOK_PATH:-/webhooks/cron-disk}" + +# Collecter l'info disque +DISK_INFO=$(df -h / /mnt/nfs /home 2>/dev/null | tail -n +2 | awk '{print " " $6 ": " $3 "/" $2 " (" $5 " used)"}') + +# Construire le message +MESSAGE="💾 **Disk Usage Report** — $(date '+%Y-%m-%d %H:%M') +${DISK_INFO} + +Warnings (>80%): +$(df -h / /mnt/nfs /home 2>/dev/null | awk 'NR>1 {gsub(/%/,"",$5); if ($5+0 > 80) print " ⚠️ " $6 " is at " $5 "%"}' || echo " ✅ All partitions under 80%")" + +# Envoyer vers ntfy-bridge +curl -s -X POST \ + -H "Content-Type: text/plain" \ + -d "$MESSAGE" \ + "${NTFY_BRIDGE_URL}${WEBHOOK_PATH}" > /dev/null + +echo "Disk report sent to ntfy-bridge"