@@ -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服务器并配置路由
6666func 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+
173226func 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+
399496func 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中定义
0 commit comments