Compare commits

...

181 Commits

Author SHA1 Message Date
OPPO9008
ea125e4a4f
Update actions/upload-artifact: v3 to v4 (#742)
Some checks failed
CodeQL / Analyze (go) (push) Failing after 3m24s
Close inactive issues / close-issues (push) Successful in 7s
https://github.blog/changelog/2024-04-16-deprecation-notice-v3-of-the-artifact-actions/
2025-03-21 17:45:20 +08:00
kia678
6754483cca
Update docker.yml
Some checks failed
CodeQL / Analyze (go) (push) Failing after 39s
2025-03-21 02:27:52 +08:00
xrayrgoon
974abc9a98
xray-core v25.3.6 and other updates (#748)
Some checks failed
CodeQL / Analyze (go) (push) Failing after 4m49s
Build and Release / build (386, freebsd) (push) Failing after 8m40s
Build and Release / build (386, linux) (push) Failing after 7m10s
Build and Release / build (386, windows) (push) Failing after 6m49s
Build and Release / build (amd64, darwin) (push) Failing after 7m8s
Build and Release / build (amd64, freebsd) (push) Failing after 7m31s
Build and Release / build (amd64, linux) (push) Failing after 7m20s
Build and Release / build (amd64, openbsd) (push) Failing after 7m4s
Build and Release / build (amd64, windows) (push) Failing after 7m9s
Build and Release / build (arm, 5, linux) (push) Failing after 6m44s
Build and Release / build (arm, 6, linux) (push) Failing after 6m42s
Build and Release / build (arm, 7, freebsd) (push) Failing after 6m32s
Build and Release / build (arm, 7, linux) (push) Failing after 6m43s
Build and Release / build (arm64, android) (push) Failing after 6m59s
Build and Release / build (arm64, darwin) (push) Failing after 6m54s
Build and Release / build (arm64, freebsd) (push) Failing after 6m52s
Build and Release / build (arm64, linux) (push) Failing after 7m5s
Build and Release / build (mips, linux) (push) Failing after 12m35s
Build and Release / build (mips64, linux) (push) Failing after 7m4s
Build and Release / build (mips64le, linux) (push) Failing after 6m39s
Build and Release / build (mipsle, linux) (push) Failing after 11m56s
Build and Release / build (ppc64le, linux) (push) Failing after 6m54s
Build and Release / build (riscv64, linux) (push) Failing after 8m10s
Build and Release / build (s390x, linux) (push) Failing after 6m56s
Close inactive issues / close-issues (push) Successful in 25s
2025-03-21 02:07:22 +08:00
dependabot[bot]
b1f5ccfee8
build(deps): bump github.com/eko/gocache/store/go_cache/v4 (#701)
Bumps [github.com/eko/gocache/store/go_cache/v4](https://github.com/eko/gocache) from 4.2.1 to 4.2.2.
- [Release notes](https://github.com/eko/gocache/releases)
- [Commits](https://github.com/eko/gocache/compare/store/redis/v4.2.1...store/redis/v4.2.2)

---
updated-dependencies:
- dependency-name: github.com/eko/gocache/store/go_cache/v4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-31 11:30:13 +08:00
dependabot[bot]
2f88334373
build(deps): bump golang.org/x/crypto from 0.25.0 to 0.28.0 (#704)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.25.0 to 0.28.0.
- [Commits](https://github.com/golang/crypto/compare/v0.25.0...v0.28.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-31 11:29:51 +08:00
dependabot[bot]
35e726e766
build(deps): bump golang.org/x/time from 0.5.0 to 0.7.0 (#703)
Bumps [golang.org/x/time](https://github.com/golang/time) from 0.5.0 to 0.7.0.
- [Commits](https://github.com/golang/time/compare/v0.5.0...v0.7.0)

---
updated-dependencies:
- dependency-name: golang.org/x/time
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-31 11:29:31 +08:00
sakzri
4bbf3c9899
fix: handle domain with wildcards (#690) 2024-10-19 22:04:08 +08:00
RyarX
9391c759c9
New transports and api changes for V2RaySocks (#685)
* New transports and api changes for V2RaySocks

* version++
2024-10-19 22:03:05 +08:00
dependabot[bot]
eb16b709e2
build(deps): bump github.com/shirou/gopsutil/v3 from 3.24.2 to 3.24.5 (#666)
Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.24.2 to 3.24.5.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/compare/v3.24.2...v3.24.5)

---
updated-dependencies:
- dependency-name: github.com/shirou/gopsutil/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-19 22:01:46 +08:00
dependabot[bot]
1f09502613
build(deps): bump github.com/spf13/cobra from 1.8.0 to 1.8.1 (#665)
Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.8.0 to 1.8.1.
- [Release notes](https://github.com/spf13/cobra/releases)
- [Commits](https://github.com/spf13/cobra/compare/v1.8.0...v1.8.1)

---
updated-dependencies:
- dependency-name: github.com/spf13/cobra
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-19 22:01:24 +08:00
dependabot[bot]
0ae73a0eca
build(deps): bump actions/download-artifact in /.github/workflows (#688)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 3 to 4.1.7.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v3...v4.1.7)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-19 22:00:58 +08:00
pings
63c1a18a7d
Optimize and upgrade docking parameters (#696) 2024-10-19 22:00:11 +08:00
dependabot[bot]
34726d1659
build(deps): bump github.com/eko/gocache/lib/v4 from 4.1.5 to 4.1.6 (#668)
Bumps [github.com/eko/gocache/lib/v4](https://github.com/eko/gocache) from 4.1.5 to 4.1.6.
- [Release notes](https://github.com/eko/gocache/releases)
- [Commits](https://github.com/eko/gocache/compare/lib/v4.1.5...lib/v4.1.6)

---
updated-dependencies:
- dependency-name: github.com/eko/gocache/lib/v4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-19 21:59:06 +08:00
dependabot[bot]
5edc68d475
build(deps): bump github.com/redis/go-redis/v9 from 9.5.1 to 9.7.0 (#699)
Bumps [github.com/redis/go-redis/v9](https://github.com/redis/go-redis) from 9.5.1 to 9.7.0.
- [Release notes](https://github.com/redis/go-redis/releases)
- [Changelog](https://github.com/redis/go-redis/blob/master/CHANGELOG.md)
- [Commits](https://github.com/redis/go-redis/compare/v9.5.1...v9.7.0)

---
updated-dependencies:
- dependency-name: github.com/redis/go-redis/v9
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-19 21:57:38 +08:00
mengxin239
944e8cd6a8 version++ 2024-07-21 13:12:31 +08:00
wyx2685
c4581ad34b
Xray core1.8.20 & support splitHTTP (#661)
* upgrade xray core 1.8.20

* support splitHTTP
2024-07-21 10:59:38 +08:00
dependabot[bot]
127318ccbd
build(deps): bump github.com/go-resty/resty/v2 from 2.11.0 to 2.13.1 (#660)
Bumps [github.com/go-resty/resty/v2](https://github.com/go-resty/resty) from 2.11.0 to 2.13.1.
- [Release notes](https://github.com/go-resty/resty/releases)
- [Commits](https://github.com/go-resty/resty/compare/v2.11.0...v2.13.1)

---
updated-dependencies:
- dependency-name: github.com/go-resty/resty/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-20 20:49:16 +08:00
dependabot[bot]
db784f18e6
build(deps): bump google.golang.org/grpc from 1.64.0 to 1.64.1 (#659)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.64.0 to 1.64.1.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.64.0...v1.64.1)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-20 19:59:59 +08:00
dependabot[bot]
8a5a5e4356
build(deps): bump github.com/Azure/azure-sdk-for-go/sdk/azidentity (#658)
Bumps [github.com/Azure/azure-sdk-for-go/sdk/azidentity](https://github.com/Azure/azure-sdk-for-go) from 1.5.1 to 1.6.0.
- [Release notes](https://github.com/Azure/azure-sdk-for-go/releases)
- [Changelog](https://github.com/Azure/azure-sdk-for-go/blob/main/documentation/release.md)
- [Commits](https://github.com/Azure/azure-sdk-for-go/compare/sdk/internal/v1.5.1...sdk/azcore/v1.6.0)

---
updated-dependencies:
- dependency-name: github.com/Azure/azure-sdk-for-go/sdk/azidentity
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-20 19:56:22 +08:00
dependabot[bot]
cfdb5166f3
build(deps): bump github.com/gogf/gf/v2 from 2.6.4 to 2.7.0 (#632)
Bumps [github.com/gogf/gf/v2](https://github.com/gogf/gf) from 2.6.4 to 2.7.0.
- [Release notes](https://github.com/gogf/gf/releases)
- [Commits](https://github.com/gogf/gf/compare/v2.6.4...v2.7.0)

---
updated-dependencies:
- dependency-name: github.com/gogf/gf/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-20 19:54:40 +08:00
dependabot[bot]
540be1b7c8
build(deps): bump github.com/hashicorp/go-retryablehttp (#657)
Bumps [github.com/hashicorp/go-retryablehttp](https://github.com/hashicorp/go-retryablehttp) from 0.7.5 to 0.7.7.
- [Changelog](https://github.com/hashicorp/go-retryablehttp/blob/main/CHANGELOG.md)
- [Commits](https://github.com/hashicorp/go-retryablehttp/compare/v0.7.5...v0.7.7)

---
updated-dependencies:
- dependency-name: github.com/hashicorp/go-retryablehttp
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-20 19:53:05 +08:00
dependabot[bot]
98014fa60f
build(deps): bump golang.org/x/net from 0.26.0 to 0.27.0 (#656)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.26.0 to 0.27.0.
- [Commits](https://github.com/golang/net/compare/v0.26.0...v0.27.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-20 19:41:24 +08:00
mengxin239
1de718a5fc Adjusting docker.yml triggers 2024-07-20 19:16:09 +08:00
MengXin239
81c068bad4 update config.yml.example 2024-06-21 12:49:12 +08:00
MengXin239
dbde0c24fc version++ 2024-06-09 22:19:33 +08:00
MengXin239
1bbd061d42 upgrade xray core 2024-06-09 22:12:24 +08:00
MengXin239
793f26e763 fixed some bug 2024-06-09 20:52:32 +08:00
MengXin239
175e46d0b7 add quic support and fixed some bug 2024-06-09 17:58:22 +08:00
_rave_cat_
9261a6063d
chore: fix typos. (#640)
* chore: fix typos.

* chore: makes go-staticcheck happy.
2024-06-09 15:21:27 +08:00
Blank Paper
5ee2679924
fix compatibility with Xray-core v1.8.10 (#629) 2024-04-16 22:46:28 +08:00
thh1451
06fe198243
add DeviceLimit to V2RaySocks (#622) 2024-04-10 17:57:19 +08:00
thh1451
b3f31bf06b
Modify the way v2raysocks obtains methods and audits (#616)
Co-authored-by: thank243 <thank243@gmail.com>
2024-03-19 00:01:19 +08:00
thank243
0010a876f3
Remove unnecessary user retrieval in v2raysocks (#617)
The updated code eliminates a redundant user retrieval operation in the v2raysocks module. This operation was not needed because the desired information (method) can be inferred elsewhere in the application. This change simplifies the code, improves efficiency, and may reduce potential errors related to user data retrieval.
2024-03-18 23:50:18 +08:00
Senis
dbed635aae
fixes, enhancing the efficiency and robustness of the system. 2024-03-15 21:44:15 +08:00
Senis
561f317e24
Update dependencies and upgrade Google Cloud services
Upgraded various dependencies and Google Cloud services to improve the application's performance and stability. Multiple version upgrades reflect ongoing development efforts and bug
2024-02-20 23:50:54 +08:00
dependabot[bot]
c24a2a28f2
build(deps): bump github.com/shirou/gopsutil/v3 from 3.23.12 to 3.24.1 (#589)
Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.23.12 to 3.24.1.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/compare/v3.23.12...v3.24.1)

---
updated-dependencies:
- dependency-name: github.com/shirou/gopsutil/v3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-03 20:34:51 +08:00
dependabot[bot]
fa80ed8014
build(deps): bump github.com/go-acme/lego/v4 from 4.14.2 to 4.15.0 (#588)
Bumps [github.com/go-acme/lego/v4](https://github.com/go-acme/lego) from 4.14.2 to 4.15.0.
- [Release notes](https://github.com/go-acme/lego/releases)
- [Changelog](https://github.com/go-acme/lego/blob/master/CHANGELOG.md)
- [Commits](https://github.com/go-acme/lego/compare/v4.14.2...v4.15.0)

---
updated-dependencies:
- dependency-name: github.com/go-acme/lego/v4
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-30 12:28:50 +08:00
dependabot[bot]
5409cb2dc3
build(deps): bump github.com/gogf/gf/v2 from 2.6.1 to 2.6.2 (#585)
Bumps [github.com/gogf/gf/v2](https://github.com/gogf/gf) from 2.6.1 to 2.6.2.
- [Release notes](https://github.com/gogf/gf/releases)
- [Commits](https://github.com/gogf/gf/compare/v2.6.1...v2.6.2)

---
updated-dependencies:
- dependency-name: github.com/gogf/gf/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-23 06:28:05 +08:00
壳壳中的宇宙
b8b3a16d3b
fix custom inbound listener to not use 0.0.0.0 (#584) 2024-01-20 09:23:12 +08:00
Jat
d1e5762937
support passing redis network and redis username to redis client (#576)
Signed-off-by: Jat <jat@sinosky.org>
2024-01-12 22:22:28 +08:00
dependabot[bot]
4f1dafa8ed
build(deps): bump golang.org/x/net from 0.19.0 to 0.20.0 (#578)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.19.0 to 0.20.0.
- [Commits](https://github.com/golang/net/compare/v0.19.0...v0.20.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-12 22:01:32 +08:00
dependabot[bot]
aae0f9b24d
build(deps): bump github.com/redis/go-redis/v9 from 9.3.1 to 9.4.0 (#574)
Bumps [github.com/redis/go-redis/v9](https://github.com/redis/go-redis) from 9.3.1 to 9.4.0.
- [Release notes](https://github.com/redis/go-redis/releases)
- [Changelog](https://github.com/redis/go-redis/blob/master/CHANGELOG.md)
- [Commits](https://github.com/redis/go-redis/compare/v9.3.1...v9.4.0)

---
updated-dependencies:
- dependency-name: github.com/redis/go-redis/v9
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-09 18:21:53 +08:00
dependabot[bot]
aee8ca2980
build(deps): bump github.com/xtls/xray-core from 1.8.6 to 1.8.7 (#573)
Bumps [github.com/xtls/xray-core](https://github.com/xtls/xray-core) from 1.8.6 to 1.8.7.
- [Release notes](https://github.com/xtls/xray-core/releases)
- [Commits](https://github.com/xtls/xray-core/compare/v1.8.6...v1.8.7)

---
updated-dependencies:
- dependency-name: github.com/xtls/xray-core
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-09 06:37:38 +08:00
dependabot[bot]
933e745a70
build(deps): bump github.com/sagernet/sing from 0.2.20 to 0.2.21 (#569)
Bumps [github.com/sagernet/sing](https://github.com/sagernet/sing) from 0.2.20 to 0.2.21.
- [Commits](https://github.com/sagernet/sing/compare/v0.2.20...v0.2.21)

---
updated-dependencies:
- dependency-name: github.com/sagernet/sing
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-07 10:26:37 +08:00
wyx2685
515fc708dc
Remove dragonfly compile. (#567)
Fix Actions
2024-01-03 10:21:46 +08:00
dependabot[bot]
5e134967fd
build(deps): bump github.com/shirou/gopsutil/v3 from 3.23.11 to 3.23.12 (#566)
Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.23.11 to 3.23.12.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/compare/v3.23.11...v3.23.12)

---
updated-dependencies:
- dependency-name: github.com/shirou/gopsutil/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-03 10:20:56 +08:00
Senis
4a234d50e2
Add debug level logging option
This update introduces the ability to enable logging from the caller when the log level is set to "debug". This additional logging feature will provide more specificity and context for debugging tasks, aiding in quicker issue resolution.
2023-12-29 12:25:33 +08:00
Senis
115d7bad6f
Replace standard log package with logrus
The standard "log" package was replaced by the structured logger "github.com/sirupsen/logrus" for better log control in various files. This change will allow to tailor the logging information more precisely and make logs easier to read and analyze. All calls of standard log methods were replaced by their logrus counterparts.
2023-12-28 13:40:31 +08:00
dependabot[bot]
5ba0624bbc
build(deps): bump github.com/go-resty/resty/v2 from 2.10.0 to 2.11.0 (#564)
Bumps [github.com/go-resty/resty/v2](https://github.com/go-resty/resty) from 2.10.0 to 2.11.0.
- [Release notes](https://github.com/go-resty/resty/releases)
- [Commits](https://github.com/go-resty/resty/compare/v2.10.0...v2.11.0)

---
updated-dependencies:
- dependency-name: github.com/go-resty/resty/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-28 00:41:09 +08:00
dependabot[bot]
4439afa29b
build(deps): bump github.com/sagernet/sing from 0.2.19 to 0.2.20 (#558)
Bumps [github.com/sagernet/sing](https://github.com/sagernet/sing) from 0.2.19 to 0.2.20.
- [Commits](https://github.com/sagernet/sing/compare/v0.2.19...v0.2.20)

---
updated-dependencies:
- dependency-name: github.com/sagernet/sing
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-24 02:50:55 +08:00
dependabot[bot]
03c9fe4218
build(deps): bump github.com/gogf/gf/v2 from 2.6.0 to 2.6.1 (#560)
Bumps [github.com/gogf/gf/v2](https://github.com/gogf/gf) from 2.6.0 to 2.6.1.
- [Release notes](https://github.com/gogf/gf/releases)
- [Commits](https://github.com/gogf/gf/compare/v2.6.0...v2.6.1)

---
updated-dependencies:
- dependency-name: github.com/gogf/gf/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-24 02:49:39 +08:00
dependabot[bot]
f648aedc91
build(deps): bump google.golang.org/protobuf from 1.31.0 to 1.32.0 (#561)
Bumps google.golang.org/protobuf from 1.31.0 to 1.32.0.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-24 02:49:28 +08:00
dependabot[bot]
1720fbdc4c
build(deps): bump github.com/redis/go-redis/v9 from 9.3.0 to 9.3.1 (#555)
Bumps [github.com/redis/go-redis/v9](https://github.com/redis/go-redis) from 9.3.0 to 9.3.1.
- [Release notes](https://github.com/redis/go-redis/releases)
- [Changelog](https://github.com/redis/go-redis/blob/master/CHANGELOG.md)
- [Commits](https://github.com/redis/go-redis/compare/v9.3.0...v9.3.1)

---
updated-dependencies:
- dependency-name: github.com/redis/go-redis/v9
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-24 02:49:13 +08:00
dependabot[bot]
cd7907c4c9
build(deps): bump github.com/spf13/viper from 1.18.1 to 1.18.2 (#556)
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.18.1 to 1.18.2.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.18.1...v1.18.2)

---
updated-dependencies:
- dependency-name: github.com/spf13/viper
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-24 02:49:05 +08:00
dependabot[bot]
296ba86bdd
build(deps): bump golang.org/x/crypto from 0.16.0 to 0.17.0 (#554)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.16.0 to 0.17.0.
- [Commits](https://github.com/golang/crypto/compare/v0.16.0...v0.17.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-19 08:18:48 +08:00
dependabot[bot]
43e6ec85cd
build(deps): bump github.com/gogf/gf/v2 from 2.5.7 to 2.6.0 (#553)
Bumps [github.com/gogf/gf/v2](https://github.com/gogf/gf) from 2.5.7 to 2.6.0.
- [Release notes](https://github.com/gogf/gf/releases)
- [Commits](https://github.com/gogf/gf/compare/v2.5.7...v2.6.0)

---
updated-dependencies:
- dependency-name: github.com/gogf/gf/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-19 00:03:33 +08:00
dependabot[bot]
7ba2d999f0
build(deps): bump github.com/sagernet/sing from 0.2.18 to 0.2.19 (#550)
Bumps [github.com/sagernet/sing](https://github.com/sagernet/sing) from 0.2.18 to 0.2.19.
- [Commits](https://github.com/sagernet/sing/compare/v0.2.18...v0.2.19)

---
updated-dependencies:
- dependency-name: github.com/sagernet/sing
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-12 21:22:06 +08:00
dependabot[bot]
53141f4416
build(deps): bump github.com/spf13/viper from 1.17.0 to 1.18.1 (#548)
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.17.0 to 1.18.1.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.17.0...v1.18.1)

---
updated-dependencies:
- dependency-name: github.com/spf13/viper
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-09 22:46:41 +08:00
dependabot[bot]
e378f0a367
build(deps): bump github.com/sagernet/sing-shadowsocks (#549)
Bumps [github.com/sagernet/sing-shadowsocks](https://github.com/sagernet/sing-shadowsocks) from 0.2.5 to 0.2.6.
- [Commits](https://github.com/sagernet/sing-shadowsocks/compare/v0.2.5...v0.2.6)

---
updated-dependencies:
- dependency-name: github.com/sagernet/sing-shadowsocks
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-09 22:42:35 +08:00
dependabot[bot]
d01a4265cd
build(deps): bump github.com/sagernet/sing from 0.2.17 to 0.2.18 (#542)
Bumps [github.com/sagernet/sing](https://github.com/sagernet/sing) from 0.2.17 to 0.2.18.
- [Commits](https://github.com/sagernet/sing/compare/v0.2.17...v0.2.18)

---
updated-dependencies:
- dependency-name: github.com/sagernet/sing
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-05 06:59:19 +08:00
dependabot[bot]
94d2c7c23a
build(deps): bump github.com/shirou/gopsutil/v3 from 3.23.10 to 3.23.11 (#538)
Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.23.10 to 3.23.11.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/compare/v3.23.10...v3.23.11)

---
updated-dependencies:
- dependency-name: github.com/shirou/gopsutil/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-03 19:42:23 +08:00
isluckys
3de7600a4c
修复类型判断问题 (#539)
之前一个pr写的if直接写成了vmess导致其他的比如说ss、Trojan的都会被覆盖
2023-12-03 19:42:03 +08:00
dependabot[bot]
a0378b6cff
build(deps): bump golang.org/x/net from 0.18.0 to 0.19.0 (#536)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.18.0 to 0.19.0.
- [Commits](https://github.com/golang/net/compare/v0.18.0...v0.19.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-29 18:13:52 +08:00
dependabot[bot]
4688833bc7
build(deps): bump golang.org/x/time from 0.4.0 to 0.5.0 (#537)
Bumps [golang.org/x/time](https://github.com/golang/time) from 0.4.0 to 0.5.0.
- [Commits](https://github.com/golang/time/compare/v0.4.0...v0.5.0)

---
updated-dependencies:
- dependency-name: golang.org/x/time
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-29 17:57:19 +08:00
dependabot[bot]
294a2dfacb
build(deps): bump golang.org/x/crypto from 0.15.0 to 0.16.0 (#535)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.15.0 to 0.16.0.
- [Commits](https://github.com/golang/crypto/compare/v0.15.0...v0.16.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-29 17:55:38 +08:00
pennyMorant
5780731cd3
add bunpanel (#533) 2023-11-28 20:08:59 +08:00
betaxab
911b0c2ff5
api: newV2board: add Vless support (#534) 2023-11-28 20:08:44 +08:00
dependabot[bot]
fa7fb7087f
build(deps): bump github.com/gogf/gf/v2 from 2.5.6 to 2.5.7 (#526)
Bumps [github.com/gogf/gf/v2](https://github.com/gogf/gf) from 2.5.6 to 2.5.7.
- [Release notes](https://github.com/gogf/gf/releases)
- [Commits](https://github.com/gogf/gf/compare/v2.5.6...v2.5.7)

---
updated-dependencies:
- dependency-name: github.com/gogf/gf/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-25 08:36:36 +08:00
dependabot[bot]
0a6bca9755
build(deps): bump github.com/go-jose/go-jose/v3 from 3.0.0 to 3.0.1 (#524)
Bumps [github.com/go-jose/go-jose/v3](https://github.com/go-jose/go-jose) from 3.0.0 to 3.0.1.
- [Release notes](https://github.com/go-jose/go-jose/releases)
- [Changelog](https://github.com/go-jose/go-jose/blob/v3/CHANGELOG.md)
- [Commits](https://github.com/go-jose/go-jose/compare/v3.0.0...v3.0.1)

---
updated-dependencies:
- dependency-name: github.com/go-jose/go-jose/v3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-25 08:36:27 +08:00
dependabot[bot]
8562c798a9
build(deps): bump github.com/xtls/xray-core from 1.8.5 to 1.8.6 (#523)
Bumps [github.com/xtls/xray-core](https://github.com/xtls/xray-core) from 1.8.5 to 1.8.6.
- [Release notes](https://github.com/xtls/xray-core/releases)
- [Commits](https://github.com/xtls/xray-core/compare/v1.8.5...v1.8.6)

---
updated-dependencies:
- dependency-name: github.com/xtls/xray-core
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-25 08:36:18 +08:00
thh1451
56199afa90
V2RaySocks supports Reality (remote config) (#521) 2023-11-18 06:21:08 +08:00
Senis
c688cf02b4
Update Go version and dependencies for better compatibility
Updated Go version in Dockerfile and go.mod to ensure better compatibility and unpinned the Go version in Dockerfile for increased flexibility. Multiple dependencies in go.mod and go.sum have been updated to their newer versions for improved functionality and security. This will keep the project up-to-date with the latest features and bug fixes offered by these dependencies. Removed unused dependencies to keep the project clean and efficient.
2023-11-14 11:32:21 +08:00
thh1451
c170272f40
Add Etag for V2RaySocks (#518) 2023-11-12 06:21:13 +08:00
dependabot[bot]
6ffbe599b4
build(deps): bump golang.org/x/net from 0.17.0 to 0.18.0 (#514)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.17.0 to 0.18.0.
- [Commits](https://github.com/golang/net/compare/v0.17.0...v0.18.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-10 21:24:01 +08:00
thh1451
78c2e31bdf
add 2 acts to V2RaySocks (#516) 2023-11-10 21:23:44 +08:00
dependabot[bot]
c04d52330d
合并拉取请求 #513
* build(deps): bump golang.org/x/crypto from 0.14.0 to 0.15.0
2023-11-10 11:52:49 +08:00
dependabot[bot]
6f4bf62113
build(deps): bump golang.org/x/time from 0.3.0 to 0.4.0 (#506)
Bumps [golang.org/x/time](https://github.com/golang/time) from 0.3.0 to 0.4.0.
- [Commits](https://github.com/golang/time/compare/v0.3.0...v0.4.0)

---
updated-dependencies:
- dependency-name: golang.org/x/time
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-08 19:28:10 +08:00
dependabot[bot]
56e993ce43
build(deps): bump github.com/spf13/cobra from 1.7.0 to 1.8.0 (#505)
Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.7.0 to 1.8.0.
- [Release notes](https://github.com/spf13/cobra/releases)
- [Commits](https://github.com/spf13/cobra/compare/v1.7.0...v1.8.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/cobra
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-08 19:28:00 +08:00
dependabot[bot]
6d3d6f9e53
build(deps): bump github.com/sagernet/sing from 0.2.15 to 0.2.17 (#504)
Bumps [github.com/sagernet/sing](https://github.com/sagernet/sing) from 0.2.15 to 0.2.17.
- [Commits](https://github.com/sagernet/sing/compare/v0.2.15...v0.2.17)

---
updated-dependencies:
- dependency-name: github.com/sagernet/sing
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-08 19:27:49 +08:00
Senis
76076b2d6a Refactor variable assignments in sspanel.go
Simplified the variable assignment logic in sspanel.go to increase code efficiency and readability. "tlsType" is now conditionally initialized once instead of reset multiple times, and it is also properly scoped. Removed an unnecessary check for "nodeConfig.Security" in the "Trojan" case as this value is already taken into account in the efficient, updated logic.
2023-11-08 19:26:38 +08:00
dependabot[bot]
4e48fdbe61
build(deps): bump github.com/shirou/gopsutil/v3 from 3.23.9 to 3.23.10 (#502)
Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.23.9 to 3.23.10.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/compare/v3.23.9...v3.23.10)

---
updated-dependencies:
- dependency-name: github.com/shirou/gopsutil/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-02 01:11:00 +08:00
dependabot[bot]
0f3a4a0008
build(deps): bump github.com/redis/go-redis/v9 from 9.2.1 to 9.3.0 (#501)
Bumps [github.com/redis/go-redis/v9](https://github.com/redis/go-redis) from 9.2.1 to 9.3.0.
- [Release notes](https://github.com/redis/go-redis/releases)
- [Changelog](https://github.com/redis/go-redis/blob/master/CHANGELOG.md)
- [Commits](https://github.com/redis/go-redis/compare/v9.2.1...v9.3.0)

---
updated-dependencies:
- dependency-name: github.com/redis/go-redis/v9
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-01 07:01:12 +08:00
Senis John
d9971b2181
Initialize server port in sspanel
This commit initializes the server port in sspanel.go if the userListResponse is not empty. This adds a level of preventative measure to ensure port is not left uninitialized.
2023-10-28 13:19:17 +08:00
dependabot[bot]
4d1ed21dc7
build(deps): bump github.com/sagernet/sing from 0.2.14 to 0.2.15 (#497)
Bumps [github.com/sagernet/sing](https://github.com/sagernet/sing) from 0.2.14 to 0.2.15.
- [Commits](https://github.com/sagernet/sing/compare/v0.2.14...v0.2.15)

---
updated-dependencies:
- dependency-name: github.com/sagernet/sing
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-27 07:05:28 +08:00
dependabot[bot]
9a03f85930
build(deps): bump github.com/gogf/gf/v2 from 2.5.5 to 2.5.6 (#495)
Bumps [github.com/gogf/gf/v2](https://github.com/gogf/gf) from 2.5.5 to 2.5.6.
- [Release notes](https://github.com/gogf/gf/releases)
- [Commits](https://github.com/gogf/gf/compare/v2.5.5...v2.5.6)

---
updated-dependencies:
- dependency-name: github.com/gogf/gf/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-24 23:51:15 +08:00
dependabot[bot]
11b46ea485
build(deps): bump github.com/fsnotify/fsnotify from 1.6.0 to 1.7.0 (#494)
Bumps [github.com/fsnotify/fsnotify](https://github.com/fsnotify/fsnotify) from 1.6.0 to 1.7.0.
- [Release notes](https://github.com/fsnotify/fsnotify/releases)
- [Changelog](https://github.com/fsnotify/fsnotify/blob/main/CHANGELOG.md)
- [Commits](https://github.com/fsnotify/fsnotify/compare/v1.6.0...v1.7.0)

---
updated-dependencies:
- dependency-name: github.com/fsnotify/fsnotify
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-24 23:51:06 +08:00
dependabot[bot]
dc3d256d85
build(deps): bump github.com/gogf/gf/v2 from 2.5.4 to 2.5.5 (#491)
Bumps [github.com/gogf/gf/v2](https://github.com/gogf/gf) from 2.5.4 to 2.5.5.
- [Release notes](https://github.com/gogf/gf/releases)
- [Commits](https://github.com/gogf/gf/compare/v2.5.4...v2.5.5)

---
updated-dependencies:
- dependency-name: github.com/gogf/gf/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-21 00:36:35 +08:00
Senis John
287c30e7d0
Update application dependencies and improve error logging
This commit updates the version of many dependencies in go.sum and go.mod. It improves error handling in sspanel.go by including both the HTTP response body and error in the logged message. It also updates the xray-core version.
2023-10-18 20:58:39 +08:00
Senis John
97d89549dd
Update application version and enhance error handling
The commit increases the version number from 0.9.1 to 0.9.2 in version.go. Additionally, an error check has been added in x25519.go to validate the private key size. Also, panel.go has been modified to accept both "NewV2board" and "V2board" as valid panel types.
2023-10-17 15:36:32 +08:00
dependabot[bot]
cac4288e07
build(deps): bump github.com/sagernet/sing from 0.2.13 to 0.2.14 (#485)
Bumps [github.com/sagernet/sing](https://github.com/sagernet/sing) from 0.2.13 to 0.2.14.
- [Commits](https://github.com/sagernet/sing/compare/v0.2.13...v0.2.14)

---
updated-dependencies:
- dependency-name: github.com/sagernet/sing
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-17 06:43:26 +08:00
dependabot[bot]
d1d5193cec
build(deps): bump github.com/spf13/cobra from 1.1.1 to 1.7.0 (#486)
Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.1.1 to 1.7.0.
- [Release notes](https://github.com/spf13/cobra/releases)
- [Commits](https://github.com/spf13/cobra/compare/v1.1.1...v1.7.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/cobra
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-17 06:42:44 +08:00
dependabot[bot]
85607527a5
build(deps): bump github.com/go-resty/resty/v2 from 2.9.1 to 2.10.0 (#484)
Bumps [github.com/go-resty/resty/v2](https://github.com/go-resty/resty) from 2.9.1 to 2.10.0.
- [Release notes](https://github.com/go-resty/resty/releases)
- [Commits](https://github.com/go-resty/resty/compare/v2.9.1...v2.10.0)

---
updated-dependencies:
- dependency-name: github.com/go-resty/resty/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-17 06:41:58 +08:00
Senis John
fd0a23bf6c
"Add option to input private key for x25519 command"
Added a command line flag to allow the user to provide an input for the private key while utilizing the x25519 command. The function `x25519` was also modified to account for the possibility of a user-inputted private key. If no private key is provided, random bytes will be generated for the private key as before. This feature provides flexibility for the user in key management.
2023-10-14 21:46:00 +08:00
Senis John
b1bfd04895
Refactor main to use Cobra command line framework and package restructure
Reorganized the Go package structure, moving the main package to 'cmd'. Upgraded the flag library to Cobra for better management of CLI commands. This included moving the X25519 key generation from a flag to its own standalone Cobra command, which improves user interaction and code modularity. This structural change will benefit future additions and code maintainability.
2023-10-14 11:21:45 +08:00
Senis John
edf02307ad
Update Go dependencies and add secure key pair generation
Updated several Go dependencies for enhanced security and performance. Additionally, a command (`-x25519`) has been added for generating a secure key pair using X25519. This provides an easier and safer way for users to generate keys for secure communication. Further instructions have been reflected in the `config.yml.example` file.
2023-10-13 21:55:30 +08:00
dependabot[bot]
551e2d4299
build(deps): bump golang.org/x/net from 0.16.0 to 0.17.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.16.0 to 0.17.0.
- [Commits](https://github.com/golang/net/compare/v0.16.0...v0.17.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-11 15:27:30 +00:00
Senis John
0254c6c557
Update docker.yml 2023-10-09 10:06:46 +08:00
pings
4c52e33adb
对接GoV2Panel面板 (#474)
* Update actions

* up

---------

Co-authored-by: thank243 <thank243@gmail.com>
2023-10-08 12:56:26 +08:00
thank243
b8d40c201b
Update actions 2023-10-08 12:06:18 +08:00
Senis John
dbd4a85a6c
Update Mergo package and Go version
Replaced the Mergo package from "github.com/imdario/mergo" to "dario.cat/mergo". This change was necessary due to the new release from the Mergo package which includes bug fixes and performance improvements.
Moreover, updated the Go version from 1.20 to 1.21.1, including the toolchain version to stay up to date with the latest language features and bug fixes.
Lastly, other dependencies were updated and cleaned up for maintaining package stability and compatibility.
2023-10-08 09:21:39 +08:00
dependabot[bot]
8f28716b21
build(deps): bump github.com/spf13/viper from 1.16.0 to 1.17.0 (#469)
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.16.0 to 1.17.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.16.0...v1.17.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/viper
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-07 13:10:53 +08:00
Senis John
73bc37cb51
Add EnableREALITY field and update REALITYConfig parsing
Enabled the REALITY feature on both model.go and sspanel.go for the api and service assemblies and updated the inboundbuilder to switch the REALITY feature based on the new field and condition statements. This enhancement allows finer control and visibility of the REALITY feature's activation.

In addition, the REALITYConfig in config.go was updated to remove the DisableLocal field and add the DisableLocalREALITYConfig field. The main/config.yml.example file has also been updated to reflect these changes and provide users with a clearer understanding of the config setup.
2023-10-04 23:08:59 +08:00
M1Screw
78f2f88296
feat: update sspanel model (#467) 2023-10-04 20:28:03 +08:00
Senis John
42a2226769
Enhanced ssPanel API by adding panel version check and improving custom config parsing
Added a function to compare the ssPanel version with the baseline version and inform users if their panel version is outdated. Made changes to improve readability and clarity in variable naming and error handling. The handling of the custom config feature has also been significantly optimized, providing more efficient use and easier debugging of parsing issues. The overall changes lead to better compatibility with different ssPanel versions and a more user-friendly custom config feature.
2023-10-04 16:50:52 +08:00
M1Screw
71aba0601e
feat: unify trojan transport & add ss2022 related config (#465) 2023-10-04 10:44:45 +08:00
Senis John
5fe18e020d
Refactor sspanel and test files in the API
Fixed typos, changed variable naming to follow camel case convention, simplified the error handling, and refactored the function names in sspanel.go for improved readability and consistency. Accordingly, updated the test function names in sspanel_test.go to match the changes made in the main file. Additionally, the if condition in ParseSSPanelNodeInfo was streamlined to improve the function's readability and error handling. The changes will help in better understanding the code while debugging or adding new features.
2023-10-04 09:45:52 +08:00
dependabot[bot]
a0f2730bb2
build(deps): bump github.com/shirou/gopsutil/v3 from 3.23.8 to 3.23.9 (#462)
Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.23.8 to 3.23.9.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/compare/v3.23.8...v3.23.9)

---
updated-dependencies:
- dependency-name: github.com/shirou/gopsutil/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-03 08:28:04 +08:00
dependabot[bot]
0c10c59877
build(deps): bump github.com/sagernet/sing from 0.2.11 to 0.2.12 (#461)
Bumps [github.com/sagernet/sing](https://github.com/sagernet/sing) from 0.2.11 to 0.2.12.
- [Commits](https://github.com/sagernet/sing/compare/v0.2.11...v0.2.12)

---
updated-dependencies:
- dependency-name: github.com/sagernet/sing
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-03 08:27:55 +08:00
dependabot[bot]
119e4810f2
build(deps): bump github.com/go-resty/resty/v2 from 2.8.0 to 2.9.1 (#463)
Bumps [github.com/go-resty/resty/v2](https://github.com/go-resty/resty) from 2.8.0 to 2.9.1.
- [Release notes](https://github.com/go-resty/resty/releases)
- [Commits](https://github.com/go-resty/resty/compare/v2.8.0...v2.9.1)

---
updated-dependencies:
- dependency-name: github.com/go-resty/resty/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-03 08:27:45 +08:00
dependabot[bot]
5dad910488
build(deps): bump github.com/redis/go-redis/v9 from 9.2.0 to 9.2.1 (#460)
Bumps [github.com/redis/go-redis/v9](https://github.com/redis/go-redis) from 9.2.0 to 9.2.1.
- [Release notes](https://github.com/redis/go-redis/releases)
- [Changelog](https://github.com/redis/go-redis/blob/master/CHANGELOG.md)
- [Commits](https://github.com/redis/go-redis/compare/v9.2.0...v9.2.1)

---
updated-dependencies:
- dependency-name: github.com/redis/go-redis/v9
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-02 23:29:38 +08:00
dependabot[bot]
8841e55f70
build(deps): bump github.com/sagernet/sing-shadowsocks (#458)
Bumps [github.com/sagernet/sing-shadowsocks](https://github.com/sagernet/sing-shadowsocks) from 0.2.4 to 0.2.5.
- [Commits](https://github.com/sagernet/sing-shadowsocks/compare/v0.2.4...v0.2.5)

---
updated-dependencies:
- dependency-name: github.com/sagernet/sing-shadowsocks
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-27 17:36:02 +08:00
dependabot[bot]
5d20732881
build(deps): bump github.com/sagernet/sing from 0.2.9 to 0.2.11 (#455)
Bumps [github.com/sagernet/sing](https://github.com/sagernet/sing) from 0.2.9 to 0.2.11.
- [Commits](https://github.com/sagernet/sing/compare/v0.2.9...v0.2.11)

---
updated-dependencies:
- dependency-name: github.com/sagernet/sing
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-21 23:30:29 +08:00
dependabot[bot]
d579933451
build(deps): bump github.com/go-acme/lego/v4 from 4.14.0 to 4.14.2 (#456)
Bumps [github.com/go-acme/lego/v4](https://github.com/go-acme/lego) from 4.14.0 to 4.14.2.
- [Release notes](https://github.com/go-acme/lego/releases)
- [Changelog](https://github.com/go-acme/lego/blob/master/CHANGELOG.md)
- [Commits](https://github.com/go-acme/lego/compare/v4.14.0...v4.14.2)

---
updated-dependencies:
- dependency-name: github.com/go-acme/lego/v4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-21 08:20:31 +08:00
dependabot[bot]
46e836f93b
build(deps): bump github.com/redis/go-redis/v9 from 9.1.0 to 9.2.0 (#457)
Bumps [github.com/redis/go-redis/v9](https://github.com/redis/go-redis) from 9.1.0 to 9.2.0.
- [Release notes](https://github.com/redis/go-redis/releases)
- [Changelog](https://github.com/redis/go-redis/blob/master/CHANGELOG.md)
- [Commits](https://github.com/redis/go-redis/compare/v9.1.0...v9.2.0)

---
updated-dependencies:
- dependency-name: github.com/redis/go-redis/v9
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-21 08:20:21 +08:00
dependabot[bot]
e27b0c6cd8
build(deps): bump github.com/go-resty/resty/v2 from 2.7.0 to 2.8.0 (#453)
Bumps [github.com/go-resty/resty/v2](https://github.com/go-resty/resty) from 2.7.0 to 2.8.0.
- [Release notes](https://github.com/go-resty/resty/releases)
- [Commits](https://github.com/go-resty/resty/compare/v2.7.0...v2.8.0)

---
updated-dependencies:
- dependency-name: github.com/go-resty/resty/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-19 01:53:53 +08:00
Senis John
e34a3b4a94
Update dependencies and replace go-redis with redis library
Replaced go-redis older version with a more updated version of redis library in limiter.go and go.mod files. This change will help to improve the performance and efficiency of the existing code. Updated the versions of 'gocache/store/redis/v4' and 'bsm/gomega' in go.sum and go.mod files to keep the project's dependencies up-to-date and fix potential security vulnerabilities. Removed the unnecessary indirect dependency 'go-redis/redis/v8' from go.sum file. Added 'redis/go-redis/v9' instead, to ensure compatibility with the newer version of Redis.
2023-09-12 17:28:27 +08:00
Senis John
b366171401
Update golang.org/x/exp and golang.org/x/tools versions
Updated versions for golang.org/x/exp and golang.org/x/tools in go.sum and go.mod files. It is crucial to keep these dependencies up-to-date for maintaining the functionality of the project as well as addressing potential security vulnerabilities.
2023-09-12 07:06:49 +08:00
dependabot[bot]
b034a2d48f
build(deps): bump github.com/eko/gocache/store/go_cache/v4 (#449)
Bumps [github.com/eko/gocache/store/go_cache/v4](https://github.com/eko/gocache) from 4.2.0 to 4.2.1.
- [Release notes](https://github.com/eko/gocache/releases)
- [Commits](https://github.com/eko/gocache/compare/store/redis/v4.2.0...store/redis/v4.2.1)

---
updated-dependencies:
- dependency-name: github.com/eko/gocache/store/go_cache/v4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-12 06:22:38 +08:00
dependabot[bot]
65b25ed3f6
build(deps): bump github.com/eko/gocache/lib/v4 from 4.1.4 to 4.1.5 (#448)
Bumps [github.com/eko/gocache/lib/v4](https://github.com/eko/gocache) from 4.1.4 to 4.1.5.
- [Release notes](https://github.com/eko/gocache/releases)
- [Commits](https://github.com/eko/gocache/compare/lib/v4.1.4...lib/v4.1.5)

---
updated-dependencies:
- dependency-name: github.com/eko/gocache/lib/v4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-12 05:54:06 +08:00
dependabot[bot]
b65dfd7f92
build(deps): bump golang.org/x/net from 0.14.0 to 0.15.0 (#446)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.14.0 to 0.15.0.
- [Commits](https://github.com/golang/net/compare/v0.14.0...v0.15.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-07 05:00:29 +08:00
Senis John
6569a0bf36
Updated several Go dependencies in go.sum
Upgraded the version of multiple dependencies listed in go.sum to incorporate bug fixes, improvements and deal with any potential security vulnerabilities. Key changes include an update to packages such as github.com/BurntSushi/toml and github.com/aws/aws-sdk-go-v2 among others.
2023-09-04 09:59:14 +08:00
dependabot[bot]
e5e5e4ef92
build(deps): bump github.com/shirou/gopsutil/v3 from 3.23.7 to 3.23.8 (#442)
Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.23.7 to 3.23.8.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/compare/v3.23.7...v3.23.8)

---
updated-dependencies:
- dependency-name: github.com/shirou/gopsutil/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-04 09:39:47 +08:00
Senis John
f35a056cc6
Update several dependencies for bug fixes and improvements
This commit includes updates to several dependencies in go.sum file in order to incorporate bug fixes and performance improvements. The updated dependencies include 'cloud.google.com/go/compute', 'github.com/gaukas/godicttls', 'github.com/google/pprof' and 'github.com/xtls/xray-core' among others.

These updates are crucial to ensure the project runs efficiently and securely, with all known issues in these dependencies addressed in their latest versions.
2023-09-04 08:53:08 +08:00
dependabot[bot]
73ffd3f505
build(deps): bump github.com/eko/gocache/lib/v4 from 4.1.3 to 4.1.4 (#433)
Bumps [github.com/eko/gocache/lib/v4](https://github.com/eko/gocache) from 4.1.3 to 4.1.4.
- [Release notes](https://github.com/eko/gocache/releases)
- [Commits](https://github.com/eko/gocache/compare/lib/v4.1.3...lib/v4.1.4)

---
updated-dependencies:
- dependency-name: github.com/eko/gocache/lib/v4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-08 23:37:35 +08:00
dependabot[bot]
73c5e28f41
build(deps): bump github.com/eko/gocache/store/go_cache/v4 (#432)
Bumps [github.com/eko/gocache/store/go_cache/v4](https://github.com/eko/gocache) from 4.1.2 to 4.2.0.
- [Release notes](https://github.com/eko/gocache/releases)
- [Commits](https://github.com/eko/gocache/compare/lib/v4.1.2...store/redis/v4.2.0)

---
updated-dependencies:
- dependency-name: github.com/eko/gocache/store/go_cache/v4
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-08 01:17:18 +08:00
dependabot[bot]
f1b45c02f4
build(deps): bump golang.org/x/net from 0.13.0 to 0.14.0 (#426)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.13.0 to 0.14.0.
- [Commits](https://github.com/golang/net/compare/v0.13.0...v0.14.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-05 16:17:56 +08:00
dependabot[bot]
941f256ba5
build(deps): bump golang.org/x/net from 0.12.0 to 0.13.0 (#423)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.12.0 to 0.13.0.
- [Commits](https://github.com/golang/net/compare/v0.12.0...v0.13.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-05 14:00:23 +08:00
dependabot[bot]
2e547afdb7
build(deps): bump github.com/shirou/gopsutil/v3 from 3.23.6 to 3.23.7 (#421)
Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.23.6 to 3.23.7.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/compare/v3.23.6...v3.23.7)

---
updated-dependencies:
- dependency-name: github.com/shirou/gopsutil/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-05 14:00:13 +08:00
dependabot[bot]
422ed1a311
build(deps): bump github.com/go-acme/lego/v4 from 4.13.2 to 4.13.3 (#418)
Bumps [github.com/go-acme/lego/v4](https://github.com/go-acme/lego) from 4.13.2 to 4.13.3.
- [Release notes](https://github.com/go-acme/lego/releases)
- [Changelog](https://github.com/go-acme/lego/blob/master/CHANGELOG.md)
- [Commits](https://github.com/go-acme/lego/compare/v4.13.2...v4.13.3)

---
updated-dependencies:
- dependency-name: github.com/go-acme/lego/v4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-27 08:08:09 +08:00
dependabot[bot]
214e412993
build(deps): bump github.com/sagernet/sing-shadowsocks (#415)
Bumps [github.com/sagernet/sing-shadowsocks](https://github.com/sagernet/sing-shadowsocks) from 0.2.2 to 0.2.4.
- [Commits](https://github.com/sagernet/sing-shadowsocks/compare/v0.2.2...v0.2.4)

---
updated-dependencies:
- dependency-name: github.com/sagernet/sing-shadowsocks
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-24 22:32:14 +08:00
dependabot[bot]
f85f6b47ee
build(deps): bump github.com/go-acme/lego/v4 from 4.13.1 to 4.13.2 (#414)
Bumps [github.com/go-acme/lego/v4](https://github.com/go-acme/lego) from 4.13.1 to 4.13.2.
- [Release notes](https://github.com/go-acme/lego/releases)
- [Changelog](https://github.com/go-acme/lego/blob/master/CHANGELOG.md)
- [Commits](https://github.com/go-acme/lego/compare/v4.13.1...v4.13.2)

---
updated-dependencies:
- dependency-name: github.com/go-acme/lego/v4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-24 22:32:05 +08:00
dependabot[bot]
7e09aef1cc
build(deps): bump github.com/go-acme/lego/v4 from 4.12.3 to 4.13.1 (#411)
Bumps [github.com/go-acme/lego/v4](https://github.com/go-acme/lego) from 4.12.3 to 4.13.1.
- [Release notes](https://github.com/go-acme/lego/releases)
- [Changelog](https://github.com/go-acme/lego/blob/master/CHANGELOG.md)
- [Commits](https://github.com/go-acme/lego/compare/v4.12.3...v4.13.1)

---
updated-dependencies:
- dependency-name: github.com/go-acme/lego/v4
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-23 21:07:59 +08:00
dependabot[bot]
0b4caba8f6
build(deps): bump golang.org/x/net from 0.11.0 to 0.12.0 (#403)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.11.0 to 0.12.0.
- [Commits](https://github.com/golang/net/compare/v0.11.0...v0.12.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-11 09:58:05 +08:00
dependabot[bot]
b0866011e5
build(deps): bump github.com/shirou/gopsutil/v3 from 3.23.5 to 3.23.6 (#401)
Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.23.5 to 3.23.6.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/compare/v3.23.5...v3.23.6)

---
updated-dependencies:
- dependency-name: github.com/shirou/gopsutil/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-06 07:53:23 +08:00
dependabot[bot]
4d1d89b837
build(deps): bump github.com/sagernet/sing from 0.2.6 to 0.2.7 (#398)
Bumps [github.com/sagernet/sing](https://github.com/sagernet/sing) from 0.2.6 to 0.2.7.
- [Commits](https://github.com/sagernet/sing/compare/v0.2.6...v0.2.7)

---
updated-dependencies:
- dependency-name: github.com/sagernet/sing
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-27 06:17:09 +08:00
dependabot[bot]
0febf96021
build(deps): bump google.golang.org/protobuf from 1.30.0 to 1.31.0 (#397)
Bumps google.golang.org/protobuf from 1.30.0 to 1.31.0.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-27 06:16:47 +08:00
Senis John
0f10e837e4
fix: typo 2023-06-25 12:05:14 +08:00
Senis John
5ab352f9c9
update: remove old v2board api
update: update xray-core to 1.8.3
alterID is not use anymore
2023-06-25 07:21:27 +08:00
OPPO9008
5d5470a919
修复http伪装配置解析 (#389)
根据sspanel代码,headerType 才是下发订阅的参数
2023-06-21 07:14:20 +08:00
dependabot[bot]
db27722bbc
build(deps): bump github.com/go-acme/lego/v4 from 4.12.2 to 4.12.3 (#395)
Bumps [github.com/go-acme/lego/v4](https://github.com/go-acme/lego) from 4.12.2 to 4.12.3.
- [Release notes](https://github.com/go-acme/lego/releases)
- [Changelog](https://github.com/go-acme/lego/blob/master/CHANGELOG.md)
- [Commits](https://github.com/go-acme/lego/compare/v4.12.2...v4.12.3)

---
updated-dependencies:
- dependency-name: github.com/go-acme/lego/v4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-21 07:13:58 +08:00
dependabot[bot]
914510c687
build(deps): bump github.com/sagernet/sing from 0.2.3 to 0.2.6 (#394)
Bumps [github.com/sagernet/sing](https://github.com/sagernet/sing) from 0.2.3 to 0.2.6.
- [Commits](https://github.com/sagernet/sing/compare/v0.2.3...v0.2.6)

---
updated-dependencies:
- dependency-name: github.com/sagernet/sing
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-21 07:13:41 +08:00
dependabot[bot]
a8226b01e2
build(deps): bump github.com/go-acme/lego/v4 from 4.12.1 to 4.12.2 (#393)
Bumps [github.com/go-acme/lego/v4](https://github.com/go-acme/lego) from 4.12.1 to 4.12.2.
- [Release notes](https://github.com/go-acme/lego/releases)
- [Changelog](https://github.com/go-acme/lego/blob/master/CHANGELOG.md)
- [Commits](https://github.com/go-acme/lego/compare/v4.12.1...v4.12.2)

---
updated-dependencies:
- dependency-name: github.com/go-acme/lego/v4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-20 08:08:50 +08:00
dependabot[bot]
54a958f39c
build(deps): bump github.com/sagernet/sing-shadowsocks (#392)
Bumps [github.com/sagernet/sing-shadowsocks](https://github.com/sagernet/sing-shadowsocks) from 0.2.1 to 0.2.2.
- [Commits](https://github.com/sagernet/sing-shadowsocks/compare/v0.2.1...v0.2.2)

---
updated-dependencies:
- dependency-name: github.com/sagernet/sing-shadowsocks
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-20 08:08:37 +08:00
dependabot[bot]
a1e407e18f
build(deps): bump golang.org/x/net from 0.10.0 to 0.11.0 (#387)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.10.0 to 0.11.0.
- [Commits](https://github.com/golang/net/compare/v0.10.0...v0.11.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-14 05:38:39 +08:00
Senis John
42da6c155d
fix: unexceptional etags handle 2023-06-11 20:07:43 +08:00
dependabot[bot]
89f2342a42
build(deps): bump github.com/go-acme/lego/v4 from 4.12.0 to 4.12.1 (#380)
Bumps [github.com/go-acme/lego/v4](https://github.com/go-acme/lego) from 4.12.0 to 4.12.1.
- [Release notes](https://github.com/go-acme/lego/releases)
- [Changelog](https://github.com/go-acme/lego/blob/master/CHANGELOG.md)
- [Commits](https://github.com/go-acme/lego/compare/v4.12.0...v4.12.1)

---
updated-dependencies:
- dependency-name: github.com/go-acme/lego/v4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-08 00:06:31 +08:00
dependabot[bot]
ee1a606888
build(deps): bump github.com/bitly/go-simplejson from 0.5.0 to 0.5.1 (#379)
Bumps [github.com/bitly/go-simplejson](https://github.com/bitly/go-simplejson) from 0.5.0 to 0.5.1.
- [Release notes](https://github.com/bitly/go-simplejson/releases)
- [Commits](https://github.com/bitly/go-simplejson/compare/v0.5.0...v0.5.1)

---
updated-dependencies:
- dependency-name: github.com/bitly/go-simplejson
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-07 09:59:16 +08:00
dependabot[bot]
c183c6492e
build(deps): bump github.com/shirou/gopsutil/v3 from 3.23.4 to 3.23.5 (#373)
Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.23.4 to 3.23.5.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/compare/v3.23.4...v3.23.5)

---
updated-dependencies:
- dependency-name: github.com/shirou/gopsutil/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-02 18:33:12 +08:00
OPPO9008
a70a0d9a31
初步适配sspanel ETag 功能 (#309)
在最新sspanel 版本 M1Screw 应该找到正确下发Etag的办法了,如果你是旧版本,可以尝试
修改
res.Header().Get("ETag")
为
res.Header().Get("webapi-etag")

Co-authored-by: admin <admin@justcn2.top>
2023-06-01 18:09:08 +08:00
dependabot[bot]
4c651e15fa
build(deps): bump github.com/stretchr/testify from 1.8.3 to 1.8.4 (#369)
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.3 to 1.8.4.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.8.3...v1.8.4)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-01 18:07:41 +08:00
dependabot[bot]
dda00c5dd6
build(deps): bump github.com/spf13/viper from 1.15.0 to 1.16.0 (#368)
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.15.0 to 1.16.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.15.0...v1.16.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/viper
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-31 00:27:41 +08:00
dependabot[bot]
28e1b82320
build(deps): bump github.com/imdario/mergo from 0.3.13 to 0.3.16 (#365)
Bumps [github.com/imdario/mergo](https://github.com/imdario/mergo) from 0.3.13 to 0.3.16.
- [Release notes](https://github.com/imdario/mergo/releases)
- [Commits](https://github.com/imdario/mergo/compare/v0.3.13...v0.3.16)

---
updated-dependencies:
- dependency-name: github.com/imdario/mergo
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-30 00:47:59 +08:00
dependabot[bot]
f1ab2eac13
build(deps): bump github.com/go-acme/lego/v4 from 4.10.2 to 4.12.0 (#364)
Bumps [github.com/go-acme/lego/v4](https://github.com/go-acme/lego) from 4.10.2 to 4.12.0.
- [Release notes](https://github.com/go-acme/lego/releases)
- [Changelog](https://github.com/go-acme/lego/blob/master/CHANGELOG.md)
- [Commits](https://github.com/go-acme/lego/compare/v4.10.2...v4.12.0)

---
updated-dependencies:
- dependency-name: github.com/go-acme/lego/v4
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-30 00:47:50 +08:00
Senis John
4f13aac094
fix: sspanel custom flow config doesn't work 2023-05-25 21:08:56 +08:00
Senis John
4ab196ad29
fix: local vless flow config doesn't work 2023-05-25 20:39:46 +08:00
Senis John
9590697c29
update: switch vless flow on local config 2023-05-25 10:12:16 +08:00
Senis John
0bec6c4fdf
fix: vless and trojan flow mode 2023-05-24 20:44:26 +08:00
dependabot[bot]
223589ba14
build(deps): bump github.com/stretchr/testify from 1.8.2 to 1.8.3 (#355)
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.2 to 1.8.3.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.8.2...v1.8.3)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-20 00:03:43 +08:00
dependabot[bot]
6ba7fe2776
build(deps): bump golang.org/x/net from 0.9.0 to 0.10.0 (#346)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.9.0 to 0.10.0.
- [Commits](https://github.com/golang/net/compare/v0.9.0...v0.10.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-20 00:03:16 +08:00
Senis John
b49798ab16
fix: sspanel version >= 2023.2 will discard node status report.
fix: return error message for null user list
2023-05-18 01:23:46 +08:00
dependabot[bot]
af9224f5bb
build(deps): bump github.com/shirou/gopsutil/v3 from 3.23.2 to 3.23.4 (#337)
Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.23.2 to 3.23.4.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/compare/v3.23.2...v3.23.4)

---
updated-dependencies:
- dependency-name: github.com/shirou/gopsutil/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-05 05:27:31 +08:00
Senis John
c54650f195
update: add readme for REALITY proto params 2023-04-22 17:49:07 +08:00
Senis John
c73af2309b
fix: When server port is 0, the program will not exit normally 2023-04-22 17:05:10 +08:00
thank243
169f742b76
Update core to 1.8.1 and support Reality proto (#326)
* update: Support reality proto

* fix: typo
2023-04-22 16:41:22 +08:00
pocketW
0fa8a45e51
Merge pull request #287 from XrayR-project/dependabot/go_modules/google.golang.org/protobuf-1.29.1
build(deps): bump google.golang.org/protobuf from 1.29.0 to 1.29.1
2023-03-15 10:34:09 +11:00
dependabot[bot]
17c538a5b7
build(deps): bump google.golang.org/protobuf from 1.29.0 to 1.29.1
Bumps [google.golang.org/protobuf](https://github.com/protocolbuffers/protobuf-go) from 1.29.0 to 1.29.1.
- [Release notes](https://github.com/protocolbuffers/protobuf-go/releases)
- [Changelog](https://github.com/protocolbuffers/protobuf-go/blob/master/release.bash)
- [Commits](https://github.com/protocolbuffers/protobuf-go/compare/v1.29.0...v1.29.1)

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-14 23:08:17 +00:00
pocketW
5625d570fd
Merge pull request #284 from XrayR-project/dependabot/go_modules/github.com/xtls/xray-core-1.8.0
build(deps): bump github.com/xtls/xray-core from 1.7.5 to 1.8.0
2023-03-11 18:27:02 +08:00
pocketW
9b0e55f037
Merge pull request #283 from XrayR-project/dependabot/go_modules/github.com/sagernet/sing-0.1.8
build(deps): bump github.com/sagernet/sing from 0.1.7 to 0.1.8
2023-03-11 18:26:50 +08:00
dependabot[bot]
4597d6ac56
build(deps): bump github.com/xtls/xray-core from 1.7.5 to 1.8.0
Bumps [github.com/xtls/xray-core](https://github.com/xtls/xray-core) from 1.7.5 to 1.8.0.
- [Release notes](https://github.com/xtls/xray-core/releases)
- [Commits](https://github.com/xtls/xray-core/compare/v1.7.5...v1.8.0)

---
updated-dependencies:
- dependency-name: github.com/xtls/xray-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-09 15:15:48 +00:00
dependabot[bot]
9aaad5e8ad
build(deps): bump github.com/sagernet/sing from 0.1.7 to 0.1.8
Bumps [github.com/sagernet/sing](https://github.com/sagernet/sing) from 0.1.7 to 0.1.8.
- [Release notes](https://github.com/sagernet/sing/releases)
- [Commits](https://github.com/sagernet/sing/compare/v0.1.7...v0.1.8)

---
updated-dependencies:
- dependency-name: github.com/sagernet/sing
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-09 15:15:03 +00:00
dependabot[bot]
94d82f33ea
build(deps): bump google.golang.org/protobuf from 1.28.1 to 1.29.0 (#282)
Bumps [google.golang.org/protobuf](https://github.com/protocolbuffers/protobuf-go) from 1.28.1 to 1.29.0.
- [Release notes](https://github.com/protocolbuffers/protobuf-go/releases)
- [Changelog](https://github.com/protocolbuffers/protobuf-go/blob/master/release.bash)
- [Commits](https://github.com/protocolbuffers/protobuf-go/compare/v1.28.1...v1.29.0)

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-09 02:08:25 +08:00
pocketW
49aa520d67
Merge pull request #280 from XrayR-project/dependabot/go_modules/golang.org/x/crypto-0.7.0
build(deps): bump golang.org/x/crypto from 0.6.0 to 0.7.0
2023-03-08 20:03:21 +08:00
dependabot[bot]
73c3047651
build(deps): bump golang.org/x/crypto from 0.6.0 to 0.7.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.6.0 to 0.7.0.
- [Release notes](https://github.com/golang/crypto/releases)
- [Commits](https://github.com/golang/crypto/compare/v0.6.0...v0.7.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-06 16:12:02 +00:00
dependabot[bot]
613da96543
build(deps): bump github.com/shirou/gopsutil/v3 from 3.23.1 to 3.23.2 (#278)
Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.23.1 to 3.23.2.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/compare/v3.23.1...v3.23.2)

---
updated-dependencies:
- dependency-name: github.com/shirou/gopsutil/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-02 00:31:15 +08:00
dependabot[bot]
76127f4757
build(deps): bump github.com/stretchr/testify from 1.8.1 to 1.8.2 (#274)
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.1 to 1.8.2.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.8.1...v1.8.2)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-28 19:09:42 +08:00
dependabot[bot]
1d5a34cf98
build(deps): bump github.com/go-acme/lego/v4 from 4.10.0 to 4.10.2 (#273)
Bumps [github.com/go-acme/lego/v4](https://github.com/go-acme/lego) from 4.10.0 to 4.10.2.
- [Release notes](https://github.com/go-acme/lego/releases)
- [Changelog](https://github.com/go-acme/lego/blob/master/CHANGELOG.md)
- [Commits](https://github.com/go-acme/lego/compare/v4.10.0...v4.10.2)

---
updated-dependencies:
- dependency-name: github.com/go-acme/lego/v4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-28 19:09:29 +08:00
dependabot[bot]
bf13971502
build(deps): bump github.com/eko/gocache/lib/v4 from 4.1.2 to 4.1.3 (#272)
Bumps [github.com/eko/gocache/lib/v4](https://github.com/eko/gocache) from 4.1.2 to 4.1.3.
- [Release notes](https://github.com/eko/gocache/releases)
- [Commits](https://github.com/eko/gocache/compare/lib/v4.1.2...lib/v4.1.3)

---
updated-dependencies:
- dependency-name: github.com/eko/gocache/lib/v4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-28 19:09:20 +08:00
pocketW
572be38ae4
Merge pull request #267 from XrayR-project/dependabot/go_modules/github.com/sagernet/sing-shadowsocks-0.1.1
build(deps): bump github.com/sagernet/sing-shadowsocks from 0.1.1-0.20230202035033-e3123545f2f7 to 0.1.1
2023-02-24 23:45:09 +11:00
pocketW
34d96c9338
Merge pull request #266 from XrayR-project/dependabot/go_modules/github.com/go-acme/lego/v4-4.10.0
build(deps): bump github.com/go-acme/lego/v4 from 4.9.2-0.20221210101705-6695fcc87344 to 4.10.0
2023-02-24 23:44:55 +11:00
dependabot[bot]
4e69c0656e
build(deps): bump github.com/sagernet/sing-shadowsocks
Bumps [github.com/sagernet/sing-shadowsocks](https://github.com/sagernet/sing-shadowsocks) from 0.1.1-0.20230202035033-e3123545f2f7 to 0.1.1.
- [Release notes](https://github.com/sagernet/sing-shadowsocks/releases)
- [Commits](https://github.com/sagernet/sing-shadowsocks/commits/v0.1.1)

---
updated-dependencies:
- dependency-name: github.com/sagernet/sing-shadowsocks
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-23 15:21:36 +00:00
dependabot[bot]
88f7709fad
build(deps): bump github.com/go-acme/lego/v4
Bumps [github.com/go-acme/lego/v4](https://github.com/go-acme/lego) from 4.9.2-0.20221210101705-6695fcc87344 to 4.10.0.
- [Release notes](https://github.com/go-acme/lego/releases)
- [Commits](https://github.com/go-acme/lego/commits/v4.10.0)

---
updated-dependencies:
- dependency-name: github.com/go-acme/lego/v4
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-23 15:21:30 +00:00
66 changed files with 4258 additions and 1878 deletions

View File

@ -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

View File

@ -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 }}

View File

@ -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
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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) | √ | √ | √ |
## 软件安装 ## 软件安装

View File

@ -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) | √ | √ | √ |
## نصب نرم افزار ## نصب نرم افزار

View File

@ -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
View 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
}

View File

@ -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
View 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
View 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
}

View 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
View 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"`
}

View File

@ -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"`
} }

View File

@ -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
} }

View File

@ -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,
} }
} }

View File

@ -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,
} }
} }

View File

@ -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)

View File

@ -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"`
}

View File

@ -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 doesnt 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
} }

View File

@ -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)

View File

@ -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"`
}

View File

@ -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
}

View File

@ -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"`
}

View File

@ -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
} }

View File

@ -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

View File

@ -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,

View File

@ -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},

View File

@ -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"

View File

@ -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
View 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
View 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
}

View File

@ -1,7 +0,0 @@
package limiter
import "github.com/xtls/xray-core/common/errors"
func newError(values ...interface{}) *errors.Error {
return errors.New(values...)
}

View File

@ -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")
} }
} }

View File

@ -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"`

View File

@ -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"

View File

@ -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"

View File

@ -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)
} }

View File

@ -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"

View File

@ -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 !!!!

View File

@ -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"

View File

@ -1,7 +0,0 @@
package rule
import "github.com/xtls/xray-core/common/errors"
func newError(values ...interface{}) *errors.Error {
return errors.New(values...)
}

View File

@ -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
View File

@ -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

2074
go.sum

File diff suppressed because it is too large Load Diff

13
main.go Normal file
View 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)
}
}

View File

@ -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

View File

@ -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

View 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

View File

@ -1,6 +1,6 @@
[ [
{ {
"listen": "0.0.0.0", "listen": "127.0.0.1",
"port": 1234, "port": 1234,
"protocol": "socks", "protocol": "socks",
"settings": { "settings": {

View File

@ -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"`
}

View File

@ -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)
} }
} }
} }

View File

@ -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"
) )

View File

@ -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,

View File

@ -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"

View File

@ -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()

View File

@ -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 {