diff --git a/README.md b/README.md
index 1f5fdcd8..8c3bb5c8 100644
--- a/README.md
+++ b/README.md
@@ -79,6 +79,8 @@ Set the robot-related parameters in the panel background, including:
Reference syntax:
+- 30 * * * * * //Notify at the 30s of each point
+- 0 */10 * * * * //Notify at the first second of each 10 minutes
- @hourly // hourly notification
- @daily // Daily notification (00:00 in the morning)
- @every 8h // notify every 8 hours
@@ -89,18 +91,40 @@ Reference syntax:
- Login notification
- CPU threshold notification
- Threshold for Expiration time and Traffic to report in advance
-- Support client report if client's telegram username is added to the end of `email` like 'test123@telegram_username'
+- Support client report menu if client's telegram username added to the user's configurations
- Support telegram traffic report searched with UID (VMESS/VLESS) or Password (TROJAN) - anonymously
- Menu based bot
- Search client by email ( only admin )
- Check all inbounds
- Check server status
-- Check Exhausted users
+- Check depleted users
- Receive backup by request and in periodic reports
+
+## API routes
+
+- `/login` with `PUSH` user data: `{username: '', password: ''}` for login
+- `/xui/API/inbounds` base for following actions:
+
+| Method | Path | Action |
+| ------------- | ------------- | ------------- |
+| GET | "/list" | Get all inbounds |
+| GET | "/get/:id" | Get inbound with inbound.id |
+| POST | "/add" | Add inbound |
+| POST | "/del/:id" | Delete Inbound |
+| POST | "/update/:id" | Update Inbound |
+| POST | "/clientIps/:email" | Client Ip address |
+| POST | "/clearClientIps/:email" | Clear Client Ip address |
+| POST | "/addClient/" | Add Client to inbound |
+| POST | "/delClient/:email" | Delete Client |
+| POST | "/updateClient/:index" | Update Client |
+| POST | "/:id/resetClientTraffic/:email" | Reset Client's Traffic |
+| POST | "/resetAllTraffics" | Reset traffics of all inbounds |
+| POST | "/resetAllClientTraffics/:id" | Reset traffics of all clients in an inbound |
+
# A Special Thanks To
- [alireza0](https://github.com/alireza0/)
-- [HexaSoftwareTech](https://github.com/HexaSoftwareTech/)
+- [FranzKafkaYu](https://github.com/FranzKafkaYu)
# Suggestion System
- Ubuntu 20.04+
diff --git a/config/version b/config/version
index 65087b4f..23aa8390 100644
--- a/config/version
+++ b/config/version
@@ -1 +1 @@
-1.1.4
+1.2.2
diff --git a/database/db.go b/database/db.go
index f7a590b2..b9c16be8 100644
--- a/database/db.go
+++ b/database/db.go
@@ -92,7 +92,7 @@ func InitDB(dbPath string) error {
if err != nil {
return err
}
-
+
return nil
}
diff --git a/database/model/model.go b/database/model/model.go
index 606b922c..778ad9b6 100644
--- a/database/model/model.go
+++ b/database/model/model.go
@@ -44,9 +44,9 @@ type Inbound struct {
Sniffing string `json:"sniffing" form:"sniffing"`
}
type InboundClientIps struct {
- Id int `json:"id" gorm:"primaryKey;autoIncrement"`
+ Id int `json:"id" gorm:"primaryKey;autoIncrement"`
ClientEmail string `json:"clientEmail" form:"clientEmail" gorm:"unique"`
- Ips string `json:"ips" form:"ips"`
+ Ips string `json:"ips" form:"ips"`
}
func (i *Inbound) GenXrayInboundConfig() *xray.InboundConfig {
@@ -80,4 +80,7 @@ type Client struct {
LimitIP int `json:"limitIp"`
TotalGB int64 `json:"totalGB" form:"totalGB"`
ExpiryTime int64 `json:"expiryTime" form:"expiryTime"`
+ Enable bool `json:"enable" form:"enable"`
+ TgID string `json:"tgId" form:"tgId"`
+ SubID string `json:"subId" form:"subId"`
}
diff --git a/go.mod b/go.mod
index 377ffab8..b3f9ac32 100644
--- a/go.mod
+++ b/go.mod
@@ -8,6 +8,7 @@ require (
github.com/gin-gonic/gin v1.9.0
github.com/go-cmd/cmd v1.4.1
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1
+ github.com/goccy/go-json v0.10.2
github.com/nicksnyder/go-i18n/v2 v2.2.1
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
github.com/pelletier/go-toml/v2 v2.0.7
@@ -15,22 +16,21 @@ require (
github.com/shirou/gopsutil/v3 v3.23.3
github.com/xtls/xray-core v1.8.0
go.uber.org/atomic v1.10.0
- golang.org/x/text v0.8.0
+ golang.org/x/text v0.9.0
google.golang.org/grpc v1.54.0
- gorm.io/driver/sqlite v1.4.4
- gorm.io/gorm v1.24.6
+ gorm.io/driver/sqlite v1.5.0
+ gorm.io/gorm v1.25.0
)
require (
github.com/BurntSushi/toml v1.2.1 // indirect
- github.com/bytedance/sonic v1.8.2 // indirect
+ github.com/bytedance/sonic v1.8.7 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-ole/go-ole v1.2.6 // 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.11.2 // indirect
- github.com/goccy/go-json v0.10.0 // indirect
+ github.com/go-playground/validator/v10 v10.12.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/gorilla/context v1.1.1 // indirect
github.com/gorilla/securecookie v1.1.1 // indirect
@@ -39,25 +39,25 @@ require (
github.com/jinzhu/now v1.1.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
- github.com/leodido/go-urn v1.2.1 // indirect
- github.com/lufia/plan9stats v0.0.0-20230110061619-bbe2e5e100de // indirect
- github.com/mattn/go-isatty v0.0.17 // indirect
+ github.com/leodido/go-urn v1.2.3 // indirect
+ github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a // indirect
+ github.com/mattn/go-isatty v0.0.18 // indirect
github.com/mattn/go-sqlite3 v1.14.16 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
- github.com/pires/go-proxyproto v0.6.2 // indirect
+ github.com/pires/go-proxyproto v0.7.0 // indirect
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect
- github.com/shoenig/go-m1cpu v0.1.4 // indirect
+ github.com/shoenig/go-m1cpu v0.1.5 // indirect
github.com/tklauser/go-sysconf v0.3.11 // indirect
github.com/tklauser/numcpus v0.6.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
- github.com/ugorji/go/codec v1.2.10 // indirect
+ github.com/ugorji/go/codec v1.2.11 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
- golang.org/x/arch v0.2.0 // indirect
- golang.org/x/crypto v0.7.0 // indirect
- golang.org/x/net v0.8.0 // indirect
- golang.org/x/sys v0.6.0 // indirect
- google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect
- google.golang.org/protobuf v1.29.1 // indirect
+ golang.org/x/arch v0.3.0 // indirect
+ golang.org/x/crypto v0.8.0 // indirect
+ golang.org/x/net v0.9.0 // indirect
+ golang.org/x/sys v0.7.0 // indirect
+ google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
+ google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
diff --git a/go.sum b/go.sum
index 61f0ec46..1dcdc37d 100644
--- a/go.sum
+++ b/go.sum
@@ -9,8 +9,8 @@ github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff/go.mod h1:+RTT1BOk5P
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
github.com/bradleypeabody/gorilla-sessions-memcache v0.0.0-20181103040241-659414f458e1/go.mod h1:dkChI7Tbtx7H1Tj7TqGSZMOeGpMP5gLHtjroHd4agiI=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
-github.com/bytedance/sonic v1.8.2 h1:Eq1oE3xWIBE3tj2ZtJFK1rDAx7+uA4bRytozVhXMHKY=
-github.com/bytedance/sonic v1.8.2/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
+github.com/bytedance/sonic v1.8.7 h1:d3sry5vGgVq/OpgozRUNP6xBsSo0mtNdwliApw+SAMQ=
+github.com/bytedance/sonic v1.8.7/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
@@ -41,14 +41,14 @@ github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
-github.com/go-playground/validator/v10 v10.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU=
-github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s=
+github.com/go-playground/validator/v10 v10.12.0 h1:E4gtWgxWxp8YSxExrQFv5BpCahla0PVF2oTTEYaWQGI=
+github.com/go-playground/validator/v10 v10.12.0/go.mod h1:hCAPuzYvKdP33pxWa+2+6AIKXEKqjIUyqsNCtbsSJrA=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 h1:wG8n/XJQ07TmjbITcGiUaOtXxdrINDz1b0J1w0SzqDc=
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1/go.mod h1:A2S0CWkNylc2phvKXWBBdD3K0iGnDBGbzRpISP2zBl8=
github.com/go-test/deep v1.0.7 h1:/VSMRlnY/JSyqxQUzQLKVMAskpY/NZKFA5j2P+0pP2M=
-github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA=
-github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
+github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
@@ -73,7 +73,6 @@ github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/z
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
-github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
@@ -84,18 +83,16 @@ github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
-github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
-github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
-github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
-github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
+github.com/leodido/go-urn v1.2.3 h1:6BE2vPT0lqoz3fmOesHZiaiFh7889ssCo2GMvLCfiuA=
+github.com/leodido/go-urn v1.2.3/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/lib/pq v1.10.3/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
-github.com/lufia/plan9stats v0.0.0-20230110061619-bbe2e5e100de h1:V53FWzU6KAZVi1tPp5UIsMoUWJ2/PNwYIDXnu7QuBCE=
-github.com/lufia/plan9stats v0.0.0-20230110061619-bbe2e5e100de/go.mod h1:JKx41uQRwqlTZabZc+kILPrO/3jlKnQ2Z8b7YiVw5cE=
+github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a h1:N9zuLhTvBSRt0gWSiJswwQ2HqDmtX/ZCDJURnKUt1Ik=
+github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a/go.mod h1:JKx41uQRwqlTZabZc+kILPrO/3jlKnQ2Z8b7YiVw5cE=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
-github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
-github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
+github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
@@ -116,8 +113,8 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v
github.com/pelletier/go-toml/v2 v2.0.7 h1:muncTPStnKRos5dpVKULv2FVd4bMOhNePj9CjgDb8Us=
github.com/pelletier/go-toml/v2 v2.0.7/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
-github.com/pires/go-proxyproto v0.6.2 h1:KAZ7UteSOt6urjme6ZldyFm4wDe/z0ZUP0Yv0Dos0d8=
-github.com/pires/go-proxyproto v0.6.2/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
+github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs=
+github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -132,15 +129,15 @@ github.com/refraction-networking/utls v1.2.3-0.20230308205431-4f1df6c200db h1:UL
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
-github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/sagernet/sing v0.1.7 h1:g4vjr3q8SUlBZSx97Emz5OBfSMBxxW5Q8C2PfdoSo08=
github.com/sagernet/sing-shadowsocks v0.1.1 h1:uFK2rlVeD/b1xhDwSMbUI2goWc6fOKxp+ZeKHZq6C9Q=
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo=
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb h1:XfLJSPIOUX+osiMraVgIrMR27uMXnRJWGm1+GL8/63U=
github.com/shirou/gopsutil/v3 v3.23.3 h1:Syt5vVZXUDXPEXpIBt5ziWsJ4LdSAAxF4l/xZeQgSEE=
github.com/shirou/gopsutil/v3 v3.23.3/go.mod h1:lSBNN6t3+D6W5e5nXTxc8KIMMVxAcS+6IJlffjRRlMU=
-github.com/shoenig/go-m1cpu v0.1.4 h1:SZPIgRM2sEF9NJy50mRHu9PKGwxyyTTJIWvCtgVbozs=
github.com/shoenig/go-m1cpu v0.1.4/go.mod h1:Wwvst4LR89UxjeFtLRMrpgRiyY4xPsejnVZym39dbAQ=
+github.com/shoenig/go-m1cpu v0.1.5 h1:LF57Z/Fpb/WdGLjt2HZilNnmZOxg/q2bSKTQhgbrLrQ=
+github.com/shoenig/go-m1cpu v0.1.5/go.mod h1:Wwvst4LR89UxjeFtLRMrpgRiyY4xPsejnVZym39dbAQ=
github.com/shoenig/test v0.6.3 h1:GVXWJFk9PiOjN0KoJ7VrJGH6uLPnqxR7/fe3HUPfE0c=
github.com/shoenig/test v0.6.3/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -148,7 +145,6 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
-github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
@@ -165,8 +161,8 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
-github.com/ugorji/go/codec v1.2.10 h1:eimT6Lsr+2lzmSZxPhLFoOWFmQqwk0fllJJ5hEbTXtQ=
-github.com/ugorji/go/codec v1.2.10/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
+github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
+github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e h1:5QefA066A1tF8gHIiADmOVOV5LS43gt3ONnlEl3xkwI=
github.com/xtls/reality v0.0.0-20230309125256-0d0713b108c8 h1:LLtLxEe3S0Ko+ckqt4t29RLskpNdOZfgjZCC2/Byr50=
github.com/xtls/xray-core v1.8.0 h1:/OD0sDv6YIBqvE+cVfnqlKrtbMs0Fm9IP5BR5d8Eu4k=
@@ -179,14 +175,14 @@ go.starlark.net v0.0.0-20230302034142-4b1e35fe2254 h1:Ss6D3hLXTM0KobyBYEAygXzFfG
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
-golang.org/x/arch v0.2.0 h1:W1sUEHXiJTfjaFJ5SLo0N6lZn+0eO5gWD1MFeTGqQEY=
-golang.org/x/arch v0.2.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
+golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
+golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
-golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
+golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
+golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/exp v0.0.0-20230307190834-24139beb5833 h1:SChBja7BCQewoTAU7IgvucQKMIXrEpFxNMs0spT3/5s=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
@@ -196,8 +192,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
-golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
+golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
+golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -213,10 +209,10 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
+golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -224,8 +220,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
-golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
+golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
+golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -236,16 +232,16 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA=
-google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s=
+google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A=
+google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag=
google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.29.1 h1:7QBf+IK2gx70Ap/hDsOmam3GE0v9HicjfEdAxE62UoM=
-google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
+google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@@ -253,11 +249,11 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gorm.io/driver/sqlite v1.4.4 h1:gIufGoR0dQzjkyqDyYSCvsYR6fba1Gw5YKDqKeChxFc=
-gorm.io/driver/sqlite v1.4.4/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI=
-gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
-gorm.io/gorm v1.24.6 h1:wy98aq9oFEetsc4CAbKD2SoBCdMzsbSIvSUUFJuHi5s=
-gorm.io/gorm v1.24.6/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
+gorm.io/driver/sqlite v1.5.0 h1:zKYbzRCpBrT1bNijRnxLDJWPjVfImGEn0lSnUY5gZ+c=
+gorm.io/driver/sqlite v1.5.0/go.mod h1:kDMDfntV9u/vuMmz8APHtHF0b4nyBB7sfCieC6G8k8I=
+gorm.io/gorm v1.24.7-0.20230306060331-85eaf9eeda11/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
+gorm.io/gorm v1.25.0 h1:+KtYtb2roDz14EQe4bla8CbQlmb9dN3VejSai3lprfU=
+gorm.io/gorm v1.25.0/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c h1:m5lcgWnL3OElQNVyp3qcncItJ2c0sQlSGjYK2+nJTA4=
lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
diff --git a/install.sh b/install.sh
index ffaeb370..15226a08 100644
--- a/install.sh
+++ b/install.sh
@@ -66,33 +66,17 @@ else
echo -e "${red}Failed to check the OS version, please contact the author!${plain}" && exit 1
fi
-# This function installs the base packages required for most scripts
install_base() {
- # Store the package names in a variable for easy modification
- local packages="wget curl tar"
-
- # Check for the package managers and install the packages if they are not already installed
- if ! command -v wget >/dev/null 2>&1 || ! command -v curl >/dev/null 2>&1 || ! command -v tar >/dev/null 2>&1; then
- if command -v apt >/dev/null 2>&1; then
- apt-get update && apt-get install -y $packages
- elif command -v dnf >/dev/null 2>&1; then
- dnf install -y $packages
- elif command -v yum >/dev/null 2>&1; then
- yum install -y $packages
- else
- echo "ERROR: No package managers found. Please install wget, curl, and tar manually."
- return 1
- fi
-
- # Print a confirmation message after the installation is complete
- echo "The following packages have been successfully installed: $packages"
- else
- # Print a message confirming that the packages are already installed
- echo "The following packages are already installed: $packages"
- fi
+ case "${release}" in
+ centos|fedora)
+ yum install -y -q wget curl tar
+ ;;
+ *)
+ apt install -y -q wget curl tar
+ ;;
+ esac
}
-
#This function will be called when user installed x-ui out of sercurity
config_after_install() {
echo -e "${yellow}Install/update finished! For security it's recommended to modify panel settings ${plain}"
diff --git a/media/1.png b/media/1.png
index 759eacf8..fa2c4ddf 100644
Binary files a/media/1.png and b/media/1.png differ
diff --git a/media/2.png b/media/2.png
index 84100deb..54a2ebff 100644
Binary files a/media/2.png and b/media/2.png differ
diff --git a/media/3.png b/media/3.png
index 750022e6..ebc97cd4 100644
Binary files a/media/3.png and b/media/3.png differ
diff --git a/media/4.png b/media/4.png
index b58f520e..3fbb70f7 100644
Binary files a/media/4.png and b/media/4.png differ
diff --git a/web/assets/css/custom.css b/web/assets/css/custom.css
index 038e30a1..229d8500 100644
--- a/web/assets/css/custom.css
+++ b/web/assets/css/custom.css
@@ -156,6 +156,12 @@
padding:16px;
}
+.ant-table-expand-icon-th,
+.ant-table-row-expand-icon-cell {
+ width: 30px;
+ min-width: 30px;
+}
+
.ant-menu-dark,
.ant-menu-dark .ant-menu-sub,
.ant-layout-header,
@@ -174,6 +180,7 @@
.ant-card-dark:hover {
border-color: #e8e8e8;
+ box-shadow: 0 2px 8px rgba(255,255,255,.15);
}
.ant-card-dark .ant-table-thead th {
@@ -216,20 +223,25 @@
.ant-card-dark .ant-table-tbody>tr:hover:not(.ant-table-expanded-row):not(.ant-table-row-selected)>td,
.ant-card-dark .ant-select-dropdown-menu-item:hover:not(.ant-select-dropdown-menu-item-disabled),
-.ant-card-dark .ant-calendar-date:hover {
+.ant-card-dark .ant-calendar-date:hover,
+.ant-card-dark .ant-select-dropdown-menu-item-active,
+.ant-card-dark li.ant-calendar-time-picker-select-option-selected {
background-color: #004488;
}
-.ant-card-dark tbody .ant-table-expanded-row {
+.ant-card-dark tbody .ant-table-expanded-row,
+.ant-card-dark .ant-calendar-time-picker-inner {
color: hsla(0,0%,100%,.65);
background-color: #1a212a;
}
.ant-card-dark .ant-input,
.ant-card-dark .ant-input-number,
+.ant-card-dark .ant-input-number-handler-wrap,
.ant-card-dark .ant-calendar-input,
.ant-card-dark .ant-select-dropdown-menu-item-selected,
-.ant-card-dark .ant-select-selection {
+.ant-card-dark .ant-select-selection,
+.ant-card-dark .ant-calendar-picker-clear {
color: hsla(0,0%,100%,.65);
background-color: #2e3b52;
}
@@ -239,6 +251,12 @@
background-color: #161b22;
}
+.ant-dropdown-menu-dark,
+.ant-card-dark .ant-modal-content {
+ border: 1px solid rgba(255, 255, 255, 0.65);
+ box-shadow: 0 2px 8px rgba(255,255,255,.15);
+}
+
.ant-card-dark .ant-modal-content,
.ant-card-dark .ant-modal-body,
.ant-card-dark .ant-modal-header,
@@ -280,6 +298,12 @@
border: 1px solid hsla(0,0%,100%,.30);
}
+.ant-card-dark .ant-tag {
+ color: hsla(0,0%,100%,.65);
+ background: rgba(255,255,255,.04);
+ border-color: #434343;
+}
+
.ant-card-dark .ant-tag-blue {
color: #3c9ae8;
background: #111d2c;
@@ -334,6 +358,29 @@
color: hsla(0,0%,100%,.65);
background-color: #073763;
border-color: #1890ff;
- text-shadow: 0 -1px 0 rgba(0,0,0,.12);
- box-shadow: 0 2px 0 rgba(0,0,0,.045);
+ text-shadow: 0 -1px 0 rgba(255,255,255,.12);
+ box-shadow: 0 2px 0 rgba(255,255,255,.045);
+}
+.ant-card-dark .ant-btn-primary:hover {
+ background-color: #40a9ff;
+ border-color: #40a9ff;
+}
+
+.ant-dark .ant-popover-content {
+ border: 1px solid #e8e8e8;
+ border-radius: 4px;
+ box-shadow: 0 2px 8px rgba(255,255,255,.15);
+}
+
+.ant-dark .ant-popover-inner {
+ background: #222a37;
+}
+
+.ant-dark .ant-popover-title,
+.ant-dark .ant-popover-inner-content {
+ color: hsla(0,0%,100%,.65);
+}
+
+.ant-dark .ant-popover-placement-top>.ant-popover-content>.ant-popover-arrow {
+ border-color: transparent #2e3b52 #2e3b52 transparent;
}
\ No newline at end of file
diff --git a/web/assets/js/model/models.js b/web/assets/js/model/models.js
index 485464e4..1de76850 100644
--- a/web/assets/js/model/models.js
+++ b/web/assets/js/model/models.js
@@ -171,13 +171,13 @@ class AllSetting {
this.webCertFile = "";
this.webKeyFile = "";
this.webBasePath = "/";
+ this.expireDiff = "";
+ this.trafficDiff = "";
this.tgBotEnable = false;
this.tgBotToken = "";
this.tgBotChatId = "";
this.tgRunTime = "@daily";
this.tgBotBackup = false;
- this.tgExpireDiff = "";
- this.tgTrafficDiff = "";
this.tgCpu = "";
this.xrayTemplateConfig = "";
diff --git a/web/assets/js/model/xray.js b/web/assets/js/model/xray.js
index eb6d07f3..31a192af 100644
--- a/web/assets/js/model/xray.js
+++ b/web/assets/js/model/xray.js
@@ -91,7 +91,11 @@ const UTLS_FINGERPRINT = {
UTLS_RANDOMIZED: "randomized",
};
+const bytesToHex = e => Array.from(e).map(e => e.toString(16).padStart(2, 0)).join('');
+const hexToBytes = e => new Uint8Array(e.match(/[0-9a-f]{2}/gi).map(e => parseInt(e, 16)));
+
const ALPN_OPTION = {
+ H3: "h3",
H2: "h2",
HTTP1: "http/1.1",
};
@@ -105,7 +109,6 @@ Object.freeze(XTLS_FLOW_CONTROL);
Object.freeze(TLS_FLOW_CONTROL);
Object.freeze(TLS_VERSION_OPTION);
Object.freeze(TLS_CIPHER_OPTION);
-Object.freeze(UTLS_FINGERPRINT);
Object.freeze(ALPN_OPTION);
class XrayCommonClass {
@@ -166,27 +169,25 @@ class XrayCommonClass {
}
class TcpStreamSettings extends XrayCommonClass {
- constructor(
- type = 'none',
- acceptProxyProtocol = false,
- request = new TcpStreamSettings.TcpRequest(),
- response = new TcpStreamSettings.TcpResponse(),
- ) {
+ constructor(acceptProxyProtocol=false,
+ type='none',
+ request=new TcpStreamSettings.TcpRequest(),
+ response=new TcpStreamSettings.TcpResponse(),
+ ) {
super();
+ this.acceptProxyProtocol = acceptProxyProtocol;
this.type = type;
this.request = request;
this.response = response;
- this.acceptProxyProtocol = acceptProxyProtocol;
}
- static fromJson(json = {}) {
+ static fromJson(json={}) {
let header = json.header;
if (!header) {
header = {};
}
- return new TcpStreamSettings(
+ return new TcpStreamSettings(json.acceptProxyProtocol,
header.type,
- json.acceptProxyProtocol,
TcpStreamSettings.TcpRequest.fromJson(header.request),
TcpStreamSettings.TcpResponse.fromJson(header.response),
);
@@ -194,21 +195,21 @@ class TcpStreamSettings extends XrayCommonClass {
toJson() {
return {
+ acceptProxyProtocol: this.acceptProxyProtocol,
header: {
type: this.type,
request: this.type === 'http' ? this.request.toJson() : undefined,
response: this.type === 'http' ? this.response.toJson() : undefined,
},
- acceptProxyProtocol: this.acceptProxyProtocol,
};
}
}
TcpStreamSettings.TcpRequest = class extends XrayCommonClass {
- constructor(version = '1.1',
- method = 'GET',
- path = ['/'],
- headers = [],
+ constructor(version='1.1',
+ method='GET',
+ path=['/'],
+ headers=[],
) {
super();
this.version = version;
@@ -242,7 +243,7 @@ TcpStreamSettings.TcpRequest = class extends XrayCommonClass {
this.headers.splice(index, 1);
}
- static fromJson(json = {}) {
+ static fromJson(json={}) {
return new TcpStreamSettings.TcpRequest(
json.version,
json.method,
@@ -261,10 +262,10 @@ TcpStreamSettings.TcpRequest = class extends XrayCommonClass {
};
TcpStreamSettings.TcpResponse = class extends XrayCommonClass {
- constructor(version = '1.1',
- status = '200',
- reason = 'OK',
- headers = [],
+ constructor(version='1.1',
+ status='200',
+ reason='OK',
+ headers=[],
) {
super();
this.version = version;
@@ -281,7 +282,7 @@ TcpStreamSettings.TcpResponse = class extends XrayCommonClass {
this.headers.splice(index, 1);
}
- static fromJson(json = {}) {
+ static fromJson(json={}) {
return new TcpStreamSettings.TcpResponse(
json.version,
json.status,
@@ -474,9 +475,13 @@ class GrpcStreamSettings extends XrayCommonClass {
}
class TlsStreamSettings extends XrayCommonClass {
- constructor(serverName = '', minVersion = TLS_VERSION_OPTION.TLS10, maxVersion = TLS_VERSION_OPTION.TLS12,
- cipherSuites = '',
- certificates = [new TlsStreamSettings.Cert()], alpn=[''] ,settings=[new TlsStreamSettings.Settings()]) {
+ constructor(serverName='',
+ minVersion = TLS_VERSION_OPTION.TLS12,
+ maxVersion = TLS_VERSION_OPTION.TLS13,
+ cipherSuites = '',
+ certificates=[new TlsStreamSettings.Cert()],
+ alpn=[],
+ settings=[new TlsStreamSettings.Settings()]) {
super();
this.server = serverName;
this.minVersion = minVersion;
@@ -484,7 +489,7 @@ class TlsStreamSettings extends XrayCommonClass {
this.cipherSuites = cipherSuites;
this.certs = certificates;
this.alpn = alpn;
- this.settings = settings;
+ this.settings = settings;
}
addCert(cert) {
@@ -497,15 +502,15 @@ class TlsStreamSettings extends XrayCommonClass {
static fromJson(json={}) {
let certs;
- let settings;
+ let settings;
if (!ObjectUtil.isEmpty(json.certificates)) {
certs = json.certificates.map(cert => TlsStreamSettings.Cert.fromJson(cert));
}
+
if (!ObjectUtil.isEmpty(json.settings)) {
let values = json.settings[0];
settings = [new TlsStreamSettings.Settings(values.allowInsecure , values.fingerprint, values.serverName)];
}
-
return new TlsStreamSettings(
json.serverName,
json.minVersion,
@@ -513,7 +518,7 @@ class TlsStreamSettings extends XrayCommonClass {
json.cipherSuites,
certs,
json.alpn,
- settings,
+ settings,
);
}
@@ -526,7 +531,6 @@ class TlsStreamSettings extends XrayCommonClass {
certificates: TlsStreamSettings.toJsonArray(this.certs),
alpn: this.alpn,
settings: TlsStreamSettings.toJsonArray(this.settings),
-
};
}
}
@@ -573,44 +577,105 @@ TlsStreamSettings.Cert = class extends XrayCommonClass {
};
TlsStreamSettings.Settings = class extends XrayCommonClass {
- constructor(allowInsecure = false, fingerprint = '', serverName = '') {
- super();
- this.allowInsecure = allowInsecure;
- this.fingerprint = fingerprint;
- this.serverName = serverName;
- }
- static fromJson(json = {}) {
- return new TlsStreamSettings.Settings(
- json.allowInsecure,
- json.fingerprint,
- json.servername,
- );
- }
- toJson() {
- return {
- allowInsecure: this.allowInsecure,
- fingerprint: this.fingerprint,
- serverName: this.serverName,
- };
- }
+ constructor(allowInsecure = false, fingerprint = '', serverName = '') {
+ super();
+ this.allowInsecure = allowInsecure;
+ this.fingerprint = fingerprint;
+ this.serverName = serverName;
+ }
+ static fromJson(json = {}) {
+ return new TlsStreamSettings.Settings(
+ json.allowInsecure,
+ json.fingerprint,
+ json.servername,
+ );
+ }
+ toJson() {
+ return {
+ allowInsecure: this.allowInsecure,
+ fingerprint: this.fingerprint,
+ serverName: this.serverName,
+ };
+ }
};
+class RealityStreamSettings extends XrayCommonClass {
+
+ constructor(
+ show = false,xver = 0,
+ fingerprint = UTLS_FINGERPRINT.UTLS_FIREFOX,
+ dest = 'yahoo.com:443',
+ serverNames = 'yahoo.com,www.yahoo.com',
+ privateKey = RandomUtil.randomX25519PrivateKey(),
+ publicKey = '',
+ minClient = '',
+ maxClient = '',
+ maxTimediff = 0,
+ shortIds = RandomUtil.randowShortId()
+ )
+ {
+ super();
+ this.show = show;
+ this.xver = xver;
+ this.fingerprint = fingerprint;
+ this.dest = dest;
+ this.serverNames = serverNames instanceof Array ? serverNames.join(",") : serverNames;
+ this.privateKey = privateKey;
+ this.publicKey = RandomUtil.randomX25519PublicKey(this.privateKey);
+ this.minClient = minClient;
+ this.maxClient = maxClient;
+ this.maxTimediff = maxTimediff;
+ this.shortIds = shortIds instanceof Array ? shortIds.join(",") : shortIds;
+ }
+ static fromJson(json = {}) {
+ return new RealityStreamSettings(
+ json.show,
+ json.xver,
+ json.fingerprint,
+ json.dest,
+ json.serverNames,
+ json.privateKey,
+ json.publicKey,
+ json.minClient,
+ json.maxClient,
+ json.maxTimediff,
+ json.shortIds
+ );
+ }
+ toJson() {
+ return {
+ show: this.show,
+ xver: this.xver,
+ fingerprint: this.fingerprint,
+ dest: this.dest,
+ serverNames: this.serverNames.split(/,|,|\s+/),
+ privateKey: this.privateKey,
+ publicKey: this.publicKey,
+ minClient: this.minClient,
+ maxClient: this.maxClient,
+ maxTimediff: this.maxTimediff,
+ shortIds: this.shortIds.split(/,|,|\s+/)
+ };
+ }
+ }
class StreamSettings extends XrayCommonClass {
constructor(network='tcp',
- security='none',
- tlsSettings=new TlsStreamSettings(),
- tcpSettings=new TcpStreamSettings(),
- kcpSettings=new KcpStreamSettings(),
- wsSettings=new WsStreamSettings(),
- httpSettings=new HttpStreamSettings(),
- quicSettings=new QuicStreamSettings(),
- grpcSettings=new GrpcStreamSettings(),
- ) {
+ security='none',
+ tlsSettings=new TlsStreamSettings(),
+ realitySettings = new RealityStreamSettings(),
+ tcpSettings=new TcpStreamSettings(),
+ kcpSettings=new KcpStreamSettings(),
+ wsSettings=new WsStreamSettings(),
+ httpSettings=new HttpStreamSettings(),
+ quicSettings=new QuicStreamSettings(),
+ grpcSettings=new GrpcStreamSettings(),
+ ) {
super();
this.network = network;
this.security = security;
this.tls = tlsSettings;
+ this.reality = realitySettings;
this.tcp = tcpSettings;
this.kcp = kcpSettings;
this.ws = wsSettings;
@@ -643,17 +708,34 @@ class StreamSettings extends XrayCommonClass {
}
}
- static fromJson(json={}) {
- let tls;
+ //for Reality
+ get isReality() {
+ return this.security === "reality";
+ }
+
+ set isReality(isReality) {
+ if (isReality) {
+ this.security = "reality";
+ } else {
+ this.security = "none";
+ }
+ }
+
+ static fromJson(json = {}) {
+ let tls, reality;
if (json.security === "xtls") {
tls = TlsStreamSettings.fromJson(json.XTLSSettings);
- } else {
+ } else if (json.security === "tls") {
tls = TlsStreamSettings.fromJson(json.tlsSettings);
}
+ if (json.security === "reality") {
+ reality = RealityStreamSettings.fromJson(json.realitySettings)
+ }
return new StreamSettings(
json.network,
json.security,
tls,
+ reality,
TcpStreamSettings.fromJson(json.tcpSettings),
KcpStreamSettings.fromJson(json.kcpSettings),
WsStreamSettings.fromJson(json.wsSettings),
@@ -671,6 +753,7 @@ class StreamSettings extends XrayCommonClass {
tlsSettings: this.isTls ? this.tls.toJson() : undefined,
XTLSSettings: this.isXTLS ? this.tls.toJson() : undefined,
tcpSettings: network === 'tcp' ? this.tcp.toJson() : undefined,
+ realitySettings: this.isReality ? this.reality.toJson() : undefined,
kcpSettings: network === 'kcp' ? this.kcp.toJson() : undefined,
wsSettings: network === 'ws' ? this.ws.toJson() : undefined,
httpSettings: network === 'http' ? this.http.toJson() : undefined,
@@ -728,20 +811,23 @@ class Inbound extends XrayCommonClass {
get protocol() {
return this._protocol;
}
-
+
set protocol(protocol) {
this._protocol = protocol;
this.settings = Inbound.Settings.getSettings(protocol);
if (protocol === Protocols.TROJAN) {
- this.tls = false;
+ this.tls = true;
}
}
+
get tls() {
return this.stream.security === 'tls';
}
set tls(isTls) {
if (isTls) {
+ this.xtls = false;
+ this.reality = false;
this.stream.security = 'tls';
} else {
this.stream.security = 'none';
@@ -754,12 +840,32 @@ class Inbound extends XrayCommonClass {
set XTLS(isXTLS) {
if (isXTLS) {
+ this.xtls = false;
+ this.reality = false;
this.stream.security = 'xtls';
} else {
this.stream.security = 'none';
}
}
+ //for Reality
+ get reality() {
+ if (this.stream.security === "reality") {
+ return this.network === "tcp" || this.network === "grpc" || this.network === "http";
+ }
+ return false;
+ }
+
+ set reality(isReality) {
+ if (isReality) {
+ this.tls = false;
+ this.xtls = false;
+ this.stream.security = "reality";
+ } else {
+ this.stream.security = "none";
+ }
+ }
+
get network() {
return this.stream.network;
}
@@ -918,16 +1024,16 @@ class Inbound extends XrayCommonClass {
isExpiry(index) {
switch (this.protocol) {
case Protocols.VMESS:
- if(this.settings.vmesses[index]._expiryTime != null)
- return this.settings.vmesses[index]._expiryTime < new Date().getTime();
+ if(this.settings.vmesses[index].expiryTime > 0)
+ return this.settings.vmesses[index].expiryTime < new Date().getTime();
return false
case Protocols.VLESS:
- if(this.settings.vlesses[index]._expiryTime != null)
- return this.settings.vlesses[index]._expiryTime < new Date().getTime();
+ if(this.settings.vlesses[index].expiryTime > 0)
+ return this.settings.vlesses[index].expiryTime < new Date().getTime();
return false
case Protocols.TROJAN:
- if(this.settings.trojans[index]._expiryTime != null)
- return this.settings.trojans[index]._expiryTime < new Date().getTime();
+ if(this.settings.trojans[index].expiryTime > 0)
+ return this.settings.trojans[index].expiryTime < new Date().getTime();
return false
default:
return false;
@@ -955,10 +1061,21 @@ class Inbound extends XrayCommonClass {
return false;
}
}
-
+
+ canEnableReality() {
+ switch (this.protocol) {
+ case Protocols.VLESS:
+ case Protocols.TROJAN:
+ break;
+ default:
+ return false;
+ }
+ return this.network === "tcp" || this.network === "grpc" || this.network === "http";
+ }
+
//this is used for xtls-rprx-vision
canEnableTlsFlow() {
- if ((this.stream.security === 'tls') && (this.network === "tcp")) {
+ if (((this.stream.security === 'tls') || (this.stream.security === 'reality')) && (this.network === "tcp")) {
switch (this.protocol) {
case Protocols.VLESS:
return true;
@@ -968,11 +1085,10 @@ class Inbound extends XrayCommonClass {
}
return false;
}
-
+
canSetTls() {
return this.canEnableTls();
}
-
canEnableXTLS() {
switch (this.protocol) {
@@ -989,7 +1105,8 @@ class Inbound extends XrayCommonClass {
switch (this.protocol) {
case Protocols.VMESS:
case Protocols.VLESS:
- case Protocols.TROJAN:
+ case Protocols.TROJAN:
+ case Protocols.SHADOWSOCKS:
return true;
default:
return false;
@@ -1065,7 +1182,7 @@ class Inbound extends XrayCommonClass {
address = this.stream.tls.server;
}
}
-
+
let obj = {
v: '2',
ps: remark,
@@ -1078,7 +1195,7 @@ class Inbound extends XrayCommonClass {
host: host,
path: path,
tls: this.stream.security,
- sni: this.stream.tls.settings[0]['serverName'],
+ sni: this.stream.tls.settings[0]['serverName'],
fp: this.stream.tls.settings[0]['fingerprint'],
alpn: this.stream.tls.alpn.join(','),
allowInsecure: this.stream.tls.settings[0].allowInsecure,
@@ -1148,26 +1265,46 @@ class Inbound extends XrayCommonClass {
if (!ObjectUtil.isEmpty(this.stream.tls.server)) {
address = this.stream.tls.server;
}
- if (this.stream.tls.settings[0]['serverName'] !== ''){
+ if (this.stream.tls.settings[0]['serverName'] !== ''){
params.set("sni", this.stream.tls.settings[0]['serverName']);
}
if (type === "tcp" && this.settings.vlesses[clientIndex].flow.length > 0) {
params.set("flow", this.settings.vlesses[clientIndex].flow);
}
}
-
- if (this.XTLS) {
+
+ if (this.XTLS) {
params.set("security", "xtls");
params.set("alpn", this.stream.tls.alpn);
if(this.stream.tls.settings[0].allowInsecure){
params.set("allowInsecure", "1");
}
- if (!ObjectUtil.isEmpty(this.stream.tls.server)) {
- address = this.stream.tls.server;
- }
- params.set("flow", this.settings.vlesses[clientIndex].flow);
- }
+ if (!ObjectUtil.isEmpty(this.stream.tls.server)) {
+ address = this.stream.tls.server;
+ }
+ params.set("flow", this.settings.vlesses[clientIndex].flow);
+ }
+ if (this.reality) {
+ params.set("security", "reality");
+ if (!ObjectUtil.isArrEmpty(this.stream.reality.serverNames)) {
+ params.set("sni", this.stream.reality.serverNames.split(/,|,|\s+/)[0]);
+ }
+ if (this.stream.reality.publicKey != "") {
+ //params.set("pbk", Ed25519.getPublicKey(this.stream.reality.privateKey));
+ params.set("pbk", this.stream.reality.publicKey);
+ }
+ if (this.stream.network === 'tcp') {
+ params.set("flow", this.settings.vlesses[clientIndex].flow);
+ }
+ if (this.stream.reality.shortIds != "") {
+ params.set("sid", this.stream.reality.shortIds);
+ }
+ if (this.stream.reality.fingerprint != "") {
+ params.set("fp", this.stream.reality.fingerprint);
+ }
+ }
+
const link = `vless://${uuid}@${address}:${port}`;
const url = new URL(link);
for (const [key, value] of params) {
@@ -1177,13 +1314,13 @@ class Inbound extends XrayCommonClass {
return url.toString();
}
- genSSLink(address = '', remark = '') {
+ genSSLink(address='', remark='') {
let settings = this.settings;
const server = this.stream.tls.server;
if (!ObjectUtil.isEmpty(server)) {
address = server;
}
- return 'ss://' + safeBase64(settings.method + ':' + settings.password) + `@${address}:${this.port}#${encodeURIComponent(remark)}`;
+ return 'ss://' + safeBase64(settings.method + ':' + settings.password) + `@${address}:${this.port}#${encodeURIComponent(remark)}`;
}
genTrojanLink(address = '', remark = '', clientIndex = 0) {
@@ -1191,7 +1328,7 @@ class Inbound extends XrayCommonClass {
const port = this.port;
const type = this.stream.network;
const params = new Map();
- params.set("type", this.stream.network);
+ params.set("type", this.stream.network);
switch (type) {
case "tcp":
const tcp = this.stream.tcp;
@@ -1246,12 +1383,32 @@ class Inbound extends XrayCommonClass {
}
if (!ObjectUtil.isEmpty(this.stream.tls.server)) {
address = this.stream.tls.server;
- }
- if (this.stream.tls.settings[0]['serverName'] !== ''){
+ }
+ if (this.stream.tls.settings[0]['serverName'] !== ''){
params.set("sni", this.stream.tls.settings[0]['serverName']);
+ }
+ }
+
+ if (this.reality) {
+ params.set("security", "reality");
+ if (!ObjectUtil.isArrEmpty(this.stream.reality.serverNames)) {
+ params.set("sni", this.stream.reality.serverNames.split(/,|,|\s+/)[0]);
+ }
+ if (this.stream.reality.publicKey != "") {
+ //params.set("pbk", Ed25519.getPublicKey(this.stream.reality.privateKey));
+ params.set("pbk", this.stream.reality.publicKey);
+ }
+ if (this.stream.network === 'tcp') {
+ params.set("flow", this.settings.trojans[clientIndex].flow);
+ }
+ if (this.stream.reality.shortIds != "") {
+ params.set("sid", this.stream.reality.shortIds);
+ }
+ if (this.stream.reality.fingerprint != "") {
+ params.set("fp", this.stream.reality.fingerprint);
}
}
-
+
if (this.XTLS) {
params.set("security", "xtls");
params.set("alpn", this.stream.tls.alpn);
@@ -1259,11 +1416,11 @@ class Inbound extends XrayCommonClass {
params.set("allowInsecure", "1");
}
if (!ObjectUtil.isEmpty(this.stream.tls.server)) {
- address = this.stream.tls.server;
- }
- params.set("flow", this.settings.trojans[clientIndex].flow);
- }
-
+ address = this.stream.tls.server;
+ }
+ params.set("flow", this.settings.trojans[clientIndex].flow);
+ }
+
const link = `trojan://${settings.trojans[clientIndex].password}@${address}:${this.port}#${encodeURIComponent(remark)}`;
const url = new URL(link);
for (const [key, value] of params) {
@@ -1294,8 +1451,9 @@ class Inbound extends XrayCommonClass {
default: return '';
}
}
+
genInboundLinks(address = '', remark = '') {
- let link = '';
+ let link = '';
switch (this.protocol) {
case Protocols.VMESS:
case Protocols.VLESS:
@@ -1308,7 +1466,7 @@ class Inbound extends XrayCommonClass {
return (this.genSSLink(address, remark) + '\r\n');
default: return '';
}
-}
+ }
static fromJson(json={}) {
return new Inbound(
@@ -1423,7 +1581,7 @@ Inbound.VmessSettings = class extends Inbound.Settings {
}
};
Inbound.VmessSettings.Vmess = class extends XrayCommonClass {
- constructor(id=RandomUtil.randomUUID(), alterId=0, email=RandomUtil.randomText(),limitIp=0, totalGB=0, expiryTime='') {
+ constructor(id=RandomUtil.randomUUID(), alterId=0, email=RandomUtil.randomText(),limitIp=0, totalGB=0, expiryTime=0, enable=true, tgId='', subId='') {
super();
this.id = id;
this.alterId = alterId;
@@ -1431,6 +1589,9 @@ Inbound.VmessSettings.Vmess = class extends XrayCommonClass {
this.limitIp = limitIp;
this.totalGB = totalGB;
this.expiryTime = expiryTime;
+ this.enable = enable;
+ this.tgId = tgId;
+ this.subId = subId;
}
static fromJson(json={}) {
@@ -1441,13 +1602,18 @@ Inbound.VmessSettings.Vmess = class extends XrayCommonClass {
json.limitIp,
json.totalGB,
json.expiryTime,
-
+ json.enable,
+ json.tgId,
+ json.subId,
);
}
get _expiryTime() {
if (this.expiryTime === 0 || this.expiryTime === "") {
return null;
}
+ if (this.expiryTime < 0){
+ return this.expiryTime / -86400000;
+ }
return moment(this.expiryTime);
}
@@ -1475,7 +1641,7 @@ Inbound.VLESSSettings = class extends Inbound.Settings {
fallbacks=[],) {
super(protocol);
this.vlesses = vlesses;
- this.decryption = 'none';
+ this.decryption = 'none'; // Using decryption is not implemented here
this.fallbacks = fallbacks;
}
@@ -1487,6 +1653,7 @@ Inbound.VLESSSettings = class extends Inbound.Settings {
this.fallbacks.splice(index, 1);
}
+ // decryption should be set to static value
static fromJson(json={}) {
return new Inbound.VLESSSettings(
Protocols.VLESS,
@@ -1499,15 +1666,14 @@ Inbound.VLESSSettings = class extends Inbound.Settings {
toJson() {
return {
clients: Inbound.VLESSSettings.toJsonArray(this.vlesses),
- decryption: this.decryption,
+ decryption: 'none',
fallbacks: Inbound.VLESSSettings.toJsonArray(this.fallbacks),
};
}
};
Inbound.VLESSSettings.VLESS = class extends XrayCommonClass {
-
- constructor(id=RandomUtil.randomUUID(), flow='', email=RandomUtil.randomText(),limitIp=0, totalGB=0, expiryTime='') {
+ constructor(id=RandomUtil.randomUUID(), flow='', email=RandomUtil.randomText(),limitIp=0, totalGB=0, expiryTime=0, enable=true, tgId='', subId='') {
super();
this.id = id;
this.flow = flow;
@@ -1515,7 +1681,9 @@ Inbound.VLESSSettings.VLESS = class extends XrayCommonClass {
this.limitIp = limitIp;
this.totalGB = totalGB;
this.expiryTime = expiryTime;
-
+ this.enable = enable;
+ this.tgId = tgId;
+ this.subId = subId;
}
static fromJson(json={}) {
@@ -1526,14 +1694,19 @@ Inbound.VLESSSettings.VLESS = class extends XrayCommonClass {
json.limitIp,
json.totalGB,
json.expiryTime,
-
+ json.enable,
+ json.tgId,
+ json.subId,
);
- }
+ }
get _expiryTime() {
if (this.expiryTime === 0 || this.expiryTime === "") {
return null;
}
+ if (this.expiryTime < 0){
+ return this.expiryTime / -86400000;
+ }
return moment(this.expiryTime);
}
@@ -1593,8 +1766,8 @@ Inbound.VLESSSettings.Fallback = class extends XrayCommonClass {
Inbound.TrojanSettings = class extends Inbound.Settings {
constructor(protocol,
- trojans=[new Inbound.TrojanSettings.Trojan()],
- fallbacks=[],) {
+ trojans=[new Inbound.TrojanSettings.Trojan()],
+ fallbacks=[],) {
super(protocol);
this.trojans = trojans;
this.fallbacks = fallbacks;
@@ -1623,7 +1796,7 @@ Inbound.TrojanSettings = class extends Inbound.Settings {
}
};
Inbound.TrojanSettings.Trojan = class extends XrayCommonClass {
- constructor(password=RandomUtil.randomSeq(10), flow='', email=RandomUtil.randomText(),limitIp=0, totalGB=0, expiryTime='') {
+ constructor(password=RandomUtil.randomSeq(10), flow='', email=RandomUtil.randomText(),limitIp=0, totalGB=0, expiryTime=0, enable=true, tgId='', subId='') {
super();
this.password = password;
this.flow = flow;
@@ -1631,6 +1804,9 @@ Inbound.TrojanSettings.Trojan = class extends XrayCommonClass {
this.limitIp = limitIp;
this.totalGB = totalGB;
this.expiryTime = expiryTime;
+ this.enable = enable;
+ this.tgId = tgId;
+ this.subId = subId;
}
toJson() {
@@ -1641,10 +1817,13 @@ Inbound.TrojanSettings.Trojan = class extends XrayCommonClass {
limitIp: this.limitIp,
totalGB: this.totalGB,
expiryTime: this.expiryTime,
+ enable: this.enable,
+ tgId: this.tgId,
+ subId: this.subId,
};
}
- static fromJson(json={}) {
+ static fromJson(json = {}) {
return new Inbound.TrojanSettings.Trojan(
json.password,
json.flow,
@@ -1652,7 +1831,9 @@ Inbound.TrojanSettings.Trojan = class extends XrayCommonClass {
json.limitIp,
json.totalGB,
json.expiryTime,
-
+ json.enable,
+ json.tgId,
+ json.subId,
);
}
@@ -1660,6 +1841,9 @@ Inbound.TrojanSettings.Trojan = class extends XrayCommonClass {
if (this.expiryTime === 0 || this.expiryTime === "") {
return null;
}
+ if (this.expiryTime < 0){
+ return this.expiryTime / -86400000;
+ }
return moment(this.expiryTime);
}
@@ -1721,9 +1905,9 @@ Inbound.TrojanSettings.Fallback = class extends XrayCommonClass {
Inbound.ShadowsocksSettings = class extends Inbound.Settings {
constructor(protocol,
- method = SSMethods.BLAKE3_AES_256_GCM,
- password = RandomUtil.randomSeq(44),
- network = 'tcp,udp'
+ method=SSMethods.BLAKE3_AES_256_GCM,
+ password=RandomUtil.randomSeq(44),
+ network='tcp,udp'
) {
super(protocol);
this.method = method;
@@ -1731,7 +1915,7 @@ Inbound.ShadowsocksSettings = class extends Inbound.Settings {
this.network = network;
}
- static fromJson(json = {}) {
+ static fromJson(json={}) {
return new Inbound.ShadowsocksSettings(
Protocols.SHADOWSOCKS,
json.method,
@@ -1755,7 +1939,7 @@ Inbound.DokodemoSettings = class extends Inbound.Settings {
this.address = address;
this.port = port;
this.network = network;
- this.followRedirect = followRedirect;
+ this.followRedirect = followRedirect;
}
static fromJson(json={}) {
@@ -1764,7 +1948,7 @@ Inbound.DokodemoSettings = class extends Inbound.Settings {
json.address,
json.port,
json.network,
- json.followRedirect,
+ json.followRedirect,
);
}
@@ -1773,7 +1957,7 @@ Inbound.DokodemoSettings = class extends Inbound.Settings {
address: this.address,
port: this.port,
network: this.network,
- followRedirect: this.followRedirect,
+ followRedirect: this.followRedirect,
};
}
};
diff --git a/web/assets/js/util/utils.js b/web/assets/js/util/utils.js
index ec6df22a..405985da 100644
--- a/web/assets/js/util/utils.js
+++ b/web/assets/js/util/utils.js
@@ -89,6 +89,31 @@ const seq = [
'U', 'V', 'W', 'X', 'Y', 'Z'
];
+const shortIdSeq = [
+ 'a', 'b', 'c', 'd', 'e', 'f',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+];
+
+const x25519Map = new Map(
+ [
+ ['EH2FWe-Ij_FFAa2u9__-aiErLvVIneP601GOCdlyPWw', "goY3OtfaA4UYbiz7Hn0NysI5QJrK0VT_Chg6RLgUPQU"],
+ ['cKI_6DoMSP1IepeWWXrG3G9nkehl94KYBhagU50g2U0', "VigpKFbSLnHLzBWobZaS1IBmw--giJ51w92y723ajnU"],
+ ['qM2SNyK3NyHB6deWpEP3ITyCGKQFRTna_mlKP0w1QH0', "HYyIGuyNFslmcnNT7mrDdmuXwn4cm7smE_FZbYguKHQ"],
+ ['qCWg5GMEDFd3n1nxDswlIpOHoPUXMLuMOIiLUVzubkI', "rJFC3dUjJxMnVZiUGzmf_LFsJUwFWY-CU5RQgFOHCWM"],
+ ['4NOBxDrEsOhNI3Y3EnVIy_TN-uyBoAjQw6QM0YsOi0s', "CbcY9qc4YuMDJDyyL0OITlU824TBg1O84ClPy27e2RM"],
+ ['eBvFb0M4HpSOwWjtXV8zliiEs_hg56zX4a2LpuuqpEI', "CjulQ2qVIky7ImIfysgQhNX7s_drGLheCGSkVHcLZhc"],
+ ['yEpOzQV04NNcycWVeWtRNTzv5TS-ynTuKRacZCH-6U8', "O9RSr5gSdok2K_tobQnf_scyKVqnCx6C4Jrl7_rCZEQ"],
+ ['CNt6TAUVCwqM6xIBHyni0K3Zqbn2htKQLvLb6XDgh0s', "d9cGLVBrDFS02L2OvkqyqwFZ1Ux3AHs28ehl4Rwiyl0"],
+ ['EInKw-6Wr0rAHXlxxDuZU5mByIzcD3Z-_iWPzXlUL1k', "LlYD2nNVAvyjNvjZGZh4R8PkMIwkc6EycPTvR2LE0nQ"],
+ ['GKIKo7rcXVyle-EUHtGIDtYnDsI6osQmOUl3DTJRAGc', "VcqHivYGGoBkcxOI6cSSjQmneltstkb2OhvO53dyhEM"],
+ ['-FVDzv68IC17fJVlNDlhrrgX44WeBfbhwjWpCQVXGHE', "PGG2EYOvsFt2lAQTD7lqHeRxz2KxvllEDKcUrtizPBU"],
+ ['0H3OJEYEu6XW7woqy7cKh2vzg6YHkbF_xSDTHKyrsn4', "mzevpYbS8kXengBY5p7tt56QE4tS3lwlwRemmkcQeyc"],
+ ['8F8XywN6ci44ES6em2Z0fYYxyptB9uaXY9Hc1WSSPE4', "qCZUdWQZ2H33vWXnOkG8NpxBeq3qn5QWXlfCOWBNkkc"],
+ ['IN0dqfkC10dj-ifRHrg2PmmOrzYs697ajGMwcLbu-1g', "2UW_EO3r7uczPGUUlpJBnMDpDmWUHE2yDzCmXS4sckE"],
+ ['uIcmks5rAhvBe4dRaJOdeSqgxLGGMZhsGk4J4PEKL2s', "F9WJV_74IZp0Ide4hWjiJXk9FRtBUBkUr3mzU-q1lzk"],
+ ]
+);
+
class RandomUtil {
static randomIntRange(min, max) {
@@ -107,6 +132,14 @@ class RandomUtil {
return str;
}
+ static randomShortIdSeq(count) {
+ let str = '';
+ for (let i = 0; i < count; ++i) {
+ str += shortIdSeq[this.randomInt(16)];
+ }
+ return str;
+ }
+
static randomLowerAndNum(count) {
let str = '';
for (let i = 0; i < count; ++i) {
@@ -137,6 +170,26 @@ class RandomUtil {
});
}
+ static randowShortId() {
+ let str = '';
+ str += this.randomShortIdSeq(8)
+ return str;
+ }
+
+ static randomX25519PrivateKey() {
+ let num = x25519Map.size;
+ let index = this.randomInt(num);
+ let cntr = 0;
+ for (let key of x25519Map.keys()) {
+ if (cntr++ === index) {
+ return key;
+ }
+ }
+ }
+
+ static randomX25519PublicKey(key) {
+ return x25519Map.get(key)
+ }
static randomText() {
var chars = 'abcdefghijklmnopqrstuvwxyz1234567890';
var string = '';
diff --git a/web/controller/api.go b/web/controller/api.go
index 843ac7e5..c64b27bf 100644
--- a/web/controller/api.go
+++ b/web/controller/api.go
@@ -3,77 +3,74 @@ package controller
import "github.com/gin-gonic/gin"
type APIController struct {
- BaseController
- inboundController *InboundController
- settingController *SettingController
+ BaseController
+ inboundController *InboundController
}
func NewAPIController(g *gin.RouterGroup) *APIController {
- a := &APIController{}
- a.initRouter(g)
- return a
+ a := &APIController{}
+ a.initRouter(g)
+ return a
}
func (a *APIController) initRouter(g *gin.RouterGroup) {
- g = g.Group("/xui/API/inbounds")
- g.Use(a.checkLogin)
+ g = g.Group("/xui/API/inbounds")
+ g.Use(a.checkLogin)
- g.POST("/list", a.getAllInbounds)
- g.GET("/get/:id", a.getSingleInbound)
- g.POST("/add", a.addInbound)
- g.POST("/del/:id", a.delInbound)
- g.POST("/update/:id", a.updateInbound)
- g.POST("/clientIps/:email", a.getClientIps)
- g.POST("/clearClientIps/:email", a.clearClientIps)
- g.POST("/addClient/", a.addInboundClient)
- g.POST("/delClient/:email", a.delInboundClient)
- g.POST("/updateClient/:index", a.updateInboundClient)
- g.POST("/:id/resetClientTraffic/:email", a.resetClientTraffic)
+ g.GET("/list", a.getAllInbounds)
+ g.GET("/get/:id", a.getSingleInbound)
+ g.POST("/add", a.addInbound)
+ g.POST("/del/:id", a.delInbound)
+ g.POST("/update/:id", a.updateInbound)
+ g.POST("/clientIps/:email", a.getClientIps)
+ g.POST("/clearClientIps/:email", a.clearClientIps)
+ g.POST("/addClient/", a.addInboundClient)
+ g.POST("/delClient/:email", a.delInboundClient)
+ g.POST("/updateClient/:index", a.updateInboundClient)
+ g.POST("/:id/resetClientTraffic/:email", a.resetClientTraffic)
+ g.POST("/resetAllTraffics", a.resetAllTraffics)
+ g.POST("/resetAllClientTraffics/:id", a.resetAllClientTraffics)
- a.inboundController = NewInboundController(g)
+ a.inboundController = NewInboundController(g)
}
-
-
func (a *APIController) getAllInbounds(c *gin.Context) {
- a.inboundController.getInbounds(c)
+ a.inboundController.getInbounds(c)
}
-
func (a *APIController) getSingleInbound(c *gin.Context) {
- a.inboundController.getInbound(c)
+ a.inboundController.getInbound(c)
}
-
func (a *APIController) addInbound(c *gin.Context) {
- a.inboundController.addInbound(c)
+ a.inboundController.addInbound(c)
}
-
func (a *APIController) delInbound(c *gin.Context) {
- a.inboundController.delInbound(c)
+ a.inboundController.delInbound(c)
}
-
func (a *APIController) updateInbound(c *gin.Context) {
- a.inboundController.updateInbound(c)
+ a.inboundController.updateInbound(c)
}
func (a *APIController) getClientIps(c *gin.Context) {
- a.inboundController.getClientIps(c)
+ a.inboundController.getClientIps(c)
}
func (a *APIController) clearClientIps(c *gin.Context) {
- a.inboundController.clearClientIps(c)
+ a.inboundController.clearClientIps(c)
}
-
func (a *APIController) addInboundClient(c *gin.Context) {
- a.inboundController.addInboundClient(c)
+ a.inboundController.addInboundClient(c)
}
-
func (a *APIController) delInboundClient(c *gin.Context) {
- a.inboundController.delInboundClient(c)
+ a.inboundController.delInboundClient(c)
}
-
func (a *APIController) updateInboundClient(c *gin.Context) {
- a.inboundController.updateInboundClient(c)
+ a.inboundController.updateInboundClient(c)
}
-
func (a *APIController) resetClientTraffic(c *gin.Context) {
- a.inboundController.resetClientTraffic(c)
+ a.inboundController.resetClientTraffic(c)
+}
+func (a *APIController) resetAllTraffics(c *gin.Context) {
+ a.inboundController.resetAllTraffics(c)
+}
+func (a *APIController) resetAllClientTraffics(c *gin.Context) {
+ a.inboundController.resetAllClientTraffics(c)
}
diff --git a/web/controller/inbound.go b/web/controller/inbound.go
index b567af8c..f7ea35eb 100644
--- a/web/controller/inbound.go
+++ b/web/controller/inbound.go
@@ -37,6 +37,8 @@ func (a *InboundController) initRouter(g *gin.RouterGroup) {
g.POST("/delClient/:email", a.delInboundClient)
g.POST("/updateClient/:index", a.updateInboundClient)
g.POST("/:id/resetClientTraffic/:email", a.resetClientTraffic)
+ g.POST("/resetAllTraffics", a.resetAllTraffics)
+ g.POST("/resetAllClientTraffics/:id", a.resetAllClientTraffics)
}
@@ -131,7 +133,7 @@ func (a *InboundController) updateInbound(c *gin.Context) {
func (a *InboundController) getClientIps(c *gin.Context) {
email := c.Param("email")
- ips , err := a.inboundService.GetInboundClientIps(email)
+ ips, err := a.inboundService.GetInboundClientIps(email)
if err != nil {
jsonObj(c, "No IP Record", nil)
return
@@ -230,3 +232,27 @@ func (a *InboundController) resetClientTraffic(c *gin.Context) {
a.xrayService.SetToNeedRestart()
}
}
+
+func (a *InboundController) resetAllTraffics(c *gin.Context) {
+ err := a.inboundService.ResetAllTraffics()
+ if err != nil {
+ jsonMsg(c, "something worng!", err)
+ return
+ }
+ jsonMsg(c, "All traffics reseted", nil)
+}
+
+func (a *InboundController) resetAllClientTraffics(c *gin.Context) {
+ id, err := strconv.Atoi(c.Param("id"))
+ if err != nil {
+ jsonMsg(c, I18n(c, "pages.inbounds.revise"), err)
+ return
+ }
+
+ err = a.inboundService.ResetAllClientTraffics(id)
+ if err != nil {
+ jsonMsg(c, "something worng!", err)
+ return
+ }
+ jsonMsg(c, "All traffics of client reseted", nil)
+}
diff --git a/web/controller/server.go b/web/controller/server.go
index 7b7239d5..c5a3965b 100644
--- a/web/controller/server.go
+++ b/web/controller/server.go
@@ -38,7 +38,9 @@ func (a *ServerController) initRouter(g *gin.RouterGroup) {
g.POST("/stopXrayService", a.stopXrayService)
g.POST("/restartXrayService", a.restartXrayService)
g.POST("/installXray/:version", a.installXray)
- g.POST("/logs", a.getLogs)
+ g.POST("/logs/:count", a.getLogs)
+ g.POST("/getConfigJson", a.getConfigJson)
+ g.GET("/getDb", a.getDb)
}
func (a *ServerController) refreshStatus() {
@@ -109,10 +111,34 @@ func (a *ServerController) restartXrayService(c *gin.Context) {
}
func (a *ServerController) getLogs(c *gin.Context) {
- logs, err := a.serverService.GetLogs()
+ count := c.Param("count")
+ logs, err := a.serverService.GetLogs(count)
if err != nil {
jsonMsg(c, I18n(c, "getLogs"), err)
return
}
jsonObj(c, logs, nil)
}
+
+func (a *ServerController) getConfigJson(c *gin.Context) {
+ configJson, err := a.serverService.GetConfigJson()
+ if err != nil {
+ jsonMsg(c, I18n(c, "getLogs"), err)
+ return
+ }
+ jsonObj(c, configJson, nil)
+}
+
+func (a *ServerController) getDb(c *gin.Context) {
+ db, err := a.serverService.GetDb()
+ if err != nil {
+ jsonMsg(c, I18n(c, "getLogs"), err)
+ return
+ }
+ // Set the headers for the response
+ c.Header("Content-Type", "application/octet-stream")
+ c.Header("Content-Disposition", "attachment; filename=xui.db")
+
+ // Write the file contents to the response
+ c.Writer.Write(db)
+}
diff --git a/web/controller/setting.go b/web/controller/setting.go
index 0456bca3..261eeec8 100644
--- a/web/controller/setting.go
+++ b/web/controller/setting.go
@@ -33,6 +33,7 @@ func (a *SettingController) initRouter(g *gin.RouterGroup) {
g = g.Group("/setting")
g.POST("/all", a.getAllSetting)
+ g.POST("/defaultSettings", a.getDefaultSettings)
g.POST("/update", a.updateSetting)
g.POST("/updateUser", a.updateUser)
g.POST("/restartPanel", a.restartPanel)
@@ -47,6 +48,36 @@ func (a *SettingController) getAllSetting(c *gin.Context) {
jsonObj(c, allSetting, nil)
}
+func (a *SettingController) getDefaultSettings(c *gin.Context) {
+ expireDiff, err := a.settingService.GetExpireDiff()
+ if err != nil {
+ jsonMsg(c, I18n(c, "pages.setting.toasts.getSetting"), err)
+ return
+ }
+ trafficDiff, err := a.settingService.GetTrafficDiff()
+ if err != nil {
+ jsonMsg(c, I18n(c, "pages.setting.toasts.getSetting"), err)
+ return
+ }
+ defaultCert, err := a.settingService.GetCertFile()
+ if err != nil {
+ jsonMsg(c, I18n(c, "pages.setting.toasts.getSetting"), err)
+ return
+ }
+ defaultKey, err := a.settingService.GetKeyFile()
+ if err != nil {
+ jsonMsg(c, I18n(c, "pages.setting.toasts.getSetting"), err)
+ return
+ }
+ result := map[string]interface{}{
+ "expireDiff": expireDiff,
+ "trafficDiff": trafficDiff,
+ "defaultCert": defaultCert,
+ "defaultKey": defaultKey,
+ }
+ jsonObj(c, result, nil)
+}
+
func (a *SettingController) updateSetting(c *gin.Context) {
allSetting := &entity.AllSetting{}
err := c.ShouldBind(allSetting)
diff --git a/web/controller/sub.go b/web/controller/sub.go
new file mode 100644
index 00000000..5695f032
--- /dev/null
+++ b/web/controller/sub.go
@@ -0,0 +1,42 @@
+package controller
+
+import (
+ "encoding/base64"
+ "strings"
+ "x-ui/web/service"
+
+ "github.com/gin-gonic/gin"
+)
+
+type SUBController struct {
+ BaseController
+
+ subService service.SubService
+}
+
+func NewSUBController(g *gin.RouterGroup) *SUBController {
+ a := &SUBController{}
+ a.initRouter(g)
+ return a
+}
+
+func (a *SUBController) initRouter(g *gin.RouterGroup) {
+ g = g.Group("/sub")
+
+ g.GET("/:subid", a.subs)
+}
+
+func (a *SUBController) subs(c *gin.Context) {
+ subId := c.Param("subid")
+ host := strings.Split(c.Request.Host, ":")[0]
+ subs, err := a.subService.GetSubs(subId, host)
+ if err != nil {
+ c.String(400, "Error!")
+ } else {
+ result := ""
+ for _, sub := range subs {
+ result += sub + "\n"
+ }
+ c.String(200, base64.StdEncoding.EncodeToString([]byte(result)))
+ }
+}
diff --git a/web/entity/entity.go b/web/entity/entity.go
index 372f5caa..b464de00 100644
--- a/web/entity/entity.go
+++ b/web/entity/entity.go
@@ -32,13 +32,13 @@ type AllSetting struct {
WebCertFile string `json:"webCertFile" form:"webCertFile"`
WebKeyFile string `json:"webKeyFile" form:"webKeyFile"`
WebBasePath string `json:"webBasePath" form:"webBasePath"`
+ ExpireDiff int `json:"expireDiff" form:"expireDiff"`
+ TrafficDiff int `json:"trafficDiff" form:"trafficDiff"`
TgBotEnable bool `json:"tgBotEnable" form:"tgBotEnable"`
TgBotToken string `json:"tgBotToken" form:"tgBotToken"`
TgBotChatId string `json:"tgBotChatId" form:"tgBotChatId"`
TgRunTime string `json:"tgRunTime" form:"tgRunTime"`
TgBotBackup bool `json:"tgBotBackup" form:"tgBotBackup"`
- TgExpireDiff int `json:"tgExpireDiff" form:"tgExpireDiff"`
- TgTrafficDiff int `json:"tgTrafficDiff" form:"tgTrafficDiff"`
TgCpu int `json:"tgCpu" form:"tgCpu"`
XrayTemplateConfig string `json:"xrayTemplateConfig" form:"xrayTemplateConfig"`
TimeLocation string `json:"timeLocation" form:"timeLocation"`
diff --git a/web/html/common/head.html b/web/html/common/head.html
index f34ce62f..5e8b1fef 100644
--- a/web/html/common/head.html
+++ b/web/html/common/head.html
@@ -7,6 +7,7 @@
+