Разработка
DumbMonit — один бинарный файл Rust (crates/server, который встраивает сборку SvelteKit) плюс VictoriaMetrics для временных рядов, запущенный сервером из двоичного файла, отправленного в образе, и встроенная база данных SQLite для конфигурации и состояния. Один контейнер. Код и комментарии на Rust на французском; веб-интерфейс на английском.
Макет проекта
Заголовок раздела «Макет проекта»crates/proto общие типы: Sample, Target, Credential, trait Collector (+ ProbeError)crates/server бинарный файл api/ маршруты axum (mod.rs — таблица маршрутов); spa.rs обслуживает web/build auth/ пароль одного экземпляра, HttpOnly сеансовый cookie, ограничение скорости collectors/ snmp (профили/*.yaml загружаются при запуске), proxmox, pbs, synology, agent, uptime scheduler.rs запускает каждый включенный монитор по его интервалу через реестр сборщика tsdb/ писатель VictoriaMetrics (пакетная очистка) + прокси запроса; embedded.rs контролирует дочерний VictoriaMetrics db/ SQLite + нумерованные миграции в db/migrations/ alerting/ правила, конечный автомат (Phase/EffectivePhase), подавление родителем, молчание, сезонная базовая линия notify/ 22 канала уведомлений, описанные интерфейсом пользователя через notify/catalog.rs crypto.rs AES-256-GCM для credentials/tokens, ключ получен из /data/secret.key или DUMBMONIT_SECRETcrates/agent агент Linux/macOS/FreeBSD/Windows; install/ содержит install.sh/.ps1, обслуживаемые серверомweb/ SvelteKit (Svelte 5 runes, Tailwind 4, uPlot), adapter-static SPA fallback, ssr=falseprofiles/ профили сбора SNMP, автоматически применяются по sysObjectIDdocs/ эта документация (MkDocs, опубликована на Read the Docs)Построение без локального набора инструментов Rust
Заголовок раздела «Построение без локального набора инструментов Rust»Компиляция происходит в Docker; на хосте не требуется устанавливать ничего, что нужно для Rust.
# Полный стек (создает образ: около 10 мин в холодном состоянии) — интерфейс на https://localhost:8080docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d --build
# Rust, внутри этапа builder (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 # один файл интеграционного тестаdocker run --rm -v "$PWD:/build" -w /build dumbmonit-devenv cargo test -p dumbmonit-server test_name # один тест по имениdocker 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 --checkС установленным локально Rust, cargo test и cargo clippy -- -D warnings работают напрямую. Edition 2024, MSRV 1.95; rustfmt.toml устанавливает max_width = 100 и use_small_heuristics = "Max".
Сборка Docker слоистая: сборка веб → зависимости Rust на dummy sources → реальные sources (с touch перед второй cargo build, иначе cargo повторно использует артефакты dummy) → агент кросс-компилируется с cargo-zigbuild для x86_64-unknown-linux-musl, aarch64-unknown-linux-musl и x86_64-pc-windows-gnu → FROM scratch. web/build должен существовать перед cargo build, или сервер отправляется без интерфейса.
Веб-интерфейс
Заголовок раздела «Веб-интерфейс»Node 22 достаточно на хосте.
cd web && npm run dev # Vite на :5173, проксирует /api на localhost:8080 (сервер Docker)cd web && npm run check # svelte-check (CI запускает это)cd web && npm run build # статическая сборка в web/build, встроена rust_embed при cargo buildСоглашения: только Svelte 5 runes ($props, $state, $derived, $effect, snippets, onclick). Весь сетевой доступ проходит через src/lib/api/; компоненты никогда не fetch. Типы в api/types.ts точно отражают структуры Rust — их необходимо синхронизировать с crates/server/src/api/*.rs. Система дизайна находится в src/app.css (tokens) и src/lib/ui/ (primitives). Никогда не запускайте два vite build одновременно: они удаляют .svelte-kit/output.
Переводы
Заголовок раздела «Переводы»Веб-интерфейс переводится через pull request, как и любой другой вклад. Исходный язык — английский (web/messages/en.json: плоские ключи, плейсхолдеры {param}); каждый другой язык — один файл web/messages/<locale>.json, который Paraglide JS компилирует в функции сообщений с tree-shaking в web/src/lib/paraglide/ (генерируется, не отслеживается).
- Отредактируйте
web/messages/<locale>.json(fr,de,es,it,pt,pt-BR,ru,zh-Hans) или скопируйтеen.json, чтобы добавить новый язык. - Сохраните все ключи и все
{variables}изen.json; переводите только значения. Никакой HTML-разметки в сообщениях. - Множественное число задаётся синтаксисом сообщений, а не строками, собранными вручную: сохраните структуру
match/pluralанглийского сообщения и переведите каждую ветку. - Запустите
cd web && npm run i18n:check: проверка падает, если в языке нет ключа или изменены его{variables}. - Откройте pull request.
Новые строки интерфейса сначала добавляются в web/messages/en.json (единственный файл, нужный pull request’у функции; npm run i18n:merge объединяет временные фрагменты). Язык появляется в переключателе (Настройки, Внешний вид), как только существует его файл. Правила именования ключей описаны в CONTRIBUTING.md.
web/project.inlang/settings.json указывает свой единственный плагин (message-format) на ./node_modules/@inlang/plugin-message-format/dist/index.js вместо URL jsdelivr, который по умолчанию использует собственный инструментарий inlang: относительный путь без http читается прямо с диска, поэтому npm run build никогда не достигает CDN — важно, так как сборка образа не гарантирует теплый npm кэш, предлагающий этот URL, и инструмент homelab, которому нужен интернет для перестройки себя, — это плохо.
Тесты и CI
Заголовок раздела «Тесты и CI».github/workflows/ci.yml запускает при каждом push и pull request:
cargo fmt --all --check,cargo clippy --all-targets --all-features -- -D warnings,cargo test --all-features;cargo deny check licenses bans— вdeny.tomlразрешены только лицензии, одобренные OSI; copyleft или лицензия с открытым исходным кодом зависимость не проходит сборку;npm run checkиnpm run build;- образ (
linux/amd64), только сборка.
.github/workflows/release.yml отправляет тот же образ на ghcr.io/laupernoe/dumbmonit: :edge при каждом push на main, :X.Y.Z, :X.Y, :X и :latest при каждом теге v*.
Интеграционные тесты находятся в crates/server/tests/; чистые модули (преобразование метрик, разбор опций, конечный автомат состояния оповещения, молчание) тестируются на месте без сети.
Добавление сборщика
Заголовок раздела «Добавление сборщика»- Реализуйте
Collectorизcrates/proto/src/collector.rs:kind(),probe(&Target) -> Result<Vec<Sample>, ProbeError>и опциональноdiscover()для обнаружения профиля. Верните вариантыProbeErrorправдиво:means_down()решает, подсчитывается ли отказ как “монитор не работает” (оповещение) или как ошибка конфигурации (показано на мониторе, не уведомляется). - Прочитайте ваши опции из
Target::tagsвoptions.rsс значениями по умолчанию. - Зарегистрируйте его в
crates/server/src/main.rsсregistry.register(...). Планировщик и API никогда не знают конкретные типы. - Описите его в
crates/server/src/api/collectors.rs: метка, резюме, примеры, типы учетных данных, подсказка адреса, примечание установки и списокoptions(ключ, метка, справка, заполнитель, по умолчанию, ввод, выбор). Интерфейс строит свою форму на основе этого; тест там проверяет, что каждый зарегистрированный вид имеет примечание и что ключи опций совпадают с тем, что читает сборщик. - Назовите метрики
<what>_<unit>без префиксаdumbmonit_(писатель добавляет его) и добавьте любую новую метрику, на которую нацелено встроенное правило, в список в тестахalerting/rules.rs. - Добавьте страницу в
docs/devices/.
Примеры — Sample::new(name, value, MetricKind::Gauge | Counter, timestamp_ms) с .with_label(k, v); метки идентификации (target, host, tag_*) добавляются реестром. Не излучайте их самостоятельно: назовите метку для каждого объекта по объекту (proxy_host, esxi, ceph_host). Столкновение метки сохраняется как exported_<name>, и опции, перечисленные для типа в api/collectors.rs, не копируются как tag_*.
Добавление канала уведомлений
Заголовок раздела «Добавление канала уведомлений»- Реализуйте
Notifier(crates/server/src/notify/mod.rs):kind()иasync fn send(&Message) -> Result<(), NotifyError>. Веб-каналы совместно используют HTTP-клиент и помощников вnotify/http.rs; посмотрите наservices.rs,chat.rs,push.rs,oncall.rsиsmtp.rsдля товарищей. - Добавьте тип в
CHANNEL_KINDSи в матчbuildвnotify/mod.rs, который превращает сохраненнуюChannelConfig(параметры + расшифрованные секреты) вNotifier. - Опишите его в
notify/catalog.rs: метка, резюме, якорьdoc_urlи поляsettingsиsecrets(ключ, метка, требуется, ввод, справка, заполнитель, опции, форма, по умолчанию). Интерфейс визуализирует форму на основе этого описания, поэтому выпуск пользовательского интерфейса не требуется. - Документируйте его в
docs/notifications.mdпод Supported channels, с заголовком, слаг которого совпадает с якоремdoc_url: интерфейс ссылается прямо на этот якорь на Read the Docs.
Снимки экрана без головы
Заголовок раздела «Снимки экрана без головы»web/tools/README.md описывает, как визуализировать каждую страницу в бесголовном Chromium, работающем в Docker (zenika/alpine-chrome:with-puppeteer, локальный браузер не требуется): войдите с помощью curl для получения cookie dumbmonit_session, затем запустите web/tools/screenshot.js на сервере предварительного просмотра или прямо на :8080. Он записывает web/shots/<page>-<theme>-<viewport>.png для обеих тем и обоих представлений, что является тем, как интерфейс проверяется после изменения и как были созданы снимки экрана этой документации.
Документация
Заголовок раздела «Документация»Этот сайт построен с помощью MkDocs и Material for MkDocs из docs/ (mkdocs.yml в корне, .readthedocs.yaml для Read the Docs). Для предварительного просмотра:
pip install -r docs/requirements.txtmkdocs servemkdocs build --strict # то, что запускает Read the Docs; должен пройти без предупреждений