feat: Hub monitoring takeover — event push system + config cleanup (v0.21.0)
Replace external Healthchecks.io with Hub-native event system. Controller now pushes structured events via POST /api/v1/event with typed detail structs. Hub handles dead man's switch, notification dispatch, and cooldowns. Phase 5: PushEvent() core method, 21 event types, expanded notification settings (11 toggles), Hub connection monitoring on dashboard, alerts. Phase 6: Deprecation log for ping UUIDs, pinger kept for transition. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -8,11 +8,20 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/config"
|
||||
)
|
||||
|
||||
// PushStatus tracks the last hub push attempt and result.
|
||||
type PushStatus struct {
|
||||
LastAttempt time.Time
|
||||
LastSuccess time.Time
|
||||
LastError string
|
||||
Consecutive int // consecutive failures
|
||||
}
|
||||
|
||||
// Pusher sends reports to the central hub.
|
||||
type Pusher struct {
|
||||
hubURL string
|
||||
@@ -20,6 +29,9 @@ type Pusher struct {
|
||||
httpClient *http.Client
|
||||
logger *log.Logger
|
||||
enabled bool
|
||||
|
||||
statusMu sync.RWMutex
|
||||
status PushStatus
|
||||
}
|
||||
|
||||
// NewPusher creates a new report pusher from hub configuration.
|
||||
@@ -48,6 +60,10 @@ func (p *Pusher) Push(report *Report) error {
|
||||
|
||||
url := p.hubURL + "/api/v1/report"
|
||||
|
||||
p.statusMu.Lock()
|
||||
p.status.LastAttempt = time.Now()
|
||||
p.statusMu.Unlock()
|
||||
|
||||
var lastErr error
|
||||
for attempt := 0; attempt < 3; attempt++ {
|
||||
if attempt > 0 {
|
||||
@@ -74,14 +90,31 @@ func (p *Pusher) Push(report *Report) error {
|
||||
|
||||
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
|
||||
p.logger.Printf("[INFO] Hub report pushed successfully (%d bytes)", len(data))
|
||||
p.statusMu.Lock()
|
||||
p.status.LastSuccess = time.Now()
|
||||
p.status.LastError = ""
|
||||
p.status.Consecutive = 0
|
||||
p.statusMu.Unlock()
|
||||
return nil
|
||||
}
|
||||
lastErr = fmt.Errorf("HTTP %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
p.statusMu.Lock()
|
||||
p.status.LastError = lastErr.Error()
|
||||
p.status.Consecutive++
|
||||
p.statusMu.Unlock()
|
||||
|
||||
return fmt.Errorf("hub push failed after 3 attempts: %w", lastErr)
|
||||
}
|
||||
|
||||
// GetStatus returns a snapshot of the current push status.
|
||||
func (p *Pusher) GetStatus() PushStatus {
|
||||
p.statusMu.RLock()
|
||||
defer p.statusMu.RUnlock()
|
||||
return p.status
|
||||
}
|
||||
|
||||
// PushInfraBackup sends the infrastructure backup payload to the Hub.
|
||||
// Uses the same retry logic as Push.
|
||||
func (p *Pusher) PushInfraBackup(data []byte) error {
|
||||
|
||||
Reference in New Issue
Block a user