Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
60bfb4cb65 | ||
|
|
9e730cc2cb | ||
|
|
08d9d14161 | ||
|
|
9b6d8ad0f2 | ||
|
|
bd0d355bd6 | ||
|
|
40ae067f99 | ||
|
|
11c29d14ad | ||
|
|
f4b7483807 | ||
|
|
3d6edd602c | ||
|
|
2590815a6c | ||
|
|
c20dfdc7a3 | ||
|
|
96c3fd433f | ||
|
|
45b0f1edfc | ||
|
|
61fdcd2a9e | ||
|
|
a474b8484b | ||
|
|
67b003a27d | ||
|
|
99aeda7939 | ||
|
|
d5744d0c73 | ||
|
|
e7837d8eaf | ||
|
|
aa81fc02ab | ||
|
|
f6531a52bd | ||
|
|
c58c4f866a | ||
|
|
fc9f1b6bcc | ||
|
|
be7b6e9c5e | ||
|
|
a503f0cf40 | ||
|
|
0b7f43b149 | ||
|
|
55f5c89c1c | ||
|
|
937696c26d | ||
|
|
08aaea8244 | ||
|
|
b886f70e48 | ||
|
|
12c6b8a6f6 | ||
|
|
ebcc8a6efb | ||
|
|
c4d8b63d32 | ||
|
|
318da135b9 | ||
|
|
da74bf4749 | ||
|
|
3250283ef9 | ||
|
|
2b40040ec5 | ||
|
|
60ada33ee9 | ||
|
|
7940e010e0 | ||
|
|
2df57e6249 | ||
|
|
106d58575b | ||
|
|
bdf36276da | ||
|
|
f32f127dfc | ||
|
|
f78ba281fb | ||
|
|
7f9f9a9129 | ||
|
|
b701efd9b5 | ||
|
|
511f43784e | ||
|
|
3f6112a919 | ||
|
|
23751440c6 | ||
|
|
989c9f9740 | ||
|
|
71508f8724 | ||
|
|
eb6dd2855e | ||
|
|
64da3c7438 | ||
|
|
fd85f599ba | ||
|
|
cae443d5c8 | ||
|
|
0ec1bb2c54 | ||
|
|
47f8447a22 | ||
|
|
9c986d06cb | ||
|
|
e9428d5757 | ||
|
|
daab64d232 | ||
|
|
da8fb57268 | ||
|
|
fda0dad5f1 | ||
|
|
c6fb3196ea | ||
|
|
711d162a46 | ||
|
|
d667ce8afe | ||
|
|
6abc0fce51 | ||
|
|
b1d77a1d27 | ||
|
|
c18e0e420e | ||
|
|
d9097540c3 | ||
|
|
6b05a21641 | ||
|
|
f289b76f47 | ||
|
|
89fe4ec621 | ||
|
|
fca2f60380 | ||
|
|
6a70e67ef9 | ||
|
|
22842ebc75 | ||
|
|
aef968250f | ||
|
|
100600d594 | ||
|
|
fe234f4f38 | ||
|
|
df9487b5fc | ||
|
|
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 | ||
|
|
8df863a3e0 | ||
|
|
07e0382598 | ||
|
|
8dd509aa08 | ||
|
|
99ac12c9fd | ||
|
|
ebc45f9b8f | ||
|
|
3b5ee464a7 | ||
|
|
c4b2c47beb | ||
|
|
0835f13f28 | ||
|
|
ee3cda8aed | ||
|
|
604fc7d4c0 | ||
|
|
5c549261dd | ||
|
|
00041c0ebd | ||
|
|
dbfea9a00b | ||
|
|
4e5cc0b642 | ||
|
|
fe2872bbb3 | ||
|
|
e8b208edfa | ||
|
|
7d38634933 | ||
|
|
5bd4b57cbf | ||
|
|
fdb2fd8839 | ||
|
|
f3fac9325c | ||
|
|
626613441d | ||
|
|
b86b73a433 | ||
|
|
e8b8e59bd7 | ||
|
|
c9bcba6f28 | ||
|
|
ad9dc5470a | ||
|
|
8539fb6559 | ||
|
|
11bcc47d49 | ||
|
|
bb789f7652 | ||
|
|
51c1e41123 | ||
|
|
83efdc6258 | ||
|
|
bf58ad24b5 | ||
|
|
53581d48fc | ||
|
|
b00a00e791 | ||
|
|
b91f29077c | ||
|
|
99111a3115 | ||
|
|
30652c3d79 | ||
|
|
ac0103a1ee | ||
|
|
f59b31590e | ||
|
|
1bcddf1944 | ||
|
|
d790ca04d6 | ||
|
|
2106ca41e1 | ||
|
|
927bf13ce4 | ||
|
|
859e263815 | ||
|
|
1dcd899591 | ||
|
|
3059bf2d49 | ||
|
|
74255717ef | ||
|
|
8fa59a4639 | ||
|
|
b3e38e8924 | ||
|
|
2280e0267c | ||
|
|
1871c481e4 | ||
|
|
c3d1eec6bf | ||
|
|
6fc8ec9446 | ||
|
|
52703f38cd | ||
|
|
d0707b9b05 |
@@ -0,0 +1,8 @@
|
||||
.git
|
||||
.gitignore
|
||||
docker-compose.yml
|
||||
Dockerfile
|
||||
Dockerfile.dev
|
||||
|
||||
|
||||
data/*
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
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 delete_gitee_releases(latest_id, client, uri, token):
|
||||
get_data = {
|
||||
'access_token': token
|
||||
}
|
||||
|
||||
release_info = []
|
||||
release_response = client.get(uri, json=get_data)
|
||||
if release_response.status_code == 200:
|
||||
release_info = release_response.json()
|
||||
else:
|
||||
print(
|
||||
f"Request failed with status code {release_response.status_code}")
|
||||
|
||||
release_ids = []
|
||||
for block in release_info:
|
||||
if 'id' in block:
|
||||
release_ids.append(block['id'])
|
||||
|
||||
print(f'Current release ids: {release_ids}')
|
||||
release_ids.remove(latest_id)
|
||||
|
||||
for id in release_ids:
|
||||
release_uri = f"{uri}/{id}"
|
||||
delete_data = {
|
||||
'access_token': token
|
||||
}
|
||||
delete_response = client.delete(release_uri, json=delete_data)
|
||||
if delete_response.status_code == 204:
|
||||
print(f'Successfully deleted release #{id}.')
|
||||
else:
|
||||
raise ValueError(
|
||||
f"Request failed with status code {delete_response.status_code}")
|
||||
|
||||
|
||||
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:
|
||||
success = False
|
||||
|
||||
while not success:
|
||||
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}!")
|
||||
success = True
|
||||
else:
|
||||
print(
|
||||
f"Request failed with status code {asset_api_response.status_code}")
|
||||
|
||||
# 仅保留最新 Release 以防超出 Gitee 仓库配额
|
||||
try:
|
||||
delete_gitee_releases(release_id, api_client,
|
||||
release_api_uri, access_token)
|
||||
except ValueError as e:
|
||||
print(e)
|
||||
|
||||
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()
|
||||
+122
-54
@@ -4,45 +4,139 @@ on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
goos: [linux, windows]
|
||||
goarch: [amd64]
|
||||
include:
|
||||
- goos: linux
|
||||
goarch: s390x
|
||||
- goos: linux
|
||||
goarch: arm64
|
||||
|
||||
name: Build artifacts
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: goreleaser/goreleaser-cross:v1.23
|
||||
env:
|
||||
GOOS: ${{ matrix.goos }}
|
||||
GOARCH: ${{ matrix.goarch }}
|
||||
GOARM: ${{ matrix.goarm }}
|
||||
outputs:
|
||||
tag_name: ${{ steps.extract_branch.outputs.tag }}
|
||||
steps:
|
||||
- run: git config --global --add safe.directory /__w/nezha/nezha
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Fetch IPInfo GeoIP Database
|
||||
if: github.event_name == 'push' && contains(github.ref, 'refs/tags/')
|
||||
env:
|
||||
IPINFO_TOKEN: ${{ secrets.IPINFO_TOKEN }}
|
||||
run: |
|
||||
rm pkg/geoip/geoip.db
|
||||
wget -qO pkg/geoip/geoip.db https://ipinfo.io/data/free/country.mmdb?token=${IPINFO_TOKEN}
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.23.x"
|
||||
|
||||
- name: Build
|
||||
uses: goreleaser/goreleaser-action@v6
|
||||
with:
|
||||
distribution: goreleaser
|
||||
version: "~> v2"
|
||||
args: build --single-target --clean --skip=validate
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: dashboard-${{ env.GOOS }}-${{ env.GOARCH }}
|
||||
path: |
|
||||
./dist/*/*
|
||||
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'push' && contains(github.ref, 'refs/tags/')
|
||||
needs: build
|
||||
name: Release
|
||||
steps:
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: ./assets
|
||||
|
||||
- name: Archive and compress binaries
|
||||
run: |
|
||||
find assets/*/*/* -type f | while read -r file; do
|
||||
chmod +x $file
|
||||
dir=$(dirname "$file")
|
||||
filename=$(basename "$file")
|
||||
fileWithoutExt="${filename%.*}"
|
||||
zip -jr "$dir/$fileWithoutExt.zip" "$file"
|
||||
done
|
||||
|
||||
- name: Release
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
artifacts: "assets/*/*/*.zip"
|
||||
generateReleaseNotes: true
|
||||
|
||||
- name: Purge jsdelivr cache
|
||||
run: |
|
||||
curl -s https://purge.jsdelivr.net/gh/${{ github.repository_owner }}/nezha@master/script/install.sh
|
||||
curl -s https://purge.jsdelivr.net/gh/${{ github.repository_owner }}/nezha@master/script/nezha-agent.service
|
||||
curl -s https://purge.jsdelivr.net/gh/${{ github.repository_owner }}/nezha@master/script/docker-compose.yaml
|
||||
curl -s https://purge.jsdelivr.net/gh/${{ github.repository_owner }}/nezha@master/script/config.yaml
|
||||
LOWER_USERNAME=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]')
|
||||
curl -s https://purge.jsdelivr.net/gh/$LOWER_USERNAME/nezha@master/script/install.sh
|
||||
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
|
||||
env:
|
||||
GH_REPO: ${{ github.repository }}
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
GH_DEBUG: api
|
||||
run: |
|
||||
gh workflow run sync-release.yml
|
||||
|
||||
release-docker:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'push' && contains(github.ref, 'refs/tags/')
|
||||
needs: build
|
||||
name: Release Docker images
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: ./assets
|
||||
|
||||
- name: Fix permissions
|
||||
run: |
|
||||
chmod -R +x ./assets/*
|
||||
mkdir dist
|
||||
mv ./assets/*/*/* ./dist
|
||||
|
||||
- name: Extract branch name
|
||||
run: |
|
||||
export TAG_NAME=$(echo ${GITHUB_REF#refs/tags/})
|
||||
echo "tag=$TAG_NAME" >> $GITHUB_OUTPUT
|
||||
id: extract_branch
|
||||
|
||||
- name: xgo build
|
||||
uses: crazy-max/ghaction-xgo@v2
|
||||
with:
|
||||
xgo_version: latest
|
||||
go_version: 1.21
|
||||
dest: dist
|
||||
pkg: cmd/dashboard
|
||||
prefix: dashboard
|
||||
targets: linux/amd64,linux/arm64,linux/arm-7,linux/s390x,linux/riscv64 # linux/386,
|
||||
v: true
|
||||
x: false
|
||||
race: false
|
||||
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: |
|
||||
mv dist/dashboard-linux-arm-7 dist/dashboard-linux-arm
|
||||
|
||||
- name: Log in to the GHCR
|
||||
- name: Log into GHCR
|
||||
uses: docker/login-action@master
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
password: ${{ github.token }}
|
||||
|
||||
- name: Log in to the AliyunCS
|
||||
uses: docker/login-action@master
|
||||
@@ -59,13 +153,13 @@ jobs:
|
||||
|
||||
- name: Set up image name
|
||||
run: |
|
||||
GHRC_IMAGE_NAME=$(echo "ghcr.io/${{ github.repository_owner }}/nezha-dashboard" | tr '[:upper:]' '[:lower:]')
|
||||
GHCR_IMAGE_NAME=$(echo "ghcr.io/${{ github.repository_owner }}/nezha-dashboard" | tr '[:upper:]' '[:lower:]')
|
||||
if [ ${{ github.repository_owner }} = "naiba" ]
|
||||
then ALI_IMAGE_NAME=$(echo "registry.cn-shanghai.aliyuncs.com/naibahq/nezha-dashboard")
|
||||
else ALI_IMAGE_NAME=$(echo "registry.cn-shanghai.aliyuncs.com/${{ github.repository_owner }}/nezha-dashboard" | tr '[:upper:]' '[:lower:]')
|
||||
fi
|
||||
echo "::set-output name=GHRC_IMAGE_NAME::$GHRC_IMAGE_NAME"
|
||||
echo "::set-output name=ALI_IMAGE_NAME::$ALI_IMAGE_NAME"
|
||||
echo "GHCR_IMAGE_NAME=$GHCR_IMAGE_NAME" >> $GITHUB_OUTPUT
|
||||
echo "ALI_IMAGE_NAME=$ALI_IMAGE_NAME" >> $GITHUB_OUTPUT
|
||||
id: image-name
|
||||
|
||||
- name: Build dasbboard image And Push
|
||||
@@ -73,36 +167,10 @@ jobs:
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
platforms: linux/amd64,linux/arm64,linux/arm,linux/s390x,linux/riscv64 # linux/386,
|
||||
platforms: linux/amd64,linux/arm64,linux/s390x
|
||||
push: true
|
||||
tags: |
|
||||
${{ steps.image-name.outputs.GHRC_IMAGE_NAME }}:latest
|
||||
${{ steps.image-name.outputs.GHRC_IMAGE_NAME }}:${{ steps.extract_branch.outputs.tag }}
|
||||
${{ steps.image-name.outputs.GHCR_IMAGE_NAME }}:latest
|
||||
${{ steps.image-name.outputs.GHCR_IMAGE_NAME }}:${{ steps.extract_branch.outputs.tag }}
|
||||
${{ steps.image-name.outputs.ALI_IMAGE_NAME }}:latest
|
||||
${{ steps.image-name.outputs.ALI_IMAGE_NAME }}:${{ steps.extract_branch.outputs.tag }}
|
||||
|
||||
- name: Compress dist files
|
||||
run: |
|
||||
for file in dist/*; do
|
||||
if [ -f "$file" ]; then
|
||||
zip -r "$file.zip" "$file"
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Release
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
artifacts: "dist/*.zip"
|
||||
generateReleaseNotes: true
|
||||
|
||||
- name: Purge jsdelivr cache
|
||||
run: |
|
||||
curl -s https://purge.jsdelivr.net/gh/${{ github.repository_owner }}/nezha@master/script/install.sh
|
||||
curl -s https://purge.jsdelivr.net/gh/${{ github.repository_owner }}/nezha@master/script/nezha-agent.service
|
||||
curl -s https://purge.jsdelivr.net/gh/${{ github.repository_owner }}/nezha@master/script/docker-compose.yaml
|
||||
curl -s https://purge.jsdelivr.net/gh/${{ github.repository_owner }}/nezha@master/script/config.yaml
|
||||
LOWER_USERNAME=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]')
|
||||
curl -s https://purge.jsdelivr.net/gh/$LOWER_USERNAME/nezha@master/script/install.sh
|
||||
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
|
||||
|
||||
@@ -11,6 +11,6 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: adambirds/[email protected]
|
||||
with:
|
||||
destination_repository: git@jihulab.com:nezha/dashboard.git
|
||||
destination_repository: git@gitee.com:naibahq/nezha.git
|
||||
destination_branch_name: master
|
||||
destination_ssh_key: ${{ secrets.GITLAB_SSH_KEY }}
|
||||
@@ -0,0 +1,17 @@
|
||||
name: Sync Release to Gitee
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
sync-release-to-gitee:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
env:
|
||||
GITEE_TOKEN: ${{ secrets.GITEE_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Sync to Gitee
|
||||
run: |
|
||||
pip3 install PyGitHub
|
||||
python3 .github/sync.py
|
||||
@@ -1,38 +0,0 @@
|
||||
name: Run Tests on PR
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
jobs:
|
||||
tests:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GO111MODULE: on
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: "^1.21.3"
|
||||
- name: Unit test
|
||||
run: |
|
||||
go test -v ./...
|
||||
- name: Run Gosec Security Scanner
|
||||
uses: securego/gosec@master
|
||||
with:
|
||||
args: --exclude=G104,G402 ./...
|
||||
- name: xgo build
|
||||
uses: crazy-max/ghaction-xgo@v2
|
||||
with:
|
||||
xgo_version: latest
|
||||
go_version: 1.21
|
||||
dest: dist
|
||||
pkg: cmd/dashboard
|
||||
prefix: dashboard
|
||||
targets: linux/amd64,linux/arm64,linux/arm-7,linux/s390x,linux/riscv64 # linux/386,
|
||||
v: true
|
||||
x: false
|
||||
race: false
|
||||
ldflags: -s -w --extldflags '-static -fpic' -X github.com/naiba/nezha/service/singleton.Version=test
|
||||
buildmode: default
|
||||
|
||||
+25
-26
@@ -4,43 +4,42 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths-ignore:
|
||||
- ".github/workflows/codeql-analysis.yml"
|
||||
- ".github/workflows/test-on-pr.yml"
|
||||
- ".github/workflows/contributors.yml"
|
||||
- ".github/workflows/sync.yml"
|
||||
- ".github/workflows/release.yml"
|
||||
- "README.md"
|
||||
- ".goreleaser.yml"
|
||||
paths:
|
||||
- "**.go"
|
||||
- "go.mod"
|
||||
- "go.sum"
|
||||
- "resource/**"
|
||||
- ".github/workflows/test.yml"
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
os: [ubuntu, windows, macos]
|
||||
|
||||
runs-on: ${{ matrix.os }}-latest
|
||||
env:
|
||||
GO111MODULE: on
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v4
|
||||
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "^1.21.3"
|
||||
go-version: "1.23.x"
|
||||
|
||||
- name: Unit test
|
||||
run: |
|
||||
go test -v ./...
|
||||
|
||||
- name: Build test
|
||||
run: go build -v ./cmd/dashboard
|
||||
|
||||
- name: Run Gosec Security Scanner
|
||||
if: runner.os == 'Linux'
|
||||
uses: securego/gosec@master
|
||||
with:
|
||||
args: --exclude=G104,G402 ./...
|
||||
- name: xgo build
|
||||
uses: crazy-max/ghaction-xgo@v2
|
||||
with:
|
||||
xgo_version: latest
|
||||
go_version: 1.21
|
||||
dest: dist
|
||||
pkg: cmd/dashboard
|
||||
prefix: dashboard
|
||||
targets: linux/amd64,linux/arm64,linux/arm-7,linux/s390x,linux/riscv64 # linux/386,
|
||||
v: true
|
||||
x: false
|
||||
race: false
|
||||
ldflags: -s -w --extldflags '-static -fpic' -X github.com/naiba/nezha/service/singleton.Version=test
|
||||
buildmode: default
|
||||
args: --exclude=G104,G402,G115,G203 ./...
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
version: 2
|
||||
before:
|
||||
hooks:
|
||||
- go mod tidy -v
|
||||
builds:
|
||||
- id: linux_arm64
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
- CC=aarch64-linux-gnu-gcc
|
||||
ldflags:
|
||||
- -s -w
|
||||
- -X github.com/naiba/nezha/service/singleton.Version={{.Version}}
|
||||
- -extldflags "-static -fpic"
|
||||
flags:
|
||||
- -trimpath
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- arm64
|
||||
main: ./cmd/dashboard
|
||||
binary: dashboard-{{ .Os }}-{{ .Arch }}
|
||||
- id: linux_amd64
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
- CC=x86_64-linux-gnu-gcc
|
||||
ldflags:
|
||||
- -s -w
|
||||
- -X github.com/naiba/nezha/service/singleton.Version={{.Version}}
|
||||
- -extldflags "-static -fpic"
|
||||
flags:
|
||||
- -trimpath
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- amd64
|
||||
main: ./cmd/dashboard
|
||||
binary: dashboard-{{ .Os }}-{{ .Arch }}
|
||||
- id: linux_s390x
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
- CC=s390x-linux-gnu-gcc
|
||||
ldflags:
|
||||
- -s -w
|
||||
- -X github.com/naiba/nezha/service/singleton.Version={{.Version}}
|
||||
- -extldflags "-static -fpic"
|
||||
flags:
|
||||
- -trimpath
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- s390x
|
||||
main: ./cmd/dashboard
|
||||
binary: dashboard-{{ .Os }}-{{ .Arch }}
|
||||
- id: windows_amd64
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
- CC=x86_64-w64-mingw32-gcc
|
||||
ldflags:
|
||||
- -s -w
|
||||
- -X github.com/naiba/nezha/service/singleton.Version={{.Version}}
|
||||
- -extldflags "-static -fpic"
|
||||
flags:
|
||||
- -trimpath
|
||||
goos:
|
||||
- windows
|
||||
goarch:
|
||||
- amd64
|
||||
main: ./cmd/dashboard
|
||||
binary: dashboard-{{ .Os }}-{{ .Arch }}
|
||||
snapshot:
|
||||
version_template: "dashboard"
|
||||
+7
-9
@@ -1,22 +1,20 @@
|
||||
FROM ubuntu:focal-20221130
|
||||
FROM alpine AS certs
|
||||
RUN apk update && apk add ca-certificates
|
||||
|
||||
ENV TZ="Asia/Shanghai"
|
||||
FROM busybox:stable-musl
|
||||
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
|
||||
COPY --from=certs /etc/ssl/certs /etc/ssl/certs
|
||||
COPY ./script/entrypoint.sh /entrypoint.sh
|
||||
|
||||
RUN export DEBIAN_FRONTEND="noninteractive" && \
|
||||
apt update && apt install -y ca-certificates tzdata && \
|
||||
update-ca-certificates && \
|
||||
ln -fs /usr/share/zoneinfo/$TZ /etc/localtime && \
|
||||
dpkg-reconfigure tzdata && \
|
||||
chmod +x /entrypoint.sh
|
||||
RUN chmod +x /entrypoint.sh
|
||||
|
||||
WORKDIR /dashboard
|
||||
COPY dist/dashboard-${TARGETOS}-${TARGETARCH} ./app
|
||||
|
||||
VOLUME ["/dashboard/data"]
|
||||
EXPOSE 80 5555
|
||||
ARG TZ=Asia/Shanghai
|
||||
ENV TZ=$TZ
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
@@ -0,0 +1,63 @@
|
||||
# Use build arguments for Go version and architecture
|
||||
ARG GO_VERSION=1.22
|
||||
ARG BUILDARCH=amd64
|
||||
|
||||
# Stage 1: Builder Stage
|
||||
# FROM golang:${GO_VERSION}-alpine AS builder
|
||||
FROM crazymax/xgo:${GO_VERSION} AS builder
|
||||
|
||||
# Set up working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Step 1: Copy the source code
|
||||
COPY . .
|
||||
|
||||
# use --mount=type=cache,target=/go/pkg/mod to cache the go mod
|
||||
# Step 2: Download dependencies
|
||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||
go mod tidy && go mod download
|
||||
|
||||
# Step 3: Build the Go application with CGO enabled and specified ldflags
|
||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||
CGO_ENABLED=1 GOOS=linux go build -a \
|
||||
-ldflags "-s -w --extldflags '-static -fpic'" \
|
||||
-installsuffix cgo -o dashboard cmd/dashboard/main.go
|
||||
|
||||
|
||||
# Stage 2: Create the final image
|
||||
FROM alpine:latest
|
||||
|
||||
ARG COUNTRY
|
||||
# Install required tools without caching index to minimize image size
|
||||
RUN if [ "$COUNTRY" = "CN" ] ; then \
|
||||
echo "It is in China, updating the repositories"; \
|
||||
sed -i 's#https\?://dl-cdn.alpinelinux.org/alpine#https://mirrors.tuna.tsinghua.edu.cn/alpine#g' /etc/apk/repositories; \
|
||||
fi && \
|
||||
apk update && apk add --no-cache tzdata && \
|
||||
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
|
||||
echo 'Asia/Shanghai' >/etc/timezone && \
|
||||
rm -rf /var/cache/apk/* && \
|
||||
mkdir -p /dashboard/data
|
||||
|
||||
|
||||
# Copy the entrypoint script and ensure it is executable
|
||||
COPY ./script/entrypoint.sh /entrypoint.sh
|
||||
|
||||
# Set up the entrypoint script
|
||||
RUN chmod +x /entrypoint.sh
|
||||
|
||||
WORKDIR /dashboard
|
||||
|
||||
# Copy the statically linked binary from the builder stage
|
||||
COPY --from=builder /app/dashboard ./app
|
||||
# Copy the configuration file and the resource directory
|
||||
COPY ./script/config.yaml ./data/config.yaml
|
||||
COPY ./resource ./resource
|
||||
|
||||
|
||||
# Set up volume and expose ports
|
||||
VOLUME ["/dashboard/data"]
|
||||
EXPOSE 80 5555 443
|
||||
|
||||
# Define the entrypoint
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
@@ -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.0-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">
|
||||
<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,108 +40,66 @@ 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/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/hhhkkk520" title="Kris">
|
||||
<img src="https://avatars.githubusercontent.com/u/52115472?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/rootmelo92118" title="rootmelo92118">
|
||||
<img src="https://avatars.githubusercontent.com/u/32770959?v=4" width="50;" alt="rootmelo92118"/>
|
||||
</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/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/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/Es-dese" title="Esdese">
|
||||
<img src="https://avatars.githubusercontent.com/u/71542548?v=4" width="50;" alt="Esdese"/>
|
||||
</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/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/KorenKrita" title="KorenKrita">
|
||||
<img src="https://avatars.githubusercontent.com/u/22239339?v=4" width="50;" alt="KorenKrita"/>
|
||||
</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/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/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/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/colour93" title="玖叁">
|
||||
<img src="https://avatars.githubusercontent.com/u/64313711?v=4" width="50;" alt="玖叁"/>
|
||||
</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/nap0o" title="nap0o"><img src="https://avatars.githubusercontent.com/u/144927971?v=4" width="50;" alt="nap0o"/></a>
|
||||
<a href="https://github.com/dysf888" title="黑歌"><img src="https://avatars.githubusercontent.com/u/47450409?v=4" width="50;" alt="黑歌"/></a>
|
||||
<a href="https://github.com/xykt" title="xykt"><img src="https://avatars.githubusercontent.com/u/152045469?v=4" width="50;" alt="xykt"/></a>
|
||||
<a href="https://github.com/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/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/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/zhucaidan" title="zhucaidan"><img src="https://avatars.githubusercontent.com/u/47970938?v=4" width="50;" alt="zhucaidan"/></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/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/HsukqiLee" title="HsukqiLee"><img src="https://avatars.githubusercontent.com/u/79034142?v=4" width="50;" alt="HsukqiLee"/></a>
|
||||
<a href="https://github.com/DarcJC" title="Darc Z."><img src="https://avatars.githubusercontent.com/u/53445798?v=4" width="50;" alt="Darc Z."/></a>
|
||||
<a href="https://github.com/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/Septrum101" title="Spetrum"><img src="https://avatars.githubusercontent.com/u/11692994?v=4" width="50;" alt="Spetrum"/></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/hiDandelion" title="hiDandelion"><img src="https://avatars.githubusercontent.com/u/77157418?v=4" width="50;" alt="hiDandelion"/></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/lyj0309" title="lyj"><img src="https://avatars.githubusercontent.com/u/50474995?v=4" width="50;" alt="lyj"/></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/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/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/Moraxyc" title="Moraxyc"><img src="https://avatars.githubusercontent.com/u/69713071?v=4" width="50;" alt="Moraxyc"/></a>
|
||||
<a href="https://github.com/silver-ymz" title="Mingzhuo Yin"><img src="https://avatars.githubusercontent.com/u/78400701?v=4" width="50;" alt="Mingzhuo Yin"/></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=":D"><img src="https://avatars.githubusercontent.com/u/17147265?v=4" width="50;" alt=":D"/></a>
|
||||
<a href="https://github.com/wellcoming" title="Coming"><img src="https://avatars.githubusercontent.com/u/74850890?v=4" width="50;" alt="Coming"/></a>
|
||||
<a href="https://github.com/choyri" title="Chotow"><img src="https://avatars.githubusercontent.com/u/13994362?v=4" width="50;" alt="Chotow"/></a>
|
||||
<a href="https://github.com/006lp" title="006lp"><img src="https://avatars.githubusercontent.com/u/144674902?v=4" width="50;" alt="006lp"/></a><!--GAMFC_DELIMITER_END-->
|
||||
|
||||
## Special Thanks
|
||||
- [IPInfo](https://ipinfo.io/) for providing an accurate GeoIP Database.
|
||||
|
||||
## Star History
|
||||
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/naiba/nezha/pkg/mygin"
|
||||
"github.com/naiba/nezha/service/singleton"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/naiba/nezha/model"
|
||||
"github.com/naiba/nezha/pkg/mygin"
|
||||
"github.com/naiba/nezha/service/singleton"
|
||||
)
|
||||
|
||||
type apiV1 struct {
|
||||
@@ -14,18 +17,33 @@ type apiV1 struct {
|
||||
|
||||
func (v *apiV1) serve() {
|
||||
r := v.r.Group("")
|
||||
// API
|
||||
// 强制认证的 API
|
||||
r.Use(mygin.Authorize(mygin.AuthorizeOption{
|
||||
Member: true,
|
||||
IsPage: false,
|
||||
AllowAPI: true,
|
||||
Msg: "访问此接口需要认证",
|
||||
Btn: "点此登录",
|
||||
Redirect: "/login",
|
||||
MemberOnly: true,
|
||||
AllowAPI: true,
|
||||
IsPage: false,
|
||||
Msg: "访问此接口需要认证",
|
||||
Btn: "点此登录",
|
||||
Redirect: "/login",
|
||||
}))
|
||||
r.GET("/server/list", v.serverList)
|
||||
r.GET("/server/details", v.serverDetails)
|
||||
|
||||
r.POST("/server/register", v.RegisterServer)
|
||||
// 不强制认证的 API
|
||||
mr := v.r.Group("monitor")
|
||||
mr.Use(mygin.Authorize(mygin.AuthorizeOption{
|
||||
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)
|
||||
}
|
||||
|
||||
// serverList 获取服务器列表 不传入Query参数则获取全部
|
||||
@@ -65,3 +83,70 @@ func (v *apiV1) serverDetails(c *gin.Context) {
|
||||
}
|
||||
c.JSON(200, singleton.ServerAPI.GetAllStatus())
|
||||
}
|
||||
|
||||
// RegisterServer adds a server and responds with the full ServerRegisterResponse
|
||||
// header: Authorization: Token
|
||||
// body: RegisterServer
|
||||
// response: ServerRegisterResponse or Secret string
|
||||
func (v *apiV1) RegisterServer(c *gin.Context) {
|
||||
var rs singleton.RegisterServer
|
||||
// Attempt to bind JSON to RegisterServer struct
|
||||
if err := c.ShouldBindJSON(&rs); err != nil {
|
||||
c.JSON(400, singleton.ServerRegisterResponse{
|
||||
CommonResponse: singleton.CommonResponse{
|
||||
Code: 400,
|
||||
Message: "Parse JSON failed",
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
// Check if simple mode is requested
|
||||
simple := c.Query("simple") == "true" || c.Query("simple") == "1"
|
||||
// Set defaults if fields are empty
|
||||
if rs.Name == "" {
|
||||
rs.Name = c.ClientIP()
|
||||
}
|
||||
if rs.Tag == "" {
|
||||
rs.Tag = "AutoRegister"
|
||||
}
|
||||
if rs.HideForGuest == "" {
|
||||
rs.HideForGuest = "on"
|
||||
}
|
||||
// Call the Register function and get the response
|
||||
response := singleton.ServerAPI.Register(&rs)
|
||||
// Respond with Secret only if in simple mode, otherwise full response
|
||||
if simple {
|
||||
c.JSON(response.Code, response.Secret)
|
||||
} else {
|
||||
c.JSON(response.Code, response)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func (v *apiV1) monitorHistoriesById(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(400, gin.H{"code": 400, "message": "id参数错误"})
|
||||
return
|
||||
}
|
||||
server, ok := singleton.ServerList[id]
|
||||
if !ok {
|
||||
c.AbortWithStatusJSON(404, gin.H{
|
||||
"code": 404,
|
||||
"message": "id不存在",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
_, isMember := c.Get(model.CtxKeyAuthorizedUser)
|
||||
_, isViewPasswordVerfied := c.Get(model.CtxKeyViewPasswordVerified)
|
||||
authorized := isMember || isViewPasswordVerfied
|
||||
|
||||
if server.HideForGuest && !authorized {
|
||||
c.AbortWithStatusJSON(403, gin.H{"code": 403, "message": "需要认证"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, singleton.MonitorAPI.GetMonitorHistories(map[string]any{"server_id": server.ID}))
|
||||
}
|
||||
|
||||
@@ -2,11 +2,9 @@ package controller
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -22,34 +20,34 @@ 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{}))
|
||||
cr.GET("/terminal/:id", cp.terminal)
|
||||
cr.Use(mygin.PreferredTheme)
|
||||
cr.POST("/view-password", cp.issueViewPassword)
|
||||
cr.Use(cp.checkViewPassword) // 前端查看密码鉴权
|
||||
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: 界面直接跳转使用该接口
|
||||
cr.GET("/network/:id", cp.network)
|
||||
cr.GET("/network", cp.network)
|
||||
cr.GET("/ws", cp.ws)
|
||||
cr.POST("/terminal", cp.createTerminal)
|
||||
cr.GET("/file", cp.createFM)
|
||||
cr.GET("/file/:id", cp.fm)
|
||||
}
|
||||
|
||||
type viewPasswordForm struct {
|
||||
@@ -81,31 +79,6 @@ func (p *commonPage) issueViewPassword(c *gin.Context) {
|
||||
c.Redirect(http.StatusFound, c.Request.Referer())
|
||||
}
|
||||
|
||||
func (p *commonPage) checkViewPassword(c *gin.Context) {
|
||||
if singleton.Conf.Site.ViewPassword == "" {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
if _, authorized := c.Get(model.CtxKeyAuthorizedUser); authorized {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
|
||||
// 验证查看密码
|
||||
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, "theme-"+singleton.Conf.Site.Theme+"/viewpassword", mygin.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)
|
||||
c.Next()
|
||||
}
|
||||
|
||||
func (p *commonPage) service(c *gin.Context) {
|
||||
res, _, _ := p.requestGroup.Do("servicePage", func() (interface{}, error) {
|
||||
singleton.AlertsLock.RLock()
|
||||
@@ -114,33 +87,150 @@ func (p *commonPage) service(c *gin.Context) {
|
||||
var statsStore map[uint64]model.CycleTransferStats
|
||||
copier.Copy(&stats, singleton.ServiceSentinelShared.LoadStats())
|
||||
copier.Copy(&statsStore, singleton.AlertsCycleTransferStatsStore)
|
||||
for k, service := range stats {
|
||||
if !service.Monitor.EnableShowInService {
|
||||
delete(stats, k)
|
||||
}
|
||||
}
|
||||
return []interface {
|
||||
}{
|
||||
stats, statsStore,
|
||||
}, nil
|
||||
})
|
||||
c.HTML(http.StatusOK, "theme-"+singleton.Conf.Site.Theme+"/service", mygin.CommonEnvironment(c, gin.H{
|
||||
c.HTML(http.StatusOK, mygin.GetPreferredTheme(c, "/service"), mygin.CommonEnvironment(c, gin.H{
|
||||
"Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "ServicesStatus"}),
|
||||
"Services": res.([]interface{})[0],
|
||||
"CycleTransferStats": res.([]interface{})[1],
|
||||
"CustomCode": singleton.Conf.Site.CustomCode,
|
||||
}))
|
||||
}
|
||||
|
||||
func (cp *commonPage) getServerStat(c *gin.Context) ([]byte, error) {
|
||||
v, err, _ := cp.requestGroup.Do("serverStats", func() (any, error) {
|
||||
func (cp *commonPage) network(c *gin.Context) {
|
||||
var (
|
||||
monitorHistory *model.MonitorHistory
|
||||
servers []*model.Server
|
||||
serverIdsWithMonitor []uint64
|
||||
monitorInfos = []byte("{}")
|
||||
id uint64
|
||||
)
|
||||
if len(singleton.SortedServerList) > 0 {
|
||||
id = singleton.SortedServerList[0].ID
|
||||
}
|
||||
if err := singleton.DB.Model(&model.MonitorHistory{}).Select("monitor_id, server_id").
|
||||
Where("monitor_id != 0 and server_id != 0").Limit(1).First(&monitorHistory).Error; err != nil {
|
||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
Code: http.StatusForbidden,
|
||||
Title: "请求失败",
|
||||
Msg: "请求参数有误:" + "server monitor history not found",
|
||||
Link: "/",
|
||||
Btn: "返回重试",
|
||||
}, true)
|
||||
return
|
||||
} else {
|
||||
if monitorHistory == nil || monitorHistory.ServerID == 0 {
|
||||
if len(singleton.SortedServerList) > 0 {
|
||||
id = singleton.SortedServerList[0].ID
|
||||
}
|
||||
} else {
|
||||
id = monitorHistory.ServerID
|
||||
}
|
||||
}
|
||||
|
||||
idStr := c.Param("id")
|
||||
if idStr != "" {
|
||||
var err error
|
||||
id, err = strconv.ParseUint(idStr, 10, 64)
|
||||
if err != nil {
|
||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
Code: http.StatusForbidden,
|
||||
Title: "请求失败",
|
||||
Msg: "请求参数有误:" + err.Error(),
|
||||
Link: "/",
|
||||
Btn: "返回重试",
|
||||
}, true)
|
||||
return
|
||||
}
|
||||
_, ok := singleton.ServerList[id]
|
||||
if !ok {
|
||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
Code: http.StatusForbidden,
|
||||
Title: "请求失败",
|
||||
Msg: "请求参数有误:" + "server id not found",
|
||||
Link: "/",
|
||||
Btn: "返回重试",
|
||||
}, true)
|
||||
return
|
||||
}
|
||||
}
|
||||
monitorHistories := singleton.MonitorAPI.GetMonitorHistories(map[string]any{"server_id": id})
|
||||
monitorInfos, _ = utils.Json.Marshal(monitorHistories)
|
||||
_, isMember := c.Get(model.CtxKeyAuthorizedUser)
|
||||
_, isViewPasswordVerfied := c.Get(model.CtxKeyViewPasswordVerified)
|
||||
|
||||
if err := singleton.DB.Model(&model.MonitorHistory{}).
|
||||
Select("distinct(server_id)").
|
||||
Where("server_id != 0").
|
||||
Find(&serverIdsWithMonitor).
|
||||
Error; err != nil {
|
||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
Code: http.StatusForbidden,
|
||||
Title: "请求失败",
|
||||
Msg: "请求参数有误:" + "no server with monitor histories",
|
||||
Link: "/",
|
||||
Btn: "返回重试",
|
||||
}, true)
|
||||
return
|
||||
}
|
||||
if isMember || isViewPasswordVerfied {
|
||||
for _, server := range singleton.SortedServerList {
|
||||
for _, id := range serverIdsWithMonitor {
|
||||
if server.ID == id {
|
||||
servers = append(servers, server)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, server := range singleton.SortedServerListForGuest {
|
||||
for _, id := range serverIdsWithMonitor {
|
||||
if server.ID == id {
|
||||
servers = append(servers, server)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
serversBytes, _ := utils.Json.Marshal(Data{
|
||||
Now: time.Now().Unix() * 1000,
|
||||
Servers: servers,
|
||||
})
|
||||
|
||||
c.HTML(http.StatusOK, mygin.GetPreferredTheme(c, "/network"), mygin.CommonEnvironment(c, gin.H{
|
||||
"Servers": string(serversBytes),
|
||||
"MonitorInfos": string(monitorInfos),
|
||||
"MaxTCPPingValue": singleton.Conf.MaxTCPPingValue,
|
||||
}))
|
||||
}
|
||||
|
||||
func (cp *commonPage) getServerStat(c *gin.Context, withPublicNote bool) ([]byte, 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 serverList []*model.Server
|
||||
if authorized {
|
||||
serverList = singleton.SortedServerList
|
||||
} else {
|
||||
serverList = singleton.SortedServerListForGuest
|
||||
}
|
||||
|
||||
var servers []*model.Server
|
||||
|
||||
if isMember || isViewPasswordVerfied {
|
||||
servers = singleton.SortedServerList
|
||||
} else {
|
||||
servers = singleton.SortedServerListForGuest
|
||||
for _, server := range serverList {
|
||||
item := *server
|
||||
if !withPublicNote {
|
||||
item.PublicNote = ""
|
||||
}
|
||||
servers = append(servers, &item)
|
||||
}
|
||||
|
||||
return utils.Json.Marshal(Data{
|
||||
@@ -152,7 +242,7 @@ func (cp *commonPage) getServerStat(c *gin.Context) ([]byte, error) {
|
||||
}
|
||||
|
||||
func (cp *commonPage) home(c *gin.Context) {
|
||||
stat, err := cp.getServerStat(c)
|
||||
stat, err := cp.getServerStat(c, true)
|
||||
if err != nil {
|
||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
Code: http.StatusInternalServerError,
|
||||
@@ -165,15 +255,14 @@ func (cp *commonPage) home(c *gin.Context) {
|
||||
}, true)
|
||||
return
|
||||
}
|
||||
c.HTML(http.StatusOK, "theme-"+singleton.Conf.Site.Theme+"/home", mygin.CommonEnvironment(c, gin.H{
|
||||
"Servers": string(stat),
|
||||
"CustomCode": singleton.Conf.Site.CustomCode,
|
||||
c.HTML(http.StatusOK, mygin.GetPreferredTheme(c, "/home"), mygin.CommonEnvironment(c, gin.H{
|
||||
"Servers": string(stat),
|
||||
}))
|
||||
}
|
||||
|
||||
var upgrader = websocket.Upgrader{
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024,
|
||||
ReadBufferSize: 32768,
|
||||
WriteBufferSize: 32768,
|
||||
}
|
||||
|
||||
type Data struct {
|
||||
@@ -181,8 +270,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 {
|
||||
@@ -200,7 +287,7 @@ func (cp *commonPage) ws(c *gin.Context) {
|
||||
defer conn.Close()
|
||||
count := 0
|
||||
for {
|
||||
stat, err := cp.getServerStat(c)
|
||||
stat, err := cp.getServerStat(c, false)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
@@ -219,10 +306,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: "无权访问",
|
||||
@@ -232,104 +317,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 {
|
||||
@@ -345,36 +333,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 保活
|
||||
@@ -386,58 +345,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 {
|
||||
@@ -469,7 +381,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,
|
||||
@@ -483,6 +395,8 @@ func (cp *commonPage) createTerminal(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
rpc.NezhaHandlerSingleton.CreateStream(streamId)
|
||||
|
||||
singleton.ServerLock.RLock()
|
||||
server := singleton.ServerList[createTerminalReq.ID]
|
||||
singleton.ServerLock.RUnlock()
|
||||
@@ -497,17 +411,150 @@ 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,
|
||||
"ServerID": server.ID,
|
||||
}))
|
||||
}
|
||||
|
||||
func (cp *commonPage) fm(c *gin.Context) {
|
||||
streamId := c.Param("id")
|
||||
if _, err := rpc.NezhaHandlerSingleton.GetStream(streamId); err != nil {
|
||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
Code: http.StatusForbidden,
|
||||
Title: "无权访问",
|
||||
Msg: "FM会话不存在",
|
||||
Link: "/",
|
||||
Btn: "返回首页",
|
||||
}, true)
|
||||
return
|
||||
}
|
||||
defer rpc.NezhaHandlerSingleton.CloseStream(streamId)
|
||||
|
||||
wsConn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
Code: http.StatusInternalServerError,
|
||||
Title: singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{
|
||||
MessageID: "NetworkError",
|
||||
}),
|
||||
Msg: "Websocket协议切换失败",
|
||||
Link: "/",
|
||||
Btn: "返回首页",
|
||||
}, true)
|
||||
return
|
||||
}
|
||||
defer wsConn.Close()
|
||||
conn := websocketx.NewConn(wsConn)
|
||||
|
||||
go func() {
|
||||
// PING 保活
|
||||
for {
|
||||
if err = conn.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
|
||||
return
|
||||
}
|
||||
time.Sleep(time.Second * 10)
|
||||
}
|
||||
}()
|
||||
|
||||
if err = rpc.NezhaHandlerSingleton.UserConnected(streamId, conn); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
rpc.NezhaHandlerSingleton.StartStream(streamId, time.Second*10)
|
||||
}
|
||||
|
||||
func (cp *commonPage) createFM(c *gin.Context) {
|
||||
IdString := c.Query("id")
|
||||
if _, authorized := c.Get(model.CtxKeyAuthorizedUser); !authorized {
|
||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
Code: http.StatusForbidden,
|
||||
Title: "无权访问",
|
||||
Msg: "用户未登录",
|
||||
Link: "/login",
|
||||
Btn: "去登录",
|
||||
}, true)
|
||||
return
|
||||
}
|
||||
|
||||
streamId, err := uuid.GenerateUUID()
|
||||
if err != nil {
|
||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
Code: http.StatusInternalServerError,
|
||||
Title: singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{
|
||||
MessageID: "SystemError",
|
||||
}),
|
||||
Msg: "生成会话ID失败",
|
||||
Link: "/server",
|
||||
Btn: "返回重试",
|
||||
}, true)
|
||||
return
|
||||
}
|
||||
|
||||
rpc.NezhaHandlerSingleton.CreateStream(streamId)
|
||||
|
||||
serverId, err := strconv.Atoi(IdString)
|
||||
if err != nil {
|
||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
Code: http.StatusForbidden,
|
||||
Title: "请求失败",
|
||||
Msg: "请求参数有误:" + err.Error(),
|
||||
Link: "/server",
|
||||
Btn: "返回重试",
|
||||
}, true)
|
||||
return
|
||||
}
|
||||
|
||||
singleton.ServerLock.RLock()
|
||||
server := singleton.ServerList[uint64(serverId)]
|
||||
singleton.ServerLock.RUnlock()
|
||||
if server == nil {
|
||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
Code: http.StatusForbidden,
|
||||
Title: "请求失败",
|
||||
Msg: "服务器不存在或处于离线状态",
|
||||
Link: "/server",
|
||||
Btn: "返回重试",
|
||||
}, true)
|
||||
return
|
||||
}
|
||||
|
||||
fmData, _ := utils.Json.Marshal(&model.TaskFM{
|
||||
StreamID: streamId,
|
||||
})
|
||||
if err := server.TaskStream.Send(&proto.Task{
|
||||
Type: model.TaskTypeFM,
|
||||
Data: string(fmData),
|
||||
}); 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+"/file", mygin.CommonEnvironment(c, gin.H{
|
||||
"SessionID": streamId,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -3,28 +3,37 @@ package controller
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io/fs"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"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,19 +42,9 @@ 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 {
|
||||
panic(err)
|
||||
}
|
||||
r.StaticFS("/static", http.FS(staticFs))
|
||||
r.Static("/static-custom", "resource/static/custom")
|
||||
r.StaticFS("/static", http.FS(resource.StaticFS))
|
||||
routers(r)
|
||||
|
||||
page404 := func(c *gin.Context) {
|
||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
Code: http.StatusNotFound,
|
||||
@@ -68,7 +67,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}
|
||||
@@ -85,7 +84,7 @@ func routers(r *gin.Engine) {
|
||||
}
|
||||
|
||||
func loadThirdPartyTemplates(tmpl *template.Template) *template.Template {
|
||||
var ret = tmpl
|
||||
ret := tmpl
|
||||
themes, err := os.ReadDir("resource/template")
|
||||
if err != nil {
|
||||
log.Printf("NEZHA>> Error reading themes folder: %v", err)
|
||||
@@ -95,17 +94,61 @@ func loadThirdPartyTemplates(tmpl *template.Template) *template.Template {
|
||||
if !theme.IsDir() {
|
||||
continue
|
||||
}
|
||||
// load templates
|
||||
t, err := ret.ParseGlob(fmt.Sprintf("resource/template/%s/*.html", theme.Name()))
|
||||
if err != nil {
|
||||
log.Printf("NEZHA>> Error parsing templates %s error: %v", theme.Name(), err)
|
||||
|
||||
themeDir := theme.Name()
|
||||
if themeDir == "theme-custom" {
|
||||
// for backward compatibility
|
||||
// note: will remove this in future versions
|
||||
ret = loadTemplates(ret, themeDir)
|
||||
continue
|
||||
}
|
||||
ret = t
|
||||
|
||||
if strings.HasPrefix(themeDir, "dashboard-") {
|
||||
// load dashboard templates, ignore desc file
|
||||
ret = loadTemplates(ret, themeDir)
|
||||
continue
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(themeDir, "theme-") {
|
||||
log.Printf("NEZHA>> Invalid theme name: %s", themeDir)
|
||||
continue
|
||||
}
|
||||
|
||||
descPath := filepath.Join("resource", "template", themeDir, "theme.json")
|
||||
desc, err := os.ReadFile(filepath.Clean(descPath))
|
||||
if err != nil {
|
||||
log.Printf("NEZHA>> Error opening %s config: %v", themeDir, err)
|
||||
continue
|
||||
}
|
||||
|
||||
themeName, err := utils.GjsonGet(desc, "name")
|
||||
if err != nil {
|
||||
log.Printf("NEZHA>> Error opening %s config: not a valid description file", theme.Name())
|
||||
continue
|
||||
}
|
||||
|
||||
// load templates
|
||||
ret = loadTemplates(ret, themeDir)
|
||||
|
||||
themeKey := strings.TrimPrefix(themeDir, "theme-")
|
||||
model.Themes[themeKey] = themeName.String()
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func loadTemplates(tmpl *template.Template, themeDir string) *template.Template {
|
||||
// load templates
|
||||
templatePath := filepath.Join("resource", "template", themeDir, "*.html")
|
||||
t, err := tmpl.ParseGlob(templatePath)
|
||||
if err != nil {
|
||||
log.Printf("NEZHA>> Error parsing templates %s: %v", themeDir, err)
|
||||
return tmpl
|
||||
}
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
var funcMap = template.FuncMap{
|
||||
"tr": func(id string, dataAndCount ...interface{}) string {
|
||||
conf := i18n.LocalizeConfig{
|
||||
@@ -239,3 +282,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 := utils.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()
|
||||
}
|
||||
|
||||
@@ -19,11 +19,11 @@ type guestPage struct {
|
||||
func (gp *guestPage) serve() {
|
||||
gr := gp.r.Group("")
|
||||
gr.Use(mygin.Authorize(mygin.AuthorizeOption{
|
||||
Guest: true,
|
||||
IsPage: true,
|
||||
Msg: "您已登录",
|
||||
Btn: "返回首页",
|
||||
Redirect: "/",
|
||||
GuestOnly: true,
|
||||
IsPage: true,
|
||||
Msg: "您已登录",
|
||||
Btn: "返回首页",
|
||||
Redirect: "/",
|
||||
}))
|
||||
|
||||
gr.GET("/login", gp.login)
|
||||
@@ -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"}),
|
||||
|
||||
@@ -7,10 +7,13 @@ import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/jinzhu/copier"
|
||||
"golang.org/x/net/idna"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/naiba/nezha/model"
|
||||
"github.com/naiba/nezha/pkg/mygin"
|
||||
@@ -27,15 +30,16 @@ type memberAPI struct {
|
||||
func (ma *memberAPI) serve() {
|
||||
mr := ma.r.Group("")
|
||||
mr.Use(mygin.Authorize(mygin.AuthorizeOption{
|
||||
Member: true,
|
||||
IsPage: false,
|
||||
Msg: "访问此接口需要登录",
|
||||
Btn: "点此登录",
|
||||
Redirect: "/login",
|
||||
MemberOnly: true,
|
||||
IsPage: false,
|
||||
Msg: "访问此接口需要登录",
|
||||
Btn: "点此登录",
|
||||
Redirect: "/login",
|
||||
}))
|
||||
|
||||
mr.GET("/search-server", ma.searchServer)
|
||||
mr.GET("/search-tasks", ma.searchTask)
|
||||
mr.GET("/search-ddns", ma.searchDDNS)
|
||||
mr.POST("/server", ma.addOrEditServer)
|
||||
mr.POST("/monitor", ma.addOrEditMonitor)
|
||||
mr.POST("/cron", ma.addOrEditCron)
|
||||
@@ -44,6 +48,8 @@ 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("/ddns", ma.addOrEditDDNS)
|
||||
mr.POST("/nat", ma.addOrEditNAT)
|
||||
mr.POST("/alert-rule", ma.addOrEditAlertRule)
|
||||
mr.POST("/setting", ma.updateSetting)
|
||||
mr.DELETE("/:model/:id", ma.delete)
|
||||
@@ -185,7 +191,17 @@ func (ma *memberAPI) delete(c *gin.Context) {
|
||||
var err error
|
||||
switch c.Param("model") {
|
||||
case "server":
|
||||
err = singleton.DB.Unscoped().Delete(&model.Server{}, "id = ?", id).Error
|
||||
err := singleton.DB.Transaction(func(tx *gorm.DB) error {
|
||||
err = singleton.DB.Unscoped().Delete(&model.Server{}, "id = ?", id).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = singleton.DB.Unscoped().Delete(&model.MonitorHistory{}, "server_id = ?", id).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err == nil {
|
||||
// 删除服务器
|
||||
singleton.ServerLock.Lock()
|
||||
@@ -198,6 +214,16 @@ func (ma *memberAPI) delete(c *gin.Context) {
|
||||
if err == nil {
|
||||
singleton.OnDeleteNotification(id)
|
||||
}
|
||||
case "ddns":
|
||||
err = singleton.DB.Unscoped().Delete(&model.DDNSProfile{}, "id = ?", id).Error
|
||||
if err == nil {
|
||||
singleton.OnDDNSUpdate()
|
||||
}
|
||||
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 {
|
||||
@@ -281,14 +307,38 @@ func (ma *memberAPI) searchTask(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
func (ma *memberAPI) searchDDNS(c *gin.Context) {
|
||||
var ddns []model.DDNSProfile
|
||||
likeWord := "%" + c.Query("word") + "%"
|
||||
singleton.DB.Select("id,name").Where("id = ? OR name LIKE ?",
|
||||
c.Query("word"), likeWord).Find(&ddns)
|
||||
|
||||
var resp []searchResult
|
||||
for i := 0; i < len(ddns); i++ {
|
||||
resp = append(resp, searchResult{
|
||||
Value: ddns[i].ID,
|
||||
Name: ddns[i].Name,
|
||||
Text: ddns[i].Name,
|
||||
})
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, map[string]interface{}{
|
||||
"success": true,
|
||||
"results": resp,
|
||||
})
|
||||
}
|
||||
|
||||
type serverForm struct {
|
||||
ID uint64
|
||||
Name string `binding:"required"`
|
||||
DisplayIndex int
|
||||
Secret string
|
||||
Tag string
|
||||
Note string
|
||||
HideForGuest string
|
||||
ID uint64
|
||||
Name string `binding:"required"`
|
||||
DisplayIndex int
|
||||
Secret string
|
||||
Tag string
|
||||
Note string
|
||||
PublicNote string
|
||||
HideForGuest string
|
||||
EnableDDNS string
|
||||
DDNSProfilesRaw string
|
||||
}
|
||||
|
||||
func (ma *memberAPI) addOrEditServer(c *gin.Context) {
|
||||
@@ -303,15 +353,21 @@ func (ma *memberAPI) addOrEditServer(c *gin.Context) {
|
||||
s.ID = sf.ID
|
||||
s.Tag = sf.Tag
|
||||
s.Note = sf.Note
|
||||
s.PublicNote = sf.PublicNote
|
||||
s.HideForGuest = sf.HideForGuest == "on"
|
||||
if s.ID == 0 {
|
||||
s.Secret, err = utils.GenerateRandomString(18)
|
||||
if err == nil {
|
||||
err = singleton.DB.Create(&s).Error
|
||||
s.EnableDDNS = sf.EnableDDNS == "on"
|
||||
s.DDNSProfilesRaw = sf.DDNSProfilesRaw
|
||||
err = utils.Json.Unmarshal([]byte(sf.DDNSProfilesRaw), &s.DDNSProfiles)
|
||||
if err == nil {
|
||||
if s.ID == 0 {
|
||||
s.Secret, err = utils.GenerateRandomString(18)
|
||||
if err == nil {
|
||||
err = singleton.DB.Create(&s).Error
|
||||
}
|
||||
} else {
|
||||
isEdit = true
|
||||
err = singleton.DB.Save(&s).Error
|
||||
}
|
||||
} else {
|
||||
isEdit = true
|
||||
err = singleton.DB.Save(&s).Error
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
@@ -357,6 +413,7 @@ func (ma *memberAPI) addOrEditServer(c *gin.Context) {
|
||||
} else {
|
||||
s.Host = &model.Host{}
|
||||
s.State = &model.HostState{}
|
||||
s.TaskCloseLock = new(sync.Mutex)
|
||||
singleton.ServerLock.Lock()
|
||||
singleton.SecretToID[s.Secret] = s.ID
|
||||
singleton.ServerList[s.ID] = &s
|
||||
@@ -383,6 +440,7 @@ type monitorForm struct {
|
||||
MaxLatency float32
|
||||
LatencyNotify string
|
||||
EnableTriggerTask string
|
||||
EnableShowInService string
|
||||
FailTriggerTasksRaw string
|
||||
RecoverTriggerTasksRaw string
|
||||
}
|
||||
@@ -404,6 +462,7 @@ func (ma *memberAPI) addOrEditMonitor(c *gin.Context) {
|
||||
m.LatencyNotify = mf.LatencyNotify == "on"
|
||||
m.MinLatency = mf.MinLatency
|
||||
m.MaxLatency = mf.MaxLatency
|
||||
m.EnableShowInService = mf.EnableShowInService == "on"
|
||||
m.EnableTriggerTask = mf.EnableTriggerTask == "on"
|
||||
m.RecoverTriggerTasksRaw = mf.RecoverTriggerTasksRaw
|
||||
m.FailTriggerTasksRaw = mf.FailTriggerTasksRaw
|
||||
@@ -414,18 +473,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 {
|
||||
@@ -705,6 +769,120 @@ func (ma *memberAPI) addOrEditNotification(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
type ddnsForm struct {
|
||||
ID uint64
|
||||
MaxRetries uint64
|
||||
EnableIPv4 string
|
||||
EnableIPv6 string
|
||||
Name string
|
||||
Provider uint8
|
||||
DomainsRaw string
|
||||
AccessID string
|
||||
AccessSecret string
|
||||
WebhookURL string
|
||||
WebhookMethod uint8
|
||||
WebhookRequestType uint8
|
||||
WebhookRequestBody string
|
||||
WebhookHeaders string
|
||||
}
|
||||
|
||||
func (ma *memberAPI) addOrEditDDNS(c *gin.Context) {
|
||||
var df ddnsForm
|
||||
var p model.DDNSProfile
|
||||
err := c.ShouldBindJSON(&df)
|
||||
if err == nil {
|
||||
if df.MaxRetries < 1 || df.MaxRetries > 10 {
|
||||
err = errors.New("重试次数必须为大于 1 且不超过 10 的整数")
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
p.Name = df.Name
|
||||
p.ID = df.ID
|
||||
enableIPv4 := df.EnableIPv4 == "on"
|
||||
enableIPv6 := df.EnableIPv6 == "on"
|
||||
p.EnableIPv4 = &enableIPv4
|
||||
p.EnableIPv6 = &enableIPv6
|
||||
p.MaxRetries = df.MaxRetries
|
||||
p.Provider = df.Provider
|
||||
p.DomainsRaw = df.DomainsRaw
|
||||
p.Domains = strings.Split(p.DomainsRaw, ",")
|
||||
p.AccessID = df.AccessID
|
||||
p.AccessSecret = df.AccessSecret
|
||||
p.WebhookURL = df.WebhookURL
|
||||
p.WebhookMethod = df.WebhookMethod
|
||||
p.WebhookRequestType = df.WebhookRequestType
|
||||
p.WebhookRequestBody = df.WebhookRequestBody
|
||||
p.WebhookHeaders = df.WebhookHeaders
|
||||
|
||||
for n, domain := range p.Domains {
|
||||
// IDN to ASCII
|
||||
domainValid, domainErr := idna.Lookup.ToASCII(domain)
|
||||
if domainErr != nil {
|
||||
err = fmt.Errorf("域名 %s 解析错误: %v", domain, domainErr)
|
||||
break
|
||||
}
|
||||
p.Domains[n] = domainValid
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
if p.ID == 0 {
|
||||
err = singleton.DB.Create(&p).Error
|
||||
} else {
|
||||
err = singleton.DB.Save(&p).Error
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, model.Response{
|
||||
Code: http.StatusBadRequest,
|
||||
Message: fmt.Sprintf("请求错误:%s", err),
|
||||
})
|
||||
return
|
||||
}
|
||||
singleton.OnDDNSUpdate()
|
||||
c.JSON(http.StatusOK, model.Response{
|
||||
Code: http.StatusOK,
|
||||
})
|
||||
}
|
||||
|
||||
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
|
||||
@@ -819,6 +997,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 {
|
||||
@@ -828,14 +1011,17 @@ type settingForm struct {
|
||||
Theme string
|
||||
DashboardTheme string
|
||||
CustomCode string
|
||||
CustomCodeDashboard string
|
||||
CustomNameservers string
|
||||
ViewPassword string
|
||||
IgnoredIPNotification string
|
||||
IPChangeNotificationTag string // IP变更提醒的通知组
|
||||
GRPCHost string
|
||||
Cover uint8
|
||||
|
||||
EnableIPChangeNotification string
|
||||
EnablePlainIPInNotification string
|
||||
EnableIPChangeNotification string
|
||||
EnablePlainIPInNotification string
|
||||
DisableSwitchTemplateInFrontend string
|
||||
}
|
||||
|
||||
func (ma *memberAPI) updateSetting(c *gin.Context) {
|
||||
@@ -856,7 +1042,7 @@ func (ma *memberAPI) updateSetting(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if _, yes := model.Themes[sf.DashboardTheme]; !yes {
|
||||
if _, yes := model.DashboardThemes[sf.DashboardTheme]; !yes {
|
||||
c.JSON(http.StatusOK, model.Response{
|
||||
Code: http.StatusBadRequest,
|
||||
Message: fmt.Sprintf("后台主题不存在:%s", sf.DashboardTheme),
|
||||
@@ -883,6 +1069,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
|
||||
@@ -891,6 +1078,8 @@ func (ma *memberAPI) updateSetting(c *gin.Context) {
|
||||
singleton.Conf.Site.Theme = sf.Theme
|
||||
singleton.Conf.Site.DashboardTheme = sf.DashboardTheme
|
||||
singleton.Conf.Site.CustomCode = sf.CustomCode
|
||||
singleton.Conf.Site.CustomCodeDashboard = sf.CustomCodeDashboard
|
||||
singleton.Conf.DNSServers = sf.CustomNameservers
|
||||
singleton.Conf.Site.ViewPassword = sf.ViewPassword
|
||||
singleton.Conf.Oauth2.Admin = sf.Admin
|
||||
// 保证NotificationTag不为空
|
||||
@@ -906,6 +1095,8 @@ func (ma *memberAPI) updateSetting(c *gin.Context) {
|
||||
}
|
||||
// 更新系统语言
|
||||
singleton.InitLocalizer()
|
||||
// 更新DNS服务器
|
||||
singleton.OnNameserverUpdate()
|
||||
c.JSON(http.StatusOK, model.Response{
|
||||
Code: http.StatusOK,
|
||||
})
|
||||
|
||||
@@ -17,16 +17,18 @@ type memberPage struct {
|
||||
func (mp *memberPage) serve() {
|
||||
mr := mp.r.Group("")
|
||||
mr.Use(mygin.Authorize(mygin.AuthorizeOption{
|
||||
Member: true,
|
||||
IsPage: true,
|
||||
Msg: singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "YouAreNotAuthorized"}),
|
||||
Btn: singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "Login"}),
|
||||
Redirect: "/login",
|
||||
MemberOnly: true,
|
||||
IsPage: true,
|
||||
Msg: singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "YouAreNotAuthorized"}),
|
||||
Btn: singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "Login"}),
|
||||
Redirect: "/login",
|
||||
}))
|
||||
mr.GET("/server", mp.server)
|
||||
mr.GET("/monitor", mp.monitor)
|
||||
mr.GET("/cron", mp.cron)
|
||||
mr.GET("/notification", mp.notification)
|
||||
mr.GET("/ddns", mp.ddns)
|
||||
mr.GET("/nat", mp.nat)
|
||||
mr.GET("/setting", mp.setting)
|
||||
mr.GET("/api", mp.api)
|
||||
}
|
||||
@@ -77,11 +79,30 @@ func (mp *memberPage) notification(c *gin.Context) {
|
||||
}))
|
||||
}
|
||||
|
||||
func (mp *memberPage) ddns(c *gin.Context) {
|
||||
var data []model.DDNSProfile
|
||||
singleton.DB.Find(&data)
|
||||
c.HTML(http.StatusOK, "dashboard-"+singleton.Conf.Site.DashboardTheme+"/ddns", mygin.CommonEnvironment(c, gin.H{
|
||||
"Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "DDNS"}),
|
||||
"DDNS": data,
|
||||
"ProviderMap": model.ProviderMap,
|
||||
"ProviderList": model.ProviderList,
|
||||
}))
|
||||
}
|
||||
|
||||
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"}),
|
||||
"Languages": model.Languages,
|
||||
"Themes": model.Themes,
|
||||
"DashboardThemes": model.DashboardThemes,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -9,23 +9,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 +78,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 +122,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 +192,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 := utils.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 +227,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 +246,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 +281,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
|
||||
}
|
||||
|
||||
+29
-8
@@ -1,13 +1,17 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
_ "time/tzdata"
|
||||
|
||||
"github.com/naiba/nezha/cmd/dashboard/controller"
|
||||
"github.com/naiba/nezha/cmd/dashboard/rpc"
|
||||
"github.com/naiba/nezha/model"
|
||||
"github.com/naiba/nezha/proto"
|
||||
"github.com/naiba/nezha/service/singleton"
|
||||
"github.com/ory/graceful"
|
||||
flag "github.com/spf13/pflag"
|
||||
@@ -29,13 +33,6 @@ func init() {
|
||||
flag.StringVarP(&dashboardCliParam.ConfigFile, "config", "c", "data/config.yaml", "配置文件路径")
|
||||
flag.StringVar(&dashboardCliParam.DatebaseLocation, "db", "data/sqlite.db", "Sqlite3数据库文件路径")
|
||||
flag.Parse()
|
||||
|
||||
// 初始化 dao 包
|
||||
singleton.InitConfigFromPath(dashboardCliParam.ConfigFile)
|
||||
singleton.InitTimezoneAndCache()
|
||||
singleton.InitDBFromPath(dashboardCliParam.DatebaseLocation)
|
||||
singleton.InitLocalizer()
|
||||
initSystem()
|
||||
}
|
||||
|
||||
func initSystem() {
|
||||
@@ -56,9 +53,17 @@ func initSystem() {
|
||||
func main() {
|
||||
if dashboardCliParam.Version {
|
||||
fmt.Println(singleton.Version)
|
||||
return
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// 初始化 dao 包
|
||||
singleton.InitConfigFromPath(dashboardCliParam.ConfigFile)
|
||||
singleton.InitTimezoneAndCache()
|
||||
singleton.InitDBFromPath(dashboardCliParam.DatebaseLocation)
|
||||
singleton.InitLocalizer()
|
||||
initSystem()
|
||||
|
||||
// TODO 使用 cmux 在同一端口服务 HTTP 和 gRPC
|
||||
singleton.CleanMonitorHistory()
|
||||
go rpc.ServeRPC(singleton.Conf.GRPCPort)
|
||||
serviceSentinelDispatchBus := make(chan model.Monitor) // 用于传递服务监控任务信息的channel
|
||||
@@ -67,6 +72,7 @@ func main() {
|
||||
go singleton.AlertSentinelStart()
|
||||
singleton.NewServiceSentinel(serviceSentinelDispatchBus)
|
||||
srv := controller.ServeWeb(singleton.Conf.HTTPPort)
|
||||
go dispatchReportInfoTask()
|
||||
if err := graceful.Graceful(func() error {
|
||||
return srv.ListenAndServe()
|
||||
}, func(c context.Context) error {
|
||||
@@ -79,3 +85,18 @@ func main() {
|
||||
log.Printf("NEZHA>> ERROR: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func dispatchReportInfoTask() {
|
||||
time.Sleep(time.Second * 15)
|
||||
singleton.ServerLock.RLock()
|
||||
defer singleton.ServerLock.RUnlock()
|
||||
for _, server := range singleton.ServerList {
|
||||
if server == nil || server.TaskStream == nil {
|
||||
continue
|
||||
}
|
||||
server.TaskStream.Send(&proto.Task{
|
||||
Type: model.TaskTypeReportHostInfo,
|
||||
Data: "",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
@@ -49,10 +48,20 @@ func DispatchTask(serviceSentinelDispatchBus <-chan model.Monitor) {
|
||||
workedServerIndex++
|
||||
continue
|
||||
}
|
||||
if task.Cover == model.MonitorCoverIgnoreAll && task.SkipServers[singleton.SortedServerList[workedServerIndex].ID] {
|
||||
singleton.SortedServerList[workedServerIndex].TaskStream.Send(task.PB())
|
||||
workedServerIndex++
|
||||
continue
|
||||
}
|
||||
if task.Cover == model.MonitorCoverAll && !task.SkipServers[singleton.SortedServerList[workedServerIndex].ID] {
|
||||
singleton.SortedServerList[workedServerIndex].TaskStream.Send(task.PB())
|
||||
workedServerIndex++
|
||||
continue
|
||||
}
|
||||
// 找到合适机器执行任务,跳出循环
|
||||
singleton.SortedServerList[workedServerIndex].TaskStream.Send(task.PB())
|
||||
workedServerIndex++
|
||||
break
|
||||
// singleton.SortedServerList[workedServerIndex].TaskStream.Send(task.PB())
|
||||
// workedServerIndex++
|
||||
// break
|
||||
}
|
||||
singleton.SortedServerLock.RUnlock()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
services:
|
||||
app:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.dev
|
||||
args:
|
||||
COUNTRY: CN
|
||||
image: nezha:dev
|
||||
container_name: nezha-dev
|
||||
ports:
|
||||
- ${NEZHA_PORT:-80}:18080
|
||||
- 5555:5555
|
||||
volumes:
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- ./data:/dashboard/data
|
||||
# - ./resource:/dashboard/resource
|
||||
@@ -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,24 +14,32 @@ 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/knadh/koanf/parsers/yaml v0.1.0
|
||||
github.com/knadh/koanf/providers/env v1.0.0
|
||||
github.com/knadh/koanf/providers/file v1.1.2
|
||||
github.com/knadh/koanf/v2 v2.1.2
|
||||
github.com/libdns/cloudflare v0.1.1
|
||||
github.com/libdns/libdns v0.2.2
|
||||
github.com/miekg/dns v1.1.62
|
||||
github.com/nezhahq/libdns-tencentcloud v0.0.0-20241029120103-889957240fff
|
||||
github.com/nicksnyder/go-i18n/v2 v2.4.0
|
||||
github.com/ory/graceful v0.1.3
|
||||
github.com/oschwald/maxminddb-golang v1.13.1
|
||||
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.15.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
|
||||
sigs.k8s.io/yaml v1.4.0
|
||||
github.com/tidwall/gjson v1.18.0
|
||||
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
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
gorm.io/driver/sqlite v1.5.5
|
||||
gorm.io/gorm v1.25.10
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -40,50 +47,44 @@ require (
|
||||
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/go-viper/mapstructure/v2 v2.2.1 // 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
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
|
||||
github.com/knadh/koanf/maps v0.1.1 // indirect
|
||||
github.com/kr/pretty v0.3.1 // 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/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // 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/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/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
github.com/tidwall/sjson v1.2.5 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
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.14.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
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
golang.org/x/mod v0.18.0 // indirect
|
||||
golang.org/x/sys v0.22.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/tools v0.22.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // 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,10 @@ 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/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 +34,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=
|
||||
@@ -108,96 +52,35 @@ github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg
|
||||
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
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 +89,19 @@ 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/knadh/koanf/maps v0.1.1 h1:G5TjmUh2D7G2YWf5SQQqSiHRJEjaicvU0KpypqB3NIs=
|
||||
github.com/knadh/koanf/maps v0.1.1/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI=
|
||||
github.com/knadh/koanf/parsers/yaml v0.1.0 h1:ZZ8/iGfRLvKSaMEECEBPM1HQslrZADk8fP1XFUxVI5w=
|
||||
github.com/knadh/koanf/parsers/yaml v0.1.0/go.mod h1:cvbUDC7AL23pImuQP0oRw/hPuccrNBS2bps8asS0CwY=
|
||||
github.com/knadh/koanf/providers/env v1.0.0 h1:ufePaI9BnWH+ajuxGGiJ8pdTG0uLEUWC7/HDDPGLah0=
|
||||
github.com/knadh/koanf/providers/env v1.0.0/go.mod h1:mzFyRZueYhb37oPmC1HAv/oGEEuyvJDA98r3XAa8Gak=
|
||||
github.com/knadh/koanf/providers/file v1.1.2 h1:aCC36YGOgV5lTtAFz2qkgtWdeQsgfxUkxDOe+2nQY3w=
|
||||
github.com/knadh/koanf/providers/file v1.1.2/go.mod h1:/faSBcv2mxPVjFrXck95qeoyoZ5myJ6uxN8OOVNJJCI=
|
||||
github.com/knadh/koanf/v2 v2.1.2 h1:I2rtLRqXRy1p01m/utEtpZSSA6dcJbgGVuE27kW2PzQ=
|
||||
github.com/knadh/koanf/v2 v2.1.2/go.mod h1:Gphfaen0q1Fc1HTgJgSTC4oRX9R2R5ErYMZJy8fLJBo=
|
||||
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=
|
||||
@@ -225,30 +114,40 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
||||
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/libdns/cloudflare v0.1.1 h1:FVPfWwP8zZCqj268LZjmkDleXlHPlFU9KC4OJ3yn054=
|
||||
github.com/libdns/cloudflare v0.1.1/go.mod h1:9VK91idpOjg6v7/WbjkEW49bSCxj00ALesIFDhJ8PBU=
|
||||
github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s=
|
||||
github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
|
||||
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=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
|
||||
github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
|
||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
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/nezhahq/libdns-tencentcloud v0.0.0-20241029120103-889957240fff h1:3WDsbsg3dmsRENYLUPGPTkEcWWmTvk41i+rM91AIIbY=
|
||||
github.com/nezhahq/libdns-tencentcloud v0.0.0-20241029120103-889957240fff/go.mod h1:k+cDtTbUY+UV56Aqv4Ahmc9bWmhpga9JJXZlAIPKBEc=
|
||||
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 +156,21 @@ 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/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/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/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=
|
||||
@@ -298,389 +178,91 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
|
||||
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
|
||||
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
|
||||
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=
|
||||
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=
|
||||
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
|
||||
github.com/xanzy/go-gitlab v0.103.0 h1:J9pTQoq0GsEFqzd6srCM1QfdfKAxSNz6mT6ntrpNF2w=
|
||||
github.com/xanzy/go-gitlab v0.103.0/go.mod h1:ETg8tcj4OhrB84UEgeE8dSuV/0h4BBL1uOV/qK0vlyI=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
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.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
|
||||
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
|
||||
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/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/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/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
|
||||
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
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.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
|
||||
golang.org/x/sys v0.14.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.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8=
|
||||
golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww=
|
||||
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.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
|
||||
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
|
||||
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=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
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=
|
||||
|
||||
+7
-7
@@ -85,13 +85,13 @@ func (r *AlertRule) Snapshot(cycleTransferStats *CycleTransferStats, server *Ser
|
||||
|
||||
// Check 传入包含当前报警规则下所有type检查结果的空接口 返回报警持续时间与是否通过报警检查(通过则返回true)
|
||||
func (r *AlertRule) Check(points [][]interface{}) (int, bool) {
|
||||
var max int // 报警持续时间
|
||||
var count int // 检查未通过的个数
|
||||
var maxNum int // 报警持续时间
|
||||
var count int // 检查未通过的个数
|
||||
for i := 0; i < len(r.Rules); i++ {
|
||||
if r.Rules[i].IsTransferDurationRule() {
|
||||
// 循环区间流量报警
|
||||
if max < 1 {
|
||||
max = 1
|
||||
if maxNum < 1 {
|
||||
maxNum = 1
|
||||
}
|
||||
for j := len(points[i]) - 1; j >= 0; j-- {
|
||||
if points[i][j] != nil {
|
||||
@@ -104,8 +104,8 @@ func (r *AlertRule) Check(points [][]interface{}) (int, bool) {
|
||||
total := 0.0
|
||||
fail := 0.0
|
||||
num := int(r.Rules[i].Duration)
|
||||
if num > max {
|
||||
max = num
|
||||
if num > maxNum {
|
||||
maxNum = num
|
||||
}
|
||||
if len(points) < num {
|
||||
continue
|
||||
@@ -124,5 +124,5 @@ func (r *AlertRule) Check(points [][]interface{}) (int, bool) {
|
||||
}
|
||||
}
|
||||
// 仅当所有检查均未通过时 返回false
|
||||
return max, count != len(r.Rules)
|
||||
return maxNum, count != len(r.Rules)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
const CtxKeyAuthorizedUser = "ckau"
|
||||
const CtxKeyViewPasswordVerified = "ckvpv"
|
||||
const CtxKeyPreferredTheme = "ckpt"
|
||||
const CacheKeyOauth2State = "p:a:state"
|
||||
|
||||
type Common struct {
|
||||
|
||||
+93
-55
@@ -1,12 +1,16 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"sigs.k8s.io/yaml"
|
||||
kyaml "github.com/knadh/koanf/parsers/yaml"
|
||||
"github.com/knadh/koanf/providers/env"
|
||||
"github.com/knadh/koanf/providers/file"
|
||||
"github.com/knadh/koanf/v2"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
var Languages = map[string]string{
|
||||
@@ -22,7 +26,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 +36,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 (
|
||||
@@ -44,53 +50,35 @@ const (
|
||||
ConfigCoverIgnoreAll
|
||||
)
|
||||
|
||||
type AgentConfig struct {
|
||||
HardDrivePartitionAllowlist []string
|
||||
NICAllowlist map[string]bool
|
||||
v *viper.Viper
|
||||
}
|
||||
|
||||
// Read 从给定的文件目录加载配置文件
|
||||
func (c *AgentConfig) Read(path string) error {
|
||||
c.v = viper.New()
|
||||
c.v.SetConfigFile(path)
|
||||
err := c.v.ReadInConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.v.Unmarshal(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *AgentConfig) Save() error {
|
||||
data, err := yaml.Marshal(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(c.v.ConfigFileUsed(), data, os.ModePerm)
|
||||
}
|
||||
|
||||
// Config 站点配置
|
||||
type Config struct {
|
||||
Debug bool // debug模式开关
|
||||
Language string // 系统语言,默认 zh-CN
|
||||
Site struct {
|
||||
Brand string // 站点名称
|
||||
CookieName string // 浏览器 Cookie 名称
|
||||
Theme string
|
||||
DashboardTheme string
|
||||
CustomCode string
|
||||
ViewPassword string // 前台查看密码
|
||||
Brand string // 站点名称
|
||||
CookieName string // 浏览器 Cookie 名称
|
||||
Theme string
|
||||
DashboardTheme string
|
||||
CustomCode string
|
||||
CustomCodeDashboard string
|
||||
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 +86,8 @@ type Config struct {
|
||||
ProxyGRPCPort uint
|
||||
TLS bool
|
||||
|
||||
EnablePlainIPInNotification bool // 通知信息IP不打码
|
||||
EnablePlainIPInNotification bool // 通知信息IP不打码
|
||||
DisableSwitchTemplateInFrontend bool // 前台禁用切换模板功能
|
||||
|
||||
// IP变更提醒
|
||||
EnableIPChangeNotification bool
|
||||
@@ -108,24 +97,52 @@ type Config struct {
|
||||
|
||||
Location string // 时区,默认为 Asia/Shanghai
|
||||
|
||||
v *viper.Viper
|
||||
IgnoredIPNotificationServerIDs map[uint64]bool // [ServerID] -> bool(值为true代表当前ServerID在特定服务器列表内)
|
||||
MaxTCPPingValue int32
|
||||
AvgPingCount int
|
||||
|
||||
DNSServers string
|
||||
|
||||
k *koanf.Koanf
|
||||
filePath string
|
||||
}
|
||||
|
||||
// Read 读取配置文件并应用
|
||||
func (c *Config) Read(path string) error {
|
||||
c.v = viper.New()
|
||||
c.v.SetConfigFile(path)
|
||||
err := c.v.ReadInConfig()
|
||||
c.k = koanf.New(".")
|
||||
c.filePath = path
|
||||
|
||||
// 先读取环境变量,然后读取配置文件;后者可以覆盖前者,因为哪吒支持在线修改配置
|
||||
|
||||
err := c.k.Load(env.Provider("NZ_", ".", func(s string) string {
|
||||
return strings.Replace(strings.ToLower(strings.TrimPrefix(s, "NZ_")), "_", ".", -1)
|
||||
}), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = c.v.Unmarshal(c)
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
err = c.k.Load(file.Provider(path), kyaml.Parser())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = c.k.Unmarshal("", c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.Oauth2.Type == "" || c.Oauth2.Admin == "" || c.Oauth2.ClientID == "" || c.Oauth2.ClientSecret == "" {
|
||||
return errors.New("missing oauth2 config")
|
||||
}
|
||||
|
||||
if c.Site.Brand == "" {
|
||||
c.Site.Brand = "Nezha Monitoring"
|
||||
}
|
||||
if c.Site.CookieName == "" {
|
||||
c.Site.CookieName = "nezha-dashboard"
|
||||
}
|
||||
if c.Site.Theme == "" {
|
||||
c.Site.Theme = "default"
|
||||
}
|
||||
@@ -135,6 +152,9 @@ func (c *Config) Read(path string) error {
|
||||
if c.Language == "" {
|
||||
c.Language = "zh-CN"
|
||||
}
|
||||
if c.HTTPPort == 0 {
|
||||
c.HTTPPort = 80
|
||||
}
|
||||
if c.GRPCPort == 0 {
|
||||
c.GRPCPort = 5555
|
||||
}
|
||||
@@ -144,6 +164,24 @@ func (c *Config) Read(path string) error {
|
||||
if c.Location == "" {
|
||||
c.Location = "Asia/Shanghai"
|
||||
}
|
||||
if c.MaxTCPPingValue == 0 {
|
||||
c.MaxTCPPingValue = 1000
|
||||
}
|
||||
if c.AvgPingCount == 0 {
|
||||
c.AvgPingCount = 2
|
||||
}
|
||||
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
|
||||
@@ -168,5 +206,5 @@ func (c *Config) Save() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(c.v.ConfigFileUsed(), data, os.ModePerm)
|
||||
return os.WriteFile(c.filePath, data, 0600)
|
||||
}
|
||||
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
const (
|
||||
ProviderDummy = iota
|
||||
ProviderWebHook
|
||||
ProviderCloudflare
|
||||
ProviderTencentCloud
|
||||
)
|
||||
|
||||
const (
|
||||
_Dummy = "dummy"
|
||||
_WebHook = "webhook"
|
||||
_Cloudflare = "cloudflare"
|
||||
_TencentCloud = "tencentcloud"
|
||||
)
|
||||
|
||||
var ProviderMap = map[uint8]string{
|
||||
ProviderDummy: _Dummy,
|
||||
ProviderWebHook: _WebHook,
|
||||
ProviderCloudflare: _Cloudflare,
|
||||
ProviderTencentCloud: _TencentCloud,
|
||||
}
|
||||
|
||||
var ProviderList = []DDNSProvider{
|
||||
{
|
||||
Name: _Dummy,
|
||||
ID: ProviderDummy,
|
||||
},
|
||||
{
|
||||
Name: _Cloudflare,
|
||||
ID: ProviderCloudflare,
|
||||
AccessSecret: true,
|
||||
},
|
||||
{
|
||||
Name: _TencentCloud,
|
||||
ID: ProviderTencentCloud,
|
||||
AccessID: true,
|
||||
AccessSecret: true,
|
||||
},
|
||||
// Least frequently used, always place this at the end
|
||||
{
|
||||
Name: _WebHook,
|
||||
ID: ProviderWebHook,
|
||||
AccessID: true,
|
||||
AccessSecret: true,
|
||||
WebhookURL: true,
|
||||
WebhookMethod: true,
|
||||
WebhookRequestType: true,
|
||||
WebhookRequestBody: true,
|
||||
WebhookHeaders: true,
|
||||
},
|
||||
}
|
||||
|
||||
type DDNSProfile struct {
|
||||
Common
|
||||
EnableIPv4 *bool
|
||||
EnableIPv6 *bool
|
||||
MaxRetries uint64
|
||||
Name string
|
||||
Provider uint8
|
||||
AccessID string
|
||||
AccessSecret string
|
||||
WebhookURL string
|
||||
WebhookMethod uint8
|
||||
WebhookRequestType uint8
|
||||
WebhookRequestBody string
|
||||
WebhookHeaders string
|
||||
|
||||
Domains []string `gorm:"-"`
|
||||
DomainsRaw string
|
||||
}
|
||||
|
||||
func (d DDNSProfile) TableName() string {
|
||||
return "ddns"
|
||||
}
|
||||
|
||||
func (d *DDNSProfile) AfterFind(tx *gorm.DB) error {
|
||||
if d.DomainsRaw != "" {
|
||||
d.Domains = strings.Split(d.DomainsRaw, ",")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type DDNSProvider struct {
|
||||
Name string
|
||||
ID uint8
|
||||
AccessID bool
|
||||
AccessSecret bool
|
||||
WebhookURL bool
|
||||
WebhookMethod bool
|
||||
WebhookRequestType bool
|
||||
WebhookRequestBody bool
|
||||
WebhookHeaders bool
|
||||
}
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
+20
-12
@@ -4,32 +4,39 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/naiba/nezha/pkg/utils"
|
||||
pb "github.com/naiba/nezha/proto"
|
||||
"github.com/robfig/cron/v3"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/naiba/nezha/pkg/utils"
|
||||
pb "github.com/naiba/nezha/proto"
|
||||
)
|
||||
|
||||
const (
|
||||
_ = iota
|
||||
TaskTypeHTTPGET
|
||||
TaskTypeHTTPGet
|
||||
TaskTypeICMPPing
|
||||
TaskTypeTCPPing
|
||||
TaskTypeCommand
|
||||
TaskTypeTerminal
|
||||
TaskTypeUpgrade
|
||||
TaskTypeKeepalive
|
||||
TaskTypeTerminalGRPC
|
||||
TaskTypeNAT
|
||||
TaskTypeReportHostInfo
|
||||
TaskTypeFM
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
type TaskFM struct {
|
||||
StreamID string
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -49,6 +56,7 @@ type Monitor struct {
|
||||
Cover uint8
|
||||
|
||||
EnableTriggerTask bool `gorm:"default: false"`
|
||||
EnableShowInService bool `gorm:"default: false"`
|
||||
FailTriggerTasksRaw string `gorm:"default:'[]'"`
|
||||
RecoverTriggerTasksRaw string `gorm:"default:'[]'"`
|
||||
FailTriggerTasks []uint64 `gorm:"-" json:"-"` // 失败时执行的触发任务id
|
||||
@@ -118,7 +126,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 {
|
||||
|
||||
@@ -1,11 +1,21 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// MonitorHistory 历史监控记录
|
||||
type MonitorHistory struct {
|
||||
Common
|
||||
MonitorID uint64
|
||||
AvgDelay float32 // 平均延迟,毫秒
|
||||
Up uint64 // 检查状态良好计数
|
||||
Down uint64 // 检查状态异常计数
|
||||
ID uint64 `gorm:"primaryKey"`
|
||||
CreatedAt time.Time `gorm:"index;<-:create;index:idx_server_id_created_at_monitor_id_avg_delay"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index"`
|
||||
MonitorID uint64 `gorm:"index:idx_server_id_created_at_monitor_id_avg_delay"`
|
||||
ServerID uint64 `gorm:"index:idx_server_id_created_at_monitor_id_avg_delay"`
|
||||
AvgDelay float32 `gorm:"index:idx_server_id_created_at_monitor_id_avg_delay"` // 平均延迟,毫秒
|
||||
Up uint64 // 检查状态良好计数
|
||||
Down uint64 // 检查状态异常计数
|
||||
Data string
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package model
|
||||
|
||||
type NAT struct {
|
||||
Common
|
||||
Name string
|
||||
ServerID uint64
|
||||
Host string
|
||||
Domain string `gorm:"unique"`
|
||||
}
|
||||
+22
-15
@@ -1,7 +1,6 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -72,8 +71,8 @@ func (ns *NotificationServerBundle) reqBody(message string) (string, error) {
|
||||
return string(msgBytes)[1 : len(msgBytes)-1]
|
||||
}), nil
|
||||
case NotificationRequestTypeForm:
|
||||
var data map[string]string
|
||||
if err := utils.Json.Unmarshal([]byte(n.RequestBody), &data); err != nil {
|
||||
data, err := utils.GjsonParseStringMap(n.RequestBody)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
params := url.Values{}
|
||||
@@ -100,8 +99,8 @@ func (n *Notification) setRequestHeader(req *http.Request) error {
|
||||
if n.RequestHeader == "" {
|
||||
return nil
|
||||
}
|
||||
var m map[string]string
|
||||
if err := utils.Json.Unmarshal([]byte(n.RequestHeader), &m); err != nil {
|
||||
m, err := utils.GjsonParseStringMap(n.RequestHeader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for k, v := range m {
|
||||
@@ -111,17 +110,14 @@ func (n *Notification) setRequestHeader(req *http.Request) error {
|
||||
}
|
||||
|
||||
func (ns *NotificationServerBundle) Send(message string) error {
|
||||
var verifySSL bool
|
||||
var client *http.Client
|
||||
n := ns.Notification
|
||||
if n.VerifySSL != nil && *n.VerifySSL {
|
||||
verifySSL = true
|
||||
client = utils.HttpClient
|
||||
} else {
|
||||
client = utils.HttpClientSkipTlsVerify
|
||||
}
|
||||
|
||||
transCfg := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: verifySSL},
|
||||
}
|
||||
|
||||
client := &http.Client{Transport: transCfg, Timeout: time.Minute * 10}
|
||||
reqBody, err := ns.reqBody(message)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -147,13 +143,15 @@ func (ns *NotificationServerBundle) Send(message string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
if resp.StatusCode < 200 || resp.StatusCode > 299 {
|
||||
defer func() {
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
return fmt.Errorf("%d@%s %s", resp.StatusCode, resp.Status, string(body))
|
||||
} else {
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -172,14 +170,23 @@ func (ns *NotificationServerBundle) replaceParamsInString(str string, message st
|
||||
|
||||
if ns.Server != nil {
|
||||
str = strings.ReplaceAll(str, "#SERVER.NAME#", mod(ns.Server.Name))
|
||||
str = strings.ReplaceAll(str, "#SERVER.ID#", mod(fmt.Sprintf("%d", ns.Server.ID)))
|
||||
str = strings.ReplaceAll(str, "#SERVER.CPU#", mod(fmt.Sprintf("%f", ns.Server.State.CPU)))
|
||||
str = strings.ReplaceAll(str, "#SERVER.MEM#", mod(fmt.Sprintf("%d", ns.Server.State.MemUsed)))
|
||||
str = strings.ReplaceAll(str, "#SERVER.SWAP#", mod(fmt.Sprintf("%d", ns.Server.State.SwapUsed)))
|
||||
str = strings.ReplaceAll(str, "#SERVER.DISK#", mod(fmt.Sprintf("%d", ns.Server.State.DiskUsed)))
|
||||
str = strings.ReplaceAll(str, "#SERVER.MEMUSED#", mod(fmt.Sprintf("%d", ns.Server.State.MemUsed)))
|
||||
str = strings.ReplaceAll(str, "#SERVER.SWAPUSED#", mod(fmt.Sprintf("%d", ns.Server.State.SwapUsed)))
|
||||
str = strings.ReplaceAll(str, "#SERVER.DISKUSED#", mod(fmt.Sprintf("%d", ns.Server.State.DiskUsed)))
|
||||
str = strings.ReplaceAll(str, "#SERVER.MEMTOTAL#", mod(fmt.Sprintf("%d", ns.Server.Host.MemTotal)))
|
||||
str = strings.ReplaceAll(str, "#SERVER.SWAPTOTAL#", mod(fmt.Sprintf("%d", ns.Server.Host.SwapTotal)))
|
||||
str = strings.ReplaceAll(str, "#SERVER.DISKTOTAL#", mod(fmt.Sprintf("%d", ns.Server.Host.DiskTotal)))
|
||||
str = strings.ReplaceAll(str, "#SERVER.NETINSPEED#", mod(fmt.Sprintf("%d", ns.Server.State.NetInSpeed)))
|
||||
str = strings.ReplaceAll(str, "#SERVER.NETOUTSPEED#", mod(fmt.Sprintf("%d", ns.Server.State.NetOutSpeed)))
|
||||
str = strings.ReplaceAll(str, "#SERVER.TRANSFERIN#", mod(fmt.Sprintf("%d", ns.Server.State.NetInTransfer)))
|
||||
str = strings.ReplaceAll(str, "#SERVER.TRANSFEROUT#", mod(fmt.Sprintf("%d", ns.Server.State.NetOutTransfer)))
|
||||
str = strings.ReplaceAll(str, "#SERVER.NETINTRANSFER#", mod(fmt.Sprintf("%d", ns.Server.State.NetInTransfer)))
|
||||
str = strings.ReplaceAll(str, "#SERVER.NETOUTTRANSFER#", mod(fmt.Sprintf("%d", ns.Server.State.NetOutTransfer)))
|
||||
str = strings.ReplaceAll(str, "#SERVER.LOAD1#", mod(fmt.Sprintf("%f", ns.Server.State.Load1)))
|
||||
str = strings.ReplaceAll(str, "#SERVER.LOAD5#", mod(fmt.Sprintf("%f", ns.Server.State.Load5)))
|
||||
str = strings.ReplaceAll(str, "#SERVER.LOAD15#", mod(fmt.Sprintf("%f", ns.Server.State.Load15)))
|
||||
|
||||
+29
-15
@@ -5,8 +5,6 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -74,32 +72,48 @@ func execCase(t *testing.T, item testSt) {
|
||||
UdpConnCount: 0,
|
||||
ProcessCount: 0,
|
||||
},
|
||||
LastActive: time.Time{},
|
||||
TaskClose: nil,
|
||||
TaskStream: nil,
|
||||
PrevHourlyTransferIn: 0,
|
||||
PrevHourlyTransferOut: 0,
|
||||
LastActive: time.Time{},
|
||||
TaskClose: nil,
|
||||
TaskStream: nil,
|
||||
PrevTransferInSnapshot: 0,
|
||||
PrevTransferOutSnapshot: 0,
|
||||
}
|
||||
ns := NotificationServerBundle{
|
||||
Notification: &n,
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+21
-6
@@ -1,10 +1,13 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/naiba/nezha/pkg/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -63,6 +66,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":
|
||||
@@ -88,24 +93,24 @@ func (u *Rule) Snapshot(cycleTransferStats *CycleTransferStats, server *Server,
|
||||
src = float64(server.LastActive.Unix())
|
||||
}
|
||||
case "transfer_in_cycle":
|
||||
src = float64(server.State.NetInTransfer - uint64(server.PrevHourlyTransferIn))
|
||||
src = float64(utils.Uint64SubInt64(server.State.NetInTransfer, server.PrevTransferInSnapshot))
|
||||
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))
|
||||
src = float64(utils.Uint64SubInt64(server.State.NetOutTransfer, server.PrevTransferOutSnapshot))
|
||||
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))
|
||||
src = float64(utils.Uint64SubInt64(server.State.NetOutTransfer, server.PrevTransferOutSnapshot) + utils.Uint64SubInt64(server.State.NetInTransfer, server.PrevTransferInSnapshot))
|
||||
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 +125,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 = slices.Max(temp)
|
||||
}
|
||||
}
|
||||
|
||||
// 循环区间流量检测 · 更新下次需要检测时间
|
||||
|
||||
+35
-13
@@ -3,30 +3,39 @@ package model
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/naiba/nezha/pkg/utils"
|
||||
pb "github.com/naiba/nezha/proto"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
Common
|
||||
Name string
|
||||
Tag string // 分组名
|
||||
Secret string `gorm:"uniqueIndex" json:"-"`
|
||||
Note string `json:"-"` // 管理员可见备注
|
||||
DisplayIndex int // 展示排序,越大越靠前
|
||||
HideForGuest bool // 对游客隐藏
|
||||
Tag string // 分组名
|
||||
Secret string `gorm:"uniqueIndex" json:"-"`
|
||||
Note string `json:"-"` // 管理员可见备注
|
||||
PublicNote string `json:"PublicNote,omitempty"` // 公开备注
|
||||
DisplayIndex int // 展示排序,越大越靠前
|
||||
HideForGuest bool // 对游客隐藏
|
||||
EnableDDNS bool // 启用DDNS
|
||||
DDNSProfiles []uint64 `gorm:"-" json:"-"` // DDNS配置
|
||||
|
||||
DDNSProfilesRaw string `gorm:"default:'[]';column:ddns_profiles_raw" json:"-"`
|
||||
|
||||
Host *Host `gorm:"-"`
|
||||
State *HostState `gorm:"-"`
|
||||
LastActive time.Time `gorm:"-"`
|
||||
|
||||
TaskClose chan error `gorm:"-" json:"-"`
|
||||
TaskStream pb.NezhaService_RequestTaskServer `gorm:"-" json:"-"`
|
||||
TaskClose chan error `gorm:"-" json:"-"`
|
||||
TaskCloseLock *sync.Mutex `gorm:"-" json:"-"`
|
||||
TaskStream pb.NezhaService_RequestTaskServer `gorm:"-" json:"-"`
|
||||
|
||||
PrevHourlyTransferIn int64 `gorm:"-" json:"-"` // 上次数据点时的入站使用量
|
||||
PrevHourlyTransferOut int64 `gorm:"-" json:"-"` // 上次数据点时的出站使用量
|
||||
PrevTransferInSnapshot int64 `gorm:"-" json:"-"` // 上次数据点时的入站使用量
|
||||
PrevTransferOutSnapshot int64 `gorm:"-" json:"-"` // 上次数据点时的出站使用量
|
||||
}
|
||||
|
||||
func (s *Server) CopyFromRunningServer(old *Server) {
|
||||
@@ -34,9 +43,20 @@ func (s *Server) CopyFromRunningServer(old *Server) {
|
||||
s.State = old.State
|
||||
s.LastActive = old.LastActive
|
||||
s.TaskClose = old.TaskClose
|
||||
s.TaskCloseLock = old.TaskCloseLock
|
||||
s.TaskStream = old.TaskStream
|
||||
s.PrevHourlyTransferIn = old.PrevHourlyTransferIn
|
||||
s.PrevHourlyTransferOut = old.PrevHourlyTransferOut
|
||||
s.PrevTransferInSnapshot = old.PrevTransferInSnapshot
|
||||
s.PrevTransferOutSnapshot = old.PrevTransferOutSnapshot
|
||||
}
|
||||
|
||||
func (s *Server) AfterFind(tx *gorm.DB) error {
|
||||
if s.DDNSProfilesRaw != "" {
|
||||
if err := utils.Json.Unmarshal([]byte(s.DDNSProfilesRaw), &s.DDNSProfiles); err != nil {
|
||||
log.Println("NEZHA>> Server.AfterFind:", err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func boolToString(b bool) string {
|
||||
@@ -46,10 +66,12 @@ func boolToString(b bool) string {
|
||||
return "false"
|
||||
}
|
||||
|
||||
func (s Server) Marshal() template.JS {
|
||||
func (s Server) MarshalForDashboard() template.JS {
|
||||
name, _ := utils.Json.Marshal(s.Name)
|
||||
tag, _ := utils.Json.Marshal(s.Tag)
|
||||
note, _ := utils.Json.Marshal(s.Note)
|
||||
secret, _ := utils.Json.Marshal(s.Secret)
|
||||
return template.JS(fmt.Sprintf(`{"ID":%d,"Name":%s,"Secret":%s,"DisplayIndex":%d,"Tag":%s,"Note":%s,"HideForGuest": %s}`, s.ID, name, secret, s.DisplayIndex, tag, note, boolToString(s.HideForGuest))) // #nosec
|
||||
ddnsProfilesRaw, _ := utils.Json.Marshal(s.DDNSProfilesRaw)
|
||||
publicNote, _ := utils.Json.Marshal(s.PublicNote)
|
||||
return template.JS(fmt.Sprintf(`{"ID":%d,"Name":%s,"Secret":%s,"DisplayIndex":%d,"Tag":%s,"Note":%s,"HideForGuest": %s,"EnableDDNS": %s,"DDNSProfilesRaw": %s,"PublicNote": %s}`, s.ID, name, secret, s.DisplayIndex, tag, note, boolToString(s.HideForGuest), boolToString(s.EnableDDNS), ddnsProfilesRaw, publicNote))
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/naiba/nezha/pkg/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestServerMarshal(t *testing.T) {
|
||||
@@ -19,9 +18,13 @@ func TestServerMarshal(t *testing.T) {
|
||||
Name: patterns[i],
|
||||
Tag: patterns[i],
|
||||
}
|
||||
serverStr := string(server.Marshal())
|
||||
serverStr := string(server.MarshalForDashboard())
|
||||
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,141 @@
|
||||
package ddns
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/libdns/libdns"
|
||||
"github.com/miekg/dns"
|
||||
|
||||
"github.com/naiba/nezha/model"
|
||||
"github.com/naiba/nezha/pkg/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
dnsTimeOut = 10 * time.Second
|
||||
customDNSServers []string
|
||||
)
|
||||
|
||||
type IP struct {
|
||||
Ipv4Addr string
|
||||
Ipv6Addr string
|
||||
}
|
||||
|
||||
type Provider struct {
|
||||
ctx context.Context
|
||||
ipAddr string
|
||||
recordType string
|
||||
domain string
|
||||
prefix string
|
||||
zone string
|
||||
|
||||
DDNSProfile *model.DDNSProfile
|
||||
IPAddrs *IP
|
||||
Setter libdns.RecordSetter
|
||||
}
|
||||
|
||||
func InitDNSServers(s string) {
|
||||
if s != "" {
|
||||
customDNSServers = strings.Split(s, ",")
|
||||
}
|
||||
}
|
||||
|
||||
func (provider *Provider) UpdateDomain(ctx context.Context) {
|
||||
provider.ctx = ctx
|
||||
for _, domain := range provider.DDNSProfile.Domains {
|
||||
for retries := 0; retries < int(provider.DDNSProfile.MaxRetries); retries++ {
|
||||
provider.domain = domain
|
||||
log.Printf("NEZHA>> 正在尝试更新域名(%s)DDNS(%d/%d)", provider.domain, retries+1, provider.DDNSProfile.MaxRetries)
|
||||
if err := provider.updateDomain(); err != nil {
|
||||
log.Printf("NEZHA>> 尝试更新域名(%s)DDNS失败: %v", provider.domain, err)
|
||||
} else {
|
||||
log.Printf("NEZHA>> 尝试更新域名(%s)DDNS成功", provider.domain)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (provider *Provider) updateDomain() error {
|
||||
var err error
|
||||
provider.prefix, provider.zone, err = splitDomainSOA(provider.domain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 当IPv4和IPv6同时成功才算作成功
|
||||
if *provider.DDNSProfile.EnableIPv4 {
|
||||
provider.recordType = getRecordString(true)
|
||||
provider.ipAddr = provider.IPAddrs.Ipv4Addr
|
||||
if err = provider.addDomainRecord(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if *provider.DDNSProfile.EnableIPv6 {
|
||||
provider.recordType = getRecordString(false)
|
||||
provider.ipAddr = provider.IPAddrs.Ipv6Addr
|
||||
if err = provider.addDomainRecord(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (provider *Provider) addDomainRecord() error {
|
||||
_, err := provider.Setter.SetRecords(provider.ctx, provider.zone,
|
||||
[]libdns.Record{
|
||||
{
|
||||
Type: provider.recordType,
|
||||
Name: provider.prefix,
|
||||
Value: provider.ipAddr,
|
||||
TTL: time.Minute,
|
||||
},
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func splitDomainSOA(domain string) (prefix string, zone string, err error) {
|
||||
c := &dns.Client{Timeout: dnsTimeOut}
|
||||
|
||||
domain += "."
|
||||
indexes := dns.Split(domain)
|
||||
|
||||
servers := utils.DNSServers
|
||||
if len(customDNSServers) > 0 {
|
||||
servers = customDNSServers
|
||||
}
|
||||
|
||||
var r *dns.Msg
|
||||
for _, idx := range indexes {
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion(domain[idx:], dns.TypeSOA)
|
||||
|
||||
for _, server := range servers {
|
||||
r, _, err = c.Exchange(m, server)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(r.Answer) > 0 {
|
||||
if soa, ok := r.Answer[0].(*dns.SOA); ok {
|
||||
zone = soa.Hdr.Name
|
||||
prefix = libdns.RelativeName(domain, zone)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", "", fmt.Errorf("SOA record not found for domain: %s", domain)
|
||||
}
|
||||
|
||||
func getRecordString(isIpv4 bool) string {
|
||||
if isIpv4 {
|
||||
return "A"
|
||||
}
|
||||
return "AAAA"
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package ddns
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type testSt struct {
|
||||
domain string
|
||||
zone string
|
||||
prefix string
|
||||
}
|
||||
|
||||
func TestSplitDomainSOA(t *testing.T) {
|
||||
if ci := os.Getenv("CI"); ci != "" { // skip if test on CI
|
||||
return
|
||||
}
|
||||
|
||||
cases := []testSt{
|
||||
{
|
||||
domain: "www.example.co.uk",
|
||||
zone: "example.co.uk.",
|
||||
prefix: "www",
|
||||
},
|
||||
{
|
||||
domain: "abc.example.com",
|
||||
zone: "example.com.",
|
||||
prefix: "abc",
|
||||
},
|
||||
{
|
||||
domain: "example.com",
|
||||
zone: "example.com.",
|
||||
prefix: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
prefix, zone, err := splitDomainSOA(c.domain)
|
||||
if err != nil {
|
||||
t.Fatalf("Error: %s", err)
|
||||
}
|
||||
if prefix != c.prefix {
|
||||
t.Fatalf("Expected prefix %s, but got %s", c.prefix, prefix)
|
||||
}
|
||||
if zone != c.zone {
|
||||
t.Fatalf("Expected zone %s, but got %s", c.zone, zone)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package dummy
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/libdns/libdns"
|
||||
)
|
||||
|
||||
// Internal use
|
||||
type Provider struct {
|
||||
}
|
||||
|
||||
func (provider *Provider) SetRecords(ctx context.Context, zone string,
|
||||
recs []libdns.Record) ([]libdns.Record, error) {
|
||||
return recs, nil
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
package webhook
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/libdns/libdns"
|
||||
"github.com/naiba/nezha/model"
|
||||
"github.com/naiba/nezha/pkg/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
_ = iota
|
||||
methodGET
|
||||
methodPOST
|
||||
methodPATCH
|
||||
methodDELETE
|
||||
methodPUT
|
||||
)
|
||||
|
||||
const (
|
||||
_ = iota
|
||||
requestTypeJSON
|
||||
requestTypeForm
|
||||
)
|
||||
|
||||
var requestTypes = map[uint8]string{
|
||||
methodGET: "GET",
|
||||
methodPOST: "POST",
|
||||
methodPATCH: "PATCH",
|
||||
methodDELETE: "DELETE",
|
||||
methodPUT: "PUT",
|
||||
}
|
||||
|
||||
// Internal use
|
||||
type Provider struct {
|
||||
ipAddr string
|
||||
ipType string
|
||||
recordType string
|
||||
domain string
|
||||
|
||||
DDNSProfile *model.DDNSProfile
|
||||
}
|
||||
|
||||
func (provider *Provider) SetRecords(ctx context.Context, zone string,
|
||||
recs []libdns.Record) ([]libdns.Record, error) {
|
||||
for _, rec := range recs {
|
||||
provider.recordType = rec.Type
|
||||
provider.ipType = recordToIPType(provider.recordType)
|
||||
provider.ipAddr = rec.Value
|
||||
provider.domain = fmt.Sprintf("%s.%s", rec.Name, strings.TrimSuffix(zone, "."))
|
||||
|
||||
req, err := provider.prepareRequest(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to update a domain: %s. Cause by: %v", provider.domain, err)
|
||||
}
|
||||
if _, err := utils.HttpClient.Do(req); err != nil {
|
||||
return nil, fmt.Errorf("failed to update a domain: %s. Cause by: %v", provider.domain, err)
|
||||
}
|
||||
}
|
||||
|
||||
return recs, nil
|
||||
}
|
||||
|
||||
func (provider *Provider) prepareRequest(ctx context.Context) (*http.Request, error) {
|
||||
u, err := provider.reqUrl()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := provider.reqBody()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
headers, err := utils.GjsonParseStringMap(
|
||||
provider.formatWebhookString(provider.DDNSProfile.WebhookHeaders))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, requestTypes[provider.DDNSProfile.WebhookMethod], u.String(), strings.NewReader(body))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
provider.setContentType(req)
|
||||
|
||||
for k, v := range headers {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func (provider *Provider) setContentType(req *http.Request) {
|
||||
if provider.DDNSProfile.WebhookMethod == methodGET {
|
||||
return
|
||||
}
|
||||
if provider.DDNSProfile.WebhookRequestType == requestTypeForm {
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
} else {
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
}
|
||||
}
|
||||
|
||||
func (provider *Provider) reqUrl() (*url.URL, error) {
|
||||
formattedUrl := strings.ReplaceAll(provider.DDNSProfile.WebhookURL, "#", "%23")
|
||||
|
||||
u, err := url.Parse(formattedUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Only handle queries here
|
||||
q := u.Query()
|
||||
for p, vals := range q {
|
||||
for n, v := range vals {
|
||||
vals[n] = provider.formatWebhookString(v)
|
||||
}
|
||||
q[p] = vals
|
||||
}
|
||||
|
||||
u.RawQuery = q.Encode()
|
||||
return u, nil
|
||||
}
|
||||
|
||||
func (provider *Provider) reqBody() (string, error) {
|
||||
if provider.DDNSProfile.WebhookMethod == methodGET ||
|
||||
provider.DDNSProfile.WebhookMethod == methodDELETE {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
switch provider.DDNSProfile.WebhookRequestType {
|
||||
case requestTypeJSON:
|
||||
return provider.formatWebhookString(provider.DDNSProfile.WebhookRequestBody), nil
|
||||
case requestTypeForm:
|
||||
data, err := utils.GjsonParseStringMap(provider.DDNSProfile.WebhookRequestBody)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
params := url.Values{}
|
||||
for k, v := range data {
|
||||
params.Add(k, provider.formatWebhookString(v))
|
||||
}
|
||||
return params.Encode(), nil
|
||||
default:
|
||||
return "", errors.New("request type not supported")
|
||||
}
|
||||
}
|
||||
|
||||
func (provider *Provider) formatWebhookString(s string) string {
|
||||
r := strings.NewReplacer(
|
||||
"#ip#", provider.ipAddr,
|
||||
"#domain#", provider.domain,
|
||||
"#type#", provider.ipType,
|
||||
"#record#", provider.recordType,
|
||||
"#access_id#", provider.DDNSProfile.AccessID,
|
||||
"#access_secret#", provider.DDNSProfile.AccessSecret,
|
||||
"\r", "",
|
||||
)
|
||||
|
||||
result := r.Replace(strings.TrimSpace(s))
|
||||
return result
|
||||
}
|
||||
|
||||
func recordToIPType(record string) string {
|
||||
switch record {
|
||||
case "A":
|
||||
return "ipv4"
|
||||
case "AAAA":
|
||||
return "ipv6"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package webhook
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/naiba/nezha/model"
|
||||
)
|
||||
|
||||
var (
|
||||
reqTypeForm = "application/x-www-form-urlencoded"
|
||||
reqTypeJSON = "application/json"
|
||||
)
|
||||
|
||||
type testSt struct {
|
||||
profile model.DDNSProfile
|
||||
expectURL string
|
||||
expectBody string
|
||||
expectContentType string
|
||||
expectHeader map[string]string
|
||||
}
|
||||
|
||||
func execCase(t *testing.T, item testSt) {
|
||||
pw := Provider{DDNSProfile: &item.profile}
|
||||
pw.ipAddr = "1.1.1.1"
|
||||
pw.domain = item.profile.Domains[0]
|
||||
pw.ipType = "ipv4"
|
||||
pw.recordType = "A"
|
||||
pw.DDNSProfile = &item.profile
|
||||
|
||||
reqUrl, err := pw.reqUrl()
|
||||
if err != nil {
|
||||
t.Fatalf("Error: %s", err)
|
||||
}
|
||||
if item.expectURL != reqUrl.String() {
|
||||
t.Fatalf("Expected %s, but got %s", item.expectURL, reqUrl.String())
|
||||
}
|
||||
|
||||
reqBody, err := pw.reqBody()
|
||||
if err != nil {
|
||||
t.Fatalf("Error: %s", err)
|
||||
}
|
||||
if item.expectBody != reqBody {
|
||||
t.Fatalf("Expected %s, but got %s", item.expectBody, reqBody)
|
||||
}
|
||||
|
||||
req, err := pw.prepareRequest(context.Background())
|
||||
if err != nil {
|
||||
t.Fatalf("Error: %s", err)
|
||||
}
|
||||
|
||||
if item.expectContentType != req.Header.Get("Content-Type") {
|
||||
t.Fatalf("Expected %s, but got %s", item.expectContentType, req.Header.Get("Content-Type"))
|
||||
}
|
||||
|
||||
for k, v := range item.expectHeader {
|
||||
if v != req.Header.Get(k) {
|
||||
t.Fatalf("Expected %s, but got %s", v, req.Header.Get(k))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWebhookRequest(t *testing.T) {
|
||||
ipv4 := true
|
||||
|
||||
cases := []testSt{
|
||||
{
|
||||
profile: model.DDNSProfile{
|
||||
Domains: []string{"www.example.com"},
|
||||
MaxRetries: 1,
|
||||
EnableIPv4: &ipv4,
|
||||
WebhookURL: "http://ddns.example.com/?ip=#ip#",
|
||||
WebhookMethod: methodGET,
|
||||
WebhookHeaders: `{"ip":"#ip#","record":"#record#"}`,
|
||||
},
|
||||
expectURL: "http://ddns.example.com/?ip=1.1.1.1",
|
||||
expectContentType: "",
|
||||
expectHeader: map[string]string{
|
||||
"ip": "1.1.1.1",
|
||||
"record": "A",
|
||||
},
|
||||
},
|
||||
{
|
||||
profile: model.DDNSProfile{
|
||||
Domains: []string{"www.example.com"},
|
||||
MaxRetries: 1,
|
||||
EnableIPv4: &ipv4,
|
||||
WebhookURL: "http://ddns.example.com/api",
|
||||
WebhookMethod: methodPOST,
|
||||
WebhookRequestType: requestTypeJSON,
|
||||
WebhookRequestBody: `{"ip":"#ip#","record":"#record#"}`,
|
||||
},
|
||||
expectURL: "http://ddns.example.com/api",
|
||||
expectContentType: reqTypeJSON,
|
||||
expectBody: `{"ip":"1.1.1.1","record":"A"}`,
|
||||
},
|
||||
{
|
||||
profile: model.DDNSProfile{
|
||||
Domains: []string{"www.example.com"},
|
||||
MaxRetries: 1,
|
||||
EnableIPv4: &ipv4,
|
||||
WebhookURL: "http://ddns.example.com/api",
|
||||
WebhookMethod: methodPOST,
|
||||
WebhookRequestType: requestTypeForm,
|
||||
WebhookRequestBody: `{"ip":"#ip#","record":"#record#"}`,
|
||||
},
|
||||
expectURL: "http://ddns.example.com/api",
|
||||
expectContentType: reqTypeForm,
|
||||
expectBody: "ip=1.1.1.1&record=A",
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
execCase(t, c)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
+12
-10
@@ -12,19 +12,19 @@ import (
|
||||
)
|
||||
|
||||
type AuthorizeOption struct {
|
||||
Guest bool
|
||||
Member 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) {
|
||||
return func(c *gin.Context) {
|
||||
var code = http.StatusForbidden
|
||||
if opt.Guest {
|
||||
if opt.GuestOnly {
|
||||
code = http.StatusBadRequest
|
||||
}
|
||||
|
||||
@@ -67,13 +67,15 @@ func Authorize(opt AuthorizeOption) func(*gin.Context) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 已登录且只能游客访问
|
||||
if isLogin && opt.Guest {
|
||||
if isLogin && opt.GuestOnly {
|
||||
ShowErrorPage(c, commonErr, opt.IsPage)
|
||||
return
|
||||
}
|
||||
|
||||
// 未登录且需要登录
|
||||
if !isLogin && opt.Member {
|
||||
if !isLogin && opt.MemberOnly {
|
||||
ShowErrorPage(c, commonErr, opt.IsPage)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ var adminPage = map[string]bool{
|
||||
"/monitor": true,
|
||||
"/setting": true,
|
||||
"/notification": true,
|
||||
"/ddns": true,
|
||||
"/nat": true,
|
||||
"/cron": true,
|
||||
"/api": true,
|
||||
}
|
||||
@@ -24,6 +26,9 @@ func CommonEnvironment(c *gin.Context, data map[string]interface{}) gin.H {
|
||||
data["MatchedPath"] = c.MustGet("MatchedPath")
|
||||
data["Version"] = singleton.Version
|
||||
data["Conf"] = singleton.Conf
|
||||
data["Themes"] = model.Themes
|
||||
data["CustomCode"] = singleton.Conf.Site.CustomCode
|
||||
data["CustomCodeDashboard"] = singleton.Conf.Site.CustomCodeDashboard
|
||||
// 是否是管理页面
|
||||
data["IsAdminPage"] = adminPage[data["MatchedPath"].(string)]
|
||||
// 站点标题
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package mygin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/naiba/nezha/model"
|
||||
"github.com/naiba/nezha/pkg/utils"
|
||||
"github.com/naiba/nezha/service/singleton"
|
||||
)
|
||||
|
||||
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/theme-custom/home.html") {
|
||||
return
|
||||
}
|
||||
c.Set(model.CtxKeyPreferredTheme, theme)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func GetPreferredTheme(c *gin.Context, path string) string {
|
||||
if theme, has := c.Get(model.CtxKeyPreferredTheme); has {
|
||||
return fmt.Sprintf("theme-%s%s", theme, path)
|
||||
}
|
||||
return fmt.Sprintf("theme-%s%s", singleton.Conf.Site.Theme, path)
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
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"}),
|
||||
}))
|
||||
|
||||
} 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,40 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrGjsonNotFound = errors.New("specified path does not exist")
|
||||
ErrGjsonWrongType = errors.New("wrong type")
|
||||
)
|
||||
|
||||
func GjsonGet(json []byte, path string) (gjson.Result, error) {
|
||||
result := gjson.GetBytes(json, path)
|
||||
if !result.Exists() {
|
||||
return result, ErrGjsonNotFound
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func GjsonParseStringMap(jsonObject string) (map[string]string, error) {
|
||||
if jsonObject == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
result := gjson.Parse(jsonObject)
|
||||
if !result.IsObject() {
|
||||
return nil, ErrGjsonWrongType
|
||||
}
|
||||
|
||||
ret := make(map[string]string)
|
||||
result.ForEach(func(key, value gjson.Result) bool {
|
||||
ret[key.String()] = value.String()
|
||||
return true
|
||||
})
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"io/fs"
|
||||
"os"
|
||||
)
|
||||
|
||||
// HybridFS combines embed.FS and os.DirFS.
|
||||
type HybridFS struct {
|
||||
embedFS, dir fs.FS
|
||||
}
|
||||
|
||||
func NewHybridFS(embed embed.FS, subDir string, localDir string) (*HybridFS, error) {
|
||||
subFS, err := fs.Sub(embed, subDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &HybridFS{
|
||||
embedFS: subFS,
|
||||
dir: os.DirFS(localDir),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (hfs *HybridFS) Open(name string) (fs.File, error) {
|
||||
// Ensure embed files are not replaced
|
||||
if file, err := hfs.embedFS.Open(name); err == nil {
|
||||
return file, nil
|
||||
}
|
||||
|
||||
return hfs.dir.Open(name)
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
HttpClientSkipTlsVerify *http.Client
|
||||
HttpClient *http.Client
|
||||
)
|
||||
|
||||
func init() {
|
||||
HttpClientSkipTlsVerify = httpClient(_httpClient{
|
||||
Transport: httpTransport(_httpTransport{
|
||||
SkipVerifySSL: true,
|
||||
}),
|
||||
})
|
||||
HttpClient = httpClient(_httpClient{
|
||||
Transport: httpTransport(_httpTransport{
|
||||
SkipVerifySSL: false,
|
||||
}),
|
||||
})
|
||||
|
||||
http.DefaultClient.Timeout = time.Minute * 10
|
||||
}
|
||||
|
||||
type _httpTransport struct {
|
||||
SkipVerifySSL bool
|
||||
}
|
||||
|
||||
func httpTransport(conf _httpTransport) *http.Transport {
|
||||
return &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: conf.SkipVerifySSL},
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
}
|
||||
}
|
||||
|
||||
type _httpClient struct {
|
||||
Transport *http.Transport
|
||||
}
|
||||
|
||||
func httpClient(conf _httpClient) *http.Client {
|
||||
return &http.Client{
|
||||
Transport: conf.Transport,
|
||||
Timeout: time.Minute * 10,
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
+15
-1
@@ -10,7 +10,11 @@ import (
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
var Json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||
var (
|
||||
Json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||
|
||||
DNSServers = []string{"1.1.1.1:53", "223.5.5.5:53"}
|
||||
)
|
||||
|
||||
func IsWindows() bool {
|
||||
return os.PathSeparator == '\\' && os.PathListSeparator == ';'
|
||||
@@ -76,3 +80,13 @@ func GenerateRandomString(n int) (string, error) {
|
||||
}
|
||||
return string(ret), nil
|
||||
}
|
||||
|
||||
func Uint64SubInt64(a uint64, b int64) uint64 {
|
||||
if b < 0 {
|
||||
return a + uint64(-b)
|
||||
}
|
||||
if a < uint64(b) {
|
||||
return 0
|
||||
}
|
||||
return a - uint64(b)
|
||||
}
|
||||
|
||||
+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
+180
-36
@@ -23,19 +23,19 @@ other = "Home"
|
||||
other = "Back to Homepage"
|
||||
|
||||
[AdminPanel]
|
||||
other = "Admin Panel"
|
||||
other = "Dashboard"
|
||||
|
||||
[Logout]
|
||||
other = "Logout"
|
||||
other = "Log out"
|
||||
|
||||
[Login]
|
||||
other = "Login"
|
||||
other = "Log in"
|
||||
|
||||
[ConfirmLogout]
|
||||
other = "Confirm Logout?"
|
||||
other = "Confirm Logging Out?"
|
||||
|
||||
[AfterLoggingOutYouHaveToLoginAgain]
|
||||
other = "You need to login again after logout to continue"
|
||||
other = "You will need to log in again to continue"
|
||||
|
||||
[Cancel]
|
||||
other = "Cancel"
|
||||
@@ -56,7 +56,7 @@ other = "Cron Expression"
|
||||
other = "Backup"
|
||||
|
||||
[3amDaily]
|
||||
other = "(Daily at 3 AM)"
|
||||
other = "(At 3 AM)"
|
||||
|
||||
[Command]
|
||||
other = "Command"
|
||||
@@ -71,7 +71,7 @@ other = "Ignore All, Execute Only on Specific Servers"
|
||||
other = "Cover All, Except Specific Servers"
|
||||
|
||||
[ExecuteByTriggerServer]
|
||||
other = "Execute on Trigger Server"
|
||||
other = "Execute on Alarmed Servers"
|
||||
|
||||
[SpecificServers]
|
||||
other = "Specific Servers"
|
||||
@@ -95,7 +95,7 @@ other = "Cron Task"
|
||||
other = "Trigger Task"
|
||||
|
||||
[TheFormaOfTheScheduleIs]
|
||||
other = "The Cron Expression is like:"
|
||||
other = "The Cron Expression format is:"
|
||||
|
||||
[SecondsMinutesHoursDaysMonthsWeeksSeeDetails]
|
||||
other = "Sec Min Hour Day Month Week, see details in "
|
||||
@@ -104,7 +104,7 @@ other = "Sec Min Hour Day Month Week, see details in "
|
||||
other = "CRON Expression Format"
|
||||
|
||||
[IntroductionOfCommands]
|
||||
other = "Introduction of Commands: Writing commands is similar to shell/bat scripts. It's advised not to break lines, and multiple commands can be connected with <code>&&</code> or <code>&</code>. If a command cannot be found, it might be due to <code>PATH</code> environment variable configuration issues. On <code>Linux</code> servers, you can add <code>source ~/.bashrc</code> at the beginning of the command, or use the command's absolute path."
|
||||
other = "Note on writing commands: It is similar to shell/bat scripts. but it's advised not to start a newline, and connect multiple commands with <code>&&</code> or <code>;</code>. If a command cannot be found, it is possibly due to <code>PATH</code> environment variable issues. On <code>Linux</code> servers, you can add <code>source ~/.bashrc</code> at the beginning of the command, or just use the absolute path."
|
||||
|
||||
[AddMonitor]
|
||||
other = "Add Service Monitor"
|
||||
@@ -119,7 +119,7 @@ other = "Target"
|
||||
other = "Type"
|
||||
|
||||
[SslExpirationOrChange]
|
||||
other = "(SSL Expiration or Change)"
|
||||
other = "(Certificate Expiration and Changes)"
|
||||
|
||||
[Duration]
|
||||
other = "Interval"
|
||||
@@ -134,10 +134,10 @@ other = "Enable Failure Notification"
|
||||
other = "Failure Notification"
|
||||
|
||||
[MaxLatency]
|
||||
other = "Max Latency (ms)"
|
||||
other = "Maximum Latency (ms)"
|
||||
|
||||
[MinLatency]
|
||||
other = "Min Latency (ms)"
|
||||
other = "Minimum Latency (ms)"
|
||||
|
||||
[EnableLatencyNotification]
|
||||
other = "Enable Latency Notification"
|
||||
@@ -147,7 +147,7 @@ other = "Latency Notification"
|
||||
|
||||
[IntroductionOfMonitor]
|
||||
other = """
|
||||
For type <b>HTTP-GET</b>, enter URL (with http/https, HTTPS protocol will also monitor SSL certificate);<br>
|
||||
For type <b>HTTP-GET</b>, enter URL (with http/https scheme, HTTPS protocol will also monitor SSL certificate);<br>
|
||||
For type <b>ICMP-Ping</b>, enter domain/IP without port: example.com;<br>
|
||||
For type <b>TCP-Ping</b>, enter domain/IP + port number: example.com:22"""
|
||||
|
||||
@@ -182,16 +182,16 @@ other = "Notification Trigger Mode"
|
||||
other = "Always Trigger"
|
||||
|
||||
[ModeOnetimeTrigger]
|
||||
other = "Always Trigger"
|
||||
other = "Trigger Once"
|
||||
|
||||
[EnableTriggerTask]
|
||||
other = "Enable Trigger Task"
|
||||
|
||||
[FailTriggerTasks]
|
||||
other = "Tasks triggered during an alarm"
|
||||
other = "Tasks to trigger on an alarm"
|
||||
|
||||
[RecoverTriggerTasks]
|
||||
other = "Tasks triggered during recovery"
|
||||
other = "Tasks to trigger after recovery"
|
||||
|
||||
[Enable]
|
||||
other = "Enable"
|
||||
@@ -218,7 +218,7 @@ other = "Einstein Lightspeed 1"
|
||||
other = "Display Index"
|
||||
|
||||
[TheLargerTheNumberTheHigherThePriority]
|
||||
other = "Higher Number, Higher Priority"
|
||||
other = "Greater Number, Higher Priority"
|
||||
|
||||
[Secret]
|
||||
other = "Secret"
|
||||
@@ -226,11 +226,14 @@ other = "Secret"
|
||||
[Note]
|
||||
other = "Note"
|
||||
|
||||
[PublicNote]
|
||||
other = "Public Note"
|
||||
|
||||
[LinuxOneKeyInstall]
|
||||
other = "Linux One-Key Install"
|
||||
other = "Linux Installation Command"
|
||||
|
||||
[NoDomainAlert]
|
||||
other = "Set CDN bypassed Domain/IP in setting before using this function"
|
||||
other = "Please set the IP (or a domain pointed to it) of the original server in settings"
|
||||
|
||||
[PushSuccessfully]
|
||||
other = "Send Success Notification"
|
||||
@@ -251,13 +254,13 @@ other = "Cover All"
|
||||
other = "Ignore All"
|
||||
|
||||
[ByTrigger]
|
||||
other = "Execute by Trigger"
|
||||
other = "Alarmed Servers"
|
||||
|
||||
[DeleteScheduledTask]
|
||||
other = "Delete Scheduled Task"
|
||||
|
||||
[ConfirmToDeleteThisScheduledTask]
|
||||
other = "Confirm Delete?"
|
||||
other = "Confirm deletion?"
|
||||
|
||||
[AccessDenied]
|
||||
other = "Access Denied"
|
||||
@@ -278,22 +281,22 @@ other = "Don't Have an Account?"
|
||||
other = "HTTP(S)/SSL Certificate"
|
||||
|
||||
[TCPPort]
|
||||
other = "TCP Port"
|
||||
other = "TCPing"
|
||||
|
||||
[DeleteService]
|
||||
other = "Remove Service Monitor"
|
||||
|
||||
[ConfirmToDeleteThisService]
|
||||
other = "Confirm to Delete This Service?"
|
||||
other = "Confirm Deleting This Service?"
|
||||
|
||||
[DeleteNotificationMethod]
|
||||
other = "Delete Notification Method"
|
||||
|
||||
[ConfirmToDeleteThisNotificationMethod]
|
||||
other = "Confirm to Delete This Notification Method?"
|
||||
other = "Confirm Deleting This Notification Method?"
|
||||
|
||||
[ForceUpdate]
|
||||
other = "Force Update"
|
||||
other = "Trigger Agent Update"
|
||||
|
||||
[SelectAll]
|
||||
other = "Select All"
|
||||
@@ -302,7 +305,7 @@ other = "Select All"
|
||||
other = "Version"
|
||||
|
||||
[OneKeyInstall]
|
||||
other = "One-Key Install"
|
||||
other = "Installation commands"
|
||||
|
||||
[ClickToCopy]
|
||||
other = "Click To Copy"
|
||||
@@ -311,7 +314,7 @@ other = "Click To Copy"
|
||||
other = "Delete Server"
|
||||
|
||||
[ConfirmToDeleteServer]
|
||||
other = "Confirm to Delete Server?"
|
||||
other = "Confirm Deleting This Server?"
|
||||
|
||||
[NoServerSelected]
|
||||
other = "No Server Selected"
|
||||
@@ -331,6 +334,9 @@ other = "Frontend Theme"
|
||||
[CustomCodes]
|
||||
other = "Custom Codes (Style and Script)"
|
||||
|
||||
[CustomCodesDashboard]
|
||||
other = "Custom Codes for Dashboard"
|
||||
|
||||
[AccessPassword]
|
||||
other = "Frontend Access Password"
|
||||
|
||||
@@ -398,7 +404,7 @@ other = "Virtualization"
|
||||
other = "Swap"
|
||||
|
||||
[NetTransfer]
|
||||
other = "Network Transfer"
|
||||
other = "Usage"
|
||||
|
||||
[Load]
|
||||
other = "Load"
|
||||
@@ -419,7 +425,7 @@ other = "Last Active"
|
||||
other = "Version"
|
||||
|
||||
[NetSpeed]
|
||||
other = "Network Speed"
|
||||
other = "NIC"
|
||||
|
||||
[Uptime]
|
||||
other = "Uptime"
|
||||
@@ -473,7 +479,7 @@ other = "Next check"
|
||||
other = "Current Usage"
|
||||
|
||||
[VerifyPassword]
|
||||
other = "Verify Password"
|
||||
other = "Please enter your password"
|
||||
|
||||
[LightMode]
|
||||
other = "Light Mode"
|
||||
@@ -482,7 +488,7 @@ other = "Light Mode"
|
||||
other = "Dark Mode"
|
||||
|
||||
[FollowSystem]
|
||||
other = "Follow System"
|
||||
other = "System Auto"
|
||||
|
||||
[GridLayout]
|
||||
other = "Grid Layout"
|
||||
@@ -539,7 +545,7 @@ other = "Scheduled Tasks"
|
||||
other = "API Management"
|
||||
|
||||
[IssueNewApiToken]
|
||||
other = "Issue New API Token"
|
||||
other = "Generate New API Token"
|
||||
|
||||
[Token]
|
||||
other = "Token"
|
||||
@@ -548,10 +554,10 @@ other = "Token"
|
||||
other = "Delete Token"
|
||||
|
||||
[ConfirmToDeleteThisToken]
|
||||
other = "Confirm to Delete This Token?"
|
||||
other = "Confirm Deleting This Token?"
|
||||
|
||||
[YouAreNotAuthorized]
|
||||
other = "Not Authorized for This Page"
|
||||
other = "This Page needs Authorization"
|
||||
|
||||
[WrongAccessPassword]
|
||||
other = "Wrong Access Password"
|
||||
@@ -599,13 +605,151 @@ other = "IP Changed"
|
||||
other = "Remaining Data"
|
||||
|
||||
[DashboardTheme]
|
||||
other = "Admin Panel Theme"
|
||||
other = "Dashboard Theme"
|
||||
|
||||
[Info]
|
||||
other = "Info"
|
||||
|
||||
[HideForGuest]
|
||||
other = "Hide for Guest"
|
||||
other = "Hide from Guest"
|
||||
|
||||
[Menu]
|
||||
other = "Menu"
|
||||
|
||||
[NetworkSpiter]
|
||||
other = "Network"
|
||||
|
||||
[EnableShowInService]
|
||||
other = "Enable Show in Service"
|
||||
|
||||
[DDNS]
|
||||
other = "Dynamic DNS"
|
||||
|
||||
[DDNSProfiles]
|
||||
other = "DDNS Profiles"
|
||||
|
||||
[AddDDNSProfile]
|
||||
other = "New Profile"
|
||||
|
||||
[EnableDDNS]
|
||||
other = "Enable DDNS"
|
||||
|
||||
[EnableIPv4]
|
||||
other = "IPv4 Enabled"
|
||||
|
||||
[EnableIPv6]
|
||||
other = "IPv6 Enabled"
|
||||
|
||||
[DDNSDomain]
|
||||
other = "Domains"
|
||||
|
||||
[DDNSDomains]
|
||||
other = "Domains (separate with comma)"
|
||||
|
||||
[DDNSProvider]
|
||||
other = "DDNS Provider"
|
||||
|
||||
[MaxRetries]
|
||||
other = "Maximum retry attempts"
|
||||
|
||||
[DDNSAccessID]
|
||||
other = "Credential 1"
|
||||
|
||||
[DDNSAccessSecret]
|
||||
other = "Credential 2"
|
||||
|
||||
[DDNSTokenID]
|
||||
other = "Token ID"
|
||||
|
||||
[DDNSTokenSecret]
|
||||
other = "Token Secret"
|
||||
|
||||
[WebhookURL]
|
||||
other = "Webhook URL"
|
||||
|
||||
[WebhookMethod]
|
||||
other = "Webhook Request Method"
|
||||
|
||||
[WebhookRequestType]
|
||||
other = "Webhook Request Type"
|
||||
|
||||
[WebhookHeaders]
|
||||
other = "Webhook Request Headers"
|
||||
|
||||
[WebhookRequestBody]
|
||||
other = "Webhook Request Body"
|
||||
|
||||
[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 Traversal"
|
||||
|
||||
[LocalService]
|
||||
other = "Local service"
|
||||
|
||||
[LocalServicePlaceholder]
|
||||
other = "192.168.1.1:80 (with port)"
|
||||
|
||||
[BindHostname]
|
||||
other = "Bind hostname"
|
||||
|
||||
[NetworkSpiterList]
|
||||
other = "Network Monitor"
|
||||
|
||||
[Refresh]
|
||||
other = "Refresh"
|
||||
|
||||
[CopyPath]
|
||||
other = "Copy Path"
|
||||
|
||||
[Goto]
|
||||
other = "Go to"
|
||||
|
||||
[GotoHeadline]
|
||||
other = "Go to a Folder"
|
||||
|
||||
[GotoGo]
|
||||
other = "Go"
|
||||
|
||||
[GotoClose]
|
||||
other = "Cancel"
|
||||
|
||||
[FMError]
|
||||
other = "Agent returned an error, please view the console for details. To open a new connection, reopen the FM again."
|
||||
|
||||
[Remaining]
|
||||
other = "Remaining"
|
||||
|
||||
[Lifetime]
|
||||
other = "Lifetime"
|
||||
|
||||
[Price]
|
||||
other = "Price"
|
||||
|
||||
[Expired]
|
||||
other = "Expired"
|
||||
|
||||
[Days]
|
||||
other = "d"
|
||||
|
||||
[CustomNameservers]
|
||||
other = "Custom Public DNS Nameservers for DDNS (separate with comma)"
|
||||
|
||||
[Plan]
|
||||
other = "Plan"
|
||||
|
||||
Vendored
+149
-5
@@ -71,7 +71,7 @@ other = "Ignorar todo, ejecutar solo a través de servidores específicos"
|
||||
other = "Incluir todo, excepto servidores específicos"
|
||||
|
||||
[ExecuteByTriggerServer]
|
||||
other = "Ejecutar por servidor disparador"
|
||||
other = "Ejecutar en servidores con alarma"
|
||||
|
||||
[SpecificServers]
|
||||
other = "Servidores Específicos"
|
||||
@@ -104,7 +104,7 @@ other = "Segundos, Minutos, Horas, Días, Meses, Semanas - Ver detalles a"
|
||||
other = "Formato de Expresión de Cron"
|
||||
|
||||
[IntroductionOfCommands]
|
||||
other = "Introducción de Comandos: Escriba comandos como en scripts shell/bat. Evite saltos de línea; conecte múltiples comandos con <code>&&</code> o <code>&</code>. Si no se encuentra un comando, verifique la variable de entorno <code>PATH</code>. En servidores <code>Linux</code>, antepóngale <code>source ~/.bashrc</code> a los comandos, o use rutas absolutas."
|
||||
other = "Introducción de Comandos: Escriba comandos como en scripts shell/bat. Evite saltos de línea; conecte múltiples comandos con <code>&&</code> o <code>;</code>. Si no se encuentra un comando, verifique la variable de entorno <code>PATH</code>. En servidores <code>Linux</code>, antepóngale <code>source ~/.bashrc</code> a los comandos, o use rutas absolutas."
|
||||
|
||||
[AddMonitor]
|
||||
other = "Agregar Monitor de Servicio"
|
||||
@@ -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"
|
||||
@@ -226,6 +226,9 @@ other = "Secreto"
|
||||
[Note]
|
||||
other = "Nota"
|
||||
|
||||
[PublicNote]
|
||||
other = "Nota Pública"
|
||||
|
||||
[LinuxOneKeyInstall]
|
||||
other = "Instalación Linux con Un Solo Clic"
|
||||
|
||||
@@ -251,7 +254,7 @@ other = "Cubrir Todo"
|
||||
other = "Ignorar Todo"
|
||||
|
||||
[ByTrigger]
|
||||
other = "Ejecutar por Disparador"
|
||||
other = "Servidores con alarma"
|
||||
|
||||
[DeleteScheduledTask]
|
||||
other = "Eliminar Tarea Programada"
|
||||
@@ -278,7 +281,7 @@ other = "¿No tiene una cuenta?"
|
||||
other = "Certificados HTTP(S)/SSL"
|
||||
|
||||
[TCPPort]
|
||||
other = "Puerto TCP"
|
||||
other = "TCPing"
|
||||
|
||||
[DeleteService]
|
||||
other = "Eliminar Servicio"
|
||||
@@ -331,6 +334,9 @@ other = "Tema de Frontend"
|
||||
[CustomCodes]
|
||||
other = "Códigos Personalizados (Incluye style y script)"
|
||||
|
||||
[CustomCodesDashboard]
|
||||
other = "Custom Codes for Dashboard"
|
||||
|
||||
[AccessPassword]
|
||||
other = "Contraseña de Acceso al Frontend"
|
||||
|
||||
@@ -609,3 +615,141 @@ other = "Ocultar para Invitados"
|
||||
|
||||
[Menu]
|
||||
other = "Menú"
|
||||
|
||||
[NetworkSpiter]
|
||||
other = "Red"
|
||||
|
||||
[EnableShowInService]
|
||||
other = "Mostrar en servicio"
|
||||
|
||||
[DDNS]
|
||||
other = "DNS Dinámico"
|
||||
|
||||
[DDNSProfiles]
|
||||
other = "Perfiles DDNS"
|
||||
|
||||
[AddDDNSProfile]
|
||||
other = "Nuevo Perfil"
|
||||
|
||||
[EnableDDNS]
|
||||
other = "Habilitar DDNS"
|
||||
|
||||
[EnableIPv4]
|
||||
other = "IPv4 Activado"
|
||||
|
||||
[EnableIPv6]
|
||||
other = "IPv6 Activado"
|
||||
|
||||
[DDNSDomain]
|
||||
other = "Dominios"
|
||||
|
||||
[DDNSDomains]
|
||||
other = "Dominios (separados por comas)"
|
||||
|
||||
[DDNSProvider]
|
||||
other = "Proveedor DDNS"
|
||||
|
||||
[MaxRetries]
|
||||
other = "Número máximo de intentos de reintento"
|
||||
|
||||
[DDNSAccessID]
|
||||
other = "Credencial 1"
|
||||
|
||||
[DDNSAccessSecret]
|
||||
other = "Credencial 2"
|
||||
|
||||
[DDNSTokenID]
|
||||
other = "ID del Token"
|
||||
|
||||
[DDNSTokenSecret]
|
||||
other = "Secreto del Token"
|
||||
|
||||
[WebhookURL]
|
||||
other = "URL del Webhook"
|
||||
|
||||
[WebhookMethod]
|
||||
other = "Método de Solicitud del Webhook"
|
||||
|
||||
[WebhookRequestType]
|
||||
other = "Tipo de solicitud del Webhook"
|
||||
|
||||
[WebhookHeaders]
|
||||
other = "Encabezados de Solicitud del Webhook"
|
||||
|
||||
[WebhookRequestBody]
|
||||
other = "Cuerpo de Solicitud del Webhook"
|
||||
|
||||
[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 traversal"
|
||||
|
||||
[LocalService]
|
||||
other = "Servicio de red local"
|
||||
|
||||
[LocalServicePlaceholder]
|
||||
other = "192.168.1.1:80 (con puerto)"
|
||||
|
||||
[BindHostname]
|
||||
other = "Vincular nombre de host"
|
||||
|
||||
[NetworkSpiterList]
|
||||
other = "Monitor de red"
|
||||
|
||||
[Refresh]
|
||||
other = "Actualizar"
|
||||
|
||||
[CopyPath]
|
||||
other = "Copiar ruta"
|
||||
|
||||
[Goto]
|
||||
other = "Ir a"
|
||||
|
||||
[GotoHeadline]
|
||||
other = "Ir a una carpeta"
|
||||
|
||||
[GotoGo]
|
||||
other = "Ir"
|
||||
|
||||
[GotoClose]
|
||||
other = "Cancelar"
|
||||
|
||||
[FMError]
|
||||
other = "Agent devolvió un error, consulte la consola para obtener más detalles. Para abrir una nueva conexión, vuelva a abrir el FM."
|
||||
|
||||
[Remaining]
|
||||
other = "Remaining"
|
||||
|
||||
[Lifetime]
|
||||
other = "Lifetime"
|
||||
|
||||
[Price]
|
||||
other = "Price"
|
||||
|
||||
[Expired]
|
||||
other = "Expired"
|
||||
|
||||
[Days]
|
||||
other = "d"
|
||||
|
||||
[CustomNameservers]
|
||||
other = "Servidores DNS públicos personalizados para DDNS (separar con coma)"
|
||||
|
||||
[Plan]
|
||||
other = "Plan"
|
||||
|
||||
Vendored
+150
-6
@@ -71,7 +71,7 @@ other = "忽略所有,仅通过特定服务器执行"
|
||||
other = "覆盖所有,仅特定服务器不执行"
|
||||
|
||||
[ExecuteByTriggerServer]
|
||||
other = "由触发的服务器执行"
|
||||
other = "由触发告警的服务器执行"
|
||||
|
||||
[SpecificServers]
|
||||
other = "特定服务器"
|
||||
@@ -104,7 +104,7 @@ other = "秒 分 时 天 月 星期,详情见"
|
||||
other = "计划表达式格式"
|
||||
|
||||
[IntroductionOfCommands]
|
||||
other = "命令说明:编写命令时类似于 shell/bat 脚本。建议不要换行,多个命令可用 <code>&&</code> 或 <code>&</code> 连接,若出现命令无法找到的情况,可能是由于 <code>PATH</code> 环境变量配置问题。在 <code>Linux</code> 服务器上,可在命令开头加入 <code>source ~/.bashrc</code>,或使用命令的绝对路径执行。"
|
||||
other = "命令说明:编写命令时类似于 shell/bat 脚本。建议不要换行,多个命令可用 <code>&&</code> 或 <code>;</code> 连接,若出现命令无法找到的情况,可能是由于 <code>PATH</code> 环境变量配置问题。在 <code>Linux</code> 服务器上,可在命令开头加入 <code>source ~/.bashrc</code>,或使用命令的绝对路径执行。"
|
||||
|
||||
[AddMonitor]
|
||||
other = "新增监控"
|
||||
@@ -226,6 +226,9 @@ other = "密钥"
|
||||
[Note]
|
||||
other = "备注"
|
||||
|
||||
[PublicNote]
|
||||
other = "公开备注"
|
||||
|
||||
[LinuxOneKeyInstall]
|
||||
other = "Linux 一键安装"
|
||||
|
||||
@@ -251,7 +254,7 @@ other = "覆盖所有"
|
||||
other = "忽略所有"
|
||||
|
||||
[ByTrigger]
|
||||
other = "触发执行"
|
||||
other = "触发告警的服务器"
|
||||
|
||||
[DeleteScheduledTask]
|
||||
other = "删除计划任务"
|
||||
@@ -278,7 +281,7 @@ other = "没有账号?"
|
||||
other = "HTTP(S)/SSL证书"
|
||||
|
||||
[TCPPort]
|
||||
other = "TCP 端口"
|
||||
other = "TCPing"
|
||||
|
||||
[DeleteService]
|
||||
other = "移除服务"
|
||||
@@ -331,6 +334,9 @@ other = "前台界面主题"
|
||||
[CustomCodes]
|
||||
other = "自定义代码(包括 style 和 script)"
|
||||
|
||||
[CustomCodesDashboard]
|
||||
other = "Custom Codes for Dashboard"
|
||||
|
||||
[AccessPassword]
|
||||
other = "前台访问密码"
|
||||
|
||||
@@ -419,7 +425,7 @@ other = "活动"
|
||||
other = "版本"
|
||||
|
||||
[NetSpeed]
|
||||
other = "网络"
|
||||
other = "网速"
|
||||
|
||||
[Uptime]
|
||||
other = "在线"
|
||||
@@ -608,4 +614,142 @@ other = "信息"
|
||||
other = "对游客隐藏"
|
||||
|
||||
[Menu]
|
||||
other = "菜单"
|
||||
other = "菜单"
|
||||
|
||||
[NetworkSpiter]
|
||||
other = "网络"
|
||||
|
||||
[EnableShowInService]
|
||||
other = "在服务中显示"
|
||||
|
||||
[DDNS]
|
||||
other = "动态 DNS"
|
||||
|
||||
[DDNSProfiles]
|
||||
other = "DDNS配置"
|
||||
|
||||
[AddDDNSProfile]
|
||||
other = "新配置"
|
||||
|
||||
[EnableDDNS]
|
||||
other = "启用 DDNS"
|
||||
|
||||
[EnableIPv4]
|
||||
other = "启用 DDNS IPv4"
|
||||
|
||||
[EnableIPv6]
|
||||
other = "启用 DDNS IPv6"
|
||||
|
||||
[DDNSDomain]
|
||||
other = "DDNS 域名"
|
||||
|
||||
[DDNSDomains]
|
||||
other = "域名(逗号分隔)"
|
||||
|
||||
[DDNSProvider]
|
||||
other = "DDNS 供应商"
|
||||
|
||||
[MaxRetries]
|
||||
other = "最大重试次数"
|
||||
|
||||
[DDNSAccessID]
|
||||
other = "DDNS 凭据 1"
|
||||
|
||||
[DDNSAccessSecret]
|
||||
other = "DDNS 凭据 2"
|
||||
|
||||
[DDNSTokenID]
|
||||
other = "令牌 ID"
|
||||
|
||||
[DDNSTokenSecret]
|
||||
other = "令牌 Secret"
|
||||
|
||||
[WebhookURL]
|
||||
other = "Webhook 地址"
|
||||
|
||||
[WebhookMethod]
|
||||
other = "Webhook 请求方式"
|
||||
|
||||
[WebhookRequestType]
|
||||
other = "Webhook 请求类型"
|
||||
|
||||
[WebhookHeaders]
|
||||
other = "Webhook 请求头"
|
||||
|
||||
[WebhookRequestBody]
|
||||
other = "Webhook 请求体"
|
||||
|
||||
[Feature]
|
||||
other = "功能"
|
||||
|
||||
[Template]
|
||||
other = "主题"
|
||||
|
||||
[Stat]
|
||||
other = "信息"
|
||||
|
||||
[Temperature]
|
||||
other = "温度"
|
||||
|
||||
[DisableSwitchTemplateInFrontend]
|
||||
other = "禁止前台切换模板"
|
||||
|
||||
[ServersOnWorldMap]
|
||||
other = "服务器世界分布图"
|
||||
|
||||
[NAT]
|
||||
other = "内网穿透"
|
||||
|
||||
[LocalService]
|
||||
other = "内网服务"
|
||||
|
||||
[LocalServicePlaceholder]
|
||||
other = "192.168.1.1:80(带端口)"
|
||||
|
||||
[BindHostname]
|
||||
other = "绑定域名"
|
||||
|
||||
[NetworkSpiterList]
|
||||
other = "网络监控"
|
||||
|
||||
[Refresh]
|
||||
other = "刷新"
|
||||
|
||||
[CopyPath]
|
||||
other = "复制路径"
|
||||
|
||||
[Goto]
|
||||
other = "跳往"
|
||||
|
||||
[GotoHeadline]
|
||||
other = "跳往文件夹"
|
||||
|
||||
[GotoGo]
|
||||
other = "确认"
|
||||
|
||||
[GotoClose]
|
||||
other = "取消"
|
||||
|
||||
[FMError]
|
||||
other = "Agent 返回了错误,请查看控制台获取详细信息。要建立新连接,请重新打开 FM。"
|
||||
|
||||
[Remaining]
|
||||
other = "剩余"
|
||||
|
||||
[Lifetime]
|
||||
other = "永续"
|
||||
|
||||
[Price]
|
||||
other = "价格"
|
||||
|
||||
[Expired]
|
||||
other = "已到期"
|
||||
|
||||
[Days]
|
||||
other = "天"
|
||||
|
||||
[CustomNameservers]
|
||||
other = "自定义DDNS使用的公共DNS服务器(逗号分隔)"
|
||||
|
||||
[Plan]
|
||||
other = "套餐"
|
||||
|
||||
Vendored
+165
-21
@@ -50,7 +50,7 @@ other = "新增計劃任務"
|
||||
other = "名稱"
|
||||
|
||||
[Scheduler]
|
||||
other = "計劃"
|
||||
other = "排程"
|
||||
|
||||
[BackUp]
|
||||
other = "備份"
|
||||
@@ -71,7 +71,7 @@ other = "忽略所有,僅通過特定伺服器執行"
|
||||
other = "覆蓋所有,僅特定伺服器不執行"
|
||||
|
||||
[ExecuteByTriggerServer]
|
||||
other = "由觸發的伺服器執行"
|
||||
other = "由觸發告警的伺服器執行"
|
||||
|
||||
[SpecificServers]
|
||||
other = "特定伺服器"
|
||||
@@ -80,37 +80,37 @@ other = "特定伺服器"
|
||||
other = "輸入ID/名稱以搜尋"
|
||||
|
||||
[NotificationMethodGroup]
|
||||
other = "通知方式組"
|
||||
other = "通知群組"
|
||||
|
||||
[PushSuccessMessages]
|
||||
other = "推送成功的消息"
|
||||
other = "推送成功的訊息"
|
||||
|
||||
[TaskType]
|
||||
other = "任務類型"
|
||||
|
||||
[CronTask]
|
||||
other = "計劃任務"
|
||||
other = "排程任務"
|
||||
|
||||
[TriggerTask]
|
||||
other = "觸發任務"
|
||||
|
||||
[TheFormaOfTheScheduleIs]
|
||||
other = "計劃的格式為:"
|
||||
other = "排程的格式為:"
|
||||
|
||||
[SecondsMinutesHoursDaysMonthsWeeksSeeDetails]
|
||||
other = "秒 分 時 天 月 星期,詳情見"
|
||||
|
||||
[ScheduleExpressionFormat]
|
||||
other = "計劃表達式格式"
|
||||
other = "排程表達式格式"
|
||||
|
||||
[IntroductionOfCommands]
|
||||
other = "命令說明:編寫命令時類似於 shell/bat 腳本。建議不要換行,多個命令可用 <code>&&</code> 或 <code>&</code> 連接,若出現命令無法找到的情況,可能是由於 <code>PATH</code> 環境變量配置問題。在 <code>Linux</code> 伺服器上,可在命令開頭加入 <code>source ~/.bashrc</code>,或使用命令的絕對路徑執行。"
|
||||
other = "命令說明:編寫命令時類似於 shell/bat 腳本。建議不要換行,多個命令可用 <code>&&</code> 或 <code>;</code> 連接,若出現命令無法找到的情況,可能是由於 <code>PATH</code> 環境變數配置問題。在 <code>Linux</code> 伺服器上,可在命令開頭加入 <code>source ~/.bashrc</code>,或使用命令的絕對路徑執行。"
|
||||
|
||||
[AddMonitor]
|
||||
other = "新增監控"
|
||||
|
||||
[Blog]
|
||||
other = "博客"
|
||||
other = "部落格"
|
||||
|
||||
[Target]
|
||||
other = "目標"
|
||||
@@ -158,7 +158,7 @@ other = "新增通知方式"
|
||||
other = "分組"
|
||||
|
||||
[DoNotSendTestMessages]
|
||||
other = "不發送測試信息"
|
||||
other = "不發送測試訊息"
|
||||
|
||||
[RequestMethod]
|
||||
other = "請求方式"
|
||||
@@ -221,11 +221,14 @@ other = "排序"
|
||||
other = "越大越靠前"
|
||||
|
||||
[Secret]
|
||||
other = "密鑰"
|
||||
other = "金鑰"
|
||||
|
||||
[Note]
|
||||
other = "備註"
|
||||
|
||||
[PublicNote]
|
||||
other = "公開備註"
|
||||
|
||||
[LinuxOneKeyInstall]
|
||||
other = "Linux 一鍵安裝"
|
||||
|
||||
@@ -251,13 +254,13 @@ other = "覆蓋所有"
|
||||
other = "忽略所有"
|
||||
|
||||
[ByTrigger]
|
||||
other = "觸發執行"
|
||||
other = "觸發告警的伺服器"
|
||||
|
||||
[DeleteScheduledTask]
|
||||
other = "刪除計劃任務"
|
||||
other = "刪除排程任務"
|
||||
|
||||
[ConfirmToDeleteThisScheduledTask]
|
||||
other = "確認刪除此計劃任務?"
|
||||
other = "確認刪除此排程任務?"
|
||||
|
||||
[AccessDenied]
|
||||
other = "訪問被拒絕"
|
||||
@@ -278,7 +281,7 @@ other = "沒有帳號?"
|
||||
other = "HTTP(S)/SSL證書"
|
||||
|
||||
[TCPPort]
|
||||
other = "TCP 端口"
|
||||
other = "TCPing"
|
||||
|
||||
[DeleteService]
|
||||
other = "移除服務"
|
||||
@@ -331,6 +334,9 @@ other = "前台界面主題"
|
||||
[CustomCodes]
|
||||
other = "自定義代碼(包括 style 和 script)"
|
||||
|
||||
[CustomCodesDashboard]
|
||||
other = "Custom Codes for Dashboard"
|
||||
|
||||
[AccessPassword]
|
||||
other = "前台訪問密碼"
|
||||
|
||||
@@ -404,7 +410,7 @@ other = "流量"
|
||||
other = "負載"
|
||||
|
||||
[ProcessCount]
|
||||
other = "進程數"
|
||||
other = "行程數"
|
||||
|
||||
[ConnCount]
|
||||
other = "連接數"
|
||||
@@ -419,7 +425,7 @@ other = "活動"
|
||||
other = "版本"
|
||||
|
||||
[NetSpeed]
|
||||
other = "網絡"
|
||||
other = "網速"
|
||||
|
||||
[Uptime]
|
||||
other = "在線"
|
||||
@@ -455,7 +461,7 @@ other = "狀態"
|
||||
other = "可用性"
|
||||
|
||||
[AverageLatency]
|
||||
other = "平均響應時間"
|
||||
other = "平均回應時間"
|
||||
|
||||
[CycleTransferStats]
|
||||
other = "周期性流量統計"
|
||||
@@ -521,7 +527,7 @@ other = "發生錯誤"
|
||||
other = "系統錯誤"
|
||||
|
||||
[NetworkError]
|
||||
other = "網絡錯誤"
|
||||
other = "網路錯誤"
|
||||
|
||||
[ServicesStatus]
|
||||
other = "服務狀態"
|
||||
@@ -533,7 +539,7 @@ other = "伺服器管理"
|
||||
other = "服務監控"
|
||||
|
||||
[ScheduledTasks]
|
||||
other = "計劃任務"
|
||||
other = "排程任務"
|
||||
|
||||
[ApiManagement]
|
||||
other = "API 管理"
|
||||
@@ -608,4 +614,142 @@ other = "信息"
|
||||
other = "對遊客隱藏"
|
||||
|
||||
[Menu]
|
||||
other = "菜單"
|
||||
other = "菜單"
|
||||
|
||||
[NetworkSpiter]
|
||||
other = "網路"
|
||||
|
||||
[EnableShowInService]
|
||||
other = "在服務中顯示"
|
||||
|
||||
[DDNS]
|
||||
other = "動態 DNS"
|
||||
|
||||
[DDNSProfiles]
|
||||
other = "DDNS配置"
|
||||
|
||||
[AddDDNSProfile]
|
||||
other = "新增配置"
|
||||
|
||||
[EnableDDNS]
|
||||
other = "啟用 DDNS"
|
||||
|
||||
[EnableIPv4]
|
||||
other = "啟用 DDNS IPv4"
|
||||
|
||||
[EnableIPv6]
|
||||
other = "啟用 DDNS IPv6"
|
||||
|
||||
[DDNSDomain]
|
||||
other = "DDNS 域名"
|
||||
|
||||
[DDNSDomains]
|
||||
other = "域名(逗號分隔)"
|
||||
|
||||
[DDNSProvider]
|
||||
other = "DDN S供應商"
|
||||
|
||||
[MaxRetries]
|
||||
other = "最大重試次數"
|
||||
|
||||
[DDNSAccessID]
|
||||
other = "DDNS 憑據 1"
|
||||
|
||||
[DDNSAccessSecret]
|
||||
other = "DDNS 憑據 2"
|
||||
|
||||
[DDNSTokenID]
|
||||
other = "令牌 ID"
|
||||
|
||||
[DDNSTokenSecret]
|
||||
other = "令牌 Secret"
|
||||
|
||||
[WebhookURL]
|
||||
other = "Webhook 地址"
|
||||
|
||||
[WebhookMethod]
|
||||
other = "Webhook 請求方式"
|
||||
|
||||
[WebhookRequestType]
|
||||
other = "Webhook 請求類型"
|
||||
|
||||
[WebhookHeaders]
|
||||
other = "Webhook 請求頭"
|
||||
|
||||
[WebhookRequestBody]
|
||||
other = "Webhook 請求體"
|
||||
|
||||
[Feature]
|
||||
other = "功能"
|
||||
|
||||
[Template]
|
||||
other = "主題"
|
||||
|
||||
[Stat]
|
||||
other = "信息"
|
||||
|
||||
[Temperature]
|
||||
other = "溫度"
|
||||
|
||||
[DisableSwitchTemplateInFrontend]
|
||||
other = "禁止前台切換主題"
|
||||
|
||||
[ServersOnWorldMap]
|
||||
other = "伺服器世界分布圖"
|
||||
|
||||
[NAT]
|
||||
other = "NAT穿透"
|
||||
|
||||
[LocalService]
|
||||
other = "內網服務"
|
||||
|
||||
[LocalServicePlaceholder]
|
||||
other = "192.168.1.1:80(帶埠號)"
|
||||
|
||||
[BindHostname]
|
||||
other = "綁定網域"
|
||||
|
||||
[NetworkSpiterList]
|
||||
other = "網路監控"
|
||||
|
||||
[Refresh]
|
||||
other = "重新整理"
|
||||
|
||||
[CopyPath]
|
||||
other = "複製路徑"
|
||||
|
||||
[Goto]
|
||||
other = "跳至"
|
||||
|
||||
[GotoHeadline]
|
||||
other = "跳至資料夾"
|
||||
|
||||
[GotoGo]
|
||||
other = "確定"
|
||||
|
||||
[GotoClose]
|
||||
other = "取消"
|
||||
|
||||
[FMError]
|
||||
other = "Agent 回傳了錯誤,請查看主控台獲取詳細資訊。要建立新連線,請重新開啟 FM。"
|
||||
|
||||
[Remaining]
|
||||
other = "剩餘"
|
||||
|
||||
[Lifetime]
|
||||
other = "永續"
|
||||
|
||||
[Price]
|
||||
other = "價格"
|
||||
|
||||
[Expired]
|
||||
other = "已到期"
|
||||
|
||||
[Days]
|
||||
other = "天"
|
||||
|
||||
[CustomNameservers]
|
||||
other = "自訂DDNS使用的公共DNS伺服器(逗號分隔)"
|
||||
|
||||
[Plan]
|
||||
other = "套餐"
|
||||
|
||||
Vendored
+13
-1
@@ -2,10 +2,14 @@ package resource
|
||||
|
||||
import (
|
||||
"embed"
|
||||
|
||||
"github.com/naiba/nezha/pkg/utils"
|
||||
)
|
||||
|
||||
var StaticFS *utils.HybridFS
|
||||
|
||||
//go:embed static
|
||||
var StaticFS embed.FS
|
||||
var staticFS embed.FS
|
||||
|
||||
//go:embed template
|
||||
var TemplateFS embed.FS
|
||||
@@ -13,6 +17,14 @@ var TemplateFS embed.FS
|
||||
//go:embed l10n
|
||||
var I18nFS embed.FS
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
StaticFS, err = utils.NewHybridFS(staticFS, "static", "resource/static/custom")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func IsTemplateFileExist(name string) bool {
|
||||
_, err := TemplateFS.Open(name)
|
||||
return err == nil
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
html[nz-theme='dark'] body {
|
||||
background-color: #121212 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .ui.menu {
|
||||
background-color: #282828 !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .ui.menu * {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .accordion {
|
||||
background-color: #282828 !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .accordion .title {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .ui.card {
|
||||
background-color: #3f3f3f !important;
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .header {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .description {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .icon {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .ui.popup {
|
||||
background-color: #575757 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .ui.table {
|
||||
background-color: #282828 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .ui thead th {
|
||||
background-color: #3f3f3f !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .ui.buttons .button {
|
||||
background-color: #3f3f3f !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .ui.modal {
|
||||
background-color: #282828 !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .ui.modal * {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] textarea,
|
||||
html[nz-theme='dark'] input,
|
||||
html[nz-theme='dark'] select,
|
||||
html[nz-theme='dark'] .dropdown {
|
||||
background-color: #3f3f3f !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .ui.message {
|
||||
background-color: unset !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .ui.dropdown .menu {
|
||||
background-color: #575757 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .ui.modal>.header {
|
||||
background-color: #3f3f3f !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .ui.modal>.content {
|
||||
background-color: #282828 !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .ui.modal>.actions {
|
||||
background-color: #3f3f3f !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] #alert {
|
||||
background-color: #3f3f3f !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .ui.form .field>label {
|
||||
color: unset !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .ui.segment {
|
||||
background-color: #3f3f3f !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .ui.segment textarea,
|
||||
html[nz-theme='dark'] input,
|
||||
html[nz-theme='dark'] select,
|
||||
html[nz-theme='dark'] .dropdown {
|
||||
background-color: #575757 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] form label {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .ui.inverted.segment {
|
||||
background-color: #121212 !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .ui.inverted.segment * {
|
||||
color: #8b8b8b !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .menu .dropdown {
|
||||
background-color: #282828 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .ui.menu .ui.dropdown .menu>.item {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
html[nz-theme='dark'] .login .ui.message{
|
||||
color: #8b8b8b !important;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
let receivedLength = 0;
|
||||
let expectedLength = 0;
|
||||
let root;
|
||||
let draftHandle;
|
||||
let accessHandle;
|
||||
|
||||
const Operation = Object.freeze({
|
||||
WriteHeader: 1,
|
||||
WriteChunks: 2,
|
||||
DeleteFiles: 3
|
||||
});
|
||||
|
||||
onmessage = async function (event) {
|
||||
try {
|
||||
const { operation, arrayBuffer, fileName } = event.data;
|
||||
|
||||
switch (operation) {
|
||||
case Operation.WriteHeader: {
|
||||
const dataView = new DataView(arrayBuffer);
|
||||
expectedLength = Number(dataView.getBigUint64(4, false));
|
||||
receivedLength = 0;
|
||||
|
||||
// Create a new temporary file
|
||||
root = await navigator.storage.getDirectory();
|
||||
draftHandle = await root.getFileHandle(fileName, { create: true });
|
||||
accessHandle = await draftHandle.createSyncAccessHandle();
|
||||
|
||||
// Inform that file handle is created
|
||||
const dataChunk = arrayBuffer.slice(12);
|
||||
receivedLength += dataChunk.byteLength;
|
||||
accessHandle.write(dataChunk, { at: 0 });
|
||||
const progress = 'got handle';
|
||||
postMessage({ type: 'progress', progress: progress });
|
||||
break;
|
||||
}
|
||||
case Operation.WriteChunks: {
|
||||
if (!accessHandle) {
|
||||
throw new Error('accessHandle is undefined');
|
||||
}
|
||||
|
||||
const dataChunk = arrayBuffer;
|
||||
accessHandle.write(dataChunk, { at: receivedLength });
|
||||
receivedLength += dataChunk.byteLength;
|
||||
|
||||
if (receivedLength === expectedLength) {
|
||||
accessHandle.flush();
|
||||
accessHandle.close();
|
||||
|
||||
const fileBlob = await draftHandle.getFile();
|
||||
const blob = new Blob([fileBlob], { type: 'application/octet-stream' });
|
||||
|
||||
postMessage({ type: 'result', blob: blob, fileName: fileName });
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Operation.DeleteFiles: {
|
||||
for await (const [name, handle] of root.entries()) {
|
||||
if (handle.kind === 'file') {
|
||||
await root.removeEntry(name);
|
||||
} else if (handle.kind === 'directory') {
|
||||
await root.removeEntry(name, { recursive: true });
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
postMessage({ error: error.message });
|
||||
}
|
||||
};
|
||||
@@ -9,6 +9,11 @@ td {
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.ui.container{
|
||||
width: 95vw !important;
|
||||
max-width: 1680px !important;
|
||||
}
|
||||
|
||||
.nb-container {
|
||||
padding-top: 75px;
|
||||
min-height: 100vh;
|
||||
|
||||
+141
-1
@@ -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" ||
|
||||
@@ -98,7 +99,11 @@ function showFormModal(modelSelector, formID, URL, getData) {
|
||||
item.name === "DisplayIndex" ||
|
||||
item.name === "Type" ||
|
||||
item.name === "Cover" ||
|
||||
item.name === "Duration"
|
||||
item.name === "Duration" ||
|
||||
item.name === "MaxRetries" ||
|
||||
item.name === "Provider" ||
|
||||
item.name === "WebhookMethod" ||
|
||||
item.name === "WebhookRequestType"
|
||||
) {
|
||||
obj[item.name] = parseInt(item.value);
|
||||
} else if (item.name.endsWith("Latency")) {
|
||||
@@ -127,6 +132,16 @@ function showFormModal(modelSelector, formID, URL, getData) {
|
||||
}
|
||||
}
|
||||
|
||||
if (item.name.endsWith("DDNSProfilesRaw")) {
|
||||
if (item.value.length > 2) {
|
||||
obj[item.name] = JSON.stringify(
|
||||
[...item.value.matchAll(/\d+/gm)].map((k) =>
|
||||
parseInt(k[0])
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return obj;
|
||||
}, {});
|
||||
$.post(URL, JSON.stringify(data))
|
||||
@@ -206,6 +221,7 @@ function addOrEditAlertRule(rule) {
|
||||
);
|
||||
}
|
||||
}
|
||||
// 需要在 showFormModal 进一步拼接数组
|
||||
modal
|
||||
.find("input[name=FailTriggerTasksRaw]")
|
||||
.val(rule ? "[]," + failTriggerTasks.substr(1, failTriggerTasks.length - 2) : "[]");
|
||||
@@ -255,6 +271,77 @@ function addOrEditNotification(notification) {
|
||||
);
|
||||
}
|
||||
|
||||
function addOrEditDDNS(ddns) {
|
||||
const modal = $(".ddns.modal");
|
||||
modal.children(".header").text((ddns ? LANG.Edit : LANG.Add));
|
||||
modal
|
||||
.find(".nezha-primary-btn.button")
|
||||
.html(
|
||||
ddns
|
||||
? LANG.Edit + '<i class="edit icon"></i>'
|
||||
: LANG.Add + '<i class="add icon"></i>'
|
||||
);
|
||||
modal.find("input[name=ID]").val(ddns ? ddns.ID : null);
|
||||
modal.find("input[name=Name]").val(ddns ? ddns.Name : null);
|
||||
modal.find("input[name=DomainsRaw]").val(ddns ? ddns.DomainsRaw : null);
|
||||
modal.find("input[name=AccessID]").val(ddns ? ddns.AccessID : null);
|
||||
modal.find("input[name=AccessSecret]").val(ddns ? ddns.AccessSecret : null);
|
||||
modal.find("input[name=MaxRetries]").val(ddns ? ddns.MaxRetries : 3);
|
||||
modal.find("input[name=WebhookURL]").val(ddns ? ddns.WebhookURL : null);
|
||||
modal
|
||||
.find("textarea[name=WebhookHeaders]")
|
||||
.val(ddns ? ddns.WebhookHeaders : null);
|
||||
modal
|
||||
.find("textarea[name=WebhookRequestBody]")
|
||||
.val(ddns ? ddns.WebhookRequestBody : null);
|
||||
modal
|
||||
.find("select[name=Provider]")
|
||||
.val(ddns ? ddns.Provider : 0);
|
||||
modal
|
||||
.find("select[name=WebhookMethod]")
|
||||
.val(ddns ? ddns.WebhookMethod : 1);
|
||||
modal
|
||||
.find("select[name=WebhookRequestType]")
|
||||
.val(ddns ? ddns.WebhookRequestType : 1);
|
||||
if (ddns && ddns.EnableIPv4) {
|
||||
modal.find(".ui.enableipv4.checkbox").checkbox("set checked");
|
||||
} else {
|
||||
modal.find(".ui.enableipv4.checkbox").checkbox("set unchecked");
|
||||
}
|
||||
if (ddns && ddns.EnableIPv6) {
|
||||
modal.find(".ui.enableipv6.checkbox").checkbox("set checked");
|
||||
} else {
|
||||
modal.find(".ui.enableipv6.checkbox").checkbox("set unchecked");
|
||||
}
|
||||
showFormModal(
|
||||
".ddns.modal",
|
||||
"#ddnsForm",
|
||||
"/api/ddns"
|
||||
);
|
||||
}
|
||||
|
||||
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 })
|
||||
}
|
||||
@@ -302,10 +389,38 @@ function addOrEditServer(server, conf) {
|
||||
modal.find("input[name=id]").val(server ? server.ID : null);
|
||||
modal.find("input[name=name]").val(server ? server.Name : null);
|
||||
modal.find("input[name=Tag]").val(server ? server.Tag : null);
|
||||
modal.find("a.ui.label.visible").each((i, el) => {
|
||||
el.remove();
|
||||
});
|
||||
var ddns;
|
||||
if (server) {
|
||||
ddns = server.DDNSProfilesRaw;
|
||||
let serverList;
|
||||
try {
|
||||
serverList = JSON.parse(ddns);
|
||||
} catch (error) {
|
||||
serverList = "[]";
|
||||
}
|
||||
const node = modal.find("i.dropdown.icon.ddnsProfiles");
|
||||
for (let i = 0; i < serverList.length; i++) {
|
||||
node.after(
|
||||
'<a class="ui label transition visible" data-value="' +
|
||||
serverList[i] +
|
||||
'" style="display: inline-block !important;">ID:' +
|
||||
serverList[i] +
|
||||
'<i class="delete icon"></i></a>'
|
||||
);
|
||||
}
|
||||
}
|
||||
// 需要在 showFormModal 进一步拼接数组
|
||||
modal
|
||||
.find("input[name=DDNSProfilesRaw]")
|
||||
.val(server ? "[]," + ddns.substr(1, ddns.length - 2) : "[]");
|
||||
modal
|
||||
.find("input[name=DisplayIndex]")
|
||||
.val(server ? server.DisplayIndex : null);
|
||||
modal.find("textarea[name=Note]").val(server ? server.Note : null);
|
||||
modal.find("textarea[name=PublicNote]").val(server ? server.PublicNote : null);
|
||||
if (server) {
|
||||
modal.find(".secret.field").attr("style", "");
|
||||
modal.find(".command.field").attr("style", "");
|
||||
@@ -316,11 +431,17 @@ function addOrEditServer(server, conf) {
|
||||
modal.find(".command.field").attr("style", "display:none");
|
||||
modal.find("input[name=secret]").val("");
|
||||
}
|
||||
if (server && server.EnableDDNS) {
|
||||
modal.find(".ui.enableddns.checkbox").checkbox("set checked");
|
||||
} else {
|
||||
modal.find(".ui.enableddns.checkbox").checkbox("set unchecked");
|
||||
}
|
||||
if (server && server.HideForGuest) {
|
||||
modal.find(".ui.hideforguest.checkbox").checkbox("set checked");
|
||||
} else {
|
||||
modal.find(".ui.hideforguest.checkbox").checkbox("set unchecked");
|
||||
}
|
||||
|
||||
showFormModal(".server.modal", "#serverForm", "/api/server");
|
||||
}
|
||||
|
||||
@@ -339,6 +460,11 @@ function addOrEditMonitor(monitor) {
|
||||
modal.find("select[name=Type]").val(monitor ? monitor.Type : 1);
|
||||
modal.find("select[name=Cover]").val(monitor ? monitor.Cover : 0);
|
||||
modal.find("input[name=NotificationTag]").val(monitor ? monitor.NotificationTag : null);
|
||||
if (monitor && monitor.EnableShowInService) {
|
||||
modal.find(".ui.nb-show-in-service.checkbox").checkbox("set checked")
|
||||
} else {
|
||||
modal.find(".ui.nb-show-in-service.checkbox").checkbox("set unchecked")
|
||||
}
|
||||
if (monitor && monitor.Notify) {
|
||||
modal.find(".ui.nb-notify.checkbox").checkbox("set checked");
|
||||
} else {
|
||||
@@ -401,6 +527,7 @@ function addOrEditMonitor(monitor) {
|
||||
);
|
||||
}
|
||||
}
|
||||
// 需要在 showFormModal 进一步拼接数组
|
||||
modal
|
||||
.find("input[name=FailTriggerTasksRaw]")
|
||||
.val(monitor ? "[]," + failTriggerTasks.substr(1, failTriggerTasks.length - 2) : "[]");
|
||||
@@ -446,6 +573,7 @@ function addOrEditCron(cron) {
|
||||
);
|
||||
}
|
||||
}
|
||||
// 需要在 showFormModal 进一步拼接数组
|
||||
modal
|
||||
.find("input[name=ServersRaw]")
|
||||
.val(cron ? "[]," + servers.substr(1, servers.length - 2) : "[]");
|
||||
@@ -575,3 +703,15 @@ $(document).ready(() => {
|
||||
});
|
||||
} catch (error) { }
|
||||
});
|
||||
|
||||
$(document).ready(() => {
|
||||
try {
|
||||
$(".ui.ddns.search.dropdown").dropdown({
|
||||
clearable: true,
|
||||
apiSettings: {
|
||||
url: "/api/search-ddns?word={query}",
|
||||
cache: false,
|
||||
},
|
||||
});
|
||||
} catch (error) { }
|
||||
});
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.9 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "Nezha",
|
||||
"short_name": "Nezha",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/static/manifest-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "/static/manifest-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
},
|
||||
{
|
||||
"src": "/static/manifest-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "/static/manifest-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
}
|
||||
],
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#000000",
|
||||
"lang": "eu-US",
|
||||
"dir": "auto"
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "哪吒监控",
|
||||
"short_name": "哪吒监控",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/static/manifest-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "/static/manifest-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
},
|
||||
{
|
||||
"src": "/static/manifest-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "/static/manifest-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
}
|
||||
],
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#000000",
|
||||
"lang": "zh-CN",
|
||||
"dir": "auto"
|
||||
}
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 5.1 KiB |
+217
@@ -0,0 +1,217 @@
|
||||
/* 屏幕适配 */
|
||||
@media only screen and (max-width:767px) {
|
||||
.ui.card>.content>.header:not(.ui),
|
||||
.ui.cards>.card>.content>.header:not(.ui) {
|
||||
margin-top: 0.4em !important;
|
||||
}
|
||||
|
||||
.ui.menu .item>img:not(.ui) {
|
||||
width: 2.2rem;
|
||||
}
|
||||
|
||||
.ui.menu .item:before {
|
||||
width: 0.5px;
|
||||
}
|
||||
|
||||
.ui.menu .item {
|
||||
padding: 0.9rem 0.55rem;
|
||||
}
|
||||
|
||||
.ui.large.menu {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
i.icon {
|
||||
color: #000;
|
||||
width: 1.2em !important;
|
||||
}
|
||||
|
||||
i.fi {
|
||||
width: 0.9em;
|
||||
margin: 0px 6px 0px 2px;
|
||||
}
|
||||
|
||||
td {
|
||||
word-wrap: break-word;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.nb-container {
|
||||
padding-top: 75px;
|
||||
min-height: 100vh;
|
||||
padding-bottom: 65px;
|
||||
margin-bottom: -47px;
|
||||
}
|
||||
|
||||
.ui.container {
|
||||
width: 95%;
|
||||
max-width: 1680px !important;
|
||||
}
|
||||
|
||||
#app .ui.fluid.accordion {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.login.nb-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-top: unset;
|
||||
}
|
||||
|
||||
.login.nb-container>.grid {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.login.nb-container>.grid .column {
|
||||
max-width: 450px;
|
||||
}
|
||||
|
||||
.ui.menu .item-right:before {
|
||||
width: 0px;
|
||||
}
|
||||
|
||||
.status.cards .flag {
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
.status.cards .header>.info.icon {
|
||||
float: right;
|
||||
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: 2.3rem !important;
|
||||
}
|
||||
|
||||
.status.cards .wide.column:nth-child(1) {
|
||||
margin-top: 1.2rem !important;
|
||||
}
|
||||
|
||||
.status.cards .wide.column:nth-child(2) {
|
||||
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;
|
||||
}
|
||||
|
||||
.status.cards .description {
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.status.cards .flag {
|
||||
margin-right: 0.5rem !important;
|
||||
}
|
||||
|
||||
.status.cards .header>.info.icon {
|
||||
float: right;
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
.ui.popup:before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.closePopup {
|
||||
color: rgb(10, 148, 242) !important;
|
||||
position: absolute;
|
||||
top: 7px;
|
||||
right: 10px;
|
||||
cursor: pointer;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.temp-detail {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ui.content {
|
||||
margin: 0 !important;
|
||||
padding: 1em !important;
|
||||
}
|
||||
|
||||
.status.cards .ui.content.popup {
|
||||
min-width: calc(100%) !important;
|
||||
line-height: 2rem !important;
|
||||
border-radius: 5px !important;
|
||||
border: 1px solid transparent !important;
|
||||
font-family: Arial, Helvetica, sans-serif !important;
|
||||
}
|
||||
|
||||
.status.cards .outline.icon {
|
||||
margin-right: 1px !important;
|
||||
}
|
||||
|
||||
.ui.progress .bar {
|
||||
min-width: 1.8em !important;
|
||||
border-radius: 5px !important;
|
||||
line-height: 1.65em !important;
|
||||
text-align: right;
|
||||
padding-right: 0.4em;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
font-weight: 700;
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
.service-status .delay-today {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.service-status .delay-today>i {
|
||||
display: inline-block;
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
border-radius: 0.6em;
|
||||
background-color: grey;
|
||||
margin-right: 0.3em;
|
||||
}
|
||||
|
||||
.service-status .danger {
|
||||
background-color: crimson !important;
|
||||
}
|
||||
|
||||
.service-status .good {
|
||||
background-color: rgb(10, 148, 242) !important;
|
||||
}
|
||||
|
||||
.service-status .warning {
|
||||
background-color: orange !important;
|
||||
}
|
||||
|
||||
.nezha-primary-btn {
|
||||
background-color: #0338d6 !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.nezha-primary-font {
|
||||
color: #0338d6 !important;
|
||||
}
|
||||
|
||||
.nezha-secondary-font {
|
||||
height: 1em;
|
||||
color: rgb(10, 148, 242) !important;
|
||||
}
|
||||
|
||||
.ui-alerts.top-center {
|
||||
z-index: 99999999;
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
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;
|
||||
},
|
||||
mounted() {
|
||||
this.initDropdown();
|
||||
},
|
||||
methods: {
|
||||
initDropdown() {
|
||||
if(this.isMobile) $('.ui.dropdown').dropdown({
|
||||
action: 'hide',
|
||||
on: 'click',
|
||||
duration: 100,
|
||||
direction: 'direction'
|
||||
});
|
||||
},
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 6.2 KiB |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+131
-77
File diff suppressed because one or more lines are too long
@@ -0,0 +1,121 @@
|
||||
body[theme="dark"]::before {
|
||||
content: "";
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-image: url(/static/theme-server-status/img/bg.jpg);
|
||||
background-repeat: no-repeat;
|
||||
background-position: 50% 50%;
|
||||
background-size: cover;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
body[theme="dark"] {
|
||||
background-color: rgba(0, 0, 0, 0.8);
|
||||
color: #f1f1f1;
|
||||
}
|
||||
|
||||
body[theme="dark"] .navbar {
|
||||
/** 顶部导航条 背景 **/
|
||||
background-color: rgba(0, 0, 0, 0.8);
|
||||
box-shadow: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
body[theme="dark"] .navbar .navbar-brand {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
body[theme="dark"] .navbar .dropdown-menu {
|
||||
/** 二级导航下拉 背景 **/
|
||||
background-color: rgba(0, 0, 0, 0.85);
|
||||
border-top: none;
|
||||
border-color: #31363b;
|
||||
box-shadow: rgba(0, 0, 0, 0.18) 0px 6px 12px;
|
||||
}
|
||||
|
||||
body[theme="dark"] .navbar .dropdown-menu > li > a {
|
||||
color: #c8c3bc;
|
||||
}
|
||||
|
||||
body[theme="dark"] .navbar .dropdown-menu > li > a:focus,
|
||||
body[theme="dark"] .navbar .dropdown-menu > li > a:hover {
|
||||
/** 二级导航鼠标悬停选中背景 **/
|
||||
background-color: rgba(0, 0, 0, 0.95);
|
||||
background-image: linear-gradient(#1c1d26 0, #1c1d26 100%);
|
||||
}
|
||||
|
||||
body[theme="dark"] .navbar .navbar-nav .open .dropdown-menu > li > a {
|
||||
color: #f1f1f1;
|
||||
}
|
||||
|
||||
body[theme="dark"] .table,
|
||||
body[theme="dark"] .table-condensed > tbody > tr,
|
||||
body[theme="dark"] .table-hover > tbody > tr,
|
||||
body[theme="dark"] .table-hover > tbody > tr:hover,
|
||||
body[theme="dark"] .table-striped tbody > tr.even,
|
||||
body[theme="dark"] .table-striped tbody > tr.odd,
|
||||
body[theme="dark"] .table-striped tbody > tr.even > td,
|
||||
body[theme="dark"] .table-striped tbody > tr.even > th,
|
||||
body[theme="dark"] .table-striped tbody > tr.odd > td,
|
||||
body[theme="dark"] .table-striped tbody > tr.odd > th,
|
||||
body[theme="dark"] .table-striped tbody > tr.even > td:hover,
|
||||
body[theme="dark"] .table-striped tbody > tr.even > th:hover,
|
||||
body[theme="dark"] .table-striped tbody > tr.odd > td:hover,
|
||||
body[theme="dark"] .table-striped tbody > tr.odd > th:hover,
|
||||
body[theme="dark"] .table-striped tbody > tr.expandRow:hover {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
body[theme="dark"] .content {
|
||||
/** 主box 背景 **/
|
||||
background-color: rgba(28, 29, 38, 0.8);
|
||||
border: none;
|
||||
box-shadow: rgba(0, 0, 0, 0.5) 0 0.625em 2em;
|
||||
}
|
||||
|
||||
body[theme="dark"] .table > thead > tr.node-group-tag > th,
|
||||
body[theme="dark"] .table > thead > tr.node-group-tag > th:before {
|
||||
background: unset;
|
||||
}
|
||||
|
||||
body[theme="dark"] .table > tbody > tr > td:before,
|
||||
body[theme="dark"] .table > tfoot > tr > td:before,
|
||||
body[theme="dark"] .table > thead > tr > td:before,
|
||||
body[theme="dark"] .table > thead > tr.node-group-cell > th:before {
|
||||
/** border-bottom 颜色 **/
|
||||
background-color: rgba(155, 155, 155, 0.1);
|
||||
}
|
||||
|
||||
body[theme="dark"] .table-hover > tbody > tr:not(.expandRow):hover > td {
|
||||
background-color: unset;
|
||||
}
|
||||
|
||||
/* expandRow展开部分样式 */
|
||||
body[theme="dark"] .table > tbody > tr.expandRow.odd > td:before {
|
||||
background-color: unset;
|
||||
}
|
||||
|
||||
body[theme="dark"] .table > tbody > tr.expandRow.even > td:before {
|
||||
background-color: unset;
|
||||
}
|
||||
/* expandRow展开部分样式结束 */
|
||||
|
||||
body[theme="dark"] .progress {
|
||||
background-image: none;
|
||||
background-color: rgba(255, 255, 255, 0.075);
|
||||
}
|
||||
|
||||
body[theme="dark"] .progress-bar-success {
|
||||
background-image: linear-gradient(to right, rgba(77, 133, 58, 0.9) 0, rgba(54, 126, 54, 0.9) 100%);
|
||||
}
|
||||
|
||||
body[theme="dark"] .progress-bar-warning {
|
||||
background-image: linear-gradient(to right, rgba(153, 95, 13, 0.9) 0, rgba(181, 113, 15, 0.9) 100%);
|
||||
}
|
||||
|
||||
body[theme="dark"] .progress-bar-danger {
|
||||
background-image: linear-gradient(to right, rgba(145, 35, 32, 0.8) 0, rgba(161, 38, 35, 0.8) 100%);
|
||||
}
|
||||
+167
-25
File diff suppressed because one or more lines are too long
@@ -0,0 +1,139 @@
|
||||
body[theme="light"]::before {
|
||||
content: "";
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-image: url(/static/theme-server-status/img/bg.jpg);
|
||||
background-repeat: no-repeat;
|
||||
background-position: 50% 50%;
|
||||
background-size: cover;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
body[theme="light"] {
|
||||
background-image: unset;
|
||||
}
|
||||
|
||||
body[theme="light"]::after {
|
||||
content: "";
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
body[theme="light"] .navbar {
|
||||
/** 顶部导航条 背景 **/
|
||||
background-color: rgba(255, 255, 255, 0.9);
|
||||
border-bottom: 1px #cecece solid;
|
||||
box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
body[theme="light"] .navbar .dropdown-menu {
|
||||
/** 二级导航下拉 **/
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
border-color: rgba(0, 0, 0, .05);
|
||||
}
|
||||
|
||||
|
||||
body[theme="light"] .navbar .dropdown-menu > li > a:focus,
|
||||
body[theme="light"] .navbar .dropdown-menu > li > a:hover {
|
||||
/** 二级导航鼠标悬停选中背景 **/
|
||||
background-color: rgba(0, 0, 0, .05);
|
||||
}
|
||||
|
||||
body[theme="light"] .navbar .navbar-collapse {
|
||||
border-color:rgba(0, 0, 0, 0.085);
|
||||
}
|
||||
|
||||
body[theme="light"] .navbar .navbar-toggle .icon-bar {
|
||||
background-color: rgba(0, 0, 0, 0.9);
|
||||
}
|
||||
|
||||
body[theme="light"] .navbar .navbar-toggle:focus,
|
||||
body[theme="light"] .navbar .navbar-toggle:hover {
|
||||
background-color: unset;
|
||||
}
|
||||
|
||||
body[theme="light"] .table,
|
||||
body[theme="light"] .table-condensed > tbody > tr,
|
||||
body[theme="light"] .table-hover > tbody > tr,
|
||||
body[theme="light"] .table-hover > tbody > tr:hover,
|
||||
body[theme="light"] .table-striped tbody > tr.even,
|
||||
body[theme="light"] .table-striped tbody > tr.odd,
|
||||
body[theme="light"] .table-striped tbody > tr.even > td,
|
||||
body[theme="light"] .table-striped tbody > tr.even > th,
|
||||
body[theme="light"] .table-striped tbody > tr.odd > td,
|
||||
body[theme="light"] .table-striped tbody > tr.odd > th,
|
||||
body[theme="light"] .table-striped tbody > tr.even > td:hover,
|
||||
body[theme="light"] .table-striped tbody > tr.even > th:hover,
|
||||
body[theme="light"] .table-striped tbody > tr.odd > td:hover,
|
||||
body[theme="light"] .table-striped tbody > tr.odd > th:hover,
|
||||
body[theme="light"] .table-striped tbody > tr.expandRow:hover {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
body[theme="light"] .content {
|
||||
/** 主box 背景 **/
|
||||
background-color: rgba(245, 245, 245, 0.8);
|
||||
border: none;
|
||||
box-shadow: rgba(0, 0, 0, 0.5) 0 0.625em 2em;
|
||||
}
|
||||
|
||||
body[theme="light"] .table > thead > tr.node-group-tag > th,
|
||||
body[theme="light"] .table > thead > tr.node-group-tag > th:before {
|
||||
background: unset;
|
||||
}
|
||||
|
||||
body[theme="light"] .table > tbody > tr > td:before,
|
||||
body[theme="light"] .table > tfoot > tr > td:before,
|
||||
body[theme="light"] .table > thead > tr > td:before,
|
||||
body[theme="light"] .table > thead > tr.node-group-cell > th:before {
|
||||
/** border-bottom 颜色 **/
|
||||
background-color: rgba(0, 0, 0, 0.075);
|
||||
}
|
||||
|
||||
body[theme="light"] .table-hover > tbody > tr:not(.expandRow):hover > td {
|
||||
background-color: unset;
|
||||
}
|
||||
|
||||
/* expandRow展开部分样式 */
|
||||
body[theme="light"] .table > tbody > tr.expandRow.odd > td:before {
|
||||
background-color: unset;
|
||||
}
|
||||
|
||||
body[theme="light"] .table > tbody > tr.expandRow.even > td:before {
|
||||
background-color: unset;
|
||||
}
|
||||
|
||||
body[theme="light"] .plan {
|
||||
background-image: none;
|
||||
background-color: rgba(0, 0, 0, 0.015);
|
||||
}
|
||||
/* expandRow展开部分样式结束 */
|
||||
|
||||
body[theme="light"] .progress {
|
||||
background-image: none;
|
||||
background-color: rgba(0, 0, 0, 0.015);
|
||||
}
|
||||
|
||||
body[theme="light"] .progress-bar-success {
|
||||
background-image: linear-gradient(to right, rgba(50, 205, 50, 0.6) 0, rgba(68, 157, 68, 0.6) 100%);
|
||||
}
|
||||
|
||||
body[theme="light"] .progress-bar-warning {
|
||||
background-image: linear-gradient(to right, rgba(236, 151, 31, 0.6) 0, rgba(255, 125, 80, 0.6) 100%);
|
||||
}
|
||||
|
||||
body[theme="light"] .progress-bar-danger {
|
||||
background-image: linear-gradient(to right, rgba(255, 70, 0, 0.6) 0, rgba(201, 48, 44,0.6) 100%);
|
||||
}
|
||||
|
||||
body[theme="light"] .toolbox i {
|
||||
background-color: rgba(255, 255, 255, .55);
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 767px) {
|
||||
body[theme="light"] .navbar .navbar-nav .open .dropdown-menu {
|
||||
background-color: rgba(235, 235, 235, 0.75);
|
||||
}
|
||||
}
|
||||
+476
-93
@@ -1,51 +1,132 @@
|
||||
body {
|
||||
padding-top: 70px !important;
|
||||
padding-top:70px !important;
|
||||
padding-bottom: 30px !important;
|
||||
}
|
||||
|
||||
.container-fluid{
|
||||
max-width: 1680px;
|
||||
width: 95vw;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.container-fluid::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.nezha {
|
||||
min-height: calc(100vh - 100px);
|
||||
}
|
||||
|
||||
/* 导航部分 开始*/
|
||||
.navbar {
|
||||
min-height: 40px !important;
|
||||
margin:0 auto;
|
||||
font-size: 14px;
|
||||
z-index:99999999;
|
||||
}
|
||||
|
||||
.navbar-collapse:not([aria-expanded]) .navbar-nav .dropdown-toggle {
|
||||
.navbar-inverse{
|
||||
background-image: none;
|
||||
background-color: #1C2127;
|
||||
box-shadow: 0 1px 40px -8px #00000080;
|
||||
}
|
||||
|
||||
.navbar-inverse .navbar-toggle:focus,
|
||||
.navbar-inverse .navbar-toggle:hover {
|
||||
background-color: #1C2127;
|
||||
}
|
||||
|
||||
.navbar .navbar-collapse:not([aria-expanded]) .navbar-nav .dropdown-toggle {
|
||||
margin-top: 18px;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
font-size: 20px;
|
||||
text-align: center;
|
||||
.navbar .navbar-toggle {
|
||||
margin-right:0
|
||||
}
|
||||
|
||||
.node-cell-expand {
|
||||
max-width: 420px;
|
||||
.navbar .navbar-brand {
|
||||
font-size: 20px;
|
||||
text-align: center;
|
||||
padding:12px 0 0 0;
|
||||
margin-right:30px;
|
||||
}
|
||||
|
||||
.navbar .node-cell-expand {
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.node-cell-expand-label {
|
||||
margin-right: 5px;
|
||||
.navbar .dropdown a{
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dropdown .dropdown-toggle {
|
||||
.navbar .dropdown .dropdown-toggle {
|
||||
padding-bottom: 10px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
margin-right: 10px;
|
||||
.navbar .navbar-nav {
|
||||
margin:0px -15px;
|
||||
}
|
||||
|
||||
.navbar-inverse, .nav.navbar-nav {
|
||||
background-image: linear-gradient(rgb(60, 60, 60) 0px, rgb(34, 34, 34) 100%) !important;
|
||||
.navbar .navbar-nav > li > a {
|
||||
color:#f1f1f1;
|
||||
}
|
||||
|
||||
.navbar-inverse .navbar-brand {
|
||||
font-size: 20px;
|
||||
.navbar-nav li a span{
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.navbar .navbar-collapse{
|
||||
max-height: 500px;
|
||||
}
|
||||
/* 导航部分 结束 */
|
||||
|
||||
|
||||
/* toolbox 开始 */
|
||||
.toolbox {
|
||||
position: fixed;
|
||||
bottom:20px;
|
||||
right: 12px;
|
||||
z-index: 999999;
|
||||
}
|
||||
|
||||
.toolbox span{
|
||||
display: block;
|
||||
width: 2.75rem;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.toolbox i{
|
||||
display: block;
|
||||
color: rgba(241,241,241,1);
|
||||
cursor: pointer;
|
||||
border-radius: 5px;
|
||||
font-size: 1.5rem;
|
||||
height: 2.75rem;
|
||||
width: 2.75rem;
|
||||
line-height: 2.75rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.toolbox .toggleView i.show-nogroup {
|
||||
font-size: 1.85rem;
|
||||
}
|
||||
|
||||
.toolbox .setTheme i.setTheme-dark {
|
||||
font-size: 1.35rem;
|
||||
}
|
||||
|
||||
.toolbox .setTheme i.setTheme-light {
|
||||
font-size: 1.45rem;
|
||||
}
|
||||
|
||||
.toolbox .showGoTop i.goTop {
|
||||
font-size: 1.55rem;
|
||||
}
|
||||
/* toolbox 结束 */
|
||||
|
||||
|
||||
/* 正文部分 开始 */
|
||||
.content {
|
||||
padding: 20px;
|
||||
@@ -59,10 +140,57 @@ body {
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.table-responsive{
|
||||
border: none;
|
||||
}
|
||||
|
||||
.table th, .table td {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.table > tbody > tr > td,
|
||||
.table > tbody > tr > th,
|
||||
.table > tfoot > tr > td,
|
||||
.table > tfoot > tr > th,
|
||||
.table > thead > tr > td,
|
||||
.table > thead > tr > th {
|
||||
position: relative;
|
||||
border:none;
|
||||
line-height:20px;
|
||||
}
|
||||
|
||||
.table > tbody > tr > td:before,
|
||||
.table > tbody > tr > th:before,
|
||||
.table > tfoot > tr > td:before,
|
||||
.table > tfoot > tr > th:before,
|
||||
.table > thead > tr > td:before,
|
||||
.table > thead > tr > th:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 0.7px;
|
||||
}
|
||||
|
||||
table.monitor-detail{
|
||||
margin:5px 0px 10px 0px;
|
||||
}
|
||||
|
||||
tr.accordion-toggle{
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
.table .network-thead-tr th,
|
||||
.table .node-group-tag th{
|
||||
font-size: 18px;
|
||||
padding-bottom:15px;
|
||||
}
|
||||
|
||||
.table .node-cell-os-text {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.progress {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
@@ -72,12 +200,15 @@ body {
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
|
||||
.expandRow > td {
|
||||
padding: 0 !important;
|
||||
border-top: 0 !important;
|
||||
}
|
||||
|
||||
.accordian-body{
|
||||
margin: 10px 0px 10px 18px;
|
||||
}
|
||||
|
||||
.node-cell.center {
|
||||
text-align: center !important;
|
||||
}
|
||||
@@ -110,21 +241,93 @@ body {
|
||||
max-width: 110px;
|
||||
}
|
||||
|
||||
.node-cell.cpu, .node-cell.ram, .node-cell.hdd {
|
||||
min-width: 45px;
|
||||
max-width: 90px;
|
||||
.node-cell.traffic {
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
.node-cell.cpu, .node-cell.ram, .node-cell.hdd, .node-cell.memory {
|
||||
min-width: 50px;
|
||||
max-width: 50px;
|
||||
}
|
||||
|
||||
.node-cell.remaining {
|
||||
min-width: 65px;
|
||||
max-width: 65px;
|
||||
}
|
||||
|
||||
.node-cell.remaining .additional {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.node-cell.remaining .additional small{
|
||||
white-space: nowrap;
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
.plan {
|
||||
display: inline-block;
|
||||
font-size: 85%;
|
||||
margin-right: 2px;
|
||||
padding: 2px 5px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.network-route, .extra {
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.last {
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
.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
|
||||
}
|
||||
/*正文结束*/
|
||||
|
||||
/* 服务页 正文*/
|
||||
.service-status {}
|
||||
|
||||
.service-status .service-status-th{
|
||||
min-width:60px;
|
||||
}
|
||||
|
||||
.service-status .service-name-th{
|
||||
min-width:50px;
|
||||
}
|
||||
|
||||
.service-status .service-averagelatency-th{
|
||||
min-width:80px;
|
||||
}
|
||||
|
||||
.service-status .service-30daysonline-th{
|
||||
min-width:80px;
|
||||
}
|
||||
|
||||
.service-status .delay-today {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: left;
|
||||
}
|
||||
|
||||
.service-status .delay-today > i {
|
||||
.service-status .delay-today i {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
@@ -138,113 +341,293 @@ body {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin-right: 4px;
|
||||
margin-bottom: -3.25px;
|
||||
border-radius: 3px;
|
||||
box-shadow: inset 0 2px 2px rgba(0, 0, 0, .1);
|
||||
}
|
||||
|
||||
.service-status {
|
||||
|
||||
.service-status .tooltip-inner {
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
td.ping-network-quality {
|
||||
width: 600px;
|
||||
}
|
||||
/* 服务页 正文结束 */
|
||||
|
||||
/* 网络页 正文*/
|
||||
.network-box .btn-group.open .dropdown-toggle {
|
||||
box-shadow:unset;
|
||||
}
|
||||
|
||||
.network-box .network-box-header {
|
||||
font-size: 18px;
|
||||
padding: 5px 0px 15px 5px;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-style: solid;
|
||||
}
|
||||
|
||||
.network-box .network-box-header .dropdown-menu {
|
||||
max-height: 22.5em;
|
||||
overflow-y: auto;
|
||||
z-index:99999998;
|
||||
min-width: 200px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.network-box .network-box-header .dropdown-menu::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.network-box .network-box-header .dropdown-menu li {
|
||||
height: 2em;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.network-box .network-box-header .dropdown-menu li.input-group {
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 99999997;
|
||||
}
|
||||
|
||||
.network-box .network-box-header .dropdown-menu li.input-group input {
|
||||
width: 100%;
|
||||
height: 2em;
|
||||
border-top: none;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.network-box .network-box-header .dropdown-menu li a {
|
||||
padding: 5px 5px 5px 15px;
|
||||
}
|
||||
|
||||
.network-box .network-box-header .dropdown-menu li.hidden {
|
||||
height: 0;
|
||||
display: none;
|
||||
visibility:hidden;
|
||||
}
|
||||
|
||||
.network-box .network-box-header .dropdown-menu li.visible {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.network-box .chartTitle {
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
margin: 18px 0px 15px 0px;
|
||||
}
|
||||
|
||||
.network-box .chartTitle i.chartCountryCode {
|
||||
font-size: 16px;
|
||||
border-radius: 12.5%;
|
||||
}
|
||||
|
||||
/* 网络页 正文结束 */
|
||||
|
||||
/* 地图版服务器分布图 */
|
||||
#mapChartBox {
|
||||
z-index: 999999999;
|
||||
padding-left: 0 !important;
|
||||
}
|
||||
|
||||
.modal-open .modal {
|
||||
overflow-x: hidden;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.modal-dialog-centered {
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
display: flex;
|
||||
align-items: center; /* 垂直居中 */
|
||||
justify-content: center; /* 水平居中 */
|
||||
min-height: calc(100%) /* 确保模态框占据整个视口高度 */
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
pointer-events: auto;
|
||||
border-radius: unset;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.modal-header h4 {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.modal-header i.xclose {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
font-size: 30px;
|
||||
top: 12px;
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
a.template-item, a.cdn-item {
|
||||
display: block;
|
||||
}
|
||||
|
||||
footer {
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
footer p {
|
||||
text-align: center;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1200px) {
|
||||
.node-cell.os,
|
||||
.node-cell.location,
|
||||
.node-cell.uptime {
|
||||
display: none;
|
||||
visibility: hidden;
|
||||
.accordian-body {
|
||||
margin: 5px 0px 5px 10px;
|
||||
}
|
||||
table.monitor-detail {
|
||||
margin:0px 0px 2px 0px;
|
||||
}
|
||||
.table .network-thead-tr th,
|
||||
.table .node-group-tag th {
|
||||
font-size:16px;
|
||||
padding-bottom:6px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 720px) {
|
||||
body {
|
||||
font-size: 10px !important;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.node-cell.os,
|
||||
.node-cell.location,
|
||||
.node-cell.uptime {
|
||||
display: none;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.service-day-status-icon {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
margin-right: 2px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
/* 彩虹旗 */
|
||||
.fi-rb {
|
||||
background-image: url(/static/theme-server-status/img/rb.png);
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 620px) {
|
||||
@media only screen and (max-width: 767px) {
|
||||
body {
|
||||
font-size: 10px !important;
|
||||
padding-top:60px !important;
|
||||
}
|
||||
.nezha {
|
||||
min-height: calc(100vh - 90px);
|
||||
min-height: calc(var(--vh, 1vh) * 100 - 90px);
|
||||
}
|
||||
#chartbox {
|
||||
min-height: calc(100vh - 170px);
|
||||
min-height: calc(var(--vh, 1vh) * 100 - 170px);
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.node-cell.type,
|
||||
.node-cell.location,
|
||||
footer {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
.cycle-transfer-stats .dataTables_wrapper {
|
||||
margin-bottom: -10px;
|
||||
}
|
||||
.navbar .navbar-nav .open .dropdown-menu>li>a {
|
||||
color: #f1f1f1;
|
||||
}
|
||||
.navbar .navbar-nav .open .dropdown-menu {
|
||||
list-style-image: initial;
|
||||
background-color: #181a1b;
|
||||
border-color: rgba(140, 130, 115, 0.15);
|
||||
box-shadow: rgba(0, 0, 0, 0.18) 0px 6px 12px;
|
||||
}
|
||||
.table > tbody > tr > td:before,
|
||||
.table > tbody > tr > th:before,
|
||||
.table > tfoot > tr > td:before,
|
||||
.table > tfoot > tr > th:before,
|
||||
.table > thead > tr > td:before,
|
||||
.table > thead > tr > th:before {
|
||||
height: 0.5px;
|
||||
}
|
||||
.node-cell.os,
|
||||
.node-cell.uptime,
|
||||
.node-cell.traffic {
|
||||
display: none;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.service-day-status-icon {
|
||||
.node-cell.location {
|
||||
display: table-cell;
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 533px) {
|
||||
body {
|
||||
font-size: 10px !important;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.node-cell.os,
|
||||
.node-cell.location,
|
||||
.node-cell.uptime,
|
||||
.node-cell.traffic {
|
||||
.node-cell-os-text,.node-cell-location-text {
|
||||
display: none;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.service-day-status-icon {
|
||||
.node-cell.network {
|
||||
min-width: 95px;
|
||||
max-width: 95px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 450px) {
|
||||
body {
|
||||
font-size: 10px !important;
|
||||
.node-cell.remaining {
|
||||
min-width: 75px;
|
||||
max-width: 75px;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 0;
|
||||
.plan {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.node-cell.type,
|
||||
.node-cell.location,
|
||||
.node-cell.uptime,
|
||||
.node-cell.traffic {
|
||||
.accordian-body {
|
||||
margin: 5px 0px 5px 10px;
|
||||
}
|
||||
table.monitor-detail {
|
||||
margin:0px 0px 2px 0px;
|
||||
}
|
||||
.table .network-thead-tr th,
|
||||
.table .node-group-tag th {
|
||||
font-size:14px;
|
||||
padding-bottom:6px;
|
||||
}
|
||||
.table tr.network td {
|
||||
font-size: 12px;
|
||||
}
|
||||
.service-status .service-status-th {
|
||||
min-width:30px;
|
||||
}
|
||||
.service-status .delay-today {
|
||||
margin-top:4px;
|
||||
justify-content: center;
|
||||
}
|
||||
.service-status .delay-today i {
|
||||
margin-right:0px;
|
||||
}
|
||||
.service-status .delay-today-text {
|
||||
display: none;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.node-cell.cpu,
|
||||
.node-cell.ram,
|
||||
.node-cell.hdd {
|
||||
min-width: 25px;
|
||||
max-width: 50px;
|
||||
.service-status .service-averagelatency-th {
|
||||
min-width:70px;
|
||||
}
|
||||
.service-status .service-30daysonline-th {
|
||||
min-width:75px;
|
||||
}
|
||||
.toolbox {
|
||||
right: 18px;
|
||||
}
|
||||
td.ping-network-quality {
|
||||
width: 110px;
|
||||
}
|
||||
.network-box .network-box-header {
|
||||
margin: 8px 0px 0px 8px;
|
||||
font-size: 16px;
|
||||
border-bottom: none;
|
||||
}
|
||||
.network-box .chartTitle {
|
||||
font-size: 16px;
|
||||
margin: 10px 0px 10px 0px;
|
||||
}
|
||||
.network-box .chartTitle i.chartCountryCode {
|
||||
font-size: 15px;
|
||||
border-radius: 12.5%;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 262 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 5.8 KiB |
File diff suppressed because one or more lines are too long
-326
@@ -1,326 +0,0 @@
|
||||
/**
|
||||
* @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
|
||||
*/
|
||||
;(function(window, document) {
|
||||
/*jshint evil:true */
|
||||
/** version */
|
||||
var version = '3.7.3';
|
||||
|
||||
/** Preset options */
|
||||
var options = window.html5 || {};
|
||||
|
||||
/** Used to skip problem elements */
|
||||
var reSkip = /^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i;
|
||||
|
||||
/** Not all elements can be cloned in IE **/
|
||||
var saveClones = /^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i;
|
||||
|
||||
/** Detect whether the browser supports default html5 styles */
|
||||
var supportsHtml5Styles;
|
||||
|
||||
/** 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 */
|
||||
var expanID = 0;
|
||||
|
||||
/** Cached data for each document */
|
||||
var expandoData = {};
|
||||
|
||||
/** Detect whether the browser supports unknown elements */
|
||||
var supportsUnknownElements;
|
||||
|
||||
(function() {
|
||||
try {
|
||||
var a = document.createElement('a');
|
||||
a.innerHTML = '<xyz></xyz>';
|
||||
//if the hidden property is implemented we can assume, that the browser supports basic HTML5 Styles
|
||||
supportsHtml5Styles = ('hidden' in a);
|
||||
|
||||
supportsUnknownElements = a.childNodes.length == 1 || (function() {
|
||||
// assign a false positive if unable to shiv
|
||||
(document.createElement)('a');
|
||||
var frag = document.createDocumentFragment();
|
||||
return (
|
||||
typeof frag.cloneNode == 'undefined' ||
|
||||
typeof frag.createDocumentFragment == 'undefined' ||
|
||||
typeof frag.createElement == 'undefined'
|
||||
);
|
||||
}());
|
||||
} catch(e) {
|
||||
// assign a false positive if detection fails => unable to shiv
|
||||
supportsHtml5Styles = true;
|
||||
supportsUnknownElements = true;
|
||||
}
|
||||
|
||||
}());
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Creates a style sheet with the given CSS text and adds it to the document.
|
||||
* @private
|
||||
* @param {Document} ownerDocument The document.
|
||||
* @param {String} cssText The CSS text.
|
||||
* @returns {StyleSheet} The style element.
|
||||
*/
|
||||
function addStyleSheet(ownerDocument, cssText) {
|
||||
var p = ownerDocument.createElement('p'),
|
||||
parent = ownerDocument.getElementsByTagName('head')[0] || ownerDocument.documentElement;
|
||||
|
||||
p.innerHTML = 'x<style>' + cssText + '</style>';
|
||||
return parent.insertBefore(p.lastChild, parent.firstChild);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of `html5.elements` as an array.
|
||||
* @private
|
||||
* @returns {Array} An array of shived element node names.
|
||||
*/
|
||||
function getElements() {
|
||||
var elements = html5.elements;
|
||||
return typeof elements == 'string' ? elements.split(' ') : elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extends the built-in list of html5 elements
|
||||
* @memberOf html5
|
||||
* @param {String|Array} newElements whitespace separated list or array of new element names to shiv
|
||||
* @param {Document} ownerDocument The context document.
|
||||
*/
|
||||
function addElements(newElements, ownerDocument) {
|
||||
var elements = html5.elements;
|
||||
if(typeof elements != 'string'){
|
||||
elements = elements.join(' ');
|
||||
}
|
||||
if(typeof newElements != 'string'){
|
||||
newElements = newElements.join(' ');
|
||||
}
|
||||
html5.elements = elements +' '+ newElements;
|
||||
shivDocument(ownerDocument);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data associated to the given document
|
||||
* @private
|
||||
* @param {Document} ownerDocument The document.
|
||||
* @returns {Object} An object of data.
|
||||
*/
|
||||
function getExpandoData(ownerDocument) {
|
||||
var data = expandoData[ownerDocument[expando]];
|
||||
if (!data) {
|
||||
data = {};
|
||||
expanID++;
|
||||
ownerDocument[expando] = expanID;
|
||||
expandoData[expanID] = data;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns a shived element for the given nodeName and document
|
||||
* @memberOf html5
|
||||
* @param {String} nodeName name of the element
|
||||
* @param {Document|DocumentFragment} ownerDocument The context document.
|
||||
* @returns {Object} The shived element.
|
||||
*/
|
||||
function createElement(nodeName, ownerDocument, data){
|
||||
if (!ownerDocument) {
|
||||
ownerDocument = document;
|
||||
}
|
||||
if(supportsUnknownElements){
|
||||
return ownerDocument.createElement(nodeName);
|
||||
}
|
||||
if (!data) {
|
||||
data = getExpandoData(ownerDocument);
|
||||
}
|
||||
var node;
|
||||
|
||||
if (data.cache[nodeName]) {
|
||||
node = data.cache[nodeName].cloneNode();
|
||||
} else if (saveClones.test(nodeName)) {
|
||||
node = (data.cache[nodeName] = data.createElem(nodeName)).cloneNode();
|
||||
} else {
|
||||
node = data.createElem(nodeName);
|
||||
}
|
||||
|
||||
// Avoid adding some elements to fragments in IE < 9 because
|
||||
// * Attributes like `name` or `type` cannot be set/changed once an element
|
||||
// is inserted into a document/fragment
|
||||
// * Link elements with `src` attributes that are inaccessible, as with
|
||||
// a 403 response, will cause the tab/window to crash
|
||||
// * Script elements appended to fragments will execute when their `src`
|
||||
// or `text` property is set
|
||||
return node.canHaveChildren && !reSkip.test(nodeName) && !node.tagUrn ? data.frag.appendChild(node) : node;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns a shived DocumentFragment for the given document
|
||||
* @memberOf html5
|
||||
* @param {Document} ownerDocument The context document.
|
||||
* @returns {Object} The shived DocumentFragment.
|
||||
*/
|
||||
function createDocumentFragment(ownerDocument, data){
|
||||
if (!ownerDocument) {
|
||||
ownerDocument = document;
|
||||
}
|
||||
if(supportsUnknownElements){
|
||||
return ownerDocument.createDocumentFragment();
|
||||
}
|
||||
data = data || getExpandoData(ownerDocument);
|
||||
var clone = data.frag.cloneNode(),
|
||||
i = 0,
|
||||
elems = getElements(),
|
||||
l = elems.length;
|
||||
for(;i<l;i++){
|
||||
clone.createElement(elems[i]);
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shivs the `createElement` and `createDocumentFragment` methods of the document.
|
||||
* @private
|
||||
* @param {Document|DocumentFragment} ownerDocument The document.
|
||||
* @param {Object} data of the document.
|
||||
*/
|
||||
function shivMethods(ownerDocument, data) {
|
||||
if (!data.cache) {
|
||||
data.cache = {};
|
||||
data.createElem = ownerDocument.createElement;
|
||||
data.createFrag = ownerDocument.createDocumentFragment;
|
||||
data.frag = data.createFrag();
|
||||
}
|
||||
|
||||
|
||||
ownerDocument.createElement = function(nodeName) {
|
||||
//abort shiv
|
||||
if (!html5.shivMethods) {
|
||||
return data.createElem(nodeName);
|
||||
}
|
||||
return createElement(nodeName, ownerDocument, data);
|
||||
};
|
||||
|
||||
ownerDocument.createDocumentFragment = Function('h,f', 'return function(){' +
|
||||
'var n=f.cloneNode(),c=n.createElement;' +
|
||||
'h.shivMethods&&(' +
|
||||
// unroll the `createElement` calls
|
||||
getElements().join().replace(/[\w\-:]+/g, function(nodeName) {
|
||||
data.createElem(nodeName);
|
||||
data.frag.createElement(nodeName);
|
||||
return 'c("' + nodeName + '")';
|
||||
}) +
|
||||
');return n}'
|
||||
)(html5, data.frag);
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Shivs the given document.
|
||||
* @memberOf html5
|
||||
* @param {Document} ownerDocument The document to shiv.
|
||||
* @returns {Document} The shived document.
|
||||
*/
|
||||
function shivDocument(ownerDocument) {
|
||||
if (!ownerDocument) {
|
||||
ownerDocument = document;
|
||||
}
|
||||
var data = getExpandoData(ownerDocument);
|
||||
|
||||
if (html5.shivCSS && !supportsHtml5Styles && !data.hasCSS) {
|
||||
data.hasCSS = !!addStyleSheet(ownerDocument,
|
||||
// corrects block display not defined in IE6/7/8/9
|
||||
'article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}' +
|
||||
// adds styling not present in IE6/7/8/9
|
||||
'mark{background:#FF0;color:#000}' +
|
||||
// hides non-rendered elements
|
||||
'template{display:none}'
|
||||
);
|
||||
}
|
||||
if (!supportsUnknownElements) {
|
||||
shivMethods(ownerDocument, data);
|
||||
}
|
||||
return ownerDocument;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* The `html5` object is exposed so that more elements can be shived and
|
||||
* existing shiving can be detected on iframes.
|
||||
* @type Object
|
||||
* @example
|
||||
*
|
||||
* // options can be changed before the script is included
|
||||
* html5 = { 'elements': 'mark section', 'shivCSS': false, 'shivMethods': false };
|
||||
*/
|
||||
var html5 = {
|
||||
|
||||
/**
|
||||
* An array or space separated string of node names of the elements to shiv.
|
||||
* @memberOf html5
|
||||
* @type Array|String
|
||||
*/
|
||||
'elements': options.elements || 'abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video',
|
||||
|
||||
/**
|
||||
* current version of html5shiv
|
||||
*/
|
||||
'version': version,
|
||||
|
||||
/**
|
||||
* A flag to indicate that the HTML5 style sheet should be inserted.
|
||||
* @memberOf html5
|
||||
* @type Boolean
|
||||
*/
|
||||
'shivCSS': (options.shivCSS !== false),
|
||||
|
||||
/**
|
||||
* Is equal to true if a browser supports creating unknown/HTML5 elements
|
||||
* @memberOf html5
|
||||
* @type boolean
|
||||
*/
|
||||
'supportsUnknownElements': supportsUnknownElements,
|
||||
|
||||
/**
|
||||
* A flag to indicate that the document's `createElement` and `createDocumentFragment`
|
||||
* methods should be overwritten.
|
||||
* @memberOf html5
|
||||
* @type Boolean
|
||||
*/
|
||||
'shivMethods': (options.shivMethods !== false),
|
||||
|
||||
/**
|
||||
* A string to describe the type of `html5` object ("default" or "default print").
|
||||
* @memberOf html5
|
||||
* @type String
|
||||
*/
|
||||
'type': 'default',
|
||||
|
||||
// shivs the document according to the specified `html5` object options
|
||||
'shivDocument': shivDocument,
|
||||
|
||||
//creates a shived element
|
||||
createElement: createElement,
|
||||
|
||||
//creates a shived documentFragment
|
||||
createDocumentFragment: createDocumentFragment,
|
||||
|
||||
//extends list of elements
|
||||
addElements: addElements
|
||||
};
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
// expose html5
|
||||
window.html5 = html5;
|
||||
|
||||
// shiv the document
|
||||
shivDocument(document);
|
||||
|
||||
if(typeof module == 'object' && module.exports){
|
||||
module.exports = html5;
|
||||
}
|
||||
|
||||
}(typeof window !== "undefined" ? window : this, document));
|
||||
File diff suppressed because one or more lines are too long
+149
-30
@@ -1,49 +1,168 @@
|
||||
const mixinsVue = {
|
||||
data: {
|
||||
cache: [],
|
||||
isMobile: false,
|
||||
theme: "light",
|
||||
isSystemTheme: false
|
||||
isSystemTheme: false,
|
||||
showGroup: false,
|
||||
showGoTop: false,
|
||||
showTools: false,
|
||||
preferredTemplate: null,
|
||||
semiTransparent: 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' }
|
||||
],
|
||||
colors: [],
|
||||
colorsDark: ['#4992FF', '#08C091', '#FDDD5F', '#FF6E76', '#58D9F9', '#7CFFB2', '#FF8A44', '#8D48E3', '#DD79FF', '#5470C6', '#3BA272', '#FAC758', '#EE6666', '#72C0DE', '#91CC76', '#FB8352', '#9A60B4', '#EA7BCC'],
|
||||
colorsLight: ['#5470C6', '#3BA272', '#FAC758', '#EE6666', '#72C0DE', '#91CC76', '#FB8352', '#9A60B4', '#EA7BCC', '#4992FF', '#08C091', '#FDDD5F', '#FF6E76', '#58D9F9', '#7CFFB2', '#FF8A44', '#8D48E3', '#DD79FF'],
|
||||
},
|
||||
created() {
|
||||
this.initTheme()
|
||||
this.isMobile = this.checkIsMobile();
|
||||
this.theme = this.initTheme();
|
||||
this.showGroup = this.initShowGroup();
|
||||
this.semiTransparent = this.initSemiTransparent();
|
||||
this.preferredTemplate = this.getCookie('preferred_theme') ? this.getCookie('preferred_theme') : this.$root.defaultTemplate;
|
||||
this.colors = this.theme == "dark" ? this.colorsDark : this.colorsLight;
|
||||
this.setBenchmarkHeight();
|
||||
window.addEventListener('scroll', this.handleScroll);
|
||||
window.addEventListener('resize', this.setBenchmarkHeight());
|
||||
},
|
||||
destroyed() {
|
||||
window.removeEventListener('scroll', this.handleScroll);
|
||||
},
|
||||
methods: {
|
||||
setTheme(title, store = false) {
|
||||
this.theme = title
|
||||
document.body.setAttribute("theme", title)
|
||||
if (store) {
|
||||
localStorage.setItem("theme", title)
|
||||
this.isSystemTheme = false
|
||||
toggleTemplate(template) {
|
||||
if( template != this.preferredTemplate){
|
||||
this.preferredTemplate = template;
|
||||
this.updateCookie("preferred_theme", template);
|
||||
window.location.reload();
|
||||
}
|
||||
},
|
||||
setSystemTheme() {
|
||||
localStorage.removeItem("theme")
|
||||
this.initTheme()
|
||||
this.isSystemTheme = true
|
||||
toggleShowTools() {
|
||||
this.showTools = !this.showTools;
|
||||
},
|
||||
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);
|
||||
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.$root.page == 'network') {
|
||||
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();
|
||||
}
|
||||
},
|
||||
initSemiTransparent() {
|
||||
const storedSemiTransparent = localStorage.getItem("semiTransparent");
|
||||
const semiTransparent = storedSemiTransparent !== null ? JSON.parse(storedSemiTransparent) : false;
|
||||
if (storedSemiTransparent === null) {
|
||||
localStorage.setItem("semiTransparent", semiTransparent);
|
||||
}
|
||||
return semiTransparent;
|
||||
},
|
||||
toggleSemiTransparent(){
|
||||
this.semiTransparent = !this.semiTransparent;
|
||||
localStorage.setItem("semiTransparent", this.semiTransparent);
|
||||
if(this.$root.page == 'index' || this.$root.page == 'network') {
|
||||
this.reloadCharts(); // 重新载入echarts图表
|
||||
}
|
||||
},
|
||||
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;
|
||||
},
|
||||
toFixed2(f) {
|
||||
return f.toFixed(2)
|
||||
},
|
||||
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);
|
||||
}
|
||||
});
|
||||
},
|
||||
goTop() {
|
||||
$('html, body').animate({ scrollTop: 0 }, 400);
|
||||
return false;
|
||||
},
|
||||
handleScroll() {
|
||||
this.showGoTop = window.scrollY >= 100;
|
||||
if(this.showTools) this.showTools = false;
|
||||
},
|
||||
groupingData(data, field) {
|
||||
let map = new Map();
|
||||
let dest = [];
|
||||
|
||||
data.forEach(item => {
|
||||
if (!map.has(item[field])) {
|
||||
dest.push({
|
||||
[field]: item[field],
|
||||
data: [item]
|
||||
});
|
||||
map.set(item[field], item);
|
||||
} else {
|
||||
dest.find(dItem => dItem[field] === item[field]).data.push(item);
|
||||
}
|
||||
});
|
||||
return dest;
|
||||
},
|
||||
checkIsMobile() { // 检测设备类型,页面宽度小于768px认为是移动设备
|
||||
return window.innerWidth <= 768;
|
||||
},
|
||||
isMenuActive(page){
|
||||
if(page == this.$root.page) {
|
||||
return this.isMobile ? 'm-active' : 'pc-active';
|
||||
}
|
||||
},
|
||||
setBenchmarkHeight() {
|
||||
let vh = window.innerHeight * 0.01;
|
||||
document.documentElement.style.setProperty('--vh', `${vh}px`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
/*! Respond.js v1.4.2: min/max-width media query polyfill * Copyright 2013 Scott Jehl
|
||||
* Licensed under https://github.com/scottjehl/Respond/blob/master/LICENSE-MIT
|
||||
* */
|
||||
|
||||
!function(a){"use strict";a.matchMedia=a.matchMedia||function(a){var b,c=a.documentElement,d=c.firstElementChild||c.firstChild,e=a.createElement("body"),f=a.createElement("div");return f.id="mq-test-1",f.style.cssText="position:absolute;top:-100em",e.style.background="none",e.appendChild(f),function(a){return f.innerHTML='­<style media="'+a+'"> #mq-test-1 { width: 42px; }</style>',c.insertBefore(e,d),b=42===f.offsetWidth,c.removeChild(e),{matches:b,media:a}}}(a.document)}(this),function(a){"use strict";function b(){u(!0)}var c={};a.respond=c,c.update=function(){};var d=[],e=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}(),f=function(a,b){var c=e();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))};if(c.ajax=f,c.queue=d,c.regex={media:/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi,keyframes:/@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,urls:/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,findStyles:/@media *([^\{]+)\{([\S\s]+?)$/,only:/(only\s+)?([a-zA-Z]+)\s?/,minw:/\([\s]*min\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/,maxw:/\([\s]*max\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/},c.mediaQueriesSupported=a.matchMedia&&null!==a.matchMedia("only all")&&a.matchMedia("only all").matches,!c.mediaQueriesSupported){var g,h,i,j=a.document,k=j.documentElement,l=[],m=[],n=[],o={},p=30,q=j.getElementsByTagName("head")[0]||k,r=j.getElementsByTagName("base")[0],s=q.getElementsByTagName("link"),t=function(){var a,b=j.createElement("div"),c=j.body,d=k.style.fontSize,e=c&&c.style.fontSize,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",c||(c=f=j.createElement("body"),c.style.background="none"),k.style.fontSize="100%",c.style.fontSize="100%",c.appendChild(b),f&&k.insertBefore(c,k.firstChild),a=b.offsetWidth,f?k.removeChild(c):c.removeChild(b),k.style.fontSize=d,e&&(c.style.fontSize=e),a=i=parseFloat(a)},u=function(b){var c="clientWidth",d=k[c],e="CSS1Compat"===j.compatMode&&d||j.body[c]||d,f={},o=s[s.length-1],r=(new Date).getTime();if(b&&g&&p>r-g)return a.clearTimeout(h),h=a.setTimeout(u,p),void 0;g=r;for(var v in l)if(l.hasOwnProperty(v)){var w=l[v],x=w.minw,y=w.maxw,z=null===x,A=null===y,B="em";x&&(x=parseFloat(x)*(x.indexOf(B)>-1?i||t():1)),y&&(y=parseFloat(y)*(y.indexOf(B)>-1?i||t():1)),w.hasquery&&(z&&A||!(z||e>=x)||!(A||y>=e))||(f[w.media]||(f[w.media]=[]),f[w.media].push(m[w.rules]))}for(var C in n)n.hasOwnProperty(C)&&n[C]&&n[C].parentNode===q&&q.removeChild(n[C]);n.length=0;for(var D in f)if(f.hasOwnProperty(D)){var E=j.createElement("style"),F=f[D].join("\n");E.type="text/css",E.media=D,q.insertBefore(E,o.nextSibling),E.styleSheet?E.styleSheet.cssText=F:E.appendChild(j.createTextNode(F)),n.push(E)}},v=function(a,b,d){var e=a.replace(c.regex.keyframes,"").match(c.regex.media),f=e&&e.length||0;b=b.substring(0,b.lastIndexOf("/"));var g=function(a){return a.replace(c.regex.urls,"$1"+b+"$2$3")},h=!f&&d;b.length&&(b+="/"),h&&(f=1);for(var i=0;f>i;i++){var j,k,n,o;h?(j=d,m.push(g(a))):(j=e[i].match(c.regex.findStyles)&&RegExp.$1,m.push(RegExp.$2&&g(RegExp.$2))),n=j.split(","),o=n.length;for(var p=0;o>p;p++)k=n[p],l.push({media:k.split("(")[0].match(c.regex.only)&&RegExp.$2||"all",rules:m.length-1,hasquery:k.indexOf("(")>-1,minw:k.match(c.regex.minw)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:k.match(c.regex.maxw)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}u()},w=function(){if(d.length){var b=d.shift();f(b.href,function(c){v(c,b.href,b.media),o[b.href]=!0,a.setTimeout(function(){w()},0)})}},x=function(){for(var b=0;b<s.length;b++){var c=s[b],e=c.href,f=c.media,g=c.rel&&"stylesheet"===c.rel.toLowerCase();e&&g&&!o[e]&&(c.styleSheet&&c.styleSheet.rawCssText?(v(c.styleSheet.rawCssText,e,f),o[e]=!0):(!/^([a-zA-Z:]*\/\/)/.test(e)&&!r||e.replace(RegExp.$1,"").split("/")[0]===a.location.host)&&("//"===e.substring(0,2)&&(e=a.location.protocol+e),d.push({href:e,media:f})))}w()};x(),c.update=x,c.getEmValue=t,a.addEventListener?a.addEventListener("resize",b,!1):a.attachEvent&&a.attachEvent("onresize",b)}}(this);
|
||||
+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
-4
@@ -6,11 +6,11 @@
|
||||
{{.Version}}</small>
|
||||
</div>
|
||||
</div>
|
||||
<script src="https://cdn.staticfile.org/jquery/3.6.0/jquery.min.js"></script>
|
||||
<script src="https://cdn.staticfile.org/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://cdn.staticfile.org/vue/2.6.14/vue.min.js"></script>
|
||||
<script src="/static/main.js?v20230614"></script>
|
||||
<script src="https://unpkg.com/vue@2.6.14/dist/vue.min.js"></script>
|
||||
<script src="/static/main.js?v20241011"></script>
|
||||
<script>
|
||||
(function () {
|
||||
updateLang({{.LANG }});
|
||||
|
||||
Vendored
+10
-9
@@ -1,21 +1,22 @@
|
||||
{{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://cdn.staticfile.org/semantic-ui/2.4.1/semantic.min.css">
|
||||
<link href="https://cdn.staticfile.org/font-logos/0.17/font-logos.min.css" type="text/css"
|
||||
rel="stylesheet" />
|
||||
<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" />
|
||||
<link rel="stylesheet" type="text/css" href="https://unpkg.com/[email protected]/dist/semantic.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="https://unpkg.com/[email protected]/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?v20240813">
|
||||
<link rel="stylesheet" type="text/css" href="/static/darkmode.css?v20240813">
|
||||
<script>
|
||||
document.documentElement.setAttribute('nz-theme', window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
|
||||
</script>
|
||||
</head>
|
||||
|
||||
{{if ts .CustomCodeDashboard}} {{.CustomCodeDashboard|safe}} {{end}}
|
||||
<body>
|
||||
{{end}}
|
||||
{{end}}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user