mirror of
https://github.com/komari-monitor/komari.git
synced 2026-11-11 00:30:54 +00:00
Compare commits
10
Commits
0.1.2-fix2
...
0.1.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b80c917f25 | ||
|
|
6b74bf90ce | ||
|
|
a2e4665ff5 | ||
|
|
2c9bd9c847 | ||
|
|
54eb957618 | ||
|
|
370f0ff815 | ||
|
|
9bd9a280e5 | ||
|
|
daf6ae291f | ||
|
|
3d774eec81 | ||
|
|
129e0be151 |
No files matched your search
+1
-1
@@ -61,7 +61,7 @@ func SaveClientReportToDB() error {
|
||||
|
||||
// 计算平均报告并添加到记录列表
|
||||
if len(filtered) > 0 {
|
||||
r := utils.AverageReport(uuid, time.Now(), filtered)
|
||||
r := utils.AverageReport(uuid, time.Now(), filtered, 0.3)
|
||||
records = append(records, r)
|
||||
}
|
||||
}
|
||||
|
||||
+20
-11
@@ -4,14 +4,13 @@ import (
|
||||
"archive/zip"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"log"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/komari-monitor/komari/api"
|
||||
"github.com/komari-monitor/komari/cmd/flags"
|
||||
@@ -80,24 +79,35 @@ func DownloadBackup(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// 如果数据库文件不在 ./data 目录下,单独添加
|
||||
dbFilePath := flags.DatabaseFile
|
||||
dbFileName := filepath.Base(dbFilePath)
|
||||
// 获取 ./data 和数据库文件的绝对路径以进行可靠的比较
|
||||
dataAbsPath, err := filepath.Abs("./data")
|
||||
if err != nil {
|
||||
api.RespondError(c, http.StatusInternalServerError, fmt.Sprintf("Error getting absolute path for data directory: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(filepath.Clean(dbFilePath), filepath.Clean("./data")) {
|
||||
// 不在 ./data,单独添加
|
||||
dbFilePath := flags.DatabaseFile
|
||||
dbAbsPath, err := filepath.Abs(dbFilePath)
|
||||
if err != nil {
|
||||
// 如果无法解析数据库路径,可能是一个配置问题,但我们记录并跳过,避免备份失败
|
||||
log.Printf("Could not determine absolute path for database file '%s', skipping explicit addition. Error: %v\n", dbFilePath, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 如果数据库文件的绝对路径不是以 ./data 目录的绝对路径开头,则单独添加它
|
||||
if !strings.HasPrefix(dbAbsPath, dataAbsPath) {
|
||||
dbFileName := filepath.Base(dbFilePath)
|
||||
dbInfo, err := os.Stat(dbFilePath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
log.Printf("Database file '%s' does not exist, skipping.\n", dbFilePath)
|
||||
// 数据库文件不存在不影响主流程
|
||||
return
|
||||
return // 数据库不存在是正常情况,直接返回
|
||||
}
|
||||
api.RespondError(c, http.StatusInternalServerError, fmt.Sprintf("Error stating database file: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
if !dbInfo.IsDir() { // 确保是文件
|
||||
if !dbInfo.IsDir() {
|
||||
file, err := os.Open(dbFilePath)
|
||||
if err != nil {
|
||||
api.RespondError(c, http.StatusInternalServerError, fmt.Sprintf("Error opening database file: %v", err))
|
||||
@@ -105,7 +115,6 @@ func DownloadBackup(c *gin.Context) {
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// 直接用文件名放在 zip 根目录
|
||||
writer, err := zipWriter.CreateHeader(&zip.FileHeader{
|
||||
Name: dbFileName,
|
||||
Method: zip.Deflate,
|
||||
|
||||
@@ -38,7 +38,7 @@ func TestGeoIp(c *gin.Context) {
|
||||
api.RespondError(c, 400, "GeoIP is not enabled in the configuration.")
|
||||
return
|
||||
}
|
||||
GeoIpRecord, err := geoip.CurrentProvider.GetGeoInfo(net.ParseIP(ip))
|
||||
GeoIpRecord, err := geoip.GetGeoInfo(net.ParseIP(ip))
|
||||
if err != nil {
|
||||
api.RespondError(c, 500, "Failed to get GeoIP record: "+err.Error())
|
||||
return
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
)
|
||||
|
||||
func UpdateMmdbGeoIP(c *gin.Context) {
|
||||
if err := geoip.CurrentProvider.UpdateDatabase(); err != nil {
|
||||
if err := geoip.UpdateDatabase(); err != nil {
|
||||
api.RespondError(c, 500, "Failed to update GeoIP database "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -29,13 +29,13 @@ func UploadBasicInfo(c *gin.Context) {
|
||||
if cfg, err := config.Get(); err == nil && cfg.GeoIpEnabled {
|
||||
if ipv4, ok := cbi["ipv4"].(string); ok && ipv4 != "" {
|
||||
ip4 := net.ParseIP(ipv4)
|
||||
ip4_record, _ := geoip.CurrentProvider.GetGeoInfo(ip4)
|
||||
ip4_record, _ := geoip.GetGeoInfo(ip4)
|
||||
if ip4_record != nil {
|
||||
cbi["region"] = geoip.GetRegionUnicodeEmoji(ip4_record.ISOCode)
|
||||
}
|
||||
} else if ipv6, ok := cbi["ipv6"].(string); ok && ipv6 != "" {
|
||||
ip6 := net.ParseIP(ipv6)
|
||||
ip6_record, _ := geoip.CurrentProvider.GetGeoInfo(ip6)
|
||||
ip6_record, _ := geoip.GetGeoInfo(ip6)
|
||||
if ip6_record != nil {
|
||||
cbi["region"] = geoip.GetRegionUnicodeEmoji(ip6_record.ISOCode)
|
||||
}
|
||||
|
||||
+8
-8
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func getEnv(key, defaultValue string) string {
|
||||
func GetEnv(key, defaultValue string) string {
|
||||
value := os.Getenv(key)
|
||||
if value == "" {
|
||||
return defaultValue
|
||||
@@ -19,13 +19,13 @@ func getEnv(key, defaultValue string) string {
|
||||
|
||||
// 从环境变量获取默认值
|
||||
var (
|
||||
dbTypeEnv = getEnv("KOMARI_DB_TYPE", "sqlite")
|
||||
dbFileEnv = getEnv("KOMARI_DB_FILE", "./data/komari.db")
|
||||
dbHostEnv = getEnv("KOMARI_DB_HOST", "localhost")
|
||||
dbPortEnv = getEnv("KOMARI_DB_PORT", "3306")
|
||||
dbUserEnv = getEnv("KOMARI_DB_USER", "root")
|
||||
dbPassEnv = getEnv("KOMARI_DB_PASS", "")
|
||||
dbNameEnv = getEnv("KOMARI_DB_NAME", "komari")
|
||||
dbTypeEnv = GetEnv("KOMARI_DB_TYPE", "sqlite")
|
||||
dbFileEnv = GetEnv("KOMARI_DB_FILE", "./data/komari.db")
|
||||
dbHostEnv = GetEnv("KOMARI_DB_HOST", "localhost")
|
||||
dbPortEnv = GetEnv("KOMARI_DB_PORT", "3306")
|
||||
dbUserEnv = GetEnv("KOMARI_DB_USER", "root")
|
||||
dbPassEnv = GetEnv("KOMARI_DB_PASS", "")
|
||||
dbNameEnv = GetEnv("KOMARI_DB_NAME", "komari")
|
||||
)
|
||||
|
||||
var RootCmd = &cobra.Command{
|
||||
|
||||
+12
-1
@@ -6,6 +6,7 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
@@ -29,6 +30,7 @@ import (
|
||||
"github.com/komari-monitor/komari/database/tasks"
|
||||
"github.com/komari-monitor/komari/public"
|
||||
"github.com/komari-monitor/komari/utils"
|
||||
"github.com/komari-monitor/komari/utils/cloudflared"
|
||||
"github.com/komari-monitor/komari/utils/geoip"
|
||||
"github.com/komari-monitor/komari/utils/messageSender"
|
||||
u_notification "github.com/komari-monitor/komari/utils/notification"
|
||||
@@ -57,6 +59,13 @@ var ServerCmd = &cobra.Command{
|
||||
go DoScheduledWork()
|
||||
go messageSender.Initialize()
|
||||
|
||||
if strings.ToLower(GetEnv("KOMARI_ENABLE_CLOUDFLARED", "false")) == "true" {
|
||||
err := cloudflared.RunCloudflared() // 阻塞,确保cloudflared跑起来
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to run cloudflared: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
r := gin.Default()
|
||||
|
||||
// 动态 CORS 中间件
|
||||
@@ -259,7 +268,7 @@ var ServerCmd = &cobra.Command{
|
||||
|
||||
func init() {
|
||||
// 从环境变量获取监听地址
|
||||
listenAddr := getEnv("KOMARI_LISTEN", "0.0.0.0:25774")
|
||||
listenAddr := GetEnv("KOMARI_LISTEN", "0.0.0.0:25774")
|
||||
ServerCmd.PersistentFlags().StringVarP(&flags.Listen, "listen", "l", listenAddr, "监听地址 [env: KOMARI_LISTEN]")
|
||||
RootCmd.AddCommand(ServerCmd)
|
||||
}
|
||||
@@ -317,8 +326,10 @@ func DoScheduledWork() {
|
||||
|
||||
func OnShutdown() {
|
||||
logOperation.Log("", "", "server is shutting down", "info")
|
||||
cloudflared.Kill()
|
||||
}
|
||||
|
||||
func OnFatal(err error) {
|
||||
logOperation.Log("", "", "server encountered a fatal error: "+err.Error(), "error")
|
||||
cloudflared.Kill()
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package cloudflared
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func downloadCloudflared(downloadURL, filePath string) error {
|
||||
log.Println("downloading cloudflared...")
|
||||
resp, err := http.Get(downloadURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
out, err := os.Create(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = io.Copy(out, resp.Body)
|
||||
closeErr := out.Close() // 先关闭文件
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if closeErr != nil {
|
||||
return closeErr
|
||||
}
|
||||
if runtime.GOOS != "windows" {
|
||||
err = os.Chmod(filePath, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var cloudflaredCmd *exec.Cmd
|
||||
|
||||
func RunCloudflared() error {
|
||||
var (
|
||||
downloadURL string
|
||||
fileName = "cloudflared"
|
||||
)
|
||||
if _, err := os.Stat("data"); os.IsNotExist(err) {
|
||||
os.Mkdir("data", 0755)
|
||||
}
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
fileName = "cloudflared.exe"
|
||||
switch runtime.GOARCH {
|
||||
case "amd64":
|
||||
downloadURL = "https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-windows-amd64.exe"
|
||||
case "386":
|
||||
downloadURL = "https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-windows-386.exe"
|
||||
default:
|
||||
return fmt.Errorf("unsupported architecture: %s", runtime.GOARCH)
|
||||
}
|
||||
case "linux":
|
||||
switch runtime.GOARCH {
|
||||
case "amd64":
|
||||
downloadURL = "https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64"
|
||||
case "386":
|
||||
downloadURL = "https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-386"
|
||||
case "arm":
|
||||
downloadURL = "https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-arm"
|
||||
case "arm64":
|
||||
downloadURL = "https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-arm64"
|
||||
default:
|
||||
return fmt.Errorf("unsupported architecture: %s", runtime.GOARCH)
|
||||
}
|
||||
case "darwin":
|
||||
switch runtime.GOARCH {
|
||||
case "amd64":
|
||||
downloadURL = "https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-darwin-amd64.tgz"
|
||||
case "arm64":
|
||||
downloadURL = "https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-darwin-arm64.tgz"
|
||||
default:
|
||||
return fmt.Errorf("unsupported architecture: %s", runtime.GOARCH)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unsupported os: %s", runtime.GOOS)
|
||||
}
|
||||
|
||||
filePath := "data/" + fileName
|
||||
if _, err := os.Stat(filePath); os.IsNotExist(err) {
|
||||
err := downloadCloudflared(downloadURL, filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
args := []string{"tunnel", "--no-autoupdate", "run", "--token"}
|
||||
token := os.Getenv("KOMARI_CLOUDFLARED_TOKEN")
|
||||
args = append(args, token)
|
||||
|
||||
cmd := exec.Command(filePath, args...)
|
||||
cloudflaredCmd = cmd
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stderr, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cmd.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
scanner := bufio.NewScanner(stdout)
|
||||
for scanner.Scan() {
|
||||
log.Printf("[cloudflared] %s", scanner.Text())
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
scanner := bufio.NewScanner(stderr)
|
||||
for scanner.Scan() {
|
||||
log.Printf("[cloudflared] %s", scanner.Text())
|
||||
}
|
||||
}()
|
||||
log.Println("cloudflared started")
|
||||
return nil
|
||||
}
|
||||
|
||||
func Kill() {
|
||||
if cloudflaredCmd != nil && cloudflaredCmd.Process != nil {
|
||||
err := cloudflaredCmd.Process.Kill()
|
||||
if err != nil {
|
||||
log.Printf("failed to kill cloudflared: %v", err)
|
||||
} else {
|
||||
log.Println("cloudflared killed")
|
||||
}
|
||||
cloudflaredCmd = nil
|
||||
} else {
|
||||
log.Println("cloudflared is not running")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package cloudflared_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/komari-monitor/komari/utils/cloudflared"
|
||||
)
|
||||
|
||||
func TestRunCloudflared(t *testing.T) {
|
||||
os.Setenv("KOMARI_CLOUDFLARED_TOKEN", "test-token")
|
||||
|
||||
err := cloudflared.RunCloudflared()
|
||||
if err != nil {
|
||||
t.Fatalf("RunCloudflared failed: %v", err)
|
||||
}
|
||||
|
||||
// 等待一段时间,确保子进程已启动
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
@@ -7,6 +7,10 @@ import (
|
||||
|
||||
type EmptyProvider struct{}
|
||||
|
||||
func (e *EmptyProvider) Name() string {
|
||||
return "EmptyProvider"
|
||||
}
|
||||
|
||||
func (e *EmptyProvider) Initialize() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -4,12 +4,15 @@ import (
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/komari-monitor/komari/database/config"
|
||||
"github.com/patrickmn/go-cache"
|
||||
)
|
||||
|
||||
var CurrentProvider GeoIPService
|
||||
var geoCache *cache.Cache
|
||||
|
||||
type GeoInfo struct {
|
||||
ISOCode string
|
||||
@@ -18,11 +21,14 @@ type GeoInfo struct {
|
||||
|
||||
func init() {
|
||||
CurrentProvider = &EmptyProvider{}
|
||||
geoCache = cache.New(48*time.Hour, 1*time.Hour)
|
||||
}
|
||||
|
||||
// GeoIPService 接口定义了获取地理位置信息的核心方法。
|
||||
// 任何实现此接口的类型都可以作为地理位置服务提供者。
|
||||
type GeoIPService interface {
|
||||
Name() string
|
||||
|
||||
GetGeoInfo(ip net.IP) (*GeoInfo, error)
|
||||
|
||||
UpdateDatabase() error
|
||||
@@ -105,3 +111,29 @@ func InitGeoIp() {
|
||||
CurrentProvider = &EmptyProvider{}
|
||||
}
|
||||
}
|
||||
|
||||
func GetGeoInfo(ip net.IP) (*GeoInfo, error) {
|
||||
providerName := CurrentProvider.Name()
|
||||
cacheKey := providerName + ":" + ip.String()
|
||||
|
||||
if cachedInfo, found := geoCache.Get(cacheKey); found {
|
||||
log.Println("GeoIP cache hit for", cacheKey)
|
||||
return cachedInfo.(*GeoInfo), nil
|
||||
}
|
||||
|
||||
info, err := CurrentProvider.GetGeoInfo(ip)
|
||||
if err == nil && info != nil {
|
||||
log.Println("GeoIP cache miss for", cacheKey)
|
||||
geoCache.Set(cacheKey, info, cache.DefaultExpiration)
|
||||
}
|
||||
return info, err
|
||||
}
|
||||
|
||||
func UpdateDatabase() error {
|
||||
err := CurrentProvider.UpdateDatabase()
|
||||
if err == nil {
|
||||
geoCache.Flush()
|
||||
log.Println("GeoIP cache cleared due to database update.")
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -30,7 +30,7 @@ func testIpAddr(t *testing.T) {
|
||||
// IPv4
|
||||
ipaddr := "8.8.8.8"
|
||||
ip := net.ParseIP(ipaddr)
|
||||
record, err := geoip.CurrentProvider.GetGeoInfo(ip)
|
||||
record, err := geoip.GetGeoInfo(ip)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to get GeoIP info for IP %s: %v", ipaddr, err)
|
||||
}
|
||||
@@ -48,7 +48,7 @@ func testIpAddr(t *testing.T) {
|
||||
// IPv6
|
||||
ipaddr = "2001:4860:4860::8888"
|
||||
ip = net.ParseIP(ipaddr)
|
||||
record, err = geoip.CurrentProvider.GetGeoInfo(ip)
|
||||
record, err = geoip.GetGeoInfo(ip)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to get GeoIP info for IPv6 %s: %v", ipaddr, err)
|
||||
}
|
||||
|
||||
@@ -32,6 +32,11 @@ func NewGeoJSService() (*GeoJSService, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Name 返回服务的名称。
|
||||
func (s *GeoJSService) Name() string {
|
||||
return "geojs.io"
|
||||
}
|
||||
|
||||
// GetGeoInfo 使用 geojs.io 服务检索给定 IP 地址的地理位置信息。
|
||||
func (s *GeoJSService) GetGeoInfo(ip net.IP) (*GeoInfo, error) {
|
||||
// GeoJS 的 API 端点
|
||||
|
||||
@@ -32,6 +32,10 @@ type ipAPIResponse struct {
|
||||
Query string `json:"query"`
|
||||
}
|
||||
|
||||
func (s *IPAPIService) Name() string {
|
||||
return "ip-api.com"
|
||||
}
|
||||
|
||||
// NewIPAPIService 创建并返回一个 IPAPIService 的新实例。
|
||||
func NewIPAPIService() (*IPAPIService, error) {
|
||||
return &IPAPIService{
|
||||
|
||||
@@ -38,6 +38,11 @@ func NewIPInfoService() (*IPInfoService, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Name 返回服务的名称。
|
||||
func (s *IPInfoService) Name() string {
|
||||
return "ipinfo.io"
|
||||
}
|
||||
|
||||
// GetGeoInfo 使用 ipinfo.io 服务检索给定 IP 地址的地理位置信息。
|
||||
// 免费额度主要提供国家信息。
|
||||
func (s *IPInfoService) GetGeoInfo(ip net.IP) (*GeoInfo, error) {
|
||||
|
||||
@@ -40,6 +40,11 @@ type MaxMindGeoIPService struct {
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// Name 返回服务的名称。
|
||||
func (s *MaxMindGeoIPService) Name() string {
|
||||
return "MaxMind"
|
||||
}
|
||||
|
||||
// NewMaxMindGeoIPService 创建并返回一个 MaxMindGeoIPService 实例。
|
||||
// 它负责初始化服务,包括尝试加载或下载数据库。
|
||||
func NewMaxMindGeoIPService() (*MaxMindGeoIPService, error) {
|
||||
|
||||
+99
-38
@@ -1,59 +1,120 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/komari-monitor/komari/common"
|
||||
"github.com/komari-monitor/komari/database/models"
|
||||
)
|
||||
|
||||
func AverageReport(uuid string, time time.Time, records []common.Report) models.Record {
|
||||
var sumCPU, sumGPU, sumLOAD, sumTEMP float32
|
||||
var sumRAM, sumRAMTotal, sumSWAP, sumSWAPTotal, sumDISK, sumDISKTotal int64
|
||||
var sumNETIn, sumNETOut, sumNETTotalUp, sumNETTotalDown int64
|
||||
var sumPROCESS, sumConnections, sumConnectionsUDP int
|
||||
// AverageReport 根据 topPercentage 参数计算报告的平均值。
|
||||
// 如果 topPercentage 为 0,则计算所有记录的平均值。
|
||||
// 如果 topPercentage 大于 0 且小于等于 1,则计算从大到小排序后的前 topPercentage 记录的平均值。
|
||||
func AverageReport(uuid string, time time.Time, records []common.Report, topPercentage float64) models.Record {
|
||||
count := len(records)
|
||||
if count == 0 {
|
||||
return models.Record{}
|
||||
}
|
||||
for _, r := range records {
|
||||
sumCPU += float32(r.CPU.Usage)
|
||||
sumGPU += 0
|
||||
sumLOAD += float32(r.Load.Load1)
|
||||
sumTEMP += 0
|
||||
sumRAM += r.Ram.Used
|
||||
sumSWAP += r.Swap.Used
|
||||
sumDISK += r.Disk.Used
|
||||
sumNETIn += r.Network.Down
|
||||
sumNETOut += r.Network.Up
|
||||
sumNETTotalUp += r.Network.TotalUp
|
||||
sumNETTotalDown += r.Network.TotalDown
|
||||
sumPROCESS += r.Process
|
||||
sumConnections += r.Connections.TCP
|
||||
sumConnectionsUDP += r.Connections.UDP
|
||||
|
||||
recordsToAverageCount := count
|
||||
if topPercentage > 0 && topPercentage <= 1 {
|
||||
recordsToAverageCount = int(float64(count) * topPercentage)
|
||||
if recordsToAverageCount == 0 && count > 0 {
|
||||
recordsToAverageCount = 1 // 确保至少选择一个记录,除非总数为0
|
||||
}
|
||||
}
|
||||
|
||||
// Create new aggregated record
|
||||
// 定义一个辅助函数,用于排序和求和
|
||||
sumAndSort := func(getFloat32Value func(common.Report) float32, getInt64Value func(common.Report) int64, isFloat bool) (float32, int64) {
|
||||
if isFloat {
|
||||
sort.Slice(records, func(i, j int) bool {
|
||||
return getFloat32Value(records[i]) > getFloat32Value(records[j])
|
||||
})
|
||||
var sum float32
|
||||
for i := 0; i < recordsToAverageCount; i++ {
|
||||
sum += getFloat32Value(records[i])
|
||||
}
|
||||
return sum, 0
|
||||
} else {
|
||||
sort.Slice(records, func(i, j int) bool {
|
||||
return getInt64Value(records[i]) > getInt64Value(records[j])
|
||||
})
|
||||
var sum int64
|
||||
for i := 0; i < recordsToAverageCount; i++ {
|
||||
sum += getInt64Value(records[i])
|
||||
}
|
||||
return 0, sum
|
||||
}
|
||||
}
|
||||
|
||||
var sumCPU, sumLOAD float32
|
||||
var sumRAM, sumRAMTotal, sumSWAP, sumSWAPTotal, sumDISK, sumDISKTotal, sumNETIn, sumNETOut, sumNETTotalUp, sumNETTotalDown int64
|
||||
var sumPROCESS, sumConnections, sumConnectionsUDP int
|
||||
|
||||
if topPercentage > 0 && topPercentage <= 1 {
|
||||
sumCPU, _ = sumAndSort(func(r common.Report) float32 { return float32(r.CPU.Usage) }, nil, true)
|
||||
sumLOAD, _ = sumAndSort(func(r common.Report) float32 { return float32(r.Load.Load1) }, nil, true)
|
||||
|
||||
_, sumRAM = sumAndSort(nil, func(r common.Report) int64 { return r.Ram.Used }, false)
|
||||
//_, sumRAMTotal = sumAndSort(nil, nil, func(r common.Report) int64 { return r.Ram.Total }, false)
|
||||
_, sumSWAP = sumAndSort(nil, func(r common.Report) int64 { return r.Swap.Used }, false)
|
||||
//_, sumSWAPTotal = sumAndSort(nil, nil, func(r common.Report) int64 { return r.Swap.Total }, false)
|
||||
_, sumDISK = sumAndSort(nil, func(r common.Report) int64 { return r.Disk.Used }, false)
|
||||
//_, sumDISKTotal = sumAndSort(nil, nil, func(r common.Report) int64 { return r.Disk.Total }, false)
|
||||
_, sumNETIn = sumAndSort(nil, func(r common.Report) int64 { return r.Network.Down }, false)
|
||||
_, sumNETOut = sumAndSort(nil, func(r common.Report) int64 { return r.Network.Up }, false)
|
||||
_, sumNETTotalUp = sumAndSort(nil, func(r common.Report) int64 { return r.Network.TotalUp }, false)
|
||||
_, sumNETTotalDown = sumAndSort(nil, func(r common.Report) int64 { return r.Network.TotalDown }, false)
|
||||
|
||||
_, sumInt := sumAndSort(nil, func(r common.Report) int64 { return int64(r.Process) }, false)
|
||||
sumPROCESS = int(sumInt)
|
||||
_, sumInt = sumAndSort(nil, func(r common.Report) int64 { return int64(r.Connections.TCP) }, false)
|
||||
sumConnections = int(sumInt)
|
||||
_, sumInt = sumAndSort(nil, func(r common.Report) int64 { return int64(r.Connections.UDP) }, false)
|
||||
sumConnectionsUDP = int(sumInt)
|
||||
|
||||
} else { // 计算所有记录的平均值
|
||||
for _, r := range records {
|
||||
sumCPU += float32(r.CPU.Usage)
|
||||
sumLOAD += float32(r.Load.Load1)
|
||||
sumRAM += r.Ram.Used
|
||||
sumRAMTotal += r.Ram.Total
|
||||
sumSWAP += r.Swap.Used
|
||||
sumSWAPTotal += r.Swap.Total
|
||||
sumDISK += r.Disk.Used
|
||||
sumDISKTotal += r.Disk.Total
|
||||
sumNETIn += r.Network.Down
|
||||
sumNETOut += r.Network.Up
|
||||
sumNETTotalUp += r.Network.TotalUp
|
||||
sumNETTotalDown += r.Network.TotalDown
|
||||
sumPROCESS += r.Process
|
||||
sumConnections += r.Connections.TCP
|
||||
sumConnectionsUDP += r.Connections.UDP
|
||||
}
|
||||
}
|
||||
|
||||
// 创建新的聚合记录
|
||||
newRecord := models.Record{
|
||||
Client: uuid,
|
||||
Time: models.FromTime(time),
|
||||
Cpu: sumCPU / float32(count),
|
||||
Gpu: sumGPU / float32(count),
|
||||
Ram: sumRAM / int64(count),
|
||||
RamTotal: sumRAMTotal / int64(count),
|
||||
Swap: sumSWAP / int64(count),
|
||||
SwapTotal: sumSWAPTotal / int64(count),
|
||||
Load: sumLOAD / float32(count),
|
||||
Temp: sumTEMP / float32(count),
|
||||
Disk: sumDISK / int64(count),
|
||||
DiskTotal: sumDISKTotal / int64(count),
|
||||
NetIn: sumNETIn / int64(count),
|
||||
NetOut: sumNETOut / int64(count),
|
||||
NetTotalUp: sumNETTotalUp / int64(count),
|
||||
NetTotalDown: sumNETTotalDown / int64(count),
|
||||
Process: sumPROCESS / count,
|
||||
Connections: sumConnections / count,
|
||||
ConnectionsUdp: sumConnectionsUDP / count,
|
||||
Cpu: sumCPU / float32(recordsToAverageCount),
|
||||
Gpu: 0, // 保持原始行为
|
||||
Ram: sumRAM / int64(recordsToAverageCount),
|
||||
RamTotal: records[0].Ram.Total,
|
||||
Swap: sumSWAP / int64(recordsToAverageCount),
|
||||
SwapTotal: records[0].Swap.Total,
|
||||
Load: sumLOAD / float32(recordsToAverageCount),
|
||||
Temp: 0, // 保持原始行为
|
||||
Disk: sumDISK / int64(recordsToAverageCount),
|
||||
DiskTotal: records[0].Disk.Total,
|
||||
NetIn: sumNETIn / int64(recordsToAverageCount),
|
||||
NetOut: sumNETOut / int64(recordsToAverageCount),
|
||||
NetTotalUp: sumNETTotalUp / int64(recordsToAverageCount),
|
||||
NetTotalDown: sumNETTotalDown / int64(recordsToAverageCount),
|
||||
Process: sumPROCESS / recordsToAverageCount,
|
||||
Connections: sumConnections / recordsToAverageCount,
|
||||
ConnectionsUdp: sumConnectionsUDP / recordsToAverageCount,
|
||||
}
|
||||
return newRecord
|
||||
}
|
||||
Reference in new issue
Block a user