package repository import ( "context" "database/sql" "fmt" "sync" "time" "gitea.d3m0k1d.ru/d3m0k1d/HellreigN/backend/internal/storage" ) type LogRepository struct { mu sync.RWMutex DB *sql.DB } func NewLogRepository() *LogRepository { return &LogRepository{} } func (r *LogRepository) SetDB(db *sql.DB) { r.mu.Lock() defer r.mu.Unlock() r.DB = db } func (r *LogRepository) IsConnected() bool { r.mu.RLock() defer r.mu.RUnlock() return r.DB != nil } func (r *LogRepository) getDB() *sql.DB { r.mu.RLock() defer r.mu.RUnlock() return r.DB } func (r *LogRepository) Init(ctx context.Context) error { db := r.getDB() if db == nil { return nil } _, err := db.ExecContext(ctx, storage.CreateLogsTable) return err } func (r *LogRepository) Insert(ctx context.Context, log storage.LogEntry) error { db := r.getDB() if db == nil { return nil } _, err := db.ExecContext(ctx, ` INSERT INTO logs (timestamp, level, service, agent, message) VALUES ($1, $2, $3, $4, $5) `, log.Timestamp, log.Level, log.Service, log.Agent, log.Message) return err } func (r *LogRepository) InsertBatch(ctx context.Context, logs []storage.LogEntry) error { db := r.getDB() if db == nil { return nil } if len(logs) == 0 { return nil } // Build multi-row INSERT statement query := "INSERT INTO logs (timestamp, level, service, agent, message) VALUES " args := make([]interface{}, 0, len(logs)*5) for i, log := range logs { if i > 0 { query += ", " } query += fmt.Sprintf("($%d, $%d, $%d, $%d, $%d)", i*5+1, i*5+2, i*5+3, i*5+4, i*5+5) args = append(args, log.Timestamp, log.Level, log.Service, log.Agent, log.Message) } _, err := db.ExecContext(ctx, query, args...) return err } type LogFilter struct { Level string Service string Agent string DateFrom time.Time DateTo time.Time Limit int Offset int } func (r *LogRepository) Search(ctx context.Context, filter LogFilter) ([]storage.LogEntry, error) { db := r.getDB() if db == nil { return []storage.LogEntry{}, nil } query := "SELECT timestamp, level, service, agent, message FROM logs WHERE 1=1" args := make([]interface{}, 0) argIdx := 1 if filter.Level != "" { query += " AND level = $" + string(rune('0'+argIdx)) args = append(args, filter.Level) argIdx++ } if filter.Service != "" { query += " AND service = $" + string(rune('0'+argIdx)) args = append(args, filter.Service) argIdx++ } if filter.Agent != "" { query += " AND agent = $" + string(rune('0'+argIdx)) args = append(args, filter.Agent) argIdx++ } if !filter.DateFrom.IsZero() { query += " AND timestamp >= $" + string(rune('0'+argIdx)) args = append(args, filter.DateFrom) argIdx++ } if !filter.DateTo.IsZero() { query += " AND timestamp <= $" + string(rune('0'+argIdx)) args = append(args, filter.DateTo) argIdx++ } query += " ORDER BY timestamp DESC" if filter.Limit > 0 { query += " LIMIT $" + string(rune('0'+argIdx)) args = append(args, filter.Limit) argIdx++ } else { query += " LIMIT 100" } if filter.Offset > 0 { query += " OFFSET $" + string(rune('0'+argIdx)) args = append(args, filter.Offset) } rows, err := db.QueryContext(ctx, query, args...) if err != nil { return nil, err } defer rows.Close() logs := make([]storage.LogEntry, 0) for rows.Next() { var log storage.LogEntry if err := rows.Scan( &log.Timestamp, &log.Level, &log.Service, &log.Agent, &log.Message, ); err != nil { return nil, err } logs = append(logs, log) } return logs, rows.Err() } func (r *LogRepository) GetDistinctServices(ctx context.Context) ([]string, error) { db := r.getDB() if db == nil { return []string{}, nil } rows, err := db.QueryContext(ctx, "SELECT DISTINCT service FROM logs ORDER BY service") if err != nil { return nil, err } defer rows.Close() services := make([]string, 0) for rows.Next() { var service string if err := rows.Scan(&service); err != nil { return nil, err } services = append(services, service) } return services, rows.Err() } func (r *LogRepository) GetDistinctAgents(ctx context.Context) ([]string, error) { db := r.getDB() if db == nil { return []string{}, nil } rows, err := db.QueryContext(ctx, "SELECT DISTINCT agent FROM logs ORDER BY agent") if err != nil { return nil, err } defer rows.Close() agents := make([]string, 0) for rows.Next() { var agent string if err := rows.Scan(&agent); err != nil { return nil, err } agents = append(agents, agent) } return agents, rows.Err() } func (r *LogRepository) GetDistinctLevels(ctx context.Context) ([]string, error) { db := r.getDB() if db == nil { return []string{}, nil } rows, err := db.QueryContext(ctx, "SELECT DISTINCT level FROM logs ORDER BY level") if err != nil { return nil, err } defer rows.Close() levels := make([]string, 0) for rows.Next() { var level string if err := rows.Scan(&level); err != nil { return nil, err } levels = append(levels, level) } return levels, rows.Err() }