Skip to content

Commit 0ea1f1c

Browse files
committed
Update.
1 parent c801bea commit 0ea1f1c

File tree

6 files changed

+637
-63
lines changed

6 files changed

+637
-63
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,5 @@ MEMORY_CLEANUP_SAFETY.md
2929
MEMORY_LEAK_ROOT_CAUSE.md
3030
test_graceful_shutdown.sh
3131
test_memory_leak_fix.sh
32+
scripts/monitor_frontend.sh
33+
scripts/test_frontend_connectivity.sh

cmd/dashboard/controller/controller.go

Lines changed: 162 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import (
1919
"github.com/nicksnyder/go-i18n/v2/i18n"
2020

2121
"github.com/xos/serverstatus/model"
22-
"github.com/xos/serverstatus/pkg/mygin"
2322
"github.com/xos/serverstatus/pkg/utils"
2423
"github.com/xos/serverstatus/proto"
2524
"github.com/xos/serverstatus/service/rpc"
@@ -63,6 +62,7 @@ func corsMiddleware(c *gin.Context) {
6362
c.Next()
6463
}
6564

65+
// ServeWeb 启动Web服务器并配置路由
6666
func ServeWeb(port uint) *http.Server {
6767
gin.SetMode(gin.ReleaseMode)
6868

@@ -97,79 +97,132 @@ func ServeWeb(port uint) *http.Server {
9797

9898
// 添加Recovery中间件,过滤网络连接错误
9999
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-
}
100+
// 检查是否为broken pipe错误
101+
if errStr := fmt.Sprintf("%v", recovered); strings.Contains(errStr, "broken pipe") ||
102+
strings.Contains(errStr, "connection reset") ||
103+
strings.Contains(errStr, "use of closed network connection") {
104+
// 静默处理broken pipe错误,不中断其他请求处理
105+
c.AbortWithStatus(499) // 使用499表示客户端关闭连接
106+
return
109107
}
110-
// 其他错误正常处理
108+
109+
// 其他panic正常处理
110+
log.Printf("服务器Panic: %v", recovered)
111111
c.AbortWithStatus(http.StatusInternalServerError)
112112
}))
113113

114-
if singleton.Conf.Debug {
115-
gin.SetMode(gin.DebugMode)
114+
// 添加handleBrokenPipe中间件
115+
r.Use(handleBrokenPipe)
116+
117+
// 添加CORS中间件
118+
r.Use(corsMiddleware)
119+
120+
// 添加模板渲染保护中间件 - 这是防止前端页面无法访问的关键
121+
r.Use(TemplateRenderingSafeguard())
122+
123+
// 添加健康检查中间件
124+
r.Use(HealthCheckMiddleware())
125+
126+
// 添加pprof性能分析
127+
if singleton.Conf != nil && singleton.Conf.Debug {
116128
pprof.Register(r)
117129
}
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)
146130

147-
srv := &http.Server{
148-
Addr: fmt.Sprintf(":%d", port),
149-
ReadHeaderTimeout: time.Second * 5,
150-
Handler: r,
151-
}
152-
return srv
153-
}
131+
// 设置HTML模板和静态资源
132+
r.SetFuncMap(template.FuncMap{
133+
"tf": func(t time.Time) string {
134+
return t.Format("2006-01-02 15:04:05")
135+
},
136+
"fisher": func(t time.Time) string {
137+
return t.Format("2006/01/02 15:04")
138+
},
139+
"tf2": func(t time.Time) string {
140+
return t.In(singleton.Loc).Format("01/02 15:04")
141+
},
142+
"markdown": func(text string) template.HTML {
143+
// 简单地将markdown转为HTML
144+
return template.HTML(text)
145+
},
146+
"tag": func(text string) template.HTML {
147+
return template.HTML(`<` + text + `>`)
148+
},
149+
"trim": func(text string) string {
150+
return strings.TrimSpace(text)
151+
},
152+
"recent": func(t time.Time) bool {
153+
return time.Since(t).Seconds() <= 60
154+
},
155+
"find": func(slice []string, val string) int {
156+
for i, item := range slice {
157+
if item == val {
158+
return i
159+
}
160+
}
161+
return -1
162+
},
163+
"next": func(pos, length int) int {
164+
pos++
165+
if pos >= length {
166+
pos = 0
167+
}
168+
return pos
169+
},
170+
"prev": func(pos, length int) int {
171+
pos--
172+
if pos < 0 {
173+
pos = length - 1
174+
}
175+
return pos
176+
},
177+
"fs": func(bytes uint64) string {
178+
return bytefmt.ByteSize(bytes)
179+
},
180+
"tf_rfc3339": func(t time.Time) string {
181+
return t.Format(time.RFC3339)
182+
},
183+
"pb": func(p float64) string {
184+
return fmt.Sprintf("%.2f", p*100)
185+
},
186+
"isPositive": func(a float64) bool {
187+
return a > 0
188+
},
189+
"isNegative": func(a float64) bool {
190+
return a < 0
191+
},
192+
"abs": func(a float64) float64 {
193+
if a < 0 {
194+
return -a
195+
}
196+
return a
197+
},
198+
"roundTo": func(decimals int, num float64) float64 {
199+
f := math.Pow10(decimals)
200+
// 四舍五入
201+
return math.Round(num*f) / f
202+
},
203+
})
154204

155-
func routers(r *gin.Engine) {
156-
// 通用页面
157-
cp := commonPage{r: r}
158-
cp.serve()
159-
// 游客页面
160-
gp := guestPage{r}
161-
gp.serve()
162-
// 会员页面
163-
mp := &memberPage{r}
164-
mp.serve()
165-
// API
166-
api := r.Group("api")
167-
{
168-
ma := &memberAPI{api}
169-
ma.serve()
205+
// 静态资源
206+
r.StaticFS("/static", http.Dir("resource/static"))
207+
208+
// 健康检查路由
209+
r.GET("/health", HealthCheckHandler)
210+
r.GET("/system-health", SystemHealthHandler)
211+
212+
// 创建路由分组和控制器
213+
CreateControllers(r)
214+
215+
// 创建HTTP服务器
216+
server := &http.Server{
217+
Addr: fmt.Sprintf(":%d", port),
218+
Handler: r,
170219
}
220+
221+
return server
171222
}
172223

224+
// 已通过CreateControllers实现
225+
173226
func loadThirdPartyTemplates(tmpl *template.Template) *template.Template {
174227
ret := tmpl
175228
themes, err := os.ReadDir("resource/template")
@@ -396,6 +449,50 @@ var funcMap = template.FuncMap{
396449
},
397450
}
398451

452+
// CreateControllers 创建并初始化所有控制器
453+
func CreateControllers(engine *gin.Engine) *commonPage {
454+
// 创建通用页面控制器
455+
cp := &commonPage{
456+
r: engine,
457+
}
458+
cp.serve()
459+
460+
// 创建访客页面控制器
461+
gp := &guestPage{
462+
r: engine,
463+
}
464+
gp.serve()
465+
466+
// 创建OAuth2控制器
467+
oa := &oauth2controller{
468+
r: engine,
469+
}
470+
oa.serve()
471+
472+
// 创建会员页面控制器
473+
mp := &memberPage{
474+
r: engine,
475+
}
476+
mp.serve()
477+
478+
// 创建会员API控制器
479+
ma := &memberAPI{
480+
r: engine,
481+
}
482+
ma.serve()
483+
484+
// 创建APIv1控制器
485+
api := &apiV1{
486+
r: engine,
487+
}
488+
api.serve()
489+
490+
// 添加NAT网关中间件
491+
engine.NoRoute(natGateway)
492+
493+
return cp
494+
}
495+
399496
func natGateway(c *gin.Context) {
400497
natConfig := singleton.GetNATConfigByDomain(c.Request.Host)
401498
if natConfig == nil {
@@ -702,3 +799,5 @@ func formatBytes(bytes uint64) string {
702799
}
703800
return fmt.Sprintf("%.2f %cB", float64(bytes)/float64(div), "KMGTPE"[exp])
704801
}
802+
803+
// 健康检查相关函数已在health_check.go中定义

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 {
@@ -231,6 +250,18 @@ func main() {
231250
singleton.StopMemoryMonitor()
232251
log.Println("内存监控器已停止")
233252

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+
}
264+
234265
// 停止定时任务
235266
if singleton.Cron != nil {
236267
singleton.Cron.Stop()

0 commit comments

Comments
 (0)