hub v0.43.0: remote app-log diagnostics — copyable issues + context + on-demand log tails + range/dismissal fixes
- store: app_log_issues gains context/context_customer (first capture wins) + dismissed_at (resurface only on last_seen > dismissed_at); log_tail_requests (pending operator intents, consume-once) + app_log_tails (transient, keep last 2 per app) - api: /report ingests log_tails (stores + clears the request); ACK advertises log_tail_requests (same additive omit-when-empty pattern as escrow) - web: Known Issues rows click-to-expand (full copyable message + context with provenance + explicit affected-customers list); Dismiss replaces Delete; period selector now filters issues (F); ?customer= filtered view + customer-page drill-down links (H); per-app Request-log-tail button + pending badge + App Log Tails section + ordered tail view with line numbers + .log download; customer-visible log_tail_requested event - tests: store (context first-capture/late-adopt, range filter, dismissal old-window vs new-occurrence, tail request/fulfill/prune/scoping), api ACK round-trip, web render (expanded row, customer page sections, tail view + download + cross-customer 404) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
This commit is contained in:
@@ -236,11 +236,11 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
} else {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
}
|
||||
case strings.HasPrefix(path, "/apps/") && strings.HasSuffix(path, "/delete-issues"):
|
||||
case strings.HasPrefix(path, "/apps/") && strings.HasSuffix(path, "/dismiss-issues"):
|
||||
appName := strings.TrimPrefix(path, "/apps/")
|
||||
appName = strings.TrimSuffix(appName, "/delete-issues")
|
||||
appName = strings.TrimSuffix(appName, "/dismiss-issues")
|
||||
if r.Method == http.MethodPost {
|
||||
s.handleDeleteAppIssues(w, r, appName)
|
||||
s.handleDismissAppIssues(w, r, appName)
|
||||
} else {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
}
|
||||
@@ -302,6 +302,22 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
customerID := strings.TrimPrefix(path, "/customers/")
|
||||
customerID = strings.TrimSuffix(customerID, "/dr-recipe.json")
|
||||
s.handleDRRecipeDownload(w, r, customerID)
|
||||
case strings.HasPrefix(path, "/customers/") && strings.HasSuffix(path, "/request-log-tail"):
|
||||
customerID := strings.TrimPrefix(path, "/customers/")
|
||||
customerID = strings.TrimSuffix(customerID, "/request-log-tail")
|
||||
if r.Method == http.MethodPost {
|
||||
s.handleRequestLogTail(w, r, customerID)
|
||||
} else {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
}
|
||||
case strings.HasPrefix(path, "/customers/") && strings.Contains(path, "/log-tail/"):
|
||||
rest := strings.TrimPrefix(path, "/customers/")
|
||||
parts := strings.SplitN(rest, "/log-tail/", 2)
|
||||
if len(parts) != 2 {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
s.handleLogTailView(w, r, parts[0], parts[1])
|
||||
case strings.HasPrefix(path, "/customers/"):
|
||||
customerID := strings.TrimPrefix(path, "/customers/")
|
||||
s.handleCustomerUnified(w, r, customerID)
|
||||
|
||||
Reference in New Issue
Block a user