Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b13fec029 | ||
|
|
67b788a969 | ||
|
|
b63f693661 | ||
|
|
ac64aa7355 | ||
|
|
05d69d5c07 | ||
|
|
ce624a0ca6 | ||
|
|
1c91fcffac | ||
|
|
417f972659 | ||
|
|
ada344cd6b | ||
|
|
9d6682fd58 | ||
|
|
22c687c044 | ||
|
|
e1513203b4 | ||
|
|
17373e2dee | ||
|
|
e7c6984c72 | ||
|
|
f4c6f4c57d | ||
|
|
4df60c6955 | ||
|
|
0501e120f6 | ||
|
|
cfa425bb33 | ||
|
|
0613b0022d | ||
|
|
740d15bf79 | ||
|
|
7ace5d04fa | ||
|
|
a38d7bb51d | ||
|
|
61e16f96c5 | ||
|
|
61127b3435 | ||
|
|
8805dffe85 | ||
|
|
aeb7c52565 | ||
|
|
877b4cd666 |
@@ -22,7 +22,7 @@ jobs:
|
|||||||
uses: crazy-max/ghaction-xgo@v2
|
uses: crazy-max/ghaction-xgo@v2
|
||||||
with:
|
with:
|
||||||
xgo_version: latest
|
xgo_version: latest
|
||||||
go_version: 1.20.x
|
go_version: 1.21.x
|
||||||
dest: dist
|
dest: dist
|
||||||
pkg: cmd/dashboard
|
pkg: cmd/dashboard
|
||||||
prefix: dashboard
|
prefix: dashboard
|
||||||
@@ -36,6 +36,7 @@ jobs:
|
|||||||
|
|
||||||
- name: fix dist
|
- name: fix dist
|
||||||
run: |
|
run: |
|
||||||
|
ls -al dist/
|
||||||
mv dist/dashboard-linux-arm-7 dist/dashboard-linux-arm
|
mv dist/dashboard-linux-arm-7 dist/dashboard-linux-arm
|
||||||
|
|
||||||
- name: Log in to the GHCR
|
- name: Log in to the GHCR
|
||||||
@@ -107,3 +108,16 @@ jobs:
|
|||||||
curl -s https://purge.jsdelivr.net/gh/$LOWER_USERNAME/nezha@master/script/nezha-agent.service
|
curl -s https://purge.jsdelivr.net/gh/$LOWER_USERNAME/nezha@master/script/nezha-agent.service
|
||||||
curl -s https://purge.jsdelivr.net/gh/$LOWER_USERNAME/nezha@master/script/docker-compose.yaml
|
curl -s https://purge.jsdelivr.net/gh/$LOWER_USERNAME/nezha@master/script/docker-compose.yaml
|
||||||
curl -s https://purge.jsdelivr.net/gh/$LOWER_USERNAME/nezha@master/script/config.yaml
|
curl -s https://purge.jsdelivr.net/gh/$LOWER_USERNAME/nezha@master/script/config.yaml
|
||||||
|
|
||||||
|
- name: Trigger sync
|
||||||
|
if: ${{ env.SYNCED == 0 }}
|
||||||
|
uses: actions/github-script@v6
|
||||||
|
with:
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
script: |
|
||||||
|
await github.rest.actions.createWorkflowDispatch({
|
||||||
|
owner: 'naiba',
|
||||||
|
repo: 'nezha',
|
||||||
|
workflow_id: 'sync-release.yml',
|
||||||
|
ref: 'master'
|
||||||
|
})
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
name: Sync Release to Gitee
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
sync-release-to-gitee:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
GITEE_TOKEN: ${{ secrets.GITEE_TOKEN }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Sync to Gitee
|
||||||
|
run: |
|
||||||
|
pip3 install PyGitHub
|
||||||
|
python3 .github/workflows/sync.py
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
import os
|
||||||
|
import time
|
||||||
|
import requests
|
||||||
|
import hashlib
|
||||||
|
from github import Github
|
||||||
|
|
||||||
|
|
||||||
|
def get_github_latest_release():
|
||||||
|
g = Github()
|
||||||
|
repo = g.get_repo("naiba/nezha")
|
||||||
|
release = repo.get_latest_release()
|
||||||
|
if release:
|
||||||
|
print(f"Latest release tag is: {release.tag_name}")
|
||||||
|
print(f"Latest release info is: {release.body}")
|
||||||
|
files = []
|
||||||
|
for asset in release.get_assets():
|
||||||
|
url = asset.browser_download_url
|
||||||
|
name = asset.name
|
||||||
|
|
||||||
|
response = requests.get(url)
|
||||||
|
if response.status_code == 200:
|
||||||
|
with open(name, 'wb') as f:
|
||||||
|
f.write(response.content)
|
||||||
|
print(f"Downloaded {name}")
|
||||||
|
else:
|
||||||
|
print(f"Failed to download {name}")
|
||||||
|
file_abs_path = get_abs_path(asset.name)
|
||||||
|
files.append(file_abs_path)
|
||||||
|
sync_to_gitee(release.tag_name, release.body, files)
|
||||||
|
else:
|
||||||
|
print("No releases found.")
|
||||||
|
|
||||||
|
|
||||||
|
def sync_to_gitee(tag: str, body: str, files: slice):
|
||||||
|
release_id = ""
|
||||||
|
owner = "naibahq"
|
||||||
|
repo = "nezha"
|
||||||
|
release_api_uri = f"https://gitee.com/api/v5/repos/{owner}/{repo}/releases"
|
||||||
|
api_client = requests.Session()
|
||||||
|
api_client.headers.update({
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
})
|
||||||
|
|
||||||
|
access_token = os.environ['GITEE_TOKEN']
|
||||||
|
release_data = {
|
||||||
|
'access_token': access_token,
|
||||||
|
'tag_name': tag,
|
||||||
|
'name': tag,
|
||||||
|
'body': body,
|
||||||
|
'prerelease': False,
|
||||||
|
'target_commitish': 'master'
|
||||||
|
}
|
||||||
|
release_api_response = api_client.post(release_api_uri, json=release_data)
|
||||||
|
if release_api_response.status_code == 201:
|
||||||
|
release_info = release_api_response.json()
|
||||||
|
release_id = release_info.get('id')
|
||||||
|
else:
|
||||||
|
print(
|
||||||
|
f"Request failed with status code {release_api_response.status_code}")
|
||||||
|
|
||||||
|
print(f"Gitee release id: {release_id}")
|
||||||
|
asset_api_uri = f"{release_api_uri}/{release_id}/attach_files"
|
||||||
|
|
||||||
|
for file_path in files:
|
||||||
|
files = {
|
||||||
|
'file': open(file_path, 'rb')
|
||||||
|
}
|
||||||
|
|
||||||
|
asset_api_response = requests.post(
|
||||||
|
asset_api_uri, params={'access_token': access_token}, files=files)
|
||||||
|
|
||||||
|
if asset_api_response.status_code == 201:
|
||||||
|
asset_info = asset_api_response.json()
|
||||||
|
asset_name = asset_info.get('name')
|
||||||
|
print(f"Successfully uploaded {asset_name}!")
|
||||||
|
else:
|
||||||
|
print(
|
||||||
|
f"Request failed with status code {asset_api_response.status_code}")
|
||||||
|
|
||||||
|
api_client.close()
|
||||||
|
print("Sync is completed!")
|
||||||
|
|
||||||
|
|
||||||
|
def get_abs_path(path: str):
|
||||||
|
wd = os.getcwd()
|
||||||
|
return os.path.join(wd, path)
|
||||||
|
|
||||||
|
|
||||||
|
get_github_latest_release()
|
||||||
@@ -25,7 +25,7 @@ jobs:
|
|||||||
uses: crazy-max/ghaction-xgo@v2
|
uses: crazy-max/ghaction-xgo@v2
|
||||||
with:
|
with:
|
||||||
xgo_version: latest
|
xgo_version: latest
|
||||||
go_version: 1.20.x
|
go_version: 1.21.x
|
||||||
dest: dist
|
dest: dist
|
||||||
pkg: cmd/dashboard
|
pkg: cmd/dashboard
|
||||||
prefix: dashboard
|
prefix: dashboard
|
||||||
@@ -36,4 +36,8 @@ jobs:
|
|||||||
tags: timetzdata
|
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
|
||||||
|
- name: fix dist
|
||||||
|
run: |
|
||||||
|
ls -al dist/
|
||||||
|
mv dist/dashboard-linux-arm-7 dist/dashboard-linux-arm
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ jobs:
|
|||||||
uses: crazy-max/ghaction-xgo@v2
|
uses: crazy-max/ghaction-xgo@v2
|
||||||
with:
|
with:
|
||||||
xgo_version: latest
|
xgo_version: latest
|
||||||
go_version: 1.20.x
|
go_version: 1.21.x
|
||||||
dest: dist
|
dest: dist
|
||||||
pkg: cmd/dashboard
|
pkg: cmd/dashboard
|
||||||
prefix: dashboard
|
prefix: dashboard
|
||||||
@@ -43,3 +43,7 @@ jobs:
|
|||||||
tags: timetzdata
|
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
|
||||||
|
- name: fix dist
|
||||||
|
run: |
|
||||||
|
ls -al dist/
|
||||||
|
mv dist/dashboard-linux-arm-7 dist/dashboard-linux-arm
|
||||||
|
|||||||
@@ -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"> <img src="https://img.shields.io/github/v/release/nezhahq/agent?color=brightgreen&label=Agent&style=for-the-badge&logo=github"> <img src="https://img.shields.io/github/actions/workflow/status/nezhahq/agent/agent.yml?label=Agent%20CI&logo=github&style=for-the-badge"> <img src="https://img.shields.io/badge/Installer-v0.16.3-brightgreen?style=for-the-badge&logo=linux">
|
<img alt="GitHub release (with filter)" src="https://img.shields.io/github/v/release/naiba/nezha?color=brightgreen&style=for-the-badge&logo=github&label=Dashboard"> <img src="https://img.shields.io/github/v/release/nezhahq/agent?color=brightgreen&label=Agent&style=for-the-badge&logo=github"> <img src="https://img.shields.io/github/actions/workflow/status/nezhahq/agent/agent.yml?label=Agent%20CI&logo=github&style=for-the-badge"> <img src="https://img.shields.io/badge/Installer-v0.17.2-brightgreen?style=for-the-badge&logo=linux">
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
<p>:trollface: <b>Nezha Monitoring: Self-hostable, lightweight, servers and websites monitoring and O&M tool.</b></p>
|
<p>:trollface: <b>Nezha Monitoring: Self-hostable, lightweight, servers and websites monitoring and O&M tool.</b></p>
|
||||||
<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)
|
||||||
@@ -41,14 +43,14 @@ You can change the dashboard language in the settings page (`/setting`) after th
|
|||||||
<!--GAMFC_DELIMITER--><a href="https://github.com/naiba" title="naiba"><img src="https://avatars.githubusercontent.com/u/29243953?v=4" width="50;" alt="naiba"/></a>
|
<!--GAMFC_DELIMITER--><a href="https://github.com/naiba" title="naiba"><img src="https://avatars.githubusercontent.com/u/29243953?v=4" width="50;" alt="naiba"/></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/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/xykt" title="xykt"><img src="https://avatars.githubusercontent.com/u/152045469?v=4" width="50;" alt="xykt"/></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/uubulb" title="UUBulb"><img src="https://avatars.githubusercontent.com/u/35923940?v=4" width="50;" alt="UUBulb"/></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/dysf888" title="黑歌"><img src="https://avatars.githubusercontent.com/u/47450409?v=4" width="50;" alt="黑歌"/></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/nap0o" title="nap0o"><img src="https://avatars.githubusercontent.com/u/144927971?v=4" width="50;" alt="nap0o"/></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/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/lemoeo" title="Lemoe"><img src="https://avatars.githubusercontent.com/u/18618627?v=4" width="50;" alt="Lemoe"/></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>
|
||||||
@@ -77,6 +79,7 @@ You can change the dashboard language in the settings page (`/setting`) after th
|
|||||||
<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/IamTaoChen" title="Tao Chen"><img src="https://avatars.githubusercontent.com/u/42793494?v=4" width="50;" alt="Tao Chen"/></a>
|
||||||
<a href="https://github.com/dreamingsleeping" title="Nanjing Hopefun Network Technology Co. Ltd."><img src="https://avatars.githubusercontent.com/u/13828658?v=4" width="50;" alt="Nanjing Hopefun Network Technology Co. Ltd."/></a>
|
<a href="https://github.com/dreamingsleeping" title="Nanjing Hopefun Network Technology Co. Ltd."><img src="https://avatars.githubusercontent.com/u/13828658?v=4" width="50;" alt="Nanjing Hopefun Network Technology Co. Ltd."/></a>
|
||||||
<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/funnyzak" title="Leon"><img src="https://avatars.githubusercontent.com/u/2562087?v=4" width="50;" alt="Leon"/></a>
|
||||||
|
|||||||
@@ -3,12 +3,8 @@ package controller
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@@ -24,22 +20,13 @@ import (
|
|||||||
"github.com/naiba/nezha/pkg/utils"
|
"github.com/naiba/nezha/pkg/utils"
|
||||||
"github.com/naiba/nezha/pkg/websocketx"
|
"github.com/naiba/nezha/pkg/websocketx"
|
||||||
"github.com/naiba/nezha/proto"
|
"github.com/naiba/nezha/proto"
|
||||||
|
"github.com/naiba/nezha/service/rpc"
|
||||||
"github.com/naiba/nezha/service/singleton"
|
"github.com/naiba/nezha/service/singleton"
|
||||||
)
|
)
|
||||||
|
|
||||||
type terminalContext struct {
|
|
||||||
agentConn *websocketx.Conn
|
|
||||||
userConn *websocketx.Conn
|
|
||||||
serverID uint64
|
|
||||||
host string
|
|
||||||
useSSL bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type commonPage struct {
|
type commonPage struct {
|
||||||
r *gin.Engine
|
r *gin.Engine
|
||||||
terminals map[string]*terminalContext
|
requestGroup singleflight.Group
|
||||||
terminalsLock *sync.Mutex
|
|
||||||
requestGroup singleflight.Group
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cp *commonPage) serve() {
|
func (cp *commonPage) serve() {
|
||||||
@@ -68,7 +55,6 @@ type viewPasswordForm struct {
|
|||||||
func (p *commonPage) issueViewPassword(c *gin.Context) {
|
func (p *commonPage) issueViewPassword(c *gin.Context) {
|
||||||
var vpf viewPasswordForm
|
var vpf viewPasswordForm
|
||||||
err := c.ShouldBind(&vpf)
|
err := c.ShouldBind(&vpf)
|
||||||
log.Println("bingo", vpf)
|
|
||||||
var hash []byte
|
var hash []byte
|
||||||
if err == nil && vpf.Password != singleton.Conf.Site.ViewPassword {
|
if err == nil && vpf.Password != singleton.Conf.Site.ViewPassword {
|
||||||
err = errors.New(singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "WrongAccessPassword"}))
|
err = errors.New(singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "WrongAccessPassword"}))
|
||||||
@@ -274,8 +260,8 @@ func (cp *commonPage) home(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var upgrader = websocket.Upgrader{
|
var upgrader = websocket.Upgrader{
|
||||||
ReadBufferSize: 1024,
|
ReadBufferSize: 10240,
|
||||||
WriteBufferSize: 1024,
|
WriteBufferSize: 10240,
|
||||||
}
|
}
|
||||||
|
|
||||||
type Data struct {
|
type Data struct {
|
||||||
@@ -283,8 +269,6 @@ type Data struct {
|
|||||||
Servers []*model.Server `json:"servers,omitempty"`
|
Servers []*model.Server `json:"servers,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var cloudflareCookiesValidator = regexp.MustCompile("^[A-Za-z0-9-_]+$")
|
|
||||||
|
|
||||||
func (cp *commonPage) ws(c *gin.Context) {
|
func (cp *commonPage) ws(c *gin.Context) {
|
||||||
conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
|
conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -321,10 +305,8 @@ func (cp *commonPage) ws(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (cp *commonPage) terminal(c *gin.Context) {
|
func (cp *commonPage) terminal(c *gin.Context) {
|
||||||
terminalID := c.Param("id")
|
streamId := c.Param("id")
|
||||||
cp.terminalsLock.Lock()
|
if _, err := rpc.NezhaHandlerSingleton.GetStream(streamId); err != nil {
|
||||||
if terminalID == "" || cp.terminals[terminalID] == nil {
|
|
||||||
cp.terminalsLock.Unlock()
|
|
||||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||||
Code: http.StatusForbidden,
|
Code: http.StatusForbidden,
|
||||||
Title: "无权访问",
|
Title: "无权访问",
|
||||||
@@ -334,104 +316,7 @@ func (cp *commonPage) terminal(c *gin.Context) {
|
|||||||
}, true)
|
}, true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
defer rpc.NezhaHandlerSingleton.CloseStream(streamId)
|
||||||
terminal := cp.terminals[terminalID]
|
|
||||||
cp.terminalsLock.Unlock()
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
// 清理 context
|
|
||||||
cp.terminalsLock.Lock()
|
|
||||||
defer cp.terminalsLock.Unlock()
|
|
||||||
delete(cp.terminals, terminalID)
|
|
||||||
}()
|
|
||||||
|
|
||||||
var isAgent bool
|
|
||||||
|
|
||||||
if _, authorized := c.Get(model.CtxKeyAuthorizedUser); !authorized {
|
|
||||||
singleton.ServerLock.RLock()
|
|
||||||
_, hasID := singleton.SecretToID[c.Request.Header.Get("Secret")]
|
|
||||||
singleton.ServerLock.RUnlock()
|
|
||||||
if !hasID {
|
|
||||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
|
||||||
Code: http.StatusForbidden,
|
|
||||||
Title: "无权访问",
|
|
||||||
Msg: "用户未登录或非法终端",
|
|
||||||
Link: "/",
|
|
||||||
Btn: "返回首页",
|
|
||||||
}, true)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if terminal.userConn == nil {
|
|
||||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
|
||||||
Code: http.StatusForbidden,
|
|
||||||
Title: "无权访问",
|
|
||||||
Msg: "用户不在线",
|
|
||||||
Link: "/",
|
|
||||||
Btn: "返回首页",
|
|
||||||
}, true)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if terminal.agentConn != nil {
|
|
||||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
|
||||||
Code: http.StatusInternalServerError,
|
|
||||||
Title: "连接已存在",
|
|
||||||
Msg: "Websocket协议切换失败",
|
|
||||||
Link: "/",
|
|
||||||
Btn: "返回首页",
|
|
||||||
}, true)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
isAgent = true
|
|
||||||
} else {
|
|
||||||
singleton.ServerLock.RLock()
|
|
||||||
server := singleton.ServerList[terminal.serverID]
|
|
||||||
singleton.ServerLock.RUnlock()
|
|
||||||
if server == nil || server.TaskStream == nil {
|
|
||||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
|
||||||
Code: http.StatusForbidden,
|
|
||||||
Title: "请求失败",
|
|
||||||
Msg: "服务器不存在或处于离线状态",
|
|
||||||
Link: "/server",
|
|
||||||
Btn: "返回重试",
|
|
||||||
}, true)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
cloudflareCookies, _ := c.Cookie("CF_Authorization")
|
|
||||||
// Cloudflare Cookies 合法性验证
|
|
||||||
// 其应该包含.分隔的三组BASE64-URL编码
|
|
||||||
if cloudflareCookies != "" {
|
|
||||||
encodedCookies := strings.Split(cloudflareCookies, ".")
|
|
||||||
if len(encodedCookies) == 3 {
|
|
||||||
for i := 0; i < 3; i++ {
|
|
||||||
if !cloudflareCookiesValidator.MatchString(encodedCookies[i]) {
|
|
||||||
cloudflareCookies = ""
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cloudflareCookies = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
terminalData, _ := utils.Json.Marshal(&model.TerminalTask{
|
|
||||||
Host: terminal.host,
|
|
||||||
UseSSL: terminal.useSSL,
|
|
||||||
Session: terminalID,
|
|
||||||
Cookie: cloudflareCookies,
|
|
||||||
})
|
|
||||||
if err := server.TaskStream.Send(&proto.Task{
|
|
||||||
Type: model.TaskTypeTerminal,
|
|
||||||
Data: string(terminalData),
|
|
||||||
}); err != nil {
|
|
||||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
|
||||||
Code: http.StatusForbidden,
|
|
||||||
Title: "请求失败",
|
|
||||||
Msg: "Agent信令下发失败",
|
|
||||||
Link: "/server",
|
|
||||||
Btn: "返回重试",
|
|
||||||
}, true)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wsConn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
|
wsConn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -447,36 +332,7 @@ func (cp *commonPage) terminal(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer wsConn.Close()
|
defer wsConn.Close()
|
||||||
conn := &websocketx.Conn{Conn: wsConn}
|
conn := websocketx.NewConn(wsConn)
|
||||||
|
|
||||||
log.Printf("NEZHA>> terminal connected %t %q", isAgent, c.Request.URL)
|
|
||||||
defer log.Printf("NEZHA>> terminal disconnected %t %q", isAgent, c.Request.URL)
|
|
||||||
|
|
||||||
if isAgent {
|
|
||||||
terminal.agentConn = conn
|
|
||||||
defer func() {
|
|
||||||
// Agent断开链接时断开用户连接
|
|
||||||
if terminal.userConn != nil {
|
|
||||||
terminal.userConn.Close()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
} else {
|
|
||||||
terminal.userConn = conn
|
|
||||||
defer func() {
|
|
||||||
// 用户断开链接时断开 Agent 连接
|
|
||||||
if terminal.agentConn != nil {
|
|
||||||
terminal.agentConn.Close()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
deadlineCh := make(chan interface{})
|
|
||||||
go func() {
|
|
||||||
// 对方连接超时
|
|
||||||
connectDeadline := time.NewTimer(time.Second * 15)
|
|
||||||
<-connectDeadline.C
|
|
||||||
deadlineCh <- struct{}{}
|
|
||||||
}()
|
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
// PING 保活
|
// PING 保活
|
||||||
@@ -488,58 +344,11 @@ func (cp *commonPage) terminal(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
dataCh := make(chan []byte)
|
if err = rpc.NezhaHandlerSingleton.UserConnected(streamId, conn); err != nil {
|
||||||
errorCh := make(chan error)
|
return
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
msgType, data, err := conn.ReadMessage()
|
|
||||||
if err != nil {
|
|
||||||
errorCh <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 将文本消息转换为命令输入
|
|
||||||
if msgType == websocket.TextMessage {
|
|
||||||
data = append([]byte{0}, data...)
|
|
||||||
}
|
|
||||||
dataCh <- data
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
var dataBuffer [][]byte
|
|
||||||
var distConn *websocketx.Conn
|
|
||||||
checkDistConn := func() {
|
|
||||||
if distConn == nil {
|
|
||||||
if isAgent {
|
|
||||||
distConn = terminal.userConn
|
|
||||||
} else {
|
|
||||||
distConn = terminal.agentConn
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for {
|
rpc.NezhaHandlerSingleton.StartStream(streamId, time.Second*10)
|
||||||
select {
|
|
||||||
case <-deadlineCh:
|
|
||||||
checkDistConn()
|
|
||||||
if distConn == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case <-errorCh:
|
|
||||||
return
|
|
||||||
case data := <-dataCh:
|
|
||||||
dataBuffer = append(dataBuffer, data)
|
|
||||||
checkDistConn()
|
|
||||||
if distConn != nil {
|
|
||||||
for i := 0; i < len(dataBuffer); i++ {
|
|
||||||
err = distConn.WriteMessage(websocket.BinaryMessage, dataBuffer[i])
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dataBuffer = dataBuffer[:0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type createTerminalRequest struct {
|
type createTerminalRequest struct {
|
||||||
@@ -571,7 +380,7 @@ func (cp *commonPage) createTerminal(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
id, err := uuid.GenerateUUID()
|
streamId, err := uuid.GenerateUUID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||||
Code: http.StatusInternalServerError,
|
Code: http.StatusInternalServerError,
|
||||||
@@ -585,6 +394,8 @@ func (cp *commonPage) createTerminal(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpc.NezhaHandlerSingleton.CreateStream(streamId)
|
||||||
|
|
||||||
singleton.ServerLock.RLock()
|
singleton.ServerLock.RLock()
|
||||||
server := singleton.ServerList[createTerminalReq.ID]
|
server := singleton.ServerList[createTerminalReq.ID]
|
||||||
singleton.ServerLock.RUnlock()
|
singleton.ServerLock.RUnlock()
|
||||||
@@ -599,17 +410,25 @@ func (cp *commonPage) createTerminal(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
cp.terminalsLock.Lock()
|
terminalData, _ := utils.Json.Marshal(&model.TerminalTask{
|
||||||
defer cp.terminalsLock.Unlock()
|
StreamID: streamId,
|
||||||
|
})
|
||||||
cp.terminals[id] = &terminalContext{
|
if err := server.TaskStream.Send(&proto.Task{
|
||||||
serverID: createTerminalReq.ID,
|
Type: model.TaskTypeTerminalGRPC,
|
||||||
host: createTerminalReq.Host,
|
Data: string(terminalData),
|
||||||
useSSL: createTerminalReq.Protocol == "https:",
|
}); err != nil {
|
||||||
|
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||||
|
Code: http.StatusForbidden,
|
||||||
|
Title: "请求失败",
|
||||||
|
Msg: "Agent信令下发失败",
|
||||||
|
Link: "/server",
|
||||||
|
Btn: "返回重试",
|
||||||
|
}, true)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.HTML(http.StatusOK, "dashboard-"+singleton.Conf.Site.DashboardTheme+"/terminal", mygin.CommonEnvironment(c, gin.H{
|
c.HTML(http.StatusOK, "dashboard-"+singleton.Conf.Site.DashboardTheme+"/terminal", mygin.CommonEnvironment(c, gin.H{
|
||||||
"SessionID": id,
|
"SessionID": streamId,
|
||||||
"ServerName": server.Name,
|
"ServerName": server.Name,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package controller
|
package controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
@@ -9,22 +10,31 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.cloudfoundry.org/bytefmt"
|
"code.cloudfoundry.org/bytefmt"
|
||||||
"github.com/gin-contrib/pprof"
|
"github.com/gin-contrib/pprof"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/hashicorp/go-uuid"
|
||||||
"github.com/nicksnyder/go-i18n/v2/i18n"
|
"github.com/nicksnyder/go-i18n/v2/i18n"
|
||||||
|
|
||||||
|
"github.com/naiba/nezha/model"
|
||||||
"github.com/naiba/nezha/pkg/mygin"
|
"github.com/naiba/nezha/pkg/mygin"
|
||||||
|
"github.com/naiba/nezha/pkg/utils"
|
||||||
|
"github.com/naiba/nezha/proto"
|
||||||
"github.com/naiba/nezha/resource"
|
"github.com/naiba/nezha/resource"
|
||||||
|
"github.com/naiba/nezha/service/rpc"
|
||||||
"github.com/naiba/nezha/service/singleton"
|
"github.com/naiba/nezha/service/singleton"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ServeWeb(port uint) *http.Server {
|
func ServeWeb(port uint) *http.Server {
|
||||||
gin.SetMode(gin.ReleaseMode)
|
gin.SetMode(gin.ReleaseMode)
|
||||||
r := gin.Default()
|
r := gin.Default()
|
||||||
|
if singleton.Conf.Debug {
|
||||||
|
gin.SetMode(gin.DebugMode)
|
||||||
|
pprof.Register(r)
|
||||||
|
}
|
||||||
|
r.Use(natGateway)
|
||||||
tmpl := template.New("").Funcs(funcMap)
|
tmpl := template.New("").Funcs(funcMap)
|
||||||
var err error
|
var err error
|
||||||
tmpl, err = tmpl.ParseFS(resource.TemplateFS, "template/**/*.html")
|
tmpl, err = tmpl.ParseFS(resource.TemplateFS, "template/**/*.html")
|
||||||
@@ -33,10 +43,6 @@ func ServeWeb(port uint) *http.Server {
|
|||||||
}
|
}
|
||||||
tmpl = loadThirdPartyTemplates(tmpl)
|
tmpl = loadThirdPartyTemplates(tmpl)
|
||||||
r.SetHTMLTemplate(tmpl)
|
r.SetHTMLTemplate(tmpl)
|
||||||
if singleton.Conf.Debug {
|
|
||||||
gin.SetMode(gin.DebugMode)
|
|
||||||
pprof.Register(r)
|
|
||||||
}
|
|
||||||
r.Use(mygin.RecordPath)
|
r.Use(mygin.RecordPath)
|
||||||
staticFs, err := fs.Sub(resource.StaticFS, "static")
|
staticFs, err := fs.Sub(resource.StaticFS, "static")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -45,7 +51,6 @@ func ServeWeb(port uint) *http.Server {
|
|||||||
r.StaticFS("/static", http.FS(staticFs))
|
r.StaticFS("/static", http.FS(staticFs))
|
||||||
r.Static("/static-custom", "resource/static/custom")
|
r.Static("/static-custom", "resource/static/custom")
|
||||||
routers(r)
|
routers(r)
|
||||||
|
|
||||||
page404 := func(c *gin.Context) {
|
page404 := func(c *gin.Context) {
|
||||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||||
Code: http.StatusNotFound,
|
Code: http.StatusNotFound,
|
||||||
@@ -68,7 +73,7 @@ func ServeWeb(port uint) *http.Server {
|
|||||||
|
|
||||||
func routers(r *gin.Engine) {
|
func routers(r *gin.Engine) {
|
||||||
// 通用页面
|
// 通用页面
|
||||||
cp := commonPage{r: r, terminals: make(map[string]*terminalContext), terminalsLock: new(sync.Mutex)}
|
cp := commonPage{r: r}
|
||||||
cp.serve()
|
cp.serve()
|
||||||
// 游客页面
|
// 游客页面
|
||||||
gp := guestPage{r}
|
gp := guestPage{r}
|
||||||
@@ -239,3 +244,64 @@ var funcMap = template.FuncMap{
|
|||||||
return singleton.StatusCodeToString(singleton.GetStatusCode(val))
|
return singleton.StatusCodeToString(singleton.GetStatusCode(val))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func natGateway(c *gin.Context) {
|
||||||
|
natConfig := singleton.GetNATConfigByDomain(c.Request.Host)
|
||||||
|
if natConfig == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
singleton.ServerLock.RLock()
|
||||||
|
server := singleton.ServerList[natConfig.ServerID]
|
||||||
|
singleton.ServerLock.RUnlock()
|
||||||
|
if server == nil || server.TaskStream == nil {
|
||||||
|
c.Writer.WriteString("server not found or not connected")
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
streamId, err := uuid.GenerateUUID()
|
||||||
|
if err != nil {
|
||||||
|
c.Writer.WriteString(fmt.Sprintf("stream id error: %v", err))
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rpc.NezhaHandlerSingleton.CreateStream(streamId)
|
||||||
|
defer rpc.NezhaHandlerSingleton.CloseStream(streamId)
|
||||||
|
|
||||||
|
taskData, err := json.Marshal(model.TaskNAT{
|
||||||
|
StreamID: streamId,
|
||||||
|
Host: natConfig.Host,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
c.Writer.WriteString(fmt.Sprintf("task data error: %v", err))
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := server.TaskStream.Send(&proto.Task{
|
||||||
|
Type: model.TaskTypeNAT,
|
||||||
|
Data: string(taskData),
|
||||||
|
}); err != nil {
|
||||||
|
c.Writer.WriteString(fmt.Sprintf("send task error: %v", err))
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w, err := utils.NewRequestWrapper(c.Request, c.Writer)
|
||||||
|
if err != nil {
|
||||||
|
c.Writer.WriteString(fmt.Sprintf("request wrapper error: %v", err))
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rpc.NezhaHandlerSingleton.UserConnected(streamId, w); err != nil {
|
||||||
|
c.Writer.WriteString(fmt.Sprintf("user connected error: %v", err))
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rpc.NezhaHandlerSingleton.StartStream(streamId, time.Second*10)
|
||||||
|
c.Abort()
|
||||||
|
}
|
||||||
|
|||||||
@@ -35,6 +35,10 @@ func (gp *guestPage) serve() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gp *guestPage) login(c *gin.Context) {
|
func (gp *guestPage) login(c *gin.Context) {
|
||||||
|
if singleton.Conf.Oauth2.OidcAutoLogin {
|
||||||
|
c.Redirect(http.StatusFound, "/oauth2/login")
|
||||||
|
return
|
||||||
|
}
|
||||||
LoginType := "GitHub"
|
LoginType := "GitHub"
|
||||||
RegistrationLink := "https://github.com/join"
|
RegistrationLink := "https://github.com/join"
|
||||||
if singleton.Conf.Oauth2.Type == model.ConfigTypeGitee {
|
if singleton.Conf.Oauth2.Type == model.ConfigTypeGitee {
|
||||||
@@ -52,6 +56,9 @@ func (gp *guestPage) login(c *gin.Context) {
|
|||||||
} else if singleton.Conf.Oauth2.Type == model.ConfigTypeCloudflare {
|
} else if singleton.Conf.Oauth2.Type == model.ConfigTypeCloudflare {
|
||||||
LoginType = "Cloudflare"
|
LoginType = "Cloudflare"
|
||||||
RegistrationLink = "https://dash.cloudflare.com/sign-up/teams"
|
RegistrationLink = "https://dash.cloudflare.com/sign-up/teams"
|
||||||
|
} else if singleton.Conf.Oauth2.Type == model.ConfigTypeOidc {
|
||||||
|
LoginType = singleton.Conf.Oauth2.OidcDisplayName
|
||||||
|
RegistrationLink = singleton.Conf.Oauth2.OidcRegisterURL
|
||||||
}
|
}
|
||||||
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"}),
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ func (ma *memberAPI) serve() {
|
|||||||
mr.POST("/batch-update-server-group", ma.batchUpdateServerGroup)
|
mr.POST("/batch-update-server-group", ma.batchUpdateServerGroup)
|
||||||
mr.POST("/batch-delete-server", ma.batchDeleteServer)
|
mr.POST("/batch-delete-server", ma.batchDeleteServer)
|
||||||
mr.POST("/notification", ma.addOrEditNotification)
|
mr.POST("/notification", ma.addOrEditNotification)
|
||||||
|
mr.POST("/nat", ma.addOrEditNAT)
|
||||||
mr.POST("/alert-rule", ma.addOrEditAlertRule)
|
mr.POST("/alert-rule", ma.addOrEditAlertRule)
|
||||||
mr.POST("/setting", ma.updateSetting)
|
mr.POST("/setting", ma.updateSetting)
|
||||||
mr.DELETE("/:model/:id", ma.delete)
|
mr.DELETE("/:model/:id", ma.delete)
|
||||||
@@ -209,6 +210,11 @@ func (ma *memberAPI) delete(c *gin.Context) {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
singleton.OnDeleteNotification(id)
|
singleton.OnDeleteNotification(id)
|
||||||
}
|
}
|
||||||
|
case "nat":
|
||||||
|
err = singleton.DB.Unscoped().Delete(&model.NAT{}, "id = ?", id).Error
|
||||||
|
if err == nil {
|
||||||
|
singleton.OnNATUpdate()
|
||||||
|
}
|
||||||
case "monitor":
|
case "monitor":
|
||||||
err = singleton.DB.Unscoped().Delete(&model.Monitor{}, "id = ?", id).Error
|
err = singleton.DB.Unscoped().Delete(&model.Monitor{}, "id = ?", id).Error
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -733,6 +739,45 @@ func (ma *memberAPI) addOrEditNotification(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type natForm struct {
|
||||||
|
ID uint64
|
||||||
|
Name string
|
||||||
|
ServerID uint64
|
||||||
|
Host string
|
||||||
|
Domain string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ma *memberAPI) addOrEditNAT(c *gin.Context) {
|
||||||
|
var nf natForm
|
||||||
|
var n model.NAT
|
||||||
|
err := c.ShouldBindJSON(&nf)
|
||||||
|
if err == nil {
|
||||||
|
n.Name = nf.Name
|
||||||
|
n.ID = nf.ID
|
||||||
|
n.Domain = nf.Domain
|
||||||
|
n.Host = nf.Host
|
||||||
|
n.ServerID = nf.ServerID
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
if n.ID == 0 {
|
||||||
|
err = singleton.DB.Create(&n).Error
|
||||||
|
} else {
|
||||||
|
err = singleton.DB.Save(&n).Error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusOK, model.Response{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
Message: fmt.Sprintf("请求错误:%s", err),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
singleton.OnNATUpdate()
|
||||||
|
c.JSON(http.StatusOK, model.Response{
|
||||||
|
Code: http.StatusOK,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
type alertRuleForm struct {
|
type alertRuleForm struct {
|
||||||
ID uint64
|
ID uint64
|
||||||
Name string
|
Name string
|
||||||
@@ -847,6 +892,11 @@ func (ma *memberAPI) logout(c *gin.Context) {
|
|||||||
c.JSON(http.StatusOK, model.Response{
|
c.JSON(http.StatusOK, model.Response{
|
||||||
Code: http.StatusOK,
|
Code: http.StatusOK,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if oidcLogoutUrl := singleton.Conf.Oauth2.OidcLogoutURL; oidcLogoutUrl != "" {
|
||||||
|
// 重定向到 OIDC 退出登录地址。不知道为什么,这里的重定向不生效
|
||||||
|
c.Redirect(http.StatusOK, oidcLogoutUrl)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type settingForm struct {
|
type settingForm struct {
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ func (mp *memberPage) serve() {
|
|||||||
mr.GET("/monitor", mp.monitor)
|
mr.GET("/monitor", mp.monitor)
|
||||||
mr.GET("/cron", mp.cron)
|
mr.GET("/cron", mp.cron)
|
||||||
mr.GET("/notification", mp.notification)
|
mr.GET("/notification", mp.notification)
|
||||||
|
mr.GET("/nat", mp.nat)
|
||||||
mr.GET("/setting", mp.setting)
|
mr.GET("/setting", mp.setting)
|
||||||
mr.GET("/api", mp.api)
|
mr.GET("/api", mp.api)
|
||||||
}
|
}
|
||||||
@@ -77,6 +78,15 @@ func (mp *memberPage) notification(c *gin.Context) {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (mp *memberPage) nat(c *gin.Context) {
|
||||||
|
var data []model.NAT
|
||||||
|
singleton.DB.Find(&data)
|
||||||
|
c.HTML(http.StatusOK, "dashboard-"+singleton.Conf.Site.DashboardTheme+"/nat", mygin.CommonEnvironment(c, gin.H{
|
||||||
|
"Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "NAT"}),
|
||||||
|
"NAT": data,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
func (mp *memberPage) setting(c *gin.Context) {
|
func (mp *memberPage) setting(c *gin.Context) {
|
||||||
c.HTML(http.StatusOK, "dashboard-"+singleton.Conf.Site.DashboardTheme+"/setting", mygin.CommonEnvironment(c, gin.H{
|
c.HTML(http.StatusOK, "dashboard-"+singleton.Conf.Site.DashboardTheme+"/setting", mygin.CommonEnvironment(c, gin.H{
|
||||||
"Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "Settings"}),
|
"Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "Settings"}),
|
||||||
|
|||||||
@@ -10,25 +10,27 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/coreos/go-oidc/v3/oidc"
|
||||||
"github.com/naiba/nezha/pkg/oidc/cloudflare"
|
"github.com/naiba/nezha/pkg/oidc/cloudflare"
|
||||||
|
myOidc "github.com/naiba/nezha/pkg/oidc/general"
|
||||||
|
|
||||||
"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"
|
||||||
|
"github.com/naiba/nezha/model"
|
||||||
|
"github.com/naiba/nezha/pkg/mygin"
|
||||||
|
"github.com/naiba/nezha/pkg/utils"
|
||||||
|
"github.com/naiba/nezha/service/singleton"
|
||||||
"github.com/patrickmn/go-cache"
|
"github.com/patrickmn/go-cache"
|
||||||
"github.com/xanzy/go-gitlab"
|
"github.com/xanzy/go-gitlab"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
GitHubOauth2 "golang.org/x/oauth2/github"
|
GitHubOauth2 "golang.org/x/oauth2/github"
|
||||||
GitlabOauth2 "golang.org/x/oauth2/gitlab"
|
GitlabOauth2 "golang.org/x/oauth2/gitlab"
|
||||||
|
|
||||||
"github.com/naiba/nezha/model"
|
|
||||||
"github.com/naiba/nezha/pkg/mygin"
|
|
||||||
"github.com/naiba/nezha/pkg/utils"
|
|
||||||
"github.com/naiba/nezha/service/singleton"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type oauth2controller struct {
|
type oauth2controller struct {
|
||||||
r gin.IRoutes
|
r gin.IRoutes
|
||||||
|
oidcProvider *oidc.Provider
|
||||||
}
|
}
|
||||||
|
|
||||||
func (oa *oauth2controller) serve() {
|
func (oa *oauth2controller) serve() {
|
||||||
@@ -88,6 +90,27 @@ func (oa *oauth2controller) getCommonOauth2Config(c *gin.Context) *oauth2.Config
|
|||||||
},
|
},
|
||||||
RedirectURL: oa.getRedirectURL(c),
|
RedirectURL: oa.getRedirectURL(c),
|
||||||
}
|
}
|
||||||
|
} else if singleton.Conf.Oauth2.Type == model.ConfigTypeOidc {
|
||||||
|
var err error
|
||||||
|
oa.oidcProvider, err = oidc.NewProvider(c.Request.Context(), singleton.Conf.Oauth2.OidcIssuer)
|
||||||
|
if err != nil {
|
||||||
|
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
Title: fmt.Sprintf("Cannot get OIDC infomaion from issuer from %s", singleton.Conf.Oauth2.OidcIssuer),
|
||||||
|
Msg: err.Error(),
|
||||||
|
}, true)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
scopes := strings.Split(singleton.Conf.Oauth2.OidcScopes, ",")
|
||||||
|
scopes = append(scopes, oidc.ScopeOpenID)
|
||||||
|
uniqueScopes := removeDuplicates(scopes)
|
||||||
|
return &oauth2.Config{
|
||||||
|
ClientID: singleton.Conf.Oauth2.ClientID,
|
||||||
|
ClientSecret: singleton.Conf.Oauth2.ClientSecret,
|
||||||
|
Scopes: uniqueScopes,
|
||||||
|
Endpoint: oa.oidcProvider.Endpoint(),
|
||||||
|
RedirectURL: oa.getRedirectURL(c),
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return &oauth2.Config{
|
return &oauth2.Config{
|
||||||
ClientID: singleton.Conf.Oauth2.ClientID,
|
ClientID: singleton.Conf.Oauth2.ClientID,
|
||||||
@@ -100,7 +123,8 @@ func (oa *oauth2controller) getCommonOauth2Config(c *gin.Context) *oauth2.Config
|
|||||||
|
|
||||||
func (oa *oauth2controller) getRedirectURL(c *gin.Context) string {
|
func (oa *oauth2controller) getRedirectURL(c *gin.Context) string {
|
||||||
scheme := "http://"
|
scheme := "http://"
|
||||||
if strings.HasPrefix(c.Request.Referer(), "https://") {
|
referer := c.Request.Referer()
|
||||||
|
if forwardedProto := c.Request.Header.Get("X-Forwarded-Proto"); forwardedProto == "https" || strings.HasPrefix(referer, "https://") {
|
||||||
scheme = "https://"
|
scheme = "https://"
|
||||||
}
|
}
|
||||||
return scheme + c.Request.Host + "/oauth2/callback"
|
return scheme + c.Request.Host + "/oauth2/callback"
|
||||||
@@ -179,7 +203,18 @@ func (oa *oauth2controller) callback(c *gin.Context) {
|
|||||||
user = cloudflareUserInfo.MapToNezhaUser()
|
user = cloudflareUserInfo.MapToNezhaUser()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if singleton.Conf.Oauth2.Type == model.ConfigTypeOidc {
|
||||||
|
userInfo, err := oa.oidcProvider.UserInfo(c.Request.Context(), oauth2.StaticTokenSource(otk))
|
||||||
|
if err == nil {
|
||||||
|
loginClaim := singleton.Conf.Oauth2.OidcLoginClaim
|
||||||
|
groupClain := singleton.Conf.Oauth2.OidcGroupClaim
|
||||||
|
adminGroups := strings.Split(singleton.Conf.Oauth2.AdminGroups, ",")
|
||||||
|
autoCreate := singleton.Conf.Oauth2.OidcAutoCreate
|
||||||
|
var oidceUserInfo *myOidc.UserInfo
|
||||||
|
if err := userInfo.Claims(&oidceUserInfo); err == nil {
|
||||||
|
user = oidceUserInfo.MapToNezhaUser(loginClaim, groupClain, adminGroups, autoCreate)
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
var client *GitHubAPI.Client
|
var client *GitHubAPI.Client
|
||||||
oc := oauth2Config.Client(ctx, otk)
|
oc := oauth2Config.Client(ctx, otk)
|
||||||
@@ -212,10 +247,15 @@ func (oa *oauth2controller) callback(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
var isAdmin bool
|
var isAdmin bool
|
||||||
for _, admin := range strings.Split(singleton.Conf.Oauth2.Admin, ",") {
|
|
||||||
if admin != "" && strings.EqualFold(user.Login, admin) {
|
if user.SuperAdmin {
|
||||||
isAdmin = true
|
isAdmin = true
|
||||||
break
|
} else {
|
||||||
|
for _, admin := range strings.Split(singleton.Conf.Oauth2.Admin, ",") {
|
||||||
|
if admin != "" && strings.EqualFold(user.Login, admin) {
|
||||||
|
isAdmin = true
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !isAdmin {
|
if !isAdmin {
|
||||||
@@ -242,3 +282,16 @@ func (oa *oauth2controller) callback(c *gin.Context) {
|
|||||||
"URL": "/",
|
"URL": "/",
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func removeDuplicates(elements []string) []string {
|
||||||
|
encountered := map[string]bool{}
|
||||||
|
result := []string{}
|
||||||
|
|
||||||
|
for _, v := range elements {
|
||||||
|
if !encountered[v] {
|
||||||
|
encountered[v] = true
|
||||||
|
result = append(result, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO 使用 cmux 在同一端口服务 HTTP 和 gRPC
|
||||||
singleton.CleanMonitorHistory()
|
singleton.CleanMonitorHistory()
|
||||||
go rpc.ServeRPC(singleton.Conf.GRPCPort)
|
go rpc.ServeRPC(singleton.Conf.GRPCPort)
|
||||||
serviceSentinelDispatchBus := make(chan model.Monitor) // 用于传递服务监控任务信息的channel
|
serviceSentinelDispatchBus := make(chan model.Monitor) // 用于传递服务监控任务信息的channel
|
||||||
|
|||||||
@@ -14,9 +14,8 @@ import (
|
|||||||
|
|
||||||
func ServeRPC(port uint) {
|
func ServeRPC(port uint) {
|
||||||
server := grpc.NewServer()
|
server := grpc.NewServer()
|
||||||
pb.RegisterNezhaServiceServer(server, &rpcService.NezhaHandler{
|
rpcService.NezhaHandlerSingleton = rpcService.NewNezhaHandler()
|
||||||
Auth: &rpcService.AuthHandler{},
|
pb.RegisterNezhaServiceServer(server, rpcService.NezhaHandlerSingleton)
|
||||||
})
|
|
||||||
listen, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
|
listen, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
module github.com/naiba/nezha
|
module github.com/naiba/nezha
|
||||||
|
|
||||||
go 1.20
|
go 1.21
|
||||||
|
|
||||||
require (
|
require (
|
||||||
code.cloudfoundry.org/bytefmt v0.0.0-20240425163905-bcdc1ad063ea
|
code.cloudfoundry.org/bytefmt v0.0.0-20240425163905-bcdc1ad063ea
|
||||||
code.gitea.io/sdk/gitea v0.18.0
|
code.gitea.io/sdk/gitea v0.18.0
|
||||||
github.com/BurntSushi/toml v1.3.2
|
github.com/BurntSushi/toml v1.3.2
|
||||||
|
github.com/coreos/go-oidc/v3 v3.11.0
|
||||||
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
|
||||||
github.com/google/go-github/v47 v47.1.0
|
github.com/google/go-github/v47 v47.1.0
|
||||||
@@ -17,17 +18,16 @@ require (
|
|||||||
github.com/ory/graceful v0.1.3
|
github.com/ory/graceful v0.1.3
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||||
github.com/robfig/cron/v3 v3.0.1
|
github.com/robfig/cron/v3 v3.0.1
|
||||||
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.103.0
|
github.com/xanzy/go-gitlab v0.103.0
|
||||||
golang.org/x/crypto v0.22.0
|
golang.org/x/crypto v0.25.0
|
||||||
golang.org/x/net v0.24.0
|
golang.org/x/net v0.27.0
|
||||||
golang.org/x/oauth2 v0.19.0
|
golang.org/x/oauth2 v0.21.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.16.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.34.2
|
||||||
gorm.io/driver/sqlite v1.5.5
|
gorm.io/driver/sqlite v1.5.5
|
||||||
gorm.io/gorm v1.25.10
|
gorm.io/gorm v1.25.10
|
||||||
sigs.k8s.io/yaml v1.4.0
|
sigs.k8s.io/yaml v1.4.0
|
||||||
@@ -41,13 +41,14 @@ require (
|
|||||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
github.com/go-fed/httpsig v1.1.0 // indirect
|
github.com/go-fed/httpsig v1.1.0 // indirect
|
||||||
|
github.com/go-jose/go-jose/v4 v4.0.2 // indirect
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.14.0 // indirect
|
github.com/go-playground/validator/v10 v10.14.0 // indirect
|
||||||
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
|
||||||
@@ -55,7 +56,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
|
||||||
@@ -74,7 +75,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/sys v0.19.0 // indirect
|
golang.org/x/sys v0.22.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
|
||||||
|
|||||||
@@ -10,14 +10,19 @@ github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZX
|
|||||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||||
|
github.com/coreos/go-oidc/v3 v3.11.0 h1:Ia3MxdwpSw702YW0xgfmP1GVCMA9aEFWu12XUZ3/OtI=
|
||||||
|
github.com/coreos/go-oidc/v3 v3.11.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
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/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
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/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||||
|
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||||
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=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
||||||
@@ -31,9 +36,13 @@ github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
|||||||
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
||||||
github.com/go-fed/httpsig v1.1.0 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI=
|
github.com/go-fed/httpsig v1.1.0 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI=
|
||||||
github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM=
|
github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM=
|
||||||
|
github.com/go-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0nvk=
|
||||||
|
github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY=
|
||||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
||||||
|
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||||
|
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
||||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
@@ -44,6 +53,7 @@ github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXS
|
|||||||
github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
|
github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
|
||||||
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||||
|
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||||
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
@@ -52,20 +62,22 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/go-github/v47 v47.1.0 h1:Cacm/WxQBOa9lF0FT0EMjZ2BWMetQ1TQfyurn4yF1z8=
|
github.com/google/go-github/v47 v47.1.0 h1:Cacm/WxQBOa9lF0FT0EMjZ2BWMetQ1TQfyurn4yF1z8=
|
||||||
github.com/google/go-github/v47 v47.1.0/go.mod h1:VPZBXNbFSJGjyjFRUKo9vZGawTajnWzC/YjGw/oFKi0=
|
github.com/google/go-github/v47 v47.1.0/go.mod h1:VPZBXNbFSJGjyjFRUKo9vZGawTajnWzC/YjGw/oFKi0=
|
||||||
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-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg=
|
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg=
|
||||||
|
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
|
||||||
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-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.2 h1:AcYqCvkpalPnPF2pn0KamgwamS42TqUDDYFRKq/RAd0=
|
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.2/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
|
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
|
||||||
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=
|
||||||
@@ -87,6 +99,7 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
|
|||||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
@@ -97,9 +110,10 @@ github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNa
|
|||||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||||
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-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
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 +126,9 @@ 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/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs=
|
||||||
github.com/onsi/gomega v1.33.0 h1:snPCflnZrpMsy94p4lXVEkHo12lmPnc3vY5XBbreexE=
|
github.com/onsi/gomega v1.33.0 h1:snPCflnZrpMsy94p4lXVEkHo12lmPnc3vY5XBbreexE=
|
||||||
|
github.com/onsi/gomega v1.33.0/go.mod h1:+925n5YtiFsLzzafLUHzVMBpvvRAzrydIBiSIxjX3wY=
|
||||||
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=
|
||||||
@@ -125,17 +141,17 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
|||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||||
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||||
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
|
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
|
||||||
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
|
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
|
||||||
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
||||||
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
||||||
github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA=
|
|
||||||
github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
|
|
||||||
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||||
@@ -149,7 +165,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=
|
||||||
@@ -181,16 +196,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.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
||||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/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/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-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
||||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||||
golang.org/x/oauth2 v0.19.0 h1:9+E/EZBCbTLNrbN35fHv/a/d/mOBatymz1zbtQrXpIg=
|
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
|
||||||
golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8=
|
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||||
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=
|
||||||
@@ -201,19 +216,21 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
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-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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.22.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.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
|
golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
|
||||||
|
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
|
||||||
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.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||||
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.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
|
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
|
||||||
|
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||||
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=
|
||||||
@@ -221,8 +238,8 @@ google.golang.org/grpc v1.63.0 h1:WjKe+dnvABXyPJMD7KDNLxtoGk5tgk+YFWN6cBWjZE8=
|
|||||||
google.golang.org/grpc v1.63.0/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
|
google.golang.org/grpc v1.63.0/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
|||||||
+28
-5
@@ -38,6 +38,7 @@ const (
|
|||||||
ConfigTypeJihulab = "jihulab"
|
ConfigTypeJihulab = "jihulab"
|
||||||
ConfigTypeGitea = "gitea"
|
ConfigTypeGitea = "gitea"
|
||||||
ConfigTypeCloudflare = "cloudflare"
|
ConfigTypeCloudflare = "cloudflare"
|
||||||
|
ConfigTypeOidc = "oidc"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -87,11 +88,21 @@ type Config struct {
|
|||||||
ViewPassword string // 前台查看密码
|
ViewPassword string // 前台查看密码
|
||||||
}
|
}
|
||||||
Oauth2 struct {
|
Oauth2 struct {
|
||||||
Type string
|
Type string
|
||||||
Admin string // 管理员用户名列表
|
Admin string // 管理员用户名列表
|
||||||
ClientID string
|
AdminGroups string // 管理员用户组列表
|
||||||
ClientSecret string
|
ClientID string
|
||||||
Endpoint string
|
ClientSecret string
|
||||||
|
Endpoint string
|
||||||
|
OidcDisplayName string // for OIDC Display Name
|
||||||
|
OidcIssuer string // for OIDC Issuer
|
||||||
|
OidcLogoutURL string // for OIDC Logout URL
|
||||||
|
OidcRegisterURL string // for OIDC Register URL
|
||||||
|
OidcLoginClaim string // for OIDC Claim
|
||||||
|
OidcGroupClaim string // for OIDC Group Claim
|
||||||
|
OidcScopes string // for OIDC Scopes
|
||||||
|
OidcAutoCreate bool // for OIDC Auto Create
|
||||||
|
OidcAutoLogin bool // for OIDC Auto Login
|
||||||
}
|
}
|
||||||
HTTPPort uint
|
HTTPPort uint
|
||||||
GRPCPort uint
|
GRPCPort uint
|
||||||
@@ -181,6 +192,18 @@ func (c *Config) Read(path string) error {
|
|||||||
if c.DDNS.MaxRetries == 0 {
|
if c.DDNS.MaxRetries == 0 {
|
||||||
c.DDNS.MaxRetries = 3
|
c.DDNS.MaxRetries = 3
|
||||||
}
|
}
|
||||||
|
if c.Oauth2.OidcScopes == "" {
|
||||||
|
c.Oauth2.OidcScopes = "openid,profile,email"
|
||||||
|
}
|
||||||
|
if c.Oauth2.OidcLoginClaim == "" {
|
||||||
|
c.Oauth2.OidcLoginClaim = "sub"
|
||||||
|
}
|
||||||
|
if c.Oauth2.OidcDisplayName == "" {
|
||||||
|
c.Oauth2.OidcDisplayName = "OIDC"
|
||||||
|
}
|
||||||
|
if c.Oauth2.OidcGroupClaim == "" {
|
||||||
|
c.Oauth2.OidcGroupClaim = "groups"
|
||||||
|
}
|
||||||
|
|
||||||
c.updateIgnoredIPNotificationID()
|
c.updateIgnoredIPNotificationID()
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
+8
-8
@@ -20,17 +20,17 @@ const (
|
|||||||
TaskTypeTerminal
|
TaskTypeTerminal
|
||||||
TaskTypeUpgrade
|
TaskTypeUpgrade
|
||||||
TaskTypeKeepalive
|
TaskTypeKeepalive
|
||||||
|
TaskTypeTerminalGRPC
|
||||||
|
TaskTypeNAT
|
||||||
)
|
)
|
||||||
|
|
||||||
type TerminalTask struct {
|
type TerminalTask struct {
|
||||||
// websocket 主机名
|
StreamID string
|
||||||
Host string `json:"host,omitempty"`
|
}
|
||||||
// 是否启用 SSL
|
|
||||||
UseSSL bool `json:"use_ssl,omitempty"`
|
type TaskNAT struct {
|
||||||
// 会话标识
|
StreamID string
|
||||||
Session string `json:"session,omitempty"`
|
Host string
|
||||||
// Agent在连接Server时需要的额外Cookie信息
|
|
||||||
Cookie string `json:"cookie,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
type NAT struct {
|
||||||
|
Common
|
||||||
|
Name string
|
||||||
|
ServerID uint64
|
||||||
|
Host string
|
||||||
|
Domain string `gorm:"unique"`
|
||||||
|
}
|
||||||
+28
-3
@@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 循环区间流量检测 · 更新下次需要检测时间
|
// 循环区间流量检测 · 更新下次需要检测时间
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-10
@@ -23,7 +23,7 @@ type ProviderTencentCloud struct {
|
|||||||
SecretKey string
|
SecretKey string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider ProviderTencentCloud) UpdateDomain(domainConfig *DomainConfig) bool {
|
func (provider *ProviderTencentCloud) UpdateDomain(domainConfig *DomainConfig) bool {
|
||||||
if domainConfig == nil {
|
if domainConfig == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -46,7 +46,7 @@ func (provider ProviderTencentCloud) UpdateDomain(domainConfig *DomainConfig) bo
|
|||||||
return resultV4 && resultV6
|
return resultV4 && resultV6
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider ProviderTencentCloud) addDomainRecord(domainConfig *DomainConfig, isIpv4 bool) bool {
|
func (provider *ProviderTencentCloud) addDomainRecord(domainConfig *DomainConfig, isIpv4 bool) bool {
|
||||||
record, err := provider.findDNSRecord(domainConfig.FullDomain, isIpv4)
|
record, err := provider.findDNSRecord(domainConfig.FullDomain, isIpv4)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("查找 DNS 记录时出错: %s\n", err)
|
log.Printf("查找 DNS 记录时出错: %s\n", err)
|
||||||
@@ -66,7 +66,7 @@ func (provider ProviderTencentCloud) addDomainRecord(domainConfig *DomainConfig,
|
|||||||
return provider.updateDNSRecord(domainConfig.FullDomain, record["RecordList"].([]interface{})[0].(map[string]interface{})["RecordId"].(float64), domainConfig, isIpv4)
|
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) {
|
func (provider *ProviderTencentCloud) findDNSRecord(domain string, isIPv4 bool) (map[string]interface{}, error) {
|
||||||
var ipType = "A"
|
var ipType = "A"
|
||||||
if !isIPv4 {
|
if !isIPv4 {
|
||||||
ipType = "AAAA"
|
ipType = "AAAA"
|
||||||
@@ -95,7 +95,7 @@ func (provider ProviderTencentCloud) findDNSRecord(domain string, isIPv4 bool) (
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider ProviderTencentCloud) createDNSRecord(domain string, domainConfig *DomainConfig, isIPv4 bool) bool {
|
func (provider *ProviderTencentCloud) createDNSRecord(domain string, domainConfig *DomainConfig, isIPv4 bool) bool {
|
||||||
var ipType = "A"
|
var ipType = "A"
|
||||||
var ipAddr = domainConfig.Ipv4Addr
|
var ipAddr = domainConfig.Ipv4Addr
|
||||||
if !isIPv4 {
|
if !isIPv4 {
|
||||||
@@ -117,7 +117,7 @@ func (provider ProviderTencentCloud) createDNSRecord(domain string, domainConfig
|
|||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider ProviderTencentCloud) updateDNSRecord(domain string, recordID float64, domainConfig *DomainConfig, isIPv4 bool) bool {
|
func (provider *ProviderTencentCloud) updateDNSRecord(domain string, recordID float64, domainConfig *DomainConfig, isIPv4 bool) bool {
|
||||||
var ipType = "A"
|
var ipType = "A"
|
||||||
var ipAddr = domainConfig.Ipv4Addr
|
var ipAddr = domainConfig.Ipv4Addr
|
||||||
if !isIPv4 {
|
if !isIPv4 {
|
||||||
@@ -141,7 +141,7 @@ func (provider ProviderTencentCloud) updateDNSRecord(domain string, recordID flo
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 以下为辅助方法,如发送 HTTP 请求等
|
// 以下为辅助方法,如发送 HTTP 请求等
|
||||||
func (provider ProviderTencentCloud) sendRequest(action string, data []byte) ([]byte, error) {
|
func (provider *ProviderTencentCloud) sendRequest(action string, data []byte) ([]byte, error) {
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
req, err := http.NewRequest("POST", url, bytes.NewBuffer(data))
|
req, err := http.NewRequest("POST", url, bytes.NewBuffer(data))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -173,18 +173,18 @@ func (provider ProviderTencentCloud) sendRequest(action string, data []byte) ([]
|
|||||||
|
|
||||||
// https://github.com/jeessy2/ddns-go/blob/master/util/tencent_cloud_signer.go
|
// https://github.com/jeessy2/ddns-go/blob/master/util/tencent_cloud_signer.go
|
||||||
|
|
||||||
func (provider ProviderTencentCloud) sha256hex(s string) string {
|
func (provider *ProviderTencentCloud) sha256hex(s string) string {
|
||||||
b := sha256.Sum256([]byte(s))
|
b := sha256.Sum256([]byte(s))
|
||||||
return hex.EncodeToString(b[:])
|
return hex.EncodeToString(b[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider ProviderTencentCloud) hmacsha256(s, key string) string {
|
func (provider *ProviderTencentCloud) hmacsha256(s, key string) string {
|
||||||
hashed := hmac.New(sha256.New, []byte(key))
|
hashed := hmac.New(sha256.New, []byte(key))
|
||||||
hashed.Write([]byte(s))
|
hashed.Write([]byte(s))
|
||||||
return string(hashed.Sum(nil))
|
return string(hashed.Sum(nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider ProviderTencentCloud) WriteString(strs ...string) string {
|
func (provider *ProviderTencentCloud) WriteString(strs ...string) string {
|
||||||
var b strings.Builder
|
var b strings.Builder
|
||||||
for _, str := range strs {
|
for _, str := range strs {
|
||||||
b.WriteString(str)
|
b.WriteString(str)
|
||||||
@@ -193,7 +193,7 @@ func (provider ProviderTencentCloud) WriteString(strs ...string) string {
|
|||||||
return b.String()
|
return b.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider ProviderTencentCloud) signRequest(secretId string, secretKey string, r *http.Request, action string, payload string) {
|
func (provider *ProviderTencentCloud) signRequest(secretId string, secretKey string, r *http.Request, action string, payload string) {
|
||||||
algorithm := "TC3-HMAC-SHA256"
|
algorithm := "TC3-HMAC-SHA256"
|
||||||
service := "dnspod"
|
service := "dnspod"
|
||||||
host := provider.WriteString(service, ".tencentcloudapi.com")
|
host := provider.WriteString(service, ".tencentcloudapi.com")
|
||||||
|
|||||||
+1
-1
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package grpcx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
"github.com/naiba/nezha/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ io.ReadWriteCloser = &IOStreamWrapper{}
|
||||||
|
|
||||||
|
type IOStream interface {
|
||||||
|
Recv() (*proto.IOStreamData, error)
|
||||||
|
Send(*proto.IOStreamData) error
|
||||||
|
Context() context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
type IOStreamWrapper struct {
|
||||||
|
IOStream
|
||||||
|
dataBuf []byte
|
||||||
|
closed *atomic.Bool
|
||||||
|
closeCh chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIOStreamWrapper(stream IOStream) *IOStreamWrapper {
|
||||||
|
return &IOStreamWrapper{
|
||||||
|
IOStream: stream,
|
||||||
|
closeCh: make(chan struct{}),
|
||||||
|
closed: new(atomic.Bool),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (iw *IOStreamWrapper) Read(p []byte) (n int, err error) {
|
||||||
|
if len(iw.dataBuf) > 0 {
|
||||||
|
n := copy(p, iw.dataBuf)
|
||||||
|
iw.dataBuf = iw.dataBuf[n:]
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
var data *proto.IOStreamData
|
||||||
|
if data, err = iw.Recv(); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
n = copy(p, data.Data)
|
||||||
|
if n < len(data.Data) {
|
||||||
|
iw.dataBuf = data.Data[n:]
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (iw *IOStreamWrapper) Write(p []byte) (n int, err error) {
|
||||||
|
err = iw.Send(&proto.IOStreamData{Data: p})
|
||||||
|
return len(p), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (iw *IOStreamWrapper) Close() error {
|
||||||
|
if iw.closed.CompareAndSwap(false, true) {
|
||||||
|
close(iw.closeCh)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (iw *IOStreamWrapper) Wait() {
|
||||||
|
<-iw.closeCh
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@ var adminPage = map[string]bool{
|
|||||||
"/monitor": true,
|
"/monitor": true,
|
||||||
"/setting": true,
|
"/setting": true,
|
||||||
"/notification": true,
|
"/notification": true,
|
||||||
|
"/nat": true,
|
||||||
"/cron": true,
|
"/cron": true,
|
||||||
"/api": true,
|
"/api": true,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package general
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/naiba/nezha/model"
|
||||||
|
"github.com/naiba/nezha/service/singleton"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserInfo struct {
|
||||||
|
Sub string `json:"sub"`
|
||||||
|
Username string `json:"preferred_username"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Groups []string `json:"groups,omitempty"`
|
||||||
|
Roles []string `json:"roles,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u UserInfo) MapToNezhaUser(loginClaim string, groupClaim string, adminGroups []string, autoCreate bool) model.User {
|
||||||
|
var user model.User
|
||||||
|
var login string
|
||||||
|
var groups []string
|
||||||
|
var isAdmin bool
|
||||||
|
if loginClaim == "email" {
|
||||||
|
login = u.Email
|
||||||
|
} else if loginClaim == "preferred_username" {
|
||||||
|
login = u.Username
|
||||||
|
} else {
|
||||||
|
login = u.Sub
|
||||||
|
}
|
||||||
|
if groupClaim == "roles" {
|
||||||
|
groups = u.Roles
|
||||||
|
} else {
|
||||||
|
groups = u.Groups
|
||||||
|
}
|
||||||
|
// Check if user is admin
|
||||||
|
adminGroupSet := make(map[string]struct{}, len(adminGroups))
|
||||||
|
for _, adminGroup := range adminGroups {
|
||||||
|
adminGroupSet[adminGroup] = struct{}{}
|
||||||
|
}
|
||||||
|
for _, group := range groups {
|
||||||
|
if _, found := adminGroupSet[group]; found {
|
||||||
|
isAdmin = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result := singleton.DB.Where("login = ?", login).First(&user)
|
||||||
|
user.Login = login
|
||||||
|
user.Email = u.Email
|
||||||
|
user.Name = u.Name
|
||||||
|
user.SuperAdmin = isAdmin
|
||||||
|
if result.Error != nil && autoCreate {
|
||||||
|
singleton.DB.Create(&user)
|
||||||
|
} else if result.Error != nil {
|
||||||
|
return model.User{}
|
||||||
|
}
|
||||||
|
return user
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ io.ReadWriteCloser = &RequestWrapper{}
|
||||||
|
|
||||||
|
type RequestWrapper struct {
|
||||||
|
req *http.Request
|
||||||
|
reader *bytes.Buffer
|
||||||
|
writer net.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRequestWrapper(req *http.Request, writer gin.ResponseWriter) (*RequestWrapper, error) {
|
||||||
|
conn, _, err := writer.Hijack()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
if err = req.Write(buf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &RequestWrapper{
|
||||||
|
req: req,
|
||||||
|
reader: buf,
|
||||||
|
writer: conn,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rw *RequestWrapper) Read(p []byte) (int, error) {
|
||||||
|
count, err := rw.reader.Read(p)
|
||||||
|
if err == nil {
|
||||||
|
return count, nil
|
||||||
|
}
|
||||||
|
if err != io.EOF {
|
||||||
|
return count, err
|
||||||
|
}
|
||||||
|
// request 数据读完之后等待客户端断开连接或 grpc 超时
|
||||||
|
return rw.writer.Read(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rw *RequestWrapper) Write(p []byte) (int, error) {
|
||||||
|
return rw.writer.Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rw *RequestWrapper) Close() error {
|
||||||
|
rw.req.Body.Close()
|
||||||
|
rw.writer.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
+35
-10
@@ -1,25 +1,50 @@
|
|||||||
package websocketx
|
package websocketx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
"github.com/samber/lo"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var _ io.ReadWriteCloser = &Conn{}
|
||||||
|
|
||||||
type Conn struct {
|
type Conn struct {
|
||||||
*websocket.Conn
|
*websocket.Conn
|
||||||
writeLock sync.Mutex
|
writeLock *sync.Mutex
|
||||||
|
dataBuf []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conn *Conn) WriteMessage(msgType int, data []byte) error {
|
func NewConn(conn *websocket.Conn) *Conn {
|
||||||
|
return &Conn{Conn: conn, writeLock: new(sync.Mutex)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *Conn) Write(data []byte) (int, error) {
|
||||||
conn.writeLock.Lock()
|
conn.writeLock.Lock()
|
||||||
defer conn.writeLock.Unlock()
|
defer conn.writeLock.Unlock()
|
||||||
var err error
|
if err := conn.Conn.WriteMessage(websocket.BinaryMessage, data); err != nil {
|
||||||
lo.TryCatchWithErrorValue(func() error {
|
return 0, err
|
||||||
return conn.Conn.WriteMessage(msgType, data)
|
}
|
||||||
}, func(res any) {
|
return len(data), nil
|
||||||
err = res.(error)
|
}
|
||||||
})
|
|
||||||
return err
|
func (conn *Conn) Read(data []byte) (int, error) {
|
||||||
|
if len(conn.dataBuf) > 0 {
|
||||||
|
n := copy(data, conn.dataBuf)
|
||||||
|
conn.dataBuf = conn.dataBuf[n:]
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
mType, innerData, err := conn.Conn.ReadMessage()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
// 将文本消息转换为命令输入
|
||||||
|
if mType == websocket.TextMessage {
|
||||||
|
innerData = append([]byte{0}, innerData...)
|
||||||
|
}
|
||||||
|
n := copy(data, innerData)
|
||||||
|
if n < len(innerData) {
|
||||||
|
conn.dataBuf = innerData[n:]
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|||||||
+100
-32
@@ -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.34.1
|
// protoc-gen-go v1.34.2
|
||||||
// protoc v5.26.1
|
// protoc v5.27.1
|
||||||
// source: proto/nezha.proto
|
// source: proto/nezha.proto
|
||||||
|
|
||||||
package proto
|
package proto
|
||||||
@@ -582,6 +582,53 @@ func (x *Receipt) GetProced() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type IOStreamData struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *IOStreamData) Reset() {
|
||||||
|
*x = IOStreamData{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_proto_nezha_proto_msgTypes[6]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *IOStreamData) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*IOStreamData) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *IOStreamData) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_proto_nezha_proto_msgTypes[6]
|
||||||
|
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 IOStreamData.ProtoReflect.Descriptor instead.
|
||||||
|
func (*IOStreamData) Descriptor() ([]byte, []int) {
|
||||||
|
return file_proto_nezha_proto_rawDescGZIP(), []int{6}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *IOStreamData) GetData() []byte {
|
||||||
|
if x != nil {
|
||||||
|
return x.Data
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
var File_proto_nezha_proto protoreflect.FileDescriptor
|
var File_proto_nezha_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
var file_proto_nezha_proto_rawDesc = []byte{
|
var file_proto_nezha_proto_rawDesc = []byte{
|
||||||
@@ -663,21 +710,27 @@ var file_proto_nezha_proto_rawDesc = []byte{
|
|||||||
0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x73, 0x75,
|
0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x73, 0x75,
|
||||||
0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x22, 0x21, 0x0a, 0x07, 0x52, 0x65, 0x63, 0x65,
|
0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x22, 0x21, 0x0a, 0x07, 0x52, 0x65, 0x63, 0x65,
|
||||||
0x69, 0x70, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x64, 0x18, 0x01, 0x20,
|
0x69, 0x70, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x64, 0x18, 0x01, 0x20,
|
||||||
0x01, 0x28, 0x08, 0x52, 0x06, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x64, 0x32, 0xd6, 0x01, 0x0a, 0x0c,
|
0x01, 0x28, 0x08, 0x52, 0x06, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x64, 0x22, 0x22, 0x0a, 0x0c, 0x49,
|
||||||
0x4e, 0x65, 0x7a, 0x68, 0x61, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x33, 0x0a, 0x11,
|
0x4f, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x44, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x64,
|
||||||
0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x53, 0x74, 0x61, 0x74,
|
0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x32,
|
||||||
0x65, 0x12, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x1a,
|
0x92, 0x02, 0x0a, 0x0c, 0x4e, 0x65, 0x7a, 0x68, 0x61, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
|
||||||
0x0e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x22,
|
0x12, 0x33, 0x0a, 0x11, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d,
|
||||||
0x00, 0x12, 0x31, 0x0a, 0x10, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x79, 0x73, 0x74, 0x65,
|
0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x74,
|
||||||
0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x0b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x48, 0x6f,
|
0x61, 0x74, 0x65, 0x1a, 0x0e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x63, 0x65,
|
||||||
0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x63, 0x65, 0x69,
|
0x69, 0x70, 0x74, 0x22, 0x00, 0x12, 0x31, 0x0a, 0x10, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x53,
|
||||||
0x70, 0x74, 0x22, 0x00, 0x12, 0x31, 0x0a, 0x0a, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x54, 0x61,
|
0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x0b, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||||
0x73, 0x6b, 0x12, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x52,
|
0x6f, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52,
|
||||||
0x65, 0x73, 0x75, 0x6c, 0x74, 0x1a, 0x0e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65,
|
0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x22, 0x00, 0x12, 0x31, 0x0a, 0x0a, 0x52, 0x65, 0x70, 0x6f,
|
||||||
0x63, 0x65, 0x69, 0x70, 0x74, 0x22, 0x00, 0x12, 0x2b, 0x0a, 0x0b, 0x52, 0x65, 0x71, 0x75, 0x65,
|
0x72, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54,
|
||||||
0x73, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x0b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x48,
|
0x61, 0x73, 0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x1a, 0x0e, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||||
0x6f, 0x73, 0x74, 0x1a, 0x0b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x61, 0x73, 0x6b,
|
0x6f, 0x2e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x22, 0x00, 0x12, 0x2b, 0x0a, 0x0b, 0x52,
|
||||||
0x22, 0x00, 0x30, 0x01, 0x42, 0x09, 0x5a, 0x07, 0x2e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
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, 0x12, 0x3a, 0x0a, 0x08, 0x49, 0x4f, 0x53, 0x74,
|
||||||
|
0x72, 0x65, 0x61, 0x6d, 0x12, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x49, 0x4f, 0x53,
|
||||||
|
0x74, 0x72, 0x65, 0x61, 0x6d, 0x44, 0x61, 0x74, 0x61, 0x1a, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||||
|
0x6f, 0x2e, 0x49, 0x4f, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x44, 0x61, 0x74, 0x61, 0x22, 0x00,
|
||||||
|
0x28, 0x01, 0x30, 0x01, 0x42, 0x09, 0x5a, 0x07, 0x2e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
||||||
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -693,14 +746,15 @@ 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, 6)
|
var file_proto_nezha_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
|
||||||
var file_proto_nezha_proto_goTypes = []interface{}{
|
var file_proto_nezha_proto_goTypes = []any{
|
||||||
(*Host)(nil), // 0: proto.Host
|
(*Host)(nil), // 0: proto.Host
|
||||||
(*State)(nil), // 1: proto.State
|
(*State)(nil), // 1: proto.State
|
||||||
(*State_SensorTemperature)(nil), // 2: proto.State_SensorTemperature
|
(*State_SensorTemperature)(nil), // 2: proto.State_SensorTemperature
|
||||||
(*Task)(nil), // 3: proto.Task
|
(*Task)(nil), // 3: proto.Task
|
||||||
(*TaskResult)(nil), // 4: proto.TaskResult
|
(*TaskResult)(nil), // 4: proto.TaskResult
|
||||||
(*Receipt)(nil), // 5: proto.Receipt
|
(*Receipt)(nil), // 5: proto.Receipt
|
||||||
|
(*IOStreamData)(nil), // 6: proto.IOStreamData
|
||||||
}
|
}
|
||||||
var file_proto_nezha_proto_depIdxs = []int32{
|
var file_proto_nezha_proto_depIdxs = []int32{
|
||||||
2, // 0: proto.State.temperatures:type_name -> proto.State_SensorTemperature
|
2, // 0: proto.State.temperatures:type_name -> proto.State_SensorTemperature
|
||||||
@@ -708,12 +762,14 @@ var file_proto_nezha_proto_depIdxs = []int32{
|
|||||||
0, // 2: proto.NezhaService.ReportSystemInfo:input_type -> proto.Host
|
0, // 2: proto.NezhaService.ReportSystemInfo:input_type -> proto.Host
|
||||||
4, // 3: proto.NezhaService.ReportTask:input_type -> proto.TaskResult
|
4, // 3: proto.NezhaService.ReportTask:input_type -> proto.TaskResult
|
||||||
0, // 4: proto.NezhaService.RequestTask:input_type -> proto.Host
|
0, // 4: proto.NezhaService.RequestTask:input_type -> proto.Host
|
||||||
5, // 5: proto.NezhaService.ReportSystemState:output_type -> proto.Receipt
|
6, // 5: proto.NezhaService.IOStream:input_type -> proto.IOStreamData
|
||||||
5, // 6: proto.NezhaService.ReportSystemInfo:output_type -> proto.Receipt
|
5, // 6: proto.NezhaService.ReportSystemState:output_type -> proto.Receipt
|
||||||
5, // 7: proto.NezhaService.ReportTask:output_type -> proto.Receipt
|
5, // 7: proto.NezhaService.ReportSystemInfo:output_type -> proto.Receipt
|
||||||
3, // 8: proto.NezhaService.RequestTask:output_type -> proto.Task
|
5, // 8: proto.NezhaService.ReportTask:output_type -> proto.Receipt
|
||||||
5, // [5:9] is the sub-list for method output_type
|
3, // 9: proto.NezhaService.RequestTask:output_type -> proto.Task
|
||||||
1, // [1:5] is the sub-list for method input_type
|
6, // 10: proto.NezhaService.IOStream:output_type -> proto.IOStreamData
|
||||||
|
6, // [6:11] is the sub-list for method output_type
|
||||||
|
1, // [1:6] is the sub-list for method input_type
|
||||||
1, // [1:1] is the sub-list for extension type_name
|
1, // [1:1] is the sub-list for extension type_name
|
||||||
1, // [1:1] is the sub-list for extension extendee
|
1, // [1:1] is the sub-list for extension extendee
|
||||||
0, // [0:1] is the sub-list for field type_name
|
0, // [0:1] is the sub-list for field type_name
|
||||||
@@ -725,7 +781,7 @@ func file_proto_nezha_proto_init() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !protoimpl.UnsafeEnabled {
|
if !protoimpl.UnsafeEnabled {
|
||||||
file_proto_nezha_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
file_proto_nezha_proto_msgTypes[0].Exporter = func(v any, i int) any {
|
||||||
switch v := v.(*Host); i {
|
switch v := v.(*Host); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
@@ -737,7 +793,7 @@ func file_proto_nezha_proto_init() {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_proto_nezha_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
file_proto_nezha_proto_msgTypes[1].Exporter = func(v any, i int) any {
|
||||||
switch v := v.(*State); i {
|
switch v := v.(*State); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
@@ -749,7 +805,7 @@ func file_proto_nezha_proto_init() {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_proto_nezha_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
file_proto_nezha_proto_msgTypes[2].Exporter = func(v any, i int) any {
|
||||||
switch v := v.(*State_SensorTemperature); i {
|
switch v := v.(*State_SensorTemperature); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
@@ -761,7 +817,7 @@ func file_proto_nezha_proto_init() {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_proto_nezha_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
file_proto_nezha_proto_msgTypes[3].Exporter = func(v any, i int) any {
|
||||||
switch v := v.(*Task); i {
|
switch v := v.(*Task); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
@@ -773,7 +829,7 @@ func file_proto_nezha_proto_init() {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_proto_nezha_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
|
file_proto_nezha_proto_msgTypes[4].Exporter = func(v any, i int) any {
|
||||||
switch v := v.(*TaskResult); i {
|
switch v := v.(*TaskResult); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
@@ -785,7 +841,7 @@ func file_proto_nezha_proto_init() {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_proto_nezha_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
|
file_proto_nezha_proto_msgTypes[5].Exporter = func(v any, i int) any {
|
||||||
switch v := v.(*Receipt); i {
|
switch v := v.(*Receipt); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
@@ -797,6 +853,18 @@ func file_proto_nezha_proto_init() {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
file_proto_nezha_proto_msgTypes[6].Exporter = func(v any, i int) any {
|
||||||
|
switch v := v.(*IOStreamData); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
type x struct{}
|
type x struct{}
|
||||||
out := protoimpl.TypeBuilder{
|
out := protoimpl.TypeBuilder{
|
||||||
@@ -804,7 +872,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: 6,
|
NumMessages: 7,
|
||||||
NumExtensions: 0,
|
NumExtensions: 0,
|
||||||
NumServices: 1,
|
NumServices: 1,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ service NezhaService {
|
|||||||
rpc ReportSystemInfo(Host)returns(Receipt){}
|
rpc ReportSystemInfo(Host)returns(Receipt){}
|
||||||
rpc ReportTask(TaskResult)returns(Receipt){}
|
rpc ReportTask(TaskResult)returns(Receipt){}
|
||||||
rpc RequestTask(Host)returns(stream Task){}
|
rpc RequestTask(Host)returns(stream Task){}
|
||||||
|
rpc IOStream(stream IOStreamData)returns(stream IOStreamData){}
|
||||||
}
|
}
|
||||||
|
|
||||||
message Host {
|
message Host {
|
||||||
@@ -68,3 +69,7 @@ message TaskResult {
|
|||||||
message Receipt{
|
message Receipt{
|
||||||
bool proced = 1;
|
bool proced = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message IOStreamData {
|
||||||
|
bytes data = 1;
|
||||||
|
}
|
||||||
|
|||||||
+70
-1
@@ -1,7 +1,7 @@
|
|||||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// - protoc-gen-go-grpc v1.3.0
|
// - protoc-gen-go-grpc v1.3.0
|
||||||
// - protoc v3.21.12
|
// - protoc v5.27.1
|
||||||
// source: proto/nezha.proto
|
// source: proto/nezha.proto
|
||||||
|
|
||||||
package proto
|
package proto
|
||||||
@@ -23,6 +23,7 @@ const (
|
|||||||
NezhaService_ReportSystemInfo_FullMethodName = "/proto.NezhaService/ReportSystemInfo"
|
NezhaService_ReportSystemInfo_FullMethodName = "/proto.NezhaService/ReportSystemInfo"
|
||||||
NezhaService_ReportTask_FullMethodName = "/proto.NezhaService/ReportTask"
|
NezhaService_ReportTask_FullMethodName = "/proto.NezhaService/ReportTask"
|
||||||
NezhaService_RequestTask_FullMethodName = "/proto.NezhaService/RequestTask"
|
NezhaService_RequestTask_FullMethodName = "/proto.NezhaService/RequestTask"
|
||||||
|
NezhaService_IOStream_FullMethodName = "/proto.NezhaService/IOStream"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NezhaServiceClient is the client API for NezhaService service.
|
// NezhaServiceClient is the client API for NezhaService service.
|
||||||
@@ -33,6 +34,7 @@ type NezhaServiceClient interface {
|
|||||||
ReportSystemInfo(ctx context.Context, in *Host, opts ...grpc.CallOption) (*Receipt, error)
|
ReportSystemInfo(ctx context.Context, in *Host, opts ...grpc.CallOption) (*Receipt, error)
|
||||||
ReportTask(ctx context.Context, in *TaskResult, opts ...grpc.CallOption) (*Receipt, error)
|
ReportTask(ctx context.Context, in *TaskResult, opts ...grpc.CallOption) (*Receipt, error)
|
||||||
RequestTask(ctx context.Context, in *Host, opts ...grpc.CallOption) (NezhaService_RequestTaskClient, error)
|
RequestTask(ctx context.Context, in *Host, opts ...grpc.CallOption) (NezhaService_RequestTaskClient, error)
|
||||||
|
IOStream(ctx context.Context, opts ...grpc.CallOption) (NezhaService_IOStreamClient, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type nezhaServiceClient struct {
|
type nezhaServiceClient struct {
|
||||||
@@ -102,6 +104,37 @@ func (x *nezhaServiceRequestTaskClient) Recv() (*Task, error) {
|
|||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *nezhaServiceClient) IOStream(ctx context.Context, opts ...grpc.CallOption) (NezhaService_IOStreamClient, error) {
|
||||||
|
stream, err := c.cc.NewStream(ctx, &NezhaService_ServiceDesc.Streams[1], NezhaService_IOStream_FullMethodName, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
x := &nezhaServiceIOStreamClient{stream}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type NezhaService_IOStreamClient interface {
|
||||||
|
Send(*IOStreamData) error
|
||||||
|
Recv() (*IOStreamData, error)
|
||||||
|
grpc.ClientStream
|
||||||
|
}
|
||||||
|
|
||||||
|
type nezhaServiceIOStreamClient struct {
|
||||||
|
grpc.ClientStream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *nezhaServiceIOStreamClient) Send(m *IOStreamData) error {
|
||||||
|
return x.ClientStream.SendMsg(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *nezhaServiceIOStreamClient) Recv() (*IOStreamData, error) {
|
||||||
|
m := new(IOStreamData)
|
||||||
|
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
// NezhaServiceServer is the server API for NezhaService service.
|
// NezhaServiceServer is the server API for NezhaService service.
|
||||||
// All implementations should embed UnimplementedNezhaServiceServer
|
// All implementations should embed UnimplementedNezhaServiceServer
|
||||||
// for forward compatibility
|
// for forward compatibility
|
||||||
@@ -110,6 +143,7 @@ type NezhaServiceServer interface {
|
|||||||
ReportSystemInfo(context.Context, *Host) (*Receipt, error)
|
ReportSystemInfo(context.Context, *Host) (*Receipt, error)
|
||||||
ReportTask(context.Context, *TaskResult) (*Receipt, error)
|
ReportTask(context.Context, *TaskResult) (*Receipt, error)
|
||||||
RequestTask(*Host, NezhaService_RequestTaskServer) error
|
RequestTask(*Host, NezhaService_RequestTaskServer) error
|
||||||
|
IOStream(NezhaService_IOStreamServer) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnimplementedNezhaServiceServer should be embedded to have forward compatible implementations.
|
// UnimplementedNezhaServiceServer should be embedded to have forward compatible implementations.
|
||||||
@@ -128,6 +162,9 @@ func (UnimplementedNezhaServiceServer) ReportTask(context.Context, *TaskResult)
|
|||||||
func (UnimplementedNezhaServiceServer) RequestTask(*Host, NezhaService_RequestTaskServer) error {
|
func (UnimplementedNezhaServiceServer) RequestTask(*Host, NezhaService_RequestTaskServer) error {
|
||||||
return status.Errorf(codes.Unimplemented, "method RequestTask not implemented")
|
return status.Errorf(codes.Unimplemented, "method RequestTask not implemented")
|
||||||
}
|
}
|
||||||
|
func (UnimplementedNezhaServiceServer) IOStream(NezhaService_IOStreamServer) error {
|
||||||
|
return status.Errorf(codes.Unimplemented, "method IOStream not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
// UnsafeNezhaServiceServer may be embedded to opt out of forward compatibility for this service.
|
// UnsafeNezhaServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||||
// Use of this interface is not recommended, as added methods to NezhaServiceServer will
|
// Use of this interface is not recommended, as added methods to NezhaServiceServer will
|
||||||
@@ -215,6 +252,32 @@ func (x *nezhaServiceRequestTaskServer) Send(m *Task) error {
|
|||||||
return x.ServerStream.SendMsg(m)
|
return x.ServerStream.SendMsg(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func _NezhaService_IOStream_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||||
|
return srv.(NezhaServiceServer).IOStream(&nezhaServiceIOStreamServer{stream})
|
||||||
|
}
|
||||||
|
|
||||||
|
type NezhaService_IOStreamServer interface {
|
||||||
|
Send(*IOStreamData) error
|
||||||
|
Recv() (*IOStreamData, error)
|
||||||
|
grpc.ServerStream
|
||||||
|
}
|
||||||
|
|
||||||
|
type nezhaServiceIOStreamServer struct {
|
||||||
|
grpc.ServerStream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *nezhaServiceIOStreamServer) Send(m *IOStreamData) error {
|
||||||
|
return x.ServerStream.SendMsg(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *nezhaServiceIOStreamServer) Recv() (*IOStreamData, error) {
|
||||||
|
m := new(IOStreamData)
|
||||||
|
if err := x.ServerStream.RecvMsg(m); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
// NezhaService_ServiceDesc is the grpc.ServiceDesc for NezhaService service.
|
// NezhaService_ServiceDesc is the grpc.ServiceDesc for NezhaService service.
|
||||||
// It's only intended for direct use with grpc.RegisterService,
|
// It's only intended for direct use with grpc.RegisterService,
|
||||||
// and not to be introspected or modified (even as a copy)
|
// and not to be introspected or modified (even as a copy)
|
||||||
@@ -241,6 +304,12 @@ var NezhaService_ServiceDesc = grpc.ServiceDesc{
|
|||||||
Handler: _NezhaService_RequestTask_Handler,
|
Handler: _NezhaService_RequestTask_Handler,
|
||||||
ServerStreams: true,
|
ServerStreams: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
StreamName: "IOStream",
|
||||||
|
Handler: _NezhaService_IOStream_Handler,
|
||||||
|
ServerStreams: true,
|
||||||
|
ClientStreams: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Metadata: "proto/nezha.proto",
|
Metadata: "proto/nezha.proto",
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+3
@@ -648,3 +648,6 @@ other = "Disable Switch Template in Frontend"
|
|||||||
|
|
||||||
[ServersOnWorldMap]
|
[ServersOnWorldMap]
|
||||||
other = "Servers On World Map"
|
other = "Servers On World Map"
|
||||||
|
|
||||||
|
[NAT]
|
||||||
|
other = "NAT"
|
||||||
Vendored
+4
-1
@@ -647,4 +647,7 @@ other = "Temperatura"
|
|||||||
other = "Deshabilitar Cambio de Plantilla en Frontend"
|
other = "Deshabilitar Cambio de Plantilla en Frontend"
|
||||||
|
|
||||||
[ServersOnWorldMap]
|
[ServersOnWorldMap]
|
||||||
other = "Servidores en el mapa mundial"
|
other = "Servidores en el mapa mundial"
|
||||||
|
|
||||||
|
[NAT]
|
||||||
|
other = "NAT"
|
||||||
Vendored
+3
@@ -648,3 +648,6 @@ other = "禁止前台切换模板"
|
|||||||
|
|
||||||
[ServersOnWorldMap]
|
[ServersOnWorldMap]
|
||||||
other = "服务器世界分布图"
|
other = "服务器世界分布图"
|
||||||
|
|
||||||
|
[NAT]
|
||||||
|
other = "内网穿透"
|
||||||
Vendored
+3
@@ -648,3 +648,6 @@ other = "禁止前台切換主題"
|
|||||||
|
|
||||||
[ServersOnWorldMap]
|
[ServersOnWorldMap]
|
||||||
other = "伺服器世界分布圖"
|
other = "伺服器世界分布圖"
|
||||||
|
|
||||||
|
[NAT]
|
||||||
|
other = "NAT"
|
||||||
@@ -91,6 +91,7 @@ function showFormModal(modelSelector, formID, URL, getData) {
|
|||||||
item.name.endsWith("_id") ||
|
item.name.endsWith("_id") ||
|
||||||
item.name === "id" ||
|
item.name === "id" ||
|
||||||
item.name === "ID" ||
|
item.name === "ID" ||
|
||||||
|
item.name === "ServerID" ||
|
||||||
item.name === "RequestType" ||
|
item.name === "RequestType" ||
|
||||||
item.name === "RequestMethod" ||
|
item.name === "RequestMethod" ||
|
||||||
item.name === "TriggerMode" ||
|
item.name === "TriggerMode" ||
|
||||||
@@ -255,6 +256,28 @@ function addOrEditNotification(notification) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addOrEditNAT(nat) {
|
||||||
|
const modal = $(".nat.modal");
|
||||||
|
modal.children(".header").text((nat ? LANG.Edit : LANG.Add));
|
||||||
|
modal
|
||||||
|
.find(".nezha-primary-btn.button")
|
||||||
|
.html(
|
||||||
|
nat
|
||||||
|
? LANG.Edit + '<i class="edit icon"></i>'
|
||||||
|
: LANG.Add + '<i class="add icon"></i>'
|
||||||
|
);
|
||||||
|
modal.find("input[name=ID]").val(nat ? nat.ID : null);
|
||||||
|
modal.find("input[name=ServerID]").val(nat ? nat.ServerID : null);
|
||||||
|
modal.find("input[name=Name]").val(nat ? nat.Name : null);
|
||||||
|
modal.find("input[name=Host]").val(nat ? nat.Host : null);
|
||||||
|
modal.find("input[name=Domain]").val(nat ? nat.Domain : null);
|
||||||
|
showFormModal(
|
||||||
|
".nat.modal",
|
||||||
|
"#natForm",
|
||||||
|
"/api/nat"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function connectToServer(id) {
|
function connectToServer(id) {
|
||||||
post('/terminal', { Host: window.location.host, Protocol: window.location.protocol, ID: id })
|
post('/terminal', { Host: window.location.host, Protocol: window.location.protocol, ID: id })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -234,6 +234,19 @@ body {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*加快Agent详情下拉展示速度*/
|
||||||
|
.collapsing{
|
||||||
|
-webkit-transition-property:height,visibility;
|
||||||
|
-o-transition-property:height,visibility;
|
||||||
|
transition-property:height,visibility;
|
||||||
|
-webkit-transition-duration:.15s;
|
||||||
|
-o-transition-duration:.15s;
|
||||||
|
transition-duration:.15s;
|
||||||
|
-webkit-transition-timing-function: linear;
|
||||||
|
-o-transition-timing-function: linear;
|
||||||
|
transition-timing-function: linear
|
||||||
|
}
|
||||||
|
|
||||||
/*正文结束*/
|
/*正文结束*/
|
||||||
|
|
||||||
/* 服务页 正文*/
|
/* 服务页 正文*/
|
||||||
|
|||||||
+35
-50
@@ -7,6 +7,7 @@ const mixinsVue = {
|
|||||||
showGoTop: false,
|
showGoTop: false,
|
||||||
preferredTemplate: null,
|
preferredTemplate: null,
|
||||||
isMobile: false,
|
isMobile: false,
|
||||||
|
staticUrl: '/static/theme-server-status',
|
||||||
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' },
|
||||||
@@ -15,8 +16,8 @@ const mixinsVue = {
|
|||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.isMobile = this.checkIsMobile();
|
this.isMobile = this.checkIsMobile();
|
||||||
this.initTheme();
|
this.theme = this.initTheme();
|
||||||
this.storedShowGroup();
|
this.showGroup = this.initShowGroup();
|
||||||
this.preferredTemplate = this.getCookie('preferred_theme') ? this.getCookie('preferred_theme') : this.$root.defaultTemplate;
|
this.preferredTemplate = this.getCookie('preferred_theme') ? this.getCookie('preferred_theme') : this.$root.defaultTemplate;
|
||||||
window.addEventListener('scroll', this.handleScroll);
|
window.addEventListener('scroll', this.handleScroll);
|
||||||
},
|
},
|
||||||
@@ -24,17 +25,6 @@ const mixinsVue = {
|
|||||||
window.removeEventListener('scroll', this.handleScroll);
|
window.removeEventListener('scroll', this.handleScroll);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
toggleView() {
|
|
||||||
this.showGroup = !this.showGroup;
|
|
||||||
localStorage.setItem("showGroup", JSON.stringify(this.showGroup));
|
|
||||||
return this.showGroup;
|
|
||||||
},
|
|
||||||
storedShowGroup() {
|
|
||||||
const storedShowGroup = localStorage.getItem("showGroup");
|
|
||||||
if (storedShowGroup !== null) {
|
|
||||||
this.showGroup = JSON.parse(storedShowGroup);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
toggleTemplate(template) {
|
toggleTemplate(template) {
|
||||||
if( template != this.preferredTemplate){
|
if( template != this.preferredTemplate){
|
||||||
this.preferredTemplate = template;
|
this.preferredTemplate = template;
|
||||||
@@ -42,6 +32,38 @@ const mixinsVue = {
|
|||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
initTheme() {
|
||||||
|
const storedTheme = localStorage.getItem("theme");
|
||||||
|
const theme = (storedTheme === 'dark' || storedTheme === 'light') ? storedTheme : (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
|
||||||
|
this.setTheme(theme);
|
||||||
|
return theme;
|
||||||
|
},
|
||||||
|
setTheme(theme) {
|
||||||
|
document.body.setAttribute("theme", theme);
|
||||||
|
this.theme = theme;
|
||||||
|
localStorage.setItem("theme", theme);
|
||||||
|
// 重新赋值全局调色
|
||||||
|
this.colors = this.theme == "dark" ? this.colorsDark : this.colorsLight;
|
||||||
|
|
||||||
|
if(this.$root.page == 'index') {
|
||||||
|
this.reloadCharts(); // 重新载入echarts图表
|
||||||
|
}
|
||||||
|
},
|
||||||
|
initShowGroup() {
|
||||||
|
const storedShowGroup = localStorage.getItem("showGroup");
|
||||||
|
const showGroup = storedShowGroup !== null ? JSON.parse(storedShowGroup) : false;
|
||||||
|
if (storedShowGroup === null) {
|
||||||
|
localStorage.setItem("showGroup", showGroup);
|
||||||
|
}
|
||||||
|
return showGroup;
|
||||||
|
},
|
||||||
|
toggleShowGroup() {
|
||||||
|
this.showGroup = !this.showGroup;
|
||||||
|
localStorage.setItem("showGroup", this.showGroup);
|
||||||
|
if (this.$root.page == 'service') {
|
||||||
|
this.$root.initTooltip();
|
||||||
|
}
|
||||||
|
},
|
||||||
updateCookie(name, value) {
|
updateCookie(name, value) {
|
||||||
document.cookie = name + "=" + value +"; path=/";
|
document.cookie = name + "=" + value +"; path=/";
|
||||||
},
|
},
|
||||||
@@ -57,43 +79,6 @@ const mixinsVue = {
|
|||||||
}
|
}
|
||||||
return cookieValue;
|
return cookieValue;
|
||||||
},
|
},
|
||||||
setTheme(title, store = false) {
|
|
||||||
this.theme = title;
|
|
||||||
document.body.setAttribute("theme", title);
|
|
||||||
if (store) {
|
|
||||||
localStorage.setItem("theme", title);
|
|
||||||
this.isSystemTheme = false;
|
|
||||||
if(this.$root.page == 'index') {
|
|
||||||
this.$root.reloadCharts(); //重新载入echarts图表
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setSystemTheme() {
|
|
||||||
localStorage.removeItem("theme");
|
|
||||||
this.initTheme();
|
|
||||||
this.isSystemTheme = true;
|
|
||||||
},
|
|
||||||
initTheme() {
|
|
||||||
const storeTheme = localStorage.getItem("theme");
|
|
||||||
if (storeTheme === 'dark' || storeTheme === 'light') {
|
|
||||||
this.setTheme(storeTheme, true);
|
|
||||||
} else {
|
|
||||||
this.isSystemTheme = true
|
|
||||||
const handleChange = (mediaQueryListEvent) => {
|
|
||||||
if (localStorage.getItem("theme")) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (mediaQueryListEvent.matches) {
|
|
||||||
this.setTheme('dark');
|
|
||||||
} else {
|
|
||||||
this.setTheme('light');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const mediaQueryListDark = window.matchMedia('(prefers-color-scheme: dark)');
|
|
||||||
this.setTheme(mediaQueryListDark.matches ? 'dark' : 'light');
|
|
||||||
mediaQueryListDark.addEventListener("change", handleChange);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
toFixed2(f) {
|
toFixed2(f) {
|
||||||
return f.toFixed(2)
|
return f.toFixed(2)
|
||||||
},
|
},
|
||||||
|
|||||||
+1277
File diff suppressed because it is too large
Load Diff
+243
File diff suppressed because one or more lines are too long
+242
File diff suppressed because one or more lines are too long
+245
File diff suppressed because one or more lines are too long
+244
File diff suppressed because one or more lines are too long
Vendored
+4
-5
@@ -6,17 +6,16 @@
|
|||||||
{{.Version}}</small>
|
{{.Version}}</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/jquery/3.6.0/jquery.min.js"></script>
|
<script src="https://unpkg.com/jquery@3.7.1/dist/jquery.min.js"></script>
|
||||||
<script src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/semantic-ui/2.4.1/semantic.min.js"></script>
|
<script src="https://unpkg.com/semantic-ui@2.4.0/dist/semantic.min.js"></script>
|
||||||
<script src="/static/semantic-ui-alerts.min.js"></script>
|
<script src="/static/semantic-ui-alerts.min.js"></script>
|
||||||
<script src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/vue/2.6.14/vue.min.js"></script>
|
<script src="https://unpkg.com/vue@2.6.14/dist/vue.min.js"></script>
|
||||||
<script src="/static/main.js?v20240330"></script>
|
<script src="/static/main.js?v20240714"></script>
|
||||||
<script>
|
<script>
|
||||||
(function () {
|
(function () {
|
||||||
updateLang({{.LANG }});
|
updateLang({{.LANG }});
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
{{end}}
|
{{end}}
|
||||||
Vendored
+4
-8
@@ -1,21 +1,17 @@
|
|||||||
{{define "common/header"}}
|
{{define "common/header"}}
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="{{.Conf.Language}}">
|
<html lang="{{.Conf.Language}}">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<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" type="text/css"
|
<link rel="shortcut icon" type="image/png" href="/static/logo.svg?v20210804" />
|
||||||
href="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/semantic-ui/2.4.1/semantic.min.css">
|
<link rel="stylesheet" type="text/css" href="https://unpkg.com/semantic-ui@2.4.0/dist/semantic.min.css">
|
||||||
<link href="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/font-logos/0.17/font-logos.min.css" type="text/css"
|
<link rel="stylesheet" type="text/css" href="https://unpkg.com/font-logos@0.17.0/assets/font-logos.css">
|
||||||
rel="stylesheet" />
|
|
||||||
<link rel="stylesheet" type="text/css" href="/static/semantic-ui-alerts.min.css">
|
<link rel="stylesheet" type="text/css" href="/static/semantic-ui-alerts.min.css">
|
||||||
<link rel="stylesheet" type="text/css" href="/static/main.css?v2022042314">
|
<link rel="stylesheet" type="text/css" href="/static/main.css?v2022042314">
|
||||||
<link rel="shortcut icon" type="image/png" href="/static/logo.svg?v20210804" />
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
{{end}}
|
{{end}}
|
||||||
Vendored
+1
@@ -9,6 +9,7 @@
|
|||||||
<a class='item{{if eq .MatchedPath "/monitor"}} active{{end}}' href="/monitor"><i class="rss icon"></i>{{tr "Services"}}</a>
|
<a class='item{{if eq .MatchedPath "/monitor"}} active{{end}}' href="/monitor"><i class="rss icon"></i>{{tr "Services"}}</a>
|
||||||
<a class='item{{if eq .MatchedPath "/cron"}} active{{end}}' href="/cron"><i class="clock icon"></i>{{tr "Task"}}</a>
|
<a class='item{{if eq .MatchedPath "/cron"}} active{{end}}' href="/cron"><i class="clock icon"></i>{{tr "Task"}}</a>
|
||||||
<a class='item{{if eq .MatchedPath "/notification"}} active{{end}}' href="/notification"><i class="bell icon"></i>{{tr "Notification"}}</a>
|
<a class='item{{if eq .MatchedPath "/notification"}} active{{end}}' href="/notification"><i class="bell icon"></i>{{tr "Notification"}}</a>
|
||||||
|
<a class='item{{if eq .MatchedPath "/nat"}} active{{end}}' href="/nat"><i class="exchange icon"></i>{{tr "NAT"}}</a>
|
||||||
<a class='item{{if eq .MatchedPath "/setting"}} active{{end}}' href="/setting">
|
<a class='item{{if eq .MatchedPath "/setting"}} active{{end}}' href="/setting">
|
||||||
<i class="settings icon"></i>{{tr "Settings"}}
|
<i class="settings icon"></i>{{tr "Settings"}}
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
Vendored
+31
@@ -0,0 +1,31 @@
|
|||||||
|
{{define "component/nat"}}
|
||||||
|
<div class="ui tiny nat modal transition hidden">
|
||||||
|
<div class="header">Add</div>
|
||||||
|
<div class="content">
|
||||||
|
<form id="natForm" class="ui form">
|
||||||
|
<input type="hidden" name="ID">
|
||||||
|
<div class="field">
|
||||||
|
<label>{{tr "Name"}}</label>
|
||||||
|
<input type="text" name="Name">
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>Agent ID</label>
|
||||||
|
<input type="number" name="ServerID" placeholder="1">
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>内网服务</label>
|
||||||
|
<input type="text" name="Host" placeholder="192.168.1.1:80(带端口)">
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>绑定域名</label>
|
||||||
|
<input type="text" name="Domain" placeholder="router.app.yourdomain.com">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="actions">
|
||||||
|
<div class="ui negative button">{{tr "Cancel"}}</div>
|
||||||
|
<button class="ui positive nezha-primary-btn right labeled icon button">{{tr "Confirm"}}<i class="checkmark icon"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
+1
-1
@@ -61,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}}
|
||||||
|
|||||||
+54
@@ -0,0 +1,54 @@
|
|||||||
|
{{define "dashboard-default/nat"}}
|
||||||
|
{{template "common/header" .}}
|
||||||
|
{{template "common/menu" .}}
|
||||||
|
<div class="nb-container">
|
||||||
|
<div class="ui container">
|
||||||
|
<div class="ui grid">
|
||||||
|
<div class="right floated right aligned twelve wide column">
|
||||||
|
<button class="ui right labeled nezha-primary-btn icon button" onclick="addOrEditNAT()"><i
|
||||||
|
class="add icon"></i> Add
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<table class="ui very basic table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>{{tr "Name"}}</th>
|
||||||
|
<th>Agent ID</th>
|
||||||
|
<th>内网服务</th>
|
||||||
|
<th>绑定域名</th>
|
||||||
|
<th>{{tr "Administration"}}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{range $item := .NAT}}
|
||||||
|
<tr>
|
||||||
|
<td>{{$item.ID}}</td>
|
||||||
|
<td>{{$item.Name}}</td>
|
||||||
|
<td>{{$item.ServerID}}</td>
|
||||||
|
<td>{{$item.Host}}</td>
|
||||||
|
<td>{{$item.Domain}}</td>
|
||||||
|
<td>
|
||||||
|
<div class="ui mini icon buttons">
|
||||||
|
<button class="ui button" onclick="addOrEditNAT({{$item}})">
|
||||||
|
<i class="edit icon"></i>
|
||||||
|
</button>
|
||||||
|
<button class="ui button"
|
||||||
|
onclick="showConfirm('确定删除NAT隧道?','确认删除',deleteRequest,'/api/nat/'+{{$item.ID}})">
|
||||||
|
<i class="trash alternate outline icon"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{end}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{template "component/nat"}}
|
||||||
|
{{template "common/footer" .}}
|
||||||
|
<script>
|
||||||
|
$('.checkbox').checkbox()
|
||||||
|
</script>
|
||||||
|
{{end}}
|
||||||
@@ -1,18 +1,15 @@
|
|||||||
{{define "dashboard-default/redirect"}}
|
{{define "dashboard-default/redirect"}}
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="{{.Conf.Language}}">
|
<html lang="{{.Conf.Language}}">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Redirecting..</title>
|
<title>Redirecting..</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<p>If you are not redirected, please click <a href="{{.URL}}">here</a>.</p>
|
<p>If you are not redirected, please click <a href="{{.URL}}">here</a>.</p>
|
||||||
<script>window.location.href = "{{.URL}}"</script>
|
<script>window.location.href = "{{.URL}}"</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
{{end}}
|
{{end}}
|
||||||
+1
-1
@@ -98,7 +98,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{{template "component/server" .}}
|
{{template "component/server" .}}
|
||||||
{{template "common/footer" .}}
|
{{template "common/footer" .}}
|
||||||
<script src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/clipboard.js/2.0.10/clipboard.min.js" type="application/javascript"></script>
|
<script src="https://unpkg.com/clipboard@2.0.11/dist/clipboard.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
var clipboard = new ClipboardJS('.ui.icon.green.mini.button');
|
var clipboard = new ClipboardJS('.ui.icon.green.mini.button');
|
||||||
const checkBoxList = document.querySelectorAll('tbody > tr > td > input.nezha-servers[type=checkbox]')
|
const checkBoxList = document.querySelectorAll('tbody > tr > td > input.nezha-servers[type=checkbox]')
|
||||||
|
|||||||
+4
-8
@@ -1,16 +1,14 @@
|
|||||||
{{define "dashboard-default/terminal"}}
|
{{define "dashboard-default/terminal"}}
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="{{.Conf.Language}}">
|
<html lang="{{.Conf.Language}}">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>tty@{{.ServerName}} - {{.Title}}</title>
|
<title>tty@{{.ServerName}} - {{.Title}}</title>
|
||||||
<link rel="shortcut icon" type="image/png" href="/static/logo.svg?v20210804" />
|
<link rel="shortcut icon" type="image/png" href="/static/logo.svg?v20210804" />
|
||||||
<link href="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/xterm/4.11.0/xterm.css" type="text/css" rel="stylesheet"/>
|
<link type="text/css" rel="stylesheet" href="https://unpkg.com/[email protected]/css/xterm.css" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
html,
|
html,
|
||||||
body,
|
body,
|
||||||
@@ -25,12 +23,11 @@
|
|||||||
background-color: black;
|
background-color: black;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<body onresize="onResize()">
|
<body onresize="onResize()">
|
||||||
<div id="terminal-container"></div>
|
<div id="terminal-container"></div>
|
||||||
<script src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/xterm/4.11.0/xterm.js" type="application/javascript"></script>
|
<script src="https://unpkg.com/[email protected]/lib/xterm.js"></script>
|
||||||
<script src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/xterm/4.11.0/addons/attach/xterm-addon-attach.js"></script>
|
<script src="https://unpkg.com/@xterm/addon-attach@0.11.0/lib/addon-attach.js"></script>
|
||||||
<script src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/xterm/4.11.0/addons/fit/xterm-addon-fit.js"></script>
|
<script src="https://unpkg.com/@xterm/addon-fit@0.10.0/lib/addon-fit.js"></script>
|
||||||
<script>
|
<script>
|
||||||
let sendResizing = false;
|
let sendResizing = false;
|
||||||
|
|
||||||
@@ -93,6 +90,5 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
{{end}}
|
{{end}}
|
||||||
+3
-1
@@ -24,7 +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 />
|
||||||
GPU: @#server.Host.GPU#@<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"}}:
|
||||||
|
|||||||
+5
-2
@@ -28,8 +28,10 @@
|
|||||||
<br />
|
<br />
|
||||||
CPU: @#server.Host.CPU#@
|
CPU: @#server.Host.CPU#@
|
||||||
<br />
|
<br />
|
||||||
GPU: @#server.Host.GPU#@
|
<template v-if="server.Host.GPU">
|
||||||
<br />
|
GPU: @#server.Host.GPU#@
|
||||||
|
<br />
|
||||||
|
</template>
|
||||||
{{tr "DiskUsed"}}:
|
{{tr "DiskUsed"}}:
|
||||||
@#formatByteSize(server.State.DiskUsed)#@/@#formatByteSize(server.Host.DiskTotal)#@
|
@#formatByteSize(server.State.DiskUsed)#@/@#formatByteSize(server.Host.DiskTotal)#@
|
||||||
<br />
|
<br />
|
||||||
@@ -178,6 +180,7 @@
|
|||||||
'TC0H', //CPU Heatsink 温度,代表 CPU 散热器的温度
|
'TC0H', //CPU Heatsink 温度,代表 CPU 散热器的温度
|
||||||
'TC0P', //CPU Proximity 温度,代表 CPU 附近的温度
|
'TC0P', //CPU Proximity 温度,代表 CPU 附近的温度
|
||||||
'k10temp', //AMD K10(Phenom、Athlon、Sempron 等)系列处理器的温度监测
|
'k10temp', //AMD K10(Phenom、Athlon、Sempron 等)系列处理器的温度监测
|
||||||
|
'k10temp_tctl', //AMD K10 (Athlon II、Phenom II 等)系列处理器的温度监测
|
||||||
'coretemp_package_id_0', //整个封装处理器温度
|
'coretemp_package_id_0', //整个封装处理器温度
|
||||||
'cpu_thermal_zone', //全志
|
'cpu_thermal_zone', //全志
|
||||||
'cpu-thermal', //树莓派(博通)
|
'cpu-thermal', //树莓派(博通)
|
||||||
|
|||||||
@@ -9,12 +9,12 @@
|
|||||||
<i @click="showMapChart" data-toggle="modal" data-target="#mapChartBox" class="bi bi-geo-alt"></i>
|
<i @click="showMapChart" data-toggle="modal" data-target="#mapChartBox" class="bi bi-geo-alt"></i>
|
||||||
</span>
|
</span>
|
||||||
<span class="toggleView">
|
<span class="toggleView">
|
||||||
<i v-if="showGroup" @click="toggleView" class="show-nogroup bi bi-justify"></i>
|
<i v-if="showGroup" @click="toggleShowGroup" class="show-nogroup bi bi-justify"></i>
|
||||||
<i v-else @click="toggleView" class="show-group bi bi-view-stacked"></i>
|
<i v-else @click="toggleShowGroup" class="show-group bi bi-view-stacked"></i>
|
||||||
</span>
|
</span>
|
||||||
<span class="setTheme">
|
<span class="setTheme">
|
||||||
<i v-if="theme === 'light'" @click="setTheme('dark', true)" class="setTheme-dark bi bi-moon-fill"></i>
|
<i v-if="theme === 'light'" @click="setTheme('dark')" class="setTheme-dark bi bi-moon-fill"></i>
|
||||||
<i v-else @click="setTheme('light', true)" class="setTheme-light bi bi-brightness-high-fill"></i>
|
<i v-else @click="setTheme('light')" class="setTheme-light bi bi-brightness-high-fill"></i>
|
||||||
</span>
|
</span>
|
||||||
<span v-if="showGoTop" class="showGoTop">
|
<span v-if="showGoTop" class="showGoTop">
|
||||||
<i @click="goTop" class="goTop bi bi-arrow-up"></i>
|
<i @click="goTop" class="goTop bi bi-arrow-up"></i>
|
||||||
|
|||||||
+2
-3
@@ -9,7 +9,7 @@
|
|||||||
<link rel="shortcut icon" type="image/png" href="/static/logo.svg" />
|
<link rel="shortcut icon" type="image/png" href="/static/logo.svg" />
|
||||||
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/css/bootstrap.min.css">
|
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/css/bootstrap.min.css">
|
||||||
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/css/bootstrap-theme.min.css">
|
<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/main.css?v20240712">
|
||||||
<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://unpkg.com/[email protected]/font/bootstrap-icons.min.css">
|
<link rel="stylesheet" href="https://unpkg.com/[email protected]/font/bootstrap-icons.min.css">
|
||||||
@@ -20,8 +20,7 @@
|
|||||||
<script src="https://unpkg.com/[email protected]/dist/js/bootstrap.min.js"></script>
|
<script src="https://unpkg.com/[email protected]/dist/js/bootstrap.min.js"></script>
|
||||||
<script src="https://unpkg.com/[email protected]/dist/vue.min.js"></script>
|
<script src="https://unpkg.com/[email protected]/dist/vue.min.js"></script>
|
||||||
<script src="https://unpkg.com/[email protected]/dist/echarts.min.js"></script>
|
<script src="https://unpkg.com/[email protected]/dist/echarts.min.js"></script>
|
||||||
<script src="https://unpkg.com/[email protected]/map/js/world.js"></script>
|
<script src="/static/theme-server-status/js/mixin.js?v20240711"></script>
|
||||||
<script src="/static/theme-server-status/js/mixin.js?v20240302"></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>
|
||||||
|
|||||||
@@ -75,7 +75,8 @@
|
|||||||
</span>
|
</span>
|
||||||
<span class="node-cell-expand" v-if="node.host.GPU">
|
<span class="node-cell-expand" v-if="node.host.GPU">
|
||||||
<span class="node-cell-expand-label">GPU:</span>
|
<span class="node-cell-expand-label">GPU:</span>
|
||||||
@#node.host.GPU.join(",")#@ (@#node.gpu.percent#@%)
|
@#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>
|
||||||
|
|||||||
@@ -78,7 +78,8 @@
|
|||||||
</span>
|
</span>
|
||||||
<span class="node-cell-expand" v-if="node.host.GPU">
|
<span class="node-cell-expand" v-if="node.host.GPU">
|
||||||
<span class="node-cell-expand-label">GPU:</span>
|
<span class="node-cell-expand-label">GPU:</span>
|
||||||
@#node.host.GPU.join(",")#@ (@#node.gpu.percent#@%)
|
@#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>
|
||||||
|
|||||||
+95
-59
File diff suppressed because one or more lines are too long
+117
-98
@@ -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,8 +210,9 @@
|
|||||||
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'
|
||||||
@@ -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
-8
@@ -65,15 +65,9 @@ pre_check() {
|
|||||||
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_Argu=" "
|
|
||||||
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="github.com"
|
GITHUB_URL="gitee.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
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
@@ -89,6 +83,9 @@ install_agent() {
|
|||||||
echo -e "Obtaining Agent version"
|
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')
|
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://gitee.com/api/v5/repos/naibahq/agent/releases/latest" | awk -F '"' '{for(i=1;i<=NF;i++){if($i=="tag_name"){print $(i+2)}}}')
|
||||||
|
fi
|
||||||
if [ ! -n "$version" ]; then
|
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')
|
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
|
fi
|
||||||
@@ -108,7 +105,12 @@ install_agent() {
|
|||||||
chmod -R 777 $NZ_AGENT_PATH
|
chmod -R 777 $NZ_AGENT_PATH
|
||||||
|
|
||||||
echo -e "Downloading Agent"
|
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 [[ -z $CN ]]; then
|
||||||
|
NZ_AGENT_URL="https://${GITHUB_URL}/nezhahq/agent/releases/download/${version}/nezha-agent_darwin_${os_arch}.zip"
|
||||||
|
else
|
||||||
|
NZ_AGENT_URL="https://${GITHUB_URL}/naibahq/agent/releases/download/${version}/nezha-agent_darwin_${os_arch}.zip"
|
||||||
|
fi
|
||||||
|
curl -o nezha-agent_darwin_${os_arch}.zip -L -f --retry 2 --retry-max-time 60 $NZ_AGENT_URL >/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}"
|
echo -e "${red}Fail to download agent, please check if the network can link ${GITHUB_URL}${plain}"
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
+1
-1
@@ -56,7 +56,7 @@ if($region -ne "CN"){
|
|||||||
$download = "https://github.com/$agentrepo/releases/download/$agenttag/$file"
|
$download = "https://github.com/$agentrepo/releases/download/$agenttag/$file"
|
||||||
Write-Host "Location:$region,connect directly!" -BackgroundColor DarkRed -ForegroundColor Green
|
Write-Host "Location:$region,connect directly!" -BackgroundColor DarkRed -ForegroundColor Green
|
||||||
}else{
|
}else{
|
||||||
$download = "https://github.com/$agentrepo/releases/download/$agenttag/$file"
|
$download = "https://gitee.com/naibahq/agent/releases/download/$agenttag/$file"
|
||||||
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
|
||||||
|
|||||||
+262
-212
@@ -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+ /
|
||||||
@@ -12,7 +12,7 @@ NZ_DASHBOARD_PATH="${NZ_BASE_PATH}/dashboard"
|
|||||||
NZ_AGENT_PATH="${NZ_BASE_PATH}/agent"
|
NZ_AGENT_PATH="${NZ_BASE_PATH}/agent"
|
||||||
NZ_DASHBOARD_SERVICE="/etc/systemd/system/nezha-dashboard.service"
|
NZ_DASHBOARD_SERVICE="/etc/systemd/system/nezha-dashboard.service"
|
||||||
NZ_DASHBOARD_SERVICERC="/etc/init.d/nezha-dashboard"
|
NZ_DASHBOARD_SERVICERC="/etc/init.d/nezha-dashboard"
|
||||||
NZ_VERSION="v0.16.3"
|
NZ_VERSION="v0.17.2"
|
||||||
|
|
||||||
red='\033[0;31m'
|
red='\033[0;31m'
|
||||||
green='\033[0;32m'
|
green='\033[0;32m'
|
||||||
@@ -21,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 "使用中国镜像"
|
||||||
@@ -61,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
|
||||||
@@ -77,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"
|
||||||
@@ -92,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="github.com"
|
GITHUB_URL="gitee.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"
|
||||||
@@ -103,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"
|
||||||
@@ -117,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"
|
||||||
@@ -131,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
|
||||||
@@ -152,7 +175,7 @@ select_version() {
|
|||||||
break
|
break
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "${red}请输入正确的数字 [1-2]${plain}"
|
err "请输入正确的选择 [1-2]"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
@@ -160,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
|
||||||
@@ -179,18 +202,18 @@ 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
|
||||||
@@ -213,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 "退出安装"
|
||||||
@@ -246,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
|
||||||
@@ -287,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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,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
|
||||||
}
|
}
|
||||||
@@ -309,11 +334,14 @@ 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
|
||||||
|
version=$(curl -m 10 -sL "https://gitee.com/api/v5/repos/naibahq/agent/releases/latest" | awk -F '"' '{for(i=1;i<=NF;i++){if($i=="tag_name"){print $(i+2)}}}')
|
||||||
|
fi
|
||||||
if [ ! -n "$version" ]; then
|
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')
|
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
|
fi
|
||||||
@@ -322,26 +350,31 @@ 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 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 [ -z "$CN" ]; then
|
||||||
if [[ $? != 0 ]]; then
|
NZ_AGENT_URL="https://${GITHUB_URL}/nezhahq/agent/releases/download/${version}/nezha-agent_linux_${os_arch}.zip"
|
||||||
echo -e "${red}Release 下载失败,请检查本机能否连接 ${GITHUB_URL}${plain}"
|
else
|
||||||
return 0
|
NZ_AGENT_URL="https://${GITHUB_URL}/naibahq/agent/releases/download/${version}/nezha-agent_linux_${os_arch}.zip"
|
||||||
|
fi
|
||||||
|
wget -t 2 -T 60 -O nezha-agent_linux_${os_arch}.zip $NZ_AGENT_URL >/dev/null 2>&1
|
||||||
|
if [ $? != 0 ]; then
|
||||||
|
err "Release 下载失败,请检查本机能否连接 ${GITHUB_URL}"
|
||||||
|
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 "$@"
|
||||||
@@ -349,27 +382,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 [ $# -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
|
||||||
@@ -382,14 +419,14 @@ modify_agent_config() {
|
|||||||
fi
|
fi
|
||||||
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
|
sudo ${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
|
if [ $? -ne 0 ]; then
|
||||||
${NZ_AGENT_PATH}/nezha-agent service uninstall >/dev/null 2>&1
|
sudo ${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
|
sudo ${NZ_AGENT_PATH}/nezha-agent service install -s "$nz_grpc_host:$nz_grpc_port" -p $nz_client_secret $args >/dev/null 2>&1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo -e "Agent配置 ${green}修改成功,请稍等重启生效${plain}"
|
printf "Agent配置 ${green}修改成功,请稍等重启生效${plain}\n"
|
||||||
|
|
||||||
#if [[ $# == 0 ]]; then
|
#if [[ $# == 0 ]]; then
|
||||||
# before_show_menu
|
# before_show_menu
|
||||||
@@ -397,46 +434,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 60 -O /tmp/nezha-docker-compose.yaml https://${GITHUB_RAW_URL}/script/docker-compose.yaml >/dev/null 2>&1
|
wget -t 2 -T 60 -O /tmp/nezha-docker-compose.yaml https://${GITHUB_RAW_URL}/script/docker-compose.yaml >/dev/null 2>&1
|
||||||
if [[ $? != 0 ]]; then
|
if [ $? != 0 ]; then
|
||||||
echo -e "${red}下载脚本失败,请检查本机能否连接 ${GITHUB_RAW_URL}${plain}"
|
err "下载脚本失败,请检查本机能否连接 ${GITHUB_RAW_URL}"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
wget -t 2 -T 60 -O /tmp/nezha-config.yaml https://${GITHUB_RAW_URL}/script/config.yaml >/dev/null 2>&1
|
wget -t 2 -T 60 -O /tmp/nezha-config.yaml https://${GITHUB_RAW_URL}/script/config.yaml >/dev/null 2>&1
|
||||||
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
|
||||||
|
|
||||||
@@ -447,260 +491,266 @@ 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 60 -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 60 -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
|
if [ -z "$CN" ]; then
|
||||||
|
NZ_DASHBOARD_URL="https://${GITHUB_URL}/naiba/nezha/releases/latest/download/dashboard-linux-$os_arch.zip"
|
||||||
|
else
|
||||||
|
NZ_DASHBOARD_URL="https://${GITHUB_URL}/naibahq/nezha/releases/latest/download/dashboard-linux-$os_arch.zip"
|
||||||
|
fi
|
||||||
|
|
||||||
|
sudo wget -qO $NZ_DASHBOARD_PATH/app.zip $NZ_DASHBOARD_URL >/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"
|
||||||
|
|
||||||
${NZ_AGENT_PATH}/nezha-agent service uninstall
|
sudo ${NZ_AGENT_PATH}/nezha-agent service uninstall
|
||||||
|
|
||||||
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"
|
||||||
|
|
||||||
${NZ_AGENT_PATH}/nezha-agent service restart
|
sudo ${NZ_AGENT_PATH}/nezha-agent service restart
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -726,7 +776,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} 安装面板端
|
||||||
@@ -747,7 +797,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
|
||||||
@@ -792,7 +842,7 @@ show_menu() {
|
|||||||
update_script
|
update_script
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo -e "${red}请输入正确的数字 [0-13]${plain}"
|
err "请输入正确的数字 [0-13]"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
@@ -800,7 +850,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
|
||||||
|
|||||||
+263
-215
@@ -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+ /
|
||||||
@@ -12,7 +12,7 @@ NZ_DASHBOARD_PATH="${NZ_BASE_PATH}/dashboard"
|
|||||||
NZ_AGENT_PATH="${NZ_BASE_PATH}/agent"
|
NZ_AGENT_PATH="${NZ_BASE_PATH}/agent"
|
||||||
NZ_DASHBOARD_SERVICE="/etc/systemd/system/nezha-dashboard.service"
|
NZ_DASHBOARD_SERVICE="/etc/systemd/system/nezha-dashboard.service"
|
||||||
NZ_DASHBOARD_SERVICERC="/etc/init.d/nezha-dashboard"
|
NZ_DASHBOARD_SERVICERC="/etc/init.d/nezha-dashboard"
|
||||||
NZ_VERSION="v0.16.3"
|
NZ_VERSION="v0.17.2"
|
||||||
|
|
||||||
red='\033[0;31m'
|
red='\033[0;31m'
|
||||||
green='\033[0;32m'
|
green='\033[0;32m'
|
||||||
@@ -21,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"
|
||||||
@@ -56,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
|
||||||
@@ -70,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"
|
||||||
@@ -91,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="github.com"
|
GITHUB_URL="gitee.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"
|
||||||
@@ -102,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."
|
||||||
@@ -115,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."
|
||||||
@@ -128,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
|
||||||
@@ -149,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
|
||||||
@@ -157,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
|
||||||
@@ -176,18 +201,18 @@ 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)
|
(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
|
||||||
@@ -210,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."
|
||||||
@@ -242,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
|
||||||
@@ -283,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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,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 running,closing 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
|
||||||
}
|
}
|
||||||
@@ -305,11 +333,14 @@ 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 number"
|
||||||
|
|
||||||
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
|
||||||
|
version=$(curl -m 10 -sL "https://gitee.com/api/v5/repos/naibahq/agent/releases/latest" | awk -F '"' '{for(i=1;i<=NF;i++){if($i=="tag_name"){print $(i+2)}}}')
|
||||||
|
fi
|
||||||
if [ ! -n "$version" ]; then
|
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')
|
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
|
fi
|
||||||
@@ -318,26 +349,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 60 -O nezha-agent_linux_${os_arch}.zip https://${GITHUB_URL}/nezhahq/agent/releases/download/${version}/nezha-agent_linux_${os_arch}.zip >/dev/null 2>&1
|
wget -t 2 -T 60 -O nezha-agent_linux_${os_arch}.zip https://${GITHUB_URL}/nezhahq/agent/releases/download/${version}/nezha-agent_linux_${os_arch}.zip >/dev/null 2>&1
|
||||||
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 "$@"
|
||||||
@@ -345,27 +376,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 [ $# -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
|
||||||
@@ -378,14 +413,14 @@ modify_agent_config() {
|
|||||||
fi
|
fi
|
||||||
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
|
sudo ${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
|
if [ $? -ne 0 ]; then
|
||||||
${NZ_AGENT_PATH}/nezha-agent service uninstall >/dev/null 2>&1
|
sudo ${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
|
sudo ${NZ_AGENT_PATH}/nezha-agent service install -s "$nz_grpc_host:$nz_grpc_port" -p $nz_client_secret $args >/dev/null 2>&1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo -e "Agent configuration ${green} modified successfully, please wait for agent self-restart to take effect${plain}"
|
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
|
||||||
@@ -393,46 +428,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 60 -O /tmp/nezha-docker-compose.yaml https://${GITHUB_RAW_URL}/script/docker-compose.yaml >/dev/null 2>&1
|
wget -t 2 -T 60 -O /tmp/nezha-docker-compose.yaml https://${GITHUB_RAW_URL}/script/docker-compose.yaml >/dev/null 2>&1
|
||||||
if [[ $? != 0 ]]; then
|
if [ $? != 0 ]; then
|
||||||
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 60 -O /tmp/nezha-config.yaml https://${GITHUB_RAW_URL}/script/config.yaml >/dev/null 2>&1
|
wget -t 2 -T 60 -O /tmp/nezha-config.yaml https://${GITHUB_RAW_URL}/script/config.yaml >/dev/null 2>&1
|
||||||
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
|
||||||
|
|
||||||
@@ -441,262 +483,268 @@ 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 60 -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 60 -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
|
if [ -z "$CN" ]; then
|
||||||
|
NZ_DASHBOARD_URL="https://${GITHUB_URL}/naiba/nezha/releases/latest/download/dashboard-linux-$os_arch.zip"
|
||||||
|
else
|
||||||
|
NZ_DASHBOARD_URL="https://${GITHUB_URL}/naibahq/nezha/releases/latest/download/dashboard-linux-$os_arch.zip"
|
||||||
|
fi
|
||||||
|
|
||||||
|
sudo wget -qO $NZ_DASHBOARD_PATH/app.zip $NZ_DASHBOARD_URL >/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"
|
||||||
|
|
||||||
${NZ_AGENT_PATH}/nezha-agent service uninstall
|
sudo ${NZ_AGENT_PATH}/nezha-agent service uninstall
|
||||||
|
|
||||||
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"
|
||||||
|
|
||||||
${NZ_AGENT_PATH}/nezha-agent service restart
|
sudo ${NZ_AGENT_PATH}/nezha-agent service restart
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -722,7 +770,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
|
||||||
@@ -743,7 +791,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
|
||||||
@@ -788,7 +836,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
|
||||||
}
|
}
|
||||||
@@ -796,7 +844,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
|
||||||
|
|||||||
+3
-1
@@ -1 +1,3 @@
|
|||||||
protoc --go-grpc_out="require_unimplemented_servers=false:." --go_out="." proto/*.proto
|
protoc --go-grpc_out="require_unimplemented_servers=false:." --go_out="." proto/*.proto
|
||||||
|
rm -rf ../agent/proto
|
||||||
|
cp -r proto ../agent
|
||||||
+4
-4
@@ -10,19 +10,19 @@ import (
|
|||||||
"github.com/naiba/nezha/service/singleton"
|
"github.com/naiba/nezha/service/singleton"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AuthHandler struct {
|
type authHandler struct {
|
||||||
ClientSecret string
|
ClientSecret string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AuthHandler) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
|
func (a *authHandler) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
|
||||||
return map[string]string{"client_secret": a.ClientSecret}, nil
|
return map[string]string{"client_secret": a.ClientSecret}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AuthHandler) RequireTransportSecurity() bool {
|
func (a *authHandler) RequireTransportSecurity() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AuthHandler) Check(ctx context.Context) (uint64, error) {
|
func (a *authHandler) Check(ctx context.Context) (uint64, error) {
|
||||||
md, ok := metadata.FromIncomingContext(ctx)
|
md, ok := metadata.FromIncomingContext(ctx)
|
||||||
if !ok {
|
if !ok {
|
||||||
return 0, status.Errorf(codes.Unauthenticated, "获取 metaData 失败")
|
return 0, status.Errorf(codes.Unauthenticated, "获取 metaData 失败")
|
||||||
|
|||||||
@@ -0,0 +1,140 @@
|
|||||||
|
package rpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ioStreamContext struct {
|
||||||
|
userIo io.ReadWriteCloser
|
||||||
|
agentIo io.ReadWriteCloser
|
||||||
|
userIoConnectCh chan struct{}
|
||||||
|
agentIoConnectCh chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *NezhaHandler) CreateStream(streamId string) {
|
||||||
|
s.ioStreamMutex.Lock()
|
||||||
|
defer s.ioStreamMutex.Unlock()
|
||||||
|
|
||||||
|
s.ioStreams[streamId] = &ioStreamContext{
|
||||||
|
userIoConnectCh: make(chan struct{}),
|
||||||
|
agentIoConnectCh: make(chan struct{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *NezhaHandler) GetStream(streamId string) (*ioStreamContext, error) {
|
||||||
|
s.ioStreamMutex.RLock()
|
||||||
|
defer s.ioStreamMutex.RUnlock()
|
||||||
|
|
||||||
|
if ctx, ok := s.ioStreams[streamId]; ok {
|
||||||
|
return ctx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errors.New("stream not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *NezhaHandler) CloseStream(streamId string) error {
|
||||||
|
s.ioStreamMutex.Lock()
|
||||||
|
defer s.ioStreamMutex.Unlock()
|
||||||
|
|
||||||
|
if ctx, ok := s.ioStreams[streamId]; ok {
|
||||||
|
if ctx.userIo != nil {
|
||||||
|
ctx.userIo.Close()
|
||||||
|
}
|
||||||
|
if ctx.agentIo != nil {
|
||||||
|
ctx.agentIo.Close()
|
||||||
|
}
|
||||||
|
delete(s.ioStreams, streamId)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *NezhaHandler) UserConnected(streamId string, userIo io.ReadWriteCloser) error {
|
||||||
|
stream, err := s.GetStream(streamId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.userIo = userIo
|
||||||
|
close(stream.userIoConnectCh)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *NezhaHandler) AgentConnected(streamId string, agentIo io.ReadWriteCloser) error {
|
||||||
|
stream, err := s.GetStream(streamId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.agentIo = agentIo
|
||||||
|
close(stream.agentIoConnectCh)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *NezhaHandler) StartStream(streamId string, timeout time.Duration) error {
|
||||||
|
stream, err := s.GetStream(streamId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
timeoutTimer := time.NewTimer(timeout)
|
||||||
|
|
||||||
|
LOOP:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-stream.userIoConnectCh:
|
||||||
|
if stream.agentIo != nil {
|
||||||
|
timeoutTimer.Stop()
|
||||||
|
break LOOP
|
||||||
|
}
|
||||||
|
case <-stream.agentIoConnectCh:
|
||||||
|
if stream.userIo != nil {
|
||||||
|
timeoutTimer.Stop()
|
||||||
|
break LOOP
|
||||||
|
}
|
||||||
|
case <-time.After(timeout):
|
||||||
|
break LOOP
|
||||||
|
}
|
||||||
|
time.Sleep(time.Millisecond * 500)
|
||||||
|
}
|
||||||
|
|
||||||
|
if stream.userIo == nil && stream.agentIo == nil {
|
||||||
|
return errors.New("timeout: no connection established")
|
||||||
|
}
|
||||||
|
if stream.userIo == nil {
|
||||||
|
return errors.New("timeout: user connection not established")
|
||||||
|
}
|
||||||
|
if stream.agentIo == nil {
|
||||||
|
return errors.New("timeout: agent connection not established")
|
||||||
|
}
|
||||||
|
|
||||||
|
isDone := new(atomic.Bool)
|
||||||
|
endCh := make(chan struct{})
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
_, innerErr := io.Copy(stream.userIo, stream.agentIo)
|
||||||
|
if innerErr != nil {
|
||||||
|
err = innerErr
|
||||||
|
}
|
||||||
|
if isDone.CompareAndSwap(false, true) {
|
||||||
|
close(endCh)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
_, innerErr := io.Copy(stream.agentIo, stream.userIo)
|
||||||
|
if innerErr != nil {
|
||||||
|
err = innerErr
|
||||||
|
}
|
||||||
|
if isDone.CompareAndSwap(false, true) {
|
||||||
|
close(endCh)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
<-endCh
|
||||||
|
return err
|
||||||
|
}
|
||||||
+43
-3
@@ -3,11 +3,14 @@ package rpc
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/naiba/nezha/pkg/ddns"
|
|
||||||
"github.com/naiba/nezha/pkg/utils"
|
|
||||||
"log"
|
"log"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/naiba/nezha/pkg/ddns"
|
||||||
|
"github.com/naiba/nezha/pkg/grpcx"
|
||||||
|
"github.com/naiba/nezha/pkg/utils"
|
||||||
|
|
||||||
"github.com/jinzhu/copier"
|
"github.com/jinzhu/copier"
|
||||||
"github.com/nicksnyder/go-i18n/v2/i18n"
|
"github.com/nicksnyder/go-i18n/v2/i18n"
|
||||||
|
|
||||||
@@ -16,8 +19,20 @@ import (
|
|||||||
"github.com/naiba/nezha/service/singleton"
|
"github.com/naiba/nezha/service/singleton"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var NezhaHandlerSingleton *NezhaHandler
|
||||||
|
|
||||||
type NezhaHandler struct {
|
type NezhaHandler struct {
|
||||||
Auth *AuthHandler
|
Auth *authHandler
|
||||||
|
ioStreams map[string]*ioStreamContext
|
||||||
|
ioStreamMutex *sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNezhaHandler() *NezhaHandler {
|
||||||
|
return &NezhaHandler{
|
||||||
|
Auth: &authHandler{},
|
||||||
|
ioStreamMutex: new(sync.RWMutex),
|
||||||
|
ioStreams: make(map[string]*ioStreamContext),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *NezhaHandler) ReportTask(c context.Context, r *pb.TaskResult) (*pb.Receipt, error) {
|
func (s *NezhaHandler) ReportTask(c context.Context, r *pb.TaskResult) (*pb.Receipt, error) {
|
||||||
@@ -177,3 +192,28 @@ func (s *NezhaHandler) ReportSystemInfo(c context.Context, r *pb.Host) (*pb.Rece
|
|||||||
singleton.ServerList[clientID].Host = &host
|
singleton.ServerList[clientID].Host = &host
|
||||||
return &pb.Receipt{Proced: true}, nil
|
return &pb.Receipt{Proced: true}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *NezhaHandler) IOStream(stream pb.NezhaService_IOStreamServer) error {
|
||||||
|
if _, err := s.Auth.Check(stream.Context()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
id, err := stream.Recv()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if id == nil || len(id.Data) < 4 || (id.Data[0] != 0xff && id.Data[1] != 0x05 && id.Data[2] != 0xff && id.Data[3] == 0x05) {
|
||||||
|
return fmt.Errorf("invalid stream id")
|
||||||
|
}
|
||||||
|
|
||||||
|
streamId := string(id.Data[4:])
|
||||||
|
|
||||||
|
if _, err := s.GetStream(streamId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
iw := grpcx.NewIOStreamWrapper(stream)
|
||||||
|
if err := s.AgentConnected(streamId, iw); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
iw.Wait()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,8 +15,6 @@ var (
|
|||||||
|
|
||||||
ServerAPI = &ServerAPIService{}
|
ServerAPI = &ServerAPIService{}
|
||||||
MonitorAPI = &MonitorAPIService{}
|
MonitorAPI = &MonitorAPIService{}
|
||||||
|
|
||||||
once = &sync.Once{}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ServerAPIService struct{}
|
type ServerAPIService struct{}
|
||||||
@@ -78,7 +76,7 @@ func InitAPI() {
|
|||||||
UserIDToApiTokenList = make(map[uint64][]string)
|
UserIDToApiTokenList = make(map[uint64][]string)
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadAPI() {
|
func loadAPI() {
|
||||||
InitAPI()
|
InitAPI()
|
||||||
var tokenList []*model.ApiToken
|
var tokenList []*model.ApiToken
|
||||||
DB.Find(&tokenList)
|
DB.Find(&tokenList)
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ func InitCronTask() {
|
|||||||
Crons = make(map[uint64]*model.Cron)
|
Crons = make(map[uint64]*model.Cron)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadCronTasks 加载计划任务
|
// loadCronTasks 加载计划任务
|
||||||
func LoadCronTasks() {
|
func loadCronTasks() {
|
||||||
InitCronTask()
|
InitCronTask()
|
||||||
var crons []model.Cron
|
var crons []model.Cron
|
||||||
DB.Find(&crons)
|
DB.Find(&crons)
|
||||||
|
|||||||
+14
-14
@@ -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,54 +25,54 @@ 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":
|
case "tencentcloud":
|
||||||
return ddns2.ProviderTencentCloud{
|
return &ddns2.ProviderTencentCloud{
|
||||||
SecretID: Conf.DDNS.AccessID,
|
SecretID: Conf.DDNS.AccessID,
|
||||||
SecretKey: Conf.DDNS.AccessSecret,
|
SecretKey: Conf.DDNS.AccessSecret,
|
||||||
}, nil
|
}, 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) {
|
func GetDDNSProviderFromProfile(profileName string) (ddns2.Provider, error) {
|
||||||
profile, ok := Conf.DDNS.Profiles[profileName]
|
profile, ok := Conf.DDNS.Profiles[profileName]
|
||||||
if !ok {
|
if !ok {
|
||||||
return ddns2.ProviderDummy{}, errors.New(fmt.Sprintf("未找到配置项 %s", profileName))
|
return &ddns2.ProviderDummy{}, fmt.Errorf("未找到配置项 %s", profileName)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch profile.Provider {
|
switch profile.Provider {
|
||||||
case "webhook":
|
case "webhook":
|
||||||
return ddns2.ProviderWebHook{
|
return &ddns2.ProviderWebHook{
|
||||||
URL: profile.WebhookURL,
|
URL: profile.WebhookURL,
|
||||||
RequestMethod: profile.WebhookMethod,
|
RequestMethod: profile.WebhookMethod,
|
||||||
RequestBody: profile.WebhookRequestBody,
|
RequestBody: profile.WebhookRequestBody,
|
||||||
RequestHeader: profile.WebhookHeaders,
|
RequestHeader: profile.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: profile.AccessSecret,
|
Secret: profile.AccessSecret,
|
||||||
}, nil
|
}, nil
|
||||||
case "tencentcloud":
|
case "tencentcloud":
|
||||||
return ddns2.ProviderTencentCloud{
|
return &ddns2.ProviderTencentCloud{
|
||||||
SecretID: profile.AccessID,
|
SecretID: profile.AccessID,
|
||||||
SecretKey: profile.AccessSecret,
|
SecretKey: profile.AccessSecret,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
return ddns2.ProviderDummy{}, errors.New(fmt.Sprintf("无法找到配置的DDNS提供者%s", profile.Provider))
|
return &ddns2.ProviderDummy{}, fmt.Errorf("无法找到配置的DDNS提供者%s", profile.Provider)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ValidateDDNSProvidersFromProfiles() error {
|
func ValidateDDNSProvidersFromProfiles() error {
|
||||||
@@ -80,7 +80,7 @@ func ValidateDDNSProvidersFromProfiles() error {
|
|||||||
providers := make(map[string]string)
|
providers := make(map[string]string)
|
||||||
for profileName, profile := range Conf.DDNS.Profiles {
|
for profileName, profile := range Conf.DDNS.Profiles {
|
||||||
if _, ok := validProviders[profile.Provider]; !ok {
|
if _, ok := validProviders[profile.Provider]; !ok {
|
||||||
return errors.New(fmt.Sprintf("无法找到配置的DDNS提供者%s", profile.Provider))
|
return fmt.Errorf("无法找到配置的DDNS提供者%s", profile.Provider)
|
||||||
}
|
}
|
||||||
providers[profileName] = profile.Provider
|
providers[profileName] = profile.Provider
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package singleton
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/naiba/nezha/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
var natCache = make(map[string]*model.NAT)
|
||||||
|
var natCacheRwLock = new(sync.RWMutex)
|
||||||
|
|
||||||
|
func initNAT() {
|
||||||
|
OnNATUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
func OnNATUpdate() {
|
||||||
|
natCacheRwLock.Lock()
|
||||||
|
defer natCacheRwLock.Unlock()
|
||||||
|
var nats []*model.NAT
|
||||||
|
DB.Find(&nats)
|
||||||
|
natCache = make(map[string]*model.NAT)
|
||||||
|
for i := 0; i < len(nats); i++ {
|
||||||
|
natCache[nats[i].Domain] = nats[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetNATConfigByDomain(domain string) *model.NAT {
|
||||||
|
natCacheRwLock.RLock()
|
||||||
|
defer natCacheRwLock.RUnlock()
|
||||||
|
return natCache[domain]
|
||||||
|
}
|
||||||
@@ -24,8 +24,8 @@ func InitNotification() {
|
|||||||
NotificationIDToTag = make(map[uint64]string)
|
NotificationIDToTag = make(map[uint64]string)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadNotifications 从 DB 初始化通知方式相关参数
|
// loadNotifications 从 DB 初始化通知方式相关参数
|
||||||
func LoadNotifications() {
|
func loadNotifications() {
|
||||||
InitNotification()
|
InitNotification()
|
||||||
notificationsLock.Lock()
|
notificationsLock.Lock()
|
||||||
defer notificationsLock.Unlock()
|
defer notificationsLock.Unlock()
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ func InitServer() {
|
|||||||
ServerTagToIDList = make(map[string][]uint64)
|
ServerTagToIDList = make(map[string][]uint64)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadServers 加载服务器列表并根据ID排序
|
// loadServers 加载服务器列表并根据ID排序
|
||||||
func LoadServers() {
|
func loadServers() {
|
||||||
InitServer()
|
InitServer()
|
||||||
var servers []model.Server
|
var servers []model.Server
|
||||||
DB.Find(&servers)
|
DB.Find(&servers)
|
||||||
|
|||||||
@@ -34,10 +34,11 @@ func InitTimezoneAndCache() {
|
|||||||
|
|
||||||
// LoadSingleton 加载子服务并执行
|
// LoadSingleton 加载子服务并执行
|
||||||
func LoadSingleton() {
|
func LoadSingleton() {
|
||||||
LoadNotifications() // 加载通知服务
|
loadNotifications() // 加载通知服务
|
||||||
LoadServers() // 加载服务器列表
|
loadServers() // 加载服务器列表
|
||||||
LoadCronTasks() // 加载定时任务
|
loadCronTasks() // 加载定时任务
|
||||||
LoadAPI()
|
loadAPI()
|
||||||
|
initNAT()
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitConfigFromPath 从给出的文件路径中加载配置
|
// InitConfigFromPath 从给出的文件路径中加载配置
|
||||||
@@ -47,11 +48,11 @@ func InitConfigFromPath(path string) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
ValidateConfig()
|
validateConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateConfig 验证配置文件有效性
|
// validateConfig 验证配置文件有效性
|
||||||
func ValidateConfig() {
|
func validateConfig() {
|
||||||
var err error
|
var err error
|
||||||
if Conf.DDNS.Provider == "" {
|
if Conf.DDNS.Provider == "" {
|
||||||
err = ValidateDDNSProvidersFromProfiles()
|
err = ValidateDDNSProvidersFromProfiles()
|
||||||
@@ -82,7 +83,8 @@ func InitDBFromPath(path string) {
|
|||||||
}
|
}
|
||||||
err = DB.AutoMigrate(model.Server{}, model.User{},
|
err = DB.AutoMigrate(model.Server{}, model.User{},
|
||||||
model.Notification{}, model.AlertRule{}, model.Monitor{},
|
model.Notification{}, model.AlertRule{}, model.Monitor{},
|
||||||
model.MonitorHistory{}, model.Cron{}, model.Transfer{}, model.ApiToken{})
|
model.MonitorHistory{}, model.Cron{}, model.Transfer{},
|
||||||
|
model.ApiToken{}, model.NAT{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@@ -93,7 +95,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{
|
||||||
@@ -136,7 +138,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 {
|
||||||
// 更新全局可以清理的数据点
|
// 更新全局可以清理的数据点
|
||||||
@@ -155,12 +157,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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user