Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d85c4c4a5 | ||
|
|
b1348eb545 | ||
|
|
753199a3b8 | ||
|
|
64ba3cd0a7 | ||
|
|
193455c675 | ||
|
|
dfb08caae1 | ||
|
|
7f7763606d | ||
|
|
77de1b44bc | ||
|
|
da6a3463ed | ||
|
|
6fa149519d | ||
|
|
8e67d3487e | ||
|
|
8033c6a0b7 | ||
|
|
abb108a83e | ||
|
|
cf9d9bd248 | ||
|
|
5e528c42c7 | ||
|
|
daa7e03240 | ||
|
|
ab39782c78 | ||
|
|
e24925b049 | ||
|
|
1d4245ee09 | ||
|
|
8df863a3e0 | ||
|
|
07e0382598 | ||
|
|
8dd509aa08 | ||
|
|
99ac12c9fd | ||
|
|
ebc45f9b8f | ||
|
|
3b5ee464a7 | ||
|
|
c4b2c47beb | ||
|
|
0835f13f28 | ||
|
|
ee3cda8aed | ||
|
|
604fc7d4c0 | ||
|
|
5c549261dd | ||
|
|
00041c0ebd | ||
|
|
dbfea9a00b | ||
|
|
e8b208edfa | ||
|
|
7d38634933 | ||
|
|
5bd4b57cbf | ||
|
|
fdb2fd8839 | ||
|
|
f3fac9325c |
@@ -4,7 +4,7 @@
|
||||
<br>
|
||||
<small><i>LOGO designed by <a href="https://xio.ng" target="_blank">熊大</a> .</i></small>
|
||||
<br><br>
|
||||
<img alt="GitHub release (with filter)" src="https://img.shields.io/github/v/release/naiba/nezha?color=brightgreen&style=for-the-badge&logo=github&label=Dashboard"> <img src="https://img.shields.io/github/v/release/nezhahq/agent?color=brightgreen&label=Agent&style=for-the-badge&logo=github"> <img src="https://img.shields.io/github/actions/workflow/status/nezhahq/agent/agent.yml?label=Agent%20CI&logo=github&style=for-the-badge"> <img src="https://img.shields.io/badge/Installer-v0.15.6-brightgreen?style=for-the-badge&logo=linux">
|
||||
<img alt="GitHub release (with filter)" src="https://img.shields.io/github/v/release/naiba/nezha?color=brightgreen&style=for-the-badge&logo=github&label=Dashboard"> <img src="https://img.shields.io/github/v/release/nezhahq/agent?color=brightgreen&label=Agent&style=for-the-badge&logo=github"> <img src="https://img.shields.io/github/actions/workflow/status/nezhahq/agent/agent.yml?label=Agent%20CI&logo=github&style=for-the-badge"> <img src="https://img.shields.io/badge/Installer-v0.15.9-brightgreen?style=for-the-badge&logo=linux">
|
||||
<br>
|
||||
<br>
|
||||
<p>:trollface: <b>Nezha Monitoring: Self-hostable, lightweight, servers and websites monitoring and O&M tool.</b></p>
|
||||
@@ -38,123 +38,48 @@ You can change the dashboard language in the settings page (`/setting`) after th
|
||||
|
||||
## Contributors
|
||||
|
||||
<!--GAMFC_DELIMITER--><a href="https://github.com/naiba" title="naiba">
|
||||
<img src="https://avatars.githubusercontent.com/u/29243953?v=4" width="50;" alt="naiba"/>
|
||||
</a>
|
||||
<a href="https://github.com/AkkiaS7" title="Akkia">
|
||||
<img src="https://avatars.githubusercontent.com/u/68485070?v=4" width="50;" alt="Akkia"/>
|
||||
</a>
|
||||
<a href="https://github.com/Erope" title="卖女孩的小火柴">
|
||||
<img src="https://avatars.githubusercontent.com/u/44471469?v=4" width="50;" alt="卖女孩的小火柴"/>
|
||||
</a>
|
||||
<a href="https://github.com/dysf888" title="黑歌">
|
||||
<img src="https://avatars.githubusercontent.com/u/47450409?v=4" width="50;" alt="黑歌"/>
|
||||
</a>
|
||||
<a href="https://github.com/MikoyChinese" title="MikoyChinese">
|
||||
<img src="https://avatars.githubusercontent.com/u/22676744?v=4" width="50;" alt="MikoyChinese"/>
|
||||
</a>
|
||||
<a href="https://github.com/JackieSung4ev" title="JackieSung4ev">
|
||||
<img src="https://avatars.githubusercontent.com/u/24974735?v=4" width="50;" alt="JackieSung4ev"/>
|
||||
</a>
|
||||
<a href="https://github.com/ilay1678" title="我若为王">
|
||||
<img src="https://avatars.githubusercontent.com/u/7021399?v=4" width="50;" alt="我若为王"/>
|
||||
</a>
|
||||
<a href="https://github.com/lemoeo" title="Lemoe">
|
||||
<img src="https://avatars.githubusercontent.com/u/18618627?v=4" width="50;" alt="Lemoe"/>
|
||||
</a>
|
||||
<a href="https://github.com/cantoblanco" title="Kris">
|
||||
<img src="https://avatars.githubusercontent.com/u/116849421?v=4" width="50;" alt="Kris"/>
|
||||
</a>
|
||||
<a href="https://github.com/spiritLHLS" title="spiritlhl">
|
||||
<img src="https://avatars.githubusercontent.com/u/103393591?v=4" width="50;" alt="spiritlhl"/>
|
||||
</a>
|
||||
<a href="https://github.com/liuyanxi975" title="刘颜溪">
|
||||
<img src="https://avatars.githubusercontent.com/u/24417037?v=4" width="50;" alt="刘颜溪"/>
|
||||
</a>
|
||||
<a href="https://github.com/CosmosZ-code" title="CosmosZ-code">
|
||||
<img src="https://avatars.githubusercontent.com/u/81398224?v=4" width="50;" alt="CosmosZ-code"/>
|
||||
</a>
|
||||
<a href="https://github.com/uubulb" title="UUBulb">
|
||||
<img src="https://avatars.githubusercontent.com/u/35923940?v=4" width="50;" alt="UUBulb"/>
|
||||
</a>
|
||||
<a href="https://github.com/hhhkkk520" title="Kris">
|
||||
<img src="https://avatars.githubusercontent.com/u/52115472?v=4" width="50;" alt="Kris"/>
|
||||
</a>
|
||||
<a href="https://github.com/Mmx233" title="Mmx">
|
||||
<img src="https://avatars.githubusercontent.com/u/36563672?v=4" width="50;" alt="Mmx"/>
|
||||
</a>
|
||||
<a href="https://github.com/rootmelo92118" title="rootmelo92118">
|
||||
<img src="https://avatars.githubusercontent.com/u/32770959?v=4" width="50;" alt="rootmelo92118"/>
|
||||
</a>
|
||||
<a href="https://github.com/1ridic" title="1ridic">
|
||||
<img src="https://avatars.githubusercontent.com/u/88495501?v=4" width="50;" alt="1ridic"/>
|
||||
</a>
|
||||
<a href="https://github.com/iilemon" title="Sean">
|
||||
<img src="https://avatars.githubusercontent.com/u/33201711?v=4" width="50;" alt="Sean"/>
|
||||
</a>
|
||||
<a href="https://github.com/fscarmen" title="fscarmen">
|
||||
<img src="https://avatars.githubusercontent.com/u/62703343?v=4" width="50;" alt="fscarmen"/>
|
||||
</a>
|
||||
<a href="https://github.com/ch8o" title="no-name-now">
|
||||
<img src="https://avatars.githubusercontent.com/u/9103372?v=4" width="50;" alt="no-name-now"/>
|
||||
</a>
|
||||
<a href="https://github.com/Creling" title="Creling">
|
||||
<img src="https://avatars.githubusercontent.com/u/43109504?v=4" width="50;" alt="Creling"/>
|
||||
</a>
|
||||
<a href="https://github.com/coreff" title="Core F">
|
||||
<img src="https://avatars.githubusercontent.com/u/38347122?v=4" width="50;" alt="Core F"/>
|
||||
</a>
|
||||
<a href="https://github.com/colour93" title="玖叁">
|
||||
<img src="https://avatars.githubusercontent.com/u/64313711?v=4" width="50;" alt="玖叁"/>
|
||||
</a>
|
||||
<a href="https://github.com/ysicing" title="缘生">
|
||||
<img src="https://avatars.githubusercontent.com/u/8605565?v=4" width="50;" alt="缘生"/>
|
||||
</a>
|
||||
<a href="https://github.com/unclezs" title="unclezs">
|
||||
<img src="https://avatars.githubusercontent.com/u/42318775?v=4" width="50;" alt="unclezs"/>
|
||||
</a>
|
||||
<a href="https://github.com/nap0o" title="nap0o">
|
||||
<img src="https://avatars.githubusercontent.com/u/144927971?v=4" width="50;" alt="nap0o"/>
|
||||
</a>
|
||||
<a href="https://github.com/yuanweize" title="I">
|
||||
<img src="https://avatars.githubusercontent.com/u/30067203?v=4" width="50;" alt="I"/>
|
||||
</a>
|
||||
<a href="https://github.com/guoyongchang" title="guoyongchang">
|
||||
<img src="https://avatars.githubusercontent.com/u/10484506?v=4" width="50;" alt="guoyongchang"/>
|
||||
</a>
|
||||
<a href="https://github.com/acgpiano" title="Acgpiano">
|
||||
<img src="https://avatars.githubusercontent.com/u/15900800?v=4" width="50;" alt="Acgpiano"/>
|
||||
</a>
|
||||
<a href="https://github.com/adminsama" title="adminsama">
|
||||
<img src="https://avatars.githubusercontent.com/u/60880076?v=4" width="50;" alt="adminsama"/>
|
||||
</a>
|
||||
<a href="https://github.com/hmsjy2017" title="Tony">
|
||||
<img src="https://avatars.githubusercontent.com/u/42692274?v=4" width="50;" alt="Tony"/>
|
||||
</a>
|
||||
<a href="https://github.com/nickfox-taterli" title="Tater Li">
|
||||
<img src="https://avatars.githubusercontent.com/u/19658596?v=4" width="50;" alt="Tater Li"/>
|
||||
</a>
|
||||
<a href="https://github.com/MartijnLindeman" title="Martijn Lindeman">
|
||||
<img src="https://avatars.githubusercontent.com/u/78365708?v=4" width="50;" alt="Martijn Lindeman"/>
|
||||
</a>
|
||||
<a href="https://github.com/KorenKrita" title="KorenKrita">
|
||||
<img src="https://avatars.githubusercontent.com/u/22239339?v=4" width="50;" alt="KorenKrita"/>
|
||||
</a>
|
||||
<a href="https://github.com/lvgj-stack" title="Ko no dio">
|
||||
<img src="https://avatars.githubusercontent.com/u/38449861?v=4" width="50;" alt="Ko no dio"/>
|
||||
</a>
|
||||
<a href="https://github.com/techotaku" title="Ian Li">
|
||||
<img src="https://avatars.githubusercontent.com/u/1948179?v=4" width="50;" alt="Ian Li"/>
|
||||
</a>
|
||||
<a href="https://github.com/GreenTeodoro839" title="GreenTeodoro839">
|
||||
<img src="https://avatars.githubusercontent.com/u/77104800?v=4" width="50;" alt="GreenTeodoro839"/>
|
||||
</a>
|
||||
<a href="https://github.com/Es-dese" title="Esdese">
|
||||
<img src="https://avatars.githubusercontent.com/u/71542548?v=4" width="50;" alt="Esdese"/>
|
||||
</a>
|
||||
<a href="https://github.com/wwng2333" title="Crazy">
|
||||
<img src="https://avatars.githubusercontent.com/u/17147265?v=4" width="50;" alt="Crazy"/>
|
||||
</a><!--GAMFC_DELIMITER_END-->
|
||||
<!--GAMFC_DELIMITER--><a href="https://github.com/naiba" title="naiba"><img src="https://avatars.githubusercontent.com/u/29243953?v=4" width="50;" alt="naiba"/></a>
|
||||
<a href="https://github.com/AkkiaS7" title="Akkia"><img src="https://avatars.githubusercontent.com/u/68485070?v=4" width="50;" alt="Akkia"/></a>
|
||||
<a href="https://github.com/Erope" title="卖女孩的小火柴"><img src="https://avatars.githubusercontent.com/u/44471469?v=4" width="50;" alt="卖女孩的小火柴"/></a>
|
||||
<a href="https://github.com/dysf888" title="黑歌"><img src="https://avatars.githubusercontent.com/u/47450409?v=4" width="50;" alt="黑歌"/></a>
|
||||
<a href="https://github.com/MikoyChinese" title="MikoyChinese"><img src="https://avatars.githubusercontent.com/u/22676744?v=4" width="50;" alt="MikoyChinese"/></a>
|
||||
<a href="https://github.com/JackieSung4ev" title="JackieSung4ev"><img src="https://avatars.githubusercontent.com/u/24974735?v=4" width="50;" alt="JackieSung4ev"/></a>
|
||||
<a href="https://github.com/lemoeo" title="Lemoe"><img src="https://avatars.githubusercontent.com/u/18618627?v=4" width="50;" alt="Lemoe"/></a>
|
||||
<a href="https://github.com/cantoblanco" title="Kris"><img src="https://avatars.githubusercontent.com/u/116849421?v=4" width="50;" alt="Kris"/></a>
|
||||
<a href="https://github.com/nap0o" title="nap0o"><img src="https://avatars.githubusercontent.com/u/144927971?v=4" width="50;" alt="nap0o"/></a>
|
||||
<a href="https://github.com/spiritLHLS" title="spiritlhl"><img src="https://avatars.githubusercontent.com/u/103393591?v=4" width="50;" alt="spiritlhl"/></a>
|
||||
<a href="https://github.com/liuyanxi975" title="刘颜溪"><img src="https://avatars.githubusercontent.com/u/24417037?v=4" width="50;" alt="刘颜溪"/></a>
|
||||
<a href="https://github.com/CosmosZ-code" title="CosmosZ-code"><img src="https://avatars.githubusercontent.com/u/81398224?v=4" width="50;" alt="CosmosZ-code"/></a>
|
||||
<a href="https://github.com/uubulb" title="UUBulb"><img src="https://avatars.githubusercontent.com/u/35923940?v=4" width="50;" alt="UUBulb"/></a>
|
||||
<a href="https://github.com/lvgj-stack" title="Ko no dio"><img src="https://avatars.githubusercontent.com/u/38449861?v=4" width="50;" alt="Ko no dio"/></a>
|
||||
<a href="https://github.com/hhhkkk520" title="Kris"><img src="https://avatars.githubusercontent.com/u/52115472?v=4" width="50;" alt="Kris"/></a>
|
||||
<a href="https://github.com/1ridic" title="1ridic"><img src="https://avatars.githubusercontent.com/u/88495501?v=4" width="50;" alt="1ridic"/></a>
|
||||
<a href="https://github.com/Mmx233" title="Mmx"><img src="https://avatars.githubusercontent.com/u/36563672?v=4" width="50;" alt="Mmx"/></a>
|
||||
<a href="https://github.com/rootmelo92118" title="rootmelo92118"><img src="https://avatars.githubusercontent.com/u/32770959?v=4" width="50;" alt="rootmelo92118"/></a>
|
||||
<a href="https://github.com/iilemon" title="Sean"><img src="https://avatars.githubusercontent.com/u/33201711?v=4" width="50;" alt="Sean"/></a>
|
||||
<a href="https://github.com/fscarmen" title="fscarmen"><img src="https://avatars.githubusercontent.com/u/62703343?v=4" width="50;" alt="fscarmen"/></a>
|
||||
<a href="https://github.com/ch8o" title="no-name-now"><img src="https://avatars.githubusercontent.com/u/9103372?v=4" width="50;" alt="no-name-now"/></a>
|
||||
<a href="https://github.com/DarcJC" title="Darc Z."><img src="https://avatars.githubusercontent.com/u/53445798?v=4" width="50;" alt="Darc Z."/></a>
|
||||
<a href="https://github.com/Creling" title="Creling"><img src="https://avatars.githubusercontent.com/u/43109504?v=4" width="50;" alt="Creling"/></a>
|
||||
<a href="https://github.com/coreff" title="Core F"><img src="https://avatars.githubusercontent.com/u/38347122?v=4" width="50;" alt="Core F"/></a>
|
||||
<a href="https://github.com/colour93" title="玖叁"><img src="https://avatars.githubusercontent.com/u/64313711?v=4" width="50;" alt="玖叁"/></a>
|
||||
<a href="https://github.com/arkylin" title="凌"><img src="https://avatars.githubusercontent.com/u/35104502?v=4" width="50;" alt="凌"/></a>
|
||||
<a href="https://github.com/ysicing" title="缘生"><img src="https://avatars.githubusercontent.com/u/8605565?v=4" width="50;" alt="缘生"/></a>
|
||||
<a href="https://github.com/xykt" title="xykt"><img src="https://avatars.githubusercontent.com/u/152045469?v=4" width="50;" alt="xykt"/></a>
|
||||
<a href="https://github.com/unclezs" title="unclezs"><img src="https://avatars.githubusercontent.com/u/42318775?v=4" width="50;" alt="unclezs"/></a>
|
||||
<a href="https://github.com/yuanweize" title="I"><img src="https://avatars.githubusercontent.com/u/30067203?v=4" width="50;" alt="I"/></a>
|
||||
<a href="https://github.com/guoyongchang" title="guoyongchang"><img src="https://avatars.githubusercontent.com/u/10484506?v=4" width="50;" alt="guoyongchang"/></a>
|
||||
<a href="https://github.com/acgpiano" title="Acgpiano"><img src="https://avatars.githubusercontent.com/u/15900800?v=4" width="50;" alt="Acgpiano"/></a>
|
||||
<a href="https://github.com/adminsama" title="adminsama"><img src="https://avatars.githubusercontent.com/u/60880076?v=4" width="50;" alt="adminsama"/></a>
|
||||
<a href="https://github.com/hmsjy2017" title="Tony"><img src="https://avatars.githubusercontent.com/u/42692274?v=4" width="50;" alt="Tony"/></a>
|
||||
<a href="https://github.com/nickfox-taterli" title="Tater Li"><img src="https://avatars.githubusercontent.com/u/19658596?v=4" width="50;" alt="Tater Li"/></a>
|
||||
<a href="https://github.com/MartijnLindeman" title="Martijn Lindeman"><img src="https://avatars.githubusercontent.com/u/78365708?v=4" width="50;" alt="Martijn Lindeman"/></a>
|
||||
<a href="https://github.com/KorenKrita" title="KorenKrita"><img src="https://avatars.githubusercontent.com/u/22239339?v=4" width="50;" alt="KorenKrita"/></a>
|
||||
<a href="https://github.com/techotaku" title="Ian Li"><img src="https://avatars.githubusercontent.com/u/1948179?v=4" width="50;" alt="Ian Li"/></a>
|
||||
<a href="https://github.com/HsukqiLee" title="HsukqiLee"><img src="https://avatars.githubusercontent.com/u/79034142?v=4" width="50;" alt="HsukqiLee"/></a>
|
||||
<a href="https://github.com/GreenTeodoro839" title="GreenTeodoro839"><img src="https://avatars.githubusercontent.com/u/77104800?v=4" width="50;" alt="GreenTeodoro839"/></a>
|
||||
<a href="https://github.com/Es-dese" title="Esdese"><img src="https://avatars.githubusercontent.com/u/71542548?v=4" width="50;" alt="Esdese"/></a>
|
||||
<a href="https://github.com/wwng2333" title="Crazy"><img src="https://avatars.githubusercontent.com/u/17147265?v=4" width="50;" alt="Crazy"/></a><!--GAMFC_DELIMITER_END-->
|
||||
|
||||
## Star History
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/naiba/nezha/model"
|
||||
"github.com/naiba/nezha/pkg/mygin"
|
||||
"github.com/naiba/nezha/service/singleton"
|
||||
)
|
||||
@@ -16,18 +17,31 @@ type apiV1 struct {
|
||||
|
||||
func (v *apiV1) serve() {
|
||||
r := v.r.Group("")
|
||||
// API
|
||||
// 强制认证的 API
|
||||
r.Use(mygin.Authorize(mygin.AuthorizeOption{
|
||||
Member: true,
|
||||
IsPage: false,
|
||||
AllowAPI: true,
|
||||
Msg: "访问此接口需要认证",
|
||||
Btn: "点此登录",
|
||||
Redirect: "/login",
|
||||
MemberOnly: true,
|
||||
AllowAPI: true,
|
||||
IsPage: false,
|
||||
Msg: "访问此接口需要认证",
|
||||
Btn: "点此登录",
|
||||
Redirect: "/login",
|
||||
}))
|
||||
r.GET("/server/list", v.serverList)
|
||||
r.GET("/server/details", v.serverDetails)
|
||||
// 不强制认证的 API
|
||||
mr := v.r.Group("monitor")
|
||||
mr.Use(mygin.Authorize(mygin.AuthorizeOption{
|
||||
MemberOnly: false,
|
||||
IsPage: false,
|
||||
AllowAPI: true,
|
||||
Msg: "访问此接口需要认证",
|
||||
Btn: "点此登录",
|
||||
Redirect: "/login",
|
||||
}))
|
||||
mr.Use(mygin.ValidateViewPassword(mygin.ValidateViewPasswordOption{
|
||||
IsPage: false,
|
||||
AbortWhenFail: true,
|
||||
}))
|
||||
mr.GET("/:id", v.monitorHistoriesById)
|
||||
}
|
||||
|
||||
@@ -84,5 +98,15 @@ func (v *apiV1) monitorHistoriesById(c *gin.Context) {
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
_, isMember := c.Get(model.CtxKeyAuthorizedUser)
|
||||
_, isViewPasswordVerfied := c.Get(model.CtxKeyViewPasswordVerified)
|
||||
authorized := isMember || isViewPasswordVerfied
|
||||
|
||||
if server.HideForGuest && !authorized {
|
||||
c.AbortWithStatusJSON(403, gin.H{"code": 403, "message": "需要认证"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, singleton.MonitorAPI.GetMonitorHistories(map[string]any{"server_id": server.ID}))
|
||||
}
|
||||
|
||||
@@ -44,9 +44,13 @@ type commonPage struct {
|
||||
func (cp *commonPage) serve() {
|
||||
cr := cp.r.Group("")
|
||||
cr.Use(mygin.Authorize(mygin.AuthorizeOption{}))
|
||||
cr.GET("/terminal/:id", cp.terminal)
|
||||
cr.Use(mygin.PreferredTheme)
|
||||
cr.POST("/view-password", cp.issueViewPassword)
|
||||
cr.Use(cp.checkViewPassword) // 前端查看密码鉴权
|
||||
cr.GET("/terminal/:id", cp.terminal)
|
||||
cr.Use(mygin.ValidateViewPassword(mygin.ValidateViewPasswordOption{
|
||||
IsPage: true,
|
||||
AbortWhenFail: true,
|
||||
}))
|
||||
cr.GET("/", cp.home)
|
||||
cr.GET("/service", cp.service)
|
||||
// TODO: 界面直接跳转使用该接口
|
||||
@@ -63,6 +67,7 @@ type viewPasswordForm struct {
|
||||
func (p *commonPage) issueViewPassword(c *gin.Context) {
|
||||
var vpf viewPasswordForm
|
||||
err := c.ShouldBind(&vpf)
|
||||
log.Println("bingo", vpf)
|
||||
var hash []byte
|
||||
if err == nil && vpf.Password != singleton.Conf.Site.ViewPassword {
|
||||
err = errors.New(singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "WrongAccessPassword"}))
|
||||
@@ -85,31 +90,6 @@ func (p *commonPage) issueViewPassword(c *gin.Context) {
|
||||
c.Redirect(http.StatusFound, c.Request.Referer())
|
||||
}
|
||||
|
||||
func (p *commonPage) checkViewPassword(c *gin.Context) {
|
||||
if singleton.Conf.Site.ViewPassword == "" {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
if _, authorized := c.Get(model.CtxKeyAuthorizedUser); authorized {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
|
||||
// 验证查看密码
|
||||
viewPassword, _ := c.Cookie(singleton.Conf.Site.CookieName + "-vp")
|
||||
if err := bcrypt.CompareHashAndPassword([]byte(viewPassword), []byte(singleton.Conf.Site.ViewPassword)); err != nil {
|
||||
c.HTML(http.StatusOK, "theme-"+singleton.Conf.Site.Theme+"/viewpassword", mygin.CommonEnvironment(c, gin.H{
|
||||
"Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "VerifyPassword"}),
|
||||
"CustomCode": singleton.Conf.Site.CustomCode,
|
||||
}))
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
c.Set(model.CtxKeyViewPasswordVerified, true)
|
||||
c.Next()
|
||||
}
|
||||
|
||||
func (p *commonPage) service(c *gin.Context) {
|
||||
res, _, _ := p.requestGroup.Do("servicePage", func() (interface{}, error) {
|
||||
singleton.AlertsLock.RLock()
|
||||
@@ -118,12 +98,17 @@ func (p *commonPage) service(c *gin.Context) {
|
||||
var statsStore map[uint64]model.CycleTransferStats
|
||||
copier.Copy(&stats, singleton.ServiceSentinelShared.LoadStats())
|
||||
copier.Copy(&statsStore, singleton.AlertsCycleTransferStatsStore)
|
||||
for k, service := range stats {
|
||||
if !service.Monitor.EnableShowInService {
|
||||
delete(stats, k)
|
||||
}
|
||||
}
|
||||
return []interface {
|
||||
}{
|
||||
stats, statsStore,
|
||||
}, nil
|
||||
})
|
||||
c.HTML(http.StatusOK, "theme-"+singleton.Conf.Site.Theme+"/service", mygin.CommonEnvironment(c, gin.H{
|
||||
c.HTML(http.StatusOK, mygin.GetPreferredTheme(c, "/service"), mygin.CommonEnvironment(c, gin.H{
|
||||
"Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "ServicesStatus"}),
|
||||
"Services": res.([]interface{})[0],
|
||||
"CycleTransferStats": res.([]interface{})[1],
|
||||
@@ -229,7 +214,7 @@ func (cp *commonPage) network(c *gin.Context) {
|
||||
Servers: servers,
|
||||
})
|
||||
|
||||
c.HTML(http.StatusOK, "theme-"+singleton.Conf.Site.Theme+"/network", mygin.CommonEnvironment(c, gin.H{
|
||||
c.HTML(http.StatusOK, mygin.GetPreferredTheme(c, "/network"), mygin.CommonEnvironment(c, gin.H{
|
||||
"Servers": string(serversBytes),
|
||||
"MonitorInfos": string(monitorInfos),
|
||||
"CustomCode": singleton.Conf.Site.CustomCode,
|
||||
@@ -275,7 +260,7 @@ func (cp *commonPage) home(c *gin.Context) {
|
||||
}, true)
|
||||
return
|
||||
}
|
||||
c.HTML(http.StatusOK, "theme-"+singleton.Conf.Site.Theme+"/home", mygin.CommonEnvironment(c, gin.H{
|
||||
c.HTML(http.StatusOK, mygin.GetPreferredTheme(c, "/home"), mygin.CommonEnvironment(c, gin.H{
|
||||
"Servers": string(stat),
|
||||
"CustomCode": singleton.Conf.Site.CustomCode,
|
||||
}))
|
||||
|
||||
@@ -19,11 +19,11 @@ type guestPage struct {
|
||||
func (gp *guestPage) serve() {
|
||||
gr := gp.r.Group("")
|
||||
gr.Use(mygin.Authorize(mygin.AuthorizeOption{
|
||||
Guest: true,
|
||||
IsPage: true,
|
||||
Msg: "您已登录",
|
||||
Btn: "返回首页",
|
||||
Redirect: "/",
|
||||
GuestOnly: true,
|
||||
IsPage: true,
|
||||
Msg: "您已登录",
|
||||
Btn: "返回首页",
|
||||
Redirect: "/",
|
||||
}))
|
||||
|
||||
gr.GET("/login", gp.login)
|
||||
|
||||
@@ -28,11 +28,11 @@ type memberAPI struct {
|
||||
func (ma *memberAPI) serve() {
|
||||
mr := ma.r.Group("")
|
||||
mr.Use(mygin.Authorize(mygin.AuthorizeOption{
|
||||
Member: true,
|
||||
IsPage: false,
|
||||
Msg: "访问此接口需要登录",
|
||||
Btn: "点此登录",
|
||||
Redirect: "/login",
|
||||
MemberOnly: true,
|
||||
IsPage: false,
|
||||
Msg: "访问此接口需要登录",
|
||||
Btn: "点此登录",
|
||||
Redirect: "/login",
|
||||
}))
|
||||
|
||||
mr.GET("/search-server", ma.searchServer)
|
||||
@@ -300,6 +300,8 @@ type serverForm struct {
|
||||
Tag string
|
||||
Note string
|
||||
HideForGuest string
|
||||
EnableDDNS string
|
||||
DDNSDomain string
|
||||
}
|
||||
|
||||
func (ma *memberAPI) addOrEditServer(c *gin.Context) {
|
||||
@@ -315,6 +317,8 @@ func (ma *memberAPI) addOrEditServer(c *gin.Context) {
|
||||
s.Tag = sf.Tag
|
||||
s.Note = sf.Note
|
||||
s.HideForGuest = sf.HideForGuest == "on"
|
||||
s.EnableDDNS = sf.EnableDDNS == "on"
|
||||
s.DDNSDomain = sf.DDNSDomain
|
||||
if s.ID == 0 {
|
||||
s.Secret, err = utils.GenerateRandomString(18)
|
||||
if err == nil {
|
||||
@@ -440,10 +444,12 @@ func (ma *memberAPI) addOrEditMonitor(c *gin.Context) {
|
||||
err = singleton.DB.Save(&m).Error
|
||||
}
|
||||
}
|
||||
if m.Cover == 0 {
|
||||
err = singleton.DB.Unscoped().Delete(&model.MonitorHistory{}, "monitor_id = ? and server_id in (?)", m.ID, strings.Split(m.SkipServersRaw[1:len(m.SkipServersRaw)-1], ",")).Error
|
||||
} else {
|
||||
err = singleton.DB.Unscoped().Delete(&model.MonitorHistory{}, "monitor_id = ? and server_id not in (?)", m.ID, strings.Split(m.SkipServersRaw[1:len(m.SkipServersRaw)-1], ",")).Error
|
||||
if err == nil {
|
||||
if m.Cover == 0 {
|
||||
err = singleton.DB.Unscoped().Delete(&model.MonitorHistory{}, "monitor_id = ? and server_id in (?)", m.ID, strings.Split(m.SkipServersRaw[1:len(m.SkipServersRaw)-1], ",")).Error
|
||||
} else {
|
||||
err = singleton.DB.Unscoped().Delete(&model.MonitorHistory{}, "monitor_id = ? and server_id not in (?)", m.ID, strings.Split(m.SkipServersRaw[1:len(m.SkipServersRaw)-1], ",")).Error
|
||||
}
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
@@ -852,8 +858,9 @@ type settingForm struct {
|
||||
GRPCHost string
|
||||
Cover uint8
|
||||
|
||||
EnableIPChangeNotification string
|
||||
EnablePlainIPInNotification string
|
||||
EnableIPChangeNotification string
|
||||
EnablePlainIPInNotification string
|
||||
DisableSwitchTemplateInFrontend string
|
||||
}
|
||||
|
||||
func (ma *memberAPI) updateSetting(c *gin.Context) {
|
||||
@@ -901,6 +908,7 @@ func (ma *memberAPI) updateSetting(c *gin.Context) {
|
||||
singleton.Conf.Language = sf.Language
|
||||
singleton.Conf.EnableIPChangeNotification = sf.EnableIPChangeNotification == "on"
|
||||
singleton.Conf.EnablePlainIPInNotification = sf.EnablePlainIPInNotification == "on"
|
||||
singleton.Conf.DisableSwitchTemplateInFrontend = sf.DisableSwitchTemplateInFrontend == "on"
|
||||
singleton.Conf.Cover = sf.Cover
|
||||
singleton.Conf.GRPCHost = sf.GRPCHost
|
||||
singleton.Conf.IgnoredIPNotification = sf.IgnoredIPNotification
|
||||
|
||||
@@ -17,11 +17,11 @@ type memberPage struct {
|
||||
func (mp *memberPage) serve() {
|
||||
mr := mp.r.Group("")
|
||||
mr.Use(mygin.Authorize(mygin.AuthorizeOption{
|
||||
Member: true,
|
||||
IsPage: true,
|
||||
Msg: singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "YouAreNotAuthorized"}),
|
||||
Btn: singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "Login"}),
|
||||
Redirect: "/login",
|
||||
MemberOnly: true,
|
||||
IsPage: true,
|
||||
Msg: singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "YouAreNotAuthorized"}),
|
||||
Btn: singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "Login"}),
|
||||
Redirect: "/login",
|
||||
}))
|
||||
mr.GET("/server", mp.server)
|
||||
mr.GET("/monitor", mp.monitor)
|
||||
@@ -81,7 +81,6 @@ func (mp *memberPage) setting(c *gin.Context) {
|
||||
c.HTML(http.StatusOK, "dashboard-"+singleton.Conf.Site.DashboardTheme+"/setting", mygin.CommonEnvironment(c, gin.H{
|
||||
"Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "Settings"}),
|
||||
"Languages": model.Languages,
|
||||
"Themes": model.Themes,
|
||||
"DashboardThemes": model.DashboardThemes,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ go 1.21
|
||||
toolchain go1.21.3
|
||||
|
||||
require (
|
||||
code.cloudfoundry.org/bytefmt v0.0.0-20231017140541-3b893ed0421b
|
||||
code.gitea.io/sdk/gitea v0.16.0
|
||||
code.cloudfoundry.org/bytefmt v0.0.0-20240322144145-4816192cb114
|
||||
code.gitea.io/sdk/gitea v0.17.1
|
||||
github.com/BurntSushi/toml v1.3.2
|
||||
github.com/gin-contrib/pprof v1.4.0
|
||||
github.com/gin-gonic/gin v1.9.1
|
||||
@@ -15,32 +15,30 @@ require (
|
||||
github.com/hashicorp/go-uuid v1.0.3
|
||||
github.com/jinzhu/copier v0.4.0
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/nicksnyder/go-i18n/v2 v2.2.2
|
||||
github.com/nicksnyder/go-i18n/v2 v2.4.0
|
||||
github.com/ory/graceful v0.1.3
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/samber/lo v1.38.1
|
||||
github.com/samber/lo v1.39.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/spf13/viper v1.17.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/xanzy/go-gitlab v0.94.0
|
||||
golang.org/x/crypto v0.17.0
|
||||
golang.org/x/oauth2 v0.14.0
|
||||
golang.org/x/sync v0.5.0
|
||||
github.com/spf13/viper v1.18.2
|
||||
github.com/xanzy/go-gitlab v0.100.0
|
||||
golang.org/x/crypto v0.21.0
|
||||
golang.org/x/oauth2 v0.18.0
|
||||
golang.org/x/sync v0.6.0
|
||||
golang.org/x/text v0.14.0
|
||||
google.golang.org/grpc v1.59.0
|
||||
google.golang.org/protobuf v1.31.0
|
||||
gorm.io/driver/sqlite v1.5.4
|
||||
gorm.io/gorm v1.25.5
|
||||
google.golang.org/grpc v1.62.1
|
||||
google.golang.org/protobuf v1.33.0
|
||||
gorm.io/driver/sqlite v1.5.5
|
||||
gorm.io/gorm v1.25.8
|
||||
sigs.k8s.io/yaml v1.4.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/bytedance/sonic v1.9.1 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/davidmz/go-pageant v1.0.2 // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-fed/httpsig v1.1.0 // indirect
|
||||
@@ -66,12 +64,11 @@ require (
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/sagikazarmark/locafero v0.3.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.10.0 // indirect
|
||||
github.com/spf13/cast v1.5.1 // indirect
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/spf13/cast v1.6.0 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||
@@ -79,11 +76,11 @@ require (
|
||||
go.uber.org/multierr v1.9.0 // indirect
|
||||
golang.org/x/arch v0.3.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||
golang.org/x/net v0.18.0 // indirect
|
||||
golang.org/x/sys v0.15.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 // indirect
|
||||
golang.org/x/net v0.22.0 // indirect
|
||||
golang.org/x/sys v0.18.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
@@ -1,64 +1,15 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
||||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
|
||||
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
|
||||
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
|
||||
code.cloudfoundry.org/bytefmt v0.0.0-20231017140541-3b893ed0421b h1:/2OEIBwZAaJ8n8iTXrM4v/+bdyLDTLwcW6RZtkO4+r0=
|
||||
code.cloudfoundry.org/bytefmt v0.0.0-20231017140541-3b893ed0421b/go.mod h1:CKNYSQxmKcMCNIKoRG5rRR4AIgJMIoK65ya+Z5xHnk4=
|
||||
code.gitea.io/sdk/gitea v0.16.0 h1:gAfssETO1Hv9QbE+/nhWu7EjoFQYKt6kPoyDytQgw00=
|
||||
code.gitea.io/sdk/gitea v0.16.0/go.mod h1:ndkDk99BnfiUCCYEUhpNzi0lpmApXlwRFqClBlOlEBg=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
code.cloudfoundry.org/bytefmt v0.0.0-20240322144145-4816192cb114 h1:7KJsK0gPah7XfO46ARJA2TUXHZaJEnPI80mzxQLre68=
|
||||
code.cloudfoundry.org/bytefmt v0.0.0-20240322144145-4816192cb114/go.mod h1:TNAH6xe5WOszc47+zM7NnpaJWBR8oYt12BUf4WLIZ98=
|
||||
code.gitea.io/sdk/gitea v0.17.1 h1:3jCPOG2ojbl8AcfaUCRYLT5MUcBMFwS0OSK2mA5Zok8=
|
||||
code.gitea.io/sdk/gitea v0.17.1/go.mod h1:aCnBqhHpoEWA180gMbaCtdX9Pl6BWBAuuP2miadoTNM=
|
||||
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
|
||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
|
||||
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -66,18 +17,12 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davidmz/go-pageant v1.0.2 h1:bPblRCh5jGU+Uptpz6LgMZGD5hJoOt7otgT454WvHn0=
|
||||
github.com/davidmz/go-pageant v1.0.2/go.mod h1:P2EDDnMqIwG5Rrp05dTRITj9z2zpGcD9efWSkTNKLIE=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w=
|
||||
github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg=
|
||||
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
|
||||
github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
||||
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
||||
github.com/gin-contrib/pprof v1.4.0 h1:XxiBSf5jWZ5i16lNOPbMTVdgHBdhfGRD5PZ1LWazzvg=
|
||||
@@ -89,11 +34,8 @@ github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
||||
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
||||
github.com/go-fed/httpsig v1.1.0 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI=
|
||||
github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
||||
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
@@ -111,73 +53,22 @@ github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4
|
||||
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-github/v47 v47.1.0 h1:Cacm/WxQBOa9lF0FT0EMjZ2BWMetQ1TQfyurn4yF1z8=
|
||||
github.com/google/go-github/v47 v47.1.0/go.mod h1:VPZBXNbFSJGjyjFRUKo9vZGawTajnWzC/YjGw/oFKi0=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
||||
github.com/google/pprof v0.0.0-20240320155624-b11c3daa6f07 h1:57oOH2Mu5Nw16KnZAVLdlUjmPH/TSYCKTJgG0OVfX0Y=
|
||||
github.com/google/pprof v0.0.0-20240320155624-b11c3daa6f07/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
|
||||
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
|
||||
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
@@ -189,15 +80,10 @@ github.com/hashicorp/go-retryablehttp v0.7.2 h1:AcYqCvkpalPnPF2pn0KamgwamS42TqUD
|
||||
github.com/hashicorp/go-retryablehttp v0.7.2/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
|
||||
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-version v1.5.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
|
||||
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
|
||||
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
@@ -206,13 +92,9 @@ github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
|
||||
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
@@ -241,12 +123,12 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.2.2 h1:Iv/FL6pvYmDqybEZkr4TrOv8jSHezwpE77K68kcaft8=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.2.2/go.mod h1:fF2++lPHlo+/kPaj3nB0uxtPwzlPm+BlgwGX7MkeGj0=
|
||||
github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU=
|
||||
github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts=
|
||||
github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E=
|
||||
github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3q3fQ=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.4.0 h1:3IcvPOAvnCKwNm0TB0dLDTuawWEj+ax/RERNC+diLMM=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.4.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4=
|
||||
github.com/onsi/ginkgo/v2 v2.17.0 h1:kdnunFXpBjbzN56hcJHrXZ8M+LOkenKA7NnBzTNigTI=
|
||||
github.com/onsi/ginkgo/v2 v2.17.0/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs=
|
||||
github.com/onsi/gomega v1.32.0 h1:JRYU78fJ1LPxlckP6Txi/EYqJvjtMrDC04/MM5XRHPk=
|
||||
github.com/onsi/gomega v1.32.0/go.mod h1:a4x4gW6Pz2yK1MAmvluYme5lvYTn61afQ2ETw/8n4Lg=
|
||||
github.com/ory/graceful v0.1.3 h1:FaeXcHZh168WzS+bqruqWEw/HgXWLdNv2nJ+fbhxbhc=
|
||||
github.com/ory/graceful v0.1.3/go.mod h1:4zFz687IAF7oNHHiB586U4iL+/4aV09o/PYLE34t2bA=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||
@@ -257,40 +139,36 @@ github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdU
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/sagikazarmark/locafero v0.3.0 h1:zT7VEGWC2DTflmccN/5T1etyKvxSxpHsjb9cJvm4SvQ=
|
||||
github.com/sagikazarmark/locafero v0.3.0/go.mod h1:w+v7UsPNFwzF1cHuOajOOzoq4U7v/ig1mpRjqV+Bu1U=
|
||||
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
|
||||
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
||||
github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM=
|
||||
github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
|
||||
github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA=
|
||||
github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
|
||||
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||
github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY=
|
||||
github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
|
||||
github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
|
||||
github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48=
|
||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
||||
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.17.0 h1:I5txKw7MJasPL/BrfkbA0Jyo/oELqVmux4pR/UxOMfI=
|
||||
github.com/spf13/viper v1.17.0/go.mod h1:BmMMMLQXSbcHK6KAOiFLz0l5JHrU89OdIRHvsk0+yVI=
|
||||
github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
|
||||
github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
@@ -308,19 +186,9 @@ github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6
|
||||
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
||||
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/xanzy/go-gitlab v0.94.0 h1:GmBl2T5zqUHqyjkxFSvsT7CbelGdAH/dmBqUBqS+4BE=
|
||||
github.com/xanzy/go-gitlab v0.94.0/go.mod h1:ETg8tcj4OhrB84UEgeE8dSuV/0h4BBL1uOV/qK0vlyI=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/xanzy/go-gitlab v0.100.0 h1:jaOtYj5nWI19+9oVVmgy233pax2oYqucwetogYU46ks=
|
||||
github.com/xanzy/go-gitlab v0.100.0/go.mod h1:ETg8tcj4OhrB84UEgeE8dSuV/0h4BBL1uOV/qK0vlyI=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
||||
@@ -329,329 +197,84 @@ golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUu
|
||||
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
|
||||
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
||||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
|
||||
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.14.0 h1:P0Vrf/2538nmC0H+pEQ3MNFRRnVR7RlqyVw+bvm26z0=
|
||||
golang.org/x/oauth2 v0.14.0/go.mod h1:lAtNWgaWfL4cm7j2OV8TxGi9Qb7ECORx8DktCY74OwM=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
|
||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI=
|
||||
golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
|
||||
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
|
||||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
|
||||
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
|
||||
golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
|
||||
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
|
||||
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 h1:N3bU/SQDCDyD6R528GJ/PwW9KjYcJA3dgyH+MovAkIM=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:KSqppvjFjtoCI+KGd4PELB0qLNxdJHRGqRI09mB6pQA=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
||||
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
|
||||
google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
||||
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s=
|
||||
google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
|
||||
google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
@@ -660,27 +283,16 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/sqlite v1.5.4 h1:IqXwXi8M/ZlPzH/947tn5uik3aYQslP9BVveoax0nV0=
|
||||
gorm.io/driver/sqlite v1.5.4/go.mod h1:qxAuCol+2r6PannQDpOP1FP6ag3mKi4esLnB/jHed+4=
|
||||
gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls=
|
||||
gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
gorm.io/driver/sqlite v1.5.5 h1:7MDMtUZhV065SilG62E0MquljeArQZNfJnjd9i9gx3E=
|
||||
gorm.io/driver/sqlite v1.5.5/go.mod h1:6NgQ7sQWAIFsPrJJl1lSNSu2TABh0ZZ/zm5fosATavE=
|
||||
gorm.io/gorm v1.25.8 h1:WAGEZ/aEcznN4D03laj8DKnehe1e9gYQAjW8xyPRdeo=
|
||||
gorm.io/gorm v1.25.8/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
const CtxKeyAuthorizedUser = "ckau"
|
||||
const CtxKeyViewPasswordVerified = "ckvpv"
|
||||
const CtxKeyPreferredTheme = "ckpt"
|
||||
const CacheKeyOauth2State = "p:a:state"
|
||||
|
||||
type Common struct {
|
||||
|
||||
+25
-2
@@ -98,7 +98,8 @@ type Config struct {
|
||||
ProxyGRPCPort uint
|
||||
TLS bool
|
||||
|
||||
EnablePlainIPInNotification bool // 通知信息IP不打码
|
||||
EnablePlainIPInNotification bool // 通知信息IP不打码
|
||||
DisableSwitchTemplateInFrontend bool // 前台禁用切换模板功能
|
||||
|
||||
// IP变更提醒
|
||||
EnableIPChangeNotification bool
|
||||
@@ -112,6 +113,19 @@ type Config struct {
|
||||
IgnoredIPNotificationServerIDs map[uint64]bool // [ServerID] -> bool(值为true代表当前ServerID在特定服务器列表内)
|
||||
MaxTCPPingValue int32
|
||||
AvgPingCount int
|
||||
|
||||
// 动态域名解析更新
|
||||
DDNS struct {
|
||||
Enable bool
|
||||
Provider string
|
||||
AccessID string
|
||||
AccessSecret string
|
||||
WebhookURL string
|
||||
WebhookMethod string
|
||||
WebhookRequestBody string
|
||||
WebhookHeaders string
|
||||
MaxRetries uint32
|
||||
}
|
||||
}
|
||||
|
||||
// Read 读取配置文件并应用
|
||||
@@ -147,11 +161,20 @@ func (c *Config) Read(path string) error {
|
||||
c.Location = "Asia/Shanghai"
|
||||
}
|
||||
if c.MaxTCPPingValue == 0 {
|
||||
c.MaxTCPPingValue = 300
|
||||
c.MaxTCPPingValue = 1000
|
||||
}
|
||||
if c.AvgPingCount == 0 {
|
||||
c.AvgPingCount = 2
|
||||
}
|
||||
if c.DDNS.Provider == "" {
|
||||
c.DDNS.Provider = "webhook"
|
||||
}
|
||||
if c.DDNS.WebhookMethod == "" {
|
||||
c.DDNS.WebhookMethod = "POST"
|
||||
}
|
||||
if c.DDNS.MaxRetries == 0 {
|
||||
c.DDNS.MaxRetries = 3
|
||||
}
|
||||
|
||||
c.updateIgnoredIPNotificationID()
|
||||
return nil
|
||||
|
||||
+14
-12
@@ -1,20 +1,22 @@
|
||||
package model
|
||||
|
||||
const (
|
||||
Cycle = iota
|
||||
Hour
|
||||
Day
|
||||
Week
|
||||
Month
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// MonitorHistory 历史监控记录
|
||||
type MonitorHistory struct {
|
||||
Common
|
||||
MonitorID uint64
|
||||
ServerID uint64
|
||||
AvgDelay float32 // 平均延迟,毫秒
|
||||
Up uint64 // 检查状态良好计数
|
||||
Down uint64 // 检查状态异常计数
|
||||
ID uint64 `gorm:"primaryKey"`
|
||||
CreatedAt time.Time `gorm:"index;<-:create;index:idx_server_id_created_at_monitor_id_avg_delay"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index"`
|
||||
MonitorID uint64 `gorm:"index:idx_server_id_created_at_monitor_id_avg_delay"`
|
||||
ServerID uint64 `gorm:"index:idx_server_id_created_at_monitor_id_avg_delay"`
|
||||
AvgDelay float32 `gorm:"index:idx_server_id_created_at_monitor_id_avg_delay"` // 平均延迟,毫秒
|
||||
Up uint64 // 检查状态良好计数
|
||||
Down uint64 // 检查状态异常计数
|
||||
Data string
|
||||
}
|
||||
|
||||
|
||||
+24
-10
@@ -5,8 +5,6 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -85,21 +83,37 @@ func execCase(t *testing.T, item testSt) {
|
||||
Server: &server,
|
||||
Loc: time.Local,
|
||||
}
|
||||
assert.Equal(t, item.expectURL, ns.reqURL(msg))
|
||||
if item.expectURL != ns.reqURL(msg) {
|
||||
t.Fatalf("Expected %s, but got %s", item.expectURL, ns.reqURL(msg))
|
||||
}
|
||||
reqBody, err := ns.reqBody(msg)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, item.expectBody, reqBody)
|
||||
if err != nil {
|
||||
t.Fatalf("Error: %s", err)
|
||||
}
|
||||
if item.expectBody != reqBody {
|
||||
t.Fatalf("Expected %s, but got %s", item.expectBody, reqBody)
|
||||
}
|
||||
reqMethod, err := n.reqMethod()
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, item.expectMethod, reqMethod)
|
||||
if err != nil {
|
||||
t.Fatalf("Error: %s", err)
|
||||
}
|
||||
if item.expectMethod != reqMethod {
|
||||
t.Fatalf("Expected %s, but got %s", item.expectMethod, reqMethod)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("", "", strings.NewReader(""))
|
||||
assert.Nil(t, err)
|
||||
if err != nil {
|
||||
t.Fatalf("Error: %s", err)
|
||||
}
|
||||
n.setContentType(req)
|
||||
assert.Equal(t, item.expectContentType, req.Header.Get("Content-Type"))
|
||||
if item.expectContentType != req.Header.Get("Content-Type") {
|
||||
t.Fatalf("Expected %s, but got %s", item.expectContentType, req.Header.Get("Content-Type"))
|
||||
}
|
||||
n.setRequestHeader(req)
|
||||
for k, v := range item.expectHeader {
|
||||
assert.Equal(t, v, req.Header.Get(k))
|
||||
if v != req.Header.Get(k) {
|
||||
t.Fatalf("Expected %s, but got %s", v, req.Header.Get(k))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-1
@@ -17,6 +17,8 @@ type Server struct {
|
||||
Note string `json:"-"` // 管理员可见备注
|
||||
DisplayIndex int // 展示排序,越大越靠前
|
||||
HideForGuest bool // 对游客隐藏
|
||||
EnableDDNS bool // 是否启用DDNS 未在配置文件中启用DDNS 或 DDNS检查时间为0时此项无效
|
||||
DDNSDomain string // DDNS中的前缀 如基础域名为abc.oracle DDNSName为mjj 就会把mjj.abc.oracle解析服务器IP 为空则停用
|
||||
|
||||
Host *Host `gorm:"-"`
|
||||
State *HostState `gorm:"-"`
|
||||
@@ -51,5 +53,6 @@ func (s Server) Marshal() template.JS {
|
||||
tag, _ := utils.Json.Marshal(s.Tag)
|
||||
note, _ := utils.Json.Marshal(s.Note)
|
||||
secret, _ := utils.Json.Marshal(s.Secret)
|
||||
return template.JS(fmt.Sprintf(`{"ID":%d,"Name":%s,"Secret":%s,"DisplayIndex":%d,"Tag":%s,"Note":%s,"HideForGuest": %s}`, s.ID, name, secret, s.DisplayIndex, tag, note, boolToString(s.HideForGuest))) // #nosec
|
||||
ddnsDomain, _ := utils.Json.Marshal(s.DDNSDomain)
|
||||
return template.JS(fmt.Sprintf(`{"ID":%d,"Name":%s,"Secret":%s,"DisplayIndex":%d,"Tag":%s,"Note":%s,"HideForGuest": %s,"EnableDDNS": %s,"DDNSDomain": %s}`, s.ID, name, secret, s.DisplayIndex, tag, note, boolToString(s.HideForGuest), boolToString(s.EnableDDNS), ddnsDomain)) // #nosec
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/naiba/nezha/pkg/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestServerMarshal(t *testing.T) {
|
||||
@@ -21,7 +20,11 @@ func TestServerMarshal(t *testing.T) {
|
||||
}
|
||||
serverStr := string(server.Marshal())
|
||||
var serverRestore Server
|
||||
assert.Nil(t, utils.Json.Unmarshal([]byte(serverStr), &serverRestore))
|
||||
assert.Equal(t, server, serverRestore)
|
||||
if utils.Json.Unmarshal([]byte(serverStr), &serverRestore) != nil {
|
||||
t.Fatalf("Error: %s", serverStr)
|
||||
}
|
||||
if server.Name != serverRestore.Name {
|
||||
t.Fatalf("Expected %s, but got %s", server.Name, serverRestore.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
package ddns
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type ProviderCloudflare struct {
|
||||
Secret string
|
||||
}
|
||||
|
||||
func (provider ProviderCloudflare) UpdateDomain(domainConfig *DomainConfig) bool {
|
||||
if domainConfig == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
zoneID, err := provider.getZoneID(domainConfig.FullDomain)
|
||||
if err != nil {
|
||||
log.Printf("无法获取 zone ID: %s\n", err)
|
||||
return false
|
||||
}
|
||||
|
||||
// 当IPv4和IPv6同时成功才算作成功
|
||||
var resultV4 = true
|
||||
var resultV6 = true
|
||||
if domainConfig.EnableIPv4 {
|
||||
if !provider.addDomainRecord(zoneID, domainConfig, true) {
|
||||
resultV4 = false
|
||||
}
|
||||
}
|
||||
|
||||
if domainConfig.EnableIpv6 {
|
||||
if !provider.addDomainRecord(zoneID, domainConfig, false) {
|
||||
resultV6 = false
|
||||
}
|
||||
}
|
||||
|
||||
return resultV4 && resultV6
|
||||
}
|
||||
|
||||
func (provider ProviderCloudflare) addDomainRecord(zoneID string, domainConfig *DomainConfig, isIpv4 bool) bool {
|
||||
record, err := provider.findDNSRecord(zoneID, domainConfig.FullDomain, isIpv4)
|
||||
if err != nil {
|
||||
log.Printf("查找 DNS 记录时出错: %s\n", err)
|
||||
return false
|
||||
}
|
||||
|
||||
if record == nil {
|
||||
// 添加 DNS 记录
|
||||
return provider.createDNSRecord(zoneID, domainConfig, isIpv4)
|
||||
} else {
|
||||
// 更新 DNS 记录
|
||||
return provider.updateDNSRecord(zoneID, record["id"].(string), domainConfig, isIpv4)
|
||||
}
|
||||
}
|
||||
|
||||
func (provider ProviderCloudflare) getZoneID(domain string) (string, error) {
|
||||
_, realDomain := SplitDomain(domain)
|
||||
url := fmt.Sprintf("https://api.cloudflare.com/client/v4/zones?name=%s", realDomain)
|
||||
body, err := provider.sendRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var res map[string]interface{}
|
||||
err = json.Unmarshal(body, &res)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
result := res["result"].([]interface{})
|
||||
if len(result) > 0 {
|
||||
zoneID := result[0].(map[string]interface{})["id"].(string)
|
||||
return zoneID, nil
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("找不到 Zone ID")
|
||||
}
|
||||
|
||||
func (provider ProviderCloudflare) findDNSRecord(zoneID string, domain string, isIPv4 bool) (map[string]interface{}, error) {
|
||||
var ipType = "A"
|
||||
if !isIPv4 {
|
||||
ipType = "AAAA"
|
||||
}
|
||||
url := fmt.Sprintf("https://api.cloudflare.com/client/v4/zones/%s/dns_records?type=%s&name=%s", zoneID, ipType, domain)
|
||||
body, err := provider.sendRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var res map[string]interface{}
|
||||
err = json.Unmarshal(body, &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := res["result"].([]interface{})
|
||||
if len(result) > 0 {
|
||||
return result[0].(map[string]interface{}), nil
|
||||
}
|
||||
|
||||
return nil, nil // 没有找到 DNS 记录
|
||||
}
|
||||
|
||||
func (provider ProviderCloudflare) createDNSRecord(zoneID string, domainConfig *DomainConfig, isIPv4 bool) bool {
|
||||
var ipType = "A"
|
||||
var ipAddr = domainConfig.Ipv4Addr
|
||||
if !isIPv4 {
|
||||
ipType = "AAAA"
|
||||
ipAddr = domainConfig.Ipv6Addr
|
||||
}
|
||||
url := fmt.Sprintf("https://api.cloudflare.com/client/v4/zones/%s/dns_records", zoneID)
|
||||
data := map[string]interface{}{
|
||||
"type": ipType,
|
||||
"name": domainConfig.FullDomain,
|
||||
"content": ipAddr,
|
||||
"ttl": 60,
|
||||
"proxied": false,
|
||||
}
|
||||
jsonData, _ := json.Marshal(data)
|
||||
_, err := provider.sendRequest("POST", url, jsonData)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (provider ProviderCloudflare) updateDNSRecord(zoneID string, recordID string, domainConfig *DomainConfig, isIPv4 bool) bool {
|
||||
var ipType = "A"
|
||||
var ipAddr = domainConfig.Ipv4Addr
|
||||
if !isIPv4 {
|
||||
ipType = "AAAA"
|
||||
ipAddr = domainConfig.Ipv6Addr
|
||||
}
|
||||
url := fmt.Sprintf("https://api.cloudflare.com/client/v4/zones/%s/dns_records/%s", zoneID, recordID)
|
||||
data := map[string]interface{}{
|
||||
"type": ipType,
|
||||
"name": domainConfig.FullDomain,
|
||||
"content": ipAddr,
|
||||
"ttl": 60,
|
||||
"proxied": false,
|
||||
}
|
||||
jsonData, _ := json.Marshal(data)
|
||||
_, err := provider.sendRequest("PATCH", url, jsonData)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// 以下为辅助方法,如发送 HTTP 请求等
|
||||
func (provider ProviderCloudflare) sendRequest(method string, url string, data []byte) ([]byte, error) {
|
||||
client := &http.Client{}
|
||||
req, err := http.NewRequest(method, url, bytes.NewBuffer(data))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", provider.Secret))
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func(Body io.ReadCloser) {
|
||||
err := Body.Close()
|
||||
if err != nil {
|
||||
log.Printf("NEZHA>> 无法关闭HTTP响应体流: %s\n", err.Error())
|
||||
}
|
||||
}(resp.Body)
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return body, nil
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package ddns
|
||||
|
||||
type DomainConfig struct {
|
||||
EnableIPv4 bool
|
||||
EnableIpv6 bool
|
||||
FullDomain string
|
||||
Ipv4Addr string
|
||||
Ipv6Addr string
|
||||
}
|
||||
|
||||
type Provider interface {
|
||||
// UpdateDomain Return is updated
|
||||
UpdateDomain(domainConfig *DomainConfig) bool
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package ddns
|
||||
|
||||
type ProviderDummy struct{}
|
||||
|
||||
func (provider ProviderDummy) UpdateDomain(domainConfig *DomainConfig) bool {
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package ddns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (provider ProviderWebHook) FormatWebhookString(s string, config *DomainConfig, ipType string) string {
|
||||
if config == nil {
|
||||
return s
|
||||
}
|
||||
|
||||
result := strings.TrimSpace(s)
|
||||
result = strings.Replace(s, "{ip}", config.Ipv4Addr, -1)
|
||||
result = strings.Replace(result, "{domain}", config.FullDomain, -1)
|
||||
result = strings.Replace(result, "{type}", ipType, -1)
|
||||
// remove \r
|
||||
result = strings.Replace(result, "\r", "", -1)
|
||||
return result
|
||||
}
|
||||
|
||||
func SetStringHeadersToRequest(req *http.Request, headers []string) {
|
||||
if req == nil {
|
||||
return
|
||||
}
|
||||
for _, element := range headers {
|
||||
kv := strings.SplitN(element, ":", 2)
|
||||
if len(kv) == 2 {
|
||||
req.Header.Add(kv[0], kv[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SplitDomain 分割域名为前缀和一级域名
|
||||
func SplitDomain(domain string) (prefix string, topLevelDomain string) {
|
||||
// 带有二级TLD的一些常见例子,需要特别处理
|
||||
secondLevelTLDs := map[string]bool{
|
||||
".co.uk": true, ".com.cn": true, ".gov.cn": true, ".net.cn": true, ".org.cn": true,
|
||||
}
|
||||
|
||||
// 分割域名为"."的各部分
|
||||
parts := strings.Split(domain, ".")
|
||||
|
||||
// 处理特殊情况,例如 ".co.uk"
|
||||
for i := len(parts) - 2; i > 0; i-- {
|
||||
potentialTLD := fmt.Sprintf(".%s.%s", parts[i], parts[i+1])
|
||||
if secondLevelTLDs[potentialTLD] {
|
||||
if i > 1 {
|
||||
return strings.Join(parts[:i-1], "."), strings.Join(parts[i-1:], ".")
|
||||
}
|
||||
return "", domain // 当域名仅为二级TLD时,无前缀
|
||||
}
|
||||
}
|
||||
|
||||
// 常规处理,查找最后一个"."前的所有内容作为前缀
|
||||
if len(parts) > 2 {
|
||||
return strings.Join(parts[:len(parts)-2], "."), strings.Join(parts[len(parts)-2:], ".")
|
||||
}
|
||||
return "", domain // 当域名不包含子域名时,无前缀
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package ddns
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ProviderWebHook struct {
|
||||
URL string
|
||||
RequestMethod string
|
||||
RequestBody string
|
||||
RequestHeader string
|
||||
}
|
||||
|
||||
func (provider ProviderWebHook) UpdateDomain(domainConfig *DomainConfig) bool {
|
||||
if domainConfig == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if domainConfig.FullDomain == "" {
|
||||
log.Println("NEZHA>> Failed to update an empty domain")
|
||||
return false
|
||||
}
|
||||
updated := false
|
||||
client := &http.Client{}
|
||||
if domainConfig.EnableIPv4 && domainConfig.Ipv4Addr != "" {
|
||||
url := provider.FormatWebhookString(provider.URL, domainConfig, "ipv4")
|
||||
body := provider.FormatWebhookString(provider.RequestBody, domainConfig, "ipv4")
|
||||
header := provider.FormatWebhookString(provider.RequestHeader, domainConfig, "ipv4")
|
||||
headers := strings.Split(header, "\n")
|
||||
req, err := http.NewRequest(provider.RequestMethod, url, bytes.NewBufferString(body))
|
||||
if err == nil && req != nil {
|
||||
SetStringHeadersToRequest(req, headers)
|
||||
if _, err := client.Do(req); err != nil {
|
||||
log.Printf("NEZHA>> Failed to update a domain: %s. Cause by: %s\n", domainConfig.FullDomain, err.Error())
|
||||
} else {
|
||||
updated = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if domainConfig.EnableIpv6 && domainConfig.Ipv6Addr != "" {
|
||||
url := provider.FormatWebhookString(provider.URL, domainConfig, "ipv6")
|
||||
body := provider.FormatWebhookString(provider.RequestBody, domainConfig, "ipv6")
|
||||
header := provider.FormatWebhookString(provider.RequestHeader, domainConfig, "ipv6")
|
||||
headers := strings.Split(header, "\n")
|
||||
req, err := http.NewRequest(provider.RequestMethod, url, bytes.NewBufferString(body))
|
||||
if err == nil && req != nil {
|
||||
SetStringHeadersToRequest(req, headers)
|
||||
if _, err := client.Do(req); err != nil {
|
||||
log.Printf("NEZHA>> Failed to update a domain: %s. Cause by: %s\n", domainConfig.FullDomain, err.Error())
|
||||
} else {
|
||||
updated = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return updated
|
||||
}
|
||||
+12
-10
@@ -12,19 +12,19 @@ import (
|
||||
)
|
||||
|
||||
type AuthorizeOption struct {
|
||||
Guest bool
|
||||
Member bool
|
||||
IsPage bool
|
||||
AllowAPI bool
|
||||
Msg string
|
||||
Redirect string
|
||||
Btn string
|
||||
GuestOnly bool
|
||||
MemberOnly bool
|
||||
IsPage bool
|
||||
AllowAPI bool
|
||||
Msg string
|
||||
Redirect string
|
||||
Btn string
|
||||
}
|
||||
|
||||
func Authorize(opt AuthorizeOption) func(*gin.Context) {
|
||||
return func(c *gin.Context) {
|
||||
var code = http.StatusForbidden
|
||||
if opt.Guest {
|
||||
if opt.GuestOnly {
|
||||
code = http.StatusBadRequest
|
||||
}
|
||||
|
||||
@@ -67,13 +67,15 @@ func Authorize(opt AuthorizeOption) func(*gin.Context) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 已登录且只能游客访问
|
||||
if isLogin && opt.Guest {
|
||||
if isLogin && opt.GuestOnly {
|
||||
ShowErrorPage(c, commonErr, opt.IsPage)
|
||||
return
|
||||
}
|
||||
|
||||
// 未登录且需要登录
|
||||
if !isLogin && opt.Member {
|
||||
if !isLogin && opt.MemberOnly {
|
||||
ShowErrorPage(c, commonErr, opt.IsPage)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ func CommonEnvironment(c *gin.Context, data map[string]interface{}) gin.H {
|
||||
data["MatchedPath"] = c.MustGet("MatchedPath")
|
||||
data["Version"] = singleton.Version
|
||||
data["Conf"] = singleton.Conf
|
||||
data["Themes"] = model.Themes
|
||||
// 是否是管理页面
|
||||
data["IsAdminPage"] = adminPage[data["MatchedPath"].(string)]
|
||||
// 站点标题
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package mygin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/naiba/nezha/model"
|
||||
"github.com/naiba/nezha/pkg/utils"
|
||||
"github.com/naiba/nezha/service/singleton"
|
||||
)
|
||||
|
||||
func PreferredTheme(c *gin.Context) {
|
||||
// 采用前端传入的主题
|
||||
if theme, err := c.Cookie("preferred_theme"); err == nil {
|
||||
if _, has := model.Themes[theme]; has {
|
||||
// 检验自定义主题
|
||||
if theme == "custom" && singleton.Conf.Site.Theme != "custom" && !utils.IsFileExists("resource/template/theme-custom/home.html") {
|
||||
return
|
||||
}
|
||||
c.Set(model.CtxKeyPreferredTheme, theme)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func GetPreferredTheme(c *gin.Context, path string) string {
|
||||
if theme, has := c.Get(model.CtxKeyPreferredTheme); has {
|
||||
return fmt.Sprintf("theme-%s%s", theme, path)
|
||||
}
|
||||
return fmt.Sprintf("theme-%s%s", singleton.Conf.Site.Theme, path)
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package mygin
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/naiba/nezha/model"
|
||||
"github.com/naiba/nezha/service/singleton"
|
||||
"github.com/nicksnyder/go-i18n/v2/i18n"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
type ValidateViewPasswordOption struct {
|
||||
IsPage bool
|
||||
AbortWhenFail bool
|
||||
}
|
||||
|
||||
func ValidateViewPassword(opt ValidateViewPasswordOption) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if singleton.Conf.Site.ViewPassword == "" {
|
||||
return
|
||||
}
|
||||
_, authorized := c.Get(model.CtxKeyAuthorizedUser)
|
||||
if authorized {
|
||||
return
|
||||
}
|
||||
viewPassword, err := c.Cookie(singleton.Conf.Site.CookieName + "-vp")
|
||||
if err == nil {
|
||||
err = bcrypt.CompareHashAndPassword([]byte(viewPassword), []byte(singleton.Conf.Site.ViewPassword))
|
||||
}
|
||||
if err == nil {
|
||||
c.Set(model.CtxKeyViewPasswordVerified, true)
|
||||
return
|
||||
}
|
||||
if !opt.AbortWhenFail {
|
||||
return
|
||||
}
|
||||
if opt.IsPage {
|
||||
c.HTML(http.StatusOK, GetPreferredTheme(c, "/viewpassword"), CommonEnvironment(c, gin.H{
|
||||
"Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "VerifyPassword"}),
|
||||
"CustomCode": singleton.Conf.Site.CustomCode,
|
||||
}))
|
||||
|
||||
} else {
|
||||
c.JSON(http.StatusOK, model.Response{
|
||||
Code: http.StatusForbidden,
|
||||
Message: "访问受限",
|
||||
})
|
||||
}
|
||||
c.Abort()
|
||||
}
|
||||
}
|
||||
+12
-6
@@ -2,8 +2,6 @@ package utils
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type testSt struct {
|
||||
@@ -36,7 +34,9 @@ func TestNotification(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
assert.Equal(t, IPDesensitize(c.input), c.output)
|
||||
if c.output != IPDesensitize(c.input) {
|
||||
t.Fatalf("Expected %s, but got %s", c.output, IPDesensitize(c.input))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,9 +44,15 @@ func TestGenerGenerateRandomString(t *testing.T) {
|
||||
generatedString := make(map[string]bool)
|
||||
for i := 0; i < 100; i++ {
|
||||
str, err := GenerateRandomString(32)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, len(str), 32)
|
||||
assert.False(t, generatedString[str])
|
||||
if err != nil {
|
||||
t.Fatalf("Error: %s", err)
|
||||
}
|
||||
if len(str) != 32 {
|
||||
t.Fatalf("Expected 32, but got %d", len(str))
|
||||
}
|
||||
if generatedString[str] {
|
||||
t.Fatalf("Duplicated string: %s", str)
|
||||
}
|
||||
generatedString[str] = true
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+21
-3
@@ -398,7 +398,7 @@ other = "Virtualization"
|
||||
other = "Swap"
|
||||
|
||||
[NetTransfer]
|
||||
other = "Network Transfer"
|
||||
other = "Transfer"
|
||||
|
||||
[Load]
|
||||
other = "Load"
|
||||
@@ -419,7 +419,7 @@ other = "Last Active"
|
||||
other = "Version"
|
||||
|
||||
[NetSpeed]
|
||||
other = "Network Speed"
|
||||
other = "Speed"
|
||||
|
||||
[Uptime]
|
||||
other = "Uptime"
|
||||
@@ -614,4 +614,22 @@ other = "Menu"
|
||||
other = "Network"
|
||||
|
||||
[EnableShowInService]
|
||||
other = "Enable Show in Service"
|
||||
other = "Enable Show in Service"
|
||||
|
||||
[EnableDDNS]
|
||||
other = "Enable DDNS"
|
||||
|
||||
[DDNSDomain]
|
||||
other = "DDNS Domain"
|
||||
|
||||
[Feature]
|
||||
other = "Feature"
|
||||
|
||||
[Template]
|
||||
other = "Template"
|
||||
|
||||
[Stat]
|
||||
other = "Stat"
|
||||
|
||||
[DisableSwitchTemplateInFrontend]
|
||||
other = "Disable Switch Template in Frontend"
|
||||
Vendored
+20
-2
@@ -203,7 +203,7 @@ other = "Agregar Servidor"
|
||||
other = "Editar Grupo de Servidores en Lote"
|
||||
|
||||
[BatchDeleteServer]
|
||||
other = "Eliminar Servidores en Lote""
|
||||
other = "Eliminar Servidores en Lote"
|
||||
|
||||
[InputServerGroupName]
|
||||
other = "Ingrese Nombre del Grupo de Servidores"
|
||||
@@ -614,4 +614,22 @@ other = "Menú"
|
||||
other = "Red"
|
||||
|
||||
[EnableShowInService]
|
||||
other = "Mostrar en servicio"
|
||||
other = "Mostrar en servicio"
|
||||
|
||||
[EnableDDNS]
|
||||
other = "Habilitar DDNS"
|
||||
|
||||
[DDNSDomain]
|
||||
other = "Dominio DDNS"
|
||||
|
||||
[Feature]
|
||||
other = "Característica"
|
||||
|
||||
[Template]
|
||||
other = "Plantilla"
|
||||
|
||||
[Stat]
|
||||
other = "Stat"
|
||||
|
||||
[DisableSwitchTemplateInFrontend]
|
||||
other = "Deshabilitar Cambio de Plantilla en Frontend"
|
||||
Vendored
+18
@@ -615,3 +615,21 @@ other = "网络"
|
||||
|
||||
[EnableShowInService]
|
||||
other = "在服务中显示"
|
||||
|
||||
[EnableDDNS]
|
||||
other = "启用DDNS"
|
||||
|
||||
[DDNSDomain]
|
||||
other = "DDNS域名"
|
||||
|
||||
[Feature]
|
||||
other = "功能"
|
||||
|
||||
[Template]
|
||||
other = "主题"
|
||||
|
||||
[Stat]
|
||||
other = "信息"
|
||||
|
||||
[DisableSwitchTemplateInFrontend]
|
||||
other = "禁止前台切换模板"
|
||||
|
||||
Vendored
+19
-1
@@ -614,4 +614,22 @@ other = "菜單"
|
||||
other = "網絡"
|
||||
|
||||
[EnableShowInService]
|
||||
other = "在服務中顯示"
|
||||
other = "在服務中顯示"
|
||||
|
||||
[EnableDDNS]
|
||||
other = "啟用DDNS"
|
||||
|
||||
[DDNSDomain]
|
||||
other = "DDNS網域"
|
||||
|
||||
[Feature]
|
||||
other = "功能"
|
||||
|
||||
[Template]
|
||||
other = "主題"
|
||||
|
||||
[Stat]
|
||||
other = "信息"
|
||||
|
||||
[DisableSwitchTemplateInFrontend]
|
||||
other = "禁止前台切換主題"
|
||||
|
||||
@@ -302,6 +302,7 @@ function addOrEditServer(server, conf) {
|
||||
modal.find("input[name=id]").val(server ? server.ID : null);
|
||||
modal.find("input[name=name]").val(server ? server.Name : null);
|
||||
modal.find("input[name=Tag]").val(server ? server.Tag : null);
|
||||
modal.find("input[name=DDNSDomain]").val(server ? server.DDNSDomain : null);
|
||||
modal
|
||||
.find("input[name=DisplayIndex]")
|
||||
.val(server ? server.DisplayIndex : null);
|
||||
@@ -321,6 +322,11 @@ function addOrEditServer(server, conf) {
|
||||
} else {
|
||||
modal.find(".ui.hideforguest.checkbox").checkbox("set unchecked");
|
||||
}
|
||||
if (server && server.EnableDDNS) {
|
||||
modal.find(".ui.enableddns.checkbox").checkbox("set checked");
|
||||
} else {
|
||||
modal.find(".ui.enableddns.checkbox").checkbox("set unchecked");
|
||||
}
|
||||
showFormModal(".server.modal", "#serverForm", "/api/server");
|
||||
}
|
||||
|
||||
|
||||
+218
@@ -0,0 +1,218 @@
|
||||
/* 屏幕适配 */
|
||||
@media only screen and (min-width:1200px) {
|
||||
.ui.container {
|
||||
width:95% !important;
|
||||
font-size: 90% !important;
|
||||
max-width: 1300px !important;
|
||||
}
|
||||
}
|
||||
@media only screen and (max-width:767px) {
|
||||
.ui.card>.content>.header:not(.ui),.ui.cards>.card>.content>.header:not(.ui) {
|
||||
margin-top:0.4em !important;
|
||||
}
|
||||
.ui.menu .item>img:not(.ui){
|
||||
width: 2.2rem;
|
||||
}
|
||||
.ui.menu .item:before{
|
||||
width:0.5px;
|
||||
}
|
||||
.ui.menu .item{
|
||||
padding: 0.9rem 0.55rem;
|
||||
}
|
||||
.ui.large.menu{
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
i.icon {
|
||||
color:#000;
|
||||
width:1.2em !important;
|
||||
}
|
||||
i.fi {
|
||||
width:0.9em;
|
||||
margin:0px 6px 0px 2px;
|
||||
}
|
||||
|
||||
body {
|
||||
content:" " !important;
|
||||
background:fixed !important;
|
||||
z-index:-1 !important;
|
||||
top:0 !important;
|
||||
right:0 !important;
|
||||
bottom:0 !important;
|
||||
left:0 !important;
|
||||
}
|
||||
|
||||
td {
|
||||
word-wrap: break-word;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.nb-container {
|
||||
padding-top: 75px;
|
||||
min-height: 100vh;
|
||||
padding-bottom: 65px;
|
||||
margin-bottom: -47px;
|
||||
}
|
||||
|
||||
#app .ui.fluid.accordion {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.login.nb-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-top: unset;
|
||||
}
|
||||
|
||||
.login.nb-container > .grid {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.login.nb-container > .grid .column {
|
||||
max-width: 450px;
|
||||
}
|
||||
|
||||
.ui.menu .item-right:before{
|
||||
width:0px;
|
||||
}
|
||||
|
||||
.status.cards .flag {
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
.status.cards .header > .info.icon {
|
||||
float: right;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.ui.grid {
|
||||
margin-bottom:-0.5em
|
||||
}
|
||||
|
||||
.ui.card>.content>.header:not(.ui), .ui.cards>.card>.content>.header:not(.ui){
|
||||
line-height: 1em;
|
||||
}
|
||||
|
||||
.status.cards .wide.column {
|
||||
padding-top: 0 !important;
|
||||
padding-bottom: 0 !important;
|
||||
height:2.3rem !important;
|
||||
}
|
||||
|
||||
.status.cards .wide.column:nth-child(1) {
|
||||
margin-top:1.2rem !important;
|
||||
}
|
||||
|
||||
.status.cards .wide.column:nth-child(2) {
|
||||
margin-top:1.2rem !important;
|
||||
}
|
||||
|
||||
.status.cards .three.wide.column {
|
||||
text-align: center;
|
||||
width: 22%!important;
|
||||
}
|
||||
|
||||
.status.cards .thirteen.wide.column{
|
||||
width: 78%!important;
|
||||
padding-left:0;
|
||||
}
|
||||
|
||||
.status.cards .description {
|
||||
padding-bottom:0 !important;
|
||||
}
|
||||
|
||||
.status.cards .flag {
|
||||
margin-right:0.5rem !important;
|
||||
}
|
||||
|
||||
.status.cards .header > .info.icon {
|
||||
float: right;
|
||||
margin-right:0 !important;
|
||||
}
|
||||
|
||||
.ui.popup:before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.closePopup{
|
||||
color:rgb(10, 148, 242) !important;
|
||||
position: absolute;
|
||||
top: 7px;
|
||||
right: 10px;
|
||||
cursor: pointer;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.ui.content {
|
||||
margin:0 !important;
|
||||
padding:1em !important;
|
||||
}
|
||||
|
||||
.status.cards .ui.content.popup {
|
||||
min-width:calc(100%)!important;
|
||||
line-height:2rem !important;
|
||||
border-radius:5px !important;
|
||||
border:1px solid transparent !important;
|
||||
font-family:Arial,Helvetica,sans-serif !important;
|
||||
}
|
||||
|
||||
.status.cards .outline.icon {
|
||||
margin-right:1px !important;
|
||||
}
|
||||
|
||||
.ui.progress .bar {
|
||||
min-width:1.8em !important;
|
||||
border-radius:5px !important;
|
||||
line-height:1.65em !important;
|
||||
text-align: right;
|
||||
padding-right: 0.4em;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
font-weight: 700;
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
.service-status .delay-today {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.service-status .delay-today > i {
|
||||
display: inline-block;
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
border-radius: 0.6em;
|
||||
background-color: grey;
|
||||
margin-right: 0.3em;
|
||||
}
|
||||
|
||||
.service-status .danger {
|
||||
background-color: crimson !important;
|
||||
}
|
||||
|
||||
.service-status .good {
|
||||
background-color: rgb(10, 148, 242) !important;
|
||||
}
|
||||
|
||||
.service-status .warning {
|
||||
background-color: orange !important;
|
||||
}
|
||||
|
||||
.nezha-primary-btn {
|
||||
background-color: #0338d6 !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.nezha-primary-font {
|
||||
color: #0338d6 !important;
|
||||
}
|
||||
|
||||
.nezha-secondary-font {
|
||||
height: 1em;
|
||||
color: rgb(10, 148, 242) !important;
|
||||
}
|
||||
|
||||
.ui-alerts.top-center {
|
||||
z-index: 99999999;
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
const mixinsVue = {
|
||||
delimiters: ['@#', '#@'],
|
||||
data: {
|
||||
preferredTemplate: null,
|
||||
isMobile: false,
|
||||
adaptedTemplates: [
|
||||
{ key: 'default', name: 'Default', icon: 'th large' },
|
||||
{ key: 'angel-kanade', name: 'AngelKanade', icon: 'square' },
|
||||
{ key: 'server-status', name: 'SeverStatus', icon: 'list' }
|
||||
]
|
||||
},
|
||||
created() {
|
||||
this.isMobile = this.checkIsMobile();
|
||||
this.preferredTemplate = this.getCookie('preferred_theme') ? this.getCookie('preferred_theme') : this.$root.defaultTemplate;
|
||||
},
|
||||
methods: {
|
||||
toggleTemplate(template) {
|
||||
if( template != this.preferredTemplate){
|
||||
this.preferredTemplate = template;
|
||||
this.updateCookie("preferred_theme", template);
|
||||
window.location.reload();
|
||||
}
|
||||
},
|
||||
updateCookie(name, value) {
|
||||
document.cookie = name + "=" + value +"; path=/";
|
||||
},
|
||||
getCookie(name) {
|
||||
const cookies = document.cookie.split(';');
|
||||
let cookieValue = null;
|
||||
for (let i = 0; i < cookies.length; i++) {
|
||||
const cookie = cookies[i].trim();
|
||||
if (cookie.startsWith(name + '=')) {
|
||||
cookieValue = cookie.substring(name.length + 1, cookie.length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return cookieValue;
|
||||
},
|
||||
checkIsMobile() { // 检测设备类型,页面宽度小于768px认为是移动设备
|
||||
return window.innerWidth <= 768;
|
||||
},
|
||||
logOut(id) {
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/api/logout',
|
||||
data: JSON.stringify({ id: id }),
|
||||
contentType: 'application/json',
|
||||
success: function (resp) {
|
||||
if (resp.code == 200) {
|
||||
window.location.reload();
|
||||
} else {
|
||||
alert('注销失败(Error ' + resp.code + '): ' + resp.message);
|
||||
}
|
||||
},
|
||||
error: function (err) {
|
||||
alert('网络错误: ' + err.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+68
-66
File diff suppressed because one or more lines are too long
+57
-26
File diff suppressed because one or more lines are too long
+186
-33
@@ -6,58 +6,120 @@ body {
|
||||
/* 导航部分 开始*/
|
||||
.navbar {
|
||||
min-height: 40px !important;
|
||||
}
|
||||
|
||||
.navbar-inner{
|
||||
margin:0 auto;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.pl-md-unset {
|
||||
.navbar .container{
|
||||
max-width: 95vw;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.navbar-collapse:not([aria-expanded]) .navbar-nav .dropdown-toggle {
|
||||
.navbar-inverse{
|
||||
background-image: none;
|
||||
background-color: #1C2127;
|
||||
box-shadow: 0 1px 40px -8px #00000080;
|
||||
}
|
||||
|
||||
.navbar-inverse .navbar-toggle:focus,
|
||||
.navbar-inverse .navbar-toggle:hover {
|
||||
background-color: #1C2127;
|
||||
}
|
||||
|
||||
.navbar .navbar-collapse:not([aria-expanded]) .navbar-nav .dropdown-toggle {
|
||||
margin-top: 18px;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.navbar-toggle {
|
||||
.navbar .navbar-toggle {
|
||||
margin-right:0
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
.navbar .navbar-brand {
|
||||
font-size: 20px;
|
||||
text-align: center;
|
||||
padding:12px 0 0 0;
|
||||
margin-right:30px;
|
||||
}
|
||||
|
||||
.node-cell-expand {
|
||||
max-width: 420px;
|
||||
.navbar .node-cell-expand {
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.node-cell-expand-label {
|
||||
margin-right: 5px;
|
||||
.navbar .node-cell-expand-label {
|
||||
/*margin-right: 5px;*/
|
||||
}
|
||||
|
||||
.dropdown .dropdown-toggle {
|
||||
.navbar .dropdown .dropdown-toggle {
|
||||
padding-bottom: 10px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.navbar-inverse, .nav.navbar-nav {
|
||||
background-image: linear-gradient(rgb(60, 60, 60) 0px, rgb(34, 34, 34) 100%) !important;
|
||||
.navbar .navbar-nav {
|
||||
margin:0px -15px;
|
||||
}
|
||||
|
||||
.navbar-inverse .navbar-nav>li>a {
|
||||
.navbar .navbar-nav>li>a {
|
||||
color:#f1f1f1;
|
||||
}
|
||||
|
||||
.navbar-inverse .navbar-brand {
|
||||
font-size: 20px;
|
||||
.navbar-nav li a span{
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.navbar .navbar-collapse{
|
||||
max-height: 500px;
|
||||
}
|
||||
|
||||
/* 导航部分 结束 */
|
||||
|
||||
|
||||
/* toolbox 开始 */
|
||||
|
||||
.toolbox {
|
||||
position: fixed;
|
||||
bottom:20px;
|
||||
right: 12px;
|
||||
z-index: 999999;
|
||||
}
|
||||
|
||||
.toolbox span{
|
||||
display: block;
|
||||
width: 2.75rem;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.toolbox i{
|
||||
display: block;
|
||||
color: rgba(241,241,241,1);
|
||||
cursor: pointer;
|
||||
border-radius: 5px;
|
||||
font-size: 1.5rem;
|
||||
height: 2.75rem;
|
||||
width: 2.75rem;
|
||||
line-height: 2.75rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.toolbox .toggleView i.show-nogroup {
|
||||
font-size: 1.85rem;
|
||||
}
|
||||
|
||||
.toolbox .setTheme i.setTheme-dark {
|
||||
font-size: 1.35rem;
|
||||
}
|
||||
|
||||
.toolbox .setTheme i.setTheme-light {
|
||||
font-size: 1.45rem;
|
||||
}
|
||||
|
||||
.toolbox .showGoTop i.goTop {
|
||||
font-size: 1.55rem;
|
||||
}
|
||||
|
||||
/* toolbox 结束 */
|
||||
|
||||
|
||||
/* 正文部分 开始 */
|
||||
.content {
|
||||
padding: 20px;
|
||||
@@ -75,15 +137,40 @@ body {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.table>thead>tr>th{
|
||||
border:none;
|
||||
.table > tbody > tr > td,
|
||||
.table > tbody > tr > th,
|
||||
.table > tfoot > tr > td,
|
||||
.table > tfoot > tr > th,
|
||||
.table > thead > tr > td,
|
||||
.table > thead > tr > th {
|
||||
position: relative;
|
||||
border:none;
|
||||
line-height:20px;
|
||||
}
|
||||
|
||||
.table .node-group-tag {
|
||||
.table > tbody > tr > td:before,
|
||||
.table > tbody > tr > th:before,
|
||||
.table > tfoot > tr > td:before,
|
||||
.table > tfoot > tr > th:before,
|
||||
.table > thead > tr > td:before,
|
||||
.table > thead > tr > th:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 0.7px;
|
||||
}
|
||||
|
||||
.table .node-group-tag th{
|
||||
font-size: 18px;
|
||||
padding-bottom:15px;
|
||||
}
|
||||
|
||||
.table .node-cell-os-text {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.progress {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
@@ -93,7 +180,6 @@ body {
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
|
||||
.expandRow > td {
|
||||
padding: 0 !important;
|
||||
border-top: 0 !important;
|
||||
@@ -131,25 +217,49 @@ body {
|
||||
}
|
||||
|
||||
.node-cell.network {
|
||||
min-width: 110px;
|
||||
max-width: 110px;
|
||||
min-width: 100px;
|
||||
max-width: 100px;
|
||||
}
|
||||
|
||||
.node-cell.cpu, .node-cell.ram, .node-cell.hdd {
|
||||
min-width: 45px;
|
||||
max-width: 90px;
|
||||
.node-cell.traffic {
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
.node-cell.cpu, .node-cell.ram, .node-cell.hdd, .node-cell.memory {
|
||||
min-width: 50px;
|
||||
max-width: 50px;
|
||||
}
|
||||
|
||||
/*正文结束*/
|
||||
|
||||
/* 服务页 正文*/
|
||||
.service-status {
|
||||
|
||||
}
|
||||
|
||||
.service-status .service-status-th{
|
||||
min-width:60px;
|
||||
}
|
||||
|
||||
.service-status .service-name-th{
|
||||
min-width:50px;
|
||||
}
|
||||
|
||||
.service-status .service-averagelatency-th{
|
||||
min-width:80px;
|
||||
}
|
||||
|
||||
.service-status .service-30daysonline-th{
|
||||
min-width:80px;
|
||||
}
|
||||
|
||||
.service-status .delay-today {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: left;
|
||||
}
|
||||
|
||||
.service-status .delay-today > i {
|
||||
.service-status .delay-today i {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
@@ -163,26 +273,29 @@ body {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin-right: 4px;
|
||||
margin-bottom: -3.25px;
|
||||
border-radius: 3px;
|
||||
box-shadow: inset 0 2px 2px rgba(0, 0, 0, .1);
|
||||
}
|
||||
|
||||
.service-status {
|
||||
|
||||
.service-status .tooltip-inner {
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* 服务页 正文结束 */
|
||||
@media only screen and (max-width: 1200px) {
|
||||
.accordian-body{
|
||||
margin: 5px 0px 5px 10px;
|
||||
}
|
||||
.table .node-group-tag {
|
||||
.table .node-group-tag th{
|
||||
font-size:16px;
|
||||
padding-bottom:6px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 720px) {
|
||||
@media only screen and (max-width: 767px) {
|
||||
body {
|
||||
font-size: 10px !important;
|
||||
padding-top:60px !important;
|
||||
@@ -191,6 +304,23 @@ body {
|
||||
padding: 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.navbar .navbar-nav .open .dropdown-menu>li>a {
|
||||
color: #f1f1f1;
|
||||
}
|
||||
.navbar .navbar-nav .open .dropdown-menu {
|
||||
list-style-image: initial;
|
||||
background-color: #181a1b;
|
||||
border-color: rgba(140, 130, 115, 0.15);
|
||||
box-shadow: rgba(0, 0, 0, 0.18) 0px 6px 12px;
|
||||
}
|
||||
.table > tbody > tr > td:before,
|
||||
.table > tbody > tr > th:before,
|
||||
.table > tfoot > tr > td:before,
|
||||
.table > tfoot > tr > th:before,
|
||||
.table > thead > tr > td:before,
|
||||
.table > thead > tr > th:before {
|
||||
height: 0.5px;
|
||||
}
|
||||
.node-cell.os,
|
||||
.node-cell.uptime,
|
||||
.node-cell.traffic{
|
||||
@@ -208,10 +338,33 @@ body {
|
||||
.accordian-body{
|
||||
margin: 5px 0px 5px 10px;
|
||||
}
|
||||
.table .node-group-tag {
|
||||
font-size:12px;
|
||||
.table .node-group-tag th{
|
||||
font-size:16px;
|
||||
padding-bottom:6px;
|
||||
}
|
||||
.service-status .service-status-th{
|
||||
min-width:30px;
|
||||
}
|
||||
.service-status .delay-today{
|
||||
margin-top:4px;
|
||||
justify-content: center;
|
||||
}
|
||||
.service-status .delay-today i{
|
||||
margin-right:0px;
|
||||
}
|
||||
.service-status .delay-today-text{
|
||||
display: none;
|
||||
visibility: hidden;
|
||||
}
|
||||
.service-status .service-averagelatency-th{
|
||||
min-width:70px;
|
||||
}
|
||||
.service-status .service-30daysonline-th{
|
||||
min-width:75px;
|
||||
}
|
||||
.toolbox {
|
||||
right: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 768px) {
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+107
-10
@@ -2,27 +2,79 @@ const mixinsVue = {
|
||||
data: {
|
||||
cache: [],
|
||||
theme: "light",
|
||||
isSystemTheme: false
|
||||
isSystemTheme: false,
|
||||
showGroup: false,
|
||||
showGoTop: false,
|
||||
preferredTemplate: null,
|
||||
isMobile: false,
|
||||
adaptedTemplates: [
|
||||
{ key: 'default', name: 'Default', icon: 'th large' },
|
||||
{ key: 'angel-kanade', name: 'AngelKanade', icon: 'square' },
|
||||
{ key: 'server-status', name: 'SeverStatus', icon: 'list' }
|
||||
]
|
||||
},
|
||||
created() {
|
||||
this.initTheme()
|
||||
this.isMobile = this.checkIsMobile();
|
||||
this.initTheme();
|
||||
this.storedShowGroup();
|
||||
this.preferredTemplate = this.getCookie('preferred_theme') ? this.getCookie('preferred_theme') : this.$root.defaultTemplate;
|
||||
window.addEventListener('scroll', this.handleScroll);
|
||||
},
|
||||
destroyed() {
|
||||
window.removeEventListener('scroll', this.handleScroll);
|
||||
},
|
||||
methods: {
|
||||
toggleView() {
|
||||
this.showGroup = !this.showGroup;
|
||||
localStorage.setItem("showGroup", JSON.stringify(this.showGroup));
|
||||
return this.showGroup;
|
||||
},
|
||||
storedShowGroup() {
|
||||
const storedShowGroup = localStorage.getItem("showGroup");
|
||||
if (storedShowGroup !== null) {
|
||||
this.showGroup = JSON.parse(storedShowGroup);
|
||||
}
|
||||
},
|
||||
toggleTemplate(template) {
|
||||
if( template != this.preferredTemplate){
|
||||
this.preferredTemplate = template;
|
||||
this.updateCookie("preferred_theme", template);
|
||||
window.location.reload();
|
||||
}
|
||||
},
|
||||
updateCookie(name, value) {
|
||||
document.cookie = name + "=" + value +"; path=/";
|
||||
},
|
||||
getCookie(name) {
|
||||
const cookies = document.cookie.split(';');
|
||||
let cookieValue = null;
|
||||
for (let i = 0; i < cookies.length; i++) {
|
||||
const cookie = cookies[i].trim();
|
||||
if (cookie.startsWith(name + '=')) {
|
||||
cookieValue = cookie.substring(name.length + 1, cookie.length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return cookieValue;
|
||||
},
|
||||
setTheme(title, store = false) {
|
||||
this.theme = title
|
||||
document.body.setAttribute("theme", title)
|
||||
this.theme = title;
|
||||
document.body.setAttribute("theme", title);
|
||||
if (store) {
|
||||
localStorage.setItem("theme", title)
|
||||
this.isSystemTheme = false
|
||||
localStorage.setItem("theme", title);
|
||||
this.isSystemTheme = false;
|
||||
if(this.$root.page == 'index') {
|
||||
this.$root.reloadCharts(); //重新载入echarts图表
|
||||
}
|
||||
}
|
||||
},
|
||||
setSystemTheme() {
|
||||
localStorage.removeItem("theme")
|
||||
this.initTheme()
|
||||
this.isSystemTheme = true
|
||||
localStorage.removeItem("theme");
|
||||
this.initTheme();
|
||||
this.isSystemTheme = true;
|
||||
},
|
||||
initTheme() {
|
||||
const storeTheme = localStorage.getItem("theme")
|
||||
const storeTheme = localStorage.getItem("theme");
|
||||
if (storeTheme === 'dark' || storeTheme === 'light') {
|
||||
this.setTheme(storeTheme, true);
|
||||
} else {
|
||||
@@ -45,5 +97,50 @@ const mixinsVue = {
|
||||
toFixed2(f) {
|
||||
return f.toFixed(2)
|
||||
},
|
||||
logOut(id) {
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/api/logout',
|
||||
data: JSON.stringify({ id: id }),
|
||||
contentType: 'application/json',
|
||||
success: function (resp) {
|
||||
if (resp.code == 200) {
|
||||
window.location.reload();
|
||||
} else {
|
||||
alert('注销失败(Error ' + resp.code + '): ' + resp.message);
|
||||
}
|
||||
},
|
||||
error: function (err) {
|
||||
alert('网络错误: ' + err.responseText);
|
||||
}
|
||||
});
|
||||
},
|
||||
goTop() {
|
||||
$('html, body').animate({ scrollTop: 0 }, 400);
|
||||
return false;
|
||||
},
|
||||
handleScroll() {
|
||||
this.showGoTop = window.scrollY >= 100;
|
||||
},
|
||||
groupingData(data, field) {
|
||||
let map = new Map();
|
||||
let dest = [];
|
||||
|
||||
data.forEach(item => {
|
||||
if (!map.has(item[field])) {
|
||||
dest.push({
|
||||
[field]: item[field],
|
||||
data: [item]
|
||||
});
|
||||
map.set(item[field], item);
|
||||
} else {
|
||||
dest.find(dItem => dItem[field] === item[field]).data.push(item);
|
||||
}
|
||||
});
|
||||
return dest;
|
||||
},
|
||||
checkIsMobile() { // 检测设备类型,页面宽度小于768px认为是移动设备
|
||||
return window.innerWidth <= 768;
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+1
-1
@@ -10,7 +10,7 @@
|
||||
<script src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/semantic-ui/2.4.1/semantic.min.js"></script>
|
||||
<script src="/static/semantic-ui-alerts.min.js"></script>
|
||||
<script src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/vue/2.6.14/vue.min.js"></script>
|
||||
<script src="/static/main.js?v20240213"></script>
|
||||
<script src="/static/main.js?v20240224"></script>
|
||||
<script>
|
||||
(function () {
|
||||
updateLang({{.LANG }});
|
||||
|
||||
+10
@@ -26,6 +26,16 @@
|
||||
<label>{{tr "HideForGuest"}}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="ui enableddns checkbox">
|
||||
<input name="EnableDDNS" type="checkbox" tabindex="0" />
|
||||
<label>{{tr "EnableDDNS"}}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{tr "DDNSDomain"}}</label>
|
||||
<input type="text" name="DDNSDomain" placeholder="{{tr "DDNSDomain"}}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{tr "Note"}}</label>
|
||||
<textarea name="Note"></textarea>
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
<th>IP</th>
|
||||
<th>{{tr "VersionNumber"}}</th>
|
||||
<th>{{tr "HideForGuest"}}</th>
|
||||
<th>{{tr "EnableDDNS"}}</th>
|
||||
<th>{{tr "DDNSDomain"}}</th>
|
||||
<th>{{tr "Secret"}}</th>
|
||||
<th>{{tr "OneKeyInstall"}}</th>
|
||||
<th>{{tr "Note"}}</th>
|
||||
@@ -45,6 +47,8 @@
|
||||
<td>{{$server.Host.IP}}</td>
|
||||
<td>{{$server.Host.Version}}</td>
|
||||
<td>{{$server.HideForGuest}}</td>
|
||||
<td>{{$server.EnableDDNS}}</td>
|
||||
<td>{{$server.DDNSDomain}}</td>
|
||||
<td>
|
||||
<button class="ui icon green mini button" data-clipboard-text="{{$server.Secret}}" data-tooltip="{{tr "ClickToCopy"}}">
|
||||
<i class="copy icon"></i>
|
||||
|
||||
+11
-2
@@ -70,18 +70,24 @@
|
||||
<input type="text" name="IPChangeNotificationTag" placeholder="" value="{{.Conf.IPChangeNotificationTag}}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="ui nf-ssl checkbox ip-change">
|
||||
<div class="ui checkbox ip-change">
|
||||
<input name="EnableIPChangeNotification" type="checkbox" tabindex="0" class="hidden">
|
||||
<label>{{tr "Enable"}}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="ui nf-ssl checkbox plain-ip">
|
||||
<div class="ui checkbox plain-ip">
|
||||
<input name="EnablePlainIPInNotification" type="checkbox" tabindex="0" class="hidden">
|
||||
<label>{{tr "NotificationMessagesDoNotHideIP"}}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="ui checkbox disable-switch-template">
|
||||
<input name="DisableSwitchTemplateInFrontend" type="checkbox" tabindex="0" class="hidden">
|
||||
<label>{{tr "DisableSwitchTemplateInFrontend"}}</label>
|
||||
</div>
|
||||
</div>
|
||||
<button class="ui button" type="submit">{{tr "Save"}}</button>
|
||||
</form>
|
||||
</div>
|
||||
@@ -128,5 +134,8 @@
|
||||
{{if .Conf.EnablePlainIPInNotification}}
|
||||
$('.checkbox.plain-ip').checkbox('set checked')
|
||||
{{ end }}
|
||||
{{if .Conf.DisableSwitchTemplateInFrontend }}
|
||||
$('.checkbox.disable-switch-template').checkbox('set checked')
|
||||
{{ end }}
|
||||
</script>
|
||||
{{end}}
|
||||
|
||||
+3
-2
@@ -1,4 +1,5 @@
|
||||
{{define "theme-angel-kanade/footer"}}
|
||||
</div>
|
||||
<div class="ui inverted vertical footer segment">
|
||||
<div class="ui center aligned is-size-7 container">
|
||||
<b>© <a style="color: white;" href="/">{{.Conf.Site.Brand}}</a></b> |
|
||||
@@ -12,13 +13,13 @@
|
||||
<script src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/semantic-ui/2.4.1/semantic.min.js"></script>
|
||||
<script src="/static/semantic-ui-alerts.min.js"></script>
|
||||
<script src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/vue/2.6.14/vue.min.js"></script>
|
||||
<script src="/static/main.js?v20240213"></script>
|
||||
<script src="/static/main.js?v20240224"></script>
|
||||
<script src="/static/theme-default/js/mixin.js?v20240302"></script>
|
||||
<script>
|
||||
(function () {
|
||||
updateLang({{.LANG }});
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
{{end}}
|
||||
+101
-100
@@ -3,118 +3,116 @@
|
||||
{{if ts .CustomCode}} {{.CustomCode|safe}} {{end}}
|
||||
{{template "theme-angel-kanade/menu" .}}
|
||||
<div class="nb-container">
|
||||
<div class="ui container">
|
||||
<div id="app">
|
||||
<div class="ui styled fluid accordion" v-for="group in groups">
|
||||
<div class="active title">
|
||||
<i class="dropdown icon"></i>
|
||||
@#(group.Tag!==''?group.Tag:'{{tr "Default"}}')#@
|
||||
</div>
|
||||
<div class="active content">
|
||||
<div class="ui four stackable status cards">
|
||||
<div v-for="server in group.data" :id="server.ID" class="ui card">
|
||||
<div class="content" v-if="server.Host" style="margin-top: 10px; padding-bottom: 5px">
|
||||
<div class="header">
|
||||
<img v-if="server.Host.CountryCode" style="border-radius: 50%;box-shadow:-1px -1px 2px #eee, 1px 1px 2px #000;width:19px;" :src="'https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/flag-icon-css/4.1.5/flags/1x1/'+server.Host.CountryCode + '.svg'" alt="国家"/> <i v-if='server.Host.Platform == "darwin"'
|
||||
class="apple icon"></i><i v-else-if='isWindowsPlatform(server.Host.Platform)'
|
||||
class="windows icon"></i><i v-else :class="'fl-' + getFontLogoClass(server.Host.Platform)"></i>
|
||||
@#server.Name + (server.live?'':'[{{tr "Offline"}}]')#@
|
||||
<i class="nezha-secondary-font info circle icon" style="height: 28px"></i>
|
||||
<div class="ui content popup" style="margin-bottom: 0">
|
||||
{{tr "Platform"}}: @#server.Host.Platform#@-@#server.Host.PlatformVersion#@
|
||||
[<span
|
||||
v-if="server.Host.Virtualization">@#server.Host.Virtualization#@:</span>@#server.Host.Arch#@]<br />
|
||||
CPU: @#server.Host.CPU#@<br />
|
||||
{{tr "DiskUsed"}}:
|
||||
@#formatByteSize(server.State.DiskUsed)#@/@#formatByteSize(server.Host.DiskTotal)#@<br />
|
||||
{{tr "MemUsed"}}:
|
||||
@#formatByteSize(server.State.MemUsed)#@/@#formatByteSize(server.Host.MemTotal)#@<br />
|
||||
{{tr "SwapUsed"}}:
|
||||
@#formatByteSize(server.State.SwapUsed)#@/@#formatByteSize(server.Host.SwapTotal)#@<br />
|
||||
{{tr "NetTransfer"}}: <i
|
||||
class="arrow alternate circle down outline icon"></i>@#formatByteSize(server.State.NetInTransfer)#@<i
|
||||
class="arrow alternate circle up outline icon"></i>@#formatByteSize(server.State.NetOutTransfer)#@<br />
|
||||
{{tr "Load"}}: @# toFixed2(server.State.Load1) #@/@# toFixed2(server.State.Load5) #@/@#
|
||||
toFixed2(server.State.Load15) #@<br />
|
||||
{{tr "ProcessCount"}}: @# server.State.ProcessCount #@<br />
|
||||
{{tr "ConnCount"}}: TCP @# server.State.TcpConnCount #@ / UDP @# server.State.UdpConnCount #@<br />
|
||||
{{tr "BootTime"}}: @# formatTimestamp(server.Host.BootTime) #@<br />
|
||||
{{tr "LastActive"}}: @# new Date(server.LastActive).toLocaleString() #@<br />
|
||||
{{tr "Version"}}: @#server.Host.Version#@<br />
|
||||
</div>
|
||||
<div class="ui divider" style="margin-bottom: 5px"></div>
|
||||
<div class="ui container">
|
||||
<div class="ui styled fluid accordion" v-for="group in groups">
|
||||
<div class="active title">
|
||||
<i class="dropdown icon"></i>
|
||||
@#(group.Tag!==''?group.Tag:'{{tr "Default"}}')#@
|
||||
</div>
|
||||
<div class="active content">
|
||||
<div class="ui four stackable status cards">
|
||||
<div v-for="server in group.data" :id="server.ID" class="ui card">
|
||||
<div class="content" v-if="server.Host" style="margin-top: 10px; padding-bottom: 5px">
|
||||
<div class="header">
|
||||
<img v-if="server.Host.CountryCode" style="border-radius: 50%;box-shadow:-1px -1px 2px #eee, 1px 1px 2px #000;width:19px;" :src="'https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/flag-icon-css/4.1.5/flags/1x1/'+server.Host.CountryCode + '.svg'" alt="国家"/> <i v-if='server.Host.Platform == "darwin"'
|
||||
class="apple icon"></i><i v-else-if='isWindowsPlatform(server.Host.Platform)'
|
||||
class="windows icon"></i><i v-else :class="'fl-' + getFontLogoClass(server.Host.Platform)"></i>
|
||||
@#server.Name + (server.live?'':'[{{tr "Offline"}}]')#@
|
||||
<i class="nezha-secondary-font info circle icon" style="height: 28px"></i>
|
||||
<div class="ui content popup" style="margin-bottom: 0">
|
||||
{{tr "Platform"}}: @#server.Host.Platform#@-@#server.Host.PlatformVersion#@
|
||||
[<span
|
||||
v-if="server.Host.Virtualization">@#server.Host.Virtualization#@:</span>@#server.Host.Arch#@]<br />
|
||||
CPU: @#server.Host.CPU#@<br />
|
||||
{{tr "DiskUsed"}}:
|
||||
@#formatByteSize(server.State.DiskUsed)#@/@#formatByteSize(server.Host.DiskTotal)#@<br />
|
||||
{{tr "MemUsed"}}:
|
||||
@#formatByteSize(server.State.MemUsed)#@/@#formatByteSize(server.Host.MemTotal)#@<br />
|
||||
{{tr "SwapUsed"}}:
|
||||
@#formatByteSize(server.State.SwapUsed)#@/@#formatByteSize(server.Host.SwapTotal)#@<br />
|
||||
{{tr "NetTransfer"}}: <i
|
||||
class="arrow alternate circle down outline icon"></i>@#formatByteSize(server.State.NetInTransfer)#@<i
|
||||
class="arrow alternate circle up outline icon"></i>@#formatByteSize(server.State.NetOutTransfer)#@<br />
|
||||
{{tr "Load"}}: @# toFixed2(server.State.Load1) #@/@# toFixed2(server.State.Load5) #@/@#
|
||||
toFixed2(server.State.Load15) #@<br />
|
||||
{{tr "ProcessCount"}}: @# server.State.ProcessCount #@<br />
|
||||
{{tr "ConnCount"}}: TCP @# server.State.TcpConnCount #@ / UDP @# server.State.UdpConnCount #@<br />
|
||||
{{tr "BootTime"}}: @# formatTimestamp(server.Host.BootTime) #@<br />
|
||||
{{tr "LastActive"}}: @# new Date(server.LastActive).toLocaleString() #@<br />
|
||||
{{tr "Version"}}: @#server.Host.Version#@<br />
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="ui grid">
|
||||
<div class="three wide column">CPU</div>
|
||||
<div class="thirteen wide column">
|
||||
<div :class="formatPercent(server.live,server.State.CPU, 100).class">
|
||||
<div class="bar" :style="formatPercent(server.live,server.State.CPU, 100).style">
|
||||
<small>@#formatPercent(server.live,server.State.CPU,100).percent#@%</small>
|
||||
</div>
|
||||
<div class="ui divider" style="margin-bottom: 5px"></div>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="ui grid">
|
||||
<div class="three wide column">CPU</div>
|
||||
<div class="thirteen wide column">
|
||||
<div :class="formatPercent(server.live,server.State.CPU, 100).class">
|
||||
<div class="bar" :style="formatPercent(server.live,server.State.CPU, 100).style">
|
||||
<small>@#formatPercent(server.live,server.State.CPU,100).percent#@%</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="three wide column">{{tr "MemUsed"}}</div>
|
||||
<div class="thirteen wide column">
|
||||
<div :class="formatPercent(server.live,server.State.MemUsed, server.Host.MemTotal).class">
|
||||
<div class="bar"
|
||||
:style="formatPercent(server.live,server.State.MemUsed, server.Host.MemTotal).style">
|
||||
<small>@#parseInt(server.State?server.State.MemUsed/server.Host.MemTotal*100:0)#@%</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="three wide column">{{tr "MemUsed"}}</div>
|
||||
<div class="thirteen wide column">
|
||||
<div :class="formatPercent(server.live,server.State.MemUsed, server.Host.MemTotal).class">
|
||||
<div class="bar"
|
||||
:style="formatPercent(server.live,server.State.MemUsed, server.Host.MemTotal).style">
|
||||
<small>@#parseInt(server.State?server.State.MemUsed/server.Host.MemTotal*100:0)#@%</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="three wide column">{{tr "SwapUsed"}}</div>
|
||||
<div class="thirteen wide column">
|
||||
<div :class="formatPercent(server.live,server.State.SwapUsed, server.Host.SwapTotal).class">
|
||||
<div class="bar"
|
||||
:style="formatPercent(server.live,server.State.SwapUsed, server.Host.SwapTotal).style">
|
||||
<small>@#parseInt(server.State?server.State.SwapUsed/server.Host.SwapTotal*100:0)#@%</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="three wide column">{{tr "SwapUsed"}}</div>
|
||||
<div class="thirteen wide column">
|
||||
<div :class="formatPercent(server.live,server.State.SwapUsed, server.Host.SwapTotal).class">
|
||||
<div class="bar"
|
||||
:style="formatPercent(server.live,server.State.SwapUsed, server.Host.SwapTotal).style">
|
||||
<small>@#parseInt(server.State?server.State.SwapUsed/server.Host.SwapTotal*100:0)#@%</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="three wide column">{{tr "NetSpeed"}}</div>
|
||||
<div class="thirteen wide column">
|
||||
<i class="arrow alternate circle down outline icon"></i>
|
||||
@#formatByteSize(server.State.NetInSpeed)#@/s
|
||||
<i class="arrow alternate circle up outline icon"></i>
|
||||
@#formatByteSize(server.State.NetOutSpeed)#@/s
|
||||
</div>
|
||||
<div class="three wide column">{{tr "NetTransfer"}}</div>
|
||||
<div class="thirteen wide column">
|
||||
<i class="arrow circle down icon"></i>
|
||||
@#formatByteSize(server.State.NetInTransfer)#@
|
||||
|
||||
<i class="arrow circle up icon"></i>
|
||||
@#formatByteSize(server.State.NetOutTransfer)#@
|
||||
</div>
|
||||
<div class="three wide column">{{tr "DiskUsed"}}</div>
|
||||
<div class="thirteen wide column">
|
||||
<div :class="formatPercent(server.live,server.State.DiskUsed, server.Host.DiskTotal).class">
|
||||
<div class="bar"
|
||||
:style="formatPercent(server.live,server.State.DiskUsed, server.Host.DiskTotal).style">
|
||||
<small>@#parseInt(server.State?server.State.DiskUsed/server.Host.DiskTotal*100:0)#@%</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="three wide column">{{tr "NetSpeed"}}</div>
|
||||
<div class="thirteen wide column">
|
||||
<i class="arrow alternate circle down outline icon"></i>
|
||||
@#formatByteSize(server.State.NetInSpeed)#@/s
|
||||
<i class="arrow alternate circle up outline icon"></i>
|
||||
@#formatByteSize(server.State.NetOutSpeed)#@/s
|
||||
</div>
|
||||
<div class="three wide column">{{tr "NetTransfer"}}</div>
|
||||
<div class="thirteen wide column">
|
||||
<i class="arrow circle down icon"></i>
|
||||
@#formatByteSize(server.State.NetInTransfer)#@
|
||||
|
||||
<i class="arrow circle up icon"></i>
|
||||
@#formatByteSize(server.State.NetOutTransfer)#@
|
||||
</div>
|
||||
<div class="three wide column">{{tr "DiskUsed"}}</div>
|
||||
<div class="thirteen wide column">
|
||||
<div :class="formatPercent(server.live,server.State.DiskUsed, server.Host.DiskTotal).class">
|
||||
<div class="bar"
|
||||
:style="formatPercent(server.live,server.State.DiskUsed, server.Host.DiskTotal).style">
|
||||
<small>@#parseInt(server.State?server.State.DiskUsed/server.Host.DiskTotal*100:0)#@%</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="three wide column">{{tr "Info"}}</div>
|
||||
<div class="thirteen wide column">
|
||||
<i class="bi bi-cpu-fill" style="font-size: 1.1rem; color: #4a86e8;"></i> @#getCoreAndGHz(server.Host.CPU)#@
|
||||
|
||||
<i class="bi bi-memory" style="font-size: 1.1rem; color: #00ac0d;"></i> @#getByteToGB(server.Host.MemTotal)#@
|
||||
|
||||
<i class="bi bi-hdd-rack-fill" style="font-size: 1.1rem; color: #980000"></i> @#getByteToGB(server.Host.DiskTotal)#@
|
||||
</div>
|
||||
<div class="three wide column">{{tr "Uptime"}}</div>
|
||||
<div class="thirteen wide column">
|
||||
<i class="clock icon"></i>@#secondToDate(server.State.Uptime)#@
|
||||
</div>
|
||||
</div>
|
||||
<div class="three wide column">{{tr "Info"}}</div>
|
||||
<div class="thirteen wide column">
|
||||
<i class="bi bi-cpu-fill" style="font-size: 1.1rem; color: #4a86e8;"></i> @#getCoreAndGHz(server.Host.CPU)#@
|
||||
|
||||
<i class="bi bi-memory" style="font-size: 1.1rem; color: #00ac0d;"></i> @#getByteToGB(server.Host.MemTotal)#@
|
||||
|
||||
<i class="bi bi-hdd-rack-fill" style="font-size: 1.1rem; color: #980000"></i> @#getByteToGB(server.Host.DiskTotal)#@
|
||||
</div>
|
||||
<div class="three wide column">{{tr "Uptime"}}</div>
|
||||
<div class="thirteen wide column">
|
||||
<i class="clock icon"></i>@#secondToDate(server.State.Uptime)#@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content" v-else>
|
||||
<p>@#server.Name#@</p>
|
||||
<p>{{tr "ServerIsOffline"}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content" v-else>
|
||||
<p>@#server.Name#@</p>
|
||||
<p>{{tr "ServerIsOffline"}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -129,10 +127,13 @@
|
||||
el: '#app',
|
||||
delimiters: ['@#', '#@'],
|
||||
data: {
|
||||
defaultTemplate: {{.Conf.Site.Theme}},
|
||||
templates: {{.Themes}},
|
||||
data: initData,
|
||||
groups: [],
|
||||
cache: [],
|
||||
},
|
||||
mixins: [mixinsVue],
|
||||
created() {
|
||||
this.group()
|
||||
},
|
||||
|
||||
+12
@@ -1,4 +1,5 @@
|
||||
{{define "theme-angel-kanade/menu"}}
|
||||
<div id="app">
|
||||
<div class="ui large top fixed menu nb-menu">
|
||||
<div class="ui container">
|
||||
<a class="item" href="/">
|
||||
@@ -15,6 +16,17 @@
|
||||
{{else}}
|
||||
<a class='item{{if eq .MatchedPath "/"}} active{{end}}' href="/"><i class="home icon"></i>{{tr "Home"}}</a>
|
||||
<a class='item{{if eq .MatchedPath "/service"}} active{{end}}' href="/service"><i class="rss icon"></i>{{tr "Services"}}</a>
|
||||
{{ if not .Conf.DisableSwitchTemplateInFrontend }}
|
||||
<div class="item ui simple dropdown">
|
||||
<div class="text"><i class="bi bi-incognito icon" style="margin-right:3px;"></i>{{tr "Template" }}<i class="dropdown icon" style="margin-right:0px;"></i></div>
|
||||
<div class="menu">
|
||||
<a v-for="(item, index) in adaptedTemplates" :key="index" @click="toggleTemplate(item.key)" class="item">
|
||||
<i :class="item.icon + ' icon'"></i>@#item.name#@
|
||||
<i class="check icon" v-if="preferredTemplate === item.key"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{end}}
|
||||
<div class="right menu">
|
||||
<div class="item">
|
||||
|
||||
+11
-1
@@ -76,10 +76,20 @@
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{template "theme-angel-kanade/footer" .}}
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#app',
|
||||
delimiters: ['@#', '#@'],
|
||||
data: {
|
||||
defaultTemplate: {{.Conf.Site.Theme}},
|
||||
templates: {{.Themes}}
|
||||
},
|
||||
mixins: [mixinsVue]
|
||||
})
|
||||
</script>
|
||||
{{end}}
|
||||
+10
-3
@@ -109,9 +109,9 @@
|
||||
<script>
|
||||
const monitorInfo = JSON.parse('{{.MonitorInfos}}');
|
||||
const initData = JSON.parse('{{.Servers}}').servers;
|
||||
let MaxTCPPingValue = {{.MaxTCPPingValue}};
|
||||
let MaxTCPPingValue = {{.Conf.MaxTCPPingValue}};
|
||||
if (MaxTCPPingValue == null) {
|
||||
MaxTCPPingValue = 300;
|
||||
MaxTCPPingValue = 1000;
|
||||
}
|
||||
// 基于准备好的dom,初始化echarts实例
|
||||
var myChart = echarts.init(document.getElementById('monitor-info-container'));
|
||||
@@ -179,6 +179,10 @@
|
||||
mounted() {
|
||||
this.DarkMode();
|
||||
this.parseMonitorInfo(monitorInfo);
|
||||
window.addEventListener('resize', this.resizeHandle);
|
||||
},
|
||||
destroyed () {
|
||||
window.removeEventListener('resize', this.resizeHandle)
|
||||
},
|
||||
methods: {
|
||||
DarkMode() {
|
||||
@@ -239,7 +243,10 @@
|
||||
}
|
||||
myChart.clear();
|
||||
myChart.setOption(this.option);
|
||||
}
|
||||
},
|
||||
resizeHandle () {
|
||||
this.myChart.resize();
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -61,7 +61,6 @@
|
||||
</section>
|
||||
<section class="monitor-container">
|
||||
{{range $service := .Services}}
|
||||
{{if $service.Monitor.EnableShowInService}}
|
||||
<section class="monitor-info-container">
|
||||
<div class="monitor-state">
|
||||
<span class="monitor-state-dot {{className (divU64 $service.CurrentUp (addU64 $service.CurrentUp $service.CurrentDown))}}"></span>
|
||||
@@ -83,7 +82,6 @@
|
||||
</div>
|
||||
</section>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</section>
|
||||
{{if .CycleTransferStats}}
|
||||
<h2 class="mdui-m-t-5 mdui-text-center">{{tr "CycleTransferStats"}}</h2>
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
{{define "theme-default/footer"}}
|
||||
</div>
|
||||
<div class="ui inverted vertical footer segment">
|
||||
<div class="ui center aligned is-size-7 container">
|
||||
<b>© <a style="color: white;" href="/">{{.Conf.Site.Brand}}</a></b> | <small>Powered by <a
|
||||
href="https://github.com/naiba/nezha" style="color: white;" target="_blank">{{tr "NezhaMonitoring"}}</a>
|
||||
{{.Version}}</small>
|
||||
</div>
|
||||
</div>
|
||||
{{ if not .Conf.DisableSwitchTemplateInFrontend }}
|
||||
<script>
|
||||
function showSwitchTemplate(list, currentBackendTheme) {
|
||||
console.log(list, currentBackendTheme);
|
||||
console.log("currentBackendTheme:",currentBackendTheme);
|
||||
}
|
||||
showSwitchTemplate({{ .Themes }}, {{ .Conf.Site.Theme }})
|
||||
</script>
|
||||
{{ end }}
|
||||
<script>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
{{end}}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
{{define "theme-default/header"}}
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{.Conf.Language}}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<meta content="telephone=no" name="format-detection">
|
||||
<title>{{.Title}}</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/semantic.min.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/assets/font-logos.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.min.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/lipis/[email protected]/css/flag-icons.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="/static/semantic-ui-alerts.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="/static/theme-default/css/main.css?v20240226">
|
||||
<link rel="shortcut icon" type="image/png" href="/static/logo.svg" />
|
||||
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/semantic.min.js"></script>
|
||||
<script src="/static/semantic-ui-alerts.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/echarts.min.js"></script>
|
||||
<script src="/static/theme-default/js/mixin.js?v20240302"></script>
|
||||
</head>
|
||||
<body>
|
||||
{{end}}
|
||||
+240
-28
@@ -1,10 +1,10 @@
|
||||
{{define "theme-default/home"}}
|
||||
{{template "common/header" .}}
|
||||
{{template "theme-default/header" .}}
|
||||
{{if ts .CustomCode}} {{.CustomCode|safe}} {{end}}
|
||||
{{template "common/menu" .}}
|
||||
{{template "theme-default/menu" .}}
|
||||
<div class="nb-container">
|
||||
<div class="ui container">
|
||||
<div id="app">
|
||||
<div class="ui container">
|
||||
<template v-if="groups">
|
||||
<div class="ui styled fluid accordion" v-for="group in groups">
|
||||
<div class="active title">
|
||||
<i class="dropdown icon"></i>
|
||||
@@ -15,12 +15,13 @@
|
||||
<div v-for="server in group.data" :id="server.ID" class="ui card">
|
||||
<div class="content" v-if="server.Host" style="margin-top: 10px; padding-bottom: 5px">
|
||||
<div class="header">
|
||||
<i :class="server.Host.CountryCode + ' flag'"></i> <i v-if='server.Host.Platform == "darwin"'
|
||||
<i :class="'fi fi-' + server.Host.CountryCode"></i> <i v-if='server.Host.Platform == "darwin"'
|
||||
class="apple icon"></i><i v-else-if='isWindowsPlatform(server.Host.Platform)'
|
||||
class="windows icon"></i><i v-else :class="'fl-' + getFontLogoClass(server.Host.Platform)"></i>
|
||||
@#server.Name + (server.live?'':'[{{tr "Offline"}}]')#@
|
||||
<i class="nezha-secondary-font info circle icon" style="height: 28px"></i>
|
||||
<div class="ui content popup" style="margin-bottom: 0">
|
||||
<i @click="togglePopup($event, server.ID)" aria-expanded="false" class="nezha-secondary-font info circle icon"></i>
|
||||
<div class="ui content popup" :class="{ 'visible': isActive(server.ID) }" style="margin-bottom: 0;">
|
||||
<i class="closePopup window close icon" @click="closePopup(server.ID)"></i>
|
||||
{{tr "Platform"}}: @#server.Host.Platform#@-@#server.Host.PlatformVersion#@
|
||||
[<span
|
||||
v-if="server.Host.Virtualization">@#server.Host.Virtualization#@:</span>@#server.Host.Arch#@]<br />
|
||||
@@ -40,7 +41,8 @@
|
||||
{{tr "ConnCount"}}: TCP @# server.State.TcpConnCount #@ / UDP @# server.State.UdpConnCount #@<br />
|
||||
{{tr "BootTime"}}: @# formatTimestamp(server.Host.BootTime) #@<br />
|
||||
{{tr "LastActive"}}: @# new Date(server.LastActive).toLocaleString() #@<br />
|
||||
{{tr "Version"}}: @#server.Host.Version#@<br />
|
||||
{{tr "Version"}}: @#server.Host.Version#@
|
||||
<div class="chartbox" :key="server.ID" :ref="`chart${server.ID}`" style="width: 100%; height: auto; margin-bottom: 2px;"></div>
|
||||
</div>
|
||||
<div class="ui divider" style="margin-bottom: 5px"></div>
|
||||
</div>
|
||||
@@ -72,13 +74,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="three wide column">{{tr "NetSpeed"}}</div>
|
||||
<div class="thirteen wide column">
|
||||
<i class="arrow alternate circle down outline icon"></i>
|
||||
@#formatByteSize(server.State.NetInSpeed)#@/s
|
||||
<i class="arrow alternate circle up outline icon"></i>
|
||||
@#formatByteSize(server.State.NetOutSpeed)#@/s
|
||||
</div>
|
||||
<div class="three wide column">{{tr "DiskUsed"}}</div>
|
||||
<div class="thirteen wide column">
|
||||
<div :class="formatPercent(server.live,server.State.DiskUsed, server.Host.DiskTotal).class">
|
||||
@@ -88,6 +83,36 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="three wide column">{{tr "NetSpeed"}}</div>
|
||||
<div class="thirteen wide column">
|
||||
<i class="arrow alternate circle down outline icon"></i>
|
||||
@#formatByteSize(server.State.NetInSpeed)#@/s
|
||||
<i class="arrow alternate circle up outline icon"></i>
|
||||
@#formatByteSize(server.State.NetOutSpeed)#@/s
|
||||
</div>
|
||||
<div class="three wide column">{{tr "NetTransfer"}}</div>
|
||||
<div class="thirteen wide column">
|
||||
<i class="arrow circle down icon"></i>
|
||||
@#formatByteSize(server.State.NetInTransfer)#@
|
||||
|
||||
<i class="arrow circle up icon"></i>
|
||||
@#formatByteSize(server.State.NetOutTransfer)#@
|
||||
</div>
|
||||
<div class="three wide column">{{tr "Stat"}}</div>
|
||||
<div class="thirteen wide column">
|
||||
<i class="bi bi-cpu-fill" style="font-size: 1.1rem; color: #4a86e8;"></i> @#getCoreAndGHz(server.Host.CPU)#@
|
||||
|
||||
<i class="bi bi-memory" style="font-size: 1.1rem; color: #00ac0d;"></i> @#getK2Gb(server.Host.MemTotal)#@
|
||||
|
||||
<i class="bi bi-hdd" style="font-size: 1.1rem; color: #e41e10"></i> @#getK2Gb(server.Host.DiskTotal)#@
|
||||
</div>
|
||||
<div class="three wide column">{{tr "Load"}}</div>
|
||||
<div class="thirteen wide column">
|
||||
<i class="bi bi-activity" style="font-size: 1.1rem; color: #e41e10;"></i>
|
||||
@# toFixed2(server.State.Load1) #@ |
|
||||
@# toFixed2(server.State.Load5) #@ |
|
||||
@# toFixed2(server.State.Load15) #@
|
||||
</div>
|
||||
<div class="three wide column">{{tr "Uptime"}}</div>
|
||||
<div class="thirteen wide column">
|
||||
<i class="clock icon"></i>@#secondToDate(server.State.Uptime)#@
|
||||
@@ -103,30 +128,177 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
{{template "common/footer" .}}
|
||||
{{template "theme-default/footer" .}}
|
||||
<script>
|
||||
const initData = JSON.parse('{{.Servers}}').servers;
|
||||
var statusCards = new Vue({
|
||||
el: '#app',
|
||||
delimiters: ['@#', '#@'],
|
||||
data: {
|
||||
data: initData,
|
||||
page: 'index',
|
||||
defaultTemplate: {{.Conf.Site.Theme}},
|
||||
templates: {{.Themes}},
|
||||
servers: [],
|
||||
groups: [],
|
||||
cache: [],
|
||||
chartDataList: [],
|
||||
activePopup: null,
|
||||
},
|
||||
mixins: [mixinsVue],
|
||||
created() {
|
||||
this.servers = JSON.parse('{{.Servers}}').servers;
|
||||
this.group()
|
||||
},
|
||||
mounted() {
|
||||
$('.nezha-secondary-font.info.icon').popup({
|
||||
popup: '.ui.content.popup',
|
||||
exclusive: true,
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
togglePopup(event, id) {
|
||||
// 切换弹出层的激活状态
|
||||
this.activePopup = this.activePopup === id ? null : id;
|
||||
this.showCharts(id);
|
||||
},
|
||||
isActive(id) {
|
||||
// 检查弹出层是否处于激活状态
|
||||
return this.activePopup === id;
|
||||
},
|
||||
closePopup(id) {
|
||||
this.activePopup = null;
|
||||
},
|
||||
showCharts(id) {
|
||||
// 发起数据请求
|
||||
const url = `/api/v1/monitor/${id}`;
|
||||
fetch(url)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.result) { // 数据请求成功,更新数据并渲染图表
|
||||
this.chartDataList[id - 1] = data.result;
|
||||
this.$nextTick(() => {
|
||||
this.renderCharts(id);
|
||||
});
|
||||
} else {
|
||||
console.log('this agent (id:'+ id + ') has no monitor.');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching data:', error);
|
||||
});
|
||||
},
|
||||
renderCharts(id) {
|
||||
if (!this.chartDataList[id - 1]) return;
|
||||
const MaxTCPPingValue = {{.Conf.MaxTCPPingValue}} ? {{.Conf.MaxTCPPingValue}} : 300;
|
||||
const isMobile = this.checkIsMobile();
|
||||
const fontSize = isMobile ? 10 : 9;
|
||||
const itemGap = isMobile ? 6 : 6;
|
||||
const itemWidth = isMobile ? 10 : 10;
|
||||
const itemHeight = isMobile ? 10 : 10;
|
||||
const gridLeft = 25;
|
||||
const gridRight = 12;
|
||||
const fontColor = "rgba(0, 0, 0, 0.68)";
|
||||
const backgroundColor = '';
|
||||
const borderColor = "#ffffff";
|
||||
const chartData = this.chartDataList[id - 1];
|
||||
const chartContainer = this.$refs[`chart${id}`][0];
|
||||
const chart = echarts.init(chartContainer, null, {
|
||||
renderer: 'canvas',
|
||||
useDirtyRect: false,
|
||||
width: 'auto',
|
||||
height: 120,
|
||||
});
|
||||
const xAxisData = chartData[0].created_at.map(time => new Date(time).toLocaleString());
|
||||
const seriesData = chartData.map(item => {
|
||||
let loss = 0;
|
||||
const data = item.avg_delay.map((avgDelay, index) => {
|
||||
if(avgDelay > 0 && avgDelay < MaxTCPPingValue){
|
||||
loss += avgDelay > 0.9 * MaxTCPPingValue ? 1 : 0;
|
||||
return [item.created_at[index], avgDelay];
|
||||
}else{
|
||||
loss += 1;
|
||||
}
|
||||
});
|
||||
const lossRate = ((loss / item.created_at.length) * 100).toFixed(1);
|
||||
item.monitor_name = item.monitor_name.includes("%") ? item.monitor_name : `${item.monitor_name} ${lossRate}%`;
|
||||
return {
|
||||
name: item.monitor_name,
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'none',
|
||||
data: data,
|
||||
connectNulls: true
|
||||
};
|
||||
});
|
||||
const option = {
|
||||
backgroundColor: backgroundColor,
|
||||
title: {
|
||||
show: false
|
||||
},
|
||||
tooltip: {
|
||||
show: true,
|
||||
trigger: 'axis',
|
||||
textStyle: {
|
||||
fontSize: fontSize,
|
||||
color: fontColor
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
icon: 'rect',
|
||||
data: chartData.map(item => item.monitor_name),
|
||||
show: true,
|
||||
textStyle: {
|
||||
fontSize: fontSize,
|
||||
color: fontColor
|
||||
},
|
||||
lineStyle: {
|
||||
cap: 'butt'
|
||||
},
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
itemGap: itemGap,
|
||||
itemWidth: itemWidth,
|
||||
itemHeight: itemHeight,
|
||||
padding: [5,0,5,0]
|
||||
},
|
||||
xAxis: {
|
||||
type: 'time',
|
||||
data: xAxisData,
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
fontSize: fontSize
|
||||
}
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
fontSize: fontSize
|
||||
}
|
||||
}
|
||||
},
|
||||
dataZoom: [
|
||||
{
|
||||
show: false,
|
||||
type: 'slider',
|
||||
start: 0,
|
||||
end: 100
|
||||
}
|
||||
],
|
||||
series: seriesData,
|
||||
textStyle: {
|
||||
fontSize: fontSize,
|
||||
color: fontColor
|
||||
},
|
||||
grid: {
|
||||
top: '30',
|
||||
bottom: '20',
|
||||
left: gridLeft,
|
||||
right: gridRight
|
||||
}
|
||||
};
|
||||
chart.setOption(option);
|
||||
},
|
||||
checkIsMobile() { // 检测设备类型,页面宽度小于768px认为是移动设备
|
||||
return window.innerWidth <= 768;
|
||||
},
|
||||
toFixed2(f) {
|
||||
return f.toFixed(2)
|
||||
},
|
||||
@@ -191,7 +363,7 @@
|
||||
return '';
|
||||
},
|
||||
group() {
|
||||
this.groups = groupingData(this.data, "Tag")
|
||||
this.groups = groupingData(this.servers, "Tag")
|
||||
},
|
||||
formatPercent(live, used, total) {
|
||||
const percent = live ? (parseInt(used / total * 100) || 0) : -1
|
||||
@@ -227,7 +399,7 @@
|
||||
secondToDate(s) {
|
||||
var d = Math.floor(s / 3600 / 24);
|
||||
if (d > 0) {
|
||||
return d + ' {{tr "Day"}}'
|
||||
return d + " {{tr "Day"}}"
|
||||
}
|
||||
var h = Math.floor(s / 3600 % 24);
|
||||
var m = Math.floor(s / 60 % 60);
|
||||
@@ -238,8 +410,48 @@
|
||||
return new Date(t * 1000).toLocaleString()
|
||||
},
|
||||
formatByteSize(bs) {
|
||||
const x = readableBytes(bs)
|
||||
const x = this.readableBytes(bs)
|
||||
return x != "NaN undefined" ? x : '0B'
|
||||
},
|
||||
readableBytes(bytes) {
|
||||
if (!bytes) {
|
||||
return '0B'
|
||||
}
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(1024)),
|
||||
sizes = ["B", "K", "M", "G", "T", "P", "E", "Z", "Y"];
|
||||
return parseFloat((bytes / Math.pow(1024, i)).toFixed(2)) + sizes[i];
|
||||
},
|
||||
getCoreAndGHz(str){
|
||||
if((str || []).hasOwnProperty(0) === false){
|
||||
return '';
|
||||
}
|
||||
str = str[0];
|
||||
let GHz = str.match(/(\d|\.)+GHz/g);
|
||||
let Core = str.match(/(\d|\.)+ Physical/g);
|
||||
GHz = GHz!==null?GHz.hasOwnProperty(0)===false?'':GHz[0]:''
|
||||
Core = Core!==null?Core.hasOwnProperty(0)===false?'?':Core[0]:'?'
|
||||
if(Core === '?'){
|
||||
let Core = str.match(/(\d|\.)+ Virtual/g);
|
||||
Core = Core!==null?Core.hasOwnProperty(0)===false?'?':Core[0]:'?'
|
||||
return Core.replace('Virtual','Core')
|
||||
}
|
||||
return Core.replace('Physical','Core');
|
||||
},
|
||||
getK2Gb(bs){
|
||||
bs = bs / 1024 /1024 /1024;
|
||||
if(bs>=1){
|
||||
return Math.ceil(bs.toFixed(2)) + 'GB';
|
||||
}else{
|
||||
bs = bs * 1024;
|
||||
return Math.ceil(bs.toFixed(2)) + 'MB';
|
||||
}
|
||||
},
|
||||
listTipsMouseenter(obj,strs,tipsNum=1){
|
||||
this.layerIndex = layer.tips(strs, '#'+obj,{tips: [tipsNum, 'rgb(0 0 0 / 85%)'],time:0});
|
||||
$('#'+obj).attr('layerIndex',this.layerIndex)
|
||||
},
|
||||
listTipsMouseleave(obj){
|
||||
layer.close(this.layerIndex)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
{{define "theme-default/menu"}}
|
||||
<div id="app">
|
||||
<div class="ui large top fixed menu nb-menu" style="z-index:9999999;">
|
||||
<div class="ui container">
|
||||
<a class="item" href="/">
|
||||
<img src="/static/logo.svg?v20210804">
|
||||
</a>
|
||||
<a class='item' href="/"><i class="home icon"></i>{{tr "Home"}}</a>
|
||||
<template v-if="isMobile">
|
||||
<div class="item ui simple dropdown">
|
||||
<div class="text"><i class="bi bi-gear-wide-connected icon" style="margin-right:3px;"></i>{{tr "Feature" }}<i class="dropdown icon" style="margin-right:0px;"></i></div>
|
||||
<div class="menu">
|
||||
<a href="/service" class="item"><i class="rss icon"></i>{{tr "Services" }}</a>
|
||||
<a href="/network" class="item"><i class="bi bi-hdd-network icon"></i>{{tr "NetworkSpiter"}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a href="/service" class='item'><i class="rss icon"></i>{{tr "Services" }}</a>
|
||||
<a href="/network" class="item"><i class="bi bi-hdd-network icon"></i>{{tr "NetworkSpiter"}}</a>
|
||||
</template>
|
||||
{{ if not .Conf.DisableSwitchTemplateInFrontend }}
|
||||
<div class="item ui simple dropdown">
|
||||
<div class="text"><i class="bi bi-incognito icon" style="margin-right:3px;"></i>{{tr "Template" }}<i class="dropdown icon" style="margin-right:0px;"></i></div>
|
||||
<div class="menu">
|
||||
<a v-for="(item, index) in adaptedTemplates" :key="index" @click="toggleTemplate(item.key)" class="item">
|
||||
<i :class="item.icon + ' icon'"></i>@#item.name#@
|
||||
<i class="check icon" v-if="preferredTemplate === item.key"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{if .Admin}}
|
||||
<div class="item right item-right ui simple dropdown">
|
||||
<div class="text">
|
||||
<i class="user icon" style="margin-right:3px"></i>{{.Admin.Name}}
|
||||
<i class="dropdown icon"></i>
|
||||
</div>
|
||||
<div class="menu">
|
||||
<a class="item" href="/server"><i class="terminal icon"></i>{{tr "AdminPanel"}}</a>
|
||||
<a class="item" @click="logOut({{.Admin.ID}})"><i class="logout icon"></i>{{tr "Logout"}}</a>
|
||||
</div>
|
||||
</div>
|
||||
{{else}}
|
||||
<a href="/login" class="item right item-right" style="padding-right:1.2rem"><i class="sign-in icon"></i>{{tr "Login"}}</a>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{template "component/confirm" .}}
|
||||
{{end}}
|
||||
+36
-14
@@ -1,10 +1,10 @@
|
||||
{{define "theme-default/network"}}
|
||||
{{template "common/header" .}}
|
||||
{{template "theme-default/header" .}}
|
||||
{{if ts .CustomCode}}
|
||||
{{.CustomCode|safe}}
|
||||
{{end}}
|
||||
{{template "common/menu" .}}
|
||||
<div class="nb-container" id="app">
|
||||
{{template "theme-default/menu" .}}
|
||||
<div class="nb-container">
|
||||
<div class="ui container">
|
||||
<div class="service-status">
|
||||
<table class="ui celled table">
|
||||
@@ -17,25 +17,27 @@
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui container" style="max-width: 95vw">
|
||||
<div class="ui container">
|
||||
<div ref="chartDom" style="border-radius: 28px; margin-top: 15px;height: 520px;max-width: 1400px;overflow: hidden"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{template "common/footer" .}}
|
||||
<script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/echarts/5.3.0-rc.1/echarts.min.js"></script>
|
||||
{{template "theme-default/footer" .}}
|
||||
|
||||
<script>
|
||||
const monitorInfo = JSON.parse('{{.MonitorInfos}}');
|
||||
const initData = JSON.parse('{{.Servers}}').servers;
|
||||
let MaxTCPPingValue = {{.MaxTCPPingValue}};
|
||||
let MaxTCPPingValue = {{.Conf.MaxTCPPingValue}};
|
||||
if (MaxTCPPingValue == null) {
|
||||
MaxTCPPingValue = 300;
|
||||
MaxTCPPingValue = 1000;
|
||||
}
|
||||
new Vue({
|
||||
el: '#app',
|
||||
delimiters: ['@#', '#@'],
|
||||
data: {
|
||||
page: 'network',
|
||||
defaultTemplate: {{.Conf.Site.Theme}},
|
||||
templates: {{.Themes}},
|
||||
servers: initData,
|
||||
option: {
|
||||
tooltip: {
|
||||
@@ -65,6 +67,10 @@
|
||||
fontSize: 14
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '8%',
|
||||
right: '8%',
|
||||
},
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.8)',
|
||||
toolbox: {
|
||||
feature: {
|
||||
@@ -77,7 +83,7 @@
|
||||
},
|
||||
dataZoom: [
|
||||
{
|
||||
start: 94,
|
||||
start: 0,
|
||||
end: 100
|
||||
}
|
||||
],
|
||||
@@ -87,15 +93,20 @@
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
boundaryGap: [0, '100%']
|
||||
boundaryGap: false
|
||||
},
|
||||
series: [],
|
||||
},
|
||||
chartOnOff: true,
|
||||
},
|
||||
mixins: [mixinsVue],
|
||||
mounted() {
|
||||
this.renderChart();
|
||||
this.parseMonitorInfo(monitorInfo);
|
||||
this.parseMonitorInfo(monitorInfo);
|
||||
window.addEventListener('resize', this.resizeHandle);
|
||||
},
|
||||
destroyed () {
|
||||
window.removeEventListener('resize', this.resizeHandle)
|
||||
},
|
||||
methods: {
|
||||
getFontLogoClass(str) {
|
||||
@@ -178,11 +189,13 @@
|
||||
let loss = 0;
|
||||
let data = [];
|
||||
for (let j = 0; j < monitorInfo.result[i].created_at.length; j++) {
|
||||
avgDelay = monitorInfo.result[i].avg_delay[j];
|
||||
avgDelay = Math.round(monitorInfo.result[i].avg_delay[j]);
|
||||
if (avgDelay > 0.9 * MaxTCPPingValue) {
|
||||
loss += 1
|
||||
}
|
||||
data.push([monitorInfo.result[i].created_at[j], avgDelay]);
|
||||
if (avgDelay > 0) {
|
||||
data.push([monitorInfo.result[i].created_at[j], avgDelay]);
|
||||
}
|
||||
}
|
||||
lossRate = ((loss / monitorInfo.result[i].created_at.length) * 100).toFixed(1);
|
||||
legendName = monitorInfo.result[i].monitor_name +" "+ lossRate + "%";
|
||||
@@ -192,7 +205,13 @@
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'none',
|
||||
data: data
|
||||
data: data,
|
||||
markPoint: {
|
||||
data: [
|
||||
{ type: 'max', symbol: 'pin', name: 'Max', itemStyle: { color: '#f00' } },
|
||||
{ type: 'min', symbol: 'pin', name: 'Min', itemStyle: { color: '#0f0' } }
|
||||
]
|
||||
}
|
||||
});
|
||||
}
|
||||
this.option.title.text = monitorInfo.result[0].server_name;
|
||||
@@ -208,6 +227,9 @@
|
||||
this.myChart = echarts.init(this.$refs.chartDom);
|
||||
this.myChart.setOption(this.option);
|
||||
},
|
||||
resizeHandle () {
|
||||
this.myChart.resize();
|
||||
},
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.myChart.dispose();
|
||||
|
||||
+16
-6
@@ -1,9 +1,9 @@
|
||||
{{define "theme-default/service"}}
|
||||
{{template "common/header" .}}
|
||||
{{template "theme-default/header" .}}
|
||||
{{if ts .CustomCode}}
|
||||
{{.CustomCode|safe}}
|
||||
{{end}}
|
||||
{{template "common/menu" .}}
|
||||
{{template "theme-default/menu" .}}
|
||||
<div class="nb-container">
|
||||
<div class="ui container">
|
||||
<div class="service-status">
|
||||
@@ -18,7 +18,6 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range $service := .Services}}
|
||||
{{if $service.Monitor.EnableShowInService}}
|
||||
<tr>
|
||||
<td class="ui center aligned">{{$service.Monitor.Name}}</td>
|
||||
<td class="ui center aligned">
|
||||
@@ -34,7 +33,7 @@
|
||||
class="delay-today {{className (divU64 $service.CurrentUp (addU64 $service.CurrentUp $service.CurrentDown))}}"></i>
|
||||
{{statusName (divU64 $service.CurrentUp (addU64 $service.CurrentUp $service.CurrentDown))}}
|
||||
</td>
|
||||
</tr> {{end}} {{end}}
|
||||
</tr> {{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -77,10 +76,21 @@
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{template "common/footer" .}}
|
||||
{{template "theme-default/footer" .}}
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#app',
|
||||
delimiters: ['@#', '#@'],
|
||||
data: {
|
||||
page: 'service',
|
||||
defaultTemplate: {{.Conf.Site.Theme}},
|
||||
templates: {{.Themes}}
|
||||
},
|
||||
mixins: [mixinsVue]
|
||||
})
|
||||
</script>
|
||||
{{end}}
|
||||
+1
@@ -12,6 +12,7 @@
|
||||
{{else}}
|
||||
<a href="/" class='mdui-ripple mdui-ripple-white mdui-hoverable{{if eq .MatchedPath "/"}} mdui-tab-active{{end}}'><i class="mdui-icon material-icons">home</i>{{tr "Home"}}</a>
|
||||
<a href="/service" class='mdui-ripple mdui-ripple-white mdui-hoverable{{if eq .MatchedPath "/service"}} mdui-tab-active{{end}}'><i class="mdui-icon material-icons">accessibility</i>{{tr "Services"}}</a>
|
||||
<a href="/network" class='mdui-ripple mdui-ripple-white mdui-hoverable{{if eq .MatchedPath "/network"}} mdui-tab-active{{end}}'><i class="mdui-icon material-icons">network_check</i>{{tr "NetworkSpiter"}}</a>
|
||||
{{end}}
|
||||
<div class="mdui-toolbar-spacer"></div>
|
||||
{{if .Admin}}
|
||||
|
||||
+266
@@ -0,0 +1,266 @@
|
||||
{{define "theme-mdui/network"}}
|
||||
<!doctype html>
|
||||
<html lang="{{.Conf.Language}}">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<title>{{.Title}}</title>
|
||||
<link rel="shortcut icon" type="image/png" href="/static/logo.svg?v20210804" />
|
||||
|
||||
<!-- MDUI CSS -->
|
||||
<link rel="stylesheet" href="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/mdui/1.0.2/css/mdui.min.css"/>
|
||||
<link rel="stylesheet" href="/static/theme-mdui/mdui.css" type="text/css">
|
||||
<style>
|
||||
.mdui-table td, .mdui-table th{padding: 6px;}
|
||||
.progress{width: 10%;min-width: 75px;}
|
||||
.progress-text{font-size: 16px;font-weight: 800;position: relative;top: 4px;left: 6px;}
|
||||
.offline st,.offline at,.offline gt,.offline .progress-text{color: grey;}
|
||||
a{text-decoration:none;color:#333;}.mdui-theme-layout-dark a{color:#fff;}
|
||||
</style>
|
||||
{{if ts .CustomCode}}
|
||||
{{.CustomCode|safe}}
|
||||
{{end}}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{{template "theme-mdui/menu" .}}
|
||||
<div class="nb-container" id="app">
|
||||
<div class="ui container">
|
||||
<div class="service-status">
|
||||
<table class="ui celled table">
|
||||
<button
|
||||
v-for="server in servers"
|
||||
@click="redirectNetwork(server.ID)"
|
||||
class="mdui-btn mdui-btn-raised mdui-color-theme mdui-color-indigo mdui-text-color-white"
|
||||
style="margin-top: 6px;margin-left:5px">
|
||||
<img :src="'https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/flag-icon-css/4.1.5/flags/4x3/' + (server.Host.CountryCode?server.Host.CountryCode:'cn') + '.svg'" alt="Flag Icon" style="vertical-align: middle;height:14px"> <span>@#server.Name#@ </span></button>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui container">
|
||||
<div ref="chartDom" style="border-radius: 28px; margin-top: 15px;height: 520px;max-width: 1400px;overflow: hidden"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{template "theme-mdui/footer" .}}
|
||||
<script src="/static/theme-mdui/mdui.js"></script>
|
||||
<script src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/mdui/1.0.2/js/mdui.min.js"></script>
|
||||
<script src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/jquery/3.6.0/jquery.min.js"></script>
|
||||
<script src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/vue/2.6.14/vue.min.js"></script>
|
||||
|
||||
<script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/echarts/5.3.0-rc.1/echarts.min.js"></script>
|
||||
|
||||
<script>
|
||||
const monitorInfo = JSON.parse('{{.MonitorInfos}}');
|
||||
const initData = JSON.parse('{{.Servers}}').servers;
|
||||
let MaxTCPPingValue = {{.Conf.MaxTCPPingValue}};
|
||||
if (MaxTCPPingValue == null) {
|
||||
MaxTCPPingValue = 1000;
|
||||
}
|
||||
new Vue({
|
||||
el: '#app',
|
||||
delimiters: ['@#', '#@'],
|
||||
data: {
|
||||
servers: initData,
|
||||
option: {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
position: function (pt) {
|
||||
return [pt[0], '10%'];
|
||||
},
|
||||
formatter: function(params){
|
||||
let result = params[0].axisValueLabel + "<br />";
|
||||
params.forEach(function(item){
|
||||
result += item.marker + item.seriesName + ": " + item.value[1].toFixed(2) + " ms<br />";
|
||||
})
|
||||
return result;
|
||||
},
|
||||
confine: true,
|
||||
transitionDuration: 0
|
||||
},
|
||||
title: {
|
||||
left: 'center',
|
||||
text: "",
|
||||
textStyle: {}
|
||||
},
|
||||
legend: {
|
||||
top: '5%',
|
||||
data: [],
|
||||
textStyle: {
|
||||
fontSize: 14
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '8%',
|
||||
right: '8%'
|
||||
},
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.8)',
|
||||
toolbox: {
|
||||
feature: {
|
||||
dataZoom: {
|
||||
yAxisIndex: 'none'
|
||||
},
|
||||
restore: {},
|
||||
saveAsImage: {}
|
||||
}
|
||||
},
|
||||
dataZoom: [
|
||||
{
|
||||
start: 0,
|
||||
end: 100
|
||||
}
|
||||
],
|
||||
xAxis: {
|
||||
type: 'time',
|
||||
boundaryGap: false
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
boundaryGap: false
|
||||
},
|
||||
series: [],
|
||||
},
|
||||
chartOnOff: true,
|
||||
},
|
||||
mounted() {
|
||||
this.renderChart();
|
||||
this.parseMonitorInfo(monitorInfo);
|
||||
window.addEventListener('resize', this.resizeHandle);
|
||||
},
|
||||
resizeHandle () {
|
||||
this.myChart.resize();
|
||||
},
|
||||
methods: {
|
||||
getFontLogoClass(str) {
|
||||
if (["almalinux",
|
||||
"alpine",
|
||||
"aosc",
|
||||
"apple",
|
||||
"archlinux",
|
||||
"archlabs",
|
||||
"artix",
|
||||
"budgie",
|
||||
"centos",
|
||||
"coreos",
|
||||
"debian",
|
||||
"deepin",
|
||||
"devuan",
|
||||
"docker",
|
||||
"elementary",
|
||||
"fedora",
|
||||
"ferris",
|
||||
"flathub",
|
||||
"freebsd",
|
||||
"gentoo",
|
||||
"gnu-guix",
|
||||
"illumos",
|
||||
"kali-linux",
|
||||
"linuxmint",
|
||||
"mageia",
|
||||
"mandriva",
|
||||
"manjaro",
|
||||
"nixos",
|
||||
"openbsd",
|
||||
"opensuse",
|
||||
"pop-os",
|
||||
"raspberry-pi",
|
||||
"redhat",
|
||||
"rocky-linux",
|
||||
"sabayon",
|
||||
"slackware",
|
||||
"snappy",
|
||||
"solus",
|
||||
"tux",
|
||||
"ubuntu",
|
||||
"void",
|
||||
"zorin"].indexOf(str)
|
||||
> -1) {
|
||||
return str;
|
||||
}
|
||||
if (['openwrt', 'linux', "immortalwrt"].indexOf(str) > -1) {
|
||||
return 'tux';
|
||||
}
|
||||
if (str == 'amazon') {
|
||||
return 'redhat';
|
||||
}
|
||||
if (str == 'arch') {
|
||||
return 'archlinux';
|
||||
}
|
||||
return '';
|
||||
},
|
||||
redirectNetwork(id) {
|
||||
this.getMonitorHistory(id)
|
||||
.then(function(monitorInfo) {
|
||||
var vm = app.__vue__;
|
||||
vm.parseMonitorInfo(monitorInfo);
|
||||
})
|
||||
.catch(function(error){
|
||||
window.location.href = "/404";
|
||||
})
|
||||
},
|
||||
getMonitorHistory(id) {
|
||||
return $.ajax({
|
||||
url: "/api/v1/monitor/"+id,
|
||||
method: "GET"
|
||||
});
|
||||
},
|
||||
parseMonitorInfo(monitorInfo) {
|
||||
let tSeries = [];
|
||||
let tLegendData = [];
|
||||
for (let i = 0; i < monitorInfo.result.length; i++) {
|
||||
let loss = 0;
|
||||
let data = [];
|
||||
for (let j = 0; j < monitorInfo.result[i].created_at.length; j++) {
|
||||
avgDelay = Math.round(monitorInfo.result[i].avg_delay[j]);
|
||||
if (avgDelay > 0.9 * MaxTCPPingValue) {
|
||||
loss += 1
|
||||
}
|
||||
if (avgDelay > 0) {
|
||||
data.push([monitorInfo.result[i].created_at[j], avgDelay]);
|
||||
}
|
||||
}
|
||||
lossRate = ((loss / monitorInfo.result[i].created_at.length) * 100).toFixed(1);
|
||||
legendName = monitorInfo.result[i].monitor_name +" "+ lossRate + "%";
|
||||
tLegendData.push(legendName);
|
||||
tSeries.push({
|
||||
name: legendName,
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'none',
|
||||
data: data,
|
||||
markPoint: {
|
||||
data: [
|
||||
{ type: 'max', symbol: 'pin', name: 'Max', itemStyle: { color: '#f00' } },
|
||||
{ type: 'min', symbol: 'pin', name: 'Min', itemStyle: { color: '#0f0' } }
|
||||
]
|
||||
}
|
||||
});
|
||||
}
|
||||
this.option.title.text = monitorInfo.result[0].server_name;
|
||||
this.option.series = tSeries;
|
||||
this.option.legend.data = tLegendData;
|
||||
this.myChart.clear();
|
||||
this.myChart.setOption(this.option);
|
||||
},
|
||||
isWindowsPlatform(str) {
|
||||
return str.includes('Windows')
|
||||
},
|
||||
renderChart() {
|
||||
this.myChart = echarts.init(this.$refs.chartDom);
|
||||
this.myChart.setOption(this.option);
|
||||
},
|
||||
resizeHandle () {
|
||||
this.myChart.resize();
|
||||
},
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.myChart.dispose();
|
||||
this.myChart = null;
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
{{end}}
|
||||
+14
-1
@@ -1,7 +1,20 @@
|
||||
{{define "theme-server-status/content-footer"}}
|
||||
<footer class="container" style="padding-bottom: 2rem;">
|
||||
<p style="text-align: center; font-size: 10px;">
|
||||
{{ .Conf.Site.Brand }} | Theme <a target="_blank" href="https://github.com/cppla/ServerStatus">ServerStatus</a> | Powered by <a target="_blank" href="https://github.com/naiba/nezha">{{tr "NezhaMonitoring"}}</a> {{.Version}}
|
||||
{{ .Conf.Site.Brand }} | Theme ServerStatus | Powered by <a target="_blank" href="https://github.com/naiba/nezha">{{tr "NezhaMonitoring"}}</a> {{.Version}}
|
||||
</p>
|
||||
</footer>
|
||||
<aside class="toolbox">
|
||||
<span class="toggleView">
|
||||
<i v-if="showGroup" @click="toggleView" class="show-nogroup bi bi-justify"></i>
|
||||
<i v-else @click="toggleView" class="show-group bi bi-view-stacked"></i>
|
||||
</span>
|
||||
<span class="setTheme">
|
||||
<i v-if="theme === 'light'" @click="setTheme('dark', true)" class="setTheme-dark bi bi-moon-fill"></i>
|
||||
<i v-else @click="setTheme('light', true)" class="setTheme-light bi bi-brightness-high-fill"></i>
|
||||
</span>
|
||||
<span v-if="showGoTop" class="showGoTop">
|
||||
<i @click="goTop" class="goTop bi bi-arrow-up"></i>
|
||||
</span>
|
||||
</aside>
|
||||
{{end}}
|
||||
+54
-40
@@ -1,49 +1,63 @@
|
||||
{{define "theme-server-status/content-nav"}}
|
||||
<div role="navigation" class="navbar navbar-inverse navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container pl-md-unset">
|
||||
<div class="navbar-header">
|
||||
<button data-target=".navbar-collapse" data-toggle="collapse" class="navbar-toggle" type="button">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a href="/" class="navbar-brand pl-md-unset">
|
||||
<img src="/static/logo.svg?v20210804" style="height: 2rem;display: inline-block;">
|
||||
{{.Conf.Site.Brand}}
|
||||
</a>
|
||||
</div>
|
||||
<div class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a href="/">{{tr "Home" }}</a></li>
|
||||
<li><a href="/service">{{tr "Services" }}</a></li>
|
||||
<li><a href="/network">{{tr "NetworkSpiter" }}</a></li>
|
||||
</ul>
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<header role="navigation" class="navbar navbar-inverse navbar-fixed-top" style="z-index:99999999;">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button data-target=".navbar-collapse" data-toggle="collapse" class="navbar-toggle" type="button">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a href="/" class="navbar-brand">
|
||||
<img src="/static/logo.svg" style="height: 2rem;display: inline-block;">
|
||||
{{.Conf.Site.Brand}}
|
||||
</a>
|
||||
</div>
|
||||
<nav id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a href="/"><i class="home icon"></i>{{tr "Home" }}</a></li>
|
||||
<template v-if="isMobile">
|
||||
<li class="dropdown">
|
||||
<a data-toggle="dropdown" href="#">{{tr "Menu" }}<b class="caret"></b></a>
|
||||
<a data-toggle="dropdown"><i class="bi bi-gear-wide-connected" style="position:relative;top:1px;margin-right:3px;font-size:1.1rem;"></i>{{tr "Feature" }}<b class="caret"></b></a>
|
||||
<ul class="dropdown-menu" style="min-width:100px;">
|
||||
<li><a href="/service"><i class="rss icon"></i>{{tr "Services" }}</a></li>
|
||||
<li><a href="/network"><i class="bi bi-hdd-network icon"></i>{{tr "NetworkSpiter"}}</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</template>
|
||||
<template v-else>
|
||||
<li><a href="/service"><i class="rss icon"></i>{{tr "Services" }}</a></li>
|
||||
<li><a href="/network"><i class="bi bi-hdd-network icon"></i>{{tr "NetworkSpiter"}}</a></li>
|
||||
</template>
|
||||
{{ if not .Conf.DisableSwitchTemplateInFrontend }}
|
||||
<li class="dropdown">
|
||||
<a data-toggle="dropdown"><i class="bi bi-incognito" style="position:relative;top:1px;margin-right:3px;font-size:1.2rem;vertical-align:top;"></i>{{tr "Template" }}<b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
{{if .Admin}}
|
||||
<li><a href="/server">{{tr "AdminPanel" }} ({{.Admin.Name}})</a></li>
|
||||
{{else}}
|
||||
<li><a href="/login">{{tr "Login" }}</a></li>
|
||||
{{end}}
|
||||
<li><a href="#" @click="setSystemTheme">{{tr "FollowSystem" }}
|
||||
<span style="color: #fff" v-if="isSystemTheme"> ✔️</span></a>
|
||||
</li>
|
||||
<li><a href="#" @click="setTheme('dark', true)">{{tr "DarkMode" }}
|
||||
<span v-if="theme === 'dark' && !isSystemTheme"> ✔️</span></a>
|
||||
</li>
|
||||
<li><a href="#" @click="setTheme('light', true)">{{tr "LightMode" }}
|
||||
<span v-if="theme === 'light' && !isSystemTheme"> ✔️</span></a>
|
||||
<li v-for="(item, index) in adaptedTemplates" :key="index">
|
||||
<a @click="toggleTemplate(item.key)">
|
||||
<i :class="item.icon + ' icon'" style="font-size:1em"></i>@#item.name#@
|
||||
<i class="check icon" v-if="preferredTemplate === item.key"></i>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
</ul>
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
{{if .Admin}}
|
||||
<li class="dropdown">
|
||||
<a data-toggle="dropdown"><i class="user icon"></i>{{.Admin.Name}}<b class="caret"></b></a>
|
||||
<ul class="dropdown-menu" style="margin-bottom:20px;">
|
||||
<li><a href="/server"><i class="terminal icon"></i>{{tr "AdminPanel" }}</a></li>
|
||||
<li><a @click="logOut({{.Admin.ID}})"><i class="logout icon"></i>{{tr "Logout"}}</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
{{else}}
|
||||
<li><a href="/login"><i class="sign-in icon"></i>{{tr "Login" }}</a></li>
|
||||
{{end}}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
{{end}}
|
||||
|
||||
|
||||
+11
-11
@@ -6,16 +6,16 @@
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="/static/theme-server-status/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="/static/theme-server-status/css/bootstrap-theme.min.css">
|
||||
<link rel="stylesheet" href="/static/theme-server-status/css/main.css?v20231207">
|
||||
<link rel="stylesheet" href="/static/theme-server-status/css/main.css?v20240225">
|
||||
<link rel="stylesheet" href="/static/theme-server-status/css/dark.css">
|
||||
<link rel="stylesheet" href="/static/theme-server-status/css/light.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.min.css">
|
||||
<link rel="stylesheet" href="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/font-logos/0.17/font-logos.min.css">
|
||||
<link rel="stylesheet" href="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/semantic-ui/2.4.1/semantic.min.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/font-logos@0.17/assets/font-logos.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/lipis/[email protected]/css/flag-icons.min.css">
|
||||
<link rel="shortcut icon" type="image/png" href="/static/logo.svg?v20210804" />
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/semantic.min.css">
|
||||
<link rel="shortcut icon" type="image/png" href="/static/logo.svg" />
|
||||
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="/static/theme-server-status/js/html5shiv.js"></script>
|
||||
@@ -24,11 +24,11 @@
|
||||
{{if ts .CustomCode}}
|
||||
{{.CustomCode|safe}}
|
||||
{{end}}
|
||||
<script src="/static/theme-server-status/js/jquery.min.js"></script>
|
||||
<script src="/static/theme-server-status/js/bootstrap.min.js"></script>
|
||||
<script src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/vue/2.6.14/vue.min.js"></script>
|
||||
<script src="/static/theme-server-status/js/mixin.js"></script>
|
||||
<script src="https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/echarts/5.3.0-rc.1/echarts.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/echarts.min.js"></script>
|
||||
<script src="/static/theme-server-status/js/mixin.js?v20240302"></script>
|
||||
</head>
|
||||
<body>
|
||||
{{end}}
|
||||
{{end}}
|
||||
@@ -0,0 +1,134 @@
|
||||
{{define "theme-server-status/home-group-false"}}
|
||||
<table class="table table-striped table-condensed table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="node-cell status center">{{tr "Status"}}</th>
|
||||
<th class="node-cell name center">{{tr "Name"}}</th>
|
||||
<th class="node-cell os center">{{tr "Platform"}}</th>
|
||||
<th class="node-cell location center">{{tr "Location"}}</th>
|
||||
<th class="node-cell uptime center">{{tr "Uptime"}}</th>
|
||||
<th class="node-cell load center">{{tr "Load"}}</th>
|
||||
<th class="node-cell network center">{{tr "NetSpeed"}}↓|↑</th>
|
||||
<th class="node-cell traffic center">{{tr "NetTransfer"}}↓|↑</th>
|
||||
<th class="node-cell cpu center">{{tr "CpuUsed"}}</th>
|
||||
<th class="node-cell memory center">{{tr "MemUsed"}}</th>
|
||||
<th class="node-cell hdd center">{{tr "DiskUsed"}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="servers">
|
||||
<template v-for="(node,index) in nodesNoTag">
|
||||
<tr :id="'r'+node.ID" data-toggle="collapse" :data-target="'#rt'+node.ID" class="accordion-toggle" :class="index % 2 === 0 ? 'odd': 'even'"
|
||||
aria-expanded="false" @click="showCharts($event, node.ID)">
|
||||
<td class="node-cell status center">
|
||||
<div class="status-container">
|
||||
<div v-if="node.online" class="status-icon online"></div>
|
||||
<div v-else class="status-icon offline"></div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="node-cell name center">@#node.name#@</td>
|
||||
<td class="node-cell os center">
|
||||
<i v-if='node.os == "darwin"' class="apple icon"></i>
|
||||
<i v-else-if='isWindowsPlatform(node.host.Platform)' class="windows icon"></i>
|
||||
<i v-else :class="'fl-' + getFontLogoClass(node.host.Platform)"></i>
|
||||
<span class="node-cell-os-text">@#node.os#@</span>
|
||||
</td>
|
||||
<td style="text-align: center;" class="node-cell location">
|
||||
<i :class="'fi fi-' + node.location"></i>
|
||||
<span class="node-cell-location-text text-uppercase">@#node.location#@</span>
|
||||
</td>
|
||||
<td style="text-align: center;" class="node-cell uptime">@#node.uptime#@</td>
|
||||
<td style="text-align: center;" class="node-cell load">@#node.load#@</td>
|
||||
<td style="text-align: center;" class="node-cell network">@#node.network#@</td>
|
||||
<td style="text-align: center;" class="node-cell traffic">@#node.traffic#@</td>
|
||||
<td class="node-cell cpu">
|
||||
<div :class="['progress', node.online ? 'progress-online' : 'progress-offline']">
|
||||
<div :style="node.cpu.style" :class="node.cpu.class"><small>@#node.cpu.percent#@%</small>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="node-cell memory">
|
||||
<div :class="['progress', node.online ? 'progress-online' : 'progress-offline']">
|
||||
<div :style="node.memory.style" :class="node.memory.class">
|
||||
<small>@#node.memory.percent#@%</small>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="node-cell hdd">
|
||||
<div :class="['progress', node.online ? 'progress-online' : 'progress-offline']">
|
||||
<div :style="node.hdd.style" :class="node.hdd.class"><small>@#node.hdd.percent#@%</small>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="expandRow" :class="index % 2 === 0 ? 'odd': 'even'">
|
||||
<td colspan="16">
|
||||
<div class="accordian-body collapse" :id="'rt'+node.ID">
|
||||
<div style="display: flex;left-items: center;justify-content: center;flex-direction: column; max-width: 89vw">
|
||||
<span class="node-cell-expand">
|
||||
<span class="node-cell-expand-label">{{tr "Platform"}}:</span>
|
||||
@#node.host.Platform#@-@#node.host.PlatformVersion#@
|
||||
[<span v-if="node.host.Virtualization">@#node.host.Virtualization#@:</span>@#node.host.Arch#@]
|
||||
</span>
|
||||
<span class="node-cell-expand" v-if="node.host.CPU">
|
||||
<span class="node-cell-expand-label">CPU:</span>
|
||||
@#node.host.CPU.join(",")#@
|
||||
</span>
|
||||
<span class="node-cell-expand">
|
||||
<span class="node-cell-expand-label">{{tr "DiskUsed"}}:</span>
|
||||
@#formatByteSize(node.state.DiskUsed)#@ / @#formatByteSize(node.host.DiskTotal)#@
|
||||
</span>
|
||||
<span class="node-cell-expand">
|
||||
<span class="node-cell-expand-label">{{tr "MemUsed"}}:</span>
|
||||
@#formatByteSize(node.state.MemUsed)#@ / @#formatByteSize(node.host.MemTotal)#@(@#toFixed2(node.state.MemUsed / node.host.MemTotal * 100)#@%)
|
||||
</span>
|
||||
<span class="node-cell-expand">
|
||||
<span class="node-cell-expand-label">{{tr "SwapUsed"}}:</span>
|
||||
@#formatByteSize(node.state.SwapUsed)#@ / @#formatByteSize(node.host.SwapTotal)#@
|
||||
<span v-if="node.host.SwapTotal">(@#toFixed2(node.state.SwapUsed / node.host.SwapTotal * 100)#@%)</span>
|
||||
</span>
|
||||
<span class="node-cell-expand">
|
||||
<span class="node-cell-expand-label">{{tr "NetTransfer"}}:</span>
|
||||
<i class="arrow alternate circle down outline icon"
|
||||
style="margin: 0"></i>@#formatByteSize(node.state.NetInTransfer)#@
|
||||
<i class="arrow alternate circle up outline icon"
|
||||
style="margin: 0"></i>@#formatByteSize(node.state.NetOutTransfer)#@
|
||||
</span>
|
||||
<span class="node-cell-expand load">
|
||||
<span class="node-cell-expand-label">{{tr "Load"}}:</span>
|
||||
@#toFixed2(node.state.Load1)#@ / @#toFixed2(node.state.Load5)#@ / @#toFixed2(node.state.Load15)#@
|
||||
</span>
|
||||
<span class="node-cell-expand">
|
||||
<span class="node-cell-expand-label">{{tr "ProcessCount"}}:</span>
|
||||
@#node.state.ProcessCount#@
|
||||
</span>
|
||||
<span class="node-cell-expand">
|
||||
<span class="node-cell-expand-label">{{tr "ConnCount"}}:</span>
|
||||
TCP @#node.state.TcpConnCount#@ / UDP @#node.state.UdpConnCount#@
|
||||
</span>
|
||||
<span class="node-cell-expand">
|
||||
<span class="node-cell-expand-label">{{tr "BootTime"}}:</span>
|
||||
@#formatTimestamp(node.host.BootTime)#@
|
||||
</span>
|
||||
<span class="node-cell-expand">
|
||||
<span class="node-cell-expand-label">{{tr "LastActive"}}:</span>
|
||||
@#new Date(node.lastActive).toLocaleString()#@
|
||||
</span>
|
||||
<span class="node-cell-expand">
|
||||
<span class="node-cell-expand-label">{{tr "Uptime"}}:</span>
|
||||
@#node.uptime#@
|
||||
</span>
|
||||
<span class="node-cell-expand">
|
||||
<span class="node-cell-expand-label">{{tr "Version"}}:</span>
|
||||
@#node.host.Version#@
|
||||
</span>
|
||||
<span class="node-echarts-expand">
|
||||
<div class="chartbox" chartbox-show="0" :key="node.ID" :ref="`chart${node.ID}`" style="width: 100%; height: auto;"></div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
{{end}}
|
||||
@@ -0,0 +1,137 @@
|
||||
{{define "theme-server-status/home-group-true"}}
|
||||
<table class="table table-striped table-condensed table-hover">
|
||||
<thead>
|
||||
<tr class="node-group-tag">
|
||||
<th colspan="16" style="border:none;">@#(group.Tag!==''?group.Tag:'{{tr "Default"}}')#@</th>
|
||||
</tr>
|
||||
<tr class="node-group-cell">
|
||||
<th class="node-cell status center">{{tr "Status"}}</th>
|
||||
<th class="node-cell name center">{{tr "Name"}}</th>
|
||||
<th class="node-cell os center">{{tr "Platform"}}</th>
|
||||
<th class="node-cell location center">{{tr "Location"}}</th>
|
||||
<th class="node-cell uptime center">{{tr "Uptime"}}</th>
|
||||
<th class="node-cell load center">{{tr "Load"}}</th>
|
||||
<th class="node-cell network center">{{tr "NetSpeed"}}↓|↑</th>
|
||||
<th class="node-cell traffic center">{{tr "NetTransfer"}}↓|↑</th>
|
||||
<th class="node-cell cpu center">{{tr "CpuUsed"}}</th>
|
||||
<th class="node-cell memory center">{{tr "MemUsed"}}</th>
|
||||
<th class="node-cell hdd center">{{tr "DiskUsed"}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="servers">
|
||||
<template v-for="(node,index) in group.data">
|
||||
<tr :id="'r'+node.ID" data-toggle="collapse" :data-target="'#rt'+node.ID" class="accordion-toggle"
|
||||
:class="index % 2 === 0 ? 'odd': 'even'" aria-expanded="false" @click="showCharts($event, node.ID)">
|
||||
<td class="node-cell status center">
|
||||
<div class="status-container">
|
||||
<div v-if="node.online" class="status-icon online"></div>
|
||||
<div v-else class="status-icon offline"></div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="node-cell name center">@#node.name#@</td>
|
||||
<td class="node-cell os center">
|
||||
<i v-if='node.os == "darwin"' class="apple icon"></i>
|
||||
<i v-else-if='isWindowsPlatform(node.host.Platform)' class="windows icon"></i>
|
||||
<i v-else :class="'fl-' + getFontLogoClass(node.host.Platform)"></i>
|
||||
<span class="node-cell-os-text">@#node.os#@</span>
|
||||
</td>
|
||||
<td style="text-align: center;" class="node-cell location">
|
||||
<i :class="'fi fi-' + node.location"></i>
|
||||
<span class="node-cell-location-text text-uppercase"> @#node.location#@</span>
|
||||
</td>
|
||||
<td style="text-align: center;" class="node-cell uptime">@#node.uptime#@</td>
|
||||
<td style="text-align: center;" class="node-cell load">@#node.load#@</td>
|
||||
<td style="text-align: center;" class="node-cell network">@#node.network#@</td>
|
||||
<td style="text-align: center;" class="node-cell traffic">@#node.traffic#@</td>
|
||||
<td class="node-cell cpu">
|
||||
<div :class="['progress', node.online ? 'progress-online' : 'progress-offline']">
|
||||
<div :style="node.cpu.style" :class="node.cpu.class"><small>@#node.cpu.percent#@%</small>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="node-cell memory">
|
||||
<div :class="['progress', node.online ? 'progress-online' : 'progress-offline']">
|
||||
<div :style="node.memory.style" :class="node.memory.class">
|
||||
<small>@#node.memory.percent#@%</small>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="node-cell hdd">
|
||||
<div :class="['progress', node.online ? 'progress-online' : 'progress-offline']">
|
||||
<div :style="node.hdd.style" :class="node.hdd.class"><small>@#node.hdd.percent#@%</small>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="expandRow" :class="index % 2 === 0 ? 'odd': 'even'">
|
||||
<td colspan="16">
|
||||
<div class="accordian-body collapse" :id="'rt'+node.ID">
|
||||
<div style="display: flex;left-items: center;justify-content: center;flex-direction: column; max-width: 89vw">
|
||||
<span class="node-cell-expand">
|
||||
<span class="node-cell-expand-label">{{tr "Platform"}}:</span>
|
||||
@#node.host.Platform#@-@#node.host.PlatformVersion#@
|
||||
[<span v-if="node.host.Virtualization">@#node.host.Virtualization#@:</span>@#node.host.Arch#@]
|
||||
</span>
|
||||
<span class="node-cell-expand" v-if="node.host.CPU">
|
||||
<span class="node-cell-expand-label">CPU:</span>
|
||||
@#node.host.CPU.join(",")#@
|
||||
</span>
|
||||
<span class="node-cell-expand">
|
||||
<span class="node-cell-expand-label">{{tr "DiskUsed"}}:</span>
|
||||
@#formatByteSize(node.state.DiskUsed)#@ / @#formatByteSize(node.host.DiskTotal)#@
|
||||
</span>
|
||||
<span class="node-cell-expand">
|
||||
<span class="node-cell-expand-label">{{tr "MemUsed"}}:</span>
|
||||
@#formatByteSize(node.state.MemUsed)#@ / @#formatByteSize(node.host.MemTotal)#@(@#toFixed2(node.state.MemUsed / node.host.MemTotal * 100)#@%)
|
||||
</span>
|
||||
<span class="node-cell-expand">
|
||||
<span class="node-cell-expand-label">{{tr "SwapUsed"}}:</span>
|
||||
@#formatByteSize(node.state.SwapUsed)#@ / @#formatByteSize(node.host.SwapTotal)#@
|
||||
<span v-if="node.host.SwapTotal">(@#toFixed2(node.state.SwapUsed / node.host.SwapTotal * 100)#@%)</span>
|
||||
</span>
|
||||
<span class="node-cell-expand">
|
||||
<span class="node-cell-expand-label">{{tr "NetTransfer"}}:</span>
|
||||
<i class="arrow alternate circle down outline icon"
|
||||
style="margin: 0"></i>@#formatByteSize(node.state.NetInTransfer)#@
|
||||
<i class="arrow alternate circle up outline icon"
|
||||
style="margin: 0"></i>@#formatByteSize(node.state.NetOutTransfer)#@
|
||||
</span>
|
||||
<span class="node-cell-expand load">
|
||||
<span class="node-cell-expand-label">{{tr "Load"}}:</span>
|
||||
@#toFixed2(node.state.Load1)#@ / @#toFixed2(node.state.Load5)#@ / @#toFixed2(node.state.Load15)#@
|
||||
</span>
|
||||
<span class="node-cell-expand">
|
||||
<span class="node-cell-expand-label">{{tr "ProcessCount"}}:</span>
|
||||
@#node.state.ProcessCount#@
|
||||
</span>
|
||||
<span class="node-cell-expand">
|
||||
<span class="node-cell-expand-label">{{tr "ConnCount"}}:</span>
|
||||
TCP @#node.state.TcpConnCount#@ / UDP @#node.state.UdpConnCount#@
|
||||
</span>
|
||||
<span class="node-cell-expand">
|
||||
<span class="node-cell-expand-label">{{tr "BootTime"}}:</span>
|
||||
@#formatTimestamp(node.host.BootTime)#@
|
||||
</span>
|
||||
<span class="node-cell-expand">
|
||||
<span class="node-cell-expand-label">{{tr "LastActive"}}:</span>
|
||||
@#new Date(node.lastActive).toLocaleString()#@
|
||||
</span>
|
||||
<span class="node-cell-expand">
|
||||
<span class="node-cell-expand-label">{{tr "Uptime"}}:</span>
|
||||
@#node.uptime#@
|
||||
</span>
|
||||
<span class="node-cell-expand">
|
||||
<span class="node-cell-expand-label">{{tr "Version"}}:</span>
|
||||
@#node.host.Version#@
|
||||
</span>
|
||||
<span class="node-echarts-expand">
|
||||
<div class="chartbox" chartbox-show="0" :key="node.ID" :ref="`chart${node.ID}`" style="width: 100%; height: auto;"></div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
{{end}}
|
||||
+225
-173
@@ -1,156 +1,58 @@
|
||||
{{define "theme-server-status/home"}}
|
||||
{{template "theme-server-status/header" .}}
|
||||
<div id="app">
|
||||
{{template "theme-server-status/content-nav" .}}
|
||||
<div class="container table-responsive content" style="max-width: 95vw" v-for="group in nodes">
|
||||
<table class="table table-striped table-condensed table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="node-group-tag" colspan="16" style="border:none;">@#(group.Tag!==''?group.Tag:'{{tr "Default"}}')#@</th>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="node-cell status center">{{tr "Status"}}</th>
|
||||
<th class="node-cell name center">{{tr "Name"}}</th>
|
||||
<th class="node-cell os center">{{tr "Platform"}}</th>
|
||||
<th class="node-cell location center">{{tr "Location"}}</th>
|
||||
<th class="node-cell uptime center">{{tr "Uptime"}}</th>
|
||||
<th class="node-cell load center">{{tr "Load"}}</th>
|
||||
<th class="node-cell network center">{{tr "NetSpeed"}}↓|↑</th>
|
||||
<th class="node-cell traffic center">{{tr "NetTransfer"}}↓|↑</th>
|
||||
<th class="node-cell cpu center">{{tr "CpuUsed"}}</th>
|
||||
<th class="node-cell ram center">{{tr "MemUsed"}}</th>
|
||||
<th class="node-cell hdd center">{{tr "DiskUsed"}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="servers">
|
||||
<template v-for="(node,index) in group.data">
|
||||
<tr :id="'r'+node.ID" data-toggle="collapse" :data-target="'#rt'+node.ID" class="accordion-toggle"
|
||||
:class="index % 2 === 0 ? 'odd': 'even'">
|
||||
<td class="node-cell status center">
|
||||
<div class="status-container">
|
||||
<div v-if="node.online" class="status-icon online"></div>
|
||||
<div v-else class="status-icon offline"></div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="node-cell name center">@#node.name#@</td>
|
||||
<td class="node-cell os center">
|
||||
<i v-if='node.os == "darwin"' class="apple icon"></i>
|
||||
<i v-else-if='isWindowsPlatform(node.host.Platform)' class="windows icon"></i>
|
||||
<i v-else :class="'fl-' + getFontLogoClass(node.host.Platform)"></i>
|
||||
<span class="node-cell-os-text">@#node.os#@</span>
|
||||
</td>
|
||||
<td style="text-align: center;" class="node-cell location">
|
||||
<i :class="'fi fi-' + node.location"></i>
|
||||
<span class="node-cell-location-text text-uppercase"> @#node.location#@</span>
|
||||
</td>
|
||||
<td style="text-align: center;" class="node-cell uptime">@#node.uptime#@</td>
|
||||
<td style="text-align: center;" class="node-cell load">@#node.load#@</td>
|
||||
<td style="text-align: center;" class="node-cell network">@#node.network#@</td>
|
||||
<td style="text-align: center;" class="node-cell traffic">@#node.traffic#@</td>
|
||||
<td class="node-cell cpu">
|
||||
<div :class="['progress', node.online ? 'progress-online' : 'progress-offline']">
|
||||
<div :style="node.cpu.style" :class="node.cpu.class"><small>@#node.cpu.percent#@%</small>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="node-cell memory">
|
||||
<div :class="['progress', node.online ? 'progress-online' : 'progress-offline']">
|
||||
<div :style="node.memory.style" :class="node.memory.class">
|
||||
<small>@#node.memory.percent#@%</small>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="node-cell hdd">
|
||||
<div :class="['progress', node.online ? 'progress-online' : 'progress-offline']">
|
||||
<div :style="node.hdd.style" :class="node.hdd.class"><small>@#node.hdd.percent#@%</small>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="expandRow" :class="index % 2 === 0 ? 'odd': 'even'">
|
||||
<td colspan="16">
|
||||
<div class="accordian-body collapse" :id="'rt'+node.ID">
|
||||
<div style="display: flex;left-items: center;justify-content: center;flex-direction: column; max-width: 89vw">
|
||||
<span class="node-cell-expand">
|
||||
<span class="node-cell-expand-label">{{tr "Platform"}}:</span>
|
||||
@#node.host.Platform#@-@#node.host.PlatformVersion#@
|
||||
[<span v-if="node.host.Virtualization">@#node.host.Virtualization#@:</span>@#node.host.Arch#@]
|
||||
</span>
|
||||
<span class="node-cell-expand" v-if="node.host.CPU">
|
||||
<span class="node-cell-expand-label">CPU:</span>
|
||||
@#node.host.CPU.join(",")#@
|
||||
</span>
|
||||
<span class="node-cell-expand">
|
||||
<span class="node-cell-expand-label">{{tr "DiskUsed"}}:</span>
|
||||
@#formatByteSize(node.state.DiskUsed)#@ / @#formatByteSize(node.host.DiskTotal)#@
|
||||
</span>
|
||||
<span class="node-cell-expand">
|
||||
<span class="node-cell-expand-label">{{tr "MemUsed"}}:</span>
|
||||
@#formatByteSize(node.state.MemUsed)#@ / @#formatByteSize(node.host.MemTotal)#@(@#toFixed2(node.state.MemUsed / node.host.MemTotal * 100)#@%)
|
||||
</span>
|
||||
<span class="node-cell-expand">
|
||||
<span class="node-cell-expand-label">{{tr "SwapUsed"}}:</span>
|
||||
@#formatByteSize(node.state.SwapUsed)#@ / @#formatByteSize(node.host.SwapTotal)#@
|
||||
<span v-if="node.host.SwapTotal">(@#toFixed2(node.state.SwapUsed / node.host.SwapTotal * 100)#@%)</span>
|
||||
</span>
|
||||
<span class="node-cell-expand">
|
||||
<span class="node-cell-expand-label">{{tr "NetTransfer"}}:</span>
|
||||
<i class="arrow alternate circle down outline icon"
|
||||
style="margin: 0"></i>@#formatByteSize(node.state.NetInTransfer)#@
|
||||
<i class="arrow alternate circle up outline icon"
|
||||
style="margin: 0"></i>@#formatByteSize(node.state.NetOutTransfer)#@
|
||||
</span>
|
||||
<span class="node-cell-expand load">
|
||||
<span class="node-cell-expand-label">{{tr "Load"}}:</span>
|
||||
@#toFixed2(node.state.Load1)#@ / @#toFixed2(node.state.Load5)#@ / @#toFixed2(node.state.Load15)#@
|
||||
</span>
|
||||
<span class="node-cell-expand">
|
||||
<span class="node-cell-expand-label">{{tr "ProcessCount"}}:</span>
|
||||
@#node.state.ProcessCount#@
|
||||
</span>
|
||||
<span class="node-cell-expand">
|
||||
<span class="node-cell-expand-label">{{tr "ConnCount"}}:</span>
|
||||
TCP @#node.state.TcpConnCount#@ / UDP @#node.state.UdpConnCount#@
|
||||
</span>
|
||||
<span class="node-cell-expand">
|
||||
<span class="node-cell-expand-label">{{tr "BootTime"}}:</span>
|
||||
@#formatTimestamp(node.host.BootTime)#@
|
||||
</span>
|
||||
<span class="node-cell-expand">
|
||||
<span class="node-cell-expand-label">{{tr "LastActive"}}:</span>
|
||||
@#new Date(node.lastActive).toLocaleString()#@
|
||||
</span>
|
||||
<span class="node-cell-expand">
|
||||
<span class="node-cell-expand-label">{{tr "Version"}}:</span>
|
||||
@#node.host.Version#@
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{template "theme-server-status/content-nav" .}}
|
||||
<!-- showGroup true -->
|
||||
<template v-if="showGroup">
|
||||
<section class="container table-responsive content" style="max-width: 95vw" v-for="group in nodesTag">
|
||||
{{template "theme-server-status/home-group-true" .}}
|
||||
</section>
|
||||
</template>
|
||||
<!-- showGroup false -->
|
||||
<template v-else>
|
||||
<section class="container table-responsive content" style="max-width: 95vw">
|
||||
{{template "theme-server-status/home-group-false" .}}
|
||||
</section>
|
||||
</template>
|
||||
{{template "theme-server-status/content-footer" .}}
|
||||
</div>
|
||||
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#app',
|
||||
delimiters: ['@#', '#@'],
|
||||
data: {
|
||||
nodes: [],
|
||||
page: 'index',
|
||||
defaultTemplate: {{.Conf.Site.Theme}},
|
||||
templates: {{.Themes}},
|
||||
cache: [],
|
||||
servers: [],
|
||||
nodesTag: [],
|
||||
nodesNoTag: [],
|
||||
chartDataList: [],
|
||||
ws: null
|
||||
},
|
||||
mixins: [mixinsVue],
|
||||
created() {
|
||||
const initData = JSON.parse('{{.Servers}}').servers;
|
||||
this.nodes = groupingData(this.handleNodes(initData),"Tag");
|
||||
this.initTheme()
|
||||
this.servers = JSON.parse('{{.Servers}}').servers;
|
||||
if(this.showGroup) {
|
||||
this.nodesTag = this.groupingData(this.handleNodes(this.servers),"Tag");
|
||||
} else {
|
||||
this.nodesNoTag = this.handleNodes(this.servers);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 初始化时建立WebSocket连接
|
||||
this.connect();
|
||||
// 监听页面可见性变化
|
||||
document.addEventListener("visibilitychange", () => {
|
||||
if (document.visibilityState === "visible") {
|
||||
setTimeout(() => {
|
||||
if (document.hasFocus()) {
|
||||
this.connect();
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
isWindowsPlatform(str) {
|
||||
@@ -231,13 +133,18 @@
|
||||
return x !== "NaN undefined" ? x : '0B'
|
||||
},
|
||||
formatPercent(live, used, total) {
|
||||
const percent = live ? (this.toFixed2(used / total * 100) || 0) : 0
|
||||
return this.formatPercents(percent)
|
||||
//const percent = live ? (this.toFixed2(used / total * 100) || 0) : 0
|
||||
const percent = (this.toFixed2(used / total * 100) || 0)
|
||||
return this.formatPercents(live,percent)
|
||||
},
|
||||
formatPercents(percent) {
|
||||
formatPercents(live,percent) {
|
||||
//if(!live) { percent = 0; }
|
||||
if (percent <= 0) {
|
||||
percent = 0;
|
||||
}
|
||||
if (percent >= 100) {
|
||||
percent = 100;
|
||||
}
|
||||
if (!this.cache[percent]) {
|
||||
this.cache[percent] = {
|
||||
class: 'progress-bar progress-bar-success',
|
||||
@@ -263,12 +170,18 @@
|
||||
return parseFloat((bytes / Math.pow(1024, i)).toFixed(2)) + sizes[i];
|
||||
},
|
||||
connect() {
|
||||
const wsProtocol = window.location.protocol === "https:" ? "wss" : "ws"
|
||||
const ws = new WebSocket(wsProtocol + '://' + window.location.host + '/ws');
|
||||
ws.onopen = function () {
|
||||
// 如果已经存在 WebSocket 连接并且处于开启状态,则不重复建立连接
|
||||
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
||||
console.log('Closing old WebSocket connection...');
|
||||
this.ws.close();
|
||||
}
|
||||
const wsProtocol = window.location.protocol === "https:" ? "wss" : "ws";
|
||||
this.ws = new WebSocket(wsProtocol + '://' + window.location.host + '/ws');
|
||||
|
||||
this.ws.onopen = function () {
|
||||
console.log("Connection open ...")
|
||||
}
|
||||
ws.onmessage = (evt) => {
|
||||
this.ws.onmessage = (evt) => {
|
||||
let jsonData = evt.data
|
||||
const data = JSON.parse(jsonData)
|
||||
for (let i = 0; i < data.servers?.length; i++) {
|
||||
@@ -279,16 +192,20 @@
|
||||
const lastActive = new Date(ns.LastActive).getTime()
|
||||
data.servers[i].live = data.now - lastActive <= 10 * 1000;
|
||||
}
|
||||
}
|
||||
this.nodes = groupingData(this.handleNodes(data.servers),"Tag");
|
||||
}
|
||||
if(this.showGroup) {
|
||||
this.nodesTag = this.groupingData(this.handleNodes(data.servers),"Tag");
|
||||
} else {
|
||||
this.nodesNoTag = this.handleNodes(data.servers);
|
||||
}
|
||||
}
|
||||
ws.onclose = () => {
|
||||
setTimeout(function () {
|
||||
this.connect()
|
||||
this.ws.onclose = () => {
|
||||
setTimeout(() => {
|
||||
this.connect();
|
||||
}, 5000);
|
||||
}
|
||||
ws.onerror = function () {
|
||||
ws.close()
|
||||
};
|
||||
this.ws.onerror = () => {
|
||||
this.ws.close()
|
||||
}
|
||||
},
|
||||
handleNodes(servers) {
|
||||
@@ -309,7 +226,7 @@
|
||||
load: this.toFixed2(server.State.Load1),
|
||||
network: this.getNetworkSpeed(server.State.NetInSpeed, server.State.NetOutSpeed),
|
||||
traffic: this.formatByteSize(server.State.NetInTransfer) + ' | ' + this.formatByteSize(server.State.NetOutTransfer),
|
||||
cpu: this.formatPercents(this.toFixed2(server.State.CPU)),
|
||||
cpu: this.formatPercents(server.live, this.toFixed2(server.State.CPU)),
|
||||
memory: this.formatPercent(server.live, server.State.MemUsed, server.Host.MemTotal),
|
||||
hdd: this.formatPercent(server.live, server.State.DiskUsed, server.Host.DiskTotal),
|
||||
online: server.live,
|
||||
@@ -324,29 +241,164 @@
|
||||
},
|
||||
getNetworkSpeed(netInSpeed, netOutSpeed) {
|
||||
return this.formatByteSize(netInSpeed) + ' | ' + this.formatByteSize(netOutSpeed)
|
||||
},
|
||||
showCharts(event, id) {
|
||||
const chartContainer = this.$refs[`chart${id}`][0];
|
||||
const chartboxShow = chartContainer.getAttribute('chartbox-show');
|
||||
chartContainer.setAttribute('chartbox-show', chartboxShow === '0' ? '1' : '0');
|
||||
const isAriaExpandedFalse = event.currentTarget.getAttribute('aria-expanded') === 'false';
|
||||
if (!isAriaExpandedFalse) return;
|
||||
// 发起数据请求
|
||||
const url = `/api/v1/monitor/${id}`;
|
||||
fetch(url)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.result) { // 数据请求成功,更新数据并渲染图表
|
||||
this.chartDataList[id - 1] = data.result;
|
||||
this.$nextTick(() => {
|
||||
this.renderCharts(id);
|
||||
});
|
||||
} else {
|
||||
console.log('this agent (id:'+ id + ') has no monitor.');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching data:', error);
|
||||
});
|
||||
},
|
||||
renderCharts(id, reload = false) {
|
||||
if (!this.chartDataList[id - 1]) return;
|
||||
const chartData = this.chartDataList[id - 1];
|
||||
const chartContainer = this.$refs[`chart${id}`][0];
|
||||
if (reload) { //点击切换亮色/暗色风格模式时,重新载入echarts图表的逻辑,
|
||||
// 第一步,查找已经渲染出的图表容器,并销毁它
|
||||
const existingChart = echarts.getInstanceByDom(chartContainer);
|
||||
if (existingChart) existingChart.dispose();
|
||||
// 第二步,如果图表容器处于不可见状态chartboxShow=0,不重新渲染出新的图表,
|
||||
// 如果图表容器处于可见状态chartboxShow=1,重新渲染出新的图表
|
||||
const chartboxShow = chartContainer.getAttribute('chartbox-show');
|
||||
if ( chartboxShow === '0' ) return;
|
||||
}
|
||||
// 定义图表参数值
|
||||
const MaxTCPPingValue = {{.Conf.MaxTCPPingValue}} ? {{.Conf.MaxTCPPingValue}} : 300;
|
||||
const isMobile = this.checkIsMobile();
|
||||
const fontSize = isMobile ? 10 : 14;
|
||||
const gridLeft = isMobile ? 25 : 36;
|
||||
const gridRight = isMobile ? 5 : 20;
|
||||
const legendLeft = isMobile ? 'center' : 'center';
|
||||
const legendTop = isMobile ? 5 : 5;
|
||||
const legendPadding= isMobile ? [5,0,5,0] : [5,0,5,0];
|
||||
const systemDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||||
const theme = localStorage.getItem("theme") ? localStorage.getItem("theme") : systemDarkMode;
|
||||
const chartTheme = theme == "dark" ? "dark" : "default";
|
||||
const fontColor = theme == "dark" ? "#f1f1f1" : "#000000";
|
||||
const backgroundColor = theme == "dark" ? "#1C1D26" : '';
|
||||
const tooltipBackgroundColor = theme == "dark" ? "#1C1D26" : '#ffffff';
|
||||
const tooltipBorderColor = theme == "dark" ? "#31363B" : "#ffffff";
|
||||
// 渲染图表
|
||||
const chart = echarts.init(chartContainer, chartTheme, {
|
||||
renderer: 'canvas',
|
||||
useDirtyRect: false,
|
||||
width: 'auto',
|
||||
height: 300,
|
||||
});
|
||||
const xAxisData = chartData[0].created_at.map(time => new Date(time).toLocaleString());
|
||||
const seriesData = chartData.map(item => {
|
||||
let loss = 0;
|
||||
const data = item.avg_delay.map((avgDelay, index) => {
|
||||
if(avgDelay > 0 && avgDelay < MaxTCPPingValue){
|
||||
loss += avgDelay > 0.9 * MaxTCPPingValue ? 1 : 0;
|
||||
return [item.created_at[index], avgDelay];
|
||||
}else{
|
||||
loss += 1;
|
||||
}
|
||||
});
|
||||
const lossRate = ((loss / item.created_at.length) * 100).toFixed(1);
|
||||
item.monitor_name = item.monitor_name.includes("%") ? item.monitor_name : `${item.monitor_name} ${lossRate}%`;
|
||||
return {
|
||||
name: item.monitor_name,
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'none',
|
||||
data: data,
|
||||
connectNulls: true
|
||||
};
|
||||
});
|
||||
const option = {
|
||||
backgroundColor: backgroundColor,
|
||||
title: {
|
||||
show: false
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
backgroundColor: tooltipBackgroundColor,
|
||||
borderColor: tooltipBorderColor,
|
||||
textStyle: {
|
||||
fontSize: fontSize,
|
||||
color: fontColor
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
data: chartData.map(item => item.monitor_name),
|
||||
show: true,
|
||||
textStyle: {
|
||||
fontSize: fontSize,
|
||||
color: fontColor
|
||||
},
|
||||
top: legendTop,
|
||||
bottom: 0,
|
||||
left: legendLeft,
|
||||
padding: legendPadding
|
||||
},
|
||||
xAxis: {
|
||||
type: 'time',
|
||||
data: xAxisData,
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
fontSize: fontSize
|
||||
}
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
fontSize: fontSize
|
||||
}
|
||||
}
|
||||
},
|
||||
dataZoom: [
|
||||
{
|
||||
type: 'slider',
|
||||
start: 0,
|
||||
end: 100
|
||||
}
|
||||
],
|
||||
series: seriesData,
|
||||
textStyle: {
|
||||
fontSize: fontSize,
|
||||
color: fontColor
|
||||
},
|
||||
grid: {
|
||||
top: '40',
|
||||
left: gridLeft,
|
||||
right: gridRight
|
||||
}
|
||||
};
|
||||
chart.setOption(option);
|
||||
},
|
||||
reloadCharts() { // 重新加载所有图表
|
||||
this.servers.forEach(node => {
|
||||
const id = node.ID;
|
||||
const chartData = this.chartDataList[id - 1];
|
||||
if (chartData) {
|
||||
this.renderCharts(id,true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
function groupingData(data, field) {
|
||||
let map = new Map();
|
||||
let dest = [];
|
||||
|
||||
data.forEach(item => {
|
||||
if (!map.has(item[field])) {
|
||||
dest.push({
|
||||
[field]: item[field],
|
||||
data: [item]
|
||||
});
|
||||
map.set(item[field], item);
|
||||
} else {
|
||||
dest.find(dItem => dItem[field] === item[field]).data.push(item);
|
||||
}
|
||||
});
|
||||
|
||||
return dest;
|
||||
}
|
||||
</script>
|
||||
{{template "theme-server-status/footer" .}}
|
||||
{{end}}
|
||||
|
||||
|
||||
+28
-21
@@ -21,14 +21,17 @@
|
||||
<script>
|
||||
const monitorInfo = JSON.parse('{{.MonitorInfos}}');
|
||||
const initData = JSON.parse('{{.Servers}}').servers;
|
||||
let MaxTCPPingValue = {{.MaxTCPPingValue}};
|
||||
let MaxTCPPingValue = {{.Conf.MaxTCPPingValue}};
|
||||
if (MaxTCPPingValue == null) {
|
||||
MaxTCPPingValue = 300;
|
||||
MaxTCPPingValue = 1000;
|
||||
}
|
||||
new Vue({
|
||||
el: '#app',
|
||||
delimiters: ['@#', '#@'],
|
||||
data: {
|
||||
page: 'network',
|
||||
defaultTemplate: {{.Conf.Site.Theme}},
|
||||
templates: {{.Themes}},
|
||||
servers: initData,
|
||||
option: {
|
||||
tooltip: {
|
||||
@@ -70,7 +73,7 @@
|
||||
},
|
||||
dataZoom: [
|
||||
{
|
||||
start: 94,
|
||||
start: 0,
|
||||
end: 100
|
||||
}
|
||||
],
|
||||
@@ -80,19 +83,16 @@
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
boundaryGap: [0, '100%']
|
||||
boundaryGap: false
|
||||
},
|
||||
series: [],
|
||||
},
|
||||
chartOnOff: true,
|
||||
},
|
||||
mixins: [mixinsVue],
|
||||
created() {
|
||||
this.initTheme();
|
||||
},
|
||||
mounted() {
|
||||
this.renderChart();
|
||||
this.parseMonitorInfo(monitorInfo);
|
||||
this.parseMonitorInfo(monitorInfo);
|
||||
},
|
||||
methods: {
|
||||
getFontLogoClass(str) {
|
||||
@@ -153,15 +153,15 @@
|
||||
return '';
|
||||
},
|
||||
redirectNetwork(id) {
|
||||
this.getMonitorHistory(id)
|
||||
.then(function(monitorInfo) {
|
||||
var vm = app.__vue__;
|
||||
vm.parseMonitorInfo(monitorInfo);
|
||||
})
|
||||
.catch(function(error){
|
||||
window.location.href = "/404";
|
||||
})
|
||||
},
|
||||
this.getMonitorHistory(id)
|
||||
.then(function(monitorInfo) {
|
||||
var vm = app.__vue__;
|
||||
vm.parseMonitorInfo(monitorInfo);
|
||||
})
|
||||
.catch(function(error){
|
||||
window.location.href = "/404";
|
||||
})
|
||||
},
|
||||
getMonitorHistory(id) {
|
||||
return $.ajax({
|
||||
url: "/api/v1/monitor/"+id,
|
||||
@@ -175,11 +175,13 @@
|
||||
let loss = 0;
|
||||
let data = [];
|
||||
for (let j = 0; j < monitorInfo.result[i].created_at.length; j++) {
|
||||
avgDelay = monitorInfo.result[i].avg_delay[j];
|
||||
avgDelay = Math.round(monitorInfo.result[i].avg_delay[j]);
|
||||
if (avgDelay > 0.9 * MaxTCPPingValue) {
|
||||
loss += 1
|
||||
}
|
||||
data.push([monitorInfo.result[i].created_at[j], avgDelay]);
|
||||
if (avgDelay > 0) {
|
||||
data.push([monitorInfo.result[i].created_at[j], avgDelay]);
|
||||
}
|
||||
}
|
||||
lossRate = ((loss / monitorInfo.result[i].created_at.length) * 100).toFixed(1);
|
||||
legendName = monitorInfo.result[i].monitor_name +" "+ lossRate + "%";
|
||||
@@ -189,7 +191,13 @@
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'none',
|
||||
data: data
|
||||
data: data,
|
||||
markPoint: {
|
||||
data: [
|
||||
{ type: 'max', symbol: 'pin', name: 'Max', itemStyle: { color: '#f00' } },
|
||||
{ type: 'min', symbol: 'pin', name: 'Min', itemStyle: { color: '#0f0' } }
|
||||
]
|
||||
}
|
||||
});
|
||||
}
|
||||
this.option.title.text = monitorInfo.result[0].server_name;
|
||||
@@ -214,4 +222,3 @@
|
||||
</script>
|
||||
{{template "theme-server-status/footer" .}}
|
||||
{{end}}
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
{{define "theme-server-status/service-group-false"}}
|
||||
<table class="table table-striped table-condensed table-hover service-status">
|
||||
<thead>
|
||||
<tr class="node-group-tag">
|
||||
<th colspan="16" style="border:none;">
|
||||
{{tr "ServicesManagement"}}
|
||||
</th>
|
||||
</tr>
|
||||
<tr class="node-group-cell">
|
||||
<th class="node-cell center service-status-th">{{tr "Status"}}</th>
|
||||
<th class="node-cell center service-name-th">{{tr "Name"}}</th>
|
||||
<th class="node-cell center service-details-th">{{tr "Details"}}</th>
|
||||
<th class="node-cell center service-averagelatency-th">{{tr "AverageLatency"}}</th>
|
||||
<th class="node-cell center service-30daysonline-th">{{tr "30DaysOnline"}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template v-for="service in servicesNoTag">
|
||||
<tr>
|
||||
<td class="node-cell center">
|
||||
<div class="delay-today">
|
||||
<i class="delay-today-icon" :class="service.health.className"></i>
|
||||
<span class="delay-today-text">@#service.health.text#@</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="node-cell center">@#service.name#@</td>
|
||||
<td class="node-cell center service-details-td">
|
||||
<template v-for="(item,index) in service.dayDetail">
|
||||
<div data-toggle="tooltip" data-placement="top" class="service-day-status-icon" :class="item.className"
|
||||
:title="item.text">
|
||||
</div>
|
||||
</template>
|
||||
</td>
|
||||
<td class="node-cell center">@#service.avgDelay#@</td>
|
||||
<td class="node-cell center">
|
||||
<div class="progress">
|
||||
<div :style="service.totalUpTime.style" :class="service.totalUpTime.className">
|
||||
<small>@#service.totalUpTime.percent#@%</small>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
{{end}}
|
||||
@@ -0,0 +1,51 @@
|
||||
{{define "theme-server-status/service-group-true"}}
|
||||
<table class="table table-striped table-condensed table-hover service-status">
|
||||
<thead>
|
||||
<tr class="node-group-tag">
|
||||
<th colspan="16" style="border:none;">
|
||||
<span v-if="group.type == 1">HTTP-GET</span>
|
||||
<span v-if="group.type == 2">ICMP-Ping</span>
|
||||
<span v-if="group.type == 3">TCP-Ping</span>
|
||||
</th>
|
||||
</tr>
|
||||
<tr class="node-group-cell">
|
||||
<th class="node-cell center service-status-th">{{tr "Status"}}</th>
|
||||
<th class="node-cell center service-name-th">{{tr "Name"}}</th>
|
||||
<th class="node-cell center service-details-th">{{tr "Details"}}</th>
|
||||
<th class="node-cell center service-averagelatency-th">{{tr "AverageLatency"}}</th>
|
||||
<th class="node-cell center service-30daysonline-th">{{tr "30DaysOnline"}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template v-for="service in group.data">
|
||||
<tr>
|
||||
<td class="node-cell center">
|
||||
<div class="delay-today">
|
||||
<i class="delay-today-icon" :class="service.health.className"></i>
|
||||
<span class="delay-today-text">@#service.health.text#@</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="node-cell center">@#service.name#@</td>
|
||||
<td class="node-cell center service-details-td">
|
||||
<template v-for="(item,index) in service.dayDetail">
|
||||
<div data-toggle="tooltip" data-placement="top" class="service-day-status-icon" :class="item.className"
|
||||
:title="item.text">
|
||||
</div>
|
||||
</template>
|
||||
</td>
|
||||
<td class="node-cell center">@#service.avgDelay#@</td>
|
||||
<td class="node-cell center">
|
||||
<div class="progress">
|
||||
<div :style="service.totalUpTime.style" :class="service.totalUpTime.className">
|
||||
<small>@#service.totalUpTime.percent#@%</small>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
{{end}}
|
||||
|
||||
|
||||
|
||||
+64
-84
@@ -2,110 +2,89 @@
|
||||
{{template "theme-server-status/header" .}}
|
||||
<div id="app">
|
||||
{{template "theme-server-status/content-nav" .}}
|
||||
<div class="container content" style="max-width: 95vw">
|
||||
<table class="table table-striped table-condensed service-status">
|
||||
<!-- showGroup true -->
|
||||
<template v-if="showGroup">
|
||||
<section class="container content" style="max-width: 95vw; min-height: .01%;overflow-x: auto;" v-for="group in servicesTag">
|
||||
{{template "theme-server-status/service-group-true" .}}
|
||||
</section>
|
||||
</template>
|
||||
<!-- showGroup false -->
|
||||
<template v-else>
|
||||
<section class="container content" style="max-width: 95vw; min-height: .01%;overflow-x: auto;">
|
||||
{{template "theme-server-status/service-group-false" .}}
|
||||
</section>
|
||||
</template>
|
||||
<section class="container content table-responsive" style="max-width: 95vw">
|
||||
{{if .CycleTransferStats}}
|
||||
<table class="table table-striped table-condensed table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="node-cell center" style="min-width:60px">{{tr "Status"}}</th>
|
||||
<th class="node-cell center" style="min-width:50px">{{tr "Name"}}</th>
|
||||
<th class="node-cell center">{{tr "Details"}}</th>
|
||||
<th class="node-cell center" style="min-width:80px">{{tr "AverageLatency"}}</th>
|
||||
<th class="node-cell center" style="min-width:80px">{{tr "30DaysOnline"}}</th>
|
||||
</tr>
|
||||
<tr class="node-group-tag">
|
||||
<th colspan="16" style="border:none;">
|
||||
{{tr "CycleTransferStats"}}
|
||||
</th>
|
||||
</tr>
|
||||
<tr class="node-group-cell">
|
||||
<th class="node-cell center">ID</th>
|
||||
<th class="node-cell center">{{tr "Rules"}}</th>
|
||||
<th class="node-cell center">{{tr "Server"}}</th>
|
||||
<th class="node-cell center">{{tr "From"}}</th>
|
||||
<th class="node-cell center">{{tr "To"}}</th>
|
||||
<th class="node-cell center">MAX</th>
|
||||
<th class="node-cell center">MIN</th>
|
||||
<th class="node-cell center">{{tr "NextCheck"}}</th>
|
||||
<th class="node-cell center">{{tr "CurrentUsage"}}</th>
|
||||
<th class="node-cell center">{{tr "Transleft"}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="servers">
|
||||
<template v-for="service in services">
|
||||
<tr v-if="service.enableShow === 'true'">
|
||||
<td class="node-cell center">
|
||||
<div class="delay-today">
|
||||
<i class="delay-today" :class="service.health.className"></i>
|
||||
@#service.health.text#@
|
||||
</div>
|
||||
</td>
|
||||
<td class="node-cell center">@#service.name#@</td>
|
||||
<td class="node-cell center">
|
||||
<template v-for="(item,index) in service.dayDetail">
|
||||
<div class="service-day-status-icon" :class="item.className"
|
||||
:data-tooltip="item.text">
|
||||
</div>
|
||||
</template>
|
||||
</td>
|
||||
<td class="node-cell center">@#service.avgDelay#@</td>
|
||||
<tbody>
|
||||
{{range $id, $stats := .CycleTransferStats}}
|
||||
{{range $innerId, $transfer := $stats.Transfer}}
|
||||
{{$TransLeftPercent := TransLeftPercent (UintToFloat $transfer) (UintToFloat $stats.Max)}}
|
||||
<tr>
|
||||
<td class="node-cell center">{{$id}}</td>
|
||||
<td class="node-cell center">{{$stats.Name}}</td>
|
||||
<td class="node-cell center">{{index $stats.ServerName $innerId}}</td>
|
||||
<td class="node-cell center">{{$stats.From|tf}}</td>
|
||||
<td class="node-cell center">{{$stats.To|tf}}</td>
|
||||
<td class="node-cell center">{{$stats.Max|bf}}</td>
|
||||
<td class="node-cell center">{{$stats.Min|bf}}</td>
|
||||
<td class="node-cell center">{{(index $stats.NextUpdate $innerId)|sft}}</td>
|
||||
<td class="node-cell center">{{$transfer|bf}}</td>
|
||||
<td class="node-cell center">
|
||||
<div class="progress">
|
||||
<div :style="service.totalUpTime.style" :class="service.totalUpTime.className">
|
||||
<small>@#service.totalUpTime.percent#@%</small>
|
||||
<div style="width: {{$TransLeftPercent}}%" :class="'progress-bar progress-bar-' + toSSBar('{{TransClassName $TransLeftPercent}}')">
|
||||
<small style="display: inline-block;width: max-content;">{{TransLeft $stats.Max $transfer}} / {{$TransLeftPercent}} %</small>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="container" style="padding:unset;max-width: 95vw">
|
||||
{{if .CycleTransferStats}}
|
||||
<h4 style="text-align: center;">{{tr "CycleTransferStats"}}</h4>
|
||||
<div class="table-responsive content">
|
||||
<table class="table table-striped table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="node-cell center">ID</th>
|
||||
<th class="node-cell center">{{tr "Rules"}}</th>
|
||||
<th class="node-cell center">{{tr "Server"}}</th>
|
||||
<th class="node-cell center">{{tr "From"}}</th>
|
||||
<th class="node-cell center">{{tr "To"}}</th>
|
||||
<th class="node-cell center">MAX</th>
|
||||
<th class="node-cell center">MIN</th>
|
||||
<th class="node-cell center">{{tr "NextCheck"}}</th>
|
||||
<th class="node-cell center">{{tr "CurrentUsage"}}</th>
|
||||
<th class="node-cell center">{{tr "Transleft"}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range $id, $stats := .CycleTransferStats}}
|
||||
{{range $innerId, $transfer := $stats.Transfer}}
|
||||
{{$TransLeftPercent := TransLeftPercent (UintToFloat $transfer) (UintToFloat $stats.Max)}}
|
||||
<tr>
|
||||
<td class="node-cell center">{{$id}}</td>
|
||||
<td class="node-cell center">{{$stats.Name}}</td>
|
||||
<td class="node-cell center">{{index $stats.ServerName $innerId}}</td>
|
||||
<td class="node-cell center">{{$stats.From|tf}}</td>
|
||||
<td class="node-cell center">{{$stats.To|tf}}</td>
|
||||
<td class="node-cell center">{{$stats.Max|bf}}</td>
|
||||
<td class="node-cell center">{{$stats.Min|bf}}</td>
|
||||
<td class="node-cell center">{{(index $stats.NextUpdate $innerId)|sft}}</td>
|
||||
<td class="node-cell center">{{$transfer|bf}}</td>
|
||||
<td class="node-cell center">
|
||||
<div class="progress">
|
||||
<div style="width: {{$TransLeftPercent}}%" :class="'progress-bar progress-bar-' + toSSBar('{{TransClassName $TransLeftPercent}}')">
|
||||
<small style="display: inline-block;width: max-content;">{{TransLeft $stats.Max $transfer}} / {{$TransLeftPercent}} %</small>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</section>
|
||||
{{template "theme-server-status/content-footer" .}}
|
||||
</div>
|
||||
<script>
|
||||
|
||||
// 初始化 Tooltip
|
||||
$(document).ready(function(){
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#app',
|
||||
delimiters: ['@#', '#@'],
|
||||
data: {
|
||||
services: []
|
||||
page: 'service',
|
||||
defaultTemplate: {{.Conf.Site.Theme}},
|
||||
templates: {{.Themes}},
|
||||
servicesTag: [],
|
||||
servicesNoTag: [],
|
||||
},
|
||||
created() {
|
||||
this.initData()
|
||||
this.initData();
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
@@ -129,6 +108,7 @@
|
||||
const services = []
|
||||
{{range $service := .Services}}
|
||||
services.push({
|
||||
type: '{{$service.Monitor.Type}}',
|
||||
name: '{{$service.Monitor.Name}}',
|
||||
currentUp: parseInt('{{$service.CurrentUp}}'),
|
||||
currentDown: parseInt('{{$service.CurrentDown}}'),
|
||||
@@ -137,7 +117,6 @@
|
||||
delay: '{{$service.Delay}}'.replaceAll("[","").replaceAll("]","").split(" "),
|
||||
up: '{{$service.Up}}'.replaceAll("[","").replaceAll("]","").split(" "),
|
||||
down: '{{$service.Down}}'.replaceAll("[","").replaceAll("]","").split(" "),
|
||||
enableShow: '{{$service.Monitor.EnableShowInService}}',
|
||||
})
|
||||
{{end}}
|
||||
// @formatter:on
|
||||
@@ -148,7 +127,8 @@
|
||||
service.dayDetail = this.getDayTails(service)
|
||||
service.totalUpTime = this.getProgressInfo(this.getPercent(service.totalUp, service.totalDown))
|
||||
}
|
||||
this.services = services
|
||||
this.servicesTag = this.groupingData(services,"type");
|
||||
this.servicesNoTag = services;
|
||||
},
|
||||
getPercent(up, down) {
|
||||
if (!up) {
|
||||
|
||||
@@ -12,3 +12,13 @@ site:
|
||||
brand: "nz_site_title"
|
||||
cookiename: "nezha-dashboard" #浏览器 Cookie 字段名,可不改
|
||||
theme: "default"
|
||||
ddns:
|
||||
enable: false
|
||||
provider: "webhook"
|
||||
accessid: ""
|
||||
accesssecret: ""
|
||||
webhookmethod: ""
|
||||
webhookurl: ""
|
||||
webhookrequestbody: ""
|
||||
webhookheaders: ""
|
||||
maxretries: 3
|
||||
|
||||
+2
-2
@@ -38,8 +38,8 @@ $download = "https://github.com/$agentrepo/releases/download/$agenttag/$file"
|
||||
$nssmdownload="https://github.com/$nssmrepo/releases/download/$nssmtag/nssm.zip"
|
||||
Write-Host "Location:$region,connect directly!" -BackgroundColor DarkRed -ForegroundColor Green
|
||||
}else{
|
||||
$download = "https://kkgithub.com/$agentrepo/releases/download/$agenttag/$file"
|
||||
$nssmdownload="https://kkgithub.com/$nssmrepo/releases/download/$nssmtag/nssm.zip"
|
||||
$download = "https://dn-dao-github-mirror.daocloud.io/$agentrepo/releases/download/$agenttag/$file"
|
||||
$nssmdownload="https://dn-dao-github-mirror.daocloud.io/$nssmrepo/releases/download/$nssmtag/nssm.zip"
|
||||
Write-Host "Location:CN,use mirror address" -BackgroundColor DarkRed -ForegroundColor Green
|
||||
}
|
||||
echo $download
|
||||
|
||||
+5
-4
@@ -14,7 +14,7 @@ NZ_AGENT_SERVICE="/etc/systemd/system/nezha-agent.service"
|
||||
NZ_AGENT_SERVICERC="/etc/init.d/nezha-agent"
|
||||
NZ_DASHBOARD_SERVICE="/etc/systemd/system/nezha-dashboard.service"
|
||||
NZ_DASHBOARD_SERVICERC="/etc/init.d/nezha-dashboard"
|
||||
NZ_VERSION="v0.15.6"
|
||||
NZ_VERSION="v0.15.9"
|
||||
|
||||
red='\033[0;31m'
|
||||
green='\033[0;32m'
|
||||
@@ -94,7 +94,7 @@ pre_check() {
|
||||
Docker_IMG="ghcr.io\/naiba\/nezha-dashboard"
|
||||
else
|
||||
GITHUB_RAW_URL="gitee.com/naibahq/nezha/raw/master"
|
||||
GITHUB_URL="kkgithub.com"
|
||||
GITHUB_URL="dn-dao-github-mirror.daocloud.io"
|
||||
Get_Docker_URL="get.docker.com"
|
||||
Get_Docker_Argu=" -s docker --mirror Aliyun"
|
||||
Docker_IMG="registry.cn-shanghai.aliyuncs.com\/naibahq\/nezha-dashboard"
|
||||
@@ -159,7 +159,7 @@ install_arch() {
|
||||
cd /tmp; git clone https://aur.archlinux.org/libsepol.git; cd libsepol; makepkg -si --noconfirm --asdeps; cd ..;
|
||||
git clone https://aur.archlinux.org/libselinux.git; cd libselinux; makepkg -si --noconfirm; cd ..;
|
||||
rm -rf libsepol libselinux'
|
||||
sed -i '/nezha-agent/d' /etc/sudoers && sleep 30s && killall -u nezha-agent && userdel nezha-agent
|
||||
sed -i '/nezha-agent/d' /etc/sudoers && sleep 30s && killall -u nezha-agent && userdel -r nezha-agent
|
||||
echo -e "${red}提示: ${plain}已删除用户nezha-agent,请务必手动核查一遍!\n"
|
||||
;;
|
||||
[nN][oO] | [nN])
|
||||
@@ -269,7 +269,8 @@ install_dashboard_standalone() {
|
||||
|
||||
selinux() {
|
||||
#判断当前的状态
|
||||
if [ "$os_alpine" != 1 ]; then
|
||||
command -v getenforce >/dev/null 2>&1
|
||||
if [ $? -eq 0 ]; then
|
||||
getenforce | grep '[Ee]nfor'
|
||||
if [ $? -eq 0 ]; then
|
||||
echo -e "SELinux是开启状态,正在关闭!"
|
||||
|
||||
@@ -14,7 +14,7 @@ NZ_AGENT_SERVICE="/etc/systemd/system/nezha-agent.service"
|
||||
NZ_AGENT_SERVICERC="/etc/init.d/nezha-agent"
|
||||
NZ_DASHBOARD_SERVICE="/etc/systemd/system/nezha-dashboard.service"
|
||||
NZ_DASHBOARD_SERVICERC="/etc/init.d/nezha-dashboard"
|
||||
NZ_VERSION="v0.15.6"
|
||||
NZ_VERSION="v0.15.9"
|
||||
|
||||
red='\033[0;31m'
|
||||
green='\033[0;32m'
|
||||
@@ -93,7 +93,7 @@ pre_check() {
|
||||
Docker_IMG="ghcr.io\/naiba\/nezha-dashboard"
|
||||
else
|
||||
GITHUB_RAW_URL="gitee.com/naibahq/nezha/raw/master"
|
||||
GITHUB_URL="kkgithub.com"
|
||||
GITHUB_URL="dn-dao-github-mirror.daocloud.io"
|
||||
Get_Docker_URL="get.docker.com"
|
||||
Get_Docker_Argu=" -s docker --mirror Aliyun"
|
||||
Docker_IMG="registry.cn-shanghai.aliyuncs.com\/naibahq\/nezha-dashboard"
|
||||
@@ -157,7 +157,7 @@ install_arch() {
|
||||
cd /tmp; git clone https://aur.archlinux.org/libsepol.git; cd libsepol; makepkg -si --noconfirm --asdeps; cd ..;
|
||||
git clone https://aur.archlinux.org/libselinux.git; cd libselinux; makepkg -si --noconfirm; cd ..;
|
||||
rm -rf libsepol libselinux'
|
||||
sed -i '/nezha-agent/d' /etc/sudoers && sleep 30s && killall -u nezha-agent && userdel nezha-agent
|
||||
sed -i '/nezha-agent/d' /etc/sudoers && sleep 30s && killall -u nezha-agent && userdel -r nezha-agent
|
||||
echo -e "${red}Info: ${plain}user nezha-agent has been deleted, Be sure to check it manually!\n"
|
||||
;;
|
||||
[nN][oO] | [nN])
|
||||
@@ -266,7 +266,8 @@ install_dashboard_standalone() {
|
||||
|
||||
selinux() {
|
||||
#Check SELinux
|
||||
if [ "$os_alpine" != 1 ]; then
|
||||
command -v getenforce >/dev/null 2>&1
|
||||
if [ $? -eq 0 ]; then
|
||||
getenforce | grep '[Ee]nfor'
|
||||
if [ $? -eq 0 ]; then
|
||||
echo -e "SELinux running,closing now!"
|
||||
|
||||
@@ -3,6 +3,9 @@ package rpc
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/naiba/nezha/pkg/ddns"
|
||||
"github.com/naiba/nezha/pkg/utils"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/jinzhu/copier"
|
||||
@@ -110,6 +113,36 @@ func (s *NezhaHandler) ReportSystemInfo(c context.Context, r *pb.Host) (*pb.Rece
|
||||
host := model.PB2Host(r)
|
||||
singleton.ServerLock.RLock()
|
||||
defer singleton.ServerLock.RUnlock()
|
||||
|
||||
// 检查并更新DDNS
|
||||
if singleton.Conf.DDNS.Enable &&
|
||||
singleton.ServerList[clientID].EnableDDNS &&
|
||||
singleton.ServerList[clientID].Host != nil &&
|
||||
host.IP != "" &&
|
||||
singleton.ServerList[clientID].Host.IP != host.IP {
|
||||
|
||||
serverDomain := singleton.ServerList[clientID].DDNSDomain
|
||||
provider, err := singleton.GetDDNSProviderFromString(singleton.Conf.DDNS.Provider)
|
||||
if err == nil && serverDomain != "" {
|
||||
ipv4, ipv6, _ := utils.SplitIPAddr(host.IP)
|
||||
maxRetries := int(singleton.Conf.DDNS.MaxRetries)
|
||||
config := &ddns.DomainConfig{
|
||||
EnableIPv4: true,
|
||||
EnableIpv6: true,
|
||||
FullDomain: serverDomain,
|
||||
Ipv4Addr: ipv4,
|
||||
Ipv6Addr: ipv6,
|
||||
}
|
||||
go singleton.RetryableUpdateDomain(provider, config, maxRetries)
|
||||
|
||||
} else {
|
||||
// 虽然会在启动时panic, 可以断言不会走这个分支, 但是考虑到动态加载配置或者其它情况, 这里输出一下方便检查奇奇怪怪的BUG
|
||||
log.Printf("NEZHA>> 未找到对应的DDNS提供者(%s), 请前往config.yml检查你的设置\n", singleton.Conf.DDNS.Provider)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 发送IP变动通知
|
||||
if singleton.Conf.EnableIPChangeNotification &&
|
||||
((singleton.Conf.Cover == model.ConfigCoverAll && !singleton.Conf.IgnoredIPNotificationServerIDs[clientID]) ||
|
||||
(singleton.Conf.Cover == model.ConfigCoverIgnoreAll && singleton.Conf.IgnoredIPNotificationServerIDs[clientID])) &&
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package singleton
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
ddns2 "github.com/naiba/nezha/pkg/ddns"
|
||||
"log"
|
||||
)
|
||||
|
||||
func RetryableUpdateDomain(provider ddns2.Provider, config *ddns2.DomainConfig, maxRetries int) bool {
|
||||
if nil == config {
|
||||
return false
|
||||
}
|
||||
for retries := 0; retries < maxRetries; retries++ {
|
||||
log.Printf("NEZHA>> 正在尝试更新域名(%s)DDNS(%d/%d)\n", config.FullDomain, retries+1, maxRetries)
|
||||
if provider.UpdateDomain(config) {
|
||||
log.Printf("NEZHA>> 尝试更新域名(%s)DDNS成功\n", config.FullDomain)
|
||||
return true
|
||||
}
|
||||
}
|
||||
log.Printf("NEZHA>> 尝试更新域名(%s)DDNS失败\n", config.FullDomain)
|
||||
return false
|
||||
}
|
||||
|
||||
func GetDDNSProviderFromString(provider string) (ddns2.Provider, error) {
|
||||
switch provider {
|
||||
case "webhook":
|
||||
return ddns2.ProviderWebHook{
|
||||
URL: Conf.DDNS.WebhookURL,
|
||||
RequestMethod: Conf.DDNS.WebhookMethod,
|
||||
RequestBody: Conf.DDNS.WebhookRequestBody,
|
||||
RequestHeader: Conf.DDNS.WebhookHeaders,
|
||||
}, nil
|
||||
case "dummy":
|
||||
return ddns2.ProviderDummy{}, nil
|
||||
case "cloudflare":
|
||||
return ddns2.ProviderCloudflare{
|
||||
Secret: Conf.DDNS.AccessSecret,
|
||||
}, nil
|
||||
}
|
||||
return ddns2.ProviderDummy{}, errors.New(fmt.Sprintf("无法找到配置的DDNS提供者%s", Conf.DDNS.Provider))
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package singleton
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
@@ -46,6 +47,21 @@ func InitConfigFromPath(path string) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
ValidateConfig()
|
||||
}
|
||||
|
||||
// ValidateConfig 验证配置文件有效性
|
||||
func ValidateConfig() {
|
||||
// 如果DDNS启用则检查Provider是否存在, 不存在直接退出
|
||||
if Conf.DDNS.Enable {
|
||||
_, err := GetDDNSProviderFromString(Conf.DDNS.Provider)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if Conf.DDNS.MaxRetries < 1 || Conf.DDNS.MaxRetries > 10 {
|
||||
panic(fmt.Errorf("DDNS.MaxRetries值域为[1, 10]的整数, 当前为 %d", Conf.DDNS.MaxRetries))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// InitDBFromPath 从给出的文件路径中加载数据库
|
||||
|
||||
Reference in New Issue
Block a user