357 Commits

Author SHA1 Message Date
dependabot[bot]
a644b43e8b build(deps): bump github.com/quic-go/quic-go from 0.45.1 to 0.48.2
Bumps [github.com/quic-go/quic-go](https://github.com/quic-go/quic-go) from 0.45.1 to 0.48.2.
- [Release notes](https://github.com/quic-go/quic-go/releases)
- [Changelog](https://github.com/quic-go/quic-go/blob/master/Changelog.md)
- [Commits](https://github.com/quic-go/quic-go/compare/v0.45.1...v0.48.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-02 17:54:25 +00: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
pocketW
f95825395d update to v0.9.0 2023-02-22 22:38:53 +11:00
dependabot[bot]
e279d39d5a build(deps): bump github.com/sagernet/sing from 0.1.6 to 0.1.7 (#263)
Bumps [github.com/sagernet/sing](https://github.com/sagernet/sing) from 0.1.6 to 0.1.7.
- [Release notes](https://github.com/sagernet/sing/releases)
- [Commits](https://github.com/sagernet/sing/compare/v0.1.6...v0.1.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-02-21 22:48:01 +08:00
dependabot[bot]
74038add93 build(deps): bump golang.org/x/net from 0.6.0 to 0.7.0 (#259)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.6.0 to 0.7.0.
- [Release notes](https://github.com/golang/net/releases)
- [Commits](https://github.com/golang/net/compare/v0.6.0...v0.7.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-02-16 07:34:28 +08:00
dependabot[bot]
0a2b8612dc build(deps): bump golang.org/x/crypto from 0.5.0 to 0.6.0 (#253)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.5.0 to 0.6.0.
- [Release notes](https://github.com/golang/crypto/releases)
- [Commits](https://github.com/golang/crypto/compare/v0.5.0...v0.6.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-02-10 15:55:37 +08:00
dependabot[bot]
5923018a9d build(deps): bump github.com/xtls/xray-core from 1.7.3 to 1.7.5 (#252)
Bumps [github.com/xtls/xray-core](https://github.com/xtls/xray-core) from 1.7.3 to 1.7.5.
- [Release notes](https://github.com/xtls/xray-core/releases)
- [Commits](https://github.com/xtls/xray-core/compare/v1.7.3...v1.7.5)

---
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-02-08 23:49:27 +08:00
pocketW
239a0eea78 fix: compatiable VLESS+TCP+TLS #245 2023-02-03 13:30:38 +11:00
pocketW
456951155d chore: support xtls-rprx-vision for VLESS 2023-02-02 22:05:33 +11:00
pocketW
a7a3d0220d update to v0.8.9 2023-02-02 22:03:09 +11:00
pocketW
b857bfac3e fix docker build 2023-01-21 15:30:25 +11:00
pocketW
f4ea59493f fix docker build 2023-01-21 14:08:46 +11:00
dependabot[bot]
957b439ced build(deps): bump github.com/spf13/viper from 1.14.0 to 1.15.0 (#228)
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.14.0 to 1.15.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.14.0...v1.15.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>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-20 23:31:28 +08:00
pocketW
19583503cd chore: keep dispatcher updated to Xray-core 2023-01-20 23:07:36 +11:00
pocketW
d8a3242e22 Merge pull request #227 from Github-Aiko/master
Translate Readme for English(en) users
2023-01-20 21:10:17 +11:00
Aiko
007d9a0717 Merge branch 'master' into master 2023-01-20 16:47:13 +07:00
pocketW
016051892b Update README.md 2023-01-20 20:17:15 +11:00
Aiko
5764c3e610 Update Readme 2023-01-20 16:02:02 +07:00
Aiko
5bacee35aa Add En 2023-01-20 16:00:50 +07:00
Aiko
e6b6abee24 Create README-en.md 2023-01-20 16:00:15 +07:00
pocketW
edddd2f965 Update README.md 2023-01-20 19:33:24 +11:00
pocketW
0cd17f2b55 Merge pull request #226 from Github-Aiko/patch-2
Translate Readme for Vietnamese(vi) users
2023-01-20 19:28:58 +11:00
pocketW
3838b2d333 Merge pull request #224 from DanielBlackBeard/master
translate Readme for Iranian(farsi) users
2023-01-20 19:26:42 +11:00
Aiko
7a90f157b8 Translate Readme for Vietnam(vi) users 2023-01-19 23:46:06 +07:00
Persian Prince
a10efcaea0 README_Fa.md
*Add Persian Translation of Readme
2023-01-19 20:12:43 +03:30
dependabot[bot]
1f85ce3762 build(deps): bump github.com/sagernet/sing from 0.1.5 to 0.1.6 (#216)
Bumps [github.com/sagernet/sing](https://github.com/sagernet/sing) from 0.1.5 to 0.1.6.
- [Release notes](https://github.com/sagernet/sing/releases)
- [Commits](https://github.com/sagernet/sing/compare/v0.1.5...v0.1.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>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-17 01:34:39 +08:00
dependabot[bot]
eed0b8ae32 build(deps): bump github.com/sagernet/sing from 0.1.3 to 0.1.5 (#209)
Bumps [github.com/sagernet/sing](https://github.com/sagernet/sing) from 0.1.3 to 0.1.5.
- [Release notes](https://github.com/sagernet/sing/releases)
- [Commits](https://github.com/sagernet/sing/compare/v0.1.3...v0.1.5)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-13 00:36:02 +08:00
Senis John
ea2f7a64e1 fix: newV2board cannot parse tcp http header
fix: typo
chore: update deps
2023-01-11 13:17:53 +08:00
dependabot[bot]
ad8243fcf5 build(deps): bump github.com/sagernet/sing from 0.1.2 to 0.1.3 (#206)
Bumps [github.com/sagernet/sing](https://github.com/sagernet/sing) from 0.1.2 to 0.1.3.
- [Release notes](https://github.com/sagernet/sing/releases)
- [Commits](https://github.com/sagernet/sing/compare/v0.1.2...v0.1.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-09 23:39:18 +08:00
pocketW
66fccfd422 Merge pull request #194 from XrayR-project/dependabot/go_modules/golang.org/x/crypto-0.5.0
build(deps): bump golang.org/x/crypto from 0.4.0 to 0.5.0
2023-01-06 09:11:11 +11:00
dependabot[bot]
448d44ff46 build(deps): bump golang.org/x/crypto from 0.4.0 to 0.5.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.4.0 to 0.5.0.
- [Release notes](https://github.com/golang/crypto/releases)
- [Commits](https://github.com/golang/crypto/compare/v0.4.0...v0.5.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-01-05 22:11:02 +00:00
pocketW
891068a091 Merge pull request #193 from XrayR-project/dependabot/go_modules/golang.org/x/net-0.5.0
build(deps): bump golang.org/x/net from 0.4.0 to 0.5.0
2023-01-06 09:09:04 +11:00
pocketW
127ccef619 Merge pull request #191 from XrayR-project/dependabot/go_modules/github.com/sagernet/sing-0.1.2
build(deps): bump github.com/sagernet/sing from 0.1.1 to 0.1.2
2023-01-06 09:08:51 +11:00
dependabot[bot]
84bc47ba18 build(deps): bump golang.org/x/net from 0.4.0 to 0.5.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.4.0 to 0.5.0.
- [Release notes](https://github.com/golang/net/releases)
- [Commits](https://github.com/golang/net/compare/v0.4.0...v0.5.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-01-05 15:25:54 +00:00
dependabot[bot]
a139d48959 build(deps): bump github.com/sagernet/sing from 0.1.1 to 0.1.2
Bumps [github.com/sagernet/sing](https://github.com/sagernet/sing) from 0.1.1 to 0.1.2.
- [Release notes](https://github.com/sagernet/sing/releases)
- [Commits](https://github.com/sagernet/sing/compare/v0.1.1...v0.1.2)

---
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-01-03 15:08:22 +00:00
dependabot[bot]
180a4af7ca build(deps): bump github.com/shirou/gopsutil/v3 from 3.22.11 to 3.22.12 (#190)
Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.22.11 to 3.22.12.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/commits/v3.22.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>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-03 00:59:23 +08:00
Senis John
8512c354f5 revert: dns hot load feat
the dispatcher mod cannot reload dns instance. So I revert the DNS feat and will create a new branch to test it.
2023-01-02 21:46:30 +08:00
Senis John
4746bd57d0 fix: reload dns service when need 2023-01-01 02:28:38 +08:00
Senis John
6477ef6c44 fix: dns memory leak
It can be properly working after xray-core accept the patch
2022-12-31 12:35:55 +08:00
Senis John
21e0ebc428 fix: Try to fix dns memory leak
update: release.yml
2022-12-30 22:19:48 +08:00
Senis John
40c65a86fc update: reformat newV2board serverConfig structure 2022-12-28 10:36:29 +08:00
Senis John
b5a8f4464c fix: ss2022 not working as expect
fix: minor bugs
2022-12-27 17:28:56 +08:00
Senis John
451b5a1fd4 fix: typo 2022-12-27 16:10:51 +08:00
Senis John
8ddf257aa4 update: release.yml
fix: typo
2022-12-27 15:10:17 +08:00
Senis John
a8742426b3 update: refactor newV2board parser's structure 2022-12-27 12:00:34 +08:00
Senis John
d44fa11eee update: support v2board v1.7.2 routes structure
After update this version, the v2board version must >= 1.7.2 release
2022-12-26 12:41:18 +08:00
Senis John
0c2d03f3e0 update: add error message for none user 2022-12-25 09:01:26 +08:00
Senis John
9c6ae5b7ab update: speedup docker build 2022-12-22 09:20:16 +08:00
Senis John
4c699d86f2 update: compatible with v2board v1.7.1 route rules 2022-12-21 20:09:09 +08:00
pocketW
be940fdcea add cache to speedup docker build 2022-12-20 11:39:48 +11:00
pocketW
619a455432 refactor: remove unused regexp 2022-12-20 09:19:17 +11:00
pocketW
03b7bf6ed1 fix: only encode userKey with base64 for V2board 2022-12-20 09:05:18 +11:00
dependabot[bot]
224224084e build(deps): bump github.com/sagernet/sing from 0.1.0 to 0.1.1 (#171)
Bumps [github.com/sagernet/sing](https://github.com/sagernet/sing) from 0.1.0 to 0.1.1.
- [Release notes](https://github.com/sagernet/sing/releases)
- [Commits](https://github.com/sagernet/sing/compare/v0.1.0...v0.1.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-19 23:24:31 +08:00
pocketW
ac460c2f71 Merge branch 'master' of https://github.com/XrayR-project/XrayR 2022-12-19 21:48:29 +11:00
pocketW
e1b512ef5b fix: check userKey for ss2022 2022-12-19 21:48:19 +11:00
Senis John
d5ab69d703 update: support v2board shadowsocks obfs settings
fix: v2board route block bugs
2022-12-19 01:21:27 +08:00
Senis John
d9bbb836b8 fix: minor bugs 2022-12-18 16:08:56 +08:00
Senis John
1de5143fde update: support v2board DNS rules 2022-12-18 15:31:23 +08:00
Senis John
77814acd1a fix: typo and remove redundant conversion 2022-12-18 10:27:59 +08:00
pocketW
3926463c59 feat: add shadowsocks2022 support for v2raysocks 2022-12-18 12:11:55 +11:00
pocketW
05ff6dff1b chore: fix build error 2022-12-18 11:26:38 +11:00
Senis John
1d5d4564e5 chore: update go.mod 2022-12-18 01:51:22 +08:00
Senis John
0777cc8892 update: add ss plugin support ss2022
The error message will have more details
2022-12-16 20:43:38 +08:00
Senis John
b09f371447 fix: typo 2022-12-16 14:43:56 +08:00
pocketW
fc71d76f4d update to v0.8.8 2022-12-16 09:51:34 +11:00
pocketW
443fb959f6 fix: type conversion 2022-12-15 23:00:56 +11:00
Senis John
040e19ee87 fix: Priority local speed limit 2022-12-15 19:51:43 +08:00
pocketW
addde0f8d4 feat: read speed limit from V2board
chore: update dependence
2022-12-15 22:31:35 +11:00
Senis John
9ef39925fd fix: newV2board rule regexp pattern 2022-12-15 09:55:49 +08:00
Senis John
a6dbf757e9 fix: add error to ss2022's length check 2022-12-14 10:12:19 +08:00
Senis John
acd3cce667 fix: trim length of userKey for ss2022 2022-12-13 20:43:02 +08:00
Senis John
eea6c0dea6 fix: typo 2022-12-11 06:51:25 +08:00
Senis John
889160b908 update: xray-core dev v1.6.6 2022-12-09 19:31:19 +08:00
dependabot[bot]
0bea22e145 build(deps): bump golang.org/x/crypto from 0.3.0 to 0.4.0 (#145)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.3.0 to 0.4.0.
- [Release notes](https://github.com/golang/crypto/releases)
- [Commits](https://github.com/golang/crypto/compare/v0.3.0...v0.4.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>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: thank243 <thank243@gmail.com>
2022-12-07 23:28:46 +08:00
dependabot[bot]
f25d5993f3 build(deps): bump golang.org/x/net from 0.3.0 to 0.4.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.3.0 to 0.4.0.
- [Release notes](https://github.com/golang/net/releases)
- [Commits](https://github.com/golang/net/compare/v0.3.0...v0.4.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>
2022-12-07 23:27:46 +08:00
Senis John
676365b13b update: Add support NewV2board api block rules 2022-12-07 23:26:09 +08:00
thank243
e774d5c822 Merge branch 'master' into globalLimit 2022-12-07 20:19:43 +08:00
pocketW
be4f9cdac1 reduce clear interval 2022-12-07 21:58:12 +11:00
Senis John
9a06f78653 fix: Add deviceLimit to the prefix of email string
Solve different node and different device limit the less limit node cannot work normally.
2022-12-07 18:49:08 +08:00
Senis John
85d73408c3 update: refactor global limit cache. Use gocache lib for level 2 cache
fix: shadowsocks2022 init feat
2022-12-07 13:29:05 +08:00
dependabot[bot]
c93fdb4cf3 build(deps): bump golang.org/x/net from 0.2.0 to 0.3.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.2.0 to 0.3.0.
- [Release notes](https://github.com/golang/net/releases)
- [Commits](https://github.com/golang/net/compare/v0.2.0...v0.3.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>
2022-12-06 23:25:19 +08:00
dependabot[bot]
0d565b034b build(deps): bump golang.org/x/time from 0.2.0 to 0.3.0
Bumps [golang.org/x/time](https://github.com/golang/time) from 0.2.0 to 0.3.0.
- [Release notes](https://github.com/golang/time/releases)
- [Commits](https://github.com/golang/time/compare/v0.2.0...v0.3.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>
2022-12-05 23:31:39 +08:00
Senis John
212f0ff135 fix: log prefix display not completed 2022-12-04 08:43:10 +08:00
Senis John
608764a8a0 fix: shadowsocks2022 user manager not working 2022-12-04 00:50:54 +08:00
Senis John
fc16cb0972 fix: shadowsocks2022 user manager not working 2022-12-04 00:42:35 +08:00
pocketW
5397720430 fix: expire time error 2022-12-03 19:17:33 +11:00
pocketW
839b15c22c fix: fix condition of check cert service
fix: check devices on redis first
2022-12-03 14:28:32 +11:00
Senis John
5b45b8ffe8 fix: refactor the InboundInfo struct
Global limit can separate settings for each node
2022-12-03 09:11:43 +08:00
dependabot[bot]
a56cd91b34 build(deps): bump github.com/shirou/gopsutil/v3 from 3.22.10 to 3.22.11 (#132)
Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.22.10 to 3.22.11.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-01 23:18:31 +08:00
Senis John
b6600729b2 update: code clean 2022-11-30 09:13:35 +08:00
Senis John
8d0225bcbb fix: On enable global limit, the online device report feat will failure
update: add pushIP method for limiter
2022-11-29 20:42:09 +08:00
pocketW
ce5fe799f4 Merge pull request #126 from XrayR-project/dependabot/go_modules/github.com/xtls/xray-core-1.6.5
build(deps): bump github.com/xtls/xray-core from 1.6.4 to 1.6.5
2022-11-29 09:51:07 +11:00
pocketW
8763d5960f Merge pull request #125 from XrayR-project/dependabot/go_modules/github.com/go-acme/lego/v4-4.9.1
build(deps): bump github.com/go-acme/lego/v4 from 4.9.0 to 4.9.1
2022-11-29 09:50:55 +11:00
dependabot[bot]
7a452a4a53 build(deps): bump github.com/xtls/xray-core from 1.6.4 to 1.6.5
Bumps [github.com/xtls/xray-core](https://github.com/xtls/xray-core) from 1.6.4 to 1.6.5.
- [Release notes](https://github.com/xtls/xray-core/releases)
- [Commits](https://github.com/xtls/xray-core/compare/v1.6.4...v1.6.5)

---
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>
2022-11-28 15:10:20 +00:00
dependabot[bot]
fac8b62286 build(deps): bump github.com/go-acme/lego/v4 from 4.9.0 to 4.9.1
Bumps [github.com/go-acme/lego/v4](https://github.com/go-acme/lego) from 4.9.0 to 4.9.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.9.0...v4.9.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>
2022-11-28 15:10:08 +00:00
thank243
d320aadb54 fix: global limit will failure on some situation (#124)
fix: typo
2022-11-28 11:16:27 +08:00
thank243
40ae48f507 update: refactor the global limit (#121)
fix: some log prefix

It an enhance device limit. Redis just use to sync local ipMap.
If global limit is enabled. The online report will faliure. All enabled nodes will report the device from redis.
2022-11-27 10:12:30 +08:00
pocketW
d1bc36782b add timeout 2022-11-26 21:44:14 +11:00
AikoCute
c04330d0bf Merge branch 'master' of https://github.com/Github-Aiko/XrayR-Real 2022-11-26 16:14:06 +07:00
AikoCute
872eb12d35 Check Connect Redis Server 2022-11-26 16:13:45 +07:00
pocketW
e1d4428d98 Merge pull request #118 from amin550/master
fix: ssPanel Report Node Status
2022-11-26 17:41:00 +11:00
pocketW
0fef3cf278 update config example 2022-11-26 17:30:20 +11:00
Amin
a4ca37b1de Fix sspanel ReportNodeStatus 2022-11-26 00:52:30 +03:30
Senis Y
74f3a75682 fix: delete unused struct 2022-11-25 00:25:39 +08:00
Senis Y
183b1be519 feature: add support v2board new API
update: add eTag on fetch users for save resources
update: split cert monitor service
update: refactor log format
update: add support shadowsocks_2022

The cert monitor period = UpdatePeriodic * 60

The xray-core official is not implementation of proxy.UserManager feature. So it may change in the future.
2022-11-24 15:11:01 +08:00
pocketW
2f10c3f6b8 revert error format 2022-11-20 14:57:20 +11:00
AikoCute
86324ff1ae Logs for GlobalDeviceLimit 2022-11-20 10:10:03 +07:00
pocketW
1897404c9d Merge pull request #104 from XrayR-project/dependabot/go_modules/golang.org/x/crypto-0.3.0
build(deps): bump golang.org/x/crypto from 0.2.0 to 0.3.0
2022-11-18 17:25:38 +11:00
dependabot[bot]
ee53e746c5 build(deps): bump golang.org/x/crypto from 0.2.0 to 0.3.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.2.0 to 0.3.0.
- [Release notes](https://github.com/golang/crypto/releases)
- [Commits](https://github.com/golang/crypto/compare/v0.2.0...v0.3.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>
2022-11-17 15:15:59 +00:00
pocketW
656df61c17 Merge pull request #101 from thank243/master
fix: http mode not working
fix: use "email|uid" for global limit user identify
update: tls alpn challenge for apply certificate
2022-11-17 12:42:53 +11:00
Senis
e0237f5c54 fix: http mode not working
fix: use "email|uid" for global limit user identify
update: tls alpn challenge for apply certificate
2022-11-16 12:59:44 +08:00
pocketW
e16d94fb4a Merge pull request #97 from XrayR-project/dependabot/go_modules/github.com/xtls/xray-core-1.6.4
build(deps): bump github.com/xtls/xray-core from 1.6.3 to 1.6.4
2022-11-15 11:25:59 +11:00
dependabot[bot]
e357fc438f build(deps): bump github.com/xtls/xray-core from 1.6.3 to 1.6.4
Bumps [github.com/xtls/xray-core](https://github.com/xtls/xray-core) from 1.6.3 to 1.6.4.
- [Release notes](https://github.com/xtls/xray-core/releases)
- [Commits](https://github.com/xtls/xray-core/compare/v1.6.3...v1.6.4)

---
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>
2022-11-14 15:12:52 +00:00
pocketW
cb1638ac21 update to v0.8.6 2022-11-13 13:08:12 +11:00
pocketW
de0da25c21 chore: update dependence 2022-11-12 21:22:13 +11:00
pocketW
faec840c23 Merge pull request #90 from XrayR-project/dependabot/go_modules/golang.org/x/crypto-0.2.0
build(deps): bump golang.org/x/crypto from 0.1.0 to 0.2.0
2022-11-12 02:26:08 +11:00
pocketW
a6a1baf70c Merge pull request #89 from thank243/master
fix: adding the issuers certificate to the new certificate
2022-11-12 02:25:54 +11:00
pocketW
4013f71e4c fix: update global device limit 2022-11-12 02:22:53 +11:00
dependabot[bot]
c8f0981b0e build(deps): bump golang.org/x/crypto from 0.1.0 to 0.2.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.1.0 to 0.2.0.
- [Release notes](https://github.com/golang/crypto/releases)
- [Commits](https://github.com/golang/crypto/compare/v0.1.0...v0.2.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>
2022-11-11 15:16:31 +00:00
Senis
5274edf657 fix: adding the issuers certificate to the new certificate 2022-11-11 22:42:31 +08:00
pocketW
e6232c1852 Merge pull request #88 from XrayR-project/dependabot/go_modules/golang.org/x/net-0.2.0
build(deps): bump golang.org/x/net from 0.1.0 to 0.2.0
2022-11-11 23:59:47 +11:00
dependabot[bot]
e0688fc609 build(deps): bump golang.org/x/net from 0.1.0 to 0.2.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.1.0 to 0.2.0.
- [Release notes](https://github.com/golang/net/releases)
- [Commits](https://github.com/golang/net/compare/v0.1.0...v0.2.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>
2022-11-11 12:58:43 +00:00
pocketW
ffa444f2ab Merge pull request #86 from XrayR-project/dependabot/go_modules/github.com/spf13/viper-1.14.0
build(deps): bump github.com/spf13/viper from 1.13.0 to 1.14.0
2022-11-11 23:57:06 +11:00
dependabot[bot]
81ba4ebb43 build(deps): bump github.com/spf13/viper from 1.13.0 to 1.14.0
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.13.0 to 1.14.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.13.0...v1.14.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>
2022-11-11 12:56:11 +00:00
pocketW
f50c61c782 fix: fix cert storage path 2022-11-11 23:44:43 +11:00
Senis
9c356cd28c Merge remote-tracking branch 'origin/master' 2022-11-08 17:50:53 +08:00
Senis
310353f344 fix: Redundant character escape 2022-11-08 17:50:03 +08:00
thank243
808b5ecc3c Merge branch 'master' into master 2022-11-08 14:48:44 +08:00
Senis
c4ef5bb843 update: xray-core v1.6.3 2022-11-08 14:46:06 +08:00
Senis
ddce3fa86d fix: typo 2022-11-08 11:57:06 +08:00
Senis
209f5a17d6 update: rebuild the implementation of legocmd 2022-11-08 11:13:27 +08:00
pocketW
af3fae9cdb Merge pull request #82 from thank243/master
fix: on some circumstance, periodic will run twice
2022-11-07 23:30:32 +11:00
Senis
3b96b352cb fix: on some circumstance, periodic will run twice 2022-11-07 15:55:30 +08:00
pocketW
398c3133d3 Merge pull request #79 from thank243/master
feat: add global user ip limit
2022-11-06 14:31:22 +11:00
Senis
a397af5d73 fix: init global limit 2022-11-04 14:36:34 +08:00
Senis
b47954ea64 fix: minor bugs 2022-11-04 08:58:41 +08:00
Senis
9a2188cb0c update: Global User IP limit. 2022-11-04 08:26:56 +08:00
pocketW
c7af43fc49 Merge pull request #75 from XrayR-project/dependabot/go_modules/github.com/shirou/gopsutil/v3-3.22.10
build(deps): bump github.com/shirou/gopsutil/v3 from 3.22.9 to 3.22.10
2022-11-03 22:49:11 +11:00
pocketW
87aa855154 Merge pull request #74 from XrayR-project/dependabot/go_modules/github.com/xtls/xray-core-1.6.2
build(deps): bump github.com/xtls/xray-core from 1.6.1 to 1.6.2
2022-11-03 22:48:58 +11:00
dependabot[bot]
37eff6755c build(deps): bump github.com/shirou/gopsutil/v3 from 3.22.9 to 3.22.10
Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.22.9 to 3.22.10.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/compare/v3.22.9...v3.22.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>
2022-11-01 15:53:37 +00:00
dependabot[bot]
5e346ddfee build(deps): bump github.com/xtls/xray-core from 1.6.1 to 1.6.2
Bumps [github.com/xtls/xray-core](https://github.com/xtls/xray-core) from 1.6.1 to 1.6.2.
- [Release notes](https://github.com/xtls/xray-core/releases)
- [Commits](https://github.com/xtls/xray-core/compare/v1.6.1...v1.6.2)

---
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>
2022-10-31 16:00:54 +00:00
Senis
838c667a87 fix: typo 2022-10-27 08:26:23 +08:00
pocketW
057f4156bf Merge pull request #71 from XrayR-project/dependabot/go_modules/github.com/stretchr/testify-1.8.1
build(deps): bump github.com/stretchr/testify from 1.8.0 to 1.8.1
2022-10-26 13:40:42 +11:00
pocketW
1f59a7cd7a Merge pull request #70 from XrayR-project/dependabot/go_modules/github.com/xtls/xray-core-1.6.1
build(deps): bump github.com/xtls/xray-core from 1.6.0 to 1.6.1
2022-10-26 13:40:30 +11:00
dependabot[bot]
2b5fa4feee build(deps): bump github.com/stretchr/testify from 1.8.0 to 1.8.1
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.0 to 1.8.1.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.8.0...v1.8.1)

---
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>
2022-10-24 16:02:42 +00:00
dependabot[bot]
571191a190 build(deps): bump github.com/xtls/xray-core from 1.6.0 to 1.6.1
Bumps [github.com/xtls/xray-core](https://github.com/xtls/xray-core) from 1.6.0 to 1.6.1.
- [Release notes](https://github.com/xtls/xray-core/releases)
- [Commits](https://github.com/xtls/xray-core/compare/v1.6.0...v1.6.1)

---
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>
2022-10-24 16:02:30 +00:00
pocketW
3d5891fef3 update to v0.8.5 2022-10-14 15:19:16 +11:00
pocketW
70a0099f2c Merge pull request #67 from XrayR-project/dependabot/go_modules/github.com/fsnotify/fsnotify-1.6.0
build(deps): bump github.com/fsnotify/fsnotify from 1.5.4 to 1.6.0
2022-10-14 08:44:50 +11:00
dependabot[bot]
af8f24b5b1 build(deps): bump github.com/fsnotify/fsnotify from 1.5.4 to 1.6.0
Bumps [github.com/fsnotify/fsnotify](https://github.com/fsnotify/fsnotify) from 1.5.4 to 1.6.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.5.4...v1.6.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>
2022-10-13 15:51:51 +00:00
pocketW
79528d3e17 feat: dynamic update speedlimit bucket 2022-10-13 08:37:59 +11:00
pocketW
2f0461ddda chore: update lego to v4.9.0 2022-10-05 10:25:07 +11:00
pocketW
023680fec7 Merge pull request #62 from XrayR-project/dependabot/go_modules/github.com/shirou/gopsutil/v3-3.22.9
build(deps): bump github.com/shirou/gopsutil/v3 from 3.22.8 to 3.22.9
2022-10-04 11:50:38 +11:00
dependabot[bot]
708adf1e43 build(deps): bump github.com/shirou/gopsutil/v3 from 3.22.8 to 3.22.9
Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.22.8 to 3.22.9.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/compare/v3.22.8...v3.22.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>
2022-10-03 15:40:50 +00:00
pocketW
0bbec7ebeb Merge pull request #61 from JoshuaCylinder/master
feat: add dynamic speed limit #39
添加动态限速功能
2022-10-03 13:03:58 +11:00
JoshuaCylinder
a9dfd5404f Add default value for AutoSpeedLimitConfig 2022-10-01 07:11:20 +00:00
JoshuaCylinder
39c1036c4a Add AutoSpeedLimitConfig for some special node (For example: IPLC, IEPL, GameNode) 2022-10-01 06:49:51 +00:00
pocketW
7604e33b03 chore: update go to 1.19 2022-09-22 16:12:37 +10:00
pocketW
a906006015 Merge pull request #54 from XrayR-project/dependabot/go_modules/github.com/xtls/xray-core-1.6.0
build(deps): bump github.com/xtls/xray-core from 1.5.10 to 1.6.0
2022-09-20 17:48:04 +10:00
dependabot[bot]
cb5cc17a82 build(deps): bump github.com/xtls/xray-core from 1.5.10 to 1.6.0
Bumps [github.com/xtls/xray-core](https://github.com/xtls/xray-core) from 1.5.10 to 1.6.0.
- [Release notes](https://github.com/xtls/xray-core/releases)
- [Commits](https://github.com/xtls/xray-core/compare/v1.5.10...v1.6.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>
2022-09-19 15:52:56 +00:00
pocketW
1cf8bca79e update v0.8.4 2022-09-18 14:26:48 +10:00
pocketW
cbffafbb4c fix: fix CF dns env error #51 2022-09-18 14:19:55 +10:00
pocketW
b37705b374 update v0.8.3 2022-09-11 00:05:22 +10:00
pocketW
e00d228c3d Merge branch 'master' of https://github.com/XrayR-project/XrayR 2022-09-09 22:43:06 +10:00
pocketW
21847fee7d remove dockerhub support 2022-09-09 22:43:00 +10:00
pocketW
f42e30cc61 Merge pull request #44 from XrayR-project/dependabot/go_modules/github.com/spf13/viper-1.13.0
build(deps): bump github.com/spf13/viper from 1.12.0 to 1.13.0
2022-09-07 11:03:44 +10:00
pocketW
e3c7cf2a5f Merge pull request #43 from XrayR-project/dependabot/go_modules/github.com/urfave/cli-1.22.10
build(deps): bump github.com/urfave/cli from 1.22.9 to 1.22.10
2022-09-07 11:03:31 +10:00
dependabot[bot]
82731d9d9c build(deps): bump github.com/spf13/viper from 1.12.0 to 1.13.0
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.12.0 to 1.13.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.12.0...v1.13.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>
2022-09-06 15:39:39 +00:00
dependabot[bot]
45f4d80a49 build(deps): bump github.com/urfave/cli from 1.22.9 to 1.22.10
Bumps [github.com/urfave/cli](https://github.com/urfave/cli) from 1.22.9 to 1.22.10.
- [Release notes](https://github.com/urfave/cli/releases)
- [Changelog](https://github.com/urfave/cli/blob/main/docs/CHANGELOG.md)
- [Commits](https://github.com/urfave/cli/compare/v1.22.9...v1.22.10)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-05 16:49:36 +00:00
pocketW
10691e4cf3 panic #42 2022-09-03 20:34:22 +10:00
pocketW
9dbb231a12 Merge pull request #41 from XrayR-project/dependabot/go_modules/github.com/shirou/gopsutil/v3-3.22.8
build(deps): bump github.com/shirou/gopsutil/v3 from 3.22.7 to 3.22.8
2022-09-03 20:32:45 +10:00
dependabot[bot]
8724ece88a build(deps): bump github.com/shirou/gopsutil/v3 from 3.22.7 to 3.22.8
Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.22.7 to 3.22.8.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/compare/v3.22.7...v3.22.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>
2022-09-01 15:46:31 +00:00
pocketW
f0610d09fd feat: not reset traffic when report failed 2022-08-30 00:51:21 +10:00
pocketW
8991284e19 chore: update Xray-core to 1.5.10 2022-08-28 14:15:57 +10:00
pocketW
7a12a806a8 Update README.md 2022-08-27 21:05:18 +10:00
pocketW
fda14882a4 Create stale.yml 2022-08-26 22:29:38 +10:00
pocketW
710f081025 fix: #37 2022-08-26 13:00:07 +10:00
pocketW
c280cca7b3 add github docker package 2022-08-25 20:32:07 +10:00
pocketW
fa51cb5309 fix: fix bug when parsing transport protocol 2022-08-24 09:52:45 +10:00
pocketW
529eb46ea0 chore: add hints for config file
doc: add some badges
2022-08-23 11:20:33 +10:00
pocketW
2a528070bf doc: update doc 2022-08-22 22:41:28 +10:00
107 changed files with 6720 additions and 5765 deletions

View File

@@ -1,33 +1,95 @@
{
"android-arm64": { "friendlyName": "android-arm64-v8a" },
"darwin-amd64": { "friendlyName": "macos-64" },
"darwin-arm64": { "friendlyName": "macos-arm64-v8a" },
"dragonfly-amd64": { "friendlyName": "dragonfly-64" },
"freebsd-386": { "friendlyName": "freebsd-32" },
"freebsd-amd64": { "friendlyName": "freebsd-64" },
"freebsd-arm64": { "friendlyName": "freebsd-arm64-v8a" },
"freebsd-arm7": { "friendlyName": "freebsd-arm32-v7a" },
"linux-386": { "friendlyName": "linux-32" },
"linux-amd64": { "friendlyName": "linux-64" },
"linux-arm5": { "friendlyName": "linux-arm32-v5" },
"linux-arm64": { "friendlyName": "linux-arm64-v8a" },
"linux-arm6": { "friendlyName": "linux-arm32-v6" },
"linux-arm7": { "friendlyName": "linux-arm32-v7a" },
"linux-mips64le": { "friendlyName": "linux-mips64le" },
"linux-mips64": { "friendlyName": "linux-mips64" },
"linux-mipslesoftfloat": { "friendlyName": "linux-mips32le-softfloat" },
"linux-mipsle": { "friendlyName": "linux-mips32le" },
"linux-mipssoftfloat": { "friendlyName": "linux-mips32-softfloat" },
"linux-mips": { "friendlyName": "linux-mips32" },
"linux-ppc64le": { "friendlyName": "linux-ppc64le" },
"linux-ppc64": { "friendlyName": "linux-ppc64" },
"linux-riscv64": { "friendlyName": "linux-riscv64" },
"linux-s390x": { "friendlyName": "linux-s390x" },
"openbsd-386": { "friendlyName": "openbsd-32" },
"openbsd-amd64": { "friendlyName": "openbsd-64" },
"openbsd-arm64": { "friendlyName": "openbsd-arm64-v8a" },
"openbsd-arm7": { "friendlyName": "openbsd-arm32-v7a" },
"windows-386": { "friendlyName": "windows-32" },
"windows-amd64": { "friendlyName": "windows-64" },
"windows-arm7": { "friendlyName": "windows-arm32-v7a" }
}
"android-arm64": {
"friendlyName": "android-arm64-v8a"
},
"darwin-amd64": {
"friendlyName": "macos-64"
},
"darwin-arm64": {
"friendlyName": "macos-arm64-v8a"
},
"dragonfly-amd64": {
"friendlyName": "dragonfly-64"
},
"freebsd-386": {
"friendlyName": "freebsd-32"
},
"freebsd-amd64": {
"friendlyName": "freebsd-64"
},
"freebsd-arm64": {
"friendlyName": "freebsd-arm64-v8a"
},
"freebsd-arm7": {
"friendlyName": "freebsd-arm32-v7a"
},
"linux-386": {
"friendlyName": "linux-32"
},
"linux-amd64": {
"friendlyName": "linux-64"
},
"linux-arm5": {
"friendlyName": "linux-arm32-v5"
},
"linux-arm64": {
"friendlyName": "linux-arm64-v8a"
},
"linux-arm6": {
"friendlyName": "linux-arm32-v6"
},
"linux-arm7": {
"friendlyName": "linux-arm32-v7a"
},
"linux-mips64le": {
"friendlyName": "linux-mips64le"
},
"linux-mips64": {
"friendlyName": "linux-mips64"
},
"linux-mipslesoftfloat": {
"friendlyName": "linux-mips32le-softfloat"
},
"linux-mipsle": {
"friendlyName": "linux-mips32le"
},
"linux-mipssoftfloat": {
"friendlyName": "linux-mips32-softfloat"
},
"linux-mips": {
"friendlyName": "linux-mips32"
},
"linux-ppc64le": {
"friendlyName": "linux-ppc64le"
},
"linux-ppc64": {
"friendlyName": "linux-ppc64"
},
"linux-riscv64": {
"friendlyName": "linux-riscv64"
},
"linux-s390x": {
"friendlyName": "linux-s390x"
},
"openbsd-386": {
"friendlyName": "openbsd-32"
},
"openbsd-amd64": {
"friendlyName": "openbsd-64"
},
"openbsd-arm64": {
"friendlyName": "openbsd-arm64-v8a"
},
"openbsd-arm7": {
"friendlyName": "openbsd-arm32-v7a"
},
"windows-386": {
"friendlyName": "windows-32"
},
"windows-amd64": {
"friendlyName": "windows-64"
},
"windows-arm7": {
"friendlyName": "windows-arm32-v7a"
}
}

View File

@@ -34,34 +34,38 @@ jobs:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Checkout repository
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or 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.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or 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.
# 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).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
# 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)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2

View File

@@ -1,47 +1,94 @@
name: Publish Docker image
on:
workflow_dispatch:
push:
branches:
- master
paths:
- "**/*.go"
- "go.mod"
- "go.sum"
- ".github/workflows/*.yml"
tags:
- 'v*'
pull_request:
branches:
- 'master'
release:
types:
- published
env:
REGISTRY: ghcr.io
IMAGE_NAME: xrayr-project/xrayr
jobs:
push_to_registry:
name: Push Docker image to Docker Hub
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
platform:
- linux/amd64
- linux/arm/v6
- linux/arm/v7
- linux/arm64
- linux/s390x
steps:
- name: Check out the repo
uses: actions/checkout@v2
-
name: Docker meta
- name: Checkout
uses: actions/checkout@v4
- name: Docker meta
id: meta
uses: docker/metadata-action@v4
uses: docker/metadata-action@v5
with:
images: mengxin239/xrayr
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
-
name: Login to DockerHub
uses: docker/login-action@v1
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- name: Set up QEMU
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:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
-
name: Build and push
uses: docker/build-push-action@v2
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:
context: .
platforms: linux/arm/v7,linux/arm64,linux/amd64,linux/s390x
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
platforms: ${{ matrix.platform }}
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@v3
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

@@ -11,14 +11,14 @@ on:
- "go.sum"
- ".github/workflows/*.yml"
pull_request:
types: [opened, synchronize, reopened]
types: [ opened, synchronize, reopened ]
paths:
- "**/*.go"
- "go.mod"
- "go.sum"
- ".github/workflows/*.yml"
release:
types: [published]
types: [ published ]
jobs:
@@ -26,12 +26,10 @@ jobs:
strategy:
matrix:
# Include amd64 on all platforms.
goos: [windows, freebsd, openbsd, linux, dragonfly, darwin]
goarch: [amd64, 386]
goos: [ windows, freebsd, openbsd, linux, darwin ]
goarch: [ amd64, 386 ]
exclude:
# Exclude i386 on darwin and dragonfly.
- goarch: 386
goos: dragonfly
# Exclude i386 on darwin.
- goarch: 386
goos: darwin
- goarch: 386
@@ -92,7 +90,7 @@ jobs:
# END S390X
# END Other architectures
fail-fast: false
runs-on: ubuntu-latest
env:
GOOS: ${{ matrix.goos }}
@@ -101,19 +99,19 @@ jobs:
CGO_ENABLED: 0
steps:
- name: Checkout codebase
uses: actions/checkout@v2
- name: Show workflow information
uses: actions/checkout@v3
- name: Show workflow information
id: get_filename
run: |
export _NAME=$(jq ".[\"$GOOS-$GOARCH$GOARM$GOMIPS\"].friendlyName" -r < .github/build/friendly-filenames.json)
echo "GOOS: $GOOS, GOARCH: $GOARCH, GOARM: $GOARM, GOMIPS: $GOMIPS, RELEASE_NAME: $_NAME"
echo "::set-output name=ASSET_NAME::$_NAME"
echo "ASSET_NAME=$_NAME" >> $GITHUB_OUTPUT
echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV
- name: Set up Go
uses: actions/setup-go@v2
uses: actions/setup-go@v3
with:
go-version: ^1.18
go-version: ^1.20
- name: Get project dependencies
run: go mod download
@@ -122,12 +120,12 @@ jobs:
- name: Build XrayR
run: |
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
if: matrix.goarch == 'mips' || matrix.goarch == 'mipsle'
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
if: matrix.goos == 'windows'
run: |
@@ -135,27 +133,32 @@ jobs:
mv XrayR XrayR.exe
- name: Prepare to release
run: |
cp ${GITHUB_WORKSPACE}/README.md ./build_assets/README.md
cp ${GITHUB_WORKSPACE}/LICENSE ./build_assets/LICENSE
cp ${GITHUB_WORKSPACE}/main/dns.json ./build_assets/dns.json
cp ${GITHUB_WORKSPACE}/main/route.json ./build_assets/route.json
cp ${GITHUB_WORKSPACE}/main/custom_outbound.json ./build_assets/custom_outbound.json
cp ${GITHUB_WORKSPACE}/main/custom_inbound.json ./build_assets/custom_inbound.json
cp ${GITHUB_WORKSPACE}/main/rulelist ./build_assets/rulelist
cp ${GITHUB_WORKSPACE}/main/config.yml.example ./build_assets/config.yml
LIST=('geoip geoip geoip' 'domain-list-community dlc geosite')
for i in "${LIST[@]}"
do
INFO=($(echo $i | awk 'BEGIN{FS=" ";OFS=" "} {print $1,$2,$3}'))
LASTEST_TAG="$(curl -sL "https://api.github.com/repos/v2fly/${INFO[0]}/releases" | jq -r ".[0].tag_name" || echo "latest")"
FILE_NAME="${INFO[2]}.dat"
echo -e "Downloading ${FILE_NAME}..."
curl -L "https://github.com/v2fly/${INFO[0]}/releases/download/${LASTEST_TAG}/${INFO[1]}.dat" -o ./build_assets/${FILE_NAME}
echo -e "Verifying HASH key..."
HASH="$(curl -sL "https://github.com/v2fly/${INFO[0]}/releases/download/${LASTEST_TAG}/${INFO[1]}.dat.sha256sum" | awk -F ' ' '{print $1}')"
[ "$(sha256sum "./build_assets/${FILE_NAME}" | awk -F ' ' '{print $1}')" == "${HASH}" ] || { echo -e "The HASH key of ${FILE_NAME} does not match cloud one."; exit 1; }
done
uses: nick-fields/retry@v2
with:
timeout_minutes: 60
retry_wait_seconds: 60
max_attempts: 5
command: |
cp ${GITHUB_WORKSPACE}/README.md ./build_assets/README.md
cp ${GITHUB_WORKSPACE}/LICENSE ./build_assets/LICENSE
cp ${GITHUB_WORKSPACE}/release/config/dns.json ./build_assets/dns.json
cp ${GITHUB_WORKSPACE}/release/config/route.json ./build_assets/route.json
cp ${GITHUB_WORKSPACE}/release/config/custom_outbound.json ./build_assets/custom_outbound.json
cp ${GITHUB_WORKSPACE}/release/config/custom_inbound.json ./build_assets/custom_inbound.json
cp ${GITHUB_WORKSPACE}/release/config/rulelist ./build_assets/rulelist
cp ${GITHUB_WORKSPACE}/release/config/config.yml.example ./build_assets/config.yml
LIST=('geoip geoip geoip' 'domain-list-community dlc geosite')
for i in "${LIST[@]}"
do
INFO=($(echo $i | awk 'BEGIN{FS=" ";OFS=" "} {print $1,$2,$3}'))
DOWNLOAD_URL="https://raw.githubusercontent.com/v2fly/${INFO[0]}/release/${INFO[1]}.dat"
FILE_NAME="${INFO[2]}.dat"
echo -e "Downloading ${DOWNLOAD_URL}..."
curl -L "${DOWNLOAD_URL}" -o ./build_assets/${FILE_NAME}
echo -e "Verifying HASH key..."
HASH="$(curl -sL "${DOWNLOAD_URL}.sha256sum" | awk -F ' ' '{print $1}')"
[ "$(sha256sum "./build_assets/${FILE_NAME}" | awk -F ' ' '{print $1}')" == "${HASH}" ] || { echo -e "The HASH key of ${FILE_NAME} does not match cloud one."; exit 1; }
done
- name: Create ZIP archive
shell: bash
run: |
@@ -173,7 +176,7 @@ jobs:
run: |
mv build_assets XrayR-$ASSET_NAME
- name: Upload files to Artifacts
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: XrayR-${{ steps.get_filename.outputs.ASSET_NAME }}
path: |

22
.github/workflows/stale.yml vendored Normal file
View File

@@ -0,0 +1,22 @@
name: Close inactive issues
on:
schedule:
- cron: "30 1 * * *"
jobs:
close-issues:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v5
with:
days-before-issue-stale: 30
days-before-issue-close: 14
stale-issue-label: "stale"
stale-issue-message: "This issue is stale because it has been open for 30 days with no activity."
close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."
days-before-pr-stale: -1
days-before-pr-close: -1
repo-token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,20 +0,0 @@
name: Sync to Gitlab
on:
push:
delete:
workflow_dispatch:
jobs:
to_gitlab:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: pixta-dev/repository-mirroring-action@v1
with:
target_repo_url:
git@gitlab.com:xrayr-project/XrayR.git
ssh_private_key:
${{ secrets.SSH_PRIVATEKEY }}

36
.gitignore vendored
View File

@@ -1,16 +1,20 @@
main/main
main/XrayR
main/XrayR*
main/mytest
main/access.logo
main/error.log
api/chooseparser.go.bak
common/Inboundbuilder/.lego/
common/legocmd/.lego/
.vscode/launch.json
main/.lego
main/cert
main/config.yml
./vscode
.idea/*
.DS_Store
.idea
*.iml
out
gen
*.exe
*.exe~
*.dll
*.so
*.dylib
*.test
*.out
go.work
main
XrayR
XrayR*
access.log
error.log
.lego
cert
config.yml

View File

@@ -1,10 +1,10 @@
# Build go
FROM golang:1.18-alpine AS builder
FROM golang:1.22.0-alpine AS builder
WORKDIR /app
COPY . .
ENV CGO_ENABLED=0
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
FROM alpine

110
README-en.md Normal file
View File

@@ -0,0 +1,110 @@
# XrayR
[![](https://img.shields.io/badge/TgChat-@XrayR讨论-blue.svg)](https://t.me/XrayR_project)
[![](https://img.shields.io/badge/Channel-@XrayR通知-blue.svg)](https://t.me/XrayR_channel)
![](https://img.shields.io/github/stars/XrayR-project/XrayR)
![](https://img.shields.io/github/forks/XrayR-project/XrayR)
![](https://github.com/XrayR-project/XrayR/actions/workflows/release.yml/badge.svg)
![](https://github.com/XrayR-project/XrayR/actions/workflows/docker.yml/badge.svg)
[![Github All Releases](https://img.shields.io/github/downloads/XrayR-project/XrayR/total.svg)]()
[Iranian(farsi) README](https://github.com/XrayR-project/XrayR/blob/master/README_Fa.md), [Vietnamese(vi) README](https://github.com/XrayR-project/XrayR/blob/master/README-vi.md), [English(en) README](https://github.com/XrayR-project/XrayR/blob/master/README-en.md)
A Xray backend framework that can easily support many panels.
A back -end framework based on XRAY supports V2ay, Trojan, Shadowsocks protocols, which are easy to expand and support multi -panel docker.
If you like this project, you can click STAR+WATCH in the upper right corner to continue to pay attention to the progress of this project.
## Guide for use
Tutorial[Detailed tutorial](https://xrayr-project.github.io/XrayR-doc/)
## Disclaimer
This project is just my personal learning and development and maintenance. I do not guarantee any availability and is not responsible for any consequences caused by the use of this software.
## Features
* Permanent open source and free.
* Support V2Ray, Trojan, Shadowsocks multiple protocols.
* Support new features such as Vless and XTLS.
* Support single instance docking multi -panel and multi -node, no need to start repeatedly.
* Support restriction online IP
* Support node port level and user level speed limit.
* The configuration is simple and clear.
* Modify the automatic restart instance.
* Easy to compile and upgrade, you can quickly update the core version and support the new features of XRAY-CORE.
## Function
| Function | v2ray | trojan | shadowsocks |
|-----------|-------|--------|-------------|
| Get node information | √ | √ | √ |
| Get user information | √ | √ | √ |
| User traffic statistics | √ | √ | √ |
| Server information report | √ | √ | √ |
| Automatically apply for a TLS certificate | √ | √ | √ |
| Automatic renewal TLS certificate | √ | √ | √ |
| Number of online people | √ | √ | √ |
| Online user restrictions | √ | √ | √ |
| Audit rules | √ | √ | √ |
| Node port speed limit | √ | √ | √ |
| According to user speed limit | √ | √ | √ |
| Custom DNS | √ | √ | √ |
## Support for panels
| Panel | v2ray | trojan | shadowsocks |
|--------------------------------------------------------|-------|--------|-------------------------|
| sspanel-uim | √ | √ | √ (Single-ended multi-user and V2Ray-Plugin) |
| v2board | √ | √ | √ |
| [PMPanel](https://github.com/ByteInternetHK/PMPanel) | √ | √ | √ |
| [ProxyPanel](https://github.com/ProxyPanel/ProxyPanel) | √ | √ | √ |
| [WHMCS (V2RaySocks)](https://v2raysocks.doxtex.com/) | √ | √ | √ |
| [BunPanel](https://github.com/pennyMorant/bunpanel-release) | √ | √ | √ |
## Software Installation
### 1-Click installation
```
wget -N https://raw.githubusercontent.com/XrayR-project/XrayR-release/master/install.sh && bash install.sh
```
### Docker
[Docker deployment tutorial](https://xrayr-project.github.io/XrayR-doc/xrayr-xia-zai-he-an-zhuang/install/docker)
### Manual installation
[Manual installation tutorial](https://xrayr-project.github.io/XrayR-doc/xrayr-xia-zai-he-an-zhuang/install/manual)
## Configuration file and detailed use tutorial
[Detailed tutorial](https://xrayr-project.github.io/XrayR-doc/)
## Thanks
* [Project X](https://github.com/XTLS/)
* [V2Fly](https://github.com/v2fly)
* [VNet-V2ray](https://github.com/ProxyPanel/VNet-V2ray)
* [Air-Universe](https://github.com/crossfw/Air-Universe)
## Licence
[Mozilla Public License Version 2.0](https://github.com/XrayR-project/XrayR/blob/master/LICENSE)
## Telgram
[Xrayr back-end discussion](https://t.me/XrayR_project)
[Xrayr notification](https://t.me/XrayR_channel)
## Stargazers over time
[![Stargazers over time](https://starchart.cc/XrayR-project/XrayR.svg)](https://starchart.cc/XrayR-project/XrayR)

105
README-vi.md Normal file
View File

@@ -0,0 +1,105 @@
# XrayR
[![](https://img.shields.io/badge/TgChat-@XrayR讨论-blue.svg)](https://t.me/XrayR_project)
[![](https://img.shields.io/badge/Channel-@XrayR通知-blue.svg)](https://t.me/XrayR_channel)
![](https://img.shields.io/github/stars/XrayR-project/XrayR)
![](https://img.shields.io/github/forks/XrayR-project/XrayR)
![](https://github.com/XrayR-project/XrayR/actions/workflows/release.yml/badge.svg)
![](https://github.com/XrayR-project/XrayR/actions/workflows/docker.yml/badge.svg)
[![Github All Releases](https://img.shields.io/github/downloads/XrayR-project/XrayR/total.svg)]()
[Iranian(farsi) README](https://github.com/XrayR-project/XrayR/blob/master/README_Fa.md), [Vietnamese(vi) README](https://github.com/XrayR-project/XrayR/blob/master/README-vi.md), [English(en) README](https://github.com/XrayR-project/XrayR/blob/master/README-en.md)
A Xray backend framework that can easily support many panels.
Khung trở lại dựa trên XRay hỗ trợ các giao thức V2ay, Trojan, Shadowsocks, dễ dàng mở rộng và hỗ trợ kết nối nhiều người.
Nếu bạn thích dự án này, bạn có thể nhấp vào Star+Watch ở góc trên bên phải để tiếp tục chú ý đến tiến trình của dự án này.
## Tài liệu
Sử dụng hướng dẫn: [Hướng dẫn chi tiết](https://xrayr-project.github.io/XrayR-doc/) ( Tiếng Trung )
## Tuyên bố miễn trừ
Dự án này chỉ là học tập và phát triển và bảo trì cá nhân của tôi. Tôi không đảm bảo bất kỳ sự sẵn có nào và không chịu trách nhiệm cho bất kỳ hậu quả nào do việc sử dụng phần mềm này.
## Đặt điểm nổi bật
* Nguồn mở vĩnh viễn và miễn phí.
* Hỗ trợ V2Ray, Trojan, Shadowsocks nhiều giao thức.
* Hỗ trợ các tính năng mới như Vless và XTL.
* Hỗ trợ trường hợp đơn lẻ kết nối Multi -Panel và Multi -Node, không cần phải bắt đầu nhiều lần.
* Hỗ trợ hạn chế IP trực tuyến
* Hỗ trợ cấp cổng nút và giới hạn tốc độ cấp người dùng.
* Cấu hình đơn giản và rõ ràng.
* Sửa đổi phiên bản khởi động lại tự động.
* Dễ dàng biên dịch và nâng cấp, bạn có thể nhanh chóng cập nhật phiên bản cốt lõi và hỗ trợ các tính năng mới của Xray-Core.
## Chức năng
| Chức năng | v2ray | trojan | shadowsocks |
|-----------|-------|--------|-------------|
| Nhận thông tin Node | √ | √ | √ |
| Nhận thông tin người dùng | √ | √ | √ |
| Thống kê lưu lượng người dùng | √ | √ | √ |
| Báo cáo thông tin máy chủ | √ | √ | √ |
| Tự động đăng ký chứng chỉ TLS | √ | √ | √ |
| Chứng chỉ TLS gia hạn tự động | √ | √ | √ |
| Số người trực tuyến | √ | √ | √ |
| Hạn chế người dùng trực tuyến | √ | √ | √ |
| Quy tắc kiểm toán | √ | √ | √ |
| Giới hạn tốc độ cổng nút | √ | √ | √ |
| Theo giới hạn tốc độ người dùng | √ | √ | √ |
| DNS tùy chỉnh | √ | √ | √ |
## Hỗ trợ Panel
| Panel | v2ray | trojan | shadowsocks |
|--------------------------------------------------------|-------|--------|-------------------------|
| sspanel-uim | √ | √ | √ (Nhiều người dùng cuối và v2ray-plugin) |
| v2board | √ | √ | √ |
| [PMPanel](https://github.com/ByteInternetHK/PMPanel) | √ | √ | √ |
| [ProxyPanel](https://github.com/ProxyPanel/ProxyPanel) | √ | √ | √ |
| [WHMCS (V2RaySocks)](https://v2raysocks.doxtex.com/) | √ | √ | √ |
| [BunPanel](https://github.com/pennyMorant/bunpanel-release) | √ | √ | √ |
## Cài đặt phần mềm
### Một cài đặt chính
```
wget -N https://raw.githubusercontent.com/XrayR-project/XrayR-release/master/install.sh && bash install.sh
```
### Sử dụng phần mềm triển khai Docker
[Hướng dẫn cài đặt thông qua Docker](https://xrayr-project.github.io/XrayR-doc/xrayr-xia-zai-he-an-zhuang/install/docker)
### Hướng dẫn cài đặt
[Hướng dẫn cài đặt thủ công](https://xrayr-project.github.io/XrayR-doc/xrayr-xia-zai-he-an-zhuang/install/manual)
## Tệp cấu hình và hướng dẫn sử dụng chi tiết
[Hướng dẫn chi tiết](https://xrayr-project.github.io/XrayR-doc/)
## Thanks
* [Project X](https://github.com/XTLS/)
* [V2Fly](https://github.com/v2fly)
* [VNet-V2ray](https://github.com/ProxyPanel/VNet-V2ray)
* [Air-Universe](https://github.com/crossfw/Air-Universe)
## Licence
[Mozilla Public License Version 2.0](https://github.com/XrayR-project/XrayR/blob/master/LICENSE)
## Telgram
[Xrayr Back-end Thảo luận](https://t.me/XrayR_project)
[Thông báo Xrayr](https://t.me/XrayR_channel)
## Stargazers over time
[![Stargazers over time](https://starchart.cc/XrayR-project/XrayR.svg)](https://starchart.cc/XrayR-project/XrayR)

View File

@@ -1,6 +1,15 @@
# XrayR
[![](https://img.shields.io/badge/TgChat-@XrayR讨论-blue.svg)](https://t.me/XrayR_project)
[![](https://img.shields.io/badge/Channel-@XrayR通知-blue.svg)](https://t.me/XrayR_channel)
![](https://img.shields.io/github/stars/XrayR-project/XrayR)
![](https://img.shields.io/github/forks/XrayR-project/XrayR)
![](https://github.com/XrayR-project/XrayR/actions/workflows/release.yml/badge.svg)
![](https://github.com/XrayR-project/XrayR/actions/workflows/docker.yml/badge.svg)
[![Github All Releases](https://img.shields.io/github/downloads/XrayR-project/XrayR/total.svg)]()
[English](https://github.com/XrayR-project/XrayR/blob/master/README-en.md)|[Iranian](https://github.com/XrayR-project/XrayR/blob/master/README_Fa.md)|[Vietnamese](https://github.com/XrayR-project/XrayR/blob/master/README-vi.md)
A Xray backend framework that can easily support many panels.
@@ -9,11 +18,14 @@ A Xray backend framework that can easily support many panels.
如果您喜欢本项目可以右上角点个star+watch持续关注本项目的进展。
使用教程:[详细使用教程](https://xrayr-project.github.io/XrayR-doc/)
## 免责声明
本项目只是本人个人学习开发并维护,本人不保证任何可用性,也不对使用本软件造成的任何后果负责。
## 特点
* 永久开源且免费。
* 支持V2rayTrojan Shadowsocks多种协议。
* 支持Vless和XTLS等新特性。
@@ -26,39 +38,47 @@ A Xray backend framework that can easily support many panels.
## 功能介绍
| 功能 | v2ray | trojan | shadowsocks |
| --------------- | ----- | ------ | ----------- |
| 功能 | v2ray | trojan | shadowsocks |
|-----------|-------|--------|-------------|
| 获取节点信息 | √ | √ | √ |
| 获取用户信息 | √ | √ | √ |
| 用户流量统计 | √ | √ | √ |
| 服务器信息上报 | √ | √ | √ |
| 服务器信息上报 | √ | √ | √ |
| 自动申请tls证书 | √ | √ | √ |
| 自动续签tls证书 | √ | √ | √ |
| 在线人数统计 | √ | √ | √ |
| 在线用户限制 | √ | √ | √ |
| 审计规则 | √ | √ | √ |
| 审计规则 | √ | √ | √ |
| 节点端口限速 | √ | √ | √ |
| 按照用户限速 | √ | √ | √ |
| 自定义DNS | √ | √ | √ |
| 自定义DNS | √ | √ | √ |
## 支持前端
| 前端 | v2ray | trojan | shadowsocks |
| ------------------------------------------------------ | ----- | ------ | ------------------------------ |
| 前端 | v2ray | trojan | shadowsocks |
|--------------------------------------------------------|-------|--------|-------------------------|
| sspanel-uim | √ | √ | √ (单端口多用户和V2ray-Plugin) |
| v2board | √ | √ | √ |
| [PMPanel](https://github.com/ByteInternetHK/PMPanel) | √ | √ | √ |
| [ProxyPanel](https://github.com/ProxyPanel/ProxyPanel) | √ | √ | √ |
| [WHMCS (v2raysocks)](https://v2raysocks.doxtex.com/) | √ | √ | √ |
| v2board | √ | √ | √ |
| [PMPanel](https://github.com/ByteInternetHK/PMPanel) | √ | √ | √ |
| [ProxyPanel](https://github.com/ProxyPanel/ProxyPanel) | √ | √ | √ |
| [WHMCS (V2RaySocks)](https://v2raysocks.doxtex.com/) | √ | √ | √ |
| [GoV2Panel](https://github.com/pingProMax/gov2panel) | √ | √ | √ |
| [BunPanel](https://github.com/pennyMorant/bunpanel-release) | √ | √ | √ |
## 软件安装
### 一键安装
```
wget -N https://raw.githubusercontent.com/XrayR-project/XrayR-release/master/install.sh && bash install.sh
```
### 使用Docker部署软件
[Docker部署教程](https://xrayr-project.github.io/XrayR-doc/xrayr-xia-zai-he-an-zhuang/install/docker)
### 手动安装
[手动安装教程](https://xrayr-project.github.io/XrayR-doc/xrayr-xia-zai-he-an-zhuang/install/manual)
## 配置文件及详细使用教程

105
README_Fa.md Normal file
View File

@@ -0,0 +1,105 @@
# XrayR
[![](https://img.shields.io/badge/TgChat-@XrayR讨论-blue.svg)](https://t.me/XrayR_project)
[![](https://img.shields.io/badge/Channel-@XrayR通知-blue.svg)](https://t.me/XrayR_channel)
![](https://img.shields.io/github/stars/XrayR-project/XrayR)
![](https://img.shields.io/github/forks/XrayR-project/XrayR)
![](https://github.com/XrayR-project/XrayR/actions/workflows/release.yml/badge.svg)
![](https://github.com/XrayR-project/XrayR/actions/workflows/docker.yml/badge.svg)
[![Github All Releases](https://img.shields.io/github/downloads/XrayR-project/XrayR/total.svg)]()
[Iranian(farsi) README](https://github.com/XrayR-project/XrayR/blob/master/README_Fa.md), [Vietnamese(vi) README](https://github.com/XrayR-project/XrayR/blob/master/README-vi.md), [English(en) README](https://github.com/XrayR-project/XrayR/blob/master/README-en.md)
یک فریمورک بک اند مبتنی بر xray که از چند از پنل پشتیبانی می کند
یک چارچوب بک‌اند مبتنی بر Xray که از پروتکل‌های V2ay، Trojan و Shadowsocks پشتیبانی می‌کند، به راحتی قابل گسترش است و از اتصال چند پنل پشتیبانی می‌کند.
اگر این پروژه را دوست دارید، می توانید با کلیک بر روی ستاره+ساعت در گوشه بالا سمت راست به ادامه روند پیشرفت این پروژه توجه کنید.
آموزش:[اموزش با جزئیات](https://xrayr-project.github.io/XrayR-doc/)
## سلب مسئولیت
این پروژه فقط مطالعه، توسعه و نگهداری شخصی من است. من هیچ گونه قابلیت استفاده را تضمین نمی کنم و مسئولیتی در قبال عواقب ناشی از استفاده از این نرم افزار ندارم.
## امکانات
* منبع باز دائمی و رایگان
* پشتیبانی از چندین پروتکل V2ray، Trojan، Shadowsocks.
* پشتیبانی از ویژگی های جدید مانند Vless و XTLS.
* پشتیبانی از اتصال یک نمونه چند پانل، چند گره، بدون نیاز به شروع مکرر.
* پشتیبانی محدود IP آنلاین
* پشتیبانی از سطح پورت گره، محدودیت سرعت سطح کاربر.
* پیکربندی ساده و سرراست است.
* پیکربندی را تغییر دهید تا نمونه به طور خودکار راه اندازی مجدد شود.
* کامپایل و ارتقاء آن آسان است و می تواند به سرعت نسخه اصلی را به روز کند و از ویژگی های جدید Xray-core پشتیبانی می کند.
## امکانات
| امکانات | v2ray | trojan | shadowsocks |
|-----------|-------|--------|-------------|
| اطلاعات گره را دریافت کنید | √ | √ | √ |
| دریافت اطلاعات کاربر | √ | √ | √ |
| آمار ترافیک کاربران | √ | √ | √ |
| گزارش اطلاعات سرور | √ | √ | √ |
| به طور خودکار برای گواهی tls درخواست دهید | √ | √ | √ |
| تمدید خودکار گواهی tls | √ | √ | √ |
| آمار آنلاین | √ | √ | √ |
| محدودیت کاربر آنلاین | √ | √ | √ |
| قوانین حسابرسی | √ | √ | √ |
| محدودیت سرعت پورت گره | √ | √ | √ |
| محدودیت سرعت بر اساس کاربر | √ | √ | √ |
| DNS سفارشی | √ | √ | √ |
## پشتیبانی از قسمت فرانت
| قسمت فرانت | v2ray | trojan | shadowsocks |
|--------------------------------------------------------|-------|--------|-------------------------|
| sspanel-uim | √ | √ | √ (تک پورت چند کاربره و V2ray-Plugin) |
| v2board | √ | √ | √ |
| [PMPanel](https://github.com/ByteInternetHK/PMPanel) | √ | √ | √ |
| [ProxyPanel](https://github.com/ProxyPanel/ProxyPanel) | √ | √ | √ |
| [WHMCS (V2RaySocks)](https://v2raysocks.doxtex.com/) | √ | √ | √ |
| [BunPanel](https://github.com/pennyMorant/bunpanel-release) | √ | √ | √ |
## نصب نرم افزار
### نصب بصورت یکپارچه
```
wget -N https://raw.githubusercontent.com/XrayR-project/XrayR-release/master/install.sh && bash install.sh
```
### استقرار نرم افزار با استفاده از Docker
[آموزش استقرار داکر](https://xrayr-project.github.io/XrayR-doc/xrayr-xia-zai-he-an-zhuang/install/docker)
### نصب دستی
[آموزش نصب دستی](https://xrayr-project.github.io/XrayR-doc/xrayr-xia-zai-he-an-zhuang/install/manual)
## فایل های پیکربندی و آموزش های با جرئیات
[آموزش مفصل](https://xrayr-project.github.io/XrayR-doc/)
## Thanks
* [Project X](https://github.com/XTLS/)
* [V2Fly](https://github.com/v2fly)
* [VNet-V2ray](https://github.com/ProxyPanel/VNet-V2ray)
* [Air-Universe](https://github.com/crossfw/Air-Universe)
## Licence
[Mozilla Public License Version 2.0](https://github.com/XrayR-project/XrayR/blob/master/LICENSE)
## Telgram
[بحث در مورد XrayR Backend](https://t.me/XrayR_project)
[کانال اعلان در مورد XrayR](https://t.me/XrayR_channel)
## Stargazers over time
[![Stargazers over time](https://starchart.cc/XrayR-project/XrayR.svg)](https://starchart.cc/XrayR-project/XrayR)

View File

@@ -3,16 +3,24 @@ package api
import (
"encoding/json"
"regexp"
"github.com/xtls/xray-core/infra/conf"
)
// API config
const (
UserNotModified = "users not modified"
NodeNotModified = "node not modified"
RuleNotModified = "rules not modified"
)
// Config API config
type Config struct {
APIHost string `mapstructure:"ApiHost"`
NodeID int `mapstructure:"NodeID"`
Key string `mapstructure:"ApiKey"`
NodeType string `mapstructure:"NodeType"`
EnableVless bool `mapstructure:"EnableVless"`
EnableXTLS bool `mapstructure:"EnableXTLS"`
VlessFlow string `mapstructure:"VlessFlow"`
Timeout int `mapstructure:"Timeout"`
SpeedLimit float64 `mapstructure:"SpeedLimit"`
DeviceLimit int `mapstructure:"DeviceLimit"`
@@ -20,7 +28,7 @@ type Config struct {
DisableCustomConfig bool `mapstructure:"DisableCustomConfig"`
}
// Node status
// NodeStatus Node status
type NodeStatus struct {
CPU float64
Mem float64
@@ -29,37 +37,59 @@ type NodeStatus struct {
}
type NodeInfo struct {
NodeType string // Must be V2ray, Trojan, and Shadowsocks
NodeID int
Port uint32
SpeedLimit uint64 // Bps
AlterID uint16
TransportProtocol string
FakeType string
Host string
Path string
EnableTLS bool
TLSType string
EnableVless bool
CypherMethod string
ServiceName string
Header json.RawMessage
AcceptProxyProtocol bool
Authority string
NodeType string // Must be V2ray, Trojan, and Shadowsocks
NodeID int
Port uint32
SpeedLimit uint64 // Bps
AlterID uint16
TransportProtocol string
FakeType string
Host string
Path string
EnableTLS bool
EnableSniffing bool
RouteOnly bool
EnableVless bool
VlessFlow string
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 {
UID int
Email string
Passwd string
Port uint32
Method string
SpeedLimit uint64 // Bps
DeviceLimit int
Protocol string
ProtocolParam string
Obfs string
ObfsParam string
UUID string
AlterID uint16
UID int
Email string
UUID string
Passwd string
Port uint32
AlterID uint16
Method string
SpeedLimit uint64 // Bps
DeviceLimit int
}
type OnlineUser struct {
@@ -90,3 +120,14 @@ type DetectResult struct {
UID 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

@@ -0,0 +1,101 @@
package bunpanel_test
import (
"testing"
"github.com/XrayR-project/XrayR/api"
"github.com/XrayR-project/XrayR/api/bunpanel"
)
func CreateClient() api.API {
apiConfig := &api.Config{
APIHost: "http://localhost:8080",
Key: "123456",
NodeID: 1,
NodeType: "V2ray",
}
client := bunpanel.New(apiConfig)
return client
}
func TestGetV2rayNodeInfo(t *testing.T) {
client := CreateClient()
nodeInfo, err := client.GetNodeInfo()
if err != nil {
t.Error(err)
}
t.Log(nodeInfo)
}
func TestGetSSNodeInfo(t *testing.T) {
apiConfig := &api.Config{
APIHost: "http://127.0.0.1:668",
Key: "qwertyuiopasdfghjkl",
NodeID: 1,
NodeType: "Shadowsocks",
}
client := bunpanel.New(apiConfig)
nodeInfo, err := client.GetNodeInfo()
if err != nil {
t.Error(err)
}
t.Log(nodeInfo)
}
func TestGetTrojanNodeInfo(t *testing.T) {
apiConfig := &api.Config{
APIHost: "http://127.0.0.1:668",
Key: "qwertyuiopasdfghjkl",
NodeID: 1,
NodeType: "Trojan",
}
client := bunpanel.New(apiConfig)
nodeInfo, err := client.GetNodeInfo()
if err != nil {
t.Error(err)
}
t.Log(nodeInfo)
}
func TestGetUserList(t *testing.T) {
client := CreateClient()
userList, err := client.GetUserList()
if err != nil {
t.Error(err)
}
t.Log(userList)
}
func TestReportReportUserTraffic(t *testing.T) {
client := CreateClient()
userList, err := client.GetUserList()
if err != nil {
t.Error(err)
}
generalUserTraffic := make([]api.UserTraffic, len(*userList))
for i, userInfo := range *userList {
generalUserTraffic[i] = api.UserTraffic{
UID: userInfo.UID,
Upload: 1111,
Download: 2222,
}
}
// client.Debug()
err = client.ReportUserTraffic(&generalUserTraffic)
if err != nil {
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)
}

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

74
api/newV2board/model.go Normal file
View File

@@ -0,0 +1,74 @@
package newV2board
import (
"encoding/json"
)
type serverConfig struct {
shadowsocks
v2ray
trojan
ServerPort int `json:"server_port"`
BaseConfig struct {
PushInterval int `json:"push_interval"`
PullInterval int `json:"pull_interval"`
} `json:"base_config"`
Routes []route `json:"routes"`
}
type shadowsocks struct {
Cipher string `json:"cipher"`
Obfs string `json:"obfs"`
ObfsSettings struct {
Path string `json:"path"`
Host string `json:"host"`
} `json:"obfs_settings"`
ServerKey string `json:"server_key"`
}
type v2ray struct {
Network string `json:"network"`
NetworkSettings struct {
Path string `json:"path"`
Host string `json:"host"`
Headers *json.RawMessage `json:"headers"`
ServiceName string `json:"serviceName"`
Header *json.RawMessage `json:"header"`
} `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"`
}
type trojan struct {
Host string `json:"host"`
ServerName string `json:"server_name"`
}
type route struct {
Id int `json:"id"`
Match []string `json:"match"`
Action string `json:"action"`
ActionValue string `json:"action_value"`
}
type user struct {
Id int `json:"id"`
Uuid string `json:"uuid"`
SpeedLimit int `json:"speed_limit"`
}

473
api/newV2board/v2board.go Normal file
View File

@@ -0,0 +1,473 @@
package newV2board
import (
"bufio"
"encoding/json"
"errors"
"fmt"
"os"
"regexp"
"strconv"
"strings"
"sync/atomic"
"time"
log "github.com/sirupsen/logrus"
"github.com/bitly/go-simplejson"
"github.com/go-resty/resty/v2"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/infra/conf"
"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
VlessFlow string
SpeedLimit float64
DeviceLimit int
LocalRuleList []api.DetectRule
resp atomic.Value
eTags map[string]string
}
// 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)
var nodeType string
if apiConfig.NodeType == "V2ray" && apiConfig.EnableVless {
nodeType = "vless"
} else {
nodeType = strings.ToLower(apiConfig.NodeType)
}
// Create Key for each requests
client.SetQueryParams(map[string]string{
"node_id": strconv.Itoa(apiConfig.NodeID),
"node_type": 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)
defer file.Close()
// 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
}
}
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: %v", c.assembleURL(path), err)
}
if res.StatusCode() > 399 {
return nil, fmt.Errorf("request %s failed: %s, %v", c.assembleURL(path), res.String(), 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 panel
func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
server := new(serverConfig)
path := "/api/v1/server/UniProxy/config"
res, err := c.client.R().
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)
}
// 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)
if err != nil {
return nil, err
}
b, _ := nodeInfoResp.Encode()
json.Unmarshal(b, server)
if server.ServerPort == 0 {
return nil, errors.New("server port must > 0")
}
c.resp.Store(server)
switch c.NodeType {
case "V2ray", "Vmess", "Vless":
nodeInfo, err = c.parseV2rayNodeResponse(server)
case "Trojan":
nodeInfo, err = c.parseTrojanNodeResponse(server)
case "Shadowsocks":
nodeInfo, err = c.parseSSNodeResponse(server)
default:
return nil, fmt.Errorf("unsupported node type: %s", c.NodeType)
}
if err != nil {
return nil, fmt.Errorf("parse node info failed: %s, \nError: %v", res.String(), err)
}
return nodeInfo, nil
}
// GetUserList will pull user form panel
func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
var users []*user
path := "/api/v1/server/UniProxy/user"
switch c.NodeType {
case "V2ray", "Trojan", "Shadowsocks", "Vmess", "Vless":
break
default:
return nil, fmt.Errorf("unsupported node type: %s", c.NodeType)
}
res, err := c.client.R().
SetHeader("If-None-Match", c.eTags["users"]).
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)
}
// update etag
if res.Header().Get("Etag") != "" && res.Header().Get("Etag") != c.eTags["users"] {
c.eTags["users"] = res.Header().Get("Etag")
}
usersResp, err := c.parseResponse(res, path, err)
if err != nil {
return nil, err
}
b, _ := usersResp.Get("users").Encode()
json.Unmarshal(b, &users)
if len(users) == 0 {
return nil, errors.New("users is null")
}
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 + "@v2board.user"
if c.NodeType == "Shadowsocks" {
u.Passwd = u.UUID
}
userList[i] = u
}
return &userList, nil
}
// ReportUserTraffic reports the user traffic
func (c *APIClient) ReportUserTraffic(userTraffic *[]api.UserTraffic) error {
path := "/api/v1/server/UniProxy/push"
// json structure: {uid1: [u, d], uid2: [u, d], uid1: [u, d], uid3: [u, d]}
data := make(map[int][]int64, len(*userTraffic))
for _, traffic := range *userTraffic {
data[traffic.UID] = []int64{traffic.Upload, traffic.Download}
}
res, err := c.client.R().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) {
routes := c.resp.Load().(*serverConfig).Routes
ruleList := c.LocalRuleList
for i := range routes {
if routes[i].Action == "block" {
ruleList = append(ruleList, api.DetectRule{
ID: i,
Pattern: regexp.MustCompile(strings.Join(routes[i].Match, "|")),
})
}
}
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 nodeInfo format
func (c *APIClient) parseTrojanNodeResponse(s *serverConfig) (*api.NodeInfo, error) {
// Create GeneralNodeInfo
nodeInfo := &api.NodeInfo{
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: uint32(s.ServerPort),
TransportProtocol: "tcp",
EnableTLS: true,
Host: s.Host,
ServiceName: s.ServerName,
NameServerConfig: s.parseDNSConfig(),
}
return nodeInfo, nil
}
// parseSSNodeResponse parse the response for the given nodeInfo format
func (c *APIClient) parseSSNodeResponse(s *serverConfig) (*api.NodeInfo, error) {
var header json.RawMessage
if s.Obfs == "http" {
path := "/"
if p := s.ObfsSettings.Path; p != "" {
if strings.HasPrefix(p, "/") {
path = p
} else {
path += p
}
}
h := simplejson.New()
h.Set("type", "http")
h.SetPath([]string{"request", "path"}, path)
header, _ = h.Encode()
}
// Create GeneralNodeInfo
return &api.NodeInfo{
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: uint32(s.ServerPort),
TransportProtocol: "tcp",
CypherMethod: s.Cipher,
ServerKey: s.ServerKey, // shadowsocks2022 share key
NameServerConfig: s.parseDNSConfig(),
Header: header,
}, nil
}
// parseV2rayNodeResponse parse the response for the given nodeInfo format
func (c *APIClient) parseV2rayNodeResponse(s *serverConfig) (*api.NodeInfo, error) {
var (
host string
header json.RawMessage
enableTLS bool
enableREALITY bool
dest string
xVer uint64
)
if s.VlessTlsSettings.Dest != "" {
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 {
case "ws":
if s.NetworkSettings.Headers != nil {
if httpHeader, err := s.NetworkSettings.Headers.MarshalJSON(); err != nil {
return nil, err
} else {
b, _ := simplejson.NewJson(httpHeader)
host = b.Get("Host").MustString()
}
}
case "tcp":
if s.NetworkSettings.Header != nil {
if httpHeader, err := s.NetworkSettings.Header.MarshalJSON(); err != nil {
return nil, err
} else {
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
}
}
switch s.Tls {
case 0:
enableTLS = false
enableREALITY = false
case 1:
enableTLS = true
enableREALITY = false
case 2:
enableTLS = true
enableREALITY = true
}
// Create GeneralNodeInfo
return &api.NodeInfo{
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: uint32(s.ServerPort),
AlterID: 0,
TransportProtocol: s.Network,
EnableTLS: enableTLS,
Path: s.NetworkSettings.Path,
Host: host,
EnableVless: c.EnableVless,
VlessFlow: s.VlessFlow,
ServiceName: s.NetworkSettings.ServiceName,
Header: header,
EnableREALITY: enableREALITY,
REALITYConfig: &realityConfig,
NameServerConfig: s.parseDNSConfig(),
}, nil
}
func (s *serverConfig) parseDNSConfig() (nameServerList []*conf.NameServerConfig) {
for i := range s.Routes {
if s.Routes[i].Action == "dns" {
nameServerList = append(nameServerList, &conf.NameServerConfig{
Address: &conf.Address{Address: net.ParseAddress(s.Routes[i].ActionValue)},
Domains: s.Routes[i].Match,
})
}
}
return
}

View File

@@ -1,10 +1,10 @@
package v2board_test
package newV2board_test
import (
"testing"
"github.com/XrayR-project/XrayR/api"
"github.com/XrayR-project/XrayR/api/v2board"
"github.com/XrayR-project/XrayR/api/newV2board"
)
func CreateClient() api.API {
@@ -14,11 +14,11 @@ func CreateClient() api.API {
NodeID: 1,
NodeType: "V2ray",
}
client := v2board.New(apiConfig)
client := newV2board.New(apiConfig)
return client
}
func TestGetV2rayNodeinfo(t *testing.T) {
func TestGetV2rayNodeInfo(t *testing.T) {
client := CreateClient()
nodeInfo, err := client.GetNodeInfo()
if err != nil {
@@ -27,14 +27,14 @@ func TestGetV2rayNodeinfo(t *testing.T) {
t.Log(nodeInfo)
}
func TestGetSSNodeinfo(t *testing.T) {
func TestGetSSNodeInfo(t *testing.T) {
apiConfig := &api.Config{
APIHost: "http://127.0.0.1:668",
Key: "qwertyuiopasdfghjkl",
NodeID: 1,
NodeType: "Shadowsocks",
}
client := v2board.New(apiConfig)
client := newV2board.New(apiConfig)
nodeInfo, err := client.GetNodeInfo()
if err != nil {
t.Error(err)
@@ -42,14 +42,14 @@ func TestGetSSNodeinfo(t *testing.T) {
t.Log(nodeInfo)
}
func TestGetTrojanNodeinfo(t *testing.T) {
func TestGetTrojanNodeInfo(t *testing.T) {
apiConfig := &api.Config{
APIHost: "http://127.0.0.1:668",
Key: "qwertyuiopasdfghjkl",
NodeID: 1,
NodeType: "Trojan",
}
client := v2board.New(apiConfig)
client := newV2board.New(apiConfig)
nodeInfo, err := client.GetNodeInfo()
if err != nil {
t.Error(err)
@@ -82,7 +82,7 @@ func TestReportReportUserTraffic(t *testing.T) {
Download: 114514,
}
}
//client.Debug()
// client.Debug()
err = client.ReportUserTraffic(&generalUserTraffic)
if err != nil {
t.Error(err)

View File

@@ -16,7 +16,7 @@ type NodeInfoResponse struct {
Host string `json:"host"`
Path string `json:"path"`
Grpc bool `json:"grpc"`
Sni string `json:sni`
Sni string `json:"sni"`
}
// UserResponse is the response of user

View File

@@ -4,15 +4,17 @@ import (
"bufio"
"encoding/json"
"fmt"
"log"
"os"
"reflect"
"regexp"
"strconv"
"time"
"github.com/XrayR-project/XrayR/api"
log "github.com/sirupsen/logrus"
"github.com/go-resty/resty/v2"
"github.com/XrayR-project/XrayR/api"
)
// APIClient create a api client to the panel.
@@ -23,7 +25,7 @@ type APIClient struct {
Key string
NodeType string
EnableVless bool
EnableXTLS bool
VlessFlow string
SpeedLimit float64
DeviceLimit int
LocalRuleList []api.DetectRule
@@ -60,7 +62,7 @@ func New(apiConfig *api.Config) *APIClient {
APIHost: apiConfig.APIHost,
NodeType: apiConfig.NodeType,
EnableVless: apiConfig.EnableVless,
EnableXTLS: apiConfig.EnableXTLS,
VlessFlow: apiConfig.VlessFlow,
SpeedLimit: apiConfig.SpeedLimit,
DeviceLimit: apiConfig.DeviceLimit,
LocalRuleList: localRuleList,
@@ -76,7 +78,7 @@ func readLocalRuleList(path string) (LocalRuleList []api.DetectRule) {
// open the file
file, err := os.Open(path)
//handle errors while opening
// handle errors while opening
if err != nil {
log.Printf("Error when opening file: %s", err)
return LocalRuleList
@@ -94,7 +96,7 @@ func readLocalRuleList(path string) (LocalRuleList []api.DetectRule) {
// handle first encountered error while reading
if err := fileScanner.Err(); err != nil {
log.Fatalf("Error while reading file: %s", err)
return make([]api.DetectRule, 0)
return
}
file.Close()
@@ -130,7 +132,7 @@ func (c *APIClient) parseResponse(res *resty.Response, path string, err error) (
if response.Ret != 200 {
res, _ := json.Marshal(&response)
return nil, fmt.Errorf("Ret %s invalid", string(res))
return nil, fmt.Errorf("ret %s invalid", string(res))
}
return response, nil
}
@@ -167,7 +169,7 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
nodeInfoResponse := new(NodeInfoResponse)
if err := json.Unmarshal(response.Data, nodeInfoResponse); 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)
}
switch c.NodeType {
case "V2ray":
@@ -177,7 +179,7 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
case "Shadowsocks":
nodeInfo, err = c.ParseSSNodeResponse(nodeInfoResponse)
default:
return nil, fmt.Errorf("Unsupported Node type: %s", c.NodeType)
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
}
if err != nil {
@@ -219,12 +221,12 @@ func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
var userListResponse *[]UserResponse
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)
}
userList, err := c.ParseUserListResponse(userListResponse)
if err != nil {
res, _ := json.Marshal(userListResponse)
return nil, fmt.Errorf("Parse user list failed: %s", string(res))
return nil, fmt.Errorf("parse user list failed: %s", string(res))
}
return userList, nil
}
@@ -234,7 +236,7 @@ func (c *APIClient) ReportNodeStatus(nodeStatus *api.NodeStatus) (err error) {
return nil
}
//ReportNodeOnlineUsers reports online user ip
// ReportNodeOnlineUsers reports online user ip
func (c *APIClient) ReportNodeOnlineUsers(onlineUserList *[]api.OnlineUser) error {
var nodeType = ""
switch c.NodeType {
@@ -338,7 +340,7 @@ func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
ruleListResponse := new([]RuleItem)
if err := json.Unmarshal(response.Data, ruleListResponse); err != nil {
return nil, fmt.Errorf("Unmarshal %s failed: %s", reflect.TypeOf(ruleListResponse), err)
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(ruleListResponse), err)
}
for _, r := range *ruleListResponse {
@@ -358,8 +360,8 @@ func (c *APIClient) ReportIllegal(detectResultList *[]api.DetectResult) error {
// ParseV2rayNodeResponse parse the response for the given nodeinfor format
func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *NodeInfoResponse) (*api.NodeInfo, error) {
var enableTLS bool
var path, host, TLStype, transportProtocol, serviceName string
var speedlimit uint64 = 0
var path, host, transportProtocol, serviceName string
var speedLimit uint64 = 0
port := nodeInfoResponse.Port
alterID := nodeInfoResponse.AlterId
@@ -375,34 +377,29 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *NodeInfoResponse) (
}
// Compatible with more node types config
switch nodeInfoResponse.Security {
case "tls", "xtls":
if c.EnableXTLS {
TLStype = "xtls"
} else {
TLStype = "tls"
}
case "tls":
enableTLS = true
default:
enableTLS = false
}
if c.SpeedLimit > 0 {
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
} else {
speedlimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
speedLimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
}
// Create GeneralNodeInfo
nodeinfo := &api.NodeInfo{
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: port,
SpeedLimit: speedlimit,
SpeedLimit: speedLimit,
AlterID: alterID,
TransportProtocol: transportProtocol,
EnableTLS: enableTLS,
TLSType: TLStype,
Path: path,
Host: host,
EnableVless: c.EnableVless,
VlessFlow: c.VlessFlow,
ServiceName: serviceName,
}
@@ -411,38 +408,33 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *NodeInfoResponse) (
// ParseSSNodeResponse parse the response for the given nodeinfor format
func (c *APIClient) ParseSSNodeResponse(nodeInfoResponse *NodeInfoResponse) (*api.NodeInfo, error) {
var speedlimit uint64 = 0
var speedLimit uint64 = 0
if c.SpeedLimit > 0 {
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
} else {
speedlimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
speedLimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
}
// Create GeneralNodeInfo
nodeinfo := &api.NodeInfo{
nodeInfo := &api.NodeInfo{
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: nodeInfoResponse.Port,
SpeedLimit: speedlimit,
SpeedLimit: speedLimit,
TransportProtocol: "tcp",
CypherMethod: nodeInfoResponse.Method,
}
return nodeinfo, nil
return nodeInfo, nil
}
// ParseTrojanNodeResponse parse the response for the given nodeinfor format
func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *NodeInfoResponse) (*api.NodeInfo, error) {
// 域名或IP;port=连接端口#偏移端口|host=xx
// gz.aaa.com;port=443#12345|host=hk.aaa.com
var TLSType, host string
var host string
var transportProtocol = "tcp"
var speedlimit uint64 = 0
if c.EnableXTLS {
TLSType = "xtls"
} else {
TLSType = "tls"
}
host = nodeInfoResponse.Host
port := nodeInfoResponse.Port
@@ -455,25 +447,24 @@ func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *NodeInfoResponse)
transportProtocol = "grpc"
}
// Create GeneralNodeInfo
nodeinfo := &api.NodeInfo{
nodeInfo := &api.NodeInfo{
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: port,
SpeedLimit: speedlimit,
TransportProtocol: transportProtocol,
EnableTLS: true,
TLSType: TLSType,
Host: host,
ServiceName: nodeInfoResponse.Sni,
}
return nodeinfo, nil
return nodeInfo, nil
}
// ParseUserListResponse parse the response for the given nodeinfo format
func (c *APIClient) ParseUserListResponse(userInfoResponse *[]UserResponse) (*[]api.UserInfo, error) {
var deviceLimit int = 0
var speedlimit uint64 = 0
var deviceLimit = 0
var speedLimit uint64 = 0
userList := make([]api.UserInfo, len(*userInfoResponse))
for i, user := range *userInfoResponse {
if c.DeviceLimit > 0 {
@@ -482,15 +473,15 @@ func (c *APIClient) ParseUserListResponse(userInfoResponse *[]UserResponse) (*[]
deviceLimit = user.DeviceLimit
}
if c.SpeedLimit > 0 {
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
} else {
speedlimit = uint64((user.SpeedLimit * 1000000) / 8)
speedLimit = uint64((user.SpeedLimit * 1000000) / 8)
}
userList[i] = api.UserInfo{
UID: user.ID,
Passwd: user.Passwd,
UUID: user.Passwd,
SpeedLimit: speedlimit,
SpeedLimit: speedLimit,
DeviceLimit: deviceLimit,
}
}

View File

@@ -85,7 +85,7 @@ func TestGetUserList(t *testing.T) {
func TestReportNodeStatus(t *testing.T) {
client := CreateClient()
nodeStatus := &api.NodeStatus{
1, 1, 1, 256,
CPU: 1, Mem: 1, Disk: 1, Uptime: 256,
}
err := client.ReportNodeStatus(nodeStatus)
if err != nil {
@@ -107,7 +107,7 @@ func TestReportReportNodeOnlineUsers(t *testing.T) {
IP: fmt.Sprintf("1.1.1.%d", i),
}
}
//client.Debug()
// client.Debug()
err = client.ReportNodeOnlineUsers(&onlineUserList)
if err != nil {
t.Error(err)
@@ -128,7 +128,7 @@ func TestReportReportUserTraffic(t *testing.T) {
Download: 114514,
}
}
//client.Debug()
// client.Debug()
err = client.ReportUserTraffic(&generalUserTraffic)
if err != nil {
t.Error(err)
@@ -150,8 +150,8 @@ func TestReportIllegal(t *testing.T) {
client := CreateClient()
detectResult := []api.DetectResult{
api.DetectResult{1, 2},
api.DetectResult{1, 3},
{1, 2},
{1, 3},
}
client.Debug()
err := client.ReportIllegal(&detectResult)

View File

@@ -49,7 +49,7 @@ type TrojanNodeInfo struct {
TrojanPort uint32 `json:"trojan_port"`
}
// Node status report
// NodeStatus Node status report
type NodeStatus struct {
CPU string `json:"cpu"`
Mem string `json:"mem"`
@@ -98,7 +98,6 @@ type NodeRuleItem struct {
Pattern string `json:"pattern"`
}
// IllegalReport
type IllegalReport struct {
UID int `json:"uid"`
RuleID int `json:"rule_id"`

View File

@@ -4,15 +4,17 @@ import (
"bufio"
"encoding/json"
"fmt"
"log"
"os"
"reflect"
"regexp"
"strconv"
"time"
"github.com/XrayR-project/XrayR/api"
log "github.com/sirupsen/logrus"
"github.com/go-resty/resty/v2"
"github.com/XrayR-project/XrayR/api"
)
// APIClient create a api client to the panel.
@@ -23,7 +25,7 @@ type APIClient struct {
Key string
NodeType string
EnableVless bool
EnableXTLS bool
VlessFlow string
SpeedLimit float64
DeviceLimit int
LocalRuleList []api.DetectRule
@@ -56,7 +58,7 @@ func New(apiConfig *api.Config) *APIClient {
APIHost: apiConfig.APIHost,
NodeType: apiConfig.NodeType,
EnableVless: apiConfig.EnableVless,
EnableXTLS: apiConfig.EnableXTLS,
VlessFlow: apiConfig.VlessFlow,
SpeedLimit: apiConfig.SpeedLimit,
DeviceLimit: apiConfig.DeviceLimit,
LocalRuleList: localRuleList,
@@ -72,7 +74,7 @@ func readLocalRuleList(path string) (LocalRuleList []api.DetectRule) {
// open the file
file, err := os.Open(path)
//handle errors while opening
// handle errors while opening
if err != nil {
log.Printf("Error when opening file: %s", err)
return LocalRuleList
@@ -90,7 +92,7 @@ func readLocalRuleList(path string) (LocalRuleList []api.DetectRule) {
// handle first encountered error while reading
if err := fileScanner.Err(); err != nil {
log.Fatalf("Error while reading file: %s", err)
return make([]api.DetectRule, 0)
return
}
file.Close()
@@ -134,7 +136,7 @@ func (c *APIClient) parseResponse(res *resty.Response, path string, err error) (
if response.Status != "success" {
res, _ := json.Marshal(&response)
return nil, fmt.Errorf("Ret %s invalid", string(res))
return nil, fmt.Errorf("ret %s invalid", string(res))
}
return response, nil
}
@@ -143,14 +145,14 @@ func (c *APIClient) parseResponse(res *resty.Response, path string, err error) (
func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
var path string
switch c.NodeType {
case "V2ray":
case "V2ray", "Vmess", "Vless":
path = fmt.Sprintf("/api/v2ray/v1/node/%d", c.NodeID)
case "Trojan":
path = fmt.Sprintf("/api/trojan/v1/node/%d", c.NodeID)
case "Shadowsocks":
path = fmt.Sprintf("/api/ss/v1/node/%d", c.NodeID)
default:
return nil, fmt.Errorf("Unsupported Node type: %s", c.NodeType)
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
}
res, err := c.createCommonRequest().
@@ -164,19 +166,19 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
}
switch c.NodeType {
case "V2ray":
case "V2ray", "Vmess", "Vless":
nodeInfo, err = c.ParseV2rayNodeResponse(&response.Data)
case "Trojan":
nodeInfo, err = c.ParseTrojanNodeResponse(&response.Data)
case "Shadowsocks":
nodeInfo, err = c.ParseSSNodeResponse(&response.Data)
default:
return nil, fmt.Errorf("Unsupported Node type: %s", c.NodeType)
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
}
if err != nil {
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
@@ -186,14 +188,14 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
var path string
switch c.NodeType {
case "V2ray":
case "V2ray", "Vmess", "Vless":
path = fmt.Sprintf("/api/v2ray/v1/userList/%d", c.NodeID)
case "Trojan":
path = fmt.Sprintf("/api/trojan/v1/userList/%d", c.NodeID)
case "Shadowsocks":
path = fmt.Sprintf("/api/ss/v1/userList/%d", c.NodeID)
default:
return nil, fmt.Errorf("Unsupported Node type: %s", c.NodeType)
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
}
res, err := c.createCommonRequest().
@@ -207,18 +209,18 @@ func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
}
userList := new([]api.UserInfo)
switch c.NodeType {
case "V2ray":
case "V2ray", "Vmess", "Vless":
userList, err = c.ParseV2rayUserListResponse(&response.Data)
case "Trojan":
userList, err = c.ParseTrojanUserListResponse(&response.Data)
case "Shadowsocks":
userList, err = c.ParseSSUserListResponse(&response.Data)
default:
return nil, fmt.Errorf("Unsupported Node type: %s", c.NodeType)
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
}
if err != nil {
res, _ := json.Marshal(response.Data)
return nil, fmt.Errorf("Parse user list failed: %s", string(res))
return nil, fmt.Errorf("parse user list failed: %s", string(res))
}
return userList, nil
}
@@ -227,14 +229,14 @@ func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
func (c *APIClient) ReportNodeStatus(nodeStatus *api.NodeStatus) (err error) {
var path string
switch c.NodeType {
case "V2ray":
case "V2ray", "Vmess", "Vless":
path = fmt.Sprintf("/api/v2ray/v1/nodeStatus/%d", c.NodeID)
case "Trojan":
path = fmt.Sprintf("/api/trojan/v1/nodeStatus/%d", c.NodeID)
case "Shadowsocks":
path = fmt.Sprintf("/api/ss/v1/nodeStatus/%d", c.NodeID)
default:
return fmt.Errorf("Unsupported Node type: %s", c.NodeType)
return fmt.Errorf("unsupported Node type: %s", c.NodeType)
}
systemload := NodeStatus{
@@ -258,19 +260,19 @@ func (c *APIClient) ReportNodeStatus(nodeStatus *api.NodeStatus) (err error) {
return nil
}
//ReportNodeOnlineUsers reports online user ip
// ReportNodeOnlineUsers reports online user ip
func (c *APIClient) ReportNodeOnlineUsers(onlineUserList *[]api.OnlineUser) error {
var path string
switch c.NodeType {
case "V2ray":
case "V2ray", "Vmess", "Vless":
path = fmt.Sprintf("/api/v2ray/v1/nodeOnline/%d", c.NodeID)
case "Trojan":
path = fmt.Sprintf("/api/trojan/v1/nodeOnline/%d", c.NodeID)
case "Shadowsocks":
path = fmt.Sprintf("/api/ss/v1/nodeOnline/%d", c.NodeID)
default:
return fmt.Errorf("Unsupported Node type: %s", c.NodeType)
return fmt.Errorf("unsupported Node type: %s", c.NodeType)
}
data := make([]NodeOnline, len(*onlineUserList))
@@ -296,14 +298,14 @@ func (c *APIClient) ReportNodeOnlineUsers(onlineUserList *[]api.OnlineUser) erro
func (c *APIClient) ReportUserTraffic(userTraffic *[]api.UserTraffic) error {
var path string
switch c.NodeType {
case "V2ray":
case "V2ray", "Vmess", "Vless":
path = fmt.Sprintf("/api/v2ray/v1/userTraffic/%d", c.NodeID)
case "Trojan":
path = fmt.Sprintf("/api/trojan/v1/userTraffic/%d", c.NodeID)
case "Shadowsocks":
path = fmt.Sprintf("/api/ss/v1/userTraffic/%d", c.NodeID)
default:
return fmt.Errorf("Unsupported Node type: %s", c.NodeType)
return fmt.Errorf("unsupported Node type: %s", c.NodeType)
}
data := make([]UserTraffic, len(*userTraffic))
@@ -331,14 +333,14 @@ func (c *APIClient) ReportUserTraffic(userTraffic *[]api.UserTraffic) error {
func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
var path string
switch c.NodeType {
case "V2ray":
case "V2ray", "Vmess", "Vless":
path = fmt.Sprintf("/api/v2ray/v1/nodeRule/%d", c.NodeID)
case "Trojan":
path = fmt.Sprintf("/api/trojan/v1/nodeRule/%d", c.NodeID)
case "Shadowsocks":
path = fmt.Sprintf("/api/ss/v1/nodeRule/%d", c.NodeID)
default:
return nil, fmt.Errorf("Unsupported Node type: %s", c.NodeType)
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
}
res, err := c.createCommonRequest().
@@ -354,7 +356,7 @@ func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
ruleListResponse := new(NodeRule)
if err := json.Unmarshal(response.Data, ruleListResponse); err != nil {
return nil, fmt.Errorf("Unmarshal %s failed: %s", reflect.TypeOf(ruleListResponse), err)
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(ruleListResponse), err)
}
ruleList := c.LocalRuleList
// Only support reject rule type
@@ -379,14 +381,14 @@ func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
func (c *APIClient) ReportIllegal(detectResultList *[]api.DetectResult) error {
var path string
switch c.NodeType {
case "V2ray":
case "V2ray", "Vmess", "Vless":
path = fmt.Sprintf("/api/v2ray/v1/trigger/%d", c.NodeID)
case "Trojan":
path = fmt.Sprintf("/api/trojan/v1/trigger/%d", c.NodeID)
case "Shadowsocks":
path = fmt.Sprintf("/api/ss/v1/trigger/%d", c.NodeID)
default:
return fmt.Errorf("Unsupported Node type: %s", c.NodeType)
return fmt.Errorf("unsupported Node type: %s", c.NodeType)
}
for _, r := range *detectResultList {
@@ -411,23 +413,17 @@ func (c *APIClient) ReportIllegal(detectResultList *[]api.DetectResult) error {
// ParseV2rayNodeResponse parse the response for the given nodeinfor format
func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *json.RawMessage) (*api.NodeInfo, error) {
var TLStype string
var speedlimit uint64 = 0
if c.EnableXTLS {
TLStype = "xtls"
} else {
TLStype = "tls"
}
var speedLimit uint64 = 0
v2rayNodeInfo := new(V2rayNodeInfo)
if err := json.Unmarshal(*nodeInfoResponse, v2rayNodeInfo); 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 {
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
} else {
speedlimit = uint64((v2rayNodeInfo.SpeedLimit * 1000000) / 8)
speedLimit = (v2rayNodeInfo.SpeedLimit * 1000000) / 8
}
if c.DeviceLimit == 0 && v2rayNodeInfo.ClientLimit > 0 {
@@ -435,72 +431,65 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *json.RawMessage) (*
}
// Create GeneralNodeInfo
nodeinfo := &api.NodeInfo{
nodeInfo := &api.NodeInfo{
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: v2rayNodeInfo.V2Port,
SpeedLimit: speedlimit,
SpeedLimit: speedLimit,
AlterID: v2rayNodeInfo.V2AlterID,
TransportProtocol: v2rayNodeInfo.V2Net,
FakeType: v2rayNodeInfo.V2Type,
EnableTLS: v2rayNodeInfo.V2TLS,
TLSType: TLStype,
Path: v2rayNodeInfo.V2Path,
Host: v2rayNodeInfo.V2Host,
EnableVless: c.EnableVless,
VlessFlow: c.VlessFlow,
}
return nodeinfo, nil
return nodeInfo, nil
}
// ParseSSNodeResponse parse the response for the given nodeinfor format
func (c *APIClient) ParseSSNodeResponse(nodeInfoResponse *json.RawMessage) (*api.NodeInfo, error) {
var speedlimit uint64 = 0
var speedLimit uint64 = 0
shadowsocksNodeInfo := new(ShadowsocksNodeInfo)
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 {
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
} else {
speedlimit = uint64((shadowsocksNodeInfo.SpeedLimit * 1000000) / 8)
speedLimit = uint64((shadowsocksNodeInfo.SpeedLimit * 1000000) / 8)
}
if c.DeviceLimit == 0 && shadowsocksNodeInfo.ClientLimit > 0 {
c.DeviceLimit = shadowsocksNodeInfo.ClientLimit
}
// Create GeneralNodeInfo
nodeinfo := &api.NodeInfo{
nodeInfo := &api.NodeInfo{
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: shadowsocksNodeInfo.Port,
SpeedLimit: speedlimit,
SpeedLimit: speedLimit,
TransportProtocol: "tcp",
CypherMethod: shadowsocksNodeInfo.Method,
}
return nodeinfo, nil
return nodeInfo, nil
}
// ParseTrojanNodeResponse parse the response for the given nodeinfor format
func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *json.RawMessage) (*api.NodeInfo, error) {
var TLSType string
var speedlimit uint64 = 0
if c.EnableXTLS {
TLSType = "xtls"
} else {
TLSType = "tls"
}
var speedLimit uint64 = 0
trojanNodeInfo := new(TrojanNodeInfo)
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 {
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
} else {
speedlimit = uint64((trojanNodeInfo.SpeedLimit * 1000000) / 8)
speedLimit = (trojanNodeInfo.SpeedLimit * 1000000) / 8
}
if c.DeviceLimit == 0 && trojanNodeInfo.ClientLimit > 0 {
@@ -508,41 +497,40 @@ func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *json.RawMessage) (
}
// Create GeneralNodeInfo
nodeinfo := &api.NodeInfo{
nodeInfo := &api.NodeInfo{
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: trojanNodeInfo.TrojanPort,
SpeedLimit: speedlimit,
SpeedLimit: speedLimit,
TransportProtocol: "tcp",
EnableTLS: true,
TLSType: TLSType,
}
return nodeinfo, nil
return nodeInfo, nil
}
// ParseV2rayUserListResponse parse the response for the given userinfo format
func (c *APIClient) ParseV2rayUserListResponse(userInfoResponse *json.RawMessage) (*[]api.UserInfo, error) {
var speedlimit uint64 = 0
var speedLimit uint64 = 0
vmessUserList := new([]*VMessUser)
if err := json.Unmarshal(*userInfoResponse, vmessUserList); err != nil {
return nil, fmt.Errorf("Unmarshal %s failed: %s", reflect.TypeOf(*userInfoResponse), err)
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(*userInfoResponse), err)
}
userList := make([]api.UserInfo, len(*vmessUserList))
for i, user := range *vmessUserList {
if c.SpeedLimit > 0 {
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
} else {
speedlimit = uint64((user.SpeedLimit * 1000000) / 8)
speedLimit = (user.SpeedLimit * 1000000) / 8
}
userList[i] = api.UserInfo{
UID: user.UID,
Email: "",
UUID: user.VmessUID,
DeviceLimit: c.DeviceLimit,
SpeedLimit: speedlimit,
SpeedLimit: speedLimit,
}
}
@@ -551,26 +539,26 @@ func (c *APIClient) ParseV2rayUserListResponse(userInfoResponse *json.RawMessage
// ParseTrojanUserListResponse parse the response for the given userinfo format
func (c *APIClient) ParseTrojanUserListResponse(userInfoResponse *json.RawMessage) (*[]api.UserInfo, error) {
var speedlimit uint64 = 0
var speedLimit uint64 = 0
trojanUserList := new([]*TrojanUser)
if err := json.Unmarshal(*userInfoResponse, trojanUserList); err != nil {
return nil, fmt.Errorf("Unmarshal %s failed: %s", reflect.TypeOf(*userInfoResponse), err)
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(*userInfoResponse), err)
}
userList := make([]api.UserInfo, len(*trojanUserList))
for i, user := range *trojanUserList {
if c.SpeedLimit > 0 {
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
} else {
speedlimit = uint64((user.SpeedLimit * 1000000) / 8)
speedLimit = (user.SpeedLimit * 1000000) / 8
}
userList[i] = api.UserInfo{
UID: user.UID,
Email: "",
UUID: user.Password,
DeviceLimit: c.DeviceLimit,
SpeedLimit: speedlimit,
SpeedLimit: speedLimit,
}
}
@@ -579,26 +567,26 @@ func (c *APIClient) ParseTrojanUserListResponse(userInfoResponse *json.RawMessag
// ParseSSUserListResponse parse the response for the given userinfo format
func (c *APIClient) ParseSSUserListResponse(userInfoResponse *json.RawMessage) (*[]api.UserInfo, error) {
var speedlimit uint64 = 0
var speedLimit uint64 = 0
ssUserList := new([]*SSUser)
if err := json.Unmarshal(*userInfoResponse, ssUserList); err != nil {
return nil, fmt.Errorf("Unmarshal %s failed: %s", reflect.TypeOf(*userInfoResponse), err)
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(*userInfoResponse), err)
}
userList := make([]api.UserInfo, len(*ssUserList))
for i, user := range *ssUserList {
if c.SpeedLimit > 0 {
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
} else {
speedlimit = uint64((user.SpeedLimit * 1000000) / 8)
speedLimit = uint64(user.SpeedLimit * 1000000 / 8)
}
userList[i] = api.UserInfo{
UID: user.UID,
Email: "",
Passwd: user.Password,
DeviceLimit: c.DeviceLimit,
SpeedLimit: speedlimit,
SpeedLimit: speedLimit,
}
}

View File

@@ -89,7 +89,7 @@ func TestGetUserList(t *testing.T) {
func TestReportNodeStatus(t *testing.T) {
client := CreateClient()
nodeStatus := &api.NodeStatus{
1, 1, 1, 256,
CPU: 1, Mem: 1, Disk: 1, Uptime: 256,
}
err := client.ReportNodeStatus(nodeStatus)
if err != nil {
@@ -111,7 +111,7 @@ func TestReportReportNodeOnlineUsers(t *testing.T) {
IP: fmt.Sprintf("1.1.1.%d", i),
}
}
//client.Debug()
// client.Debug()
err = client.ReportNodeOnlineUsers(&onlineUserList)
if err != nil {
t.Error(err)
@@ -154,8 +154,8 @@ func TestReportIllegal(t *testing.T) {
client := CreateClient()
detectResult := []api.DetectResult{
api.DetectResult{1, 1},
api.DetectResult{1, 2},
{UID: 1, RuleID: 1},
{UID: 1, RuleID: 2},
}
client.Debug()
err := client.ReportIllegal(&detectResult)

View File

@@ -8,7 +8,6 @@ type NodeInfoResponse struct {
Class int `json:"node_class"`
SpeedLimit float64 `json:"node_speedlimit"`
TrafficRate float64 `json:"traffic_rate"`
MuOnly int `json:"mu_only"`
Sort int `json:"sort"`
RawServerString string `json:"server"`
Type string `json:"type"`
@@ -17,51 +16,35 @@ type NodeInfoResponse struct {
}
type CustomConfig struct {
OffsetPortUser string `json:"offset_port_user"`
OffsetPortNode string `json:"offset_port_node"`
ServerSub string `json:"server_sub"`
Host string `json:"host"`
MuPort string `json:"mu_port"`
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"`
Method string `json:"method"`
TLS string `json:"tls"`
EnableVless string `json:"enable_vless"`
AlterID string `json:"alter_id"`
Network string `json:"network"`
Security string `json:"security"`
Path string `json:"path"`
VerifyCert bool `json:"verify_cert"`
Obfs string `json:"obfs"`
Header json.RawMessage `json:"header"`
TrojanPort string `json:"trojan_port"`
AllowInsecure string `json:"allow_insecure"`
Grpc string `json:"grpc"`
Servicename string `json:"servicename"`
EnableXtls string `json:"enable_xtls"`
Flow string `json:"flow"`
EnableREALITY bool `json:"enable_reality"`
RealityOpts *REALITYConfig `json:"reality-opts"`
}
// UserResponse is the response of user
type UserResponse struct {
ID int `json:"id"`
Email string `json:"email"`
Passwd string `json:"passwd"`
Port uint32 `json:"port"`
Method string `json:"method"`
SpeedLimit float64 `json:"node_speedlimit"`
DeviceLimit int `json:"node_connector"`
Protocol string `json:"protocol"`
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"`
ID int `json:"id"`
Passwd string `json:"passwd"`
Port uint32 `json:"port"`
Method string `json:"method"`
SpeedLimit float64 `json:"node_speedlimit"`
DeviceLimit int `json:"node_iplimit"`
UUID string `json:"uuid"`
AliveIP int `json:"alive_ip"`
}
// Response is the common response
@@ -75,7 +58,7 @@ type PostData struct {
Data interface{} `json:"data"`
}
// SystemLoad is the data structure of systemload
// SystemLoad is the data structure of system load
type SystemLoad struct {
Uptime string `json:"uptime"`
Load string `json:"load"`
@@ -103,3 +86,14 @@ type IllegalItem struct {
ID int `json:"list_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 (
"bufio"
"encoding/json"
"errors"
"fmt"
"log"
"os"
"reflect"
"regexp"
@@ -13,14 +13,17 @@ import (
"sync"
"time"
"github.com/XrayR-project/XrayR/api"
log "github.com/sirupsen/logrus"
"github.com/go-resty/resty/v2"
"github.com/XrayR-project/XrayR/api"
)
var (
firstPortRe = regexp.MustCompile(`(?m)port=(?P<outport>\d+)#?`) // First Port
secondPortRe = regexp.MustCompile(`(?m)port=\d+#(\d+)`) // Second Port
hostRe = regexp.MustCompile(`(?m)host=([\w\.]+)\|?`) // Host
hostRe = regexp.MustCompile(`(?m)host=([\w.]+)\|?`) // Host
)
// APIClient create a api client to the panel.
@@ -31,19 +34,21 @@ type APIClient struct {
Key string
NodeType string
EnableVless bool
EnableXTLS bool
VlessFlow string
SpeedLimit float64
DeviceLimit int
DisableCustomConfig bool
LocalRuleList []api.DetectRule
LastReportOnline map[int]int
access sync.Mutex
version string
eTags map[string]string
}
// New creat a api instance
// New create 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)
@@ -51,12 +56,14 @@ func New(apiConfig *api.Config) *APIClient {
client.SetTimeout(5 * time.Second)
}
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.Err contains the original error
log.Print(v.Err)
}
})
client.SetBaseURL(apiConfig.APIHost)
// Create Key for each requests
client.SetQueryParam("key", apiConfig.Key)
@@ -72,24 +79,30 @@ func New(apiConfig *api.Config) *APIClient {
APIHost: apiConfig.APIHost,
NodeType: apiConfig.NodeType,
EnableVless: apiConfig.EnableVless,
EnableXTLS: apiConfig.EnableXTLS,
VlessFlow: apiConfig.VlessFlow,
SpeedLimit: apiConfig.SpeedLimit,
DeviceLimit: apiConfig.DeviceLimit,
LocalRuleList: localRuleList,
DisableCustomConfig: apiConfig.DisableCustomConfig,
LastReportOnline: make(map[int]int),
eTags: make(map[string]string),
}
}
// 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
defer func(file *os.File) {
err := file.Close()
if err != nil {
log.Printf("Error when closing file: %s", err)
}
}(file)
// handle errors while opening
if err != nil {
log.Printf("Error when opening file: %s", err)
return LocalRuleList
@@ -107,10 +120,8 @@ func readLocalRuleList(path string) (LocalRuleList []api.DetectRule) {
// handle first encountered error while reading
if err := fileScanner.Err(); err != nil {
log.Fatalf("Error while reading file: %s", err)
return make([]api.DetectRule, 0)
return
}
file.Close()
}
return LocalRuleList
@@ -137,24 +148,33 @@ func (c *APIClient) parseResponse(res *resty.Response, path string, err error) (
if res.StatusCode() > 400 {
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)
if response.Ret != 1 {
res, _ := json.Marshal(&response)
return nil, fmt.Errorf("Ret %s invalid", string(res))
return nil, fmt.Errorf("ret %s invalid", string(res))
}
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) {
path := fmt.Sprintf("/mod_mu/nodes/%d/info", 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 {
@@ -164,28 +184,21 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
nodeInfoResponse := new(NodeInfoResponse)
if err := json.Unmarshal(response.Data, nodeInfoResponse); 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)
}
// New sspanel API
disableCustomConfig := c.DisableCustomConfig
if nodeInfoResponse.Version == "2021.11" && !disableCustomConfig {
// Check if custom_config is empty
if configString, err := json.Marshal(nodeInfoResponse.CustomConfig); err != nil || string(configString) == "[]" {
log.Printf("custom_config is empty! take config from address now.")
disableCustomConfig = true
}
} else {
disableCustomConfig = true
// determine ssPanel version, if disable custom config or version < 2021.11, then use old api
c.version = nodeInfoResponse.Version
var isExpired bool
if compareVersion(c.version, "2021.11") == -1 {
isExpired = true
}
if !disableCustomConfig {
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 c.DisableCustomConfig || isExpired {
if isExpired {
log.Print("The panel version is expired, it is recommended to update immediately")
}
} else {
switch c.NodeType {
case "V2ray":
nodeInfo, err = c.ParseV2rayNodeResponse(nodeInfoResponse)
@@ -196,26 +209,41 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
case "Shadowsocks-Plugin":
nodeInfo, err = c.ParseSSPluginNodeResponse(nodeInfoResponse)
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 {
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
}
// GetUserList will pull user form sspanel
// GetUserList will pull user form ssPanel
func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
path := "/mod_mu/users"
res, err := c.client.R().
SetQueryParam("node_id", 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 {
@@ -225,39 +253,41 @@ func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
userListResponse := new([]UserResponse)
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)
}
userList, err := c.ParseUserListResponse(userListResponse)
if err != nil {
res, _ := json.Marshal(userListResponse)
return nil, fmt.Errorf("Parse user list failed: %s", string(res))
return nil, fmt.Errorf("parse user list failed: %s", string(res))
}
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) {
path := fmt.Sprintf("/mod_mu/nodes/%d/info", c.NodeID)
systemload := SystemLoad{
Uptime: strconv.FormatUint(nodeStatus.Uptime, 10),
Load: fmt.Sprintf("%.2f %.2f %.2f", nodeStatus.CPU/100, nodeStatus.CPU/100, nodeStatus.CPU/100),
// Determine whether a status report is in need
if compareVersion(c.version, "2023.2") == -1 {
path := fmt.Sprintf("/mod_mu/nodes/%d/info", c.NodeID)
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
}
//ReportNodeOnlineUsers reports online user ip
// ReportNodeOnlineUsers reports online user ip
func (c *APIClient) ReportNodeOnlineUsers(onlineUserList *[]api.OnlineUser) error {
c.access.Lock()
defer c.access.Unlock()
@@ -266,16 +296,12 @@ func (c *APIClient) ReportNodeOnlineUsers(onlineUserList *[]api.OnlineUser) erro
data := make([]OnlineUser, len(*onlineUserList))
for i, user := range *onlineUserList {
data[i] = OnlineUser{UID: user.UID, IP: user.IP}
if _, ok := reportOnline[user.UID]; ok {
reportOnline[user.UID]++
} else {
reportOnline[user.UID] = 1
}
reportOnline[user.UID]++ // will start from 1 if key doesnt exist
}
c.LastReportOnline = reportOnline // Update LastReportOnline
postData := &PostData{Data: data}
path := fmt.Sprintf("/mod_mu/users/aliveip")
path := "/mod_mu/users/aliveip"
res, err := c.client.R().
SetQueryParam("node_id", strconv.Itoa(c.NodeID)).
SetBody(postData).
@@ -317,15 +343,25 @@ func (c *APIClient) ReportUserTraffic(userTraffic *[]api.UserTraffic) error {
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) {
ruleList := c.LocalRuleList
path := "/mod_mu/func/detect_rules"
res, err := c.client.R().
SetResult(&Response{}).
SetHeader("If-None-Match", c.eTags["rules"]).
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.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)
if err != nil {
return nil, err
@@ -334,7 +370,7 @@ func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
ruleListResponse := new([]RuleItem)
if err := json.Unmarshal(response.Data, ruleListResponse); err != nil {
return nil, fmt.Errorf("Unmarshal %s failed: %s", reflect.TypeOf(ruleListResponse), err)
return nil, fmt.Errorf("unmarshal %s failed: %s", reflect.TypeOf(ruleListResponse), err)
}
for _, r := range *ruleListResponse {
@@ -371,16 +407,16 @@ func (c *APIClient) ReportIllegal(detectResultList *[]api.DetectResult) error {
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) {
var enableTLS bool
var path, host, TLStype, transportProtocol, serviceName, HeaderType string
var path, host, transportProtocol, serviceName, HeaderType string
var header json.RawMessage
var speedlimit uint64 = 0
var speedLimit uint64 = 0
if nodeInfoResponse.RawServerString == "" {
return nil, fmt.Errorf("No server info in response")
return nil, fmt.Errorf("no server info in response")
}
//nodeInfo.RawServerString = strings.ToLower(nodeInfo.RawServerString)
// nodeInfo.RawServerString = strings.ToLower(nodeInfo.RawServerString)
serverConf := strings.Split(nodeInfoResponse.RawServerString, ";")
parsedPort, err := strconv.ParseInt(serverConf[1], 10, 32)
@@ -398,12 +434,7 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *NodeInfoResponse) (
// Compatible with more node types config
for _, value := range serverConf[3:5] {
switch value {
case "tls", "xtls":
if c.EnableXTLS {
TLStype = "xtls"
} else {
TLStype = "tls"
}
case "tls":
enableTLS = true
default:
if value != "" {
@@ -428,14 +459,14 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *NodeInfoResponse) (
host = value
case "servicename":
serviceName = value
case "headertype":
case "headerType":
HeaderType = value
}
}
if c.SpeedLimit > 0 {
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
} else {
speedlimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
speedLimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
}
if HeaderType != "" {
@@ -444,33 +475,33 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *NodeInfoResponse) (
}
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
nodeinfo := &api.NodeInfo{
nodeInfo := &api.NodeInfo{
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: port,
SpeedLimit: speedlimit,
SpeedLimit: speedLimit,
AlterID: alterID,
TransportProtocol: transportProtocol,
EnableTLS: enableTLS,
TLSType: TLStype,
Path: path,
Host: host,
EnableVless: c.EnableVless,
VlessFlow: c.VlessFlow,
ServiceName: serviceName,
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) {
var port uint32 = 0
var speedlimit uint64 = 0
var speedLimit uint64 = 0
var method string
path := "/mod_mu/users"
res, err := c.client.R().
@@ -487,43 +518,37 @@ func (c *APIClient) ParseSSNodeResponse(nodeInfoResponse *NodeInfoResponse) (*ap
userListResponse := new([]UserResponse)
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 {
if u.MultiUser > 0 {
port = u.Port
method = u.Method
break
}
}
if port == 0 || method == "" {
return nil, fmt.Errorf("Cant find the single port multi user")
// init server port
if len(*userListResponse) != 0 {
port = (*userListResponse)[0].Port
}
if c.SpeedLimit > 0 {
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
} else {
speedlimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
speedLimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
}
// Create GeneralNodeInfo
nodeinfo := &api.NodeInfo{
nodeInfo := &api.NodeInfo{
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: port,
SpeedLimit: speedlimit,
SpeedLimit: speedLimit,
TransportProtocol: "tcp",
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) {
var enableTLS bool
var path, host, TLStype, transportProtocol string
var speedlimit uint64 = 0
var path, host, transportProtocol string
var speedLimit uint64 = 0
serverConf := strings.Split(nodeInfoResponse.RawServerString, ";")
parsedPort, err := strconv.ParseInt(serverConf[1], 10, 32)
@@ -538,12 +563,7 @@ func (c *APIClient) ParseSSPluginNodeResponse(nodeInfoResponse *NodeInfoResponse
// Compatible with more node types config
for _, value := range serverConf[3:5] {
switch value {
case "tls", "xtls":
if c.EnableXTLS {
TLStype = "xtls"
} else {
TLStype = "tls"
}
case "tls":
enableTLS = true
case "ws":
transportProtocol = "ws"
@@ -569,41 +589,35 @@ func (c *APIClient) ParseSSPluginNodeResponse(nodeInfoResponse *NodeInfoResponse
}
}
if c.SpeedLimit > 0 {
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
} else {
speedlimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
speedLimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
}
// Create GeneralNodeInfo
nodeinfo := &api.NodeInfo{
nodeInfo := &api.NodeInfo{
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: port,
SpeedLimit: speedlimit,
SpeedLimit: speedLimit,
TransportProtocol: transportProtocol,
EnableTLS: enableTLS,
TLSType: TLStype,
Path: path,
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) {
// 域名或IP;port=连接端口#偏移端口|host=xx
// gz.aaa.com;port=443#12345|host=hk.aaa.com
var p, TLSType, host, outsidePort, insidePort, transportProtocol, serviceName string
var speedlimit uint64 = 0
if c.EnableXTLS {
TLSType = "xtls"
} else {
TLSType = "tls"
}
var p, host, outsidePort, insidePort, transportProtocol, serviceName string
var speedLimit uint64 = 0
if nodeInfoResponse.RawServerString == "" {
return nil, fmt.Errorf("No server info in response")
return nil, fmt.Errorf("no server info in response")
}
if result := firstPortRe.FindStringSubmatch(nodeInfoResponse.RawServerString); len(result) > 1 {
outsidePort = result[1]
@@ -647,27 +661,26 @@ func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *NodeInfoResponse)
}
if c.SpeedLimit > 0 {
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
} else {
speedlimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
speedLimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
}
// Create GeneralNodeInfo
nodeinfo := &api.NodeInfo{
nodeInfo := &api.NodeInfo{
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: port,
SpeedLimit: speedlimit,
SpeedLimit: speedLimit,
TransportProtocol: transportProtocol,
EnableTLS: true,
TLSType: TLSType,
Host: host,
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) {
c.access.Lock()
// Clear Last report log
@@ -676,9 +689,9 @@ func (c *APIClient) ParseUserListResponse(userInfoResponse *[]UserResponse) (*[]
c.access.Unlock()
}()
var deviceLimit, localDeviceLimit int = 0, 0
var speedlimit uint64 = 0
userList := []api.UserInfo{}
var deviceLimit, localDeviceLimit = 0, 0
var speedLimit uint64 = 0
var userList []api.UserInfo
for _, user := range *userInfoResponse {
if c.DeviceLimit > 0 {
deviceLimit = c.DeviceLimit
@@ -705,111 +718,142 @@ func (c *APIClient) ParseUserListResponse(userInfoResponse *[]UserResponse) (*[]
}
if c.SpeedLimit > 0 {
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
} else {
speedlimit = uint64((user.SpeedLimit * 1000000) / 8)
speedLimit = uint64((user.SpeedLimit * 1000000) / 8)
}
userList = append(userList, api.UserInfo{
UID: user.ID,
Email: user.Email,
UUID: user.UUID,
Passwd: user.Passwd,
SpeedLimit: speedlimit,
DeviceLimit: deviceLimit,
Port: user.Port,
Method: user.Method,
Protocol: user.Protocol,
ProtocolParam: user.ProtocolParam,
Obfs: user.Obfs,
ObfsParam: user.ObfsParam,
UID: user.ID,
UUID: user.UUID,
Passwd: user.Passwd,
SpeedLimit: speedLimit,
DeviceLimit: deviceLimit,
Port: user.Port,
Method: user.Method,
})
}
return &userList, nil
}
// ParseSSPanelNodeInfo parse the response for the given nodeinfor format
// Only used for SSPanel version >= 2021.11
// ParseSSPanelNodeInfo parse the response for the given node info format
// Only available for SSPanel version >= 2021.11
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
var EnableTLS, EnableVless bool
var AlterID uint16 = 0
var TLSType, transportProtocol string
// Check if custom_config is null
if len(nodeInfoResponse.CustomConfig) == 0 {
return nil, errors.New("custom_config is empty, disable custom config")
}
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 {
speedlimit = uint64((c.SpeedLimit * 1000000) / 8)
speedLimit = uint64((c.SpeedLimit * 1000000) / 8)
} else {
speedlimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
speedLimit = uint64((nodeInfoResponse.SpeedLimit * 1000000) / 8)
}
parsedPort, err := strconv.ParseInt(nodeConfig.OffsetPortNode, 10, 32)
if err != nil {
return nil, err
}
port := uint32(parsedPort)
if c.NodeType == "Shadowsocks" {
switch c.NodeType {
case "Shadowsocks":
transportProtocol = "tcp"
}
if c.NodeType == "V2ray" {
case "V2ray":
transportProtocol = nodeConfig.Network
TLSType = nodeConfig.Security
if parsedAlterID, err := strconv.ParseInt(nodeConfig.AlterID, 10, 16); err != nil {
return nil, err
} else {
AlterID = uint16(parsedAlterID)
tlsType := nodeConfig.Security
if tlsType == "tls" || tlsType == "xtls" {
enableTLS = true
}
if TLSType == "tls" || TLSType == "xtls" {
EnableTLS = true
}
if nodeConfig.EnableVless == "1" {
EnableVless = true
}
}
if c.NodeType == "Trojan" {
EnableTLS = true
TLSType = nodeConfig.Security // try to read security from config
if nodeConfig.EnableXtls == "1" {
TLSType = "xtls"
}
if TLSType == "" {
TLSType = "tls" // default
enableVless = true
}
case "Trojan":
enableTLS = true
transportProtocol = "tcp"
// Select transport protocol
transportProtocol = nodeConfig.Network // try to read transport protocol from config
if nodeConfig.Grpc == "1" {
transportProtocol = "grpc"
}
if nodeConfig.Network != "" {
transportProtocol = "tcp" // default
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
nodeinfo := &api.NodeInfo{
nodeInfo := &api.NodeInfo{
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: port,
SpeedLimit: speedlimit,
AlterID: AlterID,
SpeedLimit: speedLimit,
AlterID: alterID,
TransportProtocol: transportProtocol,
Host: nodeConfig.Host,
Path: nodeConfig.Path,
EnableTLS: EnableTLS,
TLSType: TLSType,
EnableVless: EnableVless,
CypherMethod: nodeConfig.MuEncryption,
EnableTLS: enableTLS,
EnableVless: enableVless,
VlessFlow: nodeConfig.Flow,
CypherMethod: nodeConfig.Method,
ServiceName: nodeConfig.Servicename,
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
}
func TestGetV2rayNodeinfo(t *testing.T) {
func TestGetV2rayNodeInfo(t *testing.T) {
client := CreateClient()
nodeInfo, err := client.GetNodeInfo()
@@ -29,7 +29,7 @@ func TestGetV2rayNodeinfo(t *testing.T) {
t.Log(nodeInfo)
}
func TestGetSSNodeinfo(t *testing.T) {
func TestGetSSNodeInfo(t *testing.T) {
apiConfig := &api.Config{
APIHost: "http://127.0.0.1:667",
Key: "123",
@@ -44,7 +44,7 @@ func TestGetSSNodeinfo(t *testing.T) {
t.Log(nodeInfo)
}
func TestGetTrojanNodeinfo(t *testing.T) {
func TestGetTrojanNodeInfo(t *testing.T) {
apiConfig := &api.Config{
APIHost: "http://127.0.0.1:667",
Key: "123",
@@ -59,7 +59,7 @@ func TestGetTrojanNodeinfo(t *testing.T) {
t.Log(nodeInfo)
}
func TestGetSSinfo(t *testing.T) {
func TestGetSSInfo(t *testing.T) {
client := CreateClient()
nodeInfo, err := client.GetNodeInfo()
@@ -83,7 +83,7 @@ func TestGetUserList(t *testing.T) {
func TestReportNodeStatus(t *testing.T) {
client := CreateClient()
nodeStatus := &api.NodeStatus{
1, 1, 1, 256,
CPU: 1, Mem: 1, Disk: 1, Uptime: 256,
}
err := client.ReportNodeStatus(nodeStatus)
if err != nil {
@@ -105,7 +105,7 @@ func TestReportReportNodeOnlineUsers(t *testing.T) {
IP: fmt.Sprintf("1.1.1.%d", i),
}
}
//client.Debug()
// client.Debug()
err = client.ReportNodeOnlineUsers(&onlineUserList)
if err != nil {
t.Error(err)
@@ -126,7 +126,7 @@ func TestReportReportUserTraffic(t *testing.T) {
Download: 114514,
}
}
//client.Debug()
// client.Debug()
err = client.ReportUserTraffic(&generalUserTraffic)
if err != nil {
t.Error(err)
@@ -148,8 +148,8 @@ func TestReportIllegal(t *testing.T) {
client := CreateClient()
detectResult := []api.DetectResult{
api.DetectResult{1, 2},
api.DetectResult{1, 3},
{UID: 1, RuleID: 2},
{UID: 1, RuleID: 3},
}
client.Debug()
err := client.ReportIllegal(&detectResult)

View File

@@ -1,7 +0,0 @@
package v2board
type UserTraffic struct {
UID int `json:"user_id"`
Upload int64 `json:"u"`
Download int64 `json:"d"`
}

View File

@@ -1,421 +0,0 @@
package v2board
import (
"bufio"
"encoding/json"
"fmt"
"log"
"os"
"regexp"
"strconv"
"strings"
"sync"
"time"
"github.com/XrayR-project/XrayR/api"
"github.com/bitly/go-simplejson"
"github.com/go-resty/resty/v2"
)
// 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 make([]api.DetectRule, 0)
}
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
}
// 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
type UserTraffic struct {
UID int `json:"user_id"`
UID int `json:"uid"`
Upload int64 `json:"u"`
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 (
"bufio"
"encoding/json"
"errors"
"fmt"
"log"
"os"
"regexp"
"strconv"
@@ -12,9 +12,14 @@ import (
"sync"
"time"
"github.com/XrayR-project/XrayR/api"
log "github.com/sirupsen/logrus"
"github.com/bitly/go-simplejson"
"github.com/go-resty/resty/v2"
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
C "github.com/sagernet/sing/common"
"github.com/XrayR-project/XrayR/api"
)
// APIClient create an api client to the panel.
@@ -25,12 +30,13 @@ type APIClient struct {
Key string
NodeType string
EnableVless bool
EnableXTLS bool
VlessFlow string
SpeedLimit float64
DeviceLimit int
LocalRuleList []api.DetectRule
ConfigResp *simplejson.Json
access sync.Mutex
eTags map[string]string
}
// New create an api instance
@@ -43,13 +49,16 @@ func New(apiConfig *api.Config) *APIClient {
} else {
client.SetTimeout(5 * time.Second)
}
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.Err contains the original error
log.Print(v.Err)
}
})
// Create Key for each requests
client.SetQueryParams(map[string]string{
"node_id": strconv.Itoa(apiConfig.NodeID),
@@ -64,10 +73,11 @@ func New(apiConfig *api.Config) *APIClient {
APIHost: apiConfig.APIHost,
NodeType: apiConfig.NodeType,
EnableVless: apiConfig.EnableVless,
EnableXTLS: apiConfig.EnableXTLS,
VlessFlow: apiConfig.VlessFlow,
SpeedLimit: apiConfig.SpeedLimit,
DeviceLimit: apiConfig.DeviceLimit,
LocalRuleList: localRuleList,
eTags: make(map[string]string),
}
return apiClient
}
@@ -80,7 +90,7 @@ func readLocalRuleList(path string) (LocalRuleList []api.DetectRule) {
// open the file
file, err := os.Open(path)
//handle errors while opening
// handle errors while opening
if err != nil {
log.Printf("Error when opening file: %s", err)
return LocalRuleList
@@ -98,7 +108,7 @@ func readLocalRuleList(path string) (LocalRuleList []api.DetectRule) {
// handle first encountered error while reading
if err := fileScanner.Err(); err != nil {
log.Fatalf("Error while reading file: %s", err)
return make([]api.DetectRule, 0)
return
}
file.Close()
@@ -132,28 +142,38 @@ func (c *APIClient) parseResponse(res *resty.Response, path string, err error) (
}
rtn, err := simplejson.NewJson(res.Body())
if err != nil {
return nil, fmt.Errorf("Ret %s invalid", res.String())
return nil, fmt.Errorf("ret %s invalid", res.String())
}
return rtn, nil
}
// GetNodeInfo will pull NodeInfo Config from sspanel
// GetNodeInfo will pull NodeInfo Config from panel
func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
var nodeType string
switch c.NodeType {
case "V2ray", "Trojan", "Shadowsocks":
case "V2ray", "Vmess", "Vless", "Trojan", "Shadowsocks":
nodeType = strings.ToLower(c.NodeType)
default:
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
}
res, err := c.client.R().
SetHeader("If-None-Match", c.eTags["config"]).
SetQueryParams(map[string]string{
"act": "config",
"nodetype": nodeType,
"act": "config",
"node_type": nodeType,
}).
ForceContentType("application/json").
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)
c.access.Lock()
defer c.access.Unlock()
@@ -163,7 +183,7 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
}
switch c.NodeType {
case "V2ray":
case "V2ray", "Vmess", "Vless":
nodeInfo, err = c.ParseV2rayNodeResponse(response)
case "Trojan":
nodeInfo, err = c.ParseTrojanNodeResponse(response)
@@ -181,23 +201,33 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) {
return nodeInfo, nil
}
// GetUserList will pull user form sspanel
// GetUserList will pull user form panel
func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
var nodeType string
switch c.NodeType {
case "V2ray", "Trojan", "Shadowsocks":
case "V2ray", "Vmess", "Vless", "Trojan", "Shadowsocks":
nodeType = strings.ToLower(c.NodeType)
default:
return nil, fmt.Errorf("unsupported Node type: %s", c.NodeType)
}
res, err := c.client.R().
SetHeader("If-None-Match", c.eTags["user"]).
SetQueryParams(map[string]string{
"act": "user",
"nodetype": nodeType,
"act": "user",
"node_type": nodeType,
}).
ForceContentType("application/json").
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)
if err != nil {
return nil, err
@@ -209,24 +239,30 @@ func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) {
user.UID = response.Get("data").GetIndex(i).Get("id").MustInt()
switch c.NodeType {
case "Shadowsocks":
user.Email = response.Get("data").GetIndex(i).Get("shadowsocks_user").Get("secret").MustString()
user.Passwd = response.Get("data").GetIndex(i).Get("shadowsocks_user").Get("secret").MustString()
user.Method = response.Get("data").GetIndex(i).Get("shadowsocks_user").Get("cipher").MustString()
user.SpeedLimit = uint64(response.Get("data").GetIndex(i).Get("shadowsocks_user").Get("speed_limit").MustUint64() * 1000000 / 8)
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.SpeedLimit = response.Get("data").GetIndex(i).Get("st").MustUint64() * 1000000 / 8
user.DeviceLimit = response.Get("data").GetIndex(i).Get("dt").MustInt()
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()
user.SpeedLimit = uint64(response.Get("data").GetIndex(i).Get("trojan_user").Get("speed_limit").MustUint64() * 1000000 / 8)
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())
user.SpeedLimit = uint64(response.Get("data").GetIndex(i).Get("v2ray_user").Get("speed_limit").MustUint64() * 1000000 / 8)
user.UUID = response.Get("data").GetIndex(i).Get("password").MustString()
user.Email = response.Get("data").GetIndex(i).Get("password").MustString()
user.SpeedLimit = response.Get("data").GetIndex(i).Get("st").MustUint64() * 1000000 / 8
user.DeviceLimit = response.Get("data").GetIndex(i).Get("dt").MustInt()
case "V2ray", "Vmess", "Vless":
user.UUID = response.Get("data").GetIndex(i).Get("uuid").MustString()
user.Email = user.UUID + "@x.com"
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 {
user.SpeedLimit = uint64((c.SpeedLimit * 1000000) / 8)
}
user.DeviceLimit = c.DeviceLimit
if c.DeviceLimit > 0 {
user.DeviceLimit = c.DeviceLimit
}
userList[i] = user
}
return &userList, nil
@@ -246,8 +282,8 @@ func (c *APIClient) ReportUserTraffic(userTraffic *[]api.UserTraffic) error {
res, err := c.client.R().
SetQueryParam("node_id", strconv.Itoa(c.NodeID)).
SetQueryParams(map[string]string{
"act": "submit",
"nodetype": strings.ToLower(c.NodeType),
"act": "submit",
"node_type": strings.ToLower(c.NodeType),
}).
SetBody(data).
ForceContentType("application/json").
@@ -262,11 +298,7 @@ func (c *APIClient) ReportUserTraffic(userTraffic *[]api.UserTraffic) error {
// 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()
@@ -284,26 +316,79 @@ func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) {
// ReportNodeStatus implements the API interface
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
}
//ReportNodeOnlineUsers implements the API interface
// ReportNodeOnlineUsers implements the API interface
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
}
// ReportIllegal implements the API interface
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
}
// ParseTrojanNodeResponse parse the response for the given nodeinfor format
// ParseTrojanNodeResponse parse the response for the given nodeInfo format
func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *simplejson.Json) (*api.NodeInfo, error) {
var TLSType = "tls"
if c.EnableXTLS {
TLSType = "xtls"
}
tmpInboundInfo := nodeInfoResponse.Get("inbounds").MustArray()
marshalByte, _ := json.Marshal(tmpInboundInfo[0].(map[string]interface{}))
inboundInfo, _ := simplejson.NewJson(marshalByte)
@@ -312,55 +397,52 @@ func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *simplejson.Json) (
host := inboundInfo.Get("streamSettings").Get("tlsSettings").Get("serverName").MustString()
// Create GeneralNodeInfo
nodeinfo := &api.NodeInfo{
nodeInfo := &api.NodeInfo{
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: port,
TransportProtocol: "tcp",
EnableTLS: true,
TLSType: TLSType,
Host: host,
}
return nodeinfo, nil
return nodeInfo, nil
}
// ParseSSNodeResponse parse the response for the given nodeinfor format
// ParseSSNodeResponse parse the response for the given nodeInfo format
func (c *APIClient) ParseSSNodeResponse(nodeInfoResponse *simplejson.Json) (*api.NodeInfo, error) {
var method string
var method, serverPsk string
tmpInboundInfo := nodeInfoResponse.Get("inbounds").MustArray()
marshalByte, _ := json.Marshal(tmpInboundInfo[0].(map[string]interface{}))
inboundInfo, _ := simplejson.NewJson(marshalByte)
port := uint32(inboundInfo.Get("port").MustUint64())
userInfo, err := c.GetUserList()
if err != nil {
return nil, err
}
if len(*userInfo) > 0 {
method = (*userInfo)[0].Method
method = inboundInfo.Get("settings").Get("method").MustString()
// Shadowsocks 2022
if C.Contains(shadowaead_2022.List, method) {
serverPsk = inboundInfo.Get("settings").Get("password").MustString()
}
// Create GeneralNodeInfo
nodeinfo := &api.NodeInfo{
nodeInfo := &api.NodeInfo{
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: port,
TransportProtocol: "tcp",
CypherMethod: method,
ServerKey: serverPsk,
}
return nodeinfo, nil
return nodeInfo, nil
}
// ParseV2rayNodeResponse parse the response for the given nodeinfor format
// ParseV2rayNodeResponse parse the response for the given nodeInfo 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"
}
var enableVless bool
var enableReality bool
var vlessFlow string
tmpInboundInfo := nodeInfoResponse.Get("inbounds").MustArray()
marshalByte, _ := json.Marshal(tmpInboundInfo[0].(map[string]interface{}))
@@ -373,6 +455,12 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *simplejson.Json) (*
case "ws":
path = inboundInfo.Get("streamSettings").Get("wsSettings").Get("path").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":
if data, ok := inboundInfo.Get("streamSettings").Get("grpcSettings").CheckGet("serviceName"); ok {
serviceName = data.MustString()
@@ -385,12 +473,32 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *simplejson.Json) (*
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 == "tcp" && enableReality {
vlessFlow = "xtls-rprx-vision"
} else {
enableTLS = false
vlessFlow = c.VlessFlow
}
// Create GeneralNodeInfo
@@ -399,15 +507,17 @@ func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *simplejson.Json) (*
NodeType: c.NodeType,
NodeID: c.NodeID,
Port: port,
AlterID: alterID,
AlterID: 0,
TransportProtocol: transportProtocol,
EnableTLS: enableTLS,
TLSType: TLSType,
Path: path,
Host: host,
EnableVless: c.EnableVless,
EnableVless: enableVless,
VlessFlow: vlessFlow,
ServiceName: serviceName,
Header: header,
EnableREALITY: enableReality,
REALITYConfig: realityConfig,
}
return nodeInfo, nil
}

View File

@@ -83,7 +83,7 @@ func TestReportReportUserTraffic(t *testing.T) {
Download: 114514,
}
}
//client.Debug()
// client.Debug()
err = client.ReportUserTraffic(&generalUserTraffic)
if err != nil {
t.Error(err)

View File

@@ -1,7 +1,7 @@
syntax = "proto3";
package xrayr.app.mydispatcher;
option csharp_namespace = "XrayR.App.Myispatcher";
option csharp_namespace = "XrayR.App.Mydispatcher";
option go_package = "github.com/XrayR-project/XrayR/app/mydispatcher";
option java_package = "com.xrayr.app.mydispatcher";
option java_multiple_files = true;

View File

@@ -9,10 +9,9 @@ import (
"sync"
"time"
"github.com/XrayR-project/XrayR/common/limiter"
"github.com/XrayR-project/XrayR/common/rule"
"github.com/xtls/xray-core/common"
"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/net"
"github.com/xtls/xray-core/common/protocol"
@@ -22,10 +21,13 @@ import (
"github.com/xtls/xray-core/features/outbound"
"github.com/xtls/xray-core/features/policy"
"github.com/xtls/xray-core/features/routing"
routing_session "github.com/xtls/xray-core/features/routing/session"
routingSession "github.com/xtls/xray-core/features/routing/session"
"github.com/xtls/xray-core/features/stats"
"github.com/xtls/xray-core/transport"
"github.com/xtls/xray-core/transport/pipe"
"github.com/XrayR-project/XrayR/common/limiter"
"github.com/XrayR-project/XrayR/common/rule"
)
var errSniffingTimeout = newError("timeout on sniffing")
@@ -98,7 +100,7 @@ type DefaultDispatcher struct {
dns dns.Client
fdns dns.FakeDNSEngine
Limiter *limiter.Limiter
RuleManager *rule.RuleManager
RuleManager *rule.Manager
}
func init() {
@@ -139,79 +141,14 @@ func (*DefaultDispatcher) Start() error {
}
// Close implements common.Closable.
func (*DefaultDispatcher) Close() error { return nil }
func (*DefaultDispatcher) Close() error {
return nil
}
func (d *DefaultDispatcher) getLink(ctx context.Context, network net.Network, sniffing session.SniffingRequest) (*transport.Link, *transport.Link, error) {
downOpt := pipe.OptionsFromContext(ctx)
upOpt := downOpt
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{true, true, false})
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...)
opt := pipe.OptionsFromContext(ctx)
uplinkReader, uplinkWriter := pipe.New(opt...)
downlinkReader, downlinkWriter := pipe.New(opt...)
inboundLink := &transport.Link{
Reader: downlinkReader,
@@ -233,7 +170,7 @@ func (d *DefaultDispatcher) getLink(ctx context.Context, network net.Network, sn
// Speed Limit and Device Limit
bucket, ok, reject := d.Limiter.GetUserBucket(sessionInbound.Tag, user.Email, sessionInbound.Source.Address.IP().String())
if reject {
newError("Devices reach the limit: ", user.Email).AtError().WriteToLog()
errors.LogWarning(ctx, "Devices reach the limit: ", user.Email)
common.Close(outboundLink.Writer)
common.Close(inboundLink.Writer)
common.Interrupt(outboundLink.Reader)
@@ -244,6 +181,7 @@ func (d *DefaultDispatcher) getLink(ctx context.Context, network net.Network, sn
inboundLink.Writer = d.Limiter.RateWriter(inboundLink.Writer, bucket)
outboundLink.Writer = d.Limiter.RateWriter(outboundLink.Writer, bucket)
}
p := d.policy.ForLevel(user.Level)
if p.Stats.UserUplink {
name := "user>>>" + user.Email + ">>>traffic>>>uplink"
@@ -280,12 +218,12 @@ func (d *DefaultDispatcher) shouldOverride(ctx context.Context, result SniffResu
protocolString = resComp.ProtocolForDomainResult()
}
for _, p := range request.OverrideDestinationForProtocol {
if strings.HasPrefix(protocolString, p) {
if strings.HasPrefix(protocolString, p) || strings.HasPrefix(p, protocolString) {
return true
}
if fkr0, ok := d.fdns.(dns.FakeDNSEngineRev0); ok && protocolString != "bittorrent" && p == "fakedns" &&
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
}
if resultSubset, ok := result.(SnifferIsProtoSubsetOf); ok {
@@ -303,10 +241,14 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin
if !destination.IsValid() {
panic("Dispatcher: Invalid destination.")
}
ob := &session.Outbound{
Target: destination,
outbounds := session.OutboundsFromContext(ctx)
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)
if content == nil {
content = new(session.Content)
@@ -318,39 +260,21 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin
if err != nil {
return nil, err
}
switch {
case !sniffingRequest.Enabled:
if !sniffingRequest.Enabled {
go d.routedDispatch(ctx, outbound, destination)
case destination.Network != net.Network_TCP:
// Only metadata sniff will be used for non tcp connection
result, err := sniffer(ctx, nil, true)
if err == nil {
content.Protocol = result.Protocol()
if d.shouldOverride(ctx, result, sniffingRequest, destination) {
domain := result.Domain()
newError("sniffed domain: ", domain).WriteToLog(session.ExportIDToError(ctx))
destination.Address = net.ParseAddress(domain)
if sniffingRequest.RouteOnly && result.Protocol() != "fakedns" {
ob.RouteTarget = destination
} else {
ob.Target = destination
}
}
}
go d.routedDispatch(ctx, outbound, destination)
default:
} else {
go func() {
cReader := &cachedReader{
reader: outbound.Reader.(*pipe.Reader),
}
outbound.Reader = cReader
result, err := sniffer(ctx, cReader, sniffingRequest.MetadataOnly)
result, err := sniffer(ctx, cReader, sniffingRequest.MetadataOnly, destination.Network)
if err == nil {
content.Protocol = result.Protocol()
}
if err == nil && d.shouldOverride(ctx, result, sniffingRequest, destination) {
domain := result.Domain()
newError("sniffed domain: ", domain).WriteToLog(session.ExportIDToError(ctx))
errors.LogInfo(ctx, "sniffed domain: ", domain)
destination.Address = net.ParseAddress(domain)
if sniffingRequest.RouteOnly && result.Protocol() != "fakedns" {
ob.RouteTarget = destination
@@ -369,49 +293,35 @@ func (d *DefaultDispatcher) DispatchLink(ctx context.Context, destination net.De
if !destination.IsValid() {
return newError("Dispatcher: Invalid destination.")
}
ob := &session.Outbound{
Target: destination,
outbounds := session.OutboundsFromContext(ctx)
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)
if content == nil {
content = new(session.Content)
ctx = session.ContextWithContent(ctx, content)
}
sniffingRequest := content.SniffingRequest
switch {
case !sniffingRequest.Enabled:
if !sniffingRequest.Enabled {
go d.routedDispatch(ctx, outbound, destination)
case destination.Network != net.Network_TCP:
// Only metadata sniff will be used for non tcp connection
result, err := sniffer(ctx, nil, true)
if err == nil {
content.Protocol = result.Protocol()
if d.shouldOverride(ctx, result, sniffingRequest, destination) {
domain := result.Domain()
newError("sniffed domain: ", domain).WriteToLog(session.ExportIDToError(ctx))
destination.Address = net.ParseAddress(domain)
if sniffingRequest.RouteOnly && result.Protocol() != "fakedns" {
ob.RouteTarget = destination
} else {
ob.Target = destination
}
}
}
go d.routedDispatch(ctx, outbound, destination)
default:
} else {
go func() {
cReader := &cachedReader{
reader: outbound.Reader.(*pipe.Reader),
}
outbound.Reader = cReader
result, err := sniffer(ctx, cReader, sniffingRequest.MetadataOnly)
result, err := sniffer(ctx, cReader, sniffingRequest.MetadataOnly, destination.Network)
if err == nil {
content.Protocol = result.Protocol()
}
if err == nil && d.shouldOverride(ctx, result, sniffingRequest, destination) {
domain := result.Domain()
newError("sniffed domain: ", domain).WriteToLog(session.ExportIDToError(ctx))
errors.LogInfo(ctx, "sniffed domain: ", domain)
destination.Address = net.ParseAddress(domain)
if sniffingRequest.RouteOnly && result.Protocol() != "fakedns" {
ob.RouteTarget = destination
@@ -422,10 +332,11 @@ func (d *DefaultDispatcher) DispatchLink(ctx context.Context, destination net.De
d.routedDispatch(ctx, outbound, destination)
}()
}
return nil
}
func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool) (SniffResult, error) {
func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool, network net.Network) (SniffResult, error) {
payload := buf.New()
defer payload.Release()
@@ -451,7 +362,7 @@ func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool) (Sni
cReader.Cache(payload)
if !payload.IsEmpty() {
result, err := sniffer.Sniff(ctx, payload.Bytes())
result, err := sniffer.Sniff(ctx, payload.Bytes(), network)
if err != common.ErrNoClue {
return result, err
}
@@ -472,7 +383,8 @@ func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool) (Sni
}
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() {
proxied := hosts.LookupHosts(ob.Target.String())
if proxied != nil {
@@ -493,7 +405,7 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
// Whether the inbound connection contains a user
if sessionInbound.User != nil {
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")
common.Close(link.Writer)
common.Interrupt(link.Reader)
@@ -501,17 +413,17 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
}
}
routingLink := routing_session.AsRoutingContext(ctx)
routingLink := routingSession.AsRoutingContext(ctx)
inTag := routingLink.GetInboundTag()
isPickRoute := 0
if forcedOutboundTag := session.GetForcedOutboundTagFromContext(ctx); forcedOutboundTag != "" {
ctx = session.SetForcedOutboundTagToContext(ctx, "")
if h := d.ohm.GetHandler(forcedOutboundTag); h != nil {
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
} 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.Interrupt(link.Reader)
return
@@ -521,18 +433,18 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
outTag := route.GetOutboundTag()
if h := d.ohm.GetHandler(outTag); h != nil {
isPickRoute = 2
newError("taking detour [", outTag, "] for [", destination, "]").WriteToLog(session.ExportIDToError(ctx))
errors.LogInfo(ctx, "taking detour [", outTag, "] for [", destination, "]")
handler = h
} else {
newError("non existing outTag: ", outTag).AtWarning().WriteToLog(session.ExportIDToError(ctx))
errors.LogWarning(ctx, "non existing outTag: ", outTag)
}
} else {
newError("default route for ", destination).WriteToLog(session.ExportIDToError(ctx))
errors.LogInfo(ctx, "default route for ", destination)
}
}
if handler == nil {
handler = d.ohm.GetHandler(inTag) // Default outbound hander tag should be as same as the inbound tag
handler = d.ohm.GetHandler(inTag) // Default outbound handler tag should be as same as the inbound tag
}
// If there is no outbound with tag as same as the inbound tag
@@ -541,7 +453,7 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
}
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.Interrupt(link.Reader)
return

View File

@@ -1,4 +1,4 @@
// Package dispather implement the rate limiter and the onlie device counter
// Package mydispatcher Package dispatcher implement the rate limiter and the online device counter
package mydispatcher
//go:generate go run github.com/xtls/xray-core/common/errors/errorgen

View File

@@ -2,8 +2,6 @@ package mydispatcher
import "github.com/xtls/xray-core/common/errors"
type errPathObjHolder struct{}
func newError(values ...interface{}) *errors.Error {
return errors.New(values...).WithPathObj(errPathObjHolder{})
return errors.New(values...)
}

View File

@@ -5,6 +5,7 @@ import (
"strings"
"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/session"
"github.com/xtls/xray-core/core"
@@ -26,11 +27,13 @@ func newFakeDNSSniffer(ctx context.Context) (protocolSnifferWithMetadata, error)
return protocolSnifferWithMetadata{}, errNotInit
}
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 {
domainFromFakeDNS := fakeDNSEngine.GetDomainFromFakeDNS(Target.Address)
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
}
}
@@ -107,10 +110,10 @@ func newFakeDNSThenOthers(ctx context.Context, fakeDNSSniffer protocolSnifferWit
}
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
}
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
},
metadataSniffer: false,

View File

@@ -4,8 +4,10 @@ import (
"context"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/protocol/bittorrent"
"github.com/xtls/xray-core/common/protocol/http"
"github.com/xtls/xray-core/common/protocol/quic"
"github.com/xtls/xray-core/common/protocol/tls"
)
@@ -22,6 +24,7 @@ type protocolSnifferWithMetadata struct {
// for both TCP and UDP connections
// It will not be shown as a traffic type for routing unless there is no other successful sniffing.
metadataSniffer bool
network net.Network
}
type Sniffer struct {
@@ -31,9 +34,11 @@ type Sniffer struct {
func NewSniffer(ctx context.Context) *Sniffer {
ret := &Sniffer{
sniffer: []protocolSnifferWithMetadata{
{func(c context.Context, b []byte) (SniffResult, error) { return http.SniffHTTP(b) }, false},
{func(c context.Context, b []byte) (SniffResult, error) { return tls.SniffTLS(b) }, false},
{func(c context.Context, b []byte) (SniffResult, error) { return bittorrent.SniffBittorrent(b) }, false},
{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 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 quic.SniffQUIC(b) }, false, net.Network_UDP},
{func(c context.Context, b []byte) (SniffResult, error) { return bittorrent.SniffUTP(b) }, false, net.Network_UDP},
},
}
if sniffer, err := newFakeDNSSniffer(ctx); err == nil {
@@ -49,11 +54,11 @@ func NewSniffer(ctx context.Context) *Sniffer {
var errUnknownContent = newError("unknown content")
func (s *Sniffer) Sniff(c context.Context, payload []byte) (SniffResult, error) {
func (s *Sniffer) Sniff(c context.Context, payload []byte, network net.Network) (SniffResult, error) {
var pendingSniffer []protocolSnifferWithMetadata
for _, si := range s.sniffer {
s := si.protocolSniffer
if si.metadataSniffer {
if si.metadataSniffer || si.network != network {
continue
}
result, err := s(c, payload)

View File

@@ -3,11 +3,12 @@ package all
import (
// The following are necessary as they register handlers in their init functions.
_ "github.com/xtls/xray-core/app/proxyman/inbound"
_ "github.com/xtls/xray-core/app/proxyman/outbound"
// Required features. Can't remove unless there is replacements.
// _ "github.com/xtls/xray-core/app/dispatcher"
_ "github.com/XrayR-project/XrayR/app/mydispatcher"
_ "github.com/xtls/xray-core/app/proxyman/inbound"
_ "github.com/xtls/xray-core/app/proxyman/outbound"
// Default commander and all its services. This is an optional feature.
_ "github.com/xtls/xray-core/app/commander"
@@ -30,7 +31,6 @@ import (
_ "github.com/xtls/xray-core/proxy/dokodemo"
_ "github.com/xtls/xray-core/proxy/freedom"
_ "github.com/xtls/xray-core/proxy/http"
_ "github.com/xtls/xray-core/proxy/mtproto"
_ "github.com/xtls/xray-core/proxy/shadowsocks"
_ "github.com/xtls/xray-core/proxy/socks"
_ "github.com/xtls/xray-core/proxy/trojan"
@@ -44,11 +44,11 @@ import (
_ "github.com/xtls/xray-core/transport/internet/http"
_ "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/tcp"
_ "github.com/xtls/xray-core/transport/internet/tls"
_ "github.com/xtls/xray-core/transport/internet/udp"
_ "github.com/xtls/xray-core/transport/internet/websocket"
_ "github.com/xtls/xray-core/transport/internet/xtls"
// Transport headers
_ "github.com/xtls/xray-core/transport/internet/headers/http"

View File

@@ -1,9 +1,7 @@
package main
package cmd
import (
"flag"
"fmt"
"log"
"os"
"os/signal"
"path"
@@ -12,35 +10,40 @@ import (
"syscall"
"time"
"github.com/XrayR-project/XrayR/panel"
log "github.com/sirupsen/logrus"
"github.com/fsnotify/fsnotify"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/XrayR-project/XrayR/panel"
)
var (
configFile = flag.String("config", "", "Config file for XrayR.")
printVersion = flag.Bool("version", false, "show version")
cfgFile string
rootCmd = &cobra.Command{
Use: "XrayR",
Run: func(cmd *cobra.Command, args []string) {
if err := run(); err != nil {
log.Fatal(err)
}
},
}
)
var (
version = "0.8.2"
codename = "XrayR"
intro = "A Xray backend that supports many panels"
)
func showVersion() {
fmt.Printf("%s %s (%s) \n", codename, version, intro)
func init() {
rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "Config file for XrayR.")
}
func getConfig() *viper.Viper {
config := viper.New()
// Set custom path and name
if *configFile != "" {
configName := path.Base(*configFile)
configFileExt := path.Ext(*configFile)
if cfgFile != "" {
configName := path.Base(cfgFile)
configFileExt := path.Ext(cfgFile)
configNameOnly := strings.TrimSuffix(configName, configFileExt)
configPath := path.Dir(*configFile)
configPath := path.Dir(cfgFile)
config.SetConfigName(configNameOnly)
config.SetConfigType(strings.TrimPrefix(configFileExt, "."))
config.AddConfigPath(configPath)
@@ -56,7 +59,7 @@ func getConfig() *viper.Viper {
}
if err := config.ReadInConfig(); err != nil {
log.Panicf("Fatal error config file: %s \n", err)
log.Panicf("Config file error: %s \n", err)
}
config.WatchConfig() // Watch the config
@@ -64,16 +67,19 @@ func getConfig() *viper.Viper {
return config
}
func main() {
flag.Parse()
func run() error {
showVersion()
if *printVersion {
return
}
config := getConfig()
panelConfig := &panel.Config{}
config.Unmarshal(panelConfig)
if err := config.Unmarshal(panelConfig); err != nil {
return fmt.Errorf("Parse config file %v failed: %s \n", cfgFile, err)
}
if panelConfig.LogConfig.Level == "debug" {
log.SetReportCaller(true)
}
p := panel.New(panelConfig)
lastTime := time.Now()
config.OnConfigChange(func(e fsnotify.Event) {
@@ -84,20 +90,32 @@ func main() {
p.Close()
// Delete old instance and trigger GC
runtime.GC()
config.Unmarshal(panelConfig)
if err := config.Unmarshal(panelConfig); err != nil {
log.Panicf("Parse config file %v failed: %s \n", cfgFile, err)
}
if panelConfig.LogConfig.Level == "debug" {
log.SetReportCaller(true)
}
p.Start()
lastTime = time.Now()
}
})
p.Start()
defer p.Close()
//Explicitly triggering GC to remove garbage from config loading.
// Explicitly triggering GC to remove garbage from config loading.
runtime.GC()
// Running backend
{
osSignals := make(chan os.Signal, 1)
signal.Notify(osSignals, os.Interrupt, os.Kill, syscall.SIGTERM)
<-osSignals
}
osSignals := make(chan os.Signal, 1)
signal.Notify(osSignals, os.Interrupt, os.Kill, syscall.SIGTERM)
<-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,14 +0,0 @@
package cmd
import "github.com/urfave/cli"
// CreateCommands Creates all CLI commands.
func CreateCommands() []cli.Command {
return []cli.Command{
createRun(),
createRevoke(),
createRenew(),
createDNSHelp(),
createList(),
}
}

View File

@@ -1,23 +0,0 @@
package cmd
import (
"github.com/XrayR-project/XrayR/common/legocmd/log"
"github.com/urfave/cli"
)
func Before(ctx *cli.Context) error {
if ctx.GlobalString("path") == "" {
log.Panic("Could not determine current working directory. Please pass --path.")
}
err := createNonExistingFolder(ctx.GlobalString("path"))
if err != nil {
log.Panicf("Could not check/create path: %v", err)
}
if ctx.GlobalString("server") == "" {
log.Panic("Could not determine current working server. Please pass --server.")
}
return nil
}

View File

@@ -1,73 +0,0 @@
package cmd
import (
"fmt"
"io"
"os"
"strings"
"text/tabwriter"
"github.com/urfave/cli"
)
func createDNSHelp() cli.Command {
return cli.Command{
Name: "dnshelp",
Usage: "Shows additional help for the '--dns' global option",
Action: dnsHelp,
Flags: []cli.Flag{
cli.StringFlag{
Name: "code, c",
Usage: fmt.Sprintf("DNS code: %s", allDNSCodes()),
},
},
}
}
func dnsHelp(ctx *cli.Context) error {
code := ctx.String("code")
if code == "" {
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
ew := &errWriter{w: w}
ew.writeln(`Credentials for DNS providers must be passed through environment variables.`)
ew.writeln()
ew.writeln(`To display the documentation for a DNS providers:`)
ew.writeln()
ew.writeln("\t$ lego dnshelp -c code")
ew.writeln()
ew.writeln("All DNS codes:")
ew.writef("\t%s\n", allDNSCodes())
ew.writeln()
ew.writeln("More information: https://go-acme.github.io/lego/dns")
if ew.err != nil {
return ew.err
}
return w.Flush()
}
return displayDNSHelp(strings.ToLower(code))
}
type errWriter struct {
w io.Writer
err error
}
func (ew *errWriter) writeln(a ...interface{}) {
if ew.err != nil {
return
}
_, ew.err = fmt.Fprintln(ew.w, a...)
}
func (ew *errWriter) writef(format string, a ...interface{}) {
if ew.err != nil {
return
}
_, ew.err = fmt.Fprintf(ew.w, format, a...)
}

View File

@@ -1,136 +0,0 @@
package cmd
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/url"
"path/filepath"
"strings"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/urfave/cli"
)
func createList() cli.Command {
return cli.Command{
Name: "list",
Usage: "Display certificates and accounts information.",
Action: list,
Flags: []cli.Flag{
cli.BoolFlag{
Name: "accounts, a",
Usage: "Display accounts.",
},
cli.BoolFlag{
Name: "names, n",
Usage: "Display certificate common names only.",
},
},
}
}
func list(ctx *cli.Context) error {
if ctx.Bool("accounts") && !ctx.Bool("names") {
if err := listAccount(ctx); err != nil {
return err
}
}
return listCertificates(ctx)
}
func listCertificates(ctx *cli.Context) error {
certsStorage := NewCertificatesStorage(ctx)
matches, err := filepath.Glob(filepath.Join(certsStorage.GetRootPath(), "*.crt"))
if err != nil {
return err
}
names := ctx.Bool("names")
if len(matches) == 0 {
if !names {
fmt.Println("No certificates found.")
}
return nil
}
if !names {
fmt.Println("Found the following certs:")
}
for _, filename := range matches {
if strings.HasSuffix(filename, ".issuer.crt") {
continue
}
data, err := ioutil.ReadFile(filename)
if err != nil {
return err
}
pCert, err := certcrypto.ParsePEMCertificate(data)
if err != nil {
return err
}
if names {
fmt.Println(pCert.Subject.CommonName)
} else {
fmt.Println(" Certificate Name:", pCert.Subject.CommonName)
fmt.Println(" Domains:", strings.Join(pCert.DNSNames, ", "))
fmt.Println(" Expiry Date:", pCert.NotAfter)
fmt.Println(" Certificate Path:", filename)
fmt.Println()
}
}
return nil
}
func listAccount(ctx *cli.Context) error {
// fake email, needed by NewAccountsStorage
if err := ctx.GlobalSet("email", "unknown"); err != nil {
return err
}
accountsStorage := NewAccountsStorage(ctx)
matches, err := filepath.Glob(filepath.Join(accountsStorage.GetRootPath(), "*", "*", "*.json"))
if err != nil {
return err
}
if len(matches) == 0 {
fmt.Println("No accounts found.")
return nil
}
fmt.Println("Found the following accounts:")
for _, filename := range matches {
data, err := ioutil.ReadFile(filename)
if err != nil {
return err
}
var account Account
err = json.Unmarshal(data, &account)
if err != nil {
return err
}
uri, err := url.Parse(account.Registration.URI)
if err != nil {
return err
}
fmt.Println(" Email:", account.Email)
fmt.Println(" Server:", uri.Host)
fmt.Println(" Path:", filepath.Dir(filename))
fmt.Println()
}
return nil
}

View File

@@ -1,225 +0,0 @@
package cmd
import (
"crypto"
"crypto/x509"
"time"
"github.com/XrayR-project/XrayR/common/legocmd/log"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/go-acme/lego/v4/certificate"
"github.com/go-acme/lego/v4/lego"
"github.com/urfave/cli"
)
const (
renewEnvAccountEmail = "LEGO_ACCOUNT_EMAIL"
renewEnvCertDomain = "LEGO_CERT_DOMAIN"
renewEnvCertPath = "LEGO_CERT_PATH"
renewEnvCertKeyPath = "LEGO_CERT_KEY_PATH"
)
func createRenew() cli.Command {
return cli.Command{
Name: "renew",
Usage: "Renew a certificate",
Action: renew,
Before: func(ctx *cli.Context) error {
// we require either domains or csr, but not both
hasDomains := len(ctx.GlobalStringSlice("domains")) > 0
hasCsr := len(ctx.GlobalString("csr")) > 0
if hasDomains && hasCsr {
log.Panic("Please specify either --domains/-d or --csr/-c, but not both")
}
if !hasDomains && !hasCsr {
log.Panic("Please specify --domains/-d (or --csr/-c if you already have a CSR)")
}
return nil
},
Flags: []cli.Flag{
cli.IntFlag{
Name: "days",
Value: 30,
Usage: "The number of days left on a certificate to renew it.",
},
cli.BoolFlag{
Name: "reuse-key",
Usage: "Used to indicate you want to reuse your current private key for the new certificate.",
},
cli.BoolFlag{
Name: "no-bundle",
Usage: "Do not create a certificate bundle by adding the issuers certificate to the new certificate.",
},
cli.BoolFlag{
Name: "must-staple",
Usage: "Include the OCSP must staple TLS extension in the CSR and generated certificate. Only works if the CSR is generated by lego.",
},
cli.StringFlag{
Name: "renew-hook",
Usage: "Define a hook. The hook is executed only when the certificates are effectively renewed.",
},
cli.StringFlag{
Name: "preferred-chain",
Usage: "If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name. If no match, the default offered chain will be used.",
},
},
}
}
func renew(ctx *cli.Context) error {
account, client := setup(ctx, NewAccountsStorage(ctx))
setupChallenges(ctx, client)
if account.Registration == nil {
log.Panicf("Account %s is not registered. Use 'run' to register a new account.\n", account.Email)
}
certsStorage := NewCertificatesStorage(ctx)
bundle := !ctx.Bool("no-bundle")
meta := map[string]string{renewEnvAccountEmail: account.Email}
// CSR
if ctx.GlobalIsSet("csr") {
return renewForCSR(ctx, client, certsStorage, bundle, meta)
}
// Domains
return renewForDomains(ctx, client, certsStorage, bundle, meta)
}
func renewForDomains(ctx *cli.Context, client *lego.Client, certsStorage *CertificatesStorage, bundle bool, meta map[string]string) error {
domains := ctx.GlobalStringSlice("domains")
domain := domains[0]
// load the cert resource from files.
// We store the certificate, private key and metadata in different files
// as web servers would not be able to work with a combined file.
certificates, err := certsStorage.ReadCertificate(domain, ".crt")
if err != nil {
log.Panicf("Error while loading the certificate for domain %s\n\t%v", domain, err)
}
cert := certificates[0]
if !needRenewal(cert, domain, ctx.Int("days")) {
return nil
}
// This is just meant to be informal for the user.
timeLeft := cert.NotAfter.Sub(time.Now().UTC())
log.Infof("[%s] acme: Trying renewal with %d hours remaining", domain, int(timeLeft.Hours()))
certDomains := certcrypto.ExtractDomains(cert)
var privateKey crypto.PrivateKey
if ctx.Bool("reuse-key") {
keyBytes, errR := certsStorage.ReadFile(domain, ".key")
if errR != nil {
log.Panicf("Error while loading the private key for domain %s\n\t%v", domain, errR)
}
privateKey, errR = certcrypto.ParsePEMPrivateKey(keyBytes)
if errR != nil {
return errR
}
}
request := certificate.ObtainRequest{
Domains: merge(certDomains, domains),
Bundle: bundle,
PrivateKey: privateKey,
MustStaple: ctx.Bool("must-staple"),
PreferredChain: ctx.String("preferred-chain"),
}
certRes, err := client.Certificate.Obtain(request)
if err != nil {
log.Panic(err)
}
certsStorage.SaveResource(certRes)
meta[renewEnvCertDomain] = domain
meta[renewEnvCertPath] = certsStorage.GetFileName(domain, ".crt")
meta[renewEnvCertKeyPath] = certsStorage.GetFileName(domain, ".key")
return launchHook(ctx.String("renew-hook"), meta)
}
func renewForCSR(ctx *cli.Context, client *lego.Client, certsStorage *CertificatesStorage, bundle bool, meta map[string]string) error {
csr, err := readCSRFile(ctx.GlobalString("csr"))
if err != nil {
log.Panic(err)
}
domain := csr.Subject.CommonName
// load the cert resource from files.
// We store the certificate, private key and metadata in different files
// as web servers would not be able to work with a combined file.
certificates, err := certsStorage.ReadCertificate(domain, ".crt")
if err != nil {
log.Panicf("Error while loading the certificate for domain %s\n\t%v", domain, err)
}
cert := certificates[0]
if !needRenewal(cert, domain, ctx.Int("days")) {
return nil
}
// This is just meant to be informal for the user.
timeLeft := cert.NotAfter.Sub(time.Now().UTC())
log.Infof("[%s] acme: Trying renewal with %d hours remaining", domain, int(timeLeft.Hours()))
certRes, err := client.Certificate.ObtainForCSR(certificate.ObtainForCSRRequest{
CSR: csr,
Bundle: bundle,
PreferredChain: ctx.String("preferred-chain"),
})
if err != nil {
log.Panic(err)
}
certsStorage.SaveResource(certRes)
meta[renewEnvCertDomain] = domain
meta[renewEnvCertPath] = certsStorage.GetFileName(domain, ".crt")
meta[renewEnvCertKeyPath] = certsStorage.GetFileName(domain, ".key")
return launchHook(ctx.String("renew-hook"), meta)
}
func needRenewal(x509Cert *x509.Certificate, domain string, days int) bool {
if x509Cert.IsCA {
log.Panicf("[%s] Certificate bundle starts with a CA certificate", domain)
}
if days >= 0 {
notAfter := int(time.Until(x509Cert.NotAfter).Hours() / 24.0)
if notAfter > days {
log.Printf("[%s] The certificate expires in %d days, the number of days defined to perform the renewal is %d: no renewal.",
domain, notAfter, days)
return false
}
}
return true
}
func merge(prevDomains, nextDomains []string) []string {
for _, next := range nextDomains {
var found bool
for _, prev := range prevDomains {
if prev == next {
found = true
break
}
}
if !found {
prevDomains = append(prevDomains, next)
}
}
return prevDomains
}

View File

@@ -1,62 +0,0 @@
package cmd
import (
"github.com/XrayR-project/XrayR/common/legocmd/log"
"github.com/urfave/cli"
)
func createRevoke() cli.Command {
return cli.Command{
Name: "revoke",
Usage: "Revoke a certificate",
Action: revoke,
Flags: []cli.Flag{
cli.BoolFlag{
Name: "keep, k",
Usage: "Keep the certificates after the revocation instead of archiving them.",
},
},
}
}
func revoke(ctx *cli.Context) error {
acc, client := setup(ctx, NewAccountsStorage(ctx))
if acc.Registration == nil {
log.Panicf("Account %s is not registered. Use 'run' to register a new account.\n", acc.Email)
}
certsStorage := NewCertificatesStorage(ctx)
certsStorage.CreateRootFolder()
for _, domain := range ctx.GlobalStringSlice("domains") {
log.Printf("Trying to revoke certificate for domain %s", domain)
certBytes, err := certsStorage.ReadFile(domain, ".crt")
if err != nil {
log.Panicf("Error while revoking the certificate for domain %s\n\t%v", domain, err)
}
err = client.Certificate.Revoke(certBytes)
if err != nil {
log.Panicf("Error while revoking the certificate for domain %s\n\t%v", domain, err)
}
log.Println("Certificate was revoked.")
if ctx.Bool("keep") {
return nil
}
certsStorage.CreateArchiveFolder()
err = certsStorage.MoveToArchive(domain)
if err != nil {
return err
}
log.Println("Certificate was archived for domain:", domain)
}
return nil
}

View File

@@ -1,186 +0,0 @@
package cmd
import (
"bufio"
"fmt"
"os"
"strings"
"github.com/XrayR-project/XrayR/common/legocmd/log"
"github.com/go-acme/lego/v4/certificate"
"github.com/go-acme/lego/v4/lego"
"github.com/go-acme/lego/v4/registration"
"github.com/urfave/cli"
)
func createRun() cli.Command {
return cli.Command{
Name: "run",
Usage: "Register an account, then create and install a certificate",
Before: func(ctx *cli.Context) error {
// we require either domains or csr, but not both
hasDomains := len(ctx.GlobalStringSlice("domains")) > 0
hasCsr := len(ctx.GlobalString("csr")) > 0
if hasDomains && hasCsr {
log.Panic("Please specify either --domains/-d or --csr/-c, but not both")
}
if !hasDomains && !hasCsr {
log.Panic("Please specify --domains/-d (or --csr/-c if you already have a CSR)")
}
return nil
},
Action: run,
Flags: []cli.Flag{
cli.BoolFlag{
Name: "no-bundle",
Usage: "Do not create a certificate bundle by adding the issuers certificate to the new certificate.",
},
cli.BoolFlag{
Name: "must-staple",
Usage: "Include the OCSP must staple TLS extension in the CSR and generated certificate. Only works if the CSR is generated by lego.",
},
cli.StringFlag{
Name: "run-hook",
Usage: "Define a hook. The hook is executed when the certificates are effectively created.",
},
cli.StringFlag{
Name: "preferred-chain",
Usage: "If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name. If no match, the default offered chain will be used.",
},
},
}
}
const rootPathWarningMessage = `!!!! HEADS UP !!!!
Your account credentials have been saved in your Let's Encrypt
configuration directory at "%s".
You should make a secure backup of this folder now. This
configuration directory will also contain certificates and
private keys obtained from Let's Encrypt so making regular
backups of this folder is ideal.
`
func run(ctx *cli.Context) error {
accountsStorage := NewAccountsStorage(ctx)
account, client := setup(ctx, accountsStorage)
setupChallenges(ctx, client)
if account.Registration == nil {
reg, err := register(ctx, client)
if err != nil {
log.Panicf("Could not complete registration\n\t%v", err)
}
account.Registration = reg
if err = accountsStorage.Save(account); err != nil {
log.Panic(err)
}
fmt.Printf(rootPathWarningMessage, accountsStorage.GetRootPath())
}
certsStorage := NewCertificatesStorage(ctx)
certsStorage.CreateRootFolder()
cert, err := obtainCertificate(ctx, client)
if err != nil {
// Make sure to return a non-zero exit code if ObtainSANCertificate returned at least one error.
// Due to us not returning partial certificate we can just exit here instead of at the end.
log.Panicf("Could not obtain certificates:\n\t%v", err)
}
certsStorage.SaveResource(cert)
meta := map[string]string{
renewEnvAccountEmail: account.Email,
renewEnvCertDomain: cert.Domain,
renewEnvCertPath: certsStorage.GetFileName(cert.Domain, ".crt"),
renewEnvCertKeyPath: certsStorage.GetFileName(cert.Domain, ".key"),
}
return launchHook(ctx.String("run-hook"), meta)
}
func handleTOS(ctx *cli.Context, client *lego.Client) bool {
// Check for a global accept override
if ctx.GlobalBool("accept-tos") {
return true
}
reader := bufio.NewReader(os.Stdin)
log.Printf("Please review the TOS at %s", client.GetToSURL())
for {
fmt.Println("Do you accept the TOS? Y/n")
text, err := reader.ReadString('\n')
if err != nil {
log.Panicf("Could not read from console: %v", err)
}
text = strings.Trim(text, "\r\n")
switch text {
case "", "y", "Y":
return true
case "n", "N":
return false
default:
fmt.Println("Your input was invalid. Please answer with one of Y/y, n/N or by pressing enter.")
}
}
}
func register(ctx *cli.Context, client *lego.Client) (*registration.Resource, error) {
accepted := handleTOS(ctx, client)
if !accepted {
log.Panic("You did not accept the TOS. Unable to proceed.")
}
if ctx.GlobalBool("eab") {
kid := ctx.GlobalString("kid")
hmacEncoded := ctx.GlobalString("hmac")
if kid == "" || hmacEncoded == "" {
log.Panicf("Requires arguments --kid and --hmac.")
}
return client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{
TermsOfServiceAgreed: accepted,
Kid: kid,
HmacEncoded: hmacEncoded,
})
}
return client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
}
func obtainCertificate(ctx *cli.Context, client *lego.Client) (*certificate.Resource, error) {
bundle := !ctx.Bool("no-bundle")
domains := ctx.GlobalStringSlice("domains")
if len(domains) > 0 {
// obtain a certificate, generating a new private key
request := certificate.ObtainRequest{
Domains: domains,
Bundle: bundle,
MustStaple: ctx.Bool("must-staple"),
PreferredChain: ctx.String("preferred-chain"),
}
return client.Certificate.Obtain(request)
}
// read the CSR
csr, err := readCSRFile(ctx.GlobalString("csr"))
if err != nil {
return nil, err
}
// obtain a certificate for this CSR
return client.Certificate.ObtainForCSR(certificate.ObtainForCSRRequest{
CSR: csr,
Bundle: bundle,
PreferredChain: ctx.String("preferred-chain"),
})
}

View File

@@ -1,120 +0,0 @@
package cmd
import (
"github.com/go-acme/lego/v4/lego"
"github.com/urfave/cli"
)
func CreateFlags(defaultPath string) []cli.Flag {
return []cli.Flag{
cli.StringSliceFlag{
Name: "domains, d",
Usage: "Add a domain to the process. Can be specified multiple times.",
},
cli.StringFlag{
Name: "server, s",
Usage: "CA hostname (and optionally :port). The server certificate must be trusted in order to avoid further modifications to the client.",
Value: lego.LEDirectoryProduction,
},
cli.BoolFlag{
Name: "accept-tos, a",
Usage: "By setting this flag to true you indicate that you accept the current Let's Encrypt terms of service.",
},
cli.StringFlag{
Name: "email, m",
Usage: "Email used for registration and recovery contact.",
},
cli.StringFlag{
Name: "csr, c",
Usage: "Certificate signing request filename, if an external CSR is to be used.",
},
cli.BoolFlag{
Name: "eab",
Usage: "Use External Account Binding for account registration. Requires --kid and --hmac.",
},
cli.StringFlag{
Name: "kid",
Usage: "Key identifier from External CA. Used for External Account Binding.",
},
cli.StringFlag{
Name: "hmac",
Usage: "MAC key from External CA. Should be in Base64 URL Encoding without padding format. Used for External Account Binding.",
},
cli.StringFlag{
Name: "key-type, k",
Value: "ec256",
Usage: "Key type to use for private keys. Supported: rsa2048, rsa4096, rsa8192, ec256, ec384.",
},
cli.StringFlag{
Name: "filename",
Usage: "(deprecated) Filename of the generated certificate.",
},
cli.StringFlag{
Name: "path",
EnvVar: "LEGO_PATH",
Usage: "Directory to use for storing the data.",
Value: defaultPath,
},
cli.BoolFlag{
Name: "http",
Usage: "Use the HTTP challenge to solve challenges. Can be mixed with other types of challenges.",
},
cli.StringFlag{
Name: "http.port",
Usage: "Set the port and interface to use for HTTP based challenges to listen on.Supported: interface:port or :port.",
Value: ":80",
},
cli.StringFlag{
Name: "http.proxy-header",
Usage: "Validate against this HTTP header when solving HTTP based challenges behind a reverse proxy.",
Value: "Host",
},
cli.StringFlag{
Name: "http.webroot",
Usage: "Set the webroot folder to use for HTTP based challenges to write directly in a file in .well-known/acme-challenge. This disables the built-in server and expects the given directory to be publicly served with access to .well-known/acme-challenge",
},
cli.StringSliceFlag{
Name: "http.memcached-host",
Usage: "Set the memcached host(s) to use for HTTP based challenges. Challenges will be written to all specified hosts.",
},
cli.BoolFlag{
Name: "tls",
Usage: "Use the TLS challenge to solve challenges. Can be mixed with other types of challenges.",
},
cli.StringFlag{
Name: "tls.port",
Usage: "Set the port and interface to use for TLS based challenges to listen on. Supported: interface:port or :port.",
Value: ":443",
},
cli.StringFlag{
Name: "dns",
Usage: "Solve a DNS challenge using the specified provider. Can be mixed with other types of challenges. Run 'lego dnshelp' for help on usage.",
},
cli.BoolFlag{
Name: "dns.disable-cp",
Usage: "By setting this flag to true, disables the need to wait the propagation of the TXT record to all authoritative name servers.",
},
cli.StringSliceFlag{
Name: "dns.resolvers",
Usage: "Set the resolvers to use for performing recursive DNS queries. Supported: host:port. The default is to use the system resolvers, or Google's DNS resolvers if the system's cannot be determined.",
},
cli.IntFlag{
Name: "http-timeout",
Usage: "Set the HTTP timeout value to a specific value in seconds.",
},
cli.IntFlag{
Name: "dns-timeout",
Usage: "Set the DNS timeout value to a specific value in seconds. Used only when performing authoritative name servers queries.",
Value: 10,
},
cli.BoolFlag{
Name: "pem",
Usage: "Generate a .pem file by concatenating the .key and .crt files together.",
},
cli.IntFlag{
Name: "cert.timeout",
Usage: "Set the certificate timeout value to a specific value in seconds. Only used when obtaining certificates.",
Value: 30,
},
}
}

View File

@@ -1,47 +0,0 @@
package cmd
import (
"context"
"errors"
"fmt"
"os"
"os/exec"
"strings"
"time"
)
func launchHook(hook string, meta map[string]string) error {
if hook == "" {
return nil
}
ctxCmd, cancel := context.WithTimeout(context.Background(), 120*time.Second)
defer cancel()
parts := strings.Fields(hook)
cmdCtx := exec.CommandContext(ctxCmd, parts[0], parts[1:]...)
cmdCtx.Env = append(os.Environ(), metaToEnv(meta)...)
output, err := cmdCtx.CombinedOutput()
if len(output) > 0 {
fmt.Println(string(output))
}
if errors.Is(ctxCmd.Err(), context.DeadlineExceeded) {
return errors.New("hook timed out")
}
return err
}
func metaToEnv(meta map[string]string) []string {
var envs []string
for k, v := range meta {
envs = append(envs, k+"="+v)
}
return envs
}

View File

@@ -1,129 +0,0 @@
package cmd
import (
"crypto/x509"
"encoding/pem"
"fmt"
"io/ioutil"
"os"
"strings"
"time"
"github.com/XrayR-project/XrayR/common/legocmd/log"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/go-acme/lego/v4/lego"
"github.com/go-acme/lego/v4/registration"
"github.com/urfave/cli"
)
const filePerm os.FileMode = 0o600
func setup(ctx *cli.Context, accountsStorage *AccountsStorage) (*Account, *lego.Client) {
keyType := getKeyType(ctx)
privateKey := accountsStorage.GetPrivateKey(keyType)
var account *Account
if accountsStorage.ExistsAccountFilePath() {
account = accountsStorage.LoadAccount(privateKey)
} else {
account = &Account{Email: accountsStorage.GetUserID(), key: privateKey}
}
client := newClient(ctx, account, keyType)
return account, client
}
func newClient(ctx *cli.Context, acc registration.User, keyType certcrypto.KeyType) *lego.Client {
config := lego.NewConfig(acc)
config.CADirURL = ctx.GlobalString("server")
config.Certificate = lego.CertificateConfig{
KeyType: keyType,
Timeout: time.Duration(ctx.GlobalInt("cert.timeout")) * time.Second,
}
config.UserAgent = fmt.Sprintf("lego-cli/%s", ctx.App.Version)
if ctx.GlobalIsSet("http-timeout") {
config.HTTPClient.Timeout = time.Duration(ctx.GlobalInt("http-timeout")) * time.Second
}
client, err := lego.NewClient(config)
if err != nil {
log.Panicf("Could not create client: %v", err)
}
if client.GetExternalAccountRequired() && !ctx.GlobalIsSet("eab") {
log.Panic("Server requires External Account Binding. Use --eab with --kid and --hmac.")
}
return client
}
// getKeyType the type from which private keys should be generated.
func getKeyType(ctx *cli.Context) certcrypto.KeyType {
keyType := ctx.GlobalString("key-type")
switch strings.ToUpper(keyType) {
case "RSA2048":
return certcrypto.RSA2048
case "RSA4096":
return certcrypto.RSA4096
case "RSA8192":
return certcrypto.RSA8192
case "EC256":
return certcrypto.EC256
case "EC384":
return certcrypto.EC384
}
log.Panicf("Unsupported KeyType: %s", keyType)
return ""
}
func getEmail(ctx *cli.Context) string {
email := ctx.GlobalString("email")
if email == "" {
log.Panic("You have to pass an account (email address) to the program using --email or -m")
}
return email
}
func createNonExistingFolder(path string) error {
if _, err := os.Stat(path); os.IsNotExist(err) {
return os.MkdirAll(path, 0o700)
} else if err != nil {
return err
}
return nil
}
func readCSRFile(filename string) (*x509.CertificateRequest, error) {
bytes, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
raw := bytes
// see if we can find a PEM-encoded CSR
var p *pem.Block
rest := bytes
for {
// decode a PEM block
p, rest = pem.Decode(rest)
// did we fail?
if p == nil {
break
}
// did we get a CSR?
if p.Type == "CERTIFICATE REQUEST" {
raw = p.Bytes
}
}
// no PEM-encoded CSR
// assume we were given a DER-encoded ASN.1 CSR
// (if this assumption is wrong, parsing these bytes will fail)
return x509.ParseCertificateRequest(raw)
}

View File

@@ -1,126 +0,0 @@
package cmd
import (
"net"
"strings"
"time"
"github.com/XrayR-project/XrayR/common/legocmd/log"
"github.com/go-acme/lego/v4/challenge"
"github.com/go-acme/lego/v4/challenge/dns01"
"github.com/go-acme/lego/v4/challenge/http01"
"github.com/go-acme/lego/v4/challenge/tlsalpn01"
"github.com/go-acme/lego/v4/lego"
"github.com/go-acme/lego/v4/providers/dns"
"github.com/go-acme/lego/v4/providers/http/memcached"
"github.com/go-acme/lego/v4/providers/http/webroot"
"github.com/urfave/cli"
)
func setupChallenges(ctx *cli.Context, client *lego.Client) {
if !ctx.GlobalBool("http") && !ctx.GlobalBool("tls") && !ctx.GlobalIsSet("dns") {
log.Panic("No challenge selected. You must specify at least one challenge: `--http`, `--tls`, `--dns`.")
}
if ctx.GlobalBool("http") {
err := client.Challenge.SetHTTP01Provider(setupHTTPProvider(ctx))
if err != nil {
log.Panic(err)
}
}
if ctx.GlobalBool("tls") {
err := client.Challenge.SetTLSALPN01Provider(setupTLSProvider(ctx))
if err != nil {
log.Panic(err)
}
}
if ctx.GlobalIsSet("dns") {
setupDNS(ctx, client)
}
}
func setupHTTPProvider(ctx *cli.Context) challenge.Provider {
switch {
case ctx.GlobalIsSet("http.webroot"):
ps, err := webroot.NewHTTPProvider(ctx.GlobalString("http.webroot"))
if err != nil {
log.Panic(err)
}
return ps
case ctx.GlobalIsSet("http.memcached-host"):
ps, err := memcached.NewMemcachedProvider(ctx.GlobalStringSlice("http.memcached-host"))
if err != nil {
log.Panic(err)
}
return ps
case ctx.GlobalIsSet("http.port"):
iface := ctx.GlobalString("http.port")
if !strings.Contains(iface, ":") {
log.Panicf("The --http switch only accepts interface:port or :port for its argument.")
}
host, port, err := net.SplitHostPort(iface)
if err != nil {
log.Panic(err)
}
srv := http01.NewProviderServer(host, port)
if header := ctx.GlobalString("http.proxy-header"); header != "" {
srv.SetProxyHeader(header)
}
return srv
case ctx.GlobalBool("http"):
srv := http01.NewProviderServer("", "")
if header := ctx.GlobalString("http.proxy-header"); header != "" {
srv.SetProxyHeader(header)
}
return srv
default:
log.Panic("Invalid HTTP challenge options.")
return nil
}
}
func setupTLSProvider(ctx *cli.Context) challenge.Provider {
switch {
case ctx.GlobalIsSet("tls.port"):
iface := ctx.GlobalString("tls.port")
if !strings.Contains(iface, ":") {
log.Panicf("The --tls switch only accepts interface:port or :port for its argument.")
}
host, port, err := net.SplitHostPort(iface)
if err != nil {
log.Panic(err)
}
return tlsalpn01.NewProviderServer(host, port)
case ctx.GlobalBool("tls"):
return tlsalpn01.NewProviderServer("", "")
default:
log.Panic("Invalid HTTP challenge options.")
return nil
}
}
func setupDNS(ctx *cli.Context, client *lego.Client) {
provider, err := dns.NewDNSChallengeProviderByName(ctx.GlobalString("dns"))
if err != nil {
log.Panic(err)
}
servers := ctx.GlobalStringSlice("dns.resolvers")
err = client.Challenge.SetDNS01Provider(provider,
dns01.CondOption(len(servers) > 0,
dns01.AddRecursiveNameservers(dns01.ParseNameservers(ctx.GlobalStringSlice("dns.resolvers")))),
dns01.CondOption(ctx.GlobalBool("dns.disable-cp"),
dns01.DisableCompletePropagationRequirement()),
dns01.CondOption(ctx.GlobalIsSet("dns-timeout"),
dns01.AddDNSTimeout(time.Duration(ctx.GlobalInt("dns-timeout"))*time.Second)),
)
if err != nil {
log.Panic(err)
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,189 +0,0 @@
// Let's Encrypt client to go!
// CLI application for generating Let's Encrypt certificates using the ACME package.
package legocmd
import (
"errors"
"fmt"
"os"
"path"
"path/filepath"
"runtime"
"strings"
"github.com/XrayR-project/XrayR/common/legocmd/cmd"
"github.com/urfave/cli"
)
var version = "dev"
var defaultPath string
type LegoCMD struct {
cmdClient *cli.App
}
func New() (*LegoCMD, error) {
app := cli.NewApp()
app.Name = "lego"
app.HelpName = "lego"
app.Usage = "Let's Encrypt client written in Go"
app.EnableBashCompletion = true
app.Version = version
cli.VersionPrinter = func(c *cli.Context) {
fmt.Printf("lego version %s %s/%s\n", c.App.Version, runtime.GOOS, runtime.GOARCH)
}
// Set default path to configPath/cert
var path string = ""
configPath := os.Getenv("XRAY_LOCATION_CONFIG")
if configPath != "" {
path = configPath
} else if cwd, err := os.Getwd(); err==nil{
path = cwd
} else {
path = "."
}
defaultPath = filepath.Join(path, "cert")
app.Flags = cmd.CreateFlags(defaultPath)
app.Before = cmd.Before
app.Commands = cmd.CreateCommands()
lego := &LegoCMD{
cmdClient: app,
}
return lego, nil
}
// DNSCert cert a domain using DNS API
func (l *LegoCMD) DNSCert(domain, email, provider string, DNSEnv map[string]string) (CertPath string, KeyPath string, err error) {
defer func() (string, string, error) {
// Handle any error
if r := recover(); r != nil {
switch x := r.(type) {
case string:
err = errors.New(x)
case error:
err = x
default:
err = errors.New("unknow panic")
}
return "", "", err
}
return CertPath, KeyPath, nil
}()
// Set Env for DNS configuration
for key, value := range DNSEnv {
os.Setenv(key, value)
}
// First check if the certificate exists
CertPath, KeyPath, err = checkCertfile(domain)
if err == nil {
return CertPath, KeyPath, err
}
argstring := fmt.Sprintf("lego -a -d %s -m %s --dns %s run", domain, email, provider)
err = l.cmdClient.Run(strings.Split(argstring, " "))
if err != nil {
return "", "", err
}
CertPath, KeyPath, err = checkCertfile(domain)
if err != nil {
return "", "", err
}
return CertPath, KeyPath, nil
}
// HTTPCert cert a domain using http methods
func (l *LegoCMD) HTTPCert(domain, email string) (CertPath string, KeyPath string, err error) {
defer func() (string, string, error) {
// Handle any error
if r := recover(); r != nil {
switch x := r.(type) {
case string:
err = errors.New(x)
case error:
err = x
default:
err = errors.New("unknow panic")
}
return "", "", err
}
return CertPath, KeyPath, nil
}()
// First check if the certificate exists
CertPath, KeyPath, err = checkCertfile(domain)
if err == nil {
return CertPath, KeyPath, err
}
argstring := fmt.Sprintf("lego -a -d %s -m %s --http run", domain, email)
err = l.cmdClient.Run(strings.Split(argstring, " "))
if err != nil {
return "", "", err
}
CertPath, KeyPath, err = checkCertfile(domain)
if err != nil {
return "", "", err
}
return CertPath, KeyPath, nil
}
//RenewCert renew a domain cert
func (l *LegoCMD) RenewCert(domain, email, certMode, provider string, DNSEnv map[string]string) (CertPath string, KeyPath string, err error) {
var argstring string
defer func() (string, string, error) {
// Handle any error
if r := recover(); r != nil {
switch x := r.(type) {
case string:
err = errors.New(x)
case error:
err = x
default:
err = errors.New("unknow panic")
}
return "", "", err
}
return CertPath, KeyPath, nil
}()
if certMode == "http" {
argstring = fmt.Sprintf("lego -a -d %s -m %s --http renew --days 30", domain, email)
} else if certMode == "dns" {
// Set Env for DNS configuration
for key, value := range DNSEnv {
os.Setenv(key, value)
}
argstring = fmt.Sprintf("lego -a -d %s -m %s --dns %s renew --days 30", domain, email, provider)
} else {
return "", "", fmt.Errorf("Unsupport cert mode: %s", certMode)
}
err = l.cmdClient.Run(strings.Split(argstring, " "))
if err != nil {
return "", "", err
}
CertPath, KeyPath, err = checkCertfile(domain)
if err != nil {
return "", "", err
}
return CertPath, KeyPath, nil
}
func checkCertfile(domain string) (string, string, error) {
keyPath := path.Join(defaultPath, "certificates", fmt.Sprintf("%s.key", domain))
certPath := path.Join(defaultPath, "certificates", fmt.Sprintf("%s.crt", domain))
if _, err := os.Stat(keyPath); os.IsNotExist(err) {
return "", "", fmt.Errorf("Cert key failed: %s", domain)
}
if _, err := os.Stat(certPath); os.IsNotExist(err) {
return "", "", fmt.Errorf("Cert cert failed: %s", domain)
}
absKeyPath, _ := filepath.Abs(keyPath)
absCertPath, _ := filepath.Abs(certPath)
return absCertPath, absKeyPath, nil
}

View File

@@ -1,82 +0,0 @@
package legocmd_test
import (
"testing"
"github.com/XrayR-project/XrayR/common/legocmd"
)
func TestLegoClient(t *testing.T) {
_, err := legocmd.New()
if err != nil {
t.Error(err)
}
}
func TestLegoDNSCert(t *testing.T) {
lego, err := legocmd.New()
if err != nil {
t.Error(err)
}
var (
domain string = "node1.test.com"
email string = "test@gmail.com"
provider string = "alidns"
DNSEnv map[string]string
)
DNSEnv = make(map[string]string)
DNSEnv["ALICLOUD_ACCESS_KEY"] = "aaa"
DNSEnv["ALICLOUD_SECRET_KEY"] = "bbb"
certPath, keyPath, err := lego.DNSCert(domain, email, provider, DNSEnv)
if err != nil {
t.Error(err)
}
t.Log(certPath)
t.Log(keyPath)
}
func TestLegoHTTPCert(t *testing.T) {
lego, err := legocmd.New()
if err != nil {
t.Error(err)
}
var (
domain string = "node1.test.com"
email string = "test@gmail.com"
)
certPath, keyPath, err := lego.HTTPCert(domain, email)
if err != nil {
t.Error(err)
}
t.Log(certPath)
t.Log(keyPath)
}
func TestLegoRenewCert(t *testing.T) {
lego, err := legocmd.New()
if err != nil {
t.Error(err)
}
var (
domain string = "node1.test.com"
email string = "test@gmail.com"
provider string = "alidns"
DNSEnv map[string]string
)
DNSEnv = make(map[string]string)
DNSEnv["ALICLOUD_ACCESS_KEY"] = "aaa"
DNSEnv["ALICLOUD_SECRET_KEY"] = "bbb"
certPath, keyPath, err := lego.RenewCert(domain, email, "dns", provider, DNSEnv)
if err != nil {
t.Error(err)
}
t.Log(certPath)
t.Log(keyPath)
certPath, keyPath, err = lego.RenewCert(domain, email, "http", provider, DNSEnv)
if err != nil {
t.Error(err)
}
t.Log(certPath)
t.Log(keyPath)
}

View File

@@ -1,60 +0,0 @@
package log
import (
"log"
"os"
)
// Logger is an optional custom logger.
var Logger StdLogger = log.New(os.Stdout, "", log.LstdFlags)
// StdLogger interface for Standard Logger.
type StdLogger interface {
Panic(args ...interface{})
Fatalln(args ...interface{})
Panicf(format string, args ...interface{})
Print(args ...interface{})
Println(args ...interface{})
Printf(format string, args ...interface{})
}
// Panic writes a log entry.
// It uses Logger if not nil, otherwise it uses the default log.Logger.
func Panic(args ...interface{}) {
Logger.Panic(args...)
}
// Panicf writes a log entry.
// It uses Logger if not nil, otherwise it uses the default log.Logger.
func Panicf(format string, args ...interface{}) {
Logger.Panicf(format, args...)
}
// Print writes a log entry.
// It uses Logger if not nil, otherwise it uses the default log.Logger.
func Print(args ...interface{}) {
Logger.Print(args...)
}
// Println writes a log entry.
// It uses Logger if not nil, otherwise it uses the default log.Logger.
func Println(args ...interface{}) {
Logger.Println(args...)
}
// Printf writes a log entry.
// It uses Logger if not nil, otherwise it uses the default log.Logger.
func Printf(format string, args ...interface{}) {
Logger.Printf(format, args...)
}
// Warnf writes a log entry.
func Warnf(format string, args ...interface{}) {
Printf("[WARN] "+format, args...)
}
// Infof writes a log entry.
func Infof(format string, args ...interface{}) {
Printf("[INFO] "+format, args...)
}

View File

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

View File

@@ -1,13 +1,25 @@
// Package limiter is to control the links that go into the dispather
// Package limiter is to control the links that go into the dispatcher
package limiter
import (
"context"
"fmt"
"strconv"
"strings"
"sync"
"time"
"github.com/eko/gocache/lib/v4/cache"
"github.com/eko/gocache/lib/v4/marshaler"
"github.com/eko/gocache/lib/v4/store"
goCacheStore "github.com/eko/gocache/store/go_cache/v4"
redisStore "github.com/eko/gocache/store/redis/v4"
goCache "github.com/patrickmn/go-cache"
"github.com/redis/go-redis/v9"
"github.com/xtls/xray-core/common/errors"
"golang.org/x/time/rate"
"github.com/XrayR-project/XrayR/api"
"github.com/juju/ratelimit"
)
type UserInfo struct {
@@ -20,8 +32,12 @@ type InboundInfo struct {
Tag string
NodeSpeedLimit uint64
UserInfo *sync.Map // Key: Email value: UserInfo
BucketHub *sync.Map // key: Email, value: *ratelimit.Bucket
UserOnlineIP *sync.Map // Key: Email Value: *sync.Map: Key: IP, Value: UID
BucketHub *sync.Map // key: Email, value: *rate.Limiter
UserOnlineIP *sync.Map // Key: Email, value: {Key: IP, value: UID}
GlobalLimit struct {
config *GlobalDeviceLimitConfig
globalOnlineIP *marshaler.Marshaler
}
}
type Limiter struct {
@@ -34,13 +50,39 @@ func New() *Limiter {
}
}
func (l *Limiter) AddInboundLimiter(tag string, nodeSpeedLimit uint64, userList *[]api.UserInfo) error {
func (l *Limiter) AddInboundLimiter(tag string, nodeSpeedLimit uint64, userList *[]api.UserInfo, globalLimit *GlobalDeviceLimitConfig) error {
inboundInfo := &InboundInfo{
Tag: tag,
NodeSpeedLimit: nodeSpeedLimit,
BucketHub: new(sync.Map),
UserOnlineIP: new(sync.Map),
}
if globalLimit != nil && globalLimit.Enable {
inboundInfo.GlobalLimit.config = globalLimit
// init local store
gs := goCacheStore.NewGoCache(goCache.New(time.Duration(globalLimit.Expiry)*time.Second, 1*time.Minute))
// init redis store
rs := redisStore.NewRedis(redis.NewClient(
&redis.Options{
Network: globalLimit.RedisNetwork,
Addr: globalLimit.RedisAddr,
Username: globalLimit.RedisUsername,
Password: globalLimit.RedisPassword,
DB: globalLimit.RedisDB,
}),
store.WithExpiration(time.Duration(globalLimit.Expiry)*time.Second))
// init chained cache. First use local go-cache, if go-cache is nil, then use redis cache
cacheManager := cache.NewChain[any](
cache.New[any](gs), // go-cache is priority
cache.New[any](rs),
)
inboundInfo.GlobalLimit.globalOnlineIP = marshaler.New(cacheManager)
}
userMap := new(sync.Map)
for _, u := range *userList {
userMap.Store(fmt.Sprintf("%s|%s|%d", tag, u.Email, u.UID), UserInfo{
@@ -55,7 +97,6 @@ func (l *Limiter) AddInboundLimiter(tag string, nodeSpeedLimit uint64, userList
}
func (l *Limiter) UpdateInboundLimiter(tag string, updatedUserList *[]api.UserInfo) error {
if value, ok := l.InboundInfo.Load(tag); ok {
inboundInfo := value.(*InboundInfo)
// Update User info
@@ -65,7 +106,17 @@ func (l *Limiter) UpdateInboundLimiter(tag string, updatedUserList *[]api.UserIn
SpeedLimit: u.SpeedLimit,
DeviceLimit: u.DeviceLimit,
})
inboundInfo.BucketHub.Delete(fmt.Sprintf("%s|%s|%d", tag, u.Email, u.UID)) // Delete old limiter bucket
// Update old limiter bucket
limit := determineRate(inboundInfo.NodeSpeedLimit, u.SpeedLimit)
if limit > 0 {
if bucket, ok := inboundInfo.BucketHub.Load(fmt.Sprintf("%s|%s|%d", tag, u.Email, u.UID)); ok {
limiter := bucket.(*rate.Limiter)
limiter.SetLimit(rate.Limit(limit))
limiter.SetBurst(int(limit))
}
} else {
inboundInfo.BucketHub.Delete(fmt.Sprintf("%s|%s|%d", tag, u.Email, u.UID))
}
}
} else {
return fmt.Errorf("no such inbound in limiter: %s", tag)
@@ -79,7 +130,8 @@ func (l *Limiter) DeleteInboundLimiter(tag string) error {
}
func (l *Limiter) GetOnlineDevice(tag string) (*[]api.OnlineUser, error) {
onlineUser := make([]api.OnlineUser, 0)
var onlineUser []api.OnlineUser
if value, ok := l.InboundInfo.Load(tag); ok {
inboundInfo := value.(*InboundInfo)
// Clear Speed Limiter bucket for users who are not online
@@ -91,43 +143,48 @@ func (l *Limiter) GetOnlineDevice(tag string) (*[]api.OnlineUser, error) {
return true
})
inboundInfo.UserOnlineIP.Range(func(key, value interface{}) bool {
email := key.(string)
ipMap := value.(*sync.Map)
ipMap.Range(func(key, value interface{}) bool {
ip := key.(string)
uid := value.(int)
ip := key.(string)
onlineUser = append(onlineUser, api.OnlineUser{UID: uid, IP: ip})
return true
})
email := key.(string)
inboundInfo.UserOnlineIP.Delete(email) // Reset online device
return true
})
} else {
return nil, fmt.Errorf("no such inbound in limiter: %s", tag)
}
return &onlineUser, nil
}
func (l *Limiter) GetUserBucket(tag string, email string, ip string) (limiter *ratelimit.Bucket, SpeedLimit bool, Reject bool) {
func (l *Limiter) GetUserBucket(tag string, email string, ip string) (limiter *rate.Limiter, SpeedLimit bool, Reject bool) {
if value, ok := l.InboundInfo.Load(tag); ok {
var (
userLimit uint64 = 0
deviceLimit, uid int
)
inboundInfo := value.(*InboundInfo)
nodeLimit := inboundInfo.NodeSpeedLimit
var userLimit uint64 = 0
var deviceLimit int = 0
var uid int = 0
if v, ok := inboundInfo.UserInfo.Load(email); ok {
u := v.(UserInfo)
uid = u.UID
userLimit = u.SpeedLimit
deviceLimit = u.DeviceLimit
}
// Report online device
// Local device limit
ipMap := new(sync.Map)
ipMap.Store(ip, uid)
// If any device is online
if v, ok := inboundInfo.UserOnlineIP.LoadOrStore(email, ipMap); ok {
ipMap := v.(*sync.Map)
// If this ip is a new device
// If this is a new ip
if _, ok := ipMap.LoadOrStore(ip, uid); !ok {
counter := 0
ipMap.Range(func(key, value interface{}) bool {
@@ -140,11 +197,20 @@ func (l *Limiter) GetUserBucket(tag string, email string, ip string) (limiter *r
}
}
}
limit := determineRate(nodeLimit, userLimit) // If need the Speed limit
// GlobalLimit
if inboundInfo.GlobalLimit.config != nil && inboundInfo.GlobalLimit.config.Enable {
if reject := globalLimit(inboundInfo, email, uid, ip, deviceLimit); reject {
return nil, false, true
}
}
// Speed limit
limit := determineRate(nodeLimit, userLimit) // Determine the speed limit rate
if limit > 0 {
limiter := ratelimit.NewBucketWithQuantum(time.Duration(int64(time.Second)), int64(limit), int64(limit)) // Byte/s
limiter := rate.NewLimiter(rate.Limit(limit), int(limit)) // Byte/s
if v, ok := inboundInfo.BucketHub.LoadOrStore(email, limiter); ok {
bucket := v.(*ratelimit.Bucket)
bucket := v.(*rate.Limiter)
return bucket, true, false
} else {
return limiter, true, false
@@ -153,11 +219,56 @@ func (l *Limiter) GetUserBucket(tag string, email string, ip string) (limiter *r
return nil, false, false
}
} else {
newError("Get Inbound Limiter information failed").AtDebug().WriteToLog()
errors.LogDebug(context.Background(), "Get Inbound Limiter information failed")
return nil, false, false
}
}
// Global device limit
func globalLimit(inboundInfo *InboundInfo, email string, uid int, ip string, deviceLimit int) bool {
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(inboundInfo.GlobalLimit.config.Timeout)*time.Second)
defer cancel()
// reformat email for unique key
uniqueKey := strings.Replace(email, inboundInfo.Tag, strconv.Itoa(deviceLimit), 1)
v, err := inboundInfo.GlobalLimit.globalOnlineIP.Get(ctx, uniqueKey, new(map[string]int))
if err != nil {
if _, ok := err.(*store.NotFound); ok {
// If the email is a new device
go pushIP(inboundInfo, uniqueKey, &map[string]int{ip: uid})
} else {
errors.LogErrorInner(context.Background(), err, "cache service")
}
return false
}
ipMap := v.(*map[string]int)
// Reject device reach limit directly
if deviceLimit > 0 && len(*ipMap) > deviceLimit {
return true
}
// If the ip is not in cache
if _, ok := (*ipMap)[ip]; !ok {
(*ipMap)[ip] = uid
go pushIP(inboundInfo, uniqueKey, ipMap)
}
return false
}
// push the ip to cache
func pushIP(inboundInfo *InboundInfo, uniqueKey string, ipMap *map[string]int) {
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(inboundInfo.GlobalLimit.config.Timeout)*time.Second)
defer cancel()
if err := inboundInfo.GlobalLimit.globalOnlineIP.Set(ctx, uniqueKey, ipMap); err != nil {
errors.LogErrorInner(context.Background(), err, "cache service")
}
}
// determineRate returns the minimum non-zero rate
func determineRate(nodeLimit, userLimit uint64) (limit uint64) {
if nodeLimit == 0 || userLimit == 0 {

12
common/limiter/model.go Normal file
View File

@@ -0,0 +1,12 @@
package limiter
type GlobalDeviceLimitConfig struct {
Enable bool `mapstructure:"Enable"`
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"`
RedisDB int `mapstructure:"RedisDB"`
Timeout int `mapstructure:"Timeout"`
Expiry int `mapstructure:"Expiry"` // second
}

View File

@@ -1,20 +1,21 @@
package limiter
import (
"context"
"io"
"github.com/juju/ratelimit"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/buf"
"golang.org/x/time/rate"
)
type Writer struct {
writer buf.Writer
limiter *ratelimit.Bucket
limiter *rate.Limiter
w io.Writer
}
func (l *Limiter) RateWriter(writer buf.Writer, limiter *ratelimit.Bucket) buf.Writer {
func (l *Limiter) RateWriter(writer buf.Writer, limiter *rate.Limiter) buf.Writer {
return &Writer{
writer: writer,
limiter: limiter,
@@ -26,6 +27,7 @@ func (w *Writer) Close() error {
}
func (w *Writer) WriteMultiBuffer(mb buf.MultiBuffer) error {
w.limiter.Wait(int64(mb.Len()))
ctx := context.Background()
w.limiter.WaitN(ctx, int(mb.Len()))
return w.writer.WriteMultiBuffer(mb)
}

View File

@@ -1,4 +1,4 @@
package cmd
package mylego
import (
"crypto"

View File

@@ -1,4 +1,4 @@
package cmd
package mylego
import (
"crypto"
@@ -6,18 +6,17 @@ import (
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"io/ioutil"
"net/url"
"os"
"path/filepath"
"strings"
"github.com/XrayR-project/XrayR/common/legocmd/log"
log "github.com/sirupsen/logrus"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/go-acme/lego/v4/lego"
"github.com/go-acme/lego/v4/registration"
"github.com/urfave/cli"
"golang.org/x/crypto/acme"
)
const (
@@ -30,56 +29,53 @@ const (
//
// rootPath:
//
// ./.lego/accounts/
// │ └── root accounts directory
// └── "path" option
// ./.lego/accounts/
// │ └── root accounts directory
// └── "path" option
//
// rootUserPath:
//
// ./.lego/accounts/localhost_14000/hubert@hubert.com/
// │ │ │ └── userID ("email" option)
// │ │ └── CA server ("server" option)
// │ └── root accounts directory
// └── "path" option
// ./.lego/accounts/localhost_14000/hubert@hubert.com/
// │ │ │ └── userID ("email" option)
// │ │ └── CA server ("server" option)
// │ └── root accounts directory
// └── "path" option
//
// keysPath:
//
// ./.lego/accounts/localhost_14000/hubert@hubert.com/keys/
// │ │ │ │ └── root keys directory
// │ │ │ └── userID ("email" option)
// │ │ └── CA server ("server" option)
// │ └── root accounts directory
// └── "path" option
// ./.lego/accounts/localhost_14000/hubert@hubert.com/keys/
// │ │ │ │ └── root keys directory
// │ │ │ └── userID ("email" option)
// │ │ └── CA server ("server" option)
// │ └── root accounts directory
// └── "path" option
//
// accountFilePath:
//
// ./.lego/accounts/localhost_14000/hubert@hubert.com/account.json
// │ │ │ │ └── account file
// │ │ │ └── userID ("email" option)
// │ │ └── CA server ("server" option)
// │ └── root accounts directory
// └── "path" option
//
// ./.lego/accounts/localhost_14000/hubert@hubert.com/account.json
// │ │ │ │ └── account file
// │ │ │ └── userID ("email" option)
// │ │ └── CA server ("server" option)
// │ └── root accounts directory
// └── "path" option
type AccountsStorage struct {
userID string
rootPath string
rootUserPath string
keysPath string
accountFilePath string
ctx *cli.Context
}
// NewAccountsStorage Creates a new AccountsStorage.
func NewAccountsStorage(ctx *cli.Context) *AccountsStorage {
// TODO: move to account struct? Currently MUST pass email.
email := getEmail(ctx)
func NewAccountsStorage(l *LegoCMD) *AccountsStorage {
email := l.C.Email
serverURL, err := url.Parse(ctx.GlobalString("server"))
serverURL, err := url.Parse(acme.LetsEncryptURL)
if err != nil {
log.Panic(err)
}
rootPath := filepath.Join(ctx.GlobalString("path"), baseAccountsRootFolderName)
rootPath := filepath.Join(l.path, baseAccountsRootFolderName)
serverPath := strings.NewReplacer(":", "_", "/", string(os.PathSeparator)).Replace(serverURL.Host)
accountsPath := filepath.Join(rootPath, serverPath)
rootUserPath := filepath.Join(accountsPath, email)
@@ -90,7 +86,6 @@ func NewAccountsStorage(ctx *cli.Context) *AccountsStorage {
rootUserPath: rootUserPath,
keysPath: filepath.Join(rootUserPath, baseKeysFolderName),
accountFilePath: filepath.Join(rootUserPath, accountFileName),
ctx: ctx,
}
}
@@ -122,11 +117,11 @@ func (s *AccountsStorage) Save(account *Account) error {
return err
}
return ioutil.WriteFile(s.accountFilePath, jsonBytes, filePerm)
return os.WriteFile(s.accountFilePath, jsonBytes, filePerm)
}
func (s *AccountsStorage) LoadAccount(privateKey crypto.PrivateKey) *Account {
fileBytes, err := ioutil.ReadFile(s.accountFilePath)
fileBytes, err := os.ReadFile(s.accountFilePath)
if err != nil {
log.Panicf("Could not load file for account %s: %v", s.userID, err)
}
@@ -140,7 +135,7 @@ func (s *AccountsStorage) LoadAccount(privateKey crypto.PrivateKey) *Account {
account.key = privateKey
if account.Registration == nil || account.Registration.Body.Status == "" {
reg, err := tryRecoverRegistration(s.ctx, privateKey)
reg, err := tryRecoverRegistration(privateKey)
if err != nil {
log.Panicf("Could not load account for %s. Registration is nil: %#v", s.userID, err)
}
@@ -207,7 +202,7 @@ func generatePrivateKey(file string, keyType certcrypto.KeyType) (crypto.Private
}
func loadPrivateKey(file string) (crypto.PrivateKey, error) {
keyBytes, err := ioutil.ReadFile(file)
keyBytes, err := os.ReadFile(file)
if err != nil {
return nil, err
}
@@ -224,11 +219,11 @@ func loadPrivateKey(file string) (crypto.PrivateKey, error) {
return nil, errors.New("unknown private key type")
}
func tryRecoverRegistration(ctx *cli.Context, privateKey crypto.PrivateKey) (*registration.Resource, error) {
func tryRecoverRegistration(privateKey crypto.PrivateKey) (*registration.Resource, error) {
// couldn't load account but got a key. Try to look the account up.
config := lego.NewConfig(&Account{key: privateKey})
config.CADirURL = ctx.GlobalString("server")
config.UserAgent = fmt.Sprintf("lego-cli/%s", ctx.App.Version)
config.CADirURL = acme.LetsEncryptURL
config.UserAgent = "lego-cli/dev"
client, err := lego.NewClient(config)
if err != nil {

View File

@@ -1,56 +1,46 @@
package cmd
package mylego
import (
"bytes"
"crypto/x509"
"encoding/json"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/XrayR-project/XrayR/common/legocmd/log"
log "github.com/sirupsen/logrus"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/go-acme/lego/v4/certificate"
"github.com/urfave/cli"
"golang.org/x/net/idna"
)
const (
baseCertificatesFolderName = "certificates"
baseArchivesFolderName = "archives"
)
// CertificatesStorage a certificates storage.
// CertificatesStorage a certificates' storage.
//
// rootPath:
//
// ./.lego/certificates/
// │ └── root certificates directory
// └── "path" option
// ./.lego/certificates/
// │ └── root certificates directory
// └── "path" option
//
// archivePath:
//
// ./.lego/archives/
// │ └── archived certificates directory
// └── "path" option
//
// ./.lego/archives/
// │ └── archived certificates directory
// └── "path" option
type CertificatesStorage struct {
rootPath string
archivePath string
pem bool
filename string // Deprecated
rootPath string
pem bool
}
// NewCertificatesStorage create a new certificates storage.
func NewCertificatesStorage(ctx *cli.Context) *CertificatesStorage {
func NewCertificatesStorage(path string) *CertificatesStorage {
return &CertificatesStorage{
rootPath: filepath.Join(ctx.GlobalString("path"), baseCertificatesFolderName),
archivePath: filepath.Join(ctx.GlobalString("path"), baseArchivesFolderName),
pem: ctx.GlobalBool("pem"),
filename: ctx.GlobalString("filename"),
rootPath: filepath.Join(path, baseCertificatesFolderName),
}
}
@@ -61,13 +51,6 @@ func (s *CertificatesStorage) CreateRootFolder() {
}
}
func (s *CertificatesStorage) CreateArchiveFolder() {
err := createNonExistingFolder(s.archivePath)
if err != nil {
log.Panicf("Could not check/create path: %v", err)
}
}
func (s *CertificatesStorage) GetRootPath() string {
return s.rootPath
}
@@ -144,7 +127,7 @@ func (s *CertificatesStorage) ExistsFile(domain, extension string) bool {
}
func (s *CertificatesStorage) ReadFile(domain, extension string) ([]byte, error) {
return ioutil.ReadFile(s.GetFileName(domain, extension))
return os.ReadFile(s.GetFileName(domain, extension))
}
func (s *CertificatesStorage) GetFileName(domain, extension string) string {
@@ -163,36 +146,11 @@ func (s *CertificatesStorage) ReadCertificate(domain, extension string) ([]*x509
}
func (s *CertificatesStorage) WriteFile(domain, extension string, data []byte) error {
var baseFileName string
if s.filename != "" {
baseFileName = s.filename
} else {
baseFileName = sanitizedDomain(domain)
}
var baseFileName = sanitizedDomain(domain)
filePath := filepath.Join(s.rootPath, baseFileName+extension)
return ioutil.WriteFile(filePath, data, filePerm)
}
func (s *CertificatesStorage) MoveToArchive(domain string) error {
matches, err := filepath.Glob(filepath.Join(s.rootPath, sanitizedDomain(domain)+".*"))
if err != nil {
return err
}
for _, oldFile := range matches {
date := strconv.FormatInt(time.Now().Unix(), 10)
filename := date + "." + filepath.Base(oldFile)
newFile := filepath.Join(s.archivePath, filename)
err = os.Rename(oldFile, newFile)
if err != nil {
return err
}
}
return nil
return os.WriteFile(filePath, data, filePerm)
}
// sanitizedDomain Make sure no funny chars are in the cert names (like wildcards ;)).

View File

@@ -0,0 +1,87 @@
package mylego_test
import (
"testing"
"github.com/XrayR-project/XrayR/common/mylego"
)
func TestLegoClient(t *testing.T) {
_, err := mylego.New(&mylego.CertConfig{})
if err != nil {
t.Error(err)
}
}
func TestLegoDNSCert(t *testing.T) {
lego, err := mylego.New(&mylego.CertConfig{
CertDomain: "node1.test.com",
Provider: "alidns",
Email: "test@gmail.com",
DNSEnv: map[string]string{
"ALICLOUD_ACCESS_KEY": "aaa",
"ALICLOUD_SECRET_KEY": "bbb",
},
},
)
if err != nil {
t.Error(err)
}
certPath, keyPath, err := lego.DNSCert()
if err != nil {
t.Error(err)
}
t.Log(certPath)
t.Log(keyPath)
}
func TestLegoHTTPCert(t *testing.T) {
lego, err := mylego.New(&mylego.CertConfig{
CertMode: "http",
CertDomain: "node1.test.com",
Email: "test@gmail.com",
})
if err != nil {
t.Error(err)
}
certPath, keyPath, err := lego.HTTPCert()
if err != nil {
t.Error(err)
}
t.Log(certPath)
t.Log(keyPath)
}
func TestLegoRenewCert(t *testing.T) {
lego, err := mylego.New(&mylego.CertConfig{
CertDomain: "node1.test.com",
Email: "test@gmail.com",
Provider: "alidns",
DNSEnv: map[string]string{
"ALICLOUD_ACCESS_KEY": "aaa",
"ALICLOUD_SECRET_KEY": "bbb",
},
})
if err != nil {
t.Error(err)
}
lego.C.CertMode = "http"
certPath, keyPath, ok, err := lego.RenewCert()
if err != nil {
t.Error(err)
}
t.Log(certPath)
t.Log(keyPath)
t.Log(ok)
lego.C.CertMode = "dns"
certPath, keyPath, ok, err = lego.RenewCert()
if err != nil {
t.Error(err)
}
t.Log(certPath)
t.Log(keyPath)
t.Log(ok)
}

17
common/mylego/model.go Normal file
View File

@@ -0,0 +1,17 @@
package mylego
type CertConfig struct {
CertMode string `mapstructure:"CertMode"` // none, file, http, dns
CertDomain string `mapstructure:"CertDomain"`
CertFile string `mapstructure:"CertFile"`
KeyFile string `mapstructure:"KeyFile"`
Provider string `mapstructure:"Provider"` // alidns, cloudflare, gandi, godaddy....
Email string `mapstructure:"Email"`
DNSEnv map[string]string `mapstructure:"DNSEnv"`
RejectUnknownSni bool `mapstructure:"RejectUnknownSni"`
}
type LegoCMD struct {
C *CertConfig
path string
}

163
common/mylego/mylego.go Normal file
View File

@@ -0,0 +1,163 @@
package mylego
import (
"errors"
"fmt"
"os"
"path"
"path/filepath"
"strings"
)
var defaultPath string
func New(certConf *CertConfig) (*LegoCMD, error) {
// Set default path to configPath/cert
var p = ""
configPath := os.Getenv("XRAY_LOCATION_CONFIG")
if configPath != "" {
p = configPath
} else if cwd, err := os.Getwd(); err == nil {
p = cwd
} else {
p = "."
}
defaultPath = filepath.Join(p, "cert")
lego := &LegoCMD{
C: certConf,
path: defaultPath,
}
return lego, nil
}
func (l *LegoCMD) getPath() string {
return l.path
}
func (l *LegoCMD) getCertConfig() *CertConfig {
return l.C
}
// DNSCert cert a domain using DNS API
func (l *LegoCMD) DNSCert() (CertPath string, KeyPath string, err error) {
defer func() (string, string, error) {
// Handle any error
if r := recover(); r != nil {
switch x := r.(type) {
case string:
err = errors.New(x)
case error:
err = x
default:
err = errors.New("unknown panic")
}
return "", "", err
}
return CertPath, KeyPath, nil
}()
// Set Env for DNS configuration
for key, value := range l.C.DNSEnv {
os.Setenv(strings.ToUpper(key), value)
}
// First check if the certificate exists
CertPath, KeyPath, err = checkCertFile(l.C.CertDomain)
if err == nil {
return CertPath, KeyPath, err
}
err = l.Run()
if err != nil {
return "", "", err
}
CertPath, KeyPath, err = checkCertFile(l.C.CertDomain)
if err != nil {
return "", "", err
}
return CertPath, KeyPath, nil
}
// HTTPCert cert a domain using http methods
func (l *LegoCMD) HTTPCert() (CertPath string, KeyPath string, err error) {
defer func() (string, string, error) {
// Handle any error
if r := recover(); r != nil {
switch x := r.(type) {
case string:
err = errors.New(x)
case error:
err = x
default:
err = errors.New("unknown panic")
}
return "", "", err
}
return CertPath, KeyPath, nil
}()
// First check if the certificate exists
CertPath, KeyPath, err = checkCertFile(l.C.CertDomain)
if err == nil {
return CertPath, KeyPath, err
}
err = l.Run()
if err != nil {
return "", "", err
}
CertPath, KeyPath, err = checkCertFile(l.C.CertDomain)
if err != nil {
return "", "", err
}
return CertPath, KeyPath, nil
}
// RenewCert renew a domain cert
func (l *LegoCMD) RenewCert() (CertPath string, KeyPath string, ok bool, err error) {
defer func() (string, string, bool, error) {
// Handle any error
if r := recover(); r != nil {
switch x := r.(type) {
case string:
err = errors.New(x)
case error:
err = x
default:
err = errors.New("unknown panic")
}
return "", "", false, err
}
return CertPath, KeyPath, ok, nil
}()
ok, err = l.Renew()
if err != nil {
return
}
CertPath, KeyPath, err = checkCertFile(l.C.CertDomain)
if err != nil {
return
}
return
}
func checkCertFile(domain string) (string, string, error) {
keyPath := path.Join(defaultPath, "certificates", fmt.Sprintf("%s.key", sanitizedDomain(domain)))
certPath := path.Join(defaultPath, "certificates", fmt.Sprintf("%s.crt", sanitizedDomain(domain)))
if _, err := os.Stat(keyPath); os.IsNotExist(err) {
return "", "", fmt.Errorf("cert key failed: %s", domain)
}
if _, err := os.Stat(certPath); os.IsNotExist(err) {
return "", "", fmt.Errorf("cert cert failed: %s", domain)
}
absKeyPath, _ := filepath.Abs(keyPath)
absCertPath, _ := filepath.Abs(certPath)
return absCertPath, absKeyPath, nil
}

78
common/mylego/renew.go Normal file
View File

@@ -0,0 +1,78 @@
package mylego
import (
"crypto"
"crypto/x509"
"time"
log "github.com/sirupsen/logrus"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/go-acme/lego/v4/certificate"
"github.com/go-acme/lego/v4/lego"
)
func (l *LegoCMD) Renew() (bool, error) {
account, client := setup(NewAccountsStorage(l))
setupChallenges(l, client)
if account.Registration == nil {
log.Panicf("Account %s is not registered. Use 'run' to register a new account.\n", account.Email)
}
return renewForDomains(l.C.CertDomain, client, NewCertificatesStorage(l.path))
}
func renewForDomains(domain string, client *lego.Client, certsStorage *CertificatesStorage) (bool, error) {
// load the cert resource from files.
// We store the certificate, private key and metadata in different files
// as web servers would not be able to work with a combined file.
certificates, err := certsStorage.ReadCertificate(domain, ".crt")
if err != nil {
log.Panicf("Error while loading the certificate for domain %s\n\t%v", domain, err)
}
cert := certificates[0]
if !needRenewal(cert, domain, 30) {
return false, nil
}
// This is just meant to be informal for the user.
timeLeft := cert.NotAfter.Sub(time.Now().UTC())
log.Printf("[%s] acme: Trying renewal with %d hours remaining", domain, int(timeLeft.Hours()))
certDomains := certcrypto.ExtractDomains(cert)
var privateKey crypto.PrivateKey
request := certificate.ObtainRequest{
Domains: certDomains,
Bundle: true,
PrivateKey: privateKey,
}
certRes, err := client.Certificate.Obtain(request)
if err != nil {
log.Panic(err)
}
certsStorage.SaveResource(certRes)
return true, nil
}
func needRenewal(x509Cert *x509.Certificate, domain string, days int) bool {
if x509Cert.IsCA {
log.Panicf("[%s] Certificate bundle starts with a CA certificate", domain)
}
if days >= 0 {
notAfter := int(time.Until(x509Cert.NotAfter).Hours() / 24.0)
if notAfter > days {
log.Printf("[%s] The certificate expires in %d days, the number of days defined to perform the renewal is %d: no renewal.",
domain, notAfter, days)
return false
}
}
return true
}

View File

@@ -1,4 +1,4 @@
package cmd
package mylego
import (
"crypto/x509"
@@ -116,3 +116,19 @@ func Test_needRenewal(t *testing.T) {
})
}
}
func merge(prevDomains, nextDomains []string) []string {
for _, next := range nextDomains {
var found bool
for _, prev := range prevDomains {
if prev == next {
found = true
break
}
}
if !found {
prevDomains = append(prevDomains, next)
}
}
return prevDomains
}

68
common/mylego/run.go Normal file
View File

@@ -0,0 +1,68 @@
package mylego
import (
"fmt"
"github.com/go-acme/lego/v4/certificate"
"github.com/go-acme/lego/v4/lego"
"github.com/go-acme/lego/v4/registration"
log "github.com/sirupsen/logrus"
)
const rootPathWarningMessage = `!!!! HEADS UP !!!!
Your account credentials have been saved in your Let's Encrypt
configuration directory at "%s".
You should make a secure backup of this folder now. This
configuration directory will also contain certificates and
private keys obtained from Let's Encrypt so making regular
backups of this folder is ideal.
`
func (l *LegoCMD) Run() error {
accountsStorage := NewAccountsStorage(l)
account, client := setup(accountsStorage)
setupChallenges(l, client)
if account.Registration == nil {
reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
if err != nil {
log.Panicf("Could not complete registration\n\t%v", err)
}
account.Registration = reg
if err = accountsStorage.Save(account); err != nil {
log.Panic(err)
}
fmt.Printf(rootPathWarningMessage, accountsStorage.GetRootPath())
}
certsStorage := NewCertificatesStorage(l.path)
certsStorage.CreateRootFolder()
cert, err := obtainCertificate([]string{l.C.CertDomain}, client)
if err != nil {
// Make sure to return a non-zero exit code if ObtainSANCertificate returned at least one error.
// Due to us not returning partial certificate we can just exit here instead of at the end.
log.Panicf("Could not obtain certificates:\n\t%v", err)
}
certsStorage.SaveResource(cert)
return nil
}
func obtainCertificate(domains []string, client *lego.Client) (*certificate.Resource, error) {
if len(domains) > 0 {
// obtain a certificate, generating a new private key
request := certificate.ObtainRequest{
Domains: domains,
Bundle: true,
}
return client.Certificate.Obtain(request)
}
return nil, fmt.Errorf("not a valid domain")
}

96
common/mylego/setup.go Normal file
View File

@@ -0,0 +1,96 @@
package mylego
import (
"os"
"time"
log "github.com/sirupsen/logrus"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/go-acme/lego/v4/challenge/dns01"
"github.com/go-acme/lego/v4/challenge/http01"
"github.com/go-acme/lego/v4/challenge/tlsalpn01"
"github.com/go-acme/lego/v4/lego"
"github.com/go-acme/lego/v4/providers/dns"
"github.com/go-acme/lego/v4/registration"
"golang.org/x/crypto/acme"
)
const filePerm os.FileMode = 0o600
func setup(accountsStorage *AccountsStorage) (*Account, *lego.Client) {
keyType := certcrypto.EC256
privateKey := accountsStorage.GetPrivateKey(keyType)
var account *Account
if accountsStorage.ExistsAccountFilePath() {
account = accountsStorage.LoadAccount(privateKey)
} else {
account = &Account{Email: accountsStorage.GetUserID(), key: privateKey}
}
client := newClient(account, keyType)
return account, client
}
func newClient(acc registration.User, keyType certcrypto.KeyType) *lego.Client {
config := lego.NewConfig(acc)
config.CADirURL = acme.LetsEncryptURL
config.Certificate = lego.CertificateConfig{
KeyType: keyType,
Timeout: 30 * time.Second,
}
config.UserAgent = "lego-cli/dev"
client, err := lego.NewClient(config)
if err != nil {
log.Panicf("Could not create client: %v", err)
}
return client
}
func createNonExistingFolder(path string) error {
if _, err := os.Stat(path); os.IsNotExist(err) {
return os.MkdirAll(path, 0o700)
} else if err != nil {
return err
}
return nil
}
func setupChallenges(l *LegoCMD, client *lego.Client) {
switch l.C.CertMode {
case "http":
err := client.Challenge.SetHTTP01Provider(http01.NewProviderServer("", ""))
if err != nil {
log.Panic(err)
}
case "tls":
err := client.Challenge.SetTLSALPN01Provider(tlsalpn01.NewProviderServer("", ""))
if err != nil {
log.Panic(err)
}
case "dns":
setupDNS(l.C.Provider, client)
default:
log.Panic("No challenge selected. You must specify at least one challenge: `http`, `tls`, `dns`.")
}
}
func setupDNS(p string, client *lego.Client) {
provider, err := dns.NewDNSChallengeProviderByName(p)
if err != nil {
log.Panic(err)
}
err = client.Challenge.SetDNS01Provider(
provider,
dns01.CondOption(true, dns01.AddDNSTimeout(10*time.Second)),
)
if err != nil {
log.Panic(err)
}
}

View File

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

View File

@@ -2,29 +2,32 @@
package rule
import (
"context"
"fmt"
"reflect"
"strconv"
"strings"
"sync"
"github.com/XrayR-project/XrayR/api"
mapset "github.com/deckarep/golang-set"
"github.com/xtls/xray-core/common/errors"
"github.com/XrayR-project/XrayR/api"
)
type RuleManager struct {
type Manager struct {
InboundRule *sync.Map // Key: Tag, Value: []api.DetectRule
InboundDetectResult *sync.Map // key: Tag, Value: mapset.NewSet []api.DetectResult
}
func New() *RuleManager {
return &RuleManager{
func New() *Manager {
return &Manager{
InboundRule: new(sync.Map),
InboundDetectResult: new(sync.Map),
}
}
func (r *RuleManager) UpdateRule(tag string, newRuleList []api.DetectRule) error {
func (r *Manager) UpdateRule(tag string, newRuleList []api.DetectRule) error {
if value, ok := r.InboundRule.LoadOrStore(tag, newRuleList); ok {
oldRuleList := value.([]api.DetectRule)
if !reflect.DeepEqual(oldRuleList, newRuleList) {
@@ -34,7 +37,7 @@ func (r *RuleManager) UpdateRule(tag string, newRuleList []api.DetectRule) error
return nil
}
func (r *RuleManager) GetDetectResult(tag string) (*[]api.DetectResult, error) {
func (r *Manager) GetDetectResult(tag string) (*[]api.DetectResult, error) {
detectResult := make([]api.DetectResult, 0)
if value, ok := r.InboundDetectResult.LoadAndDelete(tag); ok {
resultSet := value.(mapset.Set)
@@ -46,9 +49,9 @@ func (r *RuleManager) GetDetectResult(tag string) (*[]api.DetectResult, error) {
return &detectResult, nil
}
func (r *RuleManager) Detect(tag string, destination string, email string) (reject bool) {
func (r *Manager) Detect(tag string, destination string, email string) (reject bool) {
reject = false
var hitRuleID int = -1
var hitRuleID = -1
// If we have some rule for this inbound
if value, ok := r.InboundRule.Load(tag); ok {
ruleList := value.([]api.DetectRule)
@@ -64,7 +67,7 @@ func (r *RuleManager) Detect(tag string, destination string, email string) (reje
l := strings.Split(email, "|")
uid, err := strconv.Atoi(l[len(l)-1])
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
}
newSet := mapset.NewSetWith(api.DetectResult{UID: uid, RuleID: hitRuleID})

View File

@@ -13,7 +13,7 @@ import (
// GetSystemInfo get the system info of a given periodic
func GetSystemInfo() (Cpu float64, Mem float64, Disk float64, Uptime uint64, err error) {
error_string := ""
errorString := ""
cpuPercent, err := cpu.Percent(0, false)
// Check if cpuPercent is empty
@@ -21,32 +21,32 @@ func GetSystemInfo() (Cpu float64, Mem float64, Disk float64, Uptime uint64, err
Cpu = cpuPercent[0]
} else {
Cpu = 0
error_string += fmt.Sprintf("get cpu usage failed: %s ", err)
errorString += fmt.Sprintf("get cpu usage failed: %s ", err)
}
memUsage, err := mem.VirtualMemory()
if err != nil {
error_string += fmt.Sprintf("get mem usage failed: %s ", err)
errorString += fmt.Sprintf("get mem usage failed: %s ", err)
} else {
Mem = memUsage.UsedPercent
}
diskUsage, err := disk.Usage("/")
if err != nil {
error_string += fmt.Sprintf("get disk usage failed: %s ", err)
errorString += fmt.Sprintf("get disk usage failed: %s ", err)
} else {
Disk = diskUsage.UsedPercent
}
uptime, err := host.Uptime()
if err != nil {
error_string += fmt.Sprintf("get uptime failed: %s ", err)
errorString += fmt.Sprintf("get uptime failed: %s ", err)
} else {
Uptime = uptime
}
if error_string != "" {
err = fmt.Errorf(error_string)
if errorString != "" {
err = fmt.Errorf(errorString)
}
return Cpu, Mem, Disk, Uptime, err

379
go.mod
View File

@@ -1,176 +1,309 @@
module github.com/XrayR-project/XrayR
go 1.18
go 1.22.0
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/fsnotify/fsnotify v1.5.4
github.com/go-acme/lego/v4 v4.8.0
github.com/go-resty/resty/v2 v2.7.0
github.com/imdario/mergo v0.3.13
github.com/juju/ratelimit v1.0.2
github.com/eko/gocache/lib/v4 v4.1.6
github.com/eko/gocache/store/go_cache/v4 v4.2.2
github.com/eko/gocache/store/redis/v4 v4.2.1
github.com/fsnotify/fsnotify v1.7.0
github.com/go-acme/lego/v4 v4.16.1
github.com/go-resty/resty/v2 v2.13.1
github.com/gogf/gf/v2 v2.7.0
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/r3labs/diff/v2 v2.15.1
github.com/shirou/gopsutil/v3 v3.22.7
github.com/spf13/viper v1.12.0
github.com/stretchr/testify v1.8.0
github.com/urfave/cli v1.22.9
github.com/xtls/xray-core v1.5.9
golang.org/x/net v0.0.0-20220812174116-3211cb980234
google.golang.org/protobuf v1.28.1
github.com/redis/go-redis/v9 v9.7.0
github.com/sagernet/sing v0.4.1
github.com/sagernet/sing-shadowsocks v0.2.7
github.com/shirou/gopsutil/v3 v3.24.5
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.8.1
github.com/spf13/viper v1.18.2
github.com/stretchr/testify v1.9.0
github.com/xtls/xray-core v1.8.20
golang.org/x/crypto v0.28.0
golang.org/x/net v0.28.0
golang.org/x/time v0.7.0
google.golang.org/protobuf v1.34.2
)
require (
cloud.google.com/go/compute v1.6.1 // indirect
github.com/Azure/azure-sdk-for-go v32.4.0+incompatible // indirect
cloud.google.com/go/compute/metadata v0.3.0 // indirect
github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 // 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/autorest v0.11.19 // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.13 // indirect
github.com/Azure/go-autorest/autorest/azure/auth v0.5.8 // indirect
github.com/Azure/go-autorest/autorest/azure/cli v0.4.2 // indirect
github.com/Azure/go-autorest/autorest v0.11.29 // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.23 // indirect
github.com/Azure/go-autorest/autorest/azure/auth v0.5.12 // 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/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/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/akamai/AkamaiOPEN-edgegrid-golang v1.1.1 // indirect
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1183 // indirect
github.com/aws/aws-sdk-go v1.39.0 // indirect
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
github.com/cenkalti/backoff/v4 v4.1.1 // indirect
github.com/cheekybits/genny v1.0.0 // indirect
github.com/cloudflare/cloudflare-go v0.20.0 // indirect
github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06 // indirect
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 // indirect
github.com/aliyun/alibaba-cloud-sdk-go v1.62.695 // indirect
github.com/andybalholm/brotli v1.1.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/boombuler/barcode v1.0.1 // indirect
github.com/bytedance/sonic v1.11.3 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // 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.3.9 // indirect
github.com/cloudflare/cloudflare-go v0.90.0 // indirect
github.com/cpu/goacmedns v0.1.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/deepmap/oapi-codegen v1.6.1 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // 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-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/dimchansky/utfbom v1.1.1 // indirect
github.com/dnsimple/dnsimple-go v0.70.1 // indirect
github.com/exoscale/egoscale v0.67.0 // indirect
github.com/dnsimple/dnsimple-go v1.7.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/form3tech-oss/jwt-go v3.2.2+incompatible // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/flosch/pongo2/v4 v4.0.2 // indirect
github.com/francoispqt/gojay v1.2.13 // indirect
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 // indirect
github.com/go-errors/errors v1.0.1 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
github.com/gofrs/uuid v3.2.0+incompatible // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/gin-gonic/gin v1.9.1 // indirect
github.com/go-errors/errors v1.5.1 // indirect
github.com/go-jose/go-jose/v4 v4.0.1 // indirect
github.com/go-logr/logr v1.4.1 // 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/protobuf v1.5.2 // indirect
github.com/golang/mock v1.7.0-rc.1 // 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/go-querystring v1.1.0 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/googleapis/gax-go/v2 v2.4.0 // indirect
github.com/gophercloud/gophercloud v0.16.0 // indirect
github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/pprof v0.0.0-20240528025155-186aa0362fba // indirect
github.com/google/s2a-go v0.1.7 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
github.com/googleapis/gax-go/v2 v2.12.3 // indirect
github.com/gophercloud/gophercloud v1.11.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-retryablehttp v0.7.1 // indirect
github.com/hashicorp/go-multierror v1.1.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/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/jarcoal/httpmock v1.0.8 // indirect
github.com/iris-contrib/schema v0.0.6 // 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/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 // indirect
github.com/klauspost/cpuid/v2 v2.1.0 // indirect
github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b // indirect
github.com/kataras/blocks v0.0.8 // indirect
github.com/kataras/golog v0.1.11 // 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/goh v1.0.1 // indirect
github.com/linode/linodego v0.31.1 // indirect
github.com/liquidweb/go-lwApi v0.0.5 // indirect
github.com/liquidweb/liquidweb-cli v0.6.9 // indirect
github.com/liquidweb/liquidweb-go v1.6.3 // indirect
github.com/lucas-clemente/quic-go v0.28.0 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect
github.com/marten-seemann/qtls-go1-17 v0.1.2 // indirect
github.com/marten-seemann/qtls-go1-18 v0.1.2 // indirect
github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/miekg/dns v1.1.50 // indirect
github.com/mimuret/golang-iij-dpf v0.7.1 // indirect
github.com/labstack/echo/v4 v4.11.4 // indirect
github.com/labstack/gommon v0.4.2 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/linode/linodego v1.30.0 // 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/mailgun/raymond/v2 v2.0.48 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // 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.61 // indirect
github.com/mimuret/golang-iij-dpf v0.9.1 // indirect
github.com/mitchellh/go-homedir v1.1.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/reflect2 v1.0.2 // indirect
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 // indirect
github.com/nrdcg/auroradns v1.0.1 // indirect
github.com/nrdcg/desec v0.6.0 // indirect
github.com/nrdcg/auroradns v1.1.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/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/porkbun v0.1.1 // indirect
github.com/nxadm/tail v1.4.8 // indirect
github.com/onsi/ginkgo v1.16.5 // indirect
github.com/nrdcg/nodion v0.1.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/ovh/go-ovh v1.1.0 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/ovh/go-ovh v1.4.3 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.0.1 // indirect
github.com/pires/go-proxyproto v0.6.2 // indirect
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
github.com/pires/go-proxyproto v0.7.0 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/pquerna/otp v1.3.0 // indirect
github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2 // indirect
github.com/refraction-networking/utls v1.1.0 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/pquerna/otp v1.4.0 // indirect
github.com/prometheus/client_golang v1.19.1 // indirect
github.com/prometheus/client_model v0.6.0 // indirect
github.com/prometheus/common v0.50.0 // indirect
github.com/prometheus/procfs v0.13.0 // indirect
github.com/quic-go/qpack v0.5.1 // indirect
github.com/quic-go/quic-go v0.48.2 // indirect
github.com/refraction-networking/utls v1.6.7 // indirect
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
github.com/russross/blackfriday/v2 v2.0.1 // indirect
github.com/sacloud/libsacloud v1.36.2 // indirect
github.com/sagernet/sing v0.0.0-20220714145306-09b55ce4b6d0 // indirect
github.com/sagernet/sing-shadowsocks v0.0.0-20220716012931-952ae62e05d7 // indirect
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210127161313-bd30bebeac4f // indirect
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb // indirect
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sacloud/api-client-go v0.2.10 // indirect
github.com/sacloud/go-http v0.1.8 // indirect
github.com/sacloud/iaas-api-go v1.11.2 // indirect
github.com/sacloud/packages-go v0.0.10 // indirect
github.com/sagikazarmark/locafero v0.4.0 // 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/softlayer/softlayer-go v1.0.3 // indirect
github.com/softlayer/softlayer-go v1.1.3 // indirect
github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect
github.com/spf13/afero v1.8.2 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/objx v0.4.0 // indirect
github.com/subosito/gotenv v1.3.0 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.287 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.287 // indirect
github.com/tklauser/go-sysconf v0.3.10 // indirect
github.com/tklauser/numcpus v0.4.0 // indirect
github.com/transip/gotransip/v6 v6.6.1 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/tdewolff/minify/v2 v2.20.19 // indirect
github.com/tdewolff/parse/v2 v2.7.12 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.878 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.878 // 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/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/vishvananda/netlink v1.2.1-beta.2.0.20230316163032-ced5aaba43e3 // indirect
github.com/vishvananda/netns v0.0.4 // indirect
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
github.com/vultr/govultr/v2 v2.16.0 // indirect
github.com/xtls/go v0.0.0-20210920065950-d4af136d3672 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
go.opencensus.io v0.23.0 // indirect
go.starlark.net v0.0.0-20220714194419-4cadf0a12139 // indirect
go.uber.org/ratelimit v0.0.0-20180316092928-c15da0234277 // indirect
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 // indirect
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6 // indirect
golang.org/x/tools v0.1.11 // indirect
google.golang.org/api v0.81.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20220715211116-798f69b842b9 // indirect
google.golang.org/grpc v1.48.0 // indirect
gopkg.in/ini.v1 v1.66.4 // indirect
gopkg.in/ns1/ns1-go.v2 v2.6.2 // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // 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/xtls/reality v0.0.0-20240712055506-48f0b2d5ed6d // indirect
github.com/yandex-cloud/go-genproto v0.0.0-20240311082839-58e1a7554a75 // indirect
github.com/yandex-cloud/go-sdk v0.0.0-20240311083148-81c0846b96cd // 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.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
go.opentelemetry.io/otel v1.24.0 // indirect
go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/otel/sdk v1.22.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/mock v0.4.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/ratelimit v0.3.1 // indirect
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
golang.org/x/arch v0.7.0 // indirect
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc // indirect
golang.org/x/mod v0.18.0 // indirect
golang.org/x/oauth2 v0.20.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.26.0 // indirect
golang.org/x/text v0.19.0 // indirect
golang.org/x/tools v0.22.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-20240528184218-531527333157 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect
google.golang.org/grpc v1.65.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/ns1/ns1-go.v2 v2.9.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
lukechampine.com/blake3 v1.1.7 // indirect
gvisor.dev/gvisor v0.0.0-20231202080848-1f7806d17489 // 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.3.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/linode/linodego => github.com/linode/linodego v0.31.1
replace github.com/exoscale/egoscale => github.com/exoscale/egoscale v0.67.0
replace github.com/exoscale/egoscale => github.com/exoscale/egoscale v0.102.3

1899
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,80 +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
ConnetionConfig:
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, 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
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, 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: "V2board" # Panel type: SSpanel, V2board
# 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

@@ -1,19 +0,0 @@
[
{
"listen": "0.0.0.0",
"port": 1234,
"protocol": "socks",
"settings": {
"auth": "noauth",
"accounts": [
{
"user": "my-username",
"pass": "my-password"
}
],
"udp": false,
"ip": "127.0.0.1",
"userLevel": 0
}
}
]

View File

@@ -1,28 +0,0 @@
[
{
"tag": "IPv4_out",
"protocol": "freedom",
"settings": {}
},
{
"tag": "IPv6_out",
"protocol": "freedom",
"settings": {
"domainStrategy": "UseIPv6"
}
},
{
"tag": "socks5-warp",
"protocol": "socks",
"settings": {
"servers": [{
"address": "127.0.0.1",
"port": 1080
}]
}
},
{
"protocol": "blackhole",
"tag": "block"
}
]

View File

@@ -1,8 +0,0 @@
{
"servers": [
"1.1.1.1",
"8.8.8.8",
"localhost"
],
"tag": "dns_inbound"
}

View File

@@ -1,36 +0,0 @@
{
"domainStrategy": "IPOnDemand",
"rules": [
{
"type": "field",
"outboundTag": "block",
"ip": [
"geoip:private"
]
},
{
"type": "field",
"outboundTag": "block",
"protocol": [
"bittorrent"
]
},
{
"type": "field",
"outboundTag": "socks5-warp",
"domain": []
},
{
"type": "field",
"outboundTag": "IPv6_out",
"domain": [
"geosite:netflix"
]
},
{
"type": "field",
"outboundTag": "IPv4_out",
"network": "udp,tcp"
}
]
}

View File

@@ -6,13 +6,13 @@ import (
)
type Config struct {
LogConfig *LogConfig `mapstructure:"Log"`
DnsConfigPath string `mapstructure:"DnsConfigPath"`
InboundConfigPath string `mapstructure:"InboundConfigPath"`
OutboundConfigPath string `mapstructure:"OutboundConfigPath"`
RouteConfigPath string `mapstructure:"RouteConfigPath"`
ConnetionConfig *ConnetionConfig `mapstructure:"ConnetionConfig"`
NodesConfig []*NodesConfig `mapstructure:"Nodes"`
LogConfig *LogConfig `mapstructure:"Log"`
DnsConfigPath string `mapstructure:"DnsConfigPath"`
InboundConfigPath string `mapstructure:"InboundConfigPath"`
OutboundConfigPath string `mapstructure:"OutboundConfigPath"`
RouteConfigPath string `mapstructure:"RouteConfigPath"`
ConnectionConfig *ConnectionConfig `mapstructure:"ConnectionConfig"`
NodesConfig []*NodesConfig `mapstructure:"Nodes"`
}
type NodesConfig struct {
@@ -27,7 +27,7 @@ type LogConfig struct {
ErrorPath string `mapstructure:"ErrorPath"`
}
type ConnetionConfig struct {
type ConnectionConfig struct {
Handshake uint32 `mapstructure:"handshake"`
ConnIdle uint32 `mapstructure:"connIdle"`
UplinkOnly uint32 `mapstructure:"uplinkOnly"`

View File

@@ -10,8 +10,8 @@ func getDefaultLogConfig() *LogConfig {
}
}
func getDefaultConnetionConfig() *ConnetionConfig {
return &ConnetionConfig{
func getDefaultConnectionConfig() *ConnectionConfig {
return &ConnectionConfig{
Handshake: 4,
ConnIdle: 30,
UplinkOnly: 2,

View File

@@ -2,28 +2,30 @@ package panel
import (
"encoding/json"
io "io/ioutil"
"log"
"os"
"sync"
"github.com/XrayR-project/XrayR/app/mydispatcher"
"github.com/XrayR-project/XrayR/api"
"github.com/XrayR-project/XrayR/api/pmpanel"
"github.com/XrayR-project/XrayR/api/proxypanel"
"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/main/distro/all"
"github.com/XrayR-project/XrayR/service"
"github.com/XrayR-project/XrayR/service/controller"
"github.com/imdario/mergo"
"dario.cat/mergo"
"github.com/r3labs/diff/v2"
log "github.com/sirupsen/logrus"
"github.com/xtls/xray-core/app/proxyman"
"github.com/xtls/xray-core/app/stats"
"github.com/xtls/xray-core/common/serial"
"github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/infra/conf"
"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/proxypanel"
"github.com/XrayR-project/XrayR/api/sspanel"
"github.com/XrayR-project/XrayR/api/v2raysocks"
"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/controller"
)
// Panel Structure
@@ -56,7 +58,7 @@ func (p *Panel) loadCore(panelConfig *Config) *core.Instance {
// DNS config
coreDnsConfig := &conf.DNSConfig{}
if panelConfig.DnsConfigPath != "" {
if data, err := io.ReadFile(panelConfig.DnsConfigPath); err != nil {
if data, err := os.ReadFile(panelConfig.DnsConfigPath); err != nil {
log.Panicf("Failed to read DNS config file at: %s", panelConfig.DnsConfigPath)
} else {
if err = json.Unmarshal(data, coreDnsConfig); err != nil {
@@ -64,14 +66,21 @@ func (p *Panel) loadCore(panelConfig *Config) *core.Instance {
}
}
}
// init controller's DNS config
// for _, config := range p.panelConfig.NodesConfig {
// config.ControllerConfig.DNSConfig = coreDnsConfig
// }
dnsConfig, err := coreDnsConfig.Build()
if err != nil {
log.Panicf("Failed to understand DNS config, Please check: https://xtls.github.io/config/dns.html for help: %s", err)
}
// Routing config
coreRouterConfig := &conf.RouterConfig{}
if panelConfig.RouteConfigPath != "" {
if data, err := io.ReadFile(panelConfig.RouteConfigPath); err != nil {
if data, err := os.ReadFile(panelConfig.RouteConfigPath); err != nil {
log.Panicf("Failed to read Routing config file at: %s", panelConfig.RouteConfigPath)
} else {
if err = json.Unmarshal(data, coreRouterConfig); err != nil {
@@ -84,9 +93,9 @@ func (p *Panel) loadCore(panelConfig *Config) *core.Instance {
log.Panicf("Failed to understand Routing config Please check: https://xtls.github.io/config/routing.html for help: %s", err)
}
// Custom Inbound config
coreCustomInboundConfig := []conf.InboundDetourConfig{}
var coreCustomInboundConfig []conf.InboundDetourConfig
if panelConfig.InboundConfigPath != "" {
if data, err := io.ReadFile(panelConfig.InboundConfigPath); err != nil {
if data, err := os.ReadFile(panelConfig.InboundConfigPath); err != nil {
log.Panicf("Failed to read Custom Inbound config file at: %s", panelConfig.OutboundConfigPath)
} else {
if err = json.Unmarshal(data, &coreCustomInboundConfig); err != nil {
@@ -94,7 +103,7 @@ func (p *Panel) loadCore(panelConfig *Config) *core.Instance {
}
}
}
inBoundConfig := []*core.InboundHandlerConfig{}
var inBoundConfig []*core.InboundHandlerConfig
for _, config := range coreCustomInboundConfig {
oc, err := config.Build()
if err != nil {
@@ -103,9 +112,9 @@ func (p *Panel) loadCore(panelConfig *Config) *core.Instance {
inBoundConfig = append(inBoundConfig, oc)
}
// Custom Outbound config
coreCustomOutboundConfig := []conf.OutboundDetourConfig{}
var coreCustomOutboundConfig []conf.OutboundDetourConfig
if panelConfig.OutboundConfigPath != "" {
if data, err := io.ReadFile(panelConfig.OutboundConfigPath); err != nil {
if data, err := os.ReadFile(panelConfig.OutboundConfigPath); err != nil {
log.Panicf("Failed to read Custom Outbound config file at: %s", panelConfig.OutboundConfigPath)
} else {
if err = json.Unmarshal(data, &coreCustomOutboundConfig); err != nil {
@@ -113,7 +122,7 @@ func (p *Panel) loadCore(panelConfig *Config) *core.Instance {
}
}
}
outBoundConfig := []*core.OutboundHandlerConfig{}
var outBoundConfig []*core.OutboundHandlerConfig
for _, config := range coreCustomOutboundConfig {
oc, err := config.Build()
if err != nil {
@@ -122,7 +131,7 @@ func (p *Panel) loadCore(panelConfig *Config) *core.Instance {
outBoundConfig = append(outBoundConfig, oc)
}
// Policy config
levelPolicyConfig := parseConnectionConfig(panelConfig.ConnetionConfig)
levelPolicyConfig := parseConnectionConfig(panelConfig.ConnectionConfig)
corePolicyConfig := &conf.PolicyConfig{}
corePolicyConfig.Levels = map[uint32]*conf.Policy{0: levelPolicyConfig}
policyConfig, _ := corePolicyConfig.Build()
@@ -145,12 +154,11 @@ func (p *Panel) loadCore(panelConfig *Config) *core.Instance {
if err != nil {
log.Panicf("failed to create instance: %s", err)
}
log.Printf("Xray Core Version: %s", core.Version())
return server
}
// Start Start the panel
// Start the panel
func (p *Panel) Start() {
p.access.Lock()
defer p.access.Unlock()
@@ -161,20 +169,25 @@ func (p *Panel) Start() {
log.Panicf("Failed to start instance: %s", err)
}
p.Server = server
// Load Nodes config
for _, nodeConfig := range p.panelConfig.NodesConfig {
var apiClient api.API
switch nodeConfig.PanelType {
case "SSpanel":
apiClient = sspanel.New(nodeConfig.ApiConfig)
case "V2board":
apiClient = v2board.New(nodeConfig.ApiConfig)
case "NewV2board", "V2board":
apiClient = newV2board.New(nodeConfig.ApiConfig)
case "PMpanel":
apiClient = pmpanel.New(nodeConfig.ApiConfig)
case "Proxypanel":
apiClient = proxypanel.New(nodeConfig.ApiConfig)
case "V2RaySocks":
apiClient = v2raysocks.New(nodeConfig.ApiConfig)
case "GoV2Panel":
apiClient = gov2panel.New(nodeConfig.ApiConfig)
case "BunPanel":
apiClient = bunpanel.New(nodeConfig.ApiConfig)
default:
log.Panicf("Unsupport panel type: %s", nodeConfig.PanelType)
}
@@ -195,21 +208,21 @@ func (p *Panel) Start() {
for _, s := range p.Service {
err := s.Start()
if err != nil {
log.Panicf("Panel Start fialed: %s", err)
log.Panicf("Panel Start failed: %s", err)
}
}
p.Running = true
return
}
// Close Close the panel
// Close the panel
func (p *Panel) Close() {
p.access.Lock()
defer p.access.Unlock()
for _, s := range p.Service {
err := s.Close()
if err != nil {
log.Panicf("Panel Close fialed: %s", err)
log.Panicf("Panel Close failed: %s", err)
}
}
p.Service = nil
@@ -218,21 +231,21 @@ func (p *Panel) Close() {
return
}
func parseConnectionConfig(c *ConnetionConfig) (policy *conf.Policy) {
connetionConfig := getDefaultConnetionConfig()
func parseConnectionConfig(c *ConnectionConfig) (policy *conf.Policy) {
connectionConfig := getDefaultConnectionConfig()
if c != nil {
if _, err := diff.Merge(connetionConfig, c, connetionConfig); err != nil {
log.Panicf("Read ConnetionConfig failed: %s", err)
if _, err := diff.Merge(connectionConfig, c, connectionConfig); err != nil {
log.Panicf("Read ConnectionConfig failed: %s", err)
}
}
policy = &conf.Policy{
StatsUserUplink: true,
StatsUserDownlink: true,
Handshake: &connetionConfig.Handshake,
ConnectionIdle: &connetionConfig.ConnIdle,
UplinkOnly: &connetionConfig.UplinkOnly,
DownlinkOnly: &connetionConfig.DownlinkOnly,
BufferSize: &connetionConfig.BufferSize,
Handshake: &connectionConfig.Handshake,
ConnectionIdle: &connectionConfig.ConnIdle,
UplinkOnly: &connectionConfig.UplinkOnly,
DownlinkOnly: &connectionConfig.DownlinkOnly,
BufferSize: &connectionConfig.BufferSize,
}
return

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

@@ -0,0 +1,19 @@
[
{
"listen": "127.0.0.1",
"port": 1234,
"protocol": "socks",
"settings": {
"auth": "noauth",
"accounts": [
{
"user": "my-username",
"pass": "my-password"
}
],
"udp": false,
"ip": "127.0.0.1",
"userLevel": 0
}
}
]

View File

@@ -0,0 +1,30 @@
[
{
"tag": "IPv4_out",
"protocol": "freedom",
"settings": {}
},
{
"tag": "IPv6_out",
"protocol": "freedom",
"settings": {
"domainStrategy": "UseIPv6"
}
},
{
"tag": "socks5-warp",
"protocol": "socks",
"settings": {
"servers": [
{
"address": "127.0.0.1",
"port": 1080
}
]
}
},
{
"protocol": "blackhole",
"tag": "block"
}
]

8
release/config/dns.json Normal file
View File

@@ -0,0 +1,8 @@
{
"servers": [
"1.1.1.1",
"8.8.8.8",
"localhost"
],
"tag": "dns_inbound"
}

36
release/config/route.json Normal file
View File

@@ -0,0 +1,36 @@
{
"domainStrategy": "IPOnDemand",
"rules": [
{
"type": "field",
"outboundTag": "block",
"ip": [
"geoip:private"
]
},
{
"type": "field",
"outboundTag": "block",
"protocol": [
"bittorrent"
]
},
{
"type": "field",
"outboundTag": "socks5-warp",
"domain": []
},
{
"type": "field",
"outboundTag": "IPv6_out",
"domain": [
"geosite:netflix"
]
},
{
"type": "field",
"outboundTag": "IPv4_out",
"network": "udp,tcp"
}
]
}

View File

@@ -1,30 +1,36 @@
package controller
import (
"github.com/XrayR-project/XrayR/common/limiter"
"github.com/XrayR-project/XrayR/common/mylego"
)
type Config struct {
ListenIP string `mapstructure:"ListenIP"`
SendIP string `mapstructure:"SendIP"`
UpdatePeriodic int `mapstructure:"UpdatePeriodic"`
CertConfig *CertConfig `mapstructure:"CertConfig"`
EnableDNS bool `mapstructure:"EnableDNS"`
DNSType string `mapstructure:"DNSType"`
DisableUploadTraffic bool `mapstructure:"DisableUploadTraffic"`
DisableGetRule bool `mapstructure:"DisableGetRule"`
EnableProxyProtocol bool `mapstructure:"EnableProxyProtocol"`
EnableFallback bool `mapstructure:"EnableFallback"`
DisableIVCheck bool `mapstructure:"DisableIVCheck"`
DisableSniffing bool `mapstructure:"DisableSniffing"`
FallBackConfigs []*FallBackConfig `mapstructure:"FallBackConfigs"`
ListenIP string `mapstructure:"ListenIP"`
SendIP string `mapstructure:"SendIP"`
UpdatePeriodic int `mapstructure:"UpdatePeriodic"`
CertConfig *mylego.CertConfig `mapstructure:"CertConfig"`
EnableDNS bool `mapstructure:"EnableDNS"`
DNSType string `mapstructure:"DNSType"`
DisableUploadTraffic bool `mapstructure:"DisableUploadTraffic"`
DisableGetRule bool `mapstructure:"DisableGetRule"`
EnableProxyProtocol bool `mapstructure:"EnableProxyProtocol"`
EnableFallback bool `mapstructure:"EnableFallback"`
DisableIVCheck bool `mapstructure:"DisableIVCheck"`
DisableSniffing bool `mapstructure:"DisableSniffing"`
AutoSpeedLimitConfig *AutoSpeedLimitConfig `mapstructure:"AutoSpeedLimitConfig"`
GlobalDeviceLimitConfig *limiter.GlobalDeviceLimitConfig `mapstructure:"GlobalDeviceLimitConfig"`
FallBackConfigs []*FallBackConfig `mapstructure:"FallBackConfigs"`
DisableLocalREALITYConfig bool `mapstructure:"DisableLocalREALITYConfig"`
EnableREALITY bool `mapstructure:"EnableREALITY"`
REALITYConfigs *REALITYConfig `mapstructure:"REALITYConfigs"`
}
type CertConfig struct {
CertMode string `mapstructure:"CertMode"` // none, file, http, dns
RejectUnknownSni bool `mapstructure:"RejectUnknownSni"`
CertDomain string `mapstructure:"CertDomain"`
CertFile string `mapstructure:"CertFile"`
KeyFile string `mapstructure:"KeyFile"`
Provider string `mapstructure:"Provider"` // alidns, cloudflare, gandi, godaddy....
Email string `mapstructure:"Email"`
DNSEnv map[string]string `mapstructure:"DNSEnv"`
type AutoSpeedLimitConfig struct {
Limit int `mapstructure:"Limit"` // mbps
WarnTimes int `mapstructure:"WarnTimes"`
LimitSpeed int `mapstructure:"LimitSpeed"` // mbps
LimitDuration int `mapstructure:"LimitDuration"` // minute
}
type FallBackConfig struct {
@@ -34,3 +40,15 @@ type FallBackConfig struct {
Dest string `mapstructure:"Dest"`
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

@@ -4,21 +4,24 @@ import (
"context"
"fmt"
"github.com/XrayR-project/XrayR/api"
"github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/features/inbound"
"github.com/xtls/xray-core/features/outbound"
"github.com/xtls/xray-core/features/stats"
"github.com/xtls/xray-core/proxy"
"github.com/XrayR-project/XrayR/api"
"github.com/XrayR-project/XrayR/common/limiter"
)
func (c *Controller) removeInbound(tag string) error {
err := c.ihm.RemoveHandler(context.Background(), tag)
err := c.ibm.RemoveHandler(context.Background(), tag)
return err
}
func (c *Controller) removeOutbound(tag string) error {
err := c.ohm.RemoveHandler(context.Background(), tag)
err := c.obm.RemoveHandler(context.Background(), tag)
return err
}
@@ -31,7 +34,7 @@ func (c *Controller) addInbound(config *core.InboundHandlerConfig) error {
if !ok {
return fmt.Errorf("not an InboundHandler: %s", err)
}
if err := c.ihm.AddHandler(context.Background(), handler); err != nil {
if err := c.ibm.AddHandler(context.Background(), handler); err != nil {
return err
}
return nil
@@ -46,16 +49,16 @@ func (c *Controller) addOutbound(config *core.OutboundHandlerConfig) error {
if !ok {
return fmt.Errorf("not an InboundHandler: %s", err)
}
if err := c.ohm.AddHandler(context.Background(), handler); err != nil {
if err := c.obm.AddHandler(context.Background(), handler); err != nil {
return err
}
return nil
}
func (c *Controller) addUsers(users []*protocol.User, tag string) error {
handler, err := c.ihm.GetHandler(context.Background(), tag)
handler, err := c.ibm.GetHandler(context.Background(), tag)
if err != nil {
return fmt.Errorf("No such inbound tag: %s", err)
return fmt.Errorf("no such inbound tag: %s", err)
}
inboundInstance, ok := handler.(proxy.GetInbound)
if !ok {
@@ -80,9 +83,9 @@ func (c *Controller) addUsers(users []*protocol.User, tag string) error {
}
func (c *Controller) removeUsers(users []string, tag string) error {
handler, err := c.ihm.GetHandler(context.Background(), tag)
handler, err := c.ibm.GetHandler(context.Background(), tag)
if err != nil {
return fmt.Errorf("No such inbound tag: %s", err)
return fmt.Errorf("no such inbound tag: %s", err)
}
inboundInstance, ok := handler.(proxy.GetInbound)
if !ok {
@@ -102,25 +105,35 @@ func (c *Controller) removeUsers(users []string, tag string) error {
return nil
}
func (c *Controller) getTraffic(email string) (up int64, down int64) {
func (c *Controller) getTraffic(email string) (up int64, down int64, upCounter stats.Counter, downCounter stats.Counter) {
upName := "user>>>" + email + ">>>traffic>>>uplink"
downName := "user>>>" + email + ">>>traffic>>>downlink"
upCounter := c.stm.GetCounter(upName)
downCounter := c.stm.GetCounter(downName)
if upCounter != nil {
upCounter = c.stm.GetCounter(upName)
downCounter = c.stm.GetCounter(downName)
if upCounter != nil && upCounter.Value() != 0 {
up = upCounter.Value()
upCounter.Set(0)
} else {
upCounter = nil
}
if downCounter != nil {
if downCounter != nil && downCounter.Value() != 0 {
down = downCounter.Value()
downCounter.Set(0)
} else {
downCounter = nil
}
return up, down
return up, down, upCounter, downCounter
}
func (c *Controller) AddInboundLimiter(tag string, nodeSpeedLimit uint64, userList *[]api.UserInfo) error {
err := c.dispatcher.Limiter.AddInboundLimiter(tag, nodeSpeedLimit, userList)
func (c *Controller) resetTraffic(upCounterList *[]stats.Counter, downCounterList *[]stats.Counter) {
for _, upCounter := range *upCounterList {
upCounter.Set(0)
}
for _, downCounter := range *downCounterList {
downCounter.Set(0)
}
}
func (c *Controller) AddInboundLimiter(tag string, nodeSpeedLimit uint64, userList *[]api.UserInfo, globalDeviceLimitConfig *limiter.GlobalDeviceLimitConfig) error {
err := c.dispatcher.Limiter.AddInboundLimiter(tag, nodeSpeedLimit, userList, globalDeviceLimitConfig)
return err
}

View File

@@ -1,15 +1,12 @@
package controller
import (
"errors"
"fmt"
"log"
"reflect"
"time"
"github.com/XrayR-project/XrayR/api"
"github.com/XrayR-project/XrayR/app/mydispatcher"
"github.com/XrayR-project/XrayR/common/legocmd"
"github.com/XrayR-project/XrayR/common/serverstatus"
log "github.com/sirupsen/logrus"
"github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/common/task"
"github.com/xtls/xray-core/core"
@@ -17,37 +14,64 @@ import (
"github.com/xtls/xray-core/features/outbound"
"github.com/xtls/xray-core/features/routing"
"github.com/xtls/xray-core/features/stats"
"github.com/XrayR-project/XrayR/api"
"github.com/XrayR-project/XrayR/app/mydispatcher"
"github.com/XrayR-project/XrayR/common/mylego"
"github.com/XrayR-project/XrayR/common/serverstatus"
)
type LimitInfo struct {
end int64
currentSpeedLimit int
originSpeedLimit uint64
}
type Controller struct {
server *core.Instance
config *Config
clientInfo api.ClientInfo
apiClient api.API
nodeInfo *api.NodeInfo
Tag string
userList *[]api.UserInfo
nodeInfoMonitorPeriodic *task.Periodic
userReportPeriodic *task.Periodic
panelType string
ihm inbound.Manager
ohm outbound.Manager
stm stats.Manager
dispatcher *mydispatcher.DefaultDispatcher
server *core.Instance
config *Config
clientInfo api.ClientInfo
apiClient api.API
nodeInfo *api.NodeInfo
Tag string
userList *[]api.UserInfo
tasks []periodicTask
limitedUsers map[api.UserInfo]LimitInfo
warnedUsers map[api.UserInfo]int
panelType string
ibm inbound.Manager
obm outbound.Manager
stm stats.Manager
dispatcher *mydispatcher.DefaultDispatcher
startAt time.Time
logger *log.Entry
}
type periodicTask struct {
tag string
*task.Periodic
}
// New return a Controller service with default parameters.
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{
server: server,
config: config,
apiClient: api,
panelType: panelType,
ihm: server.GetFeature(inbound.ManagerType()).(inbound.Manager),
ohm: server.GetFeature(outbound.ManagerType()).(outbound.Manager),
ibm: server.GetFeature(inbound.ManagerType()).(inbound.Manager),
obm: server.GetFeature(outbound.ManagerType()).(outbound.Manager),
stm: server.GetFeature(stats.ManagerType()).(stats.Manager),
dispatcher: server.GetFeature(routing.DispatcherType()).(*mydispatcher.DefaultDispatcher),
startAt: time.Now(),
logger: logger,
}
return controller
}
@@ -59,12 +83,16 @@ func (c *Controller) Start() error {
if err != nil {
return err
}
if newNodeInfo.Port == 0 {
return errors.New("server port must > 0")
}
c.nodeInfo = newNodeInfo
c.Tag = c.buildNodeTag()
// Add new tag
err = c.addNewTag(newNodeInfo)
if err != nil {
log.Panic(err)
c.logger.Panic(err)
return err
}
// Update user
@@ -73,186 +101,221 @@ func (c *Controller) Start() error {
return err
}
// sync controller userList
c.userList = userInfo
err = c.addNewUser(userInfo, newNodeInfo)
if err != nil {
return err
}
//sync controller userList
c.userList = userInfo
// Add Limiter
if err := c.AddInboundLimiter(c.Tag, newNodeInfo.SpeedLimit, userInfo); err != nil {
log.Print(err)
if err := c.AddInboundLimiter(c.Tag, newNodeInfo.SpeedLimit, userInfo, c.config.GlobalDeviceLimitConfig); err != nil {
c.logger.Print(err)
}
// Add Rule Manager
if !c.config.DisableGetRule {
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 {
if err := c.UpdateRule(c.Tag, *ruleList); err != nil {
log.Print(err)
c.logger.Print(err)
}
}
}
c.nodeInfoMonitorPeriodic = &task.Periodic{
Interval: time.Duration(c.config.UpdatePeriodic) * time.Second,
Execute: c.nodeInfoMonitor,
}
c.userReportPeriodic = &task.Periodic{
Interval: time.Duration(c.config.UpdatePeriodic) * time.Second,
Execute: c.userInfoMonitor,
}
log.Printf("[%s: %d] Start monitor node status", c.nodeInfo.NodeType, c.nodeInfo.NodeID)
// delay to start nodeInfoMonitor
go func() {
time.Sleep(time.Duration(c.config.UpdatePeriodic) * time.Second)
_ = c.nodeInfoMonitorPeriodic.Start()
}()
log.Printf("[%s: %d] Start report node status", c.nodeInfo.NodeType, c.nodeInfo.NodeID)
// delay to start userReport
go func() {
time.Sleep(time.Duration(c.config.UpdatePeriodic) * time.Second)
_ = c.userReportPeriodic.Start()
}()
// Init AutoSpeedLimitConfig
if c.config.AutoSpeedLimitConfig == nil {
c.config.AutoSpeedLimitConfig = &AutoSpeedLimitConfig{0, 0, 0, 0}
}
if c.config.AutoSpeedLimitConfig.Limit > 0 {
c.limitedUsers = make(map[api.UserInfo]LimitInfo)
c.warnedUsers = make(map[api.UserInfo]int)
}
// Add periodic tasks
c.tasks = append(c.tasks,
periodicTask{
tag: "node monitor",
Periodic: &task.Periodic{
Interval: time.Duration(c.config.UpdatePeriodic) * time.Second,
Execute: c.nodeInfoMonitor,
}},
periodicTask{
tag: "user monitor",
Periodic: &task.Periodic{
Interval: time.Duration(c.config.UpdatePeriodic) * time.Second,
Execute: c.userInfoMonitor,
}},
)
// Check cert service in need
if c.nodeInfo.EnableTLS && c.config.EnableREALITY == false {
c.tasks = append(c.tasks, periodicTask{
tag: "cert monitor",
Periodic: &task.Periodic{
Interval: time.Duration(c.config.UpdatePeriodic) * time.Second * 60,
Execute: c.certMonitor,
}})
}
// Start periodic tasks
for i := range c.tasks {
c.logger.Printf("Start %s periodic task", c.tasks[i].tag)
go c.tasks[i].Start()
}
return nil
}
// Close implement the Close() function of the service interface
func (c *Controller) Close() error {
if c.nodeInfoMonitorPeriodic != nil {
err := c.nodeInfoMonitorPeriodic.Close()
if err != nil {
log.Panicf("node info periodic close failed: %s", err)
for i := range c.tasks {
if c.tasks[i].Periodic != nil {
if err := c.tasks[i].Periodic.Close(); err != nil {
c.logger.Panicf("%s periodic task close failed: %s", c.tasks[i].tag, err)
}
}
}
if c.nodeInfoMonitorPeriodic != nil {
err := c.userReportPeriodic.Close()
if err != nil {
log.Panicf("user report periodic close failed: %s", err)
}
}
return nil
}
func (c *Controller) nodeInfoMonitor() (err error) {
// delay to start
if time.Since(c.startAt) < time.Duration(c.config.UpdatePeriodic)*time.Second {
return nil
}
// First fetch Node Info
var nodeInfoChanged = true
newNodeInfo, err := c.apiClient.GetNodeInfo()
if err != nil {
log.Print(err)
return nil
if err.Error() == api.NodeNotModified {
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
var usersChanged = true
newUserInfo, err := c.apiClient.GetUserList()
if err != nil {
log.Print(err)
return nil
if err.Error() == api.UserNotModified {
usersChanged = false
newUserInfo = c.userList
} else {
c.logger.Print(err)
return nil
}
}
var nodeInfoChanged = false
// If nodeInfo changed
if !reflect.DeepEqual(c.nodeInfo, newNodeInfo) {
// Remove old tag
oldtag := c.Tag
err := c.removeOldTag(oldtag)
if err != nil {
log.Print(err)
return nil
}
if c.nodeInfo.NodeType == "Shadowsocks-Plugin" {
err = c.removeOldTag(fmt.Sprintf("dokodemo-door_%s+1", c.Tag))
}
if err != nil {
log.Print(err)
return nil
}
// Add new tag
c.nodeInfo = newNodeInfo
c.Tag = c.buildNodeTag()
err = c.addNewTag(newNodeInfo)
if err != nil {
log.Print(err)
return nil
}
nodeInfoChanged = true
// Remove Old limiter
if err = c.DeleteInboundLimiter(oldtag); err != nil {
log.Print(err)
return nil
if nodeInfoChanged {
if !reflect.DeepEqual(c.nodeInfo, newNodeInfo) {
// Remove old tag
oldTag := c.Tag
err := c.removeOldTag(oldTag)
if err != 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 err != nil {
c.logger.Print(err)
return nil
}
// Add new tag
c.nodeInfo = newNodeInfo
c.Tag = c.buildNodeTag()
err = c.addNewTag(newNodeInfo)
if err != nil {
c.logger.Print(err)
return nil
}
nodeInfoChanged = true
// Remove Old limiter
if err = c.DeleteInboundLimiter(oldTag); err != nil {
c.logger.Print(err)
return nil
}
} else {
nodeInfoChanged = false
}
}
// Check Rule
if !c.config.DisableGetRule {
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 {
if err := c.UpdateRule(c.Tag, *ruleList); err != nil {
log.Print(err)
c.logger.Print(err)
}
}
}
// Check Cert
if c.nodeInfo.EnableTLS && (c.config.CertConfig.CertMode == "dns" || c.config.CertConfig.CertMode == "http") {
lego, err := legocmd.New()
if err != nil {
log.Print(err)
}
// Xray-core supports the OcspStapling certification hot renew
_, _, err = lego.RenewCert(c.config.CertConfig.CertDomain, c.config.CertConfig.Email, c.config.CertConfig.CertMode, c.config.CertConfig.Provider, c.config.CertConfig.DNSEnv)
if err != nil {
log.Print(err)
}
}
if nodeInfoChanged {
err = c.addNewUser(newUserInfo, newNodeInfo)
if err != nil {
log.Print(err)
c.logger.Print(err)
return nil
}
// Add Limiter
if err := c.AddInboundLimiter(c.Tag, newNodeInfo.SpeedLimit, newUserInfo); err != nil {
log.Print(err)
if err := c.AddInboundLimiter(c.Tag, newNodeInfo.SpeedLimit, newUserInfo, c.config.GlobalDeviceLimitConfig); err != nil {
c.logger.Print(err)
return nil
}
} else {
deleted, added := compareUserList(c.userList, newUserInfo)
if len(deleted) > 0 {
deletedEmail := make([]string, len(deleted))
for i, u := range deleted {
deletedEmail[i] = fmt.Sprintf("%s|%s|%d", c.Tag, u.Email, u.UID)
var deleted, added []api.UserInfo
if usersChanged {
deleted, added = compareUserList(c.userList, newUserInfo)
if len(deleted) > 0 {
deletedEmail := make([]string, len(deleted))
for i, u := range deleted {
deletedEmail[i] = fmt.Sprintf("%s|%s|%d", c.Tag, u.Email, u.UID)
}
err := c.removeUsers(deletedEmail, c.Tag)
if err != nil {
c.logger.Print(err)
}
}
err := c.removeUsers(deletedEmail, c.Tag)
if err != nil {
log.Print(err)
if len(added) > 0 {
err = c.addNewUser(&added, c.nodeInfo)
if err != nil {
c.logger.Print(err)
}
// Update Limiter
if err := c.UpdateInboundLimiter(c.Tag, &added); err != nil {
c.logger.Print(err)
}
}
}
if len(added) > 0 {
err = c.addNewUser(&added, c.nodeInfo)
if err != nil {
log.Print(err)
}
// Update Limiter
if err := c.UpdateInboundLimiter(c.Tag, &added); err != nil {
log.Print(err)
}
}
log.Printf("[%s: %d] %d user deleted, %d user added", c.nodeInfo.NodeType, c.nodeInfo.NodeID, len(deleted), len(added))
c.logger.Printf("%d user deleted, %d user added", len(deleted), len(added))
}
c.userList = newUserInfo
return nil
}
func (c *Controller) removeOldTag(oldtag string) (err error) {
err = c.removeInbound(oldtag)
func (c *Controller) removeOldTag(oldTag string) (err error) {
err = c.removeInbound(oldTag)
if err != nil {
return err
}
err = c.removeOutbound(oldtag)
err = c.removeOutbound(oldTag)
if err != nil {
return err
}
@@ -288,7 +351,7 @@ func (c *Controller) addNewTag(newNodeInfo *api.NodeInfo) (err error) {
}
func (c *Controller) addInboundForSSPlugin(newNodeInfo api.NodeInfo) (err error) {
// Shadowsocks-Plugin require a seaperate inbound for other TransportProtocol likes: ws, grpc
// Shadowsocks-Plugin require a separate inbound for other TransportProtocol likes: ws, grpc
fakeNodeInfo := newNodeInfo
fakeNodeInfo.TransportProtocol = "tcp"
fakeNodeInfo.EnableTLS = false
@@ -341,64 +404,59 @@ func (c *Controller) addInboundForSSPlugin(newNodeInfo api.NodeInfo) (err error)
func (c *Controller) addNewUser(userInfo *[]api.UserInfo, nodeInfo *api.NodeInfo) (err error) {
users := make([]*protocol.User, 0)
if nodeInfo.NodeType == "V2ray" {
if nodeInfo.EnableVless {
switch nodeInfo.NodeType {
case "V2ray", "Vmess", "Vless":
if nodeInfo.EnableVless || (nodeInfo.NodeType == "Vless" && nodeInfo.NodeType != "Vmess") {
users = c.buildVlessUser(userInfo)
} else {
var alterID uint16 = 0
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)
users = c.buildVmessUser(userInfo)
}
} else if nodeInfo.NodeType == "Trojan" {
case "Trojan":
users = c.buildTrojanUser(userInfo)
} else if nodeInfo.NodeType == "Shadowsocks" {
case "Shadowsocks":
users = c.buildSSUser(userInfo, nodeInfo.CypherMethod)
} else if nodeInfo.NodeType == "Shadowsocks-Plugin" {
case "Shadowsocks-Plugin":
users = c.buildSSPluginUser(userInfo)
} else {
default:
return fmt.Errorf("unsupported node type: %s", nodeInfo.NodeType)
}
err = c.addUsers(users, c.Tag)
if err != nil {
return err
}
log.Printf("[%s: %d] Added %d new users", c.nodeInfo.NodeType, c.nodeInfo.NodeID, len(*userInfo))
c.logger.Printf("Added %d new users", len(*userInfo))
return nil
}
func compareUserList(old, new *[]api.UserInfo) (deleted, added []api.UserInfo) {
msrc := make(map[api.UserInfo]byte) //按源数组建索引
mall := make(map[api.UserInfo]byte) //源+目所有元素建索引
mSrc := make(map[api.UserInfo]byte) // 按源数组建索引
mAll := make(map[api.UserInfo]byte) // 源+目所有元素建索引
var set []api.UserInfo //交集
var set []api.UserInfo // 交集
//1.源数组建立map
// 1.源数组建立map
for _, v := range *old {
msrc[v] = 0
mall[v] = 0
mSrc[v] = 0
mAll[v] = 0
}
//2.目数组中,存不进去,即重复元素,所有存不进去的集合就是并集
// 2.目数组中,存不进去,即重复元素,所有存不进去的集合就是并集
for _, v := range *new {
l := len(mall)
mall[v] = 1
if l != len(mall) { //长度变化,即可以存
l = len(mall)
} else { //存不了,进并集
l := len(mAll)
mAll[v] = 1
if l != len(mAll) { // 长度变化,即可以存
l = len(mAll)
} else { // 存不了,进并集
set = append(set, v)
}
}
//3.遍历交集,在并集中找,找到就从并集中删,删完后就是补集(即并-交=所有变化的元素)
// 3.遍历交集,在并集中找,找到就从并集中删,删完后就是补集(即并-交=所有变化的元素)
for _, v := range set {
delete(mall, v)
delete(mAll, v)
}
//4.此时mall是补集所有元素去源中找找到就是删除的找不到的必定能在目数组中找到即新加的
for v := range mall {
_, exist := msrc[v]
// 4.此时mall是补集所有元素去源中找找到就是删除的找不到的必定能在目数组中找到即新加的
for v := range mAll {
_, exist := mSrc[v]
if exist {
deleted = append(deleted, v)
} else {
@@ -409,11 +467,27 @@ func compareUserList(old, new *[]api.UserInfo) (deleted, added []api.UserInfo) {
return deleted, added
}
func limitUser(c *Controller, user api.UserInfo, silentUsers *[]api.UserInfo) {
c.limitedUsers[user] = LimitInfo{
end: time.Now().Unix() + int64(c.config.AutoSpeedLimitConfig.LimitDuration*60),
currentSpeedLimit: c.config.AutoSpeedLimitConfig.LimitSpeed,
originSpeedLimit: user.SpeedLimit,
}
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)
*silentUsers = append(*silentUsers, user)
}
func (c *Controller) userInfoMonitor() (err error) {
// delay to start
if time.Since(c.startAt) < time.Duration(c.config.UpdatePeriodic)*time.Second {
return nil
}
// Get server status
CPU, Mem, Disk, Uptime, err := serverstatus.GetSystemInfo()
if err != nil {
log.Print(err)
c.logger.Print(err)
}
err = c.apiClient.ReportNodeStatus(
&api.NodeStatus{
@@ -423,46 +497,110 @@ func (c *Controller) userInfoMonitor() (err error) {
Uptime: Uptime,
})
if err != nil {
log.Print(err)
c.logger.Print(err)
}
// Unlock users
if c.config.AutoSpeedLimitConfig.Limit > 0 && len(c.limitedUsers) > 0 {
c.logger.Printf("Limited users:")
toReleaseUsers := make([]api.UserInfo, 0)
for user, limitInfo := range c.limitedUsers {
if time.Now().Unix() > limitInfo.end {
user.SpeedLimit = limitInfo.originSpeedLimit
toReleaseUsers = append(toReleaseUsers, user)
c.logger.Printf("User: %s Speed: %d End: nil (Unlimit)", c.buildUserTag(&user), user.SpeedLimit)
delete(c.limitedUsers, user)
} else {
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 err := c.UpdateInboundLimiter(c.Tag, &toReleaseUsers); err != nil {
c.logger.Print(err)
}
}
}
// Get User traffic
userTraffic := make([]api.UserTraffic, 0)
var userTraffic []api.UserTraffic
var upCounterList []stats.Counter
var downCounterList []stats.Counter
AutoSpeedLimit := int64(c.config.AutoSpeedLimitConfig.Limit)
UpdatePeriodic := int64(c.config.UpdatePeriodic)
limitedUsers := make([]api.UserInfo, 0)
for _, user := range *c.userList {
up, down := c.getTraffic(c.buildUserTag(&user))
up, down, upCounter, downCounter := c.getTraffic(c.buildUserTag(&user))
if up > 0 || down > 0 {
// Over speed users
if AutoSpeedLimit > 0 {
if down > AutoSpeedLimit*1000000*UpdatePeriodic/8 || up > AutoSpeedLimit*1000000*UpdatePeriodic/8 {
if _, ok := c.limitedUsers[user]; !ok {
if c.config.AutoSpeedLimitConfig.WarnTimes == 0 {
limitUser(c, user, &limitedUsers)
} else {
c.warnedUsers[user] += 1
if c.warnedUsers[user] > c.config.AutoSpeedLimitConfig.WarnTimes {
limitUser(c, user, &limitedUsers)
delete(c.warnedUsers, user)
}
}
}
} else {
delete(c.warnedUsers, user)
}
}
userTraffic = append(userTraffic, api.UserTraffic{
UID: user.UID,
Email: user.Email,
Upload: up,
Download: down})
if upCounter != nil {
upCounterList = append(upCounterList, upCounter)
}
if downCounter != nil {
downCounterList = append(downCounterList, downCounter)
}
} else {
delete(c.warnedUsers, user)
}
}
if len(userTraffic) > 0 && !c.config.DisableUploadTraffic {
err = c.apiClient.ReportUserTraffic(&userTraffic)
if len(limitedUsers) > 0 {
if err := c.UpdateInboundLimiter(c.Tag, &limitedUsers); err != nil {
c.logger.Print(err)
}
}
if len(userTraffic) > 0 {
var err error // Define an empty error
if !c.config.DisableUploadTraffic {
err = c.apiClient.ReportUserTraffic(&userTraffic)
}
// If report traffic error, not clear the traffic
if err != nil {
log.Print(err)
c.logger.Print(err)
} else {
c.resetTraffic(&upCounterList, &downCounterList)
}
}
// Report Online info
if onlineDevice, err := c.GetOnlineDevice(c.Tag); err != nil {
log.Print(err)
c.logger.Print(err)
} else if len(*onlineDevice) > 0 {
if err = c.apiClient.ReportNodeOnlineUsers(onlineDevice); err != nil {
log.Print(err)
c.logger.Print(err)
} else {
log.Printf("[%s: %d] Report %d online users", c.nodeInfo.NodeType, c.nodeInfo.NodeID, len(*onlineDevice))
c.logger.Printf("Report %d online users", len(*onlineDevice))
}
}
// Report Illegal user
if detectResult, err := c.GetDetectResult(c.Tag); err != nil {
log.Print(err)
c.logger.Print(err)
} else if len(*detectResult) > 0 {
if err = c.apiClient.ReportIllegal(detectResult); err != nil {
log.Print(err)
c.logger.Print(err)
} else {
log.Printf("[%s: %d] Report %d illegal behaviors", c.nodeInfo.NodeType, c.nodeInfo.NodeID, len(*detectResult))
c.logger.Printf("Report %d illegal behaviors", len(*detectResult))
}
}
@@ -472,3 +610,26 @@ func (c *Controller) userInfoMonitor() (err error) {
func (c *Controller) buildNodeTag() string {
return fmt.Sprintf("%s_%s_%d", c.nodeInfo.NodeType, c.config.ListenIP, c.nodeInfo.Port)
}
// func (c *Controller) logPrefix() string {
// return fmt.Sprintf("[%s] %s(ID=%d)", c.clientInfo.APIHost, c.nodeInfo.NodeType, c.nodeInfo.NodeID)
// }
// Check Cert
func (c *Controller) certMonitor() error {
if c.nodeInfo.EnableTLS && c.config.EnableREALITY == false {
switch c.config.CertConfig.CertMode {
case "dns", "http", "tls":
lego, err := mylego.New(c.config.CertConfig)
if err != nil {
c.logger.Print(err)
}
// Xray-core supports the OcspStapling certification hot renew
_, _, _, err = lego.RenewCert()
if err != nil {
c.logger.Print(err)
}
}
}
return nil
}

Some files were not shown because too many files have changed in this diff Show More