Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e9428d5757 | ||
|
|
daab64d232 | ||
|
|
da8fb57268 | ||
|
|
fda0dad5f1 | ||
|
|
c6fb3196ea | ||
|
|
711d162a46 | ||
|
|
d667ce8afe | ||
|
|
6abc0fce51 | ||
|
|
b1d77a1d27 | ||
|
|
c18e0e420e |
@@ -72,6 +72,7 @@ You can change the dashboard language in the settings page (`/setting`) after th
|
||||
<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/eya46" title="eya46"><img src="https://avatars.githubusercontent.com/u/61458340?v=4" width="50;" alt="eya46"/></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/hiDandelion" title="hiDandelion"><img src="https://avatars.githubusercontent.com/u/77157418?v=4" width="50;" alt="hiDandelion"/></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/lvyaoting" title="lvyaoting"><img src="https://avatars.githubusercontent.com/u/166296299?v=4" width="50;" alt="lvyaoting"/></a>
|
||||
<a href="https://github.com/unclezs" title="unclezs"><img src="https://avatars.githubusercontent.com/u/42318775?v=4" width="50;" alt="unclezs"/></a>
|
||||
|
||||
@@ -99,7 +99,6 @@ func (p *commonPage) service(c *gin.Context) {
|
||||
"Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "ServicesStatus"}),
|
||||
"Services": res.([]interface{})[0],
|
||||
"CycleTransferStats": res.([]interface{})[1],
|
||||
"CustomCode": singleton.Conf.Site.CustomCode,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -204,7 +203,6 @@ func (cp *commonPage) network(c *gin.Context) {
|
||||
c.HTML(http.StatusOK, mygin.GetPreferredTheme(c, "/network"), mygin.CommonEnvironment(c, gin.H{
|
||||
"Servers": string(serversBytes),
|
||||
"MonitorInfos": string(monitorInfos),
|
||||
"CustomCode": singleton.Conf.Site.CustomCode,
|
||||
"MaxTCPPingValue": singleton.Conf.MaxTCPPingValue,
|
||||
}))
|
||||
}
|
||||
@@ -254,8 +252,7 @@ func (cp *commonPage) home(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
c.HTML(http.StatusOK, mygin.GetPreferredTheme(c, "/home"), mygin.CommonEnvironment(c, gin.H{
|
||||
"Servers": string(stat),
|
||||
"CustomCode": singleton.Conf.Site.CustomCode,
|
||||
"Servers": string(stat),
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
@@ -35,20 +35,27 @@ func ServeWeb(port uint) *http.Server {
|
||||
pprof.Register(r)
|
||||
}
|
||||
r.Use(natGateway)
|
||||
tmpl := template.New("").Funcs(funcMap)
|
||||
var err error
|
||||
tmpl, err = tmpl.ParseFS(resource.TemplateFS, "template/**/*.html")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
if os.Getenv("NZ_LOCAL_TEMPLATE") == "true" {
|
||||
r.SetFuncMap(funcMap)
|
||||
r.Use(mygin.RecordPath)
|
||||
r.Static("/static", "resource/static")
|
||||
r.LoadHTMLGlob("resource/template/**/*.html")
|
||||
} else {
|
||||
tmpl := template.New("").Funcs(funcMap)
|
||||
var err error
|
||||
tmpl, err = tmpl.ParseFS(resource.TemplateFS, "template/**/*.html")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
tmpl = loadThirdPartyTemplates(tmpl)
|
||||
r.SetHTMLTemplate(tmpl)
|
||||
r.Use(mygin.RecordPath)
|
||||
staticFs, err := fs.Sub(resource.StaticFS, "static")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
r.StaticFS("/static", http.FS(staticFs))
|
||||
}
|
||||
tmpl = loadThirdPartyTemplates(tmpl)
|
||||
r.SetHTMLTemplate(tmpl)
|
||||
r.Use(mygin.RecordPath)
|
||||
staticFs, err := fs.Sub(resource.StaticFS, "static")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
r.StaticFS("/static", http.FS(staticFs))
|
||||
r.Static("/static-custom", "resource/static/custom")
|
||||
routers(r)
|
||||
page404 := func(c *gin.Context) {
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -384,6 +385,7 @@ func (ma *memberAPI) addOrEditServer(c *gin.Context) {
|
||||
} else {
|
||||
s.Host = &model.Host{}
|
||||
s.State = &model.HostState{}
|
||||
s.TaskCloseLock = new(sync.Mutex)
|
||||
singleton.ServerLock.Lock()
|
||||
singleton.SecretToID[s.Secret] = s.ID
|
||||
singleton.ServerList[s.ID] = &s
|
||||
@@ -906,6 +908,7 @@ type settingForm struct {
|
||||
Theme string
|
||||
DashboardTheme string
|
||||
CustomCode string
|
||||
CustomCodeDashboard string
|
||||
ViewPassword string
|
||||
IgnoredIPNotification string
|
||||
IPChangeNotificationTag string // IP变更提醒的通知组
|
||||
@@ -971,6 +974,7 @@ func (ma *memberAPI) updateSetting(c *gin.Context) {
|
||||
singleton.Conf.Site.Theme = sf.Theme
|
||||
singleton.Conf.Site.DashboardTheme = sf.DashboardTheme
|
||||
singleton.Conf.Site.CustomCode = sf.CustomCode
|
||||
singleton.Conf.Site.CustomCodeDashboard = sf.CustomCodeDashboard
|
||||
singleton.Conf.Site.ViewPassword = sf.ViewPassword
|
||||
singleton.Conf.Oauth2.Admin = sf.Admin
|
||||
// 保证NotificationTag不为空
|
||||
|
||||
+7
-6
@@ -80,12 +80,13 @@ type Config struct {
|
||||
Debug bool // debug模式开关
|
||||
Language string // 系统语言,默认 zh-CN
|
||||
Site struct {
|
||||
Brand string // 站点名称
|
||||
CookieName string // 浏览器 Cookie 名称
|
||||
Theme string
|
||||
DashboardTheme string
|
||||
CustomCode string
|
||||
ViewPassword string // 前台查看密码
|
||||
Brand string // 站点名称
|
||||
CookieName string // 浏览器 Cookie 名称
|
||||
Theme string
|
||||
DashboardTheme string
|
||||
CustomCode string
|
||||
CustomCodeDashboard string
|
||||
ViewPassword string // 前台查看密码
|
||||
}
|
||||
Oauth2 struct {
|
||||
Type string
|
||||
|
||||
@@ -170,14 +170,23 @@ func (ns *NotificationServerBundle) replaceParamsInString(str string, message st
|
||||
|
||||
if ns.Server != nil {
|
||||
str = strings.ReplaceAll(str, "#SERVER.NAME#", mod(ns.Server.Name))
|
||||
str = strings.ReplaceAll(str, "#SERVER.ID#", mod(fmt.Sprintf("%d", ns.Server.ID)))
|
||||
str = strings.ReplaceAll(str, "#SERVER.CPU#", mod(fmt.Sprintf("%f", ns.Server.State.CPU)))
|
||||
str = strings.ReplaceAll(str, "#SERVER.MEM#", mod(fmt.Sprintf("%d", ns.Server.State.MemUsed)))
|
||||
str = strings.ReplaceAll(str, "#SERVER.SWAP#", mod(fmt.Sprintf("%d", ns.Server.State.SwapUsed)))
|
||||
str = strings.ReplaceAll(str, "#SERVER.DISK#", mod(fmt.Sprintf("%d", ns.Server.State.DiskUsed)))
|
||||
str = strings.ReplaceAll(str, "#SERVER.MEMUSED#", mod(fmt.Sprintf("%d", ns.Server.State.MemUsed)))
|
||||
str = strings.ReplaceAll(str, "#SERVER.SWAPUSED#", mod(fmt.Sprintf("%d", ns.Server.State.SwapUsed)))
|
||||
str = strings.ReplaceAll(str, "#SERVER.DISKUSED#", mod(fmt.Sprintf("%d", ns.Server.State.DiskUsed)))
|
||||
str = strings.ReplaceAll(str, "#SERVER.MEMTOTAL#", mod(fmt.Sprintf("%d", ns.Server.Host.MemTotal)))
|
||||
str = strings.ReplaceAll(str, "#SERVER.SWAPTOTAL#", mod(fmt.Sprintf("%d", ns.Server.Host.SwapTotal)))
|
||||
str = strings.ReplaceAll(str, "#SERVER.DISKTOTAL#", mod(fmt.Sprintf("%d", ns.Server.Host.DiskTotal)))
|
||||
str = strings.ReplaceAll(str, "#SERVER.NETINSPEED#", mod(fmt.Sprintf("%d", ns.Server.State.NetInSpeed)))
|
||||
str = strings.ReplaceAll(str, "#SERVER.NETOUTSPEED#", mod(fmt.Sprintf("%d", ns.Server.State.NetOutSpeed)))
|
||||
str = strings.ReplaceAll(str, "#SERVER.TRANSFERIN#", mod(fmt.Sprintf("%d", ns.Server.State.NetInTransfer)))
|
||||
str = strings.ReplaceAll(str, "#SERVER.TRANSFEROUT#", mod(fmt.Sprintf("%d", ns.Server.State.NetOutTransfer)))
|
||||
str = strings.ReplaceAll(str, "#SERVER.NETINTRANSFER#", mod(fmt.Sprintf("%d", ns.Server.State.NetInTransfer)))
|
||||
str = strings.ReplaceAll(str, "#SERVER.NETOUTTRANSFER#", mod(fmt.Sprintf("%d", ns.Server.State.NetOutTransfer)))
|
||||
str = strings.ReplaceAll(str, "#SERVER.LOAD1#", mod(fmt.Sprintf("%f", ns.Server.State.Load1)))
|
||||
str = strings.ReplaceAll(str, "#SERVER.LOAD5#", mod(fmt.Sprintf("%f", ns.Server.State.Load5)))
|
||||
str = strings.ReplaceAll(str, "#SERVER.LOAD15#", mod(fmt.Sprintf("%f", ns.Server.State.Load15)))
|
||||
|
||||
@@ -72,11 +72,11 @@ func execCase(t *testing.T, item testSt) {
|
||||
UdpConnCount: 0,
|
||||
ProcessCount: 0,
|
||||
},
|
||||
LastActive: time.Time{},
|
||||
TaskClose: nil,
|
||||
TaskStream: nil,
|
||||
PrevHourlyTransferIn: 0,
|
||||
PrevHourlyTransferOut: 0,
|
||||
LastActive: time.Time{},
|
||||
TaskClose: nil,
|
||||
TaskStream: nil,
|
||||
PrevTransferInSnapshot: 0,
|
||||
PrevTransferOutSnapshot: 0,
|
||||
}
|
||||
ns := NotificationServerBundle{
|
||||
Notification: &n,
|
||||
|
||||
+5
-3
@@ -5,6 +5,8 @@ import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/naiba/nezha/pkg/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -103,21 +105,21 @@ func (u *Rule) Snapshot(cycleTransferStats *CycleTransferStats, server *Server,
|
||||
src = float64(server.LastActive.Unix())
|
||||
}
|
||||
case "transfer_in_cycle":
|
||||
src = float64(server.State.NetInTransfer - uint64(server.PrevHourlyTransferIn))
|
||||
src = float64(utils.Uint64SubInt64(server.State.NetInTransfer, server.PrevTransferInSnapshot))
|
||||
if u.CycleInterval != 0 {
|
||||
var res NResult
|
||||
db.Model(&Transfer{}).Select("SUM(`in`) AS n").Where("datetime(`created_at`) >= datetime(?) AND server_id = ?", u.GetTransferDurationStart().UTC(), server.ID).Scan(&res)
|
||||
src += float64(res.N)
|
||||
}
|
||||
case "transfer_out_cycle":
|
||||
src = float64(server.State.NetOutTransfer - uint64(server.PrevHourlyTransferOut))
|
||||
src = float64(utils.Uint64SubInt64(server.State.NetOutTransfer, server.PrevTransferOutSnapshot))
|
||||
if u.CycleInterval != 0 {
|
||||
var res NResult
|
||||
db.Model(&Transfer{}).Select("SUM(`out`) AS n").Where("datetime(`created_at`) >= datetime(?) AND server_id = ?", u.GetTransferDurationStart().UTC(), server.ID).Scan(&res)
|
||||
src += float64(res.N)
|
||||
}
|
||||
case "transfer_all_cycle":
|
||||
src = float64(server.State.NetOutTransfer - uint64(server.PrevHourlyTransferOut) + server.State.NetInTransfer - uint64(server.PrevHourlyTransferIn))
|
||||
src = float64(utils.Uint64SubInt64(server.State.NetOutTransfer, server.PrevTransferOutSnapshot) + utils.Uint64SubInt64(server.State.NetInTransfer, server.PrevTransferInSnapshot))
|
||||
if u.CycleInterval != 0 {
|
||||
var res NResult
|
||||
db.Model(&Transfer{}).Select("SUM(`in`+`out`) AS n").Where("datetime(`created_at`) >= datetime(?) AND server_id = ?", u.GetTransferDurationStart().UTC(), server.ID).Scan(&res)
|
||||
|
||||
+9
-6
@@ -3,6 +3,7 @@ package model
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/naiba/nezha/pkg/utils"
|
||||
@@ -27,11 +28,12 @@ type Server struct {
|
||||
State *HostState `gorm:"-"`
|
||||
LastActive time.Time `gorm:"-"`
|
||||
|
||||
TaskClose chan error `gorm:"-" json:"-"`
|
||||
TaskStream pb.NezhaService_RequestTaskServer `gorm:"-" json:"-"`
|
||||
TaskClose chan error `gorm:"-" json:"-"`
|
||||
TaskCloseLock *sync.Mutex `gorm:"-" json:"-"`
|
||||
TaskStream pb.NezhaService_RequestTaskServer `gorm:"-" json:"-"`
|
||||
|
||||
PrevHourlyTransferIn int64 `gorm:"-" json:"-"` // 上次数据点时的入站使用量
|
||||
PrevHourlyTransferOut int64 `gorm:"-" json:"-"` // 上次数据点时的出站使用量
|
||||
PrevTransferInSnapshot int64 `gorm:"-" json:"-"` // 上次数据点时的入站使用量
|
||||
PrevTransferOutSnapshot int64 `gorm:"-" json:"-"` // 上次数据点时的出站使用量
|
||||
}
|
||||
|
||||
func (s *Server) CopyFromRunningServer(old *Server) {
|
||||
@@ -39,9 +41,10 @@ func (s *Server) CopyFromRunningServer(old *Server) {
|
||||
s.State = old.State
|
||||
s.LastActive = old.LastActive
|
||||
s.TaskClose = old.TaskClose
|
||||
s.TaskCloseLock = old.TaskCloseLock
|
||||
s.TaskStream = old.TaskStream
|
||||
s.PrevHourlyTransferIn = old.PrevHourlyTransferIn
|
||||
s.PrevHourlyTransferOut = old.PrevHourlyTransferOut
|
||||
s.PrevTransferInSnapshot = old.PrevTransferInSnapshot
|
||||
s.PrevTransferOutSnapshot = old.PrevTransferOutSnapshot
|
||||
}
|
||||
|
||||
func boolToString(b bool) string {
|
||||
|
||||
@@ -26,6 +26,8 @@ func CommonEnvironment(c *gin.Context, data map[string]interface{}) gin.H {
|
||||
data["Version"] = singleton.Version
|
||||
data["Conf"] = singleton.Conf
|
||||
data["Themes"] = model.Themes
|
||||
data["CustomCode"] = singleton.Conf.Site.CustomCode
|
||||
data["CustomCodeDashboard"] = singleton.Conf.Site.CustomCodeDashboard
|
||||
// 是否是管理页面
|
||||
data["IsAdminPage"] = adminPage[data["MatchedPath"].(string)]
|
||||
// 站点标题
|
||||
|
||||
@@ -37,8 +37,7 @@ func ValidateViewPassword(opt ValidateViewPasswordOption) gin.HandlerFunc {
|
||||
}
|
||||
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,
|
||||
"Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "VerifyPassword"}),
|
||||
}))
|
||||
|
||||
} else {
|
||||
|
||||
@@ -76,3 +76,13 @@ func GenerateRandomString(n int) (string, error) {
|
||||
}
|
||||
return string(ret), nil
|
||||
}
|
||||
|
||||
func Uint64SubInt64(a uint64, b int64) uint64 {
|
||||
if b < 0 {
|
||||
return a + uint64(-b)
|
||||
}
|
||||
if a < uint64(b) {
|
||||
return 0
|
||||
}
|
||||
return a - uint64(b)
|
||||
}
|
||||
|
||||
Vendored
+7
-1
@@ -331,6 +331,9 @@ other = "Frontend Theme"
|
||||
[CustomCodes]
|
||||
other = "Custom Codes (Style and Script)"
|
||||
|
||||
[CustomCodesDashboard]
|
||||
other = "Custom Codes for Dashboard"
|
||||
|
||||
[AccessPassword]
|
||||
other = "Frontend Access Password"
|
||||
|
||||
@@ -650,4 +653,7 @@ other = "Disable Switch Template in Frontend"
|
||||
other = "Servers On World Map"
|
||||
|
||||
[NAT]
|
||||
other = "NAT"
|
||||
other = "NAT"
|
||||
|
||||
[NetworkSpiterList]
|
||||
other = "Network Monitor"
|
||||
Vendored
+7
-1
@@ -331,6 +331,9 @@ other = "Tema de Frontend"
|
||||
[CustomCodes]
|
||||
other = "Códigos Personalizados (Incluye style y script)"
|
||||
|
||||
[CustomCodesDashboard]
|
||||
other = "Custom Codes for Dashboard"
|
||||
|
||||
[AccessPassword]
|
||||
other = "Contraseña de Acceso al Frontend"
|
||||
|
||||
@@ -650,4 +653,7 @@ other = "Deshabilitar Cambio de Plantilla en Frontend"
|
||||
other = "Servidores en el mapa mundial"
|
||||
|
||||
[NAT]
|
||||
other = "NAT"
|
||||
other = "NAT"
|
||||
|
||||
[NetworkSpiterList]
|
||||
other = "Red Monitor"
|
||||
Vendored
+7
-1
@@ -331,6 +331,9 @@ other = "前台界面主题"
|
||||
[CustomCodes]
|
||||
other = "自定义代码(包括 style 和 script)"
|
||||
|
||||
[CustomCodesDashboard]
|
||||
other = "Custom Codes for Dashboard"
|
||||
|
||||
[AccessPassword]
|
||||
other = "前台访问密码"
|
||||
|
||||
@@ -650,4 +653,7 @@ other = "禁止前台切换模板"
|
||||
other = "服务器世界分布图"
|
||||
|
||||
[NAT]
|
||||
other = "内网穿透"
|
||||
other = "内网穿透"
|
||||
|
||||
[NetworkSpiterList]
|
||||
other = "网络监控"
|
||||
|
||||
Vendored
+7
-1
@@ -331,6 +331,9 @@ other = "前台界面主題"
|
||||
[CustomCodes]
|
||||
other = "自定義代碼(包括 style 和 script)"
|
||||
|
||||
[CustomCodesDashboard]
|
||||
other = "Custom Codes for Dashboard"
|
||||
|
||||
[AccessPassword]
|
||||
other = "前台訪問密碼"
|
||||
|
||||
@@ -650,4 +653,7 @@ other = "禁止前台切換主題"
|
||||
other = "伺服器世界分布圖"
|
||||
|
||||
[NAT]
|
||||
other = "NAT"
|
||||
other = "NAT"
|
||||
|
||||
[NetworkSpiterList]
|
||||
other = "網絡監控"
|
||||
@@ -0,0 +1,141 @@
|
||||
html[nz-theme='dark'] body {
|
||||
background-color: #121212 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .ui.menu {
|
||||
background-color: #282828 !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .ui.menu * {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .accordion {
|
||||
background-color: #282828 !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .accordion .title {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .ui.card {
|
||||
background-color: #3f3f3f !important;
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .header {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .description {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .icon {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .ui.popup {
|
||||
background-color: #575757 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .ui.table {
|
||||
background-color: #282828 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .ui thead th {
|
||||
background-color: #3f3f3f !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .ui.buttons .button {
|
||||
background-color: #3f3f3f !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .ui.modal {
|
||||
background-color: #282828 !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .ui.modal * {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] textarea,
|
||||
html[nz-theme='dark'] input,
|
||||
html[nz-theme='dark'] select,
|
||||
html[nz-theme='dark'] .dropdown {
|
||||
background-color: #3f3f3f !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .ui.message {
|
||||
background-color: unset !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .ui.dropdown .menu {
|
||||
background-color: #575757 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .ui.modal>.header {
|
||||
background-color: #3f3f3f !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .ui.modal>.content {
|
||||
background-color: #282828 !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .ui.modal>.actions {
|
||||
background-color: #3f3f3f !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] #alert {
|
||||
background-color: #3f3f3f !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .ui.form .field>label {
|
||||
color: unset !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .ui.segment {
|
||||
background-color: #3f3f3f !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .ui.segment textarea,
|
||||
html[nz-theme='dark'] input,
|
||||
html[nz-theme='dark'] select,
|
||||
html[nz-theme='dark'] .dropdown {
|
||||
background-color: #575757 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] form label {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .ui.inverted.segment {
|
||||
background-color: #121212 !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .ui.inverted.segment * {
|
||||
color: #8b8b8b !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .menu .dropdown {
|
||||
background-color: #282828 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .ui.menu .ui.dropdown .menu>.item {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .login .ui.message{
|
||||
color: #8b8b8b !important;
|
||||
}
|
||||
@@ -9,6 +9,11 @@ td {
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.ui.container{
|
||||
width: 95vw !important;
|
||||
max-width: 1680px !important;
|
||||
}
|
||||
|
||||
.nb-container {
|
||||
padding-top: 75px;
|
||||
min-height: 100vh;
|
||||
|
||||
+55
-60
@@ -1,46 +1,35 @@
|
||||
/* 屏幕适配 */
|
||||
@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.card>.content>.header:not(.ui),
|
||||
.ui.cards>.card>.content>.header:not(.ui) {
|
||||
margin-top: 0.4em !important;
|
||||
}
|
||||
.ui.menu .item>img:not(.ui){
|
||||
|
||||
.ui.menu .item>img:not(.ui) {
|
||||
width: 2.2rem;
|
||||
}
|
||||
.ui.menu .item:before{
|
||||
width:0.5px;
|
||||
|
||||
.ui.menu .item:before {
|
||||
width: 0.5px;
|
||||
}
|
||||
.ui.menu .item{
|
||||
|
||||
.ui.menu .item {
|
||||
padding: 0.9rem 0.55rem;
|
||||
}
|
||||
.ui.large.menu{
|
||||
|
||||
.ui.large.menu {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
i.icon {
|
||||
color:#000;
|
||||
width:1.2em !important;
|
||||
}
|
||||
i.fi {
|
||||
width:0.9em;
|
||||
margin:0px 6px 0px 2px;
|
||||
color: #000;
|
||||
width: 1.2em !important;
|
||||
}
|
||||
|
||||
body {
|
||||
content:" " !important;
|
||||
background:fixed !important;
|
||||
z-index:-1 !important;
|
||||
top:0 !important;
|
||||
right:0 !important;
|
||||
bottom:0 !important;
|
||||
left:0 !important;
|
||||
i.fi {
|
||||
width: 0.9em;
|
||||
margin: 0px 6px 0px 2px;
|
||||
}
|
||||
|
||||
td {
|
||||
@@ -55,6 +44,11 @@ td {
|
||||
margin-bottom: -47px;
|
||||
}
|
||||
|
||||
.ui.container {
|
||||
width: 95%;
|
||||
max-width: 1680px !important;
|
||||
}
|
||||
|
||||
#app .ui.fluid.accordion {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
@@ -65,79 +59,80 @@ td {
|
||||
padding-top: unset;
|
||||
}
|
||||
|
||||
.login.nb-container > .grid {
|
||||
.login.nb-container>.grid {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.login.nb-container > .grid .column {
|
||||
.login.nb-container>.grid .column {
|
||||
max-width: 450px;
|
||||
}
|
||||
|
||||
.ui.menu .item-right:before{
|
||||
width:0px;
|
||||
.ui.menu .item-right:before {
|
||||
width: 0px;
|
||||
}
|
||||
|
||||
.status.cards .flag {
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
.status.cards .header > .info.icon {
|
||||
.status.cards .header>.info.icon {
|
||||
float: right;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.ui.grid {
|
||||
margin-bottom:-0.5em
|
||||
margin-bottom: -0.5em
|
||||
}
|
||||
|
||||
.ui.card>.content>.header:not(.ui), .ui.cards>.card>.content>.header:not(.ui){
|
||||
.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;
|
||||
height: 2.3rem !important;
|
||||
}
|
||||
|
||||
.status.cards .wide.column:nth-child(1) {
|
||||
margin-top:1.2rem !important;
|
||||
margin-top: 1.2rem !important;
|
||||
}
|
||||
|
||||
.status.cards .wide.column:nth-child(2) {
|
||||
margin-top:1.2rem !important;
|
||||
margin-top: 1.2rem !important;
|
||||
}
|
||||
|
||||
.status.cards .three.wide.column {
|
||||
text-align: center;
|
||||
width: 22%!important;
|
||||
width: 22% !important;
|
||||
}
|
||||
|
||||
.status.cards .thirteen.wide.column{
|
||||
width: 78%!important;
|
||||
padding-left:0;
|
||||
.status.cards .thirteen.wide.column {
|
||||
width: 78% !important;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.status.cards .description {
|
||||
padding-bottom:0 !important;
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.status.cards .flag {
|
||||
margin-right:0.5rem !important;
|
||||
margin-right: 0.5rem !important;
|
||||
}
|
||||
|
||||
.status.cards .header > .info.icon {
|
||||
.status.cards .header>.info.icon {
|
||||
float: right;
|
||||
margin-right:0 !important;
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
.ui.popup:before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.closePopup{
|
||||
color:rgb(10, 148, 242) !important;
|
||||
.closePopup {
|
||||
color: rgb(10, 148, 242) !important;
|
||||
position: absolute;
|
||||
top: 7px;
|
||||
right: 10px;
|
||||
@@ -150,26 +145,26 @@ td {
|
||||
}
|
||||
|
||||
.ui.content {
|
||||
margin:0 !important;
|
||||
padding:1em !important;
|
||||
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;
|
||||
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;
|
||||
margin-right: 1px !important;
|
||||
}
|
||||
|
||||
.ui.progress .bar {
|
||||
min-width:1.8em !important;
|
||||
border-radius:5px !important;
|
||||
line-height:1.65em !important;
|
||||
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);
|
||||
@@ -182,7 +177,7 @@ td {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.service-status .delay-today > i {
|
||||
.service-status .delay-today>i {
|
||||
display: inline-block;
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
|
||||
@@ -171,4 +171,8 @@ body[theme="dark"] .modal-header i.xclose{
|
||||
body[theme="dark"] .toolbox i{
|
||||
color: rgba(241, 241, 241, 1);
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
body[theme="dark"] .network-box .network-box-header{
|
||||
border-bottom: 1px solid rgba(110, 112, 121, 0.25);
|
||||
}
|
||||
@@ -197,6 +197,10 @@ body[theme="light"] .modal-header i.xclose{
|
||||
color: rgba(0, 0, 0, 0.87);
|
||||
}
|
||||
|
||||
body[theme="light"] .network-box .network-box-header{
|
||||
border-bottom: 1px solid rgba(224, 230, 241, 0.6);
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 767px) {
|
||||
body[theme="light"] .navbar .navbar-nav .open .dropdown-menu {
|
||||
background-color: rgba(249, 249, 249, 1);
|
||||
|
||||
+94
-1
@@ -9,6 +9,10 @@ body {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.container-fluid::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.nezha {
|
||||
min-height: calc(100vh - 100px);
|
||||
}
|
||||
@@ -317,6 +321,78 @@ td.ping-network-quality {
|
||||
}
|
||||
/* 服务页 正文结束 */
|
||||
|
||||
/* 网络页 正文*/
|
||||
.network-box .btn-group.open .dropdown-toggle{
|
||||
box-shadow:unset;
|
||||
}
|
||||
|
||||
.network-box .network-box-header{
|
||||
font-size: 18px;
|
||||
padding: 5px 0px 15px 5px;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.network-box .network-box-header .dropdown-menu {
|
||||
max-height: 22.5em;
|
||||
overflow-y: auto;
|
||||
z-index:99999998;
|
||||
min-width: 200px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.network-box .network-box-header .dropdown-menu::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.network-box .network-box-header .dropdown-menu li{
|
||||
height: 2em;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.network-box .network-box-header .dropdown-menu li.input-group{
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 99999997;
|
||||
}
|
||||
|
||||
.network-box .network-box-header .dropdown-menu li.input-group input{
|
||||
width: 100%;
|
||||
height: 2em;
|
||||
border-top: none;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.network-box .network-box-header .dropdown-menu li a {
|
||||
padding: 5px 5px 5px 15px;
|
||||
}
|
||||
|
||||
.network-box .network-box-header .dropdown-menu li.hidden {
|
||||
height: 0;
|
||||
display: none;
|
||||
visibility:hidden;
|
||||
}
|
||||
|
||||
.network-box .network-box-header .dropdown-menu li.visible {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.network-box .chartTitle {
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
margin: 18px 0px 15px 0px;
|
||||
}
|
||||
|
||||
.network-box .chartTitle i.chartCountryCode{
|
||||
font-size: 16px;
|
||||
border-radius: 12.5%;
|
||||
}
|
||||
|
||||
/* 网络页 正文结束 */
|
||||
|
||||
/* 地图版服务器分布图 */
|
||||
#mapChartBox{
|
||||
z-index: 999999999;
|
||||
@@ -393,13 +469,18 @@ footer p{
|
||||
}
|
||||
}
|
||||
|
||||
/* 彩虹旗 */
|
||||
.fi-rb {
|
||||
background-image: url(/static/theme-server-status/img/rb.png);
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 767px) {
|
||||
body {
|
||||
font-size: 10px !important;
|
||||
padding-top:60px !important;
|
||||
}
|
||||
.nezha {
|
||||
min-height: calc(100vh - 80px);
|
||||
min-height: calc(74.5vh);
|
||||
}
|
||||
.content {
|
||||
padding: 0;
|
||||
@@ -482,6 +563,18 @@ footer p{
|
||||
td.ping-network-quality {
|
||||
width: 110px;
|
||||
}
|
||||
.network-box .network-box-header {
|
||||
margin: 8px 0px 0px 8px;
|
||||
font-size: 16px;
|
||||
}
|
||||
.network-box .chartTitle {
|
||||
font-size: 16px;
|
||||
margin: 10px 0px 10px 0px;
|
||||
}
|
||||
.network-box .chartTitle i.chartCountryCode{
|
||||
font-size: 15px;
|
||||
border-radius: 12.5%;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 768px) {
|
||||
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 5.8 KiB |
+7
-3
@@ -14,7 +14,10 @@ const mixinsVue = {
|
||||
{ key: 'default', name: 'Default', icon: 'th large' },
|
||||
{ key: 'angel-kanade', name: 'AngelKanade', icon: 'square' },
|
||||
{ key: 'server-status', name: 'ServerStatus', icon: 'list' }
|
||||
]
|
||||
],
|
||||
colors: [],
|
||||
colorsDark: ['#4992FF', '#08C091', '#FDDD5F', '#FF6E76', '#58D9F9', '#7CFFB2', '#FF8A44', '#8D48E3', '#DD79FF', '#5470C6', '#3BA272', '#FAC758', '#EE6666', '#72C0DE', '#91CC76', '#FB8352', '#9A60B4', '#EA7BCC'],
|
||||
colorsLight: ['#5470C6', '#3BA272', '#FAC758', '#EE6666', '#72C0DE', '#91CC76', '#FB8352', '#9A60B4', '#EA7BCC', '#4992FF', '#08C091', '#FDDD5F', '#FF6E76', '#58D9F9', '#7CFFB2', '#FF8A44', '#8D48E3', '#DD79FF'],
|
||||
},
|
||||
created() {
|
||||
this.isMobile = this.checkIsMobile();
|
||||
@@ -22,6 +25,7 @@ const mixinsVue = {
|
||||
this.showGroup = this.initShowGroup();
|
||||
this.semiTransparent = this.initSemiTransparent();
|
||||
this.preferredTemplate = this.getCookie('preferred_theme') ? this.getCookie('preferred_theme') : this.$root.defaultTemplate;
|
||||
this.colors = this.theme == "dark" ? this.colorsDark : this.colorsLight;
|
||||
window.addEventListener('scroll', this.handleScroll);
|
||||
},
|
||||
destroyed() {
|
||||
@@ -51,7 +55,7 @@ const mixinsVue = {
|
||||
// 重新赋值全局调色
|
||||
this.colors = this.theme == "dark" ? this.colorsDark : this.colorsLight;
|
||||
|
||||
if(this.$root.page == 'index') {
|
||||
if(this.$root.page == 'index' || this.$root.page == 'network') {
|
||||
this.reloadCharts(); // 重新载入echarts图表
|
||||
}
|
||||
},
|
||||
@@ -81,7 +85,7 @@ const mixinsVue = {
|
||||
toggleSemiTransparent(){
|
||||
this.semiTransparent = !this.semiTransparent;
|
||||
localStorage.setItem("semiTransparent", this.semiTransparent);
|
||||
if(this.$root.page == 'index') {
|
||||
if(this.$root.page == 'index' || this.$root.page == 'network') {
|
||||
this.reloadCharts(); // 重新载入echarts图表
|
||||
}
|
||||
},
|
||||
|
||||
Vendored
+1
@@ -17,5 +17,6 @@
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
{{end}}
|
||||
Vendored
+6
-1
@@ -11,7 +11,12 @@
|
||||
<link rel="stylesheet" type="text/css" href="https://unpkg.com/[email protected]/dist/semantic.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="https://unpkg.com/[email protected]/assets/font-logos.css">
|
||||
<link rel="stylesheet" type="text/css" href="/static/semantic-ui-alerts.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="/static/main.css?v2022042314">
|
||||
<link rel="stylesheet" type="text/css" href="/static/main.css?v20240813">
|
||||
<link rel="stylesheet" type="text/css" href="/static/darkmode.css?v20240813">
|
||||
<script>
|
||||
document.documentElement.setAttribute('nz-theme', window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
|
||||
</script>
|
||||
</head>
|
||||
{{if ts .CustomCodeDashboard}} {{.CustomCodeDashboard|safe}} {{end}}
|
||||
<body>
|
||||
{{end}}
|
||||
Vendored
+1
-1
@@ -10,7 +10,7 @@
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class=" actions">
|
||||
<div class="actions">
|
||||
<div class="ui negative button">{{tr "Cancel"}}</div>
|
||||
<button class="ui positive nezha-primary-btn right labeled icon button">{{tr "Confirm"}}<i class="checkmark icon"></i>
|
||||
</button>
|
||||
|
||||
+1
-1
@@ -60,7 +60,7 @@
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class=" actions">
|
||||
<div class="actions">
|
||||
<div class="ui negative button">{{tr "Cancel"}}</div>
|
||||
<button class="ui positive nezha-primary-btn right labeled icon button">{{tr "Confirm"}}<i class="checkmark icon"></i>
|
||||
</button>
|
||||
|
||||
+1
-1
@@ -71,7 +71,7 @@
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class=" actions">
|
||||
<div class="actions">
|
||||
<div class="ui negative button">{{tr "Cancel"}}</div>
|
||||
<button class="ui positive nezha-primary-btn right labeled icon button">{{tr "Confirm"}}<i class="checkmark icon"></i>
|
||||
</button>
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<table class="ui very basic table">
|
||||
<table class="ui basic table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{tr "Token"}}</th>
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<table class="ui very basic table">
|
||||
<table class="ui basic table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<table class="ui very basic table">
|
||||
<table class="ui basic table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<table class="ui very basic table">
|
||||
<table class="ui basic table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
|
||||
+2
-2
@@ -10,7 +10,7 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<table class="ui very basic table">
|
||||
<table class="ui basic table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
@@ -52,7 +52,7 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<table class="ui very basic table">
|
||||
<table class="ui basic table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<table class="ui very basic table">
|
||||
<table class="ui basic table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><button onclick="checkAllServer()" class="ui mini nezha-primary-btn button">{{tr "SelectAll"}}</button></th>
|
||||
|
||||
+7
-3
@@ -41,6 +41,10 @@
|
||||
<label>{{tr "CustomCodes"}}</label>
|
||||
<textarea name="CustomCode">{{.Conf.Site.CustomCode}}</textarea>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{tr "CustomCodesDashboard"}}</label>
|
||||
<textarea name="CustomCodeDashboard">{{.Conf.Site.CustomCodeDashboard}}</textarea>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{tr "AccessPassword"}}</label>
|
||||
<input type="text" name="ViewPassword" placeholder="" value="{{.Conf.Site.ViewPassword}}">
|
||||
@@ -128,13 +132,13 @@
|
||||
})
|
||||
$('.checkbox').checkbox()
|
||||
$('#settingForm').find("select[name=Cover]").val({{.Conf.Cover }});
|
||||
{{if .Conf.EnableIPChangeNotification}}
|
||||
{{ if .Conf.EnableIPChangeNotification}}
|
||||
$('.checkbox.ip-change').checkbox('set checked')
|
||||
{{ end }}
|
||||
{{if .Conf.EnablePlainIPInNotification}}
|
||||
{{ if .Conf.EnablePlainIPInNotification}}
|
||||
$('.checkbox.plain-ip').checkbox('set checked')
|
||||
{{ end }}
|
||||
{{if .Conf.DisableSwitchTemplateInFrontend }}
|
||||
{{ if .Conf.DisableSwitchTemplateInFrontend }}
|
||||
$('.checkbox.disable-switch-template').checkbox('set checked')
|
||||
{{ end }}
|
||||
</script>
|
||||
|
||||
+11
-3
@@ -1,6 +1,7 @@
|
||||
{{define "dashboard-default/terminal"}}
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{.Conf.Language}}">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
@@ -23,16 +24,19 @@
|
||||
background-color: black;
|
||||
}
|
||||
</style>
|
||||
|
||||
<body onresize="onResize()">
|
||||
<div id="terminal-container"></div>
|
||||
<script src="https://unpkg.com/[email protected]/lib/xterm.js"></script>
|
||||
<script src="https://unpkg.com/@xterm/[email protected]/lib/addon-attach.js"></script>
|
||||
<script src="https://unpkg.com/@xterm/[email protected]/lib/addon-fit.js"></script>
|
||||
<script src="https://unpkg.com/@xterm/[email protected]/lib/addon-web-links.js"></script>
|
||||
<script src="https://unpkg.com/[email protected]/lib/trzsz.js"></script>
|
||||
<script>
|
||||
let sendResizing = false;
|
||||
|
||||
function doResize() {
|
||||
fitAddon.fit()
|
||||
|
||||
const w = fitAddon.proposeDimensions();
|
||||
const prefix = new Int8Array([1]);
|
||||
const resizeMessage = new TextEncoder().encode(JSON.stringify({
|
||||
@@ -70,10 +74,13 @@
|
||||
cursorBlink: true,
|
||||
});
|
||||
const socket = new WebSocket((window.location.protocol == 'https:' ? 'wss' : 'ws') + '://' + window.location.host + '/terminal/' + '{{.SessionID}}');
|
||||
const attachAddon = new AttachAddon.AttachAddon(socket);
|
||||
socket.binaryType = 'arraybuffer';
|
||||
|
||||
const trzszAddon = new TrzszAddon(socket);
|
||||
term.loadAddon(trzszAddon);
|
||||
const fitAddon = new FitAddon.FitAddon();
|
||||
term.loadAddon(attachAddon);
|
||||
term.loadAddon(fitAddon);
|
||||
|
||||
term.open(document.getElementById('terminal-container'));
|
||||
|
||||
socket.onopen = () => {
|
||||
@@ -90,5 +97,6 @@
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
{{end}}
|
||||
+2
-4
@@ -10,14 +10,12 @@
|
||||
{{ if not .Conf.DisableSwitchTemplateInFrontend }}
|
||||
<script>
|
||||
function showSwitchTemplate(list, currentBackendTheme) {
|
||||
console.log(list, currentBackendTheme);
|
||||
console.log("currentBackendTheme:",currentBackendTheme);
|
||||
// console.log(list, currentBackendTheme);
|
||||
// console.log("currentBackendTheme:",currentBackendTheme);
|
||||
}
|
||||
showSwitchTemplate({{ .Themes }}, {{ .Conf.Site.Theme }})
|
||||
</script>
|
||||
{{ end }}
|
||||
<script>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
{{end}}
|
||||
|
||||
+8
-3
@@ -1,6 +1,7 @@
|
||||
{{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">
|
||||
@@ -13,14 +14,18 @@
|
||||
<link rel="stylesheet" href="https://unpkg.com/[email protected]/font/bootstrap-icons.min.css">
|
||||
<link rel="stylesheet" href="https://unpkg.com/[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?v20240616">
|
||||
<link rel="stylesheet" type="text/css" href="/static/theme-default/css/main.css?v20240816">
|
||||
<link rel="stylesheet" type="text/css" href="/static/darkmode.css?v20240816">
|
||||
<script src="https://unpkg.com/[email protected]/dist/jquery.min.js"></script>
|
||||
<script src="https://unpkg.com/[email protected]/dist/semantic.min.js"></script>
|
||||
<script src="https://unpkg.com/[email protected]/dist/vue.min.js"></script>
|
||||
<script src="https://unpkg.com/[email protected]/dist/echarts.min.js"></script>
|
||||
<script src="/static/semantic-ui-alerts.min.js"></script>
|
||||
<script src="/static/theme-default/js/mixin.js?v20240302"></script>
|
||||
<script>
|
||||
document.documentElement.setAttribute('nz-theme', window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{{end}}
|
||||
|
||||
{{end}}
|
||||
+2
-5
@@ -240,12 +240,12 @@
|
||||
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, {
|
||||
const chartTheme = $('html').attr('nz-theme') == "dark" ? "dark" : "";
|
||||
const chart = echarts.init(chartContainer, chartTheme, {
|
||||
renderer: 'canvas',
|
||||
useDirtyRect: false,
|
||||
width: 'auto',
|
||||
@@ -283,7 +283,6 @@
|
||||
trigger: 'axis',
|
||||
textStyle: {
|
||||
fontSize: fontSize,
|
||||
color: fontColor
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
@@ -292,7 +291,6 @@
|
||||
show: true,
|
||||
textStyle: {
|
||||
fontSize: fontSize,
|
||||
color: fontColor
|
||||
},
|
||||
lineStyle: {
|
||||
cap: 'butt'
|
||||
@@ -332,7 +330,6 @@
|
||||
series: seriesData,
|
||||
textStyle: {
|
||||
fontSize: fontSize,
|
||||
color: fontColor
|
||||
},
|
||||
grid: {
|
||||
top: '30',
|
||||
|
||||
+6
-5
@@ -18,7 +18,7 @@
|
||||
</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 ref="chartDom" style="margin-top: 15px;height: 520px;overflow: hidden"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -65,10 +65,10 @@
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '8%',
|
||||
right: '8%',
|
||||
left: this.isMobile ? '8%' : '3.8%',
|
||||
right: this.isMobile ? '8%' : '3.8%',
|
||||
},
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.8)',
|
||||
backgroundColor: '',
|
||||
toolbox: {
|
||||
feature: {
|
||||
dataZoom: {
|
||||
@@ -243,7 +243,8 @@
|
||||
return str.includes('Windows')
|
||||
},
|
||||
renderChart() {
|
||||
this.myChart = echarts.init(this.$refs.chartDom);
|
||||
const chartTheme = $('html').attr('nz-theme') == "dark" ? "dark" : "";
|
||||
this.myChart = echarts.init(this.$refs.chartDom,chartTheme);
|
||||
this.myChart.setOption(this.option);
|
||||
},
|
||||
resizeHandle () {
|
||||
|
||||
+32
-32
@@ -1,36 +1,36 @@
|
||||
{{define "theme-server-status/footer"}}
|
||||
</div>
|
||||
<footer class="container-fluid">
|
||||
<p>{{ .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">
|
||||
<template v-if="showTools">
|
||||
<span v-if="page=='index' && countryMapChartData.length!=0" class="showMapChart">
|
||||
<i @click="showMapChart" data-toggle="modal" data-target="#mapChartBox" class="bi bi-geo-alt"></i>
|
||||
</span>
|
||||
<span class="toggleView">
|
||||
<i v-if="showGroup" @click="toggleShowGroup" class="show-nogroup bi bi-justify"></i>
|
||||
<i v-else @click="toggleShowGroup" class="show-group bi bi-view-stacked"></i>
|
||||
</span>
|
||||
<span class="toggleSemiTransparent" @click="toggleSemiTransparent">
|
||||
<i class="bi" :class="semiTransparent ? 'bi-droplet' : 'bi-droplet-half'"></i>
|
||||
</span>
|
||||
</template>
|
||||
<span v-if="!showTools">
|
||||
<i @click="toggleShowTools" class="bi bi-three-dots"></i>
|
||||
</span>
|
||||
<span class="setTheme">
|
||||
<i v-if="theme === 'light'" @click="setTheme('dark')" class="setTheme-dark bi bi-moon-fill"></i>
|
||||
<i v-else @click="setTheme('light')" 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>
|
||||
<template v-if="semiTransparent">
|
||||
<link rel="stylesheet" href="/static/theme-server-status/css/light.plus.css?v20240807">
|
||||
<link rel="stylesheet" href="/static/theme-server-status/css/dark.plus.css?v20240807">
|
||||
</template>
|
||||
</div>
|
||||
<footer class="container-fluid">
|
||||
<p>{{ .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">
|
||||
<template v-if="showTools">
|
||||
<span v-if="page=='index' && countryMapChartData.length!=0" class="showMapChart">
|
||||
<i @click="showMapChart" data-toggle="modal" data-target="#mapChartBox" class="bi bi-geo-alt"></i>
|
||||
</span>
|
||||
<span class="toggleView">
|
||||
<i v-if="showGroup" @click="toggleShowGroup" class="show-nogroup bi bi-justify"></i>
|
||||
<i v-else @click="toggleShowGroup" class="show-group bi bi-view-stacked"></i>
|
||||
</span>
|
||||
<span class="toggleSemiTransparent" @click="toggleSemiTransparent">
|
||||
<i class="bi" :class="semiTransparent ? 'bi-droplet' : 'bi-droplet-half'"></i>
|
||||
</span>
|
||||
</template>
|
||||
<span v-if="!showTools">
|
||||
<i @click="toggleShowTools" class="bi bi-three-dots"></i>
|
||||
</span>
|
||||
<span class="setTheme">
|
||||
<i v-if="theme === 'light'" @click="setTheme('dark')" class="setTheme-dark bi bi-moon-fill"></i>
|
||||
<i v-else @click="setTheme('light')" 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>
|
||||
<template v-if="semiTransparent">
|
||||
<link rel="stylesheet" href="/static/theme-server-status/css/light.plus.css?v20240807">
|
||||
<link rel="stylesheet" href="/static/theme-server-status/css/dark.plus.css?v20240807">
|
||||
</template>
|
||||
</div>
|
||||
{{if ts .CustomCode}}{{.CustomCode|safe}}{{end}}
|
||||
</body>
|
||||
|
||||
+4
-4
@@ -16,10 +16,10 @@
|
||||
<script src="https://unpkg.com/[email protected]/dist/js/bootstrap.min.js"></script>
|
||||
<script src="https://unpkg.com/[email protected]/dist/vue.min.js"></script>
|
||||
<script src="https://unpkg.com/[email protected]/dist/echarts.min.js"></script>
|
||||
<link rel="stylesheet" href="/static/theme-server-status/css/main.css?v20240807">
|
||||
<link rel="stylesheet" href="/static/theme-server-status/css/dark.css?v20240807">
|
||||
<link rel="stylesheet" href="/static/theme-server-status/css/light.css?v20240807">
|
||||
<script src="/static/theme-server-status/js/mixin.js?v20240807"></script>
|
||||
<link rel="stylesheet" href="/static/theme-server-status/css/main.css?v202408016">
|
||||
<link rel="stylesheet" href="/static/theme-server-status/css/dark.css?v202408011">
|
||||
<link rel="stylesheet" href="/static/theme-server-status/css/light.css?v20240811">
|
||||
<script src="/static/theme-server-status/js/mixin.js?v20240811"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
|
||||
+17
-15
@@ -155,14 +155,13 @@
|
||||
return;
|
||||
}
|
||||
const unit = this.language=='zh-CN' ? '台' : 'servers';
|
||||
const isMobile = this.checkIsMobile();
|
||||
const width = window.innerWidth;
|
||||
const height = 0.95 * window.innerHeight;
|
||||
const backgroundColor = this.theme == "dark" ? '' : '';
|
||||
const inRangeColor = this.theme == "dark" ? '#D2B206' : '#FFDF32';
|
||||
const tooltipBackgroundColor = this.theme == "dark" ? "#ffffff" : '#ffffff';
|
||||
const tooltipBorderColor = this.theme == "dark" ? "#ffffff" : "#ffffff";
|
||||
const fontSize = isMobile ? 10 : 12;
|
||||
const fontSize = this.isMobile ? 10 : 12;
|
||||
const fontColor = this.theme == "dark" ? "#000000" : "#000000";
|
||||
const chartContainer = document.getElementById('mapChart');
|
||||
const mapChart = echarts.init(chartContainer, '', { // init图表
|
||||
@@ -510,13 +509,12 @@
|
||||
}
|
||||
// 定义图表参数值
|
||||
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 fontSize = this.isMobile ? 10 : 14;
|
||||
const gridLeft = this.isMobile ? 25 : 36;
|
||||
const gridRight = this.isMobile ? 5 : 20;
|
||||
const legendLeft = this.isMobile ? 'center' : 'center';
|
||||
const legendTop = this.isMobile ? 5 : 5;
|
||||
const legendPadding= this.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";
|
||||
@@ -524,8 +522,8 @@
|
||||
const backgroundColor = theme == "dark" ? '' : '';
|
||||
const tooltipBackgroundColor = this.theme == "dark" ? (this.semiTransparent ? "rgba(28,29,38,0.85)" : "rgba(28,29,38,1)") : (this.semiTransparent ? "rgba(255,255,255,0.85)" : "rgba(255,255,255,1)");
|
||||
const tooltipBorderColor = this.theme == "dark" ? (this.semiTransparent ? "rgba(28,29,38,0.9)" : "rgba(28,29,38,1)") : (this.semiTransparent ? "rgba(255,255,255,0.9)" : "rgba(255,255,255,1)");
|
||||
const lineStyleWidth = isMobile ? 1 : 2;
|
||||
const splitLineWidth = isMobile ? 0.5 : 1;
|
||||
const lineStyleWidth = this.isMobile ? 1 : 2;
|
||||
const splitLineWidth = this.isMobile ? 0.5 : 1;
|
||||
// 渲染图表
|
||||
const chart = echarts.init(chartContainer, chartTheme, {
|
||||
renderer: 'canvas',
|
||||
@@ -553,6 +551,10 @@
|
||||
symbol: 'none',
|
||||
data: data,
|
||||
connectNulls: true,
|
||||
legendHoverLink: false,
|
||||
emphasis: {
|
||||
disabled: true
|
||||
},
|
||||
lineStyle: {
|
||||
width: lineStyleWidth
|
||||
}
|
||||
@@ -562,12 +564,12 @@
|
||||
const legendData = chartData.map(item => item.monitor_name);
|
||||
const maxLegendsPerRowMobile = localStorage.getItem("maxLegendsPerRowMobile") ? localStorage.getItem("maxLegendsPerRowMobile") : 3;
|
||||
const maxLegendsPerRowPc = localStorage.getItem("maxLegendsPerRowPc") ? localStorage.getItem("maxLegendsPerRowPc") : 6;
|
||||
const autoIncrement = Math.floor((legendData.length - 1) / (isMobile ? maxLegendsPerRowMobile : maxLegendsPerRowPc)) * (isMobile ? 20 : 28)
|
||||
const autoIncrement = Math.floor((legendData.length - 1) / (this.isMobile ? maxLegendsPerRowMobile : maxLegendsPerRowPc)) * (this.isMobile ? 20 : 28);
|
||||
const height = 300 + autoIncrement;
|
||||
const gridTop = 40 + autoIncrement;
|
||||
const legendIcon = isMobile ? 'rect' : "";
|
||||
const itemWidth = isMobile ? 10 : 25;
|
||||
const itemHeight = isMobile ? 10 : 14;
|
||||
const legendIcon = this.isMobile ? 'rect' : "";
|
||||
const itemWidth = this.isMobile ? 10 : 25;
|
||||
const itemHeight = this.isMobile ? 10 : 14;
|
||||
chart.resize({
|
||||
width: 'auto',
|
||||
height: height
|
||||
|
||||
+326
-205
@@ -1,239 +1,360 @@
|
||||
{{define "theme-server-status/network"}}
|
||||
{{template "theme-server-status/header" .}}
|
||||
{{template "theme-server-status/menu" .}}
|
||||
<div class="container-fluid table-responsive content">
|
||||
<table class="table table-striped table-condensed table-hover">
|
||||
<button class="ui nezha-primary-btn button"
|
||||
v-for="server in servers"
|
||||
style="margin-top: 3px"
|
||||
@click="redirectNetwork(server.ID)">
|
||||
@#server.Name#@ <i :class="'fi fi-' + server.Host.CountryCode"></i><span class="node-cell-location-text text-uppercase"> @#server.Host.CountryCode#@</span>
|
||||
</button>
|
||||
</table>
|
||||
</div>
|
||||
<div class="container-fluid table-responsive content">
|
||||
<div ref="chartDom" style="border-radius: 28px; margin-top: 15px;height: 520px;max-width: 1400px;overflow: hidden"></div>
|
||||
<div class="container-fluid content network-box">
|
||||
<div class="network-box-header btn-group">
|
||||
<div class="dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<i class="bi bi-list-ul"></i> {{tr "NetworkSpiterList"}} <i class="bi bi-chevron-compact-down"></i>
|
||||
</div>
|
||||
<ul class="dropdown-menu">
|
||||
<li class="input-group fixed-top">
|
||||
<input type="text" id="dropdown-search" class="form-control" placeholder="Search...">
|
||||
</li>
|
||||
<li class="dropdown-item" v-for="server in servers" @click="showCharts(server.ID)">
|
||||
<a><i :class="'fi fi-' + (server.Host.CountryCode || 'rb')"></i> @#server.Name#@ <i v-if="server.ID == currentServerId" class="check icon"></i></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="chartTitle"><i class="chartCountryCode" :class="'fi fi-' + chartCountryCode"></i> @#chartTitle#@</div>
|
||||
<div id="chartbox" style="width:100%;height:auto;"></div>
|
||||
</div>
|
||||
{{template "theme-server-status/footer" .}}
|
||||
<script>
|
||||
const monitorInfo = JSON.parse('{{.MonitorInfos}}');
|
||||
const initData = JSON.parse('{{.Servers}}').servers;
|
||||
let MaxTCPPingValue = {{.Conf.MaxTCPPingValue}};
|
||||
new Vue({
|
||||
el: '#app',
|
||||
delimiters: ['@#', '#@'],
|
||||
data: {
|
||||
page: 'network',
|
||||
defaultTemplate: {{.Conf.Site.Theme}},
|
||||
templates: {{.Themes}},
|
||||
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
|
||||
}
|
||||
},
|
||||
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,
|
||||
defaultTemplate: "{{.Conf.Site.Theme}}",
|
||||
templates: "{{.Themes }}",
|
||||
servers: [],
|
||||
chartDataList: [],
|
||||
chartTitle: '',
|
||||
chartCountryCode: '',
|
||||
chart: null,
|
||||
currentServerId: ''
|
||||
},
|
||||
mixins: [mixinsVue],
|
||||
created() {
|
||||
this.servers = JSON.parse('{{.Servers}}').servers;
|
||||
this.showCharts(this.servers[0].ID);
|
||||
},
|
||||
mounted() {
|
||||
this.renderChart();
|
||||
this.parseMonitorInfo(monitorInfo);
|
||||
this.initSearch();
|
||||
},
|
||||
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 = [];
|
||||
var lcolors = ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'];
|
||||
for (let i = 0; i < monitorInfo.result.length; i++) {
|
||||
var lcolor = lcolors[i % lcolors.length];
|
||||
var rgbaColorMarker = 'rgba(' + parseInt(lcolor.slice(1, 3), 16) + ',' + parseInt(lcolor.slice(3, 5), 16) + ',' + parseInt(lcolor.slice(5, 7), 16) + ',0.5)';
|
||||
var rgbaColorBar = 'rgba(' + parseInt(lcolor.slice(1, 3), 16) + ',' + parseInt(lcolor.slice(3, 5), 16) + ',' + parseInt(lcolor.slice(5, 7), 16) + ',0.35)';
|
||||
let loss = 0;
|
||||
let data = [];
|
||||
let datal = [];
|
||||
for (let j = 0; j < monitorInfo.result[i].created_at.length; j++) {
|
||||
avgDelay = Math.round(monitorInfo.result[i].avg_delay[j]);
|
||||
if (avgDelay > 0 && avgDelay < MaxTCPPingValue) {
|
||||
data.push([monitorInfo.result[i].created_at[j], avgDelay]);
|
||||
}
|
||||
else {
|
||||
loss += 1;
|
||||
datal.push({
|
||||
xAxis: monitorInfo.result[i].created_at[j],
|
||||
label: { show: false },
|
||||
emphasis: { disabled: true },
|
||||
lineStyle: {
|
||||
type: "solid",
|
||||
color: rgbaColorBar
|
||||
}
|
||||
showCharts(id) {
|
||||
// 发起数据请求
|
||||
const url = `/api/v1/monitor/${id}`;
|
||||
fetch(url)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.result) { // 数据请求成功,更新数据并渲染图表
|
||||
this.chartDataList[id] = data.result;
|
||||
this.$nextTick(() => {
|
||||
this.renderCharts(id);
|
||||
});
|
||||
} else {
|
||||
console.log('this server (id:'+ id + ') has no monitor.');
|
||||
}
|
||||
}
|
||||
lossRate = ((loss / monitorInfo.result[i].created_at.length) * 100).toFixed(1);
|
||||
if (lossRate > 99) {
|
||||
datal = [];
|
||||
}
|
||||
legendName = monitorInfo.result[i].monitor_name +" "+ lossRate + "%";
|
||||
tLegendData.push(legendName);
|
||||
tSeries.push({
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching data:', error);
|
||||
});
|
||||
},
|
||||
renderCharts(id, reload = false) {
|
||||
if(!this.chartDataList[id]) return;
|
||||
if(this.chart) this.disposeCharts(this.chart);
|
||||
this.currentServerId = id;
|
||||
this.chartCountryCode = this.getServerCountryCode(id);
|
||||
this.chartTitle = this.chartDataList[id][0].server_name;
|
||||
const chartData = this.chartDataList[id];
|
||||
const chartContainer = document.getElementById('chartbox');
|
||||
// 定义图表参数值
|
||||
const MaxTCPPingValue = {{.Conf.MaxTCPPingValue}} ? {{.Conf.MaxTCPPingValue}} : 300;
|
||||
const autoheight = this.isMobile ? (window.innerHeight - 200) : (window.innerHeight - 250);
|
||||
const fontSize = this.isMobile ? 10 : 14;
|
||||
const gridLeft = this.isMobile ? 30 : 36;
|
||||
const gridRight = this.isMobile ? 12 : 20;
|
||||
const legendLeft = this.isMobile ? 'center' : 'center';
|
||||
const legendTop = this.isMobile ? 5 : 5;
|
||||
const legendPadding= this.isMobile ? [5,0,5,0] : [5,0,5,0];
|
||||
const fontColor = this.theme == "dark" ? "#f1f1f1" : "#000000";
|
||||
const backgroundColor = this.theme == "dark" ? '' : '';
|
||||
const tooltipBackgroundColor = this.theme == "dark" ? (this.semiTransparent ? "rgba(28,29,38,0.85)" : "rgba(28,29,38,1)") : (this.semiTransparent ? "rgba(255,255,255,0.85)" : "rgba(255,255,255,1)");
|
||||
const tooltipBorderColor = this.theme == "dark" ? (this.semiTransparent ? "rgba(28,29,38,0.9)" : "rgba(28,29,38,1)") : (this.semiTransparent ? "rgba(255,255,255,0.9)" : "rgba(255,255,255,1)");
|
||||
const lineStyleWidth = this.isMobile ? 1 : 2;
|
||||
const splitLineWidth = this.isMobile ? 0.5 : 1;
|
||||
const splitLineColor = this.theme == "dark" ? "rgba(110, 112, 121, 0.95)" : "rgba(224, 230, 241, 0.95)";
|
||||
const markPointLabelColor = this.theme == "dark" ? "#111111" : "#000000";
|
||||
const markPointItemStyleOpacity = this.semiTransparent ? 1 : 0.75;
|
||||
const markLineItemStyleOpacity = this.semiTransparent ? 1 : 0.75;
|
||||
const markLineLineStyleWidth = this.isMobile ? 0.15 : 0.3;
|
||||
this.chart = echarts.init(chartContainer, '', { // init图表
|
||||
renderer: 'canvas',
|
||||
useDirtyRect: false,
|
||||
width: 'auto',
|
||||
height: autoheight,
|
||||
});
|
||||
// 获取图表数据
|
||||
let legendData = [];
|
||||
let seriesData = [];
|
||||
chartData.forEach((item,key)=> {
|
||||
let loss = 0;
|
||||
let totalLossRate = 0;
|
||||
let legendName = '';
|
||||
let data = { main: [], markLine: []};
|
||||
item.avg_delay.forEach((avgDelay, index) => {
|
||||
const threshold = 0.9 * MaxTCPPingValue; // 定义阀值,用于判断是否丢包
|
||||
// 定义丢包 1. avgDelay==0 2. avgDelay>=MaxTCPPingValue 3. avgDelay>=threshold
|
||||
if(avgDelay == 0 || avgDelay >= MaxTCPPingValue){ //绝对丢包
|
||||
loss += 1;
|
||||
const lossrate = 100 * loss / (index + 1);
|
||||
if(lossrate != 100) {
|
||||
data['main'].push(
|
||||
[item.created_at[index], MaxTCPPingValue, lossrate]
|
||||
);
|
||||
data['markLine'].push({
|
||||
xAxis: item.created_at[index],
|
||||
label: { show: false },
|
||||
emphasis: { disabled: true },
|
||||
lineStyle: { type: "solid" }
|
||||
});
|
||||
}
|
||||
} else if (avgDelay >= threshold && avgDelay < MaxTCPPingValue){ // 相对丢包
|
||||
loss += 1;
|
||||
const lossrate = 100 * loss / (index + 1);
|
||||
if(lossrate != 100) {
|
||||
data['main'].push(
|
||||
[item.created_at[index], avgDelay, lossrate]
|
||||
);
|
||||
data['markLine'].push({
|
||||
xAxis: item.created_at[index],
|
||||
label: { show: false },
|
||||
emphasis: { disabled: true },
|
||||
lineStyle: { type: "solid" }
|
||||
});
|
||||
}
|
||||
} else { // 未丢包
|
||||
const lossrate = 100 * loss / (index + 1);
|
||||
data['main'].push(
|
||||
[item.created_at[index], avgDelay, lossrate]
|
||||
);
|
||||
}
|
||||
});
|
||||
// 处理legendData
|
||||
totalLossRate = ((loss / item.created_at.length) * 100).toFixed(1);
|
||||
legendName = `${item.monitor_name} ${totalLossRate}%`;
|
||||
legendData.push(legendName);
|
||||
// 处理seriesData
|
||||
seriesData.push(
|
||||
{
|
||||
name: legendName,
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'none',
|
||||
data: data,
|
||||
connectNulls: true,
|
||||
legendHoverLink: false,
|
||||
emphasis: {
|
||||
disabled: true
|
||||
},
|
||||
lineStyle: {
|
||||
width: lineStyleWidth
|
||||
},
|
||||
data: data['main'],
|
||||
markLine: {
|
||||
symbol: "none",
|
||||
symbolSize :0,
|
||||
data: datal
|
||||
data: data['markLine'],
|
||||
itemStyle: {
|
||||
opacity: markLineItemStyleOpacity
|
||||
},
|
||||
lineStyle:{
|
||||
width: markLineLineStyleWidth
|
||||
}
|
||||
},
|
||||
markPoint: {
|
||||
data: [
|
||||
{ type: 'max', symbol: 'pin', name: 'Max', itemStyle: { color: rgbaColorMarker }, symbolSize: 30, label: { fontSize: 8 } },
|
||||
{ type: 'min', symbol: 'pin', name: 'Min', itemStyle: { color: rgbaColorMarker }, symbolSize: 30, label: { fontSize: 8, offset: [0, 7.5] }, symbolRotate: 180 }
|
||||
{
|
||||
name: 'Max',
|
||||
type: 'max',
|
||||
symbol: 'pin',
|
||||
itemStyle: {
|
||||
opacity: markPointItemStyleOpacity
|
||||
},
|
||||
symbolSize: 30,
|
||||
label: {
|
||||
fontSize: 8,
|
||||
color: markPointLabelColor,
|
||||
formatter: function (params) {
|
||||
return Math.round(params.value);
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Min',
|
||||
type: 'min',
|
||||
symbol: 'pin',
|
||||
itemStyle: {
|
||||
opacity: markPointItemStyleOpacity
|
||||
},
|
||||
symbolSize: 30,
|
||||
label: {
|
||||
fontSize: 8,
|
||||
color: markPointLabelColor,
|
||||
offset: [0, 8],
|
||||
formatter: function (params) {
|
||||
return Math.round(params.value);
|
||||
}
|
||||
},
|
||||
symbolRotate: 180
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
const maxLegendsPerRowMobile = localStorage.getItem("maxLegendsPerRowMobile") ? localStorage.getItem("maxLegendsPerRowMobile") : 3;
|
||||
const maxLegendsPerRowPc = localStorage.getItem("maxLegendsPerRowPc") ? localStorage.getItem("maxLegendsPerRowPc") : 6;
|
||||
const autoIncrement = Math.floor((legendData.length - 1) / (this.isMobile ? maxLegendsPerRowMobile : maxLegendsPerRowPc)) * (this.isMobile ? 20 : 28);
|
||||
const height = autoheight + autoIncrement;
|
||||
const gridTop = 40 + autoIncrement;
|
||||
const legendIcon = this.isMobile ? 'rect' : "";
|
||||
const itemWidth = this.isMobile ? 10 : 25;
|
||||
const itemHeight = this.isMobile ? 10 : 14;
|
||||
this.chart.resize({
|
||||
width: 'auto',
|
||||
height: height
|
||||
});
|
||||
// 设置图表配置项
|
||||
const option = {
|
||||
// 全局调色盘
|
||||
color: this.colors,
|
||||
// 背景颜色
|
||||
backgroundColor: backgroundColor,
|
||||
// 文字样式
|
||||
textStyle: {
|
||||
fontSize: fontSize,
|
||||
color: fontColor
|
||||
},
|
||||
// 图表网格设置
|
||||
grid: {
|
||||
top: gridTop,
|
||||
left: gridLeft,
|
||||
right: gridRight
|
||||
},
|
||||
// 图表标题设置
|
||||
title: {
|
||||
show: false,
|
||||
},
|
||||
// 图表系列数据设置
|
||||
series: seriesData.flat(),
|
||||
// X轴设置
|
||||
xAxis: {
|
||||
type: 'time',
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
fontSize: fontSize
|
||||
}
|
||||
}
|
||||
},
|
||||
// Y轴设置
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
fontSize: fontSize
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
width: splitLineWidth,
|
||||
color: splitLineColor
|
||||
}
|
||||
}
|
||||
},
|
||||
// 图例设置
|
||||
legend: {
|
||||
data: legendData,
|
||||
show: true,
|
||||
icon: legendIcon,
|
||||
textStyle: {
|
||||
fontSize: fontSize,
|
||||
color: fontColor
|
||||
},
|
||||
top: legendTop,
|
||||
bottom: 0,
|
||||
left: legendLeft,
|
||||
padding: legendPadding,
|
||||
itemWidth: itemWidth,
|
||||
itemHeight: itemHeight,
|
||||
},
|
||||
// 提示框设置
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
backgroundColor: tooltipBackgroundColor,
|
||||
borderColor: tooltipBorderColor,
|
||||
textStyle: {
|
||||
fontSize: fontSize,
|
||||
color: fontColor
|
||||
},
|
||||
formatter: function (params) {
|
||||
let tooltipContent = '';
|
||||
const formattedTime = new Date(params[0].value[0]).toLocaleString();
|
||||
tooltipContent += `<span style="line-height:2em">${formattedTime}</span><br>`;
|
||||
params.forEach(param => {
|
||||
const formattedTime = new Date(param.value[0]).toLocaleString();
|
||||
if (!param.seriesName.includes('stack')) {
|
||||
const name = param.seriesName.replace(/\s\d+(\.\d+)?%$/, '');
|
||||
tooltipContent += `<span style="line-height:2em">${param.marker} ${name} ${param.value[2].toFixed(1)}% ${param.value[1].toFixed(2)}</span><br>`;
|
||||
}
|
||||
});
|
||||
return tooltipContent;
|
||||
}
|
||||
},
|
||||
// 数据缩放设置
|
||||
dataZoom: [
|
||||
{
|
||||
type: 'slider',
|
||||
start: 0,
|
||||
end: 100
|
||||
}
|
||||
]
|
||||
};
|
||||
// 设置图表的配置选项
|
||||
this.chart.setOption(option);
|
||||
},
|
||||
reloadCharts() { // 重新加载所有图表
|
||||
this.servers.forEach(node => {
|
||||
const id = node.ID;
|
||||
const chartData = this.chartDataList[id];
|
||||
if (chartData) {
|
||||
this.renderCharts(id,true);
|
||||
}
|
||||
});
|
||||
},
|
||||
disposeCharts(chart){
|
||||
chart.dispose();
|
||||
chart = null;
|
||||
},
|
||||
getServerCountryCode(id){
|
||||
const result = this.servers.find(item => item.ID == id);
|
||||
return result.Host.CountryCode ? result.Host.CountryCode : 'rb';
|
||||
},
|
||||
initSearch() {
|
||||
$('#dropdown-search').on('keyup', function() {
|
||||
var searchTerm = $(this).val().toLowerCase();
|
||||
$('.dropdown-menu .dropdown-item').each(function() {
|
||||
var text = $(this).text().toLowerCase();
|
||||
if (text.indexOf(searchTerm) > -1) {
|
||||
$(this).removeClass('hidden').addClass('visible'); // 显示元素
|
||||
} else {
|
||||
$(this).removeClass('visible').addClass('hidden'); // 隐藏元素
|
||||
}
|
||||
});
|
||||
}
|
||||
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);
|
||||
},
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.myChart.dispose();
|
||||
this.myChart = null;
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{{end}}
|
||||
|
||||
+16
-12
@@ -90,12 +90,14 @@ func (s *NezhaHandler) RequestTask(h *pb.Host, stream pb.NezhaService_RequestTas
|
||||
}
|
||||
closeCh := make(chan error)
|
||||
singleton.ServerLock.RLock()
|
||||
singleton.ServerList[clientID].TaskCloseLock.Lock()
|
||||
// 修复不断的请求 task 但是没有 return 导致内存泄漏
|
||||
if singleton.ServerList[clientID].TaskClose != nil {
|
||||
close(singleton.ServerList[clientID].TaskClose)
|
||||
}
|
||||
singleton.ServerList[clientID].TaskStream = stream
|
||||
singleton.ServerList[clientID].TaskClose = closeCh
|
||||
singleton.ServerList[clientID].TaskCloseLock.Unlock()
|
||||
singleton.ServerLock.RUnlock()
|
||||
return <-closeCh
|
||||
}
|
||||
@@ -112,10 +114,10 @@ func (s *NezhaHandler) ReportSystemState(c context.Context, r *pb.State) (*pb.Re
|
||||
singleton.ServerList[clientID].LastActive = time.Now()
|
||||
singleton.ServerList[clientID].State = &state
|
||||
|
||||
// 如果从未记录过,先打点,等到小时时间点时入库
|
||||
if singleton.ServerList[clientID].PrevHourlyTransferIn == 0 || singleton.ServerList[clientID].PrevHourlyTransferOut == 0 {
|
||||
singleton.ServerList[clientID].PrevHourlyTransferIn = int64(state.NetInTransfer)
|
||||
singleton.ServerList[clientID].PrevHourlyTransferOut = int64(state.NetOutTransfer)
|
||||
// 应对 dashboard 重启的情况,如果从未记录过,先打点,等到小时时间点时入库
|
||||
if singleton.ServerList[clientID].PrevTransferInSnapshot == 0 || singleton.ServerList[clientID].PrevTransferOutSnapshot == 0 {
|
||||
singleton.ServerList[clientID].PrevTransferInSnapshot = int64(state.NetInTransfer)
|
||||
singleton.ServerList[clientID].PrevTransferOutSnapshot = int64(state.NetOutTransfer)
|
||||
}
|
||||
|
||||
return &pb.Receipt{Proced: true}, nil
|
||||
@@ -135,9 +137,8 @@ func (s *NezhaHandler) ReportSystemInfo(c context.Context, r *pb.Host) (*pb.Rece
|
||||
// 检查并更新DDNS
|
||||
if singleton.Conf.DDNS.Enable &&
|
||||
singleton.ServerList[clientID].EnableDDNS &&
|
||||
singleton.ServerList[clientID].Host != nil &&
|
||||
host.IP != "" &&
|
||||
singleton.ServerList[clientID].Host.IP != host.IP {
|
||||
(singleton.ServerList[clientID].Host == nil || singleton.ServerList[clientID].Host.IP != host.IP) {
|
||||
serverDomain := singleton.ServerList[clientID].DDNSDomain
|
||||
if singleton.Conf.DDNS.Provider == "" {
|
||||
provider, err = singleton.GetDDNSProviderFromProfile(singleton.ServerList[clientID].DDNSProfile)
|
||||
@@ -164,10 +165,9 @@ func (s *NezhaHandler) ReportSystemInfo(c context.Context, r *pb.Host) (*pb.Rece
|
||||
}
|
||||
|
||||
// 发送IP变动通知
|
||||
if singleton.Conf.EnableIPChangeNotification &&
|
||||
if singleton.ServerList[clientID].Host != nil && singleton.Conf.EnableIPChangeNotification &&
|
||||
((singleton.Conf.Cover == model.ConfigCoverAll && !singleton.Conf.IgnoredIPNotificationServerIDs[clientID]) ||
|
||||
(singleton.Conf.Cover == model.ConfigCoverIgnoreAll && singleton.Conf.IgnoredIPNotificationServerIDs[clientID])) &&
|
||||
singleton.ServerList[clientID].Host != nil &&
|
||||
singleton.ServerList[clientID].Host.IP != "" &&
|
||||
host.IP != "" &&
|
||||
singleton.ServerList[clientID].Host.IP != host.IP {
|
||||
@@ -184,10 +184,14 @@ func (s *NezhaHandler) ReportSystemInfo(c context.Context, r *pb.Host) (*pb.Rece
|
||||
nil)
|
||||
}
|
||||
|
||||
// 判断是否是机器重启,如果是机器重启要录入最后记录的流量里面
|
||||
if singleton.ServerList[clientID].Host.BootTime < host.BootTime {
|
||||
singleton.ServerList[clientID].PrevHourlyTransferIn = singleton.ServerList[clientID].PrevHourlyTransferIn - int64(singleton.ServerList[clientID].State.NetInTransfer)
|
||||
singleton.ServerList[clientID].PrevHourlyTransferOut = singleton.ServerList[clientID].PrevHourlyTransferOut - int64(singleton.ServerList[clientID].State.NetOutTransfer)
|
||||
/**
|
||||
* 这里的 singleton 中的数据都是关机前的旧数据
|
||||
* 当 agent 重启时,bootTime 变大,agent 端会先上报 host 信息,然后上报 state 信息
|
||||
* 这是可以借助上报顺序的空档,将停机前的流量统计数据标记下来,加到下一个小时的数据点上
|
||||
*/
|
||||
if singleton.ServerList[clientID].Host != nil && singleton.ServerList[clientID].Host.BootTime < host.BootTime {
|
||||
singleton.ServerList[clientID].PrevTransferInSnapshot = singleton.ServerList[clientID].PrevTransferInSnapshot - int64(singleton.ServerList[clientID].State.NetInTransfer)
|
||||
singleton.ServerList[clientID].PrevTransferOutSnapshot = singleton.ServerList[clientID].PrevTransferOutSnapshot - int64(singleton.ServerList[clientID].State.NetOutTransfer)
|
||||
}
|
||||
|
||||
// 不要冲掉国家码
|
||||
|
||||
@@ -34,6 +34,7 @@ func loadServers() {
|
||||
innerS := s
|
||||
innerS.Host = &model.Host{}
|
||||
innerS.State = &model.HostState{}
|
||||
innerS.TaskCloseLock = new(sync.Mutex)
|
||||
ServerList[innerS.ID] = &innerS
|
||||
SecretToID[innerS.Secret] = innerS.ID
|
||||
ServerTagToIDList[innerS.Tag] = append(ServerTagToIDList[innerS.Tag], innerS.ID)
|
||||
|
||||
@@ -100,14 +100,14 @@ func RecordTransferHourlyUsage() {
|
||||
for id, server := range ServerList {
|
||||
tx := model.Transfer{
|
||||
ServerID: id,
|
||||
In: server.State.NetInTransfer - uint64(server.PrevHourlyTransferIn),
|
||||
Out: server.State.NetOutTransfer - uint64(server.PrevHourlyTransferOut),
|
||||
In: utils.Uint64SubInt64(server.State.NetInTransfer, server.PrevTransferInSnapshot),
|
||||
Out: utils.Uint64SubInt64(server.State.NetOutTransfer, server.PrevTransferOutSnapshot),
|
||||
}
|
||||
if tx.In == 0 && tx.Out == 0 {
|
||||
continue
|
||||
}
|
||||
server.PrevHourlyTransferIn = int64(server.State.NetInTransfer)
|
||||
server.PrevHourlyTransferOut = int64(server.State.NetOutTransfer)
|
||||
server.PrevTransferInSnapshot = int64(server.State.NetInTransfer)
|
||||
server.PrevTransferOutSnapshot = int64(server.State.NetOutTransfer)
|
||||
tx.CreatedAt = nowTrimSeconds
|
||||
txs = append(txs, tx)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user