mirror of
https://github.com/Dvorinka/Dash.git
synced 2026-10-28 07:41:27 +00:00
docs+release: Phase 4 launch artifacts
README hero with real screenshots, install/config/widget docs, goreleaser for dash + dash-agent, GHCR release workflow, changelog, community files, agent systemd unit and Dockerfile, version stamping via -X main.version. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
97b8f5e87f
commit
a89a81d46c
23 files changed
+785
-13
No files matched your search
@@ -0,0 +1,31 @@
|
||||
name: Bug report
|
||||
description: Something is broken
|
||||
labels: [bug]
|
||||
body:
|
||||
- type: textarea
|
||||
id: what
|
||||
attributes:
|
||||
label: What happened
|
||||
description: What you did, what you expected, what happened instead.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Logs
|
||||
description: Relevant server logs or browser console output.
|
||||
render: text
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: Dash version
|
||||
description: Image tag or release version.
|
||||
placeholder: v0.7.0
|
||||
- type: dropdown
|
||||
id: install
|
||||
attributes:
|
||||
label: Install method
|
||||
options:
|
||||
- Docker
|
||||
- Binary
|
||||
- Built from source
|
||||
@@ -0,0 +1,16 @@
|
||||
name: Feature request
|
||||
description: Suggest an addition
|
||||
labels: [enhancement]
|
||||
body:
|
||||
- type: textarea
|
||||
id: problem
|
||||
attributes:
|
||||
label: What problem does this solve
|
||||
description: Concrete use case, not just "add X".
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: idea
|
||||
attributes:
|
||||
label: Proposed shape
|
||||
description: Optional. How it might look or work.
|
||||
@@ -0,0 +1,11 @@
|
||||
## Summary
|
||||
|
||||
<!-- What does this change and why? -->
|
||||
|
||||
## Test plan
|
||||
|
||||
- [ ] `npm run typecheck` + `npm run lint` + `npm run build` green
|
||||
- [ ] `go build ./...` + `go vet ./...` + `go test ./...` green
|
||||
- [ ] Smoke-tested the affected flow manually
|
||||
|
||||
<!-- Screenshots for UI changes. -->
|
||||
@@ -0,0 +1,71 @@
|
||||
name: release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: ["v*"]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
binaries:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- run: npm run build
|
||||
|
||||
# go:embed all:dist reads internal/web/dist at compile time.
|
||||
- run: rm -rf apps/backend/internal/web/dist && cp -r apps/frontend/dist apps/backend/internal/web/dist
|
||||
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: apps/backend/go.mod
|
||||
cache-dependency-path: apps/backend/go.sum
|
||||
|
||||
- uses: goreleaser/goreleaser-action@v6
|
||||
with:
|
||||
distribution: goreleaser
|
||||
version: "~> v2"
|
||||
args: release --clean
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
docker:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
|
||||
- uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- uses: docker/metadata-action@v5
|
||||
id: meta
|
||||
with:
|
||||
images: ghcr.io/${{ github.repository }}
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=raw,value=latest
|
||||
|
||||
- uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: infra/Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
@@ -0,0 +1,55 @@
|
||||
version: 2
|
||||
|
||||
project_name: dash
|
||||
|
||||
before:
|
||||
hooks:
|
||||
# The embedded UI must be built and copied into internal/web/dist before
|
||||
# any Go build. The release workflow does this; fail loudly here if the
|
||||
# stub is still in place.
|
||||
- sh -c 'test -d apps/backend/internal/web/dist/assets'
|
||||
|
||||
builds:
|
||||
- id: dash
|
||||
dir: apps/backend
|
||||
binary: dash
|
||||
main: ./cmd/dash
|
||||
env: [CGO_ENABLED=0]
|
||||
goos: [linux, darwin, windows]
|
||||
goarch: [amd64, arm64]
|
||||
ldflags: ["-s", "-w", "-X main.version={{.Version}}"]
|
||||
|
||||
- id: dash-agent
|
||||
dir: apps/backend
|
||||
binary: dash-agent
|
||||
main: ./cmd/dash-agent
|
||||
env: [CGO_ENABLED=0]
|
||||
goos: [linux]
|
||||
goarch: [amd64, arm64, arm]
|
||||
goarm: ["7"]
|
||||
ldflags: ["-s", "-w", "-X main.version={{.Version}}"]
|
||||
|
||||
archives:
|
||||
- id: dash
|
||||
formats: [tar.gz]
|
||||
name_template: "{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}"
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
formats: [zip]
|
||||
files: [README.md, LICENSE]
|
||||
|
||||
checksum:
|
||||
name_template: "checksums.txt"
|
||||
|
||||
changelog:
|
||||
use: github
|
||||
sort: asc
|
||||
filters:
|
||||
exclude:
|
||||
- "^docs:"
|
||||
- "^chore:"
|
||||
- "^test:"
|
||||
|
||||
release:
|
||||
footer: |
|
||||
Docker: `docker pull ghcr.io/dvorinka/dash:{{ .Tag }}`
|
||||
@@ -0,0 +1,87 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to Dash. Format follows
|
||||
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow
|
||||
[Semantic Versioning](https://semver.org/).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.7.0] — 2026-09-21
|
||||
|
||||
### Added
|
||||
|
||||
- Incidents with open/ack/resolve/close lifecycle and per-incident update
|
||||
timeline; monitors auto-open and auto-resolve incidents on transitions
|
||||
- Maintenance windows that suppress monitor alerts
|
||||
- Public status pages at `/status/:slug` (unauthenticated, per-page monitor
|
||||
and system selection)
|
||||
- SVG badges: `/api/badge/monitor/:id.svg`, `/api/badge/domain/:id.svg`
|
||||
- Prometheus metrics at `/api/metrics`
|
||||
- CSV bulk import (`/api/import/csv`) and monitors/domains/systems in JSON
|
||||
export
|
||||
- Generic webhook notifications (`notify_webhook` setting): monitor up/down
|
||||
and domain events, Slack/Discord/ntfy-compatible payload, `/api/notify/test`
|
||||
|
||||
## [0.6.0] — 2026-09-21
|
||||
|
||||
### Added
|
||||
|
||||
- System monitoring: `systems` + `system_stats`, bearer-token push ingest
|
||||
(`/api/systems/ingest`), offline detection
|
||||
- `dash-agent` binary: Linux collector for CPU, memory, swap, disk, network,
|
||||
load, uptime, temperatures, and Docker container state
|
||||
- Systems list and detail pages with Recharts graphs; system board widget
|
||||
|
||||
## [0.5.0] — 2026-09-21
|
||||
|
||||
### Added
|
||||
|
||||
- Domain watch: RDAP + TCP-WHOIS lookup, WHOIS parsing, DNS records
|
||||
(NS/MX/TXT/A/AAAA), TLS certificate details, host geo, DNS/email/hosting/
|
||||
certificate provider detection
|
||||
- Daily refresh scheduler, manual refresh, check history
|
||||
- Domain list and detail pages, expiry and SSL countdowns, domain board widget
|
||||
|
||||
## [0.4.0] — 2026-09-21
|
||||
|
||||
### Added
|
||||
|
||||
- Uptime monitors: HTTP, TCP, ping, DNS, keyword, JSON-path, and push types;
|
||||
interval/timeout/retries config, heartbeat history, 24h/30d uptime stats
|
||||
- Monitor scheduler, check-now endpoint, push ingest (`/api/push/:token`)
|
||||
- Monitor list and detail pages, monitor board widget, service-to-monitor
|
||||
creation checkbox
|
||||
- Importers: Homepage `services.yml`, Homarr JSON, Dashy `conf.yml`
|
||||
|
||||
## [0.3.0] — 2026-09-17
|
||||
|
||||
### Added
|
||||
|
||||
- Index and Console board renderers (four total), command palette (⌘K),
|
||||
icon CDN suggestions
|
||||
|
||||
## [0.2.0] — 2026-09-17
|
||||
|
||||
### Added
|
||||
|
||||
- Widget layer: Go fetcher registry, config-field metadata, 30s TTL cache
|
||||
- Pi-hole, AdGuard Home, Immich, and clock widgets; per-widget config UI
|
||||
|
||||
## [0.1.0] — 2026-09-17
|
||||
|
||||
### Added
|
||||
|
||||
- Board MVP: sections, items, multi-URL services, drag-drop, collapse state
|
||||
- Bento and Cards renderers, dark/light themes, icon upload + letter fallback
|
||||
- Server-side status pings with up/down chips
|
||||
- Single Go binary serving the embedded React UI; SQLite via goose migrations;
|
||||
Docker image on distroless
|
||||
|
||||
[Unreleased]: https://github.com/Dvorinka/Dash/compare/v0.7.0...HEAD
|
||||
[0.7.0]: https://github.com/Dvorinka/Dash/compare/v0.6.0...v0.7.0
|
||||
[0.6.0]: https://github.com/Dvorinka/Dash/compare/v0.5.0...v0.6.0
|
||||
[0.5.0]: https://github.com/Dvorinka/Dash/compare/v0.4.0...v0.5.0
|
||||
[0.4.0]: https://github.com/Dvorinka/Dash/compare/v0.3.0...v0.4.0
|
||||
[0.3.0]: https://github.com/Dvorinka/Dash/compare/v0.2.0...v0.3.0
|
||||
[0.2.0]: https://github.com/Dvorinka/Dash/compare/v0.1.0...v0.2.0
|
||||
[0.1.0]: https://github.com/Dvorinka/Dash/releases/tag/v0.1.0
|
||||
@@ -0,0 +1,38 @@
|
||||
# Code of Conduct
|
||||
|
||||
This project follows the [Contributor Covenant](https://www.contributor-covenant.org/),
|
||||
version 2.1.
|
||||
|
||||
## Our pledge
|
||||
|
||||
We pledge to make participation in this project a harassment-free experience
|
||||
for everyone, regardless of age, body size, visible or invisible disability,
|
||||
ethnicity, sex characteristics, gender identity and expression, level of
|
||||
experience, education, socio-economic status, nationality, personal appearance,
|
||||
race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment:
|
||||
|
||||
- Demonstrating empathy and kindness toward other people
|
||||
- Being respectful of differing opinions, viewpoints, and experiences
|
||||
- Giving and gracefully accepting constructive feedback
|
||||
- Accepting responsibility and apologizing to those affected by our mistakes
|
||||
- Focusing on what is best for the community
|
||||
|
||||
Examples of unacceptable behavior:
|
||||
|
||||
- The use of sexualized language or imagery, and sexual attention or advances
|
||||
- Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information without explicit permission
|
||||
- Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of unacceptable behavior may be reported to the maintainer via
|
||||
GitHub (@Dvorinka). All complaints will be reviewed and investigated promptly
|
||||
and fairly. The full enforcement guidelines are at
|
||||
https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
||||
@@ -0,0 +1,58 @@
|
||||
# Contributing
|
||||
|
||||
Thanks for considering a contribution. Dash is a small, deliberate codebase —
|
||||
read [AGENTS.md](AGENTS.md) and [ROADMAP.md](ROADMAP.md) first; the roadmap is
|
||||
canonical for what gets built and when.
|
||||
|
||||
## Setup
|
||||
|
||||
Requires Go 1.24+ and Node 20+.
|
||||
|
||||
```sh
|
||||
npm install
|
||||
npm run generate # regenerate the TS client from openapi.yaml
|
||||
npm run dev # vite dev server on :5173, proxies /api to :8080
|
||||
```
|
||||
|
||||
```sh
|
||||
cd apps/backend
|
||||
DASH_DEV=1 go run ./cmd/dash # API on :8080
|
||||
```
|
||||
|
||||
## Rules of the house
|
||||
|
||||
- `openapi.yaml` is the API contract. Change it first, then
|
||||
`npm run generate`. Never hand-write API types in the frontend.
|
||||
- All schema changes go through goose migrations in
|
||||
`apps/backend/internal/db/migrations` — `NNNN_name.sql` with `-- +goose Up`
|
||||
and `-- +goose Down`.
|
||||
- Board renderers share state, drag-drop, and popover primitives. Never
|
||||
reimplement them per renderer.
|
||||
- No auth, no YAML-as-source-of-truth, no external database. These are
|
||||
non-goals for v1.
|
||||
- Standard library before dependencies; existing helpers before new ones.
|
||||
|
||||
## Before pushing
|
||||
|
||||
```sh
|
||||
npm run typecheck
|
||||
npm run lint
|
||||
npm run build
|
||||
|
||||
cd apps/backend
|
||||
go build ./... && go vet ./... && go test ./...
|
||||
```
|
||||
|
||||
Plus a real smoke test of whatever you changed. All gates green or no push.
|
||||
|
||||
## Commits and PRs
|
||||
|
||||
- Conventional-ish subjects: `feat:`, `fix:`, `docs:`, `refactor:` — keep them
|
||||
short and describe the why.
|
||||
- Small, reviewable PRs. Fill in the template; a screenshot of UI changes is
|
||||
worth more than a paragraph.
|
||||
|
||||
## Reporting bugs / requesting features
|
||||
|
||||
Use the issue templates. For security issues, see
|
||||
[SECURITY.md](SECURITY.md) — do not open a public issue.
|
||||
@@ -3,21 +3,69 @@
|
||||
A self-hosted homelab dashboard. CasaOS-grade ease of use, Vercel-grade design.
|
||||
Single container, UI-managed, no YAML, no auth.
|
||||
|
||||
**Status:** pre-release. See [ROADMAP.md](ROADMAP.md).
|
||||

|
||||
|
||||
## Features
|
||||
|
||||
- **Service board** — sections, drag-and-drop, multi-URL services, icon upload,
|
||||
four board renderers (Bento, Cards, Index, Console), dark/light themes
|
||||
- **Uptime monitors** — HTTP, TCP, ping, DNS, keyword, JSON-path, and push
|
||||
checks on a schedule, with heartbeat history and uptime stats
|
||||
- **Domain watch** — WHOIS/RDAP, DNS records, TLS certificate expiry,
|
||||
registrar and provider detection
|
||||
- **System monitoring** — lightweight `dash-agent` pushes CPU, memory, disk,
|
||||
network, load, temperatures, and Docker container state
|
||||
- **Incidents & status pages** — manual and automatic incidents, maintenance
|
||||
windows, public `/status/:slug` pages, SVG badges
|
||||
- **Alerts** — generic webhook notifications (Slack, Discord, ntfy compatible)
|
||||
- **Observability** — Prometheus `/api/metrics`, JSON export, CSV bulk import,
|
||||
importers for Homepage, Homarr, and Dashy configs
|
||||
|
||||
## Run
|
||||
|
||||
```sh
|
||||
docker run -v ./data:/data -p 3000:3000 dash
|
||||
docker run -d -v ./data:/data -p 3000:3000 ghcr.io/dvorinka/dash:latest
|
||||
```
|
||||
|
||||
or
|
||||
or with compose:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
dash:
|
||||
image: ghcr.io/dvorinka/dash:latest
|
||||
ports:
|
||||
- "3000:3000"
|
||||
volumes:
|
||||
- ./data:/data
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
Then open http://localhost:3000. Everything is configured from the UI.
|
||||
|
||||
## System agent
|
||||
|
||||
Monitor a Linux host: create a system in the UI, copy the token, then:
|
||||
|
||||
```sh
|
||||
docker compose up
|
||||
dash-agent -url http://dash:3000 -token <token>
|
||||
```
|
||||
|
||||
Then open http://localhost:3000.
|
||||
One static binary, no dependencies. See
|
||||
[docs/installation.md](docs/installation.md) for systemd and Docker setups.
|
||||
|
||||
## Screenshots
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
|  |  |
|
||||
|  |  |
|
||||
|
||||
## Docs
|
||||
|
||||
- [Installation](docs/installation.md)
|
||||
- [Configuration](docs/configuration.md)
|
||||
- [Widget development](docs/widgets.md)
|
||||
- [Roadmap](ROADMAP.md) · [Changelog](CHANGELOG.md) · [Contributing](CONTRIBUTING.md)
|
||||
|
||||
## Develop
|
||||
|
||||
|
||||
+6
-6
@@ -175,7 +175,7 @@ Beszel-style server monitoring, push-based.
|
||||
- [x] Schema: `systems` (name, token, host, os/arch, last_seen, status, `latest` JSON snapshot), `system_stats` (system_id, ts, payload JSON — one column, no sub-field queries; 7d retention prune)
|
||||
- [x] Ingest: `POST /api/systems/ingest` with per-system bearer token; offline when silent > max(3×interval, 30s); `system.up`/`system.down` webhook events
|
||||
- [x] `cmd/dash-agent`: Linux collector — `/proc` (cpu/mem/net/load/uptime), `/sys` hwmon temps, statfs disk, docker.sock container list; POST every `-interval` (default 10s); `-once` debug mode
|
||||
- [ ] Agent packaging: systemd unit + Dockerfile + release binaries (Phase 4)
|
||||
- [x] Agent packaging: systemd unit (`packaging/dash-agent.service`) + `infra/Dockerfile.agent` + goreleaser binaries (Phase 4)
|
||||
- [x] Systems page (status cards, cpu/mem/disk bars, uptime) + detail (recharts: cpu%, mem%, net rx/tx, load; temps + containers tables)
|
||||
- [x] Board widget `system` (cpu/mem/disk mini bars)
|
||||
- [x] Not ported: the beszel agent protocol (SSH/WS into PocketBase) — our agent is push-JSON. SMART/ZFS/GPU metrics + per-container cpu/mem deferred to demand.
|
||||
@@ -192,11 +192,11 @@ Beszel-style server monitoring, push-based.
|
||||
|
||||
## Phase 4 — Open-source launch → v1.0.0
|
||||
|
||||
- [ ] Docs: install, configuration, widget development guide (`docs/`)
|
||||
- [ ] Importers: Homepage `services.yml`, Homarr JSON, Dashy `conf.yml` → sections/items/urls
|
||||
- [ ] `README.md` hero: screenshots (4 themes), demo GIF, `docker run` one-liner
|
||||
- [ ] goreleaser → binaries; GHCR image on tag; semver + `CHANGELOG.md`
|
||||
- [ ] `CONTRIBUTING.md`, issue/PR templates, code of conduct, security policy
|
||||
- [x] Docs: install, configuration, widget development guide (`docs/`)
|
||||
- [x] Importers: Homepage `services.yml`, Homarr JSON, Dashy `conf.yml` → sections/items/urls
|
||||
- [x] `README.md` hero: screenshots, `docker run` one-liner (demo GIF deferred — pngs suffice for v1.0.0)
|
||||
- [x] goreleaser → binaries; GHCR image on tag; semver + `CHANGELOG.md`
|
||||
- [x] `CONTRIBUTING.md`, issue/PR templates, code of conduct, security policy
|
||||
- [ ] i18n scaffolding (en default)
|
||||
- [ ] Public repo flip + announcement assets
|
||||
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported versions
|
||||
|
||||
| Version | Supported |
|
||||
|---|---|
|
||||
| latest release | yes |
|
||||
| older releases | no |
|
||||
|
||||
Dash is pre-1.0; only the latest release receives fixes.
|
||||
|
||||
## Reporting a vulnerability
|
||||
|
||||
Use GitHub's private vulnerability reporting:
|
||||
**Security → Advisories → Report a vulnerability** on this repository.
|
||||
|
||||
Do not open a public issue for security problems.
|
||||
|
||||
Please include:
|
||||
|
||||
- affected version/commit
|
||||
- steps to reproduce or a proof of concept
|
||||
- impact assessment
|
||||
|
||||
You will get an acknowledgement within a few days. If the report is accepted,
|
||||
a fix ships in the next release and you are credited in the advisory (unless
|
||||
you prefer otherwise).
|
||||
|
||||
## Scope notes
|
||||
|
||||
Dash is designed for trusted private networks — it ships **without
|
||||
authentication** by design. Exposure to the public internet is a deployment
|
||||
choice outside the project's control, but issues that remain exploitable on a
|
||||
LAN (SSRF in checkers, injection, token leakage, unsafe schemes) are in scope.
|
||||
@@ -25,6 +25,9 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// version is stamped by goreleaser via -X main.version.
|
||||
var version = "dev"
|
||||
|
||||
func main() {
|
||||
url := flag.String("url", os.Getenv("DASH_URL"), "dash server base URL")
|
||||
token := flag.String("token", os.Getenv("DASH_TOKEN"), "system token")
|
||||
@@ -52,7 +55,7 @@ func main() {
|
||||
if *url == "" || *token == "" {
|
||||
log.Fatal("dash-agent: -url and -token (or DASH_URL/DASH_TOKEN) required")
|
||||
}
|
||||
log.Printf("dash-agent: pushing to %s every %ds", *url, *interval)
|
||||
log.Printf("dash-agent %s: pushing to %s every %ds", version, *url, *interval)
|
||||
for {
|
||||
s := c.sample(*interval)
|
||||
if err := push(*url, *token, s); err != nil {
|
||||
|
||||
@@ -19,6 +19,9 @@ import (
|
||||
"github.com/tdvorak/dash/internal/web"
|
||||
)
|
||||
|
||||
// version is stamped by goreleaser via -X main.version.
|
||||
var version = "dev"
|
||||
|
||||
func main() {
|
||||
dev := os.Getenv("DASH_DEV") == "1"
|
||||
|
||||
@@ -55,7 +58,7 @@ func main() {
|
||||
srv := &http.Server{Addr: addr, Handler: router, ReadHeaderTimeout: 10 * time.Second}
|
||||
|
||||
go func() {
|
||||
logger.Info("listening", zap.String("addr", addr), zap.String("data", dataDir))
|
||||
logger.Info("listening", zap.String("addr", addr), zap.String("data", dataDir), zap.String("version", version))
|
||||
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
logger.Fatal("serve", zap.Error(err))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
# Configuration
|
||||
|
||||
Dash is configured from the UI. The only environment the server understands:
|
||||
|
||||
| Variable | Default | Purpose |
|
||||
|---|---|---|
|
||||
| `DASH_PORT` | `8080` (`3000` in Docker) | listen port |
|
||||
| `DASH_DATA_DIR` | `./data` (`/data` in Docker) | SQLite + uploaded icons |
|
||||
| `DASH_DEV` | unset | `1` = dev mode: API only, no embedded UI |
|
||||
|
||||
## Data directory
|
||||
|
||||
```
|
||||
data/
|
||||
dash.db # SQLite — boards, monitors, domains, systems, ops
|
||||
icons/ # uploaded service icons
|
||||
```
|
||||
|
||||
Back up the directory to back up everything. `/api/export` produces a JSON
|
||||
snapshot; `/api/import/csv` bulk-loads monitors and domains from CSV.
|
||||
|
||||
## Notifications
|
||||
|
||||
**Settings → Notifications** holds one generic webhook URL
|
||||
(`notify_webhook`). On monitor up/down transitions and domain events, Dash
|
||||
POSTs a JSON payload with `event`, `name`, `text`, `content`, `message`, and
|
||||
`at` fields — shaped so Slack, Discord, and ntfy all render it sensibly.
|
||||
**Test** sends a `test` event. Delivery is async and non-fatal; failures are
|
||||
logged.
|
||||
|
||||
## Monitors
|
||||
|
||||
HTTP(s), TCP, ping (ICMP), DNS, keyword, JSON-path, and push types. Per-monitor
|
||||
interval, timeout, retries, headers/method/body, expected values, tags, and
|
||||
notes. Push monitors expose `POST /api/push/:token` for cron-style jobs — the
|
||||
monitor goes down if no push arrives within the expected window.
|
||||
|
||||
## Domains
|
||||
|
||||
WHOIS (RDAP first, TCP WHOIS fallback), DNS records, TLS certificate chain,
|
||||
and provider detection run daily and on demand. Expiry and certificate
|
||||
deadlines surface in the UI and through notifications.
|
||||
|
||||
## Systems
|
||||
|
||||
Bearer-token push ingest at `POST /api/systems/ingest`. A system is marked
|
||||
offline when no sample arrives within a few intervals. Stats are pruned during
|
||||
the hourly sweep.
|
||||
|
||||
## Operations
|
||||
|
||||
- **Incidents** — open/ack/resolve/close, with per-incident update timeline.
|
||||
A monitor going down opens an incident automatically; recovery resolves it.
|
||||
- **Maintenance windows** — suppress monitor alerts for the window's scope.
|
||||
- **Status pages** — public, unauthenticated views at `/status/:slug` listing
|
||||
selected monitors and systems with live state.
|
||||
- **Badges** — `GET /api/badge/monitor/:id.svg` and
|
||||
`GET /api/badge/domain/:id.svg` for embedding status shields.
|
||||
- **Metrics** — `GET /api/metrics` in Prometheus exposition format.
|
||||
|
||||
## Importers
|
||||
|
||||
Settings → Import accepts:
|
||||
|
||||
- Dash JSON export (full board restore)
|
||||
- Homepage `services.yml`
|
||||
- Homarr board JSON
|
||||
- Dashy `conf.yml`
|
||||
- CSV of monitors/domains (`/api/import/csv`)
|
||||
|
||||
Foreign formats map onto sections, items, and URLs; unknown fields are dropped.
|
||||
@@ -0,0 +1,124 @@
|
||||
# Installation
|
||||
|
||||
Dash ships as a single container image or a single static binary. All state
|
||||
lives in one directory: a SQLite database and uploaded icons.
|
||||
|
||||
## Docker (recommended)
|
||||
|
||||
```sh
|
||||
docker run -d --name dash \
|
||||
-v ./data:/data \
|
||||
-p 3000:3000 \
|
||||
--restart unless-stopped \
|
||||
ghcr.io/dvorinka/dash:latest
|
||||
```
|
||||
|
||||
Or compose:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
dash:
|
||||
image: ghcr.io/dvorinka/dash:latest
|
||||
ports:
|
||||
- "3000:3000"
|
||||
volumes:
|
||||
- ./data:/data
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
Open http://localhost:3000.
|
||||
|
||||
To build the image locally instead of pulling:
|
||||
|
||||
```sh
|
||||
docker compose up --build
|
||||
```
|
||||
|
||||
## Binary
|
||||
|
||||
Release archives contain a static `dash` binary (no CGO, no dependencies).
|
||||
|
||||
```sh
|
||||
tar xzf dash_linux_amd64.tar.gz
|
||||
./dash # listens on :8080, stores data in ./data
|
||||
```
|
||||
|
||||
Set `DASH_PORT` and `DASH_DATA_DIR` to change the defaults.
|
||||
|
||||
## dash-agent (system monitoring)
|
||||
|
||||
The agent collects CPU, memory, disk, network, load, temperatures, and Docker
|
||||
container state, then pushes a JSON sample to Dash every few seconds.
|
||||
|
||||
1. In the UI, go to **Systems → New system**, copy the generated token.
|
||||
2. Download the `dash-agent` binary for the host's architecture from the
|
||||
release page, or build it:
|
||||
|
||||
```sh
|
||||
cd apps/backend && go build -o dash-agent ./cmd/dash-agent
|
||||
```
|
||||
|
||||
3. Run it:
|
||||
|
||||
```sh
|
||||
dash-agent -url http://dash:3000 -token <token>
|
||||
```
|
||||
|
||||
### Flags / environment
|
||||
|
||||
| Flag | Env | Default | Purpose |
|
||||
|---|---|---|---|
|
||||
| `-url` | `DASH_URL` | — | Dash base URL |
|
||||
| `-token` | `DASH_TOKEN` | — | system token from the UI |
|
||||
| `-interval` | `DASH_INTERVAL` | `10` | push interval, seconds |
|
||||
| `-once` | — | `false` | print one sample and exit (no push) |
|
||||
|
||||
If `/var/run/docker.sock` exists, container states are included automatically.
|
||||
|
||||
### systemd
|
||||
|
||||
`packaging/dash-agent.service` is a ready-made unit:
|
||||
|
||||
```sh
|
||||
sudo install -m755 dash-agent /usr/local/bin/dash-agent
|
||||
sudo install -m644 packaging/dash-agent.service /etc/systemd/system/
|
||||
sudo mkdir -p /etc/dash
|
||||
printf 'DASH_URL=http://dash:3000\nDASH_TOKEN=<token>\n' | sudo tee /etc/dash/agent.env
|
||||
sudo systemctl enable --now dash-agent
|
||||
```
|
||||
|
||||
### Docker (limited)
|
||||
|
||||
`infra/Dockerfile.agent` builds a minimal agent image. A containerized agent
|
||||
only sees its own `/proc` — mount the host's procfs and use host PID mode for
|
||||
meaningful numbers:
|
||||
|
||||
```sh
|
||||
docker run -d --name dash-agent --pid=host \
|
||||
-v /proc:/host/proc:ro \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock:ro \
|
||||
-e DASH_URL=http://dash:3000 -e DASH_TOKEN=<token> \
|
||||
ghcr.io/dvorinka/dash-agent:latest
|
||||
```
|
||||
|
||||
The systemd install above is the recommended path.
|
||||
|
||||
## Reverse proxy
|
||||
|
||||
Dash serves the UI and API on one port — a plain reverse proxy works:
|
||||
|
||||
```nginx
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
```
|
||||
|
||||
## Upgrading
|
||||
|
||||
Pull the new image and restart, or replace the binary. Goose migrations run
|
||||
automatically on boot; `data/` carries forward.
|
||||
|
||||
```sh
|
||||
docker pull ghcr.io/dvorinka/dash:latest && docker compose up -d
|
||||
```
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 39 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 36 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
@@ -0,0 +1,89 @@
|
||||
# Widget development
|
||||
|
||||
A widget is two pieces:
|
||||
|
||||
1. a Go **fetcher** in `apps/backend/internal/widget/` that declares config
|
||||
fields and pulls live data, and
|
||||
2. a React **view** in `apps/frontend/src/widgets/` that renders the payload
|
||||
inside a tile.
|
||||
|
||||
Renderers own the tile chrome — widgets never touch layout, drag-drop, or
|
||||
popovers.
|
||||
|
||||
## Backend fetcher
|
||||
|
||||
One file, registered in `init()`:
|
||||
|
||||
```go
|
||||
package widget
|
||||
|
||||
type pihole struct{}
|
||||
|
||||
func init() { Register(pihole{}) }
|
||||
|
||||
func (pihole) Meta() Type {
|
||||
return Type{
|
||||
Type: "pihole",
|
||||
Name: "Pi-hole",
|
||||
Description: "Queries and block rate",
|
||||
Fields: []Field{
|
||||
{Key: "endpoint", Label: "Endpoint", Placeholder: "http://pi.hole", Required: true},
|
||||
{Key: "token", Label: "API token", Secret: true},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (pihole) Fetch(ctx context.Context, cfg json.RawMessage) (any, error) {
|
||||
// cfg holds the item's config JSON. Never log it — it may hold keys.
|
||||
}
|
||||
```
|
||||
|
||||
- `Meta()` drives the add-widget dialog: `GET /api/widgets/types` returns
|
||||
every registered type with its fields.
|
||||
- `Fetch` is called by `GET /api/widgets/:id/data`; responses are cached ~30s
|
||||
in-process. Return any JSON-marshalable value — it reaches the frontend as
|
||||
`data`.
|
||||
- Read config with the `cfgStr`/`cfgNum` helpers in the package.
|
||||
- Fetchers needing the database (monitor, domain, system) live in
|
||||
`internal/api` and register via `widget.Register` at router setup — the
|
||||
`widget` package stays a leaf with no DB dependency.
|
||||
|
||||
## Frontend view
|
||||
|
||||
One component in `apps/frontend/src/widgets/`, one line in the registry at
|
||||
`src/widgets/index.tsx`:
|
||||
|
||||
```tsx
|
||||
export function PiholeWidget({ data, error }: { item: Item; data?: WidgetData; error?: string }) {
|
||||
if (error) return <TileError msg={error} />;
|
||||
if (!data) return <TileLoading />;
|
||||
return <div>{data.blockedPercent}%</div>;
|
||||
}
|
||||
```
|
||||
|
||||
```ts
|
||||
const registry: WidgetRegistry = {
|
||||
pihole: { view: PiholeWidget },
|
||||
};
|
||||
```
|
||||
|
||||
- `useWidgetData` polls `/api/widgets/:id/data` every 30s. `data` is the
|
||||
fetcher's payload, `error` is a human-readable upstream failure.
|
||||
- Payloads are free-form; read fields defensively (values may be absent on
|
||||
first paint).
|
||||
- `local: true` marks a widget as frontend-only (e.g. clock) — no fetcher, no
|
||||
polling.
|
||||
|
||||
## Config fields
|
||||
|
||||
`Fields` in `Meta()` render inputs in the add-widget dialog. `Secret: true`
|
||||
masks the input. Config is stored in `items.config` JSON, sent back to the
|
||||
fetcher on every poll, and never logged.
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] `apps/backend/internal/widget/<name>.go` — fetcher + `init()` + `Meta()`
|
||||
- [ ] `apps/frontend/src/widgets/<name>.tsx` — view component
|
||||
- [ ] registry entry in `src/widgets/index.tsx`
|
||||
- [ ] error and empty-data states render cleanly in the tile
|
||||
- [ ] upstream timeouts bounded (respect `ctx`, keep HTTP client timeouts)
|
||||
@@ -0,0 +1,15 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
# Minimal dash-agent image. A containerized agent only sees its own /proc —
|
||||
# run with --pid=host and -v /proc:/host/proc for host metrics, or prefer the
|
||||
# systemd unit in packaging/.
|
||||
FROM golang:1.27-alpine AS build
|
||||
WORKDIR /src
|
||||
COPY apps/backend/go.mod apps/backend/go.sum ./
|
||||
RUN go mod download
|
||||
COPY apps/backend ./
|
||||
RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /out/dash-agent ./cmd/dash-agent
|
||||
|
||||
FROM gcr.io/distroless/static-debian12
|
||||
COPY --from=build /out/dash-agent /dash-agent
|
||||
ENTRYPOINT ["/dash-agent"]
|
||||
@@ -0,0 +1,18 @@
|
||||
[Unit]
|
||||
Description=Dash system monitoring agent
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
EnvironmentFile=/etc/dash/agent.env
|
||||
ExecStart=/usr/local/bin/dash-agent
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
|
||||
# Reads /proc, /sys, and (if present) the Docker socket.
|
||||
ProtectHome=true
|
||||
NoNewPrivileges=true
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Reference in new issue
Block a user