v0.25.0 — Debug page: operator testing & diagnostics dashboard
Debug-mode-only dashboard (/debug) with 8 collapsible sections: system diagnostics, notification testing, backup triggers, storage simulation, hub & connectivity, self-update dry-run, DR/setup wizard, and in-memory log viewer. Migrates debug dump from API router to web server. Adds ring buffer log capture, storage disconnect simulation, event history tracking, and cross-drive/self-update test methods. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,16 @@ import (
|
||||
// Notifier sends structured events to the hub via /api/v1/event.
|
||||
// Non-blocking: fires requests in goroutines, logs errors but doesn't retry aggressively.
|
||||
// Cooldown logic is handled by the Hub — the controller sends all events unconditionally.
|
||||
// EventHistoryEntry records a sent event for the debug page.
|
||||
type EventHistoryEntry struct {
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
EventType string `json:"event_type"`
|
||||
Severity string `json:"severity"`
|
||||
Message string `json:"message"`
|
||||
HubStatus int `json:"hub_status"`
|
||||
HubError string `json:"hub_error,omitempty"`
|
||||
}
|
||||
|
||||
type Notifier struct {
|
||||
hubURL string
|
||||
apiKey string
|
||||
@@ -28,6 +38,12 @@ type Notifier struct {
|
||||
|
||||
mu sync.Mutex
|
||||
prevHealthStatus string // tracks previous health check status for change detection
|
||||
|
||||
// Event history ring buffer (debug page)
|
||||
historyMu sync.RWMutex
|
||||
history [50]EventHistoryEntry
|
||||
histPos int
|
||||
histFull bool
|
||||
}
|
||||
|
||||
// New creates a new Notifier. Returns a no-op notifier if hub is not enabled.
|
||||
@@ -454,6 +470,95 @@ func (n *Notifier) SendTest() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ── Debug event testing ───────────────────────────────────────────────
|
||||
|
||||
// PushTestEventSync sends a test event synchronously and returns the Hub HTTP status code.
|
||||
// Used by the debug page for event testing with configurable type/severity.
|
||||
func (n *Notifier) PushTestEventSync(eventType, severity, message string) (statusCode int, err error) {
|
||||
if !n.enabled {
|
||||
return 0, fmt.Errorf("hub nem konfigurált")
|
||||
}
|
||||
|
||||
payload := eventRequest{
|
||||
CustomerID: n.customerID,
|
||||
EventType: eventType,
|
||||
Severity: severity,
|
||||
Message: message,
|
||||
}
|
||||
|
||||
jsonData, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("marshal: %w", err)
|
||||
}
|
||||
|
||||
url := n.hubURL + "/api/v1/event"
|
||||
req, err := http.NewRequest("POST", url, bytes.NewReader(jsonData))
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("request: %w", err)
|
||||
}
|
||||
req.Header.Set("Authorization", "Bearer "+n.apiKey)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := n.httpClient.Do(req)
|
||||
if err != nil {
|
||||
n.recordHistory(eventType, severity, message, 0, err.Error())
|
||||
return 0, fmt.Errorf("send: %w", err)
|
||||
}
|
||||
io.Copy(io.Discard, resp.Body)
|
||||
resp.Body.Close()
|
||||
|
||||
if resp.StatusCode >= 400 {
|
||||
n.recordHistory(eventType, severity, message, resp.StatusCode, fmt.Sprintf("HTTP %d", resp.StatusCode))
|
||||
return resp.StatusCode, fmt.Errorf("hub returned %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
n.recordHistory(eventType, severity, message, resp.StatusCode, "")
|
||||
return resp.StatusCode, nil
|
||||
}
|
||||
|
||||
// GetEventHistory returns the last N event history entries (newest first).
|
||||
func (n *Notifier) GetEventHistory(limit int) []EventHistoryEntry {
|
||||
n.historyMu.RLock()
|
||||
defer n.historyMu.RUnlock()
|
||||
|
||||
total := n.histPos
|
||||
if n.histFull {
|
||||
total = len(n.history)
|
||||
}
|
||||
if limit <= 0 || limit > total {
|
||||
limit = total
|
||||
}
|
||||
|
||||
result := make([]EventHistoryEntry, 0, limit)
|
||||
for i := 0; i < limit; i++ {
|
||||
idx := n.histPos - 1 - i
|
||||
if idx < 0 {
|
||||
idx += len(n.history)
|
||||
}
|
||||
result = append(result, n.history[idx])
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// recordHistory appends an entry to the event history ring buffer.
|
||||
func (n *Notifier) recordHistory(eventType, severity, message string, hubStatus int, hubError string) {
|
||||
n.historyMu.Lock()
|
||||
defer n.historyMu.Unlock()
|
||||
n.history[n.histPos] = EventHistoryEntry{
|
||||
Timestamp: time.Now(),
|
||||
EventType: eventType,
|
||||
Severity: severity,
|
||||
Message: message,
|
||||
HubStatus: hubStatus,
|
||||
HubError: hubError,
|
||||
}
|
||||
n.histPos++
|
||||
if n.histPos >= len(n.history) {
|
||||
n.histPos = 0
|
||||
n.histFull = true
|
||||
}
|
||||
}
|
||||
|
||||
// ── Backward compatibility ───────────────────────────────────────────
|
||||
|
||||
// notifyRequest is the JSON payload for the legacy /api/v1/notify endpoint.
|
||||
|
||||
Reference in New Issue
Block a user