feat(app): 添加流量统计功能和服务器配置管理
All checks were successful
Build Multi-Platform Binaries / build-frontend (push) Successful in 38s
Build Multi-Platform Binaries / build-binaries (amd64, linux, client, true) (push) Successful in 1m47s
Build Multi-Platform Binaries / build-binaries (amd64, darwin, server, false) (push) Successful in 1m48s
Build Multi-Platform Binaries / build-binaries (amd64, windows, client, true) (push) Successful in 1m29s
Build Multi-Platform Binaries / build-binaries (amd64, linux, server, true) (push) Successful in 1m53s
Build Multi-Platform Binaries / build-binaries (arm, 7, linux, client, true) (push) Successful in 1m14s
Build Multi-Platform Binaries / build-binaries (amd64, windows, server, true) (push) Successful in 1m40s
Build Multi-Platform Binaries / build-binaries (arm64, darwin, server, false) (push) Successful in 1m36s
Build Multi-Platform Binaries / build-binaries (arm, 7, linux, server, true) (push) Successful in 1m51s
Build Multi-Platform Binaries / build-binaries (arm64, linux, client, true) (push) Successful in 1m17s
Build Multi-Platform Binaries / build-binaries (arm64, linux, server, true) (push) Successful in 1m50s
Build Multi-Platform Binaries / build-binaries (arm64, windows, server, false) (push) Successful in 1m18s

- 在WebServer中添加TrafficStore存储接口
- 将Web配置从根级别移动到Server.Web子结构下
- 移除Web配置中的BindAddr字段并调整默认值逻辑
- 在前端HomeView中替换模拟流量数据显示真实统计数据
- 添加流量统计API接口(/traffic/stats和/traffic/hourly)
- 实现SQLite数据库流量统计表创建和CRUD操作
- 在Relay包中添加带流量统计的数据转发功能
- 在设置页面添加服务器配置编辑和保存功能
- 创建流量统计处理器和相关数据模型定义
This commit is contained in:
Flik
2026-01-22 19:53:40 +08:00
parent 06dfcfaff3
commit d1058f9e89
14 changed files with 634 additions and 74 deletions

View File

@@ -4,6 +4,7 @@ import (
"database/sql"
"encoding/json"
"sync"
"time"
_ "modernc.org/sqlite"
@@ -80,6 +81,33 @@ func (s *SQLiteStore) init() error {
// 迁移:添加 updated_at 列
s.db.Exec(`ALTER TABLE js_plugins ADD COLUMN updated_at DATETIME DEFAULT CURRENT_TIMESTAMP`)
// 创建流量统计表
_, err = s.db.Exec(`
CREATE TABLE IF NOT EXISTS traffic_stats (
hour_ts INTEGER PRIMARY KEY,
inbound INTEGER NOT NULL DEFAULT 0,
outbound INTEGER NOT NULL DEFAULT 0
)
`)
if err != nil {
return err
}
// 创建总流量表
_, err = s.db.Exec(`
CREATE TABLE IF NOT EXISTS traffic_total (
id INTEGER PRIMARY KEY CHECK (id = 1),
inbound INTEGER NOT NULL DEFAULT 0,
outbound INTEGER NOT NULL DEFAULT 0
)
`)
if err != nil {
return err
}
// 初始化总流量记录
s.db.Exec(`INSERT OR IGNORE INTO traffic_total (id, inbound, outbound) VALUES (1, 0, 0)`)
return nil
}
@@ -315,3 +343,82 @@ func (s *SQLiteStore) UpdateJSPluginConfig(name string, config map[string]string
_, err := s.db.Exec(`UPDATE js_plugins SET config = ?, updated_at = CURRENT_TIMESTAMP WHERE name = ?`, string(configJSON), name)
return err
}
// ========== 流量统计方法 ==========
// getHourTimestamp 获取当前小时的时间戳
func getHourTimestamp() int64 {
now := time.Now()
return time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), 0, 0, 0, now.Location()).Unix()
}
// AddTraffic 添加流量记录
func (s *SQLiteStore) AddTraffic(inbound, outbound int64) error {
s.mu.Lock()
defer s.mu.Unlock()
hourTs := getHourTimestamp()
// 更新小时统计
_, err := s.db.Exec(`
INSERT INTO traffic_stats (hour_ts, inbound, outbound) VALUES (?, ?, ?)
ON CONFLICT(hour_ts) DO UPDATE SET inbound = inbound + ?, outbound = outbound + ?
`, hourTs, inbound, outbound, inbound, outbound)
if err != nil {
return err
}
// 更新总流量
_, err = s.db.Exec(`
UPDATE traffic_total SET inbound = inbound + ?, outbound = outbound + ? WHERE id = 1
`, inbound, outbound)
return err
}
// GetTotalTraffic 获取总流量
func (s *SQLiteStore) GetTotalTraffic() (inbound, outbound int64, err error) {
s.mu.RLock()
defer s.mu.RUnlock()
err = s.db.QueryRow(`SELECT inbound, outbound FROM traffic_total WHERE id = 1`).Scan(&inbound, &outbound)
return
}
// Get24HourTraffic 获取24小时流量
func (s *SQLiteStore) Get24HourTraffic() (inbound, outbound int64, err error) {
s.mu.RLock()
defer s.mu.RUnlock()
cutoff := time.Now().Add(-24 * time.Hour).Unix()
err = s.db.QueryRow(`
SELECT COALESCE(SUM(inbound), 0), COALESCE(SUM(outbound), 0)
FROM traffic_stats WHERE hour_ts >= ?
`, cutoff).Scan(&inbound, &outbound)
return
}
// GetHourlyTraffic 获取每小时流量记录
func (s *SQLiteStore) GetHourlyTraffic(hours int) ([]TrafficRecord, error) {
s.mu.RLock()
defer s.mu.RUnlock()
cutoff := time.Now().Add(-time.Duration(hours) * time.Hour).Unix()
rows, err := s.db.Query(`
SELECT hour_ts, inbound, outbound FROM traffic_stats
WHERE hour_ts >= ? ORDER BY hour_ts ASC
`, cutoff)
if err != nil {
return nil, err
}
defer rows.Close()
var records []TrafficRecord
for rows.Next() {
var r TrafficRecord
if err := rows.Scan(&r.Timestamp, &r.Inbound, &r.Outbound); err != nil {
return nil, err
}
records = append(records, r)
}
return records, nil
}