266 lines
7.0 KiB
Go
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)
|
|
})
|
|
}
|