tests
This commit is contained in:
120
internal/diag/diag.go
Normal file
120
internal/diag/diag.go
Normal file
@@ -0,0 +1,120 @@
|
||||
// diag – Start-Diagnose: prüft Erreichbarkeit aller externen Dienste
|
||||
package diag
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"my-brain-importer/internal/config"
|
||||
)
|
||||
|
||||
// Result ist das Ergebnis einer einzelnen Prüfung.
|
||||
type Result struct {
|
||||
Name string
|
||||
OK bool
|
||||
Message string
|
||||
}
|
||||
|
||||
// RunAll führt alle Verbindungs-Checks durch und gibt eine Zusammenfassung zurück.
|
||||
// warnings = Dienste die konfiguriert aber nicht erreichbar sind.
|
||||
func RunAll() (results []Result, allOK bool) {
|
||||
allOK = true
|
||||
cfg := config.Cfg
|
||||
|
||||
check := func(name string, ok bool, msg string) {
|
||||
results = append(results, Result{Name: name, OK: ok, Message: msg})
|
||||
if !ok {
|
||||
allOK = false
|
||||
}
|
||||
}
|
||||
|
||||
// Qdrant
|
||||
qdrantAddr := fmt.Sprintf("%s:%s", cfg.Qdrant.Host, cfg.Qdrant.Port)
|
||||
ok, msg := tcpCheck(qdrantAddr)
|
||||
check("Qdrant ("+qdrantAddr+")", ok, msg)
|
||||
|
||||
// LocalAI Chat
|
||||
if cfg.Chat.URL != "" {
|
||||
ok, msg = httpCheck(cfg.Chat.URL)
|
||||
check("LocalAI Chat ("+cfg.Chat.URL+")", ok, msg)
|
||||
}
|
||||
|
||||
// LocalAI Embedding (nur prüfen wenn andere URL als Chat)
|
||||
if cfg.Embedding.URL != "" && cfg.Embedding.URL != cfg.Chat.URL {
|
||||
ok, msg = httpCheck(cfg.Embedding.URL)
|
||||
check("LocalAI Embedding ("+cfg.Embedding.URL+")", ok, msg)
|
||||
}
|
||||
|
||||
// IMAP
|
||||
if cfg.Email.Host != "" {
|
||||
imapAddr := fmt.Sprintf("%s:%d", cfg.Email.Host, cfg.Email.Port)
|
||||
ok, msg = tcpCheck(imapAddr)
|
||||
check("IMAP ("+imapAddr+")", ok, msg)
|
||||
}
|
||||
|
||||
return results, allOK
|
||||
}
|
||||
|
||||
// Log gibt die Ergebnisse über slog aus.
|
||||
func Log(results []Result) {
|
||||
for _, r := range results {
|
||||
if r.OK {
|
||||
slog.Info("Dienst-Check", "dienst", r.Name, "status", "OK", "info", r.Message)
|
||||
} else {
|
||||
slog.Warn("Dienst-Check", "dienst", r.Name, "status", "FEHLER", "info", r.Message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Format gibt eine menschenlesbare Zusammenfassung zurück (für Discord/stdout).
|
||||
func Format(results []Result, allOK bool) string {
|
||||
var sb strings.Builder
|
||||
sb.WriteString("🔍 **Start-Diagnose:**\n")
|
||||
for _, r := range results {
|
||||
icon := "✅"
|
||||
if !r.OK {
|
||||
icon = "❌"
|
||||
}
|
||||
fmt.Fprintf(&sb, "%s %s – %s\n", icon, r.Name, r.Message)
|
||||
}
|
||||
if allOK {
|
||||
sb.WriteString("\n✅ Alle Dienste erreichbar.")
|
||||
} else {
|
||||
sb.WriteString("\n⚠️ Einige Dienste sind nicht erreichbar — Bot läuft, aber Funktionen könnten fehlen.")
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func tcpCheck(addr string) (bool, string) {
|
||||
conn, err := net.DialTimeout("tcp", addr, 3*time.Second)
|
||||
if err != nil {
|
||||
return false, "nicht erreichbar: " + err.Error()
|
||||
}
|
||||
conn.Close()
|
||||
return true, "TCP OK"
|
||||
}
|
||||
|
||||
func httpCheck(baseURL string) (bool, string) {
|
||||
// Normalisiere URL: entferne trailing /v1 etc., hänge /v1/models an
|
||||
url := strings.TrimRight(baseURL, "/")
|
||||
if !strings.HasSuffix(url, "/models") {
|
||||
// Gehe zur Basis-URL zurück und frage /v1/models
|
||||
if idx := strings.LastIndex(url, "/v1"); idx >= 0 {
|
||||
url = url[:idx]
|
||||
}
|
||||
url += "/v1/models"
|
||||
}
|
||||
|
||||
client := &http.Client{Timeout: 3 * time.Second}
|
||||
resp, err := client.Get(url)
|
||||
if err != nil {
|
||||
return false, "nicht erreichbar: " + err.Error()
|
||||
}
|
||||
resp.Body.Close()
|
||||
return resp.StatusCode == http.StatusOK,
|
||||
fmt.Sprintf("HTTP %d", resp.StatusCode)
|
||||
}
|
||||
Reference in New Issue
Block a user