Skip to content

Commit 2184bb5

Browse files
committed
Update.
1 parent c801bea commit 2184bb5

File tree

5 files changed

+660
-48
lines changed

5 files changed

+660
-48
lines changed

cmd/dashboard/controller/controller.go

Lines changed: 187 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"net/http"
99
"os"
1010
"path/filepath"
11+
"runtime"
1112
"strconv"
1213
"strings"
1314
"time"
@@ -63,6 +64,7 @@ func corsMiddleware(c *gin.Context) {
6364
c.Next()
6465
}
6566

67+
// ServeWeb 启动Web服务器并配置路由
6668
func ServeWeb(port uint) *http.Server {
6769
gin.SetMode(gin.ReleaseMode)
6870

@@ -97,62 +99,155 @@ func ServeWeb(port uint) *http.Server {
9799

98100
// 添加Recovery中间件,过滤网络连接错误
99101
r.Use(gin.CustomRecovery(func(c *gin.Context, recovered interface{}) {
100-
if err, ok := recovered.(string); ok {
101-
errLower := strings.ToLower(err)
102-
if strings.Contains(errLower, "broken pipe") ||
103-
strings.Contains(errLower, "connection reset") ||
104-
strings.Contains(errLower, "use of closed network connection") {
105-
// 静默处理网络连接错误
106-
c.AbortWithStatus(http.StatusInternalServerError)
107-
return
108-
}
109-
}
110-
// 其他错误正常处理
102+
// 检查是否为broken pipe错误
103+
if errStr := fmt.Sprintf("%v", recovered); strings.Contains(errStr, "broken pipe") ||
104+
strings.Contains(errStr, "connection reset") ||
105+
strings.Contains(errStr, "use of closed network connection") {
106+
// 静默处理broken pipe错误,不中断其他请求处理
107+
c.AbortWithStatus(499) // 使用499表示客户端关闭连接
108+
return
109+
}
110+
111+
// 其他panic正常处理
112+
log.Printf("服务器Panic: %v", recovered)
111113
c.AbortWithStatus(http.StatusInternalServerError)
112114
}))
113-
114-
if singleton.Conf.Debug {
115-
gin.SetMode(gin.DebugMode)
115+
116+
// 添加handleBrokenPipe中间件
117+
r.Use(handleBrokenPipe)
118+
119+
// 添加CORS中间件
120+
r.Use(corsMiddleware)
121+
122+
// 添加模板渲染保护中间件 - 这是防止前端页面无法访问的关键
123+
r.Use(TemplateRenderingSafeguard())
124+
125+
// 添加健康检查中间件
126+
r.Use(HealthCheckMiddleware())
127+
128+
// 添加pprof性能分析
129+
if singleton.Conf != nil && singleton.Conf.Debug {
116130
pprof.Register(r)
117131
}
118-
r.Use(natGateway)
119-
r.Use(handleBrokenPipe) // 添加broken pipe错误处理中间件
120-
r.Use(corsMiddleware) // 添加CORS中间件处理OPTIONS请求
121-
tmpl := template.New("").Funcs(funcMap)
122-
var err error
123-
// 直接用本地模板目录
124-
tmpl, err = tmpl.ParseGlob("resource/template/**/*.html")
125-
if err != nil {
126-
panic(err)
127-
}
128-
tmpl = loadThirdPartyTemplates(tmpl)
129-
r.SetHTMLTemplate(tmpl)
130-
r.Use(mygin.RecordPath)
131-
// 直接用本地静态资源目录
132-
r.Static("/static", "resource/static")
133-
134-
routers(r)
135-
page404 := func(c *gin.Context) {
136-
mygin.ShowErrorPage(c, mygin.ErrInfo{
137-
Code: http.StatusNotFound,
138-
Title: "该页面不存在",
139-
Msg: "该页面内容可能已着陆火星",
140-
Link: "/",
141-
Btn: "返回首页",
142-
}, true)
143-
}
144-
r.NoRoute(page404)
145-
r.NoMethod(page404)
146-
147-
srv := &http.Server{
148-
Addr: fmt.Sprintf(":%d", port),
149-
ReadHeaderTimeout: time.Second * 5,
150-
Handler: r,
132+
133+
// 设置HTML模板和静态资源
134+
r.SetFuncMap(template.FuncMap{
135+
"tf": func(t time.Time) string {
136+
return t.Format("2006-01-02 15:04:05")
137+
},
138+
"fisher": func(t time.Time) string {
139+
return t.Format("2006/01/02 15:04")
140+
},
141+
"tf2": func(t time.Time) string {
142+
return t.In(singleton.Loc).Format("01/02 15:04")
143+
},
144+
"markdown": func(text string) template.HTML {
145+
// 简单地将markdown转为HTML
146+
return template.HTML(text)
147+
},
148+
"tag": func(text string) template.HTML {
149+
return template.HTML(`<` + text + `>`)
150+
},
151+
"trim": func(text string) string {
152+
return strings.TrimSpace(text)
153+
},
154+
"recent": func(t time.Time) bool {
155+
return time.Since(t).Seconds() <= 60
156+
},
157+
"find": func(slice []string, val string) int {
158+
for i, item := range slice {
159+
if item == val {
160+
return i
161+
}
162+
}
163+
return -1
164+
},
165+
"next": func(pos, length int) int {
166+
pos++
167+
if pos >= length {
168+
pos = 0
169+
}
170+
return pos
171+
},
172+
"prev": func(pos, length int) int {
173+
pos--
174+
if pos < 0 {
175+
pos = length - 1
176+
}
177+
return pos
178+
},
179+
"fs": func(bytes uint64) string {
180+
return bytefmt.ByteSize(bytes)
181+
},
182+
"tf_rfc3339": func(t time.Time) string {
183+
return t.Format(time.RFC3339)
184+
},
185+
"pb": func(p float64) string {
186+
return fmt.Sprintf("%.2f", p*100)
187+
},
188+
"isPositive": func(a float64) bool {
189+
return a > 0
190+
},
191+
"isNegative": func(a float64) bool {
192+
return a < 0
193+
},
194+
"abs": func(a float64) float64 {
195+
if a < 0 {
196+
return -a
197+
}
198+
return a
199+
},
200+
"roundTo": func(decimals int, num float64) float64 {
201+
f := math.Pow10(decimals)
202+
// 四舍五入
203+
return math.Round(num*f) / f
204+
},
205+
})
206+
207+
// 静态资源
208+
r.StaticFS("/static", http.Dir("resource/static"))
209+
210+
// 健康检查路由
211+
r.GET("/health", HealthCheckHandler)
212+
r.GET("/system-health", SystemHealthHandler)
213+
214+
// 创建路由分组和控制器
215+
commonController := CreateControllers(r)
216+
217+
// 创建HTTP服务器
218+
server := &http.Server{
219+
Addr: fmt.Sprintf(":%d", port),
220+
Handler: r,
151221
}
152-
return srv
222+
223+
return server
153224
}
154225

155226
func routers(r *gin.Engine) {
227+
// 添加健康检查接口
228+
r.GET("/health", func(c *gin.Context) {
229+
// 简单版本,直接返回200
230+
c.JSON(http.StatusOK, gin.H{
231+
"status": "ok",
232+
"time": time.Now().Format(time.RFC3339),
233+
})
234+
})
235+
236+
// 系统监控端点 (需要权限才能访问)
237+
r.GET("/system-health", func(c *gin.Context) {
238+
// 返回系统健康信息
239+
memStats := runtime.MemStats{}
240+
runtime.ReadMemStats(&memStats)
241+
242+
c.JSON(http.StatusOK, gin.H{
243+
"status": "ok",
244+
"goroutines": runtime.NumGoroutine(),
245+
"memoryUsageMB": float64(memStats.Alloc) / (1024 * 1024),
246+
"time": time.Now().Format(time.RFC3339),
247+
"templateHealthy": GlobalTemplateSafeguard.IsHealthy(),
248+
})
249+
})
250+
156251
// 通用页面
157252
cp := commonPage{r: r}
158253
cp.serve()
@@ -396,6 +491,50 @@ var funcMap = template.FuncMap{
396491
},
397492
}
398493

494+
// CreateControllers 创建并初始化所有控制器
495+
func CreateControllers(engine *gin.Engine) *commonPage {
496+
// 创建通用页面控制器
497+
cp := &commonPage{
498+
r: engine,
499+
}
500+
cp.serve()
501+
502+
// 创建访客页面控制器
503+
gp := &guestPage{
504+
r: engine,
505+
}
506+
gp.serve()
507+
508+
// 创建OAuth2控制器
509+
oa := &oauth2controller{
510+
r: engine,
511+
}
512+
oa.serve()
513+
514+
// 创建会员页面控制器
515+
mp := &memberPage{
516+
r: engine,
517+
}
518+
mp.serve()
519+
520+
// 创建会员API控制器
521+
ma := &memberAPI{
522+
r: engine,
523+
}
524+
ma.serve()
525+
526+
// 创建APIv1控制器
527+
api := &apiV1{
528+
r: engine,
529+
}
530+
api.serve()
531+
532+
// 添加NAT网关中间件
533+
engine.NoRoute(natGateway)
534+
535+
return cp
536+
}
537+
399538
func natGateway(c *gin.Context) {
400539
natConfig := singleton.GetNATConfigByDomain(c.Request.Host)
401540
if natConfig == nil {

cmd/dashboard/main.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@ import (
1414
flag "github.com/spf13/pflag"
1515
"github.com/xos/serverstatus/cmd/dashboard/controller"
1616
"github.com/xos/serverstatus/cmd/dashboard/rpc"
17+
"github.com/xos/serverstatus/db"
1718
"github.com/xos/serverstatus/model"
1819
"github.com/xos/serverstatus/proto"
20+
"github.com/xos/serverstatus/service"
1921
"github.com/xos/serverstatus/service/singleton"
2022
)
2123

@@ -40,6 +42,18 @@ func init() {
4042
}
4143

4244
func initSystem() {
45+
// 初始化模板保护系统
46+
controller.InitTemplateMonitoring()
47+
log.Println("模板保护系统已初始化")
48+
49+
// 启动数据库操作监控
50+
db.GlobalDBManager.StartMonitoring()
51+
log.Println("数据库操作监控已启动")
52+
53+
// 启动健康监控器
54+
service.GlobalHealthMonitor.Start()
55+
log.Println("系统健康监控器已启动")
56+
4357
// 启动 singleton 包下的所有服务
4458
singleton.LoadSingleton()
4559

@@ -51,7 +65,12 @@ func initSystem() {
5165
singleton.SyncAllServerTrafficFromDB()
5266
} else {
5367
log.Println("使用BadgerDB,执行BadgerDB监控历史清理...")
68+
69+
// 使用数据库操作管理器包装BadgerDB操作,避免与前端请求争用资源
70+
done := db.GlobalDBManager.StartOperation("清理监控历史")
5471
count, err := singleton.CleanMonitorHistory()
72+
done() // 标记操作完成
73+
5574
if err != nil {
5675
log.Printf("BadgerDB监控历史清理失败: %v", err)
5776
} else {
@@ -230,6 +249,18 @@ func main() {
230249
// 停止内存监控器,防止它继续调用limitDataSize()造成锁竞争
231250
singleton.StopMemoryMonitor()
232251
log.Println("内存监控器已停止")
252+
253+
// 停止健康监控器
254+
service.GlobalHealthMonitor.Stop()
255+
log.Println("健康监控器已停止")
256+
257+
// 等待数据库操作完成
258+
dbOps := db.GlobalDBManager.GetRunningOperations()
259+
if len(dbOps) > 0 {
260+
log.Printf("等待 %d 个数据库操作完成...", len(dbOps))
261+
// 给数据库操作一些时间完成
262+
time.Sleep(3 * time.Second)
263+
}
233264

234265
// 停止定时任务
235266
if singleton.Cron != nil {

0 commit comments

Comments
 (0)