mirror of
https://github.com/XrayR-project/XrayR.git
synced 2025-06-08 05:19:54 +00:00
Compare commits
181 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
ea125e4a4f | ||
![]() |
6754483cca | ||
![]() |
974abc9a98 | ||
![]() |
b1f5ccfee8 | ||
![]() |
2f88334373 | ||
![]() |
35e726e766 | ||
![]() |
4bbf3c9899 | ||
![]() |
9391c759c9 | ||
![]() |
eb16b709e2 | ||
![]() |
1f09502613 | ||
![]() |
0ae73a0eca | ||
![]() |
63c1a18a7d | ||
![]() |
34726d1659 | ||
![]() |
5edc68d475 | ||
![]() |
944e8cd6a8 | ||
![]() |
c4581ad34b | ||
![]() |
127318ccbd | ||
![]() |
db784f18e6 | ||
![]() |
8a5a5e4356 | ||
![]() |
cfdb5166f3 | ||
![]() |
540be1b7c8 | ||
![]() |
98014fa60f | ||
![]() |
1de718a5fc | ||
![]() |
81c068bad4 | ||
![]() |
dbde0c24fc | ||
![]() |
1bbd061d42 | ||
![]() |
793f26e763 | ||
![]() |
175e46d0b7 | ||
![]() |
9261a6063d | ||
![]() |
5ee2679924 | ||
![]() |
06fe198243 | ||
![]() |
b3f31bf06b | ||
![]() |
0010a876f3 | ||
![]() |
dbed635aae | ||
![]() |
561f317e24 | ||
![]() |
c24a2a28f2 | ||
![]() |
fa80ed8014 | ||
![]() |
5409cb2dc3 | ||
![]() |
b8b3a16d3b | ||
![]() |
d1e5762937 | ||
![]() |
4f1dafa8ed | ||
![]() |
aae0f9b24d | ||
![]() |
aee8ca2980 | ||
![]() |
933e745a70 | ||
![]() |
515fc708dc | ||
![]() |
5e134967fd | ||
![]() |
4a234d50e2 | ||
![]() |
115d7bad6f | ||
![]() |
5ba0624bbc | ||
![]() |
4439afa29b | ||
![]() |
03c9fe4218 | ||
![]() |
f648aedc91 | ||
![]() |
1720fbdc4c | ||
![]() |
cd7907c4c9 | ||
![]() |
296ba86bdd | ||
![]() |
43e6ec85cd | ||
![]() |
7ba2d999f0 | ||
![]() |
53141f4416 | ||
![]() |
e378f0a367 | ||
![]() |
d01a4265cd | ||
![]() |
94d2c7c23a | ||
![]() |
3de7600a4c | ||
![]() |
a0378b6cff | ||
![]() |
4688833bc7 | ||
![]() |
294a2dfacb | ||
![]() |
5780731cd3 | ||
![]() |
911b0c2ff5 | ||
![]() |
fa7fb7087f | ||
![]() |
0a6bca9755 | ||
![]() |
8562c798a9 | ||
![]() |
56199afa90 | ||
![]() |
c688cf02b4 | ||
![]() |
c170272f40 | ||
![]() |
6ffbe599b4 | ||
![]() |
78c2e31bdf | ||
![]() |
c04d52330d | ||
![]() |
6f4bf62113 | ||
![]() |
56e993ce43 | ||
![]() |
6d3d6f9e53 | ||
![]() |
76076b2d6a | ||
![]() |
4e48fdbe61 | ||
![]() |
0f3a4a0008 | ||
![]() |
d9971b2181 | ||
![]() |
4d1ed21dc7 | ||
![]() |
9a03f85930 | ||
![]() |
11b46ea485 | ||
![]() |
dc3d256d85 | ||
![]() |
287c30e7d0 | ||
![]() |
97d89549dd | ||
![]() |
cac4288e07 | ||
![]() |
d1d5193cec | ||
![]() |
85607527a5 | ||
![]() |
fd0a23bf6c | ||
![]() |
b1bfd04895 | ||
![]() |
edf02307ad | ||
![]() |
551e2d4299 | ||
![]() |
0254c6c557 | ||
![]() |
4c52e33adb | ||
![]() |
b8d40c201b | ||
![]() |
dbd4a85a6c | ||
![]() |
8f28716b21 | ||
![]() |
73bc37cb51 | ||
![]() |
78f2f88296 | ||
![]() |
42a2226769 | ||
![]() |
71aba0601e | ||
![]() |
5fe18e020d | ||
![]() |
a0f2730bb2 | ||
![]() |
0c10c59877 | ||
![]() |
119e4810f2 | ||
![]() |
5dad910488 | ||
![]() |
8841e55f70 | ||
![]() |
5d20732881 | ||
![]() |
d579933451 | ||
![]() |
46e836f93b | ||
![]() |
e27b0c6cd8 | ||
![]() |
e34a3b4a94 | ||
![]() |
b366171401 | ||
![]() |
b034a2d48f | ||
![]() |
65b25ed3f6 | ||
![]() |
b65dfd7f92 | ||
![]() |
6569a0bf36 | ||
![]() |
e5e5e4ef92 | ||
![]() |
f35a056cc6 | ||
![]() |
73ffd3f505 | ||
![]() |
73c5e28f41 | ||
![]() |
f1b45c02f4 | ||
![]() |
941f256ba5 | ||
![]() |
2e547afdb7 | ||
![]() |
422ed1a311 | ||
![]() |
214e412993 | ||
![]() |
f85f6b47ee | ||
![]() |
7e09aef1cc | ||
![]() |
0b4caba8f6 | ||
![]() |
b0866011e5 | ||
![]() |
4d1d89b837 | ||
![]() |
0febf96021 | ||
![]() |
0f10e837e4 | ||
![]() |
5ab352f9c9 | ||
![]() |
5d5470a919 | ||
![]() |
db27722bbc | ||
![]() |
914510c687 | ||
![]() |
a8226b01e2 | ||
![]() |
54a958f39c | ||
![]() |
a1e407e18f | ||
![]() |
42da6c155d | ||
![]() |
89f2342a42 | ||
![]() |
ee1a606888 | ||
![]() |
c183c6492e | ||
![]() |
a70a0d9a31 | ||
![]() |
4c651e15fa | ||
![]() |
dda00c5dd6 | ||
![]() |
28e1b82320 | ||
![]() |
f1ab2eac13 | ||
![]() |
4f13aac094 | ||
![]() |
4ab196ad29 | ||
![]() |
9590697c29 | ||
![]() |
0bec6c4fdf | ||
![]() |
223589ba14 | ||
![]() |
6ba7fe2776 | ||
![]() |
b49798ab16 | ||
![]() |
af9224f5bb | ||
![]() |
c54650f195 | ||
![]() |
c73af2309b | ||
![]() |
169f742b76 | ||
![]() |
0fa8a45e51 | ||
![]() |
17c538a5b7 | ||
![]() |
5625d570fd | ||
![]() |
9b0e55f037 | ||
![]() |
4597d6ac56 | ||
![]() |
9aaad5e8ad | ||
![]() |
94d82f33ea | ||
![]() |
49aa520d67 | ||
![]() |
73c3047651 | ||
![]() |
613da96543 | ||
![]() |
76127f4757 | ||
![]() |
1d5a34cf98 | ||
![]() |
bf13971502 | ||
![]() |
572be38ae4 | ||
![]() |
34d96c9338 | ||
![]() |
4e69c0656e | ||
![]() |
88f7709fad |
6
.github/workflows/codeql-analysis.yml
vendored
6
.github/workflows/codeql-analysis.yml
vendored
@ -46,7 +46,11 @@ jobs:
|
|||||||
# By default, queries listed here will override any specified in a config file.
|
# By default, queries listed here will override any specified in a config file.
|
||||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||||
|
|
||||||
|
- name: Install Go
|
||||||
|
uses: actions/setup-go@v4
|
||||||
|
with:
|
||||||
|
go-version-file: go.mod
|
||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
|
116
.github/workflows/docker.yml
vendored
116
.github/workflows/docker.yml
vendored
@ -1,44 +1,94 @@
|
|||||||
name: Publish Docker image
|
name: Publish Docker image
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
release:
|
||||||
push:
|
types:
|
||||||
branches:
|
- published
|
||||||
- master
|
env:
|
||||||
paths:
|
REGISTRY: ghcr.io
|
||||||
- "**/*.go"
|
IMAGE_NAME: xrayr-project/xrayr
|
||||||
- "go.mod"
|
|
||||||
- "go.sum"
|
|
||||||
- ".github/workflows/*.yml"
|
|
||||||
tags:
|
|
||||||
- 'v*'
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- 'master'
|
|
||||||
jobs:
|
jobs:
|
||||||
push_to_registry:
|
build:
|
||||||
name: Push Docker image to Docker Hub
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
platform:
|
||||||
|
- linux/amd64
|
||||||
|
- linux/arm/v6
|
||||||
|
- linux/arm/v7
|
||||||
|
- linux/arm64
|
||||||
|
- linux/s390x
|
||||||
steps:
|
steps:
|
||||||
- name: Check out the repo
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v2
|
|
||||||
- name: Log in to the Container registry
|
|
||||||
uses: docker/login-action@v2
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v4
|
uses: docker/metadata-action@v5
|
||||||
with:
|
with:
|
||||||
images: ghcr.io/${{ github.repository }}
|
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||||
- name: Build and push
|
- name: Set up QEMU
|
||||||
uses: docker/build-push-action@v3
|
uses: docker/setup-qemu-action@v3
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: Build and push by digest
|
||||||
|
id: build
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/arm/v7,linux/arm64,linux/amd64,linux/s390x
|
platforms: ${{ matrix.platform }}
|
||||||
push: ${{ github.event_name != 'pull_request' }}
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
outputs: type=image,name=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true
|
||||||
|
- name: Export digest
|
||||||
|
run: |
|
||||||
|
mkdir -p /tmp/digests
|
||||||
|
digest="${{ steps.build.outputs.digest }}"
|
||||||
|
touch "/tmp/digests/${digest#sha256:}"
|
||||||
|
- name: Upload digest
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: digests
|
||||||
|
path: /tmp/digests/*
|
||||||
|
if-no-files-found: error
|
||||||
|
retention-days: 1
|
||||||
|
|
||||||
|
merge:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs:
|
||||||
|
- build
|
||||||
|
steps:
|
||||||
|
- name: Download digests
|
||||||
|
uses: actions/download-artifact@v4.1.7
|
||||||
|
with:
|
||||||
|
name: digests
|
||||||
|
path: /tmp/digests
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
- name: Docker meta
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: Create manifest list and push
|
||||||
|
working-directory: /tmp/digests
|
||||||
|
run: |
|
||||||
|
ls -al
|
||||||
|
echo docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
||||||
|
$(printf '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@sha256:%s ' *)
|
||||||
|
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
||||||
|
$(printf '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@sha256:%s ' *)
|
||||||
|
- name: Inspect image
|
||||||
|
run: |
|
||||||
|
docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}
|
||||||
|
24
.github/workflows/release.yml
vendored
24
.github/workflows/release.yml
vendored
@ -26,12 +26,10 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
# Include amd64 on all platforms.
|
# Include amd64 on all platforms.
|
||||||
goos: [ windows, freebsd, openbsd, linux, dragonfly, darwin ]
|
goos: [ windows, freebsd, openbsd, linux, darwin ]
|
||||||
goarch: [ amd64, 386 ]
|
goarch: [ amd64, 386 ]
|
||||||
exclude:
|
exclude:
|
||||||
# Exclude i386 on darwin and dragonfly.
|
# Exclude i386 on darwin.
|
||||||
- goarch: 386
|
|
||||||
goos: dragonfly
|
|
||||||
- goarch: 386
|
- goarch: 386
|
||||||
goos: darwin
|
goos: darwin
|
||||||
- goarch: 386
|
- goarch: 386
|
||||||
@ -122,12 +120,12 @@ jobs:
|
|||||||
- name: Build XrayR
|
- name: Build XrayR
|
||||||
run: |
|
run: |
|
||||||
mkdir -p build_assets
|
mkdir -p build_assets
|
||||||
go build -v -o build_assets/XrayR -trimpath -ldflags "-s -w -buildid=" ./main
|
go build -v -o build_assets/XrayR -trimpath -ldflags "-s -w -buildid="
|
||||||
|
|
||||||
- name: Build Mips softfloat XrayR
|
- name: Build Mips softfloat XrayR
|
||||||
if: matrix.goarch == 'mips' || matrix.goarch == 'mipsle'
|
if: matrix.goarch == 'mips' || matrix.goarch == 'mipsle'
|
||||||
run: |
|
run: |
|
||||||
GOMIPS=softfloat go build -v -o build_assets/XrayR_softfloat -trimpath -ldflags "-s -w -buildid=" ./main
|
GOMIPS=softfloat go build -v -o build_assets/XrayR_softfloat -trimpath -ldflags "-s -w -buildid="
|
||||||
- name: Rename Windows XrayR
|
- name: Rename Windows XrayR
|
||||||
if: matrix.goos == 'windows'
|
if: matrix.goos == 'windows'
|
||||||
run: |
|
run: |
|
||||||
@ -143,12 +141,12 @@ jobs:
|
|||||||
command: |
|
command: |
|
||||||
cp ${GITHUB_WORKSPACE}/README.md ./build_assets/README.md
|
cp ${GITHUB_WORKSPACE}/README.md ./build_assets/README.md
|
||||||
cp ${GITHUB_WORKSPACE}/LICENSE ./build_assets/LICENSE
|
cp ${GITHUB_WORKSPACE}/LICENSE ./build_assets/LICENSE
|
||||||
cp ${GITHUB_WORKSPACE}/main/dns.json ./build_assets/dns.json
|
cp ${GITHUB_WORKSPACE}/release/config/dns.json ./build_assets/dns.json
|
||||||
cp ${GITHUB_WORKSPACE}/main/route.json ./build_assets/route.json
|
cp ${GITHUB_WORKSPACE}/release/config/route.json ./build_assets/route.json
|
||||||
cp ${GITHUB_WORKSPACE}/main/custom_outbound.json ./build_assets/custom_outbound.json
|
cp ${GITHUB_WORKSPACE}/release/config/custom_outbound.json ./build_assets/custom_outbound.json
|
||||||
cp ${GITHUB_WORKSPACE}/main/custom_inbound.json ./build_assets/custom_inbound.json
|
cp ${GITHUB_WORKSPACE}/release/config/custom_inbound.json ./build_assets/custom_inbound.json
|
||||||
cp ${GITHUB_WORKSPACE}/main/rulelist ./build_assets/rulelist
|
cp ${GITHUB_WORKSPACE}/release/config/rulelist ./build_assets/rulelist
|
||||||
cp ${GITHUB_WORKSPACE}/main/config.yml.example ./build_assets/config.yml
|
cp ${GITHUB_WORKSPACE}/release/config/config.yml.example ./build_assets/config.yml
|
||||||
LIST=('geoip geoip geoip' 'domain-list-community dlc geosite')
|
LIST=('geoip geoip geoip' 'domain-list-community dlc geosite')
|
||||||
for i in "${LIST[@]}"
|
for i in "${LIST[@]}"
|
||||||
do
|
do
|
||||||
@ -178,7 +176,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
mv build_assets XrayR-$ASSET_NAME
|
mv build_assets XrayR-$ASSET_NAME
|
||||||
- name: Upload files to Artifacts
|
- name: Upload files to Artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: XrayR-${{ steps.get_filename.outputs.ASSET_NAME }}
|
name: XrayR-${{ steps.get_filename.outputs.ASSET_NAME }}
|
||||||
path: |
|
path: |
|
||||||
|
38
.gitignore
vendored
38
.gitignore
vendored
@ -1,18 +1,20 @@
|
|||||||
main/main
|
.idea
|
||||||
main/XrayR
|
*.iml
|
||||||
main/XrayR*
|
out
|
||||||
main/mytest
|
gen
|
||||||
main/access.logo
|
*.exe
|
||||||
main/error.log
|
*.exe~
|
||||||
api/chooseparser.go.bak
|
*.dll
|
||||||
common/Inboundbuilder/.lego/
|
*.so
|
||||||
common/legocmd/.lego/
|
*.dylib
|
||||||
.vscode/launch.json
|
*.test
|
||||||
main/.lego
|
*.out
|
||||||
main/cert
|
go.work
|
||||||
main/config.yml
|
main
|
||||||
./vscode
|
XrayR
|
||||||
.idea/*
|
XrayR*
|
||||||
.DS_Store
|
access.log
|
||||||
*.bak
|
error.log
|
||||||
go.work*
|
.lego
|
||||||
|
cert
|
||||||
|
config.yml
|
@ -1,10 +1,10 @@
|
|||||||
# Build go
|
# Build go
|
||||||
FROM golang:1.20-alpine AS builder
|
FROM golang:1.22.0-alpine AS builder
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY . .
|
COPY . .
|
||||||
ENV CGO_ENABLED=0
|
ENV CGO_ENABLED=0
|
||||||
RUN go mod download
|
RUN go mod download
|
||||||
RUN go build -v -o XrayR -trimpath -ldflags "-s -w -buildid=" ./main
|
RUN go build -v -o XrayR -trimpath -ldflags "-s -w -buildid="
|
||||||
|
|
||||||
# Release
|
# Release
|
||||||
FROM alpine
|
FROM alpine
|
||||||
|
@ -64,6 +64,7 @@ This project is just my personal learning and development and maintenance. I do
|
|||||||
| [PMPanel](https://github.com/ByteInternetHK/PMPanel) | √ | √ | √ |
|
| [PMPanel](https://github.com/ByteInternetHK/PMPanel) | √ | √ | √ |
|
||||||
| [ProxyPanel](https://github.com/ProxyPanel/ProxyPanel) | √ | √ | √ |
|
| [ProxyPanel](https://github.com/ProxyPanel/ProxyPanel) | √ | √ | √ |
|
||||||
| [WHMCS (V2RaySocks)](https://v2raysocks.doxtex.com/) | √ | √ | √ |
|
| [WHMCS (V2RaySocks)](https://v2raysocks.doxtex.com/) | √ | √ | √ |
|
||||||
|
| [BunPanel](https://github.com/pennyMorant/bunpanel-release) | √ | √ | √ |
|
||||||
|
|
||||||
## Software Installation
|
## Software Installation
|
||||||
|
|
||||||
|
@ -61,6 +61,7 @@ Dự án này chỉ là học tập và phát triển và bảo trì cá nhân c
|
|||||||
| [PMPanel](https://github.com/ByteInternetHK/PMPanel) | √ | √ | √ |
|
| [PMPanel](https://github.com/ByteInternetHK/PMPanel) | √ | √ | √ |
|
||||||
| [ProxyPanel](https://github.com/ProxyPanel/ProxyPanel) | √ | √ | √ |
|
| [ProxyPanel](https://github.com/ProxyPanel/ProxyPanel) | √ | √ | √ |
|
||||||
| [WHMCS (V2RaySocks)](https://v2raysocks.doxtex.com/) | √ | √ | √ |
|
| [WHMCS (V2RaySocks)](https://v2raysocks.doxtex.com/) | √ | √ | √ |
|
||||||
|
| [BunPanel](https://github.com/pennyMorant/bunpanel-release) | √ | √ | √ |
|
||||||
|
|
||||||
## Cài đặt phần mềm
|
## Cài đặt phần mềm
|
||||||
|
|
||||||
|
@ -62,6 +62,8 @@ A Xray backend framework that can easily support many panels.
|
|||||||
| [PMPanel](https://github.com/ByteInternetHK/PMPanel) | √ | √ | √ |
|
| [PMPanel](https://github.com/ByteInternetHK/PMPanel) | √ | √ | √ |
|
||||||
| [ProxyPanel](https://github.com/ProxyPanel/ProxyPanel) | √ | √ | √ |
|
| [ProxyPanel](https://github.com/ProxyPanel/ProxyPanel) | √ | √ | √ |
|
||||||
| [WHMCS (V2RaySocks)](https://v2raysocks.doxtex.com/) | √ | √ | √ |
|
| [WHMCS (V2RaySocks)](https://v2raysocks.doxtex.com/) | √ | √ | √ |
|
||||||
|
| [GoV2Panel](https://github.com/pingProMax/gov2panel) | √ | √ | √ |
|
||||||
|
| [BunPanel](https://github.com/pennyMorant/bunpanel-release) | √ | √ | √ |
|
||||||
|
|
||||||
## 软件安装
|
## 软件安装
|
||||||
|
|
||||||
|
@ -59,6 +59,7 @@
|
|||||||
| [PMPanel](https://github.com/ByteInternetHK/PMPanel) | √ | √ | √ |
|
| [PMPanel](https://github.com/ByteInternetHK/PMPanel) | √ | √ | √ |
|
||||||
| [ProxyPanel](https://github.com/ProxyPanel/ProxyPanel) | √ | √ | √ |
|
| [ProxyPanel](https://github.com/ProxyPanel/ProxyPanel) | √ | √ | √ |
|
||||||
| [WHMCS (V2RaySocks)](https://v2raysocks.doxtex.com/) | √ | √ | √ |
|
| [WHMCS (V2RaySocks)](https://v2raysocks.doxtex.com/) | √ | √ | √ |
|
||||||
|
| [BunPanel](https://github.com/pennyMorant/bunpanel-release) | √ | √ | √ |
|
||||||
|
|
||||||
## نصب نرم افزار
|
## نصب نرم افزار
|
||||||
|
|
||||||
|
@ -7,6 +7,12 @@ import (
|
|||||||
"github.com/xtls/xray-core/infra/conf"
|
"github.com/xtls/xray-core/infra/conf"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
UserNotModified = "users not modified"
|
||||||
|
NodeNotModified = "node not modified"
|
||||||
|
RuleNotModified = "rules not modified"
|
||||||
|
)
|
||||||
|
|
||||||
// Config API config
|
// Config API config
|
||||||
type Config struct {
|
type Config struct {
|
||||||
APIHost string `mapstructure:"ApiHost"`
|
APIHost string `mapstructure:"ApiHost"`
|
||||||
@ -14,7 +20,7 @@ type Config struct {
|
|||||||
Key string `mapstructure:"ApiKey"`
|
Key string `mapstructure:"ApiKey"`
|
||||||
NodeType string `mapstructure:"NodeType"`
|
NodeType string `mapstructure:"NodeType"`
|
||||||
EnableVless bool `mapstructure:"EnableVless"`
|
EnableVless bool `mapstructure:"EnableVless"`
|
||||||
EnableXTLS bool `mapstructure:"EnableXTLS"`
|
VlessFlow string `mapstructure:"VlessFlow"`
|
||||||
Timeout int `mapstructure:"Timeout"`
|
Timeout int `mapstructure:"Timeout"`
|
||||||
SpeedLimit float64 `mapstructure:"SpeedLimit"`
|
SpeedLimit float64 `mapstructure:"SpeedLimit"`
|
||||||
DeviceLimit int `mapstructure:"DeviceLimit"`
|
DeviceLimit int `mapstructure:"DeviceLimit"`
|
||||||
@ -31,39 +37,59 @@ type NodeStatus struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type NodeInfo struct {
|
type NodeInfo struct {
|
||||||
NodeType string // Must be V2ray, Trojan, and Shadowsocks
|
AcceptProxyProtocol bool
|
||||||
NodeID int
|
Authority string
|
||||||
Port uint32
|
NodeType string // Must be V2ray, Trojan, and Shadowsocks
|
||||||
SpeedLimit uint64 // Bps
|
NodeID int
|
||||||
AlterID uint16
|
Port uint32
|
||||||
TransportProtocol string
|
SpeedLimit uint64 // Bps
|
||||||
FakeType string
|
AlterID uint16
|
||||||
Host string
|
TransportProtocol string
|
||||||
Path string
|
FakeType string
|
||||||
EnableTLS bool
|
Host string
|
||||||
TLSType string
|
Path string
|
||||||
EnableVless bool
|
EnableTLS bool
|
||||||
CypherMethod string
|
EnableSniffing bool
|
||||||
ServerKey string
|
RouteOnly bool
|
||||||
ServiceName string
|
EnableVless bool
|
||||||
Header json.RawMessage
|
VlessFlow string
|
||||||
NameServerConfig []*conf.NameServerConfig
|
CypherMethod string
|
||||||
|
ServerKey string
|
||||||
|
ServiceName string
|
||||||
|
Method string
|
||||||
|
Header json.RawMessage
|
||||||
|
HttpHeaders map[string]*conf.StringList
|
||||||
|
Headers map[string]string
|
||||||
|
NameServerConfig []*conf.NameServerConfig
|
||||||
|
EnableREALITY bool
|
||||||
|
REALITYConfig *REALITYConfig
|
||||||
|
Show bool
|
||||||
|
EnableTFO bool
|
||||||
|
Dest string
|
||||||
|
ProxyProtocolVer uint64
|
||||||
|
ServerNames []string
|
||||||
|
PrivateKey string
|
||||||
|
MinClientVer string
|
||||||
|
MaxClientVer string
|
||||||
|
MaxTimeDiff uint64
|
||||||
|
ShortIds []string
|
||||||
|
Xver uint64
|
||||||
|
Flow string
|
||||||
|
Security string
|
||||||
|
Key string
|
||||||
|
RejectUnknownSni bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserInfo struct {
|
type UserInfo struct {
|
||||||
UID int
|
UID int
|
||||||
Email string
|
Email string
|
||||||
Passwd string
|
UUID string
|
||||||
Port uint32
|
Passwd string
|
||||||
Method string
|
Port uint32
|
||||||
SpeedLimit uint64 // Bps
|
AlterID uint16
|
||||||
DeviceLimit int
|
Method string
|
||||||
Protocol string
|
SpeedLimit uint64 // Bps
|
||||||
ProtocolParam string
|
DeviceLimit int
|
||||||
Obfs string
|
|
||||||
ObfsParam string
|
|
||||||
UUID string
|
|
||||||
AlterID uint16
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type OnlineUser struct {
|
type OnlineUser struct {
|
||||||
@ -94,3 +120,14 @@ type DetectResult struct {
|
|||||||
UID int
|
UID int
|
||||||
RuleID int
|
RuleID int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type REALITYConfig struct {
|
||||||
|
Dest string
|
||||||
|
ProxyProtocolVer uint64
|
||||||
|
ServerNames []string
|
||||||
|
PrivateKey string
|
||||||
|
MinClientVer string
|
||||||
|
MaxClientVer string
|
||||||
|
MaxTimeDiff uint64
|
||||||
|
ShortIds []string
|
||||||
|
}
|
||||||
|
427
api/bunpanel/bunpanel.go
Normal file
427
api/bunpanel/bunpanel.go
Normal file
@ -0,0 +1,427 @@
|
|||||||
|
package bunpanel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
|
||||||
|
"github.com/XrayR-project/XrayR/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
type APIClient struct {
|
||||||
|
client *resty.Client
|
||||||
|
APIHost string
|
||||||
|
NodeID int
|
||||||
|
Key string
|
||||||
|
NodeType string
|
||||||
|
EnableVless bool
|
||||||
|
VlessFlow string
|
||||||
|
SpeedLimit float64
|
||||||
|
DeviceLimit int
|
||||||
|
LocalRuleList []api.DetectRule
|
||||||
|
LastReportOnline map[int]int
|
||||||
|
access sync.Mutex
|
||||||
|
eTags map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReportIllegal implements api.API.
|
||||||
|
func (*APIClient) ReportIllegal(detectResultList *[]api.DetectResult) (err error) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReportNodeStatus implements api.API.
|
||||||
|
func (*APIClient) ReportNodeStatus(nodeStatus *api.NodeStatus) (err error) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNodeRule implements api.API.
|
||||||
|
func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
|
||||||
|
ruleList := c.LocalRuleList
|
||||||
|
return &ruleList, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(apiConfig *api.Config) *APIClient {
|
||||||
|
client := resty.New()
|
||||||
|
client.SetRetryCount(3)
|
||||||
|
if apiConfig.Timeout > 0 {
|
||||||
|
client.SetTimeout(time.Duration(apiConfig.Timeout) * time.Second)
|
||||||
|
} else {
|
||||||
|
client.SetTimeout(5 * time.Second)
|
||||||
|
}
|
||||||
|
client.OnError(func(req *resty.Request, err error) {
|
||||||
|
if v, ok := err.(*resty.ResponseError); ok {
|
||||||
|
// v.Response contains the last response from the server
|
||||||
|
// v.Err contains the original error
|
||||||
|
log.Print(v.Err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
client.SetBaseURL(apiConfig.APIHost)
|
||||||
|
// Create Key for each requests
|
||||||
|
client.SetQueryParams(map[string]string{
|
||||||
|
"serverId": strconv.Itoa(apiConfig.NodeID),
|
||||||
|
"nodeType": strings.ToLower(apiConfig.NodeType),
|
||||||
|
"token": apiConfig.Key,
|
||||||
|
})
|
||||||
|
// Read local rule list
|
||||||
|
localRuleList := readLocalRuleList(apiConfig.RuleListPath)
|
||||||
|
apiClient := &APIClient{
|
||||||
|
client: client,
|
||||||
|
NodeID: apiConfig.NodeID,
|
||||||
|
Key: apiConfig.Key,
|
||||||
|
APIHost: apiConfig.APIHost,
|
||||||
|
NodeType: apiConfig.NodeType,
|
||||||
|
EnableVless: apiConfig.EnableVless,
|
||||||
|
VlessFlow: apiConfig.VlessFlow,
|
||||||
|
SpeedLimit: apiConfig.SpeedLimit,
|
||||||
|
DeviceLimit: apiConfig.DeviceLimit,
|
||||||
|
LocalRuleList: localRuleList,
|
||||||
|
eTags: make(map[string]string),
|
||||||
|
}
|
||||||
|
return apiClient
|
||||||
|
}
|
||||||
|
|
||||||
|
// readLocalRuleList reads the local rule list file
|
||||||
|
func readLocalRuleList(path string) (LocalRuleList []api.DetectRule) {
|
||||||
|
LocalRuleList = make([]api.DetectRule, 0)
|
||||||
|
|
||||||
|
if path != "" {
|
||||||
|
// open the file
|
||||||
|
file, err := os.Open(path)
|
||||||
|
|
||||||
|
// handle errors while opening
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error when opening file: %s", err)
|
||||||
|
return LocalRuleList
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
fileScanner := bufio.NewScanner(file)
|
||||||
|
|
||||||
|
// read line by line
|
||||||
|
for fileScanner.Scan() {
|
||||||
|
LocalRuleList = append(LocalRuleList, api.DetectRule{
|
||||||
|
ID: -1,
|
||||||
|
Pattern: regexp.MustCompile(fileScanner.Text()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// handle first encountered error while reading
|
||||||
|
if err := fileScanner.Err(); err != nil {
|
||||||
|
log.Fatalf("Error while reading file: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return LocalRuleList
|
||||||
|
}
|
||||||
|
|
||||||
|
// Describe return a description of the client
|
||||||
|
func (c *APIClient) Describe() api.ClientInfo {
|
||||||
|
return api.ClientInfo{APIHost: c.APIHost, NodeID: c.NodeID, Key: c.Key, NodeType: c.NodeType}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug set the client debug for client
|
||||||
|
func (c *APIClient) Debug() {
|
||||||
|
c.client.SetDebug(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *APIClient) assembleURL(path string) string {
|
||||||
|
return c.APIHost + path
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *APIClient) parseResponse(res *resty.Response, path string, err error) (*Response, error) {
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("request %s failed: %s", c.assembleURL(path), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.StatusCode() > 400 {
|
||||||
|
body := res.Body()
|
||||||
|
return nil, fmt.Errorf("request %s failed: %s, %v", c.assembleURL(path), string(body), err)
|
||||||
|
}
|
||||||
|
response := res.Result().(*Response)
|
||||||
|
|
||||||
|
if response.StatusCode != 200 {
|
||||||
|
res, _ := json.Marshal(&response)
|
||||||
|
return nil, fmt.Errorf("statusCode %s invalid", string(res))
|
||||||
|
}
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
||||||
|
path := fmt.Sprintf("/v2/server/%d/get", c.NodeID)
|
||||||
|
res, err := c.client.R().
|
||||||
|
SetResult(&Response{}).
|
||||||
|
SetHeader("If-None-Match", c.eTags["node"]).
|
||||||
|
ForceContentType("application/json").
|
||||||
|
Get(path)
|
||||||
|
// Etag identifier for a specific version of a resource. StatusCode = 304 means no changed
|
||||||
|
if res.StatusCode() == 304 {
|
||||||
|
return nil, errors.New(api.NodeNotModified)
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.Header().Get("ETag") != "" && res.Header().Get("ETag") != c.eTags["node"] {
|
||||||
|
c.eTags["node"] = res.Header().Get("ETag")
|
||||||
|
}
|
||||||
|
|
||||||
|
response, err := c.parseResponse(res, path, err)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeInfoResponse := new(Server)
|
||||||
|
|
||||||
|
if err := json.Unmarshal(response.Datas, nodeInfoResponse); err != nil {
|
||||||
|
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(nodeInfoResponse), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeInfo, err = c.ParseNodeInfo(nodeInfoResponse)
|
||||||
|
if err != nil {
|
||||||
|
res, _ := json.Marshal(nodeInfoResponse)
|
||||||
|
return nil, fmt.Errorf("parse node info failed: %s, \nError: %s, \nPlease check the doc of custom_config for help: https://xrayr-project.github.io/XrayR-doc/dui-jie-sspanel/sspanel/sspanel_custom_config", string(res), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
res, _ := json.Marshal(nodeInfoResponse)
|
||||||
|
return nil, fmt.Errorf("parse node info failed: %s, \nError: %s", string(res), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodeInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
|
||||||
|
path := "/v2/user/get"
|
||||||
|
res, err := c.client.R().
|
||||||
|
SetQueryParam("serverId", strconv.Itoa(c.NodeID)).
|
||||||
|
SetHeader("If-None-Match", c.eTags["users"]).
|
||||||
|
SetResult(&Response{}).
|
||||||
|
ForceContentType("application/json").
|
||||||
|
Get(path)
|
||||||
|
// Etag identifier for a specific version of a resource. StatusCode = 304 means no changed
|
||||||
|
if res.StatusCode() == 304 {
|
||||||
|
return nil, errors.New(api.UserNotModified)
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.Header().Get("ETag") != "" && res.Header().Get("ETag") != c.eTags["users"] {
|
||||||
|
c.eTags["users"] = res.Header().Get("ETag")
|
||||||
|
}
|
||||||
|
|
||||||
|
response, err := c.parseResponse(res, path, err)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
userListResponse := new([]User)
|
||||||
|
|
||||||
|
if err := json.Unmarshal(response.Datas, userListResponse); err != nil {
|
||||||
|
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(userListResponse), err)
|
||||||
|
}
|
||||||
|
userList, err := c.ParseUserListResponse(userListResponse)
|
||||||
|
if err != nil {
|
||||||
|
res, _ := json.Marshal(userListResponse)
|
||||||
|
return nil, fmt.Errorf("parse user list failed: %s", string(res))
|
||||||
|
}
|
||||||
|
return userList, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *APIClient) ReportNodeOnlineUsers(onlineUserList *[]api.OnlineUser) error {
|
||||||
|
c.access.Lock()
|
||||||
|
defer c.access.Unlock()
|
||||||
|
|
||||||
|
reportOnline := make(map[int]int)
|
||||||
|
data := make([]OnlineUser, len(*onlineUserList))
|
||||||
|
for i, user := range *onlineUserList {
|
||||||
|
data[i] = OnlineUser{UID: user.UID, IP: user.IP}
|
||||||
|
reportOnline[user.UID]++
|
||||||
|
}
|
||||||
|
c.LastReportOnline = reportOnline // Update LastReportOnline
|
||||||
|
|
||||||
|
postData := &PostData{Data: data}
|
||||||
|
path := "/v2/user/online/create"
|
||||||
|
res, err := c.client.R().
|
||||||
|
SetQueryParam("serverId", strconv.Itoa(c.NodeID)).
|
||||||
|
SetBody(postData).
|
||||||
|
SetResult(&Response{}).
|
||||||
|
ForceContentType("application/json").
|
||||||
|
Post(path)
|
||||||
|
|
||||||
|
_, err = c.parseResponse(res, path, err)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *APIClient) ReportUserTraffic(userTraffic *[]api.UserTraffic) error {
|
||||||
|
|
||||||
|
data := make([]UserTraffic, len(*userTraffic))
|
||||||
|
for i, traffic := range *userTraffic {
|
||||||
|
data[i] = UserTraffic{
|
||||||
|
UID: traffic.UID,
|
||||||
|
Upload: traffic.Upload,
|
||||||
|
Download: traffic.Download}
|
||||||
|
}
|
||||||
|
postData := &PostData{Data: data}
|
||||||
|
path := "/v2/user/data-usage/create"
|
||||||
|
res, err := c.client.R().
|
||||||
|
SetQueryParam("serverId", strconv.Itoa(c.NodeID)).
|
||||||
|
SetBody(postData).
|
||||||
|
SetResult(&Response{}).
|
||||||
|
ForceContentType("application/json").
|
||||||
|
Post(path)
|
||||||
|
_, err = c.parseResponse(res, path, err)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *APIClient) ParseUserListResponse(userInfoResponse *[]User) (*[]api.UserInfo, error) {
|
||||||
|
c.access.Lock()
|
||||||
|
// Clear Last report log
|
||||||
|
defer func() {
|
||||||
|
c.LastReportOnline = make(map[int]int)
|
||||||
|
c.access.Unlock()
|
||||||
|
}()
|
||||||
|
|
||||||
|
var deviceLimit, localDeviceLimit = 0, 0
|
||||||
|
var speedLimit uint64 = 0
|
||||||
|
var userList []api.UserInfo
|
||||||
|
for _, user := range *userInfoResponse {
|
||||||
|
if c.DeviceLimit > 0 {
|
||||||
|
deviceLimit = c.DeviceLimit
|
||||||
|
} else {
|
||||||
|
deviceLimit = user.DeviceLimit
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is still device available, add the user
|
||||||
|
if deviceLimit > 0 && user.AliveIP > 0 {
|
||||||
|
lastOnline := 0
|
||||||
|
if v, ok := c.LastReportOnline[user.ID]; ok {
|
||||||
|
lastOnline = v
|
||||||
|
}
|
||||||
|
// If there are any available device.
|
||||||
|
if localDeviceLimit = deviceLimit - user.AliveIP + lastOnline; localDeviceLimit > 0 {
|
||||||
|
deviceLimit = localDeviceLimit
|
||||||
|
// If this backend server has reported any user in the last reporting period.
|
||||||
|
} else if lastOnline > 0 {
|
||||||
|
deviceLimit = lastOnline
|
||||||
|
// Remove this user.
|
||||||
|
} else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.SpeedLimit > 0 {
|
||||||
|
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||||
|
} else {
|
||||||
|
speedLimit = uint64((user.SpeedLimit * 1000000) / 8)
|
||||||
|
}
|
||||||
|
userList = append(userList, api.UserInfo{
|
||||||
|
UID: user.ID,
|
||||||
|
UUID: user.UUID,
|
||||||
|
SpeedLimit: speedLimit,
|
||||||
|
DeviceLimit: deviceLimit,
|
||||||
|
Passwd: user.UUID,
|
||||||
|
Email: user.UUID + "@bunpanel.user",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return &userList, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *APIClient) ParseNodeInfo(nodeInfoResponse *Server) (*api.NodeInfo, error) {
|
||||||
|
var (
|
||||||
|
speedLimit uint64 = 0
|
||||||
|
enableTLS, enableVless, enableREALITY bool
|
||||||
|
alterID uint16 = 0
|
||||||
|
tlsType, transportProtocol string
|
||||||
|
)
|
||||||
|
|
||||||
|
nodeConfig := nodeInfoResponse
|
||||||
|
port := uint32(nodeConfig.Port)
|
||||||
|
|
||||||
|
switch c.NodeType {
|
||||||
|
case "Shadowsocks":
|
||||||
|
transportProtocol = "tcp"
|
||||||
|
case "V2ray":
|
||||||
|
transportProtocol = nodeConfig.Network
|
||||||
|
tlsType = nodeConfig.Security
|
||||||
|
|
||||||
|
if tlsType == "tls" || tlsType == "xtls" {
|
||||||
|
enableTLS = true
|
||||||
|
}
|
||||||
|
if tlsType == "reality" {
|
||||||
|
enableREALITY = true
|
||||||
|
enableVless = true
|
||||||
|
}
|
||||||
|
case "Trojan":
|
||||||
|
enableTLS = true
|
||||||
|
tlsType = "tls"
|
||||||
|
transportProtocol = "tcp"
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse reality config
|
||||||
|
realityConfig := new(api.REALITYConfig)
|
||||||
|
if nodeConfig.RealitySettings != nil {
|
||||||
|
r := new(RealitySettings)
|
||||||
|
json.Unmarshal(nodeConfig.RealitySettings, r)
|
||||||
|
realityConfig = &api.REALITYConfig{
|
||||||
|
Dest: r.Dest,
|
||||||
|
ProxyProtocolVer: r.ProxyProtocolVer,
|
||||||
|
ServerNames: r.ServerNames,
|
||||||
|
PrivateKey: r.PrivateKey,
|
||||||
|
MinClientVer: r.MinClientVer,
|
||||||
|
MaxClientVer: r.MaxClientVer,
|
||||||
|
MaxTimeDiff: r.MaxTimeDiff,
|
||||||
|
ShortIds: r.ShortIds,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wsConfig := new(WsSettings)
|
||||||
|
if nodeConfig.WsSettings != nil {
|
||||||
|
json.Unmarshal(nodeConfig.WsSettings, wsConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
grpcConfig := new(GrpcSettigns)
|
||||||
|
if nodeConfig.GrpcSettings != nil {
|
||||||
|
json.Unmarshal(nodeConfig.GrpcSettings, grpcConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
tcpConfig := new(TcpSettings)
|
||||||
|
if nodeConfig.TcpSettings != nil {
|
||||||
|
json.Unmarshal(nodeConfig.TcpSettings, tcpConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create GeneralNodeInfo
|
||||||
|
nodeInfo := &api.NodeInfo{
|
||||||
|
NodeType: c.NodeType,
|
||||||
|
NodeID: c.NodeID,
|
||||||
|
Port: port,
|
||||||
|
SpeedLimit: speedLimit,
|
||||||
|
AlterID: alterID,
|
||||||
|
TransportProtocol: transportProtocol,
|
||||||
|
Host: wsConfig.Headers.Host,
|
||||||
|
Path: wsConfig.Path,
|
||||||
|
EnableTLS: enableTLS,
|
||||||
|
EnableVless: enableVless,
|
||||||
|
VlessFlow: nodeConfig.Flow,
|
||||||
|
CypherMethod: nodeConfig.Method,
|
||||||
|
ServiceName: grpcConfig.ServiceName,
|
||||||
|
Header: tcpConfig.Header,
|
||||||
|
EnableREALITY: enableREALITY,
|
||||||
|
REALITYConfig: realityConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodeInfo, nil
|
||||||
|
}
|
@ -1,24 +1,24 @@
|
|||||||
package v2board_test
|
package bunpanel_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/XrayR-project/XrayR/api"
|
"github.com/XrayR-project/XrayR/api"
|
||||||
"github.com/XrayR-project/XrayR/api/v2board"
|
"github.com/XrayR-project/XrayR/api/bunpanel"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CreateClient() api.API {
|
func CreateClient() api.API {
|
||||||
apiConfig := &api.Config{
|
apiConfig := &api.Config{
|
||||||
APIHost: "http://localhost:9897",
|
APIHost: "http://localhost:8080",
|
||||||
Key: "qwertyuiopasdfghjkl",
|
Key: "123456",
|
||||||
NodeID: 1,
|
NodeID: 1,
|
||||||
NodeType: "V2ray",
|
NodeType: "V2ray",
|
||||||
}
|
}
|
||||||
client := v2board.New(apiConfig)
|
client := bunpanel.New(apiConfig)
|
||||||
return client
|
return client
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetV2rayNodeinfo(t *testing.T) {
|
func TestGetV2rayNodeInfo(t *testing.T) {
|
||||||
client := CreateClient()
|
client := CreateClient()
|
||||||
nodeInfo, err := client.GetNodeInfo()
|
nodeInfo, err := client.GetNodeInfo()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -27,14 +27,14 @@ func TestGetV2rayNodeinfo(t *testing.T) {
|
|||||||
t.Log(nodeInfo)
|
t.Log(nodeInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetSSNodeinfo(t *testing.T) {
|
func TestGetSSNodeInfo(t *testing.T) {
|
||||||
apiConfig := &api.Config{
|
apiConfig := &api.Config{
|
||||||
APIHost: "http://127.0.0.1:668",
|
APIHost: "http://127.0.0.1:668",
|
||||||
Key: "qwertyuiopasdfghjkl",
|
Key: "qwertyuiopasdfghjkl",
|
||||||
NodeID: 1,
|
NodeID: 1,
|
||||||
NodeType: "Shadowsocks",
|
NodeType: "Shadowsocks",
|
||||||
}
|
}
|
||||||
client := v2board.New(apiConfig)
|
client := bunpanel.New(apiConfig)
|
||||||
nodeInfo, err := client.GetNodeInfo()
|
nodeInfo, err := client.GetNodeInfo()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
@ -42,14 +42,14 @@ func TestGetSSNodeinfo(t *testing.T) {
|
|||||||
t.Log(nodeInfo)
|
t.Log(nodeInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetTrojanNodeinfo(t *testing.T) {
|
func TestGetTrojanNodeInfo(t *testing.T) {
|
||||||
apiConfig := &api.Config{
|
apiConfig := &api.Config{
|
||||||
APIHost: "http://127.0.0.1:668",
|
APIHost: "http://127.0.0.1:668",
|
||||||
Key: "qwertyuiopasdfghjkl",
|
Key: "qwertyuiopasdfghjkl",
|
||||||
NodeID: 1,
|
NodeID: 1,
|
||||||
NodeType: "Trojan",
|
NodeType: "Trojan",
|
||||||
}
|
}
|
||||||
client := v2board.New(apiConfig)
|
client := bunpanel.New(apiConfig)
|
||||||
nodeInfo, err := client.GetNodeInfo()
|
nodeInfo, err := client.GetNodeInfo()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
@ -78,8 +78,8 @@ func TestReportReportUserTraffic(t *testing.T) {
|
|||||||
for i, userInfo := range *userList {
|
for i, userInfo := range *userList {
|
||||||
generalUserTraffic[i] = api.UserTraffic{
|
generalUserTraffic[i] = api.UserTraffic{
|
||||||
UID: userInfo.UID,
|
UID: userInfo.UID,
|
||||||
Upload: 114514,
|
Upload: 1111,
|
||||||
Download: 114514,
|
Download: 2222,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// client.Debug()
|
// client.Debug()
|
72
api/bunpanel/model.go
Normal file
72
api/bunpanel/model.go
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
package bunpanel
|
||||||
|
|
||||||
|
import "encoding/json"
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
Port int `json:"serverPort"`
|
||||||
|
Network string `json:"network"`
|
||||||
|
Method string `json:"method"`
|
||||||
|
Security string `json:"security"`
|
||||||
|
Flow string `json:"flow"`
|
||||||
|
WsSettings json.RawMessage `json:"wsSettings"`
|
||||||
|
RealitySettings json.RawMessage `json:"realitySettings"`
|
||||||
|
GrpcSettings json.RawMessage `json:"grpcSettings"`
|
||||||
|
TcpSettings json.RawMessage `json:"tcpSettings"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WsSettings struct {
|
||||||
|
Path string `json:"path"`
|
||||||
|
Headers struct {
|
||||||
|
Host string `json:"Host"`
|
||||||
|
} `json:"headers"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GrpcSettigns struct {
|
||||||
|
ServiceName string `json:"serviceName"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TcpSettings struct {
|
||||||
|
Header json.RawMessage `json:"header"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RealitySettings struct {
|
||||||
|
Show bool `json:"show"`
|
||||||
|
Dest string `json:"dest"`
|
||||||
|
Xver uint64 `json:"xver"`
|
||||||
|
ServerNames []string `json:"serverNames"`
|
||||||
|
PrivateKey string `json:"privateKey"`
|
||||||
|
MinClientVer string `json:"minClientVer"`
|
||||||
|
MaxClientVer string `json:"maxClientVer"`
|
||||||
|
MaxTimeDiff uint64 `json:"maxTimeDiff"`
|
||||||
|
ProxyProtocolVer uint64 `json:"proxyProtocolVer"`
|
||||||
|
ShortIds []string `json:"shortIds"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
UUID string `json:"uuid"`
|
||||||
|
SpeedLimit float64 `json:"speedLimit"`
|
||||||
|
DeviceLimit int `json:"ipLimit"`
|
||||||
|
AliveIP int `json:"onlineIp"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type OnlineUser struct {
|
||||||
|
UID int `json:"userId"`
|
||||||
|
IP string `json:"ip"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserTraffic is the data structure of traffic
|
||||||
|
type UserTraffic struct {
|
||||||
|
UID int `json:"userId"`
|
||||||
|
Upload int64 `json:"u"`
|
||||||
|
Download int64 `json:"d"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Response struct {
|
||||||
|
StatusCode int `json:"statusCode"`
|
||||||
|
Datas json.RawMessage `json:"datas"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PostData struct {
|
||||||
|
Data interface{} `json:"data"`
|
||||||
|
}
|
329
api/gov2panel/gov2panel.go
Normal file
329
api/gov2panel/gov2panel.go
Normal file
@ -0,0 +1,329 @@
|
|||||||
|
package gov2panel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/XrayR-project/XrayR/api"
|
||||||
|
"github.com/gogf/gf/v2/encoding/gjson"
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/net/gclient"
|
||||||
|
"github.com/xtls/xray-core/common/net"
|
||||||
|
"github.com/xtls/xray-core/infra/conf"
|
||||||
|
)
|
||||||
|
|
||||||
|
// APIClient API config
|
||||||
|
type APIClient struct {
|
||||||
|
ctx context.Context
|
||||||
|
APIHost string
|
||||||
|
NodeID int
|
||||||
|
Key string
|
||||||
|
NodeType string
|
||||||
|
EnableVless bool
|
||||||
|
VlessFlow string
|
||||||
|
Timeout int
|
||||||
|
SpeedLimit float64
|
||||||
|
DeviceLimit int
|
||||||
|
RuleListPath string
|
||||||
|
DisableCustomConfig bool
|
||||||
|
|
||||||
|
LocalRuleList []api.DetectRule
|
||||||
|
}
|
||||||
|
|
||||||
|
// New create an api instance
|
||||||
|
func New(apiConfig *api.Config) *APIClient {
|
||||||
|
|
||||||
|
//https://goframe.org/pages/viewpage.action?pageId=1114381
|
||||||
|
|
||||||
|
apiClient := &APIClient{
|
||||||
|
ctx: context.Background(),
|
||||||
|
APIHost: apiConfig.APIHost,
|
||||||
|
NodeID: apiConfig.NodeID,
|
||||||
|
Key: apiConfig.Key,
|
||||||
|
NodeType: apiConfig.NodeType,
|
||||||
|
EnableVless: apiConfig.EnableVless,
|
||||||
|
VlessFlow: apiConfig.VlessFlow,
|
||||||
|
Timeout: apiConfig.Timeout,
|
||||||
|
DeviceLimit: apiConfig.DeviceLimit,
|
||||||
|
RuleListPath: apiConfig.RuleListPath,
|
||||||
|
DisableCustomConfig: apiConfig.DisableCustomConfig,
|
||||||
|
|
||||||
|
LocalRuleList: readLocalRuleList(apiConfig.RuleListPath), //加载本地路由规则
|
||||||
|
}
|
||||||
|
return apiClient
|
||||||
|
}
|
||||||
|
|
||||||
|
// readLocalRuleList reads the local rule list file
|
||||||
|
func readLocalRuleList(path string) (LocalRuleList []api.DetectRule) {
|
||||||
|
|
||||||
|
LocalRuleList = make([]api.DetectRule, 0)
|
||||||
|
if path != "" {
|
||||||
|
// open the file
|
||||||
|
file, err := os.Open(path)
|
||||||
|
|
||||||
|
// handle errors while opening
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error when opening file: %s", err)
|
||||||
|
return LocalRuleList
|
||||||
|
}
|
||||||
|
|
||||||
|
fileScanner := bufio.NewScanner(file)
|
||||||
|
|
||||||
|
// read line by line
|
||||||
|
for fileScanner.Scan() {
|
||||||
|
LocalRuleList = append(LocalRuleList, api.DetectRule{
|
||||||
|
ID: -1,
|
||||||
|
Pattern: regexp.MustCompile(fileScanner.Text()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// handle first encountered error while reading
|
||||||
|
if err := fileScanner.Err(); err != nil {
|
||||||
|
log.Fatalf("Error while reading file: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
file.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
return LocalRuleList
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
||||||
|
|
||||||
|
apiPath := "/api/server/config"
|
||||||
|
reslutJson, err := c.sendRequest(
|
||||||
|
nil,
|
||||||
|
"POST",
|
||||||
|
apiPath,
|
||||||
|
g.Map{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if reslutJson.Get("data").String() == "" {
|
||||||
|
return nil, errors.New("gov2panel node config data is null")
|
||||||
|
}
|
||||||
|
|
||||||
|
if reslutJson.Get("data.port").Int() == 0 {
|
||||||
|
return nil, errors.New("server port must > 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeInfo = new(api.NodeInfo)
|
||||||
|
err = reslutJson.Get("data").Scan(nodeInfo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("parse node info failed: \nError: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
routes := make([]route, 0)
|
||||||
|
err = reslutJson.Get("data.routes").Scan(&routes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("parse node routes failed: \nError: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeInfo.NodeType = c.NodeType
|
||||||
|
nodeInfo.NodeID = c.NodeID
|
||||||
|
nodeInfo.EnableVless = c.EnableVless
|
||||||
|
nodeInfo.VlessFlow = c.VlessFlow
|
||||||
|
|
||||||
|
nodeInfo.AlterID = 0
|
||||||
|
|
||||||
|
nodeInfo.NameServerConfig = parseDNSConfig(routes)
|
||||||
|
|
||||||
|
return nodeInfo, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseDNSConfig(routes []route) (nameServerList []*conf.NameServerConfig) {
|
||||||
|
|
||||||
|
nameServerList = make([]*conf.NameServerConfig, 0)
|
||||||
|
for i := range routes {
|
||||||
|
if routes[i].Action == "dns" {
|
||||||
|
nameServerList = append(nameServerList, &conf.NameServerConfig{
|
||||||
|
Address: &conf.Address{Address: net.ParseAddress(routes[i].ActionValue)},
|
||||||
|
Domains: routes[i].Match,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserList will pull user form panel
|
||||||
|
func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
|
||||||
|
|
||||||
|
apiPath := "/api/server/user"
|
||||||
|
|
||||||
|
switch c.NodeType {
|
||||||
|
case "V2ray", "Trojan", "Shadowsocks", "Vmess", "Vless":
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported node type: %s", c.NodeType)
|
||||||
|
}
|
||||||
|
|
||||||
|
reslutJson, err := c.sendRequest(
|
||||||
|
nil,
|
||||||
|
"GET",
|
||||||
|
apiPath,
|
||||||
|
g.Map{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var users []*user
|
||||||
|
reslutJson.Get("data.users").Scan(&users)
|
||||||
|
|
||||||
|
userList := make([]api.UserInfo, len(users))
|
||||||
|
for i := 0; i < len(users); i++ {
|
||||||
|
u := api.UserInfo{
|
||||||
|
UID: users[i].Id,
|
||||||
|
UUID: users[i].Uuid,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Support 1.7.1 speed limit
|
||||||
|
if c.SpeedLimit > 0 {
|
||||||
|
u.SpeedLimit = uint64(c.SpeedLimit * 1000000 / 8)
|
||||||
|
} else {
|
||||||
|
u.SpeedLimit = uint64(users[i].SpeedLimit * 1000000 / 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
u.DeviceLimit = c.DeviceLimit // todo waiting v2board send configuration
|
||||||
|
u.Email = u.UUID + "@gov2panel.user"
|
||||||
|
if c.NodeType == "Shadowsocks" {
|
||||||
|
u.Passwd = u.UUID
|
||||||
|
}
|
||||||
|
userList[i] = u
|
||||||
|
}
|
||||||
|
|
||||||
|
return &userList, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *APIClient) ReportNodeStatus(nodeStatus *api.NodeStatus) (err error) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *APIClient) ReportNodeOnlineUsers(onlineUser *[]api.OnlineUser) (err error) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReportUserTraffic reports the user traffic
|
||||||
|
func (c *APIClient) ReportUserTraffic(userTraffic *[]api.UserTraffic) (err error) {
|
||||||
|
apiPath := "/api/server/push"
|
||||||
|
reslutJson, err := c.sendRequest(
|
||||||
|
nil,
|
||||||
|
"POST",
|
||||||
|
apiPath,
|
||||||
|
g.Map{
|
||||||
|
"data": userTraffic,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if reslutJson.Get("code").Int() != 0 {
|
||||||
|
return errors.New(reslutJson.Get("message").String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *APIClient) Describe() api.ClientInfo {
|
||||||
|
return api.ClientInfo{APIHost: c.APIHost, NodeID: c.NodeID, Key: c.Key, NodeType: c.NodeType}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNodeRule implements the API interface
|
||||||
|
func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
|
||||||
|
ruleList := c.LocalRuleList
|
||||||
|
|
||||||
|
apiPath := "/api/server/config"
|
||||||
|
reslutJson, err := c.sendRequest(
|
||||||
|
nil,
|
||||||
|
"POST",
|
||||||
|
apiPath,
|
||||||
|
g.Map{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
routes := make([]route, 0)
|
||||||
|
err = reslutJson.Get("data.routes").Scan(&routes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("parse node routes failed: \nError: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range routes {
|
||||||
|
if routes[i].Action == "block" {
|
||||||
|
for _, v := range routes[i].Match {
|
||||||
|
ruleList = append(ruleList, api.DetectRule{
|
||||||
|
ID: i,
|
||||||
|
Pattern: regexp.MustCompile(v),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ruleList, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *APIClient) ReportIllegal(detectResultList *[]api.DetectResult) (err error) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *APIClient) Debug() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// request 统一请求接口
|
||||||
|
func (c *APIClient) sendRequest(headerM map[string]string, method string, url string, data g.Map) (reslutJson *gjson.Json, err error) {
|
||||||
|
url = c.APIHost + url
|
||||||
|
|
||||||
|
client := gclient.New()
|
||||||
|
|
||||||
|
var gResponse *gclient.Response
|
||||||
|
|
||||||
|
if c.Timeout > 0 {
|
||||||
|
client.SetTimeout(time.Duration(c.Timeout) * time.Second) //方法用于设置当前请求超时时间
|
||||||
|
} else {
|
||||||
|
client.SetTimeout(5 * time.Second)
|
||||||
|
}
|
||||||
|
client.Retry(3, 10*time.Second) //方法用于设置请求失败时重连次数和重连间隔。
|
||||||
|
|
||||||
|
client.SetHeaderMap(headerM)
|
||||||
|
client.SetHeader("Content-Type", "application/json")
|
||||||
|
|
||||||
|
data["token"] = c.Key
|
||||||
|
data["node_id"] = c.NodeID
|
||||||
|
|
||||||
|
switch method {
|
||||||
|
case "GET":
|
||||||
|
gResponse, err = client.Get(c.ctx, url, data)
|
||||||
|
case "POST":
|
||||||
|
gResponse, err = client.Post(c.ctx, url, data)
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("unsupported method: %s", method)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer gResponse.Close()
|
||||||
|
|
||||||
|
reslutJson = gjson.New(gResponse.ReadAllString())
|
||||||
|
if reslutJson == nil {
|
||||||
|
err = fmt.Errorf("http reslut to json, err : %s", gResponse.ReadAllString())
|
||||||
|
}
|
||||||
|
if reslutJson.Get("code").Int() != 0 {
|
||||||
|
err = errors.New(reslutJson.Get("message").String())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
83
api/gov2panel/gov2panel_test.go
Normal file
83
api/gov2panel/gov2panel_test.go
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
package gov2panel_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/XrayR-project/XrayR/api"
|
||||||
|
"github.com/XrayR-project/XrayR/api/gov2panel"
|
||||||
|
"github.com/gogf/gf/v2/encoding/gjson"
|
||||||
|
"github.com/gogf/gf/v2/util/gconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CreateClient() api.API {
|
||||||
|
apiConfig := &api.Config{
|
||||||
|
APIHost: "http://localhost:8080",
|
||||||
|
Key: "123456",
|
||||||
|
NodeID: 90,
|
||||||
|
NodeType: "V2ray",
|
||||||
|
}
|
||||||
|
client := gov2panel.New(apiConfig)
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetNodeInfo(t *testing.T) {
|
||||||
|
client := CreateClient()
|
||||||
|
nodeInfo, err := client.GetNodeInfo()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeInfoJson := gjson.New(nodeInfo)
|
||||||
|
t.Log(nodeInfoJson.String())
|
||||||
|
t.Log(nodeInfoJson.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetUserList(t *testing.T) {
|
||||||
|
client := CreateClient()
|
||||||
|
|
||||||
|
userList, err := client.GetUserList()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Log(len(*userList))
|
||||||
|
t.Log(userList)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReportReportUserTraffic(t *testing.T) {
|
||||||
|
client := CreateClient()
|
||||||
|
userList, err := client.GetUserList()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
t.Log(userList)
|
||||||
|
generalUserTraffic := make([]api.UserTraffic, len(*userList))
|
||||||
|
for i, userInfo := range *userList {
|
||||||
|
generalUserTraffic[i] = api.UserTraffic{
|
||||||
|
UID: userInfo.UID,
|
||||||
|
Upload: 1073741824,
|
||||||
|
Download: 1073741824,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Log(gconv.String(generalUserTraffic))
|
||||||
|
client = CreateClient()
|
||||||
|
err = client.ReportUserTraffic(&generalUserTraffic)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetNodeRule(t *testing.T) {
|
||||||
|
|
||||||
|
client := CreateClient()
|
||||||
|
client.Debug()
|
||||||
|
|
||||||
|
ruleList, err := client.GetNodeRule()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Log(ruleList)
|
||||||
|
}
|
14
api/gov2panel/model.go
Normal file
14
api/gov2panel/model.go
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package gov2panel
|
||||||
|
|
||||||
|
type user struct {
|
||||||
|
Id int `json:"id"`
|
||||||
|
Uuid string `json:"uuid"`
|
||||||
|
SpeedLimit int `json:"speed_limit"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type route struct {
|
||||||
|
Id int `json:"id"`
|
||||||
|
Match []string `json:"match"`
|
||||||
|
Action string `json:"action"`
|
||||||
|
ActionValue string `json:"action_value"`
|
||||||
|
}
|
@ -31,10 +31,27 @@ type v2ray struct {
|
|||||||
Network string `json:"network"`
|
Network string `json:"network"`
|
||||||
NetworkSettings struct {
|
NetworkSettings struct {
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
|
Host string `json:"host"`
|
||||||
Headers *json.RawMessage `json:"headers"`
|
Headers *json.RawMessage `json:"headers"`
|
||||||
ServiceName string `json:"serviceName"`
|
ServiceName string `json:"serviceName"`
|
||||||
Header *json.RawMessage `json:"header"`
|
Header *json.RawMessage `json:"header"`
|
||||||
} `json:"networkSettings"`
|
} `json:"networkSettings"`
|
||||||
|
VlessNetworkSettings struct {
|
||||||
|
Path string `json:"path"`
|
||||||
|
Host string `json:"host"`
|
||||||
|
Headers *json.RawMessage `json:"headers"`
|
||||||
|
ServiceName string `json:"serviceName"`
|
||||||
|
Header *json.RawMessage `json:"header"`
|
||||||
|
} `json:"network_settings"`
|
||||||
|
VlessFlow string `json:"flow"`
|
||||||
|
VlessTlsSettings struct {
|
||||||
|
ServerPort string `json:"server_port"`
|
||||||
|
Dest string `json:"dest"`
|
||||||
|
xVer uint64 `json:"xver"`
|
||||||
|
Sni string `json:"server_name"`
|
||||||
|
PrivateKey string `json:"private_key"`
|
||||||
|
ShortId string `json:"short_id"`
|
||||||
|
} `json:"tls_settings"`
|
||||||
Tls int `json:"tls"`
|
Tls int `json:"tls"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -13,6 +12,8 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/bitly/go-simplejson"
|
"github.com/bitly/go-simplejson"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
@ -29,12 +30,12 @@ type APIClient struct {
|
|||||||
Key string
|
Key string
|
||||||
NodeType string
|
NodeType string
|
||||||
EnableVless bool
|
EnableVless bool
|
||||||
EnableXTLS bool
|
VlessFlow string
|
||||||
SpeedLimit float64
|
SpeedLimit float64
|
||||||
DeviceLimit int
|
DeviceLimit int
|
||||||
LocalRuleList []api.DetectRule
|
LocalRuleList []api.DetectRule
|
||||||
resp atomic.Value
|
resp atomic.Value
|
||||||
eTag string
|
eTags map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
// New create an api instance
|
// New create an api instance
|
||||||
@ -54,10 +55,18 @@ func New(apiConfig *api.Config) *APIClient {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
client.SetBaseURL(apiConfig.APIHost)
|
client.SetBaseURL(apiConfig.APIHost)
|
||||||
|
|
||||||
|
var nodeType string
|
||||||
|
|
||||||
|
if apiConfig.NodeType == "V2ray" && apiConfig.EnableVless {
|
||||||
|
nodeType = "vless"
|
||||||
|
} else {
|
||||||
|
nodeType = strings.ToLower(apiConfig.NodeType)
|
||||||
|
}
|
||||||
// Create Key for each requests
|
// Create Key for each requests
|
||||||
client.SetQueryParams(map[string]string{
|
client.SetQueryParams(map[string]string{
|
||||||
"node_id": strconv.Itoa(apiConfig.NodeID),
|
"node_id": strconv.Itoa(apiConfig.NodeID),
|
||||||
"node_type": strings.ToLower(apiConfig.NodeType),
|
"node_type": nodeType,
|
||||||
"token": apiConfig.Key,
|
"token": apiConfig.Key,
|
||||||
})
|
})
|
||||||
// Read local rule list
|
// Read local rule list
|
||||||
@ -69,10 +78,11 @@ func New(apiConfig *api.Config) *APIClient {
|
|||||||
APIHost: apiConfig.APIHost,
|
APIHost: apiConfig.APIHost,
|
||||||
NodeType: apiConfig.NodeType,
|
NodeType: apiConfig.NodeType,
|
||||||
EnableVless: apiConfig.EnableVless,
|
EnableVless: apiConfig.EnableVless,
|
||||||
EnableXTLS: apiConfig.EnableXTLS,
|
VlessFlow: apiConfig.VlessFlow,
|
||||||
SpeedLimit: apiConfig.SpeedLimit,
|
SpeedLimit: apiConfig.SpeedLimit,
|
||||||
DeviceLimit: apiConfig.DeviceLimit,
|
DeviceLimit: apiConfig.DeviceLimit,
|
||||||
LocalRuleList: localRuleList,
|
LocalRuleList: localRuleList,
|
||||||
|
eTags: make(map[string]string),
|
||||||
}
|
}
|
||||||
return apiClient
|
return apiClient
|
||||||
}
|
}
|
||||||
@ -147,9 +157,19 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
|||||||
path := "/api/v1/server/UniProxy/config"
|
path := "/api/v1/server/UniProxy/config"
|
||||||
|
|
||||||
res, err := c.client.R().
|
res, err := c.client.R().
|
||||||
|
SetHeader("If-None-Match", c.eTags["node"]).
|
||||||
ForceContentType("application/json").
|
ForceContentType("application/json").
|
||||||
Get(path)
|
Get(path)
|
||||||
|
|
||||||
|
// Etag identifier for a specific version of a resource. StatusCode = 304 means no changed
|
||||||
|
if res.StatusCode() == 304 {
|
||||||
|
return nil, errors.New(api.NodeNotModified)
|
||||||
|
}
|
||||||
|
// update etag
|
||||||
|
if res.Header().Get("Etag") != "" && res.Header().Get("Etag") != c.eTags["node"] {
|
||||||
|
c.eTags["node"] = res.Header().Get("Etag")
|
||||||
|
}
|
||||||
|
|
||||||
nodeInfoResp, err := c.parseResponse(res, path, err)
|
nodeInfoResp, err := c.parseResponse(res, path, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -164,7 +184,7 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
|||||||
c.resp.Store(server)
|
c.resp.Store(server)
|
||||||
|
|
||||||
switch c.NodeType {
|
switch c.NodeType {
|
||||||
case "V2ray":
|
case "V2ray", "Vmess", "Vless":
|
||||||
nodeInfo, err = c.parseV2rayNodeResponse(server)
|
nodeInfo, err = c.parseV2rayNodeResponse(server)
|
||||||
case "Trojan":
|
case "Trojan":
|
||||||
nodeInfo, err = c.parseTrojanNodeResponse(server)
|
nodeInfo, err = c.parseTrojanNodeResponse(server)
|
||||||
@ -187,24 +207,24 @@ func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
|
|||||||
path := "/api/v1/server/UniProxy/user"
|
path := "/api/v1/server/UniProxy/user"
|
||||||
|
|
||||||
switch c.NodeType {
|
switch c.NodeType {
|
||||||
case "V2ray", "Trojan", "Shadowsocks":
|
case "V2ray", "Trojan", "Shadowsocks", "Vmess", "Vless":
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unsupported node type: %s", c.NodeType)
|
return nil, fmt.Errorf("unsupported node type: %s", c.NodeType)
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := c.client.R().
|
res, err := c.client.R().
|
||||||
SetHeader("If-None-Match", c.eTag).
|
SetHeader("If-None-Match", c.eTags["users"]).
|
||||||
ForceContentType("application/json").
|
ForceContentType("application/json").
|
||||||
Get(path)
|
Get(path)
|
||||||
|
|
||||||
// Etag identifier for a specific version of a resource. StatusCode = 304 means no changed
|
// Etag identifier for a specific version of a resource. StatusCode = 304 means no changed
|
||||||
if res.StatusCode() == 304 {
|
if res.StatusCode() == 304 {
|
||||||
return nil, errors.New("users no change")
|
return nil, errors.New(api.UserNotModified)
|
||||||
}
|
}
|
||||||
// update etag
|
// update etag
|
||||||
if res.Header().Get("Etag") != "" && res.Header().Get("Etag") != c.eTag {
|
if res.Header().Get("Etag") != "" && res.Header().Get("Etag") != c.eTags["users"] {
|
||||||
c.eTag = res.Header().Get("Etag")
|
c.eTags["users"] = res.Header().Get("Etag")
|
||||||
}
|
}
|
||||||
|
|
||||||
usersResp, err := c.parseResponse(res, path, err)
|
usersResp, err := c.parseResponse(res, path, err)
|
||||||
@ -213,6 +233,9 @@ func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
|
|||||||
}
|
}
|
||||||
b, _ := usersResp.Get("users").Encode()
|
b, _ := usersResp.Get("users").Encode()
|
||||||
json.Unmarshal(b, &users)
|
json.Unmarshal(b, &users)
|
||||||
|
if len(users) == 0 {
|
||||||
|
return nil, errors.New("users is null")
|
||||||
|
}
|
||||||
|
|
||||||
userList := make([]api.UserInfo, len(users))
|
userList := make([]api.UserInfo, len(users))
|
||||||
for i := 0; i < len(users); i++ {
|
for i := 0; i < len(users); i++ {
|
||||||
@ -293,11 +316,6 @@ func (c *APIClient) ReportIllegal(detectResultList *[]api.DetectResult) error {
|
|||||||
|
|
||||||
// parseTrojanNodeResponse parse the response for the given nodeInfo format
|
// parseTrojanNodeResponse parse the response for the given nodeInfo format
|
||||||
func (c *APIClient) parseTrojanNodeResponse(s *serverConfig) (*api.NodeInfo, error) {
|
func (c *APIClient) parseTrojanNodeResponse(s *serverConfig) (*api.NodeInfo, error) {
|
||||||
var TLSType = "tls"
|
|
||||||
if c.EnableXTLS {
|
|
||||||
TLSType = "xtls"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create GeneralNodeInfo
|
// Create GeneralNodeInfo
|
||||||
nodeInfo := &api.NodeInfo{
|
nodeInfo := &api.NodeInfo{
|
||||||
NodeType: c.NodeType,
|
NodeType: c.NodeType,
|
||||||
@ -305,7 +323,6 @@ func (c *APIClient) parseTrojanNodeResponse(s *serverConfig) (*api.NodeInfo, err
|
|||||||
Port: uint32(s.ServerPort),
|
Port: uint32(s.ServerPort),
|
||||||
TransportProtocol: "tcp",
|
TransportProtocol: "tcp",
|
||||||
EnableTLS: true,
|
EnableTLS: true,
|
||||||
TLSType: TLSType,
|
|
||||||
Host: s.Host,
|
Host: s.Host,
|
||||||
ServiceName: s.ServerName,
|
ServiceName: s.ServerName,
|
||||||
NameServerConfig: s.parseDNSConfig(),
|
NameServerConfig: s.parseDNSConfig(),
|
||||||
@ -347,14 +364,35 @@ func (c *APIClient) parseSSNodeResponse(s *serverConfig) (*api.NodeInfo, error)
|
|||||||
// parseV2rayNodeResponse parse the response for the given nodeInfo format
|
// parseV2rayNodeResponse parse the response for the given nodeInfo format
|
||||||
func (c *APIClient) parseV2rayNodeResponse(s *serverConfig) (*api.NodeInfo, error) {
|
func (c *APIClient) parseV2rayNodeResponse(s *serverConfig) (*api.NodeInfo, error) {
|
||||||
var (
|
var (
|
||||||
TLSType = "tls"
|
host string
|
||||||
host string
|
header json.RawMessage
|
||||||
header json.RawMessage
|
enableTLS bool
|
||||||
enableTLS bool
|
enableREALITY bool
|
||||||
|
dest string
|
||||||
|
xVer uint64
|
||||||
)
|
)
|
||||||
|
|
||||||
if c.EnableXTLS {
|
if s.VlessTlsSettings.Dest != "" {
|
||||||
TLSType = "xtls"
|
dest = s.VlessTlsSettings.Dest
|
||||||
|
} else {
|
||||||
|
dest = s.VlessTlsSettings.Sni
|
||||||
|
}
|
||||||
|
if s.VlessTlsSettings.xVer != 0 {
|
||||||
|
xVer = s.VlessTlsSettings.xVer
|
||||||
|
} else {
|
||||||
|
xVer = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
realityConfig := api.REALITYConfig{
|
||||||
|
Dest: dest + ":" + s.VlessTlsSettings.ServerPort,
|
||||||
|
ProxyProtocolVer: xVer,
|
||||||
|
ServerNames: []string{s.VlessTlsSettings.Sni},
|
||||||
|
PrivateKey: s.VlessTlsSettings.PrivateKey,
|
||||||
|
ShortIds: []string{s.VlessTlsSettings.ShortId},
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.EnableVless {
|
||||||
|
s.NetworkSettings = s.VlessNetworkSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
switch s.Network {
|
switch s.Network {
|
||||||
@ -375,10 +413,30 @@ func (c *APIClient) parseV2rayNodeResponse(s *serverConfig) (*api.NodeInfo, erro
|
|||||||
header = httpHeader
|
header = httpHeader
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case "httpupgrade", "splithttp":
|
||||||
|
if s.NetworkSettings.Headers != nil {
|
||||||
|
if httpHeaders, err := s.NetworkSettings.Headers.MarshalJSON(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
b, _ := simplejson.NewJson(httpHeaders)
|
||||||
|
host = b.Get("Host").MustString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if s.NetworkSettings.Host != "" {
|
||||||
|
host = s.NetworkSettings.Host
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.Tls == 1 {
|
switch s.Tls {
|
||||||
|
case 0:
|
||||||
|
enableTLS = false
|
||||||
|
enableREALITY = false
|
||||||
|
case 1:
|
||||||
enableTLS = true
|
enableTLS = true
|
||||||
|
enableREALITY = false
|
||||||
|
case 2:
|
||||||
|
enableTLS = true
|
||||||
|
enableREALITY = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create GeneralNodeInfo
|
// Create GeneralNodeInfo
|
||||||
@ -389,12 +447,14 @@ func (c *APIClient) parseV2rayNodeResponse(s *serverConfig) (*api.NodeInfo, erro
|
|||||||
AlterID: 0,
|
AlterID: 0,
|
||||||
TransportProtocol: s.Network,
|
TransportProtocol: s.Network,
|
||||||
EnableTLS: enableTLS,
|
EnableTLS: enableTLS,
|
||||||
TLSType: TLSType,
|
|
||||||
Path: s.NetworkSettings.Path,
|
Path: s.NetworkSettings.Path,
|
||||||
Host: host,
|
Host: host,
|
||||||
EnableVless: c.EnableVless,
|
EnableVless: c.EnableVless,
|
||||||
|
VlessFlow: s.VlessFlow,
|
||||||
ServiceName: s.NetworkSettings.ServiceName,
|
ServiceName: s.NetworkSettings.ServiceName,
|
||||||
Header: header,
|
Header: header,
|
||||||
|
EnableREALITY: enableREALITY,
|
||||||
|
REALITYConfig: &realityConfig,
|
||||||
NameServerConfig: s.parseDNSConfig(),
|
NameServerConfig: s.parseDNSConfig(),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@ -4,13 +4,14 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
|
|
||||||
"github.com/XrayR-project/XrayR/api"
|
"github.com/XrayR-project/XrayR/api"
|
||||||
@ -24,7 +25,7 @@ type APIClient struct {
|
|||||||
Key string
|
Key string
|
||||||
NodeType string
|
NodeType string
|
||||||
EnableVless bool
|
EnableVless bool
|
||||||
EnableXTLS bool
|
VlessFlow string
|
||||||
SpeedLimit float64
|
SpeedLimit float64
|
||||||
DeviceLimit int
|
DeviceLimit int
|
||||||
LocalRuleList []api.DetectRule
|
LocalRuleList []api.DetectRule
|
||||||
@ -61,7 +62,7 @@ func New(apiConfig *api.Config) *APIClient {
|
|||||||
APIHost: apiConfig.APIHost,
|
APIHost: apiConfig.APIHost,
|
||||||
NodeType: apiConfig.NodeType,
|
NodeType: apiConfig.NodeType,
|
||||||
EnableVless: apiConfig.EnableVless,
|
EnableVless: apiConfig.EnableVless,
|
||||||
EnableXTLS: apiConfig.EnableXTLS,
|
VlessFlow: apiConfig.VlessFlow,
|
||||||
SpeedLimit: apiConfig.SpeedLimit,
|
SpeedLimit: apiConfig.SpeedLimit,
|
||||||
DeviceLimit: apiConfig.DeviceLimit,
|
DeviceLimit: apiConfig.DeviceLimit,
|
||||||
LocalRuleList: localRuleList,
|
LocalRuleList: localRuleList,
|
||||||
@ -359,8 +360,8 @@ func (c *APIClient) ReportIllegal(detectResultList *[]api.DetectResult) error {
|
|||||||
// ParseV2rayNodeResponse parse the response for the given nodeinfor format
|
// ParseV2rayNodeResponse parse the response for the given nodeinfor format
|
||||||
func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *NodeInfoResponse) (*api.NodeInfo, error) {
|
func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *NodeInfoResponse) (*api.NodeInfo, error) {
|
||||||
var enableTLS bool
|
var enableTLS bool
|
||||||
var path, host, TLStype, transportProtocol, serviceName string
|
var path, host, transportProtocol, serviceName string
|
||||||
var speedlimit uint64 = 0
|
var speedLimit uint64 = 0
|
||||||
|
|
||||||
port := nodeInfoResponse.Port
|
port := nodeInfoResponse.Port
|
||||||
alterID := nodeInfoResponse.AlterId
|
alterID := nodeInfoResponse.AlterId
|
||||||
@ -376,34 +377,29 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *NodeInfoResponse) (
|
|||||||
}
|
}
|
||||||
// Compatible with more node types config
|
// Compatible with more node types config
|
||||||
switch nodeInfoResponse.Security {
|
switch nodeInfoResponse.Security {
|
||||||
case "tls", "xtls":
|
case "tls":
|
||||||
if c.EnableXTLS {
|
|
||||||
TLStype = "xtls"
|
|
||||||
} else {
|
|
||||||
TLStype = "tls"
|
|
||||||
}
|
|
||||||
enableTLS = true
|
enableTLS = true
|
||||||
default:
|
default:
|
||||||
enableTLS = false
|
enableTLS = false
|
||||||
}
|
}
|
||||||
if c.SpeedLimit > 0 {
|
if c.SpeedLimit > 0 {
|
||||||
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
|
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||||
} else {
|
} else {
|
||||||
speedlimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
|
speedLimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
|
||||||
}
|
}
|
||||||
// Create GeneralNodeInfo
|
// Create GeneralNodeInfo
|
||||||
nodeinfo := &api.NodeInfo{
|
nodeinfo := &api.NodeInfo{
|
||||||
NodeType: c.NodeType,
|
NodeType: c.NodeType,
|
||||||
NodeID: c.NodeID,
|
NodeID: c.NodeID,
|
||||||
Port: port,
|
Port: port,
|
||||||
SpeedLimit: speedlimit,
|
SpeedLimit: speedLimit,
|
||||||
AlterID: alterID,
|
AlterID: alterID,
|
||||||
TransportProtocol: transportProtocol,
|
TransportProtocol: transportProtocol,
|
||||||
EnableTLS: enableTLS,
|
EnableTLS: enableTLS,
|
||||||
TLSType: TLStype,
|
|
||||||
Path: path,
|
Path: path,
|
||||||
Host: host,
|
Host: host,
|
||||||
EnableVless: c.EnableVless,
|
EnableVless: c.EnableVless,
|
||||||
|
VlessFlow: c.VlessFlow,
|
||||||
ServiceName: serviceName,
|
ServiceName: serviceName,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -412,38 +408,33 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *NodeInfoResponse) (
|
|||||||
|
|
||||||
// ParseSSNodeResponse parse the response for the given nodeinfor format
|
// ParseSSNodeResponse parse the response for the given nodeinfor format
|
||||||
func (c *APIClient) ParseSSNodeResponse(nodeInfoResponse *NodeInfoResponse) (*api.NodeInfo, error) {
|
func (c *APIClient) ParseSSNodeResponse(nodeInfoResponse *NodeInfoResponse) (*api.NodeInfo, error) {
|
||||||
var speedlimit uint64 = 0
|
var speedLimit uint64 = 0
|
||||||
|
|
||||||
if c.SpeedLimit > 0 {
|
if c.SpeedLimit > 0 {
|
||||||
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
|
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||||
} else {
|
} else {
|
||||||
speedlimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
|
speedLimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
|
||||||
}
|
}
|
||||||
// Create GeneralNodeInfo
|
// Create GeneralNodeInfo
|
||||||
nodeinfo := &api.NodeInfo{
|
nodeInfo := &api.NodeInfo{
|
||||||
NodeType: c.NodeType,
|
NodeType: c.NodeType,
|
||||||
NodeID: c.NodeID,
|
NodeID: c.NodeID,
|
||||||
Port: nodeInfoResponse.Port,
|
Port: nodeInfoResponse.Port,
|
||||||
SpeedLimit: speedlimit,
|
SpeedLimit: speedLimit,
|
||||||
TransportProtocol: "tcp",
|
TransportProtocol: "tcp",
|
||||||
CypherMethod: nodeInfoResponse.Method,
|
CypherMethod: nodeInfoResponse.Method,
|
||||||
}
|
}
|
||||||
|
|
||||||
return nodeinfo, nil
|
return nodeInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseTrojanNodeResponse parse the response for the given nodeinfor format
|
// ParseTrojanNodeResponse parse the response for the given nodeinfor format
|
||||||
func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *NodeInfoResponse) (*api.NodeInfo, error) {
|
func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *NodeInfoResponse) (*api.NodeInfo, error) {
|
||||||
// 域名或IP;port=连接端口#偏移端口|host=xx
|
// 域名或IP;port=连接端口#偏移端口|host=xx
|
||||||
// gz.aaa.com;port=443#12345|host=hk.aaa.com
|
// gz.aaa.com;port=443#12345|host=hk.aaa.com
|
||||||
var TLSType, host string
|
var host string
|
||||||
var transportProtocol = "tcp"
|
var transportProtocol = "tcp"
|
||||||
var speedlimit uint64 = 0
|
var speedlimit uint64 = 0
|
||||||
if c.EnableXTLS {
|
|
||||||
TLSType = "xtls"
|
|
||||||
} else {
|
|
||||||
TLSType = "tls"
|
|
||||||
}
|
|
||||||
host = nodeInfoResponse.Host
|
host = nodeInfoResponse.Host
|
||||||
port := nodeInfoResponse.Port
|
port := nodeInfoResponse.Port
|
||||||
|
|
||||||
@ -456,25 +447,24 @@ func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *NodeInfoResponse)
|
|||||||
transportProtocol = "grpc"
|
transportProtocol = "grpc"
|
||||||
}
|
}
|
||||||
// Create GeneralNodeInfo
|
// Create GeneralNodeInfo
|
||||||
nodeinfo := &api.NodeInfo{
|
nodeInfo := &api.NodeInfo{
|
||||||
NodeType: c.NodeType,
|
NodeType: c.NodeType,
|
||||||
NodeID: c.NodeID,
|
NodeID: c.NodeID,
|
||||||
Port: port,
|
Port: port,
|
||||||
SpeedLimit: speedlimit,
|
SpeedLimit: speedlimit,
|
||||||
TransportProtocol: transportProtocol,
|
TransportProtocol: transportProtocol,
|
||||||
EnableTLS: true,
|
EnableTLS: true,
|
||||||
TLSType: TLSType,
|
|
||||||
Host: host,
|
Host: host,
|
||||||
ServiceName: nodeInfoResponse.Sni,
|
ServiceName: nodeInfoResponse.Sni,
|
||||||
}
|
}
|
||||||
|
|
||||||
return nodeinfo, nil
|
return nodeInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseUserListResponse parse the response for the given nodeinfo format
|
// ParseUserListResponse parse the response for the given nodeinfo format
|
||||||
func (c *APIClient) ParseUserListResponse(userInfoResponse *[]UserResponse) (*[]api.UserInfo, error) {
|
func (c *APIClient) ParseUserListResponse(userInfoResponse *[]UserResponse) (*[]api.UserInfo, error) {
|
||||||
var deviceLimit int = 0
|
var deviceLimit = 0
|
||||||
var speedlimit uint64 = 0
|
var speedLimit uint64 = 0
|
||||||
userList := make([]api.UserInfo, len(*userInfoResponse))
|
userList := make([]api.UserInfo, len(*userInfoResponse))
|
||||||
for i, user := range *userInfoResponse {
|
for i, user := range *userInfoResponse {
|
||||||
if c.DeviceLimit > 0 {
|
if c.DeviceLimit > 0 {
|
||||||
@ -483,15 +473,15 @@ func (c *APIClient) ParseUserListResponse(userInfoResponse *[]UserResponse) (*[]
|
|||||||
deviceLimit = user.DeviceLimit
|
deviceLimit = user.DeviceLimit
|
||||||
}
|
}
|
||||||
if c.SpeedLimit > 0 {
|
if c.SpeedLimit > 0 {
|
||||||
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
|
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||||
} else {
|
} else {
|
||||||
speedlimit = uint64((user.SpeedLimit * 1000000) / 8)
|
speedLimit = uint64((user.SpeedLimit * 1000000) / 8)
|
||||||
}
|
}
|
||||||
userList[i] = api.UserInfo{
|
userList[i] = api.UserInfo{
|
||||||
UID: user.ID,
|
UID: user.ID,
|
||||||
Passwd: user.Passwd,
|
Passwd: user.Passwd,
|
||||||
UUID: user.Passwd,
|
UUID: user.Passwd,
|
||||||
SpeedLimit: speedlimit,
|
SpeedLimit: speedLimit,
|
||||||
DeviceLimit: deviceLimit,
|
DeviceLimit: deviceLimit,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,13 +4,14 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
|
|
||||||
"github.com/XrayR-project/XrayR/api"
|
"github.com/XrayR-project/XrayR/api"
|
||||||
@ -24,7 +25,7 @@ type APIClient struct {
|
|||||||
Key string
|
Key string
|
||||||
NodeType string
|
NodeType string
|
||||||
EnableVless bool
|
EnableVless bool
|
||||||
EnableXTLS bool
|
VlessFlow string
|
||||||
SpeedLimit float64
|
SpeedLimit float64
|
||||||
DeviceLimit int
|
DeviceLimit int
|
||||||
LocalRuleList []api.DetectRule
|
LocalRuleList []api.DetectRule
|
||||||
@ -57,7 +58,7 @@ func New(apiConfig *api.Config) *APIClient {
|
|||||||
APIHost: apiConfig.APIHost,
|
APIHost: apiConfig.APIHost,
|
||||||
NodeType: apiConfig.NodeType,
|
NodeType: apiConfig.NodeType,
|
||||||
EnableVless: apiConfig.EnableVless,
|
EnableVless: apiConfig.EnableVless,
|
||||||
EnableXTLS: apiConfig.EnableXTLS,
|
VlessFlow: apiConfig.VlessFlow,
|
||||||
SpeedLimit: apiConfig.SpeedLimit,
|
SpeedLimit: apiConfig.SpeedLimit,
|
||||||
DeviceLimit: apiConfig.DeviceLimit,
|
DeviceLimit: apiConfig.DeviceLimit,
|
||||||
LocalRuleList: localRuleList,
|
LocalRuleList: localRuleList,
|
||||||
@ -144,7 +145,7 @@ func (c *APIClient) parseResponse(res *resty.Response, path string, err error) (
|
|||||||
func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
||||||
var path string
|
var path string
|
||||||
switch c.NodeType {
|
switch c.NodeType {
|
||||||
case "V2ray":
|
case "V2ray", "Vmess", "Vless":
|
||||||
path = fmt.Sprintf("/api/v2ray/v1/node/%d", c.NodeID)
|
path = fmt.Sprintf("/api/v2ray/v1/node/%d", c.NodeID)
|
||||||
case "Trojan":
|
case "Trojan":
|
||||||
path = fmt.Sprintf("/api/trojan/v1/node/%d", c.NodeID)
|
path = fmt.Sprintf("/api/trojan/v1/node/%d", c.NodeID)
|
||||||
@ -165,7 +166,7 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch c.NodeType {
|
switch c.NodeType {
|
||||||
case "V2ray":
|
case "V2ray", "Vmess", "Vless":
|
||||||
nodeInfo, err = c.ParseV2rayNodeResponse(&response.Data)
|
nodeInfo, err = c.ParseV2rayNodeResponse(&response.Data)
|
||||||
case "Trojan":
|
case "Trojan":
|
||||||
nodeInfo, err = c.ParseTrojanNodeResponse(&response.Data)
|
nodeInfo, err = c.ParseTrojanNodeResponse(&response.Data)
|
||||||
@ -177,7 +178,7 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
|||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
res, _ := json.Marshal(response.Data)
|
res, _ := json.Marshal(response.Data)
|
||||||
return nil, fmt.Errorf("Parse node info failed: %s, \nError: %s", string(res), err)
|
return nil, fmt.Errorf("parse node info failed: %s, \nError: %s", string(res), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nodeInfo, nil
|
return nodeInfo, nil
|
||||||
@ -187,7 +188,7 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
|||||||
func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
|
func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
|
||||||
var path string
|
var path string
|
||||||
switch c.NodeType {
|
switch c.NodeType {
|
||||||
case "V2ray":
|
case "V2ray", "Vmess", "Vless":
|
||||||
path = fmt.Sprintf("/api/v2ray/v1/userList/%d", c.NodeID)
|
path = fmt.Sprintf("/api/v2ray/v1/userList/%d", c.NodeID)
|
||||||
case "Trojan":
|
case "Trojan":
|
||||||
path = fmt.Sprintf("/api/trojan/v1/userList/%d", c.NodeID)
|
path = fmt.Sprintf("/api/trojan/v1/userList/%d", c.NodeID)
|
||||||
@ -208,7 +209,7 @@ func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
|
|||||||
}
|
}
|
||||||
userList := new([]api.UserInfo)
|
userList := new([]api.UserInfo)
|
||||||
switch c.NodeType {
|
switch c.NodeType {
|
||||||
case "V2ray":
|
case "V2ray", "Vmess", "Vless":
|
||||||
userList, err = c.ParseV2rayUserListResponse(&response.Data)
|
userList, err = c.ParseV2rayUserListResponse(&response.Data)
|
||||||
case "Trojan":
|
case "Trojan":
|
||||||
userList, err = c.ParseTrojanUserListResponse(&response.Data)
|
userList, err = c.ParseTrojanUserListResponse(&response.Data)
|
||||||
@ -228,7 +229,7 @@ func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
|
|||||||
func (c *APIClient) ReportNodeStatus(nodeStatus *api.NodeStatus) (err error) {
|
func (c *APIClient) ReportNodeStatus(nodeStatus *api.NodeStatus) (err error) {
|
||||||
var path string
|
var path string
|
||||||
switch c.NodeType {
|
switch c.NodeType {
|
||||||
case "V2ray":
|
case "V2ray", "Vmess", "Vless":
|
||||||
path = fmt.Sprintf("/api/v2ray/v1/nodeStatus/%d", c.NodeID)
|
path = fmt.Sprintf("/api/v2ray/v1/nodeStatus/%d", c.NodeID)
|
||||||
case "Trojan":
|
case "Trojan":
|
||||||
path = fmt.Sprintf("/api/trojan/v1/nodeStatus/%d", c.NodeID)
|
path = fmt.Sprintf("/api/trojan/v1/nodeStatus/%d", c.NodeID)
|
||||||
@ -264,7 +265,7 @@ func (c *APIClient) ReportNodeOnlineUsers(onlineUserList *[]api.OnlineUser) erro
|
|||||||
|
|
||||||
var path string
|
var path string
|
||||||
switch c.NodeType {
|
switch c.NodeType {
|
||||||
case "V2ray":
|
case "V2ray", "Vmess", "Vless":
|
||||||
path = fmt.Sprintf("/api/v2ray/v1/nodeOnline/%d", c.NodeID)
|
path = fmt.Sprintf("/api/v2ray/v1/nodeOnline/%d", c.NodeID)
|
||||||
case "Trojan":
|
case "Trojan":
|
||||||
path = fmt.Sprintf("/api/trojan/v1/nodeOnline/%d", c.NodeID)
|
path = fmt.Sprintf("/api/trojan/v1/nodeOnline/%d", c.NodeID)
|
||||||
@ -297,7 +298,7 @@ func (c *APIClient) ReportNodeOnlineUsers(onlineUserList *[]api.OnlineUser) erro
|
|||||||
func (c *APIClient) ReportUserTraffic(userTraffic *[]api.UserTraffic) error {
|
func (c *APIClient) ReportUserTraffic(userTraffic *[]api.UserTraffic) error {
|
||||||
var path string
|
var path string
|
||||||
switch c.NodeType {
|
switch c.NodeType {
|
||||||
case "V2ray":
|
case "V2ray", "Vmess", "Vless":
|
||||||
path = fmt.Sprintf("/api/v2ray/v1/userTraffic/%d", c.NodeID)
|
path = fmt.Sprintf("/api/v2ray/v1/userTraffic/%d", c.NodeID)
|
||||||
case "Trojan":
|
case "Trojan":
|
||||||
path = fmt.Sprintf("/api/trojan/v1/userTraffic/%d", c.NodeID)
|
path = fmt.Sprintf("/api/trojan/v1/userTraffic/%d", c.NodeID)
|
||||||
@ -332,7 +333,7 @@ func (c *APIClient) ReportUserTraffic(userTraffic *[]api.UserTraffic) error {
|
|||||||
func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
|
func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
|
||||||
var path string
|
var path string
|
||||||
switch c.NodeType {
|
switch c.NodeType {
|
||||||
case "V2ray":
|
case "V2ray", "Vmess", "Vless":
|
||||||
path = fmt.Sprintf("/api/v2ray/v1/nodeRule/%d", c.NodeID)
|
path = fmt.Sprintf("/api/v2ray/v1/nodeRule/%d", c.NodeID)
|
||||||
case "Trojan":
|
case "Trojan":
|
||||||
path = fmt.Sprintf("/api/trojan/v1/nodeRule/%d", c.NodeID)
|
path = fmt.Sprintf("/api/trojan/v1/nodeRule/%d", c.NodeID)
|
||||||
@ -380,7 +381,7 @@ func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
|
|||||||
func (c *APIClient) ReportIllegal(detectResultList *[]api.DetectResult) error {
|
func (c *APIClient) ReportIllegal(detectResultList *[]api.DetectResult) error {
|
||||||
var path string
|
var path string
|
||||||
switch c.NodeType {
|
switch c.NodeType {
|
||||||
case "V2ray":
|
case "V2ray", "Vmess", "Vless":
|
||||||
path = fmt.Sprintf("/api/v2ray/v1/trigger/%d", c.NodeID)
|
path = fmt.Sprintf("/api/v2ray/v1/trigger/%d", c.NodeID)
|
||||||
case "Trojan":
|
case "Trojan":
|
||||||
path = fmt.Sprintf("/api/trojan/v1/trigger/%d", c.NodeID)
|
path = fmt.Sprintf("/api/trojan/v1/trigger/%d", c.NodeID)
|
||||||
@ -412,13 +413,7 @@ func (c *APIClient) ReportIllegal(detectResultList *[]api.DetectResult) error {
|
|||||||
|
|
||||||
// ParseV2rayNodeResponse parse the response for the given nodeinfor format
|
// ParseV2rayNodeResponse parse the response for the given nodeinfor format
|
||||||
func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *json.RawMessage) (*api.NodeInfo, error) {
|
func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *json.RawMessage) (*api.NodeInfo, error) {
|
||||||
var TLStype string
|
var speedLimit uint64 = 0
|
||||||
var speedlimit uint64 = 0
|
|
||||||
if c.EnableXTLS {
|
|
||||||
TLStype = "xtls"
|
|
||||||
} else {
|
|
||||||
TLStype = "tls"
|
|
||||||
}
|
|
||||||
|
|
||||||
v2rayNodeInfo := new(V2rayNodeInfo)
|
v2rayNodeInfo := new(V2rayNodeInfo)
|
||||||
if err := json.Unmarshal(*nodeInfoResponse, v2rayNodeInfo); err != nil {
|
if err := json.Unmarshal(*nodeInfoResponse, v2rayNodeInfo); err != nil {
|
||||||
@ -426,9 +421,9 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *json.RawMessage) (*
|
|||||||
}
|
}
|
||||||
|
|
||||||
if c.SpeedLimit > 0 {
|
if c.SpeedLimit > 0 {
|
||||||
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
|
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||||
} else {
|
} else {
|
||||||
speedlimit = uint64((v2rayNodeInfo.SpeedLimit * 1000000) / 8)
|
speedLimit = (v2rayNodeInfo.SpeedLimit * 1000000) / 8
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.DeviceLimit == 0 && v2rayNodeInfo.ClientLimit > 0 {
|
if c.DeviceLimit == 0 && v2rayNodeInfo.ClientLimit > 0 {
|
||||||
@ -436,72 +431,65 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *json.RawMessage) (*
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create GeneralNodeInfo
|
// Create GeneralNodeInfo
|
||||||
nodeinfo := &api.NodeInfo{
|
nodeInfo := &api.NodeInfo{
|
||||||
NodeType: c.NodeType,
|
NodeType: c.NodeType,
|
||||||
NodeID: c.NodeID,
|
NodeID: c.NodeID,
|
||||||
Port: v2rayNodeInfo.V2Port,
|
Port: v2rayNodeInfo.V2Port,
|
||||||
SpeedLimit: speedlimit,
|
SpeedLimit: speedLimit,
|
||||||
AlterID: v2rayNodeInfo.V2AlterID,
|
AlterID: v2rayNodeInfo.V2AlterID,
|
||||||
TransportProtocol: v2rayNodeInfo.V2Net,
|
TransportProtocol: v2rayNodeInfo.V2Net,
|
||||||
FakeType: v2rayNodeInfo.V2Type,
|
FakeType: v2rayNodeInfo.V2Type,
|
||||||
EnableTLS: v2rayNodeInfo.V2TLS,
|
EnableTLS: v2rayNodeInfo.V2TLS,
|
||||||
TLSType: TLStype,
|
|
||||||
Path: v2rayNodeInfo.V2Path,
|
Path: v2rayNodeInfo.V2Path,
|
||||||
Host: v2rayNodeInfo.V2Host,
|
Host: v2rayNodeInfo.V2Host,
|
||||||
EnableVless: c.EnableVless,
|
EnableVless: c.EnableVless,
|
||||||
|
VlessFlow: c.VlessFlow,
|
||||||
}
|
}
|
||||||
|
|
||||||
return nodeinfo, nil
|
return nodeInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseSSNodeResponse parse the response for the given nodeinfor format
|
// ParseSSNodeResponse parse the response for the given nodeinfor format
|
||||||
func (c *APIClient) ParseSSNodeResponse(nodeInfoResponse *json.RawMessage) (*api.NodeInfo, error) {
|
func (c *APIClient) ParseSSNodeResponse(nodeInfoResponse *json.RawMessage) (*api.NodeInfo, error) {
|
||||||
var speedlimit uint64 = 0
|
var speedLimit uint64 = 0
|
||||||
shadowsocksNodeInfo := new(ShadowsocksNodeInfo)
|
shadowsocksNodeInfo := new(ShadowsocksNodeInfo)
|
||||||
if err := json.Unmarshal(*nodeInfoResponse, shadowsocksNodeInfo); err != nil {
|
if err := json.Unmarshal(*nodeInfoResponse, shadowsocksNodeInfo); err != nil {
|
||||||
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(*nodeInfoResponse), err)
|
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(*nodeInfoResponse), err)
|
||||||
}
|
}
|
||||||
if c.SpeedLimit > 0 {
|
if c.SpeedLimit > 0 {
|
||||||
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
|
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||||
} else {
|
} else {
|
||||||
speedlimit = uint64((shadowsocksNodeInfo.SpeedLimit * 1000000) / 8)
|
speedLimit = uint64((shadowsocksNodeInfo.SpeedLimit * 1000000) / 8)
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.DeviceLimit == 0 && shadowsocksNodeInfo.ClientLimit > 0 {
|
if c.DeviceLimit == 0 && shadowsocksNodeInfo.ClientLimit > 0 {
|
||||||
c.DeviceLimit = shadowsocksNodeInfo.ClientLimit
|
c.DeviceLimit = shadowsocksNodeInfo.ClientLimit
|
||||||
}
|
}
|
||||||
// Create GeneralNodeInfo
|
// Create GeneralNodeInfo
|
||||||
nodeinfo := &api.NodeInfo{
|
nodeInfo := &api.NodeInfo{
|
||||||
NodeType: c.NodeType,
|
NodeType: c.NodeType,
|
||||||
NodeID: c.NodeID,
|
NodeID: c.NodeID,
|
||||||
Port: shadowsocksNodeInfo.Port,
|
Port: shadowsocksNodeInfo.Port,
|
||||||
SpeedLimit: speedlimit,
|
SpeedLimit: speedLimit,
|
||||||
TransportProtocol: "tcp",
|
TransportProtocol: "tcp",
|
||||||
CypherMethod: shadowsocksNodeInfo.Method,
|
CypherMethod: shadowsocksNodeInfo.Method,
|
||||||
}
|
}
|
||||||
|
|
||||||
return nodeinfo, nil
|
return nodeInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseTrojanNodeResponse parse the response for the given nodeinfor format
|
// ParseTrojanNodeResponse parse the response for the given nodeinfor format
|
||||||
func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *json.RawMessage) (*api.NodeInfo, error) {
|
func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *json.RawMessage) (*api.NodeInfo, error) {
|
||||||
|
var speedLimit uint64 = 0
|
||||||
var TLSType string
|
|
||||||
var speedlimit uint64 = 0
|
|
||||||
if c.EnableXTLS {
|
|
||||||
TLSType = "xtls"
|
|
||||||
} else {
|
|
||||||
TLSType = "tls"
|
|
||||||
}
|
|
||||||
|
|
||||||
trojanNodeInfo := new(TrojanNodeInfo)
|
trojanNodeInfo := new(TrojanNodeInfo)
|
||||||
if err := json.Unmarshal(*nodeInfoResponse, trojanNodeInfo); err != nil {
|
if err := json.Unmarshal(*nodeInfoResponse, trojanNodeInfo); err != nil {
|
||||||
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(*nodeInfoResponse), err)
|
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(*nodeInfoResponse), err)
|
||||||
}
|
}
|
||||||
if c.SpeedLimit > 0 {
|
if c.SpeedLimit > 0 {
|
||||||
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
|
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||||
} else {
|
} else {
|
||||||
speedlimit = uint64((trojanNodeInfo.SpeedLimit * 1000000) / 8)
|
speedLimit = (trojanNodeInfo.SpeedLimit * 1000000) / 8
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.DeviceLimit == 0 && trojanNodeInfo.ClientLimit > 0 {
|
if c.DeviceLimit == 0 && trojanNodeInfo.ClientLimit > 0 {
|
||||||
@ -509,22 +497,21 @@ func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *json.RawMessage) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create GeneralNodeInfo
|
// Create GeneralNodeInfo
|
||||||
nodeinfo := &api.NodeInfo{
|
nodeInfo := &api.NodeInfo{
|
||||||
NodeType: c.NodeType,
|
NodeType: c.NodeType,
|
||||||
NodeID: c.NodeID,
|
NodeID: c.NodeID,
|
||||||
Port: trojanNodeInfo.TrojanPort,
|
Port: trojanNodeInfo.TrojanPort,
|
||||||
SpeedLimit: speedlimit,
|
SpeedLimit: speedLimit,
|
||||||
TransportProtocol: "tcp",
|
TransportProtocol: "tcp",
|
||||||
EnableTLS: true,
|
EnableTLS: true,
|
||||||
TLSType: TLSType,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nodeinfo, nil
|
return nodeInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseV2rayUserListResponse parse the response for the given userinfo format
|
// ParseV2rayUserListResponse parse the response for the given userinfo format
|
||||||
func (c *APIClient) ParseV2rayUserListResponse(userInfoResponse *json.RawMessage) (*[]api.UserInfo, error) {
|
func (c *APIClient) ParseV2rayUserListResponse(userInfoResponse *json.RawMessage) (*[]api.UserInfo, error) {
|
||||||
var speedlimit uint64 = 0
|
var speedLimit uint64 = 0
|
||||||
|
|
||||||
vmessUserList := new([]*VMessUser)
|
vmessUserList := new([]*VMessUser)
|
||||||
if err := json.Unmarshal(*userInfoResponse, vmessUserList); err != nil {
|
if err := json.Unmarshal(*userInfoResponse, vmessUserList); err != nil {
|
||||||
@ -534,16 +521,16 @@ func (c *APIClient) ParseV2rayUserListResponse(userInfoResponse *json.RawMessage
|
|||||||
userList := make([]api.UserInfo, len(*vmessUserList))
|
userList := make([]api.UserInfo, len(*vmessUserList))
|
||||||
for i, user := range *vmessUserList {
|
for i, user := range *vmessUserList {
|
||||||
if c.SpeedLimit > 0 {
|
if c.SpeedLimit > 0 {
|
||||||
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
|
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||||
} else {
|
} else {
|
||||||
speedlimit = uint64((user.SpeedLimit * 1000000) / 8)
|
speedLimit = (user.SpeedLimit * 1000000) / 8
|
||||||
}
|
}
|
||||||
userList[i] = api.UserInfo{
|
userList[i] = api.UserInfo{
|
||||||
UID: user.UID,
|
UID: user.UID,
|
||||||
Email: "",
|
Email: "",
|
||||||
UUID: user.VmessUID,
|
UUID: user.VmessUID,
|
||||||
DeviceLimit: c.DeviceLimit,
|
DeviceLimit: c.DeviceLimit,
|
||||||
SpeedLimit: speedlimit,
|
SpeedLimit: speedLimit,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -552,7 +539,7 @@ func (c *APIClient) ParseV2rayUserListResponse(userInfoResponse *json.RawMessage
|
|||||||
|
|
||||||
// ParseTrojanUserListResponse parse the response for the given userinfo format
|
// ParseTrojanUserListResponse parse the response for the given userinfo format
|
||||||
func (c *APIClient) ParseTrojanUserListResponse(userInfoResponse *json.RawMessage) (*[]api.UserInfo, error) {
|
func (c *APIClient) ParseTrojanUserListResponse(userInfoResponse *json.RawMessage) (*[]api.UserInfo, error) {
|
||||||
var speedlimit uint64 = 0
|
var speedLimit uint64 = 0
|
||||||
|
|
||||||
trojanUserList := new([]*TrojanUser)
|
trojanUserList := new([]*TrojanUser)
|
||||||
if err := json.Unmarshal(*userInfoResponse, trojanUserList); err != nil {
|
if err := json.Unmarshal(*userInfoResponse, trojanUserList); err != nil {
|
||||||
@ -562,16 +549,16 @@ func (c *APIClient) ParseTrojanUserListResponse(userInfoResponse *json.RawMessag
|
|||||||
userList := make([]api.UserInfo, len(*trojanUserList))
|
userList := make([]api.UserInfo, len(*trojanUserList))
|
||||||
for i, user := range *trojanUserList {
|
for i, user := range *trojanUserList {
|
||||||
if c.SpeedLimit > 0 {
|
if c.SpeedLimit > 0 {
|
||||||
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
|
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||||
} else {
|
} else {
|
||||||
speedlimit = (user.SpeedLimit * 1000000) / 8
|
speedLimit = (user.SpeedLimit * 1000000) / 8
|
||||||
}
|
}
|
||||||
userList[i] = api.UserInfo{
|
userList[i] = api.UserInfo{
|
||||||
UID: user.UID,
|
UID: user.UID,
|
||||||
Email: "",
|
Email: "",
|
||||||
UUID: user.Password,
|
UUID: user.Password,
|
||||||
DeviceLimit: c.DeviceLimit,
|
DeviceLimit: c.DeviceLimit,
|
||||||
SpeedLimit: speedlimit,
|
SpeedLimit: speedLimit,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -580,7 +567,7 @@ func (c *APIClient) ParseTrojanUserListResponse(userInfoResponse *json.RawMessag
|
|||||||
|
|
||||||
// ParseSSUserListResponse parse the response for the given userinfo format
|
// ParseSSUserListResponse parse the response for the given userinfo format
|
||||||
func (c *APIClient) ParseSSUserListResponse(userInfoResponse *json.RawMessage) (*[]api.UserInfo, error) {
|
func (c *APIClient) ParseSSUserListResponse(userInfoResponse *json.RawMessage) (*[]api.UserInfo, error) {
|
||||||
var speedlimit uint64 = 0
|
var speedLimit uint64 = 0
|
||||||
|
|
||||||
ssUserList := new([]*SSUser)
|
ssUserList := new([]*SSUser)
|
||||||
if err := json.Unmarshal(*userInfoResponse, ssUserList); err != nil {
|
if err := json.Unmarshal(*userInfoResponse, ssUserList); err != nil {
|
||||||
@ -590,16 +577,16 @@ func (c *APIClient) ParseSSUserListResponse(userInfoResponse *json.RawMessage) (
|
|||||||
userList := make([]api.UserInfo, len(*ssUserList))
|
userList := make([]api.UserInfo, len(*ssUserList))
|
||||||
for i, user := range *ssUserList {
|
for i, user := range *ssUserList {
|
||||||
if c.SpeedLimit > 0 {
|
if c.SpeedLimit > 0 {
|
||||||
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
|
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||||
} else {
|
} else {
|
||||||
speedlimit = uint64(user.SpeedLimit * 1000000 / 8)
|
speedLimit = uint64(user.SpeedLimit * 1000000 / 8)
|
||||||
}
|
}
|
||||||
userList[i] = api.UserInfo{
|
userList[i] = api.UserInfo{
|
||||||
UID: user.UID,
|
UID: user.UID,
|
||||||
Email: "",
|
Email: "",
|
||||||
Passwd: user.Password,
|
Passwd: user.Password,
|
||||||
DeviceLimit: c.DeviceLimit,
|
DeviceLimit: c.DeviceLimit,
|
||||||
SpeedLimit: speedlimit,
|
SpeedLimit: speedLimit,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,8 +154,8 @@ func TestReportIllegal(t *testing.T) {
|
|||||||
client := CreateClient()
|
client := CreateClient()
|
||||||
|
|
||||||
detectResult := []api.DetectResult{
|
detectResult := []api.DetectResult{
|
||||||
{1, 1},
|
{UID: 1, RuleID: 1},
|
||||||
{1, 2},
|
{UID: 1, RuleID: 2},
|
||||||
}
|
}
|
||||||
client.Debug()
|
client.Debug()
|
||||||
err := client.ReportIllegal(&detectResult)
|
err := client.ReportIllegal(&detectResult)
|
||||||
|
@ -8,7 +8,6 @@ type NodeInfoResponse struct {
|
|||||||
Class int `json:"node_class"`
|
Class int `json:"node_class"`
|
||||||
SpeedLimit float64 `json:"node_speedlimit"`
|
SpeedLimit float64 `json:"node_speedlimit"`
|
||||||
TrafficRate float64 `json:"traffic_rate"`
|
TrafficRate float64 `json:"traffic_rate"`
|
||||||
MuOnly int `json:"mu_only"`
|
|
||||||
Sort int `json:"sort"`
|
Sort int `json:"sort"`
|
||||||
RawServerString string `json:"server"`
|
RawServerString string `json:"server"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
@ -17,51 +16,35 @@ type NodeInfoResponse struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type CustomConfig struct {
|
type CustomConfig struct {
|
||||||
OffsetPortUser string `json:"offset_port_user"`
|
|
||||||
OffsetPortNode string `json:"offset_port_node"`
|
OffsetPortNode string `json:"offset_port_node"`
|
||||||
ServerSub string `json:"server_sub"`
|
|
||||||
Host string `json:"host"`
|
Host string `json:"host"`
|
||||||
MuPort string `json:"mu_port"`
|
Method string `json:"method"`
|
||||||
MuEncryption string `json:"mu_encryption"`
|
|
||||||
MuProtocol string `json:"mu_protocol"`
|
|
||||||
MuObfs string `json:"mu_obfs"`
|
|
||||||
MuSuffix string `json:"mu_suffix"`
|
|
||||||
V2Port string `json:"v2_port"`
|
|
||||||
TLS string `json:"tls"`
|
TLS string `json:"tls"`
|
||||||
EnableVless string `json:"enable_vless"`
|
EnableVless string `json:"enable_vless"`
|
||||||
AlterID string `json:"alter_id"`
|
|
||||||
Network string `json:"network"`
|
Network string `json:"network"`
|
||||||
Security string `json:"security"`
|
Security string `json:"security"`
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
VerifyCert bool `json:"verify_cert"`
|
VerifyCert bool `json:"verify_cert"`
|
||||||
Obfs string `json:"obfs"`
|
Obfs string `json:"obfs"`
|
||||||
Header json.RawMessage `json:"header"`
|
Header json.RawMessage `json:"header"`
|
||||||
TrojanPort string `json:"trojan_port"`
|
|
||||||
AllowInsecure string `json:"allow_insecure"`
|
AllowInsecure string `json:"allow_insecure"`
|
||||||
Grpc string `json:"grpc"`
|
|
||||||
Servicename string `json:"servicename"`
|
Servicename string `json:"servicename"`
|
||||||
EnableXtls string `json:"enable_xtls"`
|
EnableXtls string `json:"enable_xtls"`
|
||||||
Flow string `json:"flow"`
|
Flow string `json:"flow"`
|
||||||
|
EnableREALITY bool `json:"enable_reality"`
|
||||||
|
RealityOpts *REALITYConfig `json:"reality-opts"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserResponse is the response of user
|
// UserResponse is the response of user
|
||||||
type UserResponse struct {
|
type UserResponse struct {
|
||||||
ID int `json:"id"`
|
ID int `json:"id"`
|
||||||
Email string `json:"email"`
|
Passwd string `json:"passwd"`
|
||||||
Passwd string `json:"passwd"`
|
Port uint32 `json:"port"`
|
||||||
Port uint32 `json:"port"`
|
Method string `json:"method"`
|
||||||
Method string `json:"method"`
|
SpeedLimit float64 `json:"node_speedlimit"`
|
||||||
SpeedLimit float64 `json:"node_speedlimit"`
|
DeviceLimit int `json:"node_iplimit"`
|
||||||
DeviceLimit int `json:"node_connector"`
|
UUID string `json:"uuid"`
|
||||||
Protocol string `json:"protocol"`
|
AliveIP int `json:"alive_ip"`
|
||||||
ProtocolParam string `json:"protocol_param"`
|
|
||||||
Obfs string `json:"obfs"`
|
|
||||||
ObfsParam string `json:"obfs_param"`
|
|
||||||
ForbiddenIP string `json:"forbidden_ip"`
|
|
||||||
ForbiddenPort string `json:"forbidden_port"`
|
|
||||||
UUID string `json:"uuid"`
|
|
||||||
MultiUser int `json:"is_multi_user"`
|
|
||||||
AliveIP int `json:"alive_ip"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Response is the common response
|
// Response is the common response
|
||||||
@ -75,7 +58,7 @@ type PostData struct {
|
|||||||
Data interface{} `json:"data"`
|
Data interface{} `json:"data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// SystemLoad is the data structure of systemload
|
// SystemLoad is the data structure of system load
|
||||||
type SystemLoad struct {
|
type SystemLoad struct {
|
||||||
Uptime string `json:"uptime"`
|
Uptime string `json:"uptime"`
|
||||||
Load string `json:"load"`
|
Load string `json:"load"`
|
||||||
@ -103,3 +86,14 @@ type IllegalItem struct {
|
|||||||
ID int `json:"list_id"`
|
ID int `json:"list_id"`
|
||||||
UID int `json:"user_id"`
|
UID int `json:"user_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type REALITYConfig struct {
|
||||||
|
Dest string `json:"dest,omitempty"`
|
||||||
|
ProxyProtocolVer uint64 `json:"proxy_protocol_ver,omitempty"`
|
||||||
|
ServerNames []string `json:"server_names,omitempty"`
|
||||||
|
PrivateKey string `json:"private_key,omitempty"`
|
||||||
|
MinClientVer string `json:"min_client_ver,omitempty"`
|
||||||
|
MaxClientVer string `json:"max_client_ver,omitempty"`
|
||||||
|
MaxTimeDiff uint64 `json:"max_time_diff,omitempty"`
|
||||||
|
ShortIds []string `json:"short_ids,omitempty"`
|
||||||
|
}
|
||||||
|
@ -3,8 +3,8 @@ package sspanel
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
@ -13,6 +13,8 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
|
|
||||||
"github.com/XrayR-project/XrayR/api"
|
"github.com/XrayR-project/XrayR/api"
|
||||||
@ -32,19 +34,21 @@ type APIClient struct {
|
|||||||
Key string
|
Key string
|
||||||
NodeType string
|
NodeType string
|
||||||
EnableVless bool
|
EnableVless bool
|
||||||
EnableXTLS bool
|
VlessFlow string
|
||||||
SpeedLimit float64
|
SpeedLimit float64
|
||||||
DeviceLimit int
|
DeviceLimit int
|
||||||
DisableCustomConfig bool
|
DisableCustomConfig bool
|
||||||
LocalRuleList []api.DetectRule
|
LocalRuleList []api.DetectRule
|
||||||
LastReportOnline map[int]int
|
LastReportOnline map[int]int
|
||||||
access sync.Mutex
|
access sync.Mutex
|
||||||
|
version string
|
||||||
|
eTags map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creat a api instance
|
// New create api instance
|
||||||
func New(apiConfig *api.Config) *APIClient {
|
func New(apiConfig *api.Config) *APIClient {
|
||||||
|
|
||||||
client := resty.New()
|
client := resty.New()
|
||||||
|
|
||||||
client.SetRetryCount(3)
|
client.SetRetryCount(3)
|
||||||
if apiConfig.Timeout > 0 {
|
if apiConfig.Timeout > 0 {
|
||||||
client.SetTimeout(time.Duration(apiConfig.Timeout) * time.Second)
|
client.SetTimeout(time.Duration(apiConfig.Timeout) * time.Second)
|
||||||
@ -52,12 +56,14 @@ func New(apiConfig *api.Config) *APIClient {
|
|||||||
client.SetTimeout(5 * time.Second)
|
client.SetTimeout(5 * time.Second)
|
||||||
}
|
}
|
||||||
client.OnError(func(req *resty.Request, err error) {
|
client.OnError(func(req *resty.Request, err error) {
|
||||||
if v, ok := err.(*resty.ResponseError); ok {
|
var v *resty.ResponseError
|
||||||
|
if errors.As(err, &v) {
|
||||||
// v.Response contains the last response from the server
|
// v.Response contains the last response from the server
|
||||||
// v.Err contains the original error
|
// v.Err contains the original error
|
||||||
log.Print(v.Err)
|
log.Print(v.Err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
client.SetBaseURL(apiConfig.APIHost)
|
client.SetBaseURL(apiConfig.APIHost)
|
||||||
// Create Key for each requests
|
// Create Key for each requests
|
||||||
client.SetQueryParam("key", apiConfig.Key)
|
client.SetQueryParam("key", apiConfig.Key)
|
||||||
@ -73,23 +79,29 @@ func New(apiConfig *api.Config) *APIClient {
|
|||||||
APIHost: apiConfig.APIHost,
|
APIHost: apiConfig.APIHost,
|
||||||
NodeType: apiConfig.NodeType,
|
NodeType: apiConfig.NodeType,
|
||||||
EnableVless: apiConfig.EnableVless,
|
EnableVless: apiConfig.EnableVless,
|
||||||
EnableXTLS: apiConfig.EnableXTLS,
|
VlessFlow: apiConfig.VlessFlow,
|
||||||
SpeedLimit: apiConfig.SpeedLimit,
|
SpeedLimit: apiConfig.SpeedLimit,
|
||||||
DeviceLimit: apiConfig.DeviceLimit,
|
DeviceLimit: apiConfig.DeviceLimit,
|
||||||
LocalRuleList: localRuleList,
|
LocalRuleList: localRuleList,
|
||||||
DisableCustomConfig: apiConfig.DisableCustomConfig,
|
DisableCustomConfig: apiConfig.DisableCustomConfig,
|
||||||
LastReportOnline: make(map[int]int),
|
LastReportOnline: make(map[int]int),
|
||||||
|
eTags: make(map[string]string),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// readLocalRuleList reads the local rule list file
|
// readLocalRuleList reads the local rule list file
|
||||||
func readLocalRuleList(path string) (LocalRuleList []api.DetectRule) {
|
func readLocalRuleList(path string) (LocalRuleList []api.DetectRule) {
|
||||||
|
|
||||||
LocalRuleList = make([]api.DetectRule, 0)
|
LocalRuleList = make([]api.DetectRule, 0)
|
||||||
if path != "" {
|
if path != "" {
|
||||||
// open the file
|
// open the file
|
||||||
file, err := os.Open(path)
|
file, err := os.Open(path)
|
||||||
|
|
||||||
|
defer func(file *os.File) {
|
||||||
|
err := file.Close()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error when closing file: %s", err)
|
||||||
|
}
|
||||||
|
}(file)
|
||||||
// handle errors while opening
|
// handle errors while opening
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error when opening file: %s", err)
|
log.Printf("Error when opening file: %s", err)
|
||||||
@ -110,8 +122,6 @@ func readLocalRuleList(path string) (LocalRuleList []api.DetectRule) {
|
|||||||
log.Fatalf("Error while reading file: %s", err)
|
log.Fatalf("Error while reading file: %s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
file.Close()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return LocalRuleList
|
return LocalRuleList
|
||||||
@ -138,7 +148,7 @@ func (c *APIClient) parseResponse(res *resty.Response, path string, err error) (
|
|||||||
|
|
||||||
if res.StatusCode() > 400 {
|
if res.StatusCode() > 400 {
|
||||||
body := res.Body()
|
body := res.Body()
|
||||||
return nil, fmt.Errorf("request %s failed: %s, %s", c.assembleURL(path), string(body), err)
|
return nil, fmt.Errorf("request %s failed: %s, %v", c.assembleURL(path), string(body), err)
|
||||||
}
|
}
|
||||||
response := res.Result().(*Response)
|
response := res.Result().(*Response)
|
||||||
|
|
||||||
@ -149,13 +159,22 @@ func (c *APIClient) parseResponse(res *resty.Response, path string, err error) (
|
|||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNodeInfo will pull NodeInfo Config from sspanel
|
// GetNodeInfo will pull NodeInfo Config from ssPanel
|
||||||
func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
||||||
path := fmt.Sprintf("/mod_mu/nodes/%d/info", c.NodeID)
|
path := fmt.Sprintf("/mod_mu/nodes/%d/info", c.NodeID)
|
||||||
res, err := c.client.R().
|
res, err := c.client.R().
|
||||||
SetResult(&Response{}).
|
SetResult(&Response{}).
|
||||||
|
SetHeader("If-None-Match", c.eTags["node"]).
|
||||||
ForceContentType("application/json").
|
ForceContentType("application/json").
|
||||||
Get(path)
|
Get(path)
|
||||||
|
// Etag identifier for a specific version of a resource. StatusCode = 304 means no changed
|
||||||
|
if res.StatusCode() == 304 {
|
||||||
|
return nil, errors.New(api.NodeNotModified)
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.Header().Get("ETag") != "" && res.Header().Get("ETag") != c.eTags["node"] {
|
||||||
|
c.eTags["node"] = res.Header().Get("ETag")
|
||||||
|
}
|
||||||
|
|
||||||
response, err := c.parseResponse(res, path, err)
|
response, err := c.parseResponse(res, path, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -168,25 +187,18 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
|||||||
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(nodeInfoResponse), err)
|
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(nodeInfoResponse), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// New sspanel API
|
// determine ssPanel version, if disable custom config or version < 2021.11, then use old api
|
||||||
disableCustomConfig := c.DisableCustomConfig
|
c.version = nodeInfoResponse.Version
|
||||||
if nodeInfoResponse.Version != "" && !disableCustomConfig {
|
var isExpired bool
|
||||||
// Check if custom_config is empty
|
if compareVersion(c.version, "2021.11") == -1 {
|
||||||
if configString, err := json.Marshal(nodeInfoResponse.CustomConfig); err != nil || string(configString) == "[]" {
|
isExpired = true
|
||||||
log.Printf("custom_config is empty! take config from address now.")
|
|
||||||
disableCustomConfig = true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
disableCustomConfig = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !disableCustomConfig {
|
if c.DisableCustomConfig || isExpired {
|
||||||
nodeInfo, err = c.ParseSSPanelNodeInfo(nodeInfoResponse)
|
if isExpired {
|
||||||
if err != nil {
|
log.Print("The panel version is expired, it is recommended to update immediately")
|
||||||
res, _ := json.Marshal(nodeInfoResponse)
|
|
||||||
return nil, fmt.Errorf("Parse node info failed: %s, \nError: %s, \nPlease check the doc of custom_config for help: https://xrayr-project.github.io/XrayR-doc/dui-jie-sspanel/sspanel/sspanel_custom_config", string(res), err)
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
switch c.NodeType {
|
switch c.NodeType {
|
||||||
case "V2ray":
|
case "V2ray":
|
||||||
nodeInfo, err = c.ParseV2rayNodeResponse(nodeInfoResponse)
|
nodeInfo, err = c.ParseV2rayNodeResponse(nodeInfoResponse)
|
||||||
@ -199,24 +211,39 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
|||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
|
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
nodeInfo, err = c.ParseSSPanelNodeInfo(nodeInfoResponse)
|
||||||
|
if err != nil {
|
||||||
|
res, _ := json.Marshal(nodeInfoResponse)
|
||||||
|
return nil, fmt.Errorf("parse node info failed: %s, \nError: %s, \nPlease check the doc of custom_config for help: https://xrayr-project.github.io/XrayR-doc/dui-jie-sspanel/sspanel/sspanel_custom_config", string(res), err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
res, _ := json.Marshal(nodeInfoResponse)
|
res, _ := json.Marshal(nodeInfoResponse)
|
||||||
return nil, fmt.Errorf("Parse node info failed: %s, \nError: %s", string(res), err)
|
return nil, fmt.Errorf("parse node info failed: %s, \nError: %s", string(res), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nodeInfo, nil
|
return nodeInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUserList will pull user form sspanel
|
// GetUserList will pull user form ssPanel
|
||||||
func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
|
func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
|
||||||
path := "/mod_mu/users"
|
path := "/mod_mu/users"
|
||||||
res, err := c.client.R().
|
res, err := c.client.R().
|
||||||
SetQueryParam("node_id", strconv.Itoa(c.NodeID)).
|
SetQueryParam("node_id", strconv.Itoa(c.NodeID)).
|
||||||
|
SetHeader("If-None-Match", c.eTags["users"]).
|
||||||
SetResult(&Response{}).
|
SetResult(&Response{}).
|
||||||
ForceContentType("application/json").
|
ForceContentType("application/json").
|
||||||
Get(path)
|
Get(path)
|
||||||
|
// Etag identifier for a specific version of a resource. StatusCode = 304 means no changed
|
||||||
|
if res.StatusCode() == 304 {
|
||||||
|
return nil, errors.New(api.UserNotModified)
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.Header().Get("ETag") != "" && res.Header().Get("ETag") != c.eTags["users"] {
|
||||||
|
c.eTags["users"] = res.Header().Get("ETag")
|
||||||
|
}
|
||||||
|
|
||||||
response, err := c.parseResponse(res, path, err)
|
response, err := c.parseResponse(res, path, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -236,25 +263,27 @@ func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
|
|||||||
return userList, nil
|
return userList, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReportNodeStatus reports the node status to the sspanel
|
// ReportNodeStatus reports the node status to the ssPanel
|
||||||
func (c *APIClient) ReportNodeStatus(nodeStatus *api.NodeStatus) (err error) {
|
func (c *APIClient) ReportNodeStatus(nodeStatus *api.NodeStatus) (err error) {
|
||||||
path := fmt.Sprintf("/mod_mu/nodes/%d/info", c.NodeID)
|
// Determine whether a status report is in need
|
||||||
systemload := SystemLoad{
|
if compareVersion(c.version, "2023.2") == -1 {
|
||||||
Uptime: strconv.FormatUint(nodeStatus.Uptime, 10),
|
path := fmt.Sprintf("/mod_mu/nodes/%d/info", c.NodeID)
|
||||||
Load: fmt.Sprintf("%.2f %.2f %.2f", nodeStatus.CPU/100, nodeStatus.Mem/100, nodeStatus.Disk/100),
|
systemLoad := SystemLoad{
|
||||||
|
Uptime: strconv.FormatUint(nodeStatus.Uptime, 10),
|
||||||
|
Load: fmt.Sprintf("%.2f %.2f %.2f", nodeStatus.CPU/100, nodeStatus.Mem/100, nodeStatus.Disk/100),
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := c.client.R().
|
||||||
|
SetBody(systemLoad).
|
||||||
|
SetResult(&Response{}).
|
||||||
|
ForceContentType("application/json").
|
||||||
|
Post(path)
|
||||||
|
|
||||||
|
_, err = c.parseResponse(res, path, err)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := c.client.R().
|
|
||||||
SetBody(systemload).
|
|
||||||
SetResult(&Response{}).
|
|
||||||
ForceContentType("application/json").
|
|
||||||
Post(path)
|
|
||||||
|
|
||||||
_, err = c.parseResponse(res, path, err)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,16 +296,12 @@ func (c *APIClient) ReportNodeOnlineUsers(onlineUserList *[]api.OnlineUser) erro
|
|||||||
data := make([]OnlineUser, len(*onlineUserList))
|
data := make([]OnlineUser, len(*onlineUserList))
|
||||||
for i, user := range *onlineUserList {
|
for i, user := range *onlineUserList {
|
||||||
data[i] = OnlineUser{UID: user.UID, IP: user.IP}
|
data[i] = OnlineUser{UID: user.UID, IP: user.IP}
|
||||||
if _, ok := reportOnline[user.UID]; ok {
|
reportOnline[user.UID]++ // will start from 1 if key doesn’t exist
|
||||||
reportOnline[user.UID]++
|
|
||||||
} else {
|
|
||||||
reportOnline[user.UID] = 1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
c.LastReportOnline = reportOnline // Update LastReportOnline
|
c.LastReportOnline = reportOnline // Update LastReportOnline
|
||||||
|
|
||||||
postData := &PostData{Data: data}
|
postData := &PostData{Data: data}
|
||||||
path := fmt.Sprintf("/mod_mu/users/aliveip")
|
path := "/mod_mu/users/aliveip"
|
||||||
res, err := c.client.R().
|
res, err := c.client.R().
|
||||||
SetQueryParam("node_id", strconv.Itoa(c.NodeID)).
|
SetQueryParam("node_id", strconv.Itoa(c.NodeID)).
|
||||||
SetBody(postData).
|
SetBody(postData).
|
||||||
@ -318,15 +343,25 @@ func (c *APIClient) ReportUserTraffic(userTraffic *[]api.UserTraffic) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNodeRule will pull the audit rule form sspanel
|
// GetNodeRule will pull the audit rule form ssPanel
|
||||||
func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
|
func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
|
||||||
ruleList := c.LocalRuleList
|
ruleList := c.LocalRuleList
|
||||||
path := "/mod_mu/func/detect_rules"
|
path := "/mod_mu/func/detect_rules"
|
||||||
res, err := c.client.R().
|
res, err := c.client.R().
|
||||||
SetResult(&Response{}).
|
SetResult(&Response{}).
|
||||||
|
SetHeader("If-None-Match", c.eTags["rules"]).
|
||||||
ForceContentType("application/json").
|
ForceContentType("application/json").
|
||||||
Get(path)
|
Get(path)
|
||||||
|
|
||||||
|
// Etag identifier for a specific version of a resource. StatusCode = 304 means no changed
|
||||||
|
if res.StatusCode() == 304 {
|
||||||
|
return nil, errors.New(api.RuleNotModified)
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.Header().Get("ETag") != "" && res.Header().Get("ETag") != c.eTags["rules"] {
|
||||||
|
c.eTags["rules"] = res.Header().Get("ETag")
|
||||||
|
}
|
||||||
|
|
||||||
response, err := c.parseResponse(res, path, err)
|
response, err := c.parseResponse(res, path, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -372,12 +407,12 @@ func (c *APIClient) ReportIllegal(detectResultList *[]api.DetectResult) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseV2rayNodeResponse parse the response for the given nodeinfor format
|
// ParseV2rayNodeResponse parse the response for the given node info format
|
||||||
func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *NodeInfoResponse) (*api.NodeInfo, error) {
|
func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *NodeInfoResponse) (*api.NodeInfo, error) {
|
||||||
var enableTLS bool
|
var enableTLS bool
|
||||||
var path, host, TLStype, transportProtocol, serviceName, HeaderType string
|
var path, host, transportProtocol, serviceName, HeaderType string
|
||||||
var header json.RawMessage
|
var header json.RawMessage
|
||||||
var speedlimit uint64 = 0
|
var speedLimit uint64 = 0
|
||||||
if nodeInfoResponse.RawServerString == "" {
|
if nodeInfoResponse.RawServerString == "" {
|
||||||
return nil, fmt.Errorf("no server info in response")
|
return nil, fmt.Errorf("no server info in response")
|
||||||
}
|
}
|
||||||
@ -399,12 +434,7 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *NodeInfoResponse) (
|
|||||||
// Compatible with more node types config
|
// Compatible with more node types config
|
||||||
for _, value := range serverConf[3:5] {
|
for _, value := range serverConf[3:5] {
|
||||||
switch value {
|
switch value {
|
||||||
case "tls", "xtls":
|
case "tls":
|
||||||
if c.EnableXTLS {
|
|
||||||
TLStype = "xtls"
|
|
||||||
} else {
|
|
||||||
TLStype = "tls"
|
|
||||||
}
|
|
||||||
enableTLS = true
|
enableTLS = true
|
||||||
default:
|
default:
|
||||||
if value != "" {
|
if value != "" {
|
||||||
@ -429,14 +459,14 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *NodeInfoResponse) (
|
|||||||
host = value
|
host = value
|
||||||
case "servicename":
|
case "servicename":
|
||||||
serviceName = value
|
serviceName = value
|
||||||
case "headertype":
|
case "headerType":
|
||||||
HeaderType = value
|
HeaderType = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if c.SpeedLimit > 0 {
|
if c.SpeedLimit > 0 {
|
||||||
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
|
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||||
} else {
|
} else {
|
||||||
speedlimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
|
speedLimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
|
||||||
}
|
}
|
||||||
|
|
||||||
if HeaderType != "" {
|
if HeaderType != "" {
|
||||||
@ -445,33 +475,33 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *NodeInfoResponse) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("marshal Header Type %s into config fialed: %s", header, err)
|
return nil, fmt.Errorf("marshal Header Type %s into config failed: %s", header, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create GeneralNodeInfo
|
// Create GeneralNodeInfo
|
||||||
nodeinfo := &api.NodeInfo{
|
nodeInfo := &api.NodeInfo{
|
||||||
NodeType: c.NodeType,
|
NodeType: c.NodeType,
|
||||||
NodeID: c.NodeID,
|
NodeID: c.NodeID,
|
||||||
Port: port,
|
Port: port,
|
||||||
SpeedLimit: speedlimit,
|
SpeedLimit: speedLimit,
|
||||||
AlterID: alterID,
|
AlterID: alterID,
|
||||||
TransportProtocol: transportProtocol,
|
TransportProtocol: transportProtocol,
|
||||||
EnableTLS: enableTLS,
|
EnableTLS: enableTLS,
|
||||||
TLSType: TLStype,
|
|
||||||
Path: path,
|
Path: path,
|
||||||
Host: host,
|
Host: host,
|
||||||
EnableVless: c.EnableVless,
|
EnableVless: c.EnableVless,
|
||||||
|
VlessFlow: c.VlessFlow,
|
||||||
ServiceName: serviceName,
|
ServiceName: serviceName,
|
||||||
Header: header,
|
Header: header,
|
||||||
}
|
}
|
||||||
|
|
||||||
return nodeinfo, nil
|
return nodeInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseSSNodeResponse parse the response for the given nodeinfor format
|
// ParseSSNodeResponse parse the response for the given node info format
|
||||||
func (c *APIClient) ParseSSNodeResponse(nodeInfoResponse *NodeInfoResponse) (*api.NodeInfo, error) {
|
func (c *APIClient) ParseSSNodeResponse(nodeInfoResponse *NodeInfoResponse) (*api.NodeInfo, error) {
|
||||||
var port uint32 = 0
|
var port uint32 = 0
|
||||||
var speedlimit uint64 = 0
|
var speedLimit uint64 = 0
|
||||||
var method string
|
var method string
|
||||||
path := "/mod_mu/users"
|
path := "/mod_mu/users"
|
||||||
res, err := c.client.R().
|
res, err := c.client.R().
|
||||||
@ -490,41 +520,35 @@ func (c *APIClient) ParseSSNodeResponse(nodeInfoResponse *NodeInfoResponse) (*ap
|
|||||||
if err := json.Unmarshal(response.Data, userListResponse); err != nil {
|
if err := json.Unmarshal(response.Data, userListResponse); err != nil {
|
||||||
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(userListResponse), err)
|
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(userListResponse), err)
|
||||||
}
|
}
|
||||||
// Find the multi-user
|
|
||||||
for _, u := range *userListResponse {
|
// init server port
|
||||||
if u.MultiUser > 0 {
|
if len(*userListResponse) != 0 {
|
||||||
port = u.Port
|
port = (*userListResponse)[0].Port
|
||||||
method = u.Method
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if port == 0 || method == "" {
|
|
||||||
return nil, fmt.Errorf("cant find the single port multi user")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.SpeedLimit > 0 {
|
if c.SpeedLimit > 0 {
|
||||||
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
|
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||||
} else {
|
} else {
|
||||||
speedlimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
|
speedLimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
|
||||||
}
|
}
|
||||||
// Create GeneralNodeInfo
|
// Create GeneralNodeInfo
|
||||||
nodeinfo := &api.NodeInfo{
|
nodeInfo := &api.NodeInfo{
|
||||||
NodeType: c.NodeType,
|
NodeType: c.NodeType,
|
||||||
NodeID: c.NodeID,
|
NodeID: c.NodeID,
|
||||||
Port: port,
|
Port: port,
|
||||||
SpeedLimit: speedlimit,
|
SpeedLimit: speedLimit,
|
||||||
TransportProtocol: "tcp",
|
TransportProtocol: "tcp",
|
||||||
CypherMethod: method,
|
CypherMethod: method,
|
||||||
}
|
}
|
||||||
|
|
||||||
return nodeinfo, nil
|
return nodeInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseSSPluginNodeResponse parse the response for the given nodeinfor format
|
// ParseSSPluginNodeResponse parse the response for the given node info format
|
||||||
func (c *APIClient) ParseSSPluginNodeResponse(nodeInfoResponse *NodeInfoResponse) (*api.NodeInfo, error) {
|
func (c *APIClient) ParseSSPluginNodeResponse(nodeInfoResponse *NodeInfoResponse) (*api.NodeInfo, error) {
|
||||||
var enableTLS bool
|
var enableTLS bool
|
||||||
var path, host, TLStype, transportProtocol string
|
var path, host, transportProtocol string
|
||||||
var speedlimit uint64 = 0
|
var speedLimit uint64 = 0
|
||||||
|
|
||||||
serverConf := strings.Split(nodeInfoResponse.RawServerString, ";")
|
serverConf := strings.Split(nodeInfoResponse.RawServerString, ";")
|
||||||
parsedPort, err := strconv.ParseInt(serverConf[1], 10, 32)
|
parsedPort, err := strconv.ParseInt(serverConf[1], 10, 32)
|
||||||
@ -539,12 +563,7 @@ func (c *APIClient) ParseSSPluginNodeResponse(nodeInfoResponse *NodeInfoResponse
|
|||||||
// Compatible with more node types config
|
// Compatible with more node types config
|
||||||
for _, value := range serverConf[3:5] {
|
for _, value := range serverConf[3:5] {
|
||||||
switch value {
|
switch value {
|
||||||
case "tls", "xtls":
|
case "tls":
|
||||||
if c.EnableXTLS {
|
|
||||||
TLStype = "xtls"
|
|
||||||
} else {
|
|
||||||
TLStype = "tls"
|
|
||||||
}
|
|
||||||
enableTLS = true
|
enableTLS = true
|
||||||
case "ws":
|
case "ws":
|
||||||
transportProtocol = "ws"
|
transportProtocol = "ws"
|
||||||
@ -570,38 +589,32 @@ func (c *APIClient) ParseSSPluginNodeResponse(nodeInfoResponse *NodeInfoResponse
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if c.SpeedLimit > 0 {
|
if c.SpeedLimit > 0 {
|
||||||
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
|
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||||
} else {
|
} else {
|
||||||
speedlimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
|
speedLimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create GeneralNodeInfo
|
// Create GeneralNodeInfo
|
||||||
nodeinfo := &api.NodeInfo{
|
nodeInfo := &api.NodeInfo{
|
||||||
NodeType: c.NodeType,
|
NodeType: c.NodeType,
|
||||||
NodeID: c.NodeID,
|
NodeID: c.NodeID,
|
||||||
Port: port,
|
Port: port,
|
||||||
SpeedLimit: speedlimit,
|
SpeedLimit: speedLimit,
|
||||||
TransportProtocol: transportProtocol,
|
TransportProtocol: transportProtocol,
|
||||||
EnableTLS: enableTLS,
|
EnableTLS: enableTLS,
|
||||||
TLSType: TLStype,
|
|
||||||
Path: path,
|
Path: path,
|
||||||
Host: host,
|
Host: host,
|
||||||
}
|
}
|
||||||
|
|
||||||
return nodeinfo, nil
|
return nodeInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseTrojanNodeResponse parse the response for the given nodeinfor format
|
// ParseTrojanNodeResponse parse the response for the given node info format
|
||||||
func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *NodeInfoResponse) (*api.NodeInfo, error) {
|
func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *NodeInfoResponse) (*api.NodeInfo, error) {
|
||||||
// 域名或IP;port=连接端口#偏移端口|host=xx
|
// 域名或IP;port=连接端口#偏移端口|host=xx
|
||||||
// gz.aaa.com;port=443#12345|host=hk.aaa.com
|
// gz.aaa.com;port=443#12345|host=hk.aaa.com
|
||||||
var p, TLSType, host, outsidePort, insidePort, transportProtocol, serviceName string
|
var p, host, outsidePort, insidePort, transportProtocol, serviceName string
|
||||||
var speedlimit uint64 = 0
|
var speedLimit uint64 = 0
|
||||||
if c.EnableXTLS {
|
|
||||||
TLSType = "xtls"
|
|
||||||
} else {
|
|
||||||
TLSType = "tls"
|
|
||||||
}
|
|
||||||
|
|
||||||
if nodeInfoResponse.RawServerString == "" {
|
if nodeInfoResponse.RawServerString == "" {
|
||||||
return nil, fmt.Errorf("no server info in response")
|
return nil, fmt.Errorf("no server info in response")
|
||||||
@ -648,27 +661,26 @@ func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *NodeInfoResponse)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if c.SpeedLimit > 0 {
|
if c.SpeedLimit > 0 {
|
||||||
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
|
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||||
} else {
|
} else {
|
||||||
speedlimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
|
speedLimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
|
||||||
}
|
}
|
||||||
// Create GeneralNodeInfo
|
// Create GeneralNodeInfo
|
||||||
nodeinfo := &api.NodeInfo{
|
nodeInfo := &api.NodeInfo{
|
||||||
NodeType: c.NodeType,
|
NodeType: c.NodeType,
|
||||||
NodeID: c.NodeID,
|
NodeID: c.NodeID,
|
||||||
Port: port,
|
Port: port,
|
||||||
SpeedLimit: speedlimit,
|
SpeedLimit: speedLimit,
|
||||||
TransportProtocol: transportProtocol,
|
TransportProtocol: transportProtocol,
|
||||||
EnableTLS: true,
|
EnableTLS: true,
|
||||||
TLSType: TLSType,
|
|
||||||
Host: host,
|
Host: host,
|
||||||
ServiceName: serviceName,
|
ServiceName: serviceName,
|
||||||
}
|
}
|
||||||
|
|
||||||
return nodeinfo, nil
|
return nodeInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseUserListResponse parse the response for the given nodeinfo format
|
// ParseUserListResponse parse the response for the given node info format
|
||||||
func (c *APIClient) ParseUserListResponse(userInfoResponse *[]UserResponse) (*[]api.UserInfo, error) {
|
func (c *APIClient) ParseUserListResponse(userInfoResponse *[]UserResponse) (*[]api.UserInfo, error) {
|
||||||
c.access.Lock()
|
c.access.Lock()
|
||||||
// Clear Last report log
|
// Clear Last report log
|
||||||
@ -677,8 +689,8 @@ func (c *APIClient) ParseUserListResponse(userInfoResponse *[]UserResponse) (*[]
|
|||||||
c.access.Unlock()
|
c.access.Unlock()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
var deviceLimit, localDeviceLimit int = 0, 0
|
var deviceLimit, localDeviceLimit = 0, 0
|
||||||
var speedlimit uint64 = 0
|
var speedLimit uint64 = 0
|
||||||
var userList []api.UserInfo
|
var userList []api.UserInfo
|
||||||
for _, user := range *userInfoResponse {
|
for _, user := range *userInfoResponse {
|
||||||
if c.DeviceLimit > 0 {
|
if c.DeviceLimit > 0 {
|
||||||
@ -706,111 +718,142 @@ func (c *APIClient) ParseUserListResponse(userInfoResponse *[]UserResponse) (*[]
|
|||||||
}
|
}
|
||||||
|
|
||||||
if c.SpeedLimit > 0 {
|
if c.SpeedLimit > 0 {
|
||||||
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
|
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||||
} else {
|
} else {
|
||||||
speedlimit = uint64((user.SpeedLimit * 1000000) / 8)
|
speedLimit = uint64((user.SpeedLimit * 1000000) / 8)
|
||||||
}
|
}
|
||||||
userList = append(userList, api.UserInfo{
|
userList = append(userList, api.UserInfo{
|
||||||
UID: user.ID,
|
UID: user.ID,
|
||||||
Email: user.Email,
|
UUID: user.UUID,
|
||||||
UUID: user.UUID,
|
Passwd: user.Passwd,
|
||||||
Passwd: user.Passwd,
|
SpeedLimit: speedLimit,
|
||||||
SpeedLimit: speedlimit,
|
DeviceLimit: deviceLimit,
|
||||||
DeviceLimit: deviceLimit,
|
Port: user.Port,
|
||||||
Port: user.Port,
|
Method: user.Method,
|
||||||
Method: user.Method,
|
|
||||||
Protocol: user.Protocol,
|
|
||||||
ProtocolParam: user.ProtocolParam,
|
|
||||||
Obfs: user.Obfs,
|
|
||||||
ObfsParam: user.ObfsParam,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return &userList, nil
|
return &userList, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseSSPanelNodeInfo parse the response for the given nodeinfor format
|
// ParseSSPanelNodeInfo parse the response for the given node info format
|
||||||
// Only used for SSPanel version >= 2021.11
|
// Only available for SSPanel version >= 2021.11
|
||||||
func (c *APIClient) ParseSSPanelNodeInfo(nodeInfoResponse *NodeInfoResponse) (*api.NodeInfo, error) {
|
func (c *APIClient) ParseSSPanelNodeInfo(nodeInfoResponse *NodeInfoResponse) (*api.NodeInfo, error) {
|
||||||
|
var (
|
||||||
|
speedLimit uint64 = 0
|
||||||
|
enableTLS, enableVless bool
|
||||||
|
alterID uint16 = 0
|
||||||
|
transportProtocol string
|
||||||
|
)
|
||||||
|
|
||||||
var speedlimit uint64 = 0
|
// Check if custom_config is null
|
||||||
var EnableTLS, EnableVless bool
|
if len(nodeInfoResponse.CustomConfig) == 0 {
|
||||||
var AlterID uint16 = 0
|
return nil, errors.New("custom_config is empty, disable custom config")
|
||||||
var TLSType, transportProtocol string
|
}
|
||||||
|
|
||||||
nodeConfig := new(CustomConfig)
|
nodeConfig := new(CustomConfig)
|
||||||
json.Unmarshal(nodeInfoResponse.CustomConfig, nodeConfig)
|
err := json.Unmarshal(nodeInfoResponse.CustomConfig, nodeConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("custom_config format error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
if c.SpeedLimit > 0 {
|
if c.SpeedLimit > 0 {
|
||||||
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
|
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||||
} else {
|
} else {
|
||||||
speedlimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
|
speedLimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
|
||||||
}
|
}
|
||||||
|
|
||||||
parsedPort, err := strconv.ParseInt(nodeConfig.OffsetPortNode, 10, 32)
|
parsedPort, err := strconv.ParseInt(nodeConfig.OffsetPortNode, 10, 32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
port := uint32(parsedPort)
|
port := uint32(parsedPort)
|
||||||
|
|
||||||
if c.NodeType == "Shadowsocks" {
|
switch c.NodeType {
|
||||||
|
case "Shadowsocks":
|
||||||
transportProtocol = "tcp"
|
transportProtocol = "tcp"
|
||||||
}
|
case "V2ray":
|
||||||
|
|
||||||
if c.NodeType == "V2ray" {
|
|
||||||
transportProtocol = nodeConfig.Network
|
transportProtocol = nodeConfig.Network
|
||||||
TLSType = nodeConfig.Security
|
|
||||||
if parsedAlterID, err := strconv.ParseInt(nodeConfig.AlterID, 10, 16); err != nil {
|
tlsType := nodeConfig.Security
|
||||||
return nil, err
|
if tlsType == "tls" || tlsType == "xtls" {
|
||||||
} else {
|
enableTLS = true
|
||||||
AlterID = uint16(parsedAlterID)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if TLSType == "tls" || TLSType == "xtls" {
|
|
||||||
EnableTLS = true
|
|
||||||
}
|
|
||||||
if nodeConfig.EnableVless == "1" {
|
if nodeConfig.EnableVless == "1" {
|
||||||
EnableVless = true
|
enableVless = true
|
||||||
}
|
}
|
||||||
}
|
case "Trojan":
|
||||||
|
enableTLS = true
|
||||||
if c.NodeType == "Trojan" {
|
|
||||||
EnableTLS = true
|
|
||||||
TLSType = "tls"
|
|
||||||
transportProtocol = "tcp"
|
transportProtocol = "tcp"
|
||||||
|
|
||||||
// Select security type
|
|
||||||
if nodeConfig.EnableXtls == "1" {
|
|
||||||
TLSType = "xtls"
|
|
||||||
} else if nodeConfig.Security != "" {
|
|
||||||
TLSType = nodeConfig.Security // try to read security from config
|
|
||||||
}
|
|
||||||
|
|
||||||
// Select transport protocol
|
// Select transport protocol
|
||||||
if nodeConfig.Grpc == "1" {
|
if nodeConfig.Network != "" {
|
||||||
transportProtocol = "grpc"
|
|
||||||
} else if nodeConfig.Network != "" {
|
|
||||||
transportProtocol = nodeConfig.Network // try to read transport protocol from config
|
transportProtocol = nodeConfig.Network // try to read transport protocol from config
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parse reality config
|
||||||
|
realityConfig := new(api.REALITYConfig)
|
||||||
|
if nodeConfig.RealityOpts != nil {
|
||||||
|
r := nodeConfig.RealityOpts
|
||||||
|
realityConfig = &api.REALITYConfig{
|
||||||
|
Dest: r.Dest,
|
||||||
|
ProxyProtocolVer: r.ProxyProtocolVer,
|
||||||
|
ServerNames: r.ServerNames,
|
||||||
|
PrivateKey: r.PrivateKey,
|
||||||
|
MinClientVer: r.MinClientVer,
|
||||||
|
MaxClientVer: r.MaxClientVer,
|
||||||
|
MaxTimeDiff: r.MaxTimeDiff,
|
||||||
|
ShortIds: r.ShortIds,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Create GeneralNodeInfo
|
// Create GeneralNodeInfo
|
||||||
nodeinfo := &api.NodeInfo{
|
nodeInfo := &api.NodeInfo{
|
||||||
NodeType: c.NodeType,
|
NodeType: c.NodeType,
|
||||||
NodeID: c.NodeID,
|
NodeID: c.NodeID,
|
||||||
Port: port,
|
Port: port,
|
||||||
SpeedLimit: speedlimit,
|
SpeedLimit: speedLimit,
|
||||||
AlterID: AlterID,
|
AlterID: alterID,
|
||||||
TransportProtocol: transportProtocol,
|
TransportProtocol: transportProtocol,
|
||||||
Host: nodeConfig.Host,
|
Host: nodeConfig.Host,
|
||||||
Path: nodeConfig.Path,
|
Path: nodeConfig.Path,
|
||||||
EnableTLS: EnableTLS,
|
EnableTLS: enableTLS,
|
||||||
TLSType: TLSType,
|
EnableVless: enableVless,
|
||||||
EnableVless: EnableVless,
|
VlessFlow: nodeConfig.Flow,
|
||||||
CypherMethod: nodeConfig.MuEncryption,
|
CypherMethod: nodeConfig.Method,
|
||||||
ServiceName: nodeConfig.Servicename,
|
ServiceName: nodeConfig.Servicename,
|
||||||
Header: nodeConfig.Header,
|
Header: nodeConfig.Header,
|
||||||
|
EnableREALITY: nodeConfig.EnableREALITY,
|
||||||
|
REALITYConfig: realityConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
return nodeinfo, nil
|
return nodeInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// compareVersion, version1 > version2 return 1, version1 < version2 return -1, 0 means equal
|
||||||
|
func compareVersion(version1, version2 string) int {
|
||||||
|
n, m := len(version1), len(version2)
|
||||||
|
i, j := 0, 0
|
||||||
|
for i < n || j < m {
|
||||||
|
x := 0
|
||||||
|
for ; i < n && version1[i] != '.'; i++ {
|
||||||
|
x = x*10 + int(version1[i]-'0')
|
||||||
|
}
|
||||||
|
i++ // jump dot
|
||||||
|
y := 0
|
||||||
|
for ; j < m && version2[j] != '.'; j++ {
|
||||||
|
y = y*10 + int(version2[j]-'0')
|
||||||
|
}
|
||||||
|
j++ // jump dot
|
||||||
|
if x > y {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if x < y {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ func CreateClient() api.API {
|
|||||||
return client
|
return client
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetV2rayNodeinfo(t *testing.T) {
|
func TestGetV2rayNodeInfo(t *testing.T) {
|
||||||
client := CreateClient()
|
client := CreateClient()
|
||||||
|
|
||||||
nodeInfo, err := client.GetNodeInfo()
|
nodeInfo, err := client.GetNodeInfo()
|
||||||
@ -29,7 +29,7 @@ func TestGetV2rayNodeinfo(t *testing.T) {
|
|||||||
t.Log(nodeInfo)
|
t.Log(nodeInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetSSNodeinfo(t *testing.T) {
|
func TestGetSSNodeInfo(t *testing.T) {
|
||||||
apiConfig := &api.Config{
|
apiConfig := &api.Config{
|
||||||
APIHost: "http://127.0.0.1:667",
|
APIHost: "http://127.0.0.1:667",
|
||||||
Key: "123",
|
Key: "123",
|
||||||
@ -44,7 +44,7 @@ func TestGetSSNodeinfo(t *testing.T) {
|
|||||||
t.Log(nodeInfo)
|
t.Log(nodeInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetTrojanNodeinfo(t *testing.T) {
|
func TestGetTrojanNodeInfo(t *testing.T) {
|
||||||
apiConfig := &api.Config{
|
apiConfig := &api.Config{
|
||||||
APIHost: "http://127.0.0.1:667",
|
APIHost: "http://127.0.0.1:667",
|
||||||
Key: "123",
|
Key: "123",
|
||||||
@ -59,7 +59,7 @@ func TestGetTrojanNodeinfo(t *testing.T) {
|
|||||||
t.Log(nodeInfo)
|
t.Log(nodeInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetSSinfo(t *testing.T) {
|
func TestGetSSInfo(t *testing.T) {
|
||||||
client := CreateClient()
|
client := CreateClient()
|
||||||
|
|
||||||
nodeInfo, err := client.GetNodeInfo()
|
nodeInfo, err := client.GetNodeInfo()
|
||||||
@ -148,8 +148,8 @@ func TestReportIllegal(t *testing.T) {
|
|||||||
client := CreateClient()
|
client := CreateClient()
|
||||||
|
|
||||||
detectResult := []api.DetectResult{
|
detectResult := []api.DetectResult{
|
||||||
{1, 2},
|
{UID: 1, RuleID: 2},
|
||||||
{1, 3},
|
{UID: 1, RuleID: 3},
|
||||||
}
|
}
|
||||||
client.Debug()
|
client.Debug()
|
||||||
err := client.ReportIllegal(&detectResult)
|
err := client.ReportIllegal(&detectResult)
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
// Deprecated: after 2023.6.1
|
|
||||||
package v2board
|
|
||||||
|
|
||||||
type UserTraffic struct {
|
|
||||||
UID int `json:"user_id"`
|
|
||||||
Upload int64 `json:"u"`
|
|
||||||
Download int64 `json:"d"`
|
|
||||||
}
|
|
@ -1,425 +0,0 @@
|
|||||||
package v2board
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/bitly/go-simplejson"
|
|
||||||
"github.com/go-resty/resty/v2"
|
|
||||||
|
|
||||||
"github.com/XrayR-project/XrayR/api"
|
|
||||||
)
|
|
||||||
|
|
||||||
// APIClient create an api client to the panel.
|
|
||||||
type APIClient struct {
|
|
||||||
client *resty.Client
|
|
||||||
APIHost string
|
|
||||||
NodeID int
|
|
||||||
Key string
|
|
||||||
NodeType string
|
|
||||||
EnableVless bool
|
|
||||||
EnableXTLS bool
|
|
||||||
SpeedLimit float64
|
|
||||||
DeviceLimit int
|
|
||||||
LocalRuleList []api.DetectRule
|
|
||||||
ConfigResp *simplejson.Json
|
|
||||||
access sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
// New create an api instance
|
|
||||||
func New(apiConfig *api.Config) *APIClient {
|
|
||||||
|
|
||||||
client := resty.New()
|
|
||||||
client.SetRetryCount(3)
|
|
||||||
if apiConfig.Timeout > 0 {
|
|
||||||
client.SetTimeout(time.Duration(apiConfig.Timeout) * time.Second)
|
|
||||||
} else {
|
|
||||||
client.SetTimeout(5 * time.Second)
|
|
||||||
}
|
|
||||||
client.OnError(func(req *resty.Request, err error) {
|
|
||||||
if v, ok := err.(*resty.ResponseError); ok {
|
|
||||||
// v.Response contains the last response from the server
|
|
||||||
// v.Err contains the original error
|
|
||||||
log.Print(v.Err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
client.SetBaseURL(apiConfig.APIHost)
|
|
||||||
// Create Key for each requests
|
|
||||||
client.SetQueryParams(map[string]string{
|
|
||||||
"node_id": strconv.Itoa(apiConfig.NodeID),
|
|
||||||
"token": apiConfig.Key,
|
|
||||||
})
|
|
||||||
// Read local rule list
|
|
||||||
localRuleList := readLocalRuleList(apiConfig.RuleListPath)
|
|
||||||
apiClient := &APIClient{
|
|
||||||
client: client,
|
|
||||||
NodeID: apiConfig.NodeID,
|
|
||||||
Key: apiConfig.Key,
|
|
||||||
APIHost: apiConfig.APIHost,
|
|
||||||
NodeType: apiConfig.NodeType,
|
|
||||||
EnableVless: apiConfig.EnableVless,
|
|
||||||
EnableXTLS: apiConfig.EnableXTLS,
|
|
||||||
SpeedLimit: apiConfig.SpeedLimit,
|
|
||||||
DeviceLimit: apiConfig.DeviceLimit,
|
|
||||||
LocalRuleList: localRuleList,
|
|
||||||
}
|
|
||||||
return apiClient
|
|
||||||
}
|
|
||||||
|
|
||||||
// readLocalRuleList reads the local rule list file
|
|
||||||
func readLocalRuleList(path string) (LocalRuleList []api.DetectRule) {
|
|
||||||
|
|
||||||
LocalRuleList = make([]api.DetectRule, 0)
|
|
||||||
if path != "" {
|
|
||||||
// open the file
|
|
||||||
file, err := os.Open(path)
|
|
||||||
|
|
||||||
// handle errors while opening
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error when opening file: %s", err)
|
|
||||||
return LocalRuleList
|
|
||||||
}
|
|
||||||
|
|
||||||
fileScanner := bufio.NewScanner(file)
|
|
||||||
|
|
||||||
// read line by line
|
|
||||||
for fileScanner.Scan() {
|
|
||||||
LocalRuleList = append(LocalRuleList, api.DetectRule{
|
|
||||||
ID: -1,
|
|
||||||
Pattern: regexp.MustCompile(fileScanner.Text()),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// handle first encountered error while reading
|
|
||||||
if err := fileScanner.Err(); err != nil {
|
|
||||||
log.Fatalf("Error while reading file: %s", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
file.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
return LocalRuleList
|
|
||||||
}
|
|
||||||
|
|
||||||
// Describe return a description of the client
|
|
||||||
func (c *APIClient) Describe() api.ClientInfo {
|
|
||||||
return api.ClientInfo{APIHost: c.APIHost, NodeID: c.NodeID, Key: c.Key, NodeType: c.NodeType}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debug set the client debug for client
|
|
||||||
func (c *APIClient) Debug() {
|
|
||||||
c.client.SetDebug(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *APIClient) assembleURL(path string) string {
|
|
||||||
return c.APIHost + path
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *APIClient) parseResponse(res *resty.Response, path string, err error) (*simplejson.Json, error) {
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("request %s failed: %s", c.assembleURL(path), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if res.StatusCode() > 400 {
|
|
||||||
body := res.Body()
|
|
||||||
return nil, fmt.Errorf("request %s failed: %s, %s", c.assembleURL(path), string(body), err)
|
|
||||||
}
|
|
||||||
rtn, err := simplejson.NewJson(res.Body())
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("ret %s invalid", res.String())
|
|
||||||
}
|
|
||||||
return rtn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetNodeInfo will pull NodeInfo Config from sspanel
|
|
||||||
func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
|
||||||
var path string
|
|
||||||
switch c.NodeType {
|
|
||||||
case "V2ray":
|
|
||||||
path = "/api/v1/server/Deepbwork/config"
|
|
||||||
case "Trojan":
|
|
||||||
path = "/api/v1/server/TrojanTidalab/config"
|
|
||||||
case "Shadowsocks":
|
|
||||||
if nodeInfo, err = c.ParseSSNodeResponse(); err == nil {
|
|
||||||
return nodeInfo, nil
|
|
||||||
} else {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
|
|
||||||
}
|
|
||||||
res, err := c.client.R().
|
|
||||||
SetQueryParam("local_port", "1").
|
|
||||||
ForceContentType("application/json").
|
|
||||||
Get(path)
|
|
||||||
|
|
||||||
response, err := c.parseResponse(res, path, err)
|
|
||||||
c.access.Lock()
|
|
||||||
defer c.access.Unlock()
|
|
||||||
c.ConfigResp = response
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch c.NodeType {
|
|
||||||
case "V2ray":
|
|
||||||
nodeInfo, err = c.ParseV2rayNodeResponse(response)
|
|
||||||
case "Trojan":
|
|
||||||
nodeInfo, err = c.ParseTrojanNodeResponse(response)
|
|
||||||
case "Shadowsocks":
|
|
||||||
nodeInfo, err = c.ParseSSNodeResponse()
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
res, _ := response.MarshalJSON()
|
|
||||||
return nil, fmt.Errorf("Parse node info failed: %s, \nError: %s", string(res), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nodeInfo, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetUserList will pull user form sspanel
|
|
||||||
func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
|
|
||||||
var path string
|
|
||||||
switch c.NodeType {
|
|
||||||
case "V2ray":
|
|
||||||
path = "/api/v1/server/Deepbwork/user"
|
|
||||||
case "Trojan":
|
|
||||||
path = "/api/v1/server/TrojanTidalab/user"
|
|
||||||
case "Shadowsocks":
|
|
||||||
path = "/api/v1/server/ShadowsocksTidalab/user"
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
|
|
||||||
}
|
|
||||||
res, err := c.client.R().
|
|
||||||
ForceContentType("application/json").
|
|
||||||
Get(path)
|
|
||||||
|
|
||||||
response, err := c.parseResponse(res, path, err)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
numOfUsers := len(response.Get("data").MustArray())
|
|
||||||
userList := make([]api.UserInfo, numOfUsers)
|
|
||||||
for i := 0; i < numOfUsers; i++ {
|
|
||||||
user := api.UserInfo{}
|
|
||||||
user.UID = response.Get("data").GetIndex(i).Get("id").MustInt()
|
|
||||||
user.SpeedLimit = uint64(c.SpeedLimit * 1000000 / 8)
|
|
||||||
user.DeviceLimit = c.DeviceLimit
|
|
||||||
switch c.NodeType {
|
|
||||||
case "Shadowsocks":
|
|
||||||
user.Email = response.Get("data").GetIndex(i).Get("secret").MustString()
|
|
||||||
user.Passwd = response.Get("data").GetIndex(i).Get("secret").MustString()
|
|
||||||
user.Method = response.Get("data").GetIndex(i).Get("cipher").MustString()
|
|
||||||
user.Port = uint32(response.Get("data").GetIndex(i).Get("port").MustUint64())
|
|
||||||
case "Trojan":
|
|
||||||
user.UUID = response.Get("data").GetIndex(i).Get("trojan_user").Get("password").MustString()
|
|
||||||
user.Email = response.Get("data").GetIndex(i).Get("trojan_user").Get("password").MustString()
|
|
||||||
case "V2ray":
|
|
||||||
user.UUID = response.Get("data").GetIndex(i).Get("v2ray_user").Get("uuid").MustString()
|
|
||||||
user.Email = response.Get("data").GetIndex(i).Get("v2ray_user").Get("email").MustString()
|
|
||||||
user.AlterID = uint16(response.Get("data").GetIndex(i).Get("v2ray_user").Get("alter_id").MustUint64())
|
|
||||||
}
|
|
||||||
userList[i] = user
|
|
||||||
}
|
|
||||||
return &userList, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReportUserTraffic reports the user traffic
|
|
||||||
func (c *APIClient) ReportUserTraffic(userTraffic *[]api.UserTraffic) error {
|
|
||||||
var path string
|
|
||||||
switch c.NodeType {
|
|
||||||
case "V2ray":
|
|
||||||
path = "/api/v1/server/Deepbwork/submit"
|
|
||||||
case "Trojan":
|
|
||||||
path = "/api/v1/server/TrojanTidalab/submit"
|
|
||||||
case "Shadowsocks":
|
|
||||||
path = "/api/v1/server/ShadowsocksTidalab/submit"
|
|
||||||
}
|
|
||||||
|
|
||||||
data := make([]UserTraffic, len(*userTraffic))
|
|
||||||
for i, traffic := range *userTraffic {
|
|
||||||
data[i] = UserTraffic{
|
|
||||||
UID: traffic.UID,
|
|
||||||
Upload: traffic.Upload,
|
|
||||||
Download: traffic.Download}
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := c.client.R().
|
|
||||||
SetQueryParam("node_id", strconv.Itoa(c.NodeID)).
|
|
||||||
SetBody(data).
|
|
||||||
ForceContentType("application/json").
|
|
||||||
Post(path)
|
|
||||||
_, err = c.parseResponse(res, path, err)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetNodeRule implements the API interface
|
|
||||||
func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
|
|
||||||
ruleList := c.LocalRuleList
|
|
||||||
if c.NodeType != "V2ray" {
|
|
||||||
return &ruleList, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// V2board only support the rule for v2ray
|
|
||||||
// fix: reuse config response
|
|
||||||
c.access.Lock()
|
|
||||||
defer c.access.Unlock()
|
|
||||||
ruleListResponse := c.ConfigResp.Get("routing").Get("rules").GetIndex(1).Get("domain").MustStringArray()
|
|
||||||
for i, rule := range ruleListResponse {
|
|
||||||
rule = strings.TrimPrefix(rule, "regexp:")
|
|
||||||
ruleListItem := api.DetectRule{
|
|
||||||
ID: i,
|
|
||||||
Pattern: regexp.MustCompile(rule),
|
|
||||||
}
|
|
||||||
ruleList = append(ruleList, ruleListItem)
|
|
||||||
}
|
|
||||||
return &ruleList, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReportNodeStatus implements the API interface
|
|
||||||
func (c *APIClient) ReportNodeStatus(nodeStatus *api.NodeStatus) (err error) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReportNodeOnlineUsers implements the API interface
|
|
||||||
func (c *APIClient) ReportNodeOnlineUsers(onlineUserList *[]api.OnlineUser) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReportIllegal implements the API interface
|
|
||||||
func (c *APIClient) ReportIllegal(detectResultList *[]api.DetectResult) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseTrojanNodeResponse parse the response for the given nodeinfor format
|
|
||||||
func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *simplejson.Json) (*api.NodeInfo, error) {
|
|
||||||
var TLSType = "tls"
|
|
||||||
if c.EnableXTLS {
|
|
||||||
TLSType = "xtls"
|
|
||||||
}
|
|
||||||
port := uint32(nodeInfoResponse.Get("local_port").MustUint64())
|
|
||||||
host := nodeInfoResponse.Get("ssl").Get("sni").MustString()
|
|
||||||
|
|
||||||
// Create GeneralNodeInfo
|
|
||||||
nodeinfo := &api.NodeInfo{
|
|
||||||
NodeType: c.NodeType,
|
|
||||||
NodeID: c.NodeID,
|
|
||||||
Port: port,
|
|
||||||
TransportProtocol: "tcp",
|
|
||||||
EnableTLS: true,
|
|
||||||
TLSType: TLSType,
|
|
||||||
Host: host,
|
|
||||||
}
|
|
||||||
return nodeinfo, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseSSNodeResponse parse the response for the given nodeinfor format
|
|
||||||
func (c *APIClient) ParseSSNodeResponse() (*api.NodeInfo, error) {
|
|
||||||
var port uint32
|
|
||||||
var method string
|
|
||||||
userInfo, err := c.GetUserList()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(*userInfo) > 0 {
|
|
||||||
port = (*userInfo)[0].Port
|
|
||||||
method = (*userInfo)[0].Method
|
|
||||||
} else {
|
|
||||||
return nil, errors.New("the number of node users is 0")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create GeneralNodeInfo
|
|
||||||
nodeinfo := &api.NodeInfo{
|
|
||||||
NodeType: c.NodeType,
|
|
||||||
NodeID: c.NodeID,
|
|
||||||
Port: port,
|
|
||||||
TransportProtocol: "tcp",
|
|
||||||
CypherMethod: method,
|
|
||||||
}
|
|
||||||
|
|
||||||
return nodeinfo, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseV2rayNodeResponse parse the response for the given nodeinfor format
|
|
||||||
func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *simplejson.Json) (*api.NodeInfo, error) {
|
|
||||||
var TLSType string = "tls"
|
|
||||||
var path, host, serviceName string
|
|
||||||
var header json.RawMessage
|
|
||||||
var enableTLS bool
|
|
||||||
var alterID uint16 = 0
|
|
||||||
if c.EnableXTLS {
|
|
||||||
TLSType = "xtls"
|
|
||||||
}
|
|
||||||
|
|
||||||
inboundInfo := simplejson.New()
|
|
||||||
if tmpInboundInfo, ok := nodeInfoResponse.CheckGet("inbound"); ok {
|
|
||||||
inboundInfo = tmpInboundInfo
|
|
||||||
// Compatible with v2board 1.5.5-dev
|
|
||||||
} else if tmpInboundInfo, ok := nodeInfoResponse.CheckGet("inbounds"); ok {
|
|
||||||
tmpInboundInfo := tmpInboundInfo.MustArray()
|
|
||||||
marshalByte, _ := json.Marshal(tmpInboundInfo[0].(map[string]interface{}))
|
|
||||||
inboundInfo, _ = simplejson.NewJson(marshalByte)
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("unable to find inbound(s) in the nodeInfo")
|
|
||||||
}
|
|
||||||
|
|
||||||
port := uint32(inboundInfo.Get("port").MustUint64())
|
|
||||||
transportProtocol := inboundInfo.Get("streamSettings").Get("network").MustString()
|
|
||||||
|
|
||||||
switch transportProtocol {
|
|
||||||
case "ws":
|
|
||||||
path = inboundInfo.Get("streamSettings").Get("wsSettings").Get("path").MustString()
|
|
||||||
host = inboundInfo.Get("streamSettings").Get("wsSettings").Get("headers").Get("Host").MustString()
|
|
||||||
case "grpc":
|
|
||||||
if data, ok := inboundInfo.Get("streamSettings").Get("grpcSettings").CheckGet("serviceName"); ok {
|
|
||||||
serviceName = data.MustString()
|
|
||||||
}
|
|
||||||
case "tcp":
|
|
||||||
if data, ok := inboundInfo.Get("streamSettings").Get("tcpSettings").CheckGet("header"); ok {
|
|
||||||
if httpHeader, err := data.MarshalJSON(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else {
|
|
||||||
header = httpHeader
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
if inboundInfo.Get("streamSettings").Get("security").MustString() == "tls" {
|
|
||||||
enableTLS = true
|
|
||||||
} else {
|
|
||||||
enableTLS = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create GeneralNodeInfo
|
|
||||||
// AlterID will be updated after next sync
|
|
||||||
nodeInfo := &api.NodeInfo{
|
|
||||||
NodeType: c.NodeType,
|
|
||||||
NodeID: c.NodeID,
|
|
||||||
Port: port,
|
|
||||||
AlterID: alterID,
|
|
||||||
TransportProtocol: transportProtocol,
|
|
||||||
EnableTLS: enableTLS,
|
|
||||||
TLSType: TLSType,
|
|
||||||
Path: path,
|
|
||||||
Host: host,
|
|
||||||
EnableVless: c.EnableVless,
|
|
||||||
ServiceName: serviceName,
|
|
||||||
Header: header,
|
|
||||||
}
|
|
||||||
return nodeInfo, nil
|
|
||||||
}
|
|
@ -1,7 +1,35 @@
|
|||||||
package v2raysocks
|
package v2raysocks
|
||||||
|
|
||||||
type UserTraffic struct {
|
type UserTraffic struct {
|
||||||
UID int `json:"user_id"`
|
UID int `json:"uid"`
|
||||||
Upload int64 `json:"u"`
|
Upload int64 `json:"u"`
|
||||||
Download int64 `json:"d"`
|
Download int64 `json:"d"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type NodeStatus struct {
|
||||||
|
CPU string `json:"cpu"`
|
||||||
|
Mem string `json:"mem"`
|
||||||
|
Net string `json:"net"`
|
||||||
|
Disk string `json:"disk"`
|
||||||
|
Uptime int `json:"uptime"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type NodeOnline struct {
|
||||||
|
UID int `json:"uid"`
|
||||||
|
IP string `json:"ip"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type IllegalItem struct {
|
||||||
|
UID int `json:"uid"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type REALITYConfig struct {
|
||||||
|
Dest string `json:"dest,omitempty"`
|
||||||
|
ProxyProtocolVer uint64 `json:"proxy_protocol_ver,omitempty"`
|
||||||
|
ServerNames []string `json:"server_names,omitempty"`
|
||||||
|
PrivateKey string `json:"private_key,omitempty"`
|
||||||
|
MinClientVer string `json:"min_client_ver,omitempty"`
|
||||||
|
MaxClientVer string `json:"max_client_ver,omitempty"`
|
||||||
|
MaxTimeDiff uint64 `json:"max_time_diff,omitempty"`
|
||||||
|
ShortIds []string `json:"short_ids,omitempty"`
|
||||||
|
}
|
@ -3,8 +3,8 @@ package v2raysocks
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -12,6 +12,8 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/bitly/go-simplejson"
|
"github.com/bitly/go-simplejson"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
|
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
|
||||||
@ -28,12 +30,13 @@ type APIClient struct {
|
|||||||
Key string
|
Key string
|
||||||
NodeType string
|
NodeType string
|
||||||
EnableVless bool
|
EnableVless bool
|
||||||
EnableXTLS bool
|
VlessFlow string
|
||||||
SpeedLimit float64
|
SpeedLimit float64
|
||||||
DeviceLimit int
|
DeviceLimit int
|
||||||
LocalRuleList []api.DetectRule
|
LocalRuleList []api.DetectRule
|
||||||
ConfigResp *simplejson.Json
|
ConfigResp *simplejson.Json
|
||||||
access sync.Mutex
|
access sync.Mutex
|
||||||
|
eTags map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
// New create an api instance
|
// New create an api instance
|
||||||
@ -46,13 +49,16 @@ func New(apiConfig *api.Config) *APIClient {
|
|||||||
} else {
|
} else {
|
||||||
client.SetTimeout(5 * time.Second)
|
client.SetTimeout(5 * time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
client.OnError(func(req *resty.Request, err error) {
|
client.OnError(func(req *resty.Request, err error) {
|
||||||
if v, ok := err.(*resty.ResponseError); ok {
|
var v *resty.ResponseError
|
||||||
|
if errors.As(err, &v) {
|
||||||
// v.Response contains the last response from the server
|
// v.Response contains the last response from the server
|
||||||
// v.Err contains the original error
|
// v.Err contains the original error
|
||||||
log.Print(v.Err)
|
log.Print(v.Err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Create Key for each requests
|
// Create Key for each requests
|
||||||
client.SetQueryParams(map[string]string{
|
client.SetQueryParams(map[string]string{
|
||||||
"node_id": strconv.Itoa(apiConfig.NodeID),
|
"node_id": strconv.Itoa(apiConfig.NodeID),
|
||||||
@ -67,10 +73,11 @@ func New(apiConfig *api.Config) *APIClient {
|
|||||||
APIHost: apiConfig.APIHost,
|
APIHost: apiConfig.APIHost,
|
||||||
NodeType: apiConfig.NodeType,
|
NodeType: apiConfig.NodeType,
|
||||||
EnableVless: apiConfig.EnableVless,
|
EnableVless: apiConfig.EnableVless,
|
||||||
EnableXTLS: apiConfig.EnableXTLS,
|
VlessFlow: apiConfig.VlessFlow,
|
||||||
SpeedLimit: apiConfig.SpeedLimit,
|
SpeedLimit: apiConfig.SpeedLimit,
|
||||||
DeviceLimit: apiConfig.DeviceLimit,
|
DeviceLimit: apiConfig.DeviceLimit,
|
||||||
LocalRuleList: localRuleList,
|
LocalRuleList: localRuleList,
|
||||||
|
eTags: make(map[string]string),
|
||||||
}
|
}
|
||||||
return apiClient
|
return apiClient
|
||||||
}
|
}
|
||||||
@ -144,19 +151,29 @@ func (c *APIClient) parseResponse(res *resty.Response, path string, err error) (
|
|||||||
func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
||||||
var nodeType string
|
var nodeType string
|
||||||
switch c.NodeType {
|
switch c.NodeType {
|
||||||
case "V2ray", "Trojan", "Shadowsocks":
|
case "V2ray", "Vmess", "Vless", "Trojan", "Shadowsocks":
|
||||||
nodeType = strings.ToLower(c.NodeType)
|
nodeType = strings.ToLower(c.NodeType)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
|
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
|
||||||
}
|
}
|
||||||
res, err := c.client.R().
|
res, err := c.client.R().
|
||||||
|
SetHeader("If-None-Match", c.eTags["config"]).
|
||||||
SetQueryParams(map[string]string{
|
SetQueryParams(map[string]string{
|
||||||
"act": "config",
|
"act": "config",
|
||||||
"nodetype": nodeType,
|
"node_type": nodeType,
|
||||||
}).
|
}).
|
||||||
ForceContentType("application/json").
|
ForceContentType("application/json").
|
||||||
Get(c.APIHost)
|
Get(c.APIHost)
|
||||||
|
|
||||||
|
// Etag identifier for a specific version of a resource. StatusCode = 304 means no changed
|
||||||
|
if res.StatusCode() == 304 {
|
||||||
|
return nil, errors.New(api.NodeNotModified)
|
||||||
|
}
|
||||||
|
// update etag
|
||||||
|
if res.Header().Get("Etag") != "" && res.Header().Get("Etag") != c.eTags["config"] {
|
||||||
|
c.eTags["config"] = res.Header().Get("Etag")
|
||||||
|
}
|
||||||
|
|
||||||
response, err := c.parseResponse(res, "", err)
|
response, err := c.parseResponse(res, "", err)
|
||||||
c.access.Lock()
|
c.access.Lock()
|
||||||
defer c.access.Unlock()
|
defer c.access.Unlock()
|
||||||
@ -166,7 +183,7 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch c.NodeType {
|
switch c.NodeType {
|
||||||
case "V2ray":
|
case "V2ray", "Vmess", "Vless":
|
||||||
nodeInfo, err = c.ParseV2rayNodeResponse(response)
|
nodeInfo, err = c.ParseV2rayNodeResponse(response)
|
||||||
case "Trojan":
|
case "Trojan":
|
||||||
nodeInfo, err = c.ParseTrojanNodeResponse(response)
|
nodeInfo, err = c.ParseTrojanNodeResponse(response)
|
||||||
@ -178,7 +195,7 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
|||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
res, _ := response.MarshalJSON()
|
res, _ := response.MarshalJSON()
|
||||||
return nil, fmt.Errorf("Parse node info failed: %s, \nError: %s", string(res), err)
|
return nil, fmt.Errorf("parse node info failed: %s, \nError: %s", string(res), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nodeInfo, nil
|
return nodeInfo, nil
|
||||||
@ -188,19 +205,29 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
|
|||||||
func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
|
func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
|
||||||
var nodeType string
|
var nodeType string
|
||||||
switch c.NodeType {
|
switch c.NodeType {
|
||||||
case "V2ray", "Trojan", "Shadowsocks":
|
case "V2ray", "Vmess", "Vless", "Trojan", "Shadowsocks":
|
||||||
nodeType = strings.ToLower(c.NodeType)
|
nodeType = strings.ToLower(c.NodeType)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
|
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
|
||||||
}
|
}
|
||||||
res, err := c.client.R().
|
res, err := c.client.R().
|
||||||
|
SetHeader("If-None-Match", c.eTags["user"]).
|
||||||
SetQueryParams(map[string]string{
|
SetQueryParams(map[string]string{
|
||||||
"act": "user",
|
"act": "user",
|
||||||
"nodetype": nodeType,
|
"node_type": nodeType,
|
||||||
}).
|
}).
|
||||||
ForceContentType("application/json").
|
ForceContentType("application/json").
|
||||||
Get(c.APIHost)
|
Get(c.APIHost)
|
||||||
|
|
||||||
|
// Etag identifier for a specific version of a resource. StatusCode = 304 means no changed
|
||||||
|
if res.StatusCode() == 304 {
|
||||||
|
return nil, errors.New(api.UserNotModified)
|
||||||
|
}
|
||||||
|
// update etag
|
||||||
|
if res.Header().Get("Etag") != "" && res.Header().Get("Etag") != c.eTags["user"] {
|
||||||
|
c.eTags["user"] = res.Header().Get("Etag")
|
||||||
|
}
|
||||||
|
|
||||||
response, err := c.parseResponse(res, "", err)
|
response, err := c.parseResponse(res, "", err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -212,24 +239,30 @@ func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
|
|||||||
user.UID = response.Get("data").GetIndex(i).Get("id").MustInt()
|
user.UID = response.Get("data").GetIndex(i).Get("id").MustInt()
|
||||||
switch c.NodeType {
|
switch c.NodeType {
|
||||||
case "Shadowsocks":
|
case "Shadowsocks":
|
||||||
user.Email = response.Get("data").GetIndex(i).Get("shadowsocks_user").Get("secret").MustString()
|
user.Email = response.Get("data").GetIndex(i).Get("secret").MustString()
|
||||||
user.Passwd = response.Get("data").GetIndex(i).Get("shadowsocks_user").Get("secret").MustString()
|
user.Passwd = response.Get("data").GetIndex(i).Get("secret").MustString()
|
||||||
user.Method = response.Get("data").GetIndex(i).Get("shadowsocks_user").Get("cipher").MustString()
|
user.Method = response.Get("data").GetIndex(i).Get("cipher").MustString()
|
||||||
user.SpeedLimit = response.Get("data").GetIndex(i).Get("shadowsocks_user").Get("speed_limit").MustUint64() * 1000000 / 8
|
user.SpeedLimit = response.Get("data").GetIndex(i).Get("st").MustUint64() * 1000000 / 8
|
||||||
|
user.DeviceLimit = response.Get("data").GetIndex(i).Get("dt").MustInt()
|
||||||
case "Trojan":
|
case "Trojan":
|
||||||
user.UUID = response.Get("data").GetIndex(i).Get("trojan_user").Get("password").MustString()
|
user.UUID = response.Get("data").GetIndex(i).Get("password").MustString()
|
||||||
user.Email = response.Get("data").GetIndex(i).Get("trojan_user").Get("password").MustString()
|
user.Email = response.Get("data").GetIndex(i).Get("password").MustString()
|
||||||
user.SpeedLimit = response.Get("data").GetIndex(i).Get("trojan_user").Get("speed_limit").MustUint64() * 1000000 / 8
|
user.SpeedLimit = response.Get("data").GetIndex(i).Get("st").MustUint64() * 1000000 / 8
|
||||||
case "V2ray":
|
user.DeviceLimit = response.Get("data").GetIndex(i).Get("dt").MustInt()
|
||||||
user.UUID = response.Get("data").GetIndex(i).Get("v2ray_user").Get("uuid").MustString()
|
case "V2ray", "Vmess", "Vless":
|
||||||
user.Email = response.Get("data").GetIndex(i).Get("v2ray_user").Get("email").MustString()
|
user.UUID = response.Get("data").GetIndex(i).Get("uuid").MustString()
|
||||||
user.AlterID = uint16(response.Get("data").GetIndex(i).Get("v2ray_user").Get("alter_id").MustUint64())
|
user.Email = user.UUID + "@x.com"
|
||||||
user.SpeedLimit = response.Get("data").GetIndex(i).Get("v2ray_user").Get("speed_limit").MustUint64() * 1000000 / 8
|
user.SpeedLimit = response.Get("data").GetIndex(i).Get("st").MustUint64() * 1000000 / 8
|
||||||
|
user.DeviceLimit = response.Get("data").GetIndex(i).Get("dt").MustInt()
|
||||||
}
|
}
|
||||||
if c.SpeedLimit > 0 {
|
if c.SpeedLimit > 0 {
|
||||||
user.SpeedLimit = uint64((c.SpeedLimit * 1000000) / 8)
|
user.SpeedLimit = uint64((c.SpeedLimit * 1000000) / 8)
|
||||||
}
|
}
|
||||||
user.DeviceLimit = c.DeviceLimit
|
|
||||||
|
if c.DeviceLimit > 0 {
|
||||||
|
user.DeviceLimit = c.DeviceLimit
|
||||||
|
}
|
||||||
|
|
||||||
userList[i] = user
|
userList[i] = user
|
||||||
}
|
}
|
||||||
return &userList, nil
|
return &userList, nil
|
||||||
@ -249,8 +282,8 @@ func (c *APIClient) ReportUserTraffic(userTraffic *[]api.UserTraffic) error {
|
|||||||
res, err := c.client.R().
|
res, err := c.client.R().
|
||||||
SetQueryParam("node_id", strconv.Itoa(c.NodeID)).
|
SetQueryParam("node_id", strconv.Itoa(c.NodeID)).
|
||||||
SetQueryParams(map[string]string{
|
SetQueryParams(map[string]string{
|
||||||
"act": "submit",
|
"act": "submit",
|
||||||
"nodetype": strings.ToLower(c.NodeType),
|
"node_type": strings.ToLower(c.NodeType),
|
||||||
}).
|
}).
|
||||||
SetBody(data).
|
SetBody(data).
|
||||||
ForceContentType("application/json").
|
ForceContentType("application/json").
|
||||||
@ -265,11 +298,7 @@ func (c *APIClient) ReportUserTraffic(userTraffic *[]api.UserTraffic) error {
|
|||||||
// GetNodeRule implements the API interface
|
// GetNodeRule implements the API interface
|
||||||
func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
|
func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
|
||||||
ruleList := c.LocalRuleList
|
ruleList := c.LocalRuleList
|
||||||
if c.NodeType != "V2ray" {
|
|
||||||
return &ruleList, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only support the rule for v2ray
|
|
||||||
// fix: reuse config response
|
// fix: reuse config response
|
||||||
c.access.Lock()
|
c.access.Lock()
|
||||||
defer c.access.Unlock()
|
defer c.access.Unlock()
|
||||||
@ -287,26 +316,79 @@ func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
|
|||||||
|
|
||||||
// ReportNodeStatus implements the API interface
|
// ReportNodeStatus implements the API interface
|
||||||
func (c *APIClient) ReportNodeStatus(nodeStatus *api.NodeStatus) (err error) {
|
func (c *APIClient) ReportNodeStatus(nodeStatus *api.NodeStatus) (err error) {
|
||||||
|
systemload := NodeStatus{
|
||||||
|
Uptime: int(nodeStatus.Uptime),
|
||||||
|
CPU: fmt.Sprintf("%d%%", int(nodeStatus.CPU)),
|
||||||
|
Mem: fmt.Sprintf("%d%%", int(nodeStatus.Mem)),
|
||||||
|
Disk: fmt.Sprintf("%d%%", int(nodeStatus.Disk)),
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := c.client.R().
|
||||||
|
SetQueryParam("node_id", strconv.Itoa(c.NodeID)).
|
||||||
|
SetQueryParams(map[string]string{
|
||||||
|
"act": "nodestatus",
|
||||||
|
"node_type": strings.ToLower(c.NodeType),
|
||||||
|
}).
|
||||||
|
SetBody(systemload).
|
||||||
|
ForceContentType("application/json").
|
||||||
|
Post(c.APIHost)
|
||||||
|
_, err = c.parseResponse(res, "", err)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReportNodeOnlineUsers implements the API interface
|
// ReportNodeOnlineUsers implements the API interface
|
||||||
func (c *APIClient) ReportNodeOnlineUsers(onlineUserList *[]api.OnlineUser) error {
|
func (c *APIClient) ReportNodeOnlineUsers(onlineUserList *[]api.OnlineUser) error {
|
||||||
|
data := make([]NodeOnline, len(*onlineUserList))
|
||||||
|
for i, user := range *onlineUserList {
|
||||||
|
data[i] = NodeOnline{UID: user.UID, IP: user.IP}
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := c.client.R().
|
||||||
|
SetQueryParam("node_id", strconv.Itoa(c.NodeID)).
|
||||||
|
SetQueryParams(map[string]string{
|
||||||
|
"act": "onlineusers",
|
||||||
|
"node_type": strings.ToLower(c.NodeType),
|
||||||
|
}).
|
||||||
|
SetBody(data).
|
||||||
|
ForceContentType("application/json").
|
||||||
|
Post(c.APIHost)
|
||||||
|
_, err = c.parseResponse(res, "", err)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReportIllegal implements the API interface
|
// ReportIllegal implements the API interface
|
||||||
func (c *APIClient) ReportIllegal(detectResultList *[]api.DetectResult) error {
|
func (c *APIClient) ReportIllegal(detectResultList *[]api.DetectResult) error {
|
||||||
|
data := make([]IllegalItem, len(*detectResultList))
|
||||||
|
for i, r := range *detectResultList {
|
||||||
|
data[i] = IllegalItem{
|
||||||
|
UID: r.UID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := c.client.R().
|
||||||
|
SetQueryParam("node_id", strconv.Itoa(c.NodeID)).
|
||||||
|
SetQueryParams(map[string]string{
|
||||||
|
"act": "illegal",
|
||||||
|
"node_type": strings.ToLower(c.NodeType),
|
||||||
|
}).
|
||||||
|
SetBody(data).
|
||||||
|
ForceContentType("application/json").
|
||||||
|
Post(c.APIHost)
|
||||||
|
_, err = c.parseResponse(res, "", err)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseTrojanNodeResponse parse the response for the given nodeInfo format
|
// ParseTrojanNodeResponse parse the response for the given nodeInfo format
|
||||||
func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *simplejson.Json) (*api.NodeInfo, error) {
|
func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *simplejson.Json) (*api.NodeInfo, error) {
|
||||||
var TLSType = "tls"
|
|
||||||
if c.EnableXTLS {
|
|
||||||
TLSType = "xtls"
|
|
||||||
}
|
|
||||||
|
|
||||||
tmpInboundInfo := nodeInfoResponse.Get("inbounds").MustArray()
|
tmpInboundInfo := nodeInfoResponse.Get("inbounds").MustArray()
|
||||||
marshalByte, _ := json.Marshal(tmpInboundInfo[0].(map[string]interface{}))
|
marshalByte, _ := json.Marshal(tmpInboundInfo[0].(map[string]interface{}))
|
||||||
inboundInfo, _ := simplejson.NewJson(marshalByte)
|
inboundInfo, _ := simplejson.NewJson(marshalByte)
|
||||||
@ -321,7 +403,6 @@ func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *simplejson.Json) (
|
|||||||
Port: port,
|
Port: port,
|
||||||
TransportProtocol: "tcp",
|
TransportProtocol: "tcp",
|
||||||
EnableTLS: true,
|
EnableTLS: true,
|
||||||
TLSType: TLSType,
|
|
||||||
Host: host,
|
Host: host,
|
||||||
}
|
}
|
||||||
return nodeInfo, nil
|
return nodeInfo, nil
|
||||||
@ -339,14 +420,6 @@ func (c *APIClient) ParseSSNodeResponse(nodeInfoResponse *simplejson.Json) (*api
|
|||||||
// Shadowsocks 2022
|
// Shadowsocks 2022
|
||||||
if C.Contains(shadowaead_2022.List, method) {
|
if C.Contains(shadowaead_2022.List, method) {
|
||||||
serverPsk = inboundInfo.Get("settings").Get("password").MustString()
|
serverPsk = inboundInfo.Get("settings").Get("password").MustString()
|
||||||
} else {
|
|
||||||
userInfo, err := c.GetUserList()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(*userInfo) > 0 {
|
|
||||||
method = (*userInfo)[0].Method
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create GeneralNodeInfo
|
// Create GeneralNodeInfo
|
||||||
@ -364,14 +437,12 @@ func (c *APIClient) ParseSSNodeResponse(nodeInfoResponse *simplejson.Json) (*api
|
|||||||
|
|
||||||
// ParseV2rayNodeResponse parse the response for the given nodeInfo format
|
// ParseV2rayNodeResponse parse the response for the given nodeInfo format
|
||||||
func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *simplejson.Json) (*api.NodeInfo, error) {
|
func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *simplejson.Json) (*api.NodeInfo, error) {
|
||||||
var TLSType = "tls"
|
|
||||||
var path, host, serviceName string
|
var path, host, serviceName string
|
||||||
var header json.RawMessage
|
var header json.RawMessage
|
||||||
var enableTLS bool
|
var enableTLS bool
|
||||||
var alterID uint16 = 0
|
var enableVless bool
|
||||||
if c.EnableXTLS {
|
var enableReality bool
|
||||||
TLSType = "xtls"
|
var vlessFlow string
|
||||||
}
|
|
||||||
|
|
||||||
tmpInboundInfo := nodeInfoResponse.Get("inbounds").MustArray()
|
tmpInboundInfo := nodeInfoResponse.Get("inbounds").MustArray()
|
||||||
marshalByte, _ := json.Marshal(tmpInboundInfo[0].(map[string]interface{}))
|
marshalByte, _ := json.Marshal(tmpInboundInfo[0].(map[string]interface{}))
|
||||||
@ -384,6 +455,12 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *simplejson.Json) (*
|
|||||||
case "ws":
|
case "ws":
|
||||||
path = inboundInfo.Get("streamSettings").Get("wsSettings").Get("path").MustString()
|
path = inboundInfo.Get("streamSettings").Get("wsSettings").Get("path").MustString()
|
||||||
host = inboundInfo.Get("streamSettings").Get("wsSettings").Get("headers").Get("Host").MustString()
|
host = inboundInfo.Get("streamSettings").Get("wsSettings").Get("headers").Get("Host").MustString()
|
||||||
|
case "httpupgrade":
|
||||||
|
host = inboundInfo.Get("streamSettings").Get("httpupgradeSettings").Get("Host").MustString()
|
||||||
|
path = inboundInfo.Get("streamSettings").Get("httpupgradeSettings").Get("path").MustString()
|
||||||
|
case "splithttp":
|
||||||
|
host = inboundInfo.Get("streamSettings").Get("splithttpSettings").Get("Host").MustString()
|
||||||
|
path = inboundInfo.Get("streamSettings").Get("splithttpSettings").Get("path").MustString()
|
||||||
case "grpc":
|
case "grpc":
|
||||||
if data, ok := inboundInfo.Get("streamSettings").Get("grpcSettings").CheckGet("serviceName"); ok {
|
if data, ok := inboundInfo.Get("streamSettings").Get("grpcSettings").CheckGet("serviceName"); ok {
|
||||||
serviceName = data.MustString()
|
serviceName = data.MustString()
|
||||||
@ -396,12 +473,34 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *simplejson.Json) (*
|
|||||||
header = httpHeader
|
header = httpHeader
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if inboundInfo.Get("streamSettings").Get("security").MustString() == "tls" {
|
|
||||||
enableTLS = true
|
enableTLS = inboundInfo.Get("streamSettings").Get("security").MustString() == "tls"
|
||||||
|
enableVless = inboundInfo.Get("protocol").MustString() == "vless"
|
||||||
|
enableReality = inboundInfo.Get("streamSettings").Get("security").MustString() == "reality"
|
||||||
|
|
||||||
|
realityConfig := new(api.REALITYConfig)
|
||||||
|
if enableVless {
|
||||||
|
// parse reality config
|
||||||
|
realityConfig = &api.REALITYConfig{
|
||||||
|
Dest: inboundInfo.Get("streamSettings").Get("realitySettings").Get("dest").MustString(),
|
||||||
|
ProxyProtocolVer: inboundInfo.Get("streamSettings").Get("realitySettings").Get("xver").MustUint64(),
|
||||||
|
ServerNames: inboundInfo.Get("streamSettings").Get("realitySettings").Get("serverNames").MustStringArray(),
|
||||||
|
PrivateKey: inboundInfo.Get("streamSettings").Get("realitySettings").Get("privateKey").MustString(),
|
||||||
|
MinClientVer: inboundInfo.Get("streamSettings").Get("realitySettings").Get("minClientVer").MustString(),
|
||||||
|
MaxClientVer: inboundInfo.Get("streamSettings").Get("realitySettings").Get("maxClientVer").MustString(),
|
||||||
|
MaxTimeDiff: inboundInfo.Get("streamSettings").Get("realitySettings").Get("maxTimeDiff").MustUint64(),
|
||||||
|
ShortIds: inboundInfo.Get("streamSettings").Get("realitySettings").Get("shortIds").MustStringArray(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// XTLS only supports TLS and REALITY directly for now
|
||||||
|
if (transportProtocol == "grpc" || transportProtocol == "h2") && enableReality {
|
||||||
|
vlessFlow = ""
|
||||||
|
} else if transportProtocol == "tcp" && enableReality {
|
||||||
|
vlessFlow = "xtls-rprx-vision"
|
||||||
} else {
|
} else {
|
||||||
enableTLS = false
|
vlessFlow = c.VlessFlow
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create GeneralNodeInfo
|
// Create GeneralNodeInfo
|
||||||
@ -410,15 +509,17 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *simplejson.Json) (*
|
|||||||
NodeType: c.NodeType,
|
NodeType: c.NodeType,
|
||||||
NodeID: c.NodeID,
|
NodeID: c.NodeID,
|
||||||
Port: port,
|
Port: port,
|
||||||
AlterID: alterID,
|
AlterID: 0,
|
||||||
TransportProtocol: transportProtocol,
|
TransportProtocol: transportProtocol,
|
||||||
EnableTLS: enableTLS,
|
EnableTLS: enableTLS,
|
||||||
TLSType: TLSType,
|
|
||||||
Path: path,
|
Path: path,
|
||||||
Host: host,
|
Host: host,
|
||||||
EnableVless: c.EnableVless,
|
EnableVless: enableVless,
|
||||||
|
VlessFlow: vlessFlow,
|
||||||
ServiceName: serviceName,
|
ServiceName: serviceName,
|
||||||
Header: header,
|
Header: header,
|
||||||
|
EnableREALITY: enableReality,
|
||||||
|
REALITYConfig: realityConfig,
|
||||||
}
|
}
|
||||||
return nodeInfo, nil
|
return nodeInfo, nil
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/common/buf"
|
"github.com/xtls/xray-core/common/buf"
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
"github.com/xtls/xray-core/common/log"
|
"github.com/xtls/xray-core/common/log"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/common/protocol"
|
"github.com/xtls/xray-core/common/protocol"
|
||||||
@ -106,7 +107,7 @@ func init() {
|
|||||||
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
||||||
d := new(DefaultDispatcher)
|
d := new(DefaultDispatcher)
|
||||||
if err := core.RequireFeatures(ctx, func(om outbound.Manager, router routing.Router, pm policy.Manager, sm stats.Manager, dc dns.Client) error {
|
if err := core.RequireFeatures(ctx, func(om outbound.Manager, router routing.Router, pm policy.Manager, sm stats.Manager, dc dns.Client) error {
|
||||||
core.RequireFeatures(ctx, func(fdns dns.FakeDNSEngine) {
|
core.OptionalFeatures(ctx, func(fdns dns.FakeDNSEngine) {
|
||||||
d.fdns = fdns
|
d.fdns = fdns
|
||||||
})
|
})
|
||||||
return d.Init(config.(*Config), om, router, pm, sm, dc)
|
return d.Init(config.(*Config), om, router, pm, sm, dc)
|
||||||
@ -144,77 +145,10 @@ func (*DefaultDispatcher) Close() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DefaultDispatcher) getLink(ctx context.Context, network net.Network, sniffing session.SniffingRequest) (*transport.Link, *transport.Link, error) {
|
func (d *DefaultDispatcher) getLink(ctx context.Context) (*transport.Link, *transport.Link, error) {
|
||||||
downOpt := pipe.OptionsFromContext(ctx)
|
opt := pipe.OptionsFromContext(ctx)
|
||||||
upOpt := downOpt
|
uplinkReader, uplinkWriter := pipe.New(opt...)
|
||||||
|
downlinkReader, downlinkWriter := pipe.New(opt...)
|
||||||
if network == net.Network_UDP {
|
|
||||||
var ip2domain *sync.Map // net.IP.String() => domain, this map is used by server side when client turn on fakedns
|
|
||||||
// Client will send domain address in the buffer.UDP.Address, server record all possible target IP addrs.
|
|
||||||
// When target replies, server will restore the domain and send back to client.
|
|
||||||
// Note: this map is not global but per connection context
|
|
||||||
upOpt = append(upOpt, pipe.OnTransmission(func(mb buf.MultiBuffer) buf.MultiBuffer {
|
|
||||||
for i, buffer := range mb {
|
|
||||||
if buffer.UDP == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
addr := buffer.UDP.Address
|
|
||||||
if addr.Family().IsIP() {
|
|
||||||
if fkr0, ok := d.fdns.(dns.FakeDNSEngineRev0); ok && fkr0.IsIPInIPPool(addr) && sniffing.Enabled {
|
|
||||||
domain := fkr0.GetDomainFromFakeDNS(addr)
|
|
||||||
if len(domain) > 0 {
|
|
||||||
buffer.UDP.Address = net.DomainAddress(domain)
|
|
||||||
newError("[fakedns client] override with domain: ", domain, " for xUDP buffer at ", i).WriteToLog(session.ExportIDToError(ctx))
|
|
||||||
} else {
|
|
||||||
newError("[fakedns client] failed to find domain! :", addr.String(), " for xUDP buffer at ", i).AtWarning().WriteToLog(session.ExportIDToError(ctx))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ip2domain == nil {
|
|
||||||
ip2domain = new(sync.Map)
|
|
||||||
newError("[fakedns client] create a new map").WriteToLog(session.ExportIDToError(ctx))
|
|
||||||
}
|
|
||||||
domain := addr.Domain()
|
|
||||||
ips, err := d.dns.LookupIP(domain, dns.IPOption{IPv4Enable: true, IPv6Enable: true})
|
|
||||||
if err == nil {
|
|
||||||
for _, ip := range ips {
|
|
||||||
ip2domain.Store(ip.String(), domain)
|
|
||||||
}
|
|
||||||
newError("[fakedns client] candidate ip: "+fmt.Sprintf("%v", ips), " for xUDP buffer at ", i).WriteToLog(session.ExportIDToError(ctx))
|
|
||||||
} else {
|
|
||||||
newError("[fakedns client] failed to look up IP for ", domain, " for xUDP buffer at ", i).Base(err).WriteToLog(session.ExportIDToError(ctx))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mb
|
|
||||||
}))
|
|
||||||
downOpt = append(downOpt, pipe.OnTransmission(func(mb buf.MultiBuffer) buf.MultiBuffer {
|
|
||||||
for i, buffer := range mb {
|
|
||||||
if buffer.UDP == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
addr := buffer.UDP.Address
|
|
||||||
if addr.Family().IsIP() {
|
|
||||||
if ip2domain == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if domain, found := ip2domain.Load(addr.IP().String()); found {
|
|
||||||
buffer.UDP.Address = net.DomainAddress(domain.(string))
|
|
||||||
newError("[fakedns client] restore domain: ", domain.(string), " for xUDP buffer at ", i).WriteToLog(session.ExportIDToError(ctx))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if fkr0, ok := d.fdns.(dns.FakeDNSEngineRev0); ok {
|
|
||||||
fakeIp := fkr0.GetFakeIPForDomain(addr.Domain())
|
|
||||||
buffer.UDP.Address = fakeIp[0]
|
|
||||||
newError("[fakedns client] restore FakeIP: ", buffer.UDP, fmt.Sprintf("%v", fakeIp), " for xUDP buffer at ", i).WriteToLog(session.ExportIDToError(ctx))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mb
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
uplinkReader, uplinkWriter := pipe.New(upOpt...)
|
|
||||||
downlinkReader, downlinkWriter := pipe.New(downOpt...)
|
|
||||||
|
|
||||||
inboundLink := &transport.Link{
|
inboundLink := &transport.Link{
|
||||||
Reader: downlinkReader,
|
Reader: downlinkReader,
|
||||||
@ -236,7 +170,7 @@ func (d *DefaultDispatcher) getLink(ctx context.Context, network net.Network, sn
|
|||||||
// Speed Limit and Device Limit
|
// Speed Limit and Device Limit
|
||||||
bucket, ok, reject := d.Limiter.GetUserBucket(sessionInbound.Tag, user.Email, sessionInbound.Source.Address.IP().String())
|
bucket, ok, reject := d.Limiter.GetUserBucket(sessionInbound.Tag, user.Email, sessionInbound.Source.Address.IP().String())
|
||||||
if reject {
|
if reject {
|
||||||
newError("Devices reach the limit: ", user.Email).AtWarning().WriteToLog()
|
errors.LogWarning(ctx, "Devices reach the limit: ", user.Email)
|
||||||
common.Close(outboundLink.Writer)
|
common.Close(outboundLink.Writer)
|
||||||
common.Close(inboundLink.Writer)
|
common.Close(inboundLink.Writer)
|
||||||
common.Interrupt(outboundLink.Reader)
|
common.Interrupt(outboundLink.Reader)
|
||||||
@ -247,6 +181,7 @@ func (d *DefaultDispatcher) getLink(ctx context.Context, network net.Network, sn
|
|||||||
inboundLink.Writer = d.Limiter.RateWriter(inboundLink.Writer, bucket)
|
inboundLink.Writer = d.Limiter.RateWriter(inboundLink.Writer, bucket)
|
||||||
outboundLink.Writer = d.Limiter.RateWriter(outboundLink.Writer, bucket)
|
outboundLink.Writer = d.Limiter.RateWriter(outboundLink.Writer, bucket)
|
||||||
}
|
}
|
||||||
|
|
||||||
p := d.policy.ForLevel(user.Level)
|
p := d.policy.ForLevel(user.Level)
|
||||||
if p.Stats.UserUplink {
|
if p.Stats.UserUplink {
|
||||||
name := "user>>>" + user.Email + ">>>traffic>>>uplink"
|
name := "user>>>" + user.Email + ">>>traffic>>>uplink"
|
||||||
@ -283,12 +218,12 @@ func (d *DefaultDispatcher) shouldOverride(ctx context.Context, result SniffResu
|
|||||||
protocolString = resComp.ProtocolForDomainResult()
|
protocolString = resComp.ProtocolForDomainResult()
|
||||||
}
|
}
|
||||||
for _, p := range request.OverrideDestinationForProtocol {
|
for _, p := range request.OverrideDestinationForProtocol {
|
||||||
if strings.HasPrefix(protocolString, p) {
|
if strings.HasPrefix(protocolString, p) || strings.HasPrefix(p, protocolString) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if fkr0, ok := d.fdns.(dns.FakeDNSEngineRev0); ok && protocolString != "bittorrent" && p == "fakedns" &&
|
if fkr0, ok := d.fdns.(dns.FakeDNSEngineRev0); ok && protocolString != "bittorrent" && p == "fakedns" &&
|
||||||
destination.Address.Family().IsIP() && fkr0.IsIPInIPPool(destination.Address) {
|
destination.Address.Family().IsIP() && fkr0.IsIPInIPPool(destination.Address) {
|
||||||
newError("Using sniffer ", protocolString, " since the fake DNS missed").WriteToLog(session.ExportIDToError(ctx))
|
errors.LogInfo(ctx, "Using sniffer ", protocolString, " since the fake DNS missed")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if resultSubset, ok := result.(SnifferIsProtoSubsetOf); ok {
|
if resultSubset, ok := result.(SnifferIsProtoSubsetOf); ok {
|
||||||
@ -306,10 +241,14 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin
|
|||||||
if !destination.IsValid() {
|
if !destination.IsValid() {
|
||||||
panic("Dispatcher: Invalid destination.")
|
panic("Dispatcher: Invalid destination.")
|
||||||
}
|
}
|
||||||
ob := &session.Outbound{
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
Target: destination,
|
if len(outbounds) == 0 {
|
||||||
|
outbounds = []*session.Outbound{{}}
|
||||||
|
ctx = session.ContextWithOutbounds(ctx, outbounds)
|
||||||
}
|
}
|
||||||
ctx = session.ContextWithOutbound(ctx, ob)
|
ob := outbounds[len(outbounds)-1]
|
||||||
|
ob.OriginalTarget = destination
|
||||||
|
ob.Target = destination
|
||||||
content := session.ContentFromContext(ctx)
|
content := session.ContentFromContext(ctx)
|
||||||
if content == nil {
|
if content == nil {
|
||||||
content = new(session.Content)
|
content = new(session.Content)
|
||||||
@ -317,7 +256,7 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin
|
|||||||
}
|
}
|
||||||
|
|
||||||
sniffingRequest := content.SniffingRequest
|
sniffingRequest := content.SniffingRequest
|
||||||
inbound, outbound, err := d.getLink(ctx, destination.Network, sniffingRequest)
|
inbound, outbound, err := d.getLink(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -335,7 +274,7 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin
|
|||||||
}
|
}
|
||||||
if err == nil && d.shouldOverride(ctx, result, sniffingRequest, destination) {
|
if err == nil && d.shouldOverride(ctx, result, sniffingRequest, destination) {
|
||||||
domain := result.Domain()
|
domain := result.Domain()
|
||||||
newError("sniffed domain: ", domain).WriteToLog(session.ExportIDToError(ctx))
|
errors.LogInfo(ctx, "sniffed domain: ", domain)
|
||||||
destination.Address = net.ParseAddress(domain)
|
destination.Address = net.ParseAddress(domain)
|
||||||
if sniffingRequest.RouteOnly && result.Protocol() != "fakedns" {
|
if sniffingRequest.RouteOnly && result.Protocol() != "fakedns" {
|
||||||
ob.RouteTarget = destination
|
ob.RouteTarget = destination
|
||||||
@ -354,10 +293,14 @@ func (d *DefaultDispatcher) DispatchLink(ctx context.Context, destination net.De
|
|||||||
if !destination.IsValid() {
|
if !destination.IsValid() {
|
||||||
return newError("Dispatcher: Invalid destination.")
|
return newError("Dispatcher: Invalid destination.")
|
||||||
}
|
}
|
||||||
ob := &session.Outbound{
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
Target: destination,
|
if len(outbounds) == 0 {
|
||||||
|
outbounds = []*session.Outbound{{}}
|
||||||
|
ctx = session.ContextWithOutbounds(ctx, outbounds)
|
||||||
}
|
}
|
||||||
ctx = session.ContextWithOutbound(ctx, ob)
|
ob := outbounds[len(outbounds)-1]
|
||||||
|
ob.OriginalTarget = destination
|
||||||
|
ob.Target = destination
|
||||||
content := session.ContentFromContext(ctx)
|
content := session.ContentFromContext(ctx)
|
||||||
if content == nil {
|
if content == nil {
|
||||||
content = new(session.Content)
|
content = new(session.Content)
|
||||||
@ -378,7 +321,7 @@ func (d *DefaultDispatcher) DispatchLink(ctx context.Context, destination net.De
|
|||||||
}
|
}
|
||||||
if err == nil && d.shouldOverride(ctx, result, sniffingRequest, destination) {
|
if err == nil && d.shouldOverride(ctx, result, sniffingRequest, destination) {
|
||||||
domain := result.Domain()
|
domain := result.Domain()
|
||||||
newError("sniffed domain: ", domain).WriteToLog(session.ExportIDToError(ctx))
|
errors.LogInfo(ctx, "sniffed domain: ", domain)
|
||||||
destination.Address = net.ParseAddress(domain)
|
destination.Address = net.ParseAddress(domain)
|
||||||
if sniffingRequest.RouteOnly && result.Protocol() != "fakedns" {
|
if sniffingRequest.RouteOnly && result.Protocol() != "fakedns" {
|
||||||
ob.RouteTarget = destination
|
ob.RouteTarget = destination
|
||||||
@ -440,7 +383,8 @@ func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool, netw
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.Link, destination net.Destination) {
|
func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.Link, destination net.Destination) {
|
||||||
ob := session.OutboundFromContext(ctx)
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
|
ob := outbounds[len(outbounds)-1]
|
||||||
if hosts, ok := d.dns.(dns.HostsLookup); ok && destination.Address.Family().IsDomain() {
|
if hosts, ok := d.dns.(dns.HostsLookup); ok && destination.Address.Family().IsDomain() {
|
||||||
proxied := hosts.LookupHosts(ob.Target.String())
|
proxied := hosts.LookupHosts(ob.Target.String())
|
||||||
if proxied != nil {
|
if proxied != nil {
|
||||||
@ -461,7 +405,7 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
|
|||||||
// Whether the inbound connection contains a user
|
// Whether the inbound connection contains a user
|
||||||
if sessionInbound.User != nil {
|
if sessionInbound.User != nil {
|
||||||
if d.RuleManager.Detect(sessionInbound.Tag, destination.String(), sessionInbound.User.Email) {
|
if d.RuleManager.Detect(sessionInbound.Tag, destination.String(), sessionInbound.User.Email) {
|
||||||
newError(fmt.Sprintf("User %s access %s reject by rule", sessionInbound.User.Email, destination.String())).AtError().WriteToLog()
|
errors.LogError(ctx, fmt.Sprintf("User %s access %s reject by rule", sessionInbound.User.Email, destination.String()))
|
||||||
newError("destination is reject by rule")
|
newError("destination is reject by rule")
|
||||||
common.Close(link.Writer)
|
common.Close(link.Writer)
|
||||||
common.Interrupt(link.Reader)
|
common.Interrupt(link.Reader)
|
||||||
@ -476,10 +420,10 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
|
|||||||
ctx = session.SetForcedOutboundTagToContext(ctx, "")
|
ctx = session.SetForcedOutboundTagToContext(ctx, "")
|
||||||
if h := d.ohm.GetHandler(forcedOutboundTag); h != nil {
|
if h := d.ohm.GetHandler(forcedOutboundTag); h != nil {
|
||||||
isPickRoute = 1
|
isPickRoute = 1
|
||||||
newError("taking platform initialized detour [", forcedOutboundTag, "] for [", destination, "]").WriteToLog(session.ExportIDToError(ctx))
|
errors.LogInfo(ctx, "taking platform initialized detour [", forcedOutboundTag, "] for [", destination, "]")
|
||||||
handler = h
|
handler = h
|
||||||
} else {
|
} else {
|
||||||
newError("non existing tag for platform initialized detour: ", forcedOutboundTag).AtError().WriteToLog(session.ExportIDToError(ctx))
|
errors.LogError(ctx, "non existing tag for platform initialized detour: ", forcedOutboundTag)
|
||||||
common.Close(link.Writer)
|
common.Close(link.Writer)
|
||||||
common.Interrupt(link.Reader)
|
common.Interrupt(link.Reader)
|
||||||
return
|
return
|
||||||
@ -489,13 +433,13 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
|
|||||||
outTag := route.GetOutboundTag()
|
outTag := route.GetOutboundTag()
|
||||||
if h := d.ohm.GetHandler(outTag); h != nil {
|
if h := d.ohm.GetHandler(outTag); h != nil {
|
||||||
isPickRoute = 2
|
isPickRoute = 2
|
||||||
newError("taking detour [", outTag, "] for [", destination, "]").WriteToLog(session.ExportIDToError(ctx))
|
errors.LogInfo(ctx, "taking detour [", outTag, "] for [", destination, "]")
|
||||||
handler = h
|
handler = h
|
||||||
} else {
|
} else {
|
||||||
newError("non existing outTag: ", outTag).AtWarning().WriteToLog(session.ExportIDToError(ctx))
|
errors.LogWarning(ctx, "non existing outTag: ", outTag)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
newError("default route for ", destination).WriteToLog(session.ExportIDToError(ctx))
|
errors.LogInfo(ctx, "default route for ", destination)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -509,7 +453,7 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
|
|||||||
}
|
}
|
||||||
|
|
||||||
if handler == nil {
|
if handler == nil {
|
||||||
newError("default outbound handler not exist").WriteToLog(session.ExportIDToError(ctx))
|
errors.LogInfo(ctx, "default outbound handler not exist")
|
||||||
common.Close(link.Writer)
|
common.Close(link.Writer)
|
||||||
common.Interrupt(link.Reader)
|
common.Interrupt(link.Reader)
|
||||||
return
|
return
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/common/session"
|
"github.com/xtls/xray-core/common/session"
|
||||||
"github.com/xtls/xray-core/core"
|
"github.com/xtls/xray-core/core"
|
||||||
@ -26,11 +27,13 @@ func newFakeDNSSniffer(ctx context.Context) (protocolSnifferWithMetadata, error)
|
|||||||
return protocolSnifferWithMetadata{}, errNotInit
|
return protocolSnifferWithMetadata{}, errNotInit
|
||||||
}
|
}
|
||||||
return protocolSnifferWithMetadata{protocolSniffer: func(ctx context.Context, bytes []byte) (SniffResult, error) {
|
return protocolSnifferWithMetadata{protocolSniffer: func(ctx context.Context, bytes []byte) (SniffResult, error) {
|
||||||
Target := session.OutboundFromContext(ctx).Target
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
|
ob := outbounds[len(outbounds)-1]
|
||||||
|
Target := ob.Target
|
||||||
if Target.Network == net.Network_TCP || Target.Network == net.Network_UDP {
|
if Target.Network == net.Network_TCP || Target.Network == net.Network_UDP {
|
||||||
domainFromFakeDNS := fakeDNSEngine.GetDomainFromFakeDNS(Target.Address)
|
domainFromFakeDNS := fakeDNSEngine.GetDomainFromFakeDNS(Target.Address)
|
||||||
if domainFromFakeDNS != "" {
|
if domainFromFakeDNS != "" {
|
||||||
newError("fake dns got domain: ", domainFromFakeDNS, " for ip: ", Target.Address.String()).WriteToLog(session.ExportIDToError(ctx))
|
errors.LogInfo(ctx, "fake dns got domain: ", domainFromFakeDNS, " for ip: ", ob.Target.Address.String())
|
||||||
return &fakeDNSSniffResult{domainName: domainFromFakeDNS}, nil
|
return &fakeDNSSniffResult{domainName: domainFromFakeDNS}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -107,10 +110,10 @@ func newFakeDNSThenOthers(ctx context.Context, fakeDNSSniffer protocolSnifferWit
|
|||||||
}
|
}
|
||||||
return nil, common.ErrNoClue
|
return nil, common.ErrNoClue
|
||||||
}
|
}
|
||||||
newError("ip address not in fake dns range, return as is").AtDebug().WriteToLog()
|
errors.LogDebug(ctx, "ip address not in fake dns range, return as is")
|
||||||
return nil, common.ErrNoClue
|
return nil, common.ErrNoClue
|
||||||
}
|
}
|
||||||
newError("fake dns sniffer did not set address in range option, assume false.").AtWarning().WriteToLog()
|
errors.LogWarning(ctx, "fake dns sniffer did not set address in range option, assume false.")
|
||||||
return nil, common.ErrNoClue
|
return nil, common.ErrNoClue
|
||||||
},
|
},
|
||||||
metadataSniffer: false,
|
metadataSniffer: false,
|
||||||
|
@ -34,7 +34,7 @@ type Sniffer struct {
|
|||||||
func NewSniffer(ctx context.Context) *Sniffer {
|
func NewSniffer(ctx context.Context) *Sniffer {
|
||||||
ret := &Sniffer{
|
ret := &Sniffer{
|
||||||
sniffer: []protocolSnifferWithMetadata{
|
sniffer: []protocolSnifferWithMetadata{
|
||||||
{func(c context.Context, b []byte) (SniffResult, error) { return http.SniffHTTP(b) }, false, net.Network_TCP},
|
{func(c context.Context, b []byte) (SniffResult, error) { return http.SniffHTTP(b, ctx) }, false, net.Network_TCP},
|
||||||
{func(c context.Context, b []byte) (SniffResult, error) { return tls.SniffTLS(b) }, false, net.Network_TCP},
|
{func(c context.Context, b []byte) (SniffResult, error) { return tls.SniffTLS(b) }, false, net.Network_TCP},
|
||||||
{func(c context.Context, b []byte) (SniffResult, error) { return bittorrent.SniffBittorrent(b) }, false, net.Network_TCP},
|
{func(c context.Context, b []byte) (SniffResult, error) { return bittorrent.SniffBittorrent(b) }, false, net.Network_TCP},
|
||||||
{func(c context.Context, b []byte) (SniffResult, error) { return quic.SniffQUIC(b) }, false, net.Network_UDP},
|
{func(c context.Context, b []byte) (SniffResult, error) { return quic.SniffQUIC(b) }, false, net.Network_UDP},
|
||||||
|
@ -31,25 +31,26 @@ import (
|
|||||||
_ "github.com/xtls/xray-core/proxy/dokodemo"
|
_ "github.com/xtls/xray-core/proxy/dokodemo"
|
||||||
_ "github.com/xtls/xray-core/proxy/freedom"
|
_ "github.com/xtls/xray-core/proxy/freedom"
|
||||||
_ "github.com/xtls/xray-core/proxy/http"
|
_ "github.com/xtls/xray-core/proxy/http"
|
||||||
_ "github.com/xtls/xray-core/proxy/mtproto"
|
_ "github.com/xtls/xray-core/proxy/loopback"
|
||||||
_ "github.com/xtls/xray-core/proxy/shadowsocks"
|
_ "github.com/xtls/xray-core/proxy/shadowsocks"
|
||||||
|
_ "github.com/xtls/xray-core/proxy/shadowsocks_2022"
|
||||||
_ "github.com/xtls/xray-core/proxy/socks"
|
_ "github.com/xtls/xray-core/proxy/socks"
|
||||||
_ "github.com/xtls/xray-core/proxy/trojan"
|
_ "github.com/xtls/xray-core/proxy/trojan"
|
||||||
_ "github.com/xtls/xray-core/proxy/vless/inbound"
|
_ "github.com/xtls/xray-core/proxy/vless/inbound"
|
||||||
_ "github.com/xtls/xray-core/proxy/vless/outbound"
|
_ "github.com/xtls/xray-core/proxy/vless/outbound"
|
||||||
_ "github.com/xtls/xray-core/proxy/vmess/inbound"
|
_ "github.com/xtls/xray-core/proxy/vmess/inbound"
|
||||||
_ "github.com/xtls/xray-core/proxy/vmess/outbound"
|
_ "github.com/xtls/xray-core/proxy/vmess/outbound"
|
||||||
|
_ "github.com/xtls/xray-core/proxy/wireguard"
|
||||||
|
|
||||||
// Transports
|
// Transports
|
||||||
_ "github.com/xtls/xray-core/transport/internet/domainsocket"
|
_ "github.com/xtls/xray-core/transport/internet/grpc"
|
||||||
_ "github.com/xtls/xray-core/transport/internet/http"
|
|
||||||
_ "github.com/xtls/xray-core/transport/internet/kcp"
|
_ "github.com/xtls/xray-core/transport/internet/kcp"
|
||||||
_ "github.com/xtls/xray-core/transport/internet/quic"
|
_ "github.com/xtls/xray-core/transport/internet/reality"
|
||||||
|
_ "github.com/xtls/xray-core/transport/internet/splithttp"
|
||||||
_ "github.com/xtls/xray-core/transport/internet/tcp"
|
_ "github.com/xtls/xray-core/transport/internet/tcp"
|
||||||
_ "github.com/xtls/xray-core/transport/internet/tls"
|
_ "github.com/xtls/xray-core/transport/internet/tls"
|
||||||
_ "github.com/xtls/xray-core/transport/internet/udp"
|
_ "github.com/xtls/xray-core/transport/internet/udp"
|
||||||
_ "github.com/xtls/xray-core/transport/internet/websocket"
|
_ "github.com/xtls/xray-core/transport/internet/websocket"
|
||||||
_ "github.com/xtls/xray-core/transport/internet/xtls"
|
|
||||||
|
|
||||||
// Transport headers
|
// Transport headers
|
||||||
_ "github.com/xtls/xray-core/transport/internet/headers/http"
|
_ "github.com/xtls/xray-core/transport/internet/headers/http"
|
@ -1,9 +1,7 @@
|
|||||||
package main
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"path"
|
"path"
|
||||||
@ -12,36 +10,40 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/fsnotify/fsnotify"
|
"github.com/fsnotify/fsnotify"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
"github.com/XrayR-project/XrayR/panel"
|
"github.com/XrayR-project/XrayR/panel"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
configFile = flag.String("config", "", "Config file for XrayR.")
|
cfgFile string
|
||||||
printVersion = flag.Bool("version", false, "show version")
|
rootCmd = &cobra.Command{
|
||||||
|
Use: "XrayR",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
if err := run(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
func init() {
|
||||||
version = "0.9.0"
|
rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "Config file for XrayR.")
|
||||||
codename = "XrayR"
|
|
||||||
intro = "A Xray backend that supports many panels"
|
|
||||||
)
|
|
||||||
|
|
||||||
func showVersion() {
|
|
||||||
fmt.Printf("%s %s (%s) \n", codename, version, intro)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getConfig() *viper.Viper {
|
func getConfig() *viper.Viper {
|
||||||
config := viper.New()
|
config := viper.New()
|
||||||
|
|
||||||
// Set custom path and name
|
// Set custom path and name
|
||||||
if *configFile != "" {
|
if cfgFile != "" {
|
||||||
configName := path.Base(*configFile)
|
configName := path.Base(cfgFile)
|
||||||
configFileExt := path.Ext(*configFile)
|
configFileExt := path.Ext(cfgFile)
|
||||||
configNameOnly := strings.TrimSuffix(configName, configFileExt)
|
configNameOnly := strings.TrimSuffix(configName, configFileExt)
|
||||||
configPath := path.Dir(*configFile)
|
configPath := path.Dir(cfgFile)
|
||||||
config.SetConfigName(configNameOnly)
|
config.SetConfigName(configNameOnly)
|
||||||
config.SetConfigType(strings.TrimPrefix(configFileExt, "."))
|
config.SetConfigType(strings.TrimPrefix(configFileExt, "."))
|
||||||
config.AddConfigPath(configPath)
|
config.AddConfigPath(configPath)
|
||||||
@ -65,18 +67,19 @@ func getConfig() *viper.Viper {
|
|||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func run() error {
|
||||||
flag.Parse()
|
|
||||||
showVersion()
|
showVersion()
|
||||||
if *printVersion {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
config := getConfig()
|
config := getConfig()
|
||||||
panelConfig := &panel.Config{}
|
panelConfig := &panel.Config{}
|
||||||
if err := config.Unmarshal(panelConfig); err != nil {
|
if err := config.Unmarshal(panelConfig); err != nil {
|
||||||
log.Panicf("Parse config file %v failed: %s \n", configFile, err)
|
return fmt.Errorf("Parse config file %v failed: %s \n", cfgFile, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if panelConfig.LogConfig.Level == "debug" {
|
||||||
|
log.SetReportCaller(true)
|
||||||
|
}
|
||||||
|
|
||||||
p := panel.New(panelConfig)
|
p := panel.New(panelConfig)
|
||||||
lastTime := time.Now()
|
lastTime := time.Now()
|
||||||
config.OnConfigChange(func(e fsnotify.Event) {
|
config.OnConfigChange(func(e fsnotify.Event) {
|
||||||
@ -88,21 +91,31 @@ func main() {
|
|||||||
// Delete old instance and trigger GC
|
// Delete old instance and trigger GC
|
||||||
runtime.GC()
|
runtime.GC()
|
||||||
if err := config.Unmarshal(panelConfig); err != nil {
|
if err := config.Unmarshal(panelConfig); err != nil {
|
||||||
log.Panicf("Parse config file %v failed: %s \n", configFile, err)
|
log.Panicf("Parse config file %v failed: %s \n", cfgFile, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if panelConfig.LogConfig.Level == "debug" {
|
||||||
|
log.SetReportCaller(true)
|
||||||
|
}
|
||||||
|
|
||||||
p.Start()
|
p.Start()
|
||||||
lastTime = time.Now()
|
lastTime = time.Now()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
p.Start()
|
p.Start()
|
||||||
defer p.Close()
|
defer p.Close()
|
||||||
|
|
||||||
// Explicitly triggering GC to remove garbage from config loading.
|
// Explicitly triggering GC to remove garbage from config loading.
|
||||||
runtime.GC()
|
runtime.GC()
|
||||||
// Running backend
|
// Running backend
|
||||||
{
|
osSignals := make(chan os.Signal, 1)
|
||||||
osSignals := make(chan os.Signal, 1)
|
signal.Notify(osSignals, os.Interrupt, os.Kill, syscall.SIGTERM)
|
||||||
signal.Notify(osSignals, os.Interrupt, os.Kill, syscall.SIGTERM)
|
<-osSignals
|
||||||
<-osSignals
|
|
||||||
}
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Execute() error {
|
||||||
|
return rootCmd.Execute()
|
||||||
}
|
}
|
27
cmd/version.go
Normal file
27
cmd/version.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
version = "0.9.5"
|
||||||
|
codename = "XrayR"
|
||||||
|
intro = "A Xray backend that supports many panels"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(&cobra.Command{
|
||||||
|
Use: "version",
|
||||||
|
Short: "Print current version of XrayR",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
showVersion()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func showVersion() {
|
||||||
|
fmt.Printf("%s %s (%s) \n", codename, version, intro)
|
||||||
|
}
|
66
cmd/x25519.go
Normal file
66
cmd/x25519.go
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"golang.org/x/crypto/curve25519"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
priKey string
|
||||||
|
x25519Cmd = &cobra.Command{
|
||||||
|
Use: "x25519",
|
||||||
|
Short: "Generate key pair for x25519 key exchange",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
if err := x25519(); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
x25519Cmd.PersistentFlags().StringVarP(&priKey, "input", "i", "", "Input private key (base64.RawURLEncoding)")
|
||||||
|
rootCmd.AddCommand(x25519Cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func x25519() error {
|
||||||
|
privateKey := make([]byte, curve25519.ScalarSize)
|
||||||
|
|
||||||
|
if priKey == "" {
|
||||||
|
if _, err := rand.Read(privateKey); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
p, err := base64.RawURLEncoding.DecodeString(priKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(p) != curve25519.ScalarSize {
|
||||||
|
return errors.New("invalid private key")
|
||||||
|
}
|
||||||
|
privateKey = p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modify random bytes using algorithm described at:
|
||||||
|
// https://cr.yp.to/ecdh.html.
|
||||||
|
privateKey[0] &= 248
|
||||||
|
privateKey[31] &= 127
|
||||||
|
privateKey[31] |= 64
|
||||||
|
|
||||||
|
publicKey, err := curve25519.X25519(privateKey, curve25519.Basepoint)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
output := fmt.Sprintf("Private key: %v\nPublic key: %v",
|
||||||
|
base64.RawURLEncoding.EncodeToString(privateKey),
|
||||||
|
base64.RawURLEncoding.EncodeToString(publicKey))
|
||||||
|
fmt.Println(output)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -1,7 +0,0 @@
|
|||||||
package limiter
|
|
||||||
|
|
||||||
import "github.com/xtls/xray-core/common/errors"
|
|
||||||
|
|
||||||
func newError(values ...interface{}) *errors.Error {
|
|
||||||
return errors.New(values...)
|
|
||||||
}
|
|
@ -14,8 +14,9 @@ import (
|
|||||||
"github.com/eko/gocache/lib/v4/store"
|
"github.com/eko/gocache/lib/v4/store"
|
||||||
goCacheStore "github.com/eko/gocache/store/go_cache/v4"
|
goCacheStore "github.com/eko/gocache/store/go_cache/v4"
|
||||||
redisStore "github.com/eko/gocache/store/redis/v4"
|
redisStore "github.com/eko/gocache/store/redis/v4"
|
||||||
"github.com/go-redis/redis/v8"
|
|
||||||
goCache "github.com/patrickmn/go-cache"
|
goCache "github.com/patrickmn/go-cache"
|
||||||
|
"github.com/redis/go-redis/v9"
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
"golang.org/x/time/rate"
|
"golang.org/x/time/rate"
|
||||||
|
|
||||||
"github.com/XrayR-project/XrayR/api"
|
"github.com/XrayR-project/XrayR/api"
|
||||||
@ -66,7 +67,9 @@ func (l *Limiter) AddInboundLimiter(tag string, nodeSpeedLimit uint64, userList
|
|||||||
// init redis store
|
// init redis store
|
||||||
rs := redisStore.NewRedis(redis.NewClient(
|
rs := redisStore.NewRedis(redis.NewClient(
|
||||||
&redis.Options{
|
&redis.Options{
|
||||||
|
Network: globalLimit.RedisNetwork,
|
||||||
Addr: globalLimit.RedisAddr,
|
Addr: globalLimit.RedisAddr,
|
||||||
|
Username: globalLimit.RedisUsername,
|
||||||
Password: globalLimit.RedisPassword,
|
Password: globalLimit.RedisPassword,
|
||||||
DB: globalLimit.RedisDB,
|
DB: globalLimit.RedisDB,
|
||||||
}),
|
}),
|
||||||
@ -216,7 +219,7 @@ func (l *Limiter) GetUserBucket(tag string, email string, ip string) (limiter *r
|
|||||||
return nil, false, false
|
return nil, false, false
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
newError("Get Inbound Limiter information failed").AtDebug().WriteToLog()
|
errors.LogDebug(context.Background(), "Get Inbound Limiter information failed")
|
||||||
return nil, false, false
|
return nil, false, false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -236,7 +239,7 @@ func globalLimit(inboundInfo *InboundInfo, email string, uid int, ip string, dev
|
|||||||
// If the email is a new device
|
// If the email is a new device
|
||||||
go pushIP(inboundInfo, uniqueKey, &map[string]int{ip: uid})
|
go pushIP(inboundInfo, uniqueKey, &map[string]int{ip: uid})
|
||||||
} else {
|
} else {
|
||||||
newError("cache service").Base(err).AtError().WriteToLog()
|
errors.LogErrorInner(context.Background(), err, "cache service")
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -262,7 +265,7 @@ func pushIP(inboundInfo *InboundInfo, uniqueKey string, ipMap *map[string]int) {
|
|||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
if err := inboundInfo.GlobalLimit.globalOnlineIP.Set(ctx, uniqueKey, ipMap); err != nil {
|
if err := inboundInfo.GlobalLimit.globalOnlineIP.Set(ctx, uniqueKey, ipMap); err != nil {
|
||||||
newError("cache service").Base(err).AtError().WriteToLog()
|
errors.LogErrorInner(context.Background(), err, "cache service")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,9 @@ package limiter
|
|||||||
|
|
||||||
type GlobalDeviceLimitConfig struct {
|
type GlobalDeviceLimitConfig struct {
|
||||||
Enable bool `mapstructure:"Enable"`
|
Enable bool `mapstructure:"Enable"`
|
||||||
RedisAddr string `mapstructure:"RedisAddr"` // host:port
|
RedisNetwork string `mapstructure:"RedisNetwork"` // tcp or unix
|
||||||
|
RedisAddr string `mapstructure:"RedisAddr"` // host:port, or /path/to/unix.sock
|
||||||
|
RedisUsername string `mapstructure:"RedisUsername"`
|
||||||
RedisPassword string `mapstructure:"RedisPassword"`
|
RedisPassword string `mapstructure:"RedisPassword"`
|
||||||
RedisDB int `mapstructure:"RedisDB"`
|
RedisDB int `mapstructure:"RedisDB"`
|
||||||
Timeout int `mapstructure:"Timeout"`
|
Timeout int `mapstructure:"Timeout"`
|
||||||
|
@ -6,12 +6,13 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"errors"
|
"errors"
|
||||||
"log"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/go-acme/lego/v4/certcrypto"
|
"github.com/go-acme/lego/v4/certcrypto"
|
||||||
"github.com/go-acme/lego/v4/lego"
|
"github.com/go-acme/lego/v4/lego"
|
||||||
"github.com/go-acme/lego/v4/registration"
|
"github.com/go-acme/lego/v4/registration"
|
||||||
|
@ -4,11 +4,12 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/go-acme/lego/v4/certcrypto"
|
"github.com/go-acme/lego/v4/certcrypto"
|
||||||
"github.com/go-acme/lego/v4/certificate"
|
"github.com/go-acme/lego/v4/certificate"
|
||||||
"golang.org/x/net/idna"
|
"golang.org/x/net/idna"
|
||||||
|
@ -149,8 +149,8 @@ func (l *LegoCMD) RenewCert() (CertPath string, KeyPath string, ok bool, err err
|
|||||||
}
|
}
|
||||||
|
|
||||||
func checkCertFile(domain string) (string, string, error) {
|
func checkCertFile(domain string) (string, string, error) {
|
||||||
keyPath := path.Join(defaultPath, "certificates", fmt.Sprintf("%s.key", domain))
|
keyPath := path.Join(defaultPath, "certificates", fmt.Sprintf("%s.key", sanitizedDomain(domain)))
|
||||||
certPath := path.Join(defaultPath, "certificates", fmt.Sprintf("%s.crt", domain))
|
certPath := path.Join(defaultPath, "certificates", fmt.Sprintf("%s.crt", sanitizedDomain(domain)))
|
||||||
if _, err := os.Stat(keyPath); os.IsNotExist(err) {
|
if _, err := os.Stat(keyPath); os.IsNotExist(err) {
|
||||||
return "", "", fmt.Errorf("cert key failed: %s", domain)
|
return "", "", fmt.Errorf("cert key failed: %s", domain)
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,10 @@ package mylego
|
|||||||
import (
|
import (
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"log"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/go-acme/lego/v4/certcrypto"
|
"github.com/go-acme/lego/v4/certcrypto"
|
||||||
"github.com/go-acme/lego/v4/certificate"
|
"github.com/go-acme/lego/v4/certificate"
|
||||||
"github.com/go-acme/lego/v4/lego"
|
"github.com/go-acme/lego/v4/lego"
|
||||||
|
@ -2,11 +2,11 @@ package mylego
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/go-acme/lego/v4/certificate"
|
"github.com/go-acme/lego/v4/certificate"
|
||||||
"github.com/go-acme/lego/v4/lego"
|
"github.com/go-acme/lego/v4/lego"
|
||||||
"github.com/go-acme/lego/v4/registration"
|
"github.com/go-acme/lego/v4/registration"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
const rootPathWarningMessage = `!!!! HEADS UP !!!!
|
const rootPathWarningMessage = `!!!! HEADS UP !!!!
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
package mylego
|
package mylego
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/go-acme/lego/v4/certcrypto"
|
"github.com/go-acme/lego/v4/certcrypto"
|
||||||
"github.com/go-acme/lego/v4/challenge/dns01"
|
"github.com/go-acme/lego/v4/challenge/dns01"
|
||||||
"github.com/go-acme/lego/v4/challenge/http01"
|
"github.com/go-acme/lego/v4/challenge/http01"
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
package rule
|
|
||||||
|
|
||||||
import "github.com/xtls/xray-core/common/errors"
|
|
||||||
|
|
||||||
func newError(values ...interface{}) *errors.Error {
|
|
||||||
return errors.New(values...)
|
|
||||||
}
|
|
@ -2,6 +2,7 @@
|
|||||||
package rule
|
package rule
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -9,6 +10,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
mapset "github.com/deckarep/golang-set"
|
mapset "github.com/deckarep/golang-set"
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
|
|
||||||
"github.com/XrayR-project/XrayR/api"
|
"github.com/XrayR-project/XrayR/api"
|
||||||
)
|
)
|
||||||
@ -65,7 +67,7 @@ func (r *Manager) Detect(tag string, destination string, email string) (reject b
|
|||||||
l := strings.Split(email, "|")
|
l := strings.Split(email, "|")
|
||||||
uid, err := strconv.Atoi(l[len(l)-1])
|
uid, err := strconv.Atoi(l[len(l)-1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
newError(fmt.Sprintf("Record illegal behavior failed! Cannot find user's uid: %s", email)).AtDebug().WriteToLog()
|
errors.LogDebug(context.Background(), fmt.Sprintf("Record illegal behavior failed! Cannot find user's uid: %s", email))
|
||||||
return reject
|
return reject
|
||||||
}
|
}
|
||||||
newSet := mapset.NewSetWith(api.DetectResult{UID: uid, RuleID: hitRuleID})
|
newSet := mapset.NewSetWith(api.DetectResult{UID: uid, RuleID: hitRuleID})
|
||||||
|
380
go.mod
380
go.mod
@ -1,193 +1,311 @@
|
|||||||
module github.com/XrayR-project/XrayR
|
module github.com/XrayR-project/XrayR
|
||||||
|
|
||||||
go 1.20
|
go 1.24
|
||||||
|
|
||||||
|
toolchain go1.24.1
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/bitly/go-simplejson v0.5.0
|
dario.cat/mergo v1.0.0
|
||||||
|
github.com/bitly/go-simplejson v0.5.1
|
||||||
github.com/deckarep/golang-set v1.8.0
|
github.com/deckarep/golang-set v1.8.0
|
||||||
github.com/eko/gocache/lib/v4 v4.1.2
|
github.com/eko/gocache/lib/v4 v4.1.6
|
||||||
github.com/eko/gocache/store/go_cache/v4 v4.1.2
|
github.com/eko/gocache/store/go_cache/v4 v4.2.2
|
||||||
github.com/eko/gocache/store/redis/v4 v4.1.2
|
github.com/eko/gocache/store/redis/v4 v4.2.1
|
||||||
github.com/fsnotify/fsnotify v1.6.0
|
github.com/fsnotify/fsnotify v1.7.0
|
||||||
github.com/go-acme/lego/v4 v4.9.2-0.20221210101705-6695fcc87344
|
github.com/go-acme/lego/v4 v4.16.1
|
||||||
github.com/go-redis/redis/v8 v8.11.5
|
github.com/go-resty/resty/v2 v2.13.1
|
||||||
github.com/go-resty/resty/v2 v2.7.0
|
github.com/gogf/gf/v2 v2.7.0
|
||||||
github.com/imdario/mergo v0.3.13
|
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||||
github.com/r3labs/diff/v2 v2.15.1
|
github.com/r3labs/diff/v2 v2.15.1
|
||||||
github.com/sagernet/sing v0.1.7
|
github.com/redis/go-redis/v9 v9.7.0
|
||||||
github.com/sagernet/sing-shadowsocks v0.1.1-0.20230202035033-e3123545f2f7
|
github.com/sagernet/sing v0.5.1
|
||||||
github.com/shirou/gopsutil/v3 v3.23.1
|
github.com/sagernet/sing-shadowsocks v0.2.7
|
||||||
github.com/spf13/viper v1.15.0
|
github.com/shirou/gopsutil/v3 v3.24.5
|
||||||
github.com/stretchr/testify v1.8.1
|
github.com/sirupsen/logrus v1.9.3
|
||||||
github.com/xtls/xray-core v1.7.5
|
github.com/spf13/cobra v1.8.1
|
||||||
golang.org/x/crypto v0.6.0
|
github.com/spf13/viper v1.18.2
|
||||||
golang.org/x/net v0.7.0
|
github.com/stretchr/testify v1.10.0
|
||||||
golang.org/x/time v0.3.0
|
github.com/xtls/xray-core v1.250306.0
|
||||||
google.golang.org/protobuf v1.28.1
|
golang.org/x/crypto v0.36.0
|
||||||
|
golang.org/x/net v0.37.0
|
||||||
|
golang.org/x/time v0.7.0
|
||||||
|
google.golang.org/protobuf v1.36.5
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go/compute v1.15.1 // indirect
|
cloud.google.com/go/compute/metadata v0.6.0 // indirect
|
||||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 // indirect
|
||||||
github.com/Azure/azure-sdk-for-go v32.4.0+incompatible // indirect
|
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 // indirect
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0 // indirect
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0 // indirect
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 // indirect
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.2.0 // indirect
|
||||||
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||||
github.com/Azure/go-autorest/autorest v0.11.24 // indirect
|
github.com/Azure/go-autorest/autorest v0.11.29 // indirect
|
||||||
github.com/Azure/go-autorest/autorest/adal v0.9.18 // indirect
|
github.com/Azure/go-autorest/autorest/adal v0.9.23 // indirect
|
||||||
github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 // indirect
|
github.com/Azure/go-autorest/autorest/azure/auth v0.5.12 // indirect
|
||||||
github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 // indirect
|
github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 // indirect
|
||||||
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
|
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
|
||||||
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
|
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
|
||||||
github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect
|
|
||||||
github.com/Azure/go-autorest/logger v0.2.1 // indirect
|
github.com/Azure/go-autorest/logger v0.2.1 // indirect
|
||||||
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
|
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
|
||||||
|
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect
|
||||||
|
github.com/BurntSushi/toml v1.3.2 // indirect
|
||||||
|
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect
|
||||||
|
github.com/CloudyKit/jet/v6 v6.2.0 // indirect
|
||||||
|
github.com/Joker/jade v1.1.3 // indirect
|
||||||
|
github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0 // indirect
|
||||||
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 // indirect
|
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 // indirect
|
||||||
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.1 // indirect
|
github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06 // indirect
|
||||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755 // indirect
|
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 // indirect
|
||||||
github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect
|
github.com/aliyun/alibaba-cloud-sdk-go v1.62.695 // indirect
|
||||||
github.com/andybalholm/brotli v1.0.4 // indirect
|
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||||
github.com/aws/aws-sdk-go v1.39.0 // indirect
|
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2 v1.25.3 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/config v1.27.7 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/credentials v1.17.7 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.3 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.3 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.3 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.1 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.5 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/lightsail v1.36.2 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/route53 v1.40.2 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sso v1.20.2 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.2 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sts v1.28.4 // indirect
|
||||||
|
github.com/aws/smithy-go v1.20.1 // indirect
|
||||||
|
github.com/aymerick/douceur v0.2.0 // indirect
|
||||||
|
github.com/benbjohnson/clock v1.3.5 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
|
github.com/boombuler/barcode v1.0.1 // indirect
|
||||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
github.com/bytedance/sonic v1.11.3 // indirect
|
||||||
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
|
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/civo/civogo v0.3.11 // indirect
|
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
|
||||||
github.com/cloudflare/cloudflare-go v0.49.0 // indirect
|
github.com/chenzhuoyu/iasm v0.9.1 // indirect
|
||||||
|
github.com/civo/civogo v0.3.63 // indirect
|
||||||
|
github.com/clbanning/mxj/v2 v2.7.0 // indirect
|
||||||
|
github.com/cloudflare/circl v1.6.0 // indirect
|
||||||
|
github.com/cloudflare/cloudflare-go v0.90.0 // indirect
|
||||||
github.com/cpu/goacmedns v0.1.1 // indirect
|
github.com/cpu/goacmedns v0.1.1 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||||
github.com/deepmap/oapi-codegen v1.9.1 // indirect
|
github.com/deepmap/oapi-codegen v1.16.2 // indirect
|
||||||
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect
|
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
github.com/dimchansky/utfbom v1.1.1 // indirect
|
github.com/dimchansky/utfbom v1.1.1 // indirect
|
||||||
github.com/dnsimple/dnsimple-go v0.71.1 // indirect
|
github.com/dnsimple/dnsimple-go v1.7.0 // indirect
|
||||||
github.com/exoscale/egoscale v0.93.0 // indirect
|
github.com/exoscale/egoscale v1.19.0 // indirect
|
||||||
|
github.com/fatih/color v1.16.0 // indirect
|
||||||
github.com/fatih/structs v1.1.0 // indirect
|
github.com/fatih/structs v1.1.0 // indirect
|
||||||
github.com/francoispqt/gojay v1.2.13 // indirect
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
|
github.com/flosch/pongo2/v4 v4.0.2 // indirect
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||||
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 // indirect
|
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 // indirect
|
||||||
github.com/go-errors/errors v1.0.1 // indirect
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
github.com/gin-gonic/gin v1.9.1 // indirect
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
github.com/go-errors/errors v1.5.1 // indirect
|
||||||
github.com/gofrs/uuid v4.3.1+incompatible // indirect
|
github.com/go-jose/go-jose/v4 v4.0.1 // indirect
|
||||||
github.com/golang-jwt/jwt/v4 v4.2.0 // indirect
|
github.com/go-logr/logr v1.4.2 // indirect
|
||||||
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
|
github.com/go-ole/go-ole v1.3.0 // 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.19.0 // indirect
|
||||||
|
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 // indirect
|
||||||
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
|
github.com/gofrs/uuid v4.4.0+incompatible // indirect
|
||||||
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
|
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
github.com/golang/mock v1.6.0 // indirect
|
github.com/golang/mock v1.7.0-rc.1 // indirect
|
||||||
github.com/golang/protobuf v1.5.2 // indirect
|
github.com/golang/protobuf v1.5.4 // indirect
|
||||||
|
github.com/golang/snappy v0.0.4 // indirect
|
||||||
|
github.com/gomarkdown/markdown v0.0.0-20231222211730-1d6d20845b47 // indirect
|
||||||
github.com/google/btree v1.1.2 // indirect
|
github.com/google/btree v1.1.2 // indirect
|
||||||
github.com/google/go-querystring v1.1.0 // indirect
|
github.com/google/go-querystring v1.1.0 // indirect
|
||||||
github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect
|
github.com/google/gofuzz v1.2.0 // indirect
|
||||||
github.com/google/uuid v1.3.0 // indirect
|
github.com/google/pprof v0.0.0-20240528025155-186aa0362fba // indirect
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.2.1 // indirect
|
github.com/google/s2a-go v0.1.7 // indirect
|
||||||
github.com/googleapis/gax-go/v2 v2.7.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/gophercloud/gophercloud v1.0.0 // indirect
|
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
|
||||||
github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae // indirect
|
github.com/googleapis/gax-go/v2 v2.12.3 // indirect
|
||||||
github.com/gorilla/websocket v1.5.0 // indirect
|
github.com/gophercloud/gophercloud v1.11.0 // indirect
|
||||||
github.com/hashicorp/errwrap v1.0.0 // indirect
|
github.com/gophercloud/utils v0.0.0-20231010081019-80377eca5d56 // indirect
|
||||||
|
github.com/gorilla/css v1.0.1 // indirect
|
||||||
|
github.com/gorilla/websocket v1.5.3 // indirect
|
||||||
|
github.com/grokify/html-strip-tags-go v0.0.1 // indirect
|
||||||
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.1 // indirect
|
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
|
||||||
|
github.com/hashicorp/go-uuid v1.0.3 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect
|
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect
|
||||||
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/infobloxopen/infoblox-go-client v1.1.1 // indirect
|
github.com/infobloxopen/infoblox-go-client v1.1.1 // indirect
|
||||||
|
github.com/iris-contrib/schema v0.0.6 // indirect
|
||||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||||
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 // indirect
|
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 // indirect
|
||||||
github.com/klauspost/compress v1.15.15 // indirect
|
github.com/kataras/blocks v0.0.8 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.3 // indirect
|
github.com/kataras/golog v0.1.11 // indirect
|
||||||
github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b // indirect
|
github.com/kataras/iris/v12 v12.2.10 // indirect
|
||||||
|
github.com/kataras/pio v0.0.13 // indirect
|
||||||
|
github.com/kataras/sitemap v0.0.6 // indirect
|
||||||
|
github.com/kataras/tunnel v0.0.4 // indirect
|
||||||
|
github.com/klauspost/compress v1.17.8 // indirect
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||||
|
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b // indirect
|
||||||
|
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||||
github.com/labbsr0x/bindman-dns-webhook v1.0.2 // indirect
|
github.com/labbsr0x/bindman-dns-webhook v1.0.2 // indirect
|
||||||
github.com/labbsr0x/goh v1.0.1 // indirect
|
github.com/labbsr0x/goh v1.0.1 // indirect
|
||||||
github.com/linode/linodego v1.9.1 // indirect
|
github.com/labstack/echo/v4 v4.11.4 // indirect
|
||||||
github.com/liquidweb/go-lwApi v0.0.5 // indirect
|
github.com/labstack/gommon v0.4.2 // indirect
|
||||||
github.com/liquidweb/liquidweb-cli v0.6.9 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/liquidweb/liquidweb-go v1.6.3 // indirect
|
github.com/linode/linodego v1.30.0 // indirect
|
||||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
github.com/liquidweb/liquidweb-cli v0.7.0 // indirect
|
||||||
|
github.com/liquidweb/liquidweb-go v1.6.4 // indirect
|
||||||
|
github.com/lufia/plan9stats v0.0.0-20240226150601-1dcf7310316a // indirect
|
||||||
github.com/magiconair/properties v1.8.7 // indirect
|
github.com/magiconair/properties v1.8.7 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
github.com/mailgun/raymond/v2 v2.0.48 // indirect
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
github.com/mailru/easyjson v0.7.7 // indirect
|
||||||
github.com/miekg/dns v1.1.50 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mimuret/golang-iij-dpf v0.7.1 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||||
|
github.com/microcosm-cc/bluemonday v1.0.26 // indirect
|
||||||
|
github.com/miekg/dns v1.1.63 // indirect
|
||||||
|
github.com/mimuret/golang-iij-dpf v0.9.1 // indirect
|
||||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 // indirect
|
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 // indirect
|
||||||
github.com/nrdcg/auroradns v1.1.0 // indirect
|
github.com/nrdcg/auroradns v1.1.0 // indirect
|
||||||
github.com/nrdcg/desec v0.6.0 // indirect
|
github.com/nrdcg/bunny-go v0.0.0-20240207213615-dde5bf4577a3 // indirect
|
||||||
|
github.com/nrdcg/desec v0.7.0 // indirect
|
||||||
github.com/nrdcg/dnspod-go v0.4.0 // indirect
|
github.com/nrdcg/dnspod-go v0.4.0 // indirect
|
||||||
github.com/nrdcg/freemyip v0.2.0 // indirect
|
github.com/nrdcg/freemyip v0.2.0 // indirect
|
||||||
github.com/nrdcg/goinwx v0.8.1 // indirect
|
github.com/nrdcg/goinwx v0.10.0 // indirect
|
||||||
|
github.com/nrdcg/mailinabox v0.2.0 // indirect
|
||||||
github.com/nrdcg/namesilo v0.2.1 // indirect
|
github.com/nrdcg/namesilo v0.2.1 // indirect
|
||||||
github.com/nrdcg/porkbun v0.1.1 // indirect
|
github.com/nrdcg/nodion v0.1.0 // indirect
|
||||||
github.com/onsi/ginkgo/v2 v2.8.0 // indirect
|
github.com/nrdcg/porkbun v0.3.0 // indirect
|
||||||
|
github.com/nzdjb/go-metaname v1.0.0 // indirect
|
||||||
|
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||||
|
github.com/onsi/ginkgo/v2 v2.19.0 // indirect
|
||||||
|
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
|
||||||
github.com/oracle/oci-go-sdk v24.3.0+incompatible // indirect
|
github.com/oracle/oci-go-sdk v24.3.0+incompatible // indirect
|
||||||
github.com/ovh/go-ovh v1.1.0 // indirect
|
github.com/ovh/go-ovh v1.4.3 // indirect
|
||||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
|
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
|
||||||
github.com/pires/go-proxyproto v0.6.2 // indirect
|
github.com/pires/go-proxyproto v0.8.0 // indirect
|
||||||
|
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||||
github.com/pquerna/otp v1.3.0 // indirect
|
github.com/pquerna/otp v1.4.0 // indirect
|
||||||
github.com/prometheus/client_golang v1.14.0 // indirect
|
github.com/prometheus/client_golang v1.19.1 // indirect
|
||||||
github.com/prometheus/client_model v0.3.0 // indirect
|
github.com/prometheus/client_model v0.6.0 // indirect
|
||||||
github.com/prometheus/common v0.37.0 // indirect
|
github.com/prometheus/common v0.50.0 // indirect
|
||||||
github.com/prometheus/procfs v0.8.0 // indirect
|
github.com/prometheus/procfs v0.13.0 // indirect
|
||||||
github.com/quic-go/qtls-go1-18 v0.2.0 // indirect
|
github.com/quic-go/qpack v0.5.1 // indirect
|
||||||
github.com/quic-go/qtls-go1-19 v0.2.0 // indirect
|
github.com/quic-go/quic-go v0.50.0 // indirect
|
||||||
github.com/quic-go/qtls-go1-20 v0.1.0 // indirect
|
github.com/refraction-networking/utls v1.6.7 // indirect
|
||||||
github.com/quic-go/quic-go v0.32.0 // indirect
|
|
||||||
github.com/refraction-networking/utls v1.2.2-0.20230207151345-a75a4b484849 // indirect
|
|
||||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
||||||
github.com/sacloud/api-client-go v0.2.1 // indirect
|
github.com/rivo/uniseg v0.2.0 // indirect
|
||||||
github.com/sacloud/go-http v0.1.2 // indirect
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
github.com/sacloud/iaas-api-go v1.3.2 // indirect
|
github.com/sacloud/api-client-go v0.2.10 // indirect
|
||||||
github.com/sacloud/packages-go v0.0.5 // indirect
|
github.com/sacloud/go-http v0.1.8 // indirect
|
||||||
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c // indirect
|
github.com/sacloud/iaas-api-go v1.11.2 // indirect
|
||||||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9 // indirect
|
github.com/sacloud/packages-go v0.0.10 // indirect
|
||||||
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb // indirect
|
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||||
|
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.25 // indirect
|
||||||
|
github.com/schollz/closestmatch v2.1.0+incompatible // indirect
|
||||||
|
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771 // indirect
|
||||||
|
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||||
|
github.com/shopspring/decimal v1.3.1 // indirect
|
||||||
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 // indirect
|
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 // indirect
|
||||||
github.com/softlayer/softlayer-go v1.0.6 // indirect
|
github.com/softlayer/softlayer-go v1.1.3 // indirect
|
||||||
github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect
|
github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect
|
||||||
github.com/spf13/afero v1.9.3 // indirect
|
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||||
github.com/spf13/cast v1.5.0 // indirect
|
github.com/spf13/afero v1.11.0 // indirect
|
||||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
github.com/spf13/cast v1.6.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/stretchr/objx v0.5.0 // indirect
|
github.com/stretchr/objx v0.5.2 // indirect
|
||||||
github.com/subosito/gotenv v1.4.2 // indirect
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.490 // indirect
|
github.com/tdewolff/minify/v2 v2.20.19 // indirect
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.490 // indirect
|
github.com/tdewolff/parse/v2 v2.7.12 // indirect
|
||||||
github.com/tklauser/go-sysconf v0.3.11 // indirect
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.878 // indirect
|
||||||
github.com/tklauser/numcpus v0.6.0 // indirect
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.878 // indirect
|
||||||
github.com/transip/gotransip/v6 v6.17.0 // indirect
|
github.com/tklauser/go-sysconf v0.3.13 // indirect
|
||||||
|
github.com/tklauser/numcpus v0.7.0 // indirect
|
||||||
|
github.com/transip/gotransip/v6 v6.23.0 // indirect
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
|
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||||
|
github.com/ultradns/ultradns-go-sdk v1.6.1-20231103022937-8589b6a // indirect
|
||||||
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e // indirect
|
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e // indirect
|
||||||
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
|
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||||
github.com/vinyldns/go-vinyldns v0.9.16 // indirect
|
github.com/vinyldns/go-vinyldns v0.9.16 // indirect
|
||||||
|
github.com/vishvananda/netlink v1.3.0 // indirect
|
||||||
|
github.com/vishvananda/netns v0.0.4 // indirect
|
||||||
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
|
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
|
||||||
|
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
|
||||||
|
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||||
github.com/vultr/govultr/v2 v2.17.2 // indirect
|
github.com/vultr/govultr/v2 v2.17.2 // indirect
|
||||||
github.com/xtls/go v0.0.0-20230107031059-4610f88d00f3 // indirect
|
github.com/xtls/reality v0.0.0-20240712055506-48f0b2d5ed6d // indirect
|
||||||
github.com/yandex-cloud/go-genproto v0.0.0-20220805142335-27b56ddae16f // indirect
|
github.com/yandex-cloud/go-genproto v0.0.0-20240311082839-58e1a7554a75 // indirect
|
||||||
github.com/yandex-cloud/go-sdk v0.0.0-20220805164847-cf028e604997 // indirect
|
github.com/yandex-cloud/go-sdk v0.0.0-20240311083148-81c0846b96cd // indirect
|
||||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
github.com/yosssi/ace v0.0.5 // indirect
|
||||||
|
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||||
go.opencensus.io v0.24.0 // indirect
|
go.opencensus.io v0.24.0 // indirect
|
||||||
go.starlark.net v0.0.0-20230128213706-3f75dec8e403 // indirect
|
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||||
go.uber.org/atomic v1.10.0 // indirect
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
|
||||||
go.uber.org/ratelimit v0.2.0 // indirect
|
go.opentelemetry.io/otel v1.34.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20230206171751-46f607a40771 // indirect
|
go.opentelemetry.io/otel/metric v1.34.0 // indirect
|
||||||
golang.org/x/mod v0.7.0 // indirect
|
go.opentelemetry.io/otel/sdk v1.34.0 // indirect
|
||||||
golang.org/x/oauth2 v0.4.0 // indirect
|
go.opentelemetry.io/otel/trace v1.34.0 // indirect
|
||||||
golang.org/x/sys v0.5.0 // indirect
|
go.uber.org/atomic v1.11.0 // indirect
|
||||||
golang.org/x/text v0.7.0 // indirect
|
go.uber.org/mock v0.5.0 // indirect
|
||||||
golang.org/x/tools v0.5.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
google.golang.org/api v0.107.0 // indirect
|
go.uber.org/ratelimit v0.3.1 // indirect
|
||||||
google.golang.org/appengine v1.6.7 // indirect
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
|
||||||
google.golang.org/genproto v0.0.0-20230202175211-008b39050e57 // indirect
|
golang.org/x/arch v0.7.0 // indirect
|
||||||
google.golang.org/grpc v1.53.0 // indirect
|
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc // indirect
|
||||||
|
golang.org/x/mod v0.21.0 // indirect
|
||||||
|
golang.org/x/oauth2 v0.25.0 // indirect
|
||||||
|
golang.org/x/sync v0.12.0 // indirect
|
||||||
|
golang.org/x/sys v0.31.0 // indirect
|
||||||
|
golang.org/x/text v0.23.0 // indirect
|
||||||
|
golang.org/x/tools v0.26.0 // indirect
|
||||||
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||||
|
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 // indirect
|
||||||
|
google.golang.org/api v0.170.0 // indirect
|
||||||
|
google.golang.org/appengine v1.6.8 // indirect
|
||||||
|
google.golang.org/genproto v0.0.0-20240314234333-6e1732d8331c // indirect
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 // indirect
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
|
||||||
|
google.golang.org/grpc v1.71.0 // indirect
|
||||||
|
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
gopkg.in/ns1/ns1-go.v2 v2.6.5 // indirect
|
gopkg.in/ns1/ns1-go.v2 v2.9.0 // indirect
|
||||||
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c // indirect
|
gvisor.dev/gvisor v0.0.0-20240320123526-dc6abceb7ff0 // indirect
|
||||||
lukechampine.com/blake3 v1.1.7 // indirect
|
k8s.io/api v0.29.2 // indirect
|
||||||
|
k8s.io/apimachinery v0.29.2 // indirect
|
||||||
|
k8s.io/klog/v2 v2.120.1 // indirect
|
||||||
|
k8s.io/utils v0.0.0-20240310230437-4693a0247e57 // indirect
|
||||||
|
lukechampine.com/blake3 v1.4.0 // indirect
|
||||||
|
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
|
||||||
|
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
|
replace github.com/exoscale/egoscale => github.com/exoscale/egoscale v0.102.3
|
||||||
|
13
main.go
Normal file
13
main.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/XrayR-project/XrayR/cmd"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if err := cmd.Execute(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
@ -1,91 +0,0 @@
|
|||||||
Log:
|
|
||||||
Level: warning # Log level: none, error, warning, info, debug
|
|
||||||
AccessPath: # /etc/XrayR/access.Log
|
|
||||||
ErrorPath: # /etc/XrayR/error.log
|
|
||||||
DnsConfigPath: # /etc/XrayR/dns.json # Path to dns config, check https://xtls.github.io/config/dns.html for help
|
|
||||||
RouteConfigPath: # /etc/XrayR/route.json # Path to route config, check https://xtls.github.io/config/routing.html for help
|
|
||||||
InboundConfigPath: # /etc/XrayR/custom_inbound.json # Path to custom inbound config, check https://xtls.github.io/config/inbound.html for help
|
|
||||||
OutboundConfigPath: # /etc/XrayR/custom_outbound.json # Path to custom outbound config, check https://xtls.github.io/config/outbound.html for help
|
|
||||||
ConnectionConfig:
|
|
||||||
Handshake: 4 # Handshake time limit, Second
|
|
||||||
ConnIdle: 30 # Connection idle time limit, Second
|
|
||||||
UplinkOnly: 2 # Time limit when the connection downstream is closed, Second
|
|
||||||
DownlinkOnly: 4 # Time limit when the connection is closed after the uplink is closed, Second
|
|
||||||
BufferSize: 64 # The internal cache size of each connection, kB
|
|
||||||
Nodes:
|
|
||||||
-
|
|
||||||
PanelType: "SSpanel" # Panel type: SSpanel, V2board, NewV2board, PMpanel, Proxypanel, V2RaySocks
|
|
||||||
ApiConfig:
|
|
||||||
ApiHost: "http://127.0.0.1:667"
|
|
||||||
ApiKey: "123"
|
|
||||||
NodeID: 41
|
|
||||||
NodeType: V2ray # Node type: V2ray, Shadowsocks, Trojan, Shadowsocks-Plugin
|
|
||||||
Timeout: 30 # Timeout for the api request
|
|
||||||
EnableVless: false # Enable Vless for V2ray Type
|
|
||||||
EnableXTLS: false # Enable XTLS for V2ray and Trojan
|
|
||||||
SpeedLimit: 0 # Mbps, Local settings will replace remote settings, 0 means disable
|
|
||||||
DeviceLimit: 0 # Local settings will replace remote settings, 0 means disable
|
|
||||||
RuleListPath: # /etc/XrayR/rulelist Path to local rulelist file
|
|
||||||
ControllerConfig:
|
|
||||||
ListenIP: 0.0.0.0 # IP address you want to listen
|
|
||||||
SendIP: 0.0.0.0 # IP address you want to send pacakage
|
|
||||||
UpdatePeriodic: 60 # Time to update the nodeinfo, how many sec.
|
|
||||||
EnableDNS: false # Use custom DNS config, Please ensure that you set the dns.json well
|
|
||||||
DNSType: AsIs # AsIs, UseIP, UseIPv4, UseIPv6, DNS strategy
|
|
||||||
EnableProxyProtocol: false # Only works for WebSocket and TCP
|
|
||||||
AutoSpeedLimitConfig:
|
|
||||||
Limit: 0 # Warned speed. Set to 0 to disable AutoSpeedLimit (mbps)
|
|
||||||
WarnTimes: 0 # After (WarnTimes) consecutive warnings, the user will be limited. Set to 0 to punish overspeed user immediately.
|
|
||||||
LimitSpeed: 0 # The speedlimit of a limited user (unit: mbps)
|
|
||||||
LimitDuration: 0 # How many minutes will the limiting last (unit: minute)
|
|
||||||
GlobalDeviceLimitConfig:
|
|
||||||
Enable: false # Enable the global device limit of a user
|
|
||||||
RedisAddr: 127.0.0.1:6379 # The redis server address
|
|
||||||
RedisPassword: YOUR PASSWORD # Redis password
|
|
||||||
RedisDB: 0 # Redis DB
|
|
||||||
Timeout: 5 # Timeout for redis request
|
|
||||||
Expiry: 60 # Expiry time (second)
|
|
||||||
EnableFallback: false # Only support for Trojan and Vless
|
|
||||||
FallBackConfigs: # Support multiple fallbacks
|
|
||||||
-
|
|
||||||
SNI: # TLS SNI(Server Name Indication), Empty for any
|
|
||||||
Alpn: # Alpn, Empty for any
|
|
||||||
Path: # HTTP PATH, Empty for any
|
|
||||||
Dest: 80 # Required, Destination of fallback, check https://xtls.github.io/config/features/fallback.html for details.
|
|
||||||
ProxyProtocolVer: 0 # Send PROXY protocol version, 0 for dsable
|
|
||||||
CertConfig:
|
|
||||||
CertMode: dns # Option about how to get certificate: none, file, http, tls, dns. Choose "none" will forcedly disable the tls config.
|
|
||||||
CertDomain: "node1.test.com" # Domain to cert
|
|
||||||
CertFile: /etc/XrayR/cert/node1.test.com.cert # Provided if the CertMode is file
|
|
||||||
KeyFile: /etc/XrayR/cert/node1.test.com.key
|
|
||||||
Provider: alidns # DNS cert provider, Get the full support list here: https://go-acme.github.io/lego/dns/
|
|
||||||
Email: test@me.com
|
|
||||||
DNSEnv: # DNS ENV option used by DNS provider
|
|
||||||
ALICLOUD_ACCESS_KEY: aaa
|
|
||||||
ALICLOUD_SECRET_KEY: bbb
|
|
||||||
# -
|
|
||||||
# PanelType: "NewV2board" # Panel type: SSpanel, V2board, NewV2board, PMpanel, Proxypanel, V2RaySocks
|
|
||||||
# ApiConfig:
|
|
||||||
# ApiHost: "http://127.0.0.1:668"
|
|
||||||
# ApiKey: "123"
|
|
||||||
# NodeID: 4
|
|
||||||
# NodeType: Shadowsocks # Node type: V2ray, Shadowsocks, Trojan
|
|
||||||
# Timeout: 30 # Timeout for the api request
|
|
||||||
# EnableVless: false # Enable Vless for V2ray Type
|
|
||||||
# EnableXTLS: false # Enable XTLS for V2ray and Trojan
|
|
||||||
# SpeedLimit: 0 # Mbps, Local settings will replace remote settings
|
|
||||||
# DeviceLimit: 0 # Local settings will replace remote settings
|
|
||||||
# ControllerConfig:
|
|
||||||
# ListenIP: 0.0.0.0 # IP address you want to listen
|
|
||||||
# UpdatePeriodic: 10 # Time to update the nodeinfo, how many sec.
|
|
||||||
# EnableDNS: false # Use custom DNS config, Please ensure that you set the dns.json well
|
|
||||||
# CertConfig:
|
|
||||||
# CertMode: dns # Option about how to get certificate: none, file, http, dns
|
|
||||||
# CertDomain: "node1.test.com" # Domain to cert
|
|
||||||
# CertFile: /etc/XrayR/cert/node1.test.com.cert # Provided if the CertMode is file
|
|
||||||
# KeyFile: /etc/XrayR/cert/node1.test.com.pem
|
|
||||||
# Provider: alidns # DNS cert provider, Get the full support list here: https://go-acme.github.io/lego/dns/
|
|
||||||
# Email: test@me.com
|
|
||||||
# DNSEnv: # DNS ENV option used by DNS provider
|
|
||||||
# ALICLOUD_ACCESS_KEY: aaa
|
|
||||||
# ALICLOUD_SECRET_KEY: bbb
|
|
@ -2,15 +2,12 @@ package panel
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/XrayR-project/XrayR/api/newV2board"
|
"dario.cat/mergo"
|
||||||
"github.com/XrayR-project/XrayR/app/mydispatcher"
|
|
||||||
|
|
||||||
"github.com/imdario/mergo"
|
|
||||||
"github.com/r3labs/diff/v2"
|
"github.com/r3labs/diff/v2"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/xtls/xray-core/app/proxyman"
|
"github.com/xtls/xray-core/app/proxyman"
|
||||||
"github.com/xtls/xray-core/app/stats"
|
"github.com/xtls/xray-core/app/stats"
|
||||||
"github.com/xtls/xray-core/common/serial"
|
"github.com/xtls/xray-core/common/serial"
|
||||||
@ -18,12 +15,15 @@ import (
|
|||||||
"github.com/xtls/xray-core/infra/conf"
|
"github.com/xtls/xray-core/infra/conf"
|
||||||
|
|
||||||
"github.com/XrayR-project/XrayR/api"
|
"github.com/XrayR-project/XrayR/api"
|
||||||
|
"github.com/XrayR-project/XrayR/api/bunpanel"
|
||||||
|
"github.com/XrayR-project/XrayR/api/gov2panel"
|
||||||
|
"github.com/XrayR-project/XrayR/api/newV2board"
|
||||||
"github.com/XrayR-project/XrayR/api/pmpanel"
|
"github.com/XrayR-project/XrayR/api/pmpanel"
|
||||||
"github.com/XrayR-project/XrayR/api/proxypanel"
|
"github.com/XrayR-project/XrayR/api/proxypanel"
|
||||||
"github.com/XrayR-project/XrayR/api/sspanel"
|
"github.com/XrayR-project/XrayR/api/sspanel"
|
||||||
"github.com/XrayR-project/XrayR/api/v2board"
|
|
||||||
"github.com/XrayR-project/XrayR/api/v2raysocks"
|
"github.com/XrayR-project/XrayR/api/v2raysocks"
|
||||||
_ "github.com/XrayR-project/XrayR/main/distro/all"
|
"github.com/XrayR-project/XrayR/app/mydispatcher"
|
||||||
|
_ "github.com/XrayR-project/XrayR/cmd/distro/all"
|
||||||
"github.com/XrayR-project/XrayR/service"
|
"github.com/XrayR-project/XrayR/service"
|
||||||
"github.com/XrayR-project/XrayR/service/controller"
|
"github.com/XrayR-project/XrayR/service/controller"
|
||||||
)
|
)
|
||||||
@ -154,7 +154,6 @@ func (p *Panel) loadCore(panelConfig *Config) *core.Instance {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panicf("failed to create instance: %s", err)
|
log.Panicf("failed to create instance: %s", err)
|
||||||
}
|
}
|
||||||
log.Printf("Xray Core Version: %s", core.Version())
|
|
||||||
|
|
||||||
return server
|
return server
|
||||||
}
|
}
|
||||||
@ -177,10 +176,7 @@ func (p *Panel) Start() {
|
|||||||
switch nodeConfig.PanelType {
|
switch nodeConfig.PanelType {
|
||||||
case "SSpanel":
|
case "SSpanel":
|
||||||
apiClient = sspanel.New(nodeConfig.ApiConfig)
|
apiClient = sspanel.New(nodeConfig.ApiConfig)
|
||||||
// todo Deprecated after 2023.6.1
|
case "NewV2board", "V2board":
|
||||||
case "V2board":
|
|
||||||
apiClient = v2board.New(nodeConfig.ApiConfig)
|
|
||||||
case "NewV2board":
|
|
||||||
apiClient = newV2board.New(nodeConfig.ApiConfig)
|
apiClient = newV2board.New(nodeConfig.ApiConfig)
|
||||||
case "PMpanel":
|
case "PMpanel":
|
||||||
apiClient = pmpanel.New(nodeConfig.ApiConfig)
|
apiClient = pmpanel.New(nodeConfig.ApiConfig)
|
||||||
@ -188,6 +184,10 @@ func (p *Panel) Start() {
|
|||||||
apiClient = proxypanel.New(nodeConfig.ApiConfig)
|
apiClient = proxypanel.New(nodeConfig.ApiConfig)
|
||||||
case "V2RaySocks":
|
case "V2RaySocks":
|
||||||
apiClient = v2raysocks.New(nodeConfig.ApiConfig)
|
apiClient = v2raysocks.New(nodeConfig.ApiConfig)
|
||||||
|
case "GoV2Panel":
|
||||||
|
apiClient = gov2panel.New(nodeConfig.ApiConfig)
|
||||||
|
case "BunPanel":
|
||||||
|
apiClient = bunpanel.New(nodeConfig.ApiConfig)
|
||||||
default:
|
default:
|
||||||
log.Panicf("Unsupport panel type: %s", nodeConfig.PanelType)
|
log.Panicf("Unsupport panel type: %s", nodeConfig.PanelType)
|
||||||
}
|
}
|
||||||
@ -208,7 +208,7 @@ func (p *Panel) Start() {
|
|||||||
for _, s := range p.Service {
|
for _, s := range p.Service {
|
||||||
err := s.Start()
|
err := s.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panicf("Panel Start fialed: %s", err)
|
log.Panicf("Panel Start failed: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.Running = true
|
p.Running = true
|
||||||
@ -222,7 +222,7 @@ func (p *Panel) Close() {
|
|||||||
for _, s := range p.Service {
|
for _, s := range p.Service {
|
||||||
err := s.Close()
|
err := s.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panicf("Panel Close fialed: %s", err)
|
log.Panicf("Panel Close failed: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.Service = nil
|
p.Service = nil
|
||||||
|
144
release/config/config.yml.example
Normal file
144
release/config/config.yml.example
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
Log:
|
||||||
|
Level: warning # Log level: none, error, warning, info, debug
|
||||||
|
AccessPath: # /etc/XrayR/access.Log
|
||||||
|
ErrorPath: # /etc/XrayR/error.log
|
||||||
|
DnsConfigPath: # /etc/XrayR/dns.json # Path to dns config, check https://xtls.github.io/config/dns.html for help
|
||||||
|
RouteConfigPath: # /etc/XrayR/route.json # Path to route config, check https://xtls.github.io/config/routing.html for help
|
||||||
|
InboundConfigPath: # /etc/XrayR/custom_inbound.json # Path to custom inbound config, check https://xtls.github.io/config/inbound.html for help
|
||||||
|
OutboundConfigPath: # /etc/XrayR/custom_outbound.json # Path to custom outbound config, check https://xtls.github.io/config/outbound.html for help
|
||||||
|
ConnectionConfig:
|
||||||
|
Handshake: 4 # Handshake time limit, Second
|
||||||
|
ConnIdle: 30 # Connection idle time limit, Second
|
||||||
|
UplinkOnly: 2 # Time limit when the connection downstream is closed, Second
|
||||||
|
DownlinkOnly: 4 # Time limit when the connection is closed after the uplink is closed, Second
|
||||||
|
BufferSize: 64 # The internal cache size of each connection, kB
|
||||||
|
Nodes:
|
||||||
|
- PanelType: "SSpanel" # Panel type: SSpanel, NewV2board, PMpanel, Proxypanel, V2RaySocks, GoV2Panel, BunPanel
|
||||||
|
ApiConfig:
|
||||||
|
ApiHost: "http://127.0.0.1:667"
|
||||||
|
ApiKey: "123"
|
||||||
|
NodeID: 41
|
||||||
|
NodeType: V2ray # Node type: V2ray, Vmess, Vless, Shadowsocks, Trojan, Shadowsocks-Plugin
|
||||||
|
Timeout: 30 # Timeout for the api request
|
||||||
|
EnableVless: false # Enable Vless for V2ray Type
|
||||||
|
VlessFlow: "xtls-rprx-vision" # Only support vless
|
||||||
|
SpeedLimit: 0 # Mbps, Local settings will replace remote settings, 0 means disable
|
||||||
|
DeviceLimit: 0 # Local settings will replace remote settings, 0 means disable
|
||||||
|
RuleListPath: # /etc/XrayR/rulelist Path to local rulelist file
|
||||||
|
DisableCustomConfig: false # disable custom config for sspanel
|
||||||
|
ControllerConfig:
|
||||||
|
ListenIP: 0.0.0.0 # IP address you want to listen
|
||||||
|
SendIP: 0.0.0.0 # IP address you want to send pacakage
|
||||||
|
UpdatePeriodic: 60 # Time to update the nodeinfo, how many sec.
|
||||||
|
EnableDNS: false # Use custom DNS config, Please ensure that you set the dns.json well
|
||||||
|
DNSType: AsIs # AsIs, UseIP, UseIPv4, UseIPv6, DNS strategy
|
||||||
|
EnableProxyProtocol: false # Only works for WebSocket and TCP
|
||||||
|
AutoSpeedLimitConfig:
|
||||||
|
Limit: 0 # Warned speed. Set to 0 to disable AutoSpeedLimit (mbps)
|
||||||
|
WarnTimes: 0 # After (WarnTimes) consecutive warnings, the user will be limited. Set to 0 to punish overspeed user immediately.
|
||||||
|
LimitSpeed: 0 # The speedlimit of a limited user (unit: mbps)
|
||||||
|
LimitDuration: 0 # How many minutes will the limiting last (unit: minute)
|
||||||
|
GlobalDeviceLimitConfig:
|
||||||
|
Enable: false # Enable the global device limit of a user
|
||||||
|
RedisNetwork: tcp # Redis protocol, tcp or unix
|
||||||
|
RedisAddr: 127.0.0.1:6379 # Redis server address, or unix socket path
|
||||||
|
RedisUsername: # Redis username
|
||||||
|
RedisPassword: YOUR PASSWORD # Redis password
|
||||||
|
RedisDB: 0 # Redis DB
|
||||||
|
Timeout: 5 # Timeout for redis request
|
||||||
|
Expiry: 60 # Expiry time (second)
|
||||||
|
EnableFallback: false # Only support for Trojan and Vless
|
||||||
|
FallBackConfigs: # Support multiple fallbacks
|
||||||
|
- SNI: # TLS SNI(Server Name Indication), Empty for any
|
||||||
|
Alpn: # Alpn, Empty for any
|
||||||
|
Path: # HTTP PATH, Empty for any
|
||||||
|
Dest: 80 # Required, Destination of fallback, check https://xtls.github.io/config/features/fallback.html for details.
|
||||||
|
ProxyProtocolVer: 0 # Send PROXY protocol version, 0 for disable
|
||||||
|
DisableLocalREALITYConfig: false # disable local reality config
|
||||||
|
EnableREALITY: false # Enable REALITY
|
||||||
|
REALITYConfigs:
|
||||||
|
Show: true # Show REALITY debug
|
||||||
|
Dest: www.amazon.com:443 # Required, Same as fallback
|
||||||
|
ProxyProtocolVer: 0 # Send PROXY protocol version, 0 for disable
|
||||||
|
ServerNames: # Required, list of available serverNames for the client, * wildcard is not supported at the moment.
|
||||||
|
- www.amazon.com
|
||||||
|
PrivateKey: YOUR_PRIVATE_KEY # Required, execute './XrayR x25519' to generate.
|
||||||
|
MinClientVer: # Optional, minimum version of Xray client, format is x.y.z.
|
||||||
|
MaxClientVer: # Optional, maximum version of Xray client, format is x.y.z.
|
||||||
|
MaxTimeDiff: 0 # Optional, maximum allowed time difference, unit is in milliseconds.
|
||||||
|
ShortIds: # Required, list of available shortIds for the client, can be used to differentiate between different clients.
|
||||||
|
- ""
|
||||||
|
- 0123456789abcdef
|
||||||
|
CertConfig:
|
||||||
|
CertMode: dns # Option about how to get certificate: none, file, http, tls, dns. Choose "none" will forcedly disable the tls config.
|
||||||
|
CertDomain: "node1.test.com" # Domain to cert
|
||||||
|
CertFile: /etc/XrayR/cert/node1.test.com.cert # Provided if the CertMode is file
|
||||||
|
KeyFile: /etc/XrayR/cert/node1.test.com.key
|
||||||
|
Provider: alidns # DNS cert provider, Get the full support list here: https://go-acme.github.io/lego/dns/
|
||||||
|
Email: test@me.com
|
||||||
|
DNSEnv: # DNS ENV option used by DNS provider
|
||||||
|
ALICLOUD_ACCESS_KEY: aaa
|
||||||
|
ALICLOUD_SECRET_KEY: bbb
|
||||||
|
|
||||||
|
# - PanelType: "SSpanel" # Panel type: SSpanel, V2board, NewV2board, PMpanel, Proxypanel, V2RaySocks, GoV2Panel
|
||||||
|
# ApiConfig:
|
||||||
|
# ApiHost: "http://127.0.0.1:668"
|
||||||
|
# ApiKey: "123"
|
||||||
|
# NodeID: 41
|
||||||
|
# NodeType: V2ray # Node type: V2ray, Shadowsocks, Trojan, Shadowsocks-Plugin
|
||||||
|
# Timeout: 30 # Timeout for the api request
|
||||||
|
# EnableVless: false # Enable Vless for V2ray Type
|
||||||
|
# VlessFlow: "xtls-rprx-vision" # Only support vless
|
||||||
|
# SpeedLimit: 0 # Mbps, Local settings will replace remote settings, 0 means disable
|
||||||
|
# DeviceLimit: 0 # Local settings will replace remote settings, 0 means disable
|
||||||
|
# RuleListPath: # /etc/XrayR/rulelist Path to local rulelist file
|
||||||
|
# ControllerConfig:
|
||||||
|
# ListenIP: 0.0.0.0 # IP address you want to listen
|
||||||
|
# SendIP: 0.0.0.0 # IP address you want to send pacakage
|
||||||
|
# UpdatePeriodic: 60 # Time to update the nodeinfo, how many sec.
|
||||||
|
# EnableDNS: false # Use custom DNS config, Please ensure that you set the dns.json well
|
||||||
|
# DNSType: AsIs # AsIs, UseIP, UseIPv4, UseIPv6, DNS strategy
|
||||||
|
# EnableProxyProtocol: false # Only works for WebSocket and TCP
|
||||||
|
# AutoSpeedLimitConfig:
|
||||||
|
# Limit: 0 # Warned speed. Set to 0 to disable AutoSpeedLimit (mbps)
|
||||||
|
# WarnTimes: 0 # After (WarnTimes) consecutive warnings, the user will be limited. Set to 0 to punish overspeed user immediately.
|
||||||
|
# LimitSpeed: 0 # The speedlimit of a limited user (unit: mbps)
|
||||||
|
# LimitDuration: 0 # How many minutes will the limiting last (unit: minute)
|
||||||
|
# GlobalDeviceLimitConfig:
|
||||||
|
# Enable: false # Enable the global device limit of a user
|
||||||
|
# RedisAddr: 127.0.0.1:6379 # The redis server address
|
||||||
|
# RedisPassword: YOUR PASSWORD # Redis password
|
||||||
|
# RedisDB: 0 # Redis DB
|
||||||
|
# Timeout: 5 # Timeout for redis request
|
||||||
|
# Expiry: 60 # Expiry time (second)
|
||||||
|
# EnableFallback: false # Only support for Trojan and Vless
|
||||||
|
# FallBackConfigs: # Support multiple fallbacks
|
||||||
|
# - SNI: # TLS SNI(Server Name Indication), Empty for any
|
||||||
|
# Alpn: # Alpn, Empty for any
|
||||||
|
# Path: # HTTP PATH, Empty for any
|
||||||
|
# Dest: 80 # Required, Destination of fallback, check https://xtls.github.io/config/features/fallback.html for details.
|
||||||
|
# ProxyProtocolVer: 0 # Send PROXY protocol version, 0 for disable
|
||||||
|
# EnableREALITY: true # Enable REALITY
|
||||||
|
# REALITYConfigs:
|
||||||
|
# Show: true # Show REALITY debug
|
||||||
|
# Dest: www.amazon.com:443 # Required, Same as fallback
|
||||||
|
# ProxyProtocolVer: 0 # Send PROXY protocol version, 0 for disable
|
||||||
|
# ServerNames: # Required, list of available serverNames for the client, * wildcard is not supported at the moment.
|
||||||
|
# - www.amazon.com
|
||||||
|
# PrivateKey: YOUR_PRIVATE_KEY # Required, execute './XrayR x25519' to generate.
|
||||||
|
# MinClientVer: # Optional, minimum version of Xray client, format is x.y.z.
|
||||||
|
# MaxClientVer: # Optional, maximum version of Xray client, format is x.y.z.
|
||||||
|
# MaxTimeDiff: 0 # Optional, maximum allowed time difference, unit is in milliseconds.
|
||||||
|
# ShortIds: # Required, list of available shortIds for the client, can be used to differentiate between different clients.
|
||||||
|
# - ""
|
||||||
|
# - 0123456789abcdef
|
||||||
|
# CertConfig:
|
||||||
|
# CertMode: dns # Option about how to get certificate: none, file, http, tls, dns. Choose "none" will forcedly disable the tls config.
|
||||||
|
# CertDomain: "node1.test.com" # Domain to cert
|
||||||
|
# CertFile: /etc/XrayR/cert/node1.test.com.cert # Provided if the CertMode is file
|
||||||
|
# KeyFile: /etc/XrayR/cert/node1.test.com.key
|
||||||
|
# Provider: alidns # DNS cert provider, Get the full support list here: https://go-acme.github.io/lego/dns/
|
||||||
|
# Email: test@me.com
|
||||||
|
# DNSEnv: # DNS ENV option used by DNS provider
|
||||||
|
# ALICLOUD_ACCESS_KEY: aaa
|
||||||
|
# ALICLOUD_SECRET_KEY: bbb
|
@ -1,6 +1,6 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"listen": "0.0.0.0",
|
"listen": "127.0.0.1",
|
||||||
"port": 1234,
|
"port": 1234,
|
||||||
"protocol": "socks",
|
"protocol": "socks",
|
||||||
"settings": {
|
"settings": {
|
@ -6,21 +6,24 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
ListenIP string `mapstructure:"ListenIP"`
|
ListenIP string `mapstructure:"ListenIP"`
|
||||||
SendIP string `mapstructure:"SendIP"`
|
SendIP string `mapstructure:"SendIP"`
|
||||||
UpdatePeriodic int `mapstructure:"UpdatePeriodic"`
|
UpdatePeriodic int `mapstructure:"UpdatePeriodic"`
|
||||||
CertConfig *mylego.CertConfig `mapstructure:"CertConfig"`
|
CertConfig *mylego.CertConfig `mapstructure:"CertConfig"`
|
||||||
EnableDNS bool `mapstructure:"EnableDNS"`
|
EnableDNS bool `mapstructure:"EnableDNS"`
|
||||||
DNSType string `mapstructure:"DNSType"`
|
DNSType string `mapstructure:"DNSType"`
|
||||||
DisableUploadTraffic bool `mapstructure:"DisableUploadTraffic"`
|
DisableUploadTraffic bool `mapstructure:"DisableUploadTraffic"`
|
||||||
DisableGetRule bool `mapstructure:"DisableGetRule"`
|
DisableGetRule bool `mapstructure:"DisableGetRule"`
|
||||||
EnableProxyProtocol bool `mapstructure:"EnableProxyProtocol"`
|
EnableProxyProtocol bool `mapstructure:"EnableProxyProtocol"`
|
||||||
EnableFallback bool `mapstructure:"EnableFallback"`
|
EnableFallback bool `mapstructure:"EnableFallback"`
|
||||||
DisableIVCheck bool `mapstructure:"DisableIVCheck"`
|
DisableIVCheck bool `mapstructure:"DisableIVCheck"`
|
||||||
DisableSniffing bool `mapstructure:"DisableSniffing"`
|
DisableSniffing bool `mapstructure:"DisableSniffing"`
|
||||||
AutoSpeedLimitConfig *AutoSpeedLimitConfig `mapstructure:"AutoSpeedLimitConfig"`
|
AutoSpeedLimitConfig *AutoSpeedLimitConfig `mapstructure:"AutoSpeedLimitConfig"`
|
||||||
GlobalDeviceLimitConfig *limiter.GlobalDeviceLimitConfig `mapstructure:"GlobalDeviceLimitConfig"`
|
GlobalDeviceLimitConfig *limiter.GlobalDeviceLimitConfig `mapstructure:"GlobalDeviceLimitConfig"`
|
||||||
FallBackConfigs []*FallBackConfig `mapstructure:"FallBackConfigs"`
|
FallBackConfigs []*FallBackConfig `mapstructure:"FallBackConfigs"`
|
||||||
|
DisableLocalREALITYConfig bool `mapstructure:"DisableLocalREALITYConfig"`
|
||||||
|
EnableREALITY bool `mapstructure:"EnableREALITY"`
|
||||||
|
REALITYConfigs *REALITYConfig `mapstructure:"REALITYConfigs"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AutoSpeedLimitConfig struct {
|
type AutoSpeedLimitConfig struct {
|
||||||
@ -37,3 +40,15 @@ type FallBackConfig struct {
|
|||||||
Dest string `mapstructure:"Dest"`
|
Dest string `mapstructure:"Dest"`
|
||||||
ProxyProtocolVer uint64 `mapstructure:"ProxyProtocolVer"`
|
ProxyProtocolVer uint64 `mapstructure:"ProxyProtocolVer"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type REALITYConfig struct {
|
||||||
|
Show bool `mapstructure:"Show"`
|
||||||
|
Dest string `mapstructure:"Dest"`
|
||||||
|
ProxyProtocolVer uint64 `mapstructure:"ProxyProtocolVer"`
|
||||||
|
ServerNames []string `mapstructure:"ServerNames"`
|
||||||
|
PrivateKey string `mapstructure:"PrivateKey"`
|
||||||
|
MinClientVer string `mapstructure:"MinClientVer"`
|
||||||
|
MaxClientVer string `mapstructure:"MaxClientVer"`
|
||||||
|
MaxTimeDiff uint64 `mapstructure:"MaxTimeDiff"`
|
||||||
|
ShortIds []string `mapstructure:"ShortIds"`
|
||||||
|
}
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
package controller
|
package controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/xtls/xray-core/common/protocol"
|
"github.com/xtls/xray-core/common/protocol"
|
||||||
"github.com/xtls/xray-core/common/task"
|
"github.com/xtls/xray-core/common/task"
|
||||||
"github.com/xtls/xray-core/core"
|
"github.com/xtls/xray-core/core"
|
||||||
@ -43,6 +44,7 @@ type Controller struct {
|
|||||||
stm stats.Manager
|
stm stats.Manager
|
||||||
dispatcher *mydispatcher.DefaultDispatcher
|
dispatcher *mydispatcher.DefaultDispatcher
|
||||||
startAt time.Time
|
startAt time.Time
|
||||||
|
logger *log.Entry
|
||||||
}
|
}
|
||||||
|
|
||||||
type periodicTask struct {
|
type periodicTask struct {
|
||||||
@ -52,6 +54,11 @@ type periodicTask struct {
|
|||||||
|
|
||||||
// New return a Controller service with default parameters.
|
// New return a Controller service with default parameters.
|
||||||
func New(server *core.Instance, api api.API, config *Config, panelType string) *Controller {
|
func New(server *core.Instance, api api.API, config *Config, panelType string) *Controller {
|
||||||
|
logger := log.NewEntry(log.StandardLogger()).WithFields(log.Fields{
|
||||||
|
"Host": api.Describe().APIHost,
|
||||||
|
"Type": api.Describe().NodeType,
|
||||||
|
"ID": api.Describe().NodeID,
|
||||||
|
})
|
||||||
controller := &Controller{
|
controller := &Controller{
|
||||||
server: server,
|
server: server,
|
||||||
config: config,
|
config: config,
|
||||||
@ -62,6 +69,7 @@ func New(server *core.Instance, api api.API, config *Config, panelType string) *
|
|||||||
stm: server.GetFeature(stats.ManagerType()).(stats.Manager),
|
stm: server.GetFeature(stats.ManagerType()).(stats.Manager),
|
||||||
dispatcher: server.GetFeature(routing.DispatcherType()).(*mydispatcher.DefaultDispatcher),
|
dispatcher: server.GetFeature(routing.DispatcherType()).(*mydispatcher.DefaultDispatcher),
|
||||||
startAt: time.Now(),
|
startAt: time.Now(),
|
||||||
|
logger: logger,
|
||||||
}
|
}
|
||||||
|
|
||||||
return controller
|
return controller
|
||||||
@ -75,13 +83,16 @@ func (c *Controller) Start() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if newNodeInfo.Port == 0 {
|
||||||
|
return errors.New("server port must > 0")
|
||||||
|
}
|
||||||
c.nodeInfo = newNodeInfo
|
c.nodeInfo = newNodeInfo
|
||||||
c.Tag = c.buildNodeTag()
|
c.Tag = c.buildNodeTag()
|
||||||
|
|
||||||
// Add new tag
|
// Add new tag
|
||||||
err = c.addNewTag(newNodeInfo)
|
err = c.addNewTag(newNodeInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panic(err)
|
c.logger.Panic(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Update user
|
// Update user
|
||||||
@ -100,16 +111,16 @@ func (c *Controller) Start() error {
|
|||||||
|
|
||||||
// Add Limiter
|
// Add Limiter
|
||||||
if err := c.AddInboundLimiter(c.Tag, newNodeInfo.SpeedLimit, userInfo, c.config.GlobalDeviceLimitConfig); err != nil {
|
if err := c.AddInboundLimiter(c.Tag, newNodeInfo.SpeedLimit, userInfo, c.config.GlobalDeviceLimitConfig); err != nil {
|
||||||
log.Print(err)
|
c.logger.Print(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add Rule Manager
|
// Add Rule Manager
|
||||||
if !c.config.DisableGetRule {
|
if !c.config.DisableGetRule {
|
||||||
if ruleList, err := c.apiClient.GetNodeRule(); err != nil {
|
if ruleList, err := c.apiClient.GetNodeRule(); err != nil {
|
||||||
log.Printf("Get rule list filed: %s", err)
|
c.logger.Printf("Get rule list filed: %s", err)
|
||||||
} else if len(*ruleList) > 0 {
|
} else if len(*ruleList) > 0 {
|
||||||
if err := c.UpdateRule(c.Tag, *ruleList); err != nil {
|
if err := c.UpdateRule(c.Tag, *ruleList); err != nil {
|
||||||
log.Print(err)
|
c.logger.Print(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -140,7 +151,7 @@ func (c *Controller) Start() error {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Check cert service in need
|
// Check cert service in need
|
||||||
if c.nodeInfo.EnableTLS {
|
if c.nodeInfo.EnableTLS && c.config.EnableREALITY == false {
|
||||||
c.tasks = append(c.tasks, periodicTask{
|
c.tasks = append(c.tasks, periodicTask{
|
||||||
tag: "cert monitor",
|
tag: "cert monitor",
|
||||||
Periodic: &task.Periodic{
|
Periodic: &task.Periodic{
|
||||||
@ -151,7 +162,7 @@ func (c *Controller) Start() error {
|
|||||||
|
|
||||||
// Start periodic tasks
|
// Start periodic tasks
|
||||||
for i := range c.tasks {
|
for i := range c.tasks {
|
||||||
log.Printf("%s Start %s periodic task", c.logPrefix(), c.tasks[i].tag)
|
c.logger.Printf("Start %s periodic task", c.tasks[i].tag)
|
||||||
go c.tasks[i].Start()
|
go c.tasks[i].Start()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,7 +174,7 @@ func (c *Controller) Close() error {
|
|||||||
for i := range c.tasks {
|
for i := range c.tasks {
|
||||||
if c.tasks[i].Periodic != nil {
|
if c.tasks[i].Periodic != nil {
|
||||||
if err := c.tasks[i].Periodic.Close(); err != nil {
|
if err := c.tasks[i].Periodic.Close(); err != nil {
|
||||||
log.Panicf("%s %s periodic task close failed: %s", c.logPrefix(), c.tasks[i].tag, err)
|
c.logger.Panicf("%s periodic task close failed: %s", c.tasks[i].tag, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -178,65 +189,79 @@ func (c *Controller) nodeInfoMonitor() (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// First fetch Node Info
|
// First fetch Node Info
|
||||||
|
var nodeInfoChanged = true
|
||||||
newNodeInfo, err := c.apiClient.GetNodeInfo()
|
newNodeInfo, err := c.apiClient.GetNodeInfo()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Print(err)
|
if err.Error() == api.NodeNotModified {
|
||||||
return nil
|
nodeInfoChanged = false
|
||||||
|
newNodeInfo = c.nodeInfo
|
||||||
|
} else {
|
||||||
|
c.logger.Print(err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if newNodeInfo.Port == 0 {
|
||||||
|
return errors.New("server port must > 0")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update User
|
// Update User
|
||||||
var usersChanged = true
|
var usersChanged = true
|
||||||
newUserInfo, err := c.apiClient.GetUserList()
|
newUserInfo, err := c.apiClient.GetUserList()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err.Error() == "users no change" {
|
if err.Error() == api.UserNotModified {
|
||||||
usersChanged = false
|
usersChanged = false
|
||||||
newUserInfo = c.userList
|
newUserInfo = c.userList
|
||||||
} else {
|
} else {
|
||||||
log.Print(err)
|
c.logger.Print(err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var nodeInfoChanged = false
|
|
||||||
// If nodeInfo changed
|
// If nodeInfo changed
|
||||||
if !reflect.DeepEqual(c.nodeInfo, newNodeInfo) {
|
if nodeInfoChanged {
|
||||||
// Remove old tag
|
if !reflect.DeepEqual(c.nodeInfo, newNodeInfo) {
|
||||||
oldTag := c.Tag
|
// Remove old tag
|
||||||
err := c.removeOldTag(oldTag)
|
oldTag := c.Tag
|
||||||
if err != nil {
|
err := c.removeOldTag(oldTag)
|
||||||
log.Print(err)
|
if err != nil {
|
||||||
return nil
|
c.logger.Print(err)
|
||||||
}
|
return nil
|
||||||
if c.nodeInfo.NodeType == "Shadowsocks-Plugin" {
|
}
|
||||||
err = c.removeOldTag(fmt.Sprintf("dokodemo-door_%s+1", c.Tag))
|
if c.nodeInfo.NodeType == "Shadowsocks-Plugin" {
|
||||||
}
|
err = c.removeOldTag(fmt.Sprintf("dokodemo-door_%s+1", c.Tag))
|
||||||
if err != nil {
|
}
|
||||||
log.Print(err)
|
if err != nil {
|
||||||
return nil
|
c.logger.Print(err)
|
||||||
}
|
return nil
|
||||||
// Add new tag
|
}
|
||||||
c.nodeInfo = newNodeInfo
|
// Add new tag
|
||||||
c.Tag = c.buildNodeTag()
|
c.nodeInfo = newNodeInfo
|
||||||
err = c.addNewTag(newNodeInfo)
|
c.Tag = c.buildNodeTag()
|
||||||
if err != nil {
|
err = c.addNewTag(newNodeInfo)
|
||||||
log.Print(err)
|
if err != nil {
|
||||||
return nil
|
c.logger.Print(err)
|
||||||
}
|
return nil
|
||||||
nodeInfoChanged = true
|
}
|
||||||
// Remove Old limiter
|
nodeInfoChanged = true
|
||||||
if err = c.DeleteInboundLimiter(oldTag); err != nil {
|
// Remove Old limiter
|
||||||
log.Print(err)
|
if err = c.DeleteInboundLimiter(oldTag); err != nil {
|
||||||
return nil
|
c.logger.Print(err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nodeInfoChanged = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check Rule
|
// Check Rule
|
||||||
if !c.config.DisableGetRule {
|
if !c.config.DisableGetRule {
|
||||||
if ruleList, err := c.apiClient.GetNodeRule(); err != nil {
|
if ruleList, err := c.apiClient.GetNodeRule(); err != nil {
|
||||||
log.Printf("Get rule list filed: %s", err)
|
if err.Error() != api.RuleNotModified {
|
||||||
|
c.logger.Printf("Get rule list filed: %s", err)
|
||||||
|
}
|
||||||
} else if len(*ruleList) > 0 {
|
} else if len(*ruleList) > 0 {
|
||||||
if err := c.UpdateRule(c.Tag, *ruleList); err != nil {
|
if err := c.UpdateRule(c.Tag, *ruleList); err != nil {
|
||||||
log.Print(err)
|
c.logger.Print(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -244,13 +269,13 @@ func (c *Controller) nodeInfoMonitor() (err error) {
|
|||||||
if nodeInfoChanged {
|
if nodeInfoChanged {
|
||||||
err = c.addNewUser(newUserInfo, newNodeInfo)
|
err = c.addNewUser(newUserInfo, newNodeInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Print(err)
|
c.logger.Print(err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add Limiter
|
// Add Limiter
|
||||||
if err := c.AddInboundLimiter(c.Tag, newNodeInfo.SpeedLimit, newUserInfo, c.config.GlobalDeviceLimitConfig); err != nil {
|
if err := c.AddInboundLimiter(c.Tag, newNodeInfo.SpeedLimit, newUserInfo, c.config.GlobalDeviceLimitConfig); err != nil {
|
||||||
log.Print(err)
|
c.logger.Print(err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,21 +290,21 @@ func (c *Controller) nodeInfoMonitor() (err error) {
|
|||||||
}
|
}
|
||||||
err := c.removeUsers(deletedEmail, c.Tag)
|
err := c.removeUsers(deletedEmail, c.Tag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Print(err)
|
c.logger.Print(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(added) > 0 {
|
if len(added) > 0 {
|
||||||
err = c.addNewUser(&added, c.nodeInfo)
|
err = c.addNewUser(&added, c.nodeInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Print(err)
|
c.logger.Print(err)
|
||||||
}
|
}
|
||||||
// Update Limiter
|
// Update Limiter
|
||||||
if err := c.UpdateInboundLimiter(c.Tag, &added); err != nil {
|
if err := c.UpdateInboundLimiter(c.Tag, &added); err != nil {
|
||||||
log.Print(err)
|
c.logger.Print(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.Printf("%s %d user deleted, %d user added", c.logPrefix(), len(deleted), len(added))
|
c.logger.Printf("%d user deleted, %d user added", len(deleted), len(added))
|
||||||
}
|
}
|
||||||
c.userList = newUserInfo
|
c.userList = newUserInfo
|
||||||
return nil
|
return nil
|
||||||
@ -380,18 +405,11 @@ func (c *Controller) addInboundForSSPlugin(newNodeInfo api.NodeInfo) (err error)
|
|||||||
func (c *Controller) addNewUser(userInfo *[]api.UserInfo, nodeInfo *api.NodeInfo) (err error) {
|
func (c *Controller) addNewUser(userInfo *[]api.UserInfo, nodeInfo *api.NodeInfo) (err error) {
|
||||||
users := make([]*protocol.User, 0)
|
users := make([]*protocol.User, 0)
|
||||||
switch nodeInfo.NodeType {
|
switch nodeInfo.NodeType {
|
||||||
case "V2ray":
|
case "V2ray", "Vmess", "Vless":
|
||||||
if nodeInfo.EnableVless {
|
if nodeInfo.EnableVless || (nodeInfo.NodeType == "Vless" && nodeInfo.NodeType != "Vmess") {
|
||||||
users = c.buildVlessUser(userInfo)
|
users = c.buildVlessUser(userInfo)
|
||||||
} else {
|
} else {
|
||||||
var alterID uint16 = 0
|
users = c.buildVmessUser(userInfo)
|
||||||
if (c.panelType == "V2board" || c.panelType == "V2RaySocks") && len(*userInfo) > 0 {
|
|
||||||
// use latest userInfo
|
|
||||||
alterID = (*userInfo)[0].AlterID
|
|
||||||
} else {
|
|
||||||
alterID = nodeInfo.AlterID
|
|
||||||
}
|
|
||||||
users = c.buildVmessUser(userInfo, alterID)
|
|
||||||
}
|
}
|
||||||
case "Trojan":
|
case "Trojan":
|
||||||
users = c.buildTrojanUser(userInfo)
|
users = c.buildTrojanUser(userInfo)
|
||||||
@ -407,7 +425,7 @@ func (c *Controller) addNewUser(userInfo *[]api.UserInfo, nodeInfo *api.NodeInfo
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Printf("%s Added %d new users", c.logPrefix(), len(*userInfo))
|
c.logger.Printf("Added %d new users", len(*userInfo))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -455,7 +473,7 @@ func limitUser(c *Controller, user api.UserInfo, silentUsers *[]api.UserInfo) {
|
|||||||
currentSpeedLimit: c.config.AutoSpeedLimitConfig.LimitSpeed,
|
currentSpeedLimit: c.config.AutoSpeedLimitConfig.LimitSpeed,
|
||||||
originSpeedLimit: user.SpeedLimit,
|
originSpeedLimit: user.SpeedLimit,
|
||||||
}
|
}
|
||||||
log.Printf("Limit User: %s Speed: %d End: %s", c.buildUserTag(&user), c.config.AutoSpeedLimitConfig.LimitSpeed, time.Unix(c.limitedUsers[user].end, 0).Format("01-02 15:04:05"))
|
c.logger.Printf("Limit User: %s Speed: %d End: %s", c.buildUserTag(&user), c.config.AutoSpeedLimitConfig.LimitSpeed, time.Unix(c.limitedUsers[user].end, 0).Format("01-02 15:04:05"))
|
||||||
user.SpeedLimit = uint64((c.config.AutoSpeedLimitConfig.LimitSpeed * 1000000) / 8)
|
user.SpeedLimit = uint64((c.config.AutoSpeedLimitConfig.LimitSpeed * 1000000) / 8)
|
||||||
*silentUsers = append(*silentUsers, user)
|
*silentUsers = append(*silentUsers, user)
|
||||||
}
|
}
|
||||||
@ -469,7 +487,7 @@ func (c *Controller) userInfoMonitor() (err error) {
|
|||||||
// Get server status
|
// Get server status
|
||||||
CPU, Mem, Disk, Uptime, err := serverstatus.GetSystemInfo()
|
CPU, Mem, Disk, Uptime, err := serverstatus.GetSystemInfo()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Print(err)
|
c.logger.Print(err)
|
||||||
}
|
}
|
||||||
err = c.apiClient.ReportNodeStatus(
|
err = c.apiClient.ReportNodeStatus(
|
||||||
&api.NodeStatus{
|
&api.NodeStatus{
|
||||||
@ -479,25 +497,25 @@ func (c *Controller) userInfoMonitor() (err error) {
|
|||||||
Uptime: Uptime,
|
Uptime: Uptime,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Print(err)
|
c.logger.Print(err)
|
||||||
}
|
}
|
||||||
// Unlock users
|
// Unlock users
|
||||||
if c.config.AutoSpeedLimitConfig.Limit > 0 && len(c.limitedUsers) > 0 {
|
if c.config.AutoSpeedLimitConfig.Limit > 0 && len(c.limitedUsers) > 0 {
|
||||||
log.Printf("%s Limited users:", c.logPrefix())
|
c.logger.Printf("Limited users:")
|
||||||
toReleaseUsers := make([]api.UserInfo, 0)
|
toReleaseUsers := make([]api.UserInfo, 0)
|
||||||
for user, limitInfo := range c.limitedUsers {
|
for user, limitInfo := range c.limitedUsers {
|
||||||
if time.Now().Unix() > limitInfo.end {
|
if time.Now().Unix() > limitInfo.end {
|
||||||
user.SpeedLimit = limitInfo.originSpeedLimit
|
user.SpeedLimit = limitInfo.originSpeedLimit
|
||||||
toReleaseUsers = append(toReleaseUsers, user)
|
toReleaseUsers = append(toReleaseUsers, user)
|
||||||
log.Printf("User: %s Speed: %d End: nil (Unlimit)", c.buildUserTag(&user), user.SpeedLimit)
|
c.logger.Printf("User: %s Speed: %d End: nil (Unlimit)", c.buildUserTag(&user), user.SpeedLimit)
|
||||||
delete(c.limitedUsers, user)
|
delete(c.limitedUsers, user)
|
||||||
} else {
|
} else {
|
||||||
log.Printf("User: %s Speed: %d End: %s", c.buildUserTag(&user), limitInfo.currentSpeedLimit, time.Unix(c.limitedUsers[user].end, 0).Format("01-02 15:04:05"))
|
c.logger.Printf("User: %s Speed: %d End: %s", c.buildUserTag(&user), limitInfo.currentSpeedLimit, time.Unix(c.limitedUsers[user].end, 0).Format("01-02 15:04:05"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(toReleaseUsers) > 0 {
|
if len(toReleaseUsers) > 0 {
|
||||||
if err := c.UpdateInboundLimiter(c.Tag, &toReleaseUsers); err != nil {
|
if err := c.UpdateInboundLimiter(c.Tag, &toReleaseUsers); err != nil {
|
||||||
log.Print(err)
|
c.logger.Print(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -548,7 +566,7 @@ func (c *Controller) userInfoMonitor() (err error) {
|
|||||||
}
|
}
|
||||||
if len(limitedUsers) > 0 {
|
if len(limitedUsers) > 0 {
|
||||||
if err := c.UpdateInboundLimiter(c.Tag, &limitedUsers); err != nil {
|
if err := c.UpdateInboundLimiter(c.Tag, &limitedUsers); err != nil {
|
||||||
log.Print(err)
|
c.logger.Print(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(userTraffic) > 0 {
|
if len(userTraffic) > 0 {
|
||||||
@ -558,7 +576,7 @@ func (c *Controller) userInfoMonitor() (err error) {
|
|||||||
}
|
}
|
||||||
// If report traffic error, not clear the traffic
|
// If report traffic error, not clear the traffic
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Print(err)
|
c.logger.Print(err)
|
||||||
} else {
|
} else {
|
||||||
c.resetTraffic(&upCounterList, &downCounterList)
|
c.resetTraffic(&upCounterList, &downCounterList)
|
||||||
}
|
}
|
||||||
@ -566,23 +584,23 @@ func (c *Controller) userInfoMonitor() (err error) {
|
|||||||
|
|
||||||
// Report Online info
|
// Report Online info
|
||||||
if onlineDevice, err := c.GetOnlineDevice(c.Tag); err != nil {
|
if onlineDevice, err := c.GetOnlineDevice(c.Tag); err != nil {
|
||||||
log.Print(err)
|
c.logger.Print(err)
|
||||||
} else if len(*onlineDevice) > 0 {
|
} else if len(*onlineDevice) > 0 {
|
||||||
if err = c.apiClient.ReportNodeOnlineUsers(onlineDevice); err != nil {
|
if err = c.apiClient.ReportNodeOnlineUsers(onlineDevice); err != nil {
|
||||||
log.Print(err)
|
c.logger.Print(err)
|
||||||
} else {
|
} else {
|
||||||
log.Printf("%s Report %d online users", c.logPrefix(), len(*onlineDevice))
|
c.logger.Printf("Report %d online users", len(*onlineDevice))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Report Illegal user
|
// Report Illegal user
|
||||||
if detectResult, err := c.GetDetectResult(c.Tag); err != nil {
|
if detectResult, err := c.GetDetectResult(c.Tag); err != nil {
|
||||||
log.Print(err)
|
c.logger.Print(err)
|
||||||
} else if len(*detectResult) > 0 {
|
} else if len(*detectResult) > 0 {
|
||||||
if err = c.apiClient.ReportIllegal(detectResult); err != nil {
|
if err = c.apiClient.ReportIllegal(detectResult); err != nil {
|
||||||
log.Print(err)
|
c.logger.Print(err)
|
||||||
} else {
|
} else {
|
||||||
log.Printf("%s Report %d illegal behaviors", c.logPrefix(), len(*detectResult))
|
c.logger.Printf("Report %d illegal behaviors", len(*detectResult))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -593,23 +611,23 @@ func (c *Controller) buildNodeTag() string {
|
|||||||
return fmt.Sprintf("%s_%s_%d", c.nodeInfo.NodeType, c.config.ListenIP, c.nodeInfo.Port)
|
return fmt.Sprintf("%s_%s_%d", c.nodeInfo.NodeType, c.config.ListenIP, c.nodeInfo.Port)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) logPrefix() string {
|
// func (c *Controller) logPrefix() string {
|
||||||
return fmt.Sprintf("[%s] %s(ID=%d)", c.clientInfo.APIHost, c.nodeInfo.NodeType, c.nodeInfo.NodeID)
|
// return fmt.Sprintf("[%s] %s(ID=%d)", c.clientInfo.APIHost, c.nodeInfo.NodeType, c.nodeInfo.NodeID)
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Check Cert
|
// Check Cert
|
||||||
func (c *Controller) certMonitor() error {
|
func (c *Controller) certMonitor() error {
|
||||||
if c.nodeInfo.EnableTLS {
|
if c.nodeInfo.EnableTLS && c.config.EnableREALITY == false {
|
||||||
switch c.config.CertConfig.CertMode {
|
switch c.config.CertConfig.CertMode {
|
||||||
case "dns", "http", "tls":
|
case "dns", "http", "tls":
|
||||||
lego, err := mylego.New(c.config.CertConfig)
|
lego, err := mylego.New(c.config.CertConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Print(err)
|
c.logger.Print(err)
|
||||||
}
|
}
|
||||||
// Xray-core supports the OcspStapling certification hot renew
|
// Xray-core supports the OcspStapling certification hot renew
|
||||||
_, _, _, err = lego.RenewCert()
|
_, _, _, err = lego.RenewCert()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Print(err)
|
c.logger.Print(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,8 +13,8 @@ import (
|
|||||||
|
|
||||||
"github.com/XrayR-project/XrayR/api"
|
"github.com/XrayR-project/XrayR/api"
|
||||||
"github.com/XrayR-project/XrayR/api/sspanel"
|
"github.com/XrayR-project/XrayR/api/sspanel"
|
||||||
|
_ "github.com/XrayR-project/XrayR/cmd/distro/all"
|
||||||
"github.com/XrayR-project/XrayR/common/mylego"
|
"github.com/XrayR-project/XrayR/common/mylego"
|
||||||
_ "github.com/XrayR-project/XrayR/main/distro/all"
|
|
||||||
. "github.com/XrayR-project/XrayR/service/controller"
|
. "github.com/XrayR-project/XrayR/service/controller"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
|
|||||||
// SniffingConfig
|
// SniffingConfig
|
||||||
sniffingConfig := &conf.SniffingConfig{
|
sniffingConfig := &conf.SniffingConfig{
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
DestOverride: &conf.StringList{"http", "tls"},
|
DestOverride: &conf.StringList{"http", "tls", "quic", "fakedns"},
|
||||||
}
|
}
|
||||||
if config.DisableSniffing {
|
if config.DisableSniffing {
|
||||||
sniffingConfig.Enabled = false
|
sniffingConfig.Enabled = false
|
||||||
@ -57,8 +57,8 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
|
|||||||
var proxySetting any
|
var proxySetting any
|
||||||
// Build Protocol and Protocol setting
|
// Build Protocol and Protocol setting
|
||||||
switch nodeInfo.NodeType {
|
switch nodeInfo.NodeType {
|
||||||
case "V2ray":
|
case "V2ray", "Vmess", "Vless":
|
||||||
if nodeInfo.EnableVless {
|
if nodeInfo.EnableVless || (nodeInfo.NodeType == "Vless" && nodeInfo.NodeType != "Vmess") {
|
||||||
protocol = "vless"
|
protocol = "vless"
|
||||||
// Enable fallback
|
// Enable fallback
|
||||||
if config.EnableFallback {
|
if config.EnableFallback {
|
||||||
@ -139,7 +139,7 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
|
|||||||
|
|
||||||
setting, err := json.Marshal(proxySetting)
|
setting, err := json.Marshal(proxySetting)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("marshal proxy %s config fialed: %s", nodeInfo.NodeType, err)
|
return nil, fmt.Errorf("marshal proxy %s config failed: %s", nodeInfo.NodeType, err)
|
||||||
}
|
}
|
||||||
inboundDetourConfig.Protocol = protocol
|
inboundDetourConfig.Protocol = protocol
|
||||||
inboundDetourConfig.Settings = &setting
|
inboundDetourConfig.Settings = &setting
|
||||||
@ -164,47 +164,82 @@ func InboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.I
|
|||||||
headers["Host"] = nodeInfo.Host
|
headers["Host"] = nodeInfo.Host
|
||||||
wsSettings := &conf.WebSocketConfig{
|
wsSettings := &conf.WebSocketConfig{
|
||||||
AcceptProxyProtocol: config.EnableProxyProtocol,
|
AcceptProxyProtocol: config.EnableProxyProtocol,
|
||||||
|
Host: nodeInfo.Host,
|
||||||
Path: nodeInfo.Path,
|
Path: nodeInfo.Path,
|
||||||
Headers: headers,
|
Headers: headers,
|
||||||
}
|
}
|
||||||
streamSetting.WSSettings = wsSettings
|
streamSetting.WSSettings = wsSettings
|
||||||
case "http":
|
|
||||||
hosts := conf.StringList{nodeInfo.Host}
|
|
||||||
httpSettings := &conf.HTTPConfig{
|
|
||||||
Host: &hosts,
|
|
||||||
Path: nodeInfo.Path,
|
|
||||||
}
|
|
||||||
streamSetting.HTTPSettings = httpSettings
|
|
||||||
case "grpc":
|
case "grpc":
|
||||||
grpcSettings := &conf.GRPCConfig{
|
grpcSettings := &conf.GRPCConfig{
|
||||||
ServiceName: nodeInfo.ServiceName,
|
ServiceName: nodeInfo.ServiceName,
|
||||||
|
Authority: nodeInfo.Authority,
|
||||||
}
|
}
|
||||||
streamSetting.GRPCConfig = grpcSettings
|
streamSetting.GRPCSettings = grpcSettings
|
||||||
|
case "httpupgrade":
|
||||||
|
httpupgradeSettings := &conf.HttpUpgradeConfig{
|
||||||
|
Headers: nodeInfo.Headers,
|
||||||
|
Path: nodeInfo.Path,
|
||||||
|
Host: nodeInfo.Host,
|
||||||
|
AcceptProxyProtocol: nodeInfo.AcceptProxyProtocol,
|
||||||
|
}
|
||||||
|
streamSetting.HTTPUPGRADESettings = httpupgradeSettings
|
||||||
|
case "splithttp", "xhttp":
|
||||||
|
splithttpSetting := &conf.SplitHTTPConfig{
|
||||||
|
Path: nodeInfo.Path,
|
||||||
|
Host: nodeInfo.Host,
|
||||||
|
}
|
||||||
|
streamSetting.SplitHTTPSettings = splithttpSetting
|
||||||
}
|
}
|
||||||
|
|
||||||
streamSetting.Network = &transportProtocol
|
streamSetting.Network = &transportProtocol
|
||||||
|
|
||||||
// Build TLS and XTLS settings
|
// Build TLS and REALITY settings
|
||||||
if nodeInfo.EnableTLS && config.CertConfig.CertMode != "none" {
|
var isREALITY bool
|
||||||
streamSetting.Security = nodeInfo.TLSType
|
if config.DisableLocalREALITYConfig {
|
||||||
|
if nodeInfo.REALITYConfig != nil && nodeInfo.EnableREALITY {
|
||||||
|
isREALITY = true
|
||||||
|
streamSetting.Security = "reality"
|
||||||
|
|
||||||
|
r := nodeInfo.REALITYConfig
|
||||||
|
streamSetting.REALITYSettings = &conf.REALITYConfig{
|
||||||
|
Show: config.REALITYConfigs.Show,
|
||||||
|
Dest: []byte(`"` + r.Dest + `"`),
|
||||||
|
Xver: r.ProxyProtocolVer,
|
||||||
|
ServerNames: r.ServerNames,
|
||||||
|
PrivateKey: r.PrivateKey,
|
||||||
|
MinClientVer: r.MinClientVer,
|
||||||
|
MaxClientVer: r.MaxClientVer,
|
||||||
|
MaxTimeDiff: r.MaxTimeDiff,
|
||||||
|
ShortIds: r.ShortIds,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if config.EnableREALITY && config.REALITYConfigs != nil {
|
||||||
|
isREALITY = true
|
||||||
|
streamSetting.Security = "reality"
|
||||||
|
|
||||||
|
streamSetting.REALITYSettings = &conf.REALITYConfig{
|
||||||
|
Show: config.REALITYConfigs.Show,
|
||||||
|
Dest: []byte(`"` + config.REALITYConfigs.Dest + `"`),
|
||||||
|
Xver: config.REALITYConfigs.ProxyProtocolVer,
|
||||||
|
ServerNames: config.REALITYConfigs.ServerNames,
|
||||||
|
PrivateKey: config.REALITYConfigs.PrivateKey,
|
||||||
|
MinClientVer: config.REALITYConfigs.MinClientVer,
|
||||||
|
MaxClientVer: config.REALITYConfigs.MaxClientVer,
|
||||||
|
MaxTimeDiff: config.REALITYConfigs.MaxTimeDiff,
|
||||||
|
ShortIds: config.REALITYConfigs.ShortIds,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isREALITY && nodeInfo.EnableTLS && config.CertConfig.CertMode != "none" {
|
||||||
|
streamSetting.Security = "tls"
|
||||||
certFile, keyFile, err := getCertFile(config.CertConfig)
|
certFile, keyFile, err := getCertFile(config.CertConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if nodeInfo.TLSType == "tls" {
|
tlsSettings := &conf.TLSConfig{
|
||||||
tlsSettings := &conf.TLSConfig{
|
RejectUnknownSNI: config.CertConfig.RejectUnknownSni,
|
||||||
RejectUnknownSNI: config.CertConfig.RejectUnknownSni,
|
|
||||||
}
|
|
||||||
tlsSettings.Certs = append(tlsSettings.Certs, &conf.TLSCertConfig{CertFile: certFile, KeyFile: keyFile, OcspStapling: 3600})
|
|
||||||
|
|
||||||
streamSetting.TLSSettings = tlsSettings
|
|
||||||
} else if nodeInfo.TLSType == "xtls" {
|
|
||||||
xtlsSettings := &conf.XTLSConfig{
|
|
||||||
RejectUnknownSNI: config.CertConfig.RejectUnknownSni,
|
|
||||||
}
|
|
||||||
xtlsSettings.Certs = append(xtlsSettings.Certs, &conf.XTLSCertConfig{CertFile: certFile, KeyFile: keyFile, OcspStapling: 3600})
|
|
||||||
streamSetting.XTLSSettings = xtlsSettings
|
|
||||||
}
|
}
|
||||||
|
tlsSettings.Certs = append(tlsSettings.Certs, &conf.TLSCertConfig{CertFile: certFile, KeyFile: keyFile, OcspStapling: 3600})
|
||||||
|
streamSetting.TLSSettings = tlsSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
// Support ProxyProtocol for any transport protocol
|
// Support ProxyProtocol for any transport protocol
|
||||||
@ -260,13 +295,13 @@ func buildVlessFallbacks(fallbackConfigs []*FallBackConfig) ([]*conf.VLessInboun
|
|||||||
for i, c := range fallbackConfigs {
|
for i, c := range fallbackConfigs {
|
||||||
|
|
||||||
if c.Dest == "" {
|
if c.Dest == "" {
|
||||||
return nil, fmt.Errorf("dest is required for fallback fialed")
|
return nil, fmt.Errorf("dest is required for fallback failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
var dest json.RawMessage
|
var dest json.RawMessage
|
||||||
dest, err := json.Marshal(c.Dest)
|
dest, err := json.Marshal(c.Dest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("marshal dest %s config fialed: %s", dest, err)
|
return nil, fmt.Errorf("marshal dest %s config failed: %s", dest, err)
|
||||||
}
|
}
|
||||||
vlessFallBacks[i] = &conf.VLessInboundFallback{
|
vlessFallBacks[i] = &conf.VLessInboundFallback{
|
||||||
Name: c.SNI,
|
Name: c.SNI,
|
||||||
@ -288,13 +323,13 @@ func buildTrojanFallbacks(fallbackConfigs []*FallBackConfig) ([]*conf.TrojanInbo
|
|||||||
for i, c := range fallbackConfigs {
|
for i, c := range fallbackConfigs {
|
||||||
|
|
||||||
if c.Dest == "" {
|
if c.Dest == "" {
|
||||||
return nil, fmt.Errorf("dest is required for fallback fialed")
|
return nil, fmt.Errorf("dest is required for fallback failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
var dest json.RawMessage
|
var dest json.RawMessage
|
||||||
dest, err := json.Marshal(c.Dest)
|
dest, err := json.Marshal(c.Dest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("marshal dest %s config fialed: %s", dest, err)
|
return nil, fmt.Errorf("marshal dest %s config failed: %s", dest, err)
|
||||||
}
|
}
|
||||||
trojanFallBacks[i] = &conf.TrojanInboundFallback{
|
trojanFallBacks[i] = &conf.TrojanInboundFallback{
|
||||||
Name: c.SNI,
|
Name: c.SNI,
|
||||||
|
@ -19,7 +19,6 @@ func TestBuildV2ray(t *testing.T) {
|
|||||||
Host: "test.test.tk",
|
Host: "test.test.tk",
|
||||||
Path: "v2ray",
|
Path: "v2ray",
|
||||||
EnableTLS: false,
|
EnableTLS: false,
|
||||||
TLSType: "tls",
|
|
||||||
}
|
}
|
||||||
certConfig := &mylego.CertConfig{
|
certConfig := &mylego.CertConfig{
|
||||||
CertMode: "http",
|
CertMode: "http",
|
||||||
@ -47,7 +46,6 @@ func TestBuildTrojan(t *testing.T) {
|
|||||||
Host: "trojan.test.tk",
|
Host: "trojan.test.tk",
|
||||||
Path: "v2ray",
|
Path: "v2ray",
|
||||||
EnableTLS: false,
|
EnableTLS: false,
|
||||||
TLSType: "tls",
|
|
||||||
}
|
}
|
||||||
DNSEnv := make(map[string]string)
|
DNSEnv := make(map[string]string)
|
||||||
DNSEnv["ALICLOUD_ACCESS_KEY"] = "aaa"
|
DNSEnv["ALICLOUD_ACCESS_KEY"] = "aaa"
|
||||||
@ -79,7 +77,6 @@ func TestBuildSS(t *testing.T) {
|
|||||||
Host: "test.test.tk",
|
Host: "test.test.tk",
|
||||||
Path: "v2ray",
|
Path: "v2ray",
|
||||||
EnableTLS: false,
|
EnableTLS: false,
|
||||||
TLSType: "tls",
|
|
||||||
}
|
}
|
||||||
DNSEnv := make(map[string]string)
|
DNSEnv := make(map[string]string)
|
||||||
DNSEnv["ALICLOUD_ACCESS_KEY"] = "aaa"
|
DNSEnv["ALICLOUD_ACCESS_KEY"] = "aaa"
|
||||||
|
@ -4,7 +4,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common/net"
|
|
||||||
"github.com/xtls/xray-core/core"
|
"github.com/xtls/xray-core/core"
|
||||||
"github.com/xtls/xray-core/infra/conf"
|
"github.com/xtls/xray-core/infra/conf"
|
||||||
|
|
||||||
@ -17,11 +16,8 @@ func OutboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.
|
|||||||
outboundDetourConfig.Protocol = "freedom"
|
outboundDetourConfig.Protocol = "freedom"
|
||||||
outboundDetourConfig.Tag = tag
|
outboundDetourConfig.Tag = tag
|
||||||
|
|
||||||
// Build Send IP address
|
// SendThrough setting
|
||||||
if config.SendIP != "" {
|
outboundDetourConfig.SendThrough = &config.SendIP
|
||||||
ipAddress := net.ParseAddress(config.SendIP)
|
|
||||||
outboundDetourConfig.SendThrough = &conf.Address{Address: ipAddress}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Freedom Protocol setting
|
// Freedom Protocol setting
|
||||||
var domainStrategy = "Asis"
|
var domainStrategy = "Asis"
|
||||||
@ -42,7 +38,7 @@ func OutboundBuilder(config *Config, nodeInfo *api.NodeInfo, tag string) (*core.
|
|||||||
var setting json.RawMessage
|
var setting json.RawMessage
|
||||||
setting, err := json.Marshal(proxySetting)
|
setting, err := json.Marshal(proxySetting)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("marshal proxy %s config fialed: %s", nodeInfo.NodeType, err)
|
return nil, fmt.Errorf("marshal proxy %s config failed: %s", nodeInfo.NodeType, err)
|
||||||
}
|
}
|
||||||
outboundDetourConfig.Settings = &setting
|
outboundDetourConfig.Settings = &setting
|
||||||
return outboundDetourConfig.Build()
|
return outboundDetourConfig.Build()
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
package controller
|
package controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
|
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
|
||||||
C "github.com/sagernet/sing/common"
|
C "github.com/sagernet/sing/common"
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
"github.com/xtls/xray-core/common/protocol"
|
"github.com/xtls/xray-core/common/protocol"
|
||||||
"github.com/xtls/xray-core/common/serial"
|
"github.com/xtls/xray-core/common/serial"
|
||||||
"github.com/xtls/xray-core/infra/conf"
|
"github.com/xtls/xray-core/infra/conf"
|
||||||
@ -25,12 +27,11 @@ var AEADMethod = map[shadowsocks.CipherType]uint8{
|
|||||||
shadowsocks.CipherType_XCHACHA20_POLY1305: 0,
|
shadowsocks.CipherType_XCHACHA20_POLY1305: 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) buildVmessUser(userInfo *[]api.UserInfo, serverAlterID uint16) (users []*protocol.User) {
|
func (c *Controller) buildVmessUser(userInfo *[]api.UserInfo) (users []*protocol.User) {
|
||||||
users = make([]*protocol.User, len(*userInfo))
|
users = make([]*protocol.User, len(*userInfo))
|
||||||
for i, user := range *userInfo {
|
for i, user := range *userInfo {
|
||||||
vmessAccount := &conf.VMessAccount{
|
vmessAccount := &conf.VMessAccount{
|
||||||
ID: user.UUID,
|
ID: user.UUID,
|
||||||
AlterIds: serverAlterID,
|
|
||||||
Security: "auto",
|
Security: "auto",
|
||||||
}
|
}
|
||||||
users[i] = &protocol.User{
|
users[i] = &protocol.User{
|
||||||
@ -47,7 +48,7 @@ func (c *Controller) buildVlessUser(userInfo *[]api.UserInfo) (users []*protocol
|
|||||||
for i, user := range *userInfo {
|
for i, user := range *userInfo {
|
||||||
vlessAccount := &vless.Account{
|
vlessAccount := &vless.Account{
|
||||||
Id: user.UUID,
|
Id: user.UUID,
|
||||||
Flow: "xtls-rprx-vision,none",
|
Flow: c.nodeInfo.VlessFlow,
|
||||||
}
|
}
|
||||||
users[i] = &protocol.User{
|
users[i] = &protocol.User{
|
||||||
Level: 0,
|
Level: 0,
|
||||||
@ -63,7 +64,6 @@ func (c *Controller) buildTrojanUser(userInfo *[]api.UserInfo) (users []*protoco
|
|||||||
for i, user := range *userInfo {
|
for i, user := range *userInfo {
|
||||||
trojanAccount := &trojan.Account{
|
trojanAccount := &trojan.Account{
|
||||||
Password: user.UUID,
|
Password: user.UUID,
|
||||||
Flow: "xtls-rprx-direct",
|
|
||||||
}
|
}
|
||||||
users[i] = &protocol.User{
|
users[i] = &protocol.User{
|
||||||
Level: 0,
|
Level: 0,
|
||||||
@ -83,16 +83,14 @@ func (c *Controller) buildSSUser(userInfo *[]api.UserInfo, method string) (users
|
|||||||
e := c.buildUserTag(&user)
|
e := c.buildUserTag(&user)
|
||||||
userKey, err := c.checkShadowsocksPassword(user.Passwd, method)
|
userKey, err := c.checkShadowsocksPassword(user.Passwd, method)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
newError(fmt.Errorf("[UID: %d] %s", user.UID, err)).AtError().WriteToLog()
|
errors.LogError(context.Background(), "[UID: %d] %s", user.UID, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
users[i] = &protocol.User{
|
users[i] = &protocol.User{
|
||||||
Level: 0,
|
Level: 0,
|
||||||
Email: e,
|
Email: e,
|
||||||
Account: serial.ToTypedMessage(&shadowsocks_2022.User{
|
Account: serial.ToTypedMessage(&shadowsocks_2022.Account{
|
||||||
Key: userKey,
|
Key: userKey,
|
||||||
Email: e,
|
|
||||||
Level: 0,
|
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -118,16 +116,14 @@ func (c *Controller) buildSSPluginUser(userInfo *[]api.UserInfo) (users []*proto
|
|||||||
e := c.buildUserTag(&user)
|
e := c.buildUserTag(&user)
|
||||||
userKey, err := c.checkShadowsocksPassword(user.Passwd, user.Method)
|
userKey, err := c.checkShadowsocksPassword(user.Passwd, user.Method)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
newError(fmt.Errorf("[UID: %d] %s", user.UID, err)).AtError().WriteToLog()
|
errors.LogError(context.Background(), "[UID: %d] %s", user.UID, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
users[i] = &protocol.User{
|
users[i] = &protocol.User{
|
||||||
Level: 0,
|
Level: 0,
|
||||||
Email: e,
|
Email: e,
|
||||||
Account: serial.ToTypedMessage(&shadowsocks_2022.User{
|
Account: serial.ToTypedMessage(&shadowsocks_2022.Account{
|
||||||
Key: userKey,
|
Key: userKey,
|
||||||
Email: e,
|
|
||||||
Level: 0,
|
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user