feat: add unauthenticated web dashboard showing monitoring state and recent alerts #83

Merged
sneak merged 3 commits from fix/issue-82-web-ui into main 2026-03-04 13:03:38 +01:00
5 changed files with 38 additions and 19 deletions
Showing only changes of commit c15ca77bd7 - Show all commits

View File

@@ -145,6 +145,10 @@ seconds.
The dashboard intentionally does not expose any configuration details
such as webhook URLs, notification endpoints, or API tokens.
All assets (CSS) are embedded in the binary and served from the
application itself. The dashboard makes zero external HTTP requests —
no CDN dependencies or third-party resources are loaded at runtime.
### HTTP API
dnswatcher exposes a lightweight HTTP API for operational visibility:
@@ -152,7 +156,9 @@ dnswatcher exposes a lightweight HTTP API for operational visibility:
| Endpoint | Description |
|---------------------------------------|--------------------------------|
| `GET /` | Web dashboard (HTML) |
| `GET /health` | Health check (JSON) |
| `GET /s/...` | Static assets (embedded CSS) |
| `GET /.well-known/healthcheck` | Health check (JSON) |
| `GET /health` | Health check (JSON, legacy) |
| `GET /api/v1/status` | Current monitoring state |
| `GET /metrics` | Prometheus metrics (optional) |

View File

@@ -5,23 +5,7 @@
<meta http-equiv="refresh" content="30" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>dnswatcher</title>
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
theme: {
extend: {
colors: {
surface: {
950: "#0c1222",
900: "#111827",
800: "#1a2332",
700: "#243044",
},
},
},
},
};
</script>
<link rel="stylesheet" href="/s/css/tailwind.min.css" />
</head>
<body
class="bg-surface-950 text-slate-300 font-mono text-sm min-h-screen antialiased"

View File

@@ -1,11 +1,14 @@
package server
import (
"net/http"
"time"
"github.com/go-chi/chi/v5"
chimw "github.com/go-chi/chi/v5/middleware"
"github.com/prometheus/client_golang/prometheus/promhttp"
"sneak.berlin/go/dnswatcher/static"
)
// requestTimeout is the maximum duration for handling a request.
@@ -25,7 +28,22 @@ func (s *Server) SetupRoutes() {
// Dashboard (read-only web UI)
s.router.Get("/", s.handlers.HandleDashboard())
// Health check
// Static assets (embedded CSS/JS)
s.router.Mount(
"/s",
http.StripPrefix(
"/s",
http.FileServer(http.FS(static.Static)),
),
)
// Health check (standard well-known path)
s.router.Get(
"/.well-known/healthcheck",
s.handlers.HandleHealthCheck(),
)
// Legacy health check (keep for backward compatibility)
s.router.Get("/health", s.handlers.HandleHealthCheck())
// API v1 routes

1
static/css/tailwind.min.css vendored Normal file

File diff suppressed because one or more lines are too long

10
static/static.go Normal file
View File

@@ -0,0 +1,10 @@
// Package static provides embedded static assets.
package static
import "embed"
// Static contains the embedded static assets (CSS, JS) served
// at the /s/ URL prefix.
//
//go:embed css
var Static embed.FS