package handlers import ( "context" "strconv" "strings" "time" "github.com/gin-gonic/gin" "git.netcell-it.de/projekte/edgeguard-native/internal/handlers/response" "git.netcell-it.de/projekte/edgeguard-native/internal/services/syslogs" ) // LogsHandler exposes: // // GET /api/v1/logs?sources=edgeguard-api,haproxy,... // &levels=error,warn // &since=2026-05-12T20:00:00Z // &until=2026-05-12T21:00:00Z // &grep=503 // &limit=500 // // Aggregiert journalctl-Output für die EdgeGuard-Services + die // audit_log-Tabelle in ein einheitliches Entry-Format. type LogsHandler struct { Reader *syslogs.Reader } func NewLogsHandler(r *syslogs.Reader) *LogsHandler { return &LogsHandler{Reader: r} } func (h *LogsHandler) Register(rg *gin.RouterGroup) { g := rg.Group("/logs") g.GET("", h.List) g.GET("/sources", h.Sources) } // Sources gibt die statische Quellen-Liste fürs UI-Dropdown zurück — // das UI muss die nicht hartcodieren. func (h *LogsHandler) Sources(c *gin.Context) { out := make([]string, 0, len(syslogs.AllSources)) for _, s := range syslogs.AllSources { out = append(out, string(s)) } response.OK(c, gin.H{"sources": out}) } // List baut den Filter aus den Query-Params und führt Reader.Query aus. // Errors aus einzelnen Sources sind nicht-fatal (Reader.Query logged // sie selbst); wir liefern was vorhanden ist. func (h *LogsHandler) List(c *gin.Context) { ctx, cancel := context.WithTimeout(c.Request.Context(), 10*time.Second) defer cancel() f := syslogs.Filter{ Grep: c.Query("grep"), } if s := c.Query("sources"); s != "" { for _, raw := range strings.Split(s, ",") { s := strings.TrimSpace(raw) if s != "" { f.Sources = append(f.Sources, syslogs.Source(s)) } } } if s := c.Query("levels"); s != "" { for _, raw := range strings.Split(s, ",") { s := strings.TrimSpace(raw) if s != "" { f.Levels = append(f.Levels, syslogs.Level(s)) } } } if s := c.Query("since"); s != "" { if t, err := time.Parse(time.RFC3339, s); err == nil { f.Since = t } } if s := c.Query("until"); s != "" { if t, err := time.Parse(time.RFC3339, s); err == nil { f.Until = t } } if s := c.Query("limit"); s != "" { if n, err := strconv.Atoi(s); err == nil { f.Limit = n } } entries, _ := h.Reader.Query(ctx, f) response.OK(c, gin.H{"entries": entries, "count": len(entries)}) }