Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
55f5c89c1c | ||
|
|
937696c26d | ||
|
|
08aaea8244 | ||
|
|
b886f70e48 | ||
|
|
12c6b8a6f6 | ||
|
|
ebcc8a6efb | ||
|
|
c4d8b63d32 |
@@ -4,7 +4,7 @@
|
|||||||
<br>
|
<br>
|
||||||
<small><i>LOGO designed by <a href="https://xio.ng" target="_blank">熊大</a> .</i></small>
|
<small><i>LOGO designed by <a href="https://xio.ng" target="_blank">熊大</a> .</i></small>
|
||||||
<br><br>
|
<br><br>
|
||||||
<img alt="GitHub release (with filter)" src="https://img.shields.io/github/v/release/naiba/nezha?color=brightgreen&style=for-the-badge&logo=github&label=Dashboard"> <img src="https://img.shields.io/github/v/release/nezhahq/agent?color=brightgreen&label=Agent&style=for-the-badge&logo=github"> <img src="https://img.shields.io/github/actions/workflow/status/nezhahq/agent/agent.yml?label=Agent%20CI&logo=github&style=for-the-badge"> <img src="https://img.shields.io/badge/Installer-v0.19.0-brightgreen?style=for-the-badge&logo=linux">
|
<img alt="GitHub release (with filter)" src="https://img.shields.io/github/v/release/naiba/nezha?color=brightgreen&style=for-the-badge&logo=github&label=Dashboard"> <img src="https://img.shields.io/github/v/release/nezhahq/agent?color=brightgreen&label=Agent&style=for-the-badge&logo=github"> <img src="https://img.shields.io/github/actions/workflow/status/nezhahq/agent/agent.yml?label=Agent%20CI&logo=github&style=for-the-badge"> <img src="https://img.shields.io/badge/Installer-v0.19.2-brightgreen?style=for-the-badge&logo=linux">
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
<p>:trollface: <b>Nezha Monitoring: Self-hostable, lightweight, servers and websites monitoring and O&M tool.</b></p>
|
<p>:trollface: <b>Nezha Monitoring: Self-hostable, lightweight, servers and websites monitoring and O&M tool.</b></p>
|
||||||
@@ -59,6 +59,7 @@ You can change the dashboard language in the settings page (`/setting`) after th
|
|||||||
<a href="https://github.com/1ridic" title="1ridic"><img src="https://avatars.githubusercontent.com/u/88495501?v=4" width="50;" alt="1ridic"/></a>
|
<a href="https://github.com/1ridic" title="1ridic"><img src="https://avatars.githubusercontent.com/u/88495501?v=4" width="50;" alt="1ridic"/></a>
|
||||||
<a href="https://github.com/Mmx233" title="Mmx"><img src="https://avatars.githubusercontent.com/u/36563672?v=4" width="50;" alt="Mmx"/></a>
|
<a href="https://github.com/Mmx233" title="Mmx"><img src="https://avatars.githubusercontent.com/u/36563672?v=4" width="50;" alt="Mmx"/></a>
|
||||||
<a href="https://github.com/rootmelo92118" title="rootmelo92118"><img src="https://avatars.githubusercontent.com/u/32770959?v=4" width="50;" alt="rootmelo92118"/></a>
|
<a href="https://github.com/rootmelo92118" title="rootmelo92118"><img src="https://avatars.githubusercontent.com/u/32770959?v=4" width="50;" alt="rootmelo92118"/></a>
|
||||||
|
<a href="https://github.com/zhucaidan" title="zhucaidan"><img src="https://avatars.githubusercontent.com/u/47970938?v=4" width="50;" alt="zhucaidan"/></a>
|
||||||
<a href="https://github.com/iilemon" title="Sean"><img src="https://avatars.githubusercontent.com/u/33201711?v=4" width="50;" alt="Sean"/></a>
|
<a href="https://github.com/iilemon" title="Sean"><img src="https://avatars.githubusercontent.com/u/33201711?v=4" width="50;" alt="Sean"/></a>
|
||||||
<a href="https://github.com/fscarmen" title="fscarmen"><img src="https://avatars.githubusercontent.com/u/62703343?v=4" width="50;" alt="fscarmen"/></a>
|
<a href="https://github.com/fscarmen" title="fscarmen"><img src="https://avatars.githubusercontent.com/u/62703343?v=4" width="50;" alt="fscarmen"/></a>
|
||||||
<a href="https://github.com/ch8o" title="no-name-now"><img src="https://avatars.githubusercontent.com/u/9103372?v=4" width="50;" alt="no-name-now"/></a>
|
<a href="https://github.com/ch8o" title="no-name-now"><img src="https://avatars.githubusercontent.com/u/9103372?v=4" width="50;" alt="no-name-now"/></a>
|
||||||
@@ -66,6 +67,7 @@ You can change the dashboard language in the settings page (`/setting`) after th
|
|||||||
<a href="https://github.com/DarcJC" title="Darc Z."><img src="https://avatars.githubusercontent.com/u/53445798?v=4" width="50;" alt="Darc Z."/></a>
|
<a href="https://github.com/DarcJC" title="Darc Z."><img src="https://avatars.githubusercontent.com/u/53445798?v=4" width="50;" alt="Darc Z."/></a>
|
||||||
<a href="https://github.com/Creling" title="Creling"><img src="https://avatars.githubusercontent.com/u/43109504?v=4" width="50;" alt="Creling"/></a>
|
<a href="https://github.com/Creling" title="Creling"><img src="https://avatars.githubusercontent.com/u/43109504?v=4" width="50;" alt="Creling"/></a>
|
||||||
<a href="https://github.com/coreff" title="Core F"><img src="https://avatars.githubusercontent.com/u/38347122?v=4" width="50;" alt="Core F"/></a>
|
<a href="https://github.com/coreff" title="Core F"><img src="https://avatars.githubusercontent.com/u/38347122?v=4" width="50;" alt="Core F"/></a>
|
||||||
|
<a href="https://github.com/nickfox-taterli" title="Tater Li"><img src="https://avatars.githubusercontent.com/u/19658596?v=4" width="50;" alt="Tater Li"/></a>
|
||||||
<a href="https://github.com/hmsjy2017" title="Tony"><img src="https://avatars.githubusercontent.com/u/42692274?v=4" width="50;" alt="Tony"/></a>
|
<a href="https://github.com/hmsjy2017" title="Tony"><img src="https://avatars.githubusercontent.com/u/42692274?v=4" width="50;" alt="Tony"/></a>
|
||||||
<a href="https://github.com/adminsama" title="adminsama"><img src="https://avatars.githubusercontent.com/u/60880076?v=4" width="50;" alt="adminsama"/></a>
|
<a href="https://github.com/adminsama" title="adminsama"><img src="https://avatars.githubusercontent.com/u/60880076?v=4" width="50;" alt="adminsama"/></a>
|
||||||
<a href="https://github.com/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>
|
||||||
@@ -77,10 +79,8 @@ You can change the dashboard language in the settings page (`/setting`) after th
|
|||||||
<a href="https://github.com/lyj0309" title="lyj"><img src="https://avatars.githubusercontent.com/u/50474995?v=4" width="50;" alt="lyj"/></a>
|
<a href="https://github.com/lyj0309" title="lyj"><img src="https://avatars.githubusercontent.com/u/50474995?v=4" width="50;" alt="lyj"/></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>
|
||||||
<a href="https://github.com/ysicing" title="缘生"><img src="https://avatars.githubusercontent.com/u/8605565?v=4" width="50;" alt="缘生"/></a>
|
<a href="https://github.com/ysicing" title="缘生"><img src="https://avatars.githubusercontent.com/u/8605565?v=4" width="50;" alt="缘生"/></a>
|
||||||
<a href="https://github.com/zhucaidan" title="zhucaidan"><img src="https://avatars.githubusercontent.com/u/47970938?v=4" width="50;" alt="zhucaidan"/></a>
|
|
||||||
<a href="https://github.com/arkylin" title="凌"><img src="https://avatars.githubusercontent.com/u/35104502?v=4" width="50;" alt="凌"/></a>
|
<a href="https://github.com/arkylin" title="凌"><img src="https://avatars.githubusercontent.com/u/35104502?v=4" width="50;" alt="凌"/></a>
|
||||||
<a href="https://github.com/colour93" title="玖叁"><img src="https://avatars.githubusercontent.com/u/64313711?v=4" width="50;" alt="玖叁"/></a>
|
<a href="https://github.com/colour93" title="玖叁"><img src="https://avatars.githubusercontent.com/u/64313711?v=4" width="50;" alt="玖叁"/></a>
|
||||||
<a href="https://github.com/nickfox-taterli" title="Tater Li"><img src="https://avatars.githubusercontent.com/u/19658596?v=4" width="50;" alt="Tater Li"/></a>
|
|
||||||
<a href="https://github.com/IamTaoChen" title="Tao Chen"><img src="https://avatars.githubusercontent.com/u/42793494?v=4" width="50;" alt="Tao Chen"/></a>
|
<a href="https://github.com/IamTaoChen" title="Tao Chen"><img src="https://avatars.githubusercontent.com/u/42793494?v=4" width="50;" alt="Tao Chen"/></a>
|
||||||
<a href="https://github.com/Septrum101" title="Spetrum"><img src="https://avatars.githubusercontent.com/u/11692994?v=4" width="50;" alt="Spetrum"/></a>
|
<a href="https://github.com/Septrum101" title="Spetrum"><img src="https://avatars.githubusercontent.com/u/11692994?v=4" width="50;" alt="Spetrum"/></a>
|
||||||
<a href="https://github.com/dreamingsleeping" title="Nanjing Hopefun Network Technology Co. Ltd."><img src="https://avatars.githubusercontent.com/u/13828658?v=4" width="50;" alt="Nanjing Hopefun Network Technology Co. Ltd."/></a>
|
<a href="https://github.com/dreamingsleeping" title="Nanjing Hopefun Network Technology Co. Ltd."><img src="https://avatars.githubusercontent.com/u/13828658?v=4" width="50;" alt="Nanjing Hopefun Network Technology Co. Ltd."/></a>
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ package controller
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
"io/fs"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -34,12 +34,6 @@ 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,13 +43,7 @@ func ServeWeb(port uint) *http.Server {
|
|||||||
tmpl = loadThirdPartyTemplates(tmpl)
|
tmpl = loadThirdPartyTemplates(tmpl)
|
||||||
r.SetHTMLTemplate(tmpl)
|
r.SetHTMLTemplate(tmpl)
|
||||||
r.Use(mygin.RecordPath)
|
r.Use(mygin.RecordPath)
|
||||||
staticFs, err := fs.Sub(resource.StaticFS, "static")
|
r.StaticFS("/static", http.FS(resource.StaticFS))
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
r.StaticFS("/static", http.FS(staticFs))
|
|
||||||
}
|
|
||||||
r.Static("/static-custom", "resource/static/custom")
|
|
||||||
routers(r)
|
routers(r)
|
||||||
page404 := func(c *gin.Context) {
|
page404 := func(c *gin.Context) {
|
||||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||||
@@ -106,14 +94,40 @@ func loadThirdPartyTemplates(tmpl *template.Template) *template.Template {
|
|||||||
if !theme.IsDir() {
|
if !theme.IsDir() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// load templates
|
|
||||||
t, err := ret.ParseGlob(fmt.Sprintf("resource/template/%s/*.html", theme.Name()))
|
themeDir := theme.Name()
|
||||||
if err != nil {
|
if !strings.HasPrefix(themeDir, "theme-") {
|
||||||
log.Printf("NEZHA>> Error parsing templates %s error: %v", theme.Name(), err)
|
log.Printf("NEZHA>> Invalid theme name: %s", themeDir)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
descPath := filepath.Join("resource", "template", themeDir, "theme.json")
|
||||||
|
desc, err := os.ReadFile(filepath.Clean(descPath))
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("NEZHA>> Error opening %s config: %v", themeDir, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
themeName, err := utils.GjsonGet(desc, "name")
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("NEZHA>> Error opening %s config: not a valid description file", theme.Name())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// load templates
|
||||||
|
templatePath := filepath.Join("resource", "template", themeDir, "*.html")
|
||||||
|
t, err := ret.ParseGlob(templatePath)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("NEZHA>> Error parsing templates %s: %v", themeDir, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
themeKey := strings.TrimPrefix(themeDir, "theme-")
|
||||||
|
model.Themes[themeKey] = themeName.String()
|
||||||
|
|
||||||
ret = t
|
ret = t
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ require (
|
|||||||
github.com/robfig/cron/v3 v3.0.1
|
github.com/robfig/cron/v3 v3.0.1
|
||||||
github.com/spf13/pflag v1.0.5
|
github.com/spf13/pflag v1.0.5
|
||||||
github.com/spf13/viper v1.18.2
|
github.com/spf13/viper v1.18.2
|
||||||
|
github.com/tidwall/gjson v1.18.0
|
||||||
github.com/xanzy/go-gitlab v0.103.0
|
github.com/xanzy/go-gitlab v0.103.0
|
||||||
golang.org/x/crypto v0.25.0
|
golang.org/x/crypto v0.25.0
|
||||||
golang.org/x/net v0.27.0
|
golang.org/x/net v0.27.0
|
||||||
@@ -70,6 +71,8 @@ require (
|
|||||||
github.com/spf13/afero v1.11.0 // indirect
|
github.com/spf13/afero v1.11.0 // indirect
|
||||||
github.com/spf13/cast v1.6.0 // indirect
|
github.com/spf13/cast v1.6.0 // indirect
|
||||||
github.com/subosito/gotenv v1.6.0 // indirect
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
|
github.com/tidwall/match v1.1.1 // indirect
|
||||||
|
github.com/tidwall/pretty v1.2.0 // indirect
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||||
go.uber.org/atomic v1.9.0 // indirect
|
go.uber.org/atomic v1.9.0 // indirect
|
||||||
|
|||||||
@@ -180,6 +180,12 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
|
|||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||||
|
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
|
||||||
|
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||||
|
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||||
|
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||||
|
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||||
|
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
|
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ var Themes = map[string]string{
|
|||||||
"hotaru": "Hotaru",
|
"hotaru": "Hotaru",
|
||||||
"angel-kanade": "AngelKanade",
|
"angel-kanade": "AngelKanade",
|
||||||
"server-status": "ServerStatus",
|
"server-status": "ServerStatus",
|
||||||
"custom": "Custom(local)",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var DashboardThemes = map[string]string{
|
var DashboardThemes = map[string]string{
|
||||||
|
|||||||
@@ -71,8 +71,8 @@ func (ns *NotificationServerBundle) reqBody(message string) (string, error) {
|
|||||||
return string(msgBytes)[1 : len(msgBytes)-1]
|
return string(msgBytes)[1 : len(msgBytes)-1]
|
||||||
}), nil
|
}), nil
|
||||||
case NotificationRequestTypeForm:
|
case NotificationRequestTypeForm:
|
||||||
var data map[string]string
|
data, err := utils.GjsonParseStringMap(n.RequestBody)
|
||||||
if err := utils.Json.Unmarshal([]byte(n.RequestBody), &data); err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
params := url.Values{}
|
params := url.Values{}
|
||||||
@@ -99,8 +99,8 @@ func (n *Notification) setRequestHeader(req *http.Request) error {
|
|||||||
if n.RequestHeader == "" {
|
if n.RequestHeader == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var m map[string]string
|
m, err := utils.GjsonParseStringMap(n.RequestHeader)
|
||||||
if err := utils.Json.Unmarshal([]byte(n.RequestHeader), &m); err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for k, v := range m {
|
for k, v := range m {
|
||||||
|
|||||||
+32
-67
@@ -2,6 +2,7 @@ package ddns
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
@@ -14,10 +15,13 @@ import (
|
|||||||
const baseEndpoint = "https://api.cloudflare.com/client/v4/zones"
|
const baseEndpoint = "https://api.cloudflare.com/client/v4/zones"
|
||||||
|
|
||||||
type ProviderCloudflare struct {
|
type ProviderCloudflare struct {
|
||||||
|
isIpv4 bool
|
||||||
|
domainConfig *DomainConfig
|
||||||
secret string
|
secret string
|
||||||
zoneId string
|
zoneId string
|
||||||
|
ipAddr string
|
||||||
recordId string
|
recordId string
|
||||||
domainConfig *DomainConfig
|
recordType string
|
||||||
}
|
}
|
||||||
|
|
||||||
type cfReq struct {
|
type cfReq struct {
|
||||||
@@ -28,13 +32,6 @@ type cfReq struct {
|
|||||||
Proxied bool `json:"proxied"`
|
Proxied bool `json:"proxied"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type cfResp struct {
|
|
||||||
Result []struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
} `json:"result"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewProviderCloudflare(s string) *ProviderCloudflare {
|
func NewProviderCloudflare(s string) *ProviderCloudflare {
|
||||||
return &ProviderCloudflare{
|
return &ProviderCloudflare{
|
||||||
secret: s,
|
secret: s,
|
||||||
@@ -54,13 +51,19 @@ func (provider *ProviderCloudflare) UpdateDomain(domainConfig *DomainConfig) err
|
|||||||
|
|
||||||
// 当IPv4和IPv6同时成功才算作成功
|
// 当IPv4和IPv6同时成功才算作成功
|
||||||
if provider.domainConfig.EnableIPv4 {
|
if provider.domainConfig.EnableIPv4 {
|
||||||
if err = provider.addDomainRecord(true); err != nil {
|
provider.isIpv4 = true
|
||||||
|
provider.recordType = getRecordString(provider.isIpv4)
|
||||||
|
provider.ipAddr = provider.domainConfig.Ipv4Addr
|
||||||
|
if err = provider.addDomainRecord(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if provider.domainConfig.EnableIpv6 {
|
if provider.domainConfig.EnableIpv6 {
|
||||||
if err = provider.addDomainRecord(false); err != nil {
|
provider.isIpv4 = false
|
||||||
|
provider.recordType = getRecordString(provider.isIpv4)
|
||||||
|
provider.ipAddr = provider.domainConfig.Ipv6Addr
|
||||||
|
if err = provider.addDomainRecord(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -68,19 +71,18 @@ func (provider *ProviderCloudflare) UpdateDomain(domainConfig *DomainConfig) err
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *ProviderCloudflare) addDomainRecord(isIpv4 bool) error {
|
func (provider *ProviderCloudflare) addDomainRecord() error {
|
||||||
err := provider.findDNSRecord(isIpv4)
|
err := provider.findDNSRecord()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.Is(err, utils.ErrGjsonNotFound) {
|
||||||
|
// 添加 DNS 记录
|
||||||
|
return provider.createDNSRecord()
|
||||||
|
}
|
||||||
return fmt.Errorf("查找 DNS 记录时出错: %s", err)
|
return fmt.Errorf("查找 DNS 记录时出错: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if provider.recordId == "" {
|
|
||||||
// 添加 DNS 记录
|
|
||||||
return provider.createDNSRecord(isIpv4)
|
|
||||||
} else {
|
|
||||||
// 更新 DNS 记录
|
// 更新 DNS 记录
|
||||||
return provider.updateDNSRecord(isIpv4)
|
return provider.updateDNSRecord()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *ProviderCloudflare) getZoneID() error {
|
func (provider *ProviderCloudflare) getZoneID() error {
|
||||||
@@ -96,35 +98,22 @@ func (provider *ProviderCloudflare) getZoneID() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
res := &cfResp{}
|
result, err := utils.GjsonGet(body, "result.0.id")
|
||||||
err = utils.Json.Unmarshal(body, res)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
result := res.Result
|
provider.zoneId = result.String()
|
||||||
if len(result) > 0 {
|
|
||||||
provider.zoneId = result[0].ID
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("找不到 Zone ID")
|
func (provider *ProviderCloudflare) findDNSRecord() error {
|
||||||
}
|
|
||||||
|
|
||||||
func (provider *ProviderCloudflare) findDNSRecord(isIPv4 bool) error {
|
|
||||||
var ipType string
|
|
||||||
if isIPv4 {
|
|
||||||
ipType = "A"
|
|
||||||
} else {
|
|
||||||
ipType = "AAAA"
|
|
||||||
}
|
|
||||||
|
|
||||||
de, _ := url.JoinPath(baseEndpoint, provider.zoneId, "dns_records")
|
de, _ := url.JoinPath(baseEndpoint, provider.zoneId, "dns_records")
|
||||||
du, _ := url.Parse(de)
|
du, _ := url.Parse(de)
|
||||||
|
|
||||||
q := du.Query()
|
q := du.Query()
|
||||||
q.Set("name", provider.domainConfig.FullDomain)
|
q.Set("name", provider.domainConfig.FullDomain)
|
||||||
q.Set("type", ipType)
|
q.Set("type", provider.recordType)
|
||||||
du.RawQuery = q.Encode()
|
du.RawQuery = q.Encode()
|
||||||
|
|
||||||
body, err := provider.sendRequest("GET", du.String(), nil)
|
body, err := provider.sendRequest("GET", du.String(), nil)
|
||||||
@@ -132,36 +121,21 @@ func (provider *ProviderCloudflare) findDNSRecord(isIPv4 bool) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
res := &cfResp{}
|
result, err := utils.GjsonGet(body, "result.0.id")
|
||||||
err = utils.Json.Unmarshal(body, res)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
result := res.Result
|
provider.recordId = result.String()
|
||||||
if len(result) > 0 {
|
|
||||||
provider.recordId = result[0].ID
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
func (provider *ProviderCloudflare) createDNSRecord() error {
|
||||||
}
|
|
||||||
|
|
||||||
func (provider *ProviderCloudflare) createDNSRecord(isIPv4 bool) error {
|
|
||||||
var ipType, ipAddr string
|
|
||||||
if isIPv4 {
|
|
||||||
ipType = "A"
|
|
||||||
ipAddr = provider.domainConfig.Ipv4Addr
|
|
||||||
} else {
|
|
||||||
ipType = "AAAA"
|
|
||||||
ipAddr = provider.domainConfig.Ipv6Addr
|
|
||||||
}
|
|
||||||
|
|
||||||
de, _ := url.JoinPath(baseEndpoint, provider.zoneId, "dns_records")
|
de, _ := url.JoinPath(baseEndpoint, provider.zoneId, "dns_records")
|
||||||
data := &cfReq{
|
data := &cfReq{
|
||||||
Name: provider.domainConfig.FullDomain,
|
Name: provider.domainConfig.FullDomain,
|
||||||
Type: ipType,
|
Type: provider.recordType,
|
||||||
Content: ipAddr,
|
Content: provider.ipAddr,
|
||||||
TTL: 60,
|
TTL: 60,
|
||||||
Proxied: false,
|
Proxied: false,
|
||||||
}
|
}
|
||||||
@@ -171,21 +145,12 @@ func (provider *ProviderCloudflare) createDNSRecord(isIPv4 bool) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *ProviderCloudflare) updateDNSRecord(isIPv4 bool) error {
|
func (provider *ProviderCloudflare) updateDNSRecord() error {
|
||||||
var ipType, ipAddr string
|
|
||||||
if isIPv4 {
|
|
||||||
ipType = "A"
|
|
||||||
ipAddr = provider.domainConfig.Ipv4Addr
|
|
||||||
} else {
|
|
||||||
ipType = "AAAA"
|
|
||||||
ipAddr = provider.domainConfig.Ipv6Addr
|
|
||||||
}
|
|
||||||
|
|
||||||
de, _ := url.JoinPath(baseEndpoint, provider.zoneId, "dns_records", provider.recordId)
|
de, _ := url.JoinPath(baseEndpoint, provider.zoneId, "dns_records", provider.recordId)
|
||||||
data := &cfReq{
|
data := &cfReq{
|
||||||
Name: provider.domainConfig.FullDomain,
|
Name: provider.domainConfig.FullDomain,
|
||||||
Type: ipType,
|
Type: provider.recordType,
|
||||||
Content: ipAddr,
|
Content: provider.ipAddr,
|
||||||
TTL: 60,
|
TTL: 60,
|
||||||
Proxied: false,
|
Proxied: false,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,3 +20,10 @@ func splitDomain(domain string) (prefix string, realDomain string) {
|
|||||||
prefix = domain[:len(domain)-len(realDomain)-1]
|
prefix = domain[:len(domain)-len(realDomain)-1]
|
||||||
return prefix, realDomain
|
return prefix, realDomain
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getRecordString(isIpv4 bool) string {
|
||||||
|
if isIpv4 {
|
||||||
|
return "A"
|
||||||
|
}
|
||||||
|
return "AAAA"
|
||||||
|
}
|
||||||
|
|||||||
+39
-59
@@ -5,6 +5,7 @@ import (
|
|||||||
"crypto/hmac"
|
"crypto/hmac"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
@@ -19,10 +20,14 @@ import (
|
|||||||
const te = "https://dnspod.tencentcloudapi.com"
|
const te = "https://dnspod.tencentcloudapi.com"
|
||||||
|
|
||||||
type ProviderTencentCloud struct {
|
type ProviderTencentCloud struct {
|
||||||
|
isIpv4 bool
|
||||||
|
domainConfig *DomainConfig
|
||||||
|
recordID uint64
|
||||||
|
recordType string
|
||||||
secretID string
|
secretID string
|
||||||
secretKey string
|
secretKey string
|
||||||
domainConfig *DomainConfig
|
errCode string
|
||||||
resp *tcResp
|
ipAddr string
|
||||||
}
|
}
|
||||||
|
|
||||||
type tcReq struct {
|
type tcReq struct {
|
||||||
@@ -36,18 +41,6 @@ type tcReq struct {
|
|||||||
RecordId uint64 `json:"RecordId,omitempty"`
|
RecordId uint64 `json:"RecordId,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type tcResp struct {
|
|
||||||
Response struct {
|
|
||||||
RecordList []struct {
|
|
||||||
RecordId uint64
|
|
||||||
Value string
|
|
||||||
}
|
|
||||||
Error struct {
|
|
||||||
Code string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewProviderTencentCloud(id, key string) *ProviderTencentCloud {
|
func NewProviderTencentCloud(id, key string) *ProviderTencentCloud {
|
||||||
return &ProviderTencentCloud{
|
return &ProviderTencentCloud{
|
||||||
secretID: id,
|
secretID: id,
|
||||||
@@ -64,13 +57,19 @@ func (provider *ProviderTencentCloud) UpdateDomain(domainConfig *DomainConfig) e
|
|||||||
// 当IPv4和IPv6同时成功才算作成功
|
// 当IPv4和IPv6同时成功才算作成功
|
||||||
var err error
|
var err error
|
||||||
if provider.domainConfig.EnableIPv4 {
|
if provider.domainConfig.EnableIPv4 {
|
||||||
if err = provider.addDomainRecord(true); err != nil {
|
provider.isIpv4 = true
|
||||||
|
provider.recordType = getRecordString(provider.isIpv4)
|
||||||
|
provider.ipAddr = provider.domainConfig.Ipv4Addr
|
||||||
|
if err = provider.addDomainRecord(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if provider.domainConfig.EnableIpv6 {
|
if provider.domainConfig.EnableIpv6 {
|
||||||
if err = provider.addDomainRecord(false); err != nil {
|
provider.isIpv4 = false
|
||||||
|
provider.recordType = getRecordString(provider.isIpv4)
|
||||||
|
provider.ipAddr = provider.domainConfig.Ipv6Addr
|
||||||
|
if err = provider.addDomainRecord(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -78,33 +77,26 @@ func (provider *ProviderTencentCloud) UpdateDomain(domainConfig *DomainConfig) e
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *ProviderTencentCloud) addDomainRecord(isIpv4 bool) error {
|
func (provider *ProviderTencentCloud) addDomainRecord() error {
|
||||||
err := provider.findDNSRecord(isIpv4)
|
err := provider.findDNSRecord()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("查找 DNS 记录时出错: %s", err)
|
return fmt.Errorf("查找 DNS 记录时出错: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if provider.resp.Response.Error.Code == "ResourceNotFound.NoDataOfRecord" { // 没有找到 DNS 记录
|
if provider.errCode == "ResourceNotFound.NoDataOfRecord" { // 没有找到 DNS 记录
|
||||||
return provider.createDNSRecord(isIpv4)
|
return provider.createDNSRecord()
|
||||||
} else if provider.resp.Response.Error.Code != "" {
|
} else if provider.errCode != "" {
|
||||||
return fmt.Errorf("查询 DNS 记录时出错,错误代码为: %s", provider.resp.Response.Error.Code)
|
return fmt.Errorf("查询 DNS 记录时出错,错误代码为: %s", provider.errCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 默认情况下更新 DNS 记录
|
// 默认情况下更新 DNS 记录
|
||||||
return provider.updateDNSRecord(isIpv4)
|
return provider.updateDNSRecord()
|
||||||
}
|
|
||||||
|
|
||||||
func (provider *ProviderTencentCloud) findDNSRecord(isIPv4 bool) error {
|
|
||||||
var ipType string
|
|
||||||
if isIPv4 {
|
|
||||||
ipType = "A"
|
|
||||||
} else {
|
|
||||||
ipType = "AAAA"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (provider *ProviderTencentCloud) findDNSRecord() error {
|
||||||
prefix, realDomain := splitDomain(provider.domainConfig.FullDomain)
|
prefix, realDomain := splitDomain(provider.domainConfig.FullDomain)
|
||||||
data := &tcReq{
|
data := &tcReq{
|
||||||
RecordType: ipType,
|
RecordType: provider.recordType,
|
||||||
Domain: realDomain,
|
Domain: realDomain,
|
||||||
RecordLine: "默认",
|
RecordLine: "默认",
|
||||||
Subdomain: prefix,
|
Subdomain: prefix,
|
||||||
@@ -116,32 +108,29 @@ func (provider *ProviderTencentCloud) findDNSRecord(isIPv4 bool) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
provider.resp = &tcResp{}
|
result, err := utils.GjsonGet(body, "Response.RecordList.0.RecordId")
|
||||||
err = utils.Json.Unmarshal(body, provider.resp)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.Is(err, utils.ErrGjsonNotFound) {
|
||||||
|
if errCode, err := utils.GjsonGet(body, "Response.Error.Code"); err == nil {
|
||||||
|
provider.errCode = errCode.String()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
provider.recordID = result.Uint()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *ProviderTencentCloud) createDNSRecord(isIPv4 bool) error {
|
func (provider *ProviderTencentCloud) createDNSRecord() error {
|
||||||
var ipType, ipAddr string
|
|
||||||
if isIPv4 {
|
|
||||||
ipType = "A"
|
|
||||||
ipAddr = provider.domainConfig.Ipv4Addr
|
|
||||||
} else {
|
|
||||||
ipType = "AAAA"
|
|
||||||
ipAddr = provider.domainConfig.Ipv6Addr
|
|
||||||
}
|
|
||||||
|
|
||||||
prefix, realDomain := splitDomain(provider.domainConfig.FullDomain)
|
prefix, realDomain := splitDomain(provider.domainConfig.FullDomain)
|
||||||
data := &tcReq{
|
data := &tcReq{
|
||||||
RecordType: ipType,
|
RecordType: provider.recordType,
|
||||||
RecordLine: "默认",
|
RecordLine: "默认",
|
||||||
Domain: realDomain,
|
Domain: realDomain,
|
||||||
SubDomain: prefix,
|
SubDomain: prefix,
|
||||||
Value: ipAddr,
|
Value: provider.ipAddr,
|
||||||
TTL: 600,
|
TTL: 600,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,25 +139,16 @@ func (provider *ProviderTencentCloud) createDNSRecord(isIPv4 bool) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *ProviderTencentCloud) updateDNSRecord(isIPv4 bool) error {
|
func (provider *ProviderTencentCloud) updateDNSRecord() error {
|
||||||
var ipType, ipAddr string
|
|
||||||
if isIPv4 {
|
|
||||||
ipType = "A"
|
|
||||||
ipAddr = provider.domainConfig.Ipv4Addr
|
|
||||||
} else {
|
|
||||||
ipType = "AAAA"
|
|
||||||
ipAddr = provider.domainConfig.Ipv6Addr
|
|
||||||
}
|
|
||||||
|
|
||||||
prefix, realDomain := splitDomain(provider.domainConfig.FullDomain)
|
prefix, realDomain := splitDomain(provider.domainConfig.FullDomain)
|
||||||
data := &tcReq{
|
data := &tcReq{
|
||||||
RecordType: ipType,
|
RecordType: provider.recordType,
|
||||||
RecordLine: "默认",
|
RecordLine: "默认",
|
||||||
Domain: realDomain,
|
Domain: realDomain,
|
||||||
SubDomain: prefix,
|
SubDomain: prefix,
|
||||||
Value: ipAddr,
|
Value: provider.ipAddr,
|
||||||
TTL: 600,
|
TTL: 600,
|
||||||
RecordId: provider.resp.Response.RecordList[0].RecordId,
|
RecordId: provider.recordID,
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonData, _ := utils.Json.Marshal(data)
|
jsonData, _ := utils.Json.Marshal(data)
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/tidwall/gjson"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrGjsonNotFound = errors.New("specified path does not exist")
|
||||||
|
ErrGjsonWrongType = errors.New("wrong type")
|
||||||
|
)
|
||||||
|
|
||||||
|
func GjsonGet(json []byte, path string) (gjson.Result, error) {
|
||||||
|
result := gjson.GetBytes(json, path)
|
||||||
|
if !result.Exists() {
|
||||||
|
return result, ErrGjsonNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GjsonParseStringMap(jsonObject string) (map[string]string, error) {
|
||||||
|
result := gjson.Parse(jsonObject)
|
||||||
|
if !result.IsObject() {
|
||||||
|
return nil, ErrGjsonWrongType
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := make(map[string]string)
|
||||||
|
result.ForEach(func(key, value gjson.Result) bool {
|
||||||
|
ret[key.String()] = value.String()
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"io/fs"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HybridFS combines embed.FS and os.DirFS.
|
||||||
|
type HybridFS struct {
|
||||||
|
embedFS, dir fs.FS
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHybridFS(embed embed.FS, subDir string, localDir string) (*HybridFS, error) {
|
||||||
|
subFS, err := fs.Sub(embed, subDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &HybridFS{
|
||||||
|
embedFS: subFS,
|
||||||
|
dir: os.DirFS(localDir),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hfs *HybridFS) Open(name string) (fs.File, error) {
|
||||||
|
// Ensure embed files are not replaced
|
||||||
|
if file, err := hfs.embedFS.Open(name); err == nil {
|
||||||
|
return file, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return hfs.dir.Open(name)
|
||||||
|
}
|
||||||
Vendored
+30
-30
@@ -23,7 +23,7 @@ other = "Home"
|
|||||||
other = "Back to Homepage"
|
other = "Back to Homepage"
|
||||||
|
|
||||||
[AdminPanel]
|
[AdminPanel]
|
||||||
other = "Admin Panel"
|
other = "Dashboard"
|
||||||
|
|
||||||
[Logout]
|
[Logout]
|
||||||
other = "Log out"
|
other = "Log out"
|
||||||
@@ -32,10 +32,10 @@ other = "Logout"
|
|||||||
other = "Log in"
|
other = "Log in"
|
||||||
|
|
||||||
[ConfirmLogout]
|
[ConfirmLogout]
|
||||||
other = "Do you want to Logout?"
|
other = "Confirm Logging Out?"
|
||||||
|
|
||||||
[AfterLoggingOutYouHaveToLoginAgain]
|
[AfterLoggingOutYouHaveToLoginAgain]
|
||||||
other = "You need to Login again to continue"
|
other = "You will need to log in again to continue"
|
||||||
|
|
||||||
[Cancel]
|
[Cancel]
|
||||||
other = "Cancel"
|
other = "Cancel"
|
||||||
@@ -56,7 +56,7 @@ other = "Cron Expression"
|
|||||||
other = "Backup"
|
other = "Backup"
|
||||||
|
|
||||||
[3amDaily]
|
[3amDaily]
|
||||||
other = "(Daily at 3 AM)"
|
other = "(At 3 AM)"
|
||||||
|
|
||||||
[Command]
|
[Command]
|
||||||
other = "Command"
|
other = "Command"
|
||||||
@@ -71,7 +71,7 @@ other = "Ignore All, Execute Only on Specific Servers"
|
|||||||
other = "Cover All, Except Specific Servers"
|
other = "Cover All, Except Specific Servers"
|
||||||
|
|
||||||
[ExecuteByTriggerServer]
|
[ExecuteByTriggerServer]
|
||||||
other = "Execute on Trigger Server"
|
other = "Execute on Alarmed Servers"
|
||||||
|
|
||||||
[SpecificServers]
|
[SpecificServers]
|
||||||
other = "Specific Servers"
|
other = "Specific Servers"
|
||||||
@@ -95,7 +95,7 @@ other = "Cron Task"
|
|||||||
other = "Trigger Task"
|
other = "Trigger Task"
|
||||||
|
|
||||||
[TheFormaOfTheScheduleIs]
|
[TheFormaOfTheScheduleIs]
|
||||||
other = "The Cron Expression is like:"
|
other = "The Cron Expression format is:"
|
||||||
|
|
||||||
[SecondsMinutesHoursDaysMonthsWeeksSeeDetails]
|
[SecondsMinutesHoursDaysMonthsWeeksSeeDetails]
|
||||||
other = "Sec Min Hour Day Month Week, see details in "
|
other = "Sec Min Hour Day Month Week, see details in "
|
||||||
@@ -104,7 +104,7 @@ other = "Sec Min Hour Day Month Week, see details in "
|
|||||||
other = "CRON Expression Format"
|
other = "CRON Expression Format"
|
||||||
|
|
||||||
[IntroductionOfCommands]
|
[IntroductionOfCommands]
|
||||||
other = "Introduction of Commands: Writing commands is similar to shell/bat scripts. It's advised not to break lines, and multiple commands can be connected with <code>&&</code> or <code>&</code>. If a command cannot be found, it might be due to <code>PATH</code> environment variable configuration issues. On <code>Linux</code> servers, you can add <code>source ~/.bashrc</code> at the beginning of the command, or use the command's absolute path."
|
other = "Note on writing commands: It is similar to shell/bat scripts. but it's advised not to start a newline, and connect multiple commands with <code>&&</code> or <code>;</code>. If a command cannot be found, it is possibly due to <code>PATH</code> environment variable issues. On <code>Linux</code> servers, you can add <code>source ~/.bashrc</code> at the beginning of the command, or just use the absolute path."
|
||||||
|
|
||||||
[AddMonitor]
|
[AddMonitor]
|
||||||
other = "Add Service Monitor"
|
other = "Add Service Monitor"
|
||||||
@@ -119,7 +119,7 @@ other = "Target"
|
|||||||
other = "Type"
|
other = "Type"
|
||||||
|
|
||||||
[SslExpirationOrChange]
|
[SslExpirationOrChange]
|
||||||
other = "(SSL Expiration or Change)"
|
other = "(Certificate Expiration and Changes)"
|
||||||
|
|
||||||
[Duration]
|
[Duration]
|
||||||
other = "Interval"
|
other = "Interval"
|
||||||
@@ -134,10 +134,10 @@ other = "Enable Failure Notification"
|
|||||||
other = "Failure Notification"
|
other = "Failure Notification"
|
||||||
|
|
||||||
[MaxLatency]
|
[MaxLatency]
|
||||||
other = "Max Latency (ms)"
|
other = "Maximum Latency (ms)"
|
||||||
|
|
||||||
[MinLatency]
|
[MinLatency]
|
||||||
other = "Min Latency (ms)"
|
other = "Minimum Latency (ms)"
|
||||||
|
|
||||||
[EnableLatencyNotification]
|
[EnableLatencyNotification]
|
||||||
other = "Enable Latency Notification"
|
other = "Enable Latency Notification"
|
||||||
@@ -147,7 +147,7 @@ other = "Latency Notification"
|
|||||||
|
|
||||||
[IntroductionOfMonitor]
|
[IntroductionOfMonitor]
|
||||||
other = """
|
other = """
|
||||||
For type <b>HTTP-GET</b>, enter URL (with http/https, HTTPS protocol will also monitor SSL certificate);<br>
|
For type <b>HTTP-GET</b>, enter URL (with http/https scheme, HTTPS protocol will also monitor SSL certificate);<br>
|
||||||
For type <b>ICMP-Ping</b>, enter domain/IP without port: example.com;<br>
|
For type <b>ICMP-Ping</b>, enter domain/IP without port: example.com;<br>
|
||||||
For type <b>TCP-Ping</b>, enter domain/IP + port number: example.com:22"""
|
For type <b>TCP-Ping</b>, enter domain/IP + port number: example.com:22"""
|
||||||
|
|
||||||
@@ -182,16 +182,16 @@ other = "Notification Trigger Mode"
|
|||||||
other = "Always Trigger"
|
other = "Always Trigger"
|
||||||
|
|
||||||
[ModeOnetimeTrigger]
|
[ModeOnetimeTrigger]
|
||||||
other = "Always Trigger"
|
other = "Trigger Once"
|
||||||
|
|
||||||
[EnableTriggerTask]
|
[EnableTriggerTask]
|
||||||
other = "Enable Trigger Task"
|
other = "Enable Trigger Task"
|
||||||
|
|
||||||
[FailTriggerTasks]
|
[FailTriggerTasks]
|
||||||
other = "Tasks triggered during an alarm"
|
other = "Tasks to trigger on an alarm"
|
||||||
|
|
||||||
[RecoverTriggerTasks]
|
[RecoverTriggerTasks]
|
||||||
other = "Tasks triggered during recovery"
|
other = "Tasks to trigger after recovery"
|
||||||
|
|
||||||
[Enable]
|
[Enable]
|
||||||
other = "Enable"
|
other = "Enable"
|
||||||
@@ -218,7 +218,7 @@ other = "Einstein Lightspeed 1"
|
|||||||
other = "Display Index"
|
other = "Display Index"
|
||||||
|
|
||||||
[TheLargerTheNumberTheHigherThePriority]
|
[TheLargerTheNumberTheHigherThePriority]
|
||||||
other = "Higher Number, Higher Priority"
|
other = "Greater Number, Higher Priority"
|
||||||
|
|
||||||
[Secret]
|
[Secret]
|
||||||
other = "Secret"
|
other = "Secret"
|
||||||
@@ -230,10 +230,10 @@ other = "Note"
|
|||||||
other = "Public Note"
|
other = "Public Note"
|
||||||
|
|
||||||
[LinuxOneKeyInstall]
|
[LinuxOneKeyInstall]
|
||||||
other = "Linux One-Command Install"
|
other = "Linux Installation Command"
|
||||||
|
|
||||||
[NoDomainAlert]
|
[NoDomainAlert]
|
||||||
other = "Set CDN bypassed Domain/IP in setting before using this function"
|
other = "Please set the IP (or a domain pointed to it) of the original server in settings"
|
||||||
|
|
||||||
[PushSuccessfully]
|
[PushSuccessfully]
|
||||||
other = "Send Success Notification"
|
other = "Send Success Notification"
|
||||||
@@ -254,13 +254,13 @@ other = "Cover All"
|
|||||||
other = "Ignore All"
|
other = "Ignore All"
|
||||||
|
|
||||||
[ByTrigger]
|
[ByTrigger]
|
||||||
other = "Execute by Trigger"
|
other = "Alarmed Servers"
|
||||||
|
|
||||||
[DeleteScheduledTask]
|
[DeleteScheduledTask]
|
||||||
other = "Delete Scheduled Task"
|
other = "Delete Scheduled Task"
|
||||||
|
|
||||||
[ConfirmToDeleteThisScheduledTask]
|
[ConfirmToDeleteThisScheduledTask]
|
||||||
other = "Are you sure you want to Delete?"
|
other = "Confirm deletion?"
|
||||||
|
|
||||||
[AccessDenied]
|
[AccessDenied]
|
||||||
other = "Access Denied"
|
other = "Access Denied"
|
||||||
@@ -281,22 +281,22 @@ other = "Don't Have an Account?"
|
|||||||
other = "HTTP(S)/SSL Certificate"
|
other = "HTTP(S)/SSL Certificate"
|
||||||
|
|
||||||
[TCPPort]
|
[TCPPort]
|
||||||
other = "TCP Port"
|
other = "TCPing"
|
||||||
|
|
||||||
[DeleteService]
|
[DeleteService]
|
||||||
other = "Remove Service Monitor"
|
other = "Remove Service Monitor"
|
||||||
|
|
||||||
[ConfirmToDeleteThisService]
|
[ConfirmToDeleteThisService]
|
||||||
other = "Confirm to Delete This Service?"
|
other = "Confirm Deleting This Service?"
|
||||||
|
|
||||||
[DeleteNotificationMethod]
|
[DeleteNotificationMethod]
|
||||||
other = "Delete Notification Method"
|
other = "Delete Notification Method"
|
||||||
|
|
||||||
[ConfirmToDeleteThisNotificationMethod]
|
[ConfirmToDeleteThisNotificationMethod]
|
||||||
other = "Confirm to Delete This Notification Method?"
|
other = "Confirm Deleting This Notification Method?"
|
||||||
|
|
||||||
[ForceUpdate]
|
[ForceUpdate]
|
||||||
other = "Force to Update"
|
other = "Trigger Agent Update"
|
||||||
|
|
||||||
[SelectAll]
|
[SelectAll]
|
||||||
other = "Select All"
|
other = "Select All"
|
||||||
@@ -305,7 +305,7 @@ other = "Select All"
|
|||||||
other = "Version"
|
other = "Version"
|
||||||
|
|
||||||
[OneKeyInstall]
|
[OneKeyInstall]
|
||||||
other = "One-Command Install"
|
other = "Installation commands"
|
||||||
|
|
||||||
[ClickToCopy]
|
[ClickToCopy]
|
||||||
other = "Click To Copy"
|
other = "Click To Copy"
|
||||||
@@ -314,7 +314,7 @@ other = "Click To Copy"
|
|||||||
other = "Delete Server"
|
other = "Delete Server"
|
||||||
|
|
||||||
[ConfirmToDeleteServer]
|
[ConfirmToDeleteServer]
|
||||||
other = "Confirm to Delete Server?"
|
other = "Confirm Deleting This Server?"
|
||||||
|
|
||||||
[NoServerSelected]
|
[NoServerSelected]
|
||||||
other = "No Server Selected"
|
other = "No Server Selected"
|
||||||
@@ -545,7 +545,7 @@ other = "Scheduled Tasks"
|
|||||||
other = "API Management"
|
other = "API Management"
|
||||||
|
|
||||||
[IssueNewApiToken]
|
[IssueNewApiToken]
|
||||||
other = "Issue New API Token"
|
other = "Generate New API Token"
|
||||||
|
|
||||||
[Token]
|
[Token]
|
||||||
other = "Token"
|
other = "Token"
|
||||||
@@ -554,10 +554,10 @@ other = "Token"
|
|||||||
other = "Delete Token"
|
other = "Delete Token"
|
||||||
|
|
||||||
[ConfirmToDeleteThisToken]
|
[ConfirmToDeleteThisToken]
|
||||||
other = "Confirm to Delete This Token?"
|
other = "Confirm Deleting This Token?"
|
||||||
|
|
||||||
[YouAreNotAuthorized]
|
[YouAreNotAuthorized]
|
||||||
other = "Not Authorized for This Page"
|
other = "This Page needs Authorization"
|
||||||
|
|
||||||
[WrongAccessPassword]
|
[WrongAccessPassword]
|
||||||
other = "Wrong Access Password"
|
other = "Wrong Access Password"
|
||||||
@@ -605,13 +605,13 @@ other = "IP Changed"
|
|||||||
other = "Remaining Data"
|
other = "Remaining Data"
|
||||||
|
|
||||||
[DashboardTheme]
|
[DashboardTheme]
|
||||||
other = "Admin Panel Theme"
|
other = "Dashboard Theme"
|
||||||
|
|
||||||
[Info]
|
[Info]
|
||||||
other = "Info"
|
other = "Info"
|
||||||
|
|
||||||
[HideForGuest]
|
[HideForGuest]
|
||||||
other = "Hide for Guest"
|
other = "Hide from Guest"
|
||||||
|
|
||||||
[Menu]
|
[Menu]
|
||||||
other = "Menu"
|
other = "Menu"
|
||||||
|
|||||||
Vendored
+4
-4
@@ -71,7 +71,7 @@ other = "Ignorar todo, ejecutar solo a través de servidores específicos"
|
|||||||
other = "Incluir todo, excepto servidores específicos"
|
other = "Incluir todo, excepto servidores específicos"
|
||||||
|
|
||||||
[ExecuteByTriggerServer]
|
[ExecuteByTriggerServer]
|
||||||
other = "Ejecutar por servidor disparador"
|
other = "Ejecutar en servidores con alarma"
|
||||||
|
|
||||||
[SpecificServers]
|
[SpecificServers]
|
||||||
other = "Servidores Específicos"
|
other = "Servidores Específicos"
|
||||||
@@ -104,7 +104,7 @@ other = "Segundos, Minutos, Horas, Días, Meses, Semanas - Ver detalles a"
|
|||||||
other = "Formato de Expresión de Cron"
|
other = "Formato de Expresión de Cron"
|
||||||
|
|
||||||
[IntroductionOfCommands]
|
[IntroductionOfCommands]
|
||||||
other = "Introducción de Comandos: Escriba comandos como en scripts shell/bat. Evite saltos de línea; conecte múltiples comandos con <code>&&</code> o <code>&</code>. Si no se encuentra un comando, verifique la variable de entorno <code>PATH</code>. En servidores <code>Linux</code>, antepóngale <code>source ~/.bashrc</code> a los comandos, o use rutas absolutas."
|
other = "Introducción de Comandos: Escriba comandos como en scripts shell/bat. Evite saltos de línea; conecte múltiples comandos con <code>&&</code> o <code>;</code>. Si no se encuentra un comando, verifique la variable de entorno <code>PATH</code>. En servidores <code>Linux</code>, antepóngale <code>source ~/.bashrc</code> a los comandos, o use rutas absolutas."
|
||||||
|
|
||||||
[AddMonitor]
|
[AddMonitor]
|
||||||
other = "Agregar Monitor de Servicio"
|
other = "Agregar Monitor de Servicio"
|
||||||
@@ -254,7 +254,7 @@ other = "Cubrir Todo"
|
|||||||
other = "Ignorar Todo"
|
other = "Ignorar Todo"
|
||||||
|
|
||||||
[ByTrigger]
|
[ByTrigger]
|
||||||
other = "Ejecutar por Disparador"
|
other = "Servidores con alarma"
|
||||||
|
|
||||||
[DeleteScheduledTask]
|
[DeleteScheduledTask]
|
||||||
other = "Eliminar Tarea Programada"
|
other = "Eliminar Tarea Programada"
|
||||||
@@ -281,7 +281,7 @@ other = "¿No tiene una cuenta?"
|
|||||||
other = "Certificados HTTP(S)/SSL"
|
other = "Certificados HTTP(S)/SSL"
|
||||||
|
|
||||||
[TCPPort]
|
[TCPPort]
|
||||||
other = "Puerto TCP"
|
other = "TCPing"
|
||||||
|
|
||||||
[DeleteService]
|
[DeleteService]
|
||||||
other = "Eliminar Servicio"
|
other = "Eliminar Servicio"
|
||||||
|
|||||||
Vendored
+5
-5
@@ -71,7 +71,7 @@ other = "忽略所有,仅通过特定服务器执行"
|
|||||||
other = "覆盖所有,仅特定服务器不执行"
|
other = "覆盖所有,仅特定服务器不执行"
|
||||||
|
|
||||||
[ExecuteByTriggerServer]
|
[ExecuteByTriggerServer]
|
||||||
other = "由触发的服务器执行"
|
other = "由触发告警的服务器执行"
|
||||||
|
|
||||||
[SpecificServers]
|
[SpecificServers]
|
||||||
other = "特定服务器"
|
other = "特定服务器"
|
||||||
@@ -104,7 +104,7 @@ other = "秒 分 时 天 月 星期,详情见"
|
|||||||
other = "计划表达式格式"
|
other = "计划表达式格式"
|
||||||
|
|
||||||
[IntroductionOfCommands]
|
[IntroductionOfCommands]
|
||||||
other = "命令说明:编写命令时类似于 shell/bat 脚本。建议不要换行,多个命令可用 <code>&&</code> 或 <code>&</code> 连接,若出现命令无法找到的情况,可能是由于 <code>PATH</code> 环境变量配置问题。在 <code>Linux</code> 服务器上,可在命令开头加入 <code>source ~/.bashrc</code>,或使用命令的绝对路径执行。"
|
other = "命令说明:编写命令时类似于 shell/bat 脚本。建议不要换行,多个命令可用 <code>&&</code> 或 <code>;</code> 连接,若出现命令无法找到的情况,可能是由于 <code>PATH</code> 环境变量配置问题。在 <code>Linux</code> 服务器上,可在命令开头加入 <code>source ~/.bashrc</code>,或使用命令的绝对路径执行。"
|
||||||
|
|
||||||
[AddMonitor]
|
[AddMonitor]
|
||||||
other = "新增监控"
|
other = "新增监控"
|
||||||
@@ -254,7 +254,7 @@ other = "覆盖所有"
|
|||||||
other = "忽略所有"
|
other = "忽略所有"
|
||||||
|
|
||||||
[ByTrigger]
|
[ByTrigger]
|
||||||
other = "触发执行"
|
other = "触发告警的服务器"
|
||||||
|
|
||||||
[DeleteScheduledTask]
|
[DeleteScheduledTask]
|
||||||
other = "删除计划任务"
|
other = "删除计划任务"
|
||||||
@@ -281,7 +281,7 @@ other = "没有账号?"
|
|||||||
other = "HTTP(S)/SSL证书"
|
other = "HTTP(S)/SSL证书"
|
||||||
|
|
||||||
[TCPPort]
|
[TCPPort]
|
||||||
other = "TCP 端口"
|
other = "TCPing"
|
||||||
|
|
||||||
[DeleteService]
|
[DeleteService]
|
||||||
other = "移除服务"
|
other = "移除服务"
|
||||||
@@ -425,7 +425,7 @@ other = "活动"
|
|||||||
other = "版本"
|
other = "版本"
|
||||||
|
|
||||||
[NetSpeed]
|
[NetSpeed]
|
||||||
other = "网络"
|
other = "网速"
|
||||||
|
|
||||||
[Uptime]
|
[Uptime]
|
||||||
other = "在线"
|
other = "在线"
|
||||||
|
|||||||
Vendored
+5
-5
@@ -71,7 +71,7 @@ other = "忽略所有,僅通過特定伺服器執行"
|
|||||||
other = "覆蓋所有,僅特定伺服器不執行"
|
other = "覆蓋所有,僅特定伺服器不執行"
|
||||||
|
|
||||||
[ExecuteByTriggerServer]
|
[ExecuteByTriggerServer]
|
||||||
other = "由觸發的伺服器執行"
|
other = "由觸發告警的伺服器執行"
|
||||||
|
|
||||||
[SpecificServers]
|
[SpecificServers]
|
||||||
other = "特定伺服器"
|
other = "特定伺服器"
|
||||||
@@ -104,7 +104,7 @@ other = "秒 分 時 天 月 星期,詳情見"
|
|||||||
other = "排程表達式格式"
|
other = "排程表達式格式"
|
||||||
|
|
||||||
[IntroductionOfCommands]
|
[IntroductionOfCommands]
|
||||||
other = "命令說明:編寫命令時類似於 shell/bat 腳本。建議不要換行,多個命令可用 <code>&&</code> 或 <code>&</code> 連接,若出現命令無法找到的情況,可能是由於 <code>PATH</code> 環境變數配置問題。在 <code>Linux</code> 伺服器上,可在命令開頭加入 <code>source ~/.bashrc</code>,或使用命令的絕對路徑執行。"
|
other = "命令說明:編寫命令時類似於 shell/bat 腳本。建議不要換行,多個命令可用 <code>&&</code> 或 <code>;</code> 連接,若出現命令無法找到的情況,可能是由於 <code>PATH</code> 環境變數配置問題。在 <code>Linux</code> 伺服器上,可在命令開頭加入 <code>source ~/.bashrc</code>,或使用命令的絕對路徑執行。"
|
||||||
|
|
||||||
[AddMonitor]
|
[AddMonitor]
|
||||||
other = "新增監控"
|
other = "新增監控"
|
||||||
@@ -254,7 +254,7 @@ other = "覆蓋所有"
|
|||||||
other = "忽略所有"
|
other = "忽略所有"
|
||||||
|
|
||||||
[ByTrigger]
|
[ByTrigger]
|
||||||
other = "觸發執行"
|
other = "觸發告警的伺服器"
|
||||||
|
|
||||||
[DeleteScheduledTask]
|
[DeleteScheduledTask]
|
||||||
other = "刪除排程任務"
|
other = "刪除排程任務"
|
||||||
@@ -281,7 +281,7 @@ other = "沒有帳號?"
|
|||||||
other = "HTTP(S)/SSL證書"
|
other = "HTTP(S)/SSL證書"
|
||||||
|
|
||||||
[TCPPort]
|
[TCPPort]
|
||||||
other = "TCP 端口"
|
other = "TCPing"
|
||||||
|
|
||||||
[DeleteService]
|
[DeleteService]
|
||||||
other = "移除服務"
|
other = "移除服務"
|
||||||
@@ -425,7 +425,7 @@ other = "活動"
|
|||||||
other = "版本"
|
other = "版本"
|
||||||
|
|
||||||
[NetSpeed]
|
[NetSpeed]
|
||||||
other = "網路"
|
other = "網速"
|
||||||
|
|
||||||
[Uptime]
|
[Uptime]
|
||||||
other = "在線"
|
other = "在線"
|
||||||
|
|||||||
Vendored
+13
-1
@@ -2,10 +2,14 @@ package resource
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"embed"
|
"embed"
|
||||||
|
|
||||||
|
"github.com/naiba/nezha/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var StaticFS *utils.HybridFS
|
||||||
|
|
||||||
//go:embed static
|
//go:embed static
|
||||||
var StaticFS embed.FS
|
var staticFS embed.FS
|
||||||
|
|
||||||
//go:embed template
|
//go:embed template
|
||||||
var TemplateFS embed.FS
|
var TemplateFS embed.FS
|
||||||
@@ -13,6 +17,14 @@ var TemplateFS embed.FS
|
|||||||
//go:embed l10n
|
//go:embed l10n
|
||||||
var I18nFS embed.FS
|
var I18nFS embed.FS
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
var err error
|
||||||
|
StaticFS, err = utils.NewHybridFS(staticFS, "static", "resource/static/custom")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func IsTemplateFileExist(name string) bool {
|
func IsTemplateFileExist(name string) bool {
|
||||||
_, err := TemplateFS.Open(name)
|
_, err := TemplateFS.Open(name)
|
||||||
return err == nil
|
return err == nil
|
||||||
|
|||||||
+6
-2
@@ -237,8 +237,8 @@ tr.accordion-toggle{
|
|||||||
}
|
}
|
||||||
|
|
||||||
.node-cell.network {
|
.node-cell.network {
|
||||||
min-width: 110px;
|
min-width: 100px;
|
||||||
max-width: 110px;
|
max-width: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.node-cell.traffic {
|
.node-cell.traffic {
|
||||||
@@ -549,6 +549,10 @@ footer p{
|
|||||||
display: none;
|
display: none;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
.node-cell.network {
|
||||||
|
min-width: 95px;
|
||||||
|
max-width: 95px;
|
||||||
|
}
|
||||||
.node-cell.remaining {
|
.node-cell.remaining {
|
||||||
min-width: 75px;
|
min-width: 75px;
|
||||||
max-width: 75px;
|
max-width: 75px;
|
||||||
|
|||||||
+2
-2
@@ -28,8 +28,8 @@
|
|||||||
</span>
|
</span>
|
||||||
</aside>
|
</aside>
|
||||||
<template v-if="semiTransparent">
|
<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/light.plus.css?v20241008">
|
||||||
<link rel="stylesheet" href="/static/theme-server-status/css/dark.plus.css?v20240807">
|
<link rel="stylesheet" href="/static/theme-server-status/css/dark.plus.css?v20241008">
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
{{if ts .CustomCode}}{{.CustomCode|safe}}{{end}}
|
{{if ts .CustomCode}}{{.CustomCode|safe}}{{end}}
|
||||||
|
|||||||
+3
-3
@@ -26,9 +26,9 @@
|
|||||||
<script src="https://unpkg.com/[email protected]/dist/js/bootstrap.min.js"></script>
|
<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/vue.min.js"></script>
|
||||||
<script src="https://unpkg.com/[email protected]/dist/echarts.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?v20240929">
|
<link rel="stylesheet" href="/static/theme-server-status/css/main.css?v20241008">
|
||||||
<link rel="stylesheet" href="/static/theme-server-status/css/dark.css?v202409029">
|
<link rel="stylesheet" href="/static/theme-server-status/css/dark.css?v20241008">
|
||||||
<link rel="stylesheet" href="/static/theme-server-status/css/light.css?v20240916">
|
<link rel="stylesheet" href="/static/theme-server-status/css/light.css?v20241008">
|
||||||
<script src="/static/theme-server-status/js/mixin.js?v20240915"></script>
|
<script src="/static/theme-server-status/js/mixin.js?v20240915"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -32,15 +32,16 @@
|
|||||||
<i v-if='isWindowsPlatform(node.host.Platform)' class="windows icon"></i>
|
<i v-if='isWindowsPlatform(node.host.Platform)' class="windows icon"></i>
|
||||||
<i v-else-if='getFontLogoClass(node.host.Platform) == "" && node.stateuptime > 0' class="fl-tux"></i>
|
<i v-else-if='getFontLogoClass(node.host.Platform) == "" && node.stateuptime > 0' class="fl-tux"></i>
|
||||||
<i v-else :class="'fl-' + getFontLogoClass(node.host.Platform)"></i>
|
<i v-else :class="'fl-' + getFontLogoClass(node.host.Platform)"></i>
|
||||||
<span class="node-cell-os-text">@#getPlatformName(node.os)#@</span>
|
<span class="node-cell-os-text">@#getPlatformName(node.os) === '' && node.stateuptime > 0 ? 'linux' : getPlatformName(node.os)#@</span>
|
||||||
</td>
|
</td>
|
||||||
<td style="text-align: center;" class="node-cell location">
|
<td class="node-cell location center">
|
||||||
<i :class="'fi fi-' + node.location"></i>
|
<i :class="'fi fi-' + (node.stateuptime > 0 ? (node.location || 'rb') : '')"></i>
|
||||||
<span class="node-cell-location-text text-uppercase">@#node.location#@</span>
|
<span class="node-cell-location-text text-uppercase">@#node.stateuptime > 0 ? (node.location || 'RB') : ''#@</span>
|
||||||
</td>
|
</td>
|
||||||
<td v-if="nodesNoTag.some(item => item.additional && item.additional.price && Object.keys(item.additional.price).length > 0)" class="node-cell price center">
|
<td v-if="nodesNoTag.some(item => item.additional && item.additional.price && Object.keys(item.additional.price).length > 0)" class="node-cell price center">
|
||||||
<template v-if="node.additional && node.additional.price">
|
<template v-if="node.additional && node.additional.price">
|
||||||
<span v-if="node.additional.price.amount == 0" class="node-cell-price-text">Free</span>
|
<span v-if="node.additional.price.amount == 0" class="node-cell-price-text">FREE</span>
|
||||||
|
<span v-else-if="node.additional.price.amount == -1" class="node-cell-price-text">PAYG</span>
|
||||||
<span v-else class="node-cell-price-text">@#node.additional.price.amount#@@#(node.additional.price.cycle ? '/' + node.additional.price.cycle : '')#@</span>
|
<span v-else class="node-cell-price-text">@#node.additional.price.amount#@@#(node.additional.price.cycle ? '/' + node.additional.price.cycle : '')#@</span>
|
||||||
</template>
|
</template>
|
||||||
</td>
|
</td>
|
||||||
@@ -89,8 +90,8 @@
|
|||||||
<div style="display: flex;left-items: center;justify-content: center;flex-direction: column; max-width: 89vw">
|
<div style="display: flex;left-items: center;justify-content: center;flex-direction: column; max-width: 89vw">
|
||||||
<span class="node-cell-expand">
|
<span class="node-cell-expand">
|
||||||
<span class="node-cell-expand-label">{{tr "Platform"}}:</span>
|
<span class="node-cell-expand-label">{{tr "Platform"}}:</span>
|
||||||
@#node.host.Platform#@-@#node.host.PlatformVersion#@
|
<span v-if="node.host.Platform">@#node.host.Platform#@@#node.host.PlatformVersion ? '-' + node.host.PlatformVersion : ''#@</span>
|
||||||
[<span v-if="node.host.Virtualization">@#node.host.Virtualization#@:</span>@#node.host.Arch#@]
|
<span v-if="node.host.Arch">[@#node.host.Virtualization ? node.host.Virtualization + ':' : ''#@@#node.host.Arch#@]</span>
|
||||||
</span>
|
</span>
|
||||||
<span class="node-cell-expand" v-if="node.host.CPU">
|
<span class="node-cell-expand" v-if="node.host.CPU">
|
||||||
<span class="node-cell-expand-label">CPU:</span>
|
<span class="node-cell-expand-label">CPU:</span>
|
||||||
|
|||||||
@@ -35,15 +35,16 @@
|
|||||||
<i v-if='isWindowsPlatform(node.host.Platform)' class="windows icon"></i>
|
<i v-if='isWindowsPlatform(node.host.Platform)' class="windows icon"></i>
|
||||||
<i v-else-if='getFontLogoClass(node.host.Platform) == "" && node.stateuptime > 0' class="fl-tux"></i>
|
<i v-else-if='getFontLogoClass(node.host.Platform) == "" && node.stateuptime > 0' class="fl-tux"></i>
|
||||||
<i v-else :class="'fl-' + getFontLogoClass(node.host.Platform)"></i>
|
<i v-else :class="'fl-' + getFontLogoClass(node.host.Platform)"></i>
|
||||||
<span class="node-cell-os-text">@#getPlatformName(node.os)#@</span>
|
<span class="node-cell-os-text">@#getPlatformName(node.os) === '' && node.stateuptime > 0 ? 'linux' : getPlatformName(node.os)#@</span>
|
||||||
</td>
|
</td>
|
||||||
<td style="text-align: center;" class="node-cell location">
|
<td class="node-cell location center">
|
||||||
<i :class="'fi fi-' + node.location"></i>
|
<i :class="'fi fi-' + (node.stateuptime > 0 ? (node.location || 'rb') : '')"></i>
|
||||||
<span class="node-cell-location-text text-uppercase"> @#node.location#@</span>
|
<span class="node-cell-location-text text-uppercase">@#node.stateuptime > 0 ? (node.location || 'RB') : ''#@</span>
|
||||||
</td>
|
</td>
|
||||||
<td v-if="group.data.some(item => item.additional && item.additional.price && Object.keys(item.additional.price).length > 0)" class="node-cell price center">
|
<td v-if="group.data.some(item => item.additional && item.additional.price && Object.keys(item.additional.price).length > 0)" class="node-cell price center">
|
||||||
<template v-if="node.additional && node.additional.price">
|
<template v-if="node.additional && node.additional.price">
|
||||||
<span v-if="node.additional.price.amount == 0" class="node-cell-price-text">Free</span>
|
<span v-if="node.additional.price.amount == 0" class="node-cell-price-text">FREE</span>
|
||||||
|
<span v-else-if="node.additional.price.amount == -1" class="node-cell-price-text">PAYG</span>
|
||||||
<span v-else class="node-cell-price-text">@#node.additional.price.amount#@@#(node.additional.price.cycle ? '/' + node.additional.price.cycle : '')#@</span>
|
<span v-else class="node-cell-price-text">@#node.additional.price.amount#@@#(node.additional.price.cycle ? '/' + node.additional.price.cycle : '')#@</span>
|
||||||
</template>
|
</template>
|
||||||
</td>
|
</td>
|
||||||
@@ -92,8 +93,8 @@
|
|||||||
<div style="display: flex;left-items: center;justify-content: center;flex-direction: column; max-width: 89vw">
|
<div style="display: flex;left-items: center;justify-content: center;flex-direction: column; max-width: 89vw">
|
||||||
<span class="node-cell-expand">
|
<span class="node-cell-expand">
|
||||||
<span class="node-cell-expand-label">{{tr "Platform"}}:</span>
|
<span class="node-cell-expand-label">{{tr "Platform"}}:</span>
|
||||||
@#node.host.Platform#@-@#node.host.PlatformVersion#@
|
<span v-if="node.host.Platform">@#node.host.Platform#@@#node.host.PlatformVersion ? '-' + node.host.PlatformVersion : ''#@</span>
|
||||||
[<span v-if="node.host.Virtualization">@#node.host.Virtualization#@:</span>@#node.host.Arch#@]
|
<span v-if="node.host.Arch">[@#node.host.Virtualization ? node.host.Virtualization + ':' : ''#@@#node.host.Arch#@]</span>
|
||||||
</span>
|
</span>
|
||||||
<span class="node-cell-expand" v-if="node.host.CPU">
|
<span class="node-cell-expand" v-if="node.host.CPU">
|
||||||
<span class="node-cell-expand-label">CPU:</span>
|
<span class="node-cell-expand-label">CPU:</span>
|
||||||
|
|||||||
@@ -819,6 +819,7 @@
|
|||||||
|
|
||||||
switch (cycle) {
|
switch (cycle) {
|
||||||
case '月':
|
case '月':
|
||||||
|
case 'mo':
|
||||||
case 'month':
|
case 'month':
|
||||||
case 'monthly':
|
case 'monthly':
|
||||||
case 'm':
|
case 'm':
|
||||||
@@ -834,6 +835,7 @@
|
|||||||
case 'h':
|
case 'h':
|
||||||
return "H";
|
return "H";
|
||||||
case '年':
|
case '年':
|
||||||
|
case 'yr':
|
||||||
case 'year':
|
case 'year':
|
||||||
case 'annually':
|
case 'annually':
|
||||||
case 'y':
|
case 'y':
|
||||||
|
|||||||
+8
-4
@@ -12,7 +12,7 @@ NZ_DASHBOARD_PATH="${NZ_BASE_PATH}/dashboard"
|
|||||||
NZ_AGENT_PATH="${NZ_BASE_PATH}/agent"
|
NZ_AGENT_PATH="${NZ_BASE_PATH}/agent"
|
||||||
NZ_DASHBOARD_SERVICE="/etc/systemd/system/nezha-dashboard.service"
|
NZ_DASHBOARD_SERVICE="/etc/systemd/system/nezha-dashboard.service"
|
||||||
NZ_DASHBOARD_SERVICERC="/etc/init.d/nezha-dashboard"
|
NZ_DASHBOARD_SERVICERC="/etc/init.d/nezha-dashboard"
|
||||||
NZ_VERSION="v0.19.0"
|
NZ_VERSION="v0.19.2"
|
||||||
|
|
||||||
red='\033[0;31m'
|
red='\033[0;31m'
|
||||||
green='\033[0;32m'
|
green='\033[0;32m'
|
||||||
@@ -65,6 +65,8 @@ geo_check() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pre_check() {
|
pre_check() {
|
||||||
|
umask 077
|
||||||
|
|
||||||
## os_arch
|
## os_arch
|
||||||
if uname -m | grep -q 'x86_64'; then
|
if uname -m | grep -q 'x86_64'; then
|
||||||
os_arch="amd64"
|
os_arch="amd64"
|
||||||
@@ -288,8 +290,6 @@ install_dashboard() {
|
|||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
sudo chmod -R 700 $NZ_DASHBOARD_PATH
|
|
||||||
|
|
||||||
if [ "$IS_DOCKER_NEZHA" = 1 ]; then
|
if [ "$IS_DOCKER_NEZHA" = 1 ]; then
|
||||||
install_dashboard_docker
|
install_dashboard_docker
|
||||||
elif [ "$IS_DOCKER_NEZHA" = 0 ]; then
|
elif [ "$IS_DOCKER_NEZHA" = 0 ]; then
|
||||||
@@ -375,7 +375,6 @@ install_agent() {
|
|||||||
|
|
||||||
# 哪吒监控文件夹
|
# 哪吒监控文件夹
|
||||||
sudo mkdir -p $NZ_AGENT_PATH
|
sudo mkdir -p $NZ_AGENT_PATH
|
||||||
sudo chmod -R 700 $NZ_AGENT_PATH
|
|
||||||
|
|
||||||
echo "正在下载监控端"
|
echo "正在下载监控端"
|
||||||
if [ -z "$CN" ]; then
|
if [ -z "$CN" ]; then
|
||||||
@@ -454,12 +453,17 @@ modify_dashboard_config() {
|
|||||||
echo "> 修改面板配置"
|
echo "> 修改面板配置"
|
||||||
|
|
||||||
if [ "$IS_DOCKER_NEZHA" = 1 ]; then
|
if [ "$IS_DOCKER_NEZHA" = 1 ]; then
|
||||||
|
if [ ! -z "$DOCKER_COMPOSE_COMMAND" ]; then
|
||||||
echo "正在下载 Docker 脚本"
|
echo "正在下载 Docker 脚本"
|
||||||
wget -t 2 -T 60 -O /tmp/nezha-docker-compose.yaml https://${GITHUB_RAW_URL}/script/docker-compose.yaml >/dev/null 2>&1
|
wget -t 2 -T 60 -O /tmp/nezha-docker-compose.yaml https://${GITHUB_RAW_URL}/script/docker-compose.yaml >/dev/null 2>&1
|
||||||
if [ $? != 0 ]; then
|
if [ $? != 0 ]; then
|
||||||
err "下载脚本失败,请检查本机能否连接 ${GITHUB_RAW_URL}"
|
err "下载脚本失败,请检查本机能否连接 ${GITHUB_RAW_URL}"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
else
|
||||||
|
err "请手动安装 docker-compose。https://docs.docker.com/compose/install/linux/"
|
||||||
|
before_show_menu
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
wget -t 2 -T 60 -O /tmp/nezha-config.yaml https://${GITHUB_RAW_URL}/script/config.yaml >/dev/null 2>&1
|
wget -t 2 -T 60 -O /tmp/nezha-config.yaml https://${GITHUB_RAW_URL}/script/config.yaml >/dev/null 2>&1
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ NZ_DASHBOARD_PATH="${NZ_BASE_PATH}/dashboard"
|
|||||||
NZ_AGENT_PATH="${NZ_BASE_PATH}/agent"
|
NZ_AGENT_PATH="${NZ_BASE_PATH}/agent"
|
||||||
NZ_DASHBOARD_SERVICE="/etc/systemd/system/nezha-dashboard.service"
|
NZ_DASHBOARD_SERVICE="/etc/systemd/system/nezha-dashboard.service"
|
||||||
NZ_DASHBOARD_SERVICERC="/etc/init.d/nezha-dashboard"
|
NZ_DASHBOARD_SERVICERC="/etc/init.d/nezha-dashboard"
|
||||||
NZ_VERSION="v0.19.0"
|
NZ_VERSION="v0.19.2"
|
||||||
|
|
||||||
red='\033[0;31m'
|
red='\033[0;31m'
|
||||||
green='\033[0;32m'
|
green='\033[0;32m'
|
||||||
@@ -65,6 +65,8 @@ geo_check() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pre_check() {
|
pre_check() {
|
||||||
|
umask 077
|
||||||
|
|
||||||
## os_arch
|
## os_arch
|
||||||
if uname -m | grep -q 'x86_64'; then
|
if uname -m | grep -q 'x86_64'; then
|
||||||
os_arch="amd64"
|
os_arch="amd64"
|
||||||
@@ -287,8 +289,6 @@ install_dashboard() {
|
|||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
sudo chmod -R 700 $NZ_DASHBOARD_PATH
|
|
||||||
|
|
||||||
if [ "$IS_DOCKER_NEZHA" = 1 ]; then
|
if [ "$IS_DOCKER_NEZHA" = 1 ]; then
|
||||||
install_dashboard_docker
|
install_dashboard_docker
|
||||||
elif [ "$IS_DOCKER_NEZHA" = 0 ]; then
|
elif [ "$IS_DOCKER_NEZHA" = 0 ]; then
|
||||||
@@ -374,7 +374,6 @@ install_agent() {
|
|||||||
|
|
||||||
# Nezha Monitoring Folder
|
# Nezha Monitoring Folder
|
||||||
sudo mkdir -p $NZ_AGENT_PATH
|
sudo mkdir -p $NZ_AGENT_PATH
|
||||||
sudo chmod -R 700 $NZ_AGENT_PATH
|
|
||||||
|
|
||||||
echo "Downloading Agent"
|
echo "Downloading Agent"
|
||||||
wget -t 2 -T 60 -O nezha-agent_linux_${os_arch}.zip https://${GITHUB_URL}/nezhahq/agent/releases/download/${version}/nezha-agent_linux_${os_arch}.zip >/dev/null 2>&1
|
wget -t 2 -T 60 -O nezha-agent_linux_${os_arch}.zip https://${GITHUB_URL}/nezhahq/agent/releases/download/${version}/nezha-agent_linux_${os_arch}.zip >/dev/null 2>&1
|
||||||
@@ -448,12 +447,17 @@ modify_dashboard_config() {
|
|||||||
echo "> Modify Dashboard Configuration"
|
echo "> Modify Dashboard Configuration"
|
||||||
|
|
||||||
if [ "$IS_DOCKER_NEZHA" = 1 ]; then
|
if [ "$IS_DOCKER_NEZHA" = 1 ]; then
|
||||||
|
if [ ! -z "$DOCKER_COMPOSE_COMMAND" ]; then
|
||||||
echo "Download Docker Script"
|
echo "Download Docker Script"
|
||||||
wget -t 2 -T 60 -O /tmp/nezha-docker-compose.yaml https://${GITHUB_RAW_URL}/script/docker-compose.yaml >/dev/null 2>&1
|
wget -t 2 -T 60 -O /tmp/nezha-docker-compose.yaml https://${GITHUB_RAW_URL}/script/docker-compose.yaml >/dev/null 2>&1
|
||||||
if [ $? != 0 ]; then
|
if [ $? != 0 ]; then
|
||||||
err "Script failed to get, please check if the network can link ${GITHUB_RAW_URL}"
|
err "Script failed to get, please check if the network can link ${GITHUB_RAW_URL}"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
else
|
||||||
|
err "请手动安装 docker-compose。https://docs.docker.com/compose/install/linux/"
|
||||||
|
before_show_menu
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
wget -t 2 -T 60 -O /tmp/nezha-config.yaml https://${GITHUB_RAW_URL}/script/config.yaml >/dev/null 2>&1
|
wget -t 2 -T 60 -O /tmp/nezha-config.yaml https://${GITHUB_RAW_URL}/script/config.yaml >/dev/null 2>&1
|
||||||
|
|||||||
@@ -155,8 +155,8 @@ func (s *NezhaHandler) ReportSystemInfo(c context.Context, r *pb.Host) (*pb.Rece
|
|||||||
Ipv4Addr: ipv4,
|
Ipv4Addr: ipv4,
|
||||||
Ipv6Addr: ipv6,
|
Ipv6Addr: ipv6,
|
||||||
}
|
}
|
||||||
go singleton.RetryableUpdateDomain(provider, config, maxRetries)
|
|
||||||
|
|
||||||
|
go singleton.RetryableUpdateDomain(provider, config, maxRetries)
|
||||||
} else {
|
} else {
|
||||||
// 虽然会在启动时panic, 可以断言不会走这个分支, 但是考虑到动态加载配置或者其它情况, 这里输出一下方便检查奇奇怪怪的BUG
|
// 虽然会在启动时panic, 可以断言不会走这个分支, 但是考虑到动态加载配置或者其它情况, 这里输出一下方便检查奇奇怪怪的BUG
|
||||||
log.Printf("NEZHA>> 未找到对应的DDNS配置(%s), 或者是provider填写不正确, 请前往config.yml检查你的设置", singleton.ServerList[clientID].DDNSProfile)
|
log.Printf("NEZHA>> 未找到对应的DDNS配置(%s), 或者是provider填写不正确, 请前往config.yml检查你的设置", singleton.ServerList[clientID].DDNSProfile)
|
||||||
|
|||||||
Reference in New Issue
Block a user