230 lines
5.8 KiB
Go
230 lines
5.8 KiB
Go
package handlers
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"time"
|
|
|
|
"gitea.d3m0k1d.ru/d3m0k1d/HellreigN/backend/internal/repository"
|
|
"gitea.d3m0k1d.ru/d3m0k1d/HellreigN/backend/internal/storage"
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
type LogHandlers struct {
|
|
LogRepo *repository.LogRepository
|
|
}
|
|
|
|
func NewLogHandlers(logRepo *repository.LogRepository) *LogHandlers {
|
|
return &LogHandlers{LogRepo: logRepo}
|
|
}
|
|
|
|
type InsertLogRequest struct {
|
|
Timestamp time.Time `json:"timestamp"`
|
|
Level string `json:"level" binding:"required"`
|
|
Service string `json:"service" binding:"required"`
|
|
Agent string `json:"agent" binding:"required"`
|
|
Message string `json:"message" binding:"required"`
|
|
}
|
|
|
|
// @Summary Insert log entry
|
|
// @Description Inserts a single log entry into ClickHouse
|
|
// @Tags logs
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param body body InsertLogRequest true "Log entry"
|
|
// @Success 201 {object} map[string]string
|
|
// @Router /logs [post]
|
|
func (lh *LogHandlers) Insert(c *gin.Context) {
|
|
var req InsertLogRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
if req.Timestamp.IsZero() {
|
|
req.Timestamp = time.Now()
|
|
}
|
|
|
|
log := storage.LogEntry{
|
|
Timestamp: req.Timestamp,
|
|
Level: req.Level,
|
|
Service: req.Service,
|
|
Agent: req.Agent,
|
|
Message: req.Message,
|
|
}
|
|
|
|
if err := lh.LogRepo.Insert(c.Request.Context(), log); err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to insert log"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusCreated, gin.H{"status": "ok"})
|
|
}
|
|
|
|
type InsertLogsRequest struct {
|
|
Logs []InsertLogRequest `json:"logs" binding:"required"`
|
|
}
|
|
|
|
// @Summary Insert log entries (batch)
|
|
// @Description Inserts multiple log entries into ClickHouse
|
|
// @Tags logs
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param body body InsertLogsRequest true "Log entries"
|
|
// @Success 201 {object} map[string]string
|
|
// @Router /logs/batch [post]
|
|
func (lh *LogHandlers) InsertBatch(c *gin.Context) {
|
|
var req InsertLogsRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
logs := make([]storage.LogEntry, len(req.Logs))
|
|
for i, l := range req.Logs {
|
|
if l.Timestamp.IsZero() {
|
|
l.Timestamp = time.Now()
|
|
}
|
|
logs[i] = storage.LogEntry{
|
|
Timestamp: l.Timestamp,
|
|
Level: l.Level,
|
|
Service: l.Service,
|
|
Agent: l.Agent,
|
|
Message: l.Message,
|
|
}
|
|
}
|
|
|
|
if err := lh.LogRepo.InsertBatch(c.Request.Context(), logs); err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to insert logs"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusCreated, gin.H{"status": "ok", "count": len(logs)})
|
|
}
|
|
|
|
type SearchLogsRequest struct {
|
|
Level string `form:"level"`
|
|
Service string `form:"service"`
|
|
Agent string `form:"agent"`
|
|
DateFrom string `form:"date_from"`
|
|
DateTo string `form:"date_to"`
|
|
Limit int `form:"limit"`
|
|
Offset int `form:"offset"`
|
|
}
|
|
|
|
// @Summary Search logs
|
|
// @Description Searches logs with various filters
|
|
// @Tags logs
|
|
// @Produce json
|
|
// @Param level query string false "Log level (INFO, WARNING, ERROR, FATAL)"
|
|
// @Param service query string false "Service name"
|
|
// @Param agent query string false "Agent name"
|
|
// @Param date_from query string false "Date from (RFC3339)"
|
|
// @Param date_to query string false "Date to (RFC3339)"
|
|
// @Param limit query int false "Limit results" default(100)
|
|
// @Param offset query int false "Offset results" default(0)
|
|
// @Success 200 {array} storage.LogEntry
|
|
// @Router /logs [get]
|
|
func (lh *LogHandlers) Search(c *gin.Context) {
|
|
var req SearchLogsRequest
|
|
if err := c.ShouldBindQuery(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
filter := repository.LogFilter{
|
|
Level: req.Level,
|
|
Service: req.Service,
|
|
Agent: req.Agent,
|
|
Limit: req.Limit,
|
|
Offset: req.Offset,
|
|
}
|
|
|
|
if req.DateFrom != "" {
|
|
if t, err := time.Parse(time.RFC3339, req.DateFrom); err == nil {
|
|
filter.DateFrom = t
|
|
}
|
|
}
|
|
|
|
if req.DateTo != "" {
|
|
if t, err := time.Parse(time.RFC3339, req.DateTo); err == nil {
|
|
filter.DateTo = t
|
|
}
|
|
}
|
|
|
|
if filter.Limit <= 0 {
|
|
filter.Limit = 100
|
|
}
|
|
|
|
logs, err := lh.LogRepo.Search(c.Request.Context(), filter)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to search logs"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, logs)
|
|
}
|
|
|
|
// @Summary Get distinct services
|
|
// @Description Returns list of all unique service names in logs
|
|
// @Tags logs
|
|
// @Produce json
|
|
// @Success 200 {array} string
|
|
// @Router /logs/services [get]
|
|
func (lh *LogHandlers) GetServices(c *gin.Context) {
|
|
services, err := lh.LogRepo.GetDistinctServices(c.Request.Context())
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get services"})
|
|
return
|
|
}
|
|
|
|
if services == nil {
|
|
services = []string{}
|
|
}
|
|
|
|
c.JSON(http.StatusOK, services)
|
|
}
|
|
|
|
// @Summary Get distinct agents
|
|
// @Description Returns list of all unique agent names in logs
|
|
// @Tags logs
|
|
// @Produce json
|
|
// @Success 200 {array} string
|
|
// @Router /logs/agents [get]
|
|
func (lh *LogHandlers) GetAgents(c *gin.Context) {
|
|
agents, err := lh.LogRepo.GetDistinctAgents(c.Request.Context())
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get agents"})
|
|
return
|
|
}
|
|
|
|
if agents == nil {
|
|
agents = []string{}
|
|
}
|
|
|
|
c.JSON(http.StatusOK, agents)
|
|
}
|
|
|
|
// @Summary Get distinct log levels
|
|
// @Description Returns list of all unique log levels in logs
|
|
// @Tags logs
|
|
// @Produce json
|
|
// @Success 200 {array} string
|
|
// @Router /logs/levels [get]
|
|
func (lh *LogHandlers) GetLevels(c *gin.Context) {
|
|
levels, err := lh.LogRepo.GetDistinctLevels(c.Request.Context())
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get levels"})
|
|
return
|
|
}
|
|
|
|
if levels == nil {
|
|
levels = []string{}
|
|
}
|
|
|
|
c.JSON(http.StatusOK, levels)
|
|
}
|
|
|
|
// Ensure context is used
|
|
var _ = context.Background
|