Compare commits

...
47 Commits
Author SHA1 Message Date
UUBulb 4df60c6955 ddns: improve performance (#385) 2024-07-07 10:19:58 +08:00
UUBulb 0501e120f6 fix: timestamp conversion (#383) 2024-07-04 14:32:19 +08:00
UUBulb cfa425bb33 feat: notifications support for GPU & Temperature (#381) 2024-07-03 12:34:45 +08:00
nap0o 0613b0022d 优化server-status主题服务页 (#382) 2024-07-02 22:11:20 +08:00
github-actions[bot] 740d15bf79 update contributors[no ci] 2024-06-28 02:32:48 +00:00
Kris 7ace5d04fa Update README.md (#380) 2024-06-28 10:32:31 +08:00
github-actions[bot] a38d7bb51d update contributors[no ci] 2024-06-25 15:00:50 +00:00
UUBulb 61e16f96c5 fix: EUID is a readonly variable on some shells (#379) 2024-06-25 23:00:30 +08:00
nap0o 61127b3435 优化server-status主题首页网络图表 (#378) 2024-06-25 22:16:32 +08:00
UUBulb 8805dffe85 refactor: make the installer script POSIX compliant (#376) 2024-06-25 22:15:49 +08:00
dependabot[bot] aeb7c52565 Bump github.com/hashicorp/go-retryablehttp from 0.7.2 to 0.7.7 (#377) 2024-06-25 22:11:17 +08:00
nap0o 877b4cd666 优化gpu前端展示代码 (#375) 2024-06-23 22:20:04 +08:00
naiba f75847d3d7 refactor: imporve code style 2024-06-23 16:15:08 +08:00
github-actions[bot] eee3225f3a update contributors[no ci] 2024-06-23 07:59:19 +00:00
UUBulb aa9eb533ad Temporary fix for DDNSDomain leaks (#374) 2024-06-23 15:59:03 +08:00
UUBulb cf9bbb5ff4 feat: Add GPU inspection support (#373) 2024-06-23 10:27:33 +08:00
github-actions[bot] 65dee23137 update contributors[no ci] 2024-06-22 14:52:43 +00:00
UUBulb feb4659cab Hide DDNS domain info for guests (#371) 2024-06-22 22:52:29 +08:00
github-actions[bot] bc7791efd2 update contributors[no ci] 2024-06-22 14:51:57 +00:00
nap0o 6e6ec3256f default和serverstatus主题温度展示相关 (#372) 2024-06-22 22:51:42 +08:00
github-actions[bot] ce66f72cf9 update contributors[no ci] 2024-06-16 10:13:06 +00:00
UUBulb 0ed7436176 Add option to reduct temperature information (#369) 2024-06-16 18:12:45 +08:00
UUBulb 2aabe1bd61 chore: fix some typos (#370) 2024-06-16 18:04:27 +08:00
github-actions[bot] db5e94d0de update contributors[no ci] 2024-06-15 01:43:49 +00:00
nap0o 3eecdc2b3f 修复一些bug (#367)
* bug修复:
1. serverstatus主题agent详情不显示内存信息
2. windows系统icon错误
小优化
1. echarts绘制世界地图所需的world.js坐标信息从本地加载切换为从cdn加载
2. font-logos切换为0.17.0版本,使得图标间距更合适

* 优化agent离线保留信息逻辑,从判断“实时在线”调整为判断“曾经在线”。

* 优化agent离线保留信息逻辑,从判断“实时在线”调整为判断“曾经在线”。
2024-06-15 09:43:30 +08:00
github-actions[bot] b2740e5f3c update contributors[no ci] 2024-06-14 10:03:18 +00:00
nap0o c3fa4cbbee 优化default和server status主题 (#363) 2024-06-14 18:02:55 +08:00
UUBulb a9da084711 ci: using time/tzdata package to provide zoneinfo (#365) 2024-06-14 10:24:58 +08:00
github-actions[bot] f30969e946 update contributors[no ci] 2024-06-01 11:12:04 +00:00
Leon e0e2b8c3c2 Add support for sensor temperature in host state (#359)
* feat: Add support for sensor temperature in host state

* chore: Update file permissions for saving config data
2024-06-01 19:11:48 +08:00
github-actions[bot] 3782c0dcaf update contributors[no ci] 2024-05-31 14:05:21 +00:00
Coming 0331db2c12 Update GPG key for libsepol (#360) 2024-05-31 22:05:05 +08:00
github-actions[bot] 1749247aeb update contributors[no ci] 2024-05-21 15:12:02 +00:00
UUBulb eaba7abd35 Use agent's builtin support for services instead (#357) 2024-05-21 23:11:43 +08:00
github-actions[bot] f5ca66d924 update contributors[no ci] 2024-05-19 14:35:11 +00:00
eya46 d5e7b22fc6 删除多余空格 (#358) 2024-05-19 22:34:57 +08:00
nap0o 5122d48dd8 Update service.html (#356) 2024-05-07 09:09:53 +08:00
github-actions[bot] 3614ef381e update contributors[no ci] 2024-05-05 12:13:15 +00:00
UUBulb 8475674eb7 chore: fix a typo (#355) 2024-05-05 20:12:54 +08:00
Akkia 25e7f8a912 支持cloudflare access OIDC认证 (#354) 2024-05-03 08:47:53 +08:00
UUBulb 5c7652f047 Add DDNS Profiles, use publicsuffixlist domain parser (#350)
* Add DDNS Profiles, use publicsuffixlist domain parser

* Add Tencent Cloud DNS Provider

* Restore validate DDNS provider function

* chore: fmt & upgrade dependencies
2024-04-27 13:36:36 +08:00
naiba 890616f52a daocloud github 镜像已经被打废了 2024-04-18 21:42:04 +08:00
github-actions[bot] c92ec996e0 update contributors[no ci] 2024-04-13 03:15:05 +00:00
Nanjing Hopefun Network Technology Co. Ltd 7fcd4398ea Update install.sh (#347)
修改自动安装超时时间-T 10为-T 60,10秒超时在wget那一步经常连接探针下载失败。
2024-04-13 11:14:46 +08:00
github-actions[bot] 7faba40a1c update contributors[no ci] 2024-04-09 12:43:54 +00:00
nap0o 66b4c88c24 修复ServerStatus主题Bug (#346) 2024-04-09 20:43:35 +08:00
github-actions[bot] f3c90bbf59 update contributors[no ci] 2024-04-08 14:31:45 +00:00
57 changed files with 2565 additions and 1534 deletions
+1
View File
@@ -30,6 +30,7 @@ jobs:
v: true v: true
x: false x: false
race: false race: false
tags: timetzdata
ldflags: -s -w --extldflags '-static -fpic' -X github.com/naiba/nezha/service/singleton.Version=${{ steps.extract_branch.outputs.tag }} ldflags: -s -w --extldflags '-static -fpic' -X github.com/naiba/nezha/service/singleton.Version=${{ steps.extract_branch.outputs.tag }}
buildmode: default buildmode: default
+1
View File
@@ -33,6 +33,7 @@ jobs:
v: true v: true
x: false x: false
race: false race: false
tags: timetzdata
ldflags: -s -w --extldflags '-static -fpic' -X github.com/naiba/nezha/service/singleton.Version=test ldflags: -s -w --extldflags '-static -fpic' -X github.com/naiba/nezha/service/singleton.Version=test
buildmode: default buildmode: default
+1
View File
@@ -40,5 +40,6 @@ jobs:
v: true v: true
x: false x: false
race: false race: false
tags: timetzdata
ldflags: -s -w --extldflags '-static -fpic' -X github.com/naiba/nezha/service/singleton.Version=test ldflags: -s -w --extldflags '-static -fpic' -X github.com/naiba/nezha/service/singleton.Version=test
buildmode: default buildmode: default
+12 -5
View File
@@ -4,13 +4,15 @@
<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">&nbsp;<img src="https://img.shields.io/github/v/release/nezhahq/agent?color=brightgreen&label=Agent&style=for-the-badge&logo=github">&nbsp;<img src="https://img.shields.io/github/actions/workflow/status/nezhahq/agent/agent.yml?label=Agent%20CI&logo=github&style=for-the-badge">&nbsp;<img src="https://img.shields.io/badge/Installer-v0.15.9-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">&nbsp;<img src="https://img.shields.io/github/v/release/nezhahq/agent?color=brightgreen&label=Agent&style=for-the-badge&logo=github">&nbsp;<img src="https://img.shields.io/github/actions/workflow/status/nezhahq/agent/agent.yml?label=Agent%20CI&logo=github&style=for-the-badge">&nbsp;<img src="https://img.shields.io/badge/Installer-v0.17.0-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>
<p>Supports <b>monitoring</b> system status, HTTP (SSL certificate change, upcoming expiration, expired), TCP, Ping and supports <b>push alerts</b>, run scheduled tasks and <b>web terminal</b>.</p> <p>Supports <b>monitoring</b> system status, HTTP (SSL certificate change, upcoming expiration, expired), TCP, Ping and supports <b>push alerts</b>, run scheduled tasks and <b>web terminal</b>.</p>
</div> </div>
\>> Telegram Channel: [哪吒监控(中文通知频道)](https://t.me/nezhanews)
\>> Telegram Group: [Nezha Monitoring Global (English Only)](https://t.me/nezhamonitoring_global), [哪吒监控(中文群组)](https://t.me/nezhamonitoring) \>> Telegram Group: [Nezha Monitoring Global (English Only)](https://t.me/nezhamonitoring_global), [哪吒监控(中文群组)](https://t.me/nezhamonitoring)
\>> [Use Cases | 我们的用户](https://www.google.com/search?q=%22powered+by+Nezha+Monitoring%22+OR+%22powered+by+%E5%93%AA%E5%90%92%E7%9B%91%E6%8E%A7%22) (Google) \>> [Use Cases | 我们的用户](https://www.google.com/search?q=%22powered+by+Nezha+Monitoring%22+OR+%22powered+by+%E5%93%AA%E5%90%92%E7%9B%91%E6%8E%A7%22) (Google)
@@ -42,13 +44,13 @@ You can change the dashboard language in the settings page (`/setting`) after th
<a href="https://github.com/AkkiaS7" title="Akkia"><img src="https://avatars.githubusercontent.com/u/68485070?v=4" width="50;" alt="Akkia"/></a> <a href="https://github.com/AkkiaS7" title="Akkia"><img src="https://avatars.githubusercontent.com/u/68485070?v=4" width="50;" alt="Akkia"/></a>
<a href="https://github.com/Erope" title="卖女孩的小火柴"><img src="https://avatars.githubusercontent.com/u/44471469?v=4" width="50;" alt="卖女孩的小火柴"/></a> <a href="https://github.com/Erope" title="卖女孩的小火柴"><img src="https://avatars.githubusercontent.com/u/44471469?v=4" width="50;" alt="卖女孩的小火柴"/></a>
<a href="https://github.com/dysf888" title="黑歌"><img src="https://avatars.githubusercontent.com/u/47450409?v=4" width="50;" alt="黑歌"/></a> <a href="https://github.com/dysf888" title="黑歌"><img src="https://avatars.githubusercontent.com/u/47450409?v=4" width="50;" alt="黑歌"/></a>
<a href="https://github.com/uubulb" title="UUBulb"><img src="https://avatars.githubusercontent.com/u/35923940?v=4" width="50;" alt="UUBulb"/></a>
<a href="https://github.com/xykt" title="xykt"><img src="https://avatars.githubusercontent.com/u/152045469?v=4" width="50;" alt="xykt"/></a> <a href="https://github.com/xykt" title="xykt"><img src="https://avatars.githubusercontent.com/u/152045469?v=4" width="50;" alt="xykt"/></a>
<a href="https://github.com/nap0o" title="nap0o"><img src="https://avatars.githubusercontent.com/u/144927971?v=4" width="50;" alt="nap0o"/></a>
<a href="https://github.com/MikoyChinese" title="MikoyChinese"><img src="https://avatars.githubusercontent.com/u/22676744?v=4" width="50;" alt="MikoyChinese"/></a> <a href="https://github.com/MikoyChinese" title="MikoyChinese"><img src="https://avatars.githubusercontent.com/u/22676744?v=4" width="50;" alt="MikoyChinese"/></a>
<a href="https://github.com/JackieSung4ev" title="JackieSung4ev"><img src="https://avatars.githubusercontent.com/u/24974735?v=4" width="50;" alt="JackieSung4ev"/></a> <a href="https://github.com/JackieSung4ev" title="JackieSung4ev"><img src="https://avatars.githubusercontent.com/u/24974735?v=4" width="50;" alt="JackieSung4ev"/></a>
<a href="https://github.com/lemoeo" title="Lemoe"><img src="https://avatars.githubusercontent.com/u/18618627?v=4" width="50;" alt="Lemoe"/></a>
<a href="https://github.com/cantoblanco" title="Kris"><img src="https://avatars.githubusercontent.com/u/116849421?v=4" width="50;" alt="Kris"/></a> <a href="https://github.com/cantoblanco" title="Kris"><img src="https://avatars.githubusercontent.com/u/116849421?v=4" width="50;" alt="Kris"/></a>
<a href="https://github.com/nap0o" title="nap0o"><img src="https://avatars.githubusercontent.com/u/144927971?v=4" width="50;" alt="nap0o"/></a> <a href="https://github.com/lemoeo" title="Lemoe"><img src="https://avatars.githubusercontent.com/u/18618627?v=4" width="50;" alt="Lemoe"/></a>
<a href="https://github.com/uubulb" title="UUBulb"><img src="https://avatars.githubusercontent.com/u/35923940?v=4" width="50;" alt="UUBulb"/></a>
<a href="https://github.com/spiritLHLS" title="spiritlhl"><img src="https://avatars.githubusercontent.com/u/103393591?v=4" width="50;" alt="spiritlhl"/></a> <a href="https://github.com/spiritLHLS" title="spiritlhl"><img src="https://avatars.githubusercontent.com/u/103393591?v=4" width="50;" alt="spiritlhl"/></a>
<a href="https://github.com/liuyanxi975" title="刘颜溪"><img src="https://avatars.githubusercontent.com/u/24417037?v=4" width="50;" alt="刘颜溪"/></a> <a href="https://github.com/liuyanxi975" title="刘颜溪"><img src="https://avatars.githubusercontent.com/u/24417037?v=4" width="50;" alt="刘颜溪"/></a>
<a href="https://github.com/CosmosZ-code" title="CosmosZ-code"><img src="https://avatars.githubusercontent.com/u/81398224?v=4" width="50;" alt="CosmosZ-code"/></a> <a href="https://github.com/CosmosZ-code" title="CosmosZ-code"><img src="https://avatars.githubusercontent.com/u/81398224?v=4" width="50;" alt="CosmosZ-code"/></a>
@@ -69,18 +71,23 @@ You can change the dashboard language in the settings page (`/setting`) after th
<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/zhucaidan" title="zhucaidan"><img src="https://avatars.githubusercontent.com/u/47970938?v=4" width="50;" alt="zhucaidan"/></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/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/lvyaoting" title="lvyaoting"><img src="https://avatars.githubusercontent.com/u/166296299?v=4" width="50;" alt="lvyaoting"/></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/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/eya46" title="eya46"><img src="https://avatars.githubusercontent.com/u/61458340?v=4" width="50;" alt="eya46"/></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>
<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/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/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/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/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/MartijnLindeman" title="Martijn Lindeman"><img src="https://avatars.githubusercontent.com/u/78365708?v=4" width="50;" alt="Martijn Lindeman"/></a> <a href="https://github.com/MartijnLindeman" title="Martijn Lindeman"><img src="https://avatars.githubusercontent.com/u/78365708?v=4" width="50;" alt="Martijn Lindeman"/></a>
<a href="https://github.com/funnyzak" title="Leon"><img src="https://avatars.githubusercontent.com/u/2562087?v=4" width="50;" alt="Leon"/></a>
<a href="https://github.com/KorenKrita" title="KorenKrita"><img src="https://avatars.githubusercontent.com/u/22239339?v=4" width="50;" alt="KorenKrita"/></a> <a href="https://github.com/KorenKrita" title="KorenKrita"><img src="https://avatars.githubusercontent.com/u/22239339?v=4" width="50;" alt="KorenKrita"/></a>
<a href="https://github.com/techotaku" title="Ian Li"><img src="https://avatars.githubusercontent.com/u/1948179?v=4" width="50;" alt="Ian Li"/></a> <a href="https://github.com/techotaku" title="Ian Li"><img src="https://avatars.githubusercontent.com/u/1948179?v=4" width="50;" alt="Ian Li"/></a>
<a href="https://github.com/GreenTeodoro839" title="GreenTeodoro839"><img src="https://avatars.githubusercontent.com/u/77104800?v=4" width="50;" alt="GreenTeodoro839"/></a> <a href="https://github.com/GreenTeodoro839" title="GreenTeodoro839"><img src="https://avatars.githubusercontent.com/u/77104800?v=4" width="50;" alt="GreenTeodoro839"/></a>
<a href="https://github.com/Es-dese" title="Esdese"><img src="https://avatars.githubusercontent.com/u/71542548?v=4" width="50;" alt="Esdese"/></a> <a href="https://github.com/Es-dese" title="Esdese"><img src="https://avatars.githubusercontent.com/u/71542548?v=4" width="50;" alt="Esdese"/></a>
<a href="https://github.com/wwng2333" title="Crazy"><img src="https://avatars.githubusercontent.com/u/17147265?v=4" width="50;" alt="Crazy"/></a><!--GAMFC_DELIMITER_END--> <a href="https://github.com/wwng2333" title="Crazy"><img src="https://avatars.githubusercontent.com/u/17147265?v=4" width="50;" alt="Crazy"/></a>
<a href="https://github.com/wellcoming" title="Coming"><img src="https://avatars.githubusercontent.com/u/74850890?v=4" width="50;" alt="Coming"/></a><!--GAMFC_DELIMITER_END-->
## Star History ## Star History
+13 -6
View File
@@ -2,6 +2,7 @@ package controller
import ( import (
"errors" "errors"
"fmt"
"log" "log"
"net/http" "net/http"
"regexp" "regexp"
@@ -223,19 +224,25 @@ func (cp *commonPage) network(c *gin.Context) {
} }
func (cp *commonPage) getServerStat(c *gin.Context) ([]byte, error) { func (cp *commonPage) getServerStat(c *gin.Context) ([]byte, error) {
v, err, _ := cp.requestGroup.Do("serverStats", func() (any, error) { _, isMember := c.Get(model.CtxKeyAuthorizedUser)
_, isViewPasswordVerfied := c.Get(model.CtxKeyViewPasswordVerified)
authorized := isMember || isViewPasswordVerfied
v, err, _ := cp.requestGroup.Do(fmt.Sprintf("serverStats::%t", authorized), func() (interface{}, error) {
singleton.SortedServerLock.RLock() singleton.SortedServerLock.RLock()
defer singleton.SortedServerLock.RUnlock() defer singleton.SortedServerLock.RUnlock()
_, isMember := c.Get(model.CtxKeyAuthorizedUser)
_, isViewPasswordVerfied := c.Get(model.CtxKeyViewPasswordVerified)
var servers []*model.Server var servers []*model.Server
if isMember || isViewPasswordVerfied { if authorized {
servers = singleton.SortedServerList servers = singleton.SortedServerList
} else { } else {
servers = singleton.SortedServerListForGuest filteredServers := make([]*model.Server, len(singleton.SortedServerListForGuest))
for i, server := range singleton.SortedServerListForGuest {
filteredServer := *server
filteredServer.DDNSDomain = "redacted"
filteredServers[i] = &filteredServer
}
servers = filteredServers
} }
return utils.Json.Marshal(Data{ return utils.Json.Marshal(Data{
+3
View File
@@ -49,6 +49,9 @@ func (gp *guestPage) login(c *gin.Context) {
} else if singleton.Conf.Oauth2.Type == model.ConfigTypeGitea { } else if singleton.Conf.Oauth2.Type == model.ConfigTypeGitea {
LoginType = "Gitea" LoginType = "Gitea"
RegistrationLink = fmt.Sprintf("%s/user/sign_up", singleton.Conf.Oauth2.Endpoint) RegistrationLink = fmt.Sprintf("%s/user/sign_up", singleton.Conf.Oauth2.Endpoint)
} else if singleton.Conf.Oauth2.Type == model.ConfigTypeCloudflare {
LoginType = "Cloudflare"
RegistrationLink = "https://dash.cloudflare.com/sign-up/teams"
} }
c.HTML(http.StatusOK, "dashboard-"+singleton.Conf.Site.DashboardTheme+"/login", mygin.CommonEnvironment(c, gin.H{ c.HTML(http.StatusOK, "dashboard-"+singleton.Conf.Site.DashboardTheme+"/login", mygin.CommonEnvironment(c, gin.H{
"Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "Login"}), "Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "Login"}),
+18 -18
View File
@@ -304,6 +304,7 @@ type serverForm struct {
EnableIPv4 string EnableIPv4 string
EnableIpv6 string EnableIpv6 string
DDNSDomain string DDNSDomain string
DDNSProfile string
} }
func (ma *memberAPI) addOrEditServer(c *gin.Context) { func (ma *memberAPI) addOrEditServer(c *gin.Context) {
@@ -323,6 +324,7 @@ func (ma *memberAPI) addOrEditServer(c *gin.Context) {
s.EnableIPv4 = sf.EnableIPv4 == "on" s.EnableIPv4 = sf.EnableIPv4 == "on"
s.EnableIpv6 = sf.EnableIpv6 == "on" s.EnableIpv6 = sf.EnableIpv6 == "on"
s.DDNSDomain = sf.DDNSDomain s.DDNSDomain = sf.DDNSDomain
s.DDNSProfile = sf.DDNSProfile
if s.ID == 0 { if s.ID == 0 {
s.Secret, err = utils.GenerateRandomString(18) s.Secret, err = utils.GenerateRandomString(18)
if err == nil { if err == nil {
@@ -435,25 +437,23 @@ func (ma *memberAPI) addOrEditMonitor(c *gin.Context) {
if m.NotificationTag == "" { if m.NotificationTag == "" {
m.NotificationTag = "default" m.NotificationTag = "default"
} }
if err == nil { err = utils.Json.Unmarshal([]byte(mf.FailTriggerTasksRaw), &m.FailTriggerTasks)
err = utils.Json.Unmarshal([]byte(mf.FailTriggerTasksRaw), &m.FailTriggerTasks) }
if err == nil {
err = utils.Json.Unmarshal([]byte(mf.RecoverTriggerTasksRaw), &m.RecoverTriggerTasks)
}
if err == nil {
if m.ID == 0 {
err = singleton.DB.Create(&m).Error
} else {
err = singleton.DB.Save(&m).Error
} }
if err == nil { }
err = utils.Json.Unmarshal([]byte(mf.RecoverTriggerTasksRaw), &m.RecoverTriggerTasks) if err == nil {
} if m.Cover == 0 {
if err == nil { err = singleton.DB.Unscoped().Delete(&model.MonitorHistory{}, "monitor_id = ? and server_id in (?)", m.ID, strings.Split(m.SkipServersRaw[1:len(m.SkipServersRaw)-1], ",")).Error
if m.ID == 0 { } else {
err = singleton.DB.Create(&m).Error err = singleton.DB.Unscoped().Delete(&model.MonitorHistory{}, "monitor_id = ? and server_id not in (?)", m.ID, strings.Split(m.SkipServersRaw[1:len(m.SkipServersRaw)-1], ",")).Error
} else {
err = singleton.DB.Save(&m).Error
}
}
if err == nil {
if m.Cover == 0 {
err = singleton.DB.Unscoped().Delete(&model.MonitorHistory{}, "monitor_id = ? and server_id in (?)", m.ID, strings.Split(m.SkipServersRaw[1:len(m.SkipServersRaw)-1], ",")).Error
} else {
err = singleton.DB.Unscoped().Delete(&model.MonitorHistory{}, "monitor_id = ? and server_id not in (?)", m.ID, strings.Split(m.SkipServersRaw[1:len(m.SkipServersRaw)-1], ",")).Error
}
} }
} }
if err == nil { if err == nil {
+26 -4
View File
@@ -2,6 +2,7 @@ package controller
import ( import (
"context" "context"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
@@ -9,6 +10,8 @@ import (
"strings" "strings"
"time" "time"
"github.com/naiba/nezha/pkg/oidc/cloudflare"
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
GitHubAPI "github.com/google/go-github/v47/github" GitHubAPI "github.com/google/go-github/v47/github"
@@ -74,6 +77,17 @@ func (oa *oauth2controller) getCommonOauth2Config(c *gin.Context) *oauth2.Config
}, },
RedirectURL: oa.getRedirectURL(c), RedirectURL: oa.getRedirectURL(c),
} }
} else if singleton.Conf.Oauth2.Type == model.ConfigTypeCloudflare {
return &oauth2.Config{
ClientID: singleton.Conf.Oauth2.ClientID,
ClientSecret: singleton.Conf.Oauth2.ClientSecret,
Scopes: []string{"openid", "email", "profile", "groups"},
Endpoint: oauth2.Endpoint{
AuthURL: fmt.Sprintf("%s/cdn-cgi/access/sso/oidc/%s/authorization", singleton.Conf.Oauth2.Endpoint, singleton.Conf.Oauth2.ClientID),
TokenURL: fmt.Sprintf("%s/cdn-cgi/access/sso/oidc/%s/token", singleton.Conf.Oauth2.Endpoint, singleton.Conf.Oauth2.ClientID),
},
RedirectURL: oa.getRedirectURL(c),
}
} else { } else {
return &oauth2.Config{ return &oauth2.Config{
ClientID: singleton.Conf.Oauth2.ClientID, ClientID: singleton.Conf.Oauth2.ClientID,
@@ -155,6 +169,17 @@ func (oa *oauth2controller) callback(c *gin.Context) {
if err == nil { if err == nil {
user = model.NewUserFromGitea(u) user = model.NewUserFromGitea(u)
} }
} else if singleton.Conf.Oauth2.Type == model.ConfigTypeCloudflare {
client := oauth2Config.Client(context.Background(), otk)
resp, err := client.Get(fmt.Sprintf("%s/cdn-cgi/access/sso/oidc/%s/userinfo", singleton.Conf.Oauth2.Endpoint, singleton.Conf.Oauth2.ClientID))
if err == nil {
defer resp.Body.Close()
var cloudflareUserInfo *cloudflare.UserInfo
if err := json.NewDecoder(resp.Body).Decode(&cloudflareUserInfo); err == nil {
user = cloudflareUserInfo.MapToNezhaUser()
}
}
} else { } else {
var client *GitHubAPI.Client var client *GitHubAPI.Client
oc := oauth2Config.Client(ctx, otk) oc := oauth2Config.Client(ctx, otk)
@@ -168,15 +193,12 @@ func (oa *oauth2controller) callback(c *gin.Context) {
client = GitHubAPI.NewClient(oc) client = GitHubAPI.NewClient(oc)
} }
var gu *GitHubAPI.User var gu *GitHubAPI.User
if err == nil { gu, _, err = client.Users.Get(ctx, "")
gu, _, err = client.Users.Get(ctx, "")
}
if err == nil { if err == nil {
user = model.NewUserFromGitHub(gu) user = model.NewUserFromGitHub(gu)
} }
} }
} }
if err == nil && user.Login == "" { if err == nil && user.Login == "" {
err = errors.New("获取用户信息失败") err = errors.New("获取用户信息失败")
} }
+8 -8
View File
@@ -3,8 +3,8 @@ module github.com/naiba/nezha
go 1.20 go 1.20
require ( require (
code.cloudfoundry.org/bytefmt v0.0.0-20240405144452-ebb2996022ca code.cloudfoundry.org/bytefmt v0.0.0-20240425163905-bcdc1ad063ea
code.gitea.io/sdk/gitea v0.17.1 code.gitea.io/sdk/gitea v0.18.0
github.com/BurntSushi/toml v1.3.2 github.com/BurntSushi/toml v1.3.2
github.com/gin-contrib/pprof v1.4.0 github.com/gin-contrib/pprof v1.4.0
github.com/gin-gonic/gin v1.9.1 github.com/gin-gonic/gin v1.9.1
@@ -20,15 +20,16 @@ require (
github.com/samber/lo v1.39.0 github.com/samber/lo v1.39.0
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/xanzy/go-gitlab v0.102.0 github.com/xanzy/go-gitlab v0.103.0
golang.org/x/crypto v0.22.0 golang.org/x/crypto v0.22.0
golang.org/x/net v0.24.0
golang.org/x/oauth2 v0.19.0 golang.org/x/oauth2 v0.19.0
golang.org/x/sync v0.7.0 golang.org/x/sync v0.7.0
golang.org/x/text v0.14.0 golang.org/x/text v0.14.0
google.golang.org/grpc v1.63.0 google.golang.org/grpc v1.63.0
google.golang.org/protobuf v1.33.0 google.golang.org/protobuf v1.33.0
gorm.io/driver/sqlite v1.5.5 gorm.io/driver/sqlite v1.5.5
gorm.io/gorm v1.25.9 gorm.io/gorm v1.25.10
sigs.k8s.io/yaml v1.4.0 sigs.k8s.io/yaml v1.4.0
) )
@@ -46,7 +47,7 @@ require (
github.com/goccy/go-json v0.10.2 // indirect github.com/goccy/go-json v0.10.2 // indirect
github.com/google/go-querystring v1.1.0 // indirect github.com/google/go-querystring v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-retryablehttp v0.7.2 // indirect github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
github.com/hashicorp/go-version v1.6.0 // indirect github.com/hashicorp/go-version v1.6.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect
@@ -54,7 +55,7 @@ require (
github.com/klauspost/cpuid/v2 v2.2.4 // indirect github.com/klauspost/cpuid/v2 v2.2.4 // indirect
github.com/leodido/go-urn v1.2.4 // indirect github.com/leodido/go-urn v1.2.4 // indirect
github.com/magiconair/properties v1.8.7 // indirect github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-sqlite3 v1.14.17 // indirect github.com/mattn/go-sqlite3 v1.14.17 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
@@ -73,8 +74,7 @@ require (
go.uber.org/multierr v1.9.0 // indirect go.uber.org/multierr v1.9.0 // indirect
golang.org/x/arch v0.3.0 // indirect golang.org/x/arch v0.3.0 // indirect
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
golang.org/x/net v0.24.0 // indirect golang.org/x/sys v0.20.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/time v0.5.0 // indirect golang.org/x/time v0.5.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect
+18 -48
View File
@@ -1,7 +1,7 @@
code.cloudfoundry.org/bytefmt v0.0.0-20240405144452-ebb2996022ca h1:pyybVFhefCroOyqu71Rrnm0eORYFJMvTDsAsyPLjvEI= code.cloudfoundry.org/bytefmt v0.0.0-20240425163905-bcdc1ad063ea h1:1tgMNDgo8PjpsHhlaxdibj28C0WyLeOW2SPJ7GGdc9A=
code.cloudfoundry.org/bytefmt v0.0.0-20240405144452-ebb2996022ca/go.mod h1:21qd3QlOUZT1oQ2RccnLvWRu7CZWSN+3ZlUh2mzoE10= code.cloudfoundry.org/bytefmt v0.0.0-20240425163905-bcdc1ad063ea/go.mod h1:3+xXJBOD8PsGHDqHedtCLalbaVJ+yi1OW+mXx9IcNxI=
code.gitea.io/sdk/gitea v0.17.1 h1:3jCPOG2ojbl8AcfaUCRYLT5MUcBMFwS0OSK2mA5Zok8= code.gitea.io/sdk/gitea v0.18.0 h1:+zZrwVmujIrgobt6wVBWCqITz6bn1aBjnCUHmpZrerI=
code.gitea.io/sdk/gitea v0.17.1/go.mod h1:aCnBqhHpoEWA180gMbaCtdX9Pl6BWBAuuP2miadoTNM= code.gitea.io/sdk/gitea v0.18.0/go.mod h1:IG9xZJoltDNeDSW0qiF2Vqx5orMWa7OhVWrjvrd5NpI=
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
@@ -16,7 +16,7 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davidmz/go-pageant v1.0.2 h1:bPblRCh5jGU+Uptpz6LgMZGD5hJoOt7otgT454WvHn0= github.com/davidmz/go-pageant v1.0.2 h1:bPblRCh5jGU+Uptpz6LgMZGD5hJoOt7otgT454WvHn0=
github.com/davidmz/go-pageant v1.0.2/go.mod h1:P2EDDnMqIwG5Rrp05dTRITj9z2zpGcD9efWSkTNKLIE= github.com/davidmz/go-pageant v1.0.2/go.mod h1:P2EDDnMqIwG5Rrp05dTRITj9z2zpGcD9efWSkTNKLIE=
github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
@@ -57,15 +57,14 @@ github.com/google/go-github/v47 v47.1.0/go.mod h1:VPZBXNbFSJGjyjFRUKo9vZGawTajnW
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20240402174815-29b9bb013b0f h1:f00RU+zOX+B3rLAmMMkzHUF2h1z4DeYR9tTCvEq2REY= github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
github.com/hashicorp/go-retryablehttp v0.7.2 h1:AcYqCvkpalPnPF2pn0KamgwamS42TqUDDYFRKq/RAd0= github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
github.com/hashicorp/go-retryablehttp v0.7.2/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
@@ -98,8 +97,8 @@ github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0V
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM= github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
@@ -112,7 +111,7 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY
github.com/nicksnyder/go-i18n/v2 v2.4.0 h1:3IcvPOAvnCKwNm0TB0dLDTuawWEj+ax/RERNC+diLMM= github.com/nicksnyder/go-i18n/v2 v2.4.0 h1:3IcvPOAvnCKwNm0TB0dLDTuawWEj+ax/RERNC+diLMM=
github.com/nicksnyder/go-i18n/v2 v2.4.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4= github.com/nicksnyder/go-i18n/v2 v2.4.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4=
github.com/onsi/ginkgo/v2 v2.17.1 h1:V++EzdbhI4ZV4ev0UTIj0PzhzOcReJFyJaLjtSF55M8= github.com/onsi/ginkgo/v2 v2.17.1 h1:V++EzdbhI4ZV4ev0UTIj0PzhzOcReJFyJaLjtSF55M8=
github.com/onsi/gomega v1.32.0 h1:JRYU78fJ1LPxlckP6Txi/EYqJvjtMrDC04/MM5XRHPk= github.com/onsi/gomega v1.33.0 h1:snPCflnZrpMsy94p4lXVEkHo12lmPnc3vY5XBbreexE=
github.com/ory/graceful v0.1.3 h1:FaeXcHZh168WzS+bqruqWEw/HgXWLdNv2nJ+fbhxbhc= github.com/ory/graceful v0.1.3 h1:FaeXcHZh168WzS+bqruqWEw/HgXWLdNv2nJ+fbhxbhc=
github.com/ory/graceful v0.1.3/go.mod h1:4zFz687IAF7oNHHiB586U4iL+/4aV09o/PYLE34t2bA= github.com/ory/graceful v0.1.3/go.mod h1:4zFz687IAF7oNHHiB586U4iL+/4aV09o/PYLE34t2bA=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
@@ -149,7 +148,6 @@ github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMV
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@@ -168,9 +166,8 @@ github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/xanzy/go-gitlab v0.102.0 h1:ExHuJ1OTQ2yt25zBMMj0G96ChBirGYv8U7HyUiYkZ+4= github.com/xanzy/go-gitlab v0.103.0 h1:J9pTQoq0GsEFqzd6srCM1QfdfKAxSNz6mT6ntrpNF2w=
github.com/xanzy/go-gitlab v0.102.0/go.mod h1:ETg8tcj4OhrB84UEgeE8dSuV/0h4BBL1uOV/qK0vlyI= github.com/xanzy/go-gitlab v0.103.0/go.mod h1:ETg8tcj4OhrB84UEgeE8dSuV/0h4BBL1uOV/qK0vlyI=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
@@ -182,27 +179,16 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/oauth2 v0.19.0 h1:9+E/EZBCbTLNrbN35fHv/a/d/mOBatymz1zbtQrXpIg= golang.org/x/oauth2 v0.19.0 h1:9+E/EZBCbTLNrbN35fHv/a/d/mOBatymz1zbtQrXpIg=
golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8= golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -211,37 +197,21 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY=
@@ -267,8 +237,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/sqlite v1.5.5 h1:7MDMtUZhV065SilG62E0MquljeArQZNfJnjd9i9gx3E= gorm.io/driver/sqlite v1.5.5 h1:7MDMtUZhV065SilG62E0MquljeArQZNfJnjd9i9gx3E=
gorm.io/driver/sqlite v1.5.5/go.mod h1:6NgQ7sQWAIFsPrJJl1lSNSu2TABh0ZZ/zm5fosATavE= gorm.io/driver/sqlite v1.5.5/go.mod h1:6NgQ7sQWAIFsPrJJl1lSNSu2TABh0ZZ/zm5fosATavE=
gorm.io/gorm v1.25.9 h1:wct0gxZIELDk8+ZqF/MVnHLkA1rvYlBWUMv2EdsK1g8= gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s=
gorm.io/gorm v1.25.9/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
+20 -14
View File
@@ -22,7 +22,7 @@ var Themes = map[string]string{
"mdui": "Neko Mdui", "mdui": "Neko Mdui",
"hotaru": "Hotaru", "hotaru": "Hotaru",
"angel-kanade": "AngelKanade", "angel-kanade": "AngelKanade",
"server-status": "SeverStatus", "server-status": "ServerStatus",
"custom": "Custom(local)", "custom": "Custom(local)",
} }
@@ -32,11 +32,12 @@ var DashboardThemes = map[string]string{
} }
const ( const (
ConfigTypeGitHub = "github" ConfigTypeGitHub = "github"
ConfigTypeGitee = "gitee" ConfigTypeGitee = "gitee"
ConfigTypeGitlab = "gitlab" ConfigTypeGitlab = "gitlab"
ConfigTypeJihulab = "jihulab" ConfigTypeJihulab = "jihulab"
ConfigTypeGitea = "gitea" ConfigTypeGitea = "gitea"
ConfigTypeCloudflare = "cloudflare"
) )
const ( const (
@@ -70,7 +71,7 @@ func (c *AgentConfig) Save() error {
if err != nil { if err != nil {
return err return err
} }
return os.WriteFile(c.v.ConfigFileUsed(), data, os.ModePerm) return os.WriteFile(c.v.ConfigFileUsed(), data, 0600)
} }
// Config 站点配置 // Config 站点配置
@@ -125,9 +126,20 @@ type Config struct {
WebhookRequestBody string WebhookRequestBody string
WebhookHeaders string WebhookHeaders string
MaxRetries uint32 MaxRetries uint32
Profiles map[string]DDNSProfile
} }
} }
type DDNSProfile struct {
Provider string
AccessID string
AccessSecret string
WebhookURL string
WebhookMethod string
WebhookRequestBody string
WebhookHeaders string
}
// Read 读取配置文件并应用 // Read 读取配置文件并应用
func (c *Config) Read(path string) error { func (c *Config) Read(path string) error {
c.v = viper.New() c.v = viper.New()
@@ -166,12 +178,6 @@ func (c *Config) Read(path string) error {
if c.AvgPingCount == 0 { if c.AvgPingCount == 0 {
c.AvgPingCount = 2 c.AvgPingCount = 2
} }
if c.DDNS.Provider == "" {
c.DDNS.Provider = "webhook"
}
if c.DDNS.WebhookMethod == "" {
c.DDNS.WebhookMethod = "POST"
}
if c.DDNS.MaxRetries == 0 { if c.DDNS.MaxRetries == 0 {
c.DDNS.MaxRetries = 3 c.DDNS.MaxRetries = 3
} }
@@ -199,5 +205,5 @@ func (c *Config) Save() error {
if err != nil { if err != nil {
return err return err
} }
return os.WriteFile(c.v.ConfigFileUsed(), data, os.ModePerm) return os.WriteFile(c.v.ConfigFileUsed(), data, 0600)
} }
+30
View File
@@ -10,6 +10,11 @@ const (
MTReportHostState MTReportHostState
) )
type SensorTemperature struct {
Name string
Temperature float64
}
type HostState struct { type HostState struct {
CPU float64 CPU float64
MemUsed uint64 MemUsed uint64
@@ -26,9 +31,19 @@ type HostState struct {
TcpConnCount uint64 TcpConnCount uint64
UdpConnCount uint64 UdpConnCount uint64
ProcessCount uint64 ProcessCount uint64
Temperatures []SensorTemperature
GPU float64
} }
func (s *HostState) PB() *pb.State { func (s *HostState) PB() *pb.State {
var ts []*pb.State_SensorTemperature
for _, t := range s.Temperatures {
ts = append(ts, &pb.State_SensorTemperature{
Name: t.Name,
Temperature: t.Temperature,
})
}
return &pb.State{ return &pb.State{
Cpu: s.CPU, Cpu: s.CPU,
MemUsed: s.MemUsed, MemUsed: s.MemUsed,
@@ -45,10 +60,20 @@ func (s *HostState) PB() *pb.State {
TcpConnCount: s.TcpConnCount, TcpConnCount: s.TcpConnCount,
UdpConnCount: s.UdpConnCount, UdpConnCount: s.UdpConnCount,
ProcessCount: s.ProcessCount, ProcessCount: s.ProcessCount,
Temperatures: ts,
Gpu: s.GPU,
} }
} }
func PB2State(s *pb.State) HostState { func PB2State(s *pb.State) HostState {
var ts []SensorTemperature
for _, t := range s.GetTemperatures() {
ts = append(ts, SensorTemperature{
Name: t.GetName(),
Temperature: t.GetTemperature(),
})
}
return HostState{ return HostState{
CPU: s.GetCpu(), CPU: s.GetCpu(),
MemUsed: s.GetMemUsed(), MemUsed: s.GetMemUsed(),
@@ -65,6 +90,8 @@ func PB2State(s *pb.State) HostState {
TcpConnCount: s.GetTcpConnCount(), TcpConnCount: s.GetTcpConnCount(),
UdpConnCount: s.GetUdpConnCount(), UdpConnCount: s.GetUdpConnCount(),
ProcessCount: s.GetProcessCount(), ProcessCount: s.GetProcessCount(),
Temperatures: ts,
GPU: s.GetGpu(),
} }
} }
@@ -81,6 +108,7 @@ type Host struct {
IP string `json:"-"` IP string `json:"-"`
CountryCode string CountryCode string
Version string Version string
GPU []string
} }
func (h *Host) PB() *pb.Host { func (h *Host) PB() *pb.Host {
@@ -97,6 +125,7 @@ func (h *Host) PB() *pb.Host {
Ip: h.IP, Ip: h.IP,
CountryCode: h.CountryCode, CountryCode: h.CountryCode,
Version: h.Version, Version: h.Version,
Gpu: h.GPU,
} }
} }
@@ -114,5 +143,6 @@ func PB2Host(h *pb.Host) Host {
IP: h.GetIp(), IP: h.GetIp(),
CountryCode: h.GetCountryCode(), CountryCode: h.GetCountryCode(),
Version: h.GetVersion(), Version: h.GetVersion(),
GPU: h.GetGpu(),
} }
} }
-1
View File
@@ -19,4 +19,3 @@ type MonitorHistory struct {
Down uint64 // 检查状态异常计数 Down uint64 // 检查状态异常计数
Data string Data string
} }
+28 -3
View File
@@ -42,6 +42,19 @@ func percentage(used, total uint64) float64 {
return float64(used) * 100 / float64(total) return float64(used) * 100 / float64(total)
} }
func maxSliceValue(slice []float64) float64 {
if len(slice) != 0 {
max := slice[0]
for _, val := range slice {
if max < val {
max = val
}
}
return max
}
return 0
}
// Snapshot 未通过规则返回 struct{}{}, 通过返回 nil // Snapshot 未通过规则返回 struct{}{}, 通过返回 nil
func (u *Rule) Snapshot(cycleTransferStats *CycleTransferStats, server *Server, db *gorm.DB) interface{} { func (u *Rule) Snapshot(cycleTransferStats *CycleTransferStats, server *Server, db *gorm.DB) interface{} {
// 监控全部但是排除了此服务器 // 监控全部但是排除了此服务器
@@ -63,6 +76,8 @@ func (u *Rule) Snapshot(cycleTransferStats *CycleTransferStats, server *Server,
switch u.Type { switch u.Type {
case "cpu": case "cpu":
src = float64(server.State.CPU) src = float64(server.State.CPU)
case "gpu":
src = float64(server.State.GPU)
case "memory": case "memory":
src = percentage(server.State.MemUsed, server.Host.MemTotal) src = percentage(server.State.MemUsed, server.Host.MemTotal)
case "swap": case "swap":
@@ -91,21 +106,21 @@ func (u *Rule) Snapshot(cycleTransferStats *CycleTransferStats, server *Server,
src = float64(server.State.NetInTransfer - uint64(server.PrevHourlyTransferIn)) src = float64(server.State.NetInTransfer - uint64(server.PrevHourlyTransferIn))
if u.CycleInterval != 0 { if u.CycleInterval != 0 {
var res NResult var res NResult
db.Model(&Transfer{}).Select("SUM(`in`) AS n").Where("created_at > ? AND server_id = ?", u.GetTransferDurationStart(), 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(server.State.NetOutTransfer - uint64(server.PrevHourlyTransferOut))
if u.CycleInterval != 0 { if u.CycleInterval != 0 {
var res NResult var res NResult
db.Model(&Transfer{}).Select("SUM(`out`) AS n").Where("created_at > ? AND server_id = ?", u.GetTransferDurationStart(), 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(server.State.NetOutTransfer - uint64(server.PrevHourlyTransferOut) + server.State.NetInTransfer - uint64(server.PrevHourlyTransferIn))
if u.CycleInterval != 0 { if u.CycleInterval != 0 {
var res NResult var res NResult
db.Model(&Transfer{}).Select("SUM(`in`+`out`) AS n").Where("created_at > ? AND server_id = ?", u.GetTransferDurationStart(), 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)
src += float64(res.N) src += float64(res.N)
} }
case "load1": case "load1":
@@ -120,6 +135,16 @@ func (u *Rule) Snapshot(cycleTransferStats *CycleTransferStats, server *Server,
src = float64(server.State.UdpConnCount) src = float64(server.State.UdpConnCount)
case "process_count": case "process_count":
src = float64(server.State.ProcessCount) src = float64(server.State.ProcessCount)
case "temperature_max":
var temp []float64
if server.State.Temperatures != nil {
for _, tempStat := range server.State.Temperatures {
if tempStat.Temperature != 0 {
temp = append(temp, tempStat.Temperature)
}
}
src = maxSliceValue(temp)
}
} }
// 循环区间流量检测 · 更新下次需要检测时间 // 循环区间流量检测 · 更新下次需要检测时间
+3 -1
View File
@@ -21,6 +21,7 @@ type Server struct {
EnableIPv4 bool // 是否启用DDNS IPv4 EnableIPv4 bool // 是否启用DDNS IPv4
EnableIpv6 bool // 是否启用DDNS IPv6 EnableIpv6 bool // 是否启用DDNS IPv6
DDNSDomain string // DDNS中的前缀 如基础域名为abc.oracle DDNSName为mjj 就会把mjj.abc.oracle解析服务器IP 为空则停用 DDNSDomain string // DDNS中的前缀 如基础域名为abc.oracle DDNSName为mjj 就会把mjj.abc.oracle解析服务器IP 为空则停用
DDNSProfile string // DDNS配置
Host *Host `gorm:"-"` Host *Host `gorm:"-"`
State *HostState `gorm:"-"` State *HostState `gorm:"-"`
@@ -56,5 +57,6 @@ func (s Server) Marshal() template.JS {
note, _ := utils.Json.Marshal(s.Note) note, _ := utils.Json.Marshal(s.Note)
secret, _ := utils.Json.Marshal(s.Secret) secret, _ := utils.Json.Marshal(s.Secret)
ddnsDomain, _ := utils.Json.Marshal(s.DDNSDomain) ddnsDomain, _ := utils.Json.Marshal(s.DDNSDomain)
return template.JS(fmt.Sprintf(`{"ID":%d,"Name":%s,"Secret":%s,"DisplayIndex":%d,"Tag":%s,"Note":%s,"HideForGuest": %s,"EnableDDNS": %s,"EnableIPv4": %s,"EnableIpv6": %s,"DDNSDomain": %s}`, s.ID, name, secret, s.DisplayIndex, tag, note, boolToString(s.HideForGuest), boolToString(s.EnableDDNS), boolToString(s.EnableIPv4), boolToString(s.EnableIpv6), ddnsDomain)) // #nosec ddnsProfile, _ := utils.Json.Marshal(s.DDNSProfile)
return template.JS(fmt.Sprintf(`{"ID":%d,"Name":%s,"Secret":%s,"DisplayIndex":%d,"Tag":%s,"Note":%s,"HideForGuest": %s,"EnableDDNS": %s,"EnableIPv4": %s,"EnableIpv6": %s,"DDNSDomain": %s,"DDNSProfile": %s}`, s.ID, name, secret, s.DisplayIndex, tag, note, boolToString(s.HideForGuest), boolToString(s.EnableDDNS), boolToString(s.EnableIPv4), boolToString(s.EnableIpv6), ddnsDomain, ddnsProfile)) // #nosec
} }
+7 -7
View File
@@ -13,7 +13,7 @@ type ProviderCloudflare struct {
Secret string Secret string
} }
func (provider ProviderCloudflare) UpdateDomain(domainConfig *DomainConfig) bool { func (provider *ProviderCloudflare) UpdateDomain(domainConfig *DomainConfig) bool {
if domainConfig == nil { if domainConfig == nil {
return false return false
} }
@@ -42,7 +42,7 @@ func (provider ProviderCloudflare) UpdateDomain(domainConfig *DomainConfig) bool
return resultV4 && resultV6 return resultV4 && resultV6
} }
func (provider ProviderCloudflare) addDomainRecord(zoneID string, domainConfig *DomainConfig, isIpv4 bool) bool { func (provider *ProviderCloudflare) addDomainRecord(zoneID string, domainConfig *DomainConfig, isIpv4 bool) bool {
record, err := provider.findDNSRecord(zoneID, domainConfig.FullDomain, isIpv4) record, err := provider.findDNSRecord(zoneID, domainConfig.FullDomain, isIpv4)
if err != nil { if err != nil {
log.Printf("查找 DNS 记录时出错: %s\n", err) log.Printf("查找 DNS 记录时出错: %s\n", err)
@@ -58,7 +58,7 @@ func (provider ProviderCloudflare) addDomainRecord(zoneID string, domainConfig *
} }
} }
func (provider ProviderCloudflare) getZoneID(domain string) (string, error) { func (provider *ProviderCloudflare) getZoneID(domain string) (string, error) {
_, realDomain := SplitDomain(domain) _, realDomain := SplitDomain(domain)
url := fmt.Sprintf("https://api.cloudflare.com/client/v4/zones?name=%s", realDomain) url := fmt.Sprintf("https://api.cloudflare.com/client/v4/zones?name=%s", realDomain)
body, err := provider.sendRequest("GET", url, nil) body, err := provider.sendRequest("GET", url, nil)
@@ -81,7 +81,7 @@ func (provider ProviderCloudflare) getZoneID(domain string) (string, error) {
return "", fmt.Errorf("找不到 Zone ID") return "", fmt.Errorf("找不到 Zone ID")
} }
func (provider ProviderCloudflare) findDNSRecord(zoneID string, domain string, isIPv4 bool) (map[string]interface{}, error) { func (provider *ProviderCloudflare) findDNSRecord(zoneID string, domain string, isIPv4 bool) (map[string]interface{}, error) {
var ipType = "A" var ipType = "A"
if !isIPv4 { if !isIPv4 {
ipType = "AAAA" ipType = "AAAA"
@@ -106,7 +106,7 @@ func (provider ProviderCloudflare) findDNSRecord(zoneID string, domain string, i
return nil, nil // 没有找到 DNS 记录 return nil, nil // 没有找到 DNS 记录
} }
func (provider ProviderCloudflare) createDNSRecord(zoneID string, domainConfig *DomainConfig, isIPv4 bool) bool { func (provider *ProviderCloudflare) createDNSRecord(zoneID string, domainConfig *DomainConfig, isIPv4 bool) bool {
var ipType = "A" var ipType = "A"
var ipAddr = domainConfig.Ipv4Addr var ipAddr = domainConfig.Ipv4Addr
if !isIPv4 { if !isIPv4 {
@@ -126,7 +126,7 @@ func (provider ProviderCloudflare) createDNSRecord(zoneID string, domainConfig *
return err == nil return err == nil
} }
func (provider ProviderCloudflare) updateDNSRecord(zoneID string, recordID string, domainConfig *DomainConfig, isIPv4 bool) bool { func (provider *ProviderCloudflare) updateDNSRecord(zoneID string, recordID string, domainConfig *DomainConfig, isIPv4 bool) bool {
var ipType = "A" var ipType = "A"
var ipAddr = domainConfig.Ipv4Addr var ipAddr = domainConfig.Ipv4Addr
if !isIPv4 { if !isIPv4 {
@@ -147,7 +147,7 @@ func (provider ProviderCloudflare) updateDNSRecord(zoneID string, recordID strin
} }
// 以下为辅助方法,如发送 HTTP 请求等 // 以下为辅助方法,如发送 HTTP 请求等
func (provider ProviderCloudflare) sendRequest(method string, url string, data []byte) ([]byte, error) { func (provider *ProviderCloudflare) sendRequest(method string, url string, data []byte) ([]byte, error) {
client := &http.Client{} client := &http.Client{}
req, err := http.NewRequest(method, url, bytes.NewBuffer(data)) req, err := http.NewRequest(method, url, bytes.NewBuffer(data))
if err != nil { if err != nil {
+1 -1
View File
@@ -2,6 +2,6 @@ package ddns
type ProviderDummy struct{} type ProviderDummy struct{}
func (provider ProviderDummy) UpdateDomain(domainConfig *DomainConfig) bool { func (provider *ProviderDummy) UpdateDomain(domainConfig *DomainConfig) bool {
return false return false
} }
+5 -26
View File
@@ -1,7 +1,7 @@
package ddns package ddns
import ( import (
"fmt" "golang.org/x/net/publicsuffix"
"net/http" "net/http"
"strings" "strings"
) )
@@ -33,29 +33,8 @@ func SetStringHeadersToRequest(req *http.Request, headers []string) {
} }
// SplitDomain 分割域名为前缀和一级域名 // SplitDomain 分割域名为前缀和一级域名
func SplitDomain(domain string) (prefix string, topLevelDomain string) { func SplitDomain(domain string) (prefix string, realDomain string) {
// 带有二级TLD的一些常见例子,需要特别处理 realDomain, _ = publicsuffix.EffectiveTLDPlusOne(domain)
secondLevelTLDs := map[string]bool{ prefix = domain[:len(domain)-len(realDomain)-1]
".co.uk": true, ".com.cn": true, ".gov.cn": true, ".net.cn": true, ".org.cn": true, return prefix, realDomain
}
// 分割域名为"."的各部分
parts := strings.Split(domain, ".")
// 处理特殊情况,例如 ".co.uk"
for i := len(parts) - 2; i > 0; i-- {
potentialTLD := fmt.Sprintf(".%s.%s", parts[i], parts[i+1])
if secondLevelTLDs[potentialTLD] {
if i > 1 {
return strings.Join(parts[:i-1], "."), strings.Join(parts[i-1:], ".")
}
return "", domain // 当域名仅为二级TLD时,无前缀
}
}
// 常规处理,查找最后一个"."前的所有内容作为前缀
if len(parts) > 2 {
return strings.Join(parts[:len(parts)-2], "."), strings.Join(parts[len(parts)-2:], ".")
}
return "", domain // 当域名不包含子域名时,无前缀
} }
+228
View File
@@ -0,0 +1,228 @@
package ddns
import (
"bytes"
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"io"
"log"
"net/http"
"strconv"
"strings"
"time"
)
const (
url = "https://dnspod.tencentcloudapi.com"
)
type ProviderTencentCloud struct {
SecretID string
SecretKey string
}
func (provider *ProviderTencentCloud) UpdateDomain(domainConfig *DomainConfig) bool {
if domainConfig == nil {
return false
}
// 当IPv4和IPv6同时成功才算作成功
var resultV4 = true
var resultV6 = true
if domainConfig.EnableIPv4 {
if !provider.addDomainRecord(domainConfig, true) {
resultV4 = false
}
}
if domainConfig.EnableIpv6 {
if !provider.addDomainRecord(domainConfig, false) {
resultV6 = false
}
}
return resultV4 && resultV6
}
func (provider *ProviderTencentCloud) addDomainRecord(domainConfig *DomainConfig, isIpv4 bool) bool {
record, err := provider.findDNSRecord(domainConfig.FullDomain, isIpv4)
if err != nil {
log.Printf("查找 DNS 记录时出错: %s\n", err)
return false
}
if errResponse, ok := record["Error"].(map[string]interface{}); ok {
if errCode, ok := errResponse["Code"].(string); ok && errCode == "ResourceNotFound.NoDataOfRecord" { // 没有找到 DNS 记录
// 添加 DNS 记录
return provider.createDNSRecord(domainConfig.FullDomain, domainConfig, isIpv4)
} else {
log.Printf("查询 DNS 记录时出错,错误代码为: %s\n", errCode)
}
}
// 默认情况下更新 DNS 记录
return provider.updateDNSRecord(domainConfig.FullDomain, record["RecordList"].([]interface{})[0].(map[string]interface{})["RecordId"].(float64), domainConfig, isIpv4)
}
func (provider *ProviderTencentCloud) findDNSRecord(domain string, isIPv4 bool) (map[string]interface{}, error) {
var ipType = "A"
if !isIPv4 {
ipType = "AAAA"
}
_, realDomain := SplitDomain(domain)
prefix, _ := SplitDomain(domain)
data := map[string]interface{}{
"RecordType": ipType,
"Domain": realDomain,
"RecordLine": "默认",
"Subdomain": prefix,
}
jsonData, _ := json.Marshal(data)
body, err := provider.sendRequest("DescribeRecordList", jsonData)
if err != nil {
return nil, err
}
var res map[string]interface{}
err = json.Unmarshal(body, &res)
if err != nil {
return nil, err
}
result := res["Response"].(map[string]interface{})
return result, nil
}
func (provider *ProviderTencentCloud) createDNSRecord(domain string, domainConfig *DomainConfig, isIPv4 bool) bool {
var ipType = "A"
var ipAddr = domainConfig.Ipv4Addr
if !isIPv4 {
ipType = "AAAA"
ipAddr = domainConfig.Ipv6Addr
}
_, realDomain := SplitDomain(domain)
prefix, _ := SplitDomain(domain)
data := map[string]interface{}{
"RecordType": ipType,
"RecordLine": "默认",
"Domain": realDomain,
"SubDomain": prefix,
"Value": ipAddr,
"TTL": 600,
}
jsonData, _ := json.Marshal(data)
_, err := provider.sendRequest("CreateRecord", jsonData)
return err == nil
}
func (provider *ProviderTencentCloud) updateDNSRecord(domain string, recordID float64, domainConfig *DomainConfig, isIPv4 bool) bool {
var ipType = "A"
var ipAddr = domainConfig.Ipv4Addr
if !isIPv4 {
ipType = "AAAA"
ipAddr = domainConfig.Ipv6Addr
}
_, realDomain := SplitDomain(domain)
prefix, _ := SplitDomain(domain)
data := map[string]interface{}{
"RecordType": ipType,
"RecordLine": "默认",
"Domain": realDomain,
"SubDomain": prefix,
"Value": ipAddr,
"TTL": 600,
"RecordId": recordID,
}
jsonData, _ := json.Marshal(data)
_, err := provider.sendRequest("ModifyRecord", jsonData)
return err == nil
}
// 以下为辅助方法,如发送 HTTP 请求等
func (provider *ProviderTencentCloud) sendRequest(action string, data []byte) ([]byte, error) {
client := &http.Client{}
req, err := http.NewRequest("POST", url, bytes.NewBuffer(data))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-TC-Version", "2021-03-23")
provider.signRequest(provider.SecretID, provider.SecretKey, req, action, string(data))
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer func(Body io.ReadCloser) {
err := Body.Close()
if err != nil {
log.Printf("NEZHA>> 无法关闭HTTP响应体流: %s\n", err.Error())
}
}(resp.Body)
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return body, nil
}
// https://github.com/jeessy2/ddns-go/blob/master/util/tencent_cloud_signer.go
func (provider *ProviderTencentCloud) sha256hex(s string) string {
b := sha256.Sum256([]byte(s))
return hex.EncodeToString(b[:])
}
func (provider *ProviderTencentCloud) hmacsha256(s, key string) string {
hashed := hmac.New(sha256.New, []byte(key))
hashed.Write([]byte(s))
return string(hashed.Sum(nil))
}
func (provider *ProviderTencentCloud) WriteString(strs ...string) string {
var b strings.Builder
for _, str := range strs {
b.WriteString(str)
}
return b.String()
}
func (provider *ProviderTencentCloud) signRequest(secretId string, secretKey string, r *http.Request, action string, payload string) {
algorithm := "TC3-HMAC-SHA256"
service := "dnspod"
host := provider.WriteString(service, ".tencentcloudapi.com")
timestamp := time.Now().Unix()
timestampStr := strconv.FormatInt(timestamp, 10)
// 步骤 1:拼接规范请求串
canonicalHeaders := provider.WriteString("content-type:application/json\nhost:", host, "\nx-tc-action:", strings.ToLower(action), "\n")
signedHeaders := "content-type;host;x-tc-action"
hashedRequestPayload := provider.sha256hex(payload)
canonicalRequest := provider.WriteString("POST\n/\n\n", canonicalHeaders, "\n", signedHeaders, "\n", hashedRequestPayload)
// 步骤 2:拼接待签名字符串
date := time.Unix(timestamp, 0).UTC().Format("2006-01-02")
credentialScope := provider.WriteString(date, "/", service, "/tc3_request")
hashedCanonicalRequest := provider.sha256hex(canonicalRequest)
string2sign := provider.WriteString(algorithm, "\n", timestampStr, "\n", credentialScope, "\n", hashedCanonicalRequest)
// 步骤 3:计算签名
secretDate := provider.hmacsha256(date, provider.WriteString("TC3", secretKey))
secretService := provider.hmacsha256(service, secretDate)
secretSigning := provider.hmacsha256("tc3_request", secretService)
signature := hex.EncodeToString([]byte(provider.hmacsha256(string2sign, secretSigning)))
// 步骤 4:拼接 Authorization
authorization := provider.WriteString(algorithm, " Credential=", secretId, "/", credentialScope, ", SignedHeaders=", signedHeaders, ", Signature=", signature)
r.Header.Add("Authorization", authorization)
r.Header.Set("Host", host)
r.Header.Set("X-TC-Action", action)
r.Header.Add("X-TC-Timestamp", timestampStr)
}
+1 -1
View File
@@ -14,7 +14,7 @@ type ProviderWebHook struct {
RequestHeader string RequestHeader string
} }
func (provider ProviderWebHook) UpdateDomain(domainConfig *DomainConfig) bool { func (provider *ProviderWebHook) UpdateDomain(domainConfig *DomainConfig) bool {
if domainConfig == nil { if domainConfig == nil {
return false return false
} }
+22
View File
@@ -0,0 +1,22 @@
package cloudflare
import (
"github.com/naiba/nezha/model"
"github.com/naiba/nezha/service/singleton"
)
type UserInfo struct {
Sub string `json:"sub"`
Email string `json:"email"`
Name string `json:"name"`
Groups []string `json:"groups"`
}
func (u UserInfo) MapToNezhaUser() model.User {
var user model.User
singleton.DB.Where("login = ?", u.Sub).First(&user)
user.Login = u.Sub
user.Email = u.Email
user.Name = u.Name
return user
}
+213 -108
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.30.0 // protoc-gen-go v1.34.1
// protoc v3.21.12 // protoc v5.26.1
// source: proto/nezha.proto // source: proto/nezha.proto
package proto package proto
@@ -37,6 +37,7 @@ type Host struct {
Ip string `protobuf:"bytes,10,opt,name=ip,proto3" json:"ip,omitempty"` Ip string `protobuf:"bytes,10,opt,name=ip,proto3" json:"ip,omitempty"`
CountryCode string `protobuf:"bytes,11,opt,name=country_code,json=countryCode,proto3" json:"country_code,omitempty"` CountryCode string `protobuf:"bytes,11,opt,name=country_code,json=countryCode,proto3" json:"country_code,omitempty"`
Version string `protobuf:"bytes,12,opt,name=version,proto3" json:"version,omitempty"` Version string `protobuf:"bytes,12,opt,name=version,proto3" json:"version,omitempty"`
Gpu []string `protobuf:"bytes,13,rep,name=gpu,proto3" json:"gpu,omitempty"`
} }
func (x *Host) Reset() { func (x *Host) Reset() {
@@ -155,26 +156,35 @@ func (x *Host) GetVersion() string {
return "" return ""
} }
func (x *Host) GetGpu() []string {
if x != nil {
return x.Gpu
}
return nil
}
type State struct { type State struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
Cpu float64 `protobuf:"fixed64,1,opt,name=cpu,proto3" json:"cpu,omitempty"` Cpu float64 `protobuf:"fixed64,1,opt,name=cpu,proto3" json:"cpu,omitempty"`
MemUsed uint64 `protobuf:"varint,3,opt,name=mem_used,json=memUsed,proto3" json:"mem_used,omitempty"` MemUsed uint64 `protobuf:"varint,3,opt,name=mem_used,json=memUsed,proto3" json:"mem_used,omitempty"`
SwapUsed uint64 `protobuf:"varint,4,opt,name=swap_used,json=swapUsed,proto3" json:"swap_used,omitempty"` SwapUsed uint64 `protobuf:"varint,4,opt,name=swap_used,json=swapUsed,proto3" json:"swap_used,omitempty"`
DiskUsed uint64 `protobuf:"varint,5,opt,name=disk_used,json=diskUsed,proto3" json:"disk_used,omitempty"` DiskUsed uint64 `protobuf:"varint,5,opt,name=disk_used,json=diskUsed,proto3" json:"disk_used,omitempty"`
NetInTransfer uint64 `protobuf:"varint,6,opt,name=net_in_transfer,json=netInTransfer,proto3" json:"net_in_transfer,omitempty"` NetInTransfer uint64 `protobuf:"varint,6,opt,name=net_in_transfer,json=netInTransfer,proto3" json:"net_in_transfer,omitempty"`
NetOutTransfer uint64 `protobuf:"varint,7,opt,name=net_out_transfer,json=netOutTransfer,proto3" json:"net_out_transfer,omitempty"` NetOutTransfer uint64 `protobuf:"varint,7,opt,name=net_out_transfer,json=netOutTransfer,proto3" json:"net_out_transfer,omitempty"`
NetInSpeed uint64 `protobuf:"varint,8,opt,name=net_in_speed,json=netInSpeed,proto3" json:"net_in_speed,omitempty"` NetInSpeed uint64 `protobuf:"varint,8,opt,name=net_in_speed,json=netInSpeed,proto3" json:"net_in_speed,omitempty"`
NetOutSpeed uint64 `protobuf:"varint,9,opt,name=net_out_speed,json=netOutSpeed,proto3" json:"net_out_speed,omitempty"` NetOutSpeed uint64 `protobuf:"varint,9,opt,name=net_out_speed,json=netOutSpeed,proto3" json:"net_out_speed,omitempty"`
Uptime uint64 `protobuf:"varint,10,opt,name=uptime,proto3" json:"uptime,omitempty"` Uptime uint64 `protobuf:"varint,10,opt,name=uptime,proto3" json:"uptime,omitempty"`
Load1 float64 `protobuf:"fixed64,11,opt,name=load1,proto3" json:"load1,omitempty"` Load1 float64 `protobuf:"fixed64,11,opt,name=load1,proto3" json:"load1,omitempty"`
Load5 float64 `protobuf:"fixed64,12,opt,name=load5,proto3" json:"load5,omitempty"` Load5 float64 `protobuf:"fixed64,12,opt,name=load5,proto3" json:"load5,omitempty"`
Load15 float64 `protobuf:"fixed64,13,opt,name=load15,proto3" json:"load15,omitempty"` Load15 float64 `protobuf:"fixed64,13,opt,name=load15,proto3" json:"load15,omitempty"`
TcpConnCount uint64 `protobuf:"varint,14,opt,name=tcp_conn_count,json=tcpConnCount,proto3" json:"tcp_conn_count,omitempty"` TcpConnCount uint64 `protobuf:"varint,14,opt,name=tcp_conn_count,json=tcpConnCount,proto3" json:"tcp_conn_count,omitempty"`
UdpConnCount uint64 `protobuf:"varint,15,opt,name=udp_conn_count,json=udpConnCount,proto3" json:"udp_conn_count,omitempty"` UdpConnCount uint64 `protobuf:"varint,15,opt,name=udp_conn_count,json=udpConnCount,proto3" json:"udp_conn_count,omitempty"`
ProcessCount uint64 `protobuf:"varint,16,opt,name=process_count,json=processCount,proto3" json:"process_count,omitempty"` ProcessCount uint64 `protobuf:"varint,16,opt,name=process_count,json=processCount,proto3" json:"process_count,omitempty"`
Temperatures []*State_SensorTemperature `protobuf:"bytes,17,rep,name=temperatures,proto3" json:"temperatures,omitempty"`
Gpu float64 `protobuf:"fixed64,18,opt,name=gpu,proto3" json:"gpu,omitempty"`
} }
func (x *State) Reset() { func (x *State) Reset() {
@@ -314,6 +324,75 @@ func (x *State) GetProcessCount() uint64 {
return 0 return 0
} }
func (x *State) GetTemperatures() []*State_SensorTemperature {
if x != nil {
return x.Temperatures
}
return nil
}
func (x *State) GetGpu() float64 {
if x != nil {
return x.Gpu
}
return 0
}
type State_SensorTemperature struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Temperature float64 `protobuf:"fixed64,2,opt,name=temperature,proto3" json:"temperature,omitempty"`
}
func (x *State_SensorTemperature) Reset() {
*x = State_SensorTemperature{}
if protoimpl.UnsafeEnabled {
mi := &file_proto_nezha_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *State_SensorTemperature) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*State_SensorTemperature) ProtoMessage() {}
func (x *State_SensorTemperature) ProtoReflect() protoreflect.Message {
mi := &file_proto_nezha_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use State_SensorTemperature.ProtoReflect.Descriptor instead.
func (*State_SensorTemperature) Descriptor() ([]byte, []int) {
return file_proto_nezha_proto_rawDescGZIP(), []int{2}
}
func (x *State_SensorTemperature) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *State_SensorTemperature) GetTemperature() float64 {
if x != nil {
return x.Temperature
}
return 0
}
type Task struct { type Task struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@@ -327,7 +406,7 @@ type Task struct {
func (x *Task) Reset() { func (x *Task) Reset() {
*x = Task{} *x = Task{}
if protoimpl.UnsafeEnabled { if protoimpl.UnsafeEnabled {
mi := &file_proto_nezha_proto_msgTypes[2] mi := &file_proto_nezha_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -340,7 +419,7 @@ func (x *Task) String() string {
func (*Task) ProtoMessage() {} func (*Task) ProtoMessage() {}
func (x *Task) ProtoReflect() protoreflect.Message { func (x *Task) ProtoReflect() protoreflect.Message {
mi := &file_proto_nezha_proto_msgTypes[2] mi := &file_proto_nezha_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -353,7 +432,7 @@ func (x *Task) ProtoReflect() protoreflect.Message {
// Deprecated: Use Task.ProtoReflect.Descriptor instead. // Deprecated: Use Task.ProtoReflect.Descriptor instead.
func (*Task) Descriptor() ([]byte, []int) { func (*Task) Descriptor() ([]byte, []int) {
return file_proto_nezha_proto_rawDescGZIP(), []int{2} return file_proto_nezha_proto_rawDescGZIP(), []int{3}
} }
func (x *Task) GetId() uint64 { func (x *Task) GetId() uint64 {
@@ -392,7 +471,7 @@ type TaskResult struct {
func (x *TaskResult) Reset() { func (x *TaskResult) Reset() {
*x = TaskResult{} *x = TaskResult{}
if protoimpl.UnsafeEnabled { if protoimpl.UnsafeEnabled {
mi := &file_proto_nezha_proto_msgTypes[3] mi := &file_proto_nezha_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -405,7 +484,7 @@ func (x *TaskResult) String() string {
func (*TaskResult) ProtoMessage() {} func (*TaskResult) ProtoMessage() {}
func (x *TaskResult) ProtoReflect() protoreflect.Message { func (x *TaskResult) ProtoReflect() protoreflect.Message {
mi := &file_proto_nezha_proto_msgTypes[3] mi := &file_proto_nezha_proto_msgTypes[4]
if protoimpl.UnsafeEnabled && x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -418,7 +497,7 @@ func (x *TaskResult) ProtoReflect() protoreflect.Message {
// Deprecated: Use TaskResult.ProtoReflect.Descriptor instead. // Deprecated: Use TaskResult.ProtoReflect.Descriptor instead.
func (*TaskResult) Descriptor() ([]byte, []int) { func (*TaskResult) Descriptor() ([]byte, []int) {
return file_proto_nezha_proto_rawDescGZIP(), []int{3} return file_proto_nezha_proto_rawDescGZIP(), []int{4}
} }
func (x *TaskResult) GetId() uint64 { func (x *TaskResult) GetId() uint64 {
@@ -467,7 +546,7 @@ type Receipt struct {
func (x *Receipt) Reset() { func (x *Receipt) Reset() {
*x = Receipt{} *x = Receipt{}
if protoimpl.UnsafeEnabled { if protoimpl.UnsafeEnabled {
mi := &file_proto_nezha_proto_msgTypes[4] mi := &file_proto_nezha_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -480,7 +559,7 @@ func (x *Receipt) String() string {
func (*Receipt) ProtoMessage() {} func (*Receipt) ProtoMessage() {}
func (x *Receipt) ProtoReflect() protoreflect.Message { func (x *Receipt) ProtoReflect() protoreflect.Message {
mi := &file_proto_nezha_proto_msgTypes[4] mi := &file_proto_nezha_proto_msgTypes[5]
if protoimpl.UnsafeEnabled && x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -493,7 +572,7 @@ func (x *Receipt) ProtoReflect() protoreflect.Message {
// Deprecated: Use Receipt.ProtoReflect.Descriptor instead. // Deprecated: Use Receipt.ProtoReflect.Descriptor instead.
func (*Receipt) Descriptor() ([]byte, []int) { func (*Receipt) Descriptor() ([]byte, []int) {
return file_proto_nezha_proto_rawDescGZIP(), []int{4} return file_proto_nezha_proto_rawDescGZIP(), []int{5}
} }
func (x *Receipt) GetProced() bool { func (x *Receipt) GetProced() bool {
@@ -507,7 +586,7 @@ var File_proto_nezha_proto protoreflect.FileDescriptor
var file_proto_nezha_proto_rawDesc = []byte{ var file_proto_nezha_proto_rawDesc = []byte{
0x0a, 0x11, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6e, 0x65, 0x7a, 0x68, 0x61, 0x2e, 0x70, 0x72, 0x0a, 0x11, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6e, 0x65, 0x7a, 0x68, 0x61, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x12, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe0, 0x02, 0x0a, 0x04, 0x48, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf2, 0x02, 0x0a, 0x04, 0x48,
0x6f, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x6f, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18,
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12,
0x29, 0x0a, 0x10, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x29, 0x0a, 0x10, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x5f, 0x76, 0x65, 0x72, 0x73,
@@ -529,65 +608,77 @@ var file_proto_nezha_proto_rawDesc = []byte{
0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x64, 0x65,
0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x43, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x43,
0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0c, 0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0c,
0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xd3, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a,
0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x63, 0x70, 0x75, 0x18, 0x01, 0x03, 0x67, 0x70, 0x75, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x67, 0x70, 0x75, 0x22,
0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x63, 0x70, 0x75, 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x65, 0x6d, 0xa9, 0x04, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x63, 0x70, 0x75,
0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x6d, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x63, 0x70, 0x75, 0x12, 0x19, 0x0a, 0x08, 0x6d,
0x55, 0x73, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x75, 0x73, 0x65, 0x65, 0x6d, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x6d,
0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x73, 0x77, 0x61, 0x70, 0x55, 0x73, 0x65, 0x65, 0x6d, 0x55, 0x73, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x75,
0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x05, 0x73, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x73, 0x77, 0x61, 0x70, 0x55,
0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x64, 0x69, 0x73, 0x6b, 0x55, 0x73, 0x65, 0x64, 0x12, 0x26, 0x73, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x75, 0x73, 0x65, 0x64,
0x0a, 0x0f, 0x6e, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x64, 0x69, 0x73, 0x6b, 0x55, 0x73, 0x65, 0x64,
0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x6e, 0x65, 0x74, 0x49, 0x6e, 0x54, 0x72, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73,
0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x12, 0x28, 0x0a, 0x10, 0x6e, 0x65, 0x74, 0x5f, 0x6f, 0x75, 0x66, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x6e, 0x65, 0x74, 0x49, 0x6e,
0x74, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x12, 0x28, 0x0a, 0x10, 0x6e, 0x65, 0x74, 0x5f,
0x52, 0x0e, 0x6e, 0x65, 0x74, 0x4f, 0x75, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x6f, 0x75, 0x74, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01,
0x12, 0x20, 0x0a, 0x0c, 0x6e, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x5f, 0x73, 0x70, 0x65, 0x65, 0x64, 0x28, 0x04, 0x52, 0x0e, 0x6e, 0x65, 0x74, 0x4f, 0x75, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66,
0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x6e, 0x65, 0x74, 0x49, 0x6e, 0x53, 0x70, 0x65, 0x65, 0x72, 0x12, 0x20, 0x0a, 0x0c, 0x6e, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x5f, 0x73, 0x70, 0x65,
0x65, 0x64, 0x12, 0x22, 0x0a, 0x0d, 0x6e, 0x65, 0x74, 0x5f, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x70, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x6e, 0x65, 0x74, 0x49, 0x6e, 0x53,
0x65, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6e, 0x65, 0x74, 0x4f, 0x75, 0x70, 0x65, 0x65, 0x64, 0x12, 0x22, 0x0a, 0x0d, 0x6e, 0x65, 0x74, 0x5f, 0x6f, 0x75, 0x74, 0x5f,
0x74, 0x53, 0x70, 0x65, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x70, 0x65, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6e, 0x65, 0x74,
0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x14, 0x4f, 0x75, 0x74, 0x53, 0x70, 0x65, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x70, 0x74, 0x69,
0x0a, 0x05, 0x6c, 0x6f, 0x61, 0x64, 0x31, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x6c, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65,
0x6f, 0x61, 0x64, 0x31, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x6f, 0x61, 0x64, 0x35, 0x18, 0x0c, 0x20, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x6f, 0x61, 0x64, 0x31, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x01, 0x52,
0x01, 0x28, 0x01, 0x52, 0x05, 0x6c, 0x6f, 0x61, 0x64, 0x35, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x6f, 0x05, 0x6c, 0x6f, 0x61, 0x64, 0x31, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x6f, 0x61, 0x64, 0x35, 0x18,
0x61, 0x64, 0x31, 0x35, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x6c, 0x6f, 0x61, 0x64, 0x0c, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x6c, 0x6f, 0x61, 0x64, 0x35, 0x12, 0x16, 0x0a, 0x06,
0x31, 0x35, 0x12, 0x24, 0x0a, 0x0e, 0x74, 0x63, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x5f, 0x63, 0x6c, 0x6f, 0x61, 0x64, 0x31, 0x35, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x6c, 0x6f,
0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x74, 0x63, 0x70, 0x43, 0x61, 0x64, 0x31, 0x35, 0x12, 0x24, 0x0a, 0x0e, 0x74, 0x63, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x6e,
0x6f, 0x6e, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x75, 0x64, 0x70, 0x5f, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x74, 0x63,
0x63, 0x6f, 0x6e, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x04, 0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x75, 0x64,
0x52, 0x0c, 0x75, 0x64, 0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x23, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0f, 0x20, 0x01,
0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x28, 0x04, 0x52, 0x0c, 0x75, 0x64, 0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74,
0x10, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x43, 0x6f, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e,
0x75, 0x6e, 0x74, 0x22, 0x3e, 0x0a, 0x04, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x74, 0x18, 0x10, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73,
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x42, 0x0a, 0x0c, 0x74, 0x65, 0x6d, 0x70, 0x65, 0x72, 0x61,
0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x11, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x72,
0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x53, 0x65, 0x6e, 0x73, 0x6f, 0x72,
0x61, 0x74, 0x61, 0x22, 0x7a, 0x0a, 0x0a, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x54, 0x65, 0x6d, 0x70, 0x65, 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x0c, 0x74, 0x65, 0x6d,
0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x70, 0x65, 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x67, 0x70, 0x75,
0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x18, 0x12, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x67, 0x70, 0x75, 0x22, 0x4f, 0x0a, 0x17, 0x53,
0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x03, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x53, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x54, 0x65, 0x6d, 0x70, 0x65,
0x20, 0x01, 0x28, 0x02, 0x52, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x74, 0x65,
0x1e, 0x0a, 0x0a, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x18, 0x05, 0x20, 0x6d, 0x70, 0x65, 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52,
0x01, 0x28, 0x08, 0x52, 0x0a, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x22, 0x0b, 0x74, 0x65, 0x6d, 0x70, 0x65, 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x3e, 0x0a, 0x04,
0x21, 0x0a, 0x07, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04,
0x6f, 0x63, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x70, 0x72, 0x6f, 0x63, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01,
0x65, 0x64, 0x32, 0xd6, 0x01, 0x0a, 0x0c, 0x4e, 0x65, 0x7a, 0x68, 0x61, 0x53, 0x65, 0x72, 0x76, 0x28, 0x04, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61,
0x69, 0x63, 0x65, 0x12, 0x33, 0x0a, 0x11, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x79, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x7a, 0x0a, 0x0a,
0x74, 0x65, 0x6d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64,
0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x1a, 0x0e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79,
0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x22, 0x00, 0x12, 0x31, 0x0a, 0x10, 0x52, 0x65, 0x70, 0x6f, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14,
0x72, 0x74, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x0b, 0x2e, 0x70, 0x0a, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x02, 0x52, 0x05, 0x64,
0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01,
0x6f, 0x2e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x22, 0x00, 0x12, 0x31, 0x0a, 0x0a, 0x52, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x75, 0x63, 0x63,
0x65, 0x70, 0x6f, 0x72, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x73, 0x75,
0x6f, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x1a, 0x0e, 0x2e, 0x70, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x22, 0x21, 0x0a, 0x07, 0x52, 0x65, 0x63, 0x65,
0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x22, 0x00, 0x12, 0x2b, 0x69, 0x70, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x64, 0x18, 0x01, 0x20,
0x0a, 0x0b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x0b, 0x2e, 0x01, 0x28, 0x08, 0x52, 0x06, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x64, 0x32, 0xd6, 0x01, 0x0a, 0x0c,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x1a, 0x0b, 0x2e, 0x70, 0x72, 0x6f, 0x4e, 0x65, 0x7a, 0x68, 0x61, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x33, 0x0a, 0x11,
0x74, 0x6f, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x22, 0x00, 0x30, 0x01, 0x42, 0x09, 0x5a, 0x07, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x53, 0x74, 0x61, 0x74,
0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x65, 0x12, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x1a,
0x0e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x22,
0x00, 0x12, 0x31, 0x0a, 0x10, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x79, 0x73, 0x74, 0x65,
0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x0b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x48, 0x6f,
0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x63, 0x65, 0x69,
0x70, 0x74, 0x22, 0x00, 0x12, 0x31, 0x0a, 0x0a, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x54, 0x61,
0x73, 0x6b, 0x12, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x52,
0x65, 0x73, 0x75, 0x6c, 0x74, 0x1a, 0x0e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65,
0x63, 0x65, 0x69, 0x70, 0x74, 0x22, 0x00, 0x12, 0x2b, 0x0a, 0x0b, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x0b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x48,
0x6f, 0x73, 0x74, 0x1a, 0x0b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x61, 0x73, 0x6b,
0x22, 0x00, 0x30, 0x01, 0x42, 0x09, 0x5a, 0x07, 0x2e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (
@@ -602,28 +693,30 @@ func file_proto_nezha_proto_rawDescGZIP() []byte {
return file_proto_nezha_proto_rawDescData return file_proto_nezha_proto_rawDescData
} }
var file_proto_nezha_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_proto_nezha_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
var file_proto_nezha_proto_goTypes = []interface{}{ var file_proto_nezha_proto_goTypes = []interface{}{
(*Host)(nil), // 0: proto.Host (*Host)(nil), // 0: proto.Host
(*State)(nil), // 1: proto.State (*State)(nil), // 1: proto.State
(*Task)(nil), // 2: proto.Task (*State_SensorTemperature)(nil), // 2: proto.State_SensorTemperature
(*TaskResult)(nil), // 3: proto.TaskResult (*Task)(nil), // 3: proto.Task
(*Receipt)(nil), // 4: proto.Receipt (*TaskResult)(nil), // 4: proto.TaskResult
(*Receipt)(nil), // 5: proto.Receipt
} }
var file_proto_nezha_proto_depIdxs = []int32{ var file_proto_nezha_proto_depIdxs = []int32{
1, // 0: proto.NezhaService.ReportSystemState:input_type -> proto.State 2, // 0: proto.State.temperatures:type_name -> proto.State_SensorTemperature
0, // 1: proto.NezhaService.ReportSystemInfo:input_type -> proto.Host 1, // 1: proto.NezhaService.ReportSystemState:input_type -> proto.State
3, // 2: proto.NezhaService.ReportTask:input_type -> proto.TaskResult 0, // 2: proto.NezhaService.ReportSystemInfo:input_type -> proto.Host
0, // 3: proto.NezhaService.RequestTask:input_type -> proto.Host 4, // 3: proto.NezhaService.ReportTask:input_type -> proto.TaskResult
4, // 4: proto.NezhaService.ReportSystemState:output_type -> proto.Receipt 0, // 4: proto.NezhaService.RequestTask:input_type -> proto.Host
4, // 5: proto.NezhaService.ReportSystemInfo:output_type -> proto.Receipt 5, // 5: proto.NezhaService.ReportSystemState:output_type -> proto.Receipt
4, // 6: proto.NezhaService.ReportTask:output_type -> proto.Receipt 5, // 6: proto.NezhaService.ReportSystemInfo:output_type -> proto.Receipt
2, // 7: proto.NezhaService.RequestTask:output_type -> proto.Task 5, // 7: proto.NezhaService.ReportTask:output_type -> proto.Receipt
4, // [4:8] is the sub-list for method output_type 3, // 8: proto.NezhaService.RequestTask:output_type -> proto.Task
0, // [0:4] is the sub-list for method input_type 5, // [5:9] is the sub-list for method output_type
0, // [0:0] is the sub-list for extension type_name 1, // [1:5] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension extendee 1, // [1:1] is the sub-list for extension type_name
0, // [0:0] is the sub-list for field type_name 1, // [1:1] is the sub-list for extension extendee
0, // [0:1] is the sub-list for field type_name
} }
func init() { file_proto_nezha_proto_init() } func init() { file_proto_nezha_proto_init() }
@@ -657,7 +750,7 @@ func file_proto_nezha_proto_init() {
} }
} }
file_proto_nezha_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { file_proto_nezha_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Task); i { switch v := v.(*State_SensorTemperature); i {
case 0: case 0:
return &v.state return &v.state
case 1: case 1:
@@ -669,7 +762,7 @@ func file_proto_nezha_proto_init() {
} }
} }
file_proto_nezha_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { file_proto_nezha_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*TaskResult); i { switch v := v.(*Task); i {
case 0: case 0:
return &v.state return &v.state
case 1: case 1:
@@ -681,6 +774,18 @@ func file_proto_nezha_proto_init() {
} }
} }
file_proto_nezha_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { file_proto_nezha_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*TaskResult); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_proto_nezha_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Receipt); i { switch v := v.(*Receipt); i {
case 0: case 0:
return &v.state return &v.state
@@ -699,7 +804,7 @@ func file_proto_nezha_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_proto_nezha_proto_rawDesc, RawDescriptor: file_proto_nezha_proto_rawDesc,
NumEnums: 0, NumEnums: 0,
NumMessages: 5, NumMessages: 6,
NumExtensions: 0, NumExtensions: 0,
NumServices: 1, NumServices: 1,
}, },
+8
View File
@@ -23,6 +23,7 @@ message Host {
string ip = 10; string ip = 10;
string country_code = 11; string country_code = 11;
string version = 12; string version = 12;
repeated string gpu = 13;
} }
message State { message State {
@@ -41,6 +42,13 @@ message State {
uint64 tcp_conn_count = 14; uint64 tcp_conn_count = 14;
uint64 udp_conn_count = 15; uint64 udp_conn_count = 15;
uint64 process_count = 16; uint64 process_count = 16;
repeated State_SensorTemperature temperatures = 17;
double gpu = 18;
}
message State_SensorTemperature {
string name = 1;
double temperature = 2;
} }
message Task { message Task {
+6
View File
@@ -628,6 +628,9 @@ other = "Enable DDNS IPv6"
[DDNSDomain] [DDNSDomain]
other = "DDNS Domain" other = "DDNS Domain"
[DDNSProfile]
other = "DDNS Profile Name"
[Feature] [Feature]
other = "Feature" other = "Feature"
@@ -637,6 +640,9 @@ other = "Template"
[Stat] [Stat]
other = "Asset" other = "Asset"
[Temperature]
other = "Temperature"
[DisableSwitchTemplateInFrontend] [DisableSwitchTemplateInFrontend]
other = "Disable Switch Template in Frontend" other = "Disable Switch Template in Frontend"
+6
View File
@@ -628,6 +628,9 @@ other = "Habilitar DDNS IPv6"
[DDNSDomain] [DDNSDomain]
other = "Dominio DDNS" other = "Dominio DDNS"
[DDNSProfile]
other = "Nombre del perfil de DDNS"
[Feature] [Feature]
other = "Característica" other = "Característica"
@@ -637,6 +640,9 @@ other = "Plantilla"
[Stat] [Stat]
other = "Stat" other = "Stat"
[Temperature]
other = "Temperatura"
[DisableSwitchTemplateInFrontend] [DisableSwitchTemplateInFrontend]
other = "Deshabilitar Cambio de Plantilla en Frontend" other = "Deshabilitar Cambio de Plantilla en Frontend"
+6
View File
@@ -628,6 +628,9 @@ other = "启用DDNS IPv6"
[DDNSDomain] [DDNSDomain]
other = "DDNS域名" other = "DDNS域名"
[DDNSProfile]
other = "DDNS配置名"
[Feature] [Feature]
other = "功能" other = "功能"
@@ -637,6 +640,9 @@ other = "主题"
[Stat] [Stat]
other = "信息" other = "信息"
[Temperature]
other = "温度"
[DisableSwitchTemplateInFrontend] [DisableSwitchTemplateInFrontend]
other = "禁止前台切换模板" other = "禁止前台切换模板"
+6
View File
@@ -628,6 +628,9 @@ other = "啟用DDNS IPv6"
[DDNSDomain] [DDNSDomain]
other = "DDNS網域" other = "DDNS網域"
[DDNSProfile]
other = "DDNS設定名"
[Feature] [Feature]
other = "功能" other = "功能"
@@ -637,6 +640,9 @@ other = "主題"
[Stat] [Stat]
other = "信息" other = "信息"
[Temperature]
other = "溫度"
[DisableSwitchTemplateInFrontend] [DisableSwitchTemplateInFrontend]
other = "禁止前台切換主題" other = "禁止前台切換主題"
+1
View File
@@ -303,6 +303,7 @@ function addOrEditServer(server, conf) {
modal.find("input[name=name]").val(server ? server.Name : null); modal.find("input[name=name]").val(server ? server.Name : null);
modal.find("input[name=Tag]").val(server ? server.Tag : null); modal.find("input[name=Tag]").val(server ? server.Tag : null);
modal.find("input[name=DDNSDomain]").val(server ? server.DDNSDomain : null); modal.find("input[name=DDNSDomain]").val(server ? server.DDNSDomain : null);
modal.find("input[name=DDNSProfile]").val(server ? server.DDNSProfile : null);
modal modal
.find("input[name=DisplayIndex]") .find("input[name=DisplayIndex]")
.val(server ? server.DisplayIndex : null); .val(server ? server.DisplayIndex : null);
+4
View File
@@ -145,6 +145,10 @@ td {
z-index: 9999; z-index: 9999;
} }
.temp-detail {
cursor: pointer;
}
.ui.content { .ui.content {
margin:0 !important; margin:0 !important;
padding:1em !important; padding:1em !important;
+1 -1
View File
@@ -6,7 +6,7 @@ const mixinsVue = {
adaptedTemplates: [ adaptedTemplates: [
{ key: 'default', name: 'Default', icon: 'th large' }, { key: 'default', name: 'Default', icon: 'th large' },
{ key: 'angel-kanade', name: 'AngelKanade', icon: 'square' }, { key: 'angel-kanade', name: 'AngelKanade', icon: 'square' },
{ key: 'server-status', name: 'SeverStatus', icon: 'list' } { key: 'server-status', name: 'ServerStatus', icon: 'list' }
] ]
}, },
created() { created() {
+4
View File
@@ -230,6 +230,10 @@ body {
max-width: 50px; max-width: 50px;
} }
.temp-detail {
cursor: pointer;
}
/*正文结束*/ /*正文结束*/
/* 服务页 正文*/ /* 服务页 正文*/
+5 -2
View File
@@ -10,7 +10,7 @@ const mixinsVue = {
adaptedTemplates: [ adaptedTemplates: [
{ key: 'default', name: 'Default', icon: 'th large' }, { key: 'default', name: 'Default', icon: 'th large' },
{ key: 'angel-kanade', name: 'AngelKanade', icon: 'square' }, { key: 'angel-kanade', name: 'AngelKanade', icon: 'square' },
{ key: 'server-status', name: 'SeverStatus', icon: 'list' } { key: 'server-status', name: 'ServerStatus', icon: 'list' }
] ]
}, },
created() { created() {
@@ -27,13 +27,16 @@ const mixinsVue = {
toggleView() { toggleView() {
this.showGroup = !this.showGroup; this.showGroup = !this.showGroup;
localStorage.setItem("showGroup", JSON.stringify(this.showGroup)); localStorage.setItem("showGroup", JSON.stringify(this.showGroup));
if(this.$root.page == 'service') {
this.$root.initTooltip();
}
return this.showGroup; return this.showGroup;
}, },
storedShowGroup() { storedShowGroup() {
const storedShowGroup = localStorage.getItem("showGroup"); const storedShowGroup = localStorage.getItem("showGroup");
if (storedShowGroup !== null) { if (storedShowGroup !== null) {
this.showGroup = JSON.parse(storedShowGroup); this.showGroup = JSON.parse(storedShowGroup);
} }
}, },
toggleTemplate(template) { toggleTemplate(template) {
if( template != this.preferredTemplate){ if( template != this.preferredTemplate){
+5 -1
View File
@@ -48,6 +48,10 @@
<label>{{tr "DDNSDomain"}}</label> <label>{{tr "DDNSDomain"}}</label>
<input type="text" name="DDNSDomain" placeholder="{{tr "DDNSDomain"}}"> <input type="text" name="DDNSDomain" placeholder="{{tr "DDNSDomain"}}">
</div> </div>
<div class="field">
<label>{{tr "DDNSProfile"}}</label>
<input type="text" name="DDNSProfile" placeholder="{{tr "DDNSProfile"}}">
</div>
<div class="field"> <div class="field">
<label>{{tr "Note"}}</label> <label>{{tr "Note"}}</label>
<textarea name="Note"></textarea> <textarea name="Note"></textarea>
@@ -57,7 +61,7 @@
<div class="ui message"> <div class="ui message">
{{if .Conf.GRPCHost}} {{if .Conf.GRPCHost}}
curl -L https://raw.githubusercontent.com/naiba/nezha/master/script/install.sh -o nezha.sh && chmod curl -L https://raw.githubusercontent.com/naiba/nezha/master/script/install.sh -o nezha.sh && chmod
+x nezha.sh && sudo ./nezha.sh install_agent <code class="command">{{.Conf.GRPCHost}}</code> <code +x nezha.sh && ./nezha.sh install_agent <code class="command">{{.Conf.GRPCHost}}</code> <code
class="command">{{if .Conf.ProxyGRPCPort}}{{.Conf.ProxyGRPCPort}}{{else}}{{.Conf.GRPCPort}}{{end}}</code> <code class="command">{{if .Conf.ProxyGRPCPort}}{{.Conf.ProxyGRPCPort}}{{else}}{{.Conf.GRPCPort}}{{end}}</code> <code
class="command hostSecret"></code> <code class="command">{{if .Conf.TLS}}--tls{{end}}</code> class="command hostSecret"></code> <code class="command">{{if .Conf.TLS}}--tls{{end}}</code>
{{else}} {{else}}
+5
View File
@@ -69,6 +69,11 @@
data-tooltip="{{tr "ClickToCopy"}}"> data-tooltip="{{tr "ClickToCopy"}}">
<i class="windows icon"></i> <i class="windows icon"></i>
</button> </button>
<button class="ui icon green mini button"
data-clipboard-text="{{if $.Conf.GRPCHost}}curl -L https://raw.githubusercontent.com/naiba/nezha/master/script/install.command -o nezha.command && chmod +x nezha.command && sudo ./nezha.command install_agent {{$.Conf.GRPCHost}} {{if $.Conf.ProxyGRPCPort}}{{$.Conf.ProxyGRPCPort}}{{else}}{{$.Conf.GRPCPort}}{{end}} {{$server.Secret}}{{if $.Conf.TLS}} --tls{{end}}{{else}}{{tr "NoDomainAlert"}}{{end}}"
data-tooltip="{{tr "ClickToCopy"}}">
<i class="apple icon"></i>
</button>
</td> </td>
<td style="word-break: break-word;white-space: pre-wrap;">{{$server.Note}}</td> <td style="word-break: break-word;white-space: pre-wrap;">{{$server.Note}}</td>
<td> <td>
+3
View File
@@ -24,6 +24,9 @@
[<span [<span
v-if="server.Host.Virtualization">@#server.Host.Virtualization#@:</span>@#server.Host.Arch#@]<br /> v-if="server.Host.Virtualization">@#server.Host.Virtualization#@:</span>@#server.Host.Arch#@]<br />
CPU: @#server.Host.CPU#@<br /> CPU: @#server.Host.CPU#@<br />
<template v-if="server.Host.GPU">
GPU: @#server.Host.GPU#@
</template>
{{tr "DiskUsed"}}: {{tr "DiskUsed"}}:
@#formatByteSize(server.State.DiskUsed)#@/@#formatByteSize(server.Host.DiskTotal)#@<br /> @#formatByteSize(server.State.DiskUsed)#@/@#formatByteSize(server.Host.DiskTotal)#@<br />
{{tr "MemUsed"}}: {{tr "MemUsed"}}:
+3
View File
@@ -24,6 +24,9 @@
<i :class="item.icon + ' icon'"></i>@#item.name#@ <i :class="item.icon + ' icon'"></i>@#item.name#@
<i class="check icon" v-if="preferredTemplate === item.key"></i> <i class="check icon" v-if="preferredTemplate === item.key"></i>
</a> </a>
<a v-if="defaultTemplate === 'custom'" @click="toggleTemplate('custom')" class="item">
<i class="expand icon"></i>Custom
</a>
</div> </div>
</div> </div>
{{ end }} {{ end }}
+12 -11
View File
@@ -7,19 +7,20 @@
<meta http-equiv="X-UA-Compatible" content="ie=edge"> <meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta content="telephone=no" name="format-detection"> <meta content="telephone=no" name="format-detection">
<title>{{.Title}}</title> <title>{{.Title}}</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/semantic.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/assets/font-logos.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/lipis/[email protected]/css/flag-icons.min.css">
<link rel="stylesheet" type="text/css" href="/static/semantic-ui-alerts.min.css">
<link rel="stylesheet" type="text/css" href="/static/theme-default/css/main.css?v20240226">
<link rel="shortcut icon" type="image/png" href="/static/logo.svg" /> <link rel="shortcut icon" type="image/png" href="/static/logo.svg" />
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js"></script> <link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/semantic.min.css">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/semantic.min.js"></script> <link rel="stylesheet" href="https://unpkg.com/[email protected]/assets/font-logos.css">
<link rel="stylesheet" href="https://unpkg.com/[email protected]/font/bootstrap-icons.min.css">
<link rel="stylesheet" href="https://unpkg.com/[email protected]/css/flag-icons.min.css">
<link rel="stylesheet" type="text/css" href="/static/semantic-ui-alerts.min.css">
<link rel="stylesheet" type="text/css" href="/static/theme-default/css/main.css?v20240616">
<script src="https://unpkg.com/[email protected]/dist/jquery.min.js"></script>
<script src="https://unpkg.com/[email protected]/dist/semantic.min.js"></script>
<script src="https://unpkg.com/[email protected]/dist/vue.min.js"></script>
<script src="https://unpkg.com/[email protected]/dist/echarts.min.js"></script>
<script src="/static/semantic-ui-alerts.min.js"></script> <script src="/static/semantic-ui-alerts.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/echarts.min.js"></script>
<script src="/static/theme-default/js/mixin.js?v20240302"></script> <script src="/static/theme-default/js/mixin.js?v20240302"></script>
</head> </head>
<body> <body>
{{end}} {{end}}
File diff suppressed because it is too large Load Diff
+3
View File
@@ -27,6 +27,9 @@
<i :class="item.icon + ' icon'"></i>@#item.name#@ <i :class="item.icon + ' icon'"></i>@#item.name#@
<i class="check icon" v-if="preferredTemplate === item.key"></i> <i class="check icon" v-if="preferredTemplate === item.key"></i>
</a> </a>
<a v-if="defaultTemplate === 'custom'" @click="toggleTemplate('custom')" class="item">
<i class="expand icon"></i>Custom
</a>
</div> </div>
</div> </div>
{{ end }} {{ end }}
+1 -1
View File
@@ -221,7 +221,7 @@
var disk = this.formatByteSize(server.State.DiskUsed) + '/' + this.formatByteSize(server.Host.DiskTotal); var disk = this.formatByteSize(server.State.DiskUsed) + '/' + this.formatByteSize(server.Host.DiskTotal);
var upTime = this.secondToDate(server.State.Uptime); var upTime = this.secondToDate(server.State.Uptime);
var tooltip = var tooltip =
`{content: 'System: ${server.Host.Platform}-${server.Host.PlatformVersion}[${server.Host.Arch}]<br>CPU: ${server.Host.CPU}<br>Disk: ${disk}<br>Online: ${upTime}<br>Version: ${server.Host.Version}'}`; `{content: 'System: ${server.Host.Platform}-${server.Host.PlatformVersion}[${server.Host.Arch}]<br>CPU: ${server.Host.CPU}<br>GPU: ${server.Host.GPU}<br>Disk: ${disk}<br>Online: ${upTime}<br>Version: ${server.Host.Version}'}`;
return tooltip return tooltip
} }
} }
@@ -39,6 +39,11 @@
<i class="check icon" v-if="preferredTemplate === item.key"></i> <i class="check icon" v-if="preferredTemplate === item.key"></i>
</a> </a>
</li> </li>
<li v-if="defaultTemplate === 'custom'">
<a @click="toggleTemplate('custom')" class="item">
<i class="expand icon" style="font-size:1em"></i>Custom
</a>
</li>
</ul> </ul>
</li> </li>
{{ end }} {{ end }}
+21 -14
View File
@@ -6,16 +6,22 @@
<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">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"> <link rel="shortcut icon" type="image/png" href="/static/logo.svg" />
<link rel="stylesheet" href="/static/theme-server-status/css/bootstrap-theme.min.css"> <link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="/static/theme-server-status/css/main.css?v20240407"> <link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/css/bootstrap-theme.min.css">
<link rel="stylesheet" href="/static/theme-server-status/css/main.css?v20240616">
<link rel="stylesheet" href="/static/theme-server-status/css/dark.css?v20240407"> <link rel="stylesheet" href="/static/theme-server-status/css/dark.css?v20240407">
<link rel="stylesheet" href="/static/theme-server-status/css/light.css?v20240407"> <link rel="stylesheet" href="/static/theme-server-status/css/light.css?v20240407">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected].2/font/bootstrap-icons.min.css"> <link rel="stylesheet" href="https://unpkg.com/[email protected].3/font/bootstrap-icons.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/assets/font-logos.css"> <link rel="stylesheet" href="https://unpkg.com/[email protected].0/assets/font-logos.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/lipis/flag-icons@7.0.0/css/flag-icons.min.css"> <link rel="stylesheet" href="https://unpkg.com/flag-icons@7.2.3/css/flag-icons.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected].1/dist/semantic.min.css"> <link rel="stylesheet" href="https://unpkg.com/[email protected].0/dist/semantic.min.css">
<link rel="shortcut icon" type="image/png" href="/static/logo.svg" /> <script src="https://unpkg.com/[email protected]/dist/jquery.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/echarts.min.js"></script>
<script src="https://unpkg.com/[email protected]/map/js/world.js"></script>
<script src="/static/theme-server-status/js/mixin.js?v20240630"></script>
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries --> <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]> <!--[if lt IE 9]>
<script src="/static/theme-server-status/js/html5shiv.js"></script> <script src="/static/theme-server-status/js/html5shiv.js"></script>
@@ -24,12 +30,13 @@
{{if ts .CustomCode}} {{if ts .CustomCode}}
{{.CustomCode|safe}} {{.CustomCode|safe}}
{{end}} {{end}}
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/echarts.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/echarts/map/js/world.js" integrity="md5-ZmUzMzZlOGQyYzk2NTgwNGRlMzRlNTliZDgyYTY1OGUgIC0=" crossorigin="anonymous"></script>
<script src="/static/theme-server-status/js/mixin.js?v20240302"></script>
</head> </head>
<body> <body>
{{end}} {{end}}
+29 -8
View File
@@ -27,10 +27,10 @@
</td> </td>
<td class="node-cell name center">@#node.name#@</td> <td class="node-cell name center">@#node.name#@</td>
<td class="node-cell os center"> <td class="node-cell os center">
<i v-if='node.os == "darwin"' class="apple icon"></i> <i v-if='isWindowsPlatform(node.host.Platform)' class="windows icon"></i>
<i v-else-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 :class="'fl-' + getFontLogoClass(node.host.Platform)"></i> <i v-else :class="'fl-' + getFontLogoClass(node.host.Platform)"></i>
<span class="node-cell-os-text">@#node.os#@</span> <span class="node-cell-os-text">@#getPlatformName(node.os)#@</span>
</td> </td>
<td style="text-align: center;" class="node-cell location"> <td style="text-align: center;" class="node-cell location">
<i :class="'fi fi-' + node.location"></i> <i :class="'fi fi-' + node.location"></i>
@@ -71,20 +71,28 @@
</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>
@#node.host.CPU.join(",")#@ @#node.host.CPU.join(",")#@ (@#node.cpu.percent#@%)
</span>
<span class="node-cell-expand" v-if="node.host.GPU">
<span class="node-cell-expand-label">GPU:</span>
@#node.host.GPU.join(",")#@
(@#parseInt(node.state.GPU >=0 ? node.state.GPU : 0)#@%)
</span> </span>
<span class="node-cell-expand"> <span class="node-cell-expand">
<span class="node-cell-expand-label">{{tr "DiskUsed"}}:</span> <span class="node-cell-expand-label">{{tr "DiskUsed"}}:</span>
@#formatByteSize(node.state.DiskUsed)#@ / @#formatByteSize(node.host.DiskTotal)#@ @#formatByteSize(node.state.DiskUsed)#@ / @#formatByteSize(node.host.DiskTotal)#@ (@#node.hdd.percent#@%)
</span> </span>
<span class="node-cell-expand"> <span class="node-cell-expand">
<span class="node-cell-expand-label">{{tr "MemUsed"}}:</span> <span class="node-cell-expand-label">{{tr "MemUsed"}}:</span>
@#formatByteSize(node.state.MemUsed)#@ / @#formatByteSize(node.host.MemTotal)#@(@#toFixed2(node.state.MemUsed / node.host.MemTotal * 100)#@%) @#formatByteSize(node.state.MemUsed)#@ / @#formatByteSize(node.host.MemTotal)#@ (@#node.memory.percent#@%)
</span> </span>
<span class="node-cell-expand"> <span v-if="node.host.SwapTotal > 0" class="node-cell-expand">
<span class="node-cell-expand-label">{{tr "SwapUsed"}}:</span> <span class="node-cell-expand-label">{{tr "SwapUsed"}}:</span>
@#formatByteSize(node.state.SwapUsed)#@ / @#formatByteSize(node.host.SwapTotal)#@ @#formatByteSize(node.state.SwapUsed)#@ / @#formatByteSize(node.host.SwapTotal)#@
<span v-if="node.host.SwapTotal">(@#toFixed2(node.state.SwapUsed / node.host.SwapTotal * 100)#@%)</span> <span>(@#toFixed2(node.state.SwapUsed / node.host.SwapTotal * 100)#@%)</span>
</span>
<span v-else class="node-cell-expand">
<span class="node-cell-expand-label">{{tr "SwapUsed"}}: OFF</span>
</span> </span>
<span class="node-cell-expand"> <span class="node-cell-expand">
<span class="node-cell-expand-label">{{tr "NetTransfer"}}:</span> <span class="node-cell-expand-label">{{tr "NetTransfer"}}:</span>
@@ -117,6 +125,19 @@
<span class="node-cell-expand-label">{{tr "Uptime"}}:</span> <span class="node-cell-expand-label">{{tr "Uptime"}}:</span>
@#node.uptime#@ @#node.uptime#@
</span> </span>
<span v-if="node.state.Temperatures && getTemperature(node.state.Temperatures, sensorList) > 0" @click="toggleDetailedTemp(node.ID)" class="node-cell-expand temp-detail" :id="`temp-${node.ID}`" temp-detail-show="0">
<span class="node-cell-expand-label">{{tr "Temperature"}}:</span>
<span :id="`temp-main-${node.ID}`" class="node-cell-expand-label">
@#getTemperature(node.state.Temperatures, sensorList)#@°C
<i class="bi bi-chevron-double-down"></i>
</span>
<span :id="`temp-detail-${node.ID}`" class="node-cell-expand-label" style="display:none">
<span v-for="temp in node.state.Temperatures" :key="temp.Name" v-if="temp.Temperature !== 0">
[ @#temp.Name#@ : @#temp.Temperature#@°C ]
</span>
<i class="bi bi-chevron-double-up"></i>
</span>
</span>
<span class="node-cell-expand"> <span class="node-cell-expand">
<span class="node-cell-expand-label">{{tr "Version"}}:</span> <span class="node-cell-expand-label">{{tr "Version"}}:</span>
@#node.host.Version#@ @#node.host.Version#@
+29 -8
View File
@@ -30,10 +30,10 @@
</td> </td>
<td class="node-cell name center">@#node.name#@</td> <td class="node-cell name center">@#node.name#@</td>
<td class="node-cell os center"> <td class="node-cell os center">
<i v-if='node.os == "darwin"' class="apple icon"></i> <i v-if='isWindowsPlatform(node.host.Platform)' class="windows icon"></i>
<i v-else-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 :class="'fl-' + getFontLogoClass(node.host.Platform)"></i> <i v-else :class="'fl-' + getFontLogoClass(node.host.Platform)"></i>
<span class="node-cell-os-text">@#node.os#@</span> <span class="node-cell-os-text">@#getPlatformName(node.os)#@</span>
</td> </td>
<td style="text-align: center;" class="node-cell location"> <td style="text-align: center;" class="node-cell location">
<i :class="'fi fi-' + node.location"></i> <i :class="'fi fi-' + node.location"></i>
@@ -74,20 +74,28 @@
</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>
@#node.host.CPU.join(",")#@ @#node.host.CPU.join(",")#@ (@#node.cpu.percent#@%)
</span>
<span class="node-cell-expand" v-if="node.host.GPU">
<span class="node-cell-expand-label">GPU:</span>
@#node.host.GPU.join(",")#@
(@#parseInt(node.state.GPU >=0 ? node.state.GPU : 0)#@%)
</span> </span>
<span class="node-cell-expand"> <span class="node-cell-expand">
<span class="node-cell-expand-label">{{tr "DiskUsed"}}:</span> <span class="node-cell-expand-label">{{tr "DiskUsed"}}:</span>
@#formatByteSize(node.state.DiskUsed)#@ / @#formatByteSize(node.host.DiskTotal)#@ @#formatByteSize(node.state.DiskUsed)#@ / @#formatByteSize(node.host.DiskTotal)#@ (@#node.hdd.percent#@%)
</span> </span>
<span class="node-cell-expand"> <span class="node-cell-expand">
<span class="node-cell-expand-label">{{tr "MemUsed"}}:</span> <span class="node-cell-expand-label">{{tr "MemUsed"}}:</span>
@#formatByteSize(node.state.MemUsed)#@ / @#formatByteSize(node.host.MemTotal)#@(@#toFixed2(node.state.MemUsed / node.host.MemTotal * 100)#@%) @#formatByteSize(node.state.MemUsed)#@ / @#formatByteSize(node.host.MemTotal)#@ (@#node.memory.percent#@%)
</span> </span>
<span class="node-cell-expand"> <span v-if="node.host.SwapTotal > 0" class="node-cell-expand">
<span class="node-cell-expand-label">{{tr "SwapUsed"}}:</span> <span class="node-cell-expand-label">{{tr "SwapUsed"}}:</span>
@#formatByteSize(node.state.SwapUsed)#@ / @#formatByteSize(node.host.SwapTotal)#@ @#formatByteSize(node.state.SwapUsed)#@ / @#formatByteSize(node.host.SwapTotal)#@
<span v-if="node.host.SwapTotal">(@#toFixed2(node.state.SwapUsed / node.host.SwapTotal * 100)#@%)</span> <span>(@#toFixed2(node.state.SwapUsed / node.host.SwapTotal * 100)#@%)</span>
</span>
<span v-else class="node-cell-expand">
<span class="node-cell-expand-label">{{tr "SwapUsed"}}: OFF</span>
</span> </span>
<span class="node-cell-expand"> <span class="node-cell-expand">
<span class="node-cell-expand-label">{{tr "NetTransfer"}}:</span> <span class="node-cell-expand-label">{{tr "NetTransfer"}}:</span>
@@ -120,6 +128,19 @@
<span class="node-cell-expand-label">{{tr "Uptime"}}:</span> <span class="node-cell-expand-label">{{tr "Uptime"}}:</span>
@#node.uptime#@ @#node.uptime#@
</span> </span>
<span v-if="node.state.Temperatures && getTemperature(node.state.Temperatures, sensorList) > 0" @click="toggleDetailedTemp(node.ID)" class="node-cell-expand temp-detail" :id="`temp-${node.ID}`" temp-detail-show="0">
<span class="node-cell-expand-label">{{tr "Temperature"}}:</span>
<span :id="`temp-main-${node.ID}`" class="node-cell-expand-label">
@#getTemperature(node.state.Temperatures, sensorList)#@°C
<i class="bi bi-chevron-double-down"></i>
</span>
<span :id="`temp-detail-${node.ID}`" class="node-cell-expand-label" style="display:none">
<span v-for="temp in node.state.Temperatures" :key="temp.Name" v-if="temp.Temperature !== 0">
[ @#temp.Name#@ : @#temp.Temperature#@°C ]
</span>
<i class="bi bi-chevron-double-up"></i>
</span>
</span>
<span class="node-cell-expand"> <span class="node-cell-expand">
<span class="node-cell-expand-label">{{tr "Version"}}:</span> <span class="node-cell-expand-label">{{tr "Version"}}:</span>
@#node.host.Version#@ @#node.host.Version#@
File diff suppressed because one or more lines are too long
+118 -99
View File
@@ -4,74 +4,74 @@
{{template "theme-server-status/content-nav" .}} {{template "theme-server-status/content-nav" .}}
<!-- showGroup true --> <!-- showGroup true -->
<template v-if="showGroup"> <template v-if="showGroup">
<section class="container content" style="max-width: 95vw; min-height: .01%;overflow-x: auto;" v-for="group in servicesTag"> <section v-if="servicesTag.length === 0" class="container content" style="max-width: 95vw; min-height: .01%;overflow-x: auto;">
<p>No Valid Service Monitor Configuration Entries Found. Please Verify in the <a href="/monitor">Admin Panel</a>.</p>
</section>
<section v-else class="container content" style="max-width: 95vw; min-height: .01%;overflow-x: auto;" v-for="group in servicesTag">
{{template "theme-server-status/service-group-true" .}} {{template "theme-server-status/service-group-true" .}}
</section> </section>
</template> </template>
<!-- showGroup false --> <!-- showGroup false -->
<template v-else> <template v-else>
<section class="container content" style="max-width: 95vw; min-height: .01%;overflow-x: auto;"> <section v-if="servicesNoTag.length === 0" class="container content" style="max-width: 95vw; min-height: .01%;overflow-x: auto;">
<p>No Valid Service Monitor Configuration Entries Found. Please Verify in the <a href="/monitor">Admin Panel</a>.</p>
</section>
<section v-else class="container content" style="max-width: 95vw; min-height: .01%;overflow-x: auto;">
{{template "theme-server-status/service-group-false" .}} {{template "theme-server-status/service-group-false" .}}
</section> </section>
</template> </template>
<section class="container content table-responsive" style="max-width: 95vw"> {{if .CycleTransferStats}}
{{if .CycleTransferStats}} <section class="container content table-responsive" style="max-width: 95vw">
<table class="table table-striped table-condensed table-hover"> <table class="table table-striped table-condensed table-hover">
<thead> <thead>
<tr class="node-group-tag"> <tr class="node-group-tag">
<th colspan="16" style="border:none;"> <th colspan="16" style="border:none;">
{{tr "CycleTransferStats"}} {{tr "CycleTransferStats"}}
</th> </th>
</tr> </tr>
<tr class="node-group-cell"> <tr class="node-group-cell">
<th class="node-cell center">ID</th> <th class="node-cell center">ID</th>
<th class="node-cell center">{{tr "Rules"}}</th> <th class="node-cell center">{{tr "Rules"}}</th>
<th class="node-cell center">{{tr "Server"}}</th> <th class="node-cell center">{{tr "Server"}}</th>
<th class="node-cell center">{{tr "From"}}</th> <th class="node-cell center">{{tr "From"}}</th>
<th class="node-cell center">{{tr "To"}}</th> <th class="node-cell center">{{tr "To"}}</th>
<th class="node-cell center">MAX</th> <th class="node-cell center">MAX</th>
<th class="node-cell center">MIN</th> <th class="node-cell center">MIN</th>
<th class="node-cell center">{{tr "NextCheck"}}</th> <th class="node-cell center">{{tr "NextCheck"}}</th>
<th class="node-cell center">{{tr "CurrentUsage"}}</th> <th class="node-cell center">{{tr "CurrentUsage"}}</th>
<th class="node-cell center">{{tr "Transleft"}}</th> <th class="node-cell center">{{tr "Transleft"}}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{{range $id, $stats := .CycleTransferStats}} {{range $id, $stats := .CycleTransferStats}}
{{range $innerId, $transfer := $stats.Transfer}} {{range $innerId, $transfer := $stats.Transfer}}
{{$TransLeftPercent := TransLeftPercent (UintToFloat $transfer) (UintToFloat $stats.Max)}} {{$TransLeftPercent := TransLeftPercent (UintToFloat $transfer) (UintToFloat $stats.Max)}}
<tr> <tr>
<td class="node-cell center">{{$id}}</td> <td class="node-cell center">{{$id}}</td>
<td class="node-cell center">{{$stats.Name}}</td> <td class="node-cell center">{{$stats.Name}}</td>
<td class="node-cell center">{{index $stats.ServerName $innerId}}</td> <td class="node-cell center">{{index $stats.ServerName $innerId}}</td>
<td class="node-cell center">{{$stats.From|tf}}</td> <td class="node-cell center">{{$stats.From|tf}}</td>
<td class="node-cell center">{{$stats.To|tf}}</td> <td class="node-cell center">{{$stats.To|tf}}</td>
<td class="node-cell center">{{$stats.Max|bf}}</td> <td class="node-cell center">{{$stats.Max|bf}}</td>
<td class="node-cell center">{{$stats.Min|bf}}</td> <td class="node-cell center">{{$stats.Min|bf}}</td>
<td class="node-cell center">{{(index $stats.NextUpdate $innerId)|sft}}</td> <td class="node-cell center">{{(index $stats.NextUpdate $innerId)|sft}}</td>
<td class="node-cell center">{{$transfer|bf}}</td> <td class="node-cell center">{{$transfer|bf}}</td>
<td class="node-cell center"> <td class="node-cell center">
<div class="progress"> <div class="progress">
<div style="width: {{$TransLeftPercent}}%" :class="'progress-bar progress-bar-' + toSSBar('{{TransClassName $TransLeftPercent}}')"> <div style="width: {{$TransLeftPercent}}%" :class="'progress-bar progress-bar-' + toSSBar('{{TransClassName $TransLeftPercent}}')">
<small style="display: inline-block;width: max-content;">{{TransLeft $stats.Max $transfer}} / {{$TransLeftPercent}} %</small> <small style="display: inline-block;width: max-content;">{{TransLeft $stats.Max $transfer}} / {{$TransLeftPercent}} %</small>
</div> </div>
</div> </div>
</td> </td>
</tr> </tr>
{{end}} {{end}}
{{end}} {{end}}
</tbody> </tbody>
</table> </table>
{{end}} </section>
</section> {{end}}
{{template "theme-server-status/content-footer" .}} {{template "theme-server-status/content-footer" .}}
</div> </div>
<script>
// 初始化 Tooltip
$(document).ready(function(){
$('[data-toggle="tooltip"]').tooltip();
});
</script>
<script> <script>
new Vue({ new Vue({
el: '#app', el: '#app',
@@ -80,13 +80,16 @@
page: 'service', page: 'service',
defaultTemplate: {{.Conf.Site.Theme}}, defaultTemplate: {{.Conf.Site.Theme}},
templates: {{.Themes}}, templates: {{.Themes}},
services: {{.Services}},
servicesTag: [], servicesTag: [],
servicesNoTag: [], servicesNoTag: []
}, },
created() { created() {
this.initData(); this.servicesTag = this.groupingData(this.initData(this.services),"type");
this.servicesNoTag = this.initData(this.services);
}, },
mounted() { mounted() {
this.initTooltip();
}, },
mixins: [mixinsVue], mixins: [mixinsVue],
methods: { methods: {
@@ -103,32 +106,36 @@
} }
return n return n
}, },
initData() { initData(services) {
// @formatter:off let nodes = [];
const services = [] for (let key in services) {
{{range $service := .Services}} const service = services[key];
services.push({ let node = {
type: '{{$service.Monitor.Type}}', type: service.Monitor.Type,
name: '{{$service.Monitor.Name}}', name: service.Monitor.Name,
currentUp: parseInt('{{$service.CurrentUp}}'), currentUp: parseInt(service.CurrentUp),
currentDown: parseInt('{{$service.CurrentDown}}'), currentDown: parseInt(service.CurrentDown),
totalUp: parseInt('{{$service.TotalUp}}'), totalUp: parseInt(service.TotalUp),
totalDown: parseInt('{{$service.TotalDown}}'), totalDown: parseInt(service.TotalDown),
delay: '{{$service.Delay}}'.replaceAll("[","").replaceAll("]","").split(" "), up: service.Up,
up: '{{$service.Up}}'.replaceAll("[","").replaceAll("]","").split(" "), down: service.Down,
down: '{{$service.Down}}'.replaceAll("[","").replaceAll("]","").split(" "), delay: service.Delay,
}) avgDelay: parseInt(this.getAvgDelay(service.Delay)) + "ms",
{{end}} health: this.getStateInfo(this.getPercent(service.CurrentUp, service.CurrentDown)),
// @formatter:on totalUpTime: this.getProgressInfo(this.getPercent(service.TotalUp, service.TotalDown))
for (let i = 0; i < services.length; i++) { };
const service = services[i]; nodes.push(node);
service.avgDelay = parseInt(service.delay[service.delay.length - 1]) + "ms" };
service.health = this.getStateInfo(this.getPercent(service.currentUp, service.currentDown)) for (let i = 0; i < nodes.length; i++) {
service.dayDetail = this.getDayTails(service) const node = nodes[i];
service.totalUpTime = this.getProgressInfo(this.getPercent(service.totalUp, service.totalDown)) node.dayDetail = this.getDayTails(node)
} }
this.servicesTag = this.groupingData(services,"type"); return nodes
this.servicesNoTag = services; },
initTooltip() {
$(document).ready(function(){
$('[data-toggle="tooltip"]').tooltip();
});
}, },
getPercent(up, down) { getPercent(up, down) {
if (!up) { if (!up) {
@@ -151,24 +158,24 @@
return this.toFixed2(currentUp / total * 100) return this.toFixed2(currentUp / total * 100)
}, },
getDayTails(service) { getDayTails(service) {
const result = [] const result = [];
for (let i = 0; i < service.up.length; i++) { for (let i = 0; i < service.up.length; i++) {
const up = service.up[i] const up = service.up[i];
const down = service.down[i] const down = service.down[i];
const delay = service.delay[i] let delay = Number(service.delay[i]);
let percent = this.getPercent(up, down) let percent = this.getPercent(up, down);
if (percent <= 0) { if (percent <= 0) {
percent = 0; percent = 0;
} }
let className = this.getStateInfo(percent).className let className = this.getStateInfo(percent).className;
let available = '{{tr "Availability"}}' let available = '{{tr "Availability"}}';
let averageLatency = '{{tr "AverageLatency"}}' let averageLatency = '{{tr "AverageLatency"}}';
const text = `${this.beforeDay(service.up.length - i - 1)}${available}${percent}%${averageLatency}${delay}ms` const text = `${this.beforeDay(service.up.length - i - 1)}${available}${Number(percent).toFixed(3)}%${averageLatency}${Number(delay).toFixed(3)}ms`;
result.push({ result.push({
text, className text, className
}) });
} }
return result return result;
}, },
beforeDay(days) { beforeDay(days) {
const today = new Date(); const today = new Date();
@@ -203,12 +210,13 @@
return result; return result;
}, },
getProgressInfo(percent) { getProgressInfo(percent) {
const result = this.getStateInfo(percent) const result = this.getStateInfo(percent);
result.style = `width: ${parseInt(percent)}%`; result.style = `width: ${parseInt(percent)}%`;
result.percent = Number(percent).toFixed(2);
const className = result.className; const className = result.className;
if (className === "good") { if (className === "good") {
result.className = 'progress-bar progress-bar-success' result.className = 'progress-bar progress-bar-success'
} else if (className === "waining") { } else if (className === "warning") {
result.className = 'progress-bar progress-bar-warning' result.className = 'progress-bar progress-bar-warning'
} else if (className === "danger") { } else if (className === "danger") {
result.className = 'progress-bar progress-bar-danger' result.className = 'progress-bar progress-bar-danger'
@@ -218,8 +226,19 @@
} }
return result return result
}, },
getAvgDelay(array) {
const { sum, count } = array.reduce((acc, value) => {
if (value !== 0) {
acc.sum += value;
acc.count += 1;
}
return acc;
}, { sum: 0, count: 0 });
return count > 0 ? sum / count : 0;
}
} }
}) })
</script> </script>
{{template "theme-server-status/footer" .}} {{template "theme-server-status/footer" .}}
{{end}} {{end}}
+10 -1
View File
@@ -14,7 +14,7 @@ site:
theme: "default" theme: "default"
ddns: ddns:
enable: false enable: false
provider: "webhook" provider: "webhook" # 如需使用多配置功能,请把此项留空
accessid: "" accessid: ""
accesssecret: "" accesssecret: ""
webhookmethod: "" webhookmethod: ""
@@ -22,3 +22,12 @@ ddns:
webhookrequestbody: "" webhookrequestbody: ""
webhookheaders: "" webhookheaders: ""
maxretries: 3 maxretries: 3
profiles:
example:
provider: ""
accessid: ""
accesssecret: ""
webhookmethod: ""
webhookurl: ""
webhookrequestbody: ""
webhookheaders: ""
+291
View File
@@ -0,0 +1,291 @@
#!/bin/bash
#========================================================
# System Required: macOS 10.13+
# Description: Nezha Agent Install Script (macOS)
# Github: https://github.com/naiba/nezha
#========================================================
NZ_BASE_PATH="/opt/nezha"
NZ_AGENT_PATH="${NZ_BASE_PATH}/agent"
red='\033[0;31m'
green='\033[0;32m'
yellow='\033[0;33m'
plain='\033[0m'
export PATH=$PATH:/usr/local/bin
pre_check() {
# check root
[[ $EUID -ne 0 ]] && echo -e "${red}ERROR: ${plain} This script must be run with the root user!\n" && exit 1
## os_arch
if [[ $(uname -m | grep 'x86_64') != "" ]]; then
os_arch="amd64"
elif [[ $(uname -m | grep 'arm64\|arm64e') != "" ]]; then
os_arch="arm64"
fi
## China_IP
if [[ -z "${CN}" ]]; then
if [[ $(curl -m 10 -s https://ipapi.co/json | grep 'China') != "" ]]; then
echo "According to the information provided by ipapi.co, the current IP may be in China"
read -e -r -p "Is the installation done with a Chinese Mirror? [Y/n] (Custom Mirror Input 3):" input
case $input in
[yY][eE][sS] | [yY])
echo "Use Chinese Mirror"
CN=true
;;
[nN][oO] | [nN])
echo "No Use Chinese Mirror"
;;
[3])
echo "Use Custom Mirror"
read -e -r -p "Please enter a custom image (e.g. :dn-dao-github-mirror.daocloud.io), leave blank to nouse: " input
case $input in
*)
CUSTOM_MIRROR=$input
;;
esac
;;
*)
echo "No Use Chinese Mirror"
;;
esac
fi
fi
if [[ -n "${CUSTOM_MIRROR}" ]]; then
GITHUB_RAW_URL="gitee.com/naibahq/nezha/raw/master"
GITHUB_URL=$CUSTOM_MIRROR
else
if [[ -z "${CN}" ]]; then
GITHUB_RAW_URL="raw.githubusercontent.com/naiba/nezha/master"
GITHUB_URL="github.com"
Get_Docker_URL="get.docker.com"
Get_Docker_Argu=" "
Docker_IMG="ghcr.io\/naiba\/nezha-dashboard"
else
GITHUB_RAW_URL="gitee.com/naibahq/nezha/raw/master"
GITHUB_URL="github.com"
Get_Docker_URL="get.docker.com"
Get_Docker_Argu=" -s docker --mirror Aliyun"
Docker_IMG="registry.cn-shanghai.aliyuncs.com\/naibahq\/nezha-dashboard"
fi
fi
}
before_show_menu() {
echo && echo -n -e "${yellow}* Press Enter to return to the main menu *${plain}" && read temp
show_menu
}
install_agent() {
echo -e "> Install Nezha Agent"
echo -e "Obtaining Agent version"
local version=$(curl -m 10 -sL "https://api.github.com/repos/nezhahq/agent/releases/latest" | grep "tag_name" | head -n 1 | awk -F ":" '{print $2}' | sed 's/\"//g;s/,//g;s/ //g')
if [ ! -n "$version" ]; then
version=$(curl -m 10 -sL "https://fastly.jsdelivr.net/gh/nezhahq/agent/" | grep "option\.value" | awk -F "'" '{print $2}' | sed 's/nezhahq\/agent@/v/g')
fi
if [ ! -n "$version" ]; then
version=$(curl -m 10 -sL "https://gcore.jsdelivr.net/gh/nezhahq/agent/" | grep "option\.value" | awk -F "'" '{print $2}' | sed 's/nezhahq\/agent@/v/g')
fi
if [ ! -n "$version" ]; then
echo -e "Fail to obtaine agent version, please check if the network can link https://api.github.com/repos/nezhahq/agent/releases/latest"
return 0
else
echo -e "The current latest version is: ${version}"
fi
# Nezha Agent Folder
mkdir -p $NZ_AGENT_PATH
chmod -R 777 $NZ_AGENT_PATH
echo -e "Downloading Agent"
curl -o nezha-agent_darwin_${os_arch}.zip -L -f --retry 2 --retry-max-time 60 https://${GITHUB_URL}/nezhahq/agent/releases/download/${version}/nezha-agent_darwin_${os_arch}.zip >/dev/null 2>&1
if [[ $? != 0 ]]; then
echo -e "${red}Fail to download agent, please check if the network can link ${GITHUB_URL}${plain}"
return 0
fi
unzip -qo nezha-agent_darwin_${os_arch}.zip &&
mv nezha-agent $NZ_AGENT_PATH &&
rm -rf nezha-agent_darwin_${os_arch}.zip README.md
if [ $# -ge 3 ]; then
modify_agent_config "$@"
else
modify_agent_config 0
fi
if [[ $# == 0 ]]; then
before_show_menu
fi
}
modify_agent_config() {
echo -e "> Modify Agent Configuration"
if [ $# -lt 3 ]; then
echo "Please add Agent in the admin panel first, record the secret" &&
read -ep "Please enter a domain that resolves to the IP where the panel is located (no CDN sets): " nz_grpc_host &&
read -ep "Please enter the panel RPC port (default 5555): " nz_grpc_port &&
read -ep "Please enter the Agent secret: " nz_client_secret &&
read -ep "Do you want to enable SSL/TLS encryption for the gRPC port (--tls)? Press [y] if yes, the default is not required, and users can press Enter to skip if you don't understand: " nz_grpc_proxy
grep -qiw 'Y' <<<"${nz_grpc_proxy}" && args='--tls'
if [[ -z "${nz_grpc_host}" || -z "${nz_client_secret}" ]]; then
echo -e "${red}All options cannot be empty${plain}"
before_show_menu
return 1
fi
if [[ -z "${nz_grpc_port}" ]]; then
nz_grpc_port=5555
fi
else
nz_grpc_host=$1
nz_grpc_port=$2
nz_client_secret=$3
shift 3
if [ $# -gt 0 ]; then
args=" $*"
fi
fi
${NZ_AGENT_PATH}/nezha-agent service install -s "$nz_grpc_host:$nz_grpc_port" -p $nz_client_secret $args >/dev/null 2>&1
if [ $? -ne 0 ]; then
${NZ_AGENT_PATH}/nezha-agent service uninstall >/dev/null 2>&1
${NZ_AGENT_PATH}/nezha-agent service install -s "$nz_grpc_host:$nz_grpc_port" -p $nz_client_secret $args >/dev/null 2>&1
fi
echo -e "Agent configuration ${green} modified successfully, please wait for agent self-restart to take effect${plain}"
#if [[ $# == 0 ]]; then
# before_show_menu
#fi
}
show_agent_log() {
echo -e "> > View Agent Log"
tail -n 10 /var/log/nezha-agent.err.log
if [[ $# == 0 ]]; then
before_show_menu
fi
}
uninstall_agent() {
echo -e "> Uninstall Agent"
${NZ_AGENT_PATH}/nezha-agent service uninstall
rm -rf $NZ_AGENT_PATH
clean_all
if [[ $# == 0 ]]; then
before_show_menu
fi
}
restart_agent() {
echo -e "> Restart Agent"
${NZ_AGENT_PATH}/nezha-agent service restart
if [[ $# == 0 ]]; then
before_show_menu
fi
}
clean_all() {
if [ -z "$(ls -A ${NZ_BASE_PATH})" ]; then
rm -rf ${NZ_BASE_PATH}
fi
}
show_usage() {
echo "Nezha Agent Management Script Usage: "
echo "--------------------------------------------------------"
echo "./nezha.sh install_agent - Install Agent"
echo "./nezha.sh modify_agent_config - Modify Agent Configuration"
echo "./nezha.sh show_agent_log - View Agent Log"
echo "./nezha.sh uninstall_agent - Uninstall Agent"
echo "./nezha.sh restart_agent - Restart Agent"
echo "./nezha.sh update_script - Update Script"
echo "--------------------------------------------------------"
}
show_menu() {
echo -e "
${green}Nezha Agent Management Script${plain} ${red}macOS${plain}
--- https://github.com/naiba/nezha ---
${green}1.${plain} Install Agent
${green}2.${plain} Modify Agent Configuration
${green}3.${plain} View Agent Log
${green}4.${plain} Uninstall Agent
${green}5.${plain} Restart Agent
————————————————-
${green}0.${plain} Exit Script
"
echo && read -ep "Please enter [0-5]: " num
case "${num}" in
0)
exit 0
;;
1)
install_agent
;;
2)
modify_agent_config
;;
3)
show_agent_log
;;
4)
uninstall_agent
;;
5)
restart_agent
;;
*)
echo -e "${red}Please enter the correct number [0-5]${plain}"
;;
esac
}
pre_check
if [[ $# > 0 ]]; then
case $1 in
"install_agent")
shift
if [ $# -ge 3 ]; then
install_agent "$@"
else
install_agent 0
fi
;;
"modify_agent_config")
modify_agent_config 0
;;
"show_agent_log")
show_agent_log 0
;;
"uninstall_agent")
uninstall_agent 0
;;
"restart_agent")
restart_agent 0
;;
*) show_usage ;;
esac
else
show_menu
fi
+30 -25
View File
@@ -7,64 +7,69 @@ if($PSVersionTable.PSVersion.Major -lt 5){
exit exit
} }
$agentrepo = "nezhahq/agent" $agentrepo = "nezhahq/agent"
$nssmrepo = "nezhahq/nssm-backup" # x86 or x64 or arm64
# x86 or x64
if ([System.Environment]::Is64BitOperatingSystem) { if ([System.Environment]::Is64BitOperatingSystem) {
$file = "nezha-agent_windows_amd64.zip" if ($env:PROCESSOR_ARCHITECTURE -eq "ARM64") {
$file = "nezha-agent_windows_arm64.zip"
} else {
$file = "nezha-agent_windows_amd64.zip"
}
} }
else { else {
$file = "nezha-agent_windows_386.zip" $file = "nezha-agent_windows_386.zip"
} }
$agentreleases = "https://api.github.com/repos/$agentrepo/releases" $agentreleases = "https://api.github.com/repos/$agentrepo/releases"
$nssmreleases = "https://api.github.com/repos/$nssmrepo/releases"
#重复运行自动更新 #重复运行自动更新
if (Test-Path "C:\nezha") { if (Test-Path "C:\nezha\nezha-agent.exe") {
Write-Host "Nezha monitoring already exists, delete and reinstall" -BackgroundColor DarkGreen -ForegroundColor White Write-Host "Nezha monitoring already exists, delete and reinstall" -BackgroundColor DarkGreen -ForegroundColor White
C:/nezha/nssm.exe stop nezha C:\nezha\nezha-agent.exe service uninstall
C:/nezha/nssm.exe remove nezha
Remove-Item "C:\nezha" -Recurse Remove-Item "C:\nezha" -Recurse
} }
#TLS/SSL #TLS/SSL
Write-Host "Determining latest nezha release" -BackgroundColor DarkGreen -ForegroundColor White Write-Host "Determining latest nezha release" -BackgroundColor DarkGreen -ForegroundColor White
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$agenttag = (Invoke-WebRequest -Uri $agentreleases -UseBasicParsing | ConvertFrom-Json)[0].tag_name $agenttag = (Invoke-WebRequest -Uri $agentreleases -UseBasicParsing | ConvertFrom-Json)[0].tag_name
$nssmtag = (Invoke-WebRequest -Uri $nssmreleases -UseBasicParsing | ConvertFrom-Json)[0].tag_name if ([string]::IsNullOrWhiteSpace($agenttag)) {
$optionUrl = "https://fastly.jsdelivr.net/gh/nezhahq/agent/"
Try {
$response = Invoke-WebRequest -Uri $optionUrl -UseBasicParsing -TimeoutSec 10
if ($response.StatusCode -eq 200) {
$versiontext = $response.Content | findstr /c:"option.value"
$version = [regex]::Match($versiontext, "@(\d+\.\d+\.\d+)").Groups[1].Value
$agenttag = "v" + $version
}
} Catch {
$optionUrl = "https://gcore.jsdelivr.net/gh/nezhahq/agent/"
$response = Invoke-WebRequest -Uri $optionUrl -UseBasicParsing -TimeoutSec 10
if ($response.StatusCode -eq 200) {
$versiontext = $response.Content | findstr /c:"option.value"
$version = [regex]::Match($versiontext, "@(\d+\.\d+\.\d+)").Groups[1].Value
$agenttag = "v" + $version
}
}
}
#Region判断 #Region判断
$ipapi= Invoke-RestMethod -Uri "https://api.myip.com/" -UserAgent "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.163 Safari/535.1" $ipapi= Invoke-RestMethod -Uri "https://api.myip.com/" -UserAgent "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.163 Safari/535.1"
$region=$ipapi.cc $region=$ipapi.cc
echo $ipapi echo $ipapi
if($region -ne "CN"){ if($region -ne "CN"){
$download = "https://github.com/$agentrepo/releases/download/$agenttag/$file" $download = "https://github.com/$agentrepo/releases/download/$agenttag/$file"
$nssmdownload="https://github.com/$nssmrepo/releases/download/$nssmtag/nssm.zip"
Write-Host "Location:$region,connect directly!" -BackgroundColor DarkRed -ForegroundColor Green Write-Host "Location:$region,connect directly!" -BackgroundColor DarkRed -ForegroundColor Green
}else{ }else{
$download = "https://dn-dao-github-mirror.daocloud.io/$agentrepo/releases/download/$agenttag/$file" $download = "https://github.com/$agentrepo/releases/download/$agenttag/$file"
$nssmdownload="https://dn-dao-github-mirror.daocloud.io/$nssmrepo/releases/download/$nssmtag/nssm.zip"
Write-Host "Location:CN,use mirror address" -BackgroundColor DarkRed -ForegroundColor Green Write-Host "Location:CN,use mirror address" -BackgroundColor DarkRed -ForegroundColor Green
} }
echo $download echo $download
echo $nssmdownload
Invoke-WebRequest $download -OutFile "C:\nezha.zip" Invoke-WebRequest $download -OutFile "C:\nezha.zip"
#使用nssm安装服务
Invoke-WebRequest $nssmdownload -OutFile "C:\nssm.zip"
#解压 #解压
Expand-Archive "C:\nezha.zip" -DestinationPath "C:\temp" -Force Expand-Archive "C:\nezha.zip" -DestinationPath "C:\temp" -Force
Expand-Archive "C:\nssm.zip" -DestinationPath "C:\temp" -Force
if (!(Test-Path "C:\nezha")) { New-Item -Path "C:\nezha" -type directory } if (!(Test-Path "C:\nezha")) { New-Item -Path "C:\nezha" -type directory }
#整理文件 #整理文件
Move-Item -Path "C:\temp\nezha-agent.exe" -Destination "C:\nezha\nezha-agent.exe" Move-Item -Path "C:\temp\nezha-agent.exe" -Destination "C:\nezha\nezha-agent.exe"
if ($file = "nezha-agent_windows_amd64.zip") {
Move-Item -Path "C:\temp\nssm-2.24\win64\nssm.exe" -Destination "C:\nezha\nssm.exe"
}
else {
Move-Item -Path "C:\temp\nssm-2.24\win32\nssm.exe" -Destination "C:\nezha\nssm.exe"
}
#清理垃圾 #清理垃圾
Remove-Item "C:\nezha.zip" Remove-Item "C:\nezha.zip"
Remove-Item "C:\nssm.zip"
Remove-Item "C:\temp" -Recurse Remove-Item "C:\temp" -Recurse
#安装部分 #安装部分
C:\nezha\nssm.exe install nezha C:\nezha\nezha-agent.exe -s $server -p $key $tls -d C:\nezha\nezha-agent.exe service install -s $server -p $key $tls
C:\nezha\nssm.exe start nezha
#enjoy #enjoy
Write-Host "Enjoy It!" -BackgroundColor DarkGreen -ForegroundColor Red Write-Host "Enjoy It!" -BackgroundColor DarkGreen -ForegroundColor Red
+254 -262
View File
@@ -1,4 +1,4 @@
#!/bin/bash #!/bin/sh
#======================================================== #========================================================
# System Required: CentOS 7+ / Debian 8+ / Ubuntu 16+ / Alpine 3+ / # System Required: CentOS 7+ / Debian 8+ / Ubuntu 16+ / Alpine 3+ /
@@ -10,11 +10,9 @@
NZ_BASE_PATH="/opt/nezha" NZ_BASE_PATH="/opt/nezha"
NZ_DASHBOARD_PATH="${NZ_BASE_PATH}/dashboard" NZ_DASHBOARD_PATH="${NZ_BASE_PATH}/dashboard"
NZ_AGENT_PATH="${NZ_BASE_PATH}/agent" NZ_AGENT_PATH="${NZ_BASE_PATH}/agent"
NZ_AGENT_SERVICE="/etc/systemd/system/nezha-agent.service"
NZ_AGENT_SERVICERC="/etc/init.d/nezha-agent"
NZ_DASHBOARD_SERVICE="/etc/systemd/system/nezha-dashboard.service" NZ_DASHBOARD_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.16.0" NZ_VERSION="v0.17.0"
red='\033[0;31m' red='\033[0;31m'
green='\033[0;32m' green='\033[0;32m'
@@ -23,34 +21,55 @@ plain='\033[0m'
export PATH=$PATH:/usr/local/bin export PATH=$PATH:/usr/local/bin
os_arch="" os_arch=""
[ -e /etc/os-release ] && cat /etc/os-release | grep -i "PRETTY_NAME" | grep -qi "alpine" && os_alpine='1' [ -e /etc/os-release ] && grep -i "PRETTY_NAME" /etc/os-release | grep -qi "alpine" && os_alpine='1'
sudo() {
myEUID=$(id -ru)
if [ "$myEUID" -ne 0 ]; then
if command -v sudo > /dev/null 2>&1; then
command sudo "$@"
else
err "错误: 您的系统未安装 sudo,因此无法进行该项操作。"
exit 1
fi
else
"$@"
fi
}
check_systemd() {
if [ "$os_alpine" != 1 ] && ! command -v systemctl >/dev/null 2>&1; then
echo "不支持此系统:未找到 systemctl 命令"
exit 1
fi
}
err() {
printf "${red}$*${plain}\n" >&2
}
pre_check() { pre_check() {
[ "$os_alpine" != 1 ] && ! command -v systemctl >/dev/null 2>&1 && echo "不支持此系统:未找到 systemctl 命令" && exit 1
# check root
[[ $EUID -ne 0 ]] && echo -e "${red}错误: ${plain} 必须使用root用户运行此脚本!\n" && exit 1
## os_arch ## os_arch
if [[ $(uname -m | grep 'x86_64') != "" ]]; then if uname -m | grep -q 'x86_64'; then
os_arch="amd64" os_arch="amd64"
elif [[ $(uname -m | grep 'i386\|i686') != "" ]]; then elif uname -m | grep -q 'i386\|i686'; then
os_arch="386" os_arch="386"
elif [[ $(uname -m | grep 'aarch64\|armv8b\|armv8l') != "" ]]; then elif uname -m | grep -q 'aarch64\|armv8b\|armv8l'; then
os_arch="arm64" os_arch="arm64"
elif [[ $(uname -m | grep 'arm') != "" ]]; then elif uname -m | grep -q 'arm'; then
os_arch="arm" os_arch="arm"
elif [[ $(uname -m | grep 's390x') != "" ]]; then elif uname -m | grep -q 's390x'; then
os_arch="s390x" os_arch="s390x"
elif [[ $(uname -m | grep 'riscv64') != "" ]]; then elif uname -m | grep -q 'riscv64'; then
os_arch="riscv64" os_arch="riscv64"
fi fi
## China_IP ## China_IP
if [[ -z "${CN}" ]]; then if [ -z "$CN" ]; then
if [[ $(curl -m 10 -s https://ipapi.co/json | grep 'China') != "" ]]; then if curl -m 10 -s https://ipapi.co/json | grep -q 'China'; then
echo "根据ipapi.co提供的信息,当前IP可能在中国" echo "根据ipapi.co提供的信息,当前IP可能在中国"
read -e -r -p "是否选用中国镜像完成安装? [Y/n] (自定义镜像输入 3):" input printf "是否选用中国镜像完成安装? [Y/n] (自定义镜像输入 3):"
read -r input
case $input in case $input in
[yY][eE][sS] | [yY]) [yY][eE][sS] | [yY])
echo "使用中国镜像" echo "使用中国镜像"
@@ -63,7 +82,8 @@ pre_check() {
[3]) [3])
echo "使用自定义镜像" echo "使用自定义镜像"
read -e -r -p "请输入自定义镜像 (例如:dn-dao-github-mirror.daocloud.io),留空为不使用: " input printf "请输入自定义镜像 (例如:dn-dao-github-mirror.daocloud.io),留空为不使用: "
read -r input
case $input in case $input in
*) *)
CUSTOM_MIRROR=$input CUSTOM_MIRROR=$input
@@ -79,14 +99,14 @@ pre_check() {
fi fi
fi fi
if [[ -n "${CUSTOM_MIRROR}" ]]; then if [ -n "$CUSTOM_MIRROR" ]; then
GITHUB_RAW_URL="gitee.com/naibahq/nezha/raw/master" GITHUB_RAW_URL="gitee.com/naibahq/nezha/raw/master"
GITHUB_URL=$CUSTOM_MIRROR GITHUB_URL=$CUSTOM_MIRROR
Get_Docker_URL="get.docker.com" Get_Docker_URL="get.docker.com"
Get_Docker_Argu=" -s docker --mirror Aliyun" Get_Docker_Argu=" -s docker --mirror Aliyun"
Docker_IMG="registry.cn-shanghai.aliyuncs.com\/naibahq\/nezha-dashboard" Docker_IMG="registry.cn-shanghai.aliyuncs.com\/naibahq\/nezha-dashboard"
else else
if [[ -z "${CN}" ]]; then if [ -z "$CN" ]; then
GITHUB_RAW_URL="raw.githubusercontent.com/naiba/nezha/master" GITHUB_RAW_URL="raw.githubusercontent.com/naiba/nezha/master"
GITHUB_URL="github.com" GITHUB_URL="github.com"
Get_Docker_URL="get.docker.com" Get_Docker_URL="get.docker.com"
@@ -94,7 +114,7 @@ pre_check() {
Docker_IMG="ghcr.io\/naiba\/nezha-dashboard" Docker_IMG="ghcr.io\/naiba\/nezha-dashboard"
else else
GITHUB_RAW_URL="gitee.com/naibahq/nezha/raw/master" GITHUB_RAW_URL="gitee.com/naibahq/nezha/raw/master"
GITHUB_URL="dn-dao-github-mirror.daocloud.io" GITHUB_URL="github.com"
Get_Docker_URL="get.docker.com" Get_Docker_URL="get.docker.com"
Get_Docker_Argu=" -s docker --mirror Aliyun" Get_Docker_Argu=" -s docker --mirror Aliyun"
Docker_IMG="registry.cn-shanghai.aliyuncs.com\/naibahq\/nezha-dashboard" Docker_IMG="registry.cn-shanghai.aliyuncs.com\/naibahq\/nezha-dashboard"
@@ -105,8 +125,8 @@ pre_check() {
installation_check() { installation_check() {
if docker compose version >/dev/null 2>&1; then if docker compose version >/dev/null 2>&1; then
DOCKER_COMPOSE_COMMAND="docker compose" DOCKER_COMPOSE_COMMAND="docker compose"
if $DOCKER_COMPOSE_COMMAND ls | grep -qw "$NZ_DASHBOARD_PATH/docker-compose.yaml" >/dev/null 2>&1; then if sudo $DOCKER_COMPOSE_COMMAND ls | grep -qw "$NZ_DASHBOARD_PATH/docker-compose.yaml" >/dev/null 2>&1; then
NEZHA_IMAGES=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep -w "nezha-dashboard") NEZHA_IMAGES=$(sudo docker images --format "{{.Repository}}:{{.Tag}}" | grep -w "nezha-dashboard")
if [ -n "$NEZHA_IMAGES" ]; then if [ -n "$NEZHA_IMAGES" ]; then
echo "存在带有 nezha-dashboard 仓库的 Docker 镜像:" echo "存在带有 nezha-dashboard 仓库的 Docker 镜像:"
echo "$NEZHA_IMAGES" echo "$NEZHA_IMAGES"
@@ -119,8 +139,8 @@ installation_check() {
fi fi
elif command -v docker-compose >/dev/null 2>&1; then elif command -v docker-compose >/dev/null 2>&1; then
DOCKER_COMPOSE_COMMAND="docker-compose" DOCKER_COMPOSE_COMMAND="docker-compose"
if $DOCKER_COMPOSE_COMMAND -f "$NZ_DASHBOARD_PATH/docker-compose.yaml" config >/dev/null 2>&1; then if sudo $DOCKER_COMPOSE_COMMAND -f "$NZ_DASHBOARD_PATH/docker-compose.yaml" config >/dev/null 2>&1; then
NEZHA_IMAGES=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep -w "nezha-dashboard") NEZHA_IMAGES=$(sudo docker images --format "{{.Repository}}:{{.Tag}}" | grep -w "nezha-dashboard")
if [ -n "$NEZHA_IMAGES" ]; then if [ -n "$NEZHA_IMAGES" ]; then
echo "存在带有 nezha-dashboard 仓库的 Docker 镜像:" echo "存在带有 nezha-dashboard 仓库的 Docker 镜像:"
echo "$NEZHA_IMAGES" echo "$NEZHA_IMAGES"
@@ -133,17 +153,18 @@ installation_check() {
fi fi
fi fi
if [[ -f $NZ_DASHBOARD_PATH/app ]]; then if [ -f "$NZ_DASHBOARD_PATH/app" ]; then
IS_DOCKER_NEZHA=0 IS_DOCKER_NEZHA=0
FRESH_INSTALL=0 FRESH_INSTALL=0
fi fi
} }
select_version() { select_version() {
if [[ -z $IS_DOCKER_NEZHA ]]; then if [ -z "$IS_DOCKER_NEZHA" ]; then
echo -e "${yellow}请自行选择您的安装方式(如果你是安装Agent,输入哪个都是一样的):\n1. Docker\n2. 独立安装${plain}" printf "${yellow}请自行选择您的安装方式(如果你是安装Agent,输入哪个都是一样的):\n1. Docker\n2. 独立安装${plain}\n"
while true; do while true; do
read -e -r -p "请输入选择 [1-2]" option printf "请输入选择 [1-2]"
read -r option
case "${option}" in case "${option}" in
1) 1)
IS_DOCKER_NEZHA=1 IS_DOCKER_NEZHA=1
@@ -154,7 +175,7 @@ select_version() {
break break
;; ;;
*) *)
echo "${red}请输入正确的数字 [1-2]${plain}" err "请输入正确的选择 [1-2]"
;; ;;
esac esac
done done
@@ -162,18 +183,18 @@ select_version() {
} }
update_script() { update_script() {
echo -e "> 更新脚本" echo "> 更新脚本"
curl -sL https://${GITHUB_RAW_URL}/script/install.sh -o /tmp/nezha.sh curl -sL https://${GITHUB_RAW_URL}/script/install.sh -o /tmp/nezha.sh
new_version=$(cat /tmp/nezha.sh | grep "NZ_VERSION" | head -n 1 | awk -F "=" '{print $2}' | sed 's/\"//g;s/,//g;s/ //g') new_version=$(grep "NZ_VERSION" /tmp/nezha.sh | head -n 1 | awk -F "=" '{print $2}' | sed 's/\"//g;s/,//g;s/ //g')
if [ ! -n "$new_version" ]; then if [ ! -n "$new_version" ]; then
echo -e "脚本获取失败,请检查本机能否链接 https://${GITHUB_RAW_URL}/script/install.sh" echo "脚本获取失败,请检查本机能否链接 https://${GITHUB_RAW_URL}/script/install.sh"
return 1 return 1
fi fi
echo -e "当前最新版本为: ${new_version}" echo "当前最新版本为: ${new_version}"
mv -f /tmp/nezha.sh ./nezha.sh && chmod a+x ./nezha.sh mv -f /tmp/nezha.sh ./nezha.sh && chmod a+x ./nezha.sh
echo -e "3s后执行新脚本" echo "3s后执行新脚本"
sleep 3s sleep 3s
clear clear
exec ./nezha.sh exec ./nezha.sh
@@ -181,23 +202,23 @@ update_script() {
} }
before_show_menu() { before_show_menu() {
echo && echo -n -e "${yellow}* 按回车返回主菜单 *${plain}" && read temp echo && printf "${yellow}* 按回车返回主菜单 *${plain}" && read temp
show_menu show_menu
} }
install_base() { install_base() {
(command -v git >/dev/null 2>&1 && command -v curl >/dev/null 2>&1 && command -v wget >/dev/null 2>&1 && command -v unzip >/dev/null 2>&1 && command -v getenforce >/dev/null 2>&1) || (command -v curl >/dev/null 2>&1 && command -v wget >/dev/null 2>&1 && command -v unzip >/dev/null 2>&1 && command -v getenforce >/dev/null 2>&1) ||
(install_soft curl wget git unzip) (install_soft curl wget unzip)
} }
install_arch() { install_arch() {
echo -e "${green}提示: ${plain} Arch安装libselinux需添加nezha-agent用户,安装完会自动删除,建议手动检查一次\n" printf "${green}提示: ${plain} Arch安装libselinux需添加nezha-agent用户,安装完会自动删除,建议手动检查一次\n"
read -e -r -p "是否安装libselinux? [Y/n] " input read -r -p "是否安装libselinux? [Y/n] " input
case $input in case $input in
[yY][eE][sS] | [yY]) [yY][eE][sS] | [yY])
useradd -m nezha-agent useradd -m nezha-agent
sed -i "$ a\nezha-agent ALL=(ALL ) NOPASSWD:ALL" /etc/sudoers sed -i "$ a\nezha-agent ALL=(ALL ) NOPASSWD:ALL" /etc/sudoers
sudo -iu nezha-agent bash -c 'gpg --keyserver keys.gnupg.net --recv-keys BE22091E3EF62275; sudo -iu nezha-agent bash -c 'gpg --keyserver keys.gnupg.net --recv-keys 4695881C254508D1;
cd /tmp; git clone https://aur.archlinux.org/libsepol.git; cd libsepol; makepkg -si --noconfirm --asdeps; cd ..; cd /tmp; git clone https://aur.archlinux.org/libsepol.git; cd libsepol; makepkg -si --noconfirm --asdeps; cd ..;
git clone https://aur.archlinux.org/libselinux.git; cd libselinux; makepkg -si --noconfirm; cd ..; git clone https://aur.archlinux.org/libselinux.git; cd libselinux; makepkg -si --noconfirm; cd ..;
rm -rf libsepol libselinux' rm -rf libsepol libselinux'
@@ -215,24 +236,26 @@ install_arch() {
} }
install_soft() { install_soft() {
(command -v yum >/dev/null 2>&1 && yum makecache && yum install $* selinux-policy -y) || (command -v yum >/dev/null 2>&1 && sudo yum makecache && sudo yum install $* selinux-policy -y) ||
(command -v apt >/dev/null 2>&1 && apt update && apt install $* selinux-utils -y) || (command -v apt >/dev/null 2>&1 && sudo apt update && sudo apt install $* selinux-utils -y) ||
(command -v pacman >/dev/null 2>&1 && pacman -Syu $* base-devel --noconfirm && install_arch) || (command -v pacman >/dev/null 2>&1 && sudo pacman -Syu $* base-devel --noconfirm && install_arch) ||
(command -v apt-get >/dev/null 2>&1 && apt-get update && apt-get install $* selinux-utils -y) || (command -v apt-get >/dev/null 2>&1 && sudo apt-get update && sudo apt-get install $* selinux-utils -y) ||
(command -v apk >/dev/null 2>&1 && apk update && apk add $* -f) (command -v apk >/dev/null 2>&1 && sudo apk update && sudo apk add $* -f)
} }
install_dashboard() { install_dashboard() {
check_systemd
install_base install_base
echo -e "> 安装面板" echo "> 安装面板"
# 哪吒监控文件夹 # 哪吒监控文件夹
if [[ ! $FRESH_INSTALL == 0 ]]; then if [ ! "$FRESH_INSTALL" = 0 ]; then
mkdir -p $NZ_DASHBOARD_PATH sudo mkdir -p $NZ_DASHBOARD_PATH
else else
echo "您可能已经安装过面板端,重复安装会覆盖数据,请注意备份。" echo "您可能已经安装过面板端,重复安装会覆盖数据,请注意备份。"
read -e -r -p "是否退出安装? [Y/n] " input printf "是否退出安装? [Y/n] "
read -r input
case $input in case $input in
[yY][eE][sS] | [yY]) [yY][eE][sS] | [yY])
echo "退出安装" echo "退出安装"
@@ -248,40 +271,40 @@ install_dashboard() {
esac esac
fi fi
chmod 777 -R $NZ_DASHBOARD_PATH 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
install_dashboard_standalone install_dashboard_standalone
fi fi
modify_dashboard_config 0 modify_dashboard_config 0
if [[ $# == 0 ]]; then if [ $# = 0 ]; then
before_show_menu before_show_menu
fi fi
} }
install_dashboard_docker() { install_dashboard_docker() {
if [[ ! $FRESH_INSTALL == 0 ]]; then if [ ! "$FRESH_INSTALL" = 0 ]; then
command -v docker >/dev/null 2>&1 command -v docker >/dev/null 2>&1
if [[ $? != 0 ]]; then if [ $? != 0 ]; then
echo -e "正在安装 Docker" echo "正在安装 Docker"
if [ "$os_alpine" != 1 ]; then if [ "$os_alpine" != 1 ]; then
bash <(curl -sL https://${Get_Docker_URL}) ${Get_Docker_Argu} >/dev/null 2>&1 curl -sL https://${Get_Docker_URL} | sudo bash -s ${Get_Docker_Argu}
if [[ $? != 0 ]]; then if [ $? != 0 ]; then
echo -e "${red}下载脚本失败,请检查本机能否连接 ${Get_Docker_URL}${plain}" err "下载脚本失败,请检查本机能否连接 ${Get_Docker_URL}"
return 0 return 0
fi fi
systemctl enable docker.service sudo systemctl enable docker.service
systemctl start docker.service sudo systemctl start docker.service
else else
apk add docker docker-compose >/dev/null 2>&1 sudo apk add docker docker-compose
rc-update add docker sudo rc-update add docker
rc-service docker start sudo rc-service docker start
fi fi
echo -e "${green}Docker${plain} 安装成功" printf "${green}Docker${plain} 安装成功\n"
installation_check installation_check
fi fi
fi fi
@@ -289,7 +312,7 @@ install_dashboard_docker() {
install_dashboard_standalone() { install_dashboard_standalone() {
if [ ! -d "${NZ_DASHBOARD_PATH}/resource/template/theme-custom" ] || [ ! -d "${NZ_DASHBOARD_PATH}/resource/static/custom" ]; then if [ ! -d "${NZ_DASHBOARD_PATH}/resource/template/theme-custom" ] || [ ! -d "${NZ_DASHBOARD_PATH}/resource/static/custom" ]; then
mkdir -p "${NZ_DASHBOARD_PATH}/resource/template/theme-custom" "${NZ_DASHBOARD_PATH}/resource/static/custom" >/dev/null 2>&1 sudo mkdir -p "${NZ_DASHBOARD_PATH}/resource/template/theme-custom" "${NZ_DASHBOARD_PATH}/resource/static/custom" >/dev/null 2>&1
fi fi
} }
@@ -299,10 +322,10 @@ selinux() {
if [ $? -eq 0 ]; then if [ $? -eq 0 ]; then
getenforce | grep '[Ee]nfor' getenforce | grep '[Ee]nfor'
if [ $? -eq 0 ]; then if [ $? -eq 0 ]; then
echo -e "SELinux是开启状态,正在关闭!" echo "SELinux是开启状态,正在关闭!"
setenforce 0 &>/dev/null sudo setenforce 0 &>/dev/null
find_key="SELINUX=" find_key="SELINUX="
sed -ri "/^$find_key/c${find_key}disabled" /etc/selinux/config sudo sed -ri "/^$find_key/c${find_key}disabled" /etc/selinux/config
fi fi
fi fi
} }
@@ -311,9 +334,9 @@ install_agent() {
install_base install_base
selinux selinux
echo -e "> 安装监控Agent" echo "> 安装监控Agent"
echo -e "正在获取监控Agent版本号" echo "正在获取监控Agent版本号"
local version=$(curl -m 10 -sL "https://api.github.com/repos/nezhahq/agent/releases/latest" | grep "tag_name" | head -n 1 | awk -F ":" '{print $2}' | sed 's/\"//g;s/,//g;s/ //g') local version=$(curl -m 10 -sL "https://api.github.com/repos/nezhahq/agent/releases/latest" | grep "tag_name" | head -n 1 | awk -F ":" '{print $2}' | sed 's/\"//g;s/,//g;s/ //g')
if [ ! -n "$version" ]; then if [ ! -n "$version" ]; then
@@ -324,26 +347,26 @@ install_agent() {
fi fi
if [ ! -n "$version" ]; then if [ ! -n "$version" ]; then
echo -e "获取版本号失败,请检查本机能否链接 https://api.github.com/repos/nezhahq/agent/releases/latest" err "获取版本号失败,请检查本机能否链接 https://api.github.com/repos/nezhahq/agent/releases/latest"
return 0 return 1
else else
echo -e "当前最新版本为: ${version}" echo "当前最新版本为: ${version}"
fi fi
# 哪吒监控文件夹 # 哪吒监控文件夹
mkdir -p $NZ_AGENT_PATH sudo mkdir -p $NZ_AGENT_PATH
chmod 777 -R $NZ_AGENT_PATH sudo chmod -R 700 $NZ_AGENT_PATH
echo -e "正在下载监控端" echo "正在下载监控端"
wget -t 2 -T 10 -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
if [[ $? != 0 ]]; then if [ $? != 0 ]; then
echo -e "${red}Release 下载失败,请检查本机能否连接 ${GITHUB_URL}${plain}" err "Release 下载失败,请检查本机能否连接 ${GITHUB_URL}"
return 0 return 1
fi fi
unzip -qo nezha-agent_linux_${os_arch}.zip && sudo unzip -qo nezha-agent_linux_${os_arch}.zip &&
mv nezha-agent $NZ_AGENT_PATH && sudo mv nezha-agent $NZ_AGENT_PATH &&
rm -rf nezha-agent_linux_${os_arch}.zip README.md sudo rm -rf nezha-agent_linux_${os_arch}.zip README.md
if [ $# -ge 3 ]; then if [ $# -ge 3 ]; then
modify_agent_config "$@" modify_agent_config "$@"
@@ -351,42 +374,31 @@ install_agent() {
modify_agent_config 0 modify_agent_config 0
fi fi
if [[ $# == 0 ]]; then if [ $# = 0 ]; then
before_show_menu before_show_menu
fi fi
} }
modify_agent_config() { modify_agent_config() {
echo -e "> 修改Agent配置" echo "> 修改Agent配置"
if [ "$os_alpine" != 1 ]; then
wget -t 2 -T 10 -O $NZ_AGENT_SERVICE https://${GITHUB_RAW_URL}/script/nezha-agent.service >/dev/null 2>&1
if [[ $? != 0 ]]; then
echo -e "${red}文件下载失败,请检查本机能否连接 ${GITHUB_RAW_URL}${plain}"
return 0
fi
else
wget -t 2 -T 10 -O $NZ_AGENT_SERVICERC https://${GITHUB_RAW_URL}/script/nezha-agent >/dev/null 2>&1
chmod +x $NZ_AGENT_SERVICERC
if [[ $? != 0 ]]; then
echo -e "${red}Fail to download service, please check if the network can link ${GITHUB_RAW_URL}${plain}"
return 0
fi
fi
if [ $# -lt 3 ]; then if [ $# -lt 3 ]; then
echo "请先在管理面板上添加Agent,记录下密钥" && echo "请先在管理面板上添加Agent,记录下密钥"
read -ep "请输入一个解析到面板所在IP的域名(不可套CDN): " nz_grpc_host && printf "请输入一个解析到面板所在IP的域名(不可套CDN): "
read -ep "请输入面板RPC端口 (默认值 5555): " nz_grpc_port && read -r nz_grpc_host
read -ep "请输入Agent 密钥: " nz_client_secret && printf "请输入面板RPC端口 (默认值 5555): "
read -ep "是否启用针对 gRPC 端口的 SSL/TLS加密 (--tls),需要请按 [y],默认是不需要,不理解用户可回车跳过: " nz_grpc_proxy read -r nz_grpc_port
grep -qiw 'Y' <<<"${nz_grpc_proxy}" && args='--tls' printf "请输入Agent 密钥: "
if [[ -z "${nz_grpc_host}" || -z "${nz_client_secret}" ]]; then read -r nz_client_secret
echo -e "${red}所有选项都不能为空${plain}" printf "是否启用针对 gRPC 端口的 SSL/TLS加密 (--tls),需要请按 [y],默认是不需要,不理解用户可回车跳过: "
read -r nz_grpc_proxy
echo "${nz_grpc_proxy}" | grep -qiw 'Y' && args='--tls'
if [ -z "$nz_grpc_host" ] || [ -z "$nz_client_secret" ]; then
err "所有选项都不能为空"
before_show_menu before_show_menu
return 1 return 1
fi fi
if [[ -z "${nz_grpc_port}" ]]; then if [ -z "$nz_grpc_port" ]; then
nz_grpc_port=5555 nz_grpc_port=5555
fi fi
else else
@@ -395,32 +407,18 @@ modify_agent_config() {
nz_client_secret=$3 nz_client_secret=$3
shift 3 shift 3
if [ $# -gt 0 ]; then if [ $# -gt 0 ]; then
args=" $*" args="$*"
fi fi
fi fi
if [ "$os_alpine" != 1 ]; then sudo ${NZ_AGENT_PATH}/nezha-agent service install -s "$nz_grpc_host:$nz_grpc_port" -p $nz_client_secret $args >/dev/null 2>&1
sed -i "s/nz_grpc_host/${nz_grpc_host}/" ${NZ_AGENT_SERVICE}
sed -i "s/nz_grpc_port/${nz_grpc_port}/" ${NZ_AGENT_SERVICE}
sed -i "s/nz_client_secret/${nz_client_secret}/" ${NZ_AGENT_SERVICE}
[ -n "${args}" ] && sed -i "/ExecStart/ s/$/ ${args}/" ${NZ_AGENT_SERVICE}
else
sed -i "s/nz_grpc_host/${nz_grpc_host}/" ${NZ_AGENT_SERVICERC}
sed -i "s/nz_grpc_port/${nz_grpc_port}/" ${NZ_AGENT_SERVICERC}
sed -i "s/nz_client_secret/${nz_client_secret}/" ${NZ_AGENT_SERVICERC}
[ -n "${args}" ] && sed -i "/command_args/ s/\"$/ ${args}\"/" ${NZ_AGENT_SERVICERC}
fi
echo -e "Agent配置 ${green}修改成功,请稍等重启生效${plain}" if [ $? -ne 0 ]; then
sudo ${NZ_AGENT_PATH}/nezha-agent service uninstall >/dev/null 2>&1
if [ "$os_alpine" != 1 ]; then sudo ${NZ_AGENT_PATH}/nezha-agent service install -s "$nz_grpc_host:$nz_grpc_port" -p $nz_client_secret $args >/dev/null 2>&1
systemctl daemon-reload
systemctl enable nezha-agent
systemctl restart nezha-agent
else
rc-update add nezha-agent
rc-service nezha-agent restart
fi fi
printf "Agent配置 ${green}修改成功,请稍等重启生效${plain}\n"
#if [[ $# == 0 ]]; then #if [[ $# == 0 ]]; then
# before_show_menu # before_show_menu
@@ -428,46 +426,53 @@ modify_agent_config() {
} }
modify_dashboard_config() { modify_dashboard_config() {
echo -e "> 修改面板配置" echo "> 修改面板配置"
if [[ $IS_DOCKER_NEZHA == 1 ]]; then if [ "$IS_DOCKER_NEZHA" = 1 ]; then
echo -e "正在下载 Docker 脚本" echo "正在下载 Docker 脚本"
wget -t 2 -T 10 -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
echo -e "${red}下载脚本失败,请检查本机能否连接 ${GITHUB_RAW_URL}${plain}" err "下载脚本失败,请检查本机能否连接 ${GITHUB_RAW_URL}"
return 0 return 0
fi fi
fi fi
wget -t 2 -T 10 -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
if [[ $? != 0 ]]; then if [ $? != 0 ]; then
echo -e "${red}下载脚本失败,请检查本机能否连接 ${GITHUB_RAW_URL}${plain}" err "下载脚本失败,请检查本机能否连接 ${GITHUB_RAW_URL}"
return 0 return 0
fi fi
echo "关于 GitHub Oauth2 应用:在 https://github.com/settings/developers 创建,无需审核,Callback 填 http(s)://域名或IP/oauth2/callback" && echo "关于 GitHub Oauth2 应用:在 https://github.com/settings/developers 创建,无需审核,Callback 填 http(s)://域名或IP/oauth2/callback"
echo "关于 Gitee Oauth2 应用:在 https://gitee.com/oauth/applications 创建,无需审核,Callback 填 http(s)://域名或IP/oauth2/callback" && echo "关于 Gitee Oauth2 应用:在 https://gitee.com/oauth/applications 创建,无需审核,Callback 填 http(s)://域名或IP/oauth2/callback"
read -ep "请输入 OAuth2 提供商(github/gitlab/jihulab/gitee,默认 github): " nz_oauth2_type && printf "请输入 OAuth2 提供商(github/gitlab/jihulab/gitee,默认 github): "
read -ep "请输入 Oauth2 应用的 Client ID: " nz_github_oauth_client_id && read -r nz_oauth2_type
read -ep "请输入 Oauth2 应用的 Client Secret: " nz_github_oauth_client_secret && printf "请输入 Oauth2 应用的 Client ID: "
read -ep "请输入 GitHub/Gitee 登录名作为管理员,多个以逗号隔开: " nz_admin_logins && read -r nz_github_oauth_client_id
read -ep "请输入站点标题: " nz_site_title && printf "请输入 Oauth2 应用的 Client Secret: "
read -ep "请输入站点访问端口: (默认 8008)" nz_site_port && read -r nz_github_oauth_client_secret
read -ep "请输入用于 Agent 接入的 RPC 端口: (默认 5555)" nz_grpc_port printf "请输入 GitHub/Gitee 登录名作为管理员,多个以逗号隔开: "
read -r nz_admin_logins
printf "请输入站点标题: "
read -r nz_site_title
printf "请输入站点访问端口: (默认 8008)"
read -r nz_site_port
printf "请输入用于 Agent 接入的 RPC 端口: (默认 5555)"
read -r nz_grpc_port
if [[ -z "${nz_admin_logins}" || -z "${nz_github_oauth_client_id}" || -z "${nz_github_oauth_client_secret}" || -z "${nz_site_title}" ]]; then if [ -z "$nz_admin_logins" ] || [ -z "$nz_github_oauth_client_id" ] || [ -z "$nz_github_oauth_client_secret" ] || [ -z "$nz_site_title" ]; then
echo -e "${red}所有选项都不能为空${plain}" err "所有选项都不能为空"
before_show_menu before_show_menu
return 1 return 1
fi fi
if [[ -z "${nz_site_port}" ]]; then if [ -z "$nz_site_port" ]; then
nz_site_port=8008 nz_site_port=8008
fi fi
if [[ -z "${nz_grpc_port}" ]]; then if [ -z "$nz_grpc_port" ]; then
nz_grpc_port=5555 nz_grpc_port=5555
fi fi
if [[ -z "${nz_oauth2_type}" ]]; then if [ -z "$nz_oauth2_type" ]; then
nz_oauth2_type=github nz_oauth2_type=github
fi fi
@@ -478,273 +483,260 @@ modify_dashboard_config() {
sed -i "s/nz_github_oauth_client_secret/${nz_github_oauth_client_secret}/" /tmp/nezha-config.yaml sed -i "s/nz_github_oauth_client_secret/${nz_github_oauth_client_secret}/" /tmp/nezha-config.yaml
sed -i "s/nz_language/zh-CN/" /tmp/nezha-config.yaml sed -i "s/nz_language/zh-CN/" /tmp/nezha-config.yaml
sed -i "s/nz_site_title/${nz_site_title}/" /tmp/nezha-config.yaml sed -i "s/nz_site_title/${nz_site_title}/" /tmp/nezha-config.yaml
if [[ $IS_DOCKER_NEZHA == 1 ]]; then if [ "$IS_DOCKER_NEZHA" = 1 ]; then
sed -i "s/nz_site_port/${nz_site_port}/" /tmp/nezha-docker-compose.yaml sed -i "s/nz_site_port/${nz_site_port}/" /tmp/nezha-docker-compose.yaml
sed -i "s/nz_grpc_port/${nz_grpc_port}/g" /tmp/nezha-docker-compose.yaml sed -i "s/nz_grpc_port/${nz_grpc_port}/g" /tmp/nezha-docker-compose.yaml
sed -i "s/nz_image_url/${Docker_IMG}/" /tmp/nezha-docker-compose.yaml sed -i "s/nz_image_url/${Docker_IMG}/" /tmp/nezha-docker-compose.yaml
elif [[ $IS_DOCKER_NEZHA == 0 ]]; then elif [ "$IS_DOCKER_NEZHA" = 0 ]; then
sed -i "s/80/${nz_site_port}/" /tmp/nezha-config.yaml sed -i "s/80/${nz_site_port}/" /tmp/nezha-config.yaml
fi fi
mkdir -p $NZ_DASHBOARD_PATH/data sudo mkdir -p $NZ_DASHBOARD_PATH/data
mv -f /tmp/nezha-config.yaml ${NZ_DASHBOARD_PATH}/data/config.yaml sudo mv -f /tmp/nezha-config.yaml ${NZ_DASHBOARD_PATH}/data/config.yaml
if [[ $IS_DOCKER_NEZHA == 1 ]]; then if [ "$IS_DOCKER_NEZHA" = 1 ]; then
mv -f /tmp/nezha-docker-compose.yaml ${NZ_DASHBOARD_PATH}/docker-compose.yaml sudo mv -f /tmp/nezha-docker-compose.yaml ${NZ_DASHBOARD_PATH}/docker-compose.yaml
fi fi
if [[ $IS_DOCKER_NEZHA == 0 ]]; then if [ "$IS_DOCKER_NEZHA" = 0 ]; then
echo -e "正在下载服务文件" echo "正在下载服务文件"
if [ "$os_alpine" != 1 ]; then if [ "$os_alpine" != 1 ]; then
wget -t 2 -T 10 -O $NZ_DASHBOARD_SERVICE https://${GITHUB_RAW_URL}/script/nezha-dashboard.service >/dev/null 2>&1 sudo wget -t 2 -T 60 -O $NZ_DASHBOARD_SERVICE https://${GITHUB_RAW_URL}/script/nezha-dashboard.service >/dev/null 2>&1
else else
wget -t 2 -T 10 -O $NZ_DASHBOARD_SERVICERC https://${GITHUB_RAW_URL}/script/nezha-dashboard >/dev/null 2>&1 sudo wget -t 2 -T 60 -O $NZ_DASHBOARD_SERVICERC https://${GITHUB_RAW_URL}/script/nezha-dashboard >/dev/null 2>&1
chmod +x $NZ_DASHBOARD_SERVICERC sudo chmod +x $NZ_DASHBOARD_SERVICERC
if [[ $? != 0 ]]; then if [ $? != 0 ]; then
echo -e "${red}文件下载失败,请检查本机能否连接 ${GITHUB_RAW_URL}${plain}" err "文件下载失败,请检查本机能否连接 ${GITHUB_RAW_URL}"
return 0 return 0
fi fi
fi fi
fi fi
echo -e "面板配置 ${green}修改成功,请稍等重启生效${plain}" printf "面板配置 ${green}修改成功,请稍等重启生效${plain}\n"
restart_and_update restart_and_update
if [[ $# == 0 ]]; then if [ $# = 0 ]; then
before_show_menu before_show_menu
fi fi
} }
restart_and_update() { restart_and_update() {
echo -e "> 重启并更新面板" echo "> 重启并更新面板"
cd $NZ_DASHBOARD_PATH if [ "$IS_DOCKER_NEZHA" = 1 ]; then
if [[ $IS_DOCKER_NEZHA == 1 ]]; then
restart_and_update_docker restart_and_update_docker
elif [[ $IS_DOCKER_NEZHA == 0 ]]; then elif [ "$IS_DOCKER_NEZHA" = 0 ]; then
restart_and_update_standalone restart_and_update_standalone
fi fi
if [[ $? == 0 ]]; then if [ $? = 0 ]; then
echo -e "${green}哪吒监控 重启成功${plain}" printf "${green}哪吒监控 重启成功${plain}\n"
echo -e "默认管理面板地址:${yellow}域名:站点访问端口${plain}" printf "默认管理面板地址:${yellow}域名:站点访问端口${plain}\n"
else else
echo -e "${red}重启失败,可能是因为启动时间超过了两秒,请稍后查看日志信息${plain}" err "重启失败,可能是因为启动时间超过了两秒,请稍后查看日志信息"
fi fi
if [[ $# == 0 ]]; then if [ $# = 0 ]; then
before_show_menu before_show_menu
fi fi
} }
restart_and_update_docker() { restart_and_update_docker() {
$DOCKER_COMPOSE_COMMAND pull sudo $DOCKER_COMPOSE_COMMAND -f ${NZ_DASHBOARD_PATH}/docker-compose.yaml pull
$DOCKER_COMPOSE_COMMAND down sudo $DOCKER_COMPOSE_COMMAND -f ${NZ_DASHBOARD_PATH}/docker-compose.yaml down
$DOCKER_COMPOSE_COMMAND up -d sudo $DOCKER_COMPOSE_COMMAND -f ${NZ_DASHBOARD_PATH}/docker-compose.yaml up -d
} }
restart_and_update_standalone() { restart_and_update_standalone() {
if [ "$os_alpine" != 1 ]; then if [ "$os_alpine" != 1 ]; then
systemctl stop nezha-dashboard sudo systemctl daemon-reload
sudo systemctl stop nezha-dashboard
else else
rc-service nezha-dashboard stop sudo rc-service nezha-dashboard stop
fi fi
wget -qO app.zip https://${GITHUB_URL}/naiba/nezha/releases/latest/download/dashboard-linux-$os_arch.zip >/dev/null 2>&1 && unzip -qq app.zip && mv dist/dashboard-linux-$os_arch app && rm -r app.zip dist sudo wget -qO $NZ_DASHBOARD_PATH/app.zip https://${GITHUB_URL}/naiba/nezha/releases/latest/download/dashboard-linux-$os_arch.zip >/dev/null 2>&1 && sudo unzip -qq $NZ_DASHBOARD_PATH/app.zip -d $NZ_DASHBOARD_PATH && sudo mv $NZ_DASHBOARD_PATH/dist/dashboard-linux-$os_arch $NZ_DASHBOARD_PATH/app && sudo rm -r $NZ_DASHBOARD_PATH/app.zip $NZ_DASHBOARD_PATH/dist
if [ "$os_alpine" != 1 ]; then if [ "$os_alpine" != 1 ]; then
systemctl daemon-reload sudo systemctl enable nezha-dashboard
systemctl enable nezha-dashboard sudo systemctl restart nezha-dashboard
systemctl restart nezha-dashboard
else else
rc-update add nezha-dashboard sudo rc-update add nezha-dashboard
rc-service nezha-dashboard restart sudo rc-service nezha-dashboard restart
fi fi
} }
start_dashboard() { start_dashboard() {
echo -e "> 启动面板" echo "> 启动面板"
if [[ $IS_DOCKER_NEZHA == 1 ]]; then if [ "$IS_DOCKER_NEZHA" = 1 ]; then
start_dashboard_docker start_dashboard_docker
elif [[ $IS_DOCKER_NEZHA == 0 ]]; then elif [ "$IS_DOCKER_NEZHA" = 0 ]; then
start_dashboard_standalone start_dashboard_standalone
fi fi
if [[ $? == 0 ]]; then if [ $? = 0 ]; then
echo -e "${green}哪吒监控 启动成功${plain}" printf "${green}哪吒监控 启动成功${plain}\n"
else else
echo -e "${red}启动失败,请稍后查看日志信息${plain}" err "启动失败,请稍后查看日志信息"
fi fi
if [[ $# == 0 ]]; then if [ $# = 0 ]; then
before_show_menu before_show_menu
fi fi
} }
start_dashboard_docker() { start_dashboard_docker() {
cd $NZ_DASHBOARD_PATH && $DOCKER_COMPOSE_COMMAND up -d sudo $DOCKER_COMPOSE_COMMAND -f ${NZ_DASHBOARD_PATH}/docker-compose.yaml up -d
} }
start_dashboard_standalone() { start_dashboard_standalone() {
if [ "$os_alpine" != 1 ]; then if [ "$os_alpine" != 1 ]; then
systemctl start nezha-dashboard sudo systemctl start nezha-dashboard
else else
rc-service nezha-dashboard start sudo rc-service nezha-dashboard start
fi fi
} }
stop_dashboard() { stop_dashboard() {
echo -e "> 停止面板" echo "> 停止面板"
if [[ $IS_DOCKER_NEZHA == 1 ]]; then if [ "$IS_DOCKER_NEZHA" = 1 ]; then
stop_dashboard_docker stop_dashboard_docker
elif [[ $IS_DOCKER_NEZHA == 0 ]]; then elif [ "$IS_DOCKER_NEZHA" = 0 ]; then
stop_dashboard_standalone stop_dashboard_standalone
fi fi
if [[ $? == 0 ]]; then if [ $? = 0 ]; then
echo -e "${green}哪吒监控 停止成功${plain}" printf "${green}哪吒监控 停止成功${plain}\n"
else else
echo -e "${red}停止失败,请稍后查看日志信息${plain}" err "停止失败,请稍后查看日志信息"
fi fi
if [[ $# == 0 ]]; then if [ $# = 0 ]; then
before_show_menu before_show_menu
fi fi
} }
stop_dashboard_docker() { stop_dashboard_docker() {
cd $NZ_DASHBOARD_PATH && $DOCKER_COMPOSE_COMMAND down sudo $DOCKER_COMPOSE_COMMAND -f ${NZ_DASHBOARD_PATH}/docker-compose.yaml down
} }
stop_dashboard_standalone() { stop_dashboard_standalone() {
if [ "$os_alpine" != 1 ]; then if [ "$os_alpine" != 1 ]; then
systemctl stop nezha-dashboard sudo systemctl stop nezha-dashboard
else else
rc-service nezha-dashboard stop sudo rc-service nezha-dashboard stop
fi fi
} }
show_dashboard_log() { show_dashboard_log() {
echo -e "> 获取面板日志" echo "> 获取面板日志"
if [[ $IS_DOCKER_NEZHA == 1 ]]; then if [ "$IS_DOCKER_NEZHA" = 1 ]; then
show_dashboard_log_docker show_dashboard_log_docker
elif [[ $IS_DOCKER_NEZHA == 0 ]]; then elif [ "$IS_DOCKER_NEZHA" = 0 ]; then
show_dashboard_log_standalone show_dashboard_log_standalone
fi fi
if [[ $# == 0 ]]; then if [ $# = 0 ]; then
before_show_menu before_show_menu
fi fi
} }
show_dashboard_log_docker() { show_dashboard_log_docker() {
cd $NZ_DASHBOARD_PATH && $DOCKER_COMPOSE_COMMAND logs -f sudo $DOCKER_COMPOSE_COMMAND -f ${NZ_DASHBOARD_PATH}/docker-compose.yaml logs -f
} }
show_dashboard_log_standalone() { show_dashboard_log_standalone() {
if [ "$os_alpine" != 1 ]; then if [ "$os_alpine" != 1 ]; then
journalctl -xf -u nezha-dashboard.service sudo journalctl -xf -u nezha-dashboard.service
else else
tail -n 10 /var/log/nezha-dashboard.err sudo tail -n 10 /var/log/nezha-dashboard.err
fi fi
} }
uninstall_dashboard() { uninstall_dashboard() {
echo -e "> 卸载管理面板" echo "> 卸载管理面板"
if [[ $IS_DOCKER_NEZHA == 1 ]]; then if [ "$IS_DOCKER_NEZHA" = 1 ]; then
uninstall_dashboard_docker uninstall_dashboard_docker
elif [[ $IS_DOCKER_NEZHA == 0 ]]; then elif [ "$IS_DOCKER_NEZHA" = 0 ]; then
uninstall_dashboard_standalone uninstall_dashboard_standalone
fi fi
clean_all clean_all
if [[ $# == 0 ]]; then if [ $# = 0 ]; then
before_show_menu before_show_menu
fi fi
} }
uninstall_dashboard_docker() { uninstall_dashboard_docker() {
cd $NZ_DASHBOARD_PATH && $DOCKER_COMPOSE_COMMAND down sudo $DOCKER_COMPOSE_COMMAND -f ${NZ_DASHBOARD_PATH}/docker-compose.yaml down
rm -rf $NZ_DASHBOARD_PATH sudo rm -rf $NZ_DASHBOARD_PATH
docker rmi -f ghcr.io/naiba/nezha-dashboard >/dev/null 2>&1 sudo docker rmi -f ghcr.io/naiba/nezha-dashboard >/dev/null 2>&1
docker rmi -f registry.cn-shanghai.aliyuncs.com/naibahq/nezha-dashboard >/dev/null 2>&1 sudo docker rmi -f registry.cn-shanghai.aliyuncs.com/naibahq/nezha-dashboard >/dev/null 2>&1
} }
uninstall_dashboard_standalone() { uninstall_dashboard_standalone() {
rm -rf $NZ_DASHBOARD_PATH sudo rm -rf $NZ_DASHBOARD_PATH
if [ "$os_alpine" != 1 ]; then if [ "$os_alpine" != 1 ]; then
systemctl stop nezha-dashboard sudo systemctl disable nezha-dashboard
sudo systemctl stop nezha-dashboard
else else
rc-service nezha-dashboard stop sudo rc-update del nezha-dashboard
sudo rc-service nezha-dashboard stop
fi fi
if [ "$os_alpine" != 1 ]; then if [ "$os_alpine" != 1 ]; then
rm $NZ_DASHBOARD_SERVICE sudo rm $NZ_DASHBOARD_SERVICE
else else
rm $NZ_DASHBOARD_SERVICERC sudo rm $NZ_DASHBOARD_SERVICERC
fi fi
} }
show_agent_log() { show_agent_log() {
echo -e "> 获取Agent日志" echo "> 获取Agent日志"
if [ "$os_alpine" != 1 ]; then if [ "$os_alpine" != 1 ]; then
journalctl -xf -u nezha-agent.service sudo journalctl -xf -u nezha-agent.service
else else
tail -n 10 /var/log/nezha-agent.err sudo tail -n 10 /var/log/nezha-agent.err
fi fi
if [[ $# == 0 ]]; then if [ $# = 0 ]; then
before_show_menu before_show_menu
fi fi
} }
uninstall_agent() { uninstall_agent() {
echo -e "> 卸载Agent" echo "> 卸载Agent"
if [ "$os_alpine" != 1 ]; then sudo ${NZ_AGENT_PATH}/nezha-agent service uninstall
systemctl disable nezha-agent.service
systemctl stop nezha-agent.service
rm -rf $NZ_AGENT_SERVICE
systemctl daemon-reload
else
rc-update del nezha-agent
rc-service nezha-agent stop
rm -rf $NZ_AGENT_SERVICERC
fi
rm -rf $NZ_AGENT_PATH sudo rm -rf $NZ_AGENT_PATH
clean_all clean_all
if [[ $# == 0 ]]; then if [ $# = 0 ]; then
before_show_menu before_show_menu
fi fi
} }
restart_agent() { restart_agent() {
echo -e "> 重启Agent" echo "> 重启Agent"
if [ "$os_alpine" != 1 ]; then sudo ${NZ_AGENT_PATH}/nezha-agent service restart
systemctl restart nezha-agent.service
else
rc-service nezha-agent restart
fi
if [[ $# == 0 ]]; then if [ $# = 0 ]; then
before_show_menu before_show_menu
fi fi
} }
clean_all() { clean_all() {
if [ -z "$(ls -A ${NZ_BASE_PATH})" ]; then if [ -z "$(ls -A ${NZ_BASE_PATH})" ]; then
rm -rf ${NZ_BASE_PATH} sudo rm -rf ${NZ_BASE_PATH}
fi fi
} }
@@ -770,7 +762,7 @@ show_usage() {
} }
show_menu() { show_menu() {
echo -e " printf "
${green}哪吒监控管理脚本${plain} ${red}${NZ_VERSION}${plain} ${green}哪吒监控管理脚本${plain} ${red}${NZ_VERSION}${plain}
--- https://github.com/naiba/nezha --- --- https://github.com/naiba/nezha ---
${green}1.${plain} 安装面板端 ${green}1.${plain} 安装面板端
@@ -791,7 +783,7 @@ show_menu() {
————————————————- ————————————————-
${green}0.${plain} 退出脚本 ${green}0.${plain} 退出脚本
" "
echo && read -ep "请输入选择 [0-13]: " num echo && printf "请输入选择 [0-13]: " && read -r num
case "${num}" in case "${num}" in
0) 0)
exit 0 exit 0
@@ -836,7 +828,7 @@ show_menu() {
update_script update_script
;; ;;
*) *)
echo -e "${red}请输入正确的数字 [0-13]${plain}" err "请输入正确的数字 [0-13]"
;; ;;
esac esac
} }
@@ -844,7 +836,7 @@ show_menu() {
pre_check pre_check
installation_check installation_check
if [[ $# > 0 ]]; then if [ $# -gt 0 ]; then
case $1 in case $1 in
"install_dashboard") "install_dashboard")
install_dashboard 0 install_dashboard 0
+260 -265
View File
@@ -1,4 +1,4 @@
#!/bin/bash #!/bin/sh
#======================================================== #========================================================
# System Required: CentOS 7+ / Debian 8+ / Ubuntu 16+ / Alpine 3+ / # System Required: CentOS 7+ / Debian 8+ / Ubuntu 16+ / Alpine 3+ /
@@ -10,11 +10,9 @@
NZ_BASE_PATH="/opt/nezha" NZ_BASE_PATH="/opt/nezha"
NZ_DASHBOARD_PATH="${NZ_BASE_PATH}/dashboard" NZ_DASHBOARD_PATH="${NZ_BASE_PATH}/dashboard"
NZ_AGENT_PATH="${NZ_BASE_PATH}/agent" NZ_AGENT_PATH="${NZ_BASE_PATH}/agent"
NZ_AGENT_SERVICE="/etc/systemd/system/nezha-agent.service"
NZ_AGENT_SERVICERC="/etc/init.d/nezha-agent"
NZ_DASHBOARD_SERVICE="/etc/systemd/system/nezha-dashboard.service" NZ_DASHBOARD_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.16.0" NZ_VERSION="v0.17.0"
red='\033[0;31m' red='\033[0;31m'
green='\033[0;32m' green='\033[0;32m'
@@ -23,34 +21,55 @@ plain='\033[0m'
export PATH=$PATH:/usr/local/bin export PATH=$PATH:/usr/local/bin
os_arch="" os_arch=""
[ -e /etc/os-release ] && cat /etc/os-release | grep -i "PRETTY_NAME" | grep -qi "alpine" && os_alpine='1' [ -e /etc/os-release ] && grep -i "PRETTY_NAME" /etc/os-release | grep -qi "alpine" && os_alpine='1'
sudo() {
myEUID=$(id -ru)
if [ "$myEUID" -ne 0 ]; then
if command -v sudo > /dev/null 2>&1; then
command sudo "$@"
else
err "ERROR: sudo is not installed on the system, the action cannot be proceeded."
exit 1
fi
else
"$@"
fi
}
check_systemd() {
if [ "$os_alpine" != 1 ] && ! command -v systemctl >/dev/null 2>&1; then
echo "System not supported: systemctl not found"
exit 1
fi
}
err() {
printf "${red}$*${plain}\n" >&2
}
pre_check() { pre_check() {
[ "$os_alpine" != 1 ] && ! command -v systemctl >/dev/null 2>&1 && echo "This system is not supported: systemctl not found" && exit 1
# check root
[[ $EUID -ne 0 ]] && echo -e "${red}ERROR: ${plain} This script must be run with the root user!\n" && exit 1
## os_arch ## os_arch
if [[ $(uname -m | grep 'x86_64') != "" ]]; then if uname -m | grep -q 'x86_64'; then
os_arch="amd64" os_arch="amd64"
elif [[ $(uname -m | grep 'i386\|i686') != "" ]]; then elif uname -m | grep -q 'i386\|i686'; then
os_arch="386" os_arch="386"
elif [[ $(uname -m | grep 'aarch64\|armv8b\|armv8l') != "" ]]; then elif uname -m | grep -q 'aarch64\|armv8b\|armv8l'; then
os_arch="arm64" os_arch="arm64"
elif [[ $(uname -m | grep 'arm') != "" ]]; then elif uname -m | grep -q 'arm'; then
os_arch="arm" os_arch="arm"
elif [[ $(uname -m | grep 's390x') != "" ]]; then elif uname -m | grep -q 's390x'; then
os_arch="s390x" os_arch="s390x"
elif [[ $(uname -m | grep 'riscv64') != "" ]]; then elif uname -m | grep -q 'riscv64'; then
os_arch="riscv64" os_arch="riscv64"
fi fi
## China_IP ## China_IP
if [[ -z "${CN}" ]]; then if [ -z "$CN" ]; then
if [[ $(curl -m 10 -s https://ipapi.co/json | grep 'China') != "" ]]; then if curl -m 10 -s https://ipapi.co/json | grep -q 'China'; then
echo "According to the information provided by ipapi.co, the current IP may be in China" echo "According to the information provided by ipapi.co, the current IP may be in China"
read -e -r -p "Is the installation done with a Chinese Mirror? [Y/n] (Custom Mirror Input 3):" input printf "Will the installation be done with a Chinese Mirror? [Y/n] (Custom Mirror Input 3): "
read -r input
case $input in case $input in
[yY][eE][sS] | [yY]) [yY][eE][sS] | [yY])
echo "Use Chinese Mirror" echo "Use Chinese Mirror"
@@ -58,12 +77,13 @@ pre_check() {
;; ;;
[nN][oO] | [nN]) [nN][oO] | [nN])
echo "No Use Chinese Mirror" echo "Do Not Use Chinese Mirror"
;; ;;
[3]) [3])
echo "Use Custom Mirror" echo "Use Custom Mirror"
read -e -r -p "Please enter a custom image (e.g. :dn-dao-github-mirror.daocloud.io), leave blank to nouse: " input printf "Please enter a custom image (e.g. :dn-dao-github-mirror.daocloud.io). If left blank, it won't be used: "
read -r input
case $input in case $input in
*) *)
CUSTOM_MIRROR=$input CUSTOM_MIRROR=$input
@@ -72,20 +92,20 @@ pre_check() {
;; ;;
*) *)
echo "No Use Chinese Mirror" echo "Do Not Use Chinese Mirror"
;; ;;
esac esac
fi fi
fi fi
if [[ -n "${CUSTOM_MIRROR}" ]]; then if [ -n "$CUSTOM_MIRROR" ]; then
GITHUB_RAW_URL="gitee.com/naibahq/nezha/raw/master" GITHUB_RAW_URL="gitee.com/naibahq/nezha/raw/master"
GITHUB_URL=$CUSTOM_MIRROR GITHUB_URL=$CUSTOM_MIRROR
Get_Docker_URL="get.docker.com" Get_Docker_URL="get.docker.com"
Get_Docker_Argu=" -s docker --mirror Aliyun" Get_Docker_Argu=" -s docker --mirror Aliyun"
Docker_IMG="registry.cn-shanghai.aliyuncs.com\/naibahq\/nezha-dashboard" Docker_IMG="registry.cn-shanghai.aliyuncs.com\/naibahq\/nezha-dashboard"
else else
if [[ -z "${CN}" ]]; then if [ -z "$CN" ]; then
GITHUB_RAW_URL="raw.githubusercontent.com/naiba/nezha/master" GITHUB_RAW_URL="raw.githubusercontent.com/naiba/nezha/master"
GITHUB_URL="github.com" GITHUB_URL="github.com"
Get_Docker_URL="get.docker.com" Get_Docker_URL="get.docker.com"
@@ -93,7 +113,7 @@ pre_check() {
Docker_IMG="ghcr.io\/naiba\/nezha-dashboard" Docker_IMG="ghcr.io\/naiba\/nezha-dashboard"
else else
GITHUB_RAW_URL="gitee.com/naibahq/nezha/raw/master" GITHUB_RAW_URL="gitee.com/naibahq/nezha/raw/master"
GITHUB_URL="dn-dao-github-mirror.daocloud.io" GITHUB_URL="github.com"
Get_Docker_URL="get.docker.com" Get_Docker_URL="get.docker.com"
Get_Docker_Argu=" -s docker --mirror Aliyun" Get_Docker_Argu=" -s docker --mirror Aliyun"
Docker_IMG="registry.cn-shanghai.aliyuncs.com\/naibahq\/nezha-dashboard" Docker_IMG="registry.cn-shanghai.aliyuncs.com\/naibahq\/nezha-dashboard"
@@ -104,12 +124,13 @@ pre_check() {
installation_check() { installation_check() {
if docker compose version >/dev/null 2>&1; then if docker compose version >/dev/null 2>&1; then
DOCKER_COMPOSE_COMMAND="docker compose" DOCKER_COMPOSE_COMMAND="docker compose"
if $DOCKER_COMPOSE_COMMAND ls | grep -qw "$NZ_DASHBOARD_PATH/docker-compose.yaml" >/dev/null 2>&1; then if sudo $DOCKER_COMPOSE_COMMAND ls | grep -qw "$NZ_DASHBOARD_PATH/docker-compose.yaml" >/dev/null 2>&1; then
NEZHA_IMAGES=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep -w "nezha-dashboard") NEZHA_IMAGES=$(sudo docker images --format "{{.Repository}}:{{.Tag}}" | grep -w "nezha-dashboard")
if [ -n "$NEZHA_IMAGES" ]; then if [ -n "$NEZHA_IMAGES" ]; then
echo "Docker image with nezha-dashboard repository exists:" echo "Docker image with nezha-dashboard repository exists:"
echo "$NEZHA_IMAGES" echo "$NEZHA_IMAGES"
IS_DOCKER_NEZHA=1 IS_DOCKER_NEZHA=1
FRESH_INSTALL=0
return return
else else
echo "No Docker images with the nezha-dashboard repository were found." echo "No Docker images with the nezha-dashboard repository were found."
@@ -117,12 +138,13 @@ installation_check() {
fi fi
elif command -v docker-compose >/dev/null 2>&1; then elif command -v docker-compose >/dev/null 2>&1; then
DOCKER_COMPOSE_COMMAND="docker-compose" DOCKER_COMPOSE_COMMAND="docker-compose"
if $DOCKER_COMPOSE_COMMAND -f "$NZ_DASHBOARD_PATH/docker-compose.yaml" config >/dev/null 2>&1; then if sudo $DOCKER_COMPOSE_COMMAND -f "$NZ_DASHBOARD_PATH/docker-compose.yaml" config >/dev/null 2>&1; then
NEZHA_IMAGES=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep -w "nezha-dashboard") NEZHA_IMAGES=$(sudo docker images --format "{{.Repository}}:{{.Tag}}" | grep -w "nezha-dashboard")
if [ -n "$NEZHA_IMAGES" ]; then if [ -n "$NEZHA_IMAGES" ]; then
echo "Docker image with nezha-dashboard repository exists:" echo "Docker image with nezha-dashboard repository exists:"
echo "$NEZHA_IMAGES" echo "$NEZHA_IMAGES"
IS_DOCKER_NEZHA=1 IS_DOCKER_NEZHA=1
FRESH_INSTALL=0
return return
else else
echo "No Docker images with the nezha-dashboard repository were found." echo "No Docker images with the nezha-dashboard repository were found."
@@ -130,17 +152,18 @@ installation_check() {
fi fi
fi fi
if [[ -f $NZ_DASHBOARD_PATH/app ]]; then if [ -f "$NZ_DASHBOARD_PATH/app" ]; then
IS_DOCKER_NEZHA=0 IS_DOCKER_NEZHA=0
FRESH_INSTALL=0 FRESH_INSTALL=0
fi fi
} }
select_version() { select_version() {
if [[ -z $IS_DOCKER_NEZHA ]]; then if [ -z "$IS_DOCKER_NEZHA" ]; then
echo -e "${yellow}Select your installation method(Input anything is ok if you are installing agent):\n1. Docker\n2. Standalone${plain}" printf "${yellow}Select your installation method(Input anything is ok if you are installing agent):\n1. Docker\n2. Standalone${plain}\n"
while true; do while true; do
read -e -r -p "Please enter [1-2]: " option printf "Please enter [1-2]: "
read -r option
case "${option}" in case "${option}" in
1) 1)
IS_DOCKER_NEZHA=1 IS_DOCKER_NEZHA=1
@@ -151,7 +174,7 @@ select_version() {
break break
;; ;;
*) *)
echo "${red}Please enter the correct number [1-2]${plain}" err "Please enter the correct number [1-2]"
;; ;;
esac esac
done done
@@ -159,18 +182,18 @@ select_version() {
} }
update_script() { update_script() {
echo -e "> Update Script" echo "> Update Script"
curl -sL https://${GITHUB_RAW_URL}/script/install_en.sh -o /tmp/nezha.sh curl -sL https://${GITHUB_RAW_URL}/script/install_en.sh -o /tmp/nezha.sh
new_version=$(cat /tmp/nezha.sh | grep "NZ_VERSION" | head -n 1 | awk -F "=" '{print $2}' | sed 's/\"//g;s/,//g;s/ //g') new_version=$(grep "NZ_VERSION" /tmp/nezha.sh | head -n 1 | awk -F "=" '{print $2}' | sed 's/\"//g;s/,//g;s/ //g')
if [ ! -n "$new_version" ]; then if [ ! -n "$new_version" ]; then
echo -e "Script failed to get, please check if the network can link https://${GITHUB_RAW_URL}/script/install.sh" echo "Script failed to get, please check if the network can link https://${GITHUB_RAW_URL}/script/install.sh"
return 1 return 1
fi fi
echo -e "The current latest version is: ${new_version}" echo "The current latest version is: ${new_version}"
mv -f /tmp/nezha.sh ./nezha.sh && chmod a+x ./nezha.sh mv -f /tmp/nezha.sh ./nezha.sh && chmod a+x ./nezha.sh
echo -e "Execute new script after 3s" echo "Execute new script after 3s"
sleep 3s sleep 3s
clear clear
exec ./nezha.sh exec ./nezha.sh
@@ -178,23 +201,23 @@ update_script() {
} }
before_show_menu() { before_show_menu() {
echo && echo -n -e "${yellow}* Press Enter to return to the main menu *${plain}" && read temp echo && printf "${yellow}* Press Enter to return to the main menu *${plain}" && read temp
show_menu show_menu
} }
install_base() { install_base() {
(command -v git >/dev/null 2>&1 && command -v curl >/dev/null 2>&1 && command -v wget >/dev/null 2>&1 && command -v unzip >/dev/null 2>&1 && command -v getenforce >/dev/null 2>&1) || (command -v curl >/dev/null 2>&1 && command -v wget >/dev/null 2>&1 && command -v unzip >/dev/null 2>&1 && command -v getenforce >/dev/null 2>&1) ||
(install_soft curl wget git unzip tzdata) (install_soft curl wget unzip)
} }
install_arch() { install_arch() {
echo -e "${green}Info: ${plain} Archlinux needs to add nezha-agent user to install libselinux. It will be deleted automatically after installation. It is recommended to check manually\n" printf "${green}Info: ${plain} Archlinux needs to add nezha-agent user to install libselinux. It will be deleted automatically after installation. It is recommended to check manually\n"
read -e -r -p "Do you need to install libselinux? [Y/n] " input read -r -p "Do you need to install libselinux? [Y/n] " input
case $input in case $input in
[yY][eE][sS] | [yY]) [yY][eE][sS] | [yY])
useradd -m nezha-agent useradd -m nezha-agent
sed -i "$ a\nezha-agent ALL=(ALL ) NOPASSWD:ALL" /etc/sudoers sed -i "$ a\nezha-agent ALL=(ALL ) NOPASSWD:ALL" /etc/sudoers
sudo -iu nezha-agent bash -c 'gpg --keyserver keys.gnupg.net --recv-keys BE22091E3EF62275; sudo -iu nezha-agent bash -c 'gpg --keyserver keys.gnupg.net --recv-keys 4695881C254508D1;
cd /tmp; git clone https://aur.archlinux.org/libsepol.git; cd libsepol; makepkg -si --noconfirm --asdeps; cd ..; cd /tmp; git clone https://aur.archlinux.org/libsepol.git; cd libsepol; makepkg -si --noconfirm --asdeps; cd ..;
git clone https://aur.archlinux.org/libselinux.git; cd libselinux; makepkg -si --noconfirm; cd ..; git clone https://aur.archlinux.org/libselinux.git; cd libselinux; makepkg -si --noconfirm; cd ..;
rm -rf libsepol libselinux' rm -rf libsepol libselinux'
@@ -212,24 +235,26 @@ install_arch() {
} }
install_soft() { install_soft() {
(command -v yum >/dev/null 2>&1 && yum makecache && yum install $* selinux-policy -y) || (command -v yum >/dev/null 2>&1 && sudo yum makecache && sudo yum install $* selinux-policy -y) ||
(command -v apt >/dev/null 2>&1 && apt update && apt install $* selinux-utils -y) || (command -v apt >/dev/null 2>&1 && sudo apt update && sudo apt install $* selinux-utils -y) ||
(command -v pacman >/dev/null 2>&1 && pacman -Syu $* base-devel --noconfirm && install_arch) || (command -v pacman >/dev/null 2>&1 && sudo pacman -Syu $* base-devel --noconfirm && install_arch) ||
(command -v apt-get >/dev/null 2>&1 && apt-get update && apt-get install $* selinux-utils -y) || (command -v apt-get >/dev/null 2>&1 && sudo apt-get update && sudo apt-get install $* selinux-utils -y) ||
(command -v apk >/dev/null 2>&1 && apk update && apk add $* -f) (command -v apk >/dev/null 2>&1 && sudo apk update && sudo apk add $* -f)
} }
install_dashboard() { install_dashboard() {
check_systemd
install_base install_base
echo -e "> Install Panel" echo "> Install Dashboard"
# Nezha Monitoring Folder # Nezha Monitoring Folder
if [[ ! $FRESH_INSTALL == 0 ]]; then if [ ! "$FRESH_INSTALL" = 0 ]; then
mkdir -p $NZ_DASHBOARD_PATH sudo mkdir -p $NZ_DASHBOARD_PATH
else else
echo "You may have already installed the dashboard, repeated installation will overwrite the data, please pay attention to backup." echo "You may have already installed the dashboard, repeated installation will overwrite the data, please pay attention to backup."
read -e -r -p "Exit the installation? [Y/n] " input printf "Exit the installation? [Y/n] "
read -r input
case $input in case $input in
[yY][eE][sS] | [yY]) [yY][eE][sS] | [yY])
echo "Exit the installation." echo "Exit the installation."
@@ -244,40 +269,41 @@ install_dashboard() {
;; ;;
esac esac
fi fi
chmod 777 -R $NZ_DASHBOARD_PATH
if [[ $IS_DOCKER_NEZHA == 1 ]]; then sudo chmod -R 700 $NZ_DASHBOARD_PATH
if [ "$IS_DOCKER_NEZHA" = 1 ]; then
install_dashboard_docker install_dashboard_docker
elif [[ $IS_DOCKER_NEZHA == 0 ]]; then elif [ "$IS_DOCKER_NEZHA" = 0 ]; then
install_dashboard_standalone install_dashboard_standalone
fi fi
modify_dashboard_config 0 modify_dashboard_config 0
if [[ $# == 0 ]]; then if [ $# = 0 ]; then
before_show_menu before_show_menu
fi fi
} }
install_dashboard_docker() { install_dashboard_docker() {
if [[ ! $FRESH_INSTALL == 0 ]]; then if [ ! "$FRESH_INSTALL" = 0 ]; then
command -v docker >/dev/null 2>&1 command -v docker >/dev/null 2>&1
if [[ $? != 0 ]]; then if [ $? != 0 ]; then
echo -e "Installing Docker" echo "Installing Docker"
if [ "$os_alpine" != 1 ]; then if [ "$os_alpine" != 1 ]; then
bash <(curl -sL https://${Get_Docker_URL}) ${Get_Docker_Argu} >/dev/null 2>&1 curl -sL https://${Get_Docker_URL} | sudo bash -s ${Get_Docker_Argu}
if [[ $? != 0 ]]; then if [ $? != 0 ]; then
echo -e "${red}Script failed to get, please check if the network can link ${Get_Docker_URL}${plain}" err "Script failed to get, please check if the network can link ${Get_Docker_URL}"
return 0 return 0
fi fi
systemctl enable docker.service sudo systemctl enable docker.service
systemctl start docker.service sudo systemctl start docker.service
else else
apk add docker docker-compose >/dev/null 2>&1 sudo apk add docker docker-compose
rc-update add docker sudo rc-update add docker
rc-service docker start sudo rc-service docker start
fi fi
echo -e "${green}Docker${plain} installed successfully" printf "${green}Docker${plain} installed successfully\n"
installation_check installation_check
fi fi
fi fi
@@ -285,7 +311,7 @@ install_dashboard_docker() {
install_dashboard_standalone() { install_dashboard_standalone() {
if [ ! -d "${NZ_DASHBOARD_PATH}/resource/template/theme-custom" ] || [ ! -d "${NZ_DASHBOARD_PATH}/resource/static/custom" ]; then if [ ! -d "${NZ_DASHBOARD_PATH}/resource/template/theme-custom" ] || [ ! -d "${NZ_DASHBOARD_PATH}/resource/static/custom" ]; then
mkdir -p "${NZ_DASHBOARD_PATH}/resource/template/theme-custom" "${NZ_DASHBOARD_PATH}/resource/static/custom" >/dev/null 2>&1 sudo mkdir -p "${NZ_DASHBOARD_PATH}/resource/template/theme-custom" "${NZ_DASHBOARD_PATH}/resource/static/custom" >/dev/null 2>&1
fi fi
} }
@@ -295,10 +321,10 @@ selinux() {
if [ $? -eq 0 ]; then if [ $? -eq 0 ]; then
getenforce | grep '[Ee]nfor' getenforce | grep '[Ee]nfor'
if [ $? -eq 0 ]; then if [ $? -eq 0 ]; then
echo -e "SELinux runningclosing now" echo "SELinux running, closing now!"
setenforce 0 &>/dev/null sudo setenforce 0 &>/dev/null
find_key="SELINUX=" find_key="SELINUX="
sed -ri "/^$find_key/c${find_key}disabled" /etc/selinux/config sudo sed -ri "/^$find_key/c${find_key}disabled" /etc/selinux/config
fi fi
fi fi
} }
@@ -307,9 +333,9 @@ install_agent() {
install_base install_base
selinux selinux
echo -e "> Install Nezha Agent" echo "> Install Agent"
echo -e "Obtaining Agent version" echo "Obtaining Agent version"
local version=$(curl -m 10 -sL "https://api.github.com/repos/nezhahq/agent/releases/latest" | grep "tag_name" | head -n 1 | awk -F ":" '{print $2}' | sed 's/\"//g;s/,//g;s/ //g') local version=$(curl -m 10 -sL "https://api.github.com/repos/nezhahq/agent/releases/latest" | grep "tag_name" | head -n 1 | awk -F ":" '{print $2}' | sed 's/\"//g;s/,//g;s/ //g')
if [ ! -n "$version" ]; then if [ ! -n "$version" ]; then
@@ -320,26 +346,26 @@ install_agent() {
fi fi
if [ ! -n "$version" ]; then if [ ! -n "$version" ]; then
echo -e "Fail to obtaine agent version, please check if the network can link https://api.github.com/repos/nezhahq/agent/releases/latest" err "Fail to obtaine agent version, please check if the network can link https://api.github.com/repos/nezhahq/agent/releases/latest"
return 0 return 1
else else
echo -e "The current latest version is: ${version}" echo "The current latest version is: ${version}"
fi fi
# Nezha Monitoring Folder # Nezha Monitoring Folder
mkdir -p $NZ_AGENT_PATH sudo mkdir -p $NZ_AGENT_PATH
chmod 777 -R $NZ_AGENT_PATH sudo chmod -R 700 $NZ_AGENT_PATH
echo -e "Downloading Agent" echo "Downloading Agent"
wget -t 2 -T 10 -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
if [[ $? != 0 ]]; then if [ $? != 0 ]; then
echo -e "${red}Fail to download agent, please check if the network can link ${GITHUB_URL}${plain}" err "Fail to download agent, please check if the network can link ${GITHUB_URL}"
return 0 return 1
fi fi
unzip -qo nezha-agent_linux_${os_arch}.zip && sudo unzip -qo nezha-agent_linux_${os_arch}.zip &&
mv nezha-agent $NZ_AGENT_PATH && sudo mv nezha-agent $NZ_AGENT_PATH &&
rm -rf nezha-agent_linux_${os_arch}.zip README.md sudo rm -rf nezha-agent_linux_${os_arch}.zip README.md
if [ $# -ge 3 ]; then if [ $# -ge 3 ]; then
modify_agent_config "$@" modify_agent_config "$@"
@@ -347,42 +373,31 @@ install_agent() {
modify_agent_config 0 modify_agent_config 0
fi fi
if [[ $# == 0 ]]; then if [ $# = 0 ]; then
before_show_menu before_show_menu
fi fi
} }
modify_agent_config() { modify_agent_config() {
echo -e "> Modify Agent Configuration" echo "> Modify Agent Configuration"
if [ "$os_alpine" != 1 ]; then
wget -t 2 -T 10 -O $NZ_AGENT_SERVICE https://${GITHUB_RAW_URL}/script/nezha-agent.service >/dev/null 2>&1
if [[ $? != 0 ]]; then
echo -e "${red}Fail to download service, please check if the network can link ${GITHUB_RAW_URL}${plain}"
return 0
fi
else
wget -t 2 -T 10 -O $NZ_AGENT_SERVICERC https://${GITHUB_RAW_URL}/script/nezha-agent >/dev/null 2>&1
chmod +x $NZ_AGENT_SERVICERC
if [[ $? != 0 ]]; then
echo -e "${red}Fail to download service, please check if the network can link ${GITHUB_RAW_URL}${plain}"
return 0
fi
fi
if [ $# -lt 3 ]; then if [ $# -lt 3 ]; then
echo "Please add Agent in the admin panel first, record the secret" && echo "Please add Agent in the admin panel first, record the secret"
read -ep "Please enter a domain that resolves to the IP where the panel is located (no CDN sets): " nz_grpc_host && printf "Please enter a domain that resolves to the IP where the panel is located (no CDN): "
read -ep "Please enter the panel RPC port (default 5555): " nz_grpc_port && read -r nz_grpc_host
read -ep "Please enter the Agent secret: " nz_client_secret && printf "Please enter the panel RPC port (default 5555): "
read -ep "Do you want to enable SSL/TLS encryption for the gRPC port (--tls)? Press [y] if yes, the default is not required, and users can press Enter to skip if you don't understand: " nz_grpc_proxy read -r nz_grpc_port
grep -qiw 'Y' <<<"${nz_grpc_proxy}" && args='--tls' printf "Please enter the Agent secret: "
if [[ -z "${nz_grpc_host}" || -z "${nz_client_secret}" ]]; then read -r nz_client_secret
echo -e "${red}All options cannot be empty${plain}" printf "Do you want to enable SSL/TLS encryption for the gRPC port (--tls)? Press [y] if yes, the default is not required, and users can press Enter to skip if you don't understand: "
read -r nz_grpc_proxy
echo "${nz_grpc_proxy}" | grep -qiw 'Y' && args='--tls'
if [ -z "$nz_grpc_host" ] || [ -z "$nz_client_secret" ]; then
err "All options cannot be empty"
before_show_menu before_show_menu
return 1 return 1
fi fi
if [[ -z "${nz_grpc_port}" ]]; then if [ -z "$nz_grpc_port" ]; then
nz_grpc_port=5555 nz_grpc_port=5555
fi fi
else else
@@ -391,32 +406,18 @@ modify_agent_config() {
nz_client_secret=$3 nz_client_secret=$3
shift 3 shift 3
if [ $# -gt 0 ]; then if [ $# -gt 0 ]; then
args=" $*" args="$*"
fi fi
fi fi
if [ "$os_alpine" != 1 ]; then sudo ${NZ_AGENT_PATH}/nezha-agent service install -s "$nz_grpc_host:$nz_grpc_port" -p $nz_client_secret $args >/dev/null 2>&1
sed -i "s/nz_grpc_host/${nz_grpc_host}/" ${NZ_AGENT_SERVICE}
sed -i "s/nz_grpc_port/${nz_grpc_port}/" ${NZ_AGENT_SERVICE}
sed -i "s/nz_client_secret/${nz_client_secret}/" ${NZ_AGENT_SERVICE}
[ -n "${args}" ] && sed -i "/ExecStart/ s/$/ ${args}/" ${NZ_AGENT_SERVICE}
else
sed -i "s/nz_grpc_host/${nz_grpc_host}/" ${NZ_AGENT_SERVICERC}
sed -i "s/nz_grpc_port/${nz_grpc_port}/" ${NZ_AGENT_SERVICERC}
sed -i "s/nz_client_secret/${nz_client_secret}/" ${NZ_AGENT_SERVICERC}
[ -n "${args}" ] && sed -i "/command_args/ s/\"$/ ${args}\"/" ${NZ_AGENT_SERVICERC}
fi
echo -e "Agent configuration ${green} modified successfully, please wait for agent self-restart to take effect${plain}" if [ $? -ne 0 ]; then
sudo ${NZ_AGENT_PATH}/nezha-agent service uninstall >/dev/null 2>&1
if [ "$os_alpine" != 1 ]; then sudo ${NZ_AGENT_PATH}/nezha-agent service install -s "$nz_grpc_host:$nz_grpc_port" -p $nz_client_secret $args >/dev/null 2>&1
systemctl daemon-reload
systemctl enable nezha-agent
systemctl restart nezha-agent
else
rc-update add nezha-agent
rc-service nezha-agent restart
fi fi
printf "Agent configuration ${green} modified successfully, please wait for agent self-restart to take effect${plain}\n"
#if [[ $# == 0 ]]; then #if [[ $# == 0 ]]; then
# before_show_menu # before_show_menu
@@ -424,46 +425,53 @@ modify_agent_config() {
} }
modify_dashboard_config() { modify_dashboard_config() {
echo -e "> Modify Panel Configuration" echo "> Modify Dashboard Configuration"
if [[ $IS_DOCKER_NEZHA == 1 ]]; then if [ "$IS_DOCKER_NEZHA" = 1 ]; then
echo -e "Download Docker Script" echo "Download Docker Script"
wget -t 2 -T 10 -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
echo -e "${red}Script failed to get, please check if the network can link ${GITHUB_RAW_URL}${plain}" err "Script failed to get, please check if the network can link ${GITHUB_RAW_URL}"
return 0 return 0
fi fi
fi fi
wget -t 2 -T 10 -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
if [[ $? != 0 ]]; then if [ $? != 0 ]; then
echo -e "${red}Script failed to get, please check if the network can link ${GITHUB_RAW_URL}${plain}" err "Script failed to get, please check if the network can link ${GITHUB_RAW_URL}"
return 0 return 0
fi fi
echo "About the GitHub Oauth2 application: create it at https://github.com/settings/developers, no review required, and fill in the http(s)://domain_or_IP/oauth2/callback" && echo "About the GitHub Oauth2 application: create it at https://github.com/settings/developers, no review required, and fill in the http(s)://domain_or_IP/oauth2/callback"
echo "(Not recommended) About the Gitee Oauth2 application: create it at https://gitee.com/oauth/applications, no auditing required, and fill in the http(s)://domain_or_IP/oauth2/callback" && echo "(Not recommended) About the Gitee Oauth2 application: create it at https://gitee.com/oauth/applications, no auditing required, and fill in the http(s)://domain_or_IP/oauth2/callback"
read -ep "Please enter the OAuth2 provider (github/gitlab/jihulab/gitee, default github): " nz_oauth2_type && printf "Please enter the OAuth2 provider (github/gitlab/jihulab/gitee, default github): "
read -ep "Please enter the Client ID of the Oauth2 application: " nz_github_oauth_client_id && read -r nz_oauth2_type
read -ep "Please enter the Client Secret of the Oauth2 application: " nz_github_oauth_client_secret && printf "Please enter the Client ID of the Oauth2 application: "
read -ep "Please enter your GitHub/Gitee login name as the administrator, separated by commas: " nz_admin_logins && read -r nz_github_oauth_client_id
read -ep "Please enter the site title: " nz_site_title && printf "Please enter the Client Secret of the Oauth2 application: "
read -ep "Please enter the site access port: (default 8008)" nz_site_port && read -r nz_github_oauth_client_secret
read -ep "Please enter the RPC port to be used for Agent access: (default 5555)" nz_grpc_port printf "Please enter your GitHub/Gitee login name as the administrator, separated by commas: "
read -r nz_admin_logins
printf "Please enter the site title: "
read -r nz_site_title
printf "Please enter the site access port: (default 8008)"
read -r nz_site_port
printf "Please enter the RPC port to be used for Agent access: (default 5555)"
read -r nz_grpc_port
if [[ -z "${nz_admin_logins}" || -z "${nz_github_oauth_client_id}" || -z "${nz_github_oauth_client_secret}" || -z "${nz_site_title}" ]]; then if [ -z "$nz_admin_logins" ] || [ -z "$nz_github_oauth_client_id" ] || [ -z "$nz_github_oauth_client_secret" ] || [ -z "$nz_site_title" ]; then
echo -e "${red}All options cannot be empty${plain}" err "All options cannot be empty"
before_show_menu before_show_menu
return 1 return 1
fi fi
if [[ -z "${nz_site_port}" ]]; then if [ -z "$nz_site_port" ]; then
nz_site_port=8008 nz_site_port=8008
fi fi
if [[ -z "${nz_grpc_port}" ]]; then if [ -z "$nz_grpc_port" ]; then
nz_grpc_port=5555 nz_grpc_port=5555
fi fi
if [[ -z "${nz_oauth2_type}" ]]; then if [ -z "$nz_oauth2_type" ]; then
nz_oauth2_type=github nz_oauth2_type=github
fi fi
@@ -472,275 +480,262 @@ modify_dashboard_config() {
sed -i "s/nz_grpc_port/${nz_grpc_port}/" /tmp/nezha-config.yaml sed -i "s/nz_grpc_port/${nz_grpc_port}/" /tmp/nezha-config.yaml
sed -i "s/nz_github_oauth_client_id/${nz_github_oauth_client_id}/" /tmp/nezha-config.yaml sed -i "s/nz_github_oauth_client_id/${nz_github_oauth_client_id}/" /tmp/nezha-config.yaml
sed -i "s/nz_github_oauth_client_secret/${nz_github_oauth_client_secret}/" /tmp/nezha-config.yaml sed -i "s/nz_github_oauth_client_secret/${nz_github_oauth_client_secret}/" /tmp/nezha-config.yaml
sed -i "s/nz_language/zh-CN/" /tmp/nezha-config.yaml
sed -i "s/nz_site_title/${nz_site_title}/" /tmp/nezha-config.yaml sed -i "s/nz_site_title/${nz_site_title}/" /tmp/nezha-config.yaml
sed -i "s/nz_language/en-US/" /tmp/nezha-config.yaml if [ "$IS_DOCKER_NEZHA" = 1 ]; then
if [[ $IS_DOCKER_NEZHA == 1 ]]; then
sed -i "s/nz_site_port/${nz_site_port}/" /tmp/nezha-docker-compose.yaml sed -i "s/nz_site_port/${nz_site_port}/" /tmp/nezha-docker-compose.yaml
sed -i "s/nz_grpc_port/${nz_grpc_port}/g" /tmp/nezha-docker-compose.yaml sed -i "s/nz_grpc_port/${nz_grpc_port}/g" /tmp/nezha-docker-compose.yaml
sed -i "s/nz_image_url/${Docker_IMG}/" /tmp/nezha-docker-compose.yaml sed -i "s/nz_image_url/${Docker_IMG}/" /tmp/nezha-docker-compose.yaml
elif [[ $IS_DOCKER_NEZHA == 0 ]]; then elif [ "$IS_DOCKER_NEZHA" = 0 ]; then
sed -i "s/80/${nz_site_port}/" /tmp/nezha-config.yaml sed -i "s/80/${nz_site_port}/" /tmp/nezha-config.yaml
fi fi
mkdir -p $NZ_DASHBOARD_PATH/data sudo mkdir -p $NZ_DASHBOARD_PATH/data
mv -f /tmp/nezha-config.yaml ${NZ_DASHBOARD_PATH}/data/config.yaml sudo mv -f /tmp/nezha-config.yaml ${NZ_DASHBOARD_PATH}/data/config.yaml
if [[ $IS_DOCKER_NEZHA == 1 ]]; then if [ "$IS_DOCKER_NEZHA" = 1 ]; then
mv -f /tmp/nezha-docker-compose.yaml ${NZ_DASHBOARD_PATH}/docker-compose.yaml sudo mv -f /tmp/nezha-docker-compose.yaml ${NZ_DASHBOARD_PATH}/docker-compose.yaml
fi fi
if [[ $IS_DOCKER_NEZHA == 0 ]]; then if [ "$IS_DOCKER_NEZHA" = 0 ]; then
echo -e "Downloading service file" echo "Downloading service file"
if [ "$os_alpine" != 1 ]; then if [ "$os_alpine" != 1 ]; then
wget -t 2 -T 10 -O $NZ_DASHBOARD_SERVICE https://${GITHUB_RAW_URL}/script/nezha-dashboard.service >/dev/null 2>&1 sudo wget -t 2 -T 60 -O $NZ_DASHBOARD_SERVICE https://${GITHUB_RAW_URL}/script/nezha-dashboard.service >/dev/null 2>&1
else else
wget -t 2 -T 10 -O $NZ_DASHBOARD_SERVICERC https://${GITHUB_RAW_URL}/script/nezha-dashboard >/dev/null 2>&1 sudo wget -t 2 -T 60 -O $NZ_DASHBOARD_SERVICERC https://${GITHUB_RAW_URL}/script/nezha-dashboard >/dev/null 2>&1
chmod +x $NZ_DASHBOARD_SERVICERC sudo chmod +x $NZ_DASHBOARD_SERVICERC
if [[ $? != 0 ]]; then if [ $? != 0 ]; then
echo -e "${red}File failed to get, please check if the network can link ${GITHUB_RAW_URL}${plain}" err "File failed to get, please check if the network can link ${GITHUB_RAW_URL}"
return 0 return 0
fi fi
fi fi
fi fi
echo -e "Dashboard configuration ${green} modified successfully, please wait for Dashboard self-restart to take effect${plain}" printf "Dashboard configuration ${green} modified successfully, please wait for Dashboard self-restart to take effect${plain}\n"
restart_and_update restart_and_update
if [[ $# == 0 ]]; then if [ $# = 0 ]; then
before_show_menu before_show_menu
fi fi
} }
restart_and_update() { restart_and_update() {
echo -e "> Restart and Update the Panel" echo "> Restart and Update the Panel"
cd $NZ_DASHBOARD_PATH if [ "$IS_DOCKER_NEZHA" = 1 ]; then
if [[ $IS_DOCKER_NEZHA == 1 ]]; then
restart_and_update_docker restart_and_update_docker
elif [[ $IS_DOCKER_NEZHA == 0 ]]; then elif [ "$IS_DOCKER_NEZHA" = 0 ]; then
restart_and_update_standalone restart_and_update_standalone
fi fi
if [[ $? == 0 ]]; then if [ $? = 0 ]; then
echo -e "${green}Nezha Monitoring Restart Successful${plain}" printf "${green}Nezha Monitoring Restart Successful${plain}\n"
echo -e "Default panel address: ${yellow}domain:Site_access_port${plain}" printf "Default panel address: ${yellow}domain:Site_access_port${plain}\n"
else else
echo -e "${red}The restart failed, probably because the boot time exceeded two seconds, please check the log information later${plain}" err "The restart failed, probably because the boot time exceeded two seconds, please check the log information later"
fi fi
if [[ $# == 0 ]]; then if [ $# = 0 ]; then
before_show_menu before_show_menu
fi fi
} }
restart_and_update_docker() { restart_and_update_docker() {
$DOCKER_COMPOSE_COMMAND pull sudo $DOCKER_COMPOSE_COMMAND -f ${NZ_DASHBOARD_PATH}/docker-compose.yaml pull
$DOCKER_COMPOSE_COMMAND down sudo $DOCKER_COMPOSE_COMMAND -f ${NZ_DASHBOARD_PATH}/docker-compose.yaml down
$DOCKER_COMPOSE_COMMAND up -d sudo $DOCKER_COMPOSE_COMMAND -f ${NZ_DASHBOARD_PATH}/docker-compose.yaml up -d
} }
restart_and_update_standalone() { restart_and_update_standalone() {
if [ "$os_alpine" != 1 ]; then if [ "$os_alpine" != 1 ]; then
systemctl stop nezha-dashboard sudo systemctl daemon-reload
sudo systemctl stop nezha-dashboard
else else
rc-service nezha-dashboard stop sudo rc-service nezha-dashboard stop
fi fi
wget -qO app.zip https://${GITHUB_URL}/naiba/nezha/releases/latest/download/dashboard-linux-$os_arch.zip >/dev/null 2>&1 && unzip -qq app.zip && mv dist/dashboard-linux-$os_arch app && rm -r app.zip dist sudo wget -qO $NZ_DASHBOARD_PATH/app.zip https://${GITHUB_URL}/naiba/nezha/releases/latest/download/dashboard-linux-$os_arch.zip >/dev/null 2>&1 && sudo unzip -qq $NZ_DASHBOARD_PATH/app.zip -d $NZ_DASHBOARD_PATH && sudo mv $NZ_DASHBOARD_PATH/dist/dashboard-linux-$os_arch $NZ_DASHBOARD_PATH/app && sudo rm -r $NZ_DASHBOARD_PATH/app.zip $NZ_DASHBOARD_PATH/dist
if [ "$os_alpine" != 1 ]; then if [ "$os_alpine" != 1 ]; then
systemctl daemon-reload sudo systemctl enable nezha-dashboard
systemctl enable nezha-dashboard sudo systemctl restart nezha-dashboard
systemctl restart nezha-dashboard
else else
rc-update add nezha-dashboard sudo rc-update add nezha-dashboard
rc-service nezha-dashboard restart sudo rc-service nezha-dashboard restart
fi fi
} }
start_dashboard() { start_dashboard() {
echo -e "> Start Panel" echo "> Start Panel"
if [[ $IS_DOCKER_NEZHA == 1 ]]; then if [ "$IS_DOCKER_NEZHA" = 1 ]; then
start_dashboard_docker start_dashboard_docker
elif [[ $IS_DOCKER_NEZHA == 0 ]]; then elif [ "$IS_DOCKER_NEZHA" = 0 ]; then
start_dashboard_standalone start_dashboard_standalone
fi fi
if [[ $? == 0 ]]; then if [ $? = 0 ]; then
echo -e "${green}Nezha Monitoring Start Successful${plain}" printf "${green}Nezha Monitoring Start Successful${plain}\n"
else else
echo -e "${red}Failed to start, please check the log message later${plain}" err "Failed to start, please check the log message later"
fi fi
if [[ $# == 0 ]]; then if [ $# = 0 ]; then
before_show_menu before_show_menu
fi fi
} }
start_dashboard_docker() { start_dashboard_docker() {
cd $NZ_DASHBOARD_PATH && $DOCKER_COMPOSE_COMMAND up -d sudo $DOCKER_COMPOSE_COMMAND -f ${NZ_DASHBOARD_PATH}/docker-compose.yaml up -d
} }
start_dashboard_standalone() { start_dashboard_standalone() {
if [ "$os_alpine" != 1 ]; then if [ "$os_alpine" != 1 ]; then
systemctl start nezha-dashboard sudo systemctl start nezha-dashboard
else else
rc-service nezha-dashboard start sudo rc-service nezha-dashboard start
fi fi
} }
stop_dashboard() { stop_dashboard() {
echo -e "> Stop Panel" echo "> Stop Panel"
if [[ $IS_DOCKER_NEZHA == 1 ]]; then if [ "$IS_DOCKER_NEZHA" = 1 ]; then
stop_dashboard_docker stop_dashboard_docker
elif [[ $IS_DOCKER_NEZHA == 0 ]]; then elif [ "$IS_DOCKER_NEZHA" = 0 ]; then
stop_dashboard_standalone stop_dashboard_standalone
fi fi
if [[ $? == 0 ]]; then if [ $? = 0 ]; then
echo -e "${green}Nezha Monitoring Stop Successful${plain}" printf "${green}Nezha Monitoring Stop Successful${plain}\n"
else else
echo -e "${red}Failed to stop, please check the log message later${plain}" err "Failed to stop, please check the log message later"
fi fi
if [[ $# == 0 ]]; then if [ $# = 0 ]; then
before_show_menu before_show_menu
fi fi
} }
stop_dashboard_docker() { stop_dashboard_docker() {
cd $NZ_DASHBOARD_PATH && $DOCKER_COMPOSE_COMMAND down sudo $DOCKER_COMPOSE_COMMAND -f ${NZ_DASHBOARD_PATH}/docker-compose.yaml down
} }
stop_dashboard_standalone() { stop_dashboard_standalone() {
if [ "$os_alpine" != 1 ]; then if [ "$os_alpine" != 1 ]; then
systemctl stop nezha-dashboard sudo systemctl stop nezha-dashboard
else else
rc-service nezha-dashboard stop sudo rc-service nezha-dashboard stop
fi fi
} }
show_dashboard_log() { show_dashboard_log() {
echo -e "> View Panel Log" echo "> View Panel Log"
if [[ $IS_DOCKER_NEZHA == 1 ]]; then if [ "$IS_DOCKER_NEZHA" = 1 ]; then
show_dashboard_log_docker show_dashboard_log_docker
elif [[ $IS_DOCKER_NEZHA == 0 ]]; then elif [ "$IS_DOCKER_NEZHA" = 0 ]; then
show_dashboard_log_standalone show_dashboard_log_standalone
fi fi
if [[ $# == 0 ]]; then if [ $# = 0 ]; then
before_show_menu before_show_menu
fi fi
} }
show_dashboard_log_docker() { show_dashboard_log_docker() {
cd $NZ_DASHBOARD_PATH && $DOCKER_COMPOSE_COMMAND logs -f sudo $DOCKER_COMPOSE_COMMAND -f ${NZ_DASHBOARD_PATH}/docker-compose.yaml logs -f
} }
show_dashboard_log_standalone() { show_dashboard_log_standalone() {
if [ "$os_alpine" != 1 ]; then if [ "$os_alpine" != 1 ]; then
journalctl -xf -u nezha-dashboard.service sudo journalctl -xf -u nezha-dashboard.service
else else
tail -n 10 /var/log/nezha-dashboard.err sudo tail -n 10 /var/log/nezha-dashboard.err
fi fi
} }
uninstall_dashboard() { uninstall_dashboard() {
echo -e "> Uninstall Panel" echo "> Uninstall Panel"
if [[ $IS_DOCKER_NEZHA == 1 ]]; then if [ "$IS_DOCKER_NEZHA" = 1 ]; then
uninstall_dashboard_docker uninstall_dashboard_docker
elif [[ $IS_DOCKER_NEZHA == 0 ]]; then elif [ "$IS_DOCKER_NEZHA" = 0 ]; then
uninstall_dashboard_standalone uninstall_dashboard_standalone
fi fi
clean_all clean_all
if [[ $# == 0 ]]; then if [ $# = 0 ]; then
before_show_menu before_show_menu
fi fi
} }
uninstall_dashboard_docker() { uninstall_dashboard_docker() {
cd $NZ_DASHBOARD_PATH && $DOCKER_COMPOSE_COMMAND down sudo $DOCKER_COMPOSE_COMMAND -f ${NZ_DASHBOARD_PATH}/docker-compose.yaml down
rm -rf $NZ_DASHBOARD_PATH sudo rm -rf $NZ_DASHBOARD_PATH
docker rmi -f ghcr.io/naiba/nezha-dashboard >/dev/null 2>&1 sudo docker rmi -f ghcr.io/naiba/nezha-dashboard >/dev/null 2>&1
docker rmi -f registry.cn-shanghai.aliyuncs.com/naibahq/nezha-dashboard >/dev/null 2>&1 sudo docker rmi -f registry.cn-shanghai.aliyuncs.com/naibahq/nezha-dashboard >/dev/null 2>&1
} }
uninstall_dashboard_standalone() { uninstall_dashboard_standalone() {
rm -rf $NZ_DASHBOARD_PATH sudo rm -rf $NZ_DASHBOARD_PATH
if [ "$os_alpine" != 1 ]; then if [ "$os_alpine" != 1 ]; then
systemctl stop nezha-dashboard sudo systemctl disable nezha-dashboard
sudo systemctl stop nezha-dashboard
else else
rc-service nezha-dashboard stop sudo rc-update del nezha-dashboard
sudo rc-service nezha-dashboard stop
fi fi
if [ "$os_alpine" != 1 ]; then if [ "$os_alpine" != 1 ]; then
rm $NZ_DASHBOARD_SERVICE sudo rm $NZ_DASHBOARD_SERVICE
else else
rm $NZ_DASHBOARD_SERVICERC sudo rm $NZ_DASHBOARD_SERVICERC
fi fi
} }
show_agent_log() { show_agent_log() {
echo -e "> > View Agent Log" echo "> View Agent Log"
if [ "$os_alpine" != 1 ]; then if [ "$os_alpine" != 1 ]; then
journalctl -xf -u nezha-agent.service sudo journalctl -xf -u nezha-agent.service
else else
tail -n 10 /var/log/nezha-agent.err sudo tail -n 10 /var/log/nezha-agent.err
fi fi
if [[ $# == 0 ]]; then if [ $# = 0 ]; then
before_show_menu before_show_menu
fi fi
} }
uninstall_agent() { uninstall_agent() {
echo -e "> Uninstall Agent" echo "> Uninstall Agent"
if [ "$os_alpine" != 1 ]; then sudo ${NZ_AGENT_PATH}/nezha-agent service uninstall
systemctl disable nezha-agent.service
systemctl stop nezha-agent.service
rm -rf $NZ_AGENT_SERVICE
systemctl daemon-reload
else
rc-update del nezha-agent
rc-service nezha-agent stop
rm -rf $NZ_AGENT_SERVICERC
fi
rm -rf $NZ_AGENT_PATH sudo rm -rf $NZ_AGENT_PATH
clean_all clean_all
if [[ $# == 0 ]]; then if [ $# = 0 ]; then
before_show_menu before_show_menu
fi fi
} }
restart_agent() { restart_agent() {
echo -e "> Restart Agent" echo "> Restart Agent"
if [ "$os_alpine" != 1 ]; then sudo ${NZ_AGENT_PATH}/nezha-agent service restart
systemctl restart nezha-agent.service
else
rc-service nezha-agent restart
fi
if [[ $# == 0 ]]; then if [ $# = 0 ]; then
before_show_menu before_show_menu
fi fi
} }
clean_all() { clean_all() {
if [ -z "$(ls -A ${NZ_BASE_PATH})" ]; then if [ -z "$(ls -A ${NZ_BASE_PATH})" ]; then
rm -rf ${NZ_BASE_PATH} sudo rm -rf ${NZ_BASE_PATH}
fi fi
} }
@@ -766,7 +761,7 @@ show_usage() {
} }
show_menu() { show_menu() {
echo -e " printf "
${green}Nezha Monitor Management Script${plain} ${red}${NZ_VERSION}${plain} ${green}Nezha Monitor Management Script${plain} ${red}${NZ_VERSION}${plain}
--- https://github.com/naiba/nezha --- --- https://github.com/naiba/nezha ---
${green}1.${plain} Install Panel ${green}1.${plain} Install Panel
@@ -787,7 +782,7 @@ show_menu() {
————————————————- ————————————————-
${green}0.${plain} Exit Script ${green}0.${plain} Exit Script
" "
echo && read -ep "Please enter [0-13]: " num echo && printf "Please enter [0-13]: " && read -r num
case "${num}" in case "${num}" in
0) 0)
exit 0 exit 0
@@ -832,7 +827,7 @@ show_menu() {
update_script update_script
;; ;;
*) *)
echo -e "${red}Please enter the correct number [0-13]${plain}" err "Please enter the correct number [0-13]"
;; ;;
esac esac
} }
@@ -840,7 +835,7 @@ show_menu() {
pre_check pre_check
installation_check installation_check
if [[ $# > 0 ]]; then if [ $# -gt 0 ]; then
case $1 in case $1 in
"install_dashboard") "install_dashboard")
install_dashboard 0 install_dashboard 0
-19
View File
@@ -1,19 +0,0 @@
#!/sbin/openrc-run
supervisor=supervise-daemon
name=nezha-agent
output_log=/var/log/${name}.log
error_log=/var/log/${name}.err
SERVER="nz_grpc_host:nz_grpc_port" #Dashboard 地址 ip:port
SECRET="nz_client_secret" #SECRET
pidfile="/run/${RC_SVCNAME}.pid"
command="/opt/nezha/agent/nezha-agent"
command_args="-s ${SERVER} -p ${SECRET}"
command_background=true
depend() {
need net
}
start_pre() {
checkpath -f -m 0644 -o root:root "/var/log/${name}.log"
}
-30
View File
@@ -1,30 +0,0 @@
[Unit]
Description=Nezha Agent
After=syslog.target
#After=network.target
#After=nezha-dashboard.service
[Service]
# Modify these two values and uncomment them if you have
# repos with lots of files and get an HTTP error 500 because
# of that
###
#LimitMEMLOCK=infinity
#LimitNOFILE=65535
Type=simple
User=root
Group=root
WorkingDirectory=/opt/nezha/agent/
ExecStart=/opt/nezha/agent/nezha-agent -s nz_grpc_host:nz_grpc_port -p nz_client_secret
Restart=always
#Environment=DEBUG=true
# Some distributions may not support these hardening directives. If you cannot start the service due
# to an unknown option, comment out the ones not supported by your version of systemd.
#ProtectSystem=full
#PrivateDevices=yes
#PrivateTmp=yes
#NoNewPrivileges=true
[Install]
WantedBy=multi-user.target
+7 -2
View File
@@ -106,6 +106,7 @@ func (s *NezhaHandler) ReportSystemState(c context.Context, r *pb.State) (*pb.Re
func (s *NezhaHandler) ReportSystemInfo(c context.Context, r *pb.Host) (*pb.Receipt, error) { func (s *NezhaHandler) ReportSystemInfo(c context.Context, r *pb.Host) (*pb.Receipt, error) {
var clientID uint64 var clientID uint64
var provider ddns.Provider
var err error var err error
if clientID, err = s.Auth.Check(c); err != nil { if clientID, err = s.Auth.Check(c); err != nil {
return nil, err return nil, err
@@ -122,7 +123,11 @@ func (s *NezhaHandler) ReportSystemInfo(c context.Context, r *pb.Host) (*pb.Rece
singleton.ServerList[clientID].Host.IP != host.IP { singleton.ServerList[clientID].Host.IP != host.IP {
serverDomain := singleton.ServerList[clientID].DDNSDomain serverDomain := singleton.ServerList[clientID].DDNSDomain
provider, err := singleton.GetDDNSProviderFromString(singleton.Conf.DDNS.Provider) if singleton.Conf.DDNS.Provider == "" {
provider, err = singleton.GetDDNSProviderFromProfile(singleton.ServerList[clientID].DDNSProfile)
} else {
provider, err = singleton.GetDDNSProviderFromString(singleton.Conf.DDNS.Provider)
}
if err == nil && serverDomain != "" { if err == nil && serverDomain != "" {
ipv4, ipv6, _ := utils.SplitIPAddr(host.IP) ipv4, ipv6, _ := utils.SplitIPAddr(host.IP)
maxRetries := int(singleton.Conf.DDNS.MaxRetries) maxRetries := int(singleton.Conf.DDNS.MaxRetries)
@@ -137,7 +142,7 @@ func (s *NezhaHandler) ReportSystemInfo(c context.Context, r *pb.Host) (*pb.Rece
} else { } else {
// 虽然会在启动时panic, 可以断言不会走这个分支, 但是考虑到动态加载配置或者其它情况, 这里输出一下方便检查奇奇怪怪的BUG // 虽然会在启动时panic, 可以断言不会走这个分支, 但是考虑到动态加载配置或者其它情况, 这里输出一下方便检查奇奇怪怪的BUG
log.Printf("NEZHA>> 未找到对应的DDNS提供者(%s), 请前往config.yml检查你的设置\n", singleton.Conf.DDNS.Provider) log.Printf("NEZHA>> 未找到对应的DDNS配置(%s), 或者是provider填写不正确, 请前往config.yml检查你的设置\n", singleton.ServerList[clientID].DDNSProfile)
} }
} }
+52 -6
View File
@@ -1,10 +1,10 @@
package singleton package singleton
import ( import (
"errors"
"fmt" "fmt"
ddns2 "github.com/naiba/nezha/pkg/ddns"
"log" "log"
ddns2 "github.com/naiba/nezha/pkg/ddns"
) )
func RetryableUpdateDomain(provider ddns2.Provider, config *ddns2.DomainConfig, maxRetries int) bool { func RetryableUpdateDomain(provider ddns2.Provider, config *ddns2.DomainConfig, maxRetries int) bool {
@@ -25,18 +25,64 @@ func RetryableUpdateDomain(provider ddns2.Provider, config *ddns2.DomainConfig,
func GetDDNSProviderFromString(provider string) (ddns2.Provider, error) { func GetDDNSProviderFromString(provider string) (ddns2.Provider, error) {
switch provider { switch provider {
case "webhook": case "webhook":
return ddns2.ProviderWebHook{ return &ddns2.ProviderWebHook{
URL: Conf.DDNS.WebhookURL, URL: Conf.DDNS.WebhookURL,
RequestMethod: Conf.DDNS.WebhookMethod, RequestMethod: Conf.DDNS.WebhookMethod,
RequestBody: Conf.DDNS.WebhookRequestBody, RequestBody: Conf.DDNS.WebhookRequestBody,
RequestHeader: Conf.DDNS.WebhookHeaders, RequestHeader: Conf.DDNS.WebhookHeaders,
}, nil }, nil
case "dummy": case "dummy":
return ddns2.ProviderDummy{}, nil return &ddns2.ProviderDummy{}, nil
case "cloudflare": case "cloudflare":
return ddns2.ProviderCloudflare{ return &ddns2.ProviderCloudflare{
Secret: Conf.DDNS.AccessSecret, Secret: Conf.DDNS.AccessSecret,
}, nil }, nil
case "tencentcloud":
return &ddns2.ProviderTencentCloud{
SecretID: Conf.DDNS.AccessID,
SecretKey: Conf.DDNS.AccessSecret,
}, nil
} }
return ddns2.ProviderDummy{}, errors.New(fmt.Sprintf("无法找到配置的DDNS提供者%s", Conf.DDNS.Provider)) return &ddns2.ProviderDummy{}, fmt.Errorf("无法找到配置的DDNS提供者%s", Conf.DDNS.Provider)
}
func GetDDNSProviderFromProfile(profileName string) (ddns2.Provider, error) {
profile, ok := Conf.DDNS.Profiles[profileName]
if !ok {
return &ddns2.ProviderDummy{}, fmt.Errorf("未找到配置项 %s", profileName)
}
switch profile.Provider {
case "webhook":
return &ddns2.ProviderWebHook{
URL: profile.WebhookURL,
RequestMethod: profile.WebhookMethod,
RequestBody: profile.WebhookRequestBody,
RequestHeader: profile.WebhookHeaders,
}, nil
case "dummy":
return &ddns2.ProviderDummy{}, nil
case "cloudflare":
return &ddns2.ProviderCloudflare{
Secret: profile.AccessSecret,
}, nil
case "tencentcloud":
return &ddns2.ProviderTencentCloud{
SecretID: profile.AccessID,
SecretKey: profile.AccessSecret,
}, nil
}
return &ddns2.ProviderDummy{}, fmt.Errorf("无法找到配置的DDNS提供者%s", profile.Provider)
}
func ValidateDDNSProvidersFromProfiles() error {
validProviders := map[string]bool{"webhook": true, "dummy": true, "cloudflare": true, "tencentcloud": true}
providers := make(map[string]string)
for profileName, profile := range Conf.DDNS.Profiles {
if _, ok := validProviders[profile.Provider]; !ok {
return fmt.Errorf("无法找到配置的DDNS提供者%s", profile.Provider)
}
providers[profileName] = profile.Provider
}
return nil
} }
+1 -1
View File
@@ -335,7 +335,7 @@ func (ss *ServiceSentinel) worker() {
// 从服务状态汇报管道获取汇报的服务数据 // 从服务状态汇报管道获取汇报的服务数据
for r := range ss.serviceReportChannel { for r := range ss.serviceReportChannel {
if ss.monitors[r.Data.GetId()] == nil || ss.monitors[r.Data.GetId()].ID == 0 { if ss.monitors[r.Data.GetId()] == nil || ss.monitors[r.Data.GetId()].ID == 0 {
log.Printf("NEZAH>> 错误的服务监控上报 %+v", r) log.Printf("NEZHA>> 错误的服务监控上报 %+v", r)
continue continue
} }
mh := r.Data mh := r.Data
+13 -9
View File
@@ -52,12 +52,16 @@ func InitConfigFromPath(path string) {
// ValidateConfig 验证配置文件有效性 // ValidateConfig 验证配置文件有效性
func ValidateConfig() { func ValidateConfig() {
// 如果DDNS启用则检查Provider是否存在, 不存在直接退出 var err error
if Conf.DDNS.Provider == "" {
err = ValidateDDNSProvidersFromProfiles()
} else {
_, err = GetDDNSProviderFromString(Conf.DDNS.Provider)
}
if err != nil {
panic(err)
}
if Conf.DDNS.Enable { if Conf.DDNS.Enable {
_, err := GetDDNSProviderFromString(Conf.DDNS.Provider)
if err != nil {
panic(err)
}
if Conf.DDNS.MaxRetries < 1 || Conf.DDNS.MaxRetries > 10 { if Conf.DDNS.MaxRetries < 1 || Conf.DDNS.MaxRetries > 10 {
panic(fmt.Errorf("DDNS.MaxRetries值域为[1, 10]的整数, 当前为 %d", Conf.DDNS.MaxRetries)) panic(fmt.Errorf("DDNS.MaxRetries值域为[1, 10]的整数, 当前为 %d", Conf.DDNS.MaxRetries))
} }
@@ -89,7 +93,7 @@ func RecordTransferHourlyUsage() {
ServerLock.Lock() ServerLock.Lock()
defer ServerLock.Unlock() defer ServerLock.Unlock()
now := time.Now() now := time.Now()
nowTrimSeconds := time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), 0, 0, 0, Loc) nowTrimSeconds := time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), 0, 0, 0, now.Location())
var txs []model.Transfer var txs []model.Transfer
for id, server := range ServerList { for id, server := range ServerList {
tx := model.Transfer{ tx := model.Transfer{
@@ -132,7 +136,7 @@ func CleanMonitorHistory() {
if !rule.IsTransferDurationRule() { if !rule.IsTransferDurationRule() {
continue continue
} }
dataCouldRemoveBefore := rule.GetTransferDurationStart() dataCouldRemoveBefore := rule.GetTransferDurationStart().UTC()
// 判断规则影响的机器范围 // 判断规则影响的机器范围
if rule.Cover == model.RuleCoverAll { if rule.Cover == model.RuleCoverAll {
// 更新全局可以清理的数据点 // 更新全局可以清理的数据点
@@ -151,12 +155,12 @@ func CleanMonitorHistory() {
} }
} }
for id, couldRemove := range specialServerKeep { for id, couldRemove := range specialServerKeep {
DB.Unscoped().Delete(&model.Transfer{}, "server_id = ? AND created_at < ?", id, couldRemove) DB.Unscoped().Delete(&model.Transfer{}, "server_id = ? AND datetime(`created_at`) < datetime(?)", id, couldRemove)
} }
if allServerKeep.IsZero() { if allServerKeep.IsZero() {
DB.Unscoped().Delete(&model.Transfer{}, "server_id NOT IN (?)", specialServerIDs) DB.Unscoped().Delete(&model.Transfer{}, "server_id NOT IN (?)", specialServerIDs)
} else { } else {
DB.Unscoped().Delete(&model.Transfer{}, "server_id NOT IN (?) AND created_at < ?", specialServerIDs, allServerKeep) DB.Unscoped().Delete(&model.Transfer{}, "server_id NOT IN (?) AND datetime(`created_at`) < datetime(?)", specialServerIDs, allServerKeep)
} }
} }