3v324v23 commited on
Commit
de65817
·
0 Parent(s):

feat: enrich features and configure for huggingface

Browse files
Files changed (7) hide show
  1. Dockerfile +33 -0
  2. README.md +45 -0
  3. go.mod +20 -0
  4. go.sum +49 -0
  5. main.go +165 -0
  6. static/app.js +101 -0
  7. static/index.html +162 -0
Dockerfile ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 构建阶段
2
+ FROM golang:1.21-alpine AS builder
3
+
4
+ WORKDIR /app
5
+
6
+ # 安装必要的构建依赖
7
+ RUN apk add --no-cache gcc musl-dev
8
+
9
+ # 复制依赖文件并下载
10
+ COPY go.mod go.sum ./
11
+ RUN go mod download
12
+
13
+ # 复制源代码并构建
14
+ COPY . .
15
+ RUN go build -o main .
16
+
17
+ # 运行阶段
18
+ FROM alpine:latest
19
+
20
+ WORKDIR /app
21
+
22
+ # 复制构建产物和静态资源
23
+ COPY --from=builder /app/main .
24
+ COPY --from=builder /app/static ./static
25
+
26
+ # 暴露端口 (Hugging Face Spaces 默认 7860)
27
+ EXPOSE 7860
28
+
29
+ # 设置环境变量
30
+ ENV PORT=7860
31
+
32
+ # 启动应用
33
+ CMD ["./main"]
README.md ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Go 系统实时监控仪表板
3
+ emoji: 📊
4
+ colorFrom: blue
5
+ colorTo: green
6
+ sdk: docker
7
+ app_port: 7860
8
+ pinned: false
9
+ short_description: 基于 Go 语言的实时系统性能监控仪表板,支持主机信息、Top 10 进程监控及移动端适配。
10
+ ---
11
+
12
+ # Go 系统实时监控仪表板 (Go-Sys-Dash)
13
+
14
+ 本项目是一个基于 Go 语言开发的轻量级系统性能监控工具。它通过 WebSocket 实时推送服务器的 CPU、内存、磁盘及进程信息,并提供一个现代化的、响应式的 Web 界面。
15
+
16
+ ## 核心特性
17
+
18
+ - **实时数据推送**:使用 WebSocket 技术,每 2 秒自动刷新系统指标。
19
+ - **主机详情展示**:新增主机名、操作系统、平台及运行时间显示。
20
+ - **直观的可视化**:采用 Chart.js 绘制环形图,清晰展示资源占用情况。
21
+ - **增强型进程管理**:实时列出当前系统中 CPU 占用最高的前 10 个进程。
22
+ - **移动端适配**:响应式布局设计,支持在手机和平板浏览器上流畅查看。
23
+ - **汉化支持**:界面全中文显示,代码包含详尽的中文注释。
24
+ - **一键部署**:支持 Docker 容器化部署,适配主流云平台和 Hugging Face Spaces。
25
+
26
+ ## 技术栈
27
+
28
+ - **后端**: Go 1.21+, `gopsutil` (系统信息采集), `gorilla/websocket` (通信)
29
+ - **前端**: HTML5, CSS3, JavaScript (ES6+), Chart.js
30
+ - **部署**: Docker, Alpine Linux (轻量化运行环境)
31
+
32
+ ## 本地运行
33
+
34
+ 1. 确保已安装 Go 环境。
35
+ 2. 克隆项目并进入目录:
36
+ ```bash
37
+ go mod tidy
38
+ go run main.go
39
+ ```
40
+ 3. 访问 `http://localhost:7860`。
41
+
42
+ ## Hugging Face 部署说明
43
+
44
+ 本项目已针对 Hugging Face Spaces 进行优化,通过 `Dockerfile` 进行构建和部署。系统会自动识别根目录下的 `Dockerfile` 并完成环境搭建。
45
+ 默认端口已设为 `7860`,适配 Hugging Face 运行环境。
go.mod ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ module go-sys-dash
2
+
3
+ go 1.21
4
+
5
+ require (
6
+ github.com/gorilla/websocket v1.5.1
7
+ github.com/shirou/gopsutil/v3 v3.23.12
8
+ )
9
+
10
+ require (
11
+ github.com/go-ole/go-ole v1.2.6 // indirect
12
+ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
13
+ github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
14
+ github.com/shoenig/go-m1cpu v0.1.6 // indirect
15
+ github.com/tklauser/go-sysconf v0.3.12 // indirect
16
+ github.com/tklauser/numcpus v0.6.1 // indirect
17
+ github.com/yusufpapurcu/wmi v1.2.3 // indirect
18
+ golang.org/x/net v0.17.0 // indirect
19
+ golang.org/x/sys v0.15.0 // indirect
20
+ )
go.sum ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2
+ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
3
+ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4
+ github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
5
+ github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
6
+ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
7
+ github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
8
+ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
9
+ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
10
+ github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
11
+ github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
12
+ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
13
+ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
14
+ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
15
+ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
16
+ github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
17
+ github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
18
+ github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4=
19
+ github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM=
20
+ github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
21
+ github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
22
+ github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
23
+ github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
24
+ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
25
+ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
26
+ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
27
+ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
28
+ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
29
+ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
30
+ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
31
+ github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
32
+ github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
33
+ github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
34
+ github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
35
+ github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
36
+ github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
37
+ golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
38
+ golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
39
+ golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
40
+ golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
41
+ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
42
+ golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
43
+ golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
44
+ golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
45
+ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
46
+ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
47
+ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
48
+ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
49
+ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
main.go ADDED
@@ -0,0 +1,165 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package main
2
+
3
+ import (
4
+ "encoding/json"
5
+ "fmt"
6
+ "log"
7
+ "net/http"
8
+ "os"
9
+ "sort"
10
+ "time"
11
+
12
+ "github.com/gorilla/websocket"
13
+ "github.com/shirou/gopsutil/v3/cpu"
14
+ "github.com/shirou/gopsutil/v3/disk"
15
+ "github.com/shirou/gopsutil/v3/host"
16
+ "github.com/shirou/gopsutil/v3/mem"
17
+ "github.com/shirou/gopsutil/v3/process"
18
+ )
19
+
20
+ // 系统状态结构体
21
+ type SystemStatus struct {
22
+ CPUUsage float64 `json:"cpuUsage"`
23
+ MemoryUsage float64 `json:"memoryUsage"`
24
+ DiskUsage float64 `json:"diskUsage"`
25
+ Processes []Process `json:"processes"`
26
+ Timestamp string `json:"timestamp"`
27
+ HostInfo HostSummary `json:"hostInfo"`
28
+ }
29
+
30
+ // 进程信息结构体
31
+ type Process struct {
32
+ PID int32 `json:"pid"`
33
+ Name string `json:"name"`
34
+ CPU float64 `json:"cpu"`
35
+ Memory float32 `json:"memory"`
36
+ }
37
+
38
+ // 主机摘要信息
39
+ type HostSummary struct {
40
+ Hostname string `json:"hostname"`
41
+ OS string `json:"os"`
42
+ Platform string `json:"platform"`
43
+ Uptime uint64 `json:"uptime"`
44
+ }
45
+
46
+ var upgrader = websocket.Upgrader{
47
+ CheckOrigin: func(r *http.Request) bool {
48
+ return true // 允许所有来源,方便演示
49
+ },
50
+ }
51
+
52
+ // 获取当前系统状态
53
+ func getSystemStatus() SystemStatus {
54
+ // CPU 使用率
55
+ cpuPercent, _ := cpu.Percent(time.Second, false)
56
+ var cpuVal float64
57
+ if len(cpuPercent) > 0 {
58
+ cpuVal = cpuPercent[0]
59
+ }
60
+
61
+ // 内存使用率
62
+ vMem, _ := mem.VirtualMemory()
63
+ memVal := vMem.UsedPercent
64
+
65
+ // 磁盘使用率 (根目录)
66
+ dUsage, _ := disk.Usage("/")
67
+ diskVal := dUsage.UsedPercent
68
+
69
+ // 获取主机信息
70
+ hInfo, _ := host.Info()
71
+ hostSummary := HostSummary{
72
+ Hostname: hInfo.Hostname,
73
+ OS: hInfo.OS,
74
+ Platform: hInfo.Platform,
75
+ Uptime: hInfo.Uptime,
76
+ }
77
+
78
+ // 获取所有进程并按 CPU 排序
79
+ allProcs, _ := process.Processes()
80
+ var procList []Process
81
+ for _, p := range allProcs {
82
+ name, _ := p.Name()
83
+ cpuP, _ := p.CPUPercent()
84
+ memP, _ := p.MemoryPercent()
85
+ if cpuP > 0 || memP > 0 {
86
+ procList = append(procList, Process{
87
+ PID: p.Pid,
88
+ Name: name,
89
+ CPU: cpuP,
90
+ Memory: memP,
91
+ })
92
+ }
93
+ }
94
+
95
+ // 按 CPU 使用率降序排序
96
+ sort.Slice(procList, func(i, j int) bool {
97
+ return procList[i].CPU > procList[j].CPU
98
+ })
99
+
100
+ return SystemStatus{
101
+ CPUUsage: cpuVal,
102
+ MemoryUsage: memVal,
103
+ DiskUsage: diskVal,
104
+ Processes: procList[:min(10, len(procList))], // 取前10个
105
+ Timestamp: time.Now().Format("15:04:05"),
106
+ HostInfo: hostSummary,
107
+ }
108
+ }
109
+
110
+ func min(a, b int) int {
111
+ if a < b {
112
+ return a
113
+ }
114
+ return b
115
+ }
116
+
117
+ // 处理 WebSocket 连接
118
+ func handleWebSocket(w http.ResponseWriter, r *http.Request) {
119
+ conn, err := upgrader.Upgrade(w, r, nil)
120
+ if err != nil {
121
+ log.Println("升级 WebSocket 失败:", err)
122
+ return
123
+ }
124
+ defer conn.Close()
125
+
126
+ log.Println("新的客户端已连接")
127
+
128
+ // 定期发送系统数据
129
+ for {
130
+ status := getSystemStatus()
131
+ data, err := json.Marshal(status)
132
+ if err != nil {
133
+ log.Println("序列化数据失败:", err)
134
+ break
135
+ }
136
+
137
+ if err := conn.WriteMessage(websocket.TextMessage, data); err != nil {
138
+ log.Println("发送消息失败:", err)
139
+ break
140
+ }
141
+
142
+ time.Sleep(2 * time.Second) // 每2秒更新一次
143
+ }
144
+ }
145
+
146
+ func main() {
147
+ // 静态文件服务
148
+ fs := http.FileServer(http.Dir("./static"))
149
+ http.Handle("/", fs)
150
+
151
+ // WebSocket 路由
152
+ http.HandleFunc("/ws", handleWebSocket)
153
+
154
+ port := os.Getenv("PORT")
155
+ if port == "" {
156
+ port = "7860" // 默认端口 7860 (Hugging Face Spaces)
157
+ }
158
+
159
+ fmt.Printf("服务器启动在 http://0.0.0.0:%s\n", port)
160
+ fmt.Println("正在监控系统性能...")
161
+
162
+ if err := http.ListenAndServe(":"+port, nil); err != nil {
163
+ log.Fatal("服务器启动失败:", err)
164
+ }
165
+ }
static/app.js ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // 初始化图表配置
2
+ function createChart(id, label, color) {
3
+ const ctx = document.getElementById(id).getContext('2d');
4
+ return new Chart(ctx, {
5
+ type: 'doughnut',
6
+ data: {
7
+ labels: [label, '可用'],
8
+ datasets: [{
9
+ data: [0, 100],
10
+ backgroundColor: [color, '#eee'],
11
+ borderWidth: 0
12
+ }]
13
+ },
14
+ options: {
15
+ cutout: '70%',
16
+ responsive: true,
17
+ plugins: {
18
+ legend: { display: false }
19
+ }
20
+ }
21
+ });
22
+ }
23
+
24
+ const cpuChart = createChart('cpuChart', 'CPU', '#3498db');
25
+ const memChart = createChart('memChart', '内存', '#2ecc71');
26
+ const diskChart = createChart('diskChart', '磁盘', '#e67e22');
27
+
28
+ function formatUptime(seconds) {
29
+ const days = Math.floor(seconds / 86400);
30
+ const hours = Math.floor((seconds % 86400) / 3600);
31
+ const minutes = Math.floor((seconds % 3600) / 60);
32
+ let res = "";
33
+ if (days > 0) res += days + "天 ";
34
+ if (hours > 0) res += hours + "时 ";
35
+ res += minutes + "分";
36
+ return res;
37
+ }
38
+
39
+ // 连接 WebSocket
40
+ function connect() {
41
+ const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
42
+ const ws = new WebSocket(`${protocol}//${window.location.host}/ws`);
43
+
44
+ ws.onmessage = (event) => {
45
+ const data = JSON.parse(event.data);
46
+
47
+ // 更新文本显示
48
+ document.getElementById('cpu-val').innerText = `${data.cpuUsage.toFixed(1)}%`;
49
+ document.getElementById('mem-val').innerText = `${data.memoryUsage.toFixed(1)}%`;
50
+ document.getElementById('disk-val').innerText = `${data.diskUsage.toFixed(1)}%`;
51
+ document.getElementById('time-display').innerText = `最后更新: ${data.timestamp}`;
52
+
53
+ // 更新主机信息
54
+ if (data.hostInfo) {
55
+ document.getElementById('hostname').innerText = data.hostInfo.hostname;
56
+ document.getElementById('os-info').innerText = data.hostInfo.os;
57
+ document.getElementById('platform').innerText = data.hostInfo.platform;
58
+ document.getElementById('uptime').innerText = formatUptime(data.hostInfo.uptime);
59
+ }
60
+
61
+ // 更新图表
62
+ cpuChart.data.datasets[0].data = [data.cpuUsage, 100 - data.cpuUsage];
63
+ cpuChart.update();
64
+
65
+ memChart.data.datasets[0].data = [data.memoryUsage, 100 - data.memoryUsage];
66
+ memChart.update();
67
+
68
+ diskChart.data.datasets[0].data = [data.diskUsage, 100 - data.diskUsage];
69
+ diskChart.update();
70
+
71
+ // 更新进程列表
72
+ const processList = document.getElementById('process-list');
73
+ processList.innerHTML = '';
74
+ data.processes.forEach(p => {
75
+ const row = `<tr>
76
+ <td>${p.pid}</td>
77
+ <td>${p.name}</td>
78
+ <td>${p.cpu.toFixed(1)}</td>
79
+ <td>${p.memory.toFixed(1)}</td>
80
+ </tr>`;
81
+ processList.innerHTML += row;
82
+ });
83
+ };
84
+
85
+ ws.onopen = () => {
86
+ console.log('WebSocket 连接已建立');
87
+ document.querySelector('.status-dot').style.backgroundColor = '#2ecc71';
88
+ };
89
+
90
+ ws.onclose = () => {
91
+ console.log('WebSocket 连接已关闭,尝试重连...');
92
+ document.querySelector('.status-dot').style.backgroundColor = '#e74c3c';
93
+ setTimeout(connect, 3000); // 3秒后重连
94
+ };
95
+
96
+ ws.onerror = (err) => {
97
+ console.error('WebSocket 错误:', err);
98
+ };
99
+ }
100
+
101
+ connect();
static/index.html ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Go 系统监控仪表板</title>
7
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
8
+ <style>
9
+ :root {
10
+ --primary-color: #3498db;
11
+ --bg-color: #f4f7f6;
12
+ --card-bg: #ffffff;
13
+ --text-color: #2c3e50;
14
+ }
15
+
16
+ body {
17
+ font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
18
+ background-color: var(--bg-color);
19
+ color: var(--text-color);
20
+ margin: 0;
21
+ padding: 20px;
22
+ }
23
+
24
+ .container {
25
+ max-width: 1200px;
26
+ margin: 0 auto;
27
+ }
28
+
29
+ header {
30
+ text-align: center;
31
+ margin-bottom: 30px;
32
+ }
33
+
34
+ .dashboard-grid {
35
+ display: grid;
36
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
37
+ gap: 20px;
38
+ margin-bottom: 30px;
39
+ }
40
+
41
+ .info-bar {
42
+ background-color: var(--card-bg);
43
+ border-radius: 12px;
44
+ padding: 15px 20px;
45
+ margin-bottom: 20px;
46
+ display: flex;
47
+ justify-content: space-around;
48
+ flex-wrap: wrap;
49
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
50
+ font-size: 0.9em;
51
+ }
52
+
53
+ .info-item {
54
+ margin: 5px 15px;
55
+ }
56
+
57
+ .info-item strong {
58
+ color: var(--primary-color);
59
+ }
60
+
61
+ .card {
62
+ background-color: var(--card-bg);
63
+ border-radius: 12px;
64
+ padding: 20px;
65
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
66
+ text-align: center;
67
+ }
68
+
69
+ .card h3 {
70
+ margin-top: 0;
71
+ color: var(--primary-color);
72
+ }
73
+
74
+ .process-table {
75
+ width: 100%;
76
+ border-collapse: collapse;
77
+ background-color: var(--card-bg);
78
+ border-radius: 12px;
79
+ overflow: hidden;
80
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
81
+ }
82
+
83
+ .process-table th, .process-table td {
84
+ padding: 12px 15px;
85
+ text-align: left;
86
+ border-bottom: 1px solid #eee;
87
+ }
88
+
89
+ .process-table th {
90
+ background-color: var(--primary-color);
91
+ color: white;
92
+ }
93
+
94
+ .status-dot {
95
+ height: 10px;
96
+ width: 10px;
97
+ background-color: #2ecc71;
98
+ border-radius: 50%;
99
+ display: inline-block;
100
+ margin-right: 5px;
101
+ }
102
+
103
+ @media (max-width: 600px) {
104
+ body { padding: 10px; }
105
+ .dashboard-grid { grid-template-columns: 1fr; }
106
+ .process-table { font-size: 14px; }
107
+ }
108
+ </style>
109
+ </head>
110
+ <body>
111
+ <div class="container">
112
+ <header>
113
+ <h1><span class="status-dot"></span>系统实时监控</h1>
114
+ <p id="time-display">正在获取时间...</p>
115
+ </header>
116
+
117
+ <div class="info-bar" id="host-info">
118
+ <div class="info-item"><strong>主机名:</strong> <span id="hostname">-</span></div>
119
+ <div class="info-item"><strong>系统:</strong> <span id="os-info">-</span></div>
120
+ <div class="info-item"><strong>平台:</strong> <span id="platform">-</span></div>
121
+ <div class="info-item"><strong>运行时间:</strong> <span id="uptime">-</span></div>
122
+ </div>
123
+
124
+ <div class="dashboard-grid">
125
+ <div class="card">
126
+ <h3>CPU 使用率</h3>
127
+ <canvas id="cpuChart"></canvas>
128
+ <p id="cpu-val">0%</p>
129
+ </div>
130
+ <div class="card">
131
+ <h3>内存 使用率</h3>
132
+ <canvas id="memChart"></canvas>
133
+ <p id="mem-val">0%</p>
134
+ </div>
135
+ <div class="card">
136
+ <h3>磁盘 使用率</h3>
137
+ <canvas id="diskChart"></canvas>
138
+ <p id="disk-val">0%</p>
139
+ </div>
140
+ </div>
141
+
142
+ <div class="card">
143
+ <h3>活跃进程 (Top 10)</h3>
144
+ <table class="process-table">
145
+ <thead>
146
+ <tr>
147
+ <th>PID</th>
148
+ <th>名称</th>
149
+ <th>CPU %</th>
150
+ <th>内存 %</th>
151
+ </tr>
152
+ </thead>
153
+ <tbody id="process-list">
154
+ <!-- 动态加载 -->
155
+ </tbody>
156
+ </table>
157
+ </div>
158
+ </div>
159
+
160
+ <script src="app.js"></script>
161
+ </body>
162
+ </html>