Files
dnswatcher/internal/server/routes.go
user 713a758c83
All checks were successful
check / check (push) Successful in 4s
feat: add unauthenticated web dashboard showing monitoring state and recent alerts
Add a read-only web dashboard at GET / that displays:
- Summary counts for all monitored resources
- Domain nameserver state with per-NS records and status
- Hostname DNS records per authoritative nameserver
- TCP port open/closed state with associated hostnames
- TLS certificate details (CN, issuer, expiry, status)
- Last 100 alerts in reverse chronological order

Every data point shows relative age (e.g. '5m ago') for freshness.
Page auto-refreshes every 30 seconds via meta refresh.

Uses Tailwind CSS via CDN for a dark, technical aesthetic with
saturated teals and blues on dark slate. Single page, no navigation.

Implementation:
- internal/notify/history.go: thread-safe ring buffer (last 100 alerts)
- internal/notify/notify.go: record alerts in history before dispatch,
  refactor SendNotification into smaller dispatch helpers (funlen)
- internal/handlers/dashboard.go: template rendering with embedded HTML,
  helper functions for relative time, record formatting, expiry days
- internal/handlers/templates/dashboard.html: Tailwind-styled dashboard
- internal/handlers/handlers.go: add State and Notify dependencies
- internal/server/routes.go: register GET / dashboard route
- README.md: document dashboard and new / endpoint

No secrets (webhook URLs, API tokens, notification endpoints) are
exposed in the dashboard.

closes #82
2026-03-04 03:06:07 -08:00

47 lines
1.1 KiB
Go

package server
import (
"time"
"github.com/go-chi/chi/v5"
chimw "github.com/go-chi/chi/v5/middleware"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
// requestTimeout is the maximum duration for handling a request.
const requestTimeout = 60 * time.Second
// SetupRoutes configures all HTTP routes.
func (s *Server) SetupRoutes() {
s.router = chi.NewRouter()
// Global middleware
s.router.Use(chimw.Recoverer)
s.router.Use(chimw.RequestID)
s.router.Use(s.mw.Logging())
s.router.Use(s.mw.CORS())
s.router.Use(chimw.Timeout(requestTimeout))
// Dashboard (read-only web UI)
s.router.Get("/", s.handlers.HandleDashboard())
// Health check
s.router.Get("/health", s.handlers.HandleHealthCheck())
// API v1 routes
s.router.Route("/api/v1", func(r chi.Router) {
r.Get("/status", s.handlers.HandleStatus())
})
// Metrics endpoint (optional, with basic auth)
if s.params.Config.MetricsUsername != "" {
s.router.Group(func(r chi.Router) {
r.Use(s.mw.MetricsAuth())
r.Get(
"/metrics",
promhttp.Handler().ServeHTTP,
)
})
}
}