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:
Tomas DvorakandDevin committed 2026-09-21 19:39:21 +02:00
1 parent 97b8f5e87f
commit a89a81d46c
23 files changed
+785 -13

No files matched your search

+31
View File
@@ -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.
+11
View File
@@ -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. -->
+71
View File
@@ -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
+55
View File
@@ -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 }}`
+87
View File
@@ -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
+38
View File
@@ -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
+58
View File
@@ -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.
+53 -5
View File
@@ -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).
![Dash board](docs/screenshots/board.png)
## 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
| | |
|---|---|
| ![Monitors](docs/screenshots/monitors.png) | ![Domains](docs/screenshots/domains.png) |
| ![Systems](docs/screenshots/systems.png) | ![Status page](docs/screenshots/status-page.png) |
## 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
View File
@@ -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
View File
@@ -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.
+4 -1
View File
@@ -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 {
+4 -1
View File
@@ -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))
}
+71
View File
@@ -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.
+124
View File
@@ -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

+89
View File
@@ -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)
+15
View File
@@ -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"]
+18
View File
@@ -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