Files
2025-12-14 15:25:31 +08:00

266 lines
7.0 KiB
Go

package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"runtime"
"time"
)
// envelope keeps JSON responses consistent.
type envelope map[string]any
func main() {
host := getenv("HOST", "0.0.0.0")
port := getenv("PORT", "9292")
mux := http.NewServeMux()
mux.HandleFunc("/", rootHandler)
mux.HandleFunc("/api/health", healthHandler)
// 拆分的细粒度API端点
mux.HandleFunc("/api/metrics/cpu", cpuMetricsHandler)
mux.HandleFunc("/api/metrics/memory", memoryMetricsHandler)
mux.HandleFunc("/api/metrics/storage", storageMetricsHandler)
mux.HandleFunc("/api/metrics/gpu", gpuMetricsHandler)
mux.HandleFunc("/api/metrics/network", networkMetricsHandler)
mux.HandleFunc("/api/metrics/system", systemMetricsHandler)
mux.HandleFunc("/api/metrics/docker", dockerMetricsHandler)
mux.HandleFunc("/api/metrics/latency", latencyMetricsHandler)
srv := &http.Server{
Addr: fmt.Sprintf("%s:%s", host, port),
Handler: loggingMiddleware(corsMiddleware(mux)),
ReadHeaderTimeout: 5 * time.Second,
}
log.Printf("server starting on http://%s:%s", host, port)
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("server error: %v", err)
}
}
func rootHandler(w http.ResponseWriter, r *http.Request) {
respondJSON(w, http.StatusOK, envelope{
"service": "萌芽监控面板 API",
"version": "1.0.0",
"endpoints": []map[string]string{
{
"path": "/",
"description": "API 信息和可用端点列表",
},
{
"path": "/api/health",
"description": "健康检查",
},
{
"path": "/api/metrics/cpu",
"description": "获取 CPU 监控数据",
},
{
"path": "/api/metrics/memory",
"description": "获取内存监控数据",
},
{
"path": "/api/metrics/storage",
"description": "获取存储监控数据",
},
{
"path": "/api/metrics/gpu",
"description": "获取 GPU 监控数据",
},
{
"path": "/api/metrics/network",
"description": "获取网络接口监控数据",
},
{
"path": "/api/metrics/system",
"description": "获取系统统计信息(进程、包、磁盘速度等)",
},
{
"path": "/api/metrics/docker",
"description": "获取 Docker 容器监控数据",
},
},
})
}
func healthHandler(w http.ResponseWriter, r *http.Request) {
respondJSON(w, http.StatusOK, envelope{
"status": "ok",
"timestamp": time.Now().UTC(),
})
}
// CPU监控数据
func cpuMetricsHandler(w http.ResponseWriter, r *http.Request) {
cpuModel := firstMatchInFile("/proc/cpuinfo", "model name")
cpuUsage, err := readCPUUsage()
if err != nil {
cpuUsage = 0
}
cpuTemp := readCPUTemperature()
perCoreUsage := readPerCoreUsage()
loads := readLoadAverages()
cores := runtime.NumCPU()
respondJSON(w, http.StatusOK, envelope{
"data": CPUMetrics{
Model: cpuModel,
Cores: cores,
UsagePercent: round(cpuUsage, 2),
LoadAverages: loads,
Temperature: cpuTemp,
PerCoreUsage: perCoreUsage,
},
})
}
// 内存监控数据
func memoryMetricsHandler(w http.ResponseWriter, r *http.Request) {
mem, err := readMemory()
if err != nil {
respondJSON(w, http.StatusInternalServerError, envelope{
"error": "failed to read memory",
})
return
}
respondJSON(w, http.StatusOK, envelope{
"data": mem,
})
}
// 存储监控数据
func storageMetricsHandler(w http.ResponseWriter, r *http.Request) {
storage, err := readAllStorage()
if err != nil {
respondJSON(w, http.StatusInternalServerError, envelope{
"error": "failed to read storage",
})
return
}
respondJSON(w, http.StatusOK, envelope{
"data": storage,
})
}
// GPU监控数据
func gpuMetricsHandler(w http.ResponseWriter, r *http.Request) {
gpu := readGPU()
respondJSON(w, http.StatusOK, envelope{
"data": gpu,
})
}
// 网络监控数据
func networkMetricsHandler(w http.ResponseWriter, r *http.Request) {
network := readNetworkInterfaces()
respondJSON(w, http.StatusOK, envelope{
"data": network,
})
}
// 系统统计信息
func systemMetricsHandler(w http.ResponseWriter, r *http.Request) {
stats := readSystemStats()
// 读取系统基本信息
hostname, _ := os.Hostname()
osInfo := readOSInfo()
uptime := readUptime()
// 计算总网络速度(汇总所有接口)
networkInterfaces := readNetworkInterfaces()
var totalRxSpeed, totalTxSpeed float64
for _, iface := range networkInterfaces {
totalRxSpeed += iface.RxSpeed // bytes/s
totalTxSpeed += iface.TxSpeed // bytes/s
}
// 转换为 MB/s
stats.NetworkRxSpeed = round(totalRxSpeed/1024/1024, 2)
stats.NetworkTxSpeed = round(totalTxSpeed/1024/1024, 2)
respondJSON(w, http.StatusOK, envelope{
"data": map[string]interface{}{
"hostname": hostname,
"os": osInfo,
"uptimeSeconds": uptime,
"processCount": stats.ProcessCount,
"packageCount": stats.PackageCount,
"packageManager": stats.PackageManager,
"temperature": stats.Temperature,
"diskReadSpeed": stats.DiskReadSpeed,
"diskWriteSpeed": stats.DiskWriteSpeed,
"networkRxSpeed": stats.NetworkRxSpeed,
"networkTxSpeed": stats.NetworkTxSpeed,
"topProcesses": stats.TopProcesses,
"systemLogs": stats.SystemLogs,
},
})
}
// Docker监控数据
func dockerMetricsHandler(w http.ResponseWriter, r *http.Request) {
docker := readDockerStats()
respondJSON(w, http.StatusOK, envelope{
"data": docker,
})
}
// 延迟监控数据
func latencyMetricsHandler(w http.ResponseWriter, r *http.Request) {
// 记录请求开始时间,用于计算客户端到服务器的延迟
startTime := time.Now()
// 读取外部网站延迟
externalLatencies := readExternalLatencies()
// 计算服务器处理时间(这个可以作为参考,实际客户端延迟由前端计算)
serverProcessTime := time.Since(startTime).Milliseconds()
respondJSON(w, http.StatusOK, envelope{
"data": map[string]interface{}{
"serverProcessTime": serverProcessTime, // 服务器处理时间(参考)
"external": externalLatencies,
},
})
}
func respondJSON(w http.ResponseWriter, status int, body envelope) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
if err := json.NewEncoder(w).Encode(body); err != nil {
log.Printf("write json error: %v", err)
}
}
func getenv(key, fallback string) string {
if v, ok := os.LookupEnv(key); ok && v != "" {
return v
}
return fallback
}
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
next.ServeHTTP(w, r)
log.Printf("%s %s %s", r.Method, r.URL.Path, time.Since(start))
})
}
func corsMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
if r.Method == http.MethodOptions {
w.WriteHeader(http.StatusNoContent)
return
}
next.ServeHTTP(w, r)
})
}