Entwicklung
DumbMonit besteht aus einem Rust-Binary (crates/server, das den SvelteKit-Build einbettet),
VictoriaMetrics für Zeitreihen, vom Server aus dem im Image mitgelieferten Binary gestartet,
und einer eingebetteten SQLite-Datenbank für Konfiguration und Zustand. Ein Container. Rust-Code
und Kommentare sind auf Französisch; die Web-Oberfläche ist auf Englisch.
Projektstruktur
Abschnitt betitelt „Projektstruktur“crates/proto shared types: Sample, Target, Credential, trait Collector (+ ProbeError)crates/server the binary api/ axum routes (mod.rs is the route table); spa.rs serves web/build auth/ single instance password, HttpOnly session cookie, rate limit collectors/ snmp (profiles/*.yaml loaded at startup), proxmox, pbs, synology, agent, uptime scheduler.rs runs every enabled target on its interval through the collector registry tsdb/ VictoriaMetrics writer (batched flush) + query proxy; embedded.rs supervises the child VictoriaMetrics db/ SQLite + numbered migrations in db/migrations/ alerting/ rules, state machine (Phase/EffectivePhase), suppression by parent, silences, seasonal baseline notify/ 22 notification channels, described to the UI by notify/catalog.rs crypto.rs AES-256-GCM for credentials/tokens, key derived from /data/secret.key or DUMBMONIT_SECRETcrates/agent Linux/macOS/FreeBSD/Windows agent; install/ holds install.sh/.ps1 served by the serverweb/ SvelteKit (Svelte 5 runes, Tailwind 4, uPlot), adapter-static SPA fallback, ssr=falseprofiles/ SNMP collection profiles, auto-applied by sysObjectIDdocs/ this documentation (MkDocs, published on Read the Docs)Bauen ohne Rust-Toolchain
Abschnitt betitelt „Bauen ohne Rust-Toolchain“Die Kompilierung läuft in Docker; auf dem Host muss nichts von Rust installiert sein.
# Full stack (builds the image: about 10 min cold) — UI on https://localhost:8080docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d --build
# Rust, inside the builder stage (musl/alpine)docker build -t dumbmonit-devenv --target builder .docker run --rm -v "$PWD:/build" -w /build dumbmonit-devenv cargo testdocker run --rm -v "$PWD:/build" -w /build dumbmonit-devenv cargo test -p dumbmonit-server --test alerts_api # one integration test filedocker run --rm -v "$PWD:/build" -w /build dumbmonit-devenv cargo test -p dumbmonit-server test_name # one test by namedocker run --rm -v "$PWD:/build" -w /build dumbmonit-devenv cargo clippy --all-targets --all-features -- -D warningsdocker run --rm -v "$PWD:/build" -w /build dumbmonit-devenv cargo fmt --all --checkMit lokal installiertem Rust funktionieren cargo test und cargo clippy -- -D warnings
direkt. Edition 2024, MSRV 1.95; rustfmt.toml setzt max_width = 100 und
use_small_heuristics = "Max".
Der Docker-Build ist in Schichten aufgebaut: Web-Build → Rust-Abhängigkeiten auf Dummy-Quellen →
echte Quellen (mit einem touch vor dem zweiten cargo build, sonst verwendet cargo die
Dummy-Artefakte weiter) → Agent, mit cargo-zigbuild cross-kompiliert für
x86_64-unknown-linux-musl, aarch64-unknown-linux-musl und x86_64-pc-windows-gnu →
FROM scratch. web/build muss vor cargo build existieren, sonst wird der Server ohne
Oberfläche ausgeliefert.
Web-Oberfläche
Abschnitt betitelt „Web-Oberfläche“Auf dem Host genügt Node 22.
cd web && npm run dev # Vite on :5173, proxies /api to localhost:8080 (the Docker server)cd web && npm run check # svelte-check (CI runs this)cd web && npm run build # static build into web/build, embedded by rust_embed at cargo buildKonventionen: ausschließlich Svelte-5-Runes ($props, $state, $derived, $effect,
Snippets, onclick). Der gesamte Netzwerkzugriff läuft über src/lib/api/; Komponenten rufen
nie fetch auf. Die Typen in api/types.ts spiegeln die Rust-Structs exakt und müssen mit
crates/server/src/api/*.rs synchron gehalten werden. Das Designsystem liegt in src/app.css
(Tokens) und src/lib/ui/ (Primitive). Führen Sie nie zwei vite build gleichzeitig aus: Sie
löschen .svelte-kit/output.
Übersetzungen
Abschnitt betitelt „Übersetzungen“Die Weboberfläche wird wie jeder andere Beitrag per Pull Request übersetzt. Ausgangssprache ist Englisch (web/messages/en.json: flache Schlüssel, {param}-Platzhalter); jede weitere Sprache ist eine Datei, web/messages/<locale>.json, die Paraglide JS zu Tree-Shaking-fähigen Nachrichtenfunktionen unter web/src/lib/paraglide/ kompiliert (generiert, nicht versioniert).
- Bearbeiten Sie
web/messages/<locale>.json(fr,de,es,it,pt,pt-BR,ru,zh-Hans) oder kopieren Sieen.json, um eine neue Sprache anzulegen. - Behalten Sie alle Schlüssel und alle
{variables}vonen.jsonbei; übersetzen Sie nur die Werte. Kein HTML-Markup in Nachrichten. - Plurale nutzen die Nachrichtensyntax, keine von Hand gebauten Zeichenketten: Behalten Sie die
match/plural-Struktur der englischen Nachricht bei und übersetzen Sie jeden Zweig. - Führen Sie
cd web && npm run i18n:checkaus: Es schlägt fehl, wenn einer Sprache ein Schlüssel fehlt oder ihre{variables}abweichen. - Öffnen Sie einen Pull Request.
Neue UI-Texte kommen zuerst in web/messages/en.json (die einzige Datei, die ein Feature-Pull-Request braucht; npm run i18n:merge führt temporäre Fragmente zusammen). Eine Sprache erscheint in der Sprachauswahl (Einstellungen, Darstellung), sobald ihre Datei existiert. Die Namenskonvention für Schlüssel steht in CONTRIBUTING.md.
web/project.inlang/settings.json verweist das einzige Plugin (message-format) auf
./node_modules/@inlang/plugin-message-format/dist/index.js statt auf die jsdelivr-URL, die
das Tooling von inlang standardmäßig verwendet: Ein relativer Nicht-http-Pfad wird direkt von
der Festplatte gelesen, sodass npm run build nie ein CDN kontaktiert — wichtig, weil der
Image-Build keinen warmen npm-Cache für diese URL garantiert, und ein Homelab-Tool, das zum
eigenen Neubau das Internet braucht, macht keine gute Figur.
Tests und CI
Abschnitt betitelt „Tests und CI“.github/workflows/ci.yml führt bei jedem Push und Pull Request aus:
cargo fmt --all --check,cargo clippy --all-targets --all-features -- -D warnings,cargo test --all-features;cargo deny check licenses bans—deny.tomlerlaubt nur OSI-zugelassene Lizenzen; eine Copyleft- oder Source-available-Abhängigkeit lässt den Build fehlschlagen;npm run checkundnpm run build;- das Image (
linux/amd64), nur Build.
.github/workflows/release.yml pusht dasselbe Image nach ghcr.io/laupernoe/dumbmonit:
:edge bei jedem Push auf main, :X.Y.Z, :X.Y, :X und :latest bei jedem v*-Tag.
Integrationstests liegen in crates/server/tests/; reine Module (Metrikumwandlung,
Optionen-Parsing, Alarm-Zustandsmaschine, Stummschaltungen) werden vor Ort ohne Netzwerk getestet.
Einen Collector hinzufügen
Abschnitt betitelt „Einen Collector hinzufügen“- Implementieren Sie
Collectorauscrates/proto/src/collector.rs:kind(),probe(&Target) -> Result<Vec<Sample>, ProbeError>und optionaldiscover()für die Profilerkennung. Geben Sie dieProbeError-Varianten wahrheitsgemäß zurück:means_down()entscheidet, ob ein Fehler als „Monitor ausgefallen“ (alarmiert) oder als Konfigurationsfehler (am Monitor angezeigt, nicht benachrichtigt) zählt. - Lesen Sie Ihre Optionen aus
Target::tagsin eineroptions.rs, mit Standardwerten. - Registrieren Sie ihn in
crates/server/src/main.rsmitregistry.register(...). Scheduler und API kennen nie konkrete Typen. - Beschreiben Sie ihn in
crates/server/src/api/collectors.rs: Label, Zusammenfassung, Beispiele, Credential-Typen, Adresshinweis, Einrichtungshinweis und die Listeoptions(key, label, help, placeholder, default, input, choices). Die Oberfläche baut daraus ihr Formular; ein Test dort prüft, dass jede registrierte Art einen Hinweis hat und dass die Optionsschlüssel dem entsprechen, was der Collector liest. - Benennen Sie Metriken
<what>_<unit>ohne das Präfixdumbmonit_(der Writer fügt es hinzu) und tragen Sie jede neue Metrik, auf die eine eingebaute Regel zielt, in die Liste in den Tests vonalerting/rules.rsein. - Fügen Sie unter
docs/devices/eine Seite hinzu.
Samples sind Sample::new(name, value, MetricKind::Gauge | Counter, timestamp_ms) mit
.with_label(k, v); Identitätslabels (target, host, tag_*) fügt die Registry hinzu.
Geben Sie sie nicht selbst aus: Benennen Sie ein Label pro Objekt nach dem Objekt
(proxy_host, esxi, ceph_host). Ein kollidierendes Label bleibt als exported_<name>
erhalten, und die für die Art in api/collectors.rs aufgeführten Optionen werden nicht als
tag_* kopiert.
Einen Benachrichtigungskanal hinzufügen
Abschnitt betitelt „Einen Benachrichtigungskanal hinzufügen“- Implementieren Sie
Notifier(crates/server/src/notify/mod.rs):kind()undasync fn send(&Message) -> Result<(), NotifyError>. Web-Kanäle teilen sich den HTTP-Client und die Helfer innotify/http.rs; als Geschwister dienenservices.rs,chat.rs,push.rs,oncall.rsundsmtp.rs. - Fügen Sie die Art zu
CHANNEL_KINDSund zumbuild-Match innotify/mod.rshinzu, das eine gespeicherteChannelConfig(Einstellungen + entschlüsselte Geheimnisse) in einenNotifierverwandelt. - Beschreiben Sie ihn in
notify/catalog.rs: Label, Zusammenfassung,doc_url-Anker sowie die Feldersettingsundsecrets(key, label, required, input, help, placeholder, options, shape, default). Die Oberfläche rendert das Formular aus dieser Beschreibung, daher ist kein UI-Release nötig. - Dokumentieren Sie ihn in
docs/notifications.mdunter Unterstützte Kanäle, mit einer Überschrift, deren Slug zumdoc_url-Anker passt: Die Oberfläche verlinkt direkt auf diesen Anker bei Read the Docs.
Headless-Screenshots
Abschnitt betitelt „Headless-Screenshots“web/tools/README.md beschreibt, wie jede Seite in einem headless Chromium gerendert wird, der
in Docker läuft (zenika/alpine-chrome:with-puppeteer, kein lokaler Browser nötig): Mit curl
anmelden, um das Cookie dumbmonit_session zu erhalten, dann web/tools/screenshot.js gegen
den Preview-Server oder direkt gegen :8080 ausführen. Es schreibt
web/shots/<page>-<theme>-<viewport>.png für beide Themes und beide Viewports; so wird die
Oberfläche nach einer Änderung geprüft und so entstanden die Screenshots dieser Dokumentation.
Dokumentation
Abschnitt betitelt „Dokumentation“Diese Website wird mit MkDocs und Material for MkDocs aus docs/ gebaut (mkdocs.yml im
Wurzelverzeichnis, .readthedocs.yaml für Read the Docs). Zur Vorschau:
pip install -r docs/requirements.txtmkdocs servemkdocs build --strict # what Read the Docs runs; must pass with no warnings