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 | ||
|
|
f75847d3d7 | ||
|
|
eee3225f3a | ||
|
|
aa9eb533ad | ||
|
|
cf9bbb5ff4 | ||
|
|
65dee23137 | ||
|
|
feb4659cab | ||
|
|
bc7791efd2 | ||
|
|
6e6ec3256f | ||
|
|
ce66f72cf9 | ||
|
|
0ed7436176 | ||
|
|
2aabe1bd61 | ||
|
|
db5e94d0de | ||
|
|
3eecdc2b3f | ||
|
|
b2740e5f3c | ||
|
|
c3fa4cbbee | ||
|
|
a9da084711 | ||
|
|
f30969e946 | ||
|
|
e0e2b8c3c2 | ||
|
|
3782c0dcaf | ||
|
|
0331db2c12 | ||
|
|
1749247aeb | ||
|
|
eaba7abd35 | ||
|
|
f5ca66d924 | ||
|
|
d5e7b22fc6 | ||
|
|
5122d48dd8 | ||
|
|
3614ef381e | ||
|
|
8475674eb7 | ||
|
|
25e7f8a912 | ||
|
|
5c7652f047 | ||
|
|
890616f52a | ||
|
|
c92ec996e0 | ||
|
|
7fcd4398ea | ||
|
|
7faba40a1c | ||
|
|
66b4c88c24 | ||
|
|
f3c90bbf59 | ||
|
|
8ef5dfb8f8 | ||
|
|
d38f611d66 | ||
|
|
d9a7002906 | ||
|
|
d9188f5228 | ||
|
|
eb3e2883e2 | ||
|
|
83ba5ebc8a | ||
|
|
6d7d3c5e85 | ||
|
|
194f3290bc | ||
|
|
dc5c79711d | ||
|
|
d88989fb85 | ||
|
|
0373d03fb2 | ||
|
|
140f9809a0 | ||
|
|
f647a5f8e6 | ||
|
|
506c87dbd8 | ||
|
|
8b5d513c6a | ||
|
|
6c718c4f38 | ||
|
|
5a2ab8a7ee | ||
|
|
0ea0c9ad8c | ||
|
|
7d4d8c46b8 | ||
|
|
a4636ed774 | ||
|
|
bb48e0912d | ||
|
|
a575d1a6e0 | ||
|
|
420d1ca8ff | ||
|
|
9a8c0e7118 | ||
|
|
800af7ce4e | ||
|
|
78134a0e8b | ||
|
|
bad910b686 | ||
|
|
a7d1bd7487 | ||
|
|
20a10ac6b8 | ||
|
|
3a395e66c0 | ||
|
|
4e5cc0b642 | ||
|
|
fe2872bbb3 |
@@ -22,19 +22,21 @@ jobs:
|
|||||||
uses: crazy-max/ghaction-xgo@v2
|
uses: crazy-max/ghaction-xgo@v2
|
||||||
with:
|
with:
|
||||||
xgo_version: latest
|
xgo_version: latest
|
||||||
go_version: 1.21
|
go_version: 1.21.x
|
||||||
dest: dist
|
dest: dist
|
||||||
pkg: cmd/dashboard
|
pkg: cmd/dashboard
|
||||||
prefix: dashboard
|
prefix: dashboard
|
||||||
targets: linux/amd64,linux/arm64,linux/arm-7,linux/s390x,linux/riscv64 # linux/386,
|
targets: linux/amd64,linux/arm64,linux/arm-7,linux/s390x,linux/riscv64,windows/amd64,windows/386,windows/arm64 # linux/386,
|
||||||
v: true
|
v: true
|
||||||
x: false
|
x: false
|
||||||
race: false
|
race: false
|
||||||
|
tags: timetzdata
|
||||||
ldflags: -s -w --extldflags '-static -fpic' -X github.com/naiba/nezha/service/singleton.Version=${{ steps.extract_branch.outputs.tag }}
|
ldflags: -s -w --extldflags '-static -fpic' -X github.com/naiba/nezha/service/singleton.Version=${{ steps.extract_branch.outputs.tag }}
|
||||||
buildmode: default
|
buildmode: default
|
||||||
|
|
||||||
- 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
|
||||||
@@ -106,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,14 +25,19 @@ jobs:
|
|||||||
uses: crazy-max/ghaction-xgo@v2
|
uses: crazy-max/ghaction-xgo@v2
|
||||||
with:
|
with:
|
||||||
xgo_version: latest
|
xgo_version: latest
|
||||||
go_version: 1.21
|
go_version: 1.21.x
|
||||||
dest: dist
|
dest: dist
|
||||||
pkg: cmd/dashboard
|
pkg: cmd/dashboard
|
||||||
prefix: dashboard
|
prefix: dashboard
|
||||||
targets: linux/amd64,linux/arm64,linux/arm-7,linux/s390x,linux/riscv64 # linux/386,
|
targets: linux/amd64,linux/arm64,linux/arm-7,linux/s390x,linux/riscv64,windows/amd64,windows/386,windows/arm64 # linux/386,
|
||||||
v: true
|
v: true
|
||||||
x: false
|
x: false
|
||||||
race: false
|
race: false
|
||||||
|
tags: timetzdata
|
||||||
ldflags: -s -w --extldflags '-static -fpic' -X github.com/naiba/nezha/service/singleton.Version=test
|
ldflags: -s -w --extldflags '-static -fpic' -X github.com/naiba/nezha/service/singleton.Version=test
|
||||||
buildmode: default
|
buildmode: default
|
||||||
|
- name: fix dist
|
||||||
|
run: |
|
||||||
|
ls -al dist/
|
||||||
|
mv dist/dashboard-linux-arm-7 dist/dashboard-linux-arm
|
||||||
|
|
||||||
|
|||||||
@@ -32,13 +32,18 @@ jobs:
|
|||||||
uses: crazy-max/ghaction-xgo@v2
|
uses: crazy-max/ghaction-xgo@v2
|
||||||
with:
|
with:
|
||||||
xgo_version: latest
|
xgo_version: latest
|
||||||
go_version: 1.21
|
go_version: 1.21.x
|
||||||
dest: dist
|
dest: dist
|
||||||
pkg: cmd/dashboard
|
pkg: cmd/dashboard
|
||||||
prefix: dashboard
|
prefix: dashboard
|
||||||
targets: linux/amd64,linux/arm64,linux/arm-7,linux/s390x,linux/riscv64 # linux/386,
|
targets: linux/amd64,linux/arm64,linux/arm-7,linux/s390x,linux/riscv64,windows/amd64,windows/386,windows/arm64 # linux/386,
|
||||||
v: true
|
v: true
|
||||||
x: false
|
x: false
|
||||||
race: false
|
race: false
|
||||||
|
tags: timetzdata
|
||||||
ldflags: -s -w --extldflags '-static -fpic' -X github.com/naiba/nezha/service/singleton.Version=test
|
ldflags: -s -w --extldflags '-static -fpic' -X github.com/naiba/nezha/service/singleton.Version=test
|
||||||
buildmode: default
|
buildmode: default
|
||||||
|
- 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.15.9-brightgreen?style=for-the-badge&logo=linux">
|
<img alt="GitHub release (with filter)" src="https://img.shields.io/github/v/release/naiba/nezha?color=brightgreen&style=for-the-badge&logo=github&label=Dashboard"> <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,16 +43,17 @@ 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/uubulb" title="UUBulb"><img src="https://avatars.githubusercontent.com/u/35923940?v=4" width="50;" alt="UUBulb"/></a>
|
||||||
<a href="https://github.com/dysf888" title="黑歌"><img src="https://avatars.githubusercontent.com/u/47450409?v=4" width="50;" alt="黑歌"/></a>
|
<a href="https://github.com/dysf888" title="黑歌"><img src="https://avatars.githubusercontent.com/u/47450409?v=4" width="50;" alt="黑歌"/></a>
|
||||||
|
<a href="https://github.com/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/MikoyChinese" title="MikoyChinese"><img src="https://avatars.githubusercontent.com/u/22676744?v=4" width="50;" alt="MikoyChinese"/></a>
|
||||||
<a href="https://github.com/JackieSung4ev" title="JackieSung4ev"><img src="https://avatars.githubusercontent.com/u/24974735?v=4" width="50;" alt="JackieSung4ev"/></a>
|
<a href="https://github.com/JackieSung4ev" title="JackieSung4ev"><img src="https://avatars.githubusercontent.com/u/24974735?v=4" width="50;" alt="JackieSung4ev"/></a>
|
||||||
<a href="https://github.com/lemoeo" title="Lemoe"><img src="https://avatars.githubusercontent.com/u/18618627?v=4" width="50;" alt="Lemoe"/></a>
|
|
||||||
<a href="https://github.com/cantoblanco" title="Kris"><img src="https://avatars.githubusercontent.com/u/116849421?v=4" width="50;" alt="Kris"/></a>
|
<a href="https://github.com/cantoblanco" title="Kris"><img src="https://avatars.githubusercontent.com/u/116849421?v=4" width="50;" alt="Kris"/></a>
|
||||||
<a href="https://github.com/nap0o" title="nap0o"><img src="https://avatars.githubusercontent.com/u/144927971?v=4" width="50;" alt="nap0o"/></a>
|
<a href="https://github.com/lemoeo" title="Lemoe"><img src="https://avatars.githubusercontent.com/u/18618627?v=4" width="50;" alt="Lemoe"/></a>
|
||||||
<a href="https://github.com/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>
|
||||||
<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/lvgj-stack" title="Ko no dio"><img src="https://avatars.githubusercontent.com/u/38449861?v=4" width="50;" alt="Ko no dio"/></a>
|
<a href="https://github.com/lvgj-stack" title="Ko no dio"><img src="https://avatars.githubusercontent.com/u/38449861?v=4" width="50;" alt="Ko no dio"/></a>
|
||||||
<a href="https://github.com/hhhkkk520" title="Kris"><img src="https://avatars.githubusercontent.com/u/52115472?v=4" width="50;" alt="Kris"/></a>
|
<a href="https://github.com/hhhkkk520" title="Kris"><img src="https://avatars.githubusercontent.com/u/52115472?v=4" width="50;" alt="Kris"/></a>
|
||||||
<a href="https://github.com/1ridic" title="1ridic"><img src="https://avatars.githubusercontent.com/u/88495501?v=4" width="50;" alt="1ridic"/></a>
|
<a href="https://github.com/1ridic" title="1ridic"><img src="https://avatars.githubusercontent.com/u/88495501?v=4" width="50;" alt="1ridic"/></a>
|
||||||
@@ -59,27 +62,33 @@ You can change the dashboard language in the settings page (`/setting`) after th
|
|||||||
<a href="https://github.com/iilemon" title="Sean"><img src="https://avatars.githubusercontent.com/u/33201711?v=4" width="50;" alt="Sean"/></a>
|
<a href="https://github.com/iilemon" title="Sean"><img src="https://avatars.githubusercontent.com/u/33201711?v=4" width="50;" alt="Sean"/></a>
|
||||||
<a href="https://github.com/fscarmen" title="fscarmen"><img src="https://avatars.githubusercontent.com/u/62703343?v=4" width="50;" alt="fscarmen"/></a>
|
<a href="https://github.com/fscarmen" title="fscarmen"><img src="https://avatars.githubusercontent.com/u/62703343?v=4" width="50;" alt="fscarmen"/></a>
|
||||||
<a href="https://github.com/ch8o" title="no-name-now"><img src="https://avatars.githubusercontent.com/u/9103372?v=4" width="50;" alt="no-name-now"/></a>
|
<a href="https://github.com/ch8o" title="no-name-now"><img src="https://avatars.githubusercontent.com/u/9103372?v=4" width="50;" alt="no-name-now"/></a>
|
||||||
|
<a href="https://github.com/HsukqiLee" title="HsukqiLee"><img src="https://avatars.githubusercontent.com/u/79034142?v=4" width="50;" alt="HsukqiLee"/></a>
|
||||||
<a href="https://github.com/DarcJC" title="Darc Z."><img src="https://avatars.githubusercontent.com/u/53445798?v=4" width="50;" alt="Darc Z."/></a>
|
<a href="https://github.com/DarcJC" title="Darc Z."><img src="https://avatars.githubusercontent.com/u/53445798?v=4" width="50;" alt="Darc Z."/></a>
|
||||||
<a href="https://github.com/Creling" title="Creling"><img src="https://avatars.githubusercontent.com/u/43109504?v=4" width="50;" alt="Creling"/></a>
|
<a href="https://github.com/Creling" title="Creling"><img src="https://avatars.githubusercontent.com/u/43109504?v=4" width="50;" alt="Creling"/></a>
|
||||||
<a href="https://github.com/coreff" title="Core F"><img src="https://avatars.githubusercontent.com/u/38347122?v=4" width="50;" alt="Core F"/></a>
|
<a href="https://github.com/coreff" title="Core F"><img src="https://avatars.githubusercontent.com/u/38347122?v=4" width="50;" alt="Core F"/></a>
|
||||||
<a href="https://github.com/colour93" title="玖叁"><img src="https://avatars.githubusercontent.com/u/64313711?v=4" width="50;" alt="玖叁"/></a>
|
<a href="https://github.com/colour93" title="玖叁"><img src="https://avatars.githubusercontent.com/u/64313711?v=4" width="50;" alt="玖叁"/></a>
|
||||||
<a href="https://github.com/arkylin" title="凌"><img src="https://avatars.githubusercontent.com/u/35104502?v=4" width="50;" alt="凌"/></a>
|
<a href="https://github.com/arkylin" title="凌"><img src="https://avatars.githubusercontent.com/u/35104502?v=4" width="50;" alt="凌"/></a>
|
||||||
|
<a href="https://github.com/zhucaidan" title="zhucaidan"><img src="https://avatars.githubusercontent.com/u/47970938?v=4" width="50;" alt="zhucaidan"/></a>
|
||||||
<a href="https://github.com/ysicing" title="缘生"><img src="https://avatars.githubusercontent.com/u/8605565?v=4" width="50;" alt="缘生"/></a>
|
<a href="https://github.com/ysicing" title="缘生"><img src="https://avatars.githubusercontent.com/u/8605565?v=4" width="50;" alt="缘生"/></a>
|
||||||
<a href="https://github.com/xykt" title="xykt"><img src="https://avatars.githubusercontent.com/u/152045469?v=4" width="50;" alt="xykt"/></a>
|
|
||||||
<a href="https://github.com/unclezs" title="unclezs"><img src="https://avatars.githubusercontent.com/u/42318775?v=4" width="50;" alt="unclezs"/></a>
|
<a href="https://github.com/unclezs" title="unclezs"><img src="https://avatars.githubusercontent.com/u/42318775?v=4" width="50;" alt="unclezs"/></a>
|
||||||
|
<a href="https://github.com/lvyaoting" title="lvyaoting"><img src="https://avatars.githubusercontent.com/u/166296299?v=4" width="50;" alt="lvyaoting"/></a>
|
||||||
<a href="https://github.com/yuanweize" title="I"><img src="https://avatars.githubusercontent.com/u/30067203?v=4" width="50;" alt="I"/></a>
|
<a href="https://github.com/yuanweize" title="I"><img src="https://avatars.githubusercontent.com/u/30067203?v=4" width="50;" alt="I"/></a>
|
||||||
<a href="https://github.com/guoyongchang" title="guoyongchang"><img src="https://avatars.githubusercontent.com/u/10484506?v=4" width="50;" alt="guoyongchang"/></a>
|
<a href="https://github.com/guoyongchang" title="guoyongchang"><img src="https://avatars.githubusercontent.com/u/10484506?v=4" width="50;" alt="guoyongchang"/></a>
|
||||||
|
<a href="https://github.com/eya46" title="eya46"><img src="https://avatars.githubusercontent.com/u/61458340?v=4" width="50;" alt="eya46"/></a>
|
||||||
<a href="https://github.com/acgpiano" title="Acgpiano"><img src="https://avatars.githubusercontent.com/u/15900800?v=4" width="50;" alt="Acgpiano"/></a>
|
<a href="https://github.com/acgpiano" title="Acgpiano"><img src="https://avatars.githubusercontent.com/u/15900800?v=4" width="50;" alt="Acgpiano"/></a>
|
||||||
<a href="https://github.com/adminsama" title="adminsama"><img src="https://avatars.githubusercontent.com/u/60880076?v=4" width="50;" alt="adminsama"/></a>
|
<a href="https://github.com/adminsama" title="adminsama"><img src="https://avatars.githubusercontent.com/u/60880076?v=4" width="50;" alt="adminsama"/></a>
|
||||||
<a href="https://github.com/hmsjy2017" title="Tony"><img src="https://avatars.githubusercontent.com/u/42692274?v=4" width="50;" alt="Tony"/></a>
|
<a href="https://github.com/hmsjy2017" title="Tony"><img src="https://avatars.githubusercontent.com/u/42692274?v=4" width="50;" alt="Tony"/></a>
|
||||||
<a href="https://github.com/nickfox-taterli" title="Tater Li"><img src="https://avatars.githubusercontent.com/u/19658596?v=4" width="50;" alt="Tater Li"/></a>
|
<a href="https://github.com/nickfox-taterli" title="Tater Li"><img src="https://avatars.githubusercontent.com/u/19658596?v=4" width="50;" alt="Tater Li"/></a>
|
||||||
|
<a href="https://github.com/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/MartijnLindeman" title="Martijn Lindeman"><img src="https://avatars.githubusercontent.com/u/78365708?v=4" width="50;" alt="Martijn Lindeman"/></a>
|
<a href="https://github.com/MartijnLindeman" title="Martijn Lindeman"><img src="https://avatars.githubusercontent.com/u/78365708?v=4" width="50;" alt="Martijn Lindeman"/></a>
|
||||||
|
<a href="https://github.com/funnyzak" title="Leon"><img src="https://avatars.githubusercontent.com/u/2562087?v=4" width="50;" alt="Leon"/></a>
|
||||||
<a href="https://github.com/KorenKrita" title="KorenKrita"><img src="https://avatars.githubusercontent.com/u/22239339?v=4" width="50;" alt="KorenKrita"/></a>
|
<a href="https://github.com/KorenKrita" title="KorenKrita"><img src="https://avatars.githubusercontent.com/u/22239339?v=4" width="50;" alt="KorenKrita"/></a>
|
||||||
<a href="https://github.com/techotaku" title="Ian Li"><img src="https://avatars.githubusercontent.com/u/1948179?v=4" width="50;" alt="Ian Li"/></a>
|
<a href="https://github.com/techotaku" title="Ian Li"><img src="https://avatars.githubusercontent.com/u/1948179?v=4" width="50;" alt="Ian Li"/></a>
|
||||||
<a href="https://github.com/HsukqiLee" title="HsukqiLee"><img src="https://avatars.githubusercontent.com/u/79034142?v=4" width="50;" alt="HsukqiLee"/></a>
|
|
||||||
<a href="https://github.com/GreenTeodoro839" title="GreenTeodoro839"><img src="https://avatars.githubusercontent.com/u/77104800?v=4" width="50;" alt="GreenTeodoro839"/></a>
|
<a href="https://github.com/GreenTeodoro839" title="GreenTeodoro839"><img src="https://avatars.githubusercontent.com/u/77104800?v=4" width="50;" alt="GreenTeodoro839"/></a>
|
||||||
<a href="https://github.com/Es-dese" title="Esdese"><img src="https://avatars.githubusercontent.com/u/71542548?v=4" width="50;" alt="Esdese"/></a>
|
<a href="https://github.com/Es-dese" title="Esdese"><img src="https://avatars.githubusercontent.com/u/71542548?v=4" width="50;" alt="Esdese"/></a>
|
||||||
<a href="https://github.com/wwng2333" title="Crazy"><img src="https://avatars.githubusercontent.com/u/17147265?v=4" width="50;" alt="Crazy"/></a><!--GAMFC_DELIMITER_END-->
|
<a href="https://github.com/wwng2333" title="Crazy"><img src="https://avatars.githubusercontent.com/u/17147265?v=4" width="50;" alt="Crazy"/></a>
|
||||||
|
<a href="https://github.com/wellcoming" title="Coming"><img src="https://avatars.githubusercontent.com/u/74850890?v=4" width="50;" alt="Coming"/></a><!--GAMFC_DELIMITER_END-->
|
||||||
|
|
||||||
## Star History
|
## Star History
|
||||||
|
|
||||||
|
|||||||
@@ -2,12 +2,9 @@ package controller
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"log"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@@ -23,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() {
|
||||||
@@ -67,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"}))
|
||||||
@@ -223,19 +210,25 @@ func (cp *commonPage) network(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (cp *commonPage) getServerStat(c *gin.Context) ([]byte, error) {
|
func (cp *commonPage) getServerStat(c *gin.Context) ([]byte, error) {
|
||||||
v, err, _ := cp.requestGroup.Do("serverStats", func() (any, error) {
|
_, isMember := c.Get(model.CtxKeyAuthorizedUser)
|
||||||
|
_, isViewPasswordVerfied := c.Get(model.CtxKeyViewPasswordVerified)
|
||||||
|
authorized := isMember || isViewPasswordVerfied
|
||||||
|
v, err, _ := cp.requestGroup.Do(fmt.Sprintf("serverStats::%t", authorized), func() (interface{}, error) {
|
||||||
singleton.SortedServerLock.RLock()
|
singleton.SortedServerLock.RLock()
|
||||||
defer singleton.SortedServerLock.RUnlock()
|
defer singleton.SortedServerLock.RUnlock()
|
||||||
|
|
||||||
_, isMember := c.Get(model.CtxKeyAuthorizedUser)
|
|
||||||
_, isViewPasswordVerfied := c.Get(model.CtxKeyViewPasswordVerified)
|
|
||||||
|
|
||||||
var servers []*model.Server
|
var servers []*model.Server
|
||||||
|
|
||||||
if isMember || isViewPasswordVerfied {
|
if authorized {
|
||||||
servers = singleton.SortedServerList
|
servers = singleton.SortedServerList
|
||||||
} else {
|
} else {
|
||||||
servers = singleton.SortedServerListForGuest
|
filteredServers := make([]*model.Server, len(singleton.SortedServerListForGuest))
|
||||||
|
for i, server := range singleton.SortedServerListForGuest {
|
||||||
|
filteredServer := *server
|
||||||
|
filteredServer.DDNSDomain = "redacted"
|
||||||
|
filteredServers[i] = &filteredServer
|
||||||
|
}
|
||||||
|
servers = filteredServers
|
||||||
}
|
}
|
||||||
|
|
||||||
return utils.Json.Marshal(Data{
|
return utils.Json.Marshal(Data{
|
||||||
@@ -267,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 {
|
||||||
@@ -276,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 {
|
||||||
@@ -314,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: "无权访问",
|
||||||
@@ -327,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 {
|
||||||
@@ -440,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 保活
|
||||||
@@ -481,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 {
|
||||||
@@ -564,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,
|
||||||
@@ -578,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()
|
||||||
@@ -592,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 {
|
||||||
@@ -49,6 +53,12 @@ func (gp *guestPage) login(c *gin.Context) {
|
|||||||
} else if singleton.Conf.Oauth2.Type == model.ConfigTypeGitea {
|
} else if singleton.Conf.Oauth2.Type == model.ConfigTypeGitea {
|
||||||
LoginType = "Gitea"
|
LoginType = "Gitea"
|
||||||
RegistrationLink = fmt.Sprintf("%s/user/sign_up", singleton.Conf.Oauth2.Endpoint)
|
RegistrationLink = fmt.Sprintf("%s/user/sign_up", singleton.Conf.Oauth2.Endpoint)
|
||||||
|
} else if singleton.Conf.Oauth2.Type == model.ConfigTypeCloudflare {
|
||||||
|
LoginType = "Cloudflare"
|
||||||
|
RegistrationLink = "https://dash.cloudflare.com/sign-up/teams"
|
||||||
|
} 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 {
|
||||||
@@ -301,7 +307,10 @@ type serverForm struct {
|
|||||||
Note string
|
Note string
|
||||||
HideForGuest string
|
HideForGuest string
|
||||||
EnableDDNS string
|
EnableDDNS string
|
||||||
|
EnableIPv4 string
|
||||||
|
EnableIpv6 string
|
||||||
DDNSDomain string
|
DDNSDomain string
|
||||||
|
DDNSProfile string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ma *memberAPI) addOrEditServer(c *gin.Context) {
|
func (ma *memberAPI) addOrEditServer(c *gin.Context) {
|
||||||
@@ -318,7 +327,10 @@ func (ma *memberAPI) addOrEditServer(c *gin.Context) {
|
|||||||
s.Note = sf.Note
|
s.Note = sf.Note
|
||||||
s.HideForGuest = sf.HideForGuest == "on"
|
s.HideForGuest = sf.HideForGuest == "on"
|
||||||
s.EnableDDNS = sf.EnableDDNS == "on"
|
s.EnableDDNS = sf.EnableDDNS == "on"
|
||||||
|
s.EnableIPv4 = sf.EnableIPv4 == "on"
|
||||||
|
s.EnableIpv6 = sf.EnableIpv6 == "on"
|
||||||
s.DDNSDomain = sf.DDNSDomain
|
s.DDNSDomain = sf.DDNSDomain
|
||||||
|
s.DDNSProfile = sf.DDNSProfile
|
||||||
if s.ID == 0 {
|
if s.ID == 0 {
|
||||||
s.Secret, err = utils.GenerateRandomString(18)
|
s.Secret, err = utils.GenerateRandomString(18)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -431,25 +443,23 @@ func (ma *memberAPI) addOrEditMonitor(c *gin.Context) {
|
|||||||
if m.NotificationTag == "" {
|
if m.NotificationTag == "" {
|
||||||
m.NotificationTag = "default"
|
m.NotificationTag = "default"
|
||||||
}
|
}
|
||||||
if err == nil {
|
err = utils.Json.Unmarshal([]byte(mf.FailTriggerTasksRaw), &m.FailTriggerTasks)
|
||||||
err = utils.Json.Unmarshal([]byte(mf.FailTriggerTasksRaw), &m.FailTriggerTasks)
|
}
|
||||||
|
if err == nil {
|
||||||
|
err = utils.Json.Unmarshal([]byte(mf.RecoverTriggerTasksRaw), &m.RecoverTriggerTasks)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
if m.ID == 0 {
|
||||||
|
err = singleton.DB.Create(&m).Error
|
||||||
|
} else {
|
||||||
|
err = singleton.DB.Save(&m).Error
|
||||||
}
|
}
|
||||||
if err == nil {
|
}
|
||||||
err = utils.Json.Unmarshal([]byte(mf.RecoverTriggerTasksRaw), &m.RecoverTriggerTasks)
|
if err == nil {
|
||||||
}
|
if m.Cover == 0 {
|
||||||
if err == nil {
|
err = singleton.DB.Unscoped().Delete(&model.MonitorHistory{}, "monitor_id = ? and server_id in (?)", m.ID, strings.Split(m.SkipServersRaw[1:len(m.SkipServersRaw)-1], ",")).Error
|
||||||
if m.ID == 0 {
|
} else {
|
||||||
err = singleton.DB.Create(&m).Error
|
err = singleton.DB.Unscoped().Delete(&model.MonitorHistory{}, "monitor_id = ? and server_id not in (?)", m.ID, strings.Split(m.SkipServersRaw[1:len(m.SkipServersRaw)-1], ",")).Error
|
||||||
} else {
|
|
||||||
err = singleton.DB.Save(&m).Error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
if m.Cover == 0 {
|
|
||||||
err = singleton.DB.Unscoped().Delete(&model.MonitorHistory{}, "monitor_id = ? and server_id in (?)", m.ID, strings.Split(m.SkipServersRaw[1:len(m.SkipServersRaw)-1], ",")).Error
|
|
||||||
} else {
|
|
||||||
err = singleton.DB.Unscoped().Delete(&model.MonitorHistory{}, "monitor_id = ? and server_id not in (?)", m.ID, strings.Split(m.SkipServersRaw[1:len(m.SkipServersRaw)-1], ",")).Error
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -729,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
|
||||||
@@ -843,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"}),
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package controller
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -9,23 +10,27 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/coreos/go-oidc/v3/oidc"
|
||||||
|
"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() {
|
||||||
@@ -74,6 +79,38 @@ func (oa *oauth2controller) getCommonOauth2Config(c *gin.Context) *oauth2.Config
|
|||||||
},
|
},
|
||||||
RedirectURL: oa.getRedirectURL(c),
|
RedirectURL: oa.getRedirectURL(c),
|
||||||
}
|
}
|
||||||
|
} else if singleton.Conf.Oauth2.Type == model.ConfigTypeCloudflare {
|
||||||
|
return &oauth2.Config{
|
||||||
|
ClientID: singleton.Conf.Oauth2.ClientID,
|
||||||
|
ClientSecret: singleton.Conf.Oauth2.ClientSecret,
|
||||||
|
Scopes: []string{"openid", "email", "profile", "groups"},
|
||||||
|
Endpoint: oauth2.Endpoint{
|
||||||
|
AuthURL: fmt.Sprintf("%s/cdn-cgi/access/sso/oidc/%s/authorization", singleton.Conf.Oauth2.Endpoint, singleton.Conf.Oauth2.ClientID),
|
||||||
|
TokenURL: fmt.Sprintf("%s/cdn-cgi/access/sso/oidc/%s/token", singleton.Conf.Oauth2.Endpoint, singleton.Conf.Oauth2.ClientID),
|
||||||
|
},
|
||||||
|
RedirectURL: oa.getRedirectURL(c),
|
||||||
|
}
|
||||||
|
} else 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,
|
||||||
@@ -86,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"
|
||||||
@@ -155,6 +193,28 @@ func (oa *oauth2controller) callback(c *gin.Context) {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
user = model.NewUserFromGitea(u)
|
user = model.NewUserFromGitea(u)
|
||||||
}
|
}
|
||||||
|
} else if singleton.Conf.Oauth2.Type == model.ConfigTypeCloudflare {
|
||||||
|
client := oauth2Config.Client(context.Background(), otk)
|
||||||
|
resp, err := client.Get(fmt.Sprintf("%s/cdn-cgi/access/sso/oidc/%s/userinfo", singleton.Conf.Oauth2.Endpoint, singleton.Conf.Oauth2.ClientID))
|
||||||
|
if err == nil {
|
||||||
|
defer resp.Body.Close()
|
||||||
|
var cloudflareUserInfo *cloudflare.UserInfo
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&cloudflareUserInfo); err == nil {
|
||||||
|
user = cloudflareUserInfo.MapToNezhaUser()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else 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)
|
||||||
@@ -168,15 +228,12 @@ func (oa *oauth2controller) callback(c *gin.Context) {
|
|||||||
client = GitHubAPI.NewClient(oc)
|
client = GitHubAPI.NewClient(oc)
|
||||||
}
|
}
|
||||||
var gu *GitHubAPI.User
|
var gu *GitHubAPI.User
|
||||||
if err == nil {
|
gu, _, err = client.Users.Get(ctx, "")
|
||||||
gu, _, err = client.Users.Get(ctx, "")
|
|
||||||
}
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
user = model.NewUserFromGitHub(gu)
|
user = model.NewUserFromGitHub(gu)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err == nil && user.Login == "" {
|
if err == nil && user.Login == "" {
|
||||||
err = errors.New("获取用户信息失败")
|
err = errors.New("获取用户信息失败")
|
||||||
}
|
}
|
||||||
@@ -190,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 {
|
||||||
@@ -220,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)
|
||||||
|
|||||||
@@ -2,12 +2,11 @@ module github.com/naiba/nezha
|
|||||||
|
|
||||||
go 1.21
|
go 1.21
|
||||||
|
|
||||||
toolchain go1.21.3
|
|
||||||
|
|
||||||
require (
|
require (
|
||||||
code.cloudfoundry.org/bytefmt v0.0.0-20240322144145-4816192cb114
|
code.cloudfoundry.org/bytefmt v0.0.0-20240425163905-bcdc1ad063ea
|
||||||
code.gitea.io/sdk/gitea v0.17.1
|
code.gitea.io/sdk/gitea v0.18.0
|
||||||
github.com/BurntSushi/toml v1.3.2
|
github.com/BurntSushi/toml v1.3.2
|
||||||
|
github.com/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
|
||||||
@@ -19,18 +18,18 @@ 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.100.0
|
github.com/xanzy/go-gitlab v0.103.0
|
||||||
golang.org/x/crypto v0.21.0
|
golang.org/x/crypto v0.25.0
|
||||||
golang.org/x/oauth2 v0.18.0
|
golang.org/x/net v0.27.0
|
||||||
golang.org/x/sync v0.6.0
|
golang.org/x/oauth2 v0.21.0
|
||||||
golang.org/x/text v0.14.0
|
golang.org/x/sync v0.7.0
|
||||||
google.golang.org/grpc v1.62.1
|
golang.org/x/text v0.16.0
|
||||||
google.golang.org/protobuf v1.33.0
|
google.golang.org/grpc v1.63.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.8
|
gorm.io/gorm v1.25.10
|
||||||
sigs.k8s.io/yaml v1.4.0
|
sigs.k8s.io/yaml v1.4.0
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -42,14 +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/golang/protobuf v1.5.3 // 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
|
||||||
@@ -57,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
|
||||||
@@ -76,11 +75,9 @@ require (
|
|||||||
go.uber.org/multierr v1.9.0 // indirect
|
go.uber.org/multierr v1.9.0 // indirect
|
||||||
golang.org/x/arch v0.3.0 // indirect
|
golang.org/x/arch v0.3.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||||
golang.org/x/net v0.22.0 // indirect
|
golang.org/x/sys v0.22.0 // indirect
|
||||||
golang.org/x/sys v0.18.0 // indirect
|
|
||||||
golang.org/x/time v0.5.0 // indirect
|
golang.org/x/time v0.5.0 // indirect
|
||||||
google.golang.org/appengine v1.6.8 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect
|
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
code.cloudfoundry.org/bytefmt v0.0.0-20240322144145-4816192cb114 h1:7KJsK0gPah7XfO46ARJA2TUXHZaJEnPI80mzxQLre68=
|
code.cloudfoundry.org/bytefmt v0.0.0-20240425163905-bcdc1ad063ea h1:1tgMNDgo8PjpsHhlaxdibj28C0WyLeOW2SPJ7GGdc9A=
|
||||||
code.cloudfoundry.org/bytefmt v0.0.0-20240322144145-4816192cb114/go.mod h1:TNAH6xe5WOszc47+zM7NnpaJWBR8oYt12BUf4WLIZ98=
|
code.cloudfoundry.org/bytefmt v0.0.0-20240425163905-bcdc1ad063ea/go.mod h1:3+xXJBOD8PsGHDqHedtCLalbaVJ+yi1OW+mXx9IcNxI=
|
||||||
code.gitea.io/sdk/gitea v0.17.1 h1:3jCPOG2ojbl8AcfaUCRYLT5MUcBMFwS0OSK2mA5Zok8=
|
code.gitea.io/sdk/gitea v0.18.0 h1:+zZrwVmujIrgobt6wVBWCqITz6bn1aBjnCUHmpZrerI=
|
||||||
code.gitea.io/sdk/gitea v0.17.1/go.mod h1:aCnBqhHpoEWA180gMbaCtdX9Pl6BWBAuuP2miadoTNM=
|
code.gitea.io/sdk/gitea v0.18.0/go.mod h1:IG9xZJoltDNeDSW0qiF2Vqx5orMWa7OhVWrjvrd5NpI=
|
||||||
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
|
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
|
||||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||||
@@ -10,6 +10,8 @@ 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=
|
||||||
@@ -17,8 +19,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1
|
|||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
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.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg=
|
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/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=
|
||||||
@@ -34,6 +36,8 @@ 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-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=
|
||||||
@@ -54,9 +58,6 @@ github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGF
|
|||||||
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=
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
|
||||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
|
||||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
|
||||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.2/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.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=
|
||||||
@@ -67,17 +68,16 @@ github.com/google/go-github/v47 v47.1.0/go.mod h1:VPZBXNbFSJGjyjFRUKo9vZGawTajnW
|
|||||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/pprof v0.0.0-20240320155624-b11c3daa6f07 h1:57oOH2Mu5Nw16KnZAVLdlUjmPH/TSYCKTJgG0OVfX0Y=
|
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg=
|
||||||
github.com/google/pprof v0.0.0-20240320155624-b11c3daa6f07/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
|
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-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.2 h1:AcYqCvkpalPnPF2pn0KamgwamS42TqUDDYFRKq/RAd0=
|
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.2/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
|
|
||||||
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
||||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
|
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
|
||||||
@@ -112,8 +112,8 @@ github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3v
|
|||||||
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-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=
|
||||||
@@ -125,10 +125,10 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
|
|||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
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.0 h1:kdnunFXpBjbzN56hcJHrXZ8M+LOkenKA7NnBzTNigTI=
|
github.com/onsi/ginkgo/v2 v2.17.1 h1:V++EzdbhI4ZV4ev0UTIj0PzhzOcReJFyJaLjtSF55M8=
|
||||||
github.com/onsi/ginkgo/v2 v2.17.0/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs=
|
github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs=
|
||||||
github.com/onsi/gomega v1.32.0 h1:JRYU78fJ1LPxlckP6Txi/EYqJvjtMrDC04/MM5XRHPk=
|
github.com/onsi/gomega v1.33.0 h1:snPCflnZrpMsy94p4lXVEkHo12lmPnc3vY5XBbreexE=
|
||||||
github.com/onsi/gomega v1.32.0/go.mod h1:a4x4gW6Pz2yK1MAmvluYme5lvYTn61afQ2ETw/8n4Lg=
|
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=
|
||||||
@@ -152,8 +152,6 @@ github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6ke
|
|||||||
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=
|
||||||
@@ -167,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=
|
||||||
@@ -186,9 +183,8 @@ github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6
|
|||||||
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
||||||
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||||
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||||
github.com/xanzy/go-gitlab v0.100.0 h1:jaOtYj5nWI19+9oVVmgy233pax2oYqucwetogYU46ks=
|
github.com/xanzy/go-gitlab v0.103.0 h1:J9pTQoq0GsEFqzd6srCM1QfdfKAxSNz6mT6ntrpNF2w=
|
||||||
github.com/xanzy/go-gitlab v0.100.0/go.mod h1:ETg8tcj4OhrB84UEgeE8dSuV/0h4BBL1uOV/qK0vlyI=
|
github.com/xanzy/go-gitlab v0.103.0/go.mod h1:ETg8tcj4OhrB84UEgeE8dSuV/0h4BBL1uOV/qK0vlyI=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
|
||||||
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
||||||
@@ -200,81 +196,50 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
|||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
||||||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
||||||
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
|
||||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
|
||||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
||||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
|
||||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
|
||||||
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
|
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||||
golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI=
|
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
|
||||||
golang.org/x/sync v0.6.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=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
|
||||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
|
||||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
|
||||||
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
|
||||||
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
|
|
||||||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
|
||||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
|
||||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
|
||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
|
||||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
|
||||||
golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
|
|
||||||
golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY=
|
||||||
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
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-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM=
|
google.golang.org/grpc v1.63.0 h1:WjKe+dnvABXyPJMD7KDNLxtoGk5tgk+YFWN6cBWjZE8=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s=
|
google.golang.org/grpc v1.63.0/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
|
||||||
google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
|
|
||||||
google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
|
|
||||||
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.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
|
||||||
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=
|
||||||
@@ -291,8 +256,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
|||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gorm.io/driver/sqlite v1.5.5 h1:7MDMtUZhV065SilG62E0MquljeArQZNfJnjd9i9gx3E=
|
gorm.io/driver/sqlite v1.5.5 h1:7MDMtUZhV065SilG62E0MquljeArQZNfJnjd9i9gx3E=
|
||||||
gorm.io/driver/sqlite v1.5.5/go.mod h1:6NgQ7sQWAIFsPrJJl1lSNSu2TABh0ZZ/zm5fosATavE=
|
gorm.io/driver/sqlite v1.5.5/go.mod h1:6NgQ7sQWAIFsPrJJl1lSNSu2TABh0ZZ/zm5fosATavE=
|
||||||
gorm.io/gorm v1.25.8 h1:WAGEZ/aEcznN4D03laj8DKnehe1e9gYQAjW8xyPRdeo=
|
gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s=
|
||||||
gorm.io/gorm v1.25.8/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||||
|
|||||||
+48
-19
@@ -22,7 +22,7 @@ var Themes = map[string]string{
|
|||||||
"mdui": "Neko Mdui",
|
"mdui": "Neko Mdui",
|
||||||
"hotaru": "Hotaru",
|
"hotaru": "Hotaru",
|
||||||
"angel-kanade": "AngelKanade",
|
"angel-kanade": "AngelKanade",
|
||||||
"server-status": "SeverStatus",
|
"server-status": "ServerStatus",
|
||||||
"custom": "Custom(local)",
|
"custom": "Custom(local)",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,11 +32,13 @@ var DashboardThemes = map[string]string{
|
|||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ConfigTypeGitHub = "github"
|
ConfigTypeGitHub = "github"
|
||||||
ConfigTypeGitee = "gitee"
|
ConfigTypeGitee = "gitee"
|
||||||
ConfigTypeGitlab = "gitlab"
|
ConfigTypeGitlab = "gitlab"
|
||||||
ConfigTypeJihulab = "jihulab"
|
ConfigTypeJihulab = "jihulab"
|
||||||
ConfigTypeGitea = "gitea"
|
ConfigTypeGitea = "gitea"
|
||||||
|
ConfigTypeCloudflare = "cloudflare"
|
||||||
|
ConfigTypeOidc = "oidc"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -70,7 +72,7 @@ func (c *AgentConfig) Save() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return os.WriteFile(c.v.ConfigFileUsed(), data, os.ModePerm)
|
return os.WriteFile(c.v.ConfigFileUsed(), data, 0600)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config 站点配置
|
// Config 站点配置
|
||||||
@@ -86,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
|
||||||
@@ -125,9 +137,20 @@ type Config struct {
|
|||||||
WebhookRequestBody string
|
WebhookRequestBody string
|
||||||
WebhookHeaders string
|
WebhookHeaders string
|
||||||
MaxRetries uint32
|
MaxRetries uint32
|
||||||
|
Profiles map[string]DDNSProfile
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DDNSProfile struct {
|
||||||
|
Provider string
|
||||||
|
AccessID string
|
||||||
|
AccessSecret string
|
||||||
|
WebhookURL string
|
||||||
|
WebhookMethod string
|
||||||
|
WebhookRequestBody string
|
||||||
|
WebhookHeaders string
|
||||||
|
}
|
||||||
|
|
||||||
// Read 读取配置文件并应用
|
// Read 读取配置文件并应用
|
||||||
func (c *Config) Read(path string) error {
|
func (c *Config) Read(path string) error {
|
||||||
c.v = viper.New()
|
c.v = viper.New()
|
||||||
@@ -166,15 +189,21 @@ func (c *Config) Read(path string) error {
|
|||||||
if c.AvgPingCount == 0 {
|
if c.AvgPingCount == 0 {
|
||||||
c.AvgPingCount = 2
|
c.AvgPingCount = 2
|
||||||
}
|
}
|
||||||
if c.DDNS.Provider == "" {
|
|
||||||
c.DDNS.Provider = "webhook"
|
|
||||||
}
|
|
||||||
if c.DDNS.WebhookMethod == "" {
|
|
||||||
c.DDNS.WebhookMethod = "POST"
|
|
||||||
}
|
|
||||||
if c.DDNS.MaxRetries == 0 {
|
if c.DDNS.MaxRetries == 0 {
|
||||||
c.DDNS.MaxRetries = 3
|
c.DDNS.MaxRetries = 3
|
||||||
}
|
}
|
||||||
|
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
|
||||||
@@ -199,5 +228,5 @@ func (c *Config) Save() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return os.WriteFile(c.v.ConfigFileUsed(), data, os.ModePerm)
|
return os.WriteFile(c.v.ConfigFileUsed(), data, 0600)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,11 @@ const (
|
|||||||
MTReportHostState
|
MTReportHostState
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type SensorTemperature struct {
|
||||||
|
Name string
|
||||||
|
Temperature float64
|
||||||
|
}
|
||||||
|
|
||||||
type HostState struct {
|
type HostState struct {
|
||||||
CPU float64
|
CPU float64
|
||||||
MemUsed uint64
|
MemUsed uint64
|
||||||
@@ -26,9 +31,19 @@ type HostState struct {
|
|||||||
TcpConnCount uint64
|
TcpConnCount uint64
|
||||||
UdpConnCount uint64
|
UdpConnCount uint64
|
||||||
ProcessCount uint64
|
ProcessCount uint64
|
||||||
|
Temperatures []SensorTemperature
|
||||||
|
GPU float64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *HostState) PB() *pb.State {
|
func (s *HostState) PB() *pb.State {
|
||||||
|
var ts []*pb.State_SensorTemperature
|
||||||
|
for _, t := range s.Temperatures {
|
||||||
|
ts = append(ts, &pb.State_SensorTemperature{
|
||||||
|
Name: t.Name,
|
||||||
|
Temperature: t.Temperature,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return &pb.State{
|
return &pb.State{
|
||||||
Cpu: s.CPU,
|
Cpu: s.CPU,
|
||||||
MemUsed: s.MemUsed,
|
MemUsed: s.MemUsed,
|
||||||
@@ -45,10 +60,20 @@ func (s *HostState) PB() *pb.State {
|
|||||||
TcpConnCount: s.TcpConnCount,
|
TcpConnCount: s.TcpConnCount,
|
||||||
UdpConnCount: s.UdpConnCount,
|
UdpConnCount: s.UdpConnCount,
|
||||||
ProcessCount: s.ProcessCount,
|
ProcessCount: s.ProcessCount,
|
||||||
|
Temperatures: ts,
|
||||||
|
Gpu: s.GPU,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func PB2State(s *pb.State) HostState {
|
func PB2State(s *pb.State) HostState {
|
||||||
|
var ts []SensorTemperature
|
||||||
|
for _, t := range s.GetTemperatures() {
|
||||||
|
ts = append(ts, SensorTemperature{
|
||||||
|
Name: t.GetName(),
|
||||||
|
Temperature: t.GetTemperature(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return HostState{
|
return HostState{
|
||||||
CPU: s.GetCpu(),
|
CPU: s.GetCpu(),
|
||||||
MemUsed: s.GetMemUsed(),
|
MemUsed: s.GetMemUsed(),
|
||||||
@@ -65,6 +90,8 @@ func PB2State(s *pb.State) HostState {
|
|||||||
TcpConnCount: s.GetTcpConnCount(),
|
TcpConnCount: s.GetTcpConnCount(),
|
||||||
UdpConnCount: s.GetUdpConnCount(),
|
UdpConnCount: s.GetUdpConnCount(),
|
||||||
ProcessCount: s.GetProcessCount(),
|
ProcessCount: s.GetProcessCount(),
|
||||||
|
Temperatures: ts,
|
||||||
|
GPU: s.GetGpu(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,6 +108,7 @@ type Host struct {
|
|||||||
IP string `json:"-"`
|
IP string `json:"-"`
|
||||||
CountryCode string
|
CountryCode string
|
||||||
Version string
|
Version string
|
||||||
|
GPU []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Host) PB() *pb.Host {
|
func (h *Host) PB() *pb.Host {
|
||||||
@@ -97,6 +125,7 @@ func (h *Host) PB() *pb.Host {
|
|||||||
Ip: h.IP,
|
Ip: h.IP,
|
||||||
CountryCode: h.CountryCode,
|
CountryCode: h.CountryCode,
|
||||||
Version: h.Version,
|
Version: h.Version,
|
||||||
|
Gpu: h.GPU,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,5 +143,6 @@ func PB2Host(h *pb.Host) Host {
|
|||||||
IP: h.GetIp(),
|
IP: h.GetIp(),
|
||||||
CountryCode: h.GetCountryCode(),
|
CountryCode: h.GetCountryCode(),
|
||||||
Version: h.GetVersion(),
|
Version: h.GetVersion(),
|
||||||
|
GPU: h.GetGpu(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+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 (
|
||||||
|
|||||||
@@ -19,4 +19,3 @@ type MonitorHistory struct {
|
|||||||
Down uint64 // 检查状态异常计数
|
Down uint64 // 检查状态异常计数
|
||||||
Data string
|
Data string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 循环区间流量检测 · 更新下次需要检测时间
|
// 循环区间流量检测 · 更新下次需要检测时间
|
||||||
|
|||||||
+5
-1
@@ -18,7 +18,10 @@ type Server struct {
|
|||||||
DisplayIndex int // 展示排序,越大越靠前
|
DisplayIndex int // 展示排序,越大越靠前
|
||||||
HideForGuest bool // 对游客隐藏
|
HideForGuest bool // 对游客隐藏
|
||||||
EnableDDNS bool // 是否启用DDNS 未在配置文件中启用DDNS 或 DDNS检查时间为0时此项无效
|
EnableDDNS bool // 是否启用DDNS 未在配置文件中启用DDNS 或 DDNS检查时间为0时此项无效
|
||||||
|
EnableIPv4 bool // 是否启用DDNS IPv4
|
||||||
|
EnableIpv6 bool // 是否启用DDNS IPv6
|
||||||
DDNSDomain string // DDNS中的前缀 如基础域名为abc.oracle DDNSName为mjj 就会把mjj.abc.oracle解析服务器IP 为空则停用
|
DDNSDomain string // DDNS中的前缀 如基础域名为abc.oracle DDNSName为mjj 就会把mjj.abc.oracle解析服务器IP 为空则停用
|
||||||
|
DDNSProfile string // DDNS配置
|
||||||
|
|
||||||
Host *Host `gorm:"-"`
|
Host *Host `gorm:"-"`
|
||||||
State *HostState `gorm:"-"`
|
State *HostState `gorm:"-"`
|
||||||
@@ -54,5 +57,6 @@ func (s Server) Marshal() template.JS {
|
|||||||
note, _ := utils.Json.Marshal(s.Note)
|
note, _ := utils.Json.Marshal(s.Note)
|
||||||
secret, _ := utils.Json.Marshal(s.Secret)
|
secret, _ := utils.Json.Marshal(s.Secret)
|
||||||
ddnsDomain, _ := utils.Json.Marshal(s.DDNSDomain)
|
ddnsDomain, _ := utils.Json.Marshal(s.DDNSDomain)
|
||||||
return template.JS(fmt.Sprintf(`{"ID":%d,"Name":%s,"Secret":%s,"DisplayIndex":%d,"Tag":%s,"Note":%s,"HideForGuest": %s,"EnableDDNS": %s,"DDNSDomain": %s}`, s.ID, name, secret, s.DisplayIndex, tag, note, boolToString(s.HideForGuest), boolToString(s.EnableDDNS), ddnsDomain)) // #nosec
|
ddnsProfile, _ := utils.Json.Marshal(s.DDNSProfile)
|
||||||
|
return template.JS(fmt.Sprintf(`{"ID":%d,"Name":%s,"Secret":%s,"DisplayIndex":%d,"Tag":%s,"Note":%s,"HideForGuest": %s,"EnableDDNS": %s,"EnableIPv4": %s,"EnableIpv6": %s,"DDNSDomain": %s,"DDNSProfile": %s}`, s.ID, name, secret, s.DisplayIndex, tag, note, boolToString(s.HideForGuest), boolToString(s.EnableDDNS), boolToString(s.EnableIPv4), boolToString(s.EnableIpv6), ddnsDomain, ddnsProfile)) // #nosec
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-26
@@ -1,7 +1,7 @@
|
|||||||
package ddns
|
package ddns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"golang.org/x/net/publicsuffix"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@@ -33,29 +33,8 @@ func SetStringHeadersToRequest(req *http.Request, headers []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SplitDomain 分割域名为前缀和一级域名
|
// SplitDomain 分割域名为前缀和一级域名
|
||||||
func SplitDomain(domain string) (prefix string, topLevelDomain string) {
|
func SplitDomain(domain string) (prefix string, realDomain string) {
|
||||||
// 带有二级TLD的一些常见例子,需要特别处理
|
realDomain, _ = publicsuffix.EffectiveTLDPlusOne(domain)
|
||||||
secondLevelTLDs := map[string]bool{
|
prefix = domain[:len(domain)-len(realDomain)-1]
|
||||||
".co.uk": true, ".com.cn": true, ".gov.cn": true, ".net.cn": true, ".org.cn": true,
|
return prefix, realDomain
|
||||||
}
|
|
||||||
|
|
||||||
// 分割域名为"."的各部分
|
|
||||||
parts := strings.Split(domain, ".")
|
|
||||||
|
|
||||||
// 处理特殊情况,例如 ".co.uk"
|
|
||||||
for i := len(parts) - 2; i > 0; i-- {
|
|
||||||
potentialTLD := fmt.Sprintf(".%s.%s", parts[i], parts[i+1])
|
|
||||||
if secondLevelTLDs[potentialTLD] {
|
|
||||||
if i > 1 {
|
|
||||||
return strings.Join(parts[:i-1], "."), strings.Join(parts[i-1:], ".")
|
|
||||||
}
|
|
||||||
return "", domain // 当域名仅为二级TLD时,无前缀
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 常规处理,查找最后一个"."前的所有内容作为前缀
|
|
||||||
if len(parts) > 2 {
|
|
||||||
return strings.Join(parts[:len(parts)-2], "."), strings.Join(parts[len(parts)-2:], ".")
|
|
||||||
}
|
|
||||||
return "", domain // 当域名不包含子域名时,无前缀
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,228 @@
|
|||||||
|
package ddns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
url = "https://dnspod.tencentcloudapi.com"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ProviderTencentCloud struct {
|
||||||
|
SecretID string
|
||||||
|
SecretKey string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *ProviderTencentCloud) UpdateDomain(domainConfig *DomainConfig) bool {
|
||||||
|
if domainConfig == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 当IPv4和IPv6同时成功才算作成功
|
||||||
|
var resultV4 = true
|
||||||
|
var resultV6 = true
|
||||||
|
if domainConfig.EnableIPv4 {
|
||||||
|
if !provider.addDomainRecord(domainConfig, true) {
|
||||||
|
resultV4 = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if domainConfig.EnableIpv6 {
|
||||||
|
if !provider.addDomainRecord(domainConfig, false) {
|
||||||
|
resultV6 = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultV4 && resultV6
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *ProviderTencentCloud) addDomainRecord(domainConfig *DomainConfig, isIpv4 bool) bool {
|
||||||
|
record, err := provider.findDNSRecord(domainConfig.FullDomain, isIpv4)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("查找 DNS 记录时出错: %s\n", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if errResponse, ok := record["Error"].(map[string]interface{}); ok {
|
||||||
|
if errCode, ok := errResponse["Code"].(string); ok && errCode == "ResourceNotFound.NoDataOfRecord" { // 没有找到 DNS 记录
|
||||||
|
// 添加 DNS 记录
|
||||||
|
return provider.createDNSRecord(domainConfig.FullDomain, domainConfig, isIpv4)
|
||||||
|
} else {
|
||||||
|
log.Printf("查询 DNS 记录时出错,错误代码为: %s\n", errCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 默认情况下更新 DNS 记录
|
||||||
|
return provider.updateDNSRecord(domainConfig.FullDomain, record["RecordList"].([]interface{})[0].(map[string]interface{})["RecordId"].(float64), domainConfig, isIpv4)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *ProviderTencentCloud) findDNSRecord(domain string, isIPv4 bool) (map[string]interface{}, error) {
|
||||||
|
var ipType = "A"
|
||||||
|
if !isIPv4 {
|
||||||
|
ipType = "AAAA"
|
||||||
|
}
|
||||||
|
_, realDomain := SplitDomain(domain)
|
||||||
|
prefix, _ := SplitDomain(domain)
|
||||||
|
data := map[string]interface{}{
|
||||||
|
"RecordType": ipType,
|
||||||
|
"Domain": realDomain,
|
||||||
|
"RecordLine": "默认",
|
||||||
|
"Subdomain": prefix,
|
||||||
|
}
|
||||||
|
jsonData, _ := json.Marshal(data)
|
||||||
|
body, err := provider.sendRequest("DescribeRecordList", jsonData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var res map[string]interface{}
|
||||||
|
err = json.Unmarshal(body, &res)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result := res["Response"].(map[string]interface{})
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *ProviderTencentCloud) createDNSRecord(domain string, domainConfig *DomainConfig, isIPv4 bool) bool {
|
||||||
|
var ipType = "A"
|
||||||
|
var ipAddr = domainConfig.Ipv4Addr
|
||||||
|
if !isIPv4 {
|
||||||
|
ipType = "AAAA"
|
||||||
|
ipAddr = domainConfig.Ipv6Addr
|
||||||
|
}
|
||||||
|
_, realDomain := SplitDomain(domain)
|
||||||
|
prefix, _ := SplitDomain(domain)
|
||||||
|
data := map[string]interface{}{
|
||||||
|
"RecordType": ipType,
|
||||||
|
"RecordLine": "默认",
|
||||||
|
"Domain": realDomain,
|
||||||
|
"SubDomain": prefix,
|
||||||
|
"Value": ipAddr,
|
||||||
|
"TTL": 600,
|
||||||
|
}
|
||||||
|
jsonData, _ := json.Marshal(data)
|
||||||
|
_, err := provider.sendRequest("CreateRecord", jsonData)
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *ProviderTencentCloud) updateDNSRecord(domain string, recordID float64, domainConfig *DomainConfig, isIPv4 bool) bool {
|
||||||
|
var ipType = "A"
|
||||||
|
var ipAddr = domainConfig.Ipv4Addr
|
||||||
|
if !isIPv4 {
|
||||||
|
ipType = "AAAA"
|
||||||
|
ipAddr = domainConfig.Ipv6Addr
|
||||||
|
}
|
||||||
|
_, realDomain := SplitDomain(domain)
|
||||||
|
prefix, _ := SplitDomain(domain)
|
||||||
|
data := map[string]interface{}{
|
||||||
|
"RecordType": ipType,
|
||||||
|
"RecordLine": "默认",
|
||||||
|
"Domain": realDomain,
|
||||||
|
"SubDomain": prefix,
|
||||||
|
"Value": ipAddr,
|
||||||
|
"TTL": 600,
|
||||||
|
"RecordId": recordID,
|
||||||
|
}
|
||||||
|
jsonData, _ := json.Marshal(data)
|
||||||
|
_, err := provider.sendRequest("ModifyRecord", jsonData)
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 以下为辅助方法,如发送 HTTP 请求等
|
||||||
|
func (provider *ProviderTencentCloud) sendRequest(action string, data []byte) ([]byte, error) {
|
||||||
|
client := &http.Client{}
|
||||||
|
req, err := http.NewRequest("POST", url, bytes.NewBuffer(data))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
req.Header.Set("X-TC-Version", "2021-03-23")
|
||||||
|
|
||||||
|
provider.signRequest(provider.SecretID, provider.SecretKey, req, action, string(data))
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer func(Body io.ReadCloser) {
|
||||||
|
err := Body.Close()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("NEZHA>> 无法关闭HTTP响应体流: %s\n", err.Error())
|
||||||
|
}
|
||||||
|
}(resp.Body)
|
||||||
|
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return body, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/jeessy2/ddns-go/blob/master/util/tencent_cloud_signer.go
|
||||||
|
|
||||||
|
func (provider *ProviderTencentCloud) sha256hex(s string) string {
|
||||||
|
b := sha256.Sum256([]byte(s))
|
||||||
|
return hex.EncodeToString(b[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *ProviderTencentCloud) hmacsha256(s, key string) string {
|
||||||
|
hashed := hmac.New(sha256.New, []byte(key))
|
||||||
|
hashed.Write([]byte(s))
|
||||||
|
return string(hashed.Sum(nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *ProviderTencentCloud) WriteString(strs ...string) string {
|
||||||
|
var b strings.Builder
|
||||||
|
for _, str := range strs {
|
||||||
|
b.WriteString(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *ProviderTencentCloud) signRequest(secretId string, secretKey string, r *http.Request, action string, payload string) {
|
||||||
|
algorithm := "TC3-HMAC-SHA256"
|
||||||
|
service := "dnspod"
|
||||||
|
host := provider.WriteString(service, ".tencentcloudapi.com")
|
||||||
|
timestamp := time.Now().Unix()
|
||||||
|
timestampStr := strconv.FormatInt(timestamp, 10)
|
||||||
|
|
||||||
|
// 步骤 1:拼接规范请求串
|
||||||
|
canonicalHeaders := provider.WriteString("content-type:application/json\nhost:", host, "\nx-tc-action:", strings.ToLower(action), "\n")
|
||||||
|
signedHeaders := "content-type;host;x-tc-action"
|
||||||
|
hashedRequestPayload := provider.sha256hex(payload)
|
||||||
|
canonicalRequest := provider.WriteString("POST\n/\n\n", canonicalHeaders, "\n", signedHeaders, "\n", hashedRequestPayload)
|
||||||
|
|
||||||
|
// 步骤 2:拼接待签名字符串
|
||||||
|
date := time.Unix(timestamp, 0).UTC().Format("2006-01-02")
|
||||||
|
credentialScope := provider.WriteString(date, "/", service, "/tc3_request")
|
||||||
|
hashedCanonicalRequest := provider.sha256hex(canonicalRequest)
|
||||||
|
string2sign := provider.WriteString(algorithm, "\n", timestampStr, "\n", credentialScope, "\n", hashedCanonicalRequest)
|
||||||
|
|
||||||
|
// 步骤 3:计算签名
|
||||||
|
secretDate := provider.hmacsha256(date, provider.WriteString("TC3", secretKey))
|
||||||
|
secretService := provider.hmacsha256(service, secretDate)
|
||||||
|
secretSigning := provider.hmacsha256("tc3_request", secretService)
|
||||||
|
signature := hex.EncodeToString([]byte(provider.hmacsha256(string2sign, secretSigning)))
|
||||||
|
|
||||||
|
// 步骤 4:拼接 Authorization
|
||||||
|
authorization := provider.WriteString(algorithm, " Credential=", secretId, "/", credentialScope, ", SignedHeaders=", signedHeaders, ", Signature=", signature)
|
||||||
|
|
||||||
|
r.Header.Add("Authorization", authorization)
|
||||||
|
r.Header.Set("Host", host)
|
||||||
|
r.Header.Set("X-TC-Action", action)
|
||||||
|
r.Header.Add("X-TC-Timestamp", timestampStr)
|
||||||
|
}
|
||||||
+1
-1
@@ -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,22 @@
|
|||||||
|
package cloudflare
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/naiba/nezha/model"
|
||||||
|
"github.com/naiba/nezha/service/singleton"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserInfo struct {
|
||||||
|
Sub string `json:"sub"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Groups []string `json:"groups"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u UserInfo) MapToNezhaUser() model.User {
|
||||||
|
var user model.User
|
||||||
|
singleton.DB.Where("login = ?", u.Sub).First(&user)
|
||||||
|
user.Login = u.Sub
|
||||||
|
user.Email = u.Email
|
||||||
|
user.Name = u.Name
|
||||||
|
return user
|
||||||
|
}
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
+285
-112
@@ -1,7 +1,7 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.30.0
|
// protoc-gen-go v1.34.2
|
||||||
// protoc v3.21.12
|
// protoc v5.27.1
|
||||||
// source: proto/nezha.proto
|
// source: proto/nezha.proto
|
||||||
|
|
||||||
package proto
|
package proto
|
||||||
@@ -37,6 +37,7 @@ type Host struct {
|
|||||||
Ip string `protobuf:"bytes,10,opt,name=ip,proto3" json:"ip,omitempty"`
|
Ip string `protobuf:"bytes,10,opt,name=ip,proto3" json:"ip,omitempty"`
|
||||||
CountryCode string `protobuf:"bytes,11,opt,name=country_code,json=countryCode,proto3" json:"country_code,omitempty"`
|
CountryCode string `protobuf:"bytes,11,opt,name=country_code,json=countryCode,proto3" json:"country_code,omitempty"`
|
||||||
Version string `protobuf:"bytes,12,opt,name=version,proto3" json:"version,omitempty"`
|
Version string `protobuf:"bytes,12,opt,name=version,proto3" json:"version,omitempty"`
|
||||||
|
Gpu []string `protobuf:"bytes,13,rep,name=gpu,proto3" json:"gpu,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Host) Reset() {
|
func (x *Host) Reset() {
|
||||||
@@ -155,26 +156,35 @@ func (x *Host) GetVersion() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *Host) GetGpu() []string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Gpu
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type State struct {
|
type State struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
Cpu float64 `protobuf:"fixed64,1,opt,name=cpu,proto3" json:"cpu,omitempty"`
|
Cpu float64 `protobuf:"fixed64,1,opt,name=cpu,proto3" json:"cpu,omitempty"`
|
||||||
MemUsed uint64 `protobuf:"varint,3,opt,name=mem_used,json=memUsed,proto3" json:"mem_used,omitempty"`
|
MemUsed uint64 `protobuf:"varint,3,opt,name=mem_used,json=memUsed,proto3" json:"mem_used,omitempty"`
|
||||||
SwapUsed uint64 `protobuf:"varint,4,opt,name=swap_used,json=swapUsed,proto3" json:"swap_used,omitempty"`
|
SwapUsed uint64 `protobuf:"varint,4,opt,name=swap_used,json=swapUsed,proto3" json:"swap_used,omitempty"`
|
||||||
DiskUsed uint64 `protobuf:"varint,5,opt,name=disk_used,json=diskUsed,proto3" json:"disk_used,omitempty"`
|
DiskUsed uint64 `protobuf:"varint,5,opt,name=disk_used,json=diskUsed,proto3" json:"disk_used,omitempty"`
|
||||||
NetInTransfer uint64 `protobuf:"varint,6,opt,name=net_in_transfer,json=netInTransfer,proto3" json:"net_in_transfer,omitempty"`
|
NetInTransfer uint64 `protobuf:"varint,6,opt,name=net_in_transfer,json=netInTransfer,proto3" json:"net_in_transfer,omitempty"`
|
||||||
NetOutTransfer uint64 `protobuf:"varint,7,opt,name=net_out_transfer,json=netOutTransfer,proto3" json:"net_out_transfer,omitempty"`
|
NetOutTransfer uint64 `protobuf:"varint,7,opt,name=net_out_transfer,json=netOutTransfer,proto3" json:"net_out_transfer,omitempty"`
|
||||||
NetInSpeed uint64 `protobuf:"varint,8,opt,name=net_in_speed,json=netInSpeed,proto3" json:"net_in_speed,omitempty"`
|
NetInSpeed uint64 `protobuf:"varint,8,opt,name=net_in_speed,json=netInSpeed,proto3" json:"net_in_speed,omitempty"`
|
||||||
NetOutSpeed uint64 `protobuf:"varint,9,opt,name=net_out_speed,json=netOutSpeed,proto3" json:"net_out_speed,omitempty"`
|
NetOutSpeed uint64 `protobuf:"varint,9,opt,name=net_out_speed,json=netOutSpeed,proto3" json:"net_out_speed,omitempty"`
|
||||||
Uptime uint64 `protobuf:"varint,10,opt,name=uptime,proto3" json:"uptime,omitempty"`
|
Uptime uint64 `protobuf:"varint,10,opt,name=uptime,proto3" json:"uptime,omitempty"`
|
||||||
Load1 float64 `protobuf:"fixed64,11,opt,name=load1,proto3" json:"load1,omitempty"`
|
Load1 float64 `protobuf:"fixed64,11,opt,name=load1,proto3" json:"load1,omitempty"`
|
||||||
Load5 float64 `protobuf:"fixed64,12,opt,name=load5,proto3" json:"load5,omitempty"`
|
Load5 float64 `protobuf:"fixed64,12,opt,name=load5,proto3" json:"load5,omitempty"`
|
||||||
Load15 float64 `protobuf:"fixed64,13,opt,name=load15,proto3" json:"load15,omitempty"`
|
Load15 float64 `protobuf:"fixed64,13,opt,name=load15,proto3" json:"load15,omitempty"`
|
||||||
TcpConnCount uint64 `protobuf:"varint,14,opt,name=tcp_conn_count,json=tcpConnCount,proto3" json:"tcp_conn_count,omitempty"`
|
TcpConnCount uint64 `protobuf:"varint,14,opt,name=tcp_conn_count,json=tcpConnCount,proto3" json:"tcp_conn_count,omitempty"`
|
||||||
UdpConnCount uint64 `protobuf:"varint,15,opt,name=udp_conn_count,json=udpConnCount,proto3" json:"udp_conn_count,omitempty"`
|
UdpConnCount uint64 `protobuf:"varint,15,opt,name=udp_conn_count,json=udpConnCount,proto3" json:"udp_conn_count,omitempty"`
|
||||||
ProcessCount uint64 `protobuf:"varint,16,opt,name=process_count,json=processCount,proto3" json:"process_count,omitempty"`
|
ProcessCount uint64 `protobuf:"varint,16,opt,name=process_count,json=processCount,proto3" json:"process_count,omitempty"`
|
||||||
|
Temperatures []*State_SensorTemperature `protobuf:"bytes,17,rep,name=temperatures,proto3" json:"temperatures,omitempty"`
|
||||||
|
Gpu float64 `protobuf:"fixed64,18,opt,name=gpu,proto3" json:"gpu,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *State) Reset() {
|
func (x *State) Reset() {
|
||||||
@@ -314,6 +324,75 @@ func (x *State) GetProcessCount() uint64 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *State) GetTemperatures() []*State_SensorTemperature {
|
||||||
|
if x != nil {
|
||||||
|
return x.Temperatures
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *State) GetGpu() float64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Gpu
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type State_SensorTemperature struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||||
|
Temperature float64 `protobuf:"fixed64,2,opt,name=temperature,proto3" json:"temperature,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *State_SensorTemperature) Reset() {
|
||||||
|
*x = State_SensorTemperature{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_proto_nezha_proto_msgTypes[2]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *State_SensorTemperature) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*State_SensorTemperature) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *State_SensorTemperature) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_proto_nezha_proto_msgTypes[2]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use State_SensorTemperature.ProtoReflect.Descriptor instead.
|
||||||
|
func (*State_SensorTemperature) Descriptor() ([]byte, []int) {
|
||||||
|
return file_proto_nezha_proto_rawDescGZIP(), []int{2}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *State_SensorTemperature) GetName() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Name
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *State_SensorTemperature) GetTemperature() float64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Temperature
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
type Task struct {
|
type Task struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
@@ -327,7 +406,7 @@ type Task struct {
|
|||||||
func (x *Task) Reset() {
|
func (x *Task) Reset() {
|
||||||
*x = Task{}
|
*x = Task{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_proto_nezha_proto_msgTypes[2]
|
mi := &file_proto_nezha_proto_msgTypes[3]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@@ -340,7 +419,7 @@ func (x *Task) String() string {
|
|||||||
func (*Task) ProtoMessage() {}
|
func (*Task) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *Task) ProtoReflect() protoreflect.Message {
|
func (x *Task) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_proto_nezha_proto_msgTypes[2]
|
mi := &file_proto_nezha_proto_msgTypes[3]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@@ -353,7 +432,7 @@ func (x *Task) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use Task.ProtoReflect.Descriptor instead.
|
// Deprecated: Use Task.ProtoReflect.Descriptor instead.
|
||||||
func (*Task) Descriptor() ([]byte, []int) {
|
func (*Task) Descriptor() ([]byte, []int) {
|
||||||
return file_proto_nezha_proto_rawDescGZIP(), []int{2}
|
return file_proto_nezha_proto_rawDescGZIP(), []int{3}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Task) GetId() uint64 {
|
func (x *Task) GetId() uint64 {
|
||||||
@@ -392,7 +471,7 @@ type TaskResult struct {
|
|||||||
func (x *TaskResult) Reset() {
|
func (x *TaskResult) Reset() {
|
||||||
*x = TaskResult{}
|
*x = TaskResult{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_proto_nezha_proto_msgTypes[3]
|
mi := &file_proto_nezha_proto_msgTypes[4]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@@ -405,7 +484,7 @@ func (x *TaskResult) String() string {
|
|||||||
func (*TaskResult) ProtoMessage() {}
|
func (*TaskResult) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *TaskResult) ProtoReflect() protoreflect.Message {
|
func (x *TaskResult) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_proto_nezha_proto_msgTypes[3]
|
mi := &file_proto_nezha_proto_msgTypes[4]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@@ -418,7 +497,7 @@ func (x *TaskResult) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use TaskResult.ProtoReflect.Descriptor instead.
|
// Deprecated: Use TaskResult.ProtoReflect.Descriptor instead.
|
||||||
func (*TaskResult) Descriptor() ([]byte, []int) {
|
func (*TaskResult) Descriptor() ([]byte, []int) {
|
||||||
return file_proto_nezha_proto_rawDescGZIP(), []int{3}
|
return file_proto_nezha_proto_rawDescGZIP(), []int{4}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *TaskResult) GetId() uint64 {
|
func (x *TaskResult) GetId() uint64 {
|
||||||
@@ -467,7 +546,7 @@ type Receipt struct {
|
|||||||
func (x *Receipt) Reset() {
|
func (x *Receipt) Reset() {
|
||||||
*x = Receipt{}
|
*x = Receipt{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_proto_nezha_proto_msgTypes[4]
|
mi := &file_proto_nezha_proto_msgTypes[5]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@@ -480,7 +559,7 @@ func (x *Receipt) String() string {
|
|||||||
func (*Receipt) ProtoMessage() {}
|
func (*Receipt) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *Receipt) ProtoReflect() protoreflect.Message {
|
func (x *Receipt) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_proto_nezha_proto_msgTypes[4]
|
mi := &file_proto_nezha_proto_msgTypes[5]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@@ -493,7 +572,7 @@ func (x *Receipt) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use Receipt.ProtoReflect.Descriptor instead.
|
// Deprecated: Use Receipt.ProtoReflect.Descriptor instead.
|
||||||
func (*Receipt) Descriptor() ([]byte, []int) {
|
func (*Receipt) Descriptor() ([]byte, []int) {
|
||||||
return file_proto_nezha_proto_rawDescGZIP(), []int{4}
|
return file_proto_nezha_proto_rawDescGZIP(), []int{5}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Receipt) GetProced() bool {
|
func (x *Receipt) GetProced() bool {
|
||||||
@@ -503,11 +582,58 @@ 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{
|
||||||
0x0a, 0x11, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6e, 0x65, 0x7a, 0x68, 0x61, 0x2e, 0x70, 0x72,
|
0x0a, 0x11, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6e, 0x65, 0x7a, 0x68, 0x61, 0x2e, 0x70, 0x72,
|
||||||
0x6f, 0x74, 0x6f, 0x12, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe0, 0x02, 0x0a, 0x04, 0x48,
|
0x6f, 0x74, 0x6f, 0x12, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf2, 0x02, 0x0a, 0x04, 0x48,
|
||||||
0x6f, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18,
|
0x6f, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18,
|
||||||
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12,
|
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12,
|
||||||
0x29, 0x0a, 0x10, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x5f, 0x76, 0x65, 0x72, 0x73,
|
0x29, 0x0a, 0x10, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x5f, 0x76, 0x65, 0x72, 0x73,
|
||||||
@@ -529,65 +655,83 @@ var file_proto_nezha_proto_rawDesc = []byte{
|
|||||||
0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x64, 0x65,
|
0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x64, 0x65,
|
||||||
0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x43,
|
0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x43,
|
||||||
0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0c,
|
0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0c,
|
||||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xd3, 0x03,
|
0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a,
|
||||||
0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x63, 0x70, 0x75, 0x18, 0x01,
|
0x03, 0x67, 0x70, 0x75, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x67, 0x70, 0x75, 0x22,
|
||||||
0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x63, 0x70, 0x75, 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x65, 0x6d,
|
0xa9, 0x04, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x63, 0x70, 0x75,
|
||||||
0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x6d, 0x65, 0x6d,
|
0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x63, 0x70, 0x75, 0x12, 0x19, 0x0a, 0x08, 0x6d,
|
||||||
0x55, 0x73, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x75, 0x73, 0x65,
|
0x65, 0x6d, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x6d,
|
||||||
0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x73, 0x77, 0x61, 0x70, 0x55, 0x73, 0x65,
|
0x65, 0x6d, 0x55, 0x73, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x75,
|
||||||
0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x05,
|
0x73, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x73, 0x77, 0x61, 0x70, 0x55,
|
||||||
0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x64, 0x69, 0x73, 0x6b, 0x55, 0x73, 0x65, 0x64, 0x12, 0x26,
|
0x73, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x75, 0x73, 0x65, 0x64,
|
||||||
0x0a, 0x0f, 0x6e, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65,
|
0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x64, 0x69, 0x73, 0x6b, 0x55, 0x73, 0x65, 0x64,
|
||||||
0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x6e, 0x65, 0x74, 0x49, 0x6e, 0x54, 0x72,
|
0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73,
|
||||||
0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x12, 0x28, 0x0a, 0x10, 0x6e, 0x65, 0x74, 0x5f, 0x6f, 0x75,
|
0x66, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x6e, 0x65, 0x74, 0x49, 0x6e,
|
||||||
0x74, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04,
|
0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x12, 0x28, 0x0a, 0x10, 0x6e, 0x65, 0x74, 0x5f,
|
||||||
0x52, 0x0e, 0x6e, 0x65, 0x74, 0x4f, 0x75, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72,
|
0x6f, 0x75, 0x74, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01,
|
||||||
0x12, 0x20, 0x0a, 0x0c, 0x6e, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x5f, 0x73, 0x70, 0x65, 0x65, 0x64,
|
0x28, 0x04, 0x52, 0x0e, 0x6e, 0x65, 0x74, 0x4f, 0x75, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66,
|
||||||
0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x6e, 0x65, 0x74, 0x49, 0x6e, 0x53, 0x70, 0x65,
|
0x65, 0x72, 0x12, 0x20, 0x0a, 0x0c, 0x6e, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x5f, 0x73, 0x70, 0x65,
|
||||||
0x65, 0x64, 0x12, 0x22, 0x0a, 0x0d, 0x6e, 0x65, 0x74, 0x5f, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x70,
|
0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x6e, 0x65, 0x74, 0x49, 0x6e, 0x53,
|
||||||
0x65, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6e, 0x65, 0x74, 0x4f, 0x75,
|
0x70, 0x65, 0x65, 0x64, 0x12, 0x22, 0x0a, 0x0d, 0x6e, 0x65, 0x74, 0x5f, 0x6f, 0x75, 0x74, 0x5f,
|
||||||
0x74, 0x53, 0x70, 0x65, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65,
|
0x73, 0x70, 0x65, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6e, 0x65, 0x74,
|
||||||
0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x14,
|
0x4f, 0x75, 0x74, 0x53, 0x70, 0x65, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x70, 0x74, 0x69,
|
||||||
0x0a, 0x05, 0x6c, 0x6f, 0x61, 0x64, 0x31, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x6c,
|
0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65,
|
||||||
0x6f, 0x61, 0x64, 0x31, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x6f, 0x61, 0x64, 0x35, 0x18, 0x0c, 0x20,
|
0x12, 0x14, 0x0a, 0x05, 0x6c, 0x6f, 0x61, 0x64, 0x31, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x01, 0x52,
|
||||||
0x01, 0x28, 0x01, 0x52, 0x05, 0x6c, 0x6f, 0x61, 0x64, 0x35, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x6f,
|
0x05, 0x6c, 0x6f, 0x61, 0x64, 0x31, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x6f, 0x61, 0x64, 0x35, 0x18,
|
||||||
0x61, 0x64, 0x31, 0x35, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x6c, 0x6f, 0x61, 0x64,
|
0x0c, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x6c, 0x6f, 0x61, 0x64, 0x35, 0x12, 0x16, 0x0a, 0x06,
|
||||||
0x31, 0x35, 0x12, 0x24, 0x0a, 0x0e, 0x74, 0x63, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x5f, 0x63,
|
0x6c, 0x6f, 0x61, 0x64, 0x31, 0x35, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x6c, 0x6f,
|
||||||
0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x74, 0x63, 0x70, 0x43,
|
0x61, 0x64, 0x31, 0x35, 0x12, 0x24, 0x0a, 0x0e, 0x74, 0x63, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x6e,
|
||||||
0x6f, 0x6e, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x75, 0x64, 0x70, 0x5f,
|
0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x74, 0x63,
|
||||||
0x63, 0x6f, 0x6e, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x04,
|
0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x75, 0x64,
|
||||||
0x52, 0x0c, 0x75, 0x64, 0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x23,
|
0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0f, 0x20, 0x01,
|
||||||
0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18,
|
0x28, 0x04, 0x52, 0x0c, 0x75, 0x64, 0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74,
|
||||||
0x10, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x43, 0x6f,
|
0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e,
|
||||||
0x75, 0x6e, 0x74, 0x22, 0x3e, 0x0a, 0x04, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x0e, 0x0a, 0x02, 0x69,
|
0x74, 0x18, 0x10, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73,
|
||||||
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74,
|
0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x42, 0x0a, 0x0c, 0x74, 0x65, 0x6d, 0x70, 0x65, 0x72, 0x61,
|
||||||
0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12,
|
0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x11, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x72,
|
||||||
0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64,
|
0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x53, 0x65, 0x6e, 0x73, 0x6f, 0x72,
|
||||||
0x61, 0x74, 0x61, 0x22, 0x7a, 0x0a, 0x0a, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c,
|
0x54, 0x65, 0x6d, 0x70, 0x65, 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x0c, 0x74, 0x65, 0x6d,
|
||||||
0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69,
|
0x70, 0x65, 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x67, 0x70, 0x75,
|
||||||
0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52,
|
0x18, 0x12, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x67, 0x70, 0x75, 0x22, 0x4f, 0x0a, 0x17, 0x53,
|
||||||
0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x03,
|
0x74, 0x61, 0x74, 0x65, 0x5f, 0x53, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x54, 0x65, 0x6d, 0x70, 0x65,
|
||||||
0x20, 0x01, 0x28, 0x02, 0x52, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x64,
|
0x72, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
|
||||||
0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12,
|
0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x74, 0x65,
|
||||||
0x1e, 0x0a, 0x0a, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x18, 0x05, 0x20,
|
0x6d, 0x70, 0x65, 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52,
|
||||||
0x01, 0x28, 0x08, 0x52, 0x0a, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x22,
|
0x0b, 0x74, 0x65, 0x6d, 0x70, 0x65, 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x3e, 0x0a, 0x04,
|
||||||
0x21, 0x0a, 0x07, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72,
|
0x54, 0x61, 0x73, 0x6b, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04,
|
||||||
0x6f, 0x63, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x70, 0x72, 0x6f, 0x63,
|
0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01,
|
||||||
0x65, 0x64, 0x32, 0xd6, 0x01, 0x0a, 0x0c, 0x4e, 0x65, 0x7a, 0x68, 0x61, 0x53, 0x65, 0x72, 0x76,
|
0x28, 0x04, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61,
|
||||||
0x69, 0x63, 0x65, 0x12, 0x33, 0x0a, 0x11, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x79, 0x73,
|
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x7a, 0x0a, 0x0a,
|
||||||
0x74, 0x65, 0x6d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64,
|
||||||
0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x1a, 0x0e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52,
|
0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79,
|
||||||
0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x22, 0x00, 0x12, 0x31, 0x0a, 0x10, 0x52, 0x65, 0x70, 0x6f,
|
0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14,
|
||||||
0x72, 0x74, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x0b, 0x2e, 0x70,
|
0x0a, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x02, 0x52, 0x05, 0x64,
|
||||||
0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
0x65, 0x6c, 0x61, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01,
|
||||||
0x6f, 0x2e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x22, 0x00, 0x12, 0x31, 0x0a, 0x0a, 0x52,
|
0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x75, 0x63, 0x63,
|
||||||
0x65, 0x70, 0x6f, 0x72, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x73, 0x75,
|
||||||
0x6f, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x1a, 0x0e, 0x2e, 0x70,
|
0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x22, 0x21, 0x0a, 0x07, 0x52, 0x65, 0x63, 0x65,
|
||||||
0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x22, 0x00, 0x12, 0x2b,
|
0x69, 0x70, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x64, 0x18, 0x01, 0x20,
|
||||||
0x0a, 0x0b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x0b, 0x2e,
|
0x01, 0x28, 0x08, 0x52, 0x06, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x64, 0x22, 0x22, 0x0a, 0x0c, 0x49,
|
||||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x1a, 0x0b, 0x2e, 0x70, 0x72, 0x6f,
|
0x4f, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x44, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x64,
|
||||||
0x74, 0x6f, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x22, 0x00, 0x30, 0x01, 0x42, 0x09, 0x5a, 0x07, 0x2e,
|
0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x32,
|
||||||
0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
0x92, 0x02, 0x0a, 0x0c, 0x4e, 0x65, 0x7a, 0x68, 0x61, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
|
||||||
|
0x12, 0x33, 0x0a, 0x11, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d,
|
||||||
|
0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x74,
|
||||||
|
0x61, 0x74, 0x65, 0x1a, 0x0e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x63, 0x65,
|
||||||
|
0x69, 0x70, 0x74, 0x22, 0x00, 0x12, 0x31, 0x0a, 0x10, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x53,
|
||||||
|
0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x0b, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||||
|
0x6f, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52,
|
||||||
|
0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x22, 0x00, 0x12, 0x31, 0x0a, 0x0a, 0x52, 0x65, 0x70, 0x6f,
|
||||||
|
0x72, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54,
|
||||||
|
0x61, 0x73, 0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x1a, 0x0e, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||||
|
0x6f, 0x2e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x22, 0x00, 0x12, 0x2b, 0x0a, 0x0b, 0x52,
|
||||||
|
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x0b, 0x2e, 0x70, 0x72, 0x6f,
|
||||||
|
0x74, 0x6f, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x1a, 0x0b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e,
|
||||||
|
0x54, 0x61, 0x73, 0x6b, 0x22, 0x00, 0x30, 0x01, 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,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -602,28 +746,33 @@ func file_proto_nezha_proto_rawDescGZIP() []byte {
|
|||||||
return file_proto_nezha_proto_rawDescData
|
return file_proto_nezha_proto_rawDescData
|
||||||
}
|
}
|
||||||
|
|
||||||
var file_proto_nezha_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
|
var file_proto_nezha_proto_msgTypes = make([]protoimpl.MessageInfo, 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
|
||||||
(*Task)(nil), // 2: proto.Task
|
(*State_SensorTemperature)(nil), // 2: proto.State_SensorTemperature
|
||||||
(*TaskResult)(nil), // 3: proto.TaskResult
|
(*Task)(nil), // 3: proto.Task
|
||||||
(*Receipt)(nil), // 4: proto.Receipt
|
(*TaskResult)(nil), // 4: proto.TaskResult
|
||||||
|
(*Receipt)(nil), // 5: proto.Receipt
|
||||||
|
(*IOStreamData)(nil), // 6: proto.IOStreamData
|
||||||
}
|
}
|
||||||
var file_proto_nezha_proto_depIdxs = []int32{
|
var file_proto_nezha_proto_depIdxs = []int32{
|
||||||
1, // 0: proto.NezhaService.ReportSystemState:input_type -> proto.State
|
2, // 0: proto.State.temperatures:type_name -> proto.State_SensorTemperature
|
||||||
0, // 1: proto.NezhaService.ReportSystemInfo:input_type -> proto.Host
|
1, // 1: proto.NezhaService.ReportSystemState:input_type -> proto.State
|
||||||
3, // 2: proto.NezhaService.ReportTask:input_type -> proto.TaskResult
|
0, // 2: proto.NezhaService.ReportSystemInfo:input_type -> proto.Host
|
||||||
0, // 3: proto.NezhaService.RequestTask:input_type -> proto.Host
|
4, // 3: proto.NezhaService.ReportTask:input_type -> proto.TaskResult
|
||||||
4, // 4: proto.NezhaService.ReportSystemState:output_type -> proto.Receipt
|
0, // 4: proto.NezhaService.RequestTask:input_type -> proto.Host
|
||||||
4, // 5: proto.NezhaService.ReportSystemInfo:output_type -> proto.Receipt
|
6, // 5: proto.NezhaService.IOStream:input_type -> proto.IOStreamData
|
||||||
4, // 6: proto.NezhaService.ReportTask:output_type -> proto.Receipt
|
5, // 6: proto.NezhaService.ReportSystemState:output_type -> proto.Receipt
|
||||||
2, // 7: proto.NezhaService.RequestTask:output_type -> proto.Task
|
5, // 7: proto.NezhaService.ReportSystemInfo:output_type -> proto.Receipt
|
||||||
4, // [4:8] is the sub-list for method output_type
|
5, // 8: proto.NezhaService.ReportTask:output_type -> proto.Receipt
|
||||||
0, // [0:4] is the sub-list for method input_type
|
3, // 9: proto.NezhaService.RequestTask:output_type -> proto.Task
|
||||||
0, // [0:0] is the sub-list for extension type_name
|
6, // 10: proto.NezhaService.IOStream:output_type -> proto.IOStreamData
|
||||||
0, // [0:0] is the sub-list for extension extendee
|
6, // [6:11] is the sub-list for method output_type
|
||||||
0, // [0:0] is the sub-list for field type_name
|
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 extendee
|
||||||
|
0, // [0:1] is the sub-list for field type_name
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { file_proto_nezha_proto_init() }
|
func init() { file_proto_nezha_proto_init() }
|
||||||
@@ -632,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
|
||||||
@@ -644,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
|
||||||
@@ -656,7 +805,19 @@ 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 {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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
|
||||||
@@ -668,7 +829,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[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
|
||||||
@@ -680,7 +841,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[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
|
||||||
@@ -692,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{
|
||||||
@@ -699,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: 5,
|
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 {
|
||||||
@@ -23,6 +24,7 @@ message Host {
|
|||||||
string ip = 10;
|
string ip = 10;
|
||||||
string country_code = 11;
|
string country_code = 11;
|
||||||
string version = 12;
|
string version = 12;
|
||||||
|
repeated string gpu = 13;
|
||||||
}
|
}
|
||||||
|
|
||||||
message State {
|
message State {
|
||||||
@@ -41,6 +43,13 @@ message State {
|
|||||||
uint64 tcp_conn_count = 14;
|
uint64 tcp_conn_count = 14;
|
||||||
uint64 udp_conn_count = 15;
|
uint64 udp_conn_count = 15;
|
||||||
uint64 process_count = 16;
|
uint64 process_count = 16;
|
||||||
|
repeated State_SensorTemperature temperatures = 17;
|
||||||
|
double gpu = 18;
|
||||||
|
}
|
||||||
|
|
||||||
|
message State_SensorTemperature {
|
||||||
|
string name = 1;
|
||||||
|
double temperature = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Task {
|
message Task {
|
||||||
@@ -60,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
+30
-12
@@ -32,10 +32,10 @@ other = "Logout"
|
|||||||
other = "Login"
|
other = "Login"
|
||||||
|
|
||||||
[ConfirmLogout]
|
[ConfirmLogout]
|
||||||
other = "Confirm Logout?"
|
other = "Do you want to Logout?"
|
||||||
|
|
||||||
[AfterLoggingOutYouHaveToLoginAgain]
|
[AfterLoggingOutYouHaveToLoginAgain]
|
||||||
other = "You need to login again after logout to continue"
|
other = "You need to Login again to continue"
|
||||||
|
|
||||||
[Cancel]
|
[Cancel]
|
||||||
other = "Cancel"
|
other = "Cancel"
|
||||||
@@ -227,7 +227,7 @@ other = "Secret"
|
|||||||
other = "Note"
|
other = "Note"
|
||||||
|
|
||||||
[LinuxOneKeyInstall]
|
[LinuxOneKeyInstall]
|
||||||
other = "Linux One-Key Install"
|
other = "Linux One-Command Install"
|
||||||
|
|
||||||
[NoDomainAlert]
|
[NoDomainAlert]
|
||||||
other = "Set CDN bypassed Domain/IP in setting before using this function"
|
other = "Set CDN bypassed Domain/IP in setting before using this function"
|
||||||
@@ -257,7 +257,7 @@ other = "Execute by Trigger"
|
|||||||
other = "Delete Scheduled Task"
|
other = "Delete Scheduled Task"
|
||||||
|
|
||||||
[ConfirmToDeleteThisScheduledTask]
|
[ConfirmToDeleteThisScheduledTask]
|
||||||
other = "Confirm Delete?"
|
other = "Are you sure you want to Delete?"
|
||||||
|
|
||||||
[AccessDenied]
|
[AccessDenied]
|
||||||
other = "Access Denied"
|
other = "Access Denied"
|
||||||
@@ -293,7 +293,7 @@ other = "Delete Notification Method"
|
|||||||
other = "Confirm to Delete This Notification Method?"
|
other = "Confirm to Delete This Notification Method?"
|
||||||
|
|
||||||
[ForceUpdate]
|
[ForceUpdate]
|
||||||
other = "Force Update"
|
other = "Force to Update"
|
||||||
|
|
||||||
[SelectAll]
|
[SelectAll]
|
||||||
other = "Select All"
|
other = "Select All"
|
||||||
@@ -302,7 +302,7 @@ other = "Select All"
|
|||||||
other = "Version"
|
other = "Version"
|
||||||
|
|
||||||
[OneKeyInstall]
|
[OneKeyInstall]
|
||||||
other = "One-Key Install"
|
other = "One-Command Install"
|
||||||
|
|
||||||
[ClickToCopy]
|
[ClickToCopy]
|
||||||
other = "Click To Copy"
|
other = "Click To Copy"
|
||||||
@@ -398,7 +398,7 @@ other = "Virtualization"
|
|||||||
other = "Swap"
|
other = "Swap"
|
||||||
|
|
||||||
[NetTransfer]
|
[NetTransfer]
|
||||||
other = "Transfer"
|
other = "Usage"
|
||||||
|
|
||||||
[Load]
|
[Load]
|
||||||
other = "Load"
|
other = "Load"
|
||||||
@@ -419,7 +419,7 @@ other = "Last Active"
|
|||||||
other = "Version"
|
other = "Version"
|
||||||
|
|
||||||
[NetSpeed]
|
[NetSpeed]
|
||||||
other = "Speed"
|
other = "NIC"
|
||||||
|
|
||||||
[Uptime]
|
[Uptime]
|
||||||
other = "Uptime"
|
other = "Uptime"
|
||||||
@@ -473,7 +473,7 @@ other = "Next check"
|
|||||||
other = "Current Usage"
|
other = "Current Usage"
|
||||||
|
|
||||||
[VerifyPassword]
|
[VerifyPassword]
|
||||||
other = "Verify Password"
|
other = "Please enter your password"
|
||||||
|
|
||||||
[LightMode]
|
[LightMode]
|
||||||
other = "Light Mode"
|
other = "Light Mode"
|
||||||
@@ -482,7 +482,7 @@ other = "Light Mode"
|
|||||||
other = "Dark Mode"
|
other = "Dark Mode"
|
||||||
|
|
||||||
[FollowSystem]
|
[FollowSystem]
|
||||||
other = "Follow System"
|
other = "System Auto"
|
||||||
|
|
||||||
[GridLayout]
|
[GridLayout]
|
||||||
other = "Grid Layout"
|
other = "Grid Layout"
|
||||||
@@ -619,9 +619,18 @@ other = "Enable Show in Service"
|
|||||||
[EnableDDNS]
|
[EnableDDNS]
|
||||||
other = "Enable DDNS"
|
other = "Enable DDNS"
|
||||||
|
|
||||||
|
[EnableIPv4]
|
||||||
|
other = "Enable DDNS IPv4"
|
||||||
|
|
||||||
|
[EnableIpv6]
|
||||||
|
other = "Enable DDNS IPv6"
|
||||||
|
|
||||||
[DDNSDomain]
|
[DDNSDomain]
|
||||||
other = "DDNS Domain"
|
other = "DDNS Domain"
|
||||||
|
|
||||||
|
[DDNSProfile]
|
||||||
|
other = "DDNS Profile Name"
|
||||||
|
|
||||||
[Feature]
|
[Feature]
|
||||||
other = "Feature"
|
other = "Feature"
|
||||||
|
|
||||||
@@ -629,7 +638,16 @@ other = "Feature"
|
|||||||
other = "Template"
|
other = "Template"
|
||||||
|
|
||||||
[Stat]
|
[Stat]
|
||||||
other = "Stat"
|
other = "Asset"
|
||||||
|
|
||||||
|
[Temperature]
|
||||||
|
other = "Temperature"
|
||||||
|
|
||||||
[DisableSwitchTemplateInFrontend]
|
[DisableSwitchTemplateInFrontend]
|
||||||
other = "Disable Switch Template in Frontend"
|
other = "Disable Switch Template in Frontend"
|
||||||
|
|
||||||
|
[ServersOnWorldMap]
|
||||||
|
other = "Servers On World Map"
|
||||||
|
|
||||||
|
[NAT]
|
||||||
|
other = "NAT"
|
||||||
Vendored
+19
-1
@@ -619,9 +619,18 @@ other = "Mostrar en servicio"
|
|||||||
[EnableDDNS]
|
[EnableDDNS]
|
||||||
other = "Habilitar DDNS"
|
other = "Habilitar DDNS"
|
||||||
|
|
||||||
|
[EnableIPv4]
|
||||||
|
other = "Habilitar DDNS IPv4"
|
||||||
|
|
||||||
|
[EnableIpv6]
|
||||||
|
other = "Habilitar DDNS IPv6"
|
||||||
|
|
||||||
[DDNSDomain]
|
[DDNSDomain]
|
||||||
other = "Dominio DDNS"
|
other = "Dominio DDNS"
|
||||||
|
|
||||||
|
[DDNSProfile]
|
||||||
|
other = "Nombre del perfil de DDNS"
|
||||||
|
|
||||||
[Feature]
|
[Feature]
|
||||||
other = "Característica"
|
other = "Característica"
|
||||||
|
|
||||||
@@ -631,5 +640,14 @@ other = "Plantilla"
|
|||||||
[Stat]
|
[Stat]
|
||||||
other = "Stat"
|
other = "Stat"
|
||||||
|
|
||||||
|
[Temperature]
|
||||||
|
other = "Temperatura"
|
||||||
|
|
||||||
[DisableSwitchTemplateInFrontend]
|
[DisableSwitchTemplateInFrontend]
|
||||||
other = "Deshabilitar Cambio de Plantilla en Frontend"
|
other = "Deshabilitar Cambio de Plantilla en Frontend"
|
||||||
|
|
||||||
|
[ServersOnWorldMap]
|
||||||
|
other = "Servidores en el mapa mundial"
|
||||||
|
|
||||||
|
[NAT]
|
||||||
|
other = "NAT"
|
||||||
Vendored
+18
@@ -619,9 +619,18 @@ other = "在服务中显示"
|
|||||||
[EnableDDNS]
|
[EnableDDNS]
|
||||||
other = "启用DDNS"
|
other = "启用DDNS"
|
||||||
|
|
||||||
|
[EnableIPv4]
|
||||||
|
other = "启用DDNS IPv4"
|
||||||
|
|
||||||
|
[EnableIpv6]
|
||||||
|
other = "启用DDNS IPv6"
|
||||||
|
|
||||||
[DDNSDomain]
|
[DDNSDomain]
|
||||||
other = "DDNS域名"
|
other = "DDNS域名"
|
||||||
|
|
||||||
|
[DDNSProfile]
|
||||||
|
other = "DDNS配置名"
|
||||||
|
|
||||||
[Feature]
|
[Feature]
|
||||||
other = "功能"
|
other = "功能"
|
||||||
|
|
||||||
@@ -631,5 +640,14 @@ other = "主题"
|
|||||||
[Stat]
|
[Stat]
|
||||||
other = "信息"
|
other = "信息"
|
||||||
|
|
||||||
|
[Temperature]
|
||||||
|
other = "温度"
|
||||||
|
|
||||||
[DisableSwitchTemplateInFrontend]
|
[DisableSwitchTemplateInFrontend]
|
||||||
other = "禁止前台切换模板"
|
other = "禁止前台切换模板"
|
||||||
|
|
||||||
|
[ServersOnWorldMap]
|
||||||
|
other = "服务器世界分布图"
|
||||||
|
|
||||||
|
[NAT]
|
||||||
|
other = "内网穿透"
|
||||||
Vendored
+18
@@ -619,9 +619,18 @@ other = "在服務中顯示"
|
|||||||
[EnableDDNS]
|
[EnableDDNS]
|
||||||
other = "啟用DDNS"
|
other = "啟用DDNS"
|
||||||
|
|
||||||
|
[EnableIPv4]
|
||||||
|
other = "啟用DDNS IPv4"
|
||||||
|
|
||||||
|
[EnableIpv6]
|
||||||
|
other = "啟用DDNS IPv6"
|
||||||
|
|
||||||
[DDNSDomain]
|
[DDNSDomain]
|
||||||
other = "DDNS網域"
|
other = "DDNS網域"
|
||||||
|
|
||||||
|
[DDNSProfile]
|
||||||
|
other = "DDNS設定名"
|
||||||
|
|
||||||
[Feature]
|
[Feature]
|
||||||
other = "功能"
|
other = "功能"
|
||||||
|
|
||||||
@@ -631,5 +640,14 @@ other = "主題"
|
|||||||
[Stat]
|
[Stat]
|
||||||
other = "信息"
|
other = "信息"
|
||||||
|
|
||||||
|
[Temperature]
|
||||||
|
other = "溫度"
|
||||||
|
|
||||||
[DisableSwitchTemplateInFrontend]
|
[DisableSwitchTemplateInFrontend]
|
||||||
other = "禁止前台切換主題"
|
other = "禁止前台切換主題"
|
||||||
|
|
||||||
|
[ServersOnWorldMap]
|
||||||
|
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 })
|
||||||
}
|
}
|
||||||
@@ -303,6 +326,7 @@ function addOrEditServer(server, conf) {
|
|||||||
modal.find("input[name=name]").val(server ? server.Name : null);
|
modal.find("input[name=name]").val(server ? server.Name : null);
|
||||||
modal.find("input[name=Tag]").val(server ? server.Tag : null);
|
modal.find("input[name=Tag]").val(server ? server.Tag : null);
|
||||||
modal.find("input[name=DDNSDomain]").val(server ? server.DDNSDomain : null);
|
modal.find("input[name=DDNSDomain]").val(server ? server.DDNSDomain : null);
|
||||||
|
modal.find("input[name=DDNSProfile]").val(server ? server.DDNSProfile : null);
|
||||||
modal
|
modal
|
||||||
.find("input[name=DisplayIndex]")
|
.find("input[name=DisplayIndex]")
|
||||||
.val(server ? server.DisplayIndex : null);
|
.val(server ? server.DisplayIndex : null);
|
||||||
@@ -327,6 +351,16 @@ function addOrEditServer(server, conf) {
|
|||||||
} else {
|
} else {
|
||||||
modal.find(".ui.enableddns.checkbox").checkbox("set unchecked");
|
modal.find(".ui.enableddns.checkbox").checkbox("set unchecked");
|
||||||
}
|
}
|
||||||
|
if (server && server.EnableIPv4) {
|
||||||
|
modal.find(".ui.enableipv4.checkbox").checkbox("set checked");
|
||||||
|
} else {
|
||||||
|
modal.find(".ui.enableipv4.checkbox").checkbox("set unchecked");
|
||||||
|
}
|
||||||
|
if (server && server.EnableIpv6) {
|
||||||
|
modal.find(".ui.enableipv6.checkbox").checkbox("set checked");
|
||||||
|
} else {
|
||||||
|
modal.find(".ui.enableipv6.checkbox").checkbox("set unchecked");
|
||||||
|
}
|
||||||
showFormModal(".server.modal", "#serverForm", "/api/server");
|
showFormModal(".server.modal", "#serverForm", "/api/server");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
@@ -145,6 +145,10 @@ td {
|
|||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.temp-detail {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
.ui.content {
|
.ui.content {
|
||||||
margin:0 !important;
|
margin:0 !important;
|
||||||
padding:1em !important;
|
padding:1em !important;
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@ const mixinsVue = {
|
|||||||
adaptedTemplates: [
|
adaptedTemplates: [
|
||||||
{ key: 'default', name: 'Default', icon: 'th large' },
|
{ key: 'default', name: 'Default', icon: 'th large' },
|
||||||
{ key: 'angel-kanade', name: 'AngelKanade', icon: 'square' },
|
{ key: 'angel-kanade', name: 'AngelKanade', icon: 'square' },
|
||||||
{ key: 'server-status', name: 'SeverStatus', icon: 'list' }
|
{ key: 'server-status', name: 'ServerStatus', icon: 'list' }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
|
|||||||
@@ -157,3 +157,16 @@ body[theme="dark"] footer p a:hover {
|
|||||||
color: #f1f1f1;
|
color: #f1f1f1;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body[theme="dark"] .modal-content{
|
||||||
|
background-color: #1c1d26;
|
||||||
|
color: #f1f1f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
body[theme="dark"] .modal-header{
|
||||||
|
border-bottom-color:#080808;
|
||||||
|
}
|
||||||
|
|
||||||
|
body[theme="dark"] .modal-header i{
|
||||||
|
color: #f1f1f1;
|
||||||
|
}
|
||||||
|
|||||||
@@ -131,4 +131,17 @@ body[theme="light"] footer p a,
|
|||||||
body[theme="light"] footer p a:hover {
|
body[theme="light"] footer p a:hover {
|
||||||
color: #333333;
|
color: #333333;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
body[theme="light"] .modal-content{
|
||||||
|
background-color: #ffffff;
|
||||||
|
color: rgba(0, 0, 0, 0.87);
|
||||||
|
}
|
||||||
|
|
||||||
|
body[theme="light"] .modal-header{
|
||||||
|
border-bottom-color:#f1f1f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
body[theme="light"] .modal-header i{
|
||||||
|
color: rgba(0, 0, 0, 0.87);
|
||||||
}
|
}
|
||||||
+62
-3
@@ -230,6 +230,23 @@ body {
|
|||||||
max-width: 50px;
|
max-width: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.temp-detail {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
/*正文结束*/
|
/*正文结束*/
|
||||||
|
|
||||||
/* 服务页 正文*/
|
/* 服务页 正文*/
|
||||||
@@ -281,10 +298,42 @@ body {
|
|||||||
.service-status .tooltip-inner {
|
.service-status .tooltip-inner {
|
||||||
max-width: 500px;
|
max-width: 500px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* 服务页 正文结束 */
|
/* 服务页 正文结束 */
|
||||||
|
|
||||||
|
|
||||||
|
/* 服务器分布图 */
|
||||||
|
#mapChartBox{
|
||||||
|
z-index: 999999999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-dialog-centered{
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 1134px;
|
||||||
|
max-width: 1134px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center; /* 垂直居中 */
|
||||||
|
justify-content: center; /* 水平居中 */
|
||||||
|
min-height: calc(100%) /* 确保模态框占据整个视口高度 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header{
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header i{
|
||||||
|
cursor: pointer;
|
||||||
|
position: absolute;
|
||||||
|
font-size: 2em;
|
||||||
|
top: 13px;
|
||||||
|
right: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: 1200px) {
|
@media only screen and (max-width: 1200px) {
|
||||||
.accordian-body{
|
.accordian-body{
|
||||||
margin: 5px 0px 5px 10px;
|
margin: 5px 0px 5px 10px;
|
||||||
@@ -365,6 +414,16 @@ body {
|
|||||||
.toolbox {
|
.toolbox {
|
||||||
right: 18px;
|
right: 18px;
|
||||||
}
|
}
|
||||||
|
.modal-dialog-centered{
|
||||||
|
width: 95%;
|
||||||
|
}
|
||||||
|
.modal-title{
|
||||||
|
line-height: 0.5;
|
||||||
|
}
|
||||||
|
.modal-header i{
|
||||||
|
top: 8px;
|
||||||
|
right: 8px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (min-width: 768px) {
|
@media only screen and (min-width: 768px) {
|
||||||
|
|||||||
+1
-1
@@ -21,7 +21,7 @@
|
|||||||
/** Name of the expando, to work with multiple documents or to re-shiv one document */
|
/** Name of the expando, to work with multiple documents or to re-shiv one document */
|
||||||
var expando = '_html5shiv';
|
var expando = '_html5shiv';
|
||||||
|
|
||||||
/** The id for the the documents expando */
|
/** The id for the documents expando */
|
||||||
var expanID = 0;
|
var expanID = 0;
|
||||||
|
|
||||||
/** Cached data for each document */
|
/** Cached data for each document */
|
||||||
|
|||||||
+36
-51
@@ -7,16 +7,17 @@ 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' },
|
||||||
{ key: 'server-status', name: 'SeverStatus', icon: 'list' }
|
{ key: 'server-status', name: 'ServerStatus', icon: 'list' }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
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?v20240224"></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}}
|
||||||
+17
-1
@@ -32,10 +32,26 @@
|
|||||||
<label>{{tr "EnableDDNS"}}</label>
|
<label>{{tr "EnableDDNS"}}</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<div class="ui enableipv4 checkbox">
|
||||||
|
<input name="EnableIPv4" type="checkbox" tabindex="0" />
|
||||||
|
<label>{{tr "EnableIPv4"}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<div class="ui enableipv6 checkbox">
|
||||||
|
<input name="EnableIpv6" type="checkbox" tabindex="0" />
|
||||||
|
<label>{{tr "EnableIpv6"}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>{{tr "DDNSDomain"}}</label>
|
<label>{{tr "DDNSDomain"}}</label>
|
||||||
<input type="text" name="DDNSDomain" placeholder="{{tr "DDNSDomain"}}">
|
<input type="text" name="DDNSDomain" placeholder="{{tr "DDNSDomain"}}">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>{{tr "DDNSProfile"}}</label>
|
||||||
|
<input type="text" name="DDNSProfile" placeholder="{{tr "DDNSProfile"}}">
|
||||||
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>{{tr "Note"}}</label>
|
<label>{{tr "Note"}}</label>
|
||||||
<textarea name="Note"></textarea>
|
<textarea name="Note"></textarea>
|
||||||
@@ -45,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}}
|
||||||
+10
-1
@@ -30,6 +30,8 @@
|
|||||||
<th>{{tr "VersionNumber"}}</th>
|
<th>{{tr "VersionNumber"}}</th>
|
||||||
<th>{{tr "HideForGuest"}}</th>
|
<th>{{tr "HideForGuest"}}</th>
|
||||||
<th>{{tr "EnableDDNS"}}</th>
|
<th>{{tr "EnableDDNS"}}</th>
|
||||||
|
<th>{{tr "EnableIPv4"}}</th>
|
||||||
|
<th>{{tr "EnableIpv6"}}</th>
|
||||||
<th>{{tr "DDNSDomain"}}</th>
|
<th>{{tr "DDNSDomain"}}</th>
|
||||||
<th>{{tr "Secret"}}</th>
|
<th>{{tr "Secret"}}</th>
|
||||||
<th>{{tr "OneKeyInstall"}}</th>
|
<th>{{tr "OneKeyInstall"}}</th>
|
||||||
@@ -48,6 +50,8 @@
|
|||||||
<td>{{$server.Host.Version}}</td>
|
<td>{{$server.Host.Version}}</td>
|
||||||
<td>{{$server.HideForGuest}}</td>
|
<td>{{$server.HideForGuest}}</td>
|
||||||
<td>{{$server.EnableDDNS}}</td>
|
<td>{{$server.EnableDDNS}}</td>
|
||||||
|
<td>{{$server.EnableIPv4}}</td>
|
||||||
|
<td>{{$server.EnableIpv6}}</td>
|
||||||
<td>{{$server.DDNSDomain}}</td>
|
<td>{{$server.DDNSDomain}}</td>
|
||||||
<td>
|
<td>
|
||||||
<button class="ui icon green mini button" data-clipboard-text="{{$server.Secret}}" data-tooltip="{{tr "ClickToCopy"}}">
|
<button class="ui icon green mini button" data-clipboard-text="{{$server.Secret}}" data-tooltip="{{tr "ClickToCopy"}}">
|
||||||
@@ -65,6 +69,11 @@
|
|||||||
data-tooltip="{{tr "ClickToCopy"}}">
|
data-tooltip="{{tr "ClickToCopy"}}">
|
||||||
<i class="windows icon"></i>
|
<i class="windows icon"></i>
|
||||||
</button>
|
</button>
|
||||||
|
<button class="ui icon green mini button"
|
||||||
|
data-clipboard-text="{{if $.Conf.GRPCHost}}curl -L https://raw.githubusercontent.com/naiba/nezha/master/script/install.command -o nezha.command && chmod +x nezha.command && sudo ./nezha.command install_agent {{$.Conf.GRPCHost}} {{if $.Conf.ProxyGRPCPort}}{{$.Conf.ProxyGRPCPort}}{{else}}{{$.Conf.GRPCPort}}{{end}} {{$server.Secret}}{{if $.Conf.TLS}} --tls{{end}}{{else}}{{tr "NoDomainAlert"}}{{end}}"
|
||||||
|
data-tooltip="{{tr "ClickToCopy"}}">
|
||||||
|
<i class="apple icon"></i>
|
||||||
|
</button>
|
||||||
</td>
|
</td>
|
||||||
<td style="word-break: break-word;white-space: pre-wrap;">{{$server.Note}}</td>
|
<td style="word-break: break-word;white-space: pre-wrap;">{{$server.Note}}</td>
|
||||||
<td>
|
<td>
|
||||||
@@ -89,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}}
|
||||||
+1
-1
@@ -13,7 +13,7 @@
|
|||||||
<script src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/semantic-ui/2.4.1/semantic.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="/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://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/vue/2.6.14/vue.min.js"></script>
|
||||||
<script src="/static/main.js?v20240224"></script>
|
<script src="/static/main.js?v20240330"></script>
|
||||||
<script src="/static/theme-default/js/mixin.js?v20240302"></script>
|
<script src="/static/theme-default/js/mixin.js?v20240302"></script>
|
||||||
<script>
|
<script>
|
||||||
(function () {
|
(function () {
|
||||||
|
|||||||
@@ -24,6 +24,9 @@
|
|||||||
[<span
|
[<span
|
||||||
v-if="server.Host.Virtualization">@#server.Host.Virtualization#@:</span>@#server.Host.Arch#@]<br />
|
v-if="server.Host.Virtualization">@#server.Host.Virtualization#@:</span>@#server.Host.Arch#@]<br />
|
||||||
CPU: @#server.Host.CPU#@<br />
|
CPU: @#server.Host.CPU#@<br />
|
||||||
|
<template v-if="server.Host.GPU">
|
||||||
|
GPU: @#server.Host.GPU#@
|
||||||
|
</template>
|
||||||
{{tr "DiskUsed"}}:
|
{{tr "DiskUsed"}}:
|
||||||
@#formatByteSize(server.State.DiskUsed)#@/@#formatByteSize(server.Host.DiskTotal)#@<br />
|
@#formatByteSize(server.State.DiskUsed)#@/@#formatByteSize(server.Host.DiskTotal)#@<br />
|
||||||
{{tr "MemUsed"}}:
|
{{tr "MemUsed"}}:
|
||||||
|
|||||||
@@ -24,6 +24,9 @@
|
|||||||
<i :class="item.icon + ' icon'"></i>@#item.name#@
|
<i :class="item.icon + ' icon'"></i>@#item.name#@
|
||||||
<i class="check icon" v-if="preferredTemplate === item.key"></i>
|
<i class="check icon" v-if="preferredTemplate === item.key"></i>
|
||||||
</a>
|
</a>
|
||||||
|
<a v-if="defaultTemplate === 'custom'" @click="toggleTemplate('custom')" class="item">
|
||||||
|
<i class="expand icon"></i>Custom
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|||||||
+153
-128
@@ -20,101 +20,101 @@
|
|||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body data-theme="light" data-gridlist="grid">
|
<body data-theme="light" data-gridlist="grid">
|
||||||
<header>
|
<header>
|
||||||
<section class="nav-bar clearfix">
|
<section class="nav-bar clearfix">
|
||||||
<figure class="logo">
|
<figure class="logo">
|
||||||
<a href="/">
|
<a href="/">
|
||||||
<img src="/static/logo.svg?v20210804" alt='{{tr "NezhaMonitoring"}}' width="50" height="50">
|
<img src="/static/logo.svg?v20210804" alt='{{tr "NezhaMonitoring"}}' width="50" height="50">
|
||||||
</a>
|
</a>
|
||||||
<a href="/">{{.Conf.Site.Brand}}</a>
|
<a href="/">{{.Conf.Site.Brand}}</a>
|
||||||
</figure>
|
</figure>
|
||||||
<div class="icon-container">
|
<div class="icon-container">
|
||||||
<div class="row cf">
|
<div class="row cf">
|
||||||
<div class="three col">
|
<div class="three col">
|
||||||
<div class="hamburger" id="hamburger-icon"><span class="line"></span><span
|
<div class="hamburger" id="hamburger-icon"><span class="line"></span><span
|
||||||
class="line"></span><span class="line"></span></div>
|
class="line"></span><span class="line"></span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<nav class="nav-menu">
|
||||||
|
<ul>
|
||||||
|
<li><a href="/">{{tr "Home"}}</a></li>
|
||||||
|
<li><a href="/service">{{tr "Services"}}</a></li>
|
||||||
|
<li><a href="/network">{{tr "NetworkSpiter"}}</a></li>
|
||||||
|
{{if .Admin}}
|
||||||
|
<li><a href="/server">{{tr "AdminPanel"}}</a></li>
|
||||||
|
{{else}}
|
||||||
|
<li><a href="/login">{{tr "Login"}}</a></li>
|
||||||
|
{{end}}
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</section>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<div id="network">
|
||||||
|
<div class="server-info-container" v-for='server in servers' :id="server.ID">
|
||||||
|
<div class="info-body" @click="redirectNetwork(server.ID)">
|
||||||
|
<ul class="server-info-body-container">
|
||||||
|
<li>
|
||||||
|
<h3>@#server.Name#@</h3>
|
||||||
|
</li>
|
||||||
|
<li><img :src="'/static/theme-daynight/img/flag/'+(server.Host&&server.Host.CountryCode?server.Host.CountryCode.toUpperCase():'CN')+'.png'"
|
||||||
|
:title="server.Host.CountryCode.toUpperCase()" /></li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<nav class="nav-menu">
|
<div class="network-chart" style="height: 800px;overflow: hidden">
|
||||||
<ul>
|
<div id="monitor-info-container" style="height: 520px;max-width: 1400px">
|
||||||
<li><a href="/">{{tr "Home"}}</a></li>
|
|
||||||
<li><a href="/service">{{tr "Services"}}</a></li>
|
|
||||||
<li><a href="/network">{{tr "NetworkSpiter"}}</a></li>
|
|
||||||
{{if .Admin}}
|
|
||||||
<li><a href="/server">{{tr "AdminPanel"}}</a></li>
|
|
||||||
{{else}}
|
|
||||||
<li><a href="/login">{{tr "Login"}}</a></li>
|
|
||||||
{{end}}
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
</section>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<div id="network">
|
|
||||||
<div class="server-info-container" v-for='server in servers' :id="server.ID" style="font-size: .6em">
|
|
||||||
<div class="info-body" @click="redirectNetwork(server.ID)">
|
|
||||||
<ul class="server-info-body-container">
|
|
||||||
<li>
|
|
||||||
<h3>@#server.Name#@</h3>
|
|
||||||
</li>
|
|
||||||
<li><img :src="'/static/theme-daynight/img/flag/'+(server.Host&&server.Host.CountryCode?server.Host.CountryCode.toUpperCase():'CN')+'.png'"
|
|
||||||
:title="server.Host.CountryCode.toUpperCase()" /></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="sidebar-container">
|
||||||
<div class="network-chart" style="height: 800px;overflow: hidden">
|
<ul>
|
||||||
<div id="monitor-info-container" style="height: 520px;max-width: 1400px">
|
<li><i class="fas fa-sun" title='{{tr "LightMode"}}'></i><span>{{tr "LightMode"}}</span></li>
|
||||||
|
<li><i class="fas fa-moon" title='{{tr "DarkMode"}}'></i><span>{{tr "DarkMode"}}</span></li>
|
||||||
|
<li><i class="fas fa-th" title='{{tr "GridLayout"}}'></i><span>{{tr "GridLayout"}}</span></li>
|
||||||
|
<li><i class="fas fa-list-ul" title='{{tr "ListLayout"}}'></i><span>{{tr "ListLayout"}}</span></li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</main>
|
||||||
<div class="sidebar-container">
|
|
||||||
<ul>
|
|
||||||
<li><i class="fas fa-sun" title='{{tr "LightMode"}}'></i><span>{{tr "LightMode"}}</span></li>
|
|
||||||
<li><i class="fas fa-moon" title='{{tr "DarkMode"}}'></i><span>{{tr "DarkMode"}}</span></li>
|
|
||||||
<li><i class="fas fa-th" title='{{tr "GridLayout"}}'></i><span>{{tr "GridLayout"}}</span></li>
|
|
||||||
<li><i class="fas fa-list-ul" title='{{tr "ListLayout"}}'></i><span>{{tr "ListLayout"}}</span></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<section class="dark-light-toggle">
|
<section class="dark-light-toggle">
|
||||||
<label class="switcher">
|
<label class="switcher">
|
||||||
<input type="checkbox" name="theme" id="dark-light" />
|
<input type="checkbox" name="theme" id="dark-light" />
|
||||||
<div>
|
<div>
|
||||||
<i class="fas fa-adjust"></i>
|
<i class="fas fa-adjust"></i>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- Back to top button -->
|
<!-- Back to top button -->
|
||||||
<a id="back-to-top"></a>
|
<a id="back-to-top"></a>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
<div class="footer-container">
|
<div class="footer-container">
|
||||||
<div><a href="https://github.com/naiba/nezha" target="_blank">Powered by {{tr "NezhaMonitoring"}} · {{.Version}}</a>
|
<div><a href="https://github.com/naiba/nezha" target="_blank">Powered by {{tr "NezhaMonitoring"}} ·
|
||||||
<p>© <span id="copyright-date">
|
{{.Version}}</a>
|
||||||
|
<p>© <span id="copyright-date">
|
||||||
<script>document.getElementById('copyright-date').appendChild(document.createTextNode(new Date().getFullYear()))</script>
|
<script>document.getElementById('copyright-date').appendChild(document.createTextNode(new Date().getFullYear()))</script>
|
||||||
</span> · <a href="https://blog.jackiesung.com" target="_blank">Theme designed by Jackie Sung</a>
|
</span> · <a href="https://blog.jackiesung.com" target="_blank">Theme designed by Jackie Sung</a>
|
||||||
</p>
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</footer>
|
||||||
</footer>
|
|
||||||
|
|
||||||
<script src="/static/theme-daynight/js/main.js?v202102012266"></script>
|
<script src="/static/theme-daynight/js/main.js?v202102012266"></script>
|
||||||
<script src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/vue/2.6.14/vue.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://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/limonte-sweetalert2/11.4.4/sweetalert2.all.min.js"></script>
|
<script
|
||||||
|
src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/limonte-sweetalert2/11.4.4/sweetalert2.all.min.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const monitorInfo = JSON.parse('{{.MonitorInfos}}');
|
const monitorInfo = JSON.parse('{{.MonitorInfos}}');
|
||||||
const initData = JSON.parse('{{.Servers}}').servers;
|
const initData = JSON.parse('{{.Servers}}').servers;
|
||||||
let MaxTCPPingValue = {{.Conf.MaxTCPPingValue}};
|
let MaxTCPPingValue = {{.Conf.MaxTCPPingValue}};
|
||||||
if (MaxTCPPingValue == null) {
|
|
||||||
MaxTCPPingValue = 1000;
|
|
||||||
}
|
|
||||||
// 基于准备好的dom,初始化echarts实例
|
// 基于准备好的dom,初始化echarts实例
|
||||||
var myChart = echarts.init(document.getElementById('monitor-info-container'));
|
const myChart = echarts.init(document.getElementById('monitor-info-container'));
|
||||||
|
|
||||||
// 使用刚指定的配置项和数据显示图表。
|
// 使用刚指定的配置项和数据显示图表。
|
||||||
var statusCards = new Vue({
|
var statusCards = new Vue({
|
||||||
el: '#network',
|
el: '#network',
|
||||||
@@ -128,11 +128,11 @@
|
|||||||
position: function (pt) {
|
position: function (pt) {
|
||||||
return [pt[0], '10%'];
|
return [pt[0], '10%'];
|
||||||
},
|
},
|
||||||
formatter: function(params){
|
formatter: function (params) {
|
||||||
let result = params[0].axisValueLabel + "<br />";
|
let result = params[0].axisValueLabel + "<br />";
|
||||||
params.forEach(function(item){
|
params.forEach(function (item) {
|
||||||
result += item.marker + item.seriesName + ": " + item.value[1].toFixed(2) + " ms<br />";
|
result += item.marker + item.seriesName + ": " + item.value[1].toFixed(2) + " ms<br />";
|
||||||
})
|
})
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
confine: true,
|
confine: true,
|
||||||
@@ -179,9 +179,9 @@
|
|||||||
mounted() {
|
mounted() {
|
||||||
this.DarkMode();
|
this.DarkMode();
|
||||||
this.parseMonitorInfo(monitorInfo);
|
this.parseMonitorInfo(monitorInfo);
|
||||||
window.addEventListener('resize', this.resizeHandle);
|
window.addEventListener('resize', this.resizeHandle);
|
||||||
},
|
},
|
||||||
destroyed () {
|
destroyed() {
|
||||||
window.removeEventListener('resize', this.resizeHandle)
|
window.removeEventListener('resize', this.resizeHandle)
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -195,81 +195,106 @@
|
|||||||
},
|
},
|
||||||
redirectNetwork(id) {
|
redirectNetwork(id) {
|
||||||
this.getMonitorHistory(id)
|
this.getMonitorHistory(id)
|
||||||
.then(function(monitorInfo) {
|
.then(function (monitorInfo) {
|
||||||
var vm = network.__vue__;
|
var vm = network.__vue__;
|
||||||
vm.parseMonitorInfo(monitorInfo);
|
vm.parseMonitorInfo(monitorInfo);
|
||||||
})
|
})
|
||||||
.catch(function(error){
|
.catch(function (error) {
|
||||||
window.location.href = "/404";
|
window.location.href = "/404";
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
getMonitorHistory(id) {
|
getMonitorHistory(id) {
|
||||||
return $.ajax({
|
return $.ajax({
|
||||||
url: "/api/v1/monitor/"+id,
|
url: "/api/v1/monitor/" + id,
|
||||||
method: "GET"
|
method: "GET"
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
parseMonitorInfo(monitorInfo) {
|
parseMonitorInfo(monitorInfo) {
|
||||||
let tSeries = [];
|
let tSeries = [];
|
||||||
let tLegendData = [];
|
let tLegendData = [];
|
||||||
|
var lcolors = ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'];
|
||||||
for (let i = 0; i < monitorInfo.result.length; i++) {
|
for (let i = 0; i < monitorInfo.result.length; i++) {
|
||||||
|
var lcolor = lcolors[i % lcolors.length];
|
||||||
|
var rgbaColorMarker = 'rgba(' + parseInt(lcolor.slice(1, 3), 16) + ',' + parseInt(lcolor.slice(3, 5), 16) + ',' + parseInt(lcolor.slice(5, 7), 16) + ',0.5)';
|
||||||
|
var rgbaColorBar = 'rgba(' + parseInt(lcolor.slice(1, 3), 16) + ',' + parseInt(lcolor.slice(3, 5), 16) + ',' + parseInt(lcolor.slice(5, 7), 16) + ',0.35)';
|
||||||
let loss = 0;
|
let loss = 0;
|
||||||
let data = [];
|
let data = [];
|
||||||
|
let datal = [];
|
||||||
for (let j = 0; j < monitorInfo.result[i].created_at.length; j++) {
|
for (let j = 0; j < monitorInfo.result[i].created_at.length; j++) {
|
||||||
avgDelay = monitorInfo.result[i].avg_delay[j];
|
avgDelay = Math.round(monitorInfo.result[i].avg_delay[j]);
|
||||||
if (avgDelay > 0.9 * MaxTCPPingValue) {
|
if (avgDelay > 0 && avgDelay < MaxTCPPingValue) {
|
||||||
loss += 1
|
data.push([monitorInfo.result[i].created_at[j], avgDelay]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
loss += 1;
|
||||||
|
datal.push({
|
||||||
|
xAxis: monitorInfo.result[i].created_at[j],
|
||||||
|
label: { show: false },
|
||||||
|
emphasis: { disabled: true },
|
||||||
|
lineStyle: {
|
||||||
|
type: "solid",
|
||||||
|
color: rgbaColorBar
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
data.push([monitorInfo.result[i].created_at[j], avgDelay]);
|
|
||||||
}
|
}
|
||||||
lossRate = ((loss / monitorInfo.result[i].created_at.length) * 100).toFixed(1);
|
lossRate = ((loss / monitorInfo.result[i].created_at.length) * 100).toFixed(1);
|
||||||
legendName = monitorInfo.result[i].monitor_name +" "+ lossRate + "%";
|
if (lossRate > 99) {
|
||||||
|
datal = [];
|
||||||
|
}
|
||||||
|
legendName = monitorInfo.result[i].monitor_name + " " + lossRate + "%";
|
||||||
tLegendData.push(legendName);
|
tLegendData.push(legendName);
|
||||||
tSeries.push({
|
tSeries.push({
|
||||||
name: legendName,
|
name: legendName,
|
||||||
type: 'line',
|
type: 'line',
|
||||||
smooth: true,
|
smooth: true,
|
||||||
symbol: 'none',
|
symbol: 'none',
|
||||||
data: data
|
data: data,
|
||||||
|
markLine: {
|
||||||
|
symbol: "none",
|
||||||
|
symbolSize: 0,
|
||||||
|
data: datal
|
||||||
|
},
|
||||||
|
markPoint: {
|
||||||
|
data: [
|
||||||
|
{ type: 'max', symbol: 'pin', name: 'Max', itemStyle: { color: rgbaColorMarker }, symbolSize: 30, label: { fontSize: 8 } },
|
||||||
|
{ type: 'min', symbol: 'pin', name: 'Min', itemStyle: { color: rgbaColorMarker }, symbolSize: 30, label: { fontSize: 8, offset: [0, 7.5] }, symbolRotate: 180 }
|
||||||
|
]
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.option.title.text = monitorInfo.result[0].server_name;
|
this.option.title.text = monitorInfo.result[0].server_name;
|
||||||
this.option.series = tSeries;
|
this.option.series = tSeries;
|
||||||
this.option.legend.data = tLegendData;
|
this.option.legend.data = tLegendData;
|
||||||
const hour = new Date(Date.now()).getHours()
|
|
||||||
if (hour > 17 || hour < 4) {
|
|
||||||
this.option.legend.textStyle.color = "#F1F1F2";
|
|
||||||
this.option.title.textStyle.color = "#ccc";
|
|
||||||
}
|
|
||||||
myChart.clear();
|
myChart.clear();
|
||||||
myChart.setOption(this.option);
|
myChart.setOption(this.option);
|
||||||
},
|
},
|
||||||
resizeHandle () {
|
resizeHandle() {
|
||||||
this.myChart.resize();
|
myChart.resize();
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
<style>
|
<style>
|
||||||
#network {
|
#network {
|
||||||
display: grid;
|
width: calc(100vw - 6em);
|
||||||
/*grid-template-columns: repeat(5, 1fr);*/
|
max-width: 1400px;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
|
margin: 1em auto;
|
||||||
grid-gap: 1em;
|
}
|
||||||
width: 100%;
|
|
||||||
width: calc(100vw - 6em);
|
#monitor-info-container {
|
||||||
max-width: 1400px;
|
margin: 0em auto;
|
||||||
margin: 1em auto;
|
align-content: start;
|
||||||
align-content: start;
|
background-color: #F1F1F2;
|
||||||
}
|
}
|
||||||
#monitor-info-container{
|
|
||||||
margin: 0em auto;
|
.server-info-container {
|
||||||
align-content: start;
|
font-size: .6em;
|
||||||
background-color: #F1F1F2;
|
width: fit-content;
|
||||||
}
|
display: inline-block;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
+12
-11
@@ -7,19 +7,20 @@
|
|||||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
<meta content="telephone=no" name="format-detection">
|
<meta content="telephone=no" name="format-detection">
|
||||||
<title>{{.Title}}</title>
|
<title>{{.Title}}</title>
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/semantic.min.css">
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/assets/font-logos.css">
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.min.css">
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/lipis/[email protected]/css/flag-icons.min.css">
|
|
||||||
<link rel="stylesheet" type="text/css" href="/static/semantic-ui-alerts.min.css">
|
|
||||||
<link rel="stylesheet" type="text/css" href="/static/theme-default/css/main.css?v20240226">
|
|
||||||
<link rel="shortcut icon" type="image/png" href="/static/logo.svg" />
|
<link rel="shortcut icon" type="image/png" href="/static/logo.svg" />
|
||||||
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js"></script>
|
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/semantic.min.css">
|
||||||
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/semantic.min.js"></script>
|
<link rel="stylesheet" href="https://unpkg.com/[email protected]/assets/font-logos.css">
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/[email protected]/font/bootstrap-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/[email protected]/css/flag-icons.min.css">
|
||||||
|
<link rel="stylesheet" type="text/css" href="/static/semantic-ui-alerts.min.css">
|
||||||
|
<link rel="stylesheet" type="text/css" href="/static/theme-default/css/main.css?v20240616">
|
||||||
|
<script src="https://unpkg.com/[email protected]/dist/jquery.min.js"></script>
|
||||||
|
<script src="https://unpkg.com/[email protected]/dist/semantic.min.js"></script>
|
||||||
|
<script src="https://unpkg.com/[email protected]/dist/vue.min.js"></script>
|
||||||
|
<script src="https://unpkg.com/[email protected]/dist/echarts.min.js"></script>
|
||||||
<script src="/static/semantic-ui-alerts.min.js"></script>
|
<script src="/static/semantic-ui-alerts.min.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/echarts.min.js"></script>
|
|
||||||
<script src="/static/theme-default/js/mixin.js?v20240302"></script>
|
<script src="/static/theme-default/js/mixin.js?v20240302"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
|||||||
+612
-512
File diff suppressed because it is too large
Load Diff
+3
@@ -27,6 +27,9 @@
|
|||||||
<i :class="item.icon + ' icon'"></i>@#item.name#@
|
<i :class="item.icon + ' icon'"></i>@#item.name#@
|
||||||
<i class="check icon" v-if="preferredTemplate === item.key"></i>
|
<i class="check icon" v-if="preferredTemplate === item.key"></i>
|
||||||
</a>
|
</a>
|
||||||
|
<a v-if="defaultTemplate === 'custom'" @click="toggleTemplate('custom')" class="item">
|
||||||
|
<i class="expand icon"></i>Custom
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|||||||
+28
-9
@@ -28,9 +28,6 @@
|
|||||||
const monitorInfo = JSON.parse('{{.MonitorInfos}}');
|
const monitorInfo = JSON.parse('{{.MonitorInfos}}');
|
||||||
const initData = JSON.parse('{{.Servers}}').servers;
|
const initData = JSON.parse('{{.Servers}}').servers;
|
||||||
let MaxTCPPingValue = {{.Conf.MaxTCPPingValue}};
|
let MaxTCPPingValue = {{.Conf.MaxTCPPingValue}};
|
||||||
if (MaxTCPPingValue == null) {
|
|
||||||
MaxTCPPingValue = 1000;
|
|
||||||
}
|
|
||||||
new Vue({
|
new Vue({
|
||||||
el: '#app',
|
el: '#app',
|
||||||
delimiters: ['@#', '#@'],
|
delimiters: ['@#', '#@'],
|
||||||
@@ -185,19 +182,36 @@
|
|||||||
parseMonitorInfo(monitorInfo) {
|
parseMonitorInfo(monitorInfo) {
|
||||||
let tSeries = [];
|
let tSeries = [];
|
||||||
let tLegendData = [];
|
let tLegendData = [];
|
||||||
|
var lcolors = ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'];
|
||||||
for (let i = 0; i < monitorInfo.result.length; i++) {
|
for (let i = 0; i < monitorInfo.result.length; i++) {
|
||||||
|
var lcolor = lcolors[i % lcolors.length];
|
||||||
|
var rgbaColorMarker = 'rgba(' + parseInt(lcolor.slice(1, 3), 16) + ',' + parseInt(lcolor.slice(3, 5), 16) + ',' + parseInt(lcolor.slice(5, 7), 16) + ',0.5)';
|
||||||
|
var rgbaColorBar = 'rgba(' + parseInt(lcolor.slice(1, 3), 16) + ',' + parseInt(lcolor.slice(3, 5), 16) + ',' + parseInt(lcolor.slice(5, 7), 16) + ',0.35)';
|
||||||
let loss = 0;
|
let loss = 0;
|
||||||
let data = [];
|
let data = [];
|
||||||
|
let datal = [];
|
||||||
for (let j = 0; j < monitorInfo.result[i].created_at.length; j++) {
|
for (let j = 0; j < monitorInfo.result[i].created_at.length; j++) {
|
||||||
avgDelay = Math.round(monitorInfo.result[i].avg_delay[j]);
|
avgDelay = Math.round(monitorInfo.result[i].avg_delay[j]);
|
||||||
if (avgDelay > 0.9 * MaxTCPPingValue) {
|
if (avgDelay > 0 && avgDelay < MaxTCPPingValue) {
|
||||||
loss += 1
|
|
||||||
}
|
|
||||||
if (avgDelay > 0) {
|
|
||||||
data.push([monitorInfo.result[i].created_at[j], avgDelay]);
|
data.push([monitorInfo.result[i].created_at[j], avgDelay]);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
loss += 1;
|
||||||
|
datal.push({
|
||||||
|
xAxis: monitorInfo.result[i].created_at[j],
|
||||||
|
label: { show: false },
|
||||||
|
emphasis: { disabled: true },
|
||||||
|
lineStyle: {
|
||||||
|
type: "solid",
|
||||||
|
color: rgbaColorBar
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
lossRate = ((loss / monitorInfo.result[i].created_at.length) * 100).toFixed(1);
|
lossRate = ((loss / monitorInfo.result[i].created_at.length) * 100).toFixed(1);
|
||||||
|
if (lossRate > 99) {
|
||||||
|
datal = [];
|
||||||
|
}
|
||||||
legendName = monitorInfo.result[i].monitor_name +" "+ lossRate + "%";
|
legendName = monitorInfo.result[i].monitor_name +" "+ lossRate + "%";
|
||||||
tLegendData.push(legendName);
|
tLegendData.push(legendName);
|
||||||
tSeries.push({
|
tSeries.push({
|
||||||
@@ -206,10 +220,15 @@
|
|||||||
smooth: true,
|
smooth: true,
|
||||||
symbol: 'none',
|
symbol: 'none',
|
||||||
data: data,
|
data: data,
|
||||||
|
markLine: {
|
||||||
|
symbol: "none",
|
||||||
|
symbolSize :0,
|
||||||
|
data: datal
|
||||||
|
},
|
||||||
markPoint: {
|
markPoint: {
|
||||||
data: [
|
data: [
|
||||||
{ type: 'max', symbol: 'pin', name: 'Max', itemStyle: { color: '#f00' } },
|
{ type: 'max', symbol: 'pin', name: 'Max', itemStyle: { color: rgbaColorMarker }, symbolSize: 30, label: { fontSize: 8 } },
|
||||||
{ type: 'min', symbol: 'pin', name: 'Min', itemStyle: { color: '#0f0' } }
|
{ type: 'min', symbol: 'pin', name: 'Min', itemStyle: { color: rgbaColorMarker }, symbolSize: 30, label: { fontSize: 8, offset: [0, 7.5] }, symbolRotate: 180 }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
+1
-1
@@ -221,7 +221,7 @@
|
|||||||
var disk = this.formatByteSize(server.State.DiskUsed) + '/' + this.formatByteSize(server.Host.DiskTotal);
|
var disk = this.formatByteSize(server.State.DiskUsed) + '/' + this.formatByteSize(server.Host.DiskTotal);
|
||||||
var upTime = this.secondToDate(server.State.Uptime);
|
var upTime = this.secondToDate(server.State.Uptime);
|
||||||
var tooltip =
|
var tooltip =
|
||||||
`{content: 'System: ${server.Host.Platform}-${server.Host.PlatformVersion}[${server.Host.Arch}]<br>CPU: ${server.Host.CPU}<br>Disk: ${disk}<br>Online: ${upTime}<br>Version: ${server.Host.Version}'}`;
|
`{content: 'System: ${server.Host.Platform}-${server.Host.PlatformVersion}[${server.Host.Arch}]<br>CPU: ${server.Host.CPU}<br>GPU: ${server.Host.GPU}<br>Disk: ${disk}<br>Online: ${upTime}<br>Version: ${server.Host.Version}'}`;
|
||||||
return tooltip
|
return tooltip
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+29
-10
@@ -55,9 +55,6 @@
|
|||||||
const monitorInfo = JSON.parse('{{.MonitorInfos}}');
|
const monitorInfo = JSON.parse('{{.MonitorInfos}}');
|
||||||
const initData = JSON.parse('{{.Servers}}').servers;
|
const initData = JSON.parse('{{.Servers}}').servers;
|
||||||
let MaxTCPPingValue = {{.Conf.MaxTCPPingValue}};
|
let MaxTCPPingValue = {{.Conf.MaxTCPPingValue}};
|
||||||
if (MaxTCPPingValue == null) {
|
|
||||||
MaxTCPPingValue = 1000;
|
|
||||||
}
|
|
||||||
new Vue({
|
new Vue({
|
||||||
el: '#app',
|
el: '#app',
|
||||||
delimiters: ['@#', '#@'],
|
delimiters: ['@#', '#@'],
|
||||||
@@ -208,19 +205,36 @@
|
|||||||
parseMonitorInfo(monitorInfo) {
|
parseMonitorInfo(monitorInfo) {
|
||||||
let tSeries = [];
|
let tSeries = [];
|
||||||
let tLegendData = [];
|
let tLegendData = [];
|
||||||
|
var lcolors = ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'];
|
||||||
for (let i = 0; i < monitorInfo.result.length; i++) {
|
for (let i = 0; i < monitorInfo.result.length; i++) {
|
||||||
|
var lcolor = lcolors[i % lcolors.length];
|
||||||
|
var rgbaColorMarker = 'rgba(' + parseInt(lcolor.slice(1, 3), 16) + ',' + parseInt(lcolor.slice(3, 5), 16) + ',' + parseInt(lcolor.slice(5, 7), 16) + ',0.5)';
|
||||||
|
var rgbaColorBar = 'rgba(' + parseInt(lcolor.slice(1, 3), 16) + ',' + parseInt(lcolor.slice(3, 5), 16) + ',' + parseInt(lcolor.slice(5, 7), 16) + ',0.35)';
|
||||||
let loss = 0;
|
let loss = 0;
|
||||||
let data = [];
|
let data = [];
|
||||||
|
let datal = [];
|
||||||
for (let j = 0; j < monitorInfo.result[i].created_at.length; j++) {
|
for (let j = 0; j < monitorInfo.result[i].created_at.length; j++) {
|
||||||
avgDelay = Math.round(monitorInfo.result[i].avg_delay[j]);
|
avgDelay = Math.round(monitorInfo.result[i].avg_delay[j]);
|
||||||
if (avgDelay > 0.9 * MaxTCPPingValue) {
|
if (avgDelay > 0 && avgDelay < MaxTCPPingValue) {
|
||||||
loss += 1
|
|
||||||
}
|
|
||||||
if (avgDelay > 0) {
|
|
||||||
data.push([monitorInfo.result[i].created_at[j], avgDelay]);
|
data.push([monitorInfo.result[i].created_at[j], avgDelay]);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
loss += 1;
|
||||||
|
datal.push({
|
||||||
|
xAxis: monitorInfo.result[i].created_at[j],
|
||||||
|
label: { show: false },
|
||||||
|
emphasis: { disabled: true },
|
||||||
|
lineStyle: {
|
||||||
|
type: "solid",
|
||||||
|
color: rgbaColorBar
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
lossRate = ((loss / monitorInfo.result[i].created_at.length) * 100).toFixed(1);
|
lossRate = ((loss / monitorInfo.result[i].created_at.length) * 100).toFixed(1);
|
||||||
|
if (lossRate > 99) {
|
||||||
|
datal = [];
|
||||||
|
}
|
||||||
legendName = monitorInfo.result[i].monitor_name +" "+ lossRate + "%";
|
legendName = monitorInfo.result[i].monitor_name +" "+ lossRate + "%";
|
||||||
tLegendData.push(legendName);
|
tLegendData.push(legendName);
|
||||||
tSeries.push({
|
tSeries.push({
|
||||||
@@ -229,10 +243,15 @@
|
|||||||
smooth: true,
|
smooth: true,
|
||||||
symbol: 'none',
|
symbol: 'none',
|
||||||
data: data,
|
data: data,
|
||||||
|
markLine: {
|
||||||
|
symbol: "none",
|
||||||
|
symbolSize :0,
|
||||||
|
data: datal
|
||||||
|
},
|
||||||
markPoint: {
|
markPoint: {
|
||||||
data: [
|
data: [
|
||||||
{ type: 'max', symbol: 'pin', name: 'Max', itemStyle: { color: '#f00' } },
|
{ type: 'max', symbol: 'pin', name: 'Max', itemStyle: { color: rgbaColorMarker }, symbolSize: 30, label: { fontSize: 8 } },
|
||||||
{ type: 'min', symbol: 'pin', name: 'Min', itemStyle: { color: '#0f0' } }
|
{ type: 'min', symbol: 'pin', name: 'Min', itemStyle: { color: rgbaColorMarker }, symbolSize: 30, label: { fontSize: 8, offset: [0, 7.5] }, symbolRotate: 180 }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,13 +5,16 @@
|
|||||||
</p>
|
</p>
|
||||||
</footer>
|
</footer>
|
||||||
<aside class="toolbox">
|
<aside class="toolbox">
|
||||||
|
<span v-if="page=='index' && countryMapChartData.length!=0" class="showMapChart">
|
||||||
|
<i @click="showMapChart" data-toggle="modal" data-target="#mapChartBox" class="bi bi-geo-alt"></i>
|
||||||
|
</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>
|
||||||
|
|||||||
@@ -39,6 +39,11 @@
|
|||||||
<i class="check icon" v-if="preferredTemplate === item.key"></i>
|
<i class="check icon" v-if="preferredTemplate === item.key"></i>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li v-if="defaultTemplate === 'custom'">
|
||||||
|
<a @click="toggleTemplate('custom')" class="item">
|
||||||
|
<i class="expand icon" style="font-size:1em"></i>Custom
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|||||||
+22
-15
@@ -6,16 +6,21 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css">
|
|
||||||
<link rel="stylesheet" href="/static/theme-server-status/css/bootstrap-theme.min.css">
|
|
||||||
<link rel="stylesheet" href="/static/theme-server-status/css/main.css?v20240225">
|
|
||||||
<link rel="stylesheet" href="/static/theme-server-status/css/dark.css">
|
|
||||||
<link rel="stylesheet" href="/static/theme-server-status/css/light.css">
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.min.css">
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/assets/font-logos.css">
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/lipis/[email protected]/css/flag-icons.min.css">
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/semantic.min.css">
|
|
||||||
<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-theme.min.css">
|
||||||
|
<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/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]/assets/font-logos.css">
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/[email protected]/css/flag-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/semantic.min.css">
|
||||||
|
<script src="https://unpkg.com/[email protected]/dist/jquery.min.js"></script>
|
||||||
|
<script src="https://unpkg.com/[email protected]/dist/js/bootstrap.min.js"></script>
|
||||||
|
<script src="https://unpkg.com/[email protected]/dist/vue.min.js"></script>
|
||||||
|
<script src="https://unpkg.com/[email protected]/dist/echarts.min.js"></script>
|
||||||
|
<script src="/static/theme-server-status/js/mixin.js?v20240711"></script>
|
||||||
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
|
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
|
||||||
<!--[if lt IE 9]>
|
<!--[if lt IE 9]>
|
||||||
<script src="/static/theme-server-status/js/html5shiv.js"></script>
|
<script src="/static/theme-server-status/js/html5shiv.js"></script>
|
||||||
@@ -24,11 +29,13 @@
|
|||||||
{{if ts .CustomCode}}
|
{{if ts .CustomCode}}
|
||||||
{{.CustomCode|safe}}
|
{{.CustomCode|safe}}
|
||||||
{{end}}
|
{{end}}
|
||||||
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js"></script>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js"></script>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/echarts.min.js"></script>
|
|
||||||
<script src="/static/theme-server-status/js/mixin.js?v20240302"></script>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+29
-8
@@ -27,10 +27,10 @@
|
|||||||
</td>
|
</td>
|
||||||
<td class="node-cell name center">@#node.name#@</td>
|
<td class="node-cell name center">@#node.name#@</td>
|
||||||
<td class="node-cell os center">
|
<td class="node-cell os center">
|
||||||
<i v-if='node.os == "darwin"' class="apple icon"></i>
|
<i v-if='isWindowsPlatform(node.host.Platform)' class="windows icon"></i>
|
||||||
<i v-else-if='isWindowsPlatform(node.host.Platform)' class="windows icon"></i>
|
<i v-else-if='getFontLogoClass(node.host.Platform) == "" && node.stateuptime > 0' class="fl-tux"></i>
|
||||||
<i v-else :class="'fl-' + getFontLogoClass(node.host.Platform)"></i>
|
<i v-else :class="'fl-' + getFontLogoClass(node.host.Platform)"></i>
|
||||||
<span class="node-cell-os-text">@#node.os#@</span>
|
<span class="node-cell-os-text">@#getPlatformName(node.os)#@</span>
|
||||||
</td>
|
</td>
|
||||||
<td style="text-align: center;" class="node-cell location">
|
<td style="text-align: center;" class="node-cell location">
|
||||||
<i :class="'fi fi-' + node.location"></i>
|
<i :class="'fi fi-' + node.location"></i>
|
||||||
@@ -71,20 +71,28 @@
|
|||||||
</span>
|
</span>
|
||||||
<span class="node-cell-expand" v-if="node.host.CPU">
|
<span class="node-cell-expand" v-if="node.host.CPU">
|
||||||
<span class="node-cell-expand-label">CPU:</span>
|
<span class="node-cell-expand-label">CPU:</span>
|
||||||
@#node.host.CPU.join(",")#@
|
@#node.host.CPU.join(",")#@ (@#node.cpu.percent#@%)
|
||||||
|
</span>
|
||||||
|
<span class="node-cell-expand" v-if="node.host.GPU">
|
||||||
|
<span class="node-cell-expand-label">GPU:</span>
|
||||||
|
@#node.host.GPU.join(",")#@
|
||||||
|
(@#parseInt(node.state.GPU >=0 ? node.state.GPU : 0)#@%)
|
||||||
</span>
|
</span>
|
||||||
<span class="node-cell-expand">
|
<span class="node-cell-expand">
|
||||||
<span class="node-cell-expand-label">{{tr "DiskUsed"}}:</span>
|
<span class="node-cell-expand-label">{{tr "DiskUsed"}}:</span>
|
||||||
@#formatByteSize(node.state.DiskUsed)#@ / @#formatByteSize(node.host.DiskTotal)#@
|
@#formatByteSize(node.state.DiskUsed)#@ / @#formatByteSize(node.host.DiskTotal)#@ (@#node.hdd.percent#@%)
|
||||||
</span>
|
</span>
|
||||||
<span class="node-cell-expand">
|
<span class="node-cell-expand">
|
||||||
<span class="node-cell-expand-label">{{tr "MemUsed"}}:</span>
|
<span class="node-cell-expand-label">{{tr "MemUsed"}}:</span>
|
||||||
@#formatByteSize(node.state.MemUsed)#@ / @#formatByteSize(node.host.MemTotal)#@(@#toFixed2(node.state.MemUsed / node.host.MemTotal * 100)#@%)
|
@#formatByteSize(node.state.MemUsed)#@ / @#formatByteSize(node.host.MemTotal)#@ (@#node.memory.percent#@%)
|
||||||
</span>
|
</span>
|
||||||
<span class="node-cell-expand">
|
<span v-if="node.host.SwapTotal > 0" class="node-cell-expand">
|
||||||
<span class="node-cell-expand-label">{{tr "SwapUsed"}}:</span>
|
<span class="node-cell-expand-label">{{tr "SwapUsed"}}:</span>
|
||||||
@#formatByteSize(node.state.SwapUsed)#@ / @#formatByteSize(node.host.SwapTotal)#@
|
@#formatByteSize(node.state.SwapUsed)#@ / @#formatByteSize(node.host.SwapTotal)#@
|
||||||
<span v-if="node.host.SwapTotal">(@#toFixed2(node.state.SwapUsed / node.host.SwapTotal * 100)#@%)</span>
|
<span>(@#toFixed2(node.state.SwapUsed / node.host.SwapTotal * 100)#@%)</span>
|
||||||
|
</span>
|
||||||
|
<span v-else class="node-cell-expand">
|
||||||
|
<span class="node-cell-expand-label">{{tr "SwapUsed"}}: OFF</span>
|
||||||
</span>
|
</span>
|
||||||
<span class="node-cell-expand">
|
<span class="node-cell-expand">
|
||||||
<span class="node-cell-expand-label">{{tr "NetTransfer"}}:</span>
|
<span class="node-cell-expand-label">{{tr "NetTransfer"}}:</span>
|
||||||
@@ -117,6 +125,19 @@
|
|||||||
<span class="node-cell-expand-label">{{tr "Uptime"}}:</span>
|
<span class="node-cell-expand-label">{{tr "Uptime"}}:</span>
|
||||||
@#node.uptime#@
|
@#node.uptime#@
|
||||||
</span>
|
</span>
|
||||||
|
<span v-if="node.state.Temperatures && getTemperature(node.state.Temperatures, sensorList) > 0" @click="toggleDetailedTemp(node.ID)" class="node-cell-expand temp-detail" :id="`temp-${node.ID}`" temp-detail-show="0">
|
||||||
|
<span class="node-cell-expand-label">{{tr "Temperature"}}:</span>
|
||||||
|
<span :id="`temp-main-${node.ID}`" class="node-cell-expand-label">
|
||||||
|
@#getTemperature(node.state.Temperatures, sensorList)#@°C
|
||||||
|
<i class="bi bi-chevron-double-down"></i>
|
||||||
|
</span>
|
||||||
|
<span :id="`temp-detail-${node.ID}`" class="node-cell-expand-label" style="display:none">
|
||||||
|
<span v-for="temp in node.state.Temperatures" :key="temp.Name" v-if="temp.Temperature !== 0">
|
||||||
|
[ @#temp.Name#@ : @#temp.Temperature#@°C ]
|
||||||
|
</span>
|
||||||
|
<i class="bi bi-chevron-double-up"></i>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
<span class="node-cell-expand">
|
<span class="node-cell-expand">
|
||||||
<span class="node-cell-expand-label">{{tr "Version"}}:</span>
|
<span class="node-cell-expand-label">{{tr "Version"}}:</span>
|
||||||
@#node.host.Version#@
|
@#node.host.Version#@
|
||||||
|
|||||||
+29
-8
@@ -30,10 +30,10 @@
|
|||||||
</td>
|
</td>
|
||||||
<td class="node-cell name center">@#node.name#@</td>
|
<td class="node-cell name center">@#node.name#@</td>
|
||||||
<td class="node-cell os center">
|
<td class="node-cell os center">
|
||||||
<i v-if='node.os == "darwin"' class="apple icon"></i>
|
<i v-if='isWindowsPlatform(node.host.Platform)' class="windows icon"></i>
|
||||||
<i v-else-if='isWindowsPlatform(node.host.Platform)' class="windows icon"></i>
|
<i v-else-if='getFontLogoClass(node.host.Platform) == "" && node.stateuptime > 0' class="fl-tux"></i>
|
||||||
<i v-else :class="'fl-' + getFontLogoClass(node.host.Platform)"></i>
|
<i v-else :class="'fl-' + getFontLogoClass(node.host.Platform)"></i>
|
||||||
<span class="node-cell-os-text">@#node.os#@</span>
|
<span class="node-cell-os-text">@#getPlatformName(node.os)#@</span>
|
||||||
</td>
|
</td>
|
||||||
<td style="text-align: center;" class="node-cell location">
|
<td style="text-align: center;" class="node-cell location">
|
||||||
<i :class="'fi fi-' + node.location"></i>
|
<i :class="'fi fi-' + node.location"></i>
|
||||||
@@ -74,20 +74,28 @@
|
|||||||
</span>
|
</span>
|
||||||
<span class="node-cell-expand" v-if="node.host.CPU">
|
<span class="node-cell-expand" v-if="node.host.CPU">
|
||||||
<span class="node-cell-expand-label">CPU:</span>
|
<span class="node-cell-expand-label">CPU:</span>
|
||||||
@#node.host.CPU.join(",")#@
|
@#node.host.CPU.join(",")#@ (@#node.cpu.percent#@%)
|
||||||
|
</span>
|
||||||
|
<span class="node-cell-expand" v-if="node.host.GPU">
|
||||||
|
<span class="node-cell-expand-label">GPU:</span>
|
||||||
|
@#node.host.GPU.join(",")#@
|
||||||
|
(@#parseInt(node.state.GPU >=0 ? node.state.GPU : 0)#@%)
|
||||||
</span>
|
</span>
|
||||||
<span class="node-cell-expand">
|
<span class="node-cell-expand">
|
||||||
<span class="node-cell-expand-label">{{tr "DiskUsed"}}:</span>
|
<span class="node-cell-expand-label">{{tr "DiskUsed"}}:</span>
|
||||||
@#formatByteSize(node.state.DiskUsed)#@ / @#formatByteSize(node.host.DiskTotal)#@
|
@#formatByteSize(node.state.DiskUsed)#@ / @#formatByteSize(node.host.DiskTotal)#@ (@#node.hdd.percent#@%)
|
||||||
</span>
|
</span>
|
||||||
<span class="node-cell-expand">
|
<span class="node-cell-expand">
|
||||||
<span class="node-cell-expand-label">{{tr "MemUsed"}}:</span>
|
<span class="node-cell-expand-label">{{tr "MemUsed"}}:</span>
|
||||||
@#formatByteSize(node.state.MemUsed)#@ / @#formatByteSize(node.host.MemTotal)#@(@#toFixed2(node.state.MemUsed / node.host.MemTotal * 100)#@%)
|
@#formatByteSize(node.state.MemUsed)#@ / @#formatByteSize(node.host.MemTotal)#@ (@#node.memory.percent#@%)
|
||||||
</span>
|
</span>
|
||||||
<span class="node-cell-expand">
|
<span v-if="node.host.SwapTotal > 0" class="node-cell-expand">
|
||||||
<span class="node-cell-expand-label">{{tr "SwapUsed"}}:</span>
|
<span class="node-cell-expand-label">{{tr "SwapUsed"}}:</span>
|
||||||
@#formatByteSize(node.state.SwapUsed)#@ / @#formatByteSize(node.host.SwapTotal)#@
|
@#formatByteSize(node.state.SwapUsed)#@ / @#formatByteSize(node.host.SwapTotal)#@
|
||||||
<span v-if="node.host.SwapTotal">(@#toFixed2(node.state.SwapUsed / node.host.SwapTotal * 100)#@%)</span>
|
<span>(@#toFixed2(node.state.SwapUsed / node.host.SwapTotal * 100)#@%)</span>
|
||||||
|
</span>
|
||||||
|
<span v-else class="node-cell-expand">
|
||||||
|
<span class="node-cell-expand-label">{{tr "SwapUsed"}}: OFF</span>
|
||||||
</span>
|
</span>
|
||||||
<span class="node-cell-expand">
|
<span class="node-cell-expand">
|
||||||
<span class="node-cell-expand-label">{{tr "NetTransfer"}}:</span>
|
<span class="node-cell-expand-label">{{tr "NetTransfer"}}:</span>
|
||||||
@@ -120,6 +128,19 @@
|
|||||||
<span class="node-cell-expand-label">{{tr "Uptime"}}:</span>
|
<span class="node-cell-expand-label">{{tr "Uptime"}}:</span>
|
||||||
@#node.uptime#@
|
@#node.uptime#@
|
||||||
</span>
|
</span>
|
||||||
|
<span v-if="node.state.Temperatures && getTemperature(node.state.Temperatures, sensorList) > 0" @click="toggleDetailedTemp(node.ID)" class="node-cell-expand temp-detail" :id="`temp-${node.ID}`" temp-detail-show="0">
|
||||||
|
<span class="node-cell-expand-label">{{tr "Temperature"}}:</span>
|
||||||
|
<span :id="`temp-main-${node.ID}`" class="node-cell-expand-label">
|
||||||
|
@#getTemperature(node.state.Temperatures, sensorList)#@°C
|
||||||
|
<i class="bi bi-chevron-double-down"></i>
|
||||||
|
</span>
|
||||||
|
<span :id="`temp-detail-${node.ID}`" class="node-cell-expand-label" style="display:none">
|
||||||
|
<span v-for="temp in node.state.Temperatures" :key="temp.Name" v-if="temp.Temperature !== 0">
|
||||||
|
[ @#temp.Name#@ : @#temp.Temperature#@°C ]
|
||||||
|
</span>
|
||||||
|
<i class="bi bi-chevron-double-up"></i>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
<span class="node-cell-expand">
|
<span class="node-cell-expand">
|
||||||
<span class="node-cell-expand-label">{{tr "Version"}}:</span>
|
<span class="node-cell-expand-label">{{tr "Version"}}:</span>
|
||||||
@#node.host.Version#@
|
@#node.host.Version#@
|
||||||
|
|||||||
+251
-6
@@ -14,6 +14,19 @@
|
|||||||
{{template "theme-server-status/home-group-false" .}}
|
{{template "theme-server-status/home-group-false" .}}
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
<div class="modal fade" id="mapChartBox" tabindex="-1" role="dialog" aria-labelledby="mapChartTitle" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-lg modal-dialog-centered">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h4 class="modal-title" id="mapChartTitle">{{tr "ServersOnWorldMap"}}</h4>
|
||||||
|
<i class="bi bi-x" data-dismiss="modal" aria-label="Close"></i>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div id="mapChart" style="width:100%;height:auto;"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{{template "theme-server-status/content-footer" .}}
|
{{template "theme-server-status/content-footer" .}}
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
@@ -29,7 +42,30 @@
|
|||||||
nodesTag: [],
|
nodesTag: [],
|
||||||
nodesNoTag: [],
|
nodesNoTag: [],
|
||||||
chartDataList: [],
|
chartDataList: [],
|
||||||
ws: null
|
ws: null,
|
||||||
|
language: {{.Conf.Language}},
|
||||||
|
countryMap: {},
|
||||||
|
countryServer: [],
|
||||||
|
countryNameMap: {},
|
||||||
|
countryMapChartData: [],
|
||||||
|
countryMapGeoFile: localStorage.getItem("countryMapGeoFile") ? localStorage.getItem("countryMapGeoFile") : 'nezha.world.geo.json',
|
||||||
|
sensorList: [
|
||||||
|
'TC0D', //CPU Die 温度,代表 CPU 内部的温度
|
||||||
|
'TC0H', //CPU Heatsink 温度,代表 CPU 散热器的温度
|
||||||
|
'TC0P', //CPU Proximity 温度,代表 CPU 附近的温度
|
||||||
|
'k10temp', //AMD K10(Phenom、Athlon、Sempron 等)系列处理器的温度监测
|
||||||
|
'k10temp_tctl', //AMD K10 (Athlon II、Phenom II 等)系列处理器的温度监测
|
||||||
|
'coretemp_package_id_0', //整个封装处理器温度
|
||||||
|
'cpu_thermal_zone', //全志
|
||||||
|
'cpu-thermal', //树莓派(博通)
|
||||||
|
'soc_thermal', //新瑞芯微
|
||||||
|
'cpu_thermal', //老瑞芯微
|
||||||
|
'ACPI\\ThermalZone\\TZ0__0', //Windows
|
||||||
|
'ACPI\\ThermalZone\\CPUZ_0', //Windows
|
||||||
|
'ACPI\\ThermalZone\\TZ00_0', //Windows
|
||||||
|
'ACPI\\ThermalZone\\TZ001_0', //Windows
|
||||||
|
'ACPI\\ThermalZone\\THM0_0' //Windows
|
||||||
|
]
|
||||||
},
|
},
|
||||||
mixins: [mixinsVue],
|
mixins: [mixinsVue],
|
||||||
created() {
|
created() {
|
||||||
@@ -39,6 +75,12 @@
|
|||||||
} else {
|
} else {
|
||||||
this.nodesNoTag = this.handleNodes(this.servers);
|
this.nodesNoTag = this.handleNodes(this.servers);
|
||||||
}
|
}
|
||||||
|
this.initCountryMap().then(countryMap => {
|
||||||
|
this.countryMap = countryMap;
|
||||||
|
this.countryNameMap = this.initCountryNameMap();
|
||||||
|
this.countryServer = this.initCountryServer();
|
||||||
|
this.countryMapChartData = this.initCountryMapChartData();
|
||||||
|
})
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
// 初始化时建立WebSocket连接
|
// 初始化时建立WebSocket连接
|
||||||
@@ -55,9 +97,145 @@
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
initCountryMap() {
|
||||||
|
return fetch(this.staticUrl + '/maps/nezha.countrymap.json')
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
return data;
|
||||||
|
})
|
||||||
|
},
|
||||||
|
initCountryNameMap(){
|
||||||
|
const result = {};
|
||||||
|
for (const item in this.countryMap) {
|
||||||
|
result[this.countryMap[item].en] = this.language=='zh-CN' ? this.countryMap[item].zh : this.countryMap[item].en;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
initCountryServer() {
|
||||||
|
let count = {};
|
||||||
|
let result = [];
|
||||||
|
let exist = {};
|
||||||
|
this.servers.forEach(item => {
|
||||||
|
if (item.Host.CountryCode) {
|
||||||
|
if(this.countryMapGeoFile == 'nezha.world.geo.json' || this.countryMapGeoFile == 'nezha.world.antarctica.geo.json') {
|
||||||
|
if(item.Host.CountryCode.toLowerCase() == 'tw' || item.Host.CountryCode.toLowerCase() == 'hk' || item.Host.CountryCode.toLowerCase() == 'mo') item.Host.CountryCode = 'cn';
|
||||||
|
}
|
||||||
|
if (!exist[item.Host.CountryCode]) {
|
||||||
|
exist[item.Host.CountryCode] = true;
|
||||||
|
count[item.Host.CountryCode] = 1;
|
||||||
|
const obj = {
|
||||||
|
'countryCode': item.Host.CountryCode,
|
||||||
|
'count': count[item.Host.CountryCode]
|
||||||
|
};
|
||||||
|
result.push(obj);
|
||||||
|
} else {
|
||||||
|
count[item.Host.CountryCode]++;
|
||||||
|
result.find(obj => obj.countryCode === item.Host.CountryCode).count = count[item.Host.CountryCode];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
initCountryMapChartData(){
|
||||||
|
let result = [];
|
||||||
|
this.countryServer.forEach(item => {
|
||||||
|
var obj = {
|
||||||
|
"name": this.language=='zh-CN' ? this.countryMap[item.countryCode].zh : this.countryMap[item.countryCode].en,
|
||||||
|
"value": item.count
|
||||||
|
}
|
||||||
|
result.push(obj);
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
showMapChart(){
|
||||||
|
// 处理异常
|
||||||
|
if( this.countryMapChartData.length == 0 ) {
|
||||||
|
console.log('init countryMapChartData not complete');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const unit = this.language=='zh-CN' ? '台' : 'servers';
|
||||||
|
const isMobile = this.checkIsMobile();
|
||||||
|
const width = isMobile ? 338 : 1102;
|
||||||
|
const height = isMobile ? 200 : 500;
|
||||||
|
const backgroundColor = this.theme == "dark" ? '#1C1D26' : '#ffffff';
|
||||||
|
const inRangeColor = this.theme == "dark" ? '#D2B206' : '#FFDF32';
|
||||||
|
const tooltipBackgroundColor = this.theme == "dark" ? "#ffffff" : '#ffffff';
|
||||||
|
const tooltipBorderColor = this.theme == "dark" ? "#ffffff" : "#ffffff";
|
||||||
|
const fontSize = isMobile ? 10 : 12;
|
||||||
|
const fontColor = this.theme == "dark" ? "#000000" : "#000000";
|
||||||
|
const chartContainer = document.getElementById('mapChart');
|
||||||
|
const mapChart = echarts.init(chartContainer, '', { // init图表
|
||||||
|
renderer: 'canvas',
|
||||||
|
useDirtyRect: false,
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
});
|
||||||
|
fetch(this.staticUrl + '/maps/' + this.countryMapGeoFile)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(worldMap => {
|
||||||
|
echarts.registerMap('nezha-world-map', worldMap);
|
||||||
|
const option = {
|
||||||
|
title: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
backgroundColor: backgroundColor,
|
||||||
|
grid: {
|
||||||
|
top: 0,
|
||||||
|
left: 10,
|
||||||
|
right: 10
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'item',
|
||||||
|
formatter: function (params) {
|
||||||
|
if (params.data && params.data.value > 0) {
|
||||||
|
return params.name + ' ' + params.data.value + ' ' + unit;
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
backgroundColor: tooltipBackgroundColor,
|
||||||
|
borderColor: tooltipBorderColor,
|
||||||
|
padding: [4,10],
|
||||||
|
textStyle: {
|
||||||
|
fontSize: fontSize,
|
||||||
|
color: fontColor
|
||||||
|
}
|
||||||
|
},
|
||||||
|
visualMap: {
|
||||||
|
show: false,
|
||||||
|
min: 0,
|
||||||
|
max: 1000,
|
||||||
|
calculable: false,
|
||||||
|
inRange: {
|
||||||
|
color: inRangeColor
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
type: 'map',
|
||||||
|
map: 'nezha-world-map',
|
||||||
|
roam: true,
|
||||||
|
label: {
|
||||||
|
show: false,
|
||||||
|
fontSize: 8
|
||||||
|
},
|
||||||
|
data: this.countryMapChartData,
|
||||||
|
nameMap: this.countryNameMap,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
mapChart.setOption(option);
|
||||||
|
});
|
||||||
|
},
|
||||||
isWindowsPlatform(str) {
|
isWindowsPlatform(str) {
|
||||||
return str.includes('Windows')
|
return str.includes('Windows')
|
||||||
},
|
},
|
||||||
|
getPlatformName(str){
|
||||||
|
if (str.toLowerCase().includes('opensuse')) {
|
||||||
|
return 'openSUSE';
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
},
|
||||||
getFontLogoClass(str) {
|
getFontLogoClass(str) {
|
||||||
if (["almalinux",
|
if (["almalinux",
|
||||||
"alpine",
|
"alpine",
|
||||||
@@ -104,6 +282,9 @@
|
|||||||
> -1) {
|
> -1) {
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
if (str == 'darwin') {
|
||||||
|
return 'apple';
|
||||||
|
}
|
||||||
if (['openwrt', 'linux', "immortalwrt"].indexOf(str) > -1) {
|
if (['openwrt', 'linux', "immortalwrt"].indexOf(str) > -1) {
|
||||||
return 'tux';
|
return 'tux';
|
||||||
}
|
}
|
||||||
@@ -113,6 +294,9 @@
|
|||||||
if (str == 'arch') {
|
if (str == 'arch') {
|
||||||
return 'archlinux';
|
return 'archlinux';
|
||||||
}
|
}
|
||||||
|
if (str.toLowerCase().includes('opensuse')) {
|
||||||
|
return 'opensuse';
|
||||||
|
}
|
||||||
return '';
|
return '';
|
||||||
},
|
},
|
||||||
secondToDate(s) {
|
secondToDate(s) {
|
||||||
@@ -139,6 +323,9 @@
|
|||||||
},
|
},
|
||||||
formatPercents(live,percent) {
|
formatPercents(live,percent) {
|
||||||
//if(!live) { percent = 0; }
|
//if(!live) { percent = 0; }
|
||||||
|
if (isNaN(percent)) {
|
||||||
|
percent = 0;
|
||||||
|
}
|
||||||
if (percent <= 0) {
|
if (percent <= 0) {
|
||||||
percent = 0;
|
percent = 0;
|
||||||
}
|
}
|
||||||
@@ -222,6 +409,7 @@
|
|||||||
name: server.Name,
|
name: server.Name,
|
||||||
os: platform,
|
os: platform,
|
||||||
location: server.Host.CountryCode,
|
location: server.Host.CountryCode,
|
||||||
|
stateuptime: server.State.Uptime,
|
||||||
uptime: this.secondToDate(server.State.Uptime),
|
uptime: this.secondToDate(server.State.Uptime),
|
||||||
load: this.toFixed2(server.State.Load1),
|
load: this.toFixed2(server.State.Load1),
|
||||||
network: this.getNetworkSpeed(server.State.NetInSpeed, server.State.NetOutSpeed),
|
network: this.getNetworkSpeed(server.State.NetInSpeed, server.State.NetOutSpeed),
|
||||||
@@ -242,6 +430,46 @@
|
|||||||
getNetworkSpeed(netInSpeed, netOutSpeed) {
|
getNetworkSpeed(netInSpeed, netOutSpeed) {
|
||||||
return this.formatByteSize(netInSpeed) + ' | ' + this.formatByteSize(netOutSpeed)
|
return this.formatByteSize(netInSpeed) + ' | ' + this.formatByteSize(netOutSpeed)
|
||||||
},
|
},
|
||||||
|
toggleDetailedTemp(id){
|
||||||
|
const tempDom = document.getElementById(`temp-${id}`);
|
||||||
|
const tempDetailShow = tempDom.getAttribute('temp-detail-show');
|
||||||
|
const tempMain = document.getElementById(`temp-main-${id}`);
|
||||||
|
const tempDetail = document.getElementById(`temp-detail-${id}`);
|
||||||
|
if (tempDetailShow == 0){
|
||||||
|
tempMain.style.display = 'none';
|
||||||
|
tempDetail.style.display = 'inline';
|
||||||
|
tempDom.setAttribute('temp-detail-show', 1);
|
||||||
|
} else {
|
||||||
|
tempMain.style.display = 'inline';
|
||||||
|
tempDetail.style.display = 'none';
|
||||||
|
tempDom.setAttribute('temp-detail-show', 0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getTemperature(temperatureList, sensorList) {
|
||||||
|
// 将 sensorList 中的所有项转换为小写
|
||||||
|
const lowerCaseSensorList = sensorList.map(sensor => sensor.toLowerCase());
|
||||||
|
|
||||||
|
// 合并过滤逻辑:过滤出 Temperature 不为 0 且 Name 在 sensorList 中的元素(忽略大小写)
|
||||||
|
const filtered = temperatureList.filter(item => item.Temperature !== 0 && lowerCaseSensorList.includes(item.Name.toLowerCase()));
|
||||||
|
|
||||||
|
// 如果有匹配的元素,则计算这些元素的 Temperature 的最大值
|
||||||
|
if (filtered.length > 0) {
|
||||||
|
return filtered.reduce((max, current) => {
|
||||||
|
return current.Temperature > max ? current.Temperature : max;
|
||||||
|
}, filtered[0].Temperature);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有匹配的元素,则计算 temperatureList 中所有 Temperature 不为 0 的元素的最大值
|
||||||
|
const nonZeroTemps = temperatureList.filter(item => item.Temperature !== 0);
|
||||||
|
if (nonZeroTemps.length > 0) {
|
||||||
|
return nonZeroTemps.reduce((max, current) => {
|
||||||
|
return current.Temperature > max ? current.Temperature : max;
|
||||||
|
}, nonZeroTemps[0].Temperature);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果所有元素的 Temperature 都为 0,则返回一个默认值 0
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
showCharts(event, id) {
|
showCharts(event, id) {
|
||||||
const chartContainer = this.$refs[`chart${id}`][0];
|
const chartContainer = this.$refs[`chart${id}`][0];
|
||||||
const chartboxShow = chartContainer.getAttribute('chartbox-show');
|
const chartboxShow = chartContainer.getAttribute('chartbox-show');
|
||||||
@@ -308,7 +536,7 @@
|
|||||||
const data = item.avg_delay.map((avgDelay, index) => {
|
const data = item.avg_delay.map((avgDelay, index) => {
|
||||||
if(avgDelay > 0 && avgDelay < MaxTCPPingValue){
|
if(avgDelay > 0 && avgDelay < MaxTCPPingValue){
|
||||||
loss += avgDelay > 0.9 * MaxTCPPingValue ? 1 : 0;
|
loss += avgDelay > 0.9 * MaxTCPPingValue ? 1 : 0;
|
||||||
return [item.created_at[index], avgDelay];
|
return [item.created_at[index], avgDelay.toFixed(2)];
|
||||||
}else{
|
}else{
|
||||||
loss += 1;
|
loss += 1;
|
||||||
}
|
}
|
||||||
@@ -324,6 +552,21 @@
|
|||||||
connectNulls: true
|
connectNulls: true
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const legendData = chartData.map(item => item.monitor_name);
|
||||||
|
const maxLegendsPerRowMobile = localStorage.getItem("maxLegendsPerRowMobile") ? localStorage.getItem("maxLegendsPerRowMobile") : 3;
|
||||||
|
const maxLegendsPerRowPc = localStorage.getItem("maxLegendsPerRowPc") ? localStorage.getItem("maxLegendsPerRowPc") : 6;
|
||||||
|
const autoIncrement = Math.floor((legendData.length - 1) / (isMobile ? maxLegendsPerRowMobile : maxLegendsPerRowPc)) * (isMobile ? 20 : 28)
|
||||||
|
const height = 300 + autoIncrement;
|
||||||
|
const gridTop = 40 + autoIncrement;
|
||||||
|
const legendIcon = isMobile ? 'rect' : "";
|
||||||
|
const itemWidth = isMobile ? 10 : 25;
|
||||||
|
const itemHeight = isMobile ? 10 : 14;
|
||||||
|
chart.resize({
|
||||||
|
width: 'auto',
|
||||||
|
height: height
|
||||||
|
});
|
||||||
|
|
||||||
const option = {
|
const option = {
|
||||||
backgroundColor: backgroundColor,
|
backgroundColor: backgroundColor,
|
||||||
title: {
|
title: {
|
||||||
@@ -339,8 +582,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
data: chartData.map(item => item.monitor_name),
|
data: legendData,
|
||||||
show: true,
|
show: true,
|
||||||
|
icon: legendIcon,
|
||||||
textStyle: {
|
textStyle: {
|
||||||
fontSize: fontSize,
|
fontSize: fontSize,
|
||||||
color: fontColor
|
color: fontColor
|
||||||
@@ -348,7 +592,9 @@
|
|||||||
top: legendTop,
|
top: legendTop,
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
left: legendLeft,
|
left: legendLeft,
|
||||||
padding: legendPadding
|
padding: legendPadding,
|
||||||
|
itemWidth: itemWidth,
|
||||||
|
itemHeight: itemHeight,
|
||||||
},
|
},
|
||||||
xAxis: {
|
xAxis: {
|
||||||
type: 'time',
|
type: 'time',
|
||||||
@@ -380,7 +626,7 @@
|
|||||||
color: fontColor
|
color: fontColor
|
||||||
},
|
},
|
||||||
grid: {
|
grid: {
|
||||||
top: '40',
|
top: gridTop,
|
||||||
left: gridLeft,
|
left: gridLeft,
|
||||||
right: gridRight
|
right: gridRight
|
||||||
}
|
}
|
||||||
@@ -401,4 +647,3 @@
|
|||||||
</script>
|
</script>
|
||||||
{{template "theme-server-status/footer" .}}
|
{{template "theme-server-status/footer" .}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
|||||||
+28
-9
@@ -22,9 +22,6 @@
|
|||||||
const monitorInfo = JSON.parse('{{.MonitorInfos}}');
|
const monitorInfo = JSON.parse('{{.MonitorInfos}}');
|
||||||
const initData = JSON.parse('{{.Servers}}').servers;
|
const initData = JSON.parse('{{.Servers}}').servers;
|
||||||
let MaxTCPPingValue = {{.Conf.MaxTCPPingValue}};
|
let MaxTCPPingValue = {{.Conf.MaxTCPPingValue}};
|
||||||
if (MaxTCPPingValue == null) {
|
|
||||||
MaxTCPPingValue = 1000;
|
|
||||||
}
|
|
||||||
new Vue({
|
new Vue({
|
||||||
el: '#app',
|
el: '#app',
|
||||||
delimiters: ['@#', '#@'],
|
delimiters: ['@#', '#@'],
|
||||||
@@ -171,19 +168,36 @@
|
|||||||
parseMonitorInfo(monitorInfo) {
|
parseMonitorInfo(monitorInfo) {
|
||||||
let tSeries = [];
|
let tSeries = [];
|
||||||
let tLegendData = [];
|
let tLegendData = [];
|
||||||
|
var lcolors = ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'];
|
||||||
for (let i = 0; i < monitorInfo.result.length; i++) {
|
for (let i = 0; i < monitorInfo.result.length; i++) {
|
||||||
|
var lcolor = lcolors[i % lcolors.length];
|
||||||
|
var rgbaColorMarker = 'rgba(' + parseInt(lcolor.slice(1, 3), 16) + ',' + parseInt(lcolor.slice(3, 5), 16) + ',' + parseInt(lcolor.slice(5, 7), 16) + ',0.5)';
|
||||||
|
var rgbaColorBar = 'rgba(' + parseInt(lcolor.slice(1, 3), 16) + ',' + parseInt(lcolor.slice(3, 5), 16) + ',' + parseInt(lcolor.slice(5, 7), 16) + ',0.35)';
|
||||||
let loss = 0;
|
let loss = 0;
|
||||||
let data = [];
|
let data = [];
|
||||||
|
let datal = [];
|
||||||
for (let j = 0; j < monitorInfo.result[i].created_at.length; j++) {
|
for (let j = 0; j < monitorInfo.result[i].created_at.length; j++) {
|
||||||
avgDelay = Math.round(monitorInfo.result[i].avg_delay[j]);
|
avgDelay = Math.round(monitorInfo.result[i].avg_delay[j]);
|
||||||
if (avgDelay > 0.9 * MaxTCPPingValue) {
|
if (avgDelay > 0 && avgDelay < MaxTCPPingValue) {
|
||||||
loss += 1
|
|
||||||
}
|
|
||||||
if (avgDelay > 0) {
|
|
||||||
data.push([monitorInfo.result[i].created_at[j], avgDelay]);
|
data.push([monitorInfo.result[i].created_at[j], avgDelay]);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
loss += 1;
|
||||||
|
datal.push({
|
||||||
|
xAxis: monitorInfo.result[i].created_at[j],
|
||||||
|
label: { show: false },
|
||||||
|
emphasis: { disabled: true },
|
||||||
|
lineStyle: {
|
||||||
|
type: "solid",
|
||||||
|
color: rgbaColorBar
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
lossRate = ((loss / monitorInfo.result[i].created_at.length) * 100).toFixed(1);
|
lossRate = ((loss / monitorInfo.result[i].created_at.length) * 100).toFixed(1);
|
||||||
|
if (lossRate > 99) {
|
||||||
|
datal = [];
|
||||||
|
}
|
||||||
legendName = monitorInfo.result[i].monitor_name +" "+ lossRate + "%";
|
legendName = monitorInfo.result[i].monitor_name +" "+ lossRate + "%";
|
||||||
tLegendData.push(legendName);
|
tLegendData.push(legendName);
|
||||||
tSeries.push({
|
tSeries.push({
|
||||||
@@ -192,10 +206,15 @@
|
|||||||
smooth: true,
|
smooth: true,
|
||||||
symbol: 'none',
|
symbol: 'none',
|
||||||
data: data,
|
data: data,
|
||||||
|
markLine: {
|
||||||
|
symbol: "none",
|
||||||
|
symbolSize :0,
|
||||||
|
data: datal
|
||||||
|
},
|
||||||
markPoint: {
|
markPoint: {
|
||||||
data: [
|
data: [
|
||||||
{ type: 'max', symbol: 'pin', name: 'Max', itemStyle: { color: '#f00' } },
|
{ type: 'max', symbol: 'pin', name: 'Max', itemStyle: { color: rgbaColorMarker }, symbolSize: 30, label: { fontSize: 8 } },
|
||||||
{ type: 'min', symbol: 'pin', name: 'Min', itemStyle: { color: '#0f0' } }
|
{ type: 'min', symbol: 'pin', name: 'Min', itemStyle: { color: rgbaColorMarker }, symbolSize: 30, label: { fontSize: 8, offset: [0, 7.5] }, symbolRotate: 180 }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
+118
-99
@@ -4,74 +4,74 @@
|
|||||||
{{template "theme-server-status/content-nav" .}}
|
{{template "theme-server-status/content-nav" .}}
|
||||||
<!-- showGroup true -->
|
<!-- showGroup true -->
|
||||||
<template v-if="showGroup">
|
<template v-if="showGroup">
|
||||||
<section class="container content" style="max-width: 95vw; min-height: .01%;overflow-x: auto;" v-for="group in servicesTag">
|
<section v-if="servicesTag.length === 0" class="container content" style="max-width: 95vw; min-height: .01%;overflow-x: auto;">
|
||||||
|
<p>No Valid Service Monitor Configuration Entries Found. Please Verify in the <a href="/monitor">Admin Panel</a>.</p>
|
||||||
|
</section>
|
||||||
|
<section v-else class="container content" style="max-width: 95vw; min-height: .01%;overflow-x: auto;" v-for="group in servicesTag">
|
||||||
{{template "theme-server-status/service-group-true" .}}
|
{{template "theme-server-status/service-group-true" .}}
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
<!-- showGroup false -->
|
<!-- showGroup false -->
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<section class="container content" style="max-width: 95vw; min-height: .01%;overflow-x: auto;">
|
<section v-if="servicesNoTag.length === 0" class="container content" style="max-width: 95vw; min-height: .01%;overflow-x: auto;">
|
||||||
|
<p>No Valid Service Monitor Configuration Entries Found. Please Verify in the <a href="/monitor">Admin Panel</a>.</p>
|
||||||
|
</section>
|
||||||
|
<section v-else class="container content" style="max-width: 95vw; min-height: .01%;overflow-x: auto;">
|
||||||
{{template "theme-server-status/service-group-false" .}}
|
{{template "theme-server-status/service-group-false" .}}
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
<section class="container content table-responsive" style="max-width: 95vw">
|
{{if .CycleTransferStats}}
|
||||||
{{if .CycleTransferStats}}
|
<section class="container content table-responsive" style="max-width: 95vw">
|
||||||
<table class="table table-striped table-condensed table-hover">
|
<table class="table table-striped table-condensed table-hover">
|
||||||
<thead>
|
<thead>
|
||||||
<tr class="node-group-tag">
|
<tr class="node-group-tag">
|
||||||
<th colspan="16" style="border:none;">
|
<th colspan="16" style="border:none;">
|
||||||
{{tr "CycleTransferStats"}}
|
{{tr "CycleTransferStats"}}
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="node-group-cell">
|
<tr class="node-group-cell">
|
||||||
<th class="node-cell center">ID</th>
|
<th class="node-cell center">ID</th>
|
||||||
<th class="node-cell center">{{tr "Rules"}}</th>
|
<th class="node-cell center">{{tr "Rules"}}</th>
|
||||||
<th class="node-cell center">{{tr "Server"}}</th>
|
<th class="node-cell center">{{tr "Server"}}</th>
|
||||||
<th class="node-cell center">{{tr "From"}}</th>
|
<th class="node-cell center">{{tr "From"}}</th>
|
||||||
<th class="node-cell center">{{tr "To"}}</th>
|
<th class="node-cell center">{{tr "To"}}</th>
|
||||||
<th class="node-cell center">MAX</th>
|
<th class="node-cell center">MAX</th>
|
||||||
<th class="node-cell center">MIN</th>
|
<th class="node-cell center">MIN</th>
|
||||||
<th class="node-cell center">{{tr "NextCheck"}}</th>
|
<th class="node-cell center">{{tr "NextCheck"}}</th>
|
||||||
<th class="node-cell center">{{tr "CurrentUsage"}}</th>
|
<th class="node-cell center">{{tr "CurrentUsage"}}</th>
|
||||||
<th class="node-cell center">{{tr "Transleft"}}</th>
|
<th class="node-cell center">{{tr "Transleft"}}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{{range $id, $stats := .CycleTransferStats}}
|
{{range $id, $stats := .CycleTransferStats}}
|
||||||
{{range $innerId, $transfer := $stats.Transfer}}
|
{{range $innerId, $transfer := $stats.Transfer}}
|
||||||
{{$TransLeftPercent := TransLeftPercent (UintToFloat $transfer) (UintToFloat $stats.Max)}}
|
{{$TransLeftPercent := TransLeftPercent (UintToFloat $transfer) (UintToFloat $stats.Max)}}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="node-cell center">{{$id}}</td>
|
<td class="node-cell center">{{$id}}</td>
|
||||||
<td class="node-cell center">{{$stats.Name}}</td>
|
<td class="node-cell center">{{$stats.Name}}</td>
|
||||||
<td class="node-cell center">{{index $stats.ServerName $innerId}}</td>
|
<td class="node-cell center">{{index $stats.ServerName $innerId}}</td>
|
||||||
<td class="node-cell center">{{$stats.From|tf}}</td>
|
<td class="node-cell center">{{$stats.From|tf}}</td>
|
||||||
<td class="node-cell center">{{$stats.To|tf}}</td>
|
<td class="node-cell center">{{$stats.To|tf}}</td>
|
||||||
<td class="node-cell center">{{$stats.Max|bf}}</td>
|
<td class="node-cell center">{{$stats.Max|bf}}</td>
|
||||||
<td class="node-cell center">{{$stats.Min|bf}}</td>
|
<td class="node-cell center">{{$stats.Min|bf}}</td>
|
||||||
<td class="node-cell center">{{(index $stats.NextUpdate $innerId)|sft}}</td>
|
<td class="node-cell center">{{(index $stats.NextUpdate $innerId)|sft}}</td>
|
||||||
<td class="node-cell center">{{$transfer|bf}}</td>
|
<td class="node-cell center">{{$transfer|bf}}</td>
|
||||||
<td class="node-cell center">
|
<td class="node-cell center">
|
||||||
<div class="progress">
|
<div class="progress">
|
||||||
<div style="width: {{$TransLeftPercent}}%" :class="'progress-bar progress-bar-' + toSSBar('{{TransClassName $TransLeftPercent}}')">
|
<div style="width: {{$TransLeftPercent}}%" :class="'progress-bar progress-bar-' + toSSBar('{{TransClassName $TransLeftPercent}}')">
|
||||||
<small style="display: inline-block;width: max-content;">{{TransLeft $stats.Max $transfer}} / {{$TransLeftPercent}} %</small>
|
<small style="display: inline-block;width: max-content;">{{TransLeft $stats.Max $transfer}} / {{$TransLeftPercent}} %</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{{end}}
|
</section>
|
||||||
</section>
|
{{end}}
|
||||||
{{template "theme-server-status/content-footer" .}}
|
{{template "theme-server-status/content-footer" .}}
|
||||||
</div>
|
</div>
|
||||||
<script>
|
|
||||||
// 初始化 Tooltip
|
|
||||||
$(document).ready(function(){
|
|
||||||
$('[data-toggle="tooltip"]').tooltip();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<script>
|
<script>
|
||||||
new Vue({
|
new Vue({
|
||||||
el: '#app',
|
el: '#app',
|
||||||
@@ -80,13 +80,16 @@
|
|||||||
page: 'service',
|
page: 'service',
|
||||||
defaultTemplate: {{.Conf.Site.Theme}},
|
defaultTemplate: {{.Conf.Site.Theme}},
|
||||||
templates: {{.Themes}},
|
templates: {{.Themes}},
|
||||||
|
services: {{.Services}},
|
||||||
servicesTag: [],
|
servicesTag: [],
|
||||||
servicesNoTag: [],
|
servicesNoTag: []
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.initData();
|
this.servicesTag = this.groupingData(this.initData(this.services),"type");
|
||||||
|
this.servicesNoTag = this.initData(this.services);
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
this.initTooltip();
|
||||||
},
|
},
|
||||||
mixins: [mixinsVue],
|
mixins: [mixinsVue],
|
||||||
methods: {
|
methods: {
|
||||||
@@ -103,32 +106,36 @@
|
|||||||
}
|
}
|
||||||
return n
|
return n
|
||||||
},
|
},
|
||||||
initData() {
|
initData(services) {
|
||||||
// @formatter:off
|
let nodes = [];
|
||||||
const services = []
|
for (let key in services) {
|
||||||
{{range $service := .Services}}
|
const service = services[key];
|
||||||
services.push({
|
let node = {
|
||||||
type: '{{$service.Monitor.Type}}',
|
type: service.Monitor.Type,
|
||||||
name: '{{$service.Monitor.Name}}',
|
name: service.Monitor.Name,
|
||||||
currentUp: parseInt('{{$service.CurrentUp}}'),
|
currentUp: parseInt(service.CurrentUp),
|
||||||
currentDown: parseInt('{{$service.CurrentDown}}'),
|
currentDown: parseInt(service.CurrentDown),
|
||||||
totalUp: parseInt('{{$service.TotalUp}}'),
|
totalUp: parseInt(service.TotalUp),
|
||||||
totalDown: parseInt('{{$service.TotalDown}}'),
|
totalDown: parseInt(service.TotalDown),
|
||||||
delay: '{{$service.Delay}}'.replaceAll("[","").replaceAll("]","").split(" "),
|
up: service.Up,
|
||||||
up: '{{$service.Up}}'.replaceAll("[","").replaceAll("]","").split(" "),
|
down: service.Down,
|
||||||
down: '{{$service.Down}}'.replaceAll("[","").replaceAll("]","").split(" "),
|
delay: service.Delay,
|
||||||
})
|
avgDelay: parseInt(this.getAvgDelay(service.Delay)) + "ms",
|
||||||
{{end}}
|
health: this.getStateInfo(this.getPercent(service.CurrentUp, service.CurrentDown)),
|
||||||
// @formatter:on
|
totalUpTime: this.getProgressInfo(this.getPercent(service.TotalUp, service.TotalDown))
|
||||||
for (let i = 0; i < services.length; i++) {
|
};
|
||||||
const service = services[i];
|
nodes.push(node);
|
||||||
service.avgDelay = parseInt(service.delay[service.delay.length - 1]) + "ms"
|
};
|
||||||
service.health = this.getStateInfo(this.getPercent(service.currentUp, service.currentDown))
|
for (let i = 0; i < nodes.length; i++) {
|
||||||
service.dayDetail = this.getDayTails(service)
|
const node = nodes[i];
|
||||||
service.totalUpTime = this.getProgressInfo(this.getPercent(service.totalUp, service.totalDown))
|
node.dayDetail = this.getDayTails(node)
|
||||||
}
|
}
|
||||||
this.servicesTag = this.groupingData(services,"type");
|
return nodes
|
||||||
this.servicesNoTag = services;
|
},
|
||||||
|
initTooltip() {
|
||||||
|
$(document).ready(function(){
|
||||||
|
$('[data-toggle="tooltip"]').tooltip();
|
||||||
|
});
|
||||||
},
|
},
|
||||||
getPercent(up, down) {
|
getPercent(up, down) {
|
||||||
if (!up) {
|
if (!up) {
|
||||||
@@ -151,24 +158,24 @@
|
|||||||
return this.toFixed2(currentUp / total * 100)
|
return this.toFixed2(currentUp / total * 100)
|
||||||
},
|
},
|
||||||
getDayTails(service) {
|
getDayTails(service) {
|
||||||
const result = []
|
const result = [];
|
||||||
for (let i = 0; i < service.up.length; i++) {
|
for (let i = 0; i < service.up.length; i++) {
|
||||||
const up = service.up[i]
|
const up = service.up[i];
|
||||||
const down = service.down[i]
|
const down = service.down[i];
|
||||||
const delay = service.delay[i]
|
let delay = Number(service.delay[i]);
|
||||||
let percent = this.getPercent(up, down)
|
let percent = this.getPercent(up, down);
|
||||||
if (percent <= 0) {
|
if (percent <= 0) {
|
||||||
percent = 0;
|
percent = 0;
|
||||||
}
|
}
|
||||||
let className = this.getStateInfo(percent).className
|
let className = this.getStateInfo(percent).className;
|
||||||
let available = '{{tr "Availability"}}'
|
let available = '{{tr "Availability"}}';
|
||||||
let averageLatency = '{{tr "AverageLatency"}}'
|
let averageLatency = '{{tr "AverageLatency"}}';
|
||||||
const text = `${this.beforeDay(service.up.length - i - 1)},${available}:${percent}%,${averageLatency}:${delay}ms`
|
const text = `${this.beforeDay(service.up.length - i - 1)},${available}:${Number(percent).toFixed(3)}%,${averageLatency}:${Number(delay).toFixed(3)}ms`;
|
||||||
result.push({
|
result.push({
|
||||||
text, className
|
text, className
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
return result
|
return result;
|
||||||
},
|
},
|
||||||
beforeDay(days) {
|
beforeDay(days) {
|
||||||
const today = new Date();
|
const today = new Date();
|
||||||
@@ -203,12 +210,13 @@
|
|||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
getProgressInfo(percent) {
|
getProgressInfo(percent) {
|
||||||
const result = this.getStateInfo(percent)
|
const result = this.getStateInfo(percent);
|
||||||
result.style = `width: ${parseInt(percent)}%`;
|
result.style = `width: ${parseInt(percent)}%`;
|
||||||
|
result.percent = Number(percent).toFixed(2);
|
||||||
const className = result.className;
|
const className = result.className;
|
||||||
if (className === "good") {
|
if (className === "good") {
|
||||||
result.className = 'progress-bar progress-bar-success'
|
result.className = 'progress-bar progress-bar-success'
|
||||||
} else if (className === "waining") {
|
} else if (className === "warning") {
|
||||||
result.className = 'progress-bar progress-bar-warning'
|
result.className = 'progress-bar progress-bar-warning'
|
||||||
} else if (className === "danger") {
|
} else if (className === "danger") {
|
||||||
result.className = 'progress-bar progress-bar-danger'
|
result.className = 'progress-bar progress-bar-danger'
|
||||||
@@ -218,8 +226,19 @@
|
|||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
},
|
},
|
||||||
|
getAvgDelay(array) {
|
||||||
|
const { sum, count } = array.reduce((acc, value) => {
|
||||||
|
if (value !== 0) {
|
||||||
|
acc.sum += value;
|
||||||
|
acc.count += 1;
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, { sum: 0, count: 0 });
|
||||||
|
|
||||||
|
return count > 0 ? sum / count : 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
{{template "theme-server-status/footer" .}}
|
{{template "theme-server-status/footer" .}}
|
||||||
{{end}}
|
{{end}}
|
||||||
+10
-1
@@ -14,7 +14,7 @@ site:
|
|||||||
theme: "default"
|
theme: "default"
|
||||||
ddns:
|
ddns:
|
||||||
enable: false
|
enable: false
|
||||||
provider: "webhook"
|
provider: "webhook" # 如需使用多配置功能,请把此项留空
|
||||||
accessid: ""
|
accessid: ""
|
||||||
accesssecret: ""
|
accesssecret: ""
|
||||||
webhookmethod: ""
|
webhookmethod: ""
|
||||||
@@ -22,3 +22,12 @@ ddns:
|
|||||||
webhookrequestbody: ""
|
webhookrequestbody: ""
|
||||||
webhookheaders: ""
|
webhookheaders: ""
|
||||||
maxretries: 3
|
maxretries: 3
|
||||||
|
profiles:
|
||||||
|
example:
|
||||||
|
provider: ""
|
||||||
|
accessid: ""
|
||||||
|
accesssecret: ""
|
||||||
|
webhookmethod: ""
|
||||||
|
webhookurl: ""
|
||||||
|
webhookrequestbody: ""
|
||||||
|
webhookheaders: ""
|
||||||
@@ -0,0 +1,293 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
#========================================================
|
||||||
|
# System Required: macOS 10.13+
|
||||||
|
# Description: Nezha Agent Install Script (macOS)
|
||||||
|
# Github: https://github.com/naiba/nezha
|
||||||
|
#========================================================
|
||||||
|
|
||||||
|
NZ_BASE_PATH="/opt/nezha"
|
||||||
|
NZ_AGENT_PATH="${NZ_BASE_PATH}/agent"
|
||||||
|
|
||||||
|
red='\033[0;31m'
|
||||||
|
green='\033[0;32m'
|
||||||
|
yellow='\033[0;33m'
|
||||||
|
plain='\033[0m'
|
||||||
|
export PATH=$PATH:/usr/local/bin
|
||||||
|
|
||||||
|
pre_check() {
|
||||||
|
# check root
|
||||||
|
[[ $EUID -ne 0 ]] && echo -e "${red}ERROR: ${plain} This script must be run with the root user!\n" && exit 1
|
||||||
|
|
||||||
|
## os_arch
|
||||||
|
if [[ $(uname -m | grep 'x86_64') != "" ]]; then
|
||||||
|
os_arch="amd64"
|
||||||
|
elif [[ $(uname -m | grep 'arm64\|arm64e') != "" ]]; then
|
||||||
|
os_arch="arm64"
|
||||||
|
fi
|
||||||
|
|
||||||
|
## China_IP
|
||||||
|
if [[ -z "${CN}" ]]; then
|
||||||
|
if [[ $(curl -m 10 -s https://ipapi.co/json | grep 'China') != "" ]]; then
|
||||||
|
echo "According to the information provided by ipapi.co, the current IP may be in China"
|
||||||
|
read -e -r -p "Is the installation done with a Chinese Mirror? [Y/n] (Custom Mirror Input 3):" input
|
||||||
|
case $input in
|
||||||
|
[yY][eE][sS] | [yY])
|
||||||
|
echo "Use Chinese Mirror"
|
||||||
|
CN=true
|
||||||
|
;;
|
||||||
|
|
||||||
|
[nN][oO] | [nN])
|
||||||
|
echo "No Use Chinese Mirror"
|
||||||
|
;;
|
||||||
|
|
||||||
|
[3])
|
||||||
|
echo "Use Custom Mirror"
|
||||||
|
read -e -r -p "Please enter a custom image (e.g. :dn-dao-github-mirror.daocloud.io), leave blank to nouse: " input
|
||||||
|
case $input in
|
||||||
|
*)
|
||||||
|
CUSTOM_MIRROR=$input
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "No Use Chinese Mirror"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "${CUSTOM_MIRROR}" ]]; then
|
||||||
|
GITHUB_RAW_URL="gitee.com/naibahq/nezha/raw/master"
|
||||||
|
GITHUB_URL=$CUSTOM_MIRROR
|
||||||
|
else
|
||||||
|
if [[ -z "${CN}" ]]; then
|
||||||
|
GITHUB_RAW_URL="raw.githubusercontent.com/naiba/nezha/master"
|
||||||
|
GITHUB_URL="github.com"
|
||||||
|
else
|
||||||
|
GITHUB_RAW_URL="gitee.com/naibahq/nezha/raw/master"
|
||||||
|
GITHUB_URL="gitee.com"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
before_show_menu() {
|
||||||
|
echo && echo -n -e "${yellow}* Press Enter to return to the main menu *${plain}" && read temp
|
||||||
|
show_menu
|
||||||
|
}
|
||||||
|
|
||||||
|
install_agent() {
|
||||||
|
echo -e "> Install Nezha Agent"
|
||||||
|
|
||||||
|
echo -e "Obtaining Agent version"
|
||||||
|
|
||||||
|
local version=$(curl -m 10 -sL "https://api.github.com/repos/nezhahq/agent/releases/latest" | grep "tag_name" | head -n 1 | awk -F ":" '{print $2}' | sed 's/\"//g;s/,//g;s/ //g')
|
||||||
|
if [ ! -n "$version" ]; then
|
||||||
|
version=$(curl -m 10 -sL "https://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
|
||||||
|
version=$(curl -m 10 -sL "https://fastly.jsdelivr.net/gh/nezhahq/agent/" | grep "option\.value" | awk -F "'" '{print $2}' | sed 's/nezhahq\/agent@/v/g')
|
||||||
|
fi
|
||||||
|
if [ ! -n "$version" ]; then
|
||||||
|
version=$(curl -m 10 -sL "https://gcore.jsdelivr.net/gh/nezhahq/agent/" | grep "option\.value" | awk -F "'" '{print $2}' | sed 's/nezhahq\/agent@/v/g')
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -n "$version" ]; then
|
||||||
|
echo -e "Fail to obtaine agent version, please check if the network can link https://api.github.com/repos/nezhahq/agent/releases/latest"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo -e "The current latest version is: ${version}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Nezha Agent Folder
|
||||||
|
mkdir -p $NZ_AGENT_PATH
|
||||||
|
chmod -R 777 $NZ_AGENT_PATH
|
||||||
|
|
||||||
|
echo -e "Downloading Agent"
|
||||||
|
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
|
||||||
|
echo -e "${red}Fail to download agent, please check if the network can link ${GITHUB_URL}${plain}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
unzip -qo nezha-agent_darwin_${os_arch}.zip &&
|
||||||
|
mv nezha-agent $NZ_AGENT_PATH &&
|
||||||
|
rm -rf nezha-agent_darwin_${os_arch}.zip README.md
|
||||||
|
|
||||||
|
if [ $# -ge 3 ]; then
|
||||||
|
modify_agent_config "$@"
|
||||||
|
else
|
||||||
|
modify_agent_config 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $# == 0 ]]; then
|
||||||
|
before_show_menu
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
modify_agent_config() {
|
||||||
|
echo -e "> Modify Agent Configuration"
|
||||||
|
|
||||||
|
if [ $# -lt 3 ]; then
|
||||||
|
echo "Please add Agent in the admin panel first, record the secret" &&
|
||||||
|
read -ep "Please enter a domain that resolves to the IP where the panel is located (no CDN sets): " nz_grpc_host &&
|
||||||
|
read -ep "Please enter the panel RPC port (default 5555): " nz_grpc_port &&
|
||||||
|
read -ep "Please enter the Agent secret: " nz_client_secret &&
|
||||||
|
read -ep "Do you want to enable SSL/TLS encryption for the gRPC port (--tls)? Press [y] if yes, the default is not required, and users can press Enter to skip if you don't understand: " nz_grpc_proxy
|
||||||
|
grep -qiw 'Y' <<<"${nz_grpc_proxy}" && args='--tls'
|
||||||
|
if [[ -z "${nz_grpc_host}" || -z "${nz_client_secret}" ]]; then
|
||||||
|
echo -e "${red}All options cannot be empty${plain}"
|
||||||
|
before_show_menu
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if [[ -z "${nz_grpc_port}" ]]; then
|
||||||
|
nz_grpc_port=5555
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
nz_grpc_host=$1
|
||||||
|
nz_grpc_port=$2
|
||||||
|
nz_client_secret=$3
|
||||||
|
shift 3
|
||||||
|
if [ $# -gt 0 ]; then
|
||||||
|
args=" $*"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
${NZ_AGENT_PATH}/nezha-agent service install -s "$nz_grpc_host:$nz_grpc_port" -p $nz_client_secret $args >/dev/null 2>&1
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
${NZ_AGENT_PATH}/nezha-agent service uninstall >/dev/null 2>&1
|
||||||
|
${NZ_AGENT_PATH}/nezha-agent service install -s "$nz_grpc_host:$nz_grpc_port" -p $nz_client_secret $args >/dev/null 2>&1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "Agent configuration ${green} modified successfully, please wait for agent self-restart to take effect${plain}"
|
||||||
|
|
||||||
|
#if [[ $# == 0 ]]; then
|
||||||
|
# before_show_menu
|
||||||
|
#fi
|
||||||
|
}
|
||||||
|
|
||||||
|
show_agent_log() {
|
||||||
|
echo -e "> > View Agent Log"
|
||||||
|
|
||||||
|
tail -n 10 /var/log/nezha-agent.err.log
|
||||||
|
|
||||||
|
if [[ $# == 0 ]]; then
|
||||||
|
before_show_menu
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
uninstall_agent() {
|
||||||
|
echo -e "> Uninstall Agent"
|
||||||
|
|
||||||
|
${NZ_AGENT_PATH}/nezha-agent service uninstall
|
||||||
|
|
||||||
|
rm -rf $NZ_AGENT_PATH
|
||||||
|
clean_all
|
||||||
|
|
||||||
|
if [[ $# == 0 ]]; then
|
||||||
|
before_show_menu
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
restart_agent() {
|
||||||
|
echo -e "> Restart Agent"
|
||||||
|
|
||||||
|
${NZ_AGENT_PATH}/nezha-agent service restart
|
||||||
|
|
||||||
|
if [[ $# == 0 ]]; then
|
||||||
|
before_show_menu
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
clean_all() {
|
||||||
|
if [ -z "$(ls -A ${NZ_BASE_PATH})" ]; then
|
||||||
|
rm -rf ${NZ_BASE_PATH}
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
show_usage() {
|
||||||
|
echo "Nezha Agent Management Script Usage: "
|
||||||
|
echo "--------------------------------------------------------"
|
||||||
|
echo "./nezha.sh install_agent - Install Agent"
|
||||||
|
echo "./nezha.sh modify_agent_config - Modify Agent Configuration"
|
||||||
|
echo "./nezha.sh show_agent_log - View Agent Log"
|
||||||
|
echo "./nezha.sh uninstall_agent - Uninstall Agent"
|
||||||
|
echo "./nezha.sh restart_agent - Restart Agent"
|
||||||
|
echo "./nezha.sh update_script - Update Script"
|
||||||
|
echo "--------------------------------------------------------"
|
||||||
|
}
|
||||||
|
|
||||||
|
show_menu() {
|
||||||
|
echo -e "
|
||||||
|
${green}Nezha Agent Management Script${plain} ${red}macOS${plain}
|
||||||
|
--- https://github.com/naiba/nezha ---
|
||||||
|
${green}1.${plain} Install Agent
|
||||||
|
${green}2.${plain} Modify Agent Configuration
|
||||||
|
${green}3.${plain} View Agent Log
|
||||||
|
${green}4.${plain} Uninstall Agent
|
||||||
|
${green}5.${plain} Restart Agent
|
||||||
|
————————————————-
|
||||||
|
${green}0.${plain} Exit Script
|
||||||
|
"
|
||||||
|
echo && read -ep "Please enter [0-5]: " num
|
||||||
|
case "${num}" in
|
||||||
|
0)
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
1)
|
||||||
|
install_agent
|
||||||
|
;;
|
||||||
|
2)
|
||||||
|
modify_agent_config
|
||||||
|
;;
|
||||||
|
3)
|
||||||
|
show_agent_log
|
||||||
|
;;
|
||||||
|
4)
|
||||||
|
uninstall_agent
|
||||||
|
;;
|
||||||
|
5)
|
||||||
|
restart_agent
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo -e "${red}Please enter the correct number [0-5]${plain}"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
pre_check
|
||||||
|
|
||||||
|
if [[ $# > 0 ]]; then
|
||||||
|
case $1 in
|
||||||
|
"install_agent")
|
||||||
|
shift
|
||||||
|
if [ $# -ge 3 ]; then
|
||||||
|
install_agent "$@"
|
||||||
|
else
|
||||||
|
install_agent 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"modify_agent_config")
|
||||||
|
modify_agent_config 0
|
||||||
|
;;
|
||||||
|
"show_agent_log")
|
||||||
|
show_agent_log 0
|
||||||
|
;;
|
||||||
|
"uninstall_agent")
|
||||||
|
uninstall_agent 0
|
||||||
|
;;
|
||||||
|
"restart_agent")
|
||||||
|
restart_agent 0
|
||||||
|
;;
|
||||||
|
*) show_usage ;;
|
||||||
|
esac
|
||||||
|
else
|
||||||
|
show_menu
|
||||||
|
fi
|
||||||
+30
-25
@@ -7,64 +7,69 @@ if($PSVersionTable.PSVersion.Major -lt 5){
|
|||||||
exit
|
exit
|
||||||
}
|
}
|
||||||
$agentrepo = "nezhahq/agent"
|
$agentrepo = "nezhahq/agent"
|
||||||
$nssmrepo = "nezhahq/nssm-backup"
|
# x86 or x64 or arm64
|
||||||
# x86 or x64
|
|
||||||
if ([System.Environment]::Is64BitOperatingSystem) {
|
if ([System.Environment]::Is64BitOperatingSystem) {
|
||||||
$file = "nezha-agent_windows_amd64.zip"
|
if ($env:PROCESSOR_ARCHITECTURE -eq "ARM64") {
|
||||||
|
$file = "nezha-agent_windows_arm64.zip"
|
||||||
|
} else {
|
||||||
|
$file = "nezha-agent_windows_amd64.zip"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$file = "nezha-agent_windows_386.zip"
|
$file = "nezha-agent_windows_386.zip"
|
||||||
}
|
}
|
||||||
$agentreleases = "https://api.github.com/repos/$agentrepo/releases"
|
$agentreleases = "https://api.github.com/repos/$agentrepo/releases"
|
||||||
$nssmreleases = "https://api.github.com/repos/$nssmrepo/releases"
|
|
||||||
#重复运行自动更新
|
#重复运行自动更新
|
||||||
if (Test-Path "C:\nezha") {
|
if (Test-Path "C:\nezha\nezha-agent.exe") {
|
||||||
Write-Host "Nezha monitoring already exists, delete and reinstall" -BackgroundColor DarkGreen -ForegroundColor White
|
Write-Host "Nezha monitoring already exists, delete and reinstall" -BackgroundColor DarkGreen -ForegroundColor White
|
||||||
C:/nezha/nssm.exe stop nezha
|
C:\nezha\nezha-agent.exe service uninstall
|
||||||
C:/nezha/nssm.exe remove nezha
|
|
||||||
Remove-Item "C:\nezha" -Recurse
|
Remove-Item "C:\nezha" -Recurse
|
||||||
}
|
}
|
||||||
#TLS/SSL
|
#TLS/SSL
|
||||||
Write-Host "Determining latest nezha release" -BackgroundColor DarkGreen -ForegroundColor White
|
Write-Host "Determining latest nezha release" -BackgroundColor DarkGreen -ForegroundColor White
|
||||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||||
$agenttag = (Invoke-WebRequest -Uri $agentreleases -UseBasicParsing | ConvertFrom-Json)[0].tag_name
|
$agenttag = (Invoke-WebRequest -Uri $agentreleases -UseBasicParsing | ConvertFrom-Json)[0].tag_name
|
||||||
$nssmtag = (Invoke-WebRequest -Uri $nssmreleases -UseBasicParsing | ConvertFrom-Json)[0].tag_name
|
if ([string]::IsNullOrWhiteSpace($agenttag)) {
|
||||||
|
$optionUrl = "https://fastly.jsdelivr.net/gh/nezhahq/agent/"
|
||||||
|
Try {
|
||||||
|
$response = Invoke-WebRequest -Uri $optionUrl -UseBasicParsing -TimeoutSec 10
|
||||||
|
if ($response.StatusCode -eq 200) {
|
||||||
|
$versiontext = $response.Content | findstr /c:"option.value"
|
||||||
|
$version = [regex]::Match($versiontext, "@(\d+\.\d+\.\d+)").Groups[1].Value
|
||||||
|
$agenttag = "v" + $version
|
||||||
|
}
|
||||||
|
} Catch {
|
||||||
|
$optionUrl = "https://gcore.jsdelivr.net/gh/nezhahq/agent/"
|
||||||
|
$response = Invoke-WebRequest -Uri $optionUrl -UseBasicParsing -TimeoutSec 10
|
||||||
|
if ($response.StatusCode -eq 200) {
|
||||||
|
$versiontext = $response.Content | findstr /c:"option.value"
|
||||||
|
$version = [regex]::Match($versiontext, "@(\d+\.\d+\.\d+)").Groups[1].Value
|
||||||
|
$agenttag = "v" + $version
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
#Region判断
|
#Region判断
|
||||||
$ipapi= Invoke-RestMethod -Uri "https://api.myip.com/" -UserAgent "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.163 Safari/535.1"
|
$ipapi= Invoke-RestMethod -Uri "https://api.myip.com/" -UserAgent "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.163 Safari/535.1"
|
||||||
$region=$ipapi.cc
|
$region=$ipapi.cc
|
||||||
echo $ipapi
|
echo $ipapi
|
||||||
if($region -ne "CN"){
|
if($region -ne "CN"){
|
||||||
$download = "https://github.com/$agentrepo/releases/download/$agenttag/$file"
|
$download = "https://github.com/$agentrepo/releases/download/$agenttag/$file"
|
||||||
$nssmdownload="https://github.com/$nssmrepo/releases/download/$nssmtag/nssm.zip"
|
|
||||||
Write-Host "Location:$region,connect directly!" -BackgroundColor DarkRed -ForegroundColor Green
|
Write-Host "Location:$region,connect directly!" -BackgroundColor DarkRed -ForegroundColor Green
|
||||||
}else{
|
}else{
|
||||||
$download = "https://dn-dao-github-mirror.daocloud.io/$agentrepo/releases/download/$agenttag/$file"
|
$download = "https://gitee.com/naibahq/agent/releases/download/$agenttag/$file"
|
||||||
$nssmdownload="https://dn-dao-github-mirror.daocloud.io/$nssmrepo/releases/download/$nssmtag/nssm.zip"
|
|
||||||
Write-Host "Location:CN,use mirror address" -BackgroundColor DarkRed -ForegroundColor Green
|
Write-Host "Location:CN,use mirror address" -BackgroundColor DarkRed -ForegroundColor Green
|
||||||
}
|
}
|
||||||
echo $download
|
echo $download
|
||||||
echo $nssmdownload
|
|
||||||
Invoke-WebRequest $download -OutFile "C:\nezha.zip"
|
Invoke-WebRequest $download -OutFile "C:\nezha.zip"
|
||||||
#使用nssm安装服务
|
|
||||||
Invoke-WebRequest $nssmdownload -OutFile "C:\nssm.zip"
|
|
||||||
#解压
|
#解压
|
||||||
Expand-Archive "C:\nezha.zip" -DestinationPath "C:\temp" -Force
|
Expand-Archive "C:\nezha.zip" -DestinationPath "C:\temp" -Force
|
||||||
Expand-Archive "C:\nssm.zip" -DestinationPath "C:\temp" -Force
|
|
||||||
if (!(Test-Path "C:\nezha")) { New-Item -Path "C:\nezha" -type directory }
|
if (!(Test-Path "C:\nezha")) { New-Item -Path "C:\nezha" -type directory }
|
||||||
#整理文件
|
#整理文件
|
||||||
Move-Item -Path "C:\temp\nezha-agent.exe" -Destination "C:\nezha\nezha-agent.exe"
|
Move-Item -Path "C:\temp\nezha-agent.exe" -Destination "C:\nezha\nezha-agent.exe"
|
||||||
if ($file = "nezha-agent_windows_amd64.zip") {
|
|
||||||
Move-Item -Path "C:\temp\nssm-2.24\win64\nssm.exe" -Destination "C:\nezha\nssm.exe"
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Move-Item -Path "C:\temp\nssm-2.24\win32\nssm.exe" -Destination "C:\nezha\nssm.exe"
|
|
||||||
}
|
|
||||||
#清理垃圾
|
#清理垃圾
|
||||||
Remove-Item "C:\nezha.zip"
|
Remove-Item "C:\nezha.zip"
|
||||||
Remove-Item "C:\nssm.zip"
|
|
||||||
Remove-Item "C:\temp" -Recurse
|
Remove-Item "C:\temp" -Recurse
|
||||||
#安装部分
|
#安装部分
|
||||||
C:\nezha\nssm.exe install nezha C:\nezha\nezha-agent.exe -s $server -p $key $tls -d
|
C:\nezha\nezha-agent.exe service install -s $server -p $key $tls
|
||||||
C:\nezha\nssm.exe start nezha
|
|
||||||
#enjoy
|
#enjoy
|
||||||
Write-Host "Enjoy It!" -BackgroundColor DarkGreen -ForegroundColor Red
|
Write-Host "Enjoy It!" -BackgroundColor DarkGreen -ForegroundColor Red
|
||||||
+399
-637
File diff suppressed because it is too large
Load Diff
+374
-613
File diff suppressed because it is too large
Load Diff
@@ -1,62 +0,0 @@
|
|||||||
#!/sbin/openrc-run
|
|
||||||
supervisor=supervise-daemon
|
|
||||||
name=nezha-agent
|
|
||||||
supervise_daemon_args="--stdout /var/log/${name}.log --stderr /var/log/${name}.err"
|
|
||||||
SERVER="nz_grpc_host:nz_grpc_port" #Dashboard 地址 ip:port
|
|
||||||
SECRET="nz_client_secret" #SECRET
|
|
||||||
NZ_BASE_PATH="/opt/nezha"
|
|
||||||
NZ_AGENT_PATH="${NZ_BASE_PATH}/agent"
|
|
||||||
pidfile="/run/${RC_SVCNAME}.pid"
|
|
||||||
command="/opt/nezha/agent/nezha-agent"
|
|
||||||
command_args="-s ${SERVER} -p ${SECRET}"
|
|
||||||
command_background=true
|
|
||||||
depend() {
|
|
||||||
need net
|
|
||||||
}
|
|
||||||
checkconfig() {
|
|
||||||
GITHUB_URL="github.com"
|
|
||||||
if [ ! -f "${NZ_AGENT_PATH}/nezha-agent" ]; then
|
|
||||||
if [[ $(uname -m | grep 'x86_64') != "" ]]; then
|
|
||||||
os_arch="amd64"
|
|
||||||
elif [[ $(uname -m | grep 'i386\|i686') != "" ]]; then
|
|
||||||
os_arch="386"
|
|
||||||
elif [[ $(uname -m | grep 'aarch64\|armv8b\|armv8l') != "" ]]; then
|
|
||||||
os_arch="arm64"
|
|
||||||
elif [[ $(uname -m | grep 'arm') != "" ]]; then
|
|
||||||
os_arch="arm"
|
|
||||||
elif [[ $(uname -m | grep 's390x') != "" ]]; then
|
|
||||||
os_arch="s390x"
|
|
||||||
elif [[ $(uname -m | grep 'riscv64') != "" ]]; then
|
|
||||||
os_arch="riscv64"
|
|
||||||
fi
|
|
||||||
local version=$(curl -m 10 -sL "https://api.github.com/repos/nezhahq/agent/releases/latest" | grep "tag_name" | head -n 1 | awk -F ":" '{print $2}' | sed 's/\"//g;s/,//g;s/ //g')
|
|
||||||
if [ ! -n "$version" ]; then
|
|
||||||
version=$(curl -m 10 -sL "https://fastly.jsdelivr.net/gh/nezhahq/agent/" | grep "option\.value" | awk -F "'" '{print $2}' | sed 's/nezhahq\/agent@/v/g')
|
|
||||||
fi
|
|
||||||
if [ ! -n "$version" ]; then
|
|
||||||
version=$(curl -m 10 -sL "https://gcore.jsdelivr.net/gh/nezhahq/agent/" | grep "option\.value" | awk -F "'" '{print $2}' | sed 's/nezhahq\/agent@/v/g')
|
|
||||||
fi
|
|
||||||
if [ ! -n "$version" ]; then
|
|
||||||
echo -e "获取版本号失败,请检查本机能否链接 https://api.github.com/repos/nezhahq/agent/releases/latest"
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
echo -e "当前最新版本为: ${version}"
|
|
||||||
fi
|
|
||||||
wget -t 2 -T 10 -O nezha-agent_linux_${os_arch}.zip https://${GITHUB_URL}/nezhahq/agent/releases/download/${version}/nezha-agent_linux_${os_arch}.zip >/dev/null 2>&1
|
|
||||||
if [[ $? != 0 ]]; then
|
|
||||||
echo -e "Release 下载失败,请检查本机能否连接 ${GITHUB_URL}${plain}"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
mkdir -p $NZ_AGENT_PATH
|
|
||||||
chmod 755 -R $NZ_AGENT_PATH
|
|
||||||
unzip -qo nezha-agent_linux_${os_arch}.zip && mv nezha-agent $NZ_AGENT_PATH && rm -rf nezha-agent_linux_${os_arch}.zip README.md
|
|
||||||
fi
|
|
||||||
if [ ! -x "${NZ_AGENT_PATH}/nezha-agent" ]; then
|
|
||||||
chmod +x ${NZ_AGENT_PATH}/nezha-agent
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
start_pre() {
|
|
||||||
if [ "${RC_CMD}" != "restart" ]; then
|
|
||||||
checkconfig || return $?
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
[Unit]
|
|
||||||
Description=Nezha Agent
|
|
||||||
After=syslog.target
|
|
||||||
#After=network.target
|
|
||||||
#After=nezha-dashboard.service
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
# Modify these two values and uncomment them if you have
|
|
||||||
# repos with lots of files and get an HTTP error 500 because
|
|
||||||
# of that
|
|
||||||
###
|
|
||||||
#LimitMEMLOCK=infinity
|
|
||||||
#LimitNOFILE=65535
|
|
||||||
Type=simple
|
|
||||||
User=root
|
|
||||||
Group=root
|
|
||||||
WorkingDirectory=/opt/nezha/agent/
|
|
||||||
ExecStart=/opt/nezha/agent/nezha-agent -s nz_grpc_host:nz_grpc_port -p nz_client_secret
|
|
||||||
Restart=always
|
|
||||||
#Environment=DEBUG=true
|
|
||||||
|
|
||||||
# Some distributions may not support these hardening directives. If you cannot start the service due
|
|
||||||
# to an unknown option, comment out the ones not supported by your version of systemd.
|
|
||||||
#ProtectSystem=full
|
|
||||||
#PrivateDevices=yes
|
|
||||||
#PrivateTmp=yes
|
|
||||||
#NoNewPrivileges=true
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
Regular → Executable
+7
-5
@@ -1,4 +1,8 @@
|
|||||||
#!/sbin/openrc-run
|
#!/sbin/openrc-run
|
||||||
|
supervisor=supervise-daemon
|
||||||
|
name=nezha-dashboard
|
||||||
|
output_log=/var/log/${name}.log
|
||||||
|
error_log=/var/log/${name}.err
|
||||||
pidfile="/run/${RC_SVCNAME}.pid"
|
pidfile="/run/${RC_SVCNAME}.pid"
|
||||||
command="/opt/nezha/dashboard/app"
|
command="/opt/nezha/dashboard/app"
|
||||||
command_args=""
|
command_args=""
|
||||||
@@ -6,11 +10,9 @@ command_background=true
|
|||||||
directory="/opt/nezha/dashboard"
|
directory="/opt/nezha/dashboard"
|
||||||
|
|
||||||
depend() {
|
depend() {
|
||||||
need net
|
need net
|
||||||
}
|
}
|
||||||
|
|
||||||
reload() {
|
start_pre() {
|
||||||
ebegin "Reloading ${RC_SVCNAME}"
|
checkpath -f -m 0644 -o root:root "/var/log/${name}.log"
|
||||||
start-stop-daemon --signal HUP --pidfile "${pidfile}"
|
|
||||||
eend $?
|
|
||||||
}
|
}
|
||||||
|
|||||||
+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
|
||||||
|
}
|
||||||
+52
-7
@@ -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) {
|
||||||
@@ -106,6 +121,7 @@ func (s *NezhaHandler) ReportSystemState(c context.Context, r *pb.State) (*pb.Re
|
|||||||
|
|
||||||
func (s *NezhaHandler) ReportSystemInfo(c context.Context, r *pb.Host) (*pb.Receipt, error) {
|
func (s *NezhaHandler) ReportSystemInfo(c context.Context, r *pb.Host) (*pb.Receipt, error) {
|
||||||
var clientID uint64
|
var clientID uint64
|
||||||
|
var provider ddns.Provider
|
||||||
var err error
|
var err error
|
||||||
if clientID, err = s.Auth.Check(c); err != nil {
|
if clientID, err = s.Auth.Check(c); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -122,13 +138,17 @@ func (s *NezhaHandler) ReportSystemInfo(c context.Context, r *pb.Host) (*pb.Rece
|
|||||||
singleton.ServerList[clientID].Host.IP != host.IP {
|
singleton.ServerList[clientID].Host.IP != host.IP {
|
||||||
|
|
||||||
serverDomain := singleton.ServerList[clientID].DDNSDomain
|
serverDomain := singleton.ServerList[clientID].DDNSDomain
|
||||||
provider, err := singleton.GetDDNSProviderFromString(singleton.Conf.DDNS.Provider)
|
if singleton.Conf.DDNS.Provider == "" {
|
||||||
|
provider, err = singleton.GetDDNSProviderFromProfile(singleton.ServerList[clientID].DDNSProfile)
|
||||||
|
} else {
|
||||||
|
provider, err = singleton.GetDDNSProviderFromString(singleton.Conf.DDNS.Provider)
|
||||||
|
}
|
||||||
if err == nil && serverDomain != "" {
|
if err == nil && serverDomain != "" {
|
||||||
ipv4, ipv6, _ := utils.SplitIPAddr(host.IP)
|
ipv4, ipv6, _ := utils.SplitIPAddr(host.IP)
|
||||||
maxRetries := int(singleton.Conf.DDNS.MaxRetries)
|
maxRetries := int(singleton.Conf.DDNS.MaxRetries)
|
||||||
config := &ddns.DomainConfig{
|
config := &ddns.DomainConfig{
|
||||||
EnableIPv4: true,
|
EnableIPv4: singleton.ServerList[clientID].EnableIPv4,
|
||||||
EnableIpv6: true,
|
EnableIpv6: singleton.ServerList[clientID].EnableIpv6,
|
||||||
FullDomain: serverDomain,
|
FullDomain: serverDomain,
|
||||||
Ipv4Addr: ipv4,
|
Ipv4Addr: ipv4,
|
||||||
Ipv6Addr: ipv6,
|
Ipv6Addr: ipv6,
|
||||||
@@ -137,7 +157,7 @@ func (s *NezhaHandler) ReportSystemInfo(c context.Context, r *pb.Host) (*pb.Rece
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
// 虽然会在启动时panic, 可以断言不会走这个分支, 但是考虑到动态加载配置或者其它情况, 这里输出一下方便检查奇奇怪怪的BUG
|
// 虽然会在启动时panic, 可以断言不会走这个分支, 但是考虑到动态加载配置或者其它情况, 这里输出一下方便检查奇奇怪怪的BUG
|
||||||
log.Printf("NEZHA>> 未找到对应的DDNS提供者(%s), 请前往config.yml检查你的设置\n", singleton.Conf.DDNS.Provider)
|
log.Printf("NEZHA>> 未找到对应的DDNS配置(%s), 或者是provider填写不正确, 请前往config.yml检查你的设置\n", singleton.ServerList[clientID].DDNSProfile)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -172,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)
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
package singleton
|
package singleton
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
ddns2 "github.com/naiba/nezha/pkg/ddns"
|
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
|
ddns2 "github.com/naiba/nezha/pkg/ddns"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RetryableUpdateDomain(provider ddns2.Provider, config *ddns2.DomainConfig, maxRetries int) bool {
|
func RetryableUpdateDomain(provider ddns2.Provider, config *ddns2.DomainConfig, maxRetries int) bool {
|
||||||
@@ -25,18 +25,64 @@ func RetryableUpdateDomain(provider ddns2.Provider, config *ddns2.DomainConfig,
|
|||||||
func GetDDNSProviderFromString(provider string) (ddns2.Provider, error) {
|
func GetDDNSProviderFromString(provider string) (ddns2.Provider, error) {
|
||||||
switch provider {
|
switch provider {
|
||||||
case "webhook":
|
case "webhook":
|
||||||
return ddns2.ProviderWebHook{
|
return &ddns2.ProviderWebHook{
|
||||||
URL: Conf.DDNS.WebhookURL,
|
URL: Conf.DDNS.WebhookURL,
|
||||||
RequestMethod: Conf.DDNS.WebhookMethod,
|
RequestMethod: Conf.DDNS.WebhookMethod,
|
||||||
RequestBody: Conf.DDNS.WebhookRequestBody,
|
RequestBody: Conf.DDNS.WebhookRequestBody,
|
||||||
RequestHeader: Conf.DDNS.WebhookHeaders,
|
RequestHeader: Conf.DDNS.WebhookHeaders,
|
||||||
}, nil
|
}, nil
|
||||||
case "dummy":
|
case "dummy":
|
||||||
return ddns2.ProviderDummy{}, nil
|
return &ddns2.ProviderDummy{}, nil
|
||||||
case "cloudflare":
|
case "cloudflare":
|
||||||
return ddns2.ProviderCloudflare{
|
return &ddns2.ProviderCloudflare{
|
||||||
Secret: Conf.DDNS.AccessSecret,
|
Secret: Conf.DDNS.AccessSecret,
|
||||||
}, nil
|
}, nil
|
||||||
|
case "tencentcloud":
|
||||||
|
return &ddns2.ProviderTencentCloud{
|
||||||
|
SecretID: Conf.DDNS.AccessID,
|
||||||
|
SecretKey: Conf.DDNS.AccessSecret,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
return ddns2.ProviderDummy{}, errors.New(fmt.Sprintf("无法找到配置的DDNS提供者%s", Conf.DDNS.Provider))
|
return &ddns2.ProviderDummy{}, fmt.Errorf("无法找到配置的DDNS提供者%s", Conf.DDNS.Provider)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDDNSProviderFromProfile(profileName string) (ddns2.Provider, error) {
|
||||||
|
profile, ok := Conf.DDNS.Profiles[profileName]
|
||||||
|
if !ok {
|
||||||
|
return &ddns2.ProviderDummy{}, fmt.Errorf("未找到配置项 %s", profileName)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch profile.Provider {
|
||||||
|
case "webhook":
|
||||||
|
return &ddns2.ProviderWebHook{
|
||||||
|
URL: profile.WebhookURL,
|
||||||
|
RequestMethod: profile.WebhookMethod,
|
||||||
|
RequestBody: profile.WebhookRequestBody,
|
||||||
|
RequestHeader: profile.WebhookHeaders,
|
||||||
|
}, nil
|
||||||
|
case "dummy":
|
||||||
|
return &ddns2.ProviderDummy{}, nil
|
||||||
|
case "cloudflare":
|
||||||
|
return &ddns2.ProviderCloudflare{
|
||||||
|
Secret: profile.AccessSecret,
|
||||||
|
}, nil
|
||||||
|
case "tencentcloud":
|
||||||
|
return &ddns2.ProviderTencentCloud{
|
||||||
|
SecretID: profile.AccessID,
|
||||||
|
SecretKey: profile.AccessSecret,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
return &ddns2.ProviderDummy{}, fmt.Errorf("无法找到配置的DDNS提供者%s", profile.Provider)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ValidateDDNSProvidersFromProfiles() error {
|
||||||
|
validProviders := map[string]bool{"webhook": true, "dummy": true, "cloudflare": true, "tencentcloud": true}
|
||||||
|
providers := make(map[string]string)
|
||||||
|
for profileName, profile := range Conf.DDNS.Profiles {
|
||||||
|
if _, ok := validProviders[profile.Provider]; !ok {
|
||||||
|
return fmt.Errorf("无法找到配置的DDNS提供者%s", profile.Provider)
|
||||||
|
}
|
||||||
|
providers[profileName] = profile.Provider
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user