mirror of
https://github.com/komari-monitor/komari.git
synced 2025-10-23 03:31:56 +00:00
158 lines
4.1 KiB
Go
158 lines
4.1 KiB
Go
package api
|
||
|
||
import (
|
||
"log"
|
||
"net/http"
|
||
"sync"
|
||
"time"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
"github.com/gorilla/websocket"
|
||
"github.com/komari-monitor/komari/common"
|
||
"github.com/patrickmn/go-cache"
|
||
|
||
"strconv"
|
||
|
||
"github.com/komari-monitor/komari/database/config"
|
||
"github.com/komari-monitor/komari/database/dbcore"
|
||
"github.com/komari-monitor/komari/database/models"
|
||
"github.com/komari-monitor/komari/utils"
|
||
)
|
||
|
||
var (
|
||
Records = cache.New(1*time.Minute, 1*time.Minute)
|
||
)
|
||
|
||
type TerminalSession struct {
|
||
UUID string
|
||
UserUUID string
|
||
Browser *websocket.Conn
|
||
Agent *websocket.Conn
|
||
RequesterIp string
|
||
}
|
||
|
||
var TerminalSessionsMutex = &sync.Mutex{}
|
||
var TerminalSessions = make(map[string]*TerminalSession)
|
||
|
||
func SaveClientReportToDB() error {
|
||
lastMinute := time.Now().Add(-time.Minute).Unix()
|
||
var records []models.Record
|
||
var gpuRecords []models.GPURecord
|
||
|
||
// 遍历所有客户端记录
|
||
for uuid, x := range Records.Items() {
|
||
if uuid == "" {
|
||
continue
|
||
}
|
||
|
||
reports, ok := x.Object.([]common.Report)
|
||
if !ok {
|
||
log.Printf("Invalid report type for UUID %s", uuid)
|
||
continue
|
||
}
|
||
|
||
// 过滤一分钟前的记录
|
||
var filtered []common.Report
|
||
for _, r := range reports {
|
||
if r.UpdatedAt.Unix() >= lastMinute {
|
||
filtered = append(filtered, r)
|
||
}
|
||
}
|
||
|
||
// 更新缓存
|
||
Records.Set(uuid, filtered, cache.DefaultExpiration)
|
||
|
||
// 计算平均报告并添加到记录列表
|
||
if len(filtered) > 0 {
|
||
r := utils.AverageReport(uuid, time.Now(), filtered, 0.3)
|
||
records = append(records, r)
|
||
|
||
// 使用与其他数据相同的聚合逻辑处理GPU数据
|
||
gpuAggregated := utils.AverageGPUReports(uuid, time.Now(), filtered, 0.3)
|
||
gpuRecords = append(gpuRecords, gpuAggregated...)
|
||
}
|
||
}
|
||
|
||
// 批量插入数据库前去重(client与time共同构成唯一键)
|
||
db := dbcore.GetDBInstance()
|
||
|
||
if len(records) > 0 {
|
||
unique := make(map[string]models.Record)
|
||
for _, rec := range records {
|
||
key := rec.Client + "_" + strconv.FormatInt(rec.Time.ToTime().Unix(), 10)
|
||
unique[key] = rec
|
||
}
|
||
var deduped []models.Record
|
||
for _, rec := range unique {
|
||
deduped = append(deduped, rec)
|
||
}
|
||
if err := db.Model(&models.Record{}).Create(&deduped).Error; err != nil {
|
||
log.Printf("Failed to save records to database: %v", err)
|
||
return err
|
||
}
|
||
}
|
||
|
||
// 批量插入GPU记录
|
||
if len(gpuRecords) > 0 {
|
||
// GPU记录也需要去重,防止重复插入
|
||
gpuUnique := make(map[string]models.GPURecord)
|
||
for _, rec := range gpuRecords {
|
||
key := rec.Client + "_" + strconv.Itoa(rec.DeviceIndex) + "_" + strconv.FormatInt(rec.Time.ToTime().Unix(), 10)
|
||
gpuUnique[key] = rec
|
||
}
|
||
var gpuDeduped []models.GPURecord
|
||
for _, rec := range gpuUnique {
|
||
gpuDeduped = append(gpuDeduped, rec)
|
||
}
|
||
if err := db.Model(&models.GPURecord{}).Create(&gpuDeduped).Error; err != nil {
|
||
log.Printf("Failed to save GPU records to database: %v", err)
|
||
return err
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
type Response struct {
|
||
Status string `json:"status"`
|
||
Message string `json:"message"`
|
||
Data interface{} `json:"data,omitempty"`
|
||
}
|
||
|
||
// Respond sends a standardized JSON response.
|
||
func Respond(c *gin.Context, httpStatus int, status string, message string, data interface{}) {
|
||
c.JSON(httpStatus, Response{Status: status, Message: message, Data: data})
|
||
}
|
||
|
||
// RespondSuccess sends a success response with data.
|
||
func RespondSuccess(c *gin.Context, data interface{}) {
|
||
Respond(c, http.StatusOK, "success", "", data)
|
||
}
|
||
|
||
// RespondSuccessMessage sends a success response with message and data.
|
||
func RespondSuccessMessage(c *gin.Context, message string, data interface{}) {
|
||
Respond(c, http.StatusOK, "success", message, data)
|
||
}
|
||
|
||
// RespondError sends an error response with message.
|
||
func RespondError(c *gin.Context, httpStatus int, message string) {
|
||
Respond(c, httpStatus, "error", message, nil)
|
||
}
|
||
func GetVersion(c *gin.Context) {
|
||
RespondSuccess(c, gin.H{
|
||
"version": utils.CurrentVersion,
|
||
"hash": utils.VersionHash,
|
||
})
|
||
}
|
||
|
||
func isApiKeyValid(apiKey string) bool {
|
||
cfg, err := config.Get()
|
||
if err != nil {
|
||
return false
|
||
}
|
||
if cfg.ApiKey == "" || len(cfg.ApiKey) < 12 {
|
||
return false
|
||
}
|
||
return apiKey == "Bearer "+cfg.ApiKey
|
||
}
|