feat(database): add CRUD operations for parsed sessions and update session name functionality

This commit is contained in:
kever
2026-02-16 00:39:24 +08:00
parent 41814a2bc8
commit f0a26e31f9
18 changed files with 1119 additions and 1573 deletions

View File

@@ -3,6 +3,7 @@ package service
import (
"context"
"fmt"
"log"
"sync"
"time"
@@ -17,54 +18,110 @@ type CaptureService struct {
logger *utils.Logger
packetCapture *capture.PacketCapture
processor *capture.TCPProcessor
parser *ParserService
beforeRemote func()
mutex sync.RWMutex
isCapturing bool
isStarting bool
pendingStop bool
dataChan chan *model.CaptureResult
errorChan chan error
}
func NewCaptureService(cfg *config.Config, logger *utils.Logger) *CaptureService {
func NewCaptureService(cfg *config.Config, logger *utils.Logger, parser *ParserService) *CaptureService {
return &CaptureService{
config: cfg,
logger: logger,
packetCapture: capture.NewPacketCapture(),
processor: capture.NewTCPProcessor(),
parser: parser,
dataChan: make(chan *model.CaptureResult, 100),
errorChan: make(chan error, 100),
}
}
// StartCapture 开始抓包
func (cs *CaptureService) StartCapture(ctx context.Context, config capture.Config) error {
func (cs *CaptureService) SetBeforeRemote(fn func()) {
cs.mutex.Lock()
defer cs.mutex.Unlock()
cs.beforeRemote = fn
}
func (cs *CaptureService) StartCaptureAsync(ctx context.Context, config capture.Config, onStarted func(), onError func(error)) error {
cs.mutex.Lock()
defer cs.mutex.Unlock()
if cs.isCapturing {
if cs.isCapturing || cs.isStarting {
return fmt.Errorf("capture already running")
}
if err := cs.packetCapture.Start(config); err != nil {
return fmt.Errorf("failed to start capture: %w", err)
}
cs.isStarting = true
cs.pendingStop = false
cs.isCapturing = true
cs.logger.Info("Packet capture started", "interface", config.InterfaceName)
cs.logger.Info("StartCapture requested",
"interface", config.InterfaceName,
"filter", config.Filter,
"timeout_ms", config.Timeout.Milliseconds(),
"buffer_size", config.BufferSize,
)
go func() {
if err := cs.packetCapture.Start(config); err != nil {
cs.logger.Error("Packet capture start failed", "error", err)
cs.mutex.Lock()
cs.isStarting = false
cs.mutex.Unlock()
if onError != nil {
onError(fmt.Errorf("failed to start capture: %w", err))
}
return
}
var shouldStop bool
cs.mutex.Lock()
cs.isStarting = false
cs.isCapturing = true
shouldStop = cs.pendingStop
cs.pendingStop = false
cs.mutex.Unlock()
cs.logger.Info("Packet capture started", "interface", config.InterfaceName)
if onStarted != nil {
onStarted()
}
if shouldStop {
_, _ = cs.StopAndParseCapture()
}
}()
// 启动数据处理协程
go cs.processData(ctx)
return nil
}
// StopCapture 停止抓包
func (cs *CaptureService) StopCapture() error {
cs.mutex.Lock()
defer cs.mutex.Unlock()
if cs.isStarting {
cs.pendingStop = true
cs.logger.Info("StopCapture queued while starting")
return nil
}
if !cs.isCapturing {
return fmt.Errorf("capture not running")
}
beforeStats := cs.packetCapture.GetStats()
cs.logger.Info("StopCapture requested",
"total_packets", beforeStats.TotalPackets,
"tcp_packets", beforeStats.TCPPackets,
"payload_packets", beforeStats.PayloadPackets,
"ack_groups", beforeStats.AckGroups,
"unique_payloads", beforeStats.UniquePayloads,
"raw_segments", beforeStats.RawSegments,
"final_buffers", beforeStats.FinalBuffers,
)
cs.packetCapture.Stop()
cs.isCapturing = false
cs.logger.Info("Packet capture stopped")
@@ -72,62 +129,139 @@ func (cs *CaptureService) StopCapture() error {
return nil
}
// GetCapturedData 获取抓包数据
func (cs *CaptureService) GetCapturedData() []string {
return cs.packetCapture.GetCapturedData()
}
// ProcessAllData 处理所有数据
func (cs *CaptureService) ProcessAllData() {
cs.packetCapture.ProcessAllData()
}
// IsCapturing 检查是否正在抓包
func (cs *CaptureService) IsCapturing() bool {
cs.mutex.RLock()
defer cs.mutex.RUnlock()
return cs.isCapturing
}
// processData 处理抓包数据
func (cs *CaptureService) IsStarting() bool {
cs.mutex.RLock()
defer cs.mutex.RUnlock()
return cs.isStarting
}
func (cs *CaptureService) processData(ctx context.Context) {
ticker := time.NewTicker(3 * time.Second)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
default:
// 这里可以添加实时数据处理逻辑
time.Sleep(100 * time.Millisecond)
case <-ticker.C:
if !cs.IsCapturing() {
continue
}
stats := cs.packetCapture.GetStats()
cs.logger.Info("Capture heartbeat",
"total_packets", stats.TotalPackets,
"tcp_packets", stats.TCPPackets,
"payload_packets", stats.PayloadPackets,
"ack_groups", stats.AckGroups,
"unique_payloads", stats.UniquePayloads,
"raw_segments", stats.RawSegments,
"final_buffers", stats.FinalBuffers,
)
}
}
}
// StopAndParseCapture 停止抓包并解析数据
func (cs *CaptureService) StopAndParseCapture(parser *ParserService) (*model.ParsedResult, error) {
func (cs *CaptureService) StopAndParseCapture() (*model.ParsedResult, error) {
log.Printf("[service] StopAndParseCapture enter")
cs.mutex.Lock()
defer cs.mutex.Unlock()
if cs.isStarting {
cs.pendingStop = true
cs.mutex.Unlock()
log.Printf("[service] StopAndParseCapture queued while starting")
return nil, fmt.Errorf("capture starting")
}
if !cs.isCapturing {
cs.mutex.Unlock()
log.Printf("[service] StopAndParseCapture exit: not running")
return nil, fmt.Errorf("capture not running")
}
cs.mutex.Unlock()
beforeStop := cs.packetCapture.GetStats()
cs.logger.Info("StopAndParseCapture requested",
"total_packets", beforeStop.TotalPackets,
"tcp_packets", beforeStop.TCPPackets,
"payload_packets", beforeStop.PayloadPackets,
"ack_groups", beforeStop.AckGroups,
"unique_payloads", beforeStop.UniquePayloads,
"raw_segments", beforeStop.RawSegments,
"final_buffers", beforeStop.FinalBuffers,
)
log.Printf("[service] StopAndParseCapture stopping packet capture")
cs.packetCapture.Stop()
log.Printf("[service] StopAndParseCapture packet capture stopped")
cs.isCapturing = false
cs.logger.Info("Packet capture stopped (StopAndParseCapture)")
// 处理所有收集的数据
cs.packetCapture.ProcessAllData()
afterProcess := cs.packetCapture.GetStats()
cs.logger.Info("ProcessAllData finished",
"total_packets", afterProcess.TotalPackets,
"tcp_packets", afterProcess.TCPPackets,
"payload_packets", afterProcess.PayloadPackets,
"ack_groups", afterProcess.AckGroups,
"unique_payloads", afterProcess.UniquePayloads,
"raw_segments", afterProcess.RawSegments,
"final_buffers", afterProcess.FinalBuffers,
)
// 获取抓包数据
rawData := cs.packetCapture.GetCapturedData()
if len(rawData) == 0 {
port5222 := cs.packetCapture.GetPortCount(5222)
port3333 := cs.packetCapture.GetPortCount(3333)
if port5222 == 0 && port3333 == 0 {
cs.logger.Warn("No target port data after processing",
"port_5222", port5222,
"port_3333", port3333,
)
return nil, fmt.Errorf("no captured data")
}
// 解析数据
result, _, err := parser.ParseHexData(rawData)
if afterProcess.FinalBuffers == 0 {
cs.logger.Warn("No captured data after processing", "hint", "check interface and bpf filter")
return nil, fmt.Errorf("no captured data")
}
rawData := cs.packetCapture.GetCapturedData()
cs.logger.Info("Captured raw data snapshot", "hex_chunks", len(rawData))
if len(rawData) == 0 {
cs.logger.Warn("No captured data after stop", "hint", "check interface and bpf filter")
return nil, fmt.Errorf("no captured data")
}
parseStart := time.Now()
cs.logger.Info("ParseHexData starting",
"port_5222", port5222,
"port_3333", port3333,
"final_buffers", afterProcess.FinalBuffers,
"hex_chunks", len(rawData),
)
cs.mutex.RLock()
beforeRemote := cs.beforeRemote
cs.mutex.RUnlock()
if beforeRemote != nil {
beforeRemote()
}
result, _, err := cs.parser.ParseHexData(rawData)
cs.logger.Info("ParseHexData finished", "duration_ms", time.Since(parseStart).Milliseconds())
if err != nil {
cs.logger.Error("ParseHexData failed", "error", err, "hex_chunks", len(rawData))
return nil, fmt.Errorf("解析数据失败: %v", err)
}
cs.logger.Info("ParseHexData succeeded", "items", len(result.Items), "heroes", len(result.Heroes))
return result, nil
}

View File

@@ -45,6 +45,44 @@ func (s *DatabaseService) GetLatestParsedDataFromDatabase() (string, string, err
return itemsJSON, heroesJSON, nil
}
// GetParsedSessions 从数据库获取所有解析会话
func (s *DatabaseService) GetParsedSessions() ([]model.ParsedSession, error) {
sessions, err := s.db.GetParsedSessions()
if err != nil {
s.logger.Error("从数据库获取解析会话失败", "error", err)
return nil, fmt.Errorf("获取解析会话失败: %w", err)
}
return sessions, nil
}
// GetParsedDataByID 从数据库获取指定会话数据
func (s *DatabaseService) GetParsedDataByID(id int64) (string, string, error) {
itemsJSON, heroesJSON, err := s.db.GetParsedDataByID(id)
if err != nil {
s.logger.Error("从数据库获取解析数据失败", "error", err, "id", id)
return "", "", fmt.Errorf("获取解析数据失败: %w", err)
}
return itemsJSON, heroesJSON, nil
}
// UpdateParsedSessionName 更新解析会话名称
func (s *DatabaseService) UpdateParsedSessionName(id int64, name string) error {
if err := s.db.UpdateParsedSessionName(id, name); err != nil {
s.logger.Error("更新解析会话名称失败", "error", err, "id", id)
return fmt.Errorf("更新解析会话名称失败: %w", err)
}
return nil
}
// DeleteParsedSession 删除解析会话
func (s *DatabaseService) DeleteParsedSession(id int64) error {
if err := s.db.DeleteParsedSession(id); err != nil {
s.logger.Error("删除解析会话失败", "error", err, "id", id)
return fmt.Errorf("删除解析会话失败: %w", err)
}
return nil
}
// SaveAppSetting 保存应用设置
func (s *DatabaseService) SaveAppSetting(key, value string) error {
err := s.db.SaveSetting(key, value)
@@ -78,5 +116,3 @@ func (s *DatabaseService) GetAllAppSettings() (map[string]string, error) {
return settings, nil
}

View File

@@ -4,15 +4,18 @@ import (
"context"
"encoding/json"
"fmt"
"log"
"time"
"equipment-analyzer/internal/capture"
"equipment-analyzer/internal/config"
"equipment-analyzer/internal/model"
"equipment-analyzer/internal/utils"
"github.com/wailsapp/wails/v2/pkg/runtime"
)
type App struct {
ctx context.Context
config *config.Config
logger *utils.Logger
captureService *CaptureService
@@ -22,217 +25,228 @@ type App struct {
}
func NewApp(cfg *config.Config, logger *utils.Logger) *App {
// 初始化数据库
// init database
database, err := model.NewDatabase()
if err != nil {
logger.Error("初始化数据库失败", "error", err)
// 如果数据库初始化失败,仍然创建应用,但数据库功能不可用
logger.Error("database init failed", "error", err)
// allow app to run without db features
parserService := NewParserService(cfg, logger)
return &App{
config: cfg,
logger: logger,
captureService: NewCaptureService(cfg, logger),
parserService: NewParserService(cfg, logger),
captureService: NewCaptureService(cfg, logger, parserService),
parserService: parserService,
}
}
databaseService := NewDatabaseService(database, logger)
parserService := NewParserService(cfg, logger)
return &App{
config: cfg,
logger: logger,
captureService: NewCaptureService(cfg, logger),
parserService: NewParserService(cfg, logger),
captureService: NewCaptureService(cfg, logger, parserService),
parserService: parserService,
database: database,
databaseService: databaseService,
}
}
func (a *App) Startup(ctx context.Context) {
a.logger.Info("应用启动")
a.ctx = ctx
a.captureService.SetBeforeRemote(func() {
if a.ctx != nil {
runtime.EventsEmit(a.ctx, "capture:ready_to_parse")
}
})
a.logger.Info("app startup")
}
func (a *App) DomReady(ctx context.Context) {
a.logger.Info("DOM准备就绪")
a.logger.Info("dom ready")
}
func (a *App) BeforeClose(ctx context.Context) (prevent bool) {
a.logger.Info("应用即将关闭")
a.logger.Info("app closing")
return false
}
func (a *App) Shutdown(ctx context.Context) {
a.logger.Info("应用关闭")
a.logger.Info("app shutdown")
// 关闭数据库连接
if a.database != nil {
if err := a.database.Close(); err != nil {
a.logger.Error("关闭数据库连接失败", "error", err)
a.logger.Error("failed to close database connection", "error", err)
} else {
a.logger.Info("数据库连接已关闭")
a.logger.Info("database connection closed")
}
}
}
// GetNetworkInterfaces 获取网络接口列表
// GetNetworkInterfaces returns available network interfaces.
func (a *App) GetNetworkInterfaces() ([]model.NetworkInterface, error) {
interfaces, err := capture.GetNetworkInterfaces()
if err != nil {
a.logger.Error("获取网络接口失败", "error", err)
a.logger.Error("get network interfaces failed", "error", err)
return nil, err
}
return interfaces, nil
}
// StartCapture 开始抓包
// StartCapture starts capture on the given interface.
func (a *App) StartCapture(interfaceName string) error {
if a.captureService.IsCapturing() {
return fmt.Errorf("抓包已在进行中")
return fmt.Errorf("capture already running")
}
timeoutMs := a.config.Capture.DefaultTimeout
if timeoutMs > 500 {
log.Printf("[service] capture timeout too high (%dms), clamp to 500ms for responsive stop", timeoutMs)
timeoutMs = 500
}
config := capture.Config{
InterfaceName: interfaceName,
Filter: a.config.Capture.DefaultFilter,
Timeout: time.Duration(a.config.Capture.DefaultTimeout) * time.Millisecond,
Timeout: time.Duration(timeoutMs) * time.Millisecond,
BufferSize: a.config.Capture.BufferSize,
}
err := a.captureService.StartCapture(context.Background(), config)
err := a.captureService.StartCaptureAsync(context.Background(), config, func() {
if a.ctx != nil {
runtime.EventsEmit(a.ctx, "capture:started")
}
}, func(err error) {
a.logger.Error("start capture failed", "error", err)
if a.ctx != nil {
runtime.EventsEmit(a.ctx, "capture:start_failed", err.Error())
}
})
if err != nil {
a.logger.Error("开始抓包失败", "error", err)
return err
}
a.logger.Info("抓包开始", "interface", interfaceName)
a.logger.Info("capture start requested", "interface", interfaceName)
return nil
}
// StopCapture 停止抓包
// StopCapture stops capture.
func (a *App) StopCapture() error {
if !a.captureService.IsCapturing() {
return fmt.Errorf("没有正在进行的抓包")
return fmt.Errorf("capture not running")
}
err := a.captureService.StopCapture()
if err != nil {
a.logger.Error("停止抓包失败", "error", err)
a.logger.Error("stop capture failed", "error", err)
return err
}
// 处理所有收集的数据
a.captureService.ProcessAllData()
a.logger.Info("抓包停止")
a.logger.Info("capture stopped")
return nil
}
// GetCapturedData 获取抓包数据
// GetCapturedData returns raw captured data.
func (a *App) GetCapturedData() ([]string, error) {
return a.captureService.GetCapturedData(), nil
}
// ParseData 解析数据为JSON
// ParseData parses captured data to JSON (remote parser).
func (a *App) ParseData(hexDataList []string) (string, error) {
_, rawJson, err := a.parserService.ParseHexData(hexDataList)
if err != nil {
a.logger.Error("解析数据失败", "error", err)
a.logger.Error("parse data failed", "error", err)
return "", err
}
return rawJson, nil
}
// ExportData 导出数据到文件
// ExportData exports data to a file.
func (a *App) ExportData(hexDataList []string, filename string) error {
result, rawJson, err := a.parserService.ParseHexData(hexDataList)
if err != nil {
a.logger.Error("解析数据失败", "error", err)
a.logger.Error("parse data failed", "error", err)
return err
}
// 这里可以添加文件写入逻辑
a.logger.Info("导出数据", "filename", filename, "count", len(result.Items))
// 简单示例:写入到当前目录
a.logger.Info("export data", "filename", filename, "count", len(result.Items))
err = utils.WriteFile(filename, []byte(rawJson))
if err != nil {
a.logger.Error("写入文件失败", "error", err)
a.logger.Error("write file failed", "error", err)
return err
}
return nil
}
// ExportCurrentData 导出当前数据库中的数据到文件
// ExportCurrentData exports latest data from database to a file.
func (a *App) ExportCurrentData(filename string) error {
if a.databaseService == nil {
return fmt.Errorf("数据库服务未初始化")
return fmt.Errorf("database service not initialized")
}
// 从数据库获取最新数据
parsedResult, err := a.GetLatestParsedDataFromDatabase()
if err != nil {
a.logger.Error("获取数据库数据失败", "error", err)
a.logger.Error("failed to load data from database", "error", err)
return err
}
if parsedResult == nil || (len(parsedResult.Items) == 0 && len(parsedResult.Heroes) == 0) {
return fmt.Errorf("没有数据可导出")
return fmt.Errorf("no data to export")
}
// 创建导出数据格式
exportData := map[string]interface{}{
"items": parsedResult.Items,
"heroes": parsedResult.Heroes,
}
// 序列化为JSON
jsonData, err := json.MarshalIndent(exportData, "", " ")
if err != nil {
a.logger.Error("序列化数据失败", "error", err)
a.logger.Error("failed to marshal data", "error", err)
return err
}
// 写入文件
err = utils.WriteFile(filename, jsonData)
if err != nil {
a.logger.Error("写入文件失败", "error", err)
a.logger.Error("write file failed", "error", err)
return err
}
a.logger.Info("数据导出成功", "filename", filename, "items_count", len(parsedResult.Items), "heroes_count", len(parsedResult.Heroes))
a.logger.Info("export current data succeeded", "filename", filename, "items_count", len(parsedResult.Items), "heroes_count", len(parsedResult.Heroes))
return nil
}
// GetCurrentDataForExport 获取当前数据库中的数据,供前端导出使用
// GetCurrentDataForExport returns latest data from database as JSON string.
func (a *App) GetCurrentDataForExport() (string, error) {
if a.databaseService == nil {
return "", fmt.Errorf("数据库服务未初始化")
return "", fmt.Errorf("database service not initialized")
}
// 从数据库获取最新数据
parsedResult, err := a.GetLatestParsedDataFromDatabase()
if err != nil {
a.logger.Error("获取数据库数据失败", "error", err)
a.logger.Error("failed to load data from database", "error", err)
return "", err
}
if parsedResult == nil || (len(parsedResult.Items) == 0 && len(parsedResult.Heroes) == 0) {
return "", fmt.Errorf("没有数据可导出")
return "", fmt.Errorf("no data to export")
}
// 创建导出数据格式
exportData := map[string]interface{}{
"items": parsedResult.Items,
"heroes": parsedResult.Heroes,
}
// 序列化为JSON
jsonData, err := json.MarshalIndent(exportData, "", " ")
if err != nil {
a.logger.Error("序列化数据失败", "error", err)
a.logger.Error("failed to marshal data", "error", err)
return "", err
}
return string(jsonData), nil
}
// GetCaptureStatus 获取抓包状态
// GetCaptureStatus returns capture status.
func (a *App) GetCaptureStatus() model.CaptureStatus {
return model.CaptureStatus{
IsCapturing: a.captureService.IsCapturing(),
@@ -242,27 +256,29 @@ func (a *App) GetCaptureStatus() model.CaptureStatus {
func (a *App) getStatusMessage() string {
if a.captureService.IsCapturing() {
return "正在抓包..."
return "capturing"
}
return "准备就绪"
if a.captureService.IsStarting() {
return "starting"
}
return "ready"
}
// ReadRawJsonFile 已废弃,请使用GetLatestParsedDataFromDatabase从数据库获取数据
// ReadRawJsonFile is deprecated; use GetLatestParsedDataFromDatabase.
func (a *App) ReadRawJsonFile() (*model.ParsedResult, error) {
return a.GetLatestParsedDataFromDatabase()
}
// StopAndParseCapture 停止抓包并解析数据,供前端调用
// StopAndParseCapture stops capture and parses data.
func (a *App) StopAndParseCapture() (*model.ParsedResult, error) {
result, err := a.captureService.StopAndParseCapture(a.parserService)
log.Printf("[service] StopAndParseCapture entry")
result, err := a.captureService.StopAndParseCapture()
if err != nil {
a.logger.Error("停止抓包并解析数据失败", "error", err)
a.logger.Error("stop and parse capture failed", "error", err)
return nil, err
}
// 将解析结果保存到数据库
if a.databaseService != nil && result != nil {
// 序列化装备数据
itemsJSON := "[]"
if result.Items != nil {
if jsonData, err := json.Marshal(result.Items); err == nil {
@@ -270,7 +286,6 @@ func (a *App) StopAndParseCapture() (*model.ParsedResult, error) {
}
}
// 序列化英雄数据
heroesJSON := "[]"
if result.Heroes != nil {
if jsonData, err := json.Marshal(result.Heroes); err == nil {
@@ -278,33 +293,29 @@ func (a *App) StopAndParseCapture() (*model.ParsedResult, error) {
}
}
// 保存到数据库
sessionName := fmt.Sprintf("capture_%d", time.Now().Unix())
if err := a.databaseService.SaveParsedDataToDatabase(sessionName, itemsJSON, heroesJSON); err != nil {
a.logger.Error("保存解析数据到数据库失败", "error", err)
// 不返回错误,因为解析成功了,只是保存失败
a.logger.Error("save parsed data failed", "error", err)
} else {
a.logger.Info("解析数据已保存到数据库", "session_name", sessionName)
a.logger.Info("parsed data saved", "session_name", sessionName)
}
}
return result, nil
}
// ========== 数据库相关API ==========
// SaveParsedDataToDatabase 保存解析后的数据到数据库
// SaveParsedDataToDatabase saves parsed data.
func (a *App) SaveParsedDataToDatabase(sessionName string, itemsJSON, heroesJSON string) error {
if a.databaseService == nil {
return fmt.Errorf("数据库服务未初始化")
return fmt.Errorf("database service not initialized")
}
return a.databaseService.SaveParsedDataToDatabase(sessionName, itemsJSON, heroesJSON)
}
// GetLatestParsedDataFromDatabase 从数据库获取最新的解析数据
// GetLatestParsedDataFromDatabase returns latest parsed data from database.
func (a *App) GetLatestParsedDataFromDatabase() (*model.ParsedResult, error) {
if a.databaseService == nil {
return nil, fmt.Errorf("数据库服务未初始化")
return nil, fmt.Errorf("database service not initialized")
}
itemsJSON, heroesJSON, err := a.databaseService.GetLatestParsedDataFromDatabase()
@@ -312,19 +323,17 @@ func (a *App) GetLatestParsedDataFromDatabase() (*model.ParsedResult, error) {
return nil, err
}
// 解析装备数据
var items []interface{}
if itemsJSON != "" {
if err := json.Unmarshal([]byte(itemsJSON), &items); err != nil {
return nil, fmt.Errorf("解析装备数据失败: %w", err)
return nil, fmt.Errorf("failed to unmarshal items: %w", err)
}
}
// 解析英雄数据
var heroes []interface{}
if heroesJSON != "" {
if err := json.Unmarshal([]byte(heroesJSON), &heroes); err != nil {
return nil, fmt.Errorf("解析英雄数据失败: %w", err)
return nil, fmt.Errorf("failed to unmarshal heroes: %w", err)
}
}
@@ -334,26 +343,125 @@ func (a *App) GetLatestParsedDataFromDatabase() (*model.ParsedResult, error) {
}, nil
}
// SaveAppSetting 保存应用设置
// GetParsedSessions returns all parsed sessions.
func (a *App) GetParsedSessions() ([]model.ParsedSession, error) {
if a.databaseService == nil {
return nil, fmt.Errorf("database service not initialized")
}
return a.databaseService.GetParsedSessions()
}
// GetParsedDataByID returns parsed data by session id.
func (a *App) GetParsedDataByID(id int64) (*model.ParsedResult, error) {
if a.databaseService == nil {
return nil, fmt.Errorf("database service not initialized")
}
itemsJSON, heroesJSON, err := a.databaseService.GetParsedDataByID(id)
if err != nil {
return nil, err
}
var items []interface{}
if itemsJSON != "" {
if err := json.Unmarshal([]byte(itemsJSON), &items); err != nil {
return nil, fmt.Errorf("failed to unmarshal items: %w", err)
}
}
var heroes []interface{}
if heroesJSON != "" {
if err := json.Unmarshal([]byte(heroesJSON), &heroes); err != nil {
return nil, fmt.Errorf("failed to unmarshal heroes: %w", err)
}
}
return &model.ParsedResult{
Items: items,
Heroes: heroes,
}, nil
}
// UpdateParsedSessionName updates session name.
func (a *App) UpdateParsedSessionName(id int64, name string) error {
if a.databaseService == nil {
return fmt.Errorf("database service not initialized")
}
if name == "" {
return fmt.Errorf("session name cannot be empty")
}
return a.databaseService.UpdateParsedSessionName(id, name)
}
// DeleteParsedSession deletes a parsed session by id.
func (a *App) DeleteParsedSession(id int64) error {
if a.databaseService == nil {
return fmt.Errorf("database service not initialized")
}
return a.databaseService.DeleteParsedSession(id)
}
// SaveAppSetting saves app setting.
func (a *App) SaveAppSetting(key, value string) error {
if a.databaseService == nil {
return fmt.Errorf("数据库服务未初始化")
return fmt.Errorf("database service not initialized")
}
return a.databaseService.SaveAppSetting(key, value)
}
// GetAppSetting 获取应用设置
// GetAppSetting gets app setting.
func (a *App) GetAppSetting(key string) (string, error) {
if a.databaseService == nil {
return "", fmt.Errorf("数据库服务未初始化")
return "", fmt.Errorf("database service not initialized")
}
return a.databaseService.GetAppSetting(key)
}
// GetAllAppSettings 获取所有应用设置
// GetAllAppSettings gets all app settings.
func (a *App) GetAllAppSettings() (map[string]string, error) {
if a.databaseService == nil {
return nil, fmt.Errorf("数据库服务未初始化")
return nil, fmt.Errorf("database service not initialized")
}
return a.databaseService.GetAllAppSettings()
}
// StartCaptureWithFilter allows frontend to provide a custom BPF filter.
func (a *App) StartCaptureWithFilter(interfaceName string, filter string) error {
a.logger.Info("StartCaptureWithFilter requested", "interface", interfaceName, "filter", filter)
if a.captureService.IsCapturing() {
return fmt.Errorf("capture already running")
}
useFilter := filter
if useFilter == "" {
useFilter = a.config.Capture.DefaultFilter
}
timeoutMs := a.config.Capture.DefaultTimeout
if timeoutMs > 500 {
log.Printf("[service] capture timeout too high (%dms), clamp to 500ms for responsive stop", timeoutMs)
timeoutMs = 500
}
config := capture.Config{
InterfaceName: interfaceName,
Filter: useFilter,
Timeout: time.Duration(timeoutMs) * time.Millisecond,
BufferSize: a.config.Capture.BufferSize,
}
err := a.captureService.StartCaptureAsync(context.Background(), config, func() {
if a.ctx != nil {
runtime.EventsEmit(a.ctx, "capture:started")
}
}, func(err error) {
a.logger.Error("start capture failed", "error", err)
if a.ctx != nil {
runtime.EventsEmit(a.ctx, "capture:start_failed", err.Error())
}
})
if err != nil {
return err
}
a.logger.Info("capture start requested", "interface", interfaceName, "filter", useFilter)
return nil
}