feat(check,serve): Startvalidierung, HTTPS-Server, Signal-Handling und Versionscheck

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBHF4R9EAejDJUMdwr6C68
This commit is contained in:
Carsten Abele 2026-08-14 09:34:22 +02:00
parent 7e23df9fec
commit 2d223c5823
9 changed files with 1158 additions and 6 deletions

62
cmd/vpnportal/health.go Normal file
View file

@ -0,0 +1,62 @@
package main
import (
"context"
"sync"
"time"
"git.ravensburg.dev/cabele/opnsense-portal/internal/check"
"git.ravensburg.dev/cabele/opnsense-portal/internal/web"
)
// healthCacheTTL verhindert, dass häufige Monitoring-Abfragen Firewall und
// Verzeichnis belasten.
const healthCacheTTL = 30 * time.Second
// directoryPinger ist der für /healthz benötigte Ausschnitt des AD-Clients.
type directoryPinger interface {
ResolveGroupDN(ctx context.Context) (string, error)
}
// healthChecker beantwortet /healthz mit kurz gecachten Ergebnissen.
type healthChecker struct {
fw check.Pinger
dir directoryPinger
clock func() time.Time
mu sync.Mutex
cached web.HealthReport
cachedAt time.Time
}
func newHealthChecker(fw check.Pinger, dir directoryPinger) *healthChecker {
return &healthChecker{fw: fw, dir: dir, clock: time.Now}
}
func (h *healthChecker) Check(ctx context.Context) web.HealthReport {
now := h.clock()
h.mu.Lock()
if !h.cachedAt.IsZero() && now.Sub(h.cachedAt) < healthCacheTTL {
rep := h.cached
h.mu.Unlock()
return rep
}
h.mu.Unlock()
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
var rep web.HealthReport
_, fwErr := h.fw.Ping(ctx)
rep.OPNsense = fwErr == nil
_, dirErr := h.dir.ResolveGroupDN(ctx)
rep.Directory = dirErr == nil
rep.OK = rep.OPNsense && rep.Directory
h.mu.Lock()
h.cached, h.cachedAt = rep, now
h.mu.Unlock()
return rep
}
var _ web.HealthChecker = (*healthChecker)(nil)