Compare commits

..
7 Commits
28 changed files with 321 additions and 112 deletions
+1
View File
@@ -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/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/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/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/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/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> <a href="https://github.com/unclezs" title="unclezs"><img src="https://avatars.githubusercontent.com/u/42318775?v=4" width="50;" alt="unclezs"/></a>
+7
View File
@@ -35,6 +35,12 @@ func ServeWeb(port uint) *http.Server {
pprof.Register(r) pprof.Register(r)
} }
r.Use(natGateway) r.Use(natGateway)
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) tmpl := template.New("").Funcs(funcMap)
var err error var err error
tmpl, err = tmpl.ParseFS(resource.TemplateFS, "template/**/*.html") tmpl, err = tmpl.ParseFS(resource.TemplateFS, "template/**/*.html")
@@ -49,6 +55,7 @@ func ServeWeb(port uint) *http.Server {
panic(err) panic(err)
} }
r.StaticFS("/static", http.FS(staticFs)) r.StaticFS("/static", http.FS(staticFs))
}
r.Static("/static-custom", "resource/static/custom") r.Static("/static-custom", "resource/static/custom")
routers(r) routers(r)
page404 := func(c *gin.Context) { page404 := func(c *gin.Context) {
+2
View File
@@ -7,6 +7,7 @@ import (
"net/http" "net/http"
"strconv" "strconv"
"strings" "strings"
"sync"
"time" "time"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@@ -384,6 +385,7 @@ func (ma *memberAPI) addOrEditServer(c *gin.Context) {
} else { } else {
s.Host = &model.Host{} s.Host = &model.Host{}
s.State = &model.HostState{} s.State = &model.HostState{}
s.TaskCloseLock = new(sync.Mutex)
singleton.ServerLock.Lock() singleton.ServerLock.Lock()
singleton.SecretToID[s.Secret] = s.ID singleton.SecretToID[s.Secret] = s.ID
singleton.ServerList[s.ID] = &s singleton.ServerList[s.ID] = &s
+9
View File
@@ -170,14 +170,23 @@ func (ns *NotificationServerBundle) replaceParamsInString(str string, message st
if ns.Server != nil { if ns.Server != nil {
str = strings.ReplaceAll(str, "#SERVER.NAME#", mod(ns.Server.Name)) 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.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.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.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.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.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.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.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.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.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.LOAD5#", mod(fmt.Sprintf("%f", ns.Server.State.Load5)))
str = strings.ReplaceAll(str, "#SERVER.LOAD15#", mod(fmt.Sprintf("%f", ns.Server.State.Load15))) str = strings.ReplaceAll(str, "#SERVER.LOAD15#", mod(fmt.Sprintf("%f", ns.Server.State.Load15)))
+2 -2
View File
@@ -75,8 +75,8 @@ func execCase(t *testing.T, item testSt) {
LastActive: time.Time{}, LastActive: time.Time{},
TaskClose: nil, TaskClose: nil,
TaskStream: nil, TaskStream: nil,
PrevHourlyTransferIn: 0, PrevTransferInSnapshot: 0,
PrevHourlyTransferOut: 0, PrevTransferOutSnapshot: 0,
} }
ns := NotificationServerBundle{ ns := NotificationServerBundle{
Notification: &n, Notification: &n,
+5 -3
View File
@@ -5,6 +5,8 @@ import (
"time" "time"
"gorm.io/gorm" "gorm.io/gorm"
"github.com/naiba/nezha/pkg/utils"
) )
const ( const (
@@ -103,21 +105,21 @@ func (u *Rule) Snapshot(cycleTransferStats *CycleTransferStats, server *Server,
src = float64(server.LastActive.Unix()) src = float64(server.LastActive.Unix())
} }
case "transfer_in_cycle": 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 { if u.CycleInterval != 0 {
var res NResult 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) 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) src += float64(res.N)
} }
case "transfer_out_cycle": 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 { if u.CycleInterval != 0 {
var res NResult 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) 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) src += float64(res.N)
} }
case "transfer_all_cycle": 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 { if u.CycleInterval != 0 {
var res NResult 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) db.Model(&Transfer{}).Select("SUM(`in`+`out`) AS n").Where("datetime(`created_at`) >= datetime(?) AND server_id = ?", u.GetTransferDurationStart().UTC(), server.ID).Scan(&res)
+7 -4
View File
@@ -3,6 +3,7 @@ package model
import ( import (
"fmt" "fmt"
"html/template" "html/template"
"sync"
"time" "time"
"github.com/naiba/nezha/pkg/utils" "github.com/naiba/nezha/pkg/utils"
@@ -28,10 +29,11 @@ type Server struct {
LastActive time.Time `gorm:"-"` LastActive time.Time `gorm:"-"`
TaskClose chan error `gorm:"-" json:"-"` TaskClose chan error `gorm:"-" json:"-"`
TaskCloseLock *sync.Mutex `gorm:"-" json:"-"`
TaskStream pb.NezhaService_RequestTaskServer `gorm:"-" json:"-"` TaskStream pb.NezhaService_RequestTaskServer `gorm:"-" json:"-"`
PrevHourlyTransferIn int64 `gorm:"-" json:"-"` // 上次数据点时的入站使用量 PrevTransferInSnapshot int64 `gorm:"-" json:"-"` // 上次数据点时的入站使用量
PrevHourlyTransferOut int64 `gorm:"-" json:"-"` // 上次数据点时的出站使用量 PrevTransferOutSnapshot int64 `gorm:"-" json:"-"` // 上次数据点时的出站使用量
} }
func (s *Server) CopyFromRunningServer(old *Server) { func (s *Server) CopyFromRunningServer(old *Server) {
@@ -39,9 +41,10 @@ func (s *Server) CopyFromRunningServer(old *Server) {
s.State = old.State s.State = old.State
s.LastActive = old.LastActive s.LastActive = old.LastActive
s.TaskClose = old.TaskClose s.TaskClose = old.TaskClose
s.TaskCloseLock = old.TaskCloseLock
s.TaskStream = old.TaskStream s.TaskStream = old.TaskStream
s.PrevHourlyTransferIn = old.PrevHourlyTransferIn s.PrevTransferInSnapshot = old.PrevTransferInSnapshot
s.PrevHourlyTransferOut = old.PrevHourlyTransferOut s.PrevTransferOutSnapshot = old.PrevTransferOutSnapshot
} }
func boolToString(b bool) string { func boolToString(b bool) string {
+10
View File
@@ -76,3 +76,13 @@ func GenerateRandomString(n int) (string, error) {
} }
return string(ret), nil 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)
}
+148
View File
@@ -0,0 +1,148 @@
.ui.container {
width: 95vw !important;
max-width: 1680px !important;
}
html[nz-theme='dark'] {
body {
background-color: #121212 !important;
color: #fff !important;
}
.ui.menu {
background-color: #282828 !important;
}
.ui.menu * {
color: #fff !important;
}
.accordion {
background-color: #282828 !important;
}
.accordion .title {
color: #fff !important;
}
.ui.card {
background-color: #3f3f3f !important;
border: none !important;
box-shadow: none !important;
}
.header {
color: #fff !important;
}
.description {
color: #fff !important;
}
.icon {
color: #fff !important;
}
.ui.popup {
background-color: #575757 !important;
color: #fff !important;
}
.ui.table {
background-color: #282828 !important;
color: #fff !important;
}
.ui thead th {
background-color: #3f3f3f !important;
color: #fff !important;
}
.ui.buttons .button {
background-color: #3f3f3f !important;
color: #fff !important;
}
.ui.modal {
background-color: #282828 !important;
}
.ui.modal * {
color: #fff !important;
}
textarea,
input,
select,
.dropdown {
background-color: #3f3f3f !important;
color: #fff !important;
}
.ui.message {
background-color: unset !important;
}
.ui.dropdown .menu {
background-color: #575757 !important;
color: #fff !important;
}
.ui.modal>.header {
background-color: #3f3f3f !important;
}
.ui.modal>.content {
background-color: #282828 !important;
}
.ui.modal>.actions {
background-color: #3f3f3f !important;
}
#alert {
background-color: #3f3f3f !important;
color: #fff !important;
}
.ui.form .field>label {
color: unset !important;
}
.ui.segment {
background-color: #3f3f3f !important;
}
.ui.segment textarea,
input,
select,
.dropdown {
background-color: #575757 !important;
color: #fff !important;
}
form label {
color: #fff !important;
}
.ui.inverted.segment {
background-color: #121212 !important;
}
.ui.inverted.segment * {
color: #8b8b8b !important;
}
.menu .dropdown {
background-color: #282828 !important;
color: #fff !important;
}
.ui.menu .ui.dropdown .menu>.item {
color: #fff !important;
}
.login .ui.message{
color: #8b8b8b !important;
}
}
+11 -2
View File
@@ -6,19 +6,26 @@
max-width: 1300px !important; max-width: 1300px !important;
} }
} }
@media only screen and (max-width:767px) { @media only screen and (max-width:767px) {
.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) {
margin-top: 0.4em !important; margin-top: 0.4em !important;
} }
.ui.menu .item>img:not(.ui) { .ui.menu .item>img:not(.ui) {
width: 2.2rem; width: 2.2rem;
} }
.ui.menu .item:before { .ui.menu .item:before {
width: 0.5px; width: 0.5px;
} }
.ui.menu .item { .ui.menu .item {
padding: 0.9rem 0.55rem; padding: 0.9rem 0.55rem;
} }
.ui.large.menu { .ui.large.menu {
font-size: 1rem; font-size: 1rem;
} }
@@ -28,6 +35,7 @@ i.icon {
color: #000; color: #000;
width: 1.2em !important; width: 1.2em !important;
} }
i.fi { i.fi {
width: 0.9em; width: 0.9em;
margin: 0px 6px 0px 2px; margin: 0px 6px 0px 2px;
@@ -91,7 +99,8 @@ td {
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; line-height: 1em;
} }
+2
View File
@@ -14,8 +14,10 @@
<script> <script>
(function () { (function () {
updateLang({{.LANG }}); updateLang({{.LANG }});
document.documentElement.setAttribute('nz-theme', window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
}) (); }) ();
</script> </script>
</body> </body>
</html> </html>
{{end}} {{end}}
+2 -1
View File
@@ -11,7 +11,8 @@
<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]/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="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/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">
</head> </head>
<body> <body>
{{end}} {{end}}
+1 -1
View File
@@ -10,7 +10,7 @@
</button> </button>
</div> </div>
</div> </div>
<table class="ui very basic table"> <table class="ui basic table">
<thead> <thead>
<tr> <tr>
<th>{{tr "Token"}}</th> <th>{{tr "Token"}}</th>
+1 -1
View File
@@ -10,7 +10,7 @@
</button> </button>
</div> </div>
</div> </div>
<table class="ui very basic table"> <table class="ui basic table">
<thead> <thead>
<tr> <tr>
<th>ID</th> <th>ID</th>
+1 -1
View File
@@ -9,7 +9,7 @@
</button> </button>
</div> </div>
</div> </div>
<table class="ui very basic table"> <table class="ui basic table">
<thead> <thead>
<tr> <tr>
<th>ID</th> <th>ID</th>
+1 -1
View File
@@ -10,7 +10,7 @@
</button> </button>
</div> </div>
</div> </div>
<table class="ui very basic table"> <table class="ui basic table">
<thead> <thead>
<tr> <tr>
<th>ID</th> <th>ID</th>
+2 -2
View File
@@ -10,7 +10,7 @@
</button> </button>
</div> </div>
</div> </div>
<table class="ui very basic table"> <table class="ui basic table">
<thead> <thead>
<tr> <tr>
<th>ID</th> <th>ID</th>
@@ -52,7 +52,7 @@
</button> </button>
</div> </div>
</div> </div>
<table class="ui very basic table"> <table class="ui basic table">
<thead> <thead>
<tr> <tr>
<th>ID</th> <th>ID</th>
+1 -1
View File
@@ -19,7 +19,7 @@
</button> </button>
</div> </div>
</div> </div>
<table class="ui very basic table"> <table class="ui basic table">
<thead> <thead>
<tr> <tr>
<th><button onclick="checkAllServer()" class="ui mini nezha-primary-btn button">{{tr "SelectAll"}}</button></th> <th><button onclick="checkAllServer()" class="ui mini nezha-primary-btn button">{{tr "SelectAll"}}</button></th>
+11 -3
View File
@@ -1,6 +1,7 @@
{{define "dashboard-default/terminal"}} {{define "dashboard-default/terminal"}}
<!DOCTYPE html> <!DOCTYPE html>
<html lang="{{.Conf.Language}}"> <html lang="{{.Conf.Language}}">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
@@ -23,16 +24,19 @@
background-color: black; background-color: black;
} }
</style> </style>
<body onresize="onResize()"> <body onresize="onResize()">
<div id="terminal-container"></div> <div id="terminal-container"></div>
<script src="https://unpkg.com/[email protected]/lib/xterm.js"></script> <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-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> <script>
let sendResizing = false; let sendResizing = false;
function doResize() { function doResize() {
fitAddon.fit() fitAddon.fit()
const w = fitAddon.proposeDimensions(); const w = fitAddon.proposeDimensions();
const prefix = new Int8Array([1]); const prefix = new Int8Array([1]);
const resizeMessage = new TextEncoder().encode(JSON.stringify({ const resizeMessage = new TextEncoder().encode(JSON.stringify({
@@ -70,10 +74,13 @@
cursorBlink: true, cursorBlink: true,
}); });
const socket = new WebSocket((window.location.protocol == 'https:' ? 'wss' : 'ws') + '://' + window.location.host + '/terminal/' + '{{.SessionID}}'); 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(); const fitAddon = new FitAddon.FitAddon();
term.loadAddon(attachAddon);
term.loadAddon(fitAddon); term.loadAddon(fitAddon);
term.open(document.getElementById('terminal-container')); term.open(document.getElementById('terminal-container'));
socket.onopen = () => { socket.onopen = () => {
@@ -90,5 +97,6 @@
} }
</script> </script>
</body> </body>
</html> </html>
{{end}} {{end}}
+3 -2
View File
@@ -10,10 +10,11 @@
{{ if not .Conf.DisableSwitchTemplateInFrontend }} {{ if not .Conf.DisableSwitchTemplateInFrontend }}
<script> <script>
function showSwitchTemplate(list, currentBackendTheme) { function showSwitchTemplate(list, currentBackendTheme) {
console.log(list, currentBackendTheme); // console.log(list, currentBackendTheme);
console.log("currentBackendTheme:",currentBackendTheme); // console.log("currentBackendTheme:",currentBackendTheme);
} }
showSwitchTemplate({{ .Themes }}, {{ .Conf.Site.Theme }}) showSwitchTemplate({{ .Themes }}, {{ .Conf.Site.Theme }})
document.documentElement.setAttribute('nz-theme', window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
</script> </script>
{{ end }} {{ end }}
<script> <script>
+1
View File
@@ -14,6 +14,7 @@
<link rel="stylesheet" href="https://unpkg.com/[email protected]/css/flag-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/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?v20240616">
<link rel="stylesheet" type="text/css" href="/static/darkmode.css?v20240813">
<script src="https://unpkg.com/[email protected]/dist/jquery.min.js"></script> <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/semantic.min.js"></script>
<script src="https://unpkg.com/[email protected]/dist/vue.min.js"></script> <script src="https://unpkg.com/[email protected]/dist/vue.min.js"></script>
+1 -1
View File
@@ -18,7 +18,7 @@
</div> </div>
</div> </div>
<div class="ui container"> <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="border-radius: 28px; margin-top: 15px;height: 520px;overflow: hidden"></div>
</div> </div>
</div> </div>
+16 -12
View File
@@ -90,12 +90,14 @@ func (s *NezhaHandler) RequestTask(h *pb.Host, stream pb.NezhaService_RequestTas
} }
closeCh := make(chan error) closeCh := make(chan error)
singleton.ServerLock.RLock() singleton.ServerLock.RLock()
singleton.ServerList[clientID].TaskCloseLock.Lock()
// 修复不断的请求 task 但是没有 return 导致内存泄漏 // 修复不断的请求 task 但是没有 return 导致内存泄漏
if singleton.ServerList[clientID].TaskClose != nil { if singleton.ServerList[clientID].TaskClose != nil {
close(singleton.ServerList[clientID].TaskClose) close(singleton.ServerList[clientID].TaskClose)
} }
singleton.ServerList[clientID].TaskStream = stream singleton.ServerList[clientID].TaskStream = stream
singleton.ServerList[clientID].TaskClose = closeCh singleton.ServerList[clientID].TaskClose = closeCh
singleton.ServerList[clientID].TaskCloseLock.Unlock()
singleton.ServerLock.RUnlock() singleton.ServerLock.RUnlock()
return <-closeCh 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].LastActive = time.Now()
singleton.ServerList[clientID].State = &state singleton.ServerList[clientID].State = &state
// 如果从未记录过,先打点,等到小时时间点时入库 // 应对 dashboard 重启的情况,如果从未记录过,先打点,等到小时时间点时入库
if singleton.ServerList[clientID].PrevHourlyTransferIn == 0 || singleton.ServerList[clientID].PrevHourlyTransferOut == 0 { if singleton.ServerList[clientID].PrevTransferInSnapshot == 0 || singleton.ServerList[clientID].PrevTransferOutSnapshot == 0 {
singleton.ServerList[clientID].PrevHourlyTransferIn = int64(state.NetInTransfer) singleton.ServerList[clientID].PrevTransferInSnapshot = int64(state.NetInTransfer)
singleton.ServerList[clientID].PrevHourlyTransferOut = int64(state.NetOutTransfer) singleton.ServerList[clientID].PrevTransferOutSnapshot = int64(state.NetOutTransfer)
} }
return &pb.Receipt{Proced: true}, nil return &pb.Receipt{Proced: true}, nil
@@ -135,9 +137,8 @@ func (s *NezhaHandler) ReportSystemInfo(c context.Context, r *pb.Host) (*pb.Rece
// 检查并更新DDNS // 检查并更新DDNS
if singleton.Conf.DDNS.Enable && if singleton.Conf.DDNS.Enable &&
singleton.ServerList[clientID].EnableDDNS && singleton.ServerList[clientID].EnableDDNS &&
singleton.ServerList[clientID].Host != nil &&
host.IP != "" && 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 serverDomain := singleton.ServerList[clientID].DDNSDomain
if singleton.Conf.DDNS.Provider == "" { if singleton.Conf.DDNS.Provider == "" {
provider, err = singleton.GetDDNSProviderFromProfile(singleton.ServerList[clientID].DDNSProfile) 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变动通知 // 发送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.ConfigCoverAll && !singleton.Conf.IgnoredIPNotificationServerIDs[clientID]) ||
(singleton.Conf.Cover == model.ConfigCoverIgnoreAll && singleton.Conf.IgnoredIPNotificationServerIDs[clientID])) && (singleton.Conf.Cover == model.ConfigCoverIgnoreAll && singleton.Conf.IgnoredIPNotificationServerIDs[clientID])) &&
singleton.ServerList[clientID].Host != nil &&
singleton.ServerList[clientID].Host.IP != "" && singleton.ServerList[clientID].Host.IP != "" &&
host.IP != "" && host.IP != "" &&
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) nil)
} }
// 判断是否是机器重启,如果是机器重启要录入最后记录的流量里面 /**
if singleton.ServerList[clientID].Host.BootTime < host.BootTime { * 这里的 singleton 中的数据都是关机前的旧数据
singleton.ServerList[clientID].PrevHourlyTransferIn = singleton.ServerList[clientID].PrevHourlyTransferIn - int64(singleton.ServerList[clientID].State.NetInTransfer) * 当 agent 重启时,bootTime 变大,agent 端会先上报 host 信息,然后上报 state 信息
singleton.ServerList[clientID].PrevHourlyTransferOut = singleton.ServerList[clientID].PrevHourlyTransferOut - int64(singleton.ServerList[clientID].State.NetOutTransfer) * 这是可以借助上报顺序的空档,将停机前的流量统计数据标记下来,加到下一个小时的数据点上
*/
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)
} }
// 不要冲掉国家码 // 不要冲掉国家码
+1
View File
@@ -34,6 +34,7 @@ func loadServers() {
innerS := s innerS := s
innerS.Host = &model.Host{} innerS.Host = &model.Host{}
innerS.State = &model.HostState{} innerS.State = &model.HostState{}
innerS.TaskCloseLock = new(sync.Mutex)
ServerList[innerS.ID] = &innerS ServerList[innerS.ID] = &innerS
SecretToID[innerS.Secret] = innerS.ID SecretToID[innerS.Secret] = innerS.ID
ServerTagToIDList[innerS.Tag] = append(ServerTagToIDList[innerS.Tag], innerS.ID) ServerTagToIDList[innerS.Tag] = append(ServerTagToIDList[innerS.Tag], innerS.ID)
+4 -4
View File
@@ -100,14 +100,14 @@ func RecordTransferHourlyUsage() {
for id, server := range ServerList { for id, server := range ServerList {
tx := model.Transfer{ tx := model.Transfer{
ServerID: id, ServerID: id,
In: server.State.NetInTransfer - uint64(server.PrevHourlyTransferIn), In: utils.Uint64SubInt64(server.State.NetInTransfer, server.PrevTransferInSnapshot),
Out: server.State.NetOutTransfer - uint64(server.PrevHourlyTransferOut), Out: utils.Uint64SubInt64(server.State.NetOutTransfer, server.PrevTransferOutSnapshot),
} }
if tx.In == 0 && tx.Out == 0 { if tx.In == 0 && tx.Out == 0 {
continue continue
} }
server.PrevHourlyTransferIn = int64(server.State.NetInTransfer) server.PrevTransferInSnapshot = int64(server.State.NetInTransfer)
server.PrevHourlyTransferOut = int64(server.State.NetOutTransfer) server.PrevTransferOutSnapshot = int64(server.State.NetOutTransfer)
tx.CreatedAt = nowTrimSeconds tx.CreatedAt = nowTrimSeconds
txs = append(txs, tx) txs = append(txs, tx)
} }