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