78e5c90ed0
also small various features
211 lines
5.0 KiB
Go
211 lines
5.0 KiB
Go
package storage
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"gorm.io/driver/sqlite"
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/logger"
|
|
)
|
|
|
|
type DB struct {
|
|
db *gorm.DB
|
|
}
|
|
|
|
func New(path string) (*DB, error) {
|
|
db, err := gorm.Open(sqlite.Open(path), &gorm.Config{
|
|
Logger: logger.Default.LogMode(logger.Silent),
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("open database: %w", err)
|
|
}
|
|
|
|
if err := db.AutoMigrate(&Alert{}, &PacketStat{}, &CaptureSession{}); err != nil {
|
|
return nil, fmt.Errorf("migrate: %w", err)
|
|
}
|
|
|
|
return &DB{db: db}, nil
|
|
}
|
|
|
|
// ── Alerts ──────────────────────────────────────────────────────────────────
|
|
|
|
func (d *DB) CreateAlert(a *Alert) error {
|
|
return d.db.Create(a).Error
|
|
}
|
|
|
|
func (d *DB) GetAlerts(f AlertFilter) ([]Alert, int64, error) {
|
|
q := d.db.Model(&Alert{})
|
|
|
|
if f.Type != "" {
|
|
q = q.Where("type = ?", f.Type)
|
|
}
|
|
if f.Severity != "" {
|
|
q = q.Where("severity = ?", f.Severity)
|
|
}
|
|
if f.SrcIP != "" {
|
|
q = q.Where("src_ip = ?", f.SrcIP)
|
|
}
|
|
if f.DateFrom != nil {
|
|
q = q.Where("created_at >= ?", f.DateFrom)
|
|
}
|
|
if f.DateTo != nil {
|
|
q = q.Where("created_at <= ?", f.DateTo)
|
|
}
|
|
|
|
var total int64
|
|
q.Count(&total)
|
|
|
|
if f.PageSize <= 0 {
|
|
f.PageSize = 50
|
|
}
|
|
if f.Page <= 0 {
|
|
f.Page = 1
|
|
}
|
|
|
|
var alerts []Alert
|
|
err := q.Order("created_at desc").
|
|
Offset((f.Page - 1) * f.PageSize).
|
|
Limit(f.PageSize).
|
|
Find(&alerts).Error
|
|
|
|
return alerts, total, err
|
|
}
|
|
|
|
func (d *DB) DeleteAlert(id uint) error {
|
|
return d.db.Delete(&Alert{}, id).Error
|
|
}
|
|
|
|
func (d *DB) DeleteAllAlerts() error {
|
|
return d.db.Where("1 = 1").Delete(&Alert{}).Error
|
|
}
|
|
|
|
// ── Stats ────────────────────────────────────────────────────────────────────
|
|
|
|
func (d *DB) CreatePacketStat(s *PacketStat) error {
|
|
return d.db.Create(s).Error
|
|
}
|
|
|
|
type SeverityCount struct {
|
|
Severity string `json:"severity"`
|
|
Count int64 `json:"count"`
|
|
}
|
|
|
|
func (d *DB) AlertsBySeverity() ([]SeverityCount, error) {
|
|
var result []SeverityCount
|
|
err := d.db.Model(&Alert{}).
|
|
Select("severity, count(*) as count").
|
|
Group("severity").
|
|
Find(&result).Error
|
|
return result, err
|
|
}
|
|
|
|
type TypeCount struct {
|
|
Type string `json:"type"`
|
|
Count int64 `json:"count"`
|
|
}
|
|
|
|
func (d *DB) AlertsByType() ([]TypeCount, error) {
|
|
var result []TypeCount
|
|
err := d.db.Model(&Alert{}).
|
|
Select("type, count(*) as count").
|
|
Group("type").
|
|
Find(&result).Error
|
|
return result, err
|
|
}
|
|
|
|
type IPCount struct {
|
|
SrcIP string `json:"src_ip"`
|
|
Count int64 `json:"count"`
|
|
}
|
|
|
|
func (d *DB) TopSourceIPs(limit int) ([]IPCount, error) {
|
|
var result []IPCount
|
|
err := d.db.Model(&Alert{}).
|
|
Select("src_ip, count(*) as count").
|
|
Group("src_ip").
|
|
Order("count desc").
|
|
Limit(limit).
|
|
Find(&result).Error
|
|
return result, err
|
|
}
|
|
|
|
type TimelinePoint struct {
|
|
Hour string `json:"hour"`
|
|
Count int64 `json:"count"`
|
|
}
|
|
|
|
func (d *DB) AlertsTimeline(hours int) ([]TimelinePoint, error) {
|
|
since := time.Now().UTC().Add(-time.Duration(hours) * time.Hour)
|
|
var result []TimelinePoint
|
|
err := d.db.Model(&Alert{}).
|
|
Select("strftime('%Y-%m-%dT%H:00:00Z', created_at) as hour, count(*) as count").
|
|
Where("created_at >= datetime(?)", since.Format("2006-01-02 15:04:05")).
|
|
Group("hour").
|
|
Order("hour asc").
|
|
Find(&result).Error
|
|
return result, err
|
|
}
|
|
|
|
func (d *DB) TotalAlerts() (int64, error) {
|
|
var count int64
|
|
err := d.db.Model(&Alert{}).Count(&count).Error
|
|
return count, err
|
|
}
|
|
|
|
type ProtocolCount struct {
|
|
Protocol string `json:"protocol"`
|
|
Count int64 `json:"count"`
|
|
Bytes int64 `json:"bytes"`
|
|
}
|
|
|
|
func (d *DB) TrafficByProtocol() ([]ProtocolCount, error) {
|
|
var result []ProtocolCount
|
|
err := d.db.Model(&PacketStat{}).
|
|
Select("protocol, count(*) as count, sum(bytes) as bytes").
|
|
Group("protocol").
|
|
Order("count desc").
|
|
Find(&result).Error
|
|
return result, err
|
|
}
|
|
|
|
func (d *DB) GetRecentPacketStats(limit int) ([]PacketStat, error) {
|
|
var stats []PacketStat
|
|
err := d.db.Order("created_at desc").Limit(limit).Find(&stats).Error
|
|
return stats, err
|
|
}
|
|
|
|
// ── Sessions ─────────────────────────────────────────────────────────────────
|
|
|
|
func (d *DB) CreateSession(s *CaptureSession) error {
|
|
return d.db.Create(s).Error
|
|
}
|
|
|
|
func (d *DB) UpdateSession(s *CaptureSession) error {
|
|
return d.db.Save(s).Error
|
|
}
|
|
|
|
func (d *DB) GetActiveSession() (*CaptureSession, error) {
|
|
var s CaptureSession
|
|
err := d.db.Where("status = ?", "running").First(&s).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &s, nil
|
|
}
|
|
|
|
func (d *DB) GetLastSession() (*CaptureSession, error) {
|
|
var s CaptureSession
|
|
err := d.db.Order("created_at desc").First(&s).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &s, nil
|
|
}
|
|
|
|
func (d *DB) GetAllSessions() ([]CaptureSession, error) {
|
|
var sessions []CaptureSession
|
|
err := d.db.Order("created_at desc").Limit(20).Find(&sessions).Error
|
|
return sessions, err
|
|
}
|