mirror of
https://github.com/Heretek-AI/telemetry-service.git
synced 2026-07-01 13:54:38 -04:00
Revert "Drop 'configuring' status; embed public assets"
This reverts commit 871b97501e.
This commit is contained in:
+1
-1
@@ -206,7 +206,7 @@ func (p *PBClient) FetchDashboardData(ctx context.Context, days int, repoSource
|
||||
}
|
||||
case "aborted":
|
||||
data.AbortedCount++
|
||||
case "installing":
|
||||
case "installing", "configuring":
|
||||
data.InstallingCount++
|
||||
}
|
||||
|
||||
|
||||
+23
-48
@@ -4,13 +4,11 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"embed"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -21,9 +19,6 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
//go:embed public
|
||||
var publicFS embed.FS
|
||||
|
||||
type Config struct {
|
||||
ListenAddr string
|
||||
TrustedProxiesCIDR []string
|
||||
@@ -70,7 +65,11 @@ type TelemetryIn struct {
|
||||
RandomID string `json:"random_id"` // Session UUID
|
||||
Type string `json:"type"` // "lxc", "vm", "tool", "addon"
|
||||
NSAPP string `json:"nsapp"` // Application name (e.g., "jellyfin")
|
||||
Status string `json:"status"` // "installing", "success", "failed", "aborted", "unknown"
|
||||
Status string `json:"status"` // "installing", "configuring", "success", "failed", "aborted", "unknown"
|
||||
|
||||
// Session tracking (ignored by storage, accepted to prevent unknown-field rejection)
|
||||
ExecutionID string `json:"execution_id,omitempty"` // Execution UUID (from bash scripts)
|
||||
SessionID string `json:"session_id,omitempty"` // Session UUID (from bash scripts)
|
||||
|
||||
// Container/VM specs
|
||||
CTType int `json:"ct_type,omitempty"` // 1=unprivileged, 2=privileged/VM
|
||||
@@ -341,6 +340,9 @@ func (p *PBClient) FetchRecordsPaginated(ctx context.Context, page, limit int, s
|
||||
} else if status == "failed" {
|
||||
// Exclude SIGINT records from "failed" (they are reclassified as "aborted")
|
||||
filters = append(filters, "(status='failed' && exit_code!=130 && !(error~'SIGINT') && !(error~'Ctrl+C') && !(error~'Ctrl-C'))")
|
||||
} else if status == "installing" {
|
||||
// Include both "installing" and "configuring" (configuring = install in progress)
|
||||
filters = append(filters, "(status='installing' || status='configuring')")
|
||||
} else {
|
||||
filters = append(filters, fmt.Sprintf("status='%s'", status))
|
||||
}
|
||||
@@ -415,14 +417,16 @@ func (p *PBClient) FetchRecordsPaginated(ctx context.Context, page, limit int, s
|
||||
// All records go to the same collection; repo_source is stored as a field.
|
||||
//
|
||||
// For status="installing": always creates a new record.
|
||||
// For status!="installing": updates existing record (found by random_id).
|
||||
// For status="configuring": updates existing record status (progress ping from container).
|
||||
// For terminal statuses (success/failed/aborted/unknown): updates existing record.
|
||||
func (p *PBClient) UpsertTelemetry(ctx context.Context, payload TelemetryOut) error {
|
||||
// For "installing" status, always create new record
|
||||
if payload.Status == "installing" {
|
||||
return p.CreateTelemetry(ctx, payload)
|
||||
}
|
||||
|
||||
// For status updates (success/failed/unknown), find and update existing record
|
||||
// For all other statuses (configuring, success, failed, aborted, unknown),
|
||||
// find and update the existing record
|
||||
recordID, err := p.FindRecordByRandomID(ctx, payload.RandomID)
|
||||
if err != nil {
|
||||
// Search failed, log and return error
|
||||
@@ -430,8 +434,13 @@ func (p *PBClient) UpsertTelemetry(ctx context.Context, payload TelemetryOut) er
|
||||
}
|
||||
|
||||
if recordID == "" {
|
||||
// Record not found - this shouldn't happen normally
|
||||
// Create a full record as fallback
|
||||
// Record not found - for configuring, just silently ignore (the initial
|
||||
// record may not have been created yet). For terminal statuses, create
|
||||
// a full record as fallback.
|
||||
if payload.Status == "configuring" {
|
||||
log.Printf("INFO: configuring update for unknown random_id=%s nsapp=%s - ignored", payload.RandomID, payload.NSAPP)
|
||||
return nil
|
||||
}
|
||||
return p.CreateTelemetry(ctx, payload)
|
||||
}
|
||||
|
||||
@@ -605,7 +614,7 @@ var (
|
||||
allowedType = map[string]bool{"lxc": true, "vm": true, "tool": true, "addon": true}
|
||||
|
||||
// Allowed values for 'status' field
|
||||
allowedStatus = map[string]bool{"installing": true, "success": true, "failed": true, "aborted": true, "unknown": true}
|
||||
allowedStatus = map[string]bool{"installing": true, "configuring": true, "success": true, "failed": true, "aborted": true, "unknown": true}
|
||||
|
||||
// Allowed values for 'os_type' field
|
||||
allowedOsType = map[string]bool{
|
||||
@@ -769,23 +778,6 @@ func validate(in *TelemetryIn) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// serveHTMLFile serves an embedded HTML file
|
||||
func serveHTMLFile(w http.ResponseWriter, r *http.Request, filePath string) {
|
||||
content, err := publicFS.ReadFile(filePath)
|
||||
if err != nil {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
log.Printf("Error reading embedded file %s: %v", filePath, err)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
||||
_, _ = w.Write(content)
|
||||
}
|
||||
|
||||
// computeHash generates a hash for deduplication (GDPR-safe, no IP)
|
||||
func computeHash(out TelemetryOut) string {
|
||||
key := fmt.Sprintf("%s|%s|%s|%s|%d",
|
||||
@@ -808,7 +800,7 @@ func main() {
|
||||
PBPassword: mustEnv("PB_PASSWORD"),
|
||||
PBTargetColl: mustEnv("PB_TARGET_COLLECTION"),
|
||||
|
||||
MaxBodyBytes: envInt64("MAX_BODY_BYTES", 1024),
|
||||
MaxBodyBytes: envInt64("MAX_BODY_BYTES", 16384),
|
||||
RateLimitRPM: envInt("RATE_LIMIT_RPM", 60),
|
||||
RateBurst: envInt("RATE_BURST", 20),
|
||||
RateKeyMode: env("RATE_KEY_MODE", "ip"), // "ip" or "header"
|
||||
@@ -874,8 +866,8 @@ func main() {
|
||||
// Initialize cleanup/retention job (GDPR Löschkonzept)
|
||||
cleaner := NewCleaner(CleanupConfig{
|
||||
Enabled: envBool("CLEANUP_ENABLED", true),
|
||||
CheckInterval: time.Duration(envInt("CLEANUP_INTERVAL_MIN", 60)) * time.Minute,
|
||||
StuckAfterHours: envInt("CLEANUP_STUCK_HOURS", 24),
|
||||
CheckInterval: time.Duration(envInt("CLEANUP_INTERVAL_MIN", 15)) * time.Minute,
|
||||
StuckAfterHours: envInt("CLEANUP_STUCK_HOURS", 2),
|
||||
RetentionEnabled: envBool("RETENTION_ENABLED", false),
|
||||
RetentionDays: envInt("RETENTION_DAYS", 365),
|
||||
}, pb)
|
||||
@@ -922,16 +914,6 @@ func main() {
|
||||
http.Redirect(w, r, "/", http.StatusMovedPermanently)
|
||||
})
|
||||
|
||||
// Error Analysis page
|
||||
mux.HandleFunc("/error-analysis", func(w http.ResponseWriter, r *http.Request) {
|
||||
serveHTMLFile(w, r, "public/templates/error-analysis.html")
|
||||
})
|
||||
|
||||
// Script Analysis page
|
||||
mux.HandleFunc("/script-analysis", func(w http.ResponseWriter, r *http.Request) {
|
||||
serveHTMLFile(w, r, "public/templates/script-analysis.html")
|
||||
})
|
||||
|
||||
// Prometheus-style metrics endpoint
|
||||
mux.HandleFunc("/metrics", func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second)
|
||||
@@ -1271,13 +1253,6 @@ func main() {
|
||||
_, _ = w.Write([]byte("accepted"))
|
||||
})
|
||||
|
||||
// Serve embedded static files
|
||||
staticFS, err := fs.Sub(publicFS, "public/static")
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create static FS: %v", err)
|
||||
}
|
||||
mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(staticFS))))
|
||||
|
||||
srv := &http.Server{
|
||||
Addr: cfg.ListenAddr,
|
||||
Handler: securityHeaders(mux),
|
||||
|
||||
Reference in New Issue
Block a user