Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
26d81f6d7a | ||
|
|
9d96c58ff9 | ||
|
|
5bb7efdeb9 | ||
|
|
519882c4a9 | ||
|
|
ed96432b79 | ||
|
|
1c057d93bd | ||
|
|
6005004681 | ||
|
|
d8f4236404 | ||
|
|
f95191c8af | ||
|
|
34fedd91d2 | ||
|
|
f675b884f5 | ||
|
|
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 | ||
|
|
29554ec7ec | ||
|
|
9a8c0e7118 | ||
|
|
800af7ce4e | ||
|
|
78134a0e8b | ||
|
|
bad910b686 | ||
|
|
a7d1bd7487 | ||
|
|
20a10ac6b8 | ||
|
|
7d85c4c4a5 | ||
|
|
b1348eb545 | ||
|
|
753199a3b8 | ||
|
|
64ba3cd0a7 | ||
|
|
3a395e66c0 | ||
|
|
193455c675 | ||
|
|
dfb08caae1 | ||
|
|
7f7763606d | ||
|
|
77de1b44bc | ||
|
|
da6a3463ed | ||
|
|
6fa149519d | ||
|
|
8e67d3487e | ||
|
|
8033c6a0b7 | ||
|
|
abb108a83e | ||
|
|
cf9d9bd248 | ||
|
|
5e528c42c7 | ||
|
|
daa7e03240 | ||
|
|
ab39782c78 | ||
|
|
e24925b049 | ||
|
|
1d4245ee09 | ||
|
|
4e5cc0b642 | ||
|
|
fe2872bbb3 |
@@ -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()
|
||||
@@ -12,6 +12,13 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Fetch IPInfo GeoIP Database
|
||||
env:
|
||||
IPINFO_TOKEN: ${{ secrets.IPINFO_TOKEN }}
|
||||
run: |
|
||||
rm pkg/geoip/geoip.db
|
||||
wget -O pkg/geoip/geoip.db https://ipinfo.io/data/free/country.mmdb?token=${IPINFO_TOKEN}
|
||||
|
||||
- name: Extract branch name
|
||||
run: |
|
||||
export TAG_NAME=$(echo ${GITHUB_REF#refs/tags/})
|
||||
@@ -22,19 +29,21 @@ jobs:
|
||||
uses: crazy-max/ghaction-xgo@v2
|
||||
with:
|
||||
xgo_version: latest
|
||||
go_version: 1.21
|
||||
go_version: 1.21.x
|
||||
dest: dist
|
||||
pkg: cmd/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
|
||||
x: false
|
||||
race: false
|
||||
tags: timetzdata
|
||||
ldflags: -s -w --extldflags '-static -fpic' -X github.com/naiba/nezha/service/singleton.Version=${{ steps.extract_branch.outputs.tag }}
|
||||
buildmode: default
|
||||
|
||||
- name: fix dist
|
||||
run: |
|
||||
ls -al dist/
|
||||
mv dist/dashboard-linux-arm-7 dist/dashboard-linux-arm
|
||||
|
||||
- name: Log in to the GHCR
|
||||
@@ -106,3 +115,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/docker-compose.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/sync.py
|
||||
@@ -25,14 +25,19 @@ jobs:
|
||||
uses: crazy-max/ghaction-xgo@v2
|
||||
with:
|
||||
xgo_version: latest
|
||||
go_version: 1.21
|
||||
go_version: 1.21.x
|
||||
dest: dist
|
||||
pkg: cmd/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
|
||||
x: false
|
||||
race: false
|
||||
tags: timetzdata
|
||||
ldflags: -s -w --extldflags '-static -fpic' -X github.com/naiba/nezha/service/singleton.Version=test
|
||||
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
|
||||
with:
|
||||
xgo_version: latest
|
||||
go_version: 1.21
|
||||
go_version: 1.21.x
|
||||
dest: dist
|
||||
pkg: cmd/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
|
||||
x: false
|
||||
race: false
|
||||
tags: timetzdata
|
||||
ldflags: -s -w --extldflags '-static -fpic' -X github.com/naiba/nezha/service/singleton.Version=test
|
||||
buildmode: default
|
||||
- name: fix dist
|
||||
run: |
|
||||
ls -al dist/
|
||||
mv dist/dashboard-linux-arm-7 dist/dashboard-linux-arm
|
||||
|
||||
@@ -4,13 +4,15 @@
|
||||
<br>
|
||||
<small><i>LOGO designed by <a href="https://xio.ng" target="_blank">熊大</a> .</i></small>
|
||||
<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.8-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.18.2-brightgreen?style=for-the-badge&logo=linux">
|
||||
<br>
|
||||
<br>
|
||||
<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>
|
||||
</div>
|
||||
|
||||
\>> Telegram Channel: [哪吒监控(中文通知频道)](https://t.me/nezhanews)
|
||||
|
||||
\>> 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)
|
||||
@@ -38,135 +40,60 @@ You can change the dashboard language in the settings page (`/setting`) after th
|
||||
|
||||
## Contributors
|
||||
|
||||
<!--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/Erope" title="卖女孩的小火柴">
|
||||
<img src="https://avatars.githubusercontent.com/u/44471469?v=4" width="50;" alt="卖女孩的小火柴"/>
|
||||
</a>
|
||||
<a href="https://github.com/dysf888" title="黑歌">
|
||||
<img src="https://avatars.githubusercontent.com/u/47450409?v=4" width="50;" alt="黑歌"/>
|
||||
</a>
|
||||
<a href="https://github.com/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/ilay1678" title="我若为王">
|
||||
<img src="https://avatars.githubusercontent.com/u/7021399?v=4" width="50;" alt="我若为王"/>
|
||||
</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/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/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/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/Mmx233" title="Mmx">
|
||||
<img src="https://avatars.githubusercontent.com/u/36563672?v=4" width="50;" alt="Mmx"/>
|
||||
</a>
|
||||
<a href="https://github.com/rootmelo92118" title="rootmelo92118">
|
||||
<img src="https://avatars.githubusercontent.com/u/32770959?v=4" width="50;" alt="rootmelo92118"/>
|
||||
</a>
|
||||
<a href="https://github.com/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/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/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/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/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/nap0o" title="nap0o">
|
||||
<img src="https://avatars.githubusercontent.com/u/144927971?v=4" width="50;" alt="nap0o"/>
|
||||
</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/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/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/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/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/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/Es-dese" title="Esdese">
|
||||
<img src="https://avatars.githubusercontent.com/u/71542548?v=4" width="50;" alt="Esdese"/>
|
||||
</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/wwng2333" title="Crazy">
|
||||
<img src="https://avatars.githubusercontent.com/u/17147265?v=4" width="50;" alt="Crazy"/>
|
||||
</a><!--GAMFC_DELIMITER_END-->
|
||||
<!--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/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/nap0o" title="nap0o"><img src="https://avatars.githubusercontent.com/u/144927971?v=4" width="50;" alt="nap0o"/></a>
|
||||
<a href="https://github.com/xykt" title="xykt"><img src="https://avatars.githubusercontent.com/u/152045469?v=4" width="50;" alt="xykt"/></a>
|
||||
<a href="https://github.com/MikoyChinese" title="MikoyChinese"><img src="https://avatars.githubusercontent.com/u/22676744?v=4" width="50;" alt="MikoyChinese"/></a>
|
||||
<a href="https://github.com/JackieSung4ev" title="JackieSung4ev"><img src="https://avatars.githubusercontent.com/u/24974735?v=4" width="50;" alt="JackieSung4ev"/></a>
|
||||
<a href="https://github.com/cantoblanco" title="Kris"><img src="https://avatars.githubusercontent.com/u/116849421?v=4" width="50;" alt="Kris"/></a>
|
||||
<a href="https://github.com/lemoeo" title="Lemoe"><img src="https://avatars.githubusercontent.com/u/18618627?v=4" width="50;" alt="Lemoe"/></a>
|
||||
<a href="https://github.com/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/liuyanxi975" title="刘颜溪"><img src="https://avatars.githubusercontent.com/u/24417037?v=4" width="50;" alt="刘颜溪"/></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/1ridic" title="1ridic"><img src="https://avatars.githubusercontent.com/u/88495501?v=4" width="50;" alt="1ridic"/></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/rootmelo92118" title="rootmelo92118"><img src="https://avatars.githubusercontent.com/u/32770959?v=4" width="50;" alt="rootmelo92118"/></a>
|
||||
<a href="https://github.com/Mmx233" title="Mmx"><img src="https://avatars.githubusercontent.com/u/36563672?v=4" width="50;" alt="Mmx"/></a>
|
||||
<a href="https://github.com/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/Creling" title="Creling"><img src="https://avatars.githubusercontent.com/u/43109504?v=4" width="50;" alt="Creling"/></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/HsukqiLee" title="HsukqiLee"><img src="https://avatars.githubusercontent.com/u/79034142?v=4" width="50;" alt="HsukqiLee"/></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/fscarmen" title="fscarmen"><img src="https://avatars.githubusercontent.com/u/62703343?v=4" width="50;" alt="fscarmen"/></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/nickfox-taterli" title="Tater Li"><img src="https://avatars.githubusercontent.com/u/19658596?v=4" width="50;" alt="Tater Li"/></a>
|
||||
<a href="https://github.com/hmsjy2017" title="Tony"><img src="https://avatars.githubusercontent.com/u/42692274?v=4" width="50;" alt="Tony"/></a>
|
||||
<a href="https://github.com/adminsama" title="adminsama"><img src="https://avatars.githubusercontent.com/u/60880076?v=4" width="50;" alt="adminsama"/></a>
|
||||
<a href="https://github.com/acgpiano" title="Acgpiano"><img src="https://avatars.githubusercontent.com/u/15900800?v=4" width="50;" alt="Acgpiano"/></a>
|
||||
<a href="https://github.com/eya46" title="eya46"><img src="https://avatars.githubusercontent.com/u/61458340?v=4" width="50;" alt="eya46"/></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/yuanweize" title="I"><img src="https://avatars.githubusercontent.com/u/30067203?v=4" width="50;" alt="I"/></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/unclezs" title="unclezs"><img src="https://avatars.githubusercontent.com/u/42318775?v=4" width="50;" alt="unclezs"/></a>
|
||||
<a href="https://github.com/ysicing" title="缘生"><img src="https://avatars.githubusercontent.com/u/8605565?v=4" width="50;" alt="缘生"/></a>
|
||||
<a href="https://github.com/zhucaidan" title="zhucaidan"><img src="https://avatars.githubusercontent.com/u/47970938?v=4" width="50;" alt="zhucaidan"/></a>
|
||||
<a href="https://github.com/arkylin" title="凌"><img src="https://avatars.githubusercontent.com/u/35104502?v=4" width="50;" alt="凌"/></a>
|
||||
<a href="https://github.com/colour93" title="玖叁"><img src="https://avatars.githubusercontent.com/u/64313711?v=4" width="50;" alt="玖叁"/></a>
|
||||
<a href="https://github.com/IamTaoChen" title="Tao Chen"><img src="https://avatars.githubusercontent.com/u/42793494?v=4" width="50;" alt="Tao Chen"/></a>
|
||||
<a href="https://github.com/Septrum101" title="Spetrum"><img src="https://avatars.githubusercontent.com/u/11692994?v=4" width="50;" alt="Spetrum"/></a>
|
||||
<a href="https://github.com/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/xrgzs" title="MadDogOwner"><img src="https://avatars.githubusercontent.com/u/26499123?v=4" width="50;" alt="MadDogOwner"/></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/techotaku" title="Ian Li"><img src="https://avatars.githubusercontent.com/u/1948179?v=4" width="50;" alt="Ian Li"/></a>
|
||||
<a href="https://github.com/GreenTeodoro839" title="GreenTeodoro839"><img src="https://avatars.githubusercontent.com/u/77104800?v=4" width="50;" alt="GreenTeodoro839"/></a>
|
||||
<a href="https://github.com/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>
|
||||
<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-->
|
||||
|
||||
## Special Thanks
|
||||
- [IPInfo](https://ipinfo.io/) for providing an accurate GeoIP Database.
|
||||
|
||||
## Star History
|
||||
|
||||
|
||||
@@ -31,13 +31,16 @@ func (v *apiV1) serve() {
|
||||
// 不强制认证的 API
|
||||
mr := v.r.Group("monitor")
|
||||
mr.Use(mygin.Authorize(mygin.AuthorizeOption{
|
||||
MemberOnly: false,
|
||||
IsPage: false,
|
||||
ValidateViewPassword: true,
|
||||
AllowAPI: true,
|
||||
Msg: "访问此接口需要认证",
|
||||
Btn: "点此登录",
|
||||
Redirect: "/login",
|
||||
MemberOnly: false,
|
||||
IsPage: false,
|
||||
AllowAPI: true,
|
||||
Msg: "访问此接口需要认证",
|
||||
Btn: "点此登录",
|
||||
Redirect: "/login",
|
||||
}))
|
||||
mr.Use(mygin.ValidateViewPassword(mygin.ValidateViewPasswordOption{
|
||||
IsPage: false,
|
||||
AbortWhenFail: true,
|
||||
}))
|
||||
mr.GET("/:id", v.monitorHistoriesById)
|
||||
}
|
||||
|
||||
@@ -2,12 +2,9 @@ package controller
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -23,32 +20,25 @@ import (
|
||||
"github.com/naiba/nezha/pkg/utils"
|
||||
"github.com/naiba/nezha/pkg/websocketx"
|
||||
"github.com/naiba/nezha/proto"
|
||||
"github.com/naiba/nezha/service/rpc"
|
||||
"github.com/naiba/nezha/service/singleton"
|
||||
)
|
||||
|
||||
type terminalContext struct {
|
||||
agentConn *websocketx.Conn
|
||||
userConn *websocketx.Conn
|
||||
serverID uint64
|
||||
host string
|
||||
useSSL bool
|
||||
}
|
||||
|
||||
type commonPage struct {
|
||||
r *gin.Engine
|
||||
terminals map[string]*terminalContext
|
||||
terminalsLock *sync.Mutex
|
||||
requestGroup singleflight.Group
|
||||
r *gin.Engine
|
||||
requestGroup singleflight.Group
|
||||
}
|
||||
|
||||
func (cp *commonPage) serve() {
|
||||
cr := cp.r.Group("")
|
||||
cr.Use(mygin.Authorize(mygin.AuthorizeOption{
|
||||
ValidateViewPassword: true,
|
||||
}))
|
||||
cr.Use(mygin.Authorize(mygin.AuthorizeOption{}))
|
||||
cr.Use(mygin.PreferredTheme)
|
||||
cr.GET("/terminal/:id", cp.terminal)
|
||||
cr.POST("/view-password", cp.issueViewPassword)
|
||||
cr.GET("/terminal/:id", cp.terminal)
|
||||
cr.Use(mygin.ValidateViewPassword(mygin.ValidateViewPasswordOption{
|
||||
IsPage: true,
|
||||
AbortWhenFail: true,
|
||||
}))
|
||||
cr.GET("/", cp.home)
|
||||
cr.GET("/service", cp.service)
|
||||
// TODO: 界面直接跳转使用该接口
|
||||
@@ -220,19 +210,25 @@ func (cp *commonPage) network(c *gin.Context) {
|
||||
}
|
||||
|
||||
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()
|
||||
defer singleton.SortedServerLock.RUnlock()
|
||||
|
||||
_, isMember := c.Get(model.CtxKeyAuthorizedUser)
|
||||
_, isViewPasswordVerfied := c.Get(model.CtxKeyViewPasswordVerified)
|
||||
|
||||
var servers []*model.Server
|
||||
|
||||
if isMember || isViewPasswordVerfied {
|
||||
if authorized {
|
||||
servers = singleton.SortedServerList
|
||||
} 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{
|
||||
@@ -264,8 +260,8 @@ func (cp *commonPage) home(c *gin.Context) {
|
||||
}
|
||||
|
||||
var upgrader = websocket.Upgrader{
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024,
|
||||
ReadBufferSize: 10240,
|
||||
WriteBufferSize: 10240,
|
||||
}
|
||||
|
||||
type Data struct {
|
||||
@@ -273,8 +269,6 @@ type Data struct {
|
||||
Servers []*model.Server `json:"servers,omitempty"`
|
||||
}
|
||||
|
||||
var cloudflareCookiesValidator = regexp.MustCompile("^[A-Za-z0-9-_]+$")
|
||||
|
||||
func (cp *commonPage) ws(c *gin.Context) {
|
||||
conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
@@ -311,10 +305,8 @@ func (cp *commonPage) ws(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (cp *commonPage) terminal(c *gin.Context) {
|
||||
terminalID := c.Param("id")
|
||||
cp.terminalsLock.Lock()
|
||||
if terminalID == "" || cp.terminals[terminalID] == nil {
|
||||
cp.terminalsLock.Unlock()
|
||||
streamId := c.Param("id")
|
||||
if _, err := rpc.NezhaHandlerSingleton.GetStream(streamId); err != nil {
|
||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
Code: http.StatusForbidden,
|
||||
Title: "无权访问",
|
||||
@@ -324,104 +316,7 @@ func (cp *commonPage) terminal(c *gin.Context) {
|
||||
}, true)
|
||||
return
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
defer rpc.NezhaHandlerSingleton.CloseStream(streamId)
|
||||
|
||||
wsConn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
@@ -437,36 +332,7 @@ func (cp *commonPage) terminal(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
defer wsConn.Close()
|
||||
conn := &websocketx.Conn{Conn: 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{}{}
|
||||
}()
|
||||
conn := websocketx.NewConn(wsConn)
|
||||
|
||||
go func() {
|
||||
// PING 保活
|
||||
@@ -478,58 +344,11 @@ func (cp *commonPage) terminal(c *gin.Context) {
|
||||
}
|
||||
}()
|
||||
|
||||
dataCh := make(chan []byte)
|
||||
errorCh := make(chan error)
|
||||
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
|
||||
}
|
||||
}
|
||||
if err = rpc.NezhaHandlerSingleton.UserConnected(streamId, conn); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
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]
|
||||
}
|
||||
}
|
||||
}
|
||||
rpc.NezhaHandlerSingleton.StartStream(streamId, time.Second*10)
|
||||
}
|
||||
|
||||
type createTerminalRequest struct {
|
||||
@@ -561,7 +380,7 @@ func (cp *commonPage) createTerminal(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
id, err := uuid.GenerateUUID()
|
||||
streamId, err := uuid.GenerateUUID()
|
||||
if err != nil {
|
||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
Code: http.StatusInternalServerError,
|
||||
@@ -575,6 +394,8 @@ func (cp *commonPage) createTerminal(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
rpc.NezhaHandlerSingleton.CreateStream(streamId)
|
||||
|
||||
singleton.ServerLock.RLock()
|
||||
server := singleton.ServerList[createTerminalReq.ID]
|
||||
singleton.ServerLock.RUnlock()
|
||||
@@ -589,17 +410,25 @@ func (cp *commonPage) createTerminal(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
cp.terminalsLock.Lock()
|
||||
defer cp.terminalsLock.Unlock()
|
||||
|
||||
cp.terminals[id] = &terminalContext{
|
||||
serverID: createTerminalReq.ID,
|
||||
host: createTerminalReq.Host,
|
||||
useSSL: createTerminalReq.Protocol == "https:",
|
||||
terminalData, _ := utils.Json.Marshal(&model.TerminalTask{
|
||||
StreamID: streamId,
|
||||
})
|
||||
if err := server.TaskStream.Send(&proto.Task{
|
||||
Type: model.TaskTypeTerminalGRPC,
|
||||
Data: string(terminalData),
|
||||
}); 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{
|
||||
"SessionID": id,
|
||||
"SessionID": streamId,
|
||||
"ServerName": server.Name,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io/fs"
|
||||
@@ -9,22 +10,31 @@ import (
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"code.cloudfoundry.org/bytefmt"
|
||||
"github.com/gin-contrib/pprof"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/hashicorp/go-uuid"
|
||||
"github.com/nicksnyder/go-i18n/v2/i18n"
|
||||
|
||||
"github.com/naiba/nezha/model"
|
||||
"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/service/rpc"
|
||||
"github.com/naiba/nezha/service/singleton"
|
||||
)
|
||||
|
||||
func ServeWeb(port uint) *http.Server {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
r := gin.Default()
|
||||
if singleton.Conf.Debug {
|
||||
gin.SetMode(gin.DebugMode)
|
||||
pprof.Register(r)
|
||||
}
|
||||
r.Use(natGateway)
|
||||
tmpl := template.New("").Funcs(funcMap)
|
||||
var err error
|
||||
tmpl, err = tmpl.ParseFS(resource.TemplateFS, "template/**/*.html")
|
||||
@@ -33,10 +43,6 @@ func ServeWeb(port uint) *http.Server {
|
||||
}
|
||||
tmpl = loadThirdPartyTemplates(tmpl)
|
||||
r.SetHTMLTemplate(tmpl)
|
||||
if singleton.Conf.Debug {
|
||||
gin.SetMode(gin.DebugMode)
|
||||
pprof.Register(r)
|
||||
}
|
||||
r.Use(mygin.RecordPath)
|
||||
staticFs, err := fs.Sub(resource.StaticFS, "static")
|
||||
if err != nil {
|
||||
@@ -45,7 +51,6 @@ func ServeWeb(port uint) *http.Server {
|
||||
r.StaticFS("/static", http.FS(staticFs))
|
||||
r.Static("/static-custom", "resource/static/custom")
|
||||
routers(r)
|
||||
|
||||
page404 := func(c *gin.Context) {
|
||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
Code: http.StatusNotFound,
|
||||
@@ -68,7 +73,7 @@ func ServeWeb(port uint) *http.Server {
|
||||
|
||||
func routers(r *gin.Engine) {
|
||||
// 通用页面
|
||||
cp := commonPage{r: r, terminals: make(map[string]*terminalContext), terminalsLock: new(sync.Mutex)}
|
||||
cp := commonPage{r: r}
|
||||
cp.serve()
|
||||
// 游客页面
|
||||
gp := guestPage{r}
|
||||
@@ -239,3 +244,64 @@ var funcMap = template.FuncMap{
|
||||
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) {
|
||||
if singleton.Conf.Oauth2.OidcAutoLogin {
|
||||
c.Redirect(http.StatusFound, "/oauth2/login")
|
||||
return
|
||||
}
|
||||
LoginType := "GitHub"
|
||||
RegistrationLink := "https://github.com/join"
|
||||
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 {
|
||||
LoginType = "Gitea"
|
||||
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{
|
||||
"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-delete-server", ma.batchDeleteServer)
|
||||
mr.POST("/notification", ma.addOrEditNotification)
|
||||
mr.POST("/nat", ma.addOrEditNAT)
|
||||
mr.POST("/alert-rule", ma.addOrEditAlertRule)
|
||||
mr.POST("/setting", ma.updateSetting)
|
||||
mr.DELETE("/:model/:id", ma.delete)
|
||||
@@ -209,6 +210,11 @@ func (ma *memberAPI) delete(c *gin.Context) {
|
||||
if err == nil {
|
||||
singleton.OnDeleteNotification(id)
|
||||
}
|
||||
case "nat":
|
||||
err = singleton.DB.Unscoped().Delete(&model.NAT{}, "id = ?", id).Error
|
||||
if err == nil {
|
||||
singleton.OnNATUpdate()
|
||||
}
|
||||
case "monitor":
|
||||
err = singleton.DB.Unscoped().Delete(&model.Monitor{}, "id = ?", id).Error
|
||||
if err == nil {
|
||||
@@ -301,7 +307,10 @@ type serverForm struct {
|
||||
Note string
|
||||
HideForGuest string
|
||||
EnableDDNS string
|
||||
EnableIPv4 string
|
||||
EnableIpv6 string
|
||||
DDNSDomain string
|
||||
DDNSProfile string
|
||||
}
|
||||
|
||||
func (ma *memberAPI) addOrEditServer(c *gin.Context) {
|
||||
@@ -318,7 +327,10 @@ func (ma *memberAPI) addOrEditServer(c *gin.Context) {
|
||||
s.Note = sf.Note
|
||||
s.HideForGuest = sf.HideForGuest == "on"
|
||||
s.EnableDDNS = sf.EnableDDNS == "on"
|
||||
s.EnableIPv4 = sf.EnableIPv4 == "on"
|
||||
s.EnableIpv6 = sf.EnableIpv6 == "on"
|
||||
s.DDNSDomain = sf.DDNSDomain
|
||||
s.DDNSProfile = sf.DDNSProfile
|
||||
if s.ID == 0 {
|
||||
s.Secret, err = utils.GenerateRandomString(18)
|
||||
if err == nil {
|
||||
@@ -431,25 +443,23 @@ func (ma *memberAPI) addOrEditMonitor(c *gin.Context) {
|
||||
if m.NotificationTag == "" {
|
||||
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.ID == 0 {
|
||||
err = singleton.DB.Create(&m).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 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 {
|
||||
@@ -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 {
|
||||
ID uint64
|
||||
Name string
|
||||
@@ -843,6 +892,11 @@ func (ma *memberAPI) logout(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, model.Response{
|
||||
Code: http.StatusOK,
|
||||
})
|
||||
|
||||
if oidcLogoutUrl := singleton.Conf.Oauth2.OidcLogoutURL; oidcLogoutUrl != "" {
|
||||
// 重定向到 OIDC 退出登录地址。不知道为什么,这里的重定向不生效
|
||||
c.Redirect(http.StatusOK, oidcLogoutUrl)
|
||||
}
|
||||
}
|
||||
|
||||
type settingForm struct {
|
||||
@@ -858,8 +912,9 @@ type settingForm struct {
|
||||
GRPCHost string
|
||||
Cover uint8
|
||||
|
||||
EnableIPChangeNotification string
|
||||
EnablePlainIPInNotification string
|
||||
EnableIPChangeNotification string
|
||||
EnablePlainIPInNotification string
|
||||
DisableSwitchTemplateInFrontend string
|
||||
}
|
||||
|
||||
func (ma *memberAPI) updateSetting(c *gin.Context) {
|
||||
@@ -907,6 +962,7 @@ func (ma *memberAPI) updateSetting(c *gin.Context) {
|
||||
singleton.Conf.Language = sf.Language
|
||||
singleton.Conf.EnableIPChangeNotification = sf.EnableIPChangeNotification == "on"
|
||||
singleton.Conf.EnablePlainIPInNotification = sf.EnablePlainIPInNotification == "on"
|
||||
singleton.Conf.DisableSwitchTemplateInFrontend = sf.DisableSwitchTemplateInFrontend == "on"
|
||||
singleton.Conf.Cover = sf.Cover
|
||||
singleton.Conf.GRPCHost = sf.GRPCHost
|
||||
singleton.Conf.IgnoredIPNotification = sf.IgnoredIPNotification
|
||||
|
||||
@@ -27,6 +27,7 @@ func (mp *memberPage) serve() {
|
||||
mr.GET("/monitor", mp.monitor)
|
||||
mr.GET("/cron", mp.cron)
|
||||
mr.GET("/notification", mp.notification)
|
||||
mr.GET("/nat", mp.nat)
|
||||
mr.GET("/setting", mp.setting)
|
||||
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) {
|
||||
c.HTML(http.StatusOK, "dashboard-"+singleton.Conf.Site.DashboardTheme+"/setting", mygin.CommonEnvironment(c, gin.H{
|
||||
"Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "Settings"}),
|
||||
|
||||
@@ -2,6 +2,7 @@ package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
@@ -9,23 +10,27 @@ import (
|
||||
"strings"
|
||||
"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"
|
||||
"github.com/gin-gonic/gin"
|
||||
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/xanzy/go-gitlab"
|
||||
"golang.org/x/oauth2"
|
||||
GitHubOauth2 "golang.org/x/oauth2/github"
|
||||
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 {
|
||||
r gin.IRoutes
|
||||
r gin.IRoutes
|
||||
oidcProvider *oidc.Provider
|
||||
}
|
||||
|
||||
func (oa *oauth2controller) serve() {
|
||||
@@ -74,6 +79,38 @@ func (oa *oauth2controller) getCommonOauth2Config(c *gin.Context) *oauth2.Config
|
||||
},
|
||||
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 {
|
||||
return &oauth2.Config{
|
||||
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 {
|
||||
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://"
|
||||
}
|
||||
return scheme + c.Request.Host + "/oauth2/callback"
|
||||
@@ -155,6 +193,28 @@ func (oa *oauth2controller) callback(c *gin.Context) {
|
||||
if err == nil {
|
||||
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 {
|
||||
var client *GitHubAPI.Client
|
||||
oc := oauth2Config.Client(ctx, otk)
|
||||
@@ -168,15 +228,12 @@ func (oa *oauth2controller) callback(c *gin.Context) {
|
||||
client = GitHubAPI.NewClient(oc)
|
||||
}
|
||||
var gu *GitHubAPI.User
|
||||
if err == nil {
|
||||
gu, _, err = client.Users.Get(ctx, "")
|
||||
}
|
||||
gu, _, err = client.Users.Get(ctx, "")
|
||||
if err == nil {
|
||||
user = model.NewUserFromGitHub(gu)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err == nil && user.Login == "" {
|
||||
err = errors.New("获取用户信息失败")
|
||||
}
|
||||
@@ -190,10 +247,15 @@ func (oa *oauth2controller) callback(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
var isAdmin bool
|
||||
for _, admin := range strings.Split(singleton.Conf.Oauth2.Admin, ",") {
|
||||
if admin != "" && strings.EqualFold(user.Login, admin) {
|
||||
isAdmin = true
|
||||
break
|
||||
|
||||
if user.SuperAdmin {
|
||||
isAdmin = true
|
||||
} else {
|
||||
for _, admin := range strings.Split(singleton.Conf.Oauth2.Admin, ",") {
|
||||
if admin != "" && strings.EqualFold(user.Login, admin) {
|
||||
isAdmin = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if !isAdmin {
|
||||
@@ -220,3 +282,16 @@ func (oa *oauth2controller) callback(c *gin.Context) {
|
||||
"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
|
||||
}
|
||||
|
||||
// TODO 使用 cmux 在同一端口服务 HTTP 和 gRPC
|
||||
singleton.CleanMonitorHistory()
|
||||
go rpc.ServeRPC(singleton.Conf.GRPCPort)
|
||||
serviceSentinelDispatchBus := make(chan model.Monitor) // 用于传递服务监控任务信息的channel
|
||||
|
||||
@@ -14,9 +14,8 @@ import (
|
||||
|
||||
func ServeRPC(port uint) {
|
||||
server := grpc.NewServer()
|
||||
pb.RegisterNezhaServiceServer(server, &rpcService.NezhaHandler{
|
||||
Auth: &rpcService.AuthHandler{},
|
||||
})
|
||||
rpcService.NezhaHandlerSingleton = rpcService.NewNezhaHandler()
|
||||
pb.RegisterNezhaServiceServer(server, rpcService.NezhaHandlerSingleton)
|
||||
listen, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
||||
@@ -2,12 +2,11 @@ module github.com/naiba/nezha
|
||||
|
||||
go 1.21
|
||||
|
||||
toolchain go1.21.3
|
||||
|
||||
require (
|
||||
code.cloudfoundry.org/bytefmt v0.0.0-20231017140541-3b893ed0421b
|
||||
code.gitea.io/sdk/gitea v0.16.0
|
||||
code.cloudfoundry.org/bytefmt v0.0.0-20240425163905-bcdc1ad063ea
|
||||
code.gitea.io/sdk/gitea v0.18.0
|
||||
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-gonic/gin v1.9.1
|
||||
github.com/google/go-github/v47 v47.1.0
|
||||
@@ -15,43 +14,41 @@ require (
|
||||
github.com/hashicorp/go-uuid v1.0.3
|
||||
github.com/jinzhu/copier v0.4.0
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/nicksnyder/go-i18n/v2 v2.2.2
|
||||
github.com/nicksnyder/go-i18n/v2 v2.4.0
|
||||
github.com/ory/graceful v0.1.3
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/samber/lo v1.38.1
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/spf13/viper v1.17.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/xanzy/go-gitlab v0.94.0
|
||||
golang.org/x/crypto v0.17.0
|
||||
golang.org/x/oauth2 v0.14.0
|
||||
golang.org/x/sync v0.5.0
|
||||
golang.org/x/text v0.14.0
|
||||
google.golang.org/grpc v1.59.0
|
||||
google.golang.org/protobuf v1.31.0
|
||||
gorm.io/driver/sqlite v1.5.4
|
||||
gorm.io/gorm v1.25.5
|
||||
github.com/spf13/viper v1.18.2
|
||||
github.com/xanzy/go-gitlab v0.103.0
|
||||
golang.org/x/crypto v0.25.0
|
||||
golang.org/x/net v0.27.0
|
||||
golang.org/x/oauth2 v0.21.0
|
||||
golang.org/x/sync v0.7.0
|
||||
golang.org/x/text v0.16.0
|
||||
google.golang.org/grpc v1.63.0
|
||||
google.golang.org/protobuf v1.34.2
|
||||
gorm.io/driver/sqlite v1.5.5
|
||||
gorm.io/gorm v1.25.10
|
||||
sigs.k8s.io/yaml v1.4.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/bytedance/sonic v1.9.1 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/davidmz/go-pageant v1.0.2 // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||
github.com/gin-contrib/sse v0.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/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.14.0 // 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/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/hcl v1.0.0 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
@@ -59,19 +56,19 @@ require (
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
|
||||
github.com/leodido/go-urn v1.2.4 // 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/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/oschwald/maxminddb-golang v1.13.1 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/sagikazarmark/locafero v0.3.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.10.0 // indirect
|
||||
github.com/spf13/cast v1.5.1 // indirect
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/spf13/cast v1.6.0 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||
@@ -79,11 +76,9 @@ require (
|
||||
go.uber.org/multierr v1.9.0 // indirect
|
||||
golang.org/x/arch v0.3.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||
golang.org/x/net v0.18.0 // indirect
|
||||
golang.org/x/sys v0.15.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 // indirect
|
||||
golang.org/x/sys v0.22.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
@@ -1,64 +1,17 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
||||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
|
||||
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
|
||||
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
|
||||
code.cloudfoundry.org/bytefmt v0.0.0-20231017140541-3b893ed0421b h1:/2OEIBwZAaJ8n8iTXrM4v/+bdyLDTLwcW6RZtkO4+r0=
|
||||
code.cloudfoundry.org/bytefmt v0.0.0-20231017140541-3b893ed0421b/go.mod h1:CKNYSQxmKcMCNIKoRG5rRR4AIgJMIoK65ya+Z5xHnk4=
|
||||
code.gitea.io/sdk/gitea v0.16.0 h1:gAfssETO1Hv9QbE+/nhWu7EjoFQYKt6kPoyDytQgw00=
|
||||
code.gitea.io/sdk/gitea v0.16.0/go.mod h1:ndkDk99BnfiUCCYEUhpNzi0lpmApXlwRFqClBlOlEBg=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
code.cloudfoundry.org/bytefmt v0.0.0-20240425163905-bcdc1ad063ea h1:1tgMNDgo8PjpsHhlaxdibj28C0WyLeOW2SPJ7GGdc9A=
|
||||
code.cloudfoundry.org/bytefmt v0.0.0-20240425163905-bcdc1ad063ea/go.mod h1:3+xXJBOD8PsGHDqHedtCLalbaVJ+yi1OW+mXx9IcNxI=
|
||||
code.gitea.io/sdk/gitea v0.18.0 h1:+zZrwVmujIrgobt6wVBWCqITz6bn1aBjnCUHmpZrerI=
|
||||
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/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
|
||||
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
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/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
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/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=
|
||||
@@ -66,18 +19,12 @@ 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/davidmz/go-pageant v1.0.2 h1:bPblRCh5jGU+Uptpz6LgMZGD5hJoOt7otgT454WvHn0=
|
||||
github.com/davidmz/go-pageant v1.0.2/go.mod h1:P2EDDnMqIwG5Rrp05dTRITj9z2zpGcD9efWSkTNKLIE=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w=
|
||||
github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg=
|
||||
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
|
||||
github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
||||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
||||
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
||||
github.com/gin-contrib/pprof v1.4.0 h1:XxiBSf5jWZ5i16lNOPbMTVdgHBdhfGRD5PZ1LWazzvg=
|
||||
@@ -89,11 +36,10 @@ 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/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-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
||||
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
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/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.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
@@ -111,93 +57,33 @@ github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4
|
||||
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
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/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/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.4/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 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-github/v47 v47.1.0 h1:Cacm/WxQBOa9lF0FT0EMjZ2BWMetQ1TQfyurn4yF1z8=
|
||||
github.com/google/go-github/v47 v47.1.0/go.mod h1:VPZBXNbFSJGjyjFRUKo9vZGawTajnWzC/YjGw/oFKi0=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
||||
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg=
|
||||
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
|
||||
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
|
||||
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
|
||||
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-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=
|
||||
github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.2 h1:AcYqCvkpalPnPF2pn0KamgwamS42TqUDDYFRKq/RAd0=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.2/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
|
||||
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
|
||||
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
|
||||
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-version v1.5.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
|
||||
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
|
||||
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
@@ -206,13 +92,9 @@ github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
|
||||
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
@@ -230,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/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.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
|
||||
github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
@@ -241,14 +123,16 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.2.2 h1:Iv/FL6pvYmDqybEZkr4TrOv8jSHezwpE77K68kcaft8=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.2.2/go.mod h1:fF2++lPHlo+/kPaj3nB0uxtPwzlPm+BlgwGX7MkeGj0=
|
||||
github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU=
|
||||
github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts=
|
||||
github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E=
|
||||
github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3q3fQ=
|
||||
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/onsi/ginkgo/v2 v2.17.1 h1:V++EzdbhI4ZV4ev0UTIj0PzhzOcReJFyJaLjtSF55M8=
|
||||
github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs=
|
||||
github.com/onsi/gomega v1.33.0 h1:snPCflnZrpMsy94p4lXVEkHo12lmPnc3vY5XBbreexE=
|
||||
github.com/onsi/gomega v1.33.0/go.mod h1:+925n5YtiFsLzzafLUHzVMBpvvRAzrydIBiSIxjX3wY=
|
||||
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/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE=
|
||||
github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
|
||||
@@ -257,40 +141,33 @@ github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdU
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/sagikazarmark/locafero v0.3.0 h1:zT7VEGWC2DTflmccN/5T1etyKvxSxpHsjb9cJvm4SvQ=
|
||||
github.com/sagikazarmark/locafero v0.3.0/go.mod h1:w+v7UsPNFwzF1cHuOajOOzoq4U7v/ig1mpRjqV+Bu1U=
|
||||
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
|
||||
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
||||
github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM=
|
||||
github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
|
||||
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/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY=
|
||||
github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
|
||||
github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
|
||||
github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48=
|
||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
||||
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.17.0 h1:I5txKw7MJasPL/BrfkbA0Jyo/oELqVmux4pR/UxOMfI=
|
||||
github.com/spf13/viper v1.17.0/go.mod h1:BmMMMLQXSbcHK6KAOiFLz0l5JHrU89OdIRHvsk0+yVI=
|
||||
github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
|
||||
github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
|
||||
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.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.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
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.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
@@ -308,19 +185,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.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/xanzy/go-gitlab v0.94.0 h1:GmBl2T5zqUHqyjkxFSvsT7CbelGdAH/dmBqUBqS+4BE=
|
||||
github.com/xanzy/go-gitlab v0.94.0/go.mod h1:ETg8tcj4OhrB84UEgeE8dSuV/0h4BBL1uOV/qK0vlyI=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
github.com/xanzy/go-gitlab v0.103.0 h1:J9pTQoq0GsEFqzd6srCM1QfdfKAxSNz6mT6ntrpNF2w=
|
||||
github.com/xanzy/go-gitlab v0.103.0/go.mod h1:ETg8tcj4OhrB84UEgeE8dSuV/0h4BBL1uOV/qK0vlyI=
|
||||
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/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
||||
@@ -329,329 +195,53 @@ golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUu
|
||||
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
|
||||
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
||||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
||||
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
|
||||
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.14.0 h1:P0Vrf/2538nmC0H+pEQ3MNFRRnVR7RlqyVw+bvm26z0=
|
||||
golang.org/x/oauth2 v0.14.0/go.mod h1:lAtNWgaWfL4cm7j2OV8TxGi9Qb7ECORx8DktCY74OwM=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/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.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
|
||||
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
||||
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
|
||||
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/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-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-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-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
|
||||
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
|
||||
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/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.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
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.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/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-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
|
||||
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
|
||||
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
|
||||
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 h1:N3bU/SQDCDyD6R528GJ/PwW9KjYcJA3dgyH+MovAkIM=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:KSqppvjFjtoCI+KGd4PELB0qLNxdJHRGqRI09mB6pQA=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
||||
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
|
||||
google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY=
|
||||
google.golang.org/grpc v1.63.0 h1:WjKe+dnvABXyPJMD7KDNLxtoGk5tgk+YFWN6cBWjZE8=
|
||||
google.golang.org/grpc v1.63.0/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
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 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
@@ -660,27 +250,16 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/sqlite v1.5.4 h1:IqXwXi8M/ZlPzH/947tn5uik3aYQslP9BVveoax0nV0=
|
||||
gorm.io/driver/sqlite v1.5.4/go.mod h1:qxAuCol+2r6PannQDpOP1FP6ag3mKi4esLnB/jHed+4=
|
||||
gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls=
|
||||
gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
gorm.io/driver/sqlite v1.5.5 h1:7MDMtUZhV065SilG62E0MquljeArQZNfJnjd9i9gx3E=
|
||||
gorm.io/driver/sqlite v1.5.5/go.mod h1:6NgQ7sQWAIFsPrJJl1lSNSu2TABh0ZZ/zm5fosATavE=
|
||||
gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s=
|
||||
gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||
|
||||
+50
-20
@@ -22,7 +22,7 @@ var Themes = map[string]string{
|
||||
"mdui": "Neko Mdui",
|
||||
"hotaru": "Hotaru",
|
||||
"angel-kanade": "AngelKanade",
|
||||
"server-status": "SeverStatus",
|
||||
"server-status": "ServerStatus",
|
||||
"custom": "Custom(local)",
|
||||
}
|
||||
|
||||
@@ -32,11 +32,13 @@ var DashboardThemes = map[string]string{
|
||||
}
|
||||
|
||||
const (
|
||||
ConfigTypeGitHub = "github"
|
||||
ConfigTypeGitee = "gitee"
|
||||
ConfigTypeGitlab = "gitlab"
|
||||
ConfigTypeJihulab = "jihulab"
|
||||
ConfigTypeGitea = "gitea"
|
||||
ConfigTypeGitHub = "github"
|
||||
ConfigTypeGitee = "gitee"
|
||||
ConfigTypeGitlab = "gitlab"
|
||||
ConfigTypeJihulab = "jihulab"
|
||||
ConfigTypeGitea = "gitea"
|
||||
ConfigTypeCloudflare = "cloudflare"
|
||||
ConfigTypeOidc = "oidc"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -70,7 +72,7 @@ func (c *AgentConfig) Save() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(c.v.ConfigFileUsed(), data, os.ModePerm)
|
||||
return os.WriteFile(c.v.ConfigFileUsed(), data, 0600)
|
||||
}
|
||||
|
||||
// Config 站点配置
|
||||
@@ -86,11 +88,21 @@ type Config struct {
|
||||
ViewPassword string // 前台查看密码
|
||||
}
|
||||
Oauth2 struct {
|
||||
Type string
|
||||
Admin string // 管理员用户名列表
|
||||
ClientID string
|
||||
ClientSecret string
|
||||
Endpoint string
|
||||
Type string
|
||||
Admin string // 管理员用户名列表
|
||||
AdminGroups string // 管理员用户组列表
|
||||
ClientID 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
|
||||
GRPCPort uint
|
||||
@@ -98,7 +110,8 @@ type Config struct {
|
||||
ProxyGRPCPort uint
|
||||
TLS bool
|
||||
|
||||
EnablePlainIPInNotification bool // 通知信息IP不打码
|
||||
EnablePlainIPInNotification bool // 通知信息IP不打码
|
||||
DisableSwitchTemplateInFrontend bool // 前台禁用切换模板功能
|
||||
|
||||
// IP变更提醒
|
||||
EnableIPChangeNotification bool
|
||||
@@ -124,9 +137,20 @@ type Config struct {
|
||||
WebhookRequestBody string
|
||||
WebhookHeaders string
|
||||
MaxRetries uint32
|
||||
Profiles map[string]DDNSProfile
|
||||
}
|
||||
}
|
||||
|
||||
type DDNSProfile struct {
|
||||
Provider string
|
||||
AccessID string
|
||||
AccessSecret string
|
||||
WebhookURL string
|
||||
WebhookMethod string
|
||||
WebhookRequestBody string
|
||||
WebhookHeaders string
|
||||
}
|
||||
|
||||
// Read 读取配置文件并应用
|
||||
func (c *Config) Read(path string) error {
|
||||
c.v = viper.New()
|
||||
@@ -165,15 +189,21 @@ func (c *Config) Read(path string) error {
|
||||
if c.AvgPingCount == 0 {
|
||||
c.AvgPingCount = 2
|
||||
}
|
||||
if c.DDNS.Provider == "" {
|
||||
c.DDNS.Provider = "webhook"
|
||||
}
|
||||
if c.DDNS.WebhookMethod == "" {
|
||||
c.DDNS.WebhookMethod = "POST"
|
||||
}
|
||||
if c.DDNS.MaxRetries == 0 {
|
||||
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()
|
||||
return nil
|
||||
@@ -198,5 +228,5 @@ func (c *Config) Save() error {
|
||||
if err != nil {
|
||||
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
|
||||
)
|
||||
|
||||
type SensorTemperature struct {
|
||||
Name string
|
||||
Temperature float64
|
||||
}
|
||||
|
||||
type HostState struct {
|
||||
CPU float64
|
||||
MemUsed uint64
|
||||
@@ -26,9 +31,19 @@ type HostState struct {
|
||||
TcpConnCount uint64
|
||||
UdpConnCount uint64
|
||||
ProcessCount uint64
|
||||
Temperatures []SensorTemperature
|
||||
GPU float64
|
||||
}
|
||||
|
||||
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{
|
||||
Cpu: s.CPU,
|
||||
MemUsed: s.MemUsed,
|
||||
@@ -45,10 +60,20 @@ func (s *HostState) PB() *pb.State {
|
||||
TcpConnCount: s.TcpConnCount,
|
||||
UdpConnCount: s.UdpConnCount,
|
||||
ProcessCount: s.ProcessCount,
|
||||
Temperatures: ts,
|
||||
Gpu: s.GPU,
|
||||
}
|
||||
}
|
||||
|
||||
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{
|
||||
CPU: s.GetCpu(),
|
||||
MemUsed: s.GetMemUsed(),
|
||||
@@ -65,6 +90,8 @@ func PB2State(s *pb.State) HostState {
|
||||
TcpConnCount: s.GetTcpConnCount(),
|
||||
UdpConnCount: s.GetUdpConnCount(),
|
||||
ProcessCount: s.GetProcessCount(),
|
||||
Temperatures: ts,
|
||||
GPU: s.GetGpu(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,6 +108,7 @@ type Host struct {
|
||||
IP string `json:"-"`
|
||||
CountryCode string
|
||||
Version string
|
||||
GPU []string
|
||||
}
|
||||
|
||||
func (h *Host) PB() *pb.Host {
|
||||
@@ -97,6 +125,7 @@ func (h *Host) PB() *pb.Host {
|
||||
Ip: h.IP,
|
||||
CountryCode: h.CountryCode,
|
||||
Version: h.Version,
|
||||
Gpu: h.GPU,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,5 +143,6 @@ func PB2Host(h *pb.Host) Host {
|
||||
IP: h.GetIp(),
|
||||
CountryCode: h.GetCountryCode(),
|
||||
Version: h.GetVersion(),
|
||||
GPU: h.GetGpu(),
|
||||
}
|
||||
}
|
||||
|
||||
+9
-9
@@ -20,17 +20,17 @@ const (
|
||||
TaskTypeTerminal
|
||||
TaskTypeUpgrade
|
||||
TaskTypeKeepalive
|
||||
TaskTypeTerminalGRPC
|
||||
TaskTypeNAT
|
||||
)
|
||||
|
||||
type TerminalTask struct {
|
||||
// websocket 主机名
|
||||
Host string `json:"host,omitempty"`
|
||||
// 是否启用 SSL
|
||||
UseSSL bool `json:"use_ssl,omitempty"`
|
||||
// 会话标识
|
||||
Session string `json:"session,omitempty"`
|
||||
// Agent在连接Server时需要的额外Cookie信息
|
||||
Cookie string `json:"cookie,omitempty"`
|
||||
StreamID string
|
||||
}
|
||||
|
||||
type TaskNAT struct {
|
||||
StreamID string
|
||||
Host string
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -120,7 +120,7 @@ func (m *Monitor) AfterFind(tx *gorm.DB) error {
|
||||
|
||||
// IsServiceSentinelNeeded 判断该任务类型是否需要进行服务监控 需要则返回true
|
||||
func IsServiceSentinelNeeded(t uint64) bool {
|
||||
return t != TaskTypeCommand && t != TaskTypeTerminal && t != TaskTypeUpgrade
|
||||
return t != TaskTypeCommand && t != TaskTypeTerminalGRPC && t != TaskTypeUpgrade
|
||||
}
|
||||
|
||||
func (m *Monitor) InitSkipServers() error {
|
||||
|
||||
@@ -19,4 +19,3 @@ type MonitorHistory struct {
|
||||
Down uint64 // 检查状态异常计数
|
||||
Data string
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package model
|
||||
|
||||
type NAT struct {
|
||||
Common
|
||||
Name string
|
||||
ServerID uint64
|
||||
Host string
|
||||
Domain string `gorm:"unique"`
|
||||
}
|
||||
+24
-10
@@ -5,8 +5,6 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -85,21 +83,37 @@ func execCase(t *testing.T, item testSt) {
|
||||
Server: &server,
|
||||
Loc: time.Local,
|
||||
}
|
||||
assert.Equal(t, item.expectURL, ns.reqURL(msg))
|
||||
if item.expectURL != ns.reqURL(msg) {
|
||||
t.Fatalf("Expected %s, but got %s", item.expectURL, ns.reqURL(msg))
|
||||
}
|
||||
reqBody, err := ns.reqBody(msg)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, item.expectBody, reqBody)
|
||||
if err != nil {
|
||||
t.Fatalf("Error: %s", err)
|
||||
}
|
||||
if item.expectBody != reqBody {
|
||||
t.Fatalf("Expected %s, but got %s", item.expectBody, reqBody)
|
||||
}
|
||||
reqMethod, err := n.reqMethod()
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, item.expectMethod, reqMethod)
|
||||
if err != nil {
|
||||
t.Fatalf("Error: %s", err)
|
||||
}
|
||||
if item.expectMethod != reqMethod {
|
||||
t.Fatalf("Expected %s, but got %s", item.expectMethod, reqMethod)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("", "", strings.NewReader(""))
|
||||
assert.Nil(t, err)
|
||||
if err != nil {
|
||||
t.Fatalf("Error: %s", err)
|
||||
}
|
||||
n.setContentType(req)
|
||||
assert.Equal(t, item.expectContentType, req.Header.Get("Content-Type"))
|
||||
if item.expectContentType != req.Header.Get("Content-Type") {
|
||||
t.Fatalf("Expected %s, but got %s", item.expectContentType, req.Header.Get("Content-Type"))
|
||||
}
|
||||
n.setRequestHeader(req)
|
||||
for k, v := range item.expectHeader {
|
||||
assert.Equal(t, v, req.Header.Get(k))
|
||||
if v != req.Header.Get(k) {
|
||||
t.Fatalf("Expected %s, but got %s", v, req.Header.Get(k))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+28
-3
@@ -42,6 +42,19 @@ func percentage(used, total uint64) float64 {
|
||||
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
|
||||
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 {
|
||||
case "cpu":
|
||||
src = float64(server.State.CPU)
|
||||
case "gpu":
|
||||
src = float64(server.State.GPU)
|
||||
case "memory":
|
||||
src = percentage(server.State.MemUsed, server.Host.MemTotal)
|
||||
case "swap":
|
||||
@@ -91,21 +106,21 @@ func (u *Rule) Snapshot(cycleTransferStats *CycleTransferStats, server *Server,
|
||||
src = float64(server.State.NetInTransfer - uint64(server.PrevHourlyTransferIn))
|
||||
if u.CycleInterval != 0 {
|
||||
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)
|
||||
}
|
||||
case "transfer_out_cycle":
|
||||
src = float64(server.State.NetOutTransfer - uint64(server.PrevHourlyTransferOut))
|
||||
if u.CycleInterval != 0 {
|
||||
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)
|
||||
}
|
||||
case "transfer_all_cycle":
|
||||
src = float64(server.State.NetOutTransfer - uint64(server.PrevHourlyTransferOut) + server.State.NetInTransfer - uint64(server.PrevHourlyTransferIn))
|
||||
if u.CycleInterval != 0 {
|
||||
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)
|
||||
}
|
||||
case "load1":
|
||||
@@ -120,6 +135,16 @@ func (u *Rule) Snapshot(cycleTransferStats *CycleTransferStats, server *Server,
|
||||
src = float64(server.State.UdpConnCount)
|
||||
case "process_count":
|
||||
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 // 展示排序,越大越靠前
|
||||
HideForGuest bool // 对游客隐藏
|
||||
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 为空则停用
|
||||
DDNSProfile string // DDNS配置
|
||||
|
||||
Host *Host `gorm:"-"`
|
||||
State *HostState `gorm:"-"`
|
||||
@@ -54,5 +57,6 @@ func (s Server) Marshal() template.JS {
|
||||
note, _ := utils.Json.Marshal(s.Note)
|
||||
secret, _ := utils.Json.Marshal(s.Secret)
|
||||
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
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/naiba/nezha/pkg/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestServerMarshal(t *testing.T) {
|
||||
@@ -21,7 +20,11 @@ func TestServerMarshal(t *testing.T) {
|
||||
}
|
||||
serverStr := string(server.Marshal())
|
||||
var serverRestore Server
|
||||
assert.Nil(t, utils.Json.Unmarshal([]byte(serverStr), &serverRestore))
|
||||
assert.Equal(t, server, serverRestore)
|
||||
if utils.Json.Unmarshal([]byte(serverStr), &serverRestore) != nil {
|
||||
t.Fatalf("Error: %s", serverStr)
|
||||
}
|
||||
if server.Name != serverRestore.Name {
|
||||
t.Fatalf("Expected %s, but got %s", server.Name, serverRestore.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
package ddns
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type ProviderCloudflare struct {
|
||||
Secret string
|
||||
}
|
||||
|
||||
func (provider *ProviderCloudflare) UpdateDomain(domainConfig *DomainConfig) bool {
|
||||
if domainConfig == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
zoneID, err := provider.getZoneID(domainConfig.FullDomain)
|
||||
if err != nil {
|
||||
log.Printf("无法获取 zone ID: %s\n", err)
|
||||
return false
|
||||
}
|
||||
|
||||
// 当IPv4和IPv6同时成功才算作成功
|
||||
var resultV4 = true
|
||||
var resultV6 = true
|
||||
if domainConfig.EnableIPv4 {
|
||||
if !provider.addDomainRecord(zoneID, domainConfig, true) {
|
||||
resultV4 = false
|
||||
}
|
||||
}
|
||||
|
||||
if domainConfig.EnableIpv6 {
|
||||
if !provider.addDomainRecord(zoneID, domainConfig, false) {
|
||||
resultV6 = false
|
||||
}
|
||||
}
|
||||
|
||||
return resultV4 && resultV6
|
||||
}
|
||||
|
||||
func (provider *ProviderCloudflare) addDomainRecord(zoneID string, domainConfig *DomainConfig, isIpv4 bool) bool {
|
||||
record, err := provider.findDNSRecord(zoneID, domainConfig.FullDomain, isIpv4)
|
||||
if err != nil {
|
||||
log.Printf("查找 DNS 记录时出错: %s\n", err)
|
||||
return false
|
||||
}
|
||||
|
||||
if record == nil {
|
||||
// 添加 DNS 记录
|
||||
return provider.createDNSRecord(zoneID, domainConfig, isIpv4)
|
||||
} else {
|
||||
// 更新 DNS 记录
|
||||
return provider.updateDNSRecord(zoneID, record["id"].(string), domainConfig, isIpv4)
|
||||
}
|
||||
}
|
||||
|
||||
func (provider *ProviderCloudflare) getZoneID(domain string) (string, error) {
|
||||
_, realDomain := SplitDomain(domain)
|
||||
url := fmt.Sprintf("https://api.cloudflare.com/client/v4/zones?name=%s", realDomain)
|
||||
body, err := provider.sendRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var res map[string]interface{}
|
||||
err = json.Unmarshal(body, &res)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
result := res["result"].([]interface{})
|
||||
if len(result) > 0 {
|
||||
zoneID := result[0].(map[string]interface{})["id"].(string)
|
||||
return zoneID, nil
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("找不到 Zone ID")
|
||||
}
|
||||
|
||||
func (provider *ProviderCloudflare) findDNSRecord(zoneID string, domain string, isIPv4 bool) (map[string]interface{}, error) {
|
||||
var ipType = "A"
|
||||
if !isIPv4 {
|
||||
ipType = "AAAA"
|
||||
}
|
||||
url := fmt.Sprintf("https://api.cloudflare.com/client/v4/zones/%s/dns_records?type=%s&name=%s", zoneID, ipType, domain)
|
||||
body, err := provider.sendRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var res map[string]interface{}
|
||||
err = json.Unmarshal(body, &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := res["result"].([]interface{})
|
||||
if len(result) > 0 {
|
||||
return result[0].(map[string]interface{}), nil
|
||||
}
|
||||
|
||||
return nil, nil // 没有找到 DNS 记录
|
||||
}
|
||||
|
||||
func (provider *ProviderCloudflare) createDNSRecord(zoneID string, domainConfig *DomainConfig, isIPv4 bool) bool {
|
||||
var ipType = "A"
|
||||
var ipAddr = domainConfig.Ipv4Addr
|
||||
if !isIPv4 {
|
||||
ipType = "AAAA"
|
||||
ipAddr = domainConfig.Ipv6Addr
|
||||
}
|
||||
url := fmt.Sprintf("https://api.cloudflare.com/client/v4/zones/%s/dns_records", zoneID)
|
||||
data := map[string]interface{}{
|
||||
"type": ipType,
|
||||
"name": domainConfig.FullDomain,
|
||||
"content": ipAddr,
|
||||
"ttl": 60,
|
||||
"proxied": false,
|
||||
}
|
||||
jsonData, _ := json.Marshal(data)
|
||||
_, err := provider.sendRequest("POST", url, jsonData)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (provider *ProviderCloudflare) updateDNSRecord(zoneID string, recordID string, domainConfig *DomainConfig, isIPv4 bool) bool {
|
||||
var ipType = "A"
|
||||
var ipAddr = domainConfig.Ipv4Addr
|
||||
if !isIPv4 {
|
||||
ipType = "AAAA"
|
||||
ipAddr = domainConfig.Ipv6Addr
|
||||
}
|
||||
url := fmt.Sprintf("https://api.cloudflare.com/client/v4/zones/%s/dns_records/%s", zoneID, recordID)
|
||||
data := map[string]interface{}{
|
||||
"type": ipType,
|
||||
"name": domainConfig.FullDomain,
|
||||
"content": ipAddr,
|
||||
"ttl": 60,
|
||||
"proxied": false,
|
||||
}
|
||||
jsonData, _ := json.Marshal(data)
|
||||
_, err := provider.sendRequest("PATCH", url, jsonData)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// 以下为辅助方法,如发送 HTTP 请求等
|
||||
func (provider *ProviderCloudflare) sendRequest(method string, url string, data []byte) ([]byte, error) {
|
||||
client := &http.Client{}
|
||||
req, err := http.NewRequest(method, url, bytes.NewBuffer(data))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", provider.Secret))
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package ddns
|
||||
|
||||
type DomainConfig struct {
|
||||
EnableIPv4 bool
|
||||
EnableIpv6 bool
|
||||
FullDomain string
|
||||
Ipv4Addr string
|
||||
Ipv6Addr string
|
||||
}
|
||||
|
||||
type Provider interface {
|
||||
// UpdateDomain Return is updated
|
||||
UpdateDomain(domainConfig *DomainConfig) bool
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package ddns
|
||||
|
||||
type ProviderDummy struct{}
|
||||
|
||||
func (provider *ProviderDummy) UpdateDomain(domainConfig *DomainConfig) bool {
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package ddns
|
||||
|
||||
import (
|
||||
"golang.org/x/net/publicsuffix"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (provider ProviderWebHook) FormatWebhookString(s string, config *DomainConfig, ipType string) string {
|
||||
if config == nil {
|
||||
return s
|
||||
}
|
||||
|
||||
result := strings.TrimSpace(s)
|
||||
result = strings.Replace(s, "{ip}", config.Ipv4Addr, -1)
|
||||
result = strings.Replace(result, "{domain}", config.FullDomain, -1)
|
||||
result = strings.Replace(result, "{type}", ipType, -1)
|
||||
// remove \r
|
||||
result = strings.Replace(result, "\r", "", -1)
|
||||
return result
|
||||
}
|
||||
|
||||
func SetStringHeadersToRequest(req *http.Request, headers []string) {
|
||||
if req == nil {
|
||||
return
|
||||
}
|
||||
for _, element := range headers {
|
||||
kv := strings.SplitN(element, ":", 2)
|
||||
if len(kv) == 2 {
|
||||
req.Header.Add(kv[0], kv[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SplitDomain 分割域名为前缀和一级域名
|
||||
func SplitDomain(domain string) (prefix string, realDomain string) {
|
||||
realDomain, _ = publicsuffix.EffectiveTLDPlusOne(domain)
|
||||
prefix = domain[:len(domain)-len(realDomain)-1]
|
||||
return prefix, realDomain
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package ddns
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ProviderWebHook struct {
|
||||
URL string
|
||||
RequestMethod string
|
||||
RequestBody string
|
||||
RequestHeader string
|
||||
}
|
||||
|
||||
func (provider *ProviderWebHook) UpdateDomain(domainConfig *DomainConfig) bool {
|
||||
if domainConfig == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if domainConfig.FullDomain == "" {
|
||||
log.Println("NEZHA>> Failed to update an empty domain")
|
||||
return false
|
||||
}
|
||||
updated := false
|
||||
client := &http.Client{}
|
||||
if domainConfig.EnableIPv4 && domainConfig.Ipv4Addr != "" {
|
||||
url := provider.FormatWebhookString(provider.URL, domainConfig, "ipv4")
|
||||
body := provider.FormatWebhookString(provider.RequestBody, domainConfig, "ipv4")
|
||||
header := provider.FormatWebhookString(provider.RequestHeader, domainConfig, "ipv4")
|
||||
headers := strings.Split(header, "\n")
|
||||
req, err := http.NewRequest(provider.RequestMethod, url, bytes.NewBufferString(body))
|
||||
if err == nil && req != nil {
|
||||
SetStringHeadersToRequest(req, headers)
|
||||
if _, err := client.Do(req); err != nil {
|
||||
log.Printf("NEZHA>> Failed to update a domain: %s. Cause by: %s\n", domainConfig.FullDomain, err.Error())
|
||||
} else {
|
||||
updated = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if domainConfig.EnableIpv6 && domainConfig.Ipv6Addr != "" {
|
||||
url := provider.FormatWebhookString(provider.URL, domainConfig, "ipv6")
|
||||
body := provider.FormatWebhookString(provider.RequestBody, domainConfig, "ipv6")
|
||||
header := provider.FormatWebhookString(provider.RequestHeader, domainConfig, "ipv6")
|
||||
headers := strings.Split(header, "\n")
|
||||
req, err := http.NewRequest(provider.RequestMethod, url, bytes.NewBufferString(body))
|
||||
if err == nil && req != nil {
|
||||
SetStringHeadersToRequest(req, headers)
|
||||
if _, err := client.Do(req); err != nil {
|
||||
log.Printf("NEZHA>> Failed to update a domain: %s. Cause by: %s\n", domainConfig.FullDomain, err.Error())
|
||||
} else {
|
||||
updated = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return updated
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
stub
|
||||
@@ -0,0 +1,54 @@
|
||||
package geoip
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
maxminddb "github.com/oschwald/maxminddb-golang"
|
||||
)
|
||||
|
||||
//go:embed geoip.db
|
||||
var geoDBFS embed.FS
|
||||
|
||||
var (
|
||||
dbData []byte
|
||||
err error
|
||||
)
|
||||
|
||||
type IPInfo struct {
|
||||
Country string `maxminddb:"country"`
|
||||
CountryName string `maxminddb:"country_name"`
|
||||
Continent string `maxminddb:"continent"`
|
||||
ContinentName string `maxminddb:"continent_name"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
dbData, err = geoDBFS.ReadFile("geoip.db")
|
||||
if err != nil {
|
||||
log.Printf("NEZHA>> Failed to open geoip database: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func Lookup(ip net.IP, record *IPInfo) (string, error) {
|
||||
db, err := maxminddb.FromBytes(dbData)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
err = db.Lookup(ip, record)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if record.Country != "" {
|
||||
return strings.ToLower(record.Country), nil
|
||||
} else if record.Continent != "" {
|
||||
return strings.ToLower(record.Continent), nil
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("IP not found")
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
+7
-25
@@ -6,22 +6,19 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/nicksnyder/go-i18n/v2/i18n"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
|
||||
"github.com/naiba/nezha/model"
|
||||
"github.com/naiba/nezha/service/singleton"
|
||||
)
|
||||
|
||||
type AuthorizeOption struct {
|
||||
GuestOnly bool
|
||||
MemberOnly bool
|
||||
ValidateViewPassword bool
|
||||
IsPage bool
|
||||
AllowAPI bool
|
||||
Msg string
|
||||
Redirect string
|
||||
Btn string
|
||||
GuestOnly bool
|
||||
MemberOnly bool
|
||||
IsPage bool
|
||||
AllowAPI bool
|
||||
Msg string
|
||||
Redirect string
|
||||
Btn string
|
||||
}
|
||||
|
||||
func Authorize(opt AuthorizeOption) func(*gin.Context) {
|
||||
@@ -82,20 +79,5 @@ func Authorize(opt AuthorizeOption) func(*gin.Context) {
|
||||
ShowErrorPage(c, commonErr, opt.IsPage)
|
||||
return
|
||||
}
|
||||
|
||||
// 验证查看密码
|
||||
if opt.ValidateViewPassword && singleton.Conf.Site.ViewPassword != "" {
|
||||
viewPassword, _ := c.Cookie(singleton.Conf.Site.CookieName + "-vp")
|
||||
if err := bcrypt.CompareHashAndPassword([]byte(viewPassword), []byte(singleton.Conf.Site.ViewPassword)); err != nil {
|
||||
c.HTML(http.StatusOK, GetPreferredTheme(c, "/viewpassword"), CommonEnvironment(c, gin.H{
|
||||
"Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "VerifyPassword"}),
|
||||
"CustomCode": singleton.Conf.Site.CustomCode,
|
||||
}))
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
c.Set(model.CtxKeyViewPasswordVerified, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ var adminPage = map[string]bool{
|
||||
"/monitor": true,
|
||||
"/setting": true,
|
||||
"/notification": true,
|
||||
"/nat": true,
|
||||
"/cron": true,
|
||||
"/api": true,
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ func PreferredTheme(c *gin.Context) {
|
||||
if theme, err := c.Cookie("preferred_theme"); err == nil {
|
||||
if _, has := model.Themes[theme]; has {
|
||||
// 检验自定义主题
|
||||
if theme == "custom" && singleton.Conf.Site.Theme != "custom" && !utils.IsFileExists("resource/template/custom/home.html") {
|
||||
if theme == "custom" && singleton.Conf.Site.Theme != "custom" && !utils.IsFileExists("resource/template/theme-custom/home.html") {
|
||||
return
|
||||
}
|
||||
c.Set(model.CtxKeyPreferredTheme, theme)
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package mygin
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/naiba/nezha/model"
|
||||
"github.com/naiba/nezha/service/singleton"
|
||||
"github.com/nicksnyder/go-i18n/v2/i18n"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
type ValidateViewPasswordOption struct {
|
||||
IsPage bool
|
||||
AbortWhenFail bool
|
||||
}
|
||||
|
||||
func ValidateViewPassword(opt ValidateViewPasswordOption) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if singleton.Conf.Site.ViewPassword == "" {
|
||||
return
|
||||
}
|
||||
_, authorized := c.Get(model.CtxKeyAuthorizedUser)
|
||||
if authorized {
|
||||
return
|
||||
}
|
||||
viewPassword, err := c.Cookie(singleton.Conf.Site.CookieName + "-vp")
|
||||
if err == nil {
|
||||
err = bcrypt.CompareHashAndPassword([]byte(viewPassword), []byte(singleton.Conf.Site.ViewPassword))
|
||||
}
|
||||
if err == nil {
|
||||
c.Set(model.CtxKeyViewPasswordVerified, true)
|
||||
return
|
||||
}
|
||||
if !opt.AbortWhenFail {
|
||||
return
|
||||
}
|
||||
if opt.IsPage {
|
||||
c.HTML(http.StatusOK, GetPreferredTheme(c, "/viewpassword"), CommonEnvironment(c, gin.H{
|
||||
"Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "VerifyPassword"}),
|
||||
"CustomCode": singleton.Conf.Site.CustomCode,
|
||||
}))
|
||||
|
||||
} else {
|
||||
c.JSON(http.StatusOK, model.Response{
|
||||
Code: http.StatusForbidden,
|
||||
Message: "访问受限",
|
||||
})
|
||||
}
|
||||
c.Abort()
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
+12
-6
@@ -2,8 +2,6 @@ package utils
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type testSt struct {
|
||||
@@ -36,7 +34,9 @@ func TestNotification(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
assert.Equal(t, IPDesensitize(c.input), c.output)
|
||||
if c.output != IPDesensitize(c.input) {
|
||||
t.Fatalf("Expected %s, but got %s", c.output, IPDesensitize(c.input))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,9 +44,15 @@ func TestGenerGenerateRandomString(t *testing.T) {
|
||||
generatedString := make(map[string]bool)
|
||||
for i := 0; i < 100; i++ {
|
||||
str, err := GenerateRandomString(32)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, len(str), 32)
|
||||
assert.False(t, generatedString[str])
|
||||
if err != nil {
|
||||
t.Fatalf("Error: %s", err)
|
||||
}
|
||||
if len(str) != 32 {
|
||||
t.Fatalf("Expected 32, but got %d", len(str))
|
||||
}
|
||||
if generatedString[str] {
|
||||
t.Fatalf("Duplicated string: %s", str)
|
||||
}
|
||||
generatedString[str] = true
|
||||
}
|
||||
}
|
||||
|
||||
+41
-10
@@ -1,25 +1,56 @@
|
||||
package websocketx
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
var _ io.ReadWriteCloser = &Conn{}
|
||||
|
||||
type Conn struct {
|
||||
*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()
|
||||
defer conn.writeLock.Unlock()
|
||||
var err error
|
||||
lo.TryCatchWithErrorValue(func() error {
|
||||
return conn.Conn.WriteMessage(msgType, data)
|
||||
}, func(res any) {
|
||||
err = res.(error)
|
||||
})
|
||||
return err
|
||||
if err := conn.Conn.WriteMessage(websocket.BinaryMessage, data); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(data), nil
|
||||
}
|
||||
|
||||
func (conn *Conn) WriteMessage(messageType int, data []byte) error {
|
||||
conn.writeLock.Lock()
|
||||
defer conn.writeLock.Unlock()
|
||||
return conn.Conn.WriteMessage(messageType, data)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
+358
-109
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.30.0
|
||||
// protoc v3.21.12
|
||||
// protoc-gen-go v1.34.1
|
||||
// protoc v5.26.1
|
||||
// source: proto/nezha.proto
|
||||
|
||||
package proto
|
||||
@@ -35,8 +35,9 @@ type Host struct {
|
||||
Virtualization string `protobuf:"bytes,8,opt,name=virtualization,proto3" json:"virtualization,omitempty"`
|
||||
BootTime uint64 `protobuf:"varint,9,opt,name=boot_time,json=bootTime,proto3" json:"boot_time,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"` // deprecated
|
||||
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() {
|
||||
@@ -155,26 +156,35 @@ func (x *Host) GetVersion() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Host) GetGpu() []string {
|
||||
if x != nil {
|
||||
return x.Gpu
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type State struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
Uptime uint64 `protobuf:"varint,10,opt,name=uptime,proto3" json:"uptime,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"`
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
Uptime uint64 `protobuf:"varint,10,opt,name=uptime,proto3" json:"uptime,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"`
|
||||
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"`
|
||||
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"`
|
||||
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() {
|
||||
@@ -314,6 +324,75 @@ func (x *State) GetProcessCount() uint64 {
|
||||
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 {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -327,7 +406,7 @@ type Task struct {
|
||||
func (x *Task) Reset() {
|
||||
*x = Task{}
|
||||
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.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -340,7 +419,7 @@ func (x *Task) String() string {
|
||||
func (*Task) ProtoMessage() {}
|
||||
|
||||
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 {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -353,7 +432,7 @@ func (x *Task) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use Task.ProtoReflect.Descriptor instead.
|
||||
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 {
|
||||
@@ -392,7 +471,7 @@ type TaskResult struct {
|
||||
func (x *TaskResult) Reset() {
|
||||
*x = TaskResult{}
|
||||
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.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -405,7 +484,7 @@ func (x *TaskResult) String() string {
|
||||
func (*TaskResult) ProtoMessage() {}
|
||||
|
||||
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 {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -418,7 +497,7 @@ func (x *TaskResult) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use TaskResult.ProtoReflect.Descriptor instead.
|
||||
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 {
|
||||
@@ -467,7 +546,7 @@ type Receipt struct {
|
||||
func (x *Receipt) Reset() {
|
||||
*x = Receipt{}
|
||||
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.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -480,7 +559,7 @@ func (x *Receipt) String() string {
|
||||
func (*Receipt) ProtoMessage() {}
|
||||
|
||||
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 {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -493,7 +572,7 @@ func (x *Receipt) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use Receipt.ProtoReflect.Descriptor instead.
|
||||
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 {
|
||||
@@ -503,11 +582,113 @@ func (x *Receipt) GetProced() bool {
|
||||
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
|
||||
}
|
||||
|
||||
type GeoIP struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Ip string `protobuf:"bytes,1,opt,name=ip,proto3" json:"ip,omitempty"`
|
||||
CountryCode string `protobuf:"bytes,2,opt,name=country_code,json=countryCode,proto3" json:"country_code,omitempty"`
|
||||
}
|
||||
|
||||
func (x *GeoIP) Reset() {
|
||||
*x = GeoIP{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_proto_nezha_proto_msgTypes[7]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *GeoIP) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GeoIP) ProtoMessage() {}
|
||||
|
||||
func (x *GeoIP) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_nezha_proto_msgTypes[7]
|
||||
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 GeoIP.ProtoReflect.Descriptor instead.
|
||||
func (*GeoIP) Descriptor() ([]byte, []int) {
|
||||
return file_proto_nezha_proto_rawDescGZIP(), []int{7}
|
||||
}
|
||||
|
||||
func (x *GeoIP) GetIp() string {
|
||||
if x != nil {
|
||||
return x.Ip
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *GeoIP) GetCountryCode() string {
|
||||
if x != nil {
|
||||
return x.CountryCode
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_proto_nezha_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_proto_nezha_proto_rawDesc = []byte{
|
||||
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,
|
||||
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,
|
||||
@@ -529,65 +710,89 @@ var file_proto_nezha_proto_rawDesc = []byte{
|
||||
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,
|
||||
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,
|
||||
0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x63, 0x70, 0x75, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x63, 0x70, 0x75, 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x65, 0x6d,
|
||||
0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x6d, 0x65, 0x6d,
|
||||
0x55, 0x73, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x75, 0x73, 0x65,
|
||||
0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x73, 0x77, 0x61, 0x70, 0x55, 0x73, 0x65,
|
||||
0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x05,
|
||||
0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x64, 0x69, 0x73, 0x6b, 0x55, 0x73, 0x65, 0x64, 0x12, 0x26,
|
||||
0x0a, 0x0f, 0x6e, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65,
|
||||
0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x6e, 0x65, 0x74, 0x49, 0x6e, 0x54, 0x72,
|
||||
0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x12, 0x28, 0x0a, 0x10, 0x6e, 0x65, 0x74, 0x5f, 0x6f, 0x75,
|
||||
0x74, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04,
|
||||
0x52, 0x0e, 0x6e, 0x65, 0x74, 0x4f, 0x75, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72,
|
||||
0x12, 0x20, 0x0a, 0x0c, 0x6e, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x5f, 0x73, 0x70, 0x65, 0x65, 0x64,
|
||||
0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x6e, 0x65, 0x74, 0x49, 0x6e, 0x53, 0x70, 0x65,
|
||||
0x65, 0x64, 0x12, 0x22, 0x0a, 0x0d, 0x6e, 0x65, 0x74, 0x5f, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x70,
|
||||
0x65, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6e, 0x65, 0x74, 0x4f, 0x75,
|
||||
0x74, 0x53, 0x70, 0x65, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65,
|
||||
0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x14,
|
||||
0x0a, 0x05, 0x6c, 0x6f, 0x61, 0x64, 0x31, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x6c,
|
||||
0x6f, 0x61, 0x64, 0x31, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x6f, 0x61, 0x64, 0x35, 0x18, 0x0c, 0x20,
|
||||
0x01, 0x28, 0x01, 0x52, 0x05, 0x6c, 0x6f, 0x61, 0x64, 0x35, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x6f,
|
||||
0x61, 0x64, 0x31, 0x35, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x6c, 0x6f, 0x61, 0x64,
|
||||
0x31, 0x35, 0x12, 0x24, 0x0a, 0x0e, 0x74, 0x63, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x5f, 0x63,
|
||||
0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x74, 0x63, 0x70, 0x43,
|
||||
0x6f, 0x6e, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x75, 0x64, 0x70, 0x5f,
|
||||
0x63, 0x6f, 0x6e, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x04,
|
||||
0x52, 0x0c, 0x75, 0x64, 0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x23,
|
||||
0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18,
|
||||
0x10, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x43, 0x6f,
|
||||
0x75, 0x6e, 0x74, 0x22, 0x3e, 0x0a, 0x04, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x0e, 0x0a, 0x02, 0x69,
|
||||
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74,
|
||||
0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12,
|
||||
0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64,
|
||||
0x61, 0x74, 0x61, 0x22, 0x7a, 0x0a, 0x0a, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c,
|
||||
0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69,
|
||||
0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52,
|
||||
0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x03,
|
||||
0x20, 0x01, 0x28, 0x02, 0x52, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x64,
|
||||
0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12,
|
||||
0x1e, 0x0a, 0x0a, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x18, 0x05, 0x20,
|
||||
0x01, 0x28, 0x08, 0x52, 0x0a, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x22,
|
||||
0x21, 0x0a, 0x07, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72,
|
||||
0x6f, 0x63, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x70, 0x72, 0x6f, 0x63,
|
||||
0x65, 0x64, 0x32, 0xd6, 0x01, 0x0a, 0x0c, 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, 0x42, 0x09, 0x5a, 0x07, 0x2e,
|
||||
0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a,
|
||||
0x03, 0x67, 0x70, 0x75, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x67, 0x70, 0x75, 0x22,
|
||||
0xa9, 0x04, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x63, 0x70, 0x75,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x63, 0x70, 0x75, 0x12, 0x19, 0x0a, 0x08, 0x6d,
|
||||
0x65, 0x6d, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x6d,
|
||||
0x65, 0x6d, 0x55, 0x73, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x75,
|
||||
0x73, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x73, 0x77, 0x61, 0x70, 0x55,
|
||||
0x73, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x75, 0x73, 0x65, 0x64,
|
||||
0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x64, 0x69, 0x73, 0x6b, 0x55, 0x73, 0x65, 0x64,
|
||||
0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73,
|
||||
0x66, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x6e, 0x65, 0x74, 0x49, 0x6e,
|
||||
0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x12, 0x28, 0x0a, 0x10, 0x6e, 0x65, 0x74, 0x5f,
|
||||
0x6f, 0x75, 0x74, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01,
|
||||
0x28, 0x04, 0x52, 0x0e, 0x6e, 0x65, 0x74, 0x4f, 0x75, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66,
|
||||
0x65, 0x72, 0x12, 0x20, 0x0a, 0x0c, 0x6e, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x5f, 0x73, 0x70, 0x65,
|
||||
0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x6e, 0x65, 0x74, 0x49, 0x6e, 0x53,
|
||||
0x70, 0x65, 0x65, 0x64, 0x12, 0x22, 0x0a, 0x0d, 0x6e, 0x65, 0x74, 0x5f, 0x6f, 0x75, 0x74, 0x5f,
|
||||
0x73, 0x70, 0x65, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6e, 0x65, 0x74,
|
||||
0x4f, 0x75, 0x74, 0x53, 0x70, 0x65, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x70, 0x74, 0x69,
|
||||
0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65,
|
||||
0x12, 0x14, 0x0a, 0x05, 0x6c, 0x6f, 0x61, 0x64, 0x31, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x01, 0x52,
|
||||
0x05, 0x6c, 0x6f, 0x61, 0x64, 0x31, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x6f, 0x61, 0x64, 0x35, 0x18,
|
||||
0x0c, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x6c, 0x6f, 0x61, 0x64, 0x35, 0x12, 0x16, 0x0a, 0x06,
|
||||
0x6c, 0x6f, 0x61, 0x64, 0x31, 0x35, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x6c, 0x6f,
|
||||
0x61, 0x64, 0x31, 0x35, 0x12, 0x24, 0x0a, 0x0e, 0x74, 0x63, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x6e,
|
||||
0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x74, 0x63,
|
||||
0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x75, 0x64,
|
||||
0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0f, 0x20, 0x01,
|
||||
0x28, 0x04, 0x52, 0x0c, 0x75, 0x64, 0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74,
|
||||
0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e,
|
||||
0x74, 0x18, 0x10, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73,
|
||||
0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x42, 0x0a, 0x0c, 0x74, 0x65, 0x6d, 0x70, 0x65, 0x72, 0x61,
|
||||
0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x11, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x53, 0x65, 0x6e, 0x73, 0x6f, 0x72,
|
||||
0x54, 0x65, 0x6d, 0x70, 0x65, 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x0c, 0x74, 0x65, 0x6d,
|
||||
0x70, 0x65, 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x67, 0x70, 0x75,
|
||||
0x18, 0x12, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x67, 0x70, 0x75, 0x22, 0x4f, 0x0a, 0x17, 0x53,
|
||||
0x74, 0x61, 0x74, 0x65, 0x5f, 0x53, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x54, 0x65, 0x6d, 0x70, 0x65,
|
||||
0x72, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x74, 0x65,
|
||||
0x6d, 0x70, 0x65, 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52,
|
||||
0x0b, 0x74, 0x65, 0x6d, 0x70, 0x65, 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x3e, 0x0a, 0x04,
|
||||
0x54, 0x61, 0x73, 0x6b, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04,
|
||||
0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01,
|
||||
0x28, 0x04, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61,
|
||||
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x7a, 0x0a, 0x0a,
|
||||
0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79,
|
||||
0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14,
|
||||
0x0a, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x02, 0x52, 0x05, 0x64,
|
||||
0x65, 0x6c, 0x61, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x75, 0x63, 0x63,
|
||||
0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x73, 0x75,
|
||||
0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x22, 0x21, 0x0a, 0x07, 0x52, 0x65, 0x63, 0x65,
|
||||
0x69, 0x70, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x64, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x08, 0x52, 0x06, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x64, 0x22, 0x22, 0x0a, 0x0c, 0x49,
|
||||
0x4f, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x44, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x64,
|
||||
0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22,
|
||||
0x3a, 0x0a, 0x05, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x70, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x75, 0x6e,
|
||||
0x74, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b,
|
||||
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x43, 0x6f, 0x64, 0x65, 0x32, 0xbf, 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,
|
||||
0x12, 0x2b, 0x0a, 0x0b, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x12,
|
||||
0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x1a, 0x0c, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x22, 0x00, 0x42, 0x09, 0x5a,
|
||||
0x07, 0x2e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -602,28 +807,36 @@ func file_proto_nezha_proto_rawDescGZIP() []byte {
|
||||
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, 8)
|
||||
var file_proto_nezha_proto_goTypes = []interface{}{
|
||||
(*Host)(nil), // 0: proto.Host
|
||||
(*State)(nil), // 1: proto.State
|
||||
(*Task)(nil), // 2: proto.Task
|
||||
(*TaskResult)(nil), // 3: proto.TaskResult
|
||||
(*Receipt)(nil), // 4: proto.Receipt
|
||||
(*Host)(nil), // 0: proto.Host
|
||||
(*State)(nil), // 1: proto.State
|
||||
(*State_SensorTemperature)(nil), // 2: proto.State_SensorTemperature
|
||||
(*Task)(nil), // 3: proto.Task
|
||||
(*TaskResult)(nil), // 4: proto.TaskResult
|
||||
(*Receipt)(nil), // 5: proto.Receipt
|
||||
(*IOStreamData)(nil), // 6: proto.IOStreamData
|
||||
(*GeoIP)(nil), // 7: proto.GeoIP
|
||||
}
|
||||
var file_proto_nezha_proto_depIdxs = []int32{
|
||||
1, // 0: proto.NezhaService.ReportSystemState:input_type -> proto.State
|
||||
0, // 1: proto.NezhaService.ReportSystemInfo:input_type -> proto.Host
|
||||
3, // 2: proto.NezhaService.ReportTask:input_type -> proto.TaskResult
|
||||
0, // 3: proto.NezhaService.RequestTask:input_type -> proto.Host
|
||||
4, // 4: proto.NezhaService.ReportSystemState:output_type -> proto.Receipt
|
||||
4, // 5: proto.NezhaService.ReportSystemInfo:output_type -> proto.Receipt
|
||||
4, // 6: proto.NezhaService.ReportTask:output_type -> proto.Receipt
|
||||
2, // 7: proto.NezhaService.RequestTask:output_type -> proto.Task
|
||||
4, // [4:8] is the sub-list for method output_type
|
||||
0, // [0:4] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
2, // 0: proto.State.temperatures:type_name -> proto.State_SensorTemperature
|
||||
1, // 1: proto.NezhaService.ReportSystemState:input_type -> proto.State
|
||||
0, // 2: proto.NezhaService.ReportSystemInfo:input_type -> proto.Host
|
||||
4, // 3: proto.NezhaService.ReportTask:input_type -> proto.TaskResult
|
||||
0, // 4: proto.NezhaService.RequestTask:input_type -> proto.Host
|
||||
6, // 5: proto.NezhaService.IOStream:input_type -> proto.IOStreamData
|
||||
7, // 6: proto.NezhaService.LookupGeoIP:input_type -> proto.GeoIP
|
||||
5, // 7: proto.NezhaService.ReportSystemState:output_type -> proto.Receipt
|
||||
5, // 8: proto.NezhaService.ReportSystemInfo:output_type -> proto.Receipt
|
||||
5, // 9: proto.NezhaService.ReportTask:output_type -> proto.Receipt
|
||||
3, // 10: proto.NezhaService.RequestTask:output_type -> proto.Task
|
||||
6, // 11: proto.NezhaService.IOStream:output_type -> proto.IOStreamData
|
||||
7, // 12: proto.NezhaService.LookupGeoIP:output_type -> proto.GeoIP
|
||||
7, // [7:13] is the sub-list for method output_type
|
||||
1, // [1:7] 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() }
|
||||
@@ -657,7 +870,7 @@ func file_proto_nezha_proto_init() {
|
||||
}
|
||||
}
|
||||
file_proto_nezha_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Task); i {
|
||||
switch v := v.(*State_SensorTemperature); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
@@ -669,7 +882,7 @@ func file_proto_nezha_proto_init() {
|
||||
}
|
||||
}
|
||||
file_proto_nezha_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*TaskResult); i {
|
||||
switch v := v.(*Task); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
@@ -681,6 +894,18 @@ func file_proto_nezha_proto_init() {
|
||||
}
|
||||
}
|
||||
file_proto_nezha_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*TaskResult); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_proto_nezha_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Receipt); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
@@ -692,6 +917,30 @@ func file_proto_nezha_proto_init() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_proto_nezha_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*IOStreamData); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_proto_nezha_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*GeoIP); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
@@ -699,7 +948,7 @@ func file_proto_nezha_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_proto_nezha_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 5,
|
||||
NumMessages: 8,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
|
||||
+20
-1
@@ -8,6 +8,8 @@ service NezhaService {
|
||||
rpc ReportSystemInfo(Host)returns(Receipt){}
|
||||
rpc ReportTask(TaskResult)returns(Receipt){}
|
||||
rpc RequestTask(Host)returns(stream Task){}
|
||||
rpc IOStream(stream IOStreamData)returns(stream IOStreamData){}
|
||||
rpc LookupGeoIP(GeoIP)returns(GeoIP){}
|
||||
}
|
||||
|
||||
message Host {
|
||||
@@ -21,8 +23,9 @@ message Host {
|
||||
string virtualization = 8;
|
||||
uint64 boot_time = 9;
|
||||
string ip = 10;
|
||||
string country_code = 11;
|
||||
string country_code = 11; // deprecated
|
||||
string version = 12;
|
||||
repeated string gpu = 13;
|
||||
}
|
||||
|
||||
message State {
|
||||
@@ -41,6 +44,13 @@ message State {
|
||||
uint64 tcp_conn_count = 14;
|
||||
uint64 udp_conn_count = 15;
|
||||
uint64 process_count = 16;
|
||||
repeated State_SensorTemperature temperatures = 17;
|
||||
double gpu = 18;
|
||||
}
|
||||
|
||||
message State_SensorTemperature {
|
||||
string name = 1;
|
||||
double temperature = 2;
|
||||
}
|
||||
|
||||
message Task {
|
||||
@@ -60,3 +70,12 @@ message TaskResult {
|
||||
message Receipt{
|
||||
bool proced = 1;
|
||||
}
|
||||
|
||||
message IOStreamData {
|
||||
bytes data = 1;
|
||||
}
|
||||
|
||||
message GeoIP {
|
||||
string ip = 1;
|
||||
string country_code = 2;
|
||||
}
|
||||
+107
-1
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.3.0
|
||||
// - protoc v3.21.12
|
||||
// - protoc v5.26.1
|
||||
// source: proto/nezha.proto
|
||||
|
||||
package proto
|
||||
@@ -23,6 +23,8 @@ const (
|
||||
NezhaService_ReportSystemInfo_FullMethodName = "/proto.NezhaService/ReportSystemInfo"
|
||||
NezhaService_ReportTask_FullMethodName = "/proto.NezhaService/ReportTask"
|
||||
NezhaService_RequestTask_FullMethodName = "/proto.NezhaService/RequestTask"
|
||||
NezhaService_IOStream_FullMethodName = "/proto.NezhaService/IOStream"
|
||||
NezhaService_LookupGeoIP_FullMethodName = "/proto.NezhaService/LookupGeoIP"
|
||||
)
|
||||
|
||||
// NezhaServiceClient is the client API for NezhaService service.
|
||||
@@ -33,6 +35,8 @@ type NezhaServiceClient interface {
|
||||
ReportSystemInfo(ctx context.Context, in *Host, 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)
|
||||
IOStream(ctx context.Context, opts ...grpc.CallOption) (NezhaService_IOStreamClient, error)
|
||||
LookupGeoIP(ctx context.Context, in *GeoIP, opts ...grpc.CallOption) (*GeoIP, error)
|
||||
}
|
||||
|
||||
type nezhaServiceClient struct {
|
||||
@@ -102,6 +106,46 @@ func (x *nezhaServiceRequestTaskClient) Recv() (*Task, error) {
|
||||
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
|
||||
}
|
||||
|
||||
func (c *nezhaServiceClient) LookupGeoIP(ctx context.Context, in *GeoIP, opts ...grpc.CallOption) (*GeoIP, error) {
|
||||
out := new(GeoIP)
|
||||
err := c.cc.Invoke(ctx, NezhaService_LookupGeoIP_FullMethodName, in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// NezhaServiceServer is the server API for NezhaService service.
|
||||
// All implementations should embed UnimplementedNezhaServiceServer
|
||||
// for forward compatibility
|
||||
@@ -110,6 +154,8 @@ type NezhaServiceServer interface {
|
||||
ReportSystemInfo(context.Context, *Host) (*Receipt, error)
|
||||
ReportTask(context.Context, *TaskResult) (*Receipt, error)
|
||||
RequestTask(*Host, NezhaService_RequestTaskServer) error
|
||||
IOStream(NezhaService_IOStreamServer) error
|
||||
LookupGeoIP(context.Context, *GeoIP) (*GeoIP, error)
|
||||
}
|
||||
|
||||
// UnimplementedNezhaServiceServer should be embedded to have forward compatible implementations.
|
||||
@@ -128,6 +174,12 @@ func (UnimplementedNezhaServiceServer) ReportTask(context.Context, *TaskResult)
|
||||
func (UnimplementedNezhaServiceServer) RequestTask(*Host, NezhaService_RequestTaskServer) error {
|
||||
return status.Errorf(codes.Unimplemented, "method RequestTask not implemented")
|
||||
}
|
||||
func (UnimplementedNezhaServiceServer) IOStream(NezhaService_IOStreamServer) error {
|
||||
return status.Errorf(codes.Unimplemented, "method IOStream not implemented")
|
||||
}
|
||||
func (UnimplementedNezhaServiceServer) LookupGeoIP(context.Context, *GeoIP) (*GeoIP, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method LookupGeoIP not implemented")
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -215,6 +267,50 @@ func (x *nezhaServiceRequestTaskServer) Send(m *Task) error {
|
||||
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
|
||||
}
|
||||
|
||||
func _NezhaService_LookupGeoIP_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GeoIP)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(NezhaServiceServer).LookupGeoIP(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: NezhaService_LookupGeoIP_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(NezhaServiceServer).LookupGeoIP(ctx, req.(*GeoIP))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// NezhaService_ServiceDesc is the grpc.ServiceDesc for NezhaService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
@@ -234,6 +330,10 @@ var NezhaService_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "ReportTask",
|
||||
Handler: _NezhaService_ReportTask_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "LookupGeoIP",
|
||||
Handler: _NezhaService_LookupGeoIP_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
@@ -241,6 +341,12 @@ var NezhaService_ServiceDesc = grpc.ServiceDesc{
|
||||
Handler: _NezhaService_RequestTask_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
{
|
||||
StreamName: "IOStream",
|
||||
Handler: _NezhaService_IOStream_Handler,
|
||||
ServerStreams: true,
|
||||
ClientStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "proto/nezha.proto",
|
||||
}
|
||||
|
||||
Vendored
+34
-10
@@ -32,10 +32,10 @@ other = "Logout"
|
||||
other = "Login"
|
||||
|
||||
[ConfirmLogout]
|
||||
other = "Confirm Logout?"
|
||||
other = "Do you want to Logout?"
|
||||
|
||||
[AfterLoggingOutYouHaveToLoginAgain]
|
||||
other = "You need to login again after logout to continue"
|
||||
other = "You need to Login again to continue"
|
||||
|
||||
[Cancel]
|
||||
other = "Cancel"
|
||||
@@ -227,7 +227,7 @@ other = "Secret"
|
||||
other = "Note"
|
||||
|
||||
[LinuxOneKeyInstall]
|
||||
other = "Linux One-Key Install"
|
||||
other = "Linux One-Command Install"
|
||||
|
||||
[NoDomainAlert]
|
||||
other = "Set CDN bypassed Domain/IP in setting before using this function"
|
||||
@@ -257,7 +257,7 @@ other = "Execute by Trigger"
|
||||
other = "Delete Scheduled Task"
|
||||
|
||||
[ConfirmToDeleteThisScheduledTask]
|
||||
other = "Confirm Delete?"
|
||||
other = "Are you sure you want to Delete?"
|
||||
|
||||
[AccessDenied]
|
||||
other = "Access Denied"
|
||||
@@ -293,7 +293,7 @@ other = "Delete Notification Method"
|
||||
other = "Confirm to Delete This Notification Method?"
|
||||
|
||||
[ForceUpdate]
|
||||
other = "Force Update"
|
||||
other = "Force to Update"
|
||||
|
||||
[SelectAll]
|
||||
other = "Select All"
|
||||
@@ -302,7 +302,7 @@ other = "Select All"
|
||||
other = "Version"
|
||||
|
||||
[OneKeyInstall]
|
||||
other = "One-Key Install"
|
||||
other = "One-Command Install"
|
||||
|
||||
[ClickToCopy]
|
||||
other = "Click To Copy"
|
||||
@@ -398,7 +398,7 @@ other = "Virtualization"
|
||||
other = "Swap"
|
||||
|
||||
[NetTransfer]
|
||||
other = "Network Transfer"
|
||||
other = "Usage"
|
||||
|
||||
[Load]
|
||||
other = "Load"
|
||||
@@ -419,7 +419,7 @@ other = "Last Active"
|
||||
other = "Version"
|
||||
|
||||
[NetSpeed]
|
||||
other = "Network Speed"
|
||||
other = "NIC"
|
||||
|
||||
[Uptime]
|
||||
other = "Uptime"
|
||||
@@ -473,7 +473,7 @@ other = "Next check"
|
||||
other = "Current Usage"
|
||||
|
||||
[VerifyPassword]
|
||||
other = "Verify Password"
|
||||
other = "Please enter your password"
|
||||
|
||||
[LightMode]
|
||||
other = "Light Mode"
|
||||
@@ -482,7 +482,7 @@ other = "Light Mode"
|
||||
other = "Dark Mode"
|
||||
|
||||
[FollowSystem]
|
||||
other = "Follow System"
|
||||
other = "System Auto"
|
||||
|
||||
[GridLayout]
|
||||
other = "Grid Layout"
|
||||
@@ -619,11 +619,35 @@ other = "Enable Show in Service"
|
||||
[EnableDDNS]
|
||||
other = "Enable DDNS"
|
||||
|
||||
[EnableIPv4]
|
||||
other = "Enable DDNS IPv4"
|
||||
|
||||
[EnableIpv6]
|
||||
other = "Enable DDNS IPv6"
|
||||
|
||||
[DDNSDomain]
|
||||
other = "DDNS Domain"
|
||||
|
||||
[DDNSProfile]
|
||||
other = "DDNS Profile Name"
|
||||
|
||||
[Feature]
|
||||
other = "Feature"
|
||||
|
||||
[Template]
|
||||
other = "Template"
|
||||
|
||||
[Stat]
|
||||
other = "Asset"
|
||||
|
||||
[Temperature]
|
||||
other = "Temperature"
|
||||
|
||||
[DisableSwitchTemplateInFrontend]
|
||||
other = "Disable Switch Template in Frontend"
|
||||
|
||||
[ServersOnWorldMap]
|
||||
other = "Servers On World Map"
|
||||
|
||||
[NAT]
|
||||
other = "NAT"
|
||||
Vendored
+25
-1
@@ -203,7 +203,7 @@ other = "Agregar Servidor"
|
||||
other = "Editar Grupo de Servidores en Lote"
|
||||
|
||||
[BatchDeleteServer]
|
||||
other = "Eliminar Servidores en Lote""
|
||||
other = "Eliminar Servidores en Lote"
|
||||
|
||||
[InputServerGroupName]
|
||||
other = "Ingrese Nombre del Grupo de Servidores"
|
||||
@@ -619,11 +619,35 @@ other = "Mostrar en servicio"
|
||||
[EnableDDNS]
|
||||
other = "Habilitar DDNS"
|
||||
|
||||
[EnableIPv4]
|
||||
other = "Habilitar DDNS IPv4"
|
||||
|
||||
[EnableIpv6]
|
||||
other = "Habilitar DDNS IPv6"
|
||||
|
||||
[DDNSDomain]
|
||||
other = "Dominio DDNS"
|
||||
|
||||
[DDNSProfile]
|
||||
other = "Nombre del perfil de DDNS"
|
||||
|
||||
[Feature]
|
||||
other = "Característica"
|
||||
|
||||
[Template]
|
||||
other = "Plantilla"
|
||||
|
||||
[Stat]
|
||||
other = "Stat"
|
||||
|
||||
[Temperature]
|
||||
other = "Temperatura"
|
||||
|
||||
[DisableSwitchTemplateInFrontend]
|
||||
other = "Deshabilitar Cambio de Plantilla en Frontend"
|
||||
|
||||
[ServersOnWorldMap]
|
||||
other = "Servidores en el mapa mundial"
|
||||
|
||||
[NAT]
|
||||
other = "NAT"
|
||||
Vendored
+24
@@ -619,11 +619,35 @@ other = "在服务中显示"
|
||||
[EnableDDNS]
|
||||
other = "启用DDNS"
|
||||
|
||||
[EnableIPv4]
|
||||
other = "启用DDNS IPv4"
|
||||
|
||||
[EnableIpv6]
|
||||
other = "启用DDNS IPv6"
|
||||
|
||||
[DDNSDomain]
|
||||
other = "DDNS域名"
|
||||
|
||||
[DDNSProfile]
|
||||
other = "DDNS配置名"
|
||||
|
||||
[Feature]
|
||||
other = "功能"
|
||||
|
||||
[Template]
|
||||
other = "主题"
|
||||
|
||||
[Stat]
|
||||
other = "信息"
|
||||
|
||||
[Temperature]
|
||||
other = "温度"
|
||||
|
||||
[DisableSwitchTemplateInFrontend]
|
||||
other = "禁止前台切换模板"
|
||||
|
||||
[ServersOnWorldMap]
|
||||
other = "服务器世界分布图"
|
||||
|
||||
[NAT]
|
||||
other = "内网穿透"
|
||||
Vendored
+24
@@ -619,11 +619,35 @@ other = "在服務中顯示"
|
||||
[EnableDDNS]
|
||||
other = "啟用DDNS"
|
||||
|
||||
[EnableIPv4]
|
||||
other = "啟用DDNS IPv4"
|
||||
|
||||
[EnableIpv6]
|
||||
other = "啟用DDNS IPv6"
|
||||
|
||||
[DDNSDomain]
|
||||
other = "DDNS網域"
|
||||
|
||||
[DDNSProfile]
|
||||
other = "DDNS設定名"
|
||||
|
||||
[Feature]
|
||||
other = "功能"
|
||||
|
||||
[Template]
|
||||
other = "主題"
|
||||
|
||||
[Stat]
|
||||
other = "信息"
|
||||
|
||||
[Temperature]
|
||||
other = "溫度"
|
||||
|
||||
[DisableSwitchTemplateInFrontend]
|
||||
other = "禁止前台切換主題"
|
||||
|
||||
[ServersOnWorldMap]
|
||||
other = "伺服器世界分布圖"
|
||||
|
||||
[NAT]
|
||||
other = "NAT"
|
||||
@@ -91,6 +91,7 @@ function showFormModal(modelSelector, formID, URL, getData) {
|
||||
item.name.endsWith("_id") ||
|
||||
item.name === "id" ||
|
||||
item.name === "ID" ||
|
||||
item.name === "ServerID" ||
|
||||
item.name === "RequestType" ||
|
||||
item.name === "RequestMethod" ||
|
||||
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) {
|
||||
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=Tag]").val(server ? server.Tag : null);
|
||||
modal.find("input[name=DDNSDomain]").val(server ? server.DDNSDomain : null);
|
||||
modal.find("input[name=DDNSProfile]").val(server ? server.DDNSProfile : null);
|
||||
modal
|
||||
.find("input[name=DisplayIndex]")
|
||||
.val(server ? server.DisplayIndex : null);
|
||||
@@ -327,6 +351,16 @@ function addOrEditServer(server, conf) {
|
||||
} else {
|
||||
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");
|
||||
}
|
||||
|
||||
|
||||
+24
-4
@@ -3,6 +3,7 @@
|
||||
.ui.container {
|
||||
width:95% !important;
|
||||
font-size: 90% !important;
|
||||
max-width: 1300px !important;
|
||||
}
|
||||
}
|
||||
@media only screen and (max-width:767px) {
|
||||
@@ -86,21 +87,35 @@ td {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.ui.grid {
|
||||
margin-bottom:-0.5em
|
||||
}
|
||||
|
||||
.ui.card>.content>.header:not(.ui), .ui.cards>.card>.content>.header:not(.ui){
|
||||
line-height: 1em;
|
||||
}
|
||||
|
||||
.status.cards .wide.column {
|
||||
padding-top: 0 !important;
|
||||
padding-bottom: 0 !important;
|
||||
height:3.3rem !important;
|
||||
height:2.3rem !important;
|
||||
}
|
||||
|
||||
.status.cards .wide.column:nth-child(1) {
|
||||
margin-top:2rem !important;
|
||||
margin-top:1.2rem !important;
|
||||
}
|
||||
|
||||
.status.cards .wide.column:nth-child(2) {
|
||||
margin-top:2rem !important;
|
||||
margin-top:1.2rem !important;
|
||||
}
|
||||
|
||||
.status.cards .three.wide.column {
|
||||
text-align: center;
|
||||
width: 22%!important;
|
||||
}
|
||||
|
||||
.status.cards .thirteen.wide.column{
|
||||
width: 78%!important;
|
||||
padding-left:0;
|
||||
}
|
||||
|
||||
@@ -124,12 +139,16 @@ td {
|
||||
.closePopup{
|
||||
color:rgb(10, 148, 242) !important;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
top: 7px;
|
||||
right: 10px;
|
||||
cursor: pointer;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.temp-detail {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ui.content {
|
||||
margin:0 !important;
|
||||
padding:1em !important;
|
||||
@@ -194,6 +213,7 @@ td {
|
||||
}
|
||||
|
||||
.nezha-secondary-font {
|
||||
height: 1em;
|
||||
color: rgb(10, 148, 242) !important;
|
||||
}
|
||||
|
||||
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
const mixinsVue = {
|
||||
delimiters: ['@#', '#@'],
|
||||
data: {
|
||||
preferredTemplate: null,
|
||||
isMobile: false,
|
||||
adaptedTemplates: [
|
||||
{ key: 'default', name: 'Default', icon: 'th large' },
|
||||
{ key: 'angel-kanade', name: 'AngelKanade', icon: 'square' },
|
||||
{ key: 'server-status', name: 'ServerStatus', icon: 'list' }
|
||||
]
|
||||
},
|
||||
created() {
|
||||
this.isMobile = this.checkIsMobile();
|
||||
this.preferredTemplate = this.getCookie('preferred_theme') ? this.getCookie('preferred_theme') : this.$root.defaultTemplate;
|
||||
},
|
||||
methods: {
|
||||
toggleTemplate(template) {
|
||||
if( template != this.preferredTemplate){
|
||||
this.preferredTemplate = template;
|
||||
this.updateCookie("preferred_theme", template);
|
||||
window.location.reload();
|
||||
}
|
||||
},
|
||||
updateCookie(name, value) {
|
||||
document.cookie = name + "=" + value +"; path=/";
|
||||
},
|
||||
getCookie(name) {
|
||||
const cookies = document.cookie.split(';');
|
||||
let cookieValue = null;
|
||||
for (let i = 0; i < cookies.length; i++) {
|
||||
const cookie = cookies[i].trim();
|
||||
if (cookie.startsWith(name + '=')) {
|
||||
cookieValue = cookie.substring(name.length + 1, cookie.length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return cookieValue;
|
||||
},
|
||||
checkIsMobile() { // 检测设备类型,页面宽度小于768px认为是移动设备
|
||||
return window.innerWidth <= 768;
|
||||
},
|
||||
logOut(id) {
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/api/logout',
|
||||
data: JSON.stringify({ id: id }),
|
||||
contentType: 'application/json',
|
||||
success: function (resp) {
|
||||
if (resp.code == 200) {
|
||||
window.location.reload();
|
||||
} else {
|
||||
alert('注销失败(Error ' + resp.code + '): ' + resp.message);
|
||||
}
|
||||
},
|
||||
error: function (err) {
|
||||
alert('网络错误: ' + err.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
+86
-71
@@ -1,5 +1,6 @@
|
||||
body[theme="dark"] {
|
||||
background: #31363b;
|
||||
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
|
||||
background: rgba(49, 54, 59, 1);
|
||||
color: #f1f1f1;
|
||||
}
|
||||
|
||||
@@ -7,14 +8,10 @@ body[theme="dark"] .navbar .navbar-brand {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
body[theme="dark"] .navbar .navbar-brand {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
body[theme="dark"] .navbar .dropdown-menu {
|
||||
list-style-image: initial;
|
||||
background-color: #171a1e;
|
||||
border-color: #31363b;
|
||||
background-color: rgba(23, 26, 30, 1);
|
||||
border-color: rgba(49, 54, 59, 1);
|
||||
box-shadow: rgba(0, 0, 0, 0.18) 0px 6px 12px;
|
||||
}
|
||||
|
||||
@@ -24,8 +21,8 @@ body[theme="dark"] .navbar .dropdown-menu > li > a {
|
||||
|
||||
body[theme="dark"] .navbar .dropdown-menu > li > a:focus,
|
||||
body[theme="dark"] .navbar .dropdown-menu > li > a:hover {
|
||||
background-color: #25282a;
|
||||
background-image: linear-gradient(#1c1d26 0, #1c1d26 100%);
|
||||
background-color: rgba(37, 40, 42, 1);
|
||||
background-image: linear-gradient(rgba(28, 29, 38, 1) 0, rgba(28, 29, 38, 1) 100%);
|
||||
}
|
||||
|
||||
body[theme="dark"] .navbar .navbar-nav > .open > a:focus,
|
||||
@@ -37,18 +34,13 @@ body[theme="dark"] .navbar .navbar-nav > .open > a{
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
body[theme="dark"] .navbar .navbar-nav .open .dropdown-menu>li>a {
|
||||
body[theme="dark"] .navbar .navbar-nav .open .dropdown-menu > li > a {
|
||||
color: #f1f1f1;
|
||||
}
|
||||
|
||||
body[theme="dark"] .toolbox i{
|
||||
color: rgba(241,241,241,1);
|
||||
background-color: rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
body[theme="dark"] .content {
|
||||
background-image: initial;
|
||||
background-color: #1c1d26;
|
||||
background-color: rgba(28, 29, 38, 1);
|
||||
border: none;
|
||||
box-shadow: rgba(0, 0, 0, 0.5) 0 0.625em 2em;
|
||||
-webkit-box-shadow: rgba(0, 0, 0, 0.5) 0 0.625em 2em;
|
||||
@@ -57,57 +49,10 @@ body[theme="dark"] .content {
|
||||
|
||||
body[theme="dark"] .table {
|
||||
background-image: initial;
|
||||
background-color: #1c1d26;
|
||||
background-color: rgba(28, 29, 38, 1);
|
||||
border-color: #545b5e;
|
||||
}
|
||||
|
||||
body[theme="dark"] .table>thead>tr.node-group-tag>th {
|
||||
background-color: #1c1d26;
|
||||
}
|
||||
|
||||
body[theme="dark"] .table-striped tbody > tr.even > td,
|
||||
body[theme="dark"] .table-striped tbody > tr.even > th {
|
||||
background-color: #1c1d26;
|
||||
}
|
||||
|
||||
body[theme="dark"] .table-striped tbody > tr.odd > td,
|
||||
body[theme="dark"] .table-striped tbody > tr.odd > th {
|
||||
background-color: #1c1d26;
|
||||
}
|
||||
|
||||
body[theme="dark"] .progress {
|
||||
background-image: linear-gradient(#2c2c2c 0,#1c1d26 100%);
|
||||
background-color: #1c1d26;
|
||||
}
|
||||
|
||||
body[theme="dark"] .progress-bar {
|
||||
color: white;
|
||||
}
|
||||
|
||||
body[theme="dark"] .progress-bar-success {
|
||||
background-image: linear-gradient(#4d853a 0, #367e36 100%);
|
||||
}
|
||||
|
||||
body[theme="dark"] .progress-offline .progress-bar-success {
|
||||
background-image: linear-gradient(grey 0, grey 100%);
|
||||
}
|
||||
|
||||
body[theme="dark"] .progress-bar-warning {
|
||||
background-image: linear-gradient(#995f0d 0, #b5710f 100%);
|
||||
}
|
||||
|
||||
body[theme="dark"] .progress-offline .progress-bar-warning {
|
||||
background-image: linear-gradient(grey 0, grey 100%);
|
||||
}
|
||||
|
||||
body[theme="dark"] .progress-bar-danger {
|
||||
background-image: linear-gradient(#942320 0, #a12623 100%);
|
||||
}
|
||||
|
||||
body[theme="dark"] .progress-offline .progress-bar-danger {
|
||||
background-image: linear-gradient(grey 0, grey 100%);
|
||||
}
|
||||
|
||||
body[theme="dark"] .table > tbody > tr > td:before,
|
||||
body[theme="dark"] .table > tfoot > tr > td:before,
|
||||
body[theme="dark"] .table > thead > tr > td:before,
|
||||
@@ -115,17 +60,69 @@ body[theme="dark"] .table > thead > tr.node-group-cell > th:before{
|
||||
background-color: rgba(58, 62, 65, 1);
|
||||
}
|
||||
|
||||
body[theme="dark"] .table > tbody > tr.expandRow > td:before,
|
||||
body[theme="dark"] .table > thead > tr.node-group-tag > th:before{
|
||||
background-color: #1c1d26;
|
||||
body[theme="dark"] .table-striped tbody > tr,
|
||||
body[theme="dark"] .table-striped tbody > tr.even > td,
|
||||
body[theme="dark"] .table-striped tbody > tr.even > th {
|
||||
background-color: rgba(28, 29, 38, 1);
|
||||
}
|
||||
|
||||
body[theme="dark"] .table-striped tbody > tr,
|
||||
body[theme="dark"] .table-striped tbody > tr.odd > td,
|
||||
body[theme="dark"] .table-striped tbody > tr.odd > th {
|
||||
/* background-color: rgba(28, 29, 38, 1); 282B2E*/
|
||||
background-color: rgba(34, 35, 46, 1);
|
||||
}
|
||||
|
||||
body[theme="dark"] .table-hover > tbody > tr:hover{
|
||||
background-color: unset;
|
||||
}
|
||||
|
||||
body[theme="dark"] .table-hover > tbody > tr:not(.expandRow):hover > td {
|
||||
background-color: #171a1e;
|
||||
background-color: rgba(48, 50, 65, 1);
|
||||
}
|
||||
|
||||
body[theme="dark"] .table-striped > tbody > tr:nth-of-type(2n+1) {
|
||||
background-color: #1c1d26;
|
||||
/* expandRow展开部分样式 */
|
||||
body[theme="dark"] .table > tbody > tr.expandRow.odd > td:before{
|
||||
/* background-color: rgba(28, 29, 38, 1); 282B2E*/
|
||||
background-color: rgba(34, 35, 46, 1);
|
||||
}
|
||||
|
||||
body[theme="dark"] .table > tbody > tr.expandRow.even > td:before{
|
||||
background-color: rgba(28, 29, 38, 1);
|
||||
}
|
||||
/* expandRow展开部分样式结束 */
|
||||
|
||||
body[theme="dark"] .progress {
|
||||
background-image: linear-gradient(#2c2c2c 0,rgba(28, 29, 38, 1) 100%);
|
||||
background-color: rgba(28, 29, 38, 1);
|
||||
}
|
||||
|
||||
body[theme="dark"] .progress-bar {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
body[theme="dark"] .progress-bar-success {
|
||||
background-image: linear-gradient(#4d853a 0, #367e36 100%);
|
||||
}
|
||||
|
||||
body[theme="dark"] .progress-offline .progress-bar-success {
|
||||
background-image: linear-gradient(#808080 0, #808080 100%);
|
||||
}
|
||||
|
||||
body[theme="dark"] .progress-bar-warning {
|
||||
background-image: linear-gradient(#995f0d 0, #b5710f 100%);
|
||||
}
|
||||
|
||||
body[theme="dark"] .progress-offline .progress-bar-warning {
|
||||
background-image: linear-gradient(#808080 0, #808080 100%);
|
||||
}
|
||||
|
||||
body[theme="dark"] .progress-bar-danger {
|
||||
background-image: linear-gradient(#942320 0, #a12623 100%);
|
||||
}
|
||||
|
||||
body[theme="dark"] .progress-offline .progress-bar-danger {
|
||||
background-image: linear-gradient(#808080 0, #808080 100%);
|
||||
}
|
||||
|
||||
body[theme="dark"] .node-cell.status .status-icon.online {
|
||||
@@ -149,7 +146,7 @@ body[theme="dark"] .service-status .warning {
|
||||
}
|
||||
|
||||
body[theme="dark"] .service-day-status-icon {
|
||||
background-color: grey;
|
||||
background-color: rgba(128, 128, 128, 1);
|
||||
}
|
||||
|
||||
body[theme="dark"] footer p a,
|
||||
@@ -157,3 +154,21 @@ body[theme="dark"] footer p a:hover {
|
||||
color: #f1f1f1;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
body[theme="dark"] .modal-content{
|
||||
background-color: rgba(28, 29, 38, 1);
|
||||
color: #f1f1f1;
|
||||
}
|
||||
|
||||
body[theme="dark"] .modal-header{
|
||||
border-bottom-color:#080808;
|
||||
}
|
||||
|
||||
body[theme="dark"] .modal-header i{
|
||||
color: #f1f1f1;
|
||||
}
|
||||
|
||||
body[theme="dark"] .toolbox i{
|
||||
color: rgba(241, 241, 241, 1);
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
@@ -131,4 +131,17 @@ body[theme="light"] footer p a,
|
||||
body[theme="light"] footer p a:hover {
|
||||
color: #333333;
|
||||
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);
|
||||
}
|
||||
+81
-4
@@ -50,6 +50,11 @@ body {
|
||||
/*margin-right: 5px;*/
|
||||
}
|
||||
|
||||
.navbar .dropdown:hover,
|
||||
.navbar .dropdown a{
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.navbar .dropdown .dropdown-toggle {
|
||||
padding-bottom: 10px;
|
||||
padding-top: 10px;
|
||||
@@ -59,7 +64,7 @@ body {
|
||||
margin:0px -15px;
|
||||
}
|
||||
|
||||
.navbar .navbar-nav>li>a {
|
||||
.navbar .navbar-nav > li > a {
|
||||
color:#f1f1f1;
|
||||
}
|
||||
|
||||
@@ -137,6 +142,10 @@ body {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
tr.accordion-toggle{
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
.table > tbody > tr > td,
|
||||
.table > tbody > tr > th,
|
||||
.table > tfoot > tr > td,
|
||||
@@ -230,6 +239,23 @@ body {
|
||||
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 +307,51 @@ body {
|
||||
.service-status .tooltip-inner {
|
||||
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;
|
||||
}
|
||||
|
||||
footer{
|
||||
padding-bottom: 2rem;
|
||||
}
|
||||
|
||||
footer p{
|
||||
text-align: center;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1200px) {
|
||||
.accordian-body{
|
||||
margin: 5px 0px 5px 10px;
|
||||
@@ -365,6 +432,16 @@ body {
|
||||
.toolbox {
|
||||
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) {
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@
|
||||
/** Name of the expando, to work with multiple documents or to re-shiv one document */
|
||||
var expando = '_html5shiv';
|
||||
|
||||
/** The id for the the documents expando */
|
||||
/** The id for the documents expando */
|
||||
var expanID = 0;
|
||||
|
||||
/** Cached data for each document */
|
||||
|
||||
+43
-53
@@ -5,31 +5,26 @@ const mixinsVue = {
|
||||
isSystemTheme: false,
|
||||
showGroup: false,
|
||||
showGoTop: false,
|
||||
preferredTemplate: 'default',
|
||||
isMobile: false
|
||||
preferredTemplate: null,
|
||||
isMobile: false,
|
||||
staticUrl: '/static/theme-server-status',
|
||||
adaptedTemplates: [
|
||||
{ key: 'default', name: 'Default', icon: 'th large' },
|
||||
{ key: 'angel-kanade', name: 'AngelKanade', icon: 'square' },
|
||||
{ key: 'server-status', name: 'ServerStatus', icon: 'list' }
|
||||
]
|
||||
},
|
||||
created() {
|
||||
this.isMobile = this.checkIsMobile();
|
||||
this.initTheme();
|
||||
this.storedShowGroup();
|
||||
this.preferredTemplate = this.getCookie('preferred_theme') ? this.getCookie('preferred_theme') : 'default';
|
||||
this.theme = this.initTheme();
|
||||
this.showGroup = this.initShowGroup();
|
||||
this.preferredTemplate = this.getCookie('preferred_theme') ? this.getCookie('preferred_theme') : this.$root.defaultTemplate;
|
||||
window.addEventListener('scroll', this.handleScroll);
|
||||
},
|
||||
destroyed() {
|
||||
window.removeEventListener('scroll', this.handleScroll);
|
||||
},
|
||||
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) {
|
||||
if( template != this.preferredTemplate){
|
||||
this.preferredTemplate = template;
|
||||
@@ -37,6 +32,38 @@ const mixinsVue = {
|
||||
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) {
|
||||
document.cookie = name + "=" + value +"; path=/";
|
||||
},
|
||||
@@ -52,43 +79,6 @@ const mixinsVue = {
|
||||
}
|
||||
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) {
|
||||
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>
|
||||
</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://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/semantic-ui/2.4.1/semantic.min.js"></script>
|
||||
<script src="https://unpkg.com/jquery@3.7.1/dist/jquery.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="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="https://unpkg.com/vue@2.6.14/dist/vue.min.js"></script>
|
||||
<script src="/static/main.js?v20240714"></script>
|
||||
<script>
|
||||
(function () {
|
||||
updateLang({{.LANG }});
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
{{end}}
|
||||
Vendored
+4
-8
@@ -1,21 +1,17 @@
|
||||
{{define "common/header"}}
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{.Conf.Language}}">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<meta content="telephone=no" name="format-detection">
|
||||
<title>{{.Title}}</title>
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/semantic-ui/2.4.1/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"
|
||||
rel="stylesheet" />
|
||||
<link rel="shortcut icon" type="image/png" href="/static/logo.svg?v20210804" />
|
||||
<link rel="stylesheet" type="text/css" href="https://unpkg.com/semantic-ui@2.4.0/dist/semantic.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="https://unpkg.com/font-logos@0.17.0/assets/font-logos.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="shortcut icon" type="image/png" href="/static/logo.svg?v20210804" />
|
||||
</head>
|
||||
|
||||
<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 "/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 "/nat"}} active{{end}}' href="/nat"><i class="exchange icon"></i>{{tr "NAT"}}</a>
|
||||
<a class='item{{if eq .MatchedPath "/setting"}} active{{end}}' href="/setting">
|
||||
<i class="settings icon"></i>{{tr "Settings"}}
|
||||
</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>
|
||||
</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">
|
||||
<label>{{tr "DDNSDomain"}}</label>
|
||||
<input type="text" name="DDNSDomain" placeholder="{{tr "DDNSDomain"}}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{tr "DDNSProfile"}}</label>
|
||||
<input type="text" name="DDNSProfile" placeholder="{{tr "DDNSProfile"}}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{tr "Note"}}</label>
|
||||
<textarea name="Note"></textarea>
|
||||
@@ -45,7 +61,7 @@
|
||||
<div class="ui message">
|
||||
{{if .Conf.GRPCHost}}
|
||||
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 hostSecret"></code> <code class="command">{{if .Conf.TLS}}--tls{{end}}</code>
|
||||
{{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"}}
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{.Conf.Language}}">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Redirecting..</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p>If you are not redirected, please click <a href="{{.URL}}">here</a>.</p>
|
||||
<script>window.location.href = "{{.URL}}"</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
{{end}}
|
||||
+11
-2
@@ -30,6 +30,8 @@
|
||||
<th>{{tr "VersionNumber"}}</th>
|
||||
<th>{{tr "HideForGuest"}}</th>
|
||||
<th>{{tr "EnableDDNS"}}</th>
|
||||
<th>{{tr "EnableIPv4"}}</th>
|
||||
<th>{{tr "EnableIpv6"}}</th>
|
||||
<th>{{tr "DDNSDomain"}}</th>
|
||||
<th>{{tr "Secret"}}</th>
|
||||
<th>{{tr "OneKeyInstall"}}</th>
|
||||
@@ -48,6 +50,8 @@
|
||||
<td>{{$server.Host.Version}}</td>
|
||||
<td>{{$server.HideForGuest}}</td>
|
||||
<td>{{$server.EnableDDNS}}</td>
|
||||
<td>{{$server.EnableIPv4}}</td>
|
||||
<td>{{$server.EnableIpv6}}</td>
|
||||
<td>{{$server.DDNSDomain}}</td>
|
||||
<td>
|
||||
<button class="ui icon green mini button" data-clipboard-text="{{$server.Secret}}" data-tooltip="{{tr "ClickToCopy"}}">
|
||||
@@ -56,7 +60,7 @@
|
||||
</td>
|
||||
<td>
|
||||
<button class="ui icon green mini button"
|
||||
data-clipboard-text="{{if $.Conf.GRPCHost}}{{if eq $.Conf.Language "zh-CN"}}curl -L https://raw.githubusercontent.com/naiba/nezha/master/script/install.sh{{else}}curl -L https://raw.githubusercontent.com/naiba/nezha/master/script/install_en.sh{{end}} -o nezha.sh && chmod +x nezha.sh && sudo ./nezha.sh 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-clipboard-text="{{if $.Conf.GRPCHost}}{{if eq $.Conf.Language "zh-CN"}}curl -L https://raw.githubusercontent.com/naiba/nezha/master/script/install.sh{{else}}curl -L https://raw.githubusercontent.com/naiba/nezha/master/script/install_en.sh{{end}} -o nezha.sh && chmod +x nezha.sh && ./nezha.sh 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="linux icon"></i>
|
||||
</button>
|
||||
@@ -65,6 +69,11 @@
|
||||
data-tooltip="{{tr "ClickToCopy"}}">
|
||||
<i class="windows icon"></i>
|
||||
</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 style="word-break: break-word;white-space: pre-wrap;">{{$server.Note}}</td>
|
||||
<td>
|
||||
@@ -89,7 +98,7 @@
|
||||
</div>
|
||||
{{template "component/server" .}}
|
||||
{{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>
|
||||
var clipboard = new ClipboardJS('.ui.icon.green.mini.button');
|
||||
const checkBoxList = document.querySelectorAll('tbody > tr > td > input.nezha-servers[type=checkbox]')
|
||||
|
||||
+11
-2
@@ -70,18 +70,24 @@
|
||||
<input type="text" name="IPChangeNotificationTag" placeholder="" value="{{.Conf.IPChangeNotificationTag}}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="ui nf-ssl checkbox ip-change">
|
||||
<div class="ui checkbox ip-change">
|
||||
<input name="EnableIPChangeNotification" type="checkbox" tabindex="0" class="hidden">
|
||||
<label>{{tr "Enable"}}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="ui nf-ssl checkbox plain-ip">
|
||||
<div class="ui checkbox plain-ip">
|
||||
<input name="EnablePlainIPInNotification" type="checkbox" tabindex="0" class="hidden">
|
||||
<label>{{tr "NotificationMessagesDoNotHideIP"}}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="ui checkbox disable-switch-template">
|
||||
<input name="DisableSwitchTemplateInFrontend" type="checkbox" tabindex="0" class="hidden">
|
||||
<label>{{tr "DisableSwitchTemplateInFrontend"}}</label>
|
||||
</div>
|
||||
</div>
|
||||
<button class="ui button" type="submit">{{tr "Save"}}</button>
|
||||
</form>
|
||||
</div>
|
||||
@@ -128,5 +134,8 @@
|
||||
{{if .Conf.EnablePlainIPInNotification}}
|
||||
$('.checkbox.plain-ip').checkbox('set checked')
|
||||
{{ end }}
|
||||
{{if .Conf.DisableSwitchTemplateInFrontend }}
|
||||
$('.checkbox.disable-switch-template').checkbox('set checked')
|
||||
{{ end }}
|
||||
</script>
|
||||
{{end}}
|
||||
|
||||
+4
-8
@@ -1,16 +1,14 @@
|
||||
{{define "dashboard-default/terminal"}}
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{.Conf.Language}}">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>tty@{{.ServerName}} - {{.Title}}</title>
|
||||
<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>
|
||||
|
||||
<style>
|
||||
html,
|
||||
body,
|
||||
@@ -25,12 +23,11 @@
|
||||
background-color: black;
|
||||
}
|
||||
</style>
|
||||
|
||||
<body onresize="onResize()">
|
||||
<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://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/xterm/4.11.0/addons/attach/xterm-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/[email protected]/lib/xterm.js"></script>
|
||||
<script src="https://unpkg.com/@xterm/addon-attach@0.11.0/lib/addon-attach.js"></script>
|
||||
<script src="https://unpkg.com/@xterm/addon-fit@0.10.0/lib/addon-fit.js"></script>
|
||||
<script>
|
||||
let sendResizing = false;
|
||||
|
||||
@@ -93,6 +90,5 @@
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
{{end}}
|
||||
+3
-2
@@ -1,4 +1,5 @@
|
||||
{{define "theme-angel-kanade/footer"}}
|
||||
</div>
|
||||
<div class="ui inverted vertical footer segment">
|
||||
<div class="ui center aligned is-size-7 container">
|
||||
<b>© <a style="color: white;" href="/">{{.Conf.Site.Brand}}</a></b> |
|
||||
@@ -12,13 +13,13 @@
|
||||
<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="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>
|
||||
(function () {
|
||||
updateLang({{.LANG }});
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
{{end}}
|
||||
+104
-100
@@ -3,118 +3,119 @@
|
||||
{{if ts .CustomCode}} {{.CustomCode|safe}} {{end}}
|
||||
{{template "theme-angel-kanade/menu" .}}
|
||||
<div class="nb-container">
|
||||
<div class="ui container">
|
||||
<div id="app">
|
||||
<div class="ui styled fluid accordion" v-for="group in groups">
|
||||
<div class="active title">
|
||||
<i class="dropdown icon"></i>
|
||||
@#(group.Tag!==''?group.Tag:'{{tr "Default"}}')#@
|
||||
</div>
|
||||
<div class="active content">
|
||||
<div class="ui four stackable status cards">
|
||||
<div v-for="server in group.data" :id="server.ID" class="ui card">
|
||||
<div class="content" v-if="server.Host" style="margin-top: 10px; padding-bottom: 5px">
|
||||
<div class="header">
|
||||
<img v-if="server.Host.CountryCode" style="border-radius: 50%;box-shadow:-1px -1px 2px #eee, 1px 1px 2px #000;width:19px;" :src="'https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/flag-icon-css/4.1.5/flags/1x1/'+server.Host.CountryCode + '.svg'" alt="国家"/> <i v-if='server.Host.Platform == "darwin"'
|
||||
class="apple icon"></i><i v-else-if='isWindowsPlatform(server.Host.Platform)'
|
||||
class="windows icon"></i><i v-else :class="'fl-' + getFontLogoClass(server.Host.Platform)"></i>
|
||||
@#server.Name + (server.live?'':'[{{tr "Offline"}}]')#@
|
||||
<i class="nezha-secondary-font info circle icon" style="height: 28px"></i>
|
||||
<div class="ui content popup" style="margin-bottom: 0">
|
||||
{{tr "Platform"}}: @#server.Host.Platform#@-@#server.Host.PlatformVersion#@
|
||||
[<span
|
||||
v-if="server.Host.Virtualization">@#server.Host.Virtualization#@:</span>@#server.Host.Arch#@]<br />
|
||||
CPU: @#server.Host.CPU#@<br />
|
||||
{{tr "DiskUsed"}}:
|
||||
@#formatByteSize(server.State.DiskUsed)#@/@#formatByteSize(server.Host.DiskTotal)#@<br />
|
||||
{{tr "MemUsed"}}:
|
||||
@#formatByteSize(server.State.MemUsed)#@/@#formatByteSize(server.Host.MemTotal)#@<br />
|
||||
{{tr "SwapUsed"}}:
|
||||
@#formatByteSize(server.State.SwapUsed)#@/@#formatByteSize(server.Host.SwapTotal)#@<br />
|
||||
{{tr "NetTransfer"}}: <i
|
||||
class="arrow alternate circle down outline icon"></i>@#formatByteSize(server.State.NetInTransfer)#@<i
|
||||
class="arrow alternate circle up outline icon"></i>@#formatByteSize(server.State.NetOutTransfer)#@<br />
|
||||
{{tr "Load"}}: @# toFixed2(server.State.Load1) #@/@# toFixed2(server.State.Load5) #@/@#
|
||||
toFixed2(server.State.Load15) #@<br />
|
||||
{{tr "ProcessCount"}}: @# server.State.ProcessCount #@<br />
|
||||
{{tr "ConnCount"}}: TCP @# server.State.TcpConnCount #@ / UDP @# server.State.UdpConnCount #@<br />
|
||||
{{tr "BootTime"}}: @# formatTimestamp(server.Host.BootTime) #@<br />
|
||||
{{tr "LastActive"}}: @# new Date(server.LastActive).toLocaleString() #@<br />
|
||||
{{tr "Version"}}: @#server.Host.Version#@<br />
|
||||
</div>
|
||||
<div class="ui divider" style="margin-bottom: 5px"></div>
|
||||
<div class="ui container">
|
||||
<div class="ui styled fluid accordion" v-for="group in groups">
|
||||
<div class="active title">
|
||||
<i class="dropdown icon"></i>
|
||||
@#(group.Tag!==''?group.Tag:'{{tr "Default"}}')#@
|
||||
</div>
|
||||
<div class="active content">
|
||||
<div class="ui four stackable status cards">
|
||||
<div v-for="server in group.data" :id="server.ID" class="ui card">
|
||||
<div class="content" v-if="server.Host" style="margin-top: 10px; padding-bottom: 5px">
|
||||
<div class="header">
|
||||
<img v-if="server.Host.CountryCode" style="border-radius: 50%;box-shadow:-1px -1px 2px #eee, 1px 1px 2px #000;width:19px;" :src="'https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/flag-icon-css/4.1.5/flags/1x1/'+server.Host.CountryCode + '.svg'" alt="国家"/> <i v-if='server.Host.Platform == "darwin"'
|
||||
class="apple icon"></i><i v-else-if='isWindowsPlatform(server.Host.Platform)'
|
||||
class="windows icon"></i><i v-else :class="'fl-' + getFontLogoClass(server.Host.Platform)"></i>
|
||||
@#server.Name + (server.live?'':'[{{tr "Offline"}}]')#@
|
||||
<i class="nezha-secondary-font info circle icon" style="height: 28px"></i>
|
||||
<div class="ui content popup" style="margin-bottom: 0">
|
||||
{{tr "Platform"}}: @#server.Host.Platform#@-@#server.Host.PlatformVersion#@
|
||||
[<span
|
||||
v-if="server.Host.Virtualization">@#server.Host.Virtualization#@:</span>@#server.Host.Arch#@]<br />
|
||||
CPU: @#server.Host.CPU#@<br />
|
||||
<template v-if="server.Host.GPU">
|
||||
GPU: @#server.Host.GPU#@
|
||||
</template>
|
||||
{{tr "DiskUsed"}}:
|
||||
@#formatByteSize(server.State.DiskUsed)#@/@#formatByteSize(server.Host.DiskTotal)#@<br />
|
||||
{{tr "MemUsed"}}:
|
||||
@#formatByteSize(server.State.MemUsed)#@/@#formatByteSize(server.Host.MemTotal)#@<br />
|
||||
{{tr "SwapUsed"}}:
|
||||
@#formatByteSize(server.State.SwapUsed)#@/@#formatByteSize(server.Host.SwapTotal)#@<br />
|
||||
{{tr "NetTransfer"}}: <i
|
||||
class="arrow alternate circle down outline icon"></i>@#formatByteSize(server.State.NetInTransfer)#@<i
|
||||
class="arrow alternate circle up outline icon"></i>@#formatByteSize(server.State.NetOutTransfer)#@<br />
|
||||
{{tr "Load"}}: @# toFixed2(server.State.Load1) #@/@# toFixed2(server.State.Load5) #@/@#
|
||||
toFixed2(server.State.Load15) #@<br />
|
||||
{{tr "ProcessCount"}}: @# server.State.ProcessCount #@<br />
|
||||
{{tr "ConnCount"}}: TCP @# server.State.TcpConnCount #@ / UDP @# server.State.UdpConnCount #@<br />
|
||||
{{tr "BootTime"}}: @# formatTimestamp(server.Host.BootTime) #@<br />
|
||||
{{tr "LastActive"}}: @# new Date(server.LastActive).toLocaleString() #@<br />
|
||||
{{tr "Version"}}: @#server.Host.Version#@<br />
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="ui grid">
|
||||
<div class="three wide column">CPU</div>
|
||||
<div class="thirteen wide column">
|
||||
<div :class="formatPercent(server.live,server.State.CPU, 100).class">
|
||||
<div class="bar" :style="formatPercent(server.live,server.State.CPU, 100).style">
|
||||
<small>@#formatPercent(server.live,server.State.CPU,100).percent#@%</small>
|
||||
</div>
|
||||
<div class="ui divider" style="margin-bottom: 5px"></div>
|
||||
</div>
|
||||
<div class="description">
|
||||
<div class="ui grid">
|
||||
<div class="three wide column">CPU</div>
|
||||
<div class="thirteen wide column">
|
||||
<div :class="formatPercent(server.live,server.State.CPU, 100).class">
|
||||
<div class="bar" :style="formatPercent(server.live,server.State.CPU, 100).style">
|
||||
<small>@#formatPercent(server.live,server.State.CPU,100).percent#@%</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="three wide column">{{tr "MemUsed"}}</div>
|
||||
<div class="thirteen wide column">
|
||||
<div :class="formatPercent(server.live,server.State.MemUsed, server.Host.MemTotal).class">
|
||||
<div class="bar"
|
||||
:style="formatPercent(server.live,server.State.MemUsed, server.Host.MemTotal).style">
|
||||
<small>@#parseInt(server.State?server.State.MemUsed/server.Host.MemTotal*100:0)#@%</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="three wide column">{{tr "MemUsed"}}</div>
|
||||
<div class="thirteen wide column">
|
||||
<div :class="formatPercent(server.live,server.State.MemUsed, server.Host.MemTotal).class">
|
||||
<div class="bar"
|
||||
:style="formatPercent(server.live,server.State.MemUsed, server.Host.MemTotal).style">
|
||||
<small>@#parseInt(server.State?server.State.MemUsed/server.Host.MemTotal*100:0)#@%</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="three wide column">{{tr "SwapUsed"}}</div>
|
||||
<div class="thirteen wide column">
|
||||
<div :class="formatPercent(server.live,server.State.SwapUsed, server.Host.SwapTotal).class">
|
||||
<div class="bar"
|
||||
:style="formatPercent(server.live,server.State.SwapUsed, server.Host.SwapTotal).style">
|
||||
<small>@#parseInt(server.State?server.State.SwapUsed/server.Host.SwapTotal*100:0)#@%</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="three wide column">{{tr "SwapUsed"}}</div>
|
||||
<div class="thirteen wide column">
|
||||
<div :class="formatPercent(server.live,server.State.SwapUsed, server.Host.SwapTotal).class">
|
||||
<div class="bar"
|
||||
:style="formatPercent(server.live,server.State.SwapUsed, server.Host.SwapTotal).style">
|
||||
<small>@#parseInt(server.State?server.State.SwapUsed/server.Host.SwapTotal*100:0)#@%</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="three wide column">{{tr "NetSpeed"}}</div>
|
||||
<div class="thirteen wide column">
|
||||
<i class="arrow alternate circle down outline icon"></i>
|
||||
@#formatByteSize(server.State.NetInSpeed)#@/s
|
||||
<i class="arrow alternate circle up outline icon"></i>
|
||||
@#formatByteSize(server.State.NetOutSpeed)#@/s
|
||||
</div>
|
||||
<div class="three wide column">{{tr "NetTransfer"}}</div>
|
||||
<div class="thirteen wide column">
|
||||
<i class="arrow circle down icon"></i>
|
||||
@#formatByteSize(server.State.NetInTransfer)#@
|
||||
|
||||
<i class="arrow circle up icon"></i>
|
||||
@#formatByteSize(server.State.NetOutTransfer)#@
|
||||
</div>
|
||||
<div class="three wide column">{{tr "DiskUsed"}}</div>
|
||||
<div class="thirteen wide column">
|
||||
<div :class="formatPercent(server.live,server.State.DiskUsed, server.Host.DiskTotal).class">
|
||||
<div class="bar"
|
||||
:style="formatPercent(server.live,server.State.DiskUsed, server.Host.DiskTotal).style">
|
||||
<small>@#parseInt(server.State?server.State.DiskUsed/server.Host.DiskTotal*100:0)#@%</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="three wide column">{{tr "NetSpeed"}}</div>
|
||||
<div class="thirteen wide column">
|
||||
<i class="arrow alternate circle down outline icon"></i>
|
||||
@#formatByteSize(server.State.NetInSpeed)#@/s
|
||||
<i class="arrow alternate circle up outline icon"></i>
|
||||
@#formatByteSize(server.State.NetOutSpeed)#@/s
|
||||
</div>
|
||||
<div class="three wide column">{{tr "NetTransfer"}}</div>
|
||||
<div class="thirteen wide column">
|
||||
<i class="arrow circle down icon"></i>
|
||||
@#formatByteSize(server.State.NetInTransfer)#@
|
||||
|
||||
<i class="arrow circle up icon"></i>
|
||||
@#formatByteSize(server.State.NetOutTransfer)#@
|
||||
</div>
|
||||
<div class="three wide column">{{tr "DiskUsed"}}</div>
|
||||
<div class="thirteen wide column">
|
||||
<div :class="formatPercent(server.live,server.State.DiskUsed, server.Host.DiskTotal).class">
|
||||
<div class="bar"
|
||||
:style="formatPercent(server.live,server.State.DiskUsed, server.Host.DiskTotal).style">
|
||||
<small>@#parseInt(server.State?server.State.DiskUsed/server.Host.DiskTotal*100:0)#@%</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="three wide column">{{tr "Info"}}</div>
|
||||
<div class="thirteen wide column">
|
||||
<i class="bi bi-cpu-fill" style="font-size: 1.1rem; color: #4a86e8;"></i> @#getCoreAndGHz(server.Host.CPU)#@
|
||||
|
||||
<i class="bi bi-memory" style="font-size: 1.1rem; color: #00ac0d;"></i> @#getByteToGB(server.Host.MemTotal)#@
|
||||
|
||||
<i class="bi bi-hdd-rack-fill" style="font-size: 1.1rem; color: #980000"></i> @#getByteToGB(server.Host.DiskTotal)#@
|
||||
</div>
|
||||
<div class="three wide column">{{tr "Uptime"}}</div>
|
||||
<div class="thirteen wide column">
|
||||
<i class="clock icon"></i>@#secondToDate(server.State.Uptime)#@
|
||||
</div>
|
||||
</div>
|
||||
<div class="three wide column">{{tr "Info"}}</div>
|
||||
<div class="thirteen wide column">
|
||||
<i class="bi bi-cpu-fill" style="font-size: 1.1rem; color: #4a86e8;"></i> @#getCoreAndGHz(server.Host.CPU)#@
|
||||
|
||||
<i class="bi bi-memory" style="font-size: 1.1rem; color: #00ac0d;"></i> @#getByteToGB(server.Host.MemTotal)#@
|
||||
|
||||
<i class="bi bi-hdd-rack-fill" style="font-size: 1.1rem; color: #980000"></i> @#getByteToGB(server.Host.DiskTotal)#@
|
||||
</div>
|
||||
<div class="three wide column">{{tr "Uptime"}}</div>
|
||||
<div class="thirteen wide column">
|
||||
<i class="clock icon"></i>@#secondToDate(server.State.Uptime)#@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content" v-else>
|
||||
<p>@#server.Name#@</p>
|
||||
<p>{{tr "ServerIsOffline"}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content" v-else>
|
||||
<p>@#server.Name#@</p>
|
||||
<p>{{tr "ServerIsOffline"}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -129,10 +130,13 @@
|
||||
el: '#app',
|
||||
delimiters: ['@#', '#@'],
|
||||
data: {
|
||||
defaultTemplate: {{.Conf.Site.Theme}},
|
||||
templates: {{.Themes}},
|
||||
data: initData,
|
||||
groups: [],
|
||||
cache: [],
|
||||
},
|
||||
mixins: [mixinsVue],
|
||||
created() {
|
||||
this.group()
|
||||
},
|
||||
|
||||
+15
@@ -1,4 +1,5 @@
|
||||
{{define "theme-angel-kanade/menu"}}
|
||||
<div id="app">
|
||||
<div class="ui large top fixed menu nb-menu">
|
||||
<div class="ui container">
|
||||
<a class="item" href="/">
|
||||
@@ -15,6 +16,20 @@
|
||||
{{else}}
|
||||
<a class='item{{if eq .MatchedPath "/"}} active{{end}}' href="/"><i class="home icon"></i>{{tr "Home"}}</a>
|
||||
<a class='item{{if eq .MatchedPath "/service"}} active{{end}}' href="/service"><i class="rss icon"></i>{{tr "Services"}}</a>
|
||||
{{ if not .Conf.DisableSwitchTemplateInFrontend }}
|
||||
<div class="item ui simple dropdown">
|
||||
<div class="text"><i class="bi bi-incognito icon" style="margin-right:3px;"></i>{{tr "Template" }}<i class="dropdown icon" style="margin-right:0px;"></i></div>
|
||||
<div class="menu">
|
||||
<a v-for="(item, index) in adaptedTemplates" :key="index" @click="toggleTemplate(item.key)" class="item">
|
||||
<i :class="item.icon + ' icon'"></i>@#item.name#@
|
||||
<i class="check icon" v-if="preferredTemplate === item.key"></i>
|
||||
</a>
|
||||
<a v-if="defaultTemplate === 'custom'" @click="toggleTemplate('custom')" class="item">
|
||||
<i class="expand icon"></i>Custom
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{end}}
|
||||
<div class="right menu">
|
||||
<div class="item">
|
||||
|
||||
+11
-1
@@ -76,10 +76,20 @@
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{template "theme-angel-kanade/footer" .}}
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#app',
|
||||
delimiters: ['@#', '#@'],
|
||||
data: {
|
||||
defaultTemplate: {{.Conf.Site.Theme}},
|
||||
templates: {{.Themes}}
|
||||
},
|
||||
mixins: [mixinsVue]
|
||||
})
|
||||
</script>
|
||||
{{end}}
|
||||
+158
-126
@@ -20,101 +20,101 @@
|
||||
</head>
|
||||
|
||||
<body data-theme="light" data-gridlist="grid">
|
||||
<header>
|
||||
<section class="nav-bar clearfix">
|
||||
<figure class="logo">
|
||||
<a href="/">
|
||||
<img src="/static/logo.svg?v20210804" alt='{{tr "NezhaMonitoring"}}' width="50" height="50">
|
||||
</a>
|
||||
<a href="/">{{.Conf.Site.Brand}}</a>
|
||||
</figure>
|
||||
<div class="icon-container">
|
||||
<div class="row cf">
|
||||
<div class="three col">
|
||||
<div class="hamburger" id="hamburger-icon"><span class="line"></span><span
|
||||
class="line"></span><span class="line"></span></div>
|
||||
<header>
|
||||
<section class="nav-bar clearfix">
|
||||
<figure class="logo">
|
||||
<a href="/">
|
||||
<img src="/static/logo.svg?v20210804" alt='{{tr "NezhaMonitoring"}}' width="50" height="50">
|
||||
</a>
|
||||
<a href="/">{{.Conf.Site.Brand}}</a>
|
||||
</figure>
|
||||
<div class="icon-container">
|
||||
<div class="row cf">
|
||||
<div class="three col">
|
||||
<div class="hamburger" id="hamburger-icon"><span class="line"></span><span
|
||||
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>
|
||||
<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" 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 class="network-chart" style="height: 800px;overflow: hidden">
|
||||
<div id="monitor-info-container" style="height: 520px;max-width: 1400px">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="network-chart" style="height: 800px;overflow: hidden">
|
||||
<div id="monitor-info-container" style="height: 520px;max-width: 1400px">
|
||||
<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>
|
||||
</div>
|
||||
<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>
|
||||
</main>
|
||||
|
||||
<section class="dark-light-toggle">
|
||||
<label class="switcher">
|
||||
<input type="checkbox" name="theme" id="dark-light" />
|
||||
<div>
|
||||
<i class="fas fa-adjust"></i>
|
||||
</div>
|
||||
</label>
|
||||
</section>
|
||||
<section class="dark-light-toggle">
|
||||
<label class="switcher">
|
||||
<input type="checkbox" name="theme" id="dark-light" />
|
||||
<div>
|
||||
<i class="fas fa-adjust"></i>
|
||||
</div>
|
||||
</label>
|
||||
</section>
|
||||
|
||||
<!-- Back to top button -->
|
||||
<a id="back-to-top"></a>
|
||||
<!-- Back to top button -->
|
||||
<a id="back-to-top"></a>
|
||||
|
||||
<footer>
|
||||
<div class="footer-container">
|
||||
<div><a href="https://github.com/naiba/nezha" target="_blank">Powered by {{tr "NezhaMonitoring"}} · {{.Version}}</a>
|
||||
<p>© <span id="copyright-date">
|
||||
<footer>
|
||||
<div class="footer-container">
|
||||
<div><a href="https://github.com/naiba/nezha" target="_blank">Powered by {{tr "NezhaMonitoring"}} ·
|
||||
{{.Version}}</a>
|
||||
<p>© <span id="copyright-date">
|
||||
<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>
|
||||
</p>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</footer>
|
||||
|
||||
<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/limonte-sweetalert2/11.4.4/sweetalert2.all.min.js"></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/limonte-sweetalert2/11.4.4/sweetalert2.all.min.js"></script>
|
||||
|
||||
<script>
|
||||
const monitorInfo = JSON.parse('{{.MonitorInfos}}');
|
||||
<script>
|
||||
const monitorInfo = JSON.parse('{{.MonitorInfos}}');
|
||||
const initData = JSON.parse('{{.Servers}}').servers;
|
||||
let MaxTCPPingValue = {{.MaxTCPPingValue}};
|
||||
if (MaxTCPPingValue == null) {
|
||||
MaxTCPPingValue = 300;
|
||||
}
|
||||
let MaxTCPPingValue = {{.Conf.MaxTCPPingValue}};
|
||||
// 基于准备好的dom,初始化echarts实例
|
||||
var myChart = echarts.init(document.getElementById('monitor-info-container'));
|
||||
const myChart = echarts.init(document.getElementById('monitor-info-container'));
|
||||
|
||||
// 使用刚指定的配置项和数据显示图表。
|
||||
var statusCards = new Vue({
|
||||
el: '#network',
|
||||
@@ -128,11 +128,11 @@
|
||||
position: function (pt) {
|
||||
return [pt[0], '10%'];
|
||||
},
|
||||
formatter: function(params){
|
||||
formatter: function (params) {
|
||||
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 />";
|
||||
})
|
||||
})
|
||||
return result;
|
||||
},
|
||||
confine: true,
|
||||
@@ -179,6 +179,10 @@
|
||||
mounted() {
|
||||
this.DarkMode();
|
||||
this.parseMonitorInfo(monitorInfo);
|
||||
window.addEventListener('resize', this.resizeHandle);
|
||||
},
|
||||
destroyed() {
|
||||
window.removeEventListener('resize', this.resizeHandle)
|
||||
},
|
||||
methods: {
|
||||
DarkMode() {
|
||||
@@ -191,78 +195,106 @@
|
||||
},
|
||||
redirectNetwork(id) {
|
||||
this.getMonitorHistory(id)
|
||||
.then(function(monitorInfo) {
|
||||
var vm = network.__vue__;
|
||||
vm.parseMonitorInfo(monitorInfo);
|
||||
})
|
||||
.catch(function(error){
|
||||
window.location.href = "/404";
|
||||
})
|
||||
.then(function (monitorInfo) {
|
||||
var vm = network.__vue__;
|
||||
vm.parseMonitorInfo(monitorInfo);
|
||||
})
|
||||
.catch(function (error) {
|
||||
window.location.href = "/404";
|
||||
})
|
||||
},
|
||||
getMonitorHistory(id) {
|
||||
return $.ajax({
|
||||
url: "/api/v1/monitor/"+id,
|
||||
return $.ajax({
|
||||
url: "/api/v1/monitor/" + id,
|
||||
method: "GET"
|
||||
});
|
||||
});
|
||||
},
|
||||
parseMonitorInfo(monitorInfo) {
|
||||
let tSeries = [];
|
||||
let tLegendData = [];
|
||||
var lcolors = ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'];
|
||||
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 data = [];
|
||||
let datal = [];
|
||||
for (let j = 0; j < monitorInfo.result[i].created_at.length; j++) {
|
||||
avgDelay = monitorInfo.result[i].avg_delay[j];
|
||||
if (avgDelay > 0.9 * MaxTCPPingValue) {
|
||||
loss += 1
|
||||
avgDelay = Math.round(monitorInfo.result[i].avg_delay[j]);
|
||||
if (avgDelay > 0 && avgDelay < MaxTCPPingValue) {
|
||||
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);
|
||||
legendName = monitorInfo.result[i].monitor_name +" "+ lossRate + "%";
|
||||
if (lossRate > 99) {
|
||||
datal = [];
|
||||
}
|
||||
legendName = monitorInfo.result[i].monitor_name + " " + lossRate + "%";
|
||||
tLegendData.push(legendName);
|
||||
tSeries.push({
|
||||
name: legendName,
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'none',
|
||||
data: data
|
||||
name: legendName,
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'none',
|
||||
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.series = tSeries;
|
||||
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.setOption(this.option);
|
||||
}
|
||||
},
|
||||
resizeHandle() {
|
||||
myChart.resize();
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
<style>
|
||||
#network {
|
||||
display: grid;
|
||||
/*grid-template-columns: repeat(5, 1fr);*/
|
||||
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
|
||||
grid-gap: 1em;
|
||||
width: 100%;
|
||||
width: calc(100vw - 6em);
|
||||
max-width: 1400px;
|
||||
margin: 1em auto;
|
||||
align-content: start;
|
||||
}
|
||||
#monitor-info-container{
|
||||
margin: 0em auto;
|
||||
align-content: start;
|
||||
background-color: #F1F1F2;
|
||||
}
|
||||
#network {
|
||||
width: calc(100vw - 6em);
|
||||
max-width: 1400px;
|
||||
margin: 1em auto;
|
||||
}
|
||||
|
||||
#monitor-info-container {
|
||||
margin: 0em auto;
|
||||
align-content: start;
|
||||
background-color: #F1F1F2;
|
||||
}
|
||||
|
||||
.server-info-container {
|
||||
font-size: .6em;
|
||||
width: fit-content;
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
||||
|
||||
</html>
|
||||
{{end}}
|
||||
|
||||
{{end}}
|
||||
+11
@@ -7,6 +7,17 @@
|
||||
{{.Version}}</small>
|
||||
</div>
|
||||
</div>
|
||||
{{ if not .Conf.DisableSwitchTemplateInFrontend }}
|
||||
<script>
|
||||
function showSwitchTemplate(list, currentBackendTheme) {
|
||||
console.log(list, currentBackendTheme);
|
||||
console.log("currentBackendTheme:",currentBackendTheme);
|
||||
}
|
||||
showSwitchTemplate({{ .Themes }}, {{ .Conf.Site.Theme }})
|
||||
</script>
|
||||
{{ end }}
|
||||
<script>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
{{end}}
|
||||
|
||||
+13
-12
@@ -7,19 +7,20 @@
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<meta content="telephone=no" name="format-detection">
|
||||
<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?v20240222">
|
||||
<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>
|
||||
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/semantic.min.js"></script>
|
||||
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/semantic.min.css">
|
||||
<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="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?v20240225"></script>
|
||||
<script src="/static/theme-default/js/mixin.js?v20240302"></script>
|
||||
</head>
|
||||
<body>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
|
||||
+612
-506
File diff suppressed because it is too large
Load Diff
+13
-7
@@ -19,14 +19,20 @@
|
||||
<a href="/service" class='item'><i class="rss icon"></i>{{tr "Services" }}</a>
|
||||
<a href="/network" class="item"><i class="bi bi-hdd-network icon"></i>{{tr "NetworkSpiter"}}</a>
|
||||
</template>
|
||||
<div class="item ui simple dropdown">
|
||||
<div class="text"><i class="bi bi-incognito icon" style="margin-right:3px;"></i>{{tr "Template" }}<i class="dropdown icon" style="margin-right:0px;"></i></div>
|
||||
<div class="menu">
|
||||
<a v-for="(value, key) in templates" :key="key" @click="toggleTemplate(key)" class="item"><i class="th large icon"></i>@#value#@
|
||||
<i class="check icon" v-if="preferredTemplate === key"></i>
|
||||
</a>
|
||||
{{ if not .Conf.DisableSwitchTemplateInFrontend }}
|
||||
<div class="item ui simple dropdown">
|
||||
<div class="text"><i class="bi bi-incognito icon" style="margin-right:3px;"></i>{{tr "Template" }}<i class="dropdown icon" style="margin-right:0px;"></i></div>
|
||||
<div class="menu">
|
||||
<a v-for="(item, index) in adaptedTemplates" :key="index" @click="toggleTemplate(item.key)" class="item">
|
||||
<i :class="item.icon + ' icon'"></i>@#item.name#@
|
||||
<i class="check icon" v-if="preferredTemplate === item.key"></i>
|
||||
</a>
|
||||
<a v-if="defaultTemplate === 'custom'" @click="toggleTemplate('custom')" class="item">
|
||||
<i class="expand icon"></i>Custom
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{if .Admin}}
|
||||
<div class="item right item-right ui simple dropdown">
|
||||
<div class="text">
|
||||
|
||||
+43
-12
@@ -17,7 +17,7 @@
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui container" style="max-width: 95vw">
|
||||
<div class="ui container">
|
||||
<div ref="chartDom" style="border-radius: 28px; margin-top: 15px;height: 520px;max-width: 1400px;overflow: hidden"></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -27,15 +27,13 @@
|
||||
<script>
|
||||
const monitorInfo = JSON.parse('{{.MonitorInfos}}');
|
||||
const initData = JSON.parse('{{.Servers}}').servers;
|
||||
let MaxTCPPingValue = {{.MaxTCPPingValue}};
|
||||
if (MaxTCPPingValue == null) {
|
||||
MaxTCPPingValue = 1000;
|
||||
}
|
||||
let MaxTCPPingValue = {{.Conf.MaxTCPPingValue}};
|
||||
new Vue({
|
||||
el: '#app',
|
||||
delimiters: ['@#', '#@'],
|
||||
data: {
|
||||
page: 'network',
|
||||
defaultTemplate: {{.Conf.Site.Theme}},
|
||||
templates: {{.Themes}},
|
||||
servers: initData,
|
||||
option: {
|
||||
@@ -66,6 +64,10 @@
|
||||
fontSize: 14
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '8%',
|
||||
right: '8%',
|
||||
},
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.8)',
|
||||
toolbox: {
|
||||
feature: {
|
||||
@@ -97,7 +99,11 @@
|
||||
mixins: [mixinsVue],
|
||||
mounted() {
|
||||
this.renderChart();
|
||||
this.parseMonitorInfo(monitorInfo);
|
||||
this.parseMonitorInfo(monitorInfo);
|
||||
window.addEventListener('resize', this.resizeHandle);
|
||||
},
|
||||
destroyed () {
|
||||
window.removeEventListener('resize', this.resizeHandle)
|
||||
},
|
||||
methods: {
|
||||
getFontLogoClass(str) {
|
||||
@@ -176,19 +182,36 @@
|
||||
parseMonitorInfo(monitorInfo) {
|
||||
let tSeries = [];
|
||||
let tLegendData = [];
|
||||
var lcolors = ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'];
|
||||
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 data = [];
|
||||
let datal = [];
|
||||
for (let j = 0; j < monitorInfo.result[i].created_at.length; j++) {
|
||||
avgDelay = Math.round(monitorInfo.result[i].avg_delay[j]);
|
||||
if (avgDelay > 0.9 * MaxTCPPingValue) {
|
||||
loss += 1
|
||||
}
|
||||
if (avgDelay > 0) {
|
||||
if (avgDelay > 0 && avgDelay < MaxTCPPingValue) {
|
||||
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);
|
||||
if (lossRate > 99) {
|
||||
datal = [];
|
||||
}
|
||||
legendName = monitorInfo.result[i].monitor_name +" "+ lossRate + "%";
|
||||
tLegendData.push(legendName);
|
||||
tSeries.push({
|
||||
@@ -197,10 +220,15 @@
|
||||
smooth: true,
|
||||
symbol: 'none',
|
||||
data: data,
|
||||
markLine: {
|
||||
symbol: "none",
|
||||
symbolSize :0,
|
||||
data: datal
|
||||
},
|
||||
markPoint: {
|
||||
data: [
|
||||
{ type: 'max', symbol: 'pin', name: 'Max', itemStyle: { color: '#f00' } },
|
||||
{ type: 'min', symbol: 'pin', name: 'Min', itemStyle: { color: '#0f0' } }
|
||||
{ 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 }
|
||||
]
|
||||
}
|
||||
});
|
||||
@@ -218,6 +246,9 @@
|
||||
this.myChart = echarts.init(this.$refs.chartDom);
|
||||
this.myChart.setOption(this.option);
|
||||
},
|
||||
resizeHandle () {
|
||||
this.myChart.resize();
|
||||
},
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.myChart.dispose();
|
||||
|
||||
@@ -87,6 +87,7 @@
|
||||
delimiters: ['@#', '#@'],
|
||||
data: {
|
||||
page: 'service',
|
||||
defaultTemplate: {{.Conf.Site.Theme}},
|
||||
templates: {{.Themes}}
|
||||
},
|
||||
mixins: [mixinsVue]
|
||||
|
||||
+1
-1
@@ -221,7 +221,7 @@
|
||||
var disk = this.formatByteSize(server.State.DiskUsed) + '/' + this.formatByteSize(server.Host.DiskTotal);
|
||||
var upTime = this.secondToDate(server.State.Uptime);
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
+43
-13
@@ -38,7 +38,7 @@
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui container" style="max-width: 95vw">
|
||||
<div class="ui container">
|
||||
<div ref="chartDom" style="border-radius: 28px; margin-top: 15px;height: 520px;max-width: 1400px;overflow: hidden"></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -54,10 +54,7 @@
|
||||
<script>
|
||||
const monitorInfo = JSON.parse('{{.MonitorInfos}}');
|
||||
const initData = JSON.parse('{{.Servers}}').servers;
|
||||
let MaxTCPPingValue = {{.MaxTCPPingValue}};
|
||||
if (MaxTCPPingValue == null) {
|
||||
MaxTCPPingValue = 1000;
|
||||
}
|
||||
let MaxTCPPingValue = {{.Conf.MaxTCPPingValue}};
|
||||
new Vue({
|
||||
el: '#app',
|
||||
delimiters: ['@#', '#@'],
|
||||
@@ -91,6 +88,10 @@
|
||||
fontSize: 14
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '8%',
|
||||
right: '8%'
|
||||
},
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.8)',
|
||||
toolbox: {
|
||||
feature: {
|
||||
@@ -121,7 +122,11 @@
|
||||
},
|
||||
mounted() {
|
||||
this.renderChart();
|
||||
this.parseMonitorInfo(monitorInfo);
|
||||
this.parseMonitorInfo(monitorInfo);
|
||||
window.addEventListener('resize', this.resizeHandle);
|
||||
},
|
||||
resizeHandle () {
|
||||
this.myChart.resize();
|
||||
},
|
||||
methods: {
|
||||
getFontLogoClass(str) {
|
||||
@@ -200,19 +205,36 @@
|
||||
parseMonitorInfo(monitorInfo) {
|
||||
let tSeries = [];
|
||||
let tLegendData = [];
|
||||
var lcolors = ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'];
|
||||
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 data = [];
|
||||
let datal = [];
|
||||
for (let j = 0; j < monitorInfo.result[i].created_at.length; j++) {
|
||||
avgDelay = Math.round(monitorInfo.result[i].avg_delay[j]);
|
||||
if (avgDelay > 0.9 * MaxTCPPingValue) {
|
||||
loss += 1
|
||||
}
|
||||
if (avgDelay > 0) {
|
||||
if (avgDelay > 0 && avgDelay < MaxTCPPingValue) {
|
||||
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);
|
||||
if (lossRate > 99) {
|
||||
datal = [];
|
||||
}
|
||||
legendName = monitorInfo.result[i].monitor_name +" "+ lossRate + "%";
|
||||
tLegendData.push(legendName);
|
||||
tSeries.push({
|
||||
@@ -221,10 +243,15 @@
|
||||
smooth: true,
|
||||
symbol: 'none',
|
||||
data: data,
|
||||
markLine: {
|
||||
symbol: "none",
|
||||
symbolSize :0,
|
||||
data: datal
|
||||
},
|
||||
markPoint: {
|
||||
data: [
|
||||
{ type: 'max', symbol: 'pin', name: 'Max', itemStyle: { color: '#f00' } },
|
||||
{ type: 'min', symbol: 'pin', name: 'Min', itemStyle: { color: '#0f0' } }
|
||||
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 }
|
||||
]
|
||||
}
|
||||
});
|
||||
@@ -242,6 +269,9 @@
|
||||
this.myChart = echarts.init(this.$refs.chartDom);
|
||||
this.myChart.setOption(this.option);
|
||||
},
|
||||
resizeHandle () {
|
||||
this.myChart.resize();
|
||||
},
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.myChart.dispose();
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
{{define "theme-server-status/content-footer"}}
|
||||
<footer class="container" style="padding-bottom: 2rem;">
|
||||
<p style="text-align: center; font-size: 10px;">
|
||||
{{ .Conf.Site.Brand }} | Theme ServerStatus | Powered by <a target="_blank" href="https://github.com/naiba/nezha">{{tr "NezhaMonitoring"}}</a> {{.Version}}
|
||||
</p>
|
||||
<footer>
|
||||
<p>{{ .Conf.Site.Brand }} | Theme ServerStatus | Powered by <a target="_blank" href="https://github.com/naiba/nezha">{{tr "NezhaMonitoring"}}</a> {{.Version}}</p>
|
||||
</footer>
|
||||
<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">
|
||||
<i v-if="showGroup" @click="toggleView" class="show-nogroup bi bi-justify"></i>
|
||||
<i v-else @click="toggleView" class="show-group bi bi-view-stacked"></i>
|
||||
<i v-if="showGroup" @click="toggleShowGroup" class="show-nogroup bi bi-justify"></i>
|
||||
<i v-else @click="toggleShowGroup" class="show-group bi bi-view-stacked"></i>
|
||||
</span>
|
||||
<span class="setTheme">
|
||||
<i v-if="theme === 'light'" @click="setTheme('dark', true)" 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-if="theme === 'light'" @click="setTheme('dark')" class="setTheme-dark bi bi-moon-fill"></i>
|
||||
<i v-else @click="setTheme('light')" class="setTheme-light bi bi-brightness-high-fill"></i>
|
||||
</span>
|
||||
<span v-if="showGoTop" class="showGoTop">
|
||||
<i @click="goTop" class="goTop bi bi-arrow-up"></i>
|
||||
|
||||
+19
-12
@@ -21,7 +21,7 @@
|
||||
<a data-toggle="dropdown"><i class="bi bi-gear-wide-connected" style="position:relative;top:1px;margin-right:3px;font-size:1.1rem;"></i>{{tr "Feature" }}<b class="caret"></b></a>
|
||||
<ul class="dropdown-menu" style="min-width:100px;">
|
||||
<li><a href="/service"><i class="rss icon"></i>{{tr "Services" }}</a></li>
|
||||
<li><a href="/network"><i class="bi bi-hdd-network icon"></i>{{tr "NetworkSpiter"}}</a></li>
|
||||
<li><a href="/network"><i class="bi bi-hdd-network icon"></i>{{tr "NetworkSpiter"}}</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</template>
|
||||
@@ -29,17 +29,24 @@
|
||||
<li><a href="/service"><i class="rss icon"></i>{{tr "Services" }}</a></li>
|
||||
<li><a href="/network"><i class="bi bi-hdd-network icon"></i>{{tr "NetworkSpiter"}}</a></li>
|
||||
</template>
|
||||
<li class="dropdown">
|
||||
<a data-toggle="dropdown"><i class="bi bi-incognito" style="position:relative;top:1px;margin-right:3px;font-size:1.2rem;vertical-align:top;"></i>{{tr "Template" }}<b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li v-for="(value, key) in templates" :key="key">
|
||||
<a @click="toggleTemplate(key)">
|
||||
<i class="list icon"></i>@#value#@
|
||||
<i class="check icon" v-if="preferredTemplate === key"></i>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
{{ if not .Conf.DisableSwitchTemplateInFrontend }}
|
||||
<li class="dropdown">
|
||||
<a data-toggle="dropdown"><i class="bi bi-incognito" style="position:relative;top:1px;margin-right:3px;font-size:1.2rem;vertical-align:top;"></i>{{tr "Template" }}<b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li v-for="(item, index) in adaptedTemplates" :key="index">
|
||||
<a @click="toggleTemplate(item.key)">
|
||||
<i :class="item.icon + ' icon'" style="font-size:1em"></i>@#item.name#@
|
||||
<i class="check icon" v-if="preferredTemplate === item.key"></i>
|
||||
</a>
|
||||
</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>
|
||||
</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
{{if .Admin}}
|
||||
|
||||
+22
-15
@@ -6,16 +6,21 @@
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<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="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?v20240724">
|
||||
<link rel="stylesheet" href="/static/theme-server-status/css/dark.css?v20240724">
|
||||
<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 -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="/static/theme-server-status/js/html5shiv.js"></script>
|
||||
@@ -24,11 +29,13 @@
|
||||
{{if ts .CustomCode}}
|
||||
{{.CustomCode|safe}}
|
||||
{{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?v20240225"></script>
|
||||
</head>
|
||||
<body>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+29
-8
@@ -27,10 +27,10 @@
|
||||
</td>
|
||||
<td class="node-cell name center">@#node.name#@</td>
|
||||
<td class="node-cell os center">
|
||||
<i v-if='node.os == "darwin"' class="apple icon"></i>
|
||||
<i v-else-if='isWindowsPlatform(node.host.Platform)' class="windows icon"></i>
|
||||
<i v-if='isWindowsPlatform(node.host.Platform)' class="windows icon"></i>
|
||||
<i v-else-if='getFontLogoClass(node.host.Platform) == "" && node.stateuptime > 0' class="fl-tux"></i>
|
||||
<i v-else :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 style="text-align: center;" class="node-cell location">
|
||||
<i :class="'fi fi-' + node.location"></i>
|
||||
@@ -71,20 +71,28 @@
|
||||
</span>
|
||||
<span class="node-cell-expand" v-if="node.host.CPU">
|
||||
<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 class="node-cell-expand">
|
||||
<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 class="node-cell-expand">
|
||||
<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 class="node-cell-expand">
|
||||
<span v-if="node.host.SwapTotal > 0" class="node-cell-expand">
|
||||
<span class="node-cell-expand-label">{{tr "SwapUsed"}}:</span>
|
||||
@#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 class="node-cell-expand">
|
||||
<span class="node-cell-expand-label">{{tr "NetTransfer"}}:</span>
|
||||
@@ -117,6 +125,19 @@
|
||||
<span class="node-cell-expand-label">{{tr "Uptime"}}:</span>
|
||||
@#node.uptime#@
|
||||
</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-label">{{tr "Version"}}:</span>
|
||||
@#node.host.Version#@
|
||||
|
||||
+29
-8
@@ -30,10 +30,10 @@
|
||||
</td>
|
||||
<td class="node-cell name center">@#node.name#@</td>
|
||||
<td class="node-cell os center">
|
||||
<i v-if='node.os == "darwin"' class="apple icon"></i>
|
||||
<i v-else-if='isWindowsPlatform(node.host.Platform)' class="windows icon"></i>
|
||||
<i v-if='isWindowsPlatform(node.host.Platform)' class="windows icon"></i>
|
||||
<i v-else-if='getFontLogoClass(node.host.Platform) == "" && node.stateuptime > 0' class="fl-tux"></i>
|
||||
<i v-else :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 style="text-align: center;" class="node-cell location">
|
||||
<i :class="'fi fi-' + node.location"></i>
|
||||
@@ -74,20 +74,28 @@
|
||||
</span>
|
||||
<span class="node-cell-expand" v-if="node.host.CPU">
|
||||
<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 class="node-cell-expand">
|
||||
<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 class="node-cell-expand">
|
||||
<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 class="node-cell-expand">
|
||||
<span v-if="node.host.SwapTotal > 0" class="node-cell-expand">
|
||||
<span class="node-cell-expand-label">{{tr "SwapUsed"}}:</span>
|
||||
@#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 class="node-cell-expand">
|
||||
<span class="node-cell-expand-label">{{tr "NetTransfer"}}:</span>
|
||||
@@ -120,6 +128,19 @@
|
||||
<span class="node-cell-expand-label">{{tr "Uptime"}}:</span>
|
||||
@#node.uptime#@
|
||||
</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-label">{{tr "Version"}}:</span>
|
||||
@#node.host.Version#@
|
||||
|
||||
+295
-29
@@ -14,6 +14,19 @@
|
||||
{{template "theme-server-status/home-group-false" .}}
|
||||
</section>
|
||||
</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" .}}
|
||||
</div>
|
||||
<script>
|
||||
@@ -22,27 +35,207 @@
|
||||
delimiters: ['@#', '#@'],
|
||||
data: {
|
||||
page: 'index',
|
||||
defaultTemplate: {{.Conf.Site.Theme}},
|
||||
templates: {{.Themes}},
|
||||
cache: [],
|
||||
servers: [],
|
||||
nodesTag: [],
|
||||
nodesNoTag: [],
|
||||
chartDataList: []
|
||||
chartDataList: [],
|
||||
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],
|
||||
created() {
|
||||
initData = JSON.parse('{{.Servers}}').servers;
|
||||
this.servers = JSON.parse('{{.Servers}}').servers;
|
||||
if(this.showGroup) {
|
||||
this.nodesTag = this.groupingData(this.handleNodes(initData),"Tag");
|
||||
this.nodesTag = this.groupingData(this.handleNodes(this.servers),"Tag");
|
||||
} else {
|
||||
this.nodesNoTag = this.handleNodes(initData);
|
||||
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() {
|
||||
// 初始化时建立WebSocket连接
|
||||
this.connect();
|
||||
// 监听页面可见性变化
|
||||
document.addEventListener("visibilitychange", () => {
|
||||
if (document.visibilityState === "visible") {
|
||||
setTimeout(() => {
|
||||
if (document.hasFocus()) {
|
||||
this.connect();
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
});
|
||||
},
|
||||
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" ? '' : '';
|
||||
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) {
|
||||
return str.includes('Windows')
|
||||
},
|
||||
getPlatformName(str){
|
||||
if (str.toLowerCase().includes('opensuse')) {
|
||||
return 'openSUSE';
|
||||
}
|
||||
return str;
|
||||
},
|
||||
getFontLogoClass(str) {
|
||||
if (["almalinux",
|
||||
"alpine",
|
||||
@@ -89,6 +282,9 @@
|
||||
> -1) {
|
||||
return str;
|
||||
}
|
||||
if (str == 'darwin') {
|
||||
return 'apple';
|
||||
}
|
||||
if (['openwrt', 'linux', "immortalwrt"].indexOf(str) > -1) {
|
||||
return 'tux';
|
||||
}
|
||||
@@ -98,6 +294,9 @@
|
||||
if (str == 'arch') {
|
||||
return 'archlinux';
|
||||
}
|
||||
if (str.toLowerCase().includes('opensuse')) {
|
||||
return 'opensuse';
|
||||
}
|
||||
return '';
|
||||
},
|
||||
secondToDate(s) {
|
||||
@@ -124,6 +323,9 @@
|
||||
},
|
||||
formatPercents(live,percent) {
|
||||
//if(!live) { percent = 0; }
|
||||
if (isNaN(percent)) {
|
||||
percent = 0;
|
||||
}
|
||||
if (percent <= 0) {
|
||||
percent = 0;
|
||||
}
|
||||
@@ -155,12 +357,18 @@
|
||||
return parseFloat((bytes / Math.pow(1024, i)).toFixed(2)) + sizes[i];
|
||||
},
|
||||
connect() {
|
||||
const wsProtocol = window.location.protocol === "https:" ? "wss" : "ws"
|
||||
const ws = new WebSocket(wsProtocol + '://' + window.location.host + '/ws');
|
||||
ws.onopen = function () {
|
||||
// 如果已经存在 WebSocket 连接并且处于开启状态,则不重复建立连接
|
||||
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
||||
console.log('Closing old WebSocket connection...');
|
||||
this.ws.close();
|
||||
}
|
||||
const wsProtocol = window.location.protocol === "https:" ? "wss" : "ws";
|
||||
this.ws = new WebSocket(wsProtocol + '://' + window.location.host + '/ws');
|
||||
|
||||
this.ws.onopen = function () {
|
||||
console.log("Connection open ...")
|
||||
}
|
||||
ws.onmessage = (evt) => {
|
||||
this.ws.onmessage = (evt) => {
|
||||
let jsonData = evt.data
|
||||
const data = JSON.parse(jsonData)
|
||||
for (let i = 0; i < data.servers?.length; i++) {
|
||||
@@ -178,13 +386,13 @@
|
||||
this.nodesNoTag = this.handleNodes(data.servers);
|
||||
}
|
||||
}
|
||||
ws.onclose = () => {
|
||||
setTimeout(function () {
|
||||
this.connect()
|
||||
this.ws.onclose = () => {
|
||||
setTimeout(() => {
|
||||
this.connect();
|
||||
}, 5000);
|
||||
}
|
||||
ws.onerror = function () {
|
||||
ws.close()
|
||||
};
|
||||
this.ws.onerror = () => {
|
||||
this.ws.close()
|
||||
}
|
||||
},
|
||||
handleNodes(servers) {
|
||||
@@ -201,6 +409,7 @@
|
||||
name: server.Name,
|
||||
os: platform,
|
||||
location: server.Host.CountryCode,
|
||||
stateuptime: server.State.Uptime,
|
||||
uptime: this.secondToDate(server.State.Uptime),
|
||||
load: this.toFixed2(server.State.Load1),
|
||||
network: this.getNetworkSpeed(server.State.NetInSpeed, server.State.NetOutSpeed),
|
||||
@@ -221,6 +430,46 @@
|
||||
getNetworkSpeed(netInSpeed, 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) {
|
||||
const chartContainer = this.$refs[`chart${id}`][0];
|
||||
const chartboxShow = chartContainer.getAttribute('chartbox-show');
|
||||
@@ -259,7 +508,7 @@
|
||||
if ( chartboxShow === '0' ) return;
|
||||
}
|
||||
// 定义图表参数值
|
||||
const MaxTCPPingValue = {{.MaxTCPPingValue}} ? {{.MaxTCPPingValue}} : 300;
|
||||
const MaxTCPPingValue = {{.Conf.MaxTCPPingValue}} ? {{.Conf.MaxTCPPingValue}} : 300;
|
||||
const isMobile = this.checkIsMobile();
|
||||
const fontSize = isMobile ? 10 : 14;
|
||||
const gridLeft = isMobile ? 25 : 36;
|
||||
@@ -271,7 +520,7 @@
|
||||
const theme = localStorage.getItem("theme") ? localStorage.getItem("theme") : systemDarkMode;
|
||||
const chartTheme = theme == "dark" ? "dark" : "default";
|
||||
const fontColor = theme == "dark" ? "#f1f1f1" : "#000000";
|
||||
const backgroundColor = theme == "dark" ? "#1C1D26" : '';
|
||||
const backgroundColor = theme == "dark" ? '' : '';
|
||||
const tooltipBackgroundColor = theme == "dark" ? "#1C1D26" : '#ffffff';
|
||||
const tooltipBorderColor = theme == "dark" ? "#31363B" : "#ffffff";
|
||||
// 渲染图表
|
||||
@@ -284,24 +533,40 @@
|
||||
const xAxisData = chartData[0].created_at.map(time => new Date(time).toLocaleString());
|
||||
const seriesData = chartData.map(item => {
|
||||
let loss = 0;
|
||||
const data = item.avg_delay.map((avgDelay, index) => {
|
||||
if (avgDelay > 0.9 * MaxTCPPingValue) {
|
||||
const data = item.avg_delay.map((avgDelay, index) => {
|
||||
if(avgDelay > 0 && avgDelay < MaxTCPPingValue){
|
||||
loss += avgDelay > 0.9 * MaxTCPPingValue ? 1 : 0;
|
||||
return [item.created_at[index], avgDelay.toFixed(2)];
|
||||
}else{
|
||||
loss += 1;
|
||||
}
|
||||
return [new Date(item.created_at[index]).toLocaleString(), avgDelay];
|
||||
});
|
||||
const lossRate = ((loss / item.created_at.length) * 100).toFixed(1);
|
||||
if (!item.monitor_name.includes("%")) {
|
||||
item.monitor_name = item.monitor_name + " " + lossRate + "%";
|
||||
}
|
||||
item.monitor_name = item.monitor_name.includes("%") ? item.monitor_name : `${item.monitor_name} ${lossRate}%`;
|
||||
return {
|
||||
name: item.monitor_name,
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'none',
|
||||
data: data
|
||||
data: data,
|
||||
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 = {
|
||||
backgroundColor: backgroundColor,
|
||||
title: {
|
||||
@@ -317,8 +582,9 @@
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
data: chartData.map(item => item.monitor_name),
|
||||
data: legendData,
|
||||
show: true,
|
||||
icon: legendIcon,
|
||||
textStyle: {
|
||||
fontSize: fontSize,
|
||||
color: fontColor
|
||||
@@ -326,7 +592,9 @@
|
||||
top: legendTop,
|
||||
bottom: 0,
|
||||
left: legendLeft,
|
||||
padding: legendPadding
|
||||
padding: legendPadding,
|
||||
itemWidth: itemWidth,
|
||||
itemHeight: itemHeight,
|
||||
},
|
||||
xAxis: {
|
||||
type: 'time',
|
||||
@@ -358,7 +626,7 @@
|
||||
color: fontColor
|
||||
},
|
||||
grid: {
|
||||
top: '40',
|
||||
top: gridTop,
|
||||
left: gridLeft,
|
||||
right: gridRight
|
||||
}
|
||||
@@ -366,8 +634,7 @@
|
||||
chart.setOption(option);
|
||||
},
|
||||
reloadCharts() { // 重新加载所有图表
|
||||
const data = JSON.parse('{{.Servers}}').servers;
|
||||
data.forEach(node => {
|
||||
this.servers.forEach(node => {
|
||||
const id = node.ID;
|
||||
const chartData = this.chartDataList[id - 1];
|
||||
if (chartData) {
|
||||
@@ -380,4 +647,3 @@
|
||||
</script>
|
||||
{{template "theme-server-status/footer" .}}
|
||||
{{end}}
|
||||
|
||||
|
||||
+30
-10
@@ -21,15 +21,13 @@
|
||||
<script>
|
||||
const monitorInfo = JSON.parse('{{.MonitorInfos}}');
|
||||
const initData = JSON.parse('{{.Servers}}').servers;
|
||||
let MaxTCPPingValue = {{.MaxTCPPingValue}};
|
||||
if (MaxTCPPingValue == null) {
|
||||
MaxTCPPingValue = 1000;
|
||||
}
|
||||
let MaxTCPPingValue = {{.Conf.MaxTCPPingValue}};
|
||||
new Vue({
|
||||
el: '#app',
|
||||
delimiters: ['@#', '#@'],
|
||||
data: {
|
||||
page: 'network',
|
||||
defaultTemplate: {{.Conf.Site.Theme}},
|
||||
templates: {{.Themes}},
|
||||
servers: initData,
|
||||
option: {
|
||||
@@ -170,19 +168,36 @@
|
||||
parseMonitorInfo(monitorInfo) {
|
||||
let tSeries = [];
|
||||
let tLegendData = [];
|
||||
var lcolors = ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'];
|
||||
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 data = [];
|
||||
let datal = [];
|
||||
for (let j = 0; j < monitorInfo.result[i].created_at.length; j++) {
|
||||
avgDelay = Math.round(monitorInfo.result[i].avg_delay[j]);
|
||||
if (avgDelay > 0.9 * MaxTCPPingValue) {
|
||||
loss += 1
|
||||
}
|
||||
if (avgDelay > 0) {
|
||||
if (avgDelay > 0 && avgDelay < MaxTCPPingValue) {
|
||||
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);
|
||||
if (lossRate > 99) {
|
||||
datal = [];
|
||||
}
|
||||
legendName = monitorInfo.result[i].monitor_name +" "+ lossRate + "%";
|
||||
tLegendData.push(legendName);
|
||||
tSeries.push({
|
||||
@@ -191,10 +206,15 @@
|
||||
smooth: true,
|
||||
symbol: 'none',
|
||||
data: data,
|
||||
markLine: {
|
||||
symbol: "none",
|
||||
symbolSize :0,
|
||||
data: datal
|
||||
},
|
||||
markPoint: {
|
||||
data: [
|
||||
{ type: 'max', symbol: 'pin', name: 'Max', itemStyle: { color: '#f00' } },
|
||||
{ type: 'min', symbol: 'pin', name: 'Min', itemStyle: { color: '#0f0' } }
|
||||
{ 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 }
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
+119
-99
@@ -4,88 +4,92 @@
|
||||
{{template "theme-server-status/content-nav" .}}
|
||||
<!-- showGroup true -->
|
||||
<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" .}}
|
||||
</section>
|
||||
</template>
|
||||
<!-- showGroup false -->
|
||||
<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" .}}
|
||||
</section>
|
||||
</template>
|
||||
<section class="container content table-responsive" style="max-width: 95vw">
|
||||
{{if .CycleTransferStats}}
|
||||
<table class="table table-striped table-condensed table-hover">
|
||||
<thead>
|
||||
<tr class="node-group-tag">
|
||||
<th colspan="16" style="border:none;">
|
||||
{{tr "CycleTransferStats"}}
|
||||
</th>
|
||||
</tr>
|
||||
<tr class="node-group-cell">
|
||||
<th class="node-cell center">ID</th>
|
||||
<th class="node-cell center">{{tr "Rules"}}</th>
|
||||
<th class="node-cell center">{{tr "Server"}}</th>
|
||||
<th class="node-cell center">{{tr "From"}}</th>
|
||||
<th class="node-cell center">{{tr "To"}}</th>
|
||||
<th class="node-cell center">MAX</th>
|
||||
<th class="node-cell center">MIN</th>
|
||||
<th class="node-cell center">{{tr "NextCheck"}}</th>
|
||||
<th class="node-cell center">{{tr "CurrentUsage"}}</th>
|
||||
<th class="node-cell center">{{tr "Transleft"}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range $id, $stats := .CycleTransferStats}}
|
||||
{{range $innerId, $transfer := $stats.Transfer}}
|
||||
{{$TransLeftPercent := TransLeftPercent (UintToFloat $transfer) (UintToFloat $stats.Max)}}
|
||||
<tr>
|
||||
<td class="node-cell center">{{$id}}</td>
|
||||
<td class="node-cell center">{{$stats.Name}}</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.To|tf}}</td>
|
||||
<td class="node-cell center">{{$stats.Max|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">{{$transfer|bf}}</td>
|
||||
<td class="node-cell center">
|
||||
<div class="progress">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{end}}
|
||||
</section>
|
||||
{{if .CycleTransferStats}}
|
||||
<section class="container content table-responsive" style="max-width: 95vw">
|
||||
<table class="table table-striped table-condensed table-hover">
|
||||
<thead>
|
||||
<tr class="node-group-tag">
|
||||
<th colspan="16" style="border:none;">
|
||||
{{tr "CycleTransferStats"}}
|
||||
</th>
|
||||
</tr>
|
||||
<tr class="node-group-cell">
|
||||
<th class="node-cell center">ID</th>
|
||||
<th class="node-cell center">{{tr "Rules"}}</th>
|
||||
<th class="node-cell center">{{tr "Server"}}</th>
|
||||
<th class="node-cell center">{{tr "From"}}</th>
|
||||
<th class="node-cell center">{{tr "To"}}</th>
|
||||
<th class="node-cell center">MAX</th>
|
||||
<th class="node-cell center">MIN</th>
|
||||
<th class="node-cell center">{{tr "NextCheck"}}</th>
|
||||
<th class="node-cell center">{{tr "CurrentUsage"}}</th>
|
||||
<th class="node-cell center">{{tr "Transleft"}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range $id, $stats := .CycleTransferStats}}
|
||||
{{range $innerId, $transfer := $stats.Transfer}}
|
||||
{{$TransLeftPercent := TransLeftPercent (UintToFloat $transfer) (UintToFloat $stats.Max)}}
|
||||
<tr>
|
||||
<td class="node-cell center">{{$id}}</td>
|
||||
<td class="node-cell center">{{$stats.Name}}</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.To|tf}}</td>
|
||||
<td class="node-cell center">{{$stats.Max|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">{{$transfer|bf}}</td>
|
||||
<td class="node-cell center">
|
||||
<div class="progress">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
{{end}}
|
||||
{{template "theme-server-status/content-footer" .}}
|
||||
</div>
|
||||
<script>
|
||||
// 初始化 Tooltip
|
||||
$(document).ready(function(){
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#app',
|
||||
delimiters: ['@#', '#@'],
|
||||
data: {
|
||||
page: 'service',
|
||||
defaultTemplate: {{.Conf.Site.Theme}},
|
||||
templates: {{.Themes}},
|
||||
services: {{.Services}},
|
||||
servicesTag: [],
|
||||
servicesNoTag: [],
|
||||
servicesNoTag: []
|
||||
},
|
||||
created() {
|
||||
this.initData();
|
||||
this.servicesTag = this.groupingData(this.initData(this.services),"type");
|
||||
this.servicesNoTag = this.initData(this.services);
|
||||
},
|
||||
mounted() {
|
||||
this.initTooltip();
|
||||
},
|
||||
mixins: [mixinsVue],
|
||||
methods: {
|
||||
@@ -102,32 +106,36 @@
|
||||
}
|
||||
return n
|
||||
},
|
||||
initData() {
|
||||
// @formatter:off
|
||||
const services = []
|
||||
{{range $service := .Services}}
|
||||
services.push({
|
||||
type: '{{$service.Monitor.Type}}',
|
||||
name: '{{$service.Monitor.Name}}',
|
||||
currentUp: parseInt('{{$service.CurrentUp}}'),
|
||||
currentDown: parseInt('{{$service.CurrentDown}}'),
|
||||
totalUp: parseInt('{{$service.TotalUp}}'),
|
||||
totalDown: parseInt('{{$service.TotalDown}}'),
|
||||
delay: '{{$service.Delay}}'.replaceAll("[","").replaceAll("]","").split(" "),
|
||||
up: '{{$service.Up}}'.replaceAll("[","").replaceAll("]","").split(" "),
|
||||
down: '{{$service.Down}}'.replaceAll("[","").replaceAll("]","").split(" "),
|
||||
})
|
||||
{{end}}
|
||||
// @formatter:on
|
||||
for (let i = 0; i < services.length; i++) {
|
||||
const service = services[i];
|
||||
service.avgDelay = parseInt(service.delay[service.delay.length - 1]) + "ms"
|
||||
service.health = this.getStateInfo(this.getPercent(service.currentUp, service.currentDown))
|
||||
service.dayDetail = this.getDayTails(service)
|
||||
service.totalUpTime = this.getProgressInfo(this.getPercent(service.totalUp, service.totalDown))
|
||||
initData(services) {
|
||||
let nodes = [];
|
||||
for (let key in services) {
|
||||
const service = services[key];
|
||||
let node = {
|
||||
type: service.Monitor.Type,
|
||||
name: service.Monitor.Name,
|
||||
currentUp: parseInt(service.CurrentUp),
|
||||
currentDown: parseInt(service.CurrentDown),
|
||||
totalUp: parseInt(service.TotalUp),
|
||||
totalDown: parseInt(service.TotalDown),
|
||||
up: service.Up,
|
||||
down: service.Down,
|
||||
delay: service.Delay,
|
||||
avgDelay: parseInt(this.getAvgDelay(service.Delay)) + "ms",
|
||||
health: this.getStateInfo(this.getPercent(service.CurrentUp, service.CurrentDown)),
|
||||
totalUpTime: this.getProgressInfo(this.getPercent(service.TotalUp, service.TotalDown))
|
||||
};
|
||||
nodes.push(node);
|
||||
};
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
const node = nodes[i];
|
||||
node.dayDetail = this.getDayTails(node)
|
||||
}
|
||||
this.servicesTag = this.groupingData(services,"type");
|
||||
this.servicesNoTag = services;
|
||||
return nodes
|
||||
},
|
||||
initTooltip() {
|
||||
$(document).ready(function(){
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
});
|
||||
},
|
||||
getPercent(up, down) {
|
||||
if (!up) {
|
||||
@@ -150,24 +158,24 @@
|
||||
return this.toFixed2(currentUp / total * 100)
|
||||
},
|
||||
getDayTails(service) {
|
||||
const result = []
|
||||
const result = [];
|
||||
for (let i = 0; i < service.up.length; i++) {
|
||||
const up = service.up[i]
|
||||
const down = service.down[i]
|
||||
const delay = service.delay[i]
|
||||
let percent = this.getPercent(up, down)
|
||||
const up = service.up[i];
|
||||
const down = service.down[i];
|
||||
let delay = Number(service.delay[i]);
|
||||
let percent = this.getPercent(up, down);
|
||||
if (percent <= 0) {
|
||||
percent = 0;
|
||||
}
|
||||
let className = this.getStateInfo(percent).className
|
||||
let available = '{{tr "Availability"}}'
|
||||
let averageLatency = '{{tr "AverageLatency"}}'
|
||||
const text = `${this.beforeDay(service.up.length - i - 1)},${available}:${percent}%,${averageLatency}:${delay}ms`
|
||||
let className = this.getStateInfo(percent).className;
|
||||
let available = '{{tr "Availability"}}';
|
||||
let averageLatency = '{{tr "AverageLatency"}}';
|
||||
const text = `${this.beforeDay(service.up.length - i - 1)},${available}:${Number(percent).toFixed(3)}%,${averageLatency}:${Number(delay).toFixed(3)}ms`;
|
||||
result.push({
|
||||
text, className
|
||||
})
|
||||
});
|
||||
}
|
||||
return result
|
||||
return result;
|
||||
},
|
||||
beforeDay(days) {
|
||||
const today = new Date();
|
||||
@@ -202,12 +210,13 @@
|
||||
return result;
|
||||
},
|
||||
getProgressInfo(percent) {
|
||||
const result = this.getStateInfo(percent)
|
||||
const result = this.getStateInfo(percent);
|
||||
result.style = `width: ${parseInt(percent)}%`;
|
||||
result.percent = Number(percent).toFixed(2);
|
||||
const className = result.className;
|
||||
if (className === "good") {
|
||||
result.className = 'progress-bar progress-bar-success'
|
||||
} else if (className === "waining") {
|
||||
} else if (className === "warning") {
|
||||
result.className = 'progress-bar progress-bar-warning'
|
||||
} else if (className === "danger") {
|
||||
result.className = 'progress-bar progress-bar-danger'
|
||||
@@ -217,8 +226,19 @@
|
||||
}
|
||||
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>
|
||||
{{template "theme-server-status/footer" .}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
+10
-1
@@ -14,7 +14,7 @@ site:
|
||||
theme: "default"
|
||||
ddns:
|
||||
enable: false
|
||||
provider: "webhook"
|
||||
provider: "webhook" # 如需使用多配置功能,请把此项留空
|
||||
accessid: ""
|
||||
accesssecret: ""
|
||||
webhookmethod: ""
|
||||
@@ -22,3 +22,12 @@ ddns:
|
||||
webhookrequestbody: ""
|
||||
webhookheaders: ""
|
||||
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 http://ip-api.com/json |grep 'country' |grep -q 'China') != "" ]]; then
|
||||
echo "According to the information provided by ip-api.com, 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
|
||||
+44
-27
@@ -7,64 +7,81 @@ if($PSVersionTable.PSVersion.Major -lt 5){
|
||||
exit
|
||||
}
|
||||
$agentrepo = "nezhahq/agent"
|
||||
$nssmrepo = "nezhahq/nssm-backup"
|
||||
# x86 or x64
|
||||
# x86 or x64 or arm64
|
||||
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 {
|
||||
$file = "nezha-agent_windows_386.zip"
|
||||
}
|
||||
$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
|
||||
C:/nezha/nssm.exe stop nezha
|
||||
C:/nezha/nssm.exe remove nezha
|
||||
C:\nezha\nezha-agent.exe service uninstall
|
||||
Remove-Item "C:\nezha" -Recurse
|
||||
}
|
||||
#TLS/SSL
|
||||
Write-Host "Determining latest nezha release" -BackgroundColor DarkGreen -ForegroundColor White
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||
$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判断
|
||||
$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
|
||||
$ipapi = ""
|
||||
$region = "Unknown"
|
||||
foreach ($url in ("https://dash.cloudflare.com/cdn-cgi/trace","https://cf-ns.com/cdn-cgi/trace","https://1.0.0.1/cdn-cgi/trace")) {
|
||||
try {
|
||||
$ipapi = Invoke-RestMethod -Uri $url -TimeoutSec 5 -UseBasicParsing
|
||||
if ($ipapi -match "loc=(\w+)" ) {
|
||||
$region = $Matches[1]
|
||||
break
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-Host "Error occurred while querying $url : $_"
|
||||
}
|
||||
}
|
||||
echo $ipapi
|
||||
if($region -ne "CN"){
|
||||
$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
|
||||
}else{
|
||||
$download = "https://kkgithub.com/$agentrepo/releases/download/$agenttag/$file"
|
||||
$nssmdownload="https://kkgithub.com/$nssmrepo/releases/download/$nssmtag/nssm.zip"
|
||||
$download = "https://gitee.com/naibahq/agent/releases/download/$agenttag/$file"
|
||||
Write-Host "Location:CN,use mirror address" -BackgroundColor DarkRed -ForegroundColor Green
|
||||
}
|
||||
echo $download
|
||||
echo $nssmdownload
|
||||
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:\nssm.zip" -DestinationPath "C:\temp" -Force
|
||||
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"
|
||||
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:\nssm.zip"
|
||||
Remove-Item "C:\temp" -Recurse
|
||||
#安装部分
|
||||
C:\nezha\nssm.exe install nezha C:\nezha\nezha-agent.exe -s $server -p $key $tls -d
|
||||
C:\nezha\nssm.exe start nezha
|
||||
C:\nezha\nezha-agent.exe service install -s $server -p $key $tls
|
||||
#enjoy
|
||||
Write-Host "Enjoy It!" -BackgroundColor DarkGreen -ForegroundColor Red
|
||||
Write-Host "Enjoy It!" -BackgroundColor DarkGreen -ForegroundColor Red
|
||||
+428
-634
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user