feat(hub): app telemetry analytics dashboard (v0.4.0)

- store/telemetry.go: new app_telemetry + app_log_issues tables with
  SaveAppTelemetry, GetFleetAppSummary (with P95), GetAppTelemetryHistory,
  GetAppCustomerBreakdown, GetCustomerAppSummary, GetAppIssues, prune methods
- api/handler.go: parse and save optional app_telemetry from report body,
  backward-compatible with old controllers
- cmd/hub/main.go: prune app_telemetry (90d) and stale issues (30d)
- web/apps.go: handleApps + handleAppDetail + chart data aggregation helpers
- web/server.go: routes for /apps, /apps/{name}, /static/chart.min.js;
  added memoryColor/accuracyClass/gt template functions
- web/embed.go: embed static/chart.min.js
- web/configs.go: add app telemetry section to handleCustomerUnified
- templates/apps.html: fleet-wide app list with summary cards and sortable table
- templates/app_detail.html: per-app page with Chart.js memory trend,
  customer breakdown, and known issues table
- templates/customer_unified.html: new Alkalmazás telemetria card
- templates/style.css: badge, summary-card, chart, period-selector,
  accuracy-dot, mem-color, data-table styles
- All templates: added Alkalmazások nav link

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-23 10:46:50 +01:00
parent 8bed5ec339
commit a757bee07a
20 changed files with 1323 additions and 2 deletions
+10
View File
@@ -166,6 +166,16 @@ func (h *Handler) handleReport(w http.ResponseWriter, r *http.Request) {
return
}
// Parse and save app telemetry (backward-compatible — old controllers won't have this field)
var telemetryPayload struct {
AppTelemetry []store.AppTelemetryRecord `json:"app_telemetry"`
}
if err := json.Unmarshal(body, &telemetryPayload); err == nil && len(telemetryPayload.AppTelemetry) > 0 {
if err := h.store.SaveAppTelemetry(payload.CustomerID, time.Now(), telemetryPayload.AppTelemetry); err != nil {
h.logger.Printf("[WARN] Failed to save app telemetry for %s: %v", payload.CustomerID, err)
}
}
h.logger.Printf("[INFO] Received report from %s (%d bytes)", payload.CustomerID, len(body))
// Build response with optional customer_blocked flag