2026-09-23 09:40:59 +08:00
|
|
|
package traffic
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"database/sql"
|
|
|
|
|
"strings"
|
|
|
|
|
"time"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type Store struct {
|
|
|
|
|
db *sql.DB
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewStore(db *sql.DB) *Store {
|
|
|
|
|
return &Store{db: db}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *Store) LoadTotals(ctx context.Context) (map[string]hostTotals, error) {
|
|
|
|
|
rows, err := s.db.QueryContext(ctx, `SELECT host, upload_bytes, download_bytes FROM proxy_traffic`)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
defer rows.Close()
|
|
|
|
|
|
|
|
|
|
out := map[string]hostTotals{}
|
|
|
|
|
for rows.Next() {
|
|
|
|
|
var host string
|
|
|
|
|
var up, down int64
|
|
|
|
|
if err := rows.Scan(&host, &up, &down); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
key := strings.ToLower(strings.TrimSpace(host))
|
|
|
|
|
out[key] = hostTotals{Upload: up, Download: down}
|
|
|
|
|
}
|
|
|
|
|
return out, rows.Err()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *Store) SaveTotals(ctx context.Context, hosts map[string]hostTotals) error {
|
|
|
|
|
tx, err := s.db.BeginTx(ctx, nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
for host, totals := range hosts {
|
|
|
|
|
_, err := tx.ExecContext(ctx, `
|
|
|
|
|
INSERT INTO proxy_traffic(host, upload_bytes, download_bytes, updated_at)
|
|
|
|
|
VALUES (?, ?, ?, ?)
|
|
|
|
|
ON CONFLICT(host) DO UPDATE SET
|
|
|
|
|
upload_bytes = excluded.upload_bytes,
|
|
|
|
|
download_bytes = excluded.download_bytes,
|
|
|
|
|
updated_at = excluded.updated_at
|
|
|
|
|
`, host, totals.Upload, totals.Download, time.Now().UTC().Format(time.RFC3339))
|
|
|
|
|
if err != nil {
|
|
|
|
|
_ = tx.Rollback()
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return tx.Commit()
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-24 16:30:00 +08:00
|
|
|
// DeleteHosts 删除指定 host:port 的累计流量。
|
|
|
|
|
// 规则被删掉后这些行就成了孤儿:既没有界面能查到,又会在同名规则重建时被当成本规则的既有流量。
|
|
|
|
|
func (s *Store) DeleteHosts(ctx context.Context, keys []string) error {
|
|
|
|
|
if len(keys) == 0 {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
tx, err := s.db.BeginTx(ctx, nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
for _, key := range keys {
|
|
|
|
|
if _, err := tx.ExecContext(ctx,
|
|
|
|
|
`DELETE FROM proxy_traffic WHERE host = ?`, strings.ToLower(strings.TrimSpace(key))); err != nil {
|
|
|
|
|
_ = tx.Rollback()
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return tx.Commit()
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-23 09:40:59 +08:00
|
|
|
type hostTotals struct {
|
|
|
|
|
Upload int64
|
|
|
|
|
Download int64
|
|
|
|
|
}
|