// scrub-metrics stellt BAK-08s Befund-Zaehler unter /metrics bereit — die // OPS-05-Anbindung ist Pull-basiert (Core OPS-03 scrapt /metrics-URLs, kein // Push-Mechanismus), daher braucht es einen eigenen, dauerhaft laufenden // HTTP-Endpunkt getrennt vom Oneshot-scrub-cli (dessen Prozess nach jedem // Lauf beendet ist und daher zum Scrape-Zeitpunkt nicht erreichbar waere). package main import ( "context" "fmt" "log" "net/http" "os" "github.com/jackc/pgx/v5/pgxpool" "gitea.perlbach24.de/scripte/nexarch/archive/internal/scrub" ) func main() { dsn := os.Getenv("NEXARCH_SCRUB_TENANT_DSN") if dsn == "" { log.Fatal("NEXARCH_SCRUB_TENANT_DSN muss gesetzt sein") } addr := os.Getenv("NEXARCH_SCRUB_METRICS_LISTEN_ADDR") if addr == "" { addr = ":8090" } ctx := context.Background() pool, err := pgxpool.New(ctx, dsn) if err != nil { log.Fatalf("datenbankverbindung: %v", err) } defer pool.Close() mux := http.NewServeMux() mux.HandleFunc("/metrics", func(w http.ResponseWriter, r *http.Request) { total, err := scrub.FindingsTotal(r.Context(), pool) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "text/plain; version=0.0.4") // Counter (Akzeptanzkriterium/Nutzervorgabe: monoton steigend, kein // Gauge) - kein Befund => Wert 0, kein Dauer-Alarm ("kein Befund // bedeutet kein Alarm", nicht "kein Wert"). body := fmt.Sprintf( "# HELP nexarch_archive_storage_integrity_failures_total Anzahl seit Einrichtung gefundener Pruefsummen-Abweichungen (BAK-08).\n"+ "# TYPE nexarch_archive_storage_integrity_failures_total counter\n"+ "nexarch_archive_storage_integrity_failures_total %d\n", total) if _, err := w.Write([]byte(body)); err != nil { log.Printf("scrub-metrics: antwort schreiben: %v", err) } }) mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) }) log.Printf("scrub-metrics: listening on %s", addr) if err := http.ListenAndServe(addr, mux); err != nil { log.Fatalf("http server: %v", err) } }