From fc0567db3541cb82fb1867adcd668327141d21f2 Mon Sep 17 00:00:00 2001 From: mei Date: Tue, 9 Sep 2025 22:02:59 +0800 Subject: [PATCH] first commit --- .gitignore | 2 ++ configs/config.go | 6 ++++ go.mod | 23 +++++++++++++++ go.sum | 55 ++++++++++++++++++++++++++++++++++++ internal/database.go | 26 +++++++++++++++++ internal/recoder.go | 63 +++++++++++++++++++++++++++++++++++++++++ main.go | 26 +++++++++++++++++ pkg/nezha.go | 65 +++++++++++++++++++++++++++++++++++++++++++ sql.db | Bin 0 -> 28672 bytes 9 files changed, 266 insertions(+) create mode 100644 .gitignore create mode 100644 configs/config.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 internal/database.go create mode 100644 internal/recoder.go create mode 100644 main.go create mode 100644 pkg/nezha.go create mode 100644 sql.db diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c8bd3b4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +nezha-recoder.exe +nezha-recoder \ No newline at end of file diff --git a/configs/config.go b/configs/config.go new file mode 100644 index 0000000..046292a --- /dev/null +++ b/configs/config.go @@ -0,0 +1,6 @@ +package configs + +const ( + NEZHA_HOST = "192.178.7.123:8008" + SCHEME = "ws" +) diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..126a6cb --- /dev/null +++ b/go.mod @@ -0,0 +1,23 @@ +module git.mmeiblog.cn/mei/nezha-recoder + +go 1.23.4 + +require ( + github.com/gorilla/websocket v1.5.3 + github.com/mattn/go-sqlite3 v1.14.32 + github.com/robfig/cron/v3 v3.0.1 + modernc.org/sqlite v1.38.2 +) + +require ( + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/ncruces/go-strftime v0.1.9 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect + golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect + golang.org/x/sys v0.34.0 // indirect + modernc.org/libc v1.66.3 // indirect + modernc.org/mathutil v1.7.1 // indirect + modernc.org/memory v1.11.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..f1b0e6c --- /dev/null +++ b/go.sum @@ -0,0 +1,55 @@ +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= +github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs= +github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= +github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= +github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= +golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o= +golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= +golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= +golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= +golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= +golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= +golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= +modernc.org/cc/v4 v4.26.2 h1:991HMkLjJzYBIfha6ECZdjrIYz2/1ayr+FL8GN+CNzM= +modernc.org/cc/v4 v4.26.2/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= +modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU= +modernc.org/ccgo/v4 v4.28.0/go.mod h1:JygV3+9AV6SmPhDasu4JgquwU81XAKLd3OKTUDNOiKE= +modernc.org/fileutil v1.3.8 h1:qtzNm7ED75pd1C7WgAGcK4edm4fvhtBsEiI/0NQ54YM= +modernc.org/fileutil v1.3.8/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc= +modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI= +modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= +modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks= +modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI= +modernc.org/libc v1.66.3 h1:cfCbjTUcdsKyyZZfEUKfoHcP3S0Wkvz3jgSzByEWVCQ= +modernc.org/libc v1.66.3/go.mod h1:XD9zO8kt59cANKvHPXpx7yS2ELPheAey0vjIuZOhOU8= +modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= +modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= +modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI= +modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= +modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8= +modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= +modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= +modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= +modernc.org/sqlite v1.38.2 h1:Aclu7+tgjgcQVShZqim41Bbw9Cho0y/7WzYptXqkEek= +modernc.org/sqlite v1.38.2/go.mod h1:cPTJYSlgg3Sfg046yBShXENNtPrWrDX8bsbAQBzgQ5E= +modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= +modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= diff --git a/internal/database.go b/internal/database.go new file mode 100644 index 0000000..3df3d34 --- /dev/null +++ b/internal/database.go @@ -0,0 +1,26 @@ +package internal + +import ( + "database/sql" + "log" + + _ "modernc.org/sqlite" +) + +func HandleSqlConnection() *sql.DB { + db, err := sql.Open("sqlite", "sql.db") + if err != nil { + log.Fatal(err) + } + + // 设置连接池参数 + db.SetMaxOpenConns(25) + db.SetMaxIdleConns(25) + + // 验证数据库连接是否可用 + if err := db.Ping(); err != nil { + log.Fatal(err) + } + + return db +} diff --git a/internal/recoder.go b/internal/recoder.go new file mode 100644 index 0000000..954fbea --- /dev/null +++ b/internal/recoder.go @@ -0,0 +1,63 @@ +package internal + +import ( + "database/sql" + "log" + "time" + + "git.mmeiblog.cn/mei/nezha-recoder/pkg" + _ "modernc.org/sqlite" +) + +func Recoder() { + servers := pkg.GetData() + for _, server := range servers.Servers { + checkNameChange(server) + db := HandleSqlConnection() + defer db.Close() + + var diskUse float64 + if server.Host.Disk_total != 0 { + diskUse = float64(server.State.Disk_used) / float64(server.Host.Disk_total) * 100 + } else { + diskUse = 0 // 或者根据业务需求设置其他默认值 + } + + sql := "INSERT INTO history (id, cpu, timestamp, disk_use, load_5, net_in_speed, net_out_speed, process_count) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" + _, err := db.Exec(sql, server.Id, server.State.Cpu, time.Now().Unix(), diskUse, server.State.Load_5, server.State.Net_in_speed, server.State.Net_out_speed, server.State.Process_count) + if err != nil { + log.Println("insert error:", err) + } + log.Printf("写入数据成功: %s", server.Name) + } +} + +func checkNameChange(server pkg.Servers) { + db := HandleSqlConnection() + defer db.Close() + + Query := "SELECT name FROM agent WHERE id = ?" + var name string + err := db.QueryRow(Query, server.Id).Scan(&name) + if err == sql.ErrNoRows { + addServer(server) + } else if err != nil { + log.Println("query error:", err) + } + + if name != server.Name { + sql := "UPDATE agent SET name = ? WHERE id = ?" + db.Exec(sql, server.Name, server.Id) + } +} + +func addServer(server pkg.Servers) { + db := HandleSqlConnection() + defer db.Close() + + sql := "INSERT INTO agent (id, name, disk_total, mem_total, arch, platform) VALUES (?, ?, ?, ?, ?, ?)" + _, err := db.Exec(sql, server.Id, server.Name, server.Host.Disk_total, server.Host.Mem_total, server.Host.Arch, server.Host.Platform) + if err != nil { + log.Println("add Server error:", err) + } +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..7e2ca13 --- /dev/null +++ b/main.go @@ -0,0 +1,26 @@ +package main + +import ( + "fmt" + "os" + "os/signal" + "syscall" + + "git.mmeiblog.cn/mei/nezha-recoder/internal" + "github.com/robfig/cron/v3" +) + +func main() { + internal.Recoder() + c := cron.New() + + c.AddFunc("@every 1h", internal.Recoder) + + c.Start() + + sig := make(chan os.Signal, 1) + signal.Notify(sig, os.Interrupt, syscall.SIGTERM) + <-sig // 阻塞直到收到信号 + fmt.Printf("Hava a good Day!") + c.Stop() // 关闭 cron +} diff --git a/pkg/nezha.go b/pkg/nezha.go new file mode 100644 index 0000000..d84fece --- /dev/null +++ b/pkg/nezha.go @@ -0,0 +1,65 @@ +package pkg + +import ( + "encoding/json" + "log" + "net/url" + + "git.mmeiblog.cn/mei/nezha-recoder/configs" + "github.com/gorilla/websocket" +) + +// Struct +type Message struct { + Now int64 `json:"now"` // timestamp + Online int `json:"online"` // online number + Servers []Servers `json:"servers"` +} + +type Servers struct { + Id int `json:"id"` + Name string `json:"name"` + Host Host `json:"host"` + State State `json:"state"` +} + +type Host struct { + Disk_total int `json:"disk_total"` + Mem_total int `json:"mem_total"` + Arch string `json:"arch"` + Platform string `json:"platform"` +} + +type State struct { + Cpu float64 `json:"cpu"` + Disk_used int `json:"disk_used"` + Load_5 float32 `json:"load_5"` + Net_in_speed int `json:"net_in_speed"` + Net_out_speed int `json:"net_out_speed"` + Process_count int `json:"process_count"` +} + +func GetData() (servers Message) { + u := url.URL{Scheme: configs.SCHEME, Host: configs.NEZHA_HOST, Path: "/api/v1/ws/server"} + + conn, _, err := websocket.DefaultDialer.Dial(u.String(), nil) + if err != nil { + log.Fatal("dial error:", err) + return + } + defer conn.Close() + + _, data, err := conn.ReadMessage() + if err != nil { + log.Fatal("read error:", err) + return + } + + var msg Message + if err = json.Unmarshal(data, &msg); err != nil { + log.Println("json parse error:", err) + return + } + + return msg +} diff --git a/sql.db b/sql.db new file mode 100644 index 0000000000000000000000000000000000000000..92be1f723dd1d94967552c5e8b0c83bfd1d66d81 GIT binary patch literal 28672 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 YfC5ke3P1rU00p1`6o3Ly;Qw6UKmQBrWB>pF literal 0 HcmV?d00001