mirror of
https://github.com/alireza0/x-ui.git
synced 2026-03-20 07:45:48 +00:00
Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e3c7b1db6 | ||
|
|
ab6c6c0ca6 | ||
|
|
9c0890dd9b | ||
|
|
eee0503200 | ||
|
|
f3c539dd73 | ||
|
|
c0464f1d97 | ||
|
|
b87474d70c | ||
|
|
063309dbb7 | ||
|
|
294b680972 | ||
|
|
f55478422b | ||
|
|
619e0c69cd | ||
|
|
da5dc3a04f | ||
|
|
1f920bb08c | ||
|
|
3528135297 | ||
|
|
bca0f63239 | ||
|
|
607fdc9f47 | ||
|
|
8775eb70e2 | ||
|
|
59204cdb0c | ||
|
|
49eedb7057 | ||
|
|
312c551cfb | ||
|
|
c5389d86a3 | ||
|
|
adf73cd87a | ||
|
|
a3f4e6f35c | ||
|
|
b8eee6e373 | ||
|
|
b3d3f76e84 | ||
|
|
3be40f8595 |
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
@@ -38,7 +38,7 @@ jobs:
|
|||||||
- name: package
|
- name: package
|
||||||
run: tar -zcvf x-ui-linux-amd64.tar.gz x-ui
|
run: tar -zcvf x-ui-linux-amd64.tar.gz x-ui
|
||||||
- name: upload
|
- name: upload
|
||||||
uses: svenstaro/upload-release-action@2.6.1
|
uses: svenstaro/upload-release-action@2.7.0
|
||||||
with:
|
with:
|
||||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
tag: ${{ github.ref }}
|
tag: ${{ github.ref }}
|
||||||
@@ -79,7 +79,7 @@ jobs:
|
|||||||
- name: package
|
- name: package
|
||||||
run: tar -zcvf x-ui-linux-arm64.tar.gz x-ui
|
run: tar -zcvf x-ui-linux-arm64.tar.gz x-ui
|
||||||
- name: upload
|
- name: upload
|
||||||
uses: svenstaro/upload-release-action@2.6.1
|
uses: svenstaro/upload-release-action@2.7.0
|
||||||
with:
|
with:
|
||||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
tag: ${{ github.ref }}
|
tag: ${{ github.ref }}
|
||||||
@@ -120,7 +120,7 @@ jobs:
|
|||||||
- name: package
|
- name: package
|
||||||
run: tar -zcvf x-ui-linux-s390x.tar.gz x-ui
|
run: tar -zcvf x-ui-linux-s390x.tar.gz x-ui
|
||||||
- name: upload
|
- name: upload
|
||||||
uses: svenstaro/upload-release-action@2.6.1
|
uses: svenstaro/upload-release-action@2.7.0
|
||||||
with:
|
with:
|
||||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
tag: ${{ github.ref }}
|
tag: ${{ github.ref }}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ else
|
|||||||
fi
|
fi
|
||||||
mkdir -p build/bin
|
mkdir -p build/bin
|
||||||
cd build/bin
|
cd build/bin
|
||||||
wget "https://github.com/XTLS/Xray-core/releases/download/v1.8.1/Xray-linux-${ARCH}.zip"
|
wget "https://github.com/XTLS/Xray-core/releases/download/v1.8.3/Xray-linux-${ARCH}.zip"
|
||||||
unzip "Xray-linux-${ARCH}.zip"
|
unzip "Xray-linux-${ARCH}.zip"
|
||||||
rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat iran.dat
|
rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat iran.dat
|
||||||
mv xray "xray-linux-${FNAME}"
|
mv xray "xray-linux-${FNAME}"
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
1.5.1
|
1.5.3
|
||||||
10
go.mod
10
go.mod
@@ -12,11 +12,11 @@ require (
|
|||||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
|
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
|
||||||
github.com/pelletier/go-toml/v2 v2.0.9
|
github.com/pelletier/go-toml/v2 v2.0.9
|
||||||
github.com/robfig/cron/v3 v3.0.1
|
github.com/robfig/cron/v3 v3.0.1
|
||||||
github.com/shirou/gopsutil/v3 v3.23.6
|
github.com/shirou/gopsutil/v3 v3.23.7
|
||||||
github.com/xtls/xray-core v1.8.3
|
github.com/xtls/xray-core v1.8.3
|
||||||
go.uber.org/atomic v1.11.0
|
go.uber.org/atomic v1.11.0
|
||||||
golang.org/x/text v0.11.0
|
golang.org/x/text v0.12.0
|
||||||
google.golang.org/grpc v1.56.2
|
google.golang.org/grpc v1.57.0
|
||||||
gorm.io/driver/sqlite v1.5.2
|
gorm.io/driver/sqlite v1.5.2
|
||||||
gorm.io/gorm v1.25.2
|
gorm.io/gorm v1.25.2
|
||||||
)
|
)
|
||||||
@@ -80,10 +80,10 @@ require (
|
|||||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
|
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
|
||||||
golang.org/x/mod v0.11.0 // indirect
|
golang.org/x/mod v0.11.0 // indirect
|
||||||
golang.org/x/net v0.11.0 // indirect
|
golang.org/x/net v0.11.0 // indirect
|
||||||
golang.org/x/sys v0.9.0 // indirect
|
golang.org/x/sys v0.10.0 // indirect
|
||||||
golang.org/x/time v0.3.0 // indirect
|
golang.org/x/time v0.3.0 // indirect
|
||||||
golang.org/x/tools v0.10.0 // indirect
|
golang.org/x/tools v0.10.0 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect
|
||||||
google.golang.org/protobuf v1.30.0 // indirect
|
google.golang.org/protobuf v1.30.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c // indirect
|
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c // indirect
|
||||||
|
|||||||
20
go.sum
20
go.sum
@@ -220,8 +220,8 @@ github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c/go.mod h1:eu
|
|||||||
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb h1:XfLJSPIOUX+osiMraVgIrMR27uMXnRJWGm1+GL8/63U=
|
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb h1:XfLJSPIOUX+osiMraVgIrMR27uMXnRJWGm1+GL8/63U=
|
||||||
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg=
|
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg=
|
||||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||||
github.com/shirou/gopsutil/v3 v3.23.6 h1:5y46WPI9QBKBbK7EEccUPNXpJpNrvPuTD0O2zHEHT08=
|
github.com/shirou/gopsutil/v3 v3.23.7 h1:C+fHO8hfIppoJ1WdsVm1RoI0RwXoNdfTK7yWXV0wVj4=
|
||||||
github.com/shirou/gopsutil/v3 v3.23.6/go.mod h1:j7QX50DrXYggrpN30W0Mo+I4/8U2UUIQrnrhqUeWrAU=
|
github.com/shirou/gopsutil/v3 v3.23.7/go.mod h1:c4gnmoRC0hQuaLqvxnx1//VXQ0Ms/X9UnJF8pddY5z4=
|
||||||
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
||||||
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
|
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
|
||||||
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
|
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
|
||||||
@@ -366,8 +366,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
|
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
|
||||||
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.10.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-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
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=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
@@ -376,8 +376,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.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.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.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
|
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
|
||||||
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||||
@@ -409,14 +409,14 @@ google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoA
|
|||||||
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
|
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
|
||||||
google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc h1:XSJ8Vk1SWuNr8S18z1NZSziL0CPIXLCCMDOEFtHBOFc=
|
||||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
|
||||||
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||||
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
||||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.56.2 h1:fVRFRnXvU+x6C4IlHZewvJOVHoOv1TUuQyoRsYnB4bI=
|
google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw=
|
||||||
google.golang.org/grpc v1.56.2/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
|
google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo=
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
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.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||||
|
|||||||
@@ -113,7 +113,6 @@ config_after_install() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
install_x-ui() {
|
install_x-ui() {
|
||||||
systemctl stop x-ui
|
|
||||||
cd /usr/local/
|
cd /usr/local/
|
||||||
|
|
||||||
if [ $# == 0 ]; then
|
if [ $# == 0 ]; then
|
||||||
@@ -140,6 +139,7 @@ install_x-ui() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -e /usr/local/x-ui/ ]]; then
|
if [[ -e /usr/local/x-ui/ ]]; then
|
||||||
|
systemctl stop x-ui
|
||||||
rm /usr/local/x-ui/ -rf
|
rm /usr/local/x-ui/ -rf
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +1,47 @@
|
|||||||
package logger
|
package logger
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/op/go-logging"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/op/go-logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
var logger *logging.Logger
|
var logger *logging.Logger
|
||||||
|
var logBuffer []struct {
|
||||||
|
time string
|
||||||
|
level logging.Level
|
||||||
|
log string
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
InitLogger(logging.INFO)
|
InitLogger(logging.INFO)
|
||||||
}
|
}
|
||||||
|
|
||||||
func InitLogger(level logging.Level) {
|
func InitLogger(level logging.Level) {
|
||||||
format := logging.MustStringFormatter(
|
|
||||||
`%{time:2006/01/02 15:04:05} %{level} - %{message}`,
|
|
||||||
)
|
|
||||||
newLogger := logging.MustGetLogger("x-ui")
|
newLogger := logging.MustGetLogger("x-ui")
|
||||||
backend := logging.NewLogBackend(os.Stderr, "", 0)
|
var err error
|
||||||
|
var backend logging.Backend
|
||||||
|
var format logging.Formatter
|
||||||
|
ppid := os.Getppid()
|
||||||
|
|
||||||
|
if ppid == 1 {
|
||||||
|
backend, err = logging.NewSyslogBackend("")
|
||||||
|
format = logging.MustStringFormatter(
|
||||||
|
`%{level} - %{message}`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if err != nil || ppid != 1 {
|
||||||
|
backend = logging.NewLogBackend(os.Stderr, "", 0)
|
||||||
|
format = logging.MustStringFormatter(
|
||||||
|
`%{time:2006/01/02 15:04:05} %{level} - %{message}`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
backendFormatter := logging.NewBackendFormatter(backend, format)
|
backendFormatter := logging.NewBackendFormatter(backend, format)
|
||||||
backendLeveled := logging.AddModuleLevel(backendFormatter)
|
backendLeveled := logging.AddModuleLevel(backendFormatter)
|
||||||
backendLeveled.SetLevel(level, "")
|
backendLeveled.SetLevel(level, "x-ui")
|
||||||
newLogger.SetBackend(backendLeveled)
|
newLogger.SetBackend(backendLeveled)
|
||||||
|
|
||||||
logger = newLogger
|
logger = newLogger
|
||||||
@@ -27,32 +49,70 @@ func InitLogger(level logging.Level) {
|
|||||||
|
|
||||||
func Debug(args ...interface{}) {
|
func Debug(args ...interface{}) {
|
||||||
logger.Debug(args...)
|
logger.Debug(args...)
|
||||||
|
addToBuffer("DEBUG", fmt.Sprint(args...))
|
||||||
}
|
}
|
||||||
|
|
||||||
func Debugf(format string, args ...interface{}) {
|
func Debugf(format string, args ...interface{}) {
|
||||||
logger.Debugf(format, args...)
|
logger.Debugf(format, args...)
|
||||||
|
addToBuffer("DEBUG", fmt.Sprintf(format, args...))
|
||||||
}
|
}
|
||||||
|
|
||||||
func Info(args ...interface{}) {
|
func Info(args ...interface{}) {
|
||||||
logger.Info(args...)
|
logger.Info(args...)
|
||||||
|
addToBuffer("INFO", fmt.Sprint(args...))
|
||||||
}
|
}
|
||||||
|
|
||||||
func Infof(format string, args ...interface{}) {
|
func Infof(format string, args ...interface{}) {
|
||||||
logger.Infof(format, args...)
|
logger.Infof(format, args...)
|
||||||
|
addToBuffer("INFO", fmt.Sprintf(format, args...))
|
||||||
}
|
}
|
||||||
|
|
||||||
func Warning(args ...interface{}) {
|
func Warning(args ...interface{}) {
|
||||||
logger.Warning(args...)
|
logger.Warning(args...)
|
||||||
|
addToBuffer("WARNING", fmt.Sprint(args...))
|
||||||
}
|
}
|
||||||
|
|
||||||
func Warningf(format string, args ...interface{}) {
|
func Warningf(format string, args ...interface{}) {
|
||||||
logger.Warningf(format, args...)
|
logger.Warningf(format, args...)
|
||||||
|
addToBuffer("WARNING", fmt.Sprintf(format, args...))
|
||||||
}
|
}
|
||||||
|
|
||||||
func Error(args ...interface{}) {
|
func Error(args ...interface{}) {
|
||||||
logger.Error(args...)
|
logger.Error(args...)
|
||||||
|
addToBuffer("ERROR", fmt.Sprint(args...))
|
||||||
}
|
}
|
||||||
|
|
||||||
func Errorf(format string, args ...interface{}) {
|
func Errorf(format string, args ...interface{}) {
|
||||||
logger.Errorf(format, args...)
|
logger.Errorf(format, args...)
|
||||||
|
addToBuffer("ERROR", fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func addToBuffer(level string, newLog string) {
|
||||||
|
t := time.Now()
|
||||||
|
if len(logBuffer) >= 10240 {
|
||||||
|
logBuffer = logBuffer[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
logLevel, _ := logging.LogLevel(level)
|
||||||
|
logBuffer = append(logBuffer, struct {
|
||||||
|
time string
|
||||||
|
level logging.Level
|
||||||
|
log string
|
||||||
|
}{
|
||||||
|
time: t.Format("2006/01/02 15:04:05"),
|
||||||
|
level: logLevel,
|
||||||
|
log: newLog,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetLogs(c int, level string) []string {
|
||||||
|
var output []string
|
||||||
|
logLevel, _ := logging.LogLevel(level)
|
||||||
|
|
||||||
|
for i := len(logBuffer) - 1; i >= 0 && len(output) <= c; i-- {
|
||||||
|
if logBuffer[i].level <= logLevel {
|
||||||
|
output = append(output, fmt.Sprintf("%s %s - %s", logBuffer[i].time, logBuffer[i].level, logBuffer[i].log))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -657,7 +657,10 @@ func (s *SubService) genShadowsocksLink(inbound *model.Inbound, email string) st
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
encPart := fmt.Sprintf("%s:%s:%s", method, inboundPassword, clients[clientIndex].Password)
|
encPart := fmt.Sprintf("%s:%s", method, clients[clientIndex].Password)
|
||||||
|
if method[0] == '2' {
|
||||||
|
encPart = fmt.Sprintf("%s:%s:%s", method, inboundPassword, clients[clientIndex].Password)
|
||||||
|
}
|
||||||
link := fmt.Sprintf("ss://%s@%s:%d", base64.StdEncoding.EncodeToString([]byte(encPart)), address, inbound.Port)
|
link := fmt.Sprintf("ss://%s@%s:%d", base64.StdEncoding.EncodeToString([]byte(encPart)), address, inbound.Port)
|
||||||
url, _ := url.Parse(link)
|
url, _ := url.Parse(link)
|
||||||
q := url.Query()
|
q := url.Query()
|
||||||
|
|||||||
@@ -16,9 +16,10 @@ const VmessMethods = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const SSMethods = {
|
const SSMethods = {
|
||||||
CHACHA20_POLY1305: 'chacha20-poly1305',
|
|
||||||
AES_256_GCM: 'aes-256-gcm',
|
AES_256_GCM: 'aes-256-gcm',
|
||||||
AES_128_GCM: 'aes-128-gcm',
|
AES_128_GCM: 'aes-128-gcm',
|
||||||
|
CHACHA20_POLY1305: 'chacha20-poly1305',
|
||||||
|
XCHACHA20_POLY1305: 'xchacha20-poly1305',
|
||||||
BLAKE3_AES_128_GCM: '2022-blake3-aes-128-gcm',
|
BLAKE3_AES_128_GCM: '2022-blake3-aes-128-gcm',
|
||||||
BLAKE3_AES_256_GCM: '2022-blake3-aes-256-gcm',
|
BLAKE3_AES_256_GCM: '2022-blake3-aes-256-gcm',
|
||||||
BLAKE3_CHACHA20_POLY1305: '2022-blake3-chacha20-poly1305',
|
BLAKE3_CHACHA20_POLY1305: '2022-blake3-chacha20-poly1305',
|
||||||
@@ -873,7 +874,10 @@ class Inbound extends XrayCommonClass {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
get isSSMultiUser() {
|
get isSSMultiUser() {
|
||||||
return [SSMethods.BLAKE3_AES_128_GCM,SSMethods.BLAKE3_AES_256_GCM].includes(this.method);
|
return this.method != SSMethods.BLAKE3_CHACHA20_POLY1305;
|
||||||
|
}
|
||||||
|
get isSS2022(){
|
||||||
|
return this.method.substring(0,4) === "2022";
|
||||||
}
|
}
|
||||||
|
|
||||||
get serverName() {
|
get serverName() {
|
||||||
@@ -1274,9 +1278,11 @@ class Inbound extends XrayCommonClass {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let clientPassword = this.isSSMultiUser ? ':' + settings.shadowsockses[clientIndex].password : '';
|
let password = new Array();
|
||||||
|
if (this.isSS2022) password.push(settings.password);
|
||||||
|
if (this.isSSMultiUser) password.push(settings.shadowsockses[clientIndex].password);
|
||||||
|
|
||||||
let link = `ss://${safeBase64(settings.method + ':' + settings.password + clientPassword)}@${address}:${this.port}`;
|
let link = `ss://${safeBase64(settings.method + ':' + password.join(':'))}@${address}:${this.port}`;
|
||||||
const url = new URL(link);
|
const url = new URL(link);
|
||||||
for (const [key, value] of params) {
|
for (const [key, value] of params) {
|
||||||
url.searchParams.set(key, value)
|
url.searchParams.set(key, value)
|
||||||
@@ -1872,8 +1878,9 @@ Inbound.ShadowsocksSettings = class extends Inbound.Settings {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Inbound.ShadowsocksSettings.Shadowsocks = class extends XrayCommonClass {
|
Inbound.ShadowsocksSettings.Shadowsocks = class extends XrayCommonClass {
|
||||||
constructor(password=RandomUtil.randomShadowsocksPassword(), email=RandomUtil.randomText(), totalGB=0, expiryTime=0, enable=true, tgId='', subId=RandomUtil.randomText(16,16)) {
|
constructor(method='', password=RandomUtil.randomShadowsocksPassword(), email=RandomUtil.randomText(), totalGB=0, expiryTime=0, enable=true, tgId='', subId=RandomUtil.randomText(16,16)) {
|
||||||
super();
|
super();
|
||||||
|
this.method = method;
|
||||||
this.password = password;
|
this.password = password;
|
||||||
this.email = email;
|
this.email = email;
|
||||||
this.totalGB = totalGB;
|
this.totalGB = totalGB;
|
||||||
@@ -1885,6 +1892,7 @@ Inbound.ShadowsocksSettings.Shadowsocks = class extends XrayCommonClass {
|
|||||||
|
|
||||||
toJson() {
|
toJson() {
|
||||||
return {
|
return {
|
||||||
|
method: this.method,
|
||||||
password: this.password,
|
password: this.password,
|
||||||
email: this.email,
|
email: this.email,
|
||||||
totalGB: this.totalGB,
|
totalGB: this.totalGB,
|
||||||
@@ -1897,6 +1905,7 @@ Inbound.ShadowsocksSettings.Shadowsocks = class extends XrayCommonClass {
|
|||||||
|
|
||||||
static fromJson(json = {}) {
|
static fromJson(json = {}) {
|
||||||
return new Inbound.ShadowsocksSettings.Shadowsocks(
|
return new Inbound.ShadowsocksSettings.Shadowsocks(
|
||||||
|
json.method,
|
||||||
json.password,
|
json.password,
|
||||||
json.email,
|
json.email,
|
||||||
json.totalGB,
|
json.totalGB,
|
||||||
|
|||||||
@@ -118,11 +118,9 @@ func (a *ServerController) restartXrayService(c *gin.Context) {
|
|||||||
|
|
||||||
func (a *ServerController) getLogs(c *gin.Context) {
|
func (a *ServerController) getLogs(c *gin.Context) {
|
||||||
count := c.Param("count")
|
count := c.Param("count")
|
||||||
logs, err := a.serverService.GetLogs(count)
|
level := c.PostForm("level")
|
||||||
if err != nil {
|
syslog := c.PostForm("syslog")
|
||||||
jsonMsg(c, "getLogs", err)
|
logs := a.serverService.GetLogs(count, level, syslog)
|
||||||
return
|
|
||||||
}
|
|
||||||
jsonObj(c, logs, nil)
|
jsonObj(c, logs, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -210,21 +210,12 @@
|
|||||||
this.inbound = dbInbound.toInbound();
|
this.inbound = dbInbound.toInbound();
|
||||||
this.delayedStart = false;
|
this.delayedStart = false;
|
||||||
},
|
},
|
||||||
getClients(protocol, clientSettings) {
|
|
||||||
switch(protocol){
|
|
||||||
case Protocols.VMESS: return clientSettings.vmesses;
|
|
||||||
case Protocols.VLESS: return clientSettings.vlesses;
|
|
||||||
case Protocols.TROJAN: return clientSettings.trojans;
|
|
||||||
case Protocols.SHADOWSOCKS: return clientSettings.shadowsockses;
|
|
||||||
default: return null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
newClient(protocol) {
|
newClient(protocol) {
|
||||||
switch (protocol) {
|
switch (protocol) {
|
||||||
case Protocols.VMESS: return new Inbound.VmessSettings.Vmess();
|
case Protocols.VMESS: return new Inbound.VmessSettings.Vmess();
|
||||||
case Protocols.VLESS: return new Inbound.VLESSSettings.VLESS();
|
case Protocols.VLESS: return new Inbound.VLESSSettings.VLESS();
|
||||||
case Protocols.TROJAN: return new Inbound.TrojanSettings.Trojan();
|
case Protocols.TROJAN: return new Inbound.TrojanSettings.Trojan();
|
||||||
case Protocols.SHADOWSOCKS: return new Inbound.ShadowsocksSettings.Shadowsocks();
|
case Protocols.SHADOWSOCKS: return new Inbound.ShadowsocksSettings.Shadowsocks(clientsBulkModal.inbound.settings.shadowsockses[0].method);
|
||||||
default: return null;
|
default: return null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -69,7 +69,7 @@
|
|||||||
case Protocols.VMESS: return clients.push(new Inbound.VmessSettings.Vmess());
|
case Protocols.VMESS: return clients.push(new Inbound.VmessSettings.Vmess());
|
||||||
case Protocols.VLESS: return clients.push(new Inbound.VLESSSettings.VLESS());
|
case Protocols.VLESS: return clients.push(new Inbound.VLESSSettings.VLESS());
|
||||||
case Protocols.TROJAN: return clients.push(new Inbound.TrojanSettings.Trojan());
|
case Protocols.TROJAN: return clients.push(new Inbound.TrojanSettings.Trojan());
|
||||||
case Protocols.SHADOWSOCKS: return clients.push(new Inbound.ShadowsocksSettings.Shadowsocks());
|
case Protocols.SHADOWSOCKS: return clients.push(new Inbound.ShadowsocksSettings.Shadowsocks(clients[0].method));
|
||||||
default: return null;
|
default: return null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,22 +1,19 @@
|
|||||||
{{define "form/http"}}
|
{{define "form/http"}}
|
||||||
<a-form layout="inline">
|
<a-form layout="inline">
|
||||||
<table width="100%" class="ant-table-tbody">
|
<a-form-item>
|
||||||
<tr>
|
<a-row>
|
||||||
<td>{{ i18n "username"}}</td>
|
<a-button size="small" @click="inbound.settings.addAccount(new Inbound.SocksSettings.SocksAccount())">+</a-button>
|
||||||
<td>
|
</a-row>
|
||||||
<a-form-item>
|
<a-input-group v-for="(account, index) in inbound.settings.accounts">
|
||||||
<a-input v-model.trim="inbound.settings.accounts[0].user"></a-input>
|
<a-input style="width: 45%" v-model.trim="account.user"
|
||||||
</a-form-item>
|
addon-before='{{ i18n "username" }}'></a-input>
|
||||||
</td>
|
<a-input style="width: 55%" v-model.trim="account.pass"
|
||||||
</tr>
|
addon-before='{{ i18n "password" }}'>
|
||||||
<tr>
|
<template slot="addonAfter">
|
||||||
<td>{{ i18n "password" }}</td>
|
<a-button size="small" @click="inbound.settings.delAccount(index)">-</a-button>
|
||||||
<td>
|
</template>
|
||||||
<a-form-item>
|
</a-input>
|
||||||
<a-input v-model.trim="inbound.settings.accounts[0].pass"></a-input>
|
</a-input-group>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</a-form>
|
</a-form>
|
||||||
{{end}}
|
{{end}}
|
||||||
@@ -125,7 +125,7 @@
|
|||||||
</a-form-item>
|
</a-form-item>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr v-if="inbound.isSS2022">
|
||||||
<td>{{ i18n "password" }}
|
<td>{{ i18n "password" }}
|
||||||
<a-icon @click="inbound.settings.password = RandomUtil.randomShadowsocksPassword()" type="sync"> </a-icon>
|
<a-icon @click="inbound.settings.password = RandomUtil.randomShadowsocksPassword()" type="sync"> </a-icon>
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
@@ -10,24 +10,25 @@
|
|||||||
</a-form-item>
|
</a-form-item>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<template v-if="inbound.settings.auth === 'password'">
|
<tr v-if="inbound.settings.auth === 'password'">
|
||||||
<tr>
|
<td colspan="2">
|
||||||
<td>{{ i18n "username" }}</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
<a-form-item>
|
||||||
<a-input v-model.trim="inbound.settings.accounts[0].user"></a-input>
|
<a-row>
|
||||||
|
<a-button size="small" @click="inbound.settings.addAccount(new Inbound.SocksSettings.SocksAccount())">+</a-button>
|
||||||
|
</a-row>
|
||||||
|
<a-input-group v-for="(account, index) in inbound.settings.accounts">
|
||||||
|
<a-input style="width: 45%" v-model.trim="account.user"
|
||||||
|
addon-before='{{ i18n "username" }}'></a-input>
|
||||||
|
<a-input style="width: 55%" v-model.trim="account.pass"
|
||||||
|
addon-before='{{ i18n "password" }}'>
|
||||||
|
<template slot="addonAfter">
|
||||||
|
<a-button size="small" @click="inbound.settings.delAccount(index)">-</a-button>
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
</a-input-group>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<td>{{ i18n "password" }}</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-input v-model.trim="inbound.settings.accounts[0].pass"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</template>
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ i18n "pages.inbounds.enable" }} udp</td>
|
<td>{{ i18n "pages.inbounds.enable" }} udp</td>
|
||||||
<td>
|
<td>
|
||||||
@@ -36,10 +37,10 @@
|
|||||||
</a-form-item>
|
</a-form-item>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr v-if="inbound.settings.udp">
|
||||||
<td>IP</td>
|
<td>IP</td>
|
||||||
<td>
|
<td>
|
||||||
<a-form-item v-if="inbound.settings.udp">
|
<a-form-item>
|
||||||
<a-input v-model.trim="inbound.settings.ip"></a-input>
|
<a-input v-model.trim="inbound.settings.ip"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
@@ -45,17 +45,28 @@
|
|||||||
</template>
|
</template>
|
||||||
</table>
|
</table>
|
||||||
</td></tr>
|
</td></tr>
|
||||||
<tr colspan="2" v-if="dbInbound.hasLink()">
|
<tr colspan="2" v-if="dbInbound.hasLink()">
|
||||||
<td v-if="inbound.tls">
|
<td v-if="inbound.tls">
|
||||||
tls: <a-tag color="green">{{ i18n "enabled" }}</a-tag><br />
|
tls: <a-tag color="green">{{ i18n "enabled" }}</a-tag><br />
|
||||||
tls {{ i18n "domainName" }}: <a-tag :color="inbound.serverName ? 'green' : 'orange'">[[ inbound.serverName ? inbound.serverName : '' ]]</a-tag>
|
tls {{ i18n "domainName" }}: <a-tag :color="inbound.serverName ? 'green' : 'orange'">[[ inbound.serverName ? inbound.serverName : '' ]]</a-tag>
|
||||||
</td>
|
|
||||||
<td v-else-if="inbound.reality">
|
|
||||||
reality: <a-tag color="green">{{ i18n "enabled" }}</a-tag><br />
|
|
||||||
reality Destination: <a-tag :color="inbound.stream.reality.dest ? 'green' : 'orange'">[[ inbound.stream.reality.dest ]]</a-tag>
|
|
||||||
</td>
|
|
||||||
<td v-else>tls: <a-tag color="red">{{ i18n "disabled" }}</a-tag>
|
|
||||||
</td>
|
</td>
|
||||||
|
<td v-else-if="inbound.reality">
|
||||||
|
reality: <a-tag color="green">{{ i18n "enabled" }}</a-tag><br />
|
||||||
|
reality Destination: <a-tag :color="inbound.stream.reality.dest ? 'green' : 'orange'">[[ inbound.stream.reality.dest ]]</a-tag>
|
||||||
|
</td>
|
||||||
|
<td v-else>tls: <a-tag color="red">{{ i18n "disabled" }}</a-tag></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<table v-if="dbInbound.isSS" style="margin-bottom: 10px; width: 100%;">
|
||||||
|
<tr>
|
||||||
|
<td>{{ i18n "encryption" }}</td>
|
||||||
|
<td><a-tag color="green">[[ inbound.settings.method ]]</a-tag></td>
|
||||||
|
</tr><tr v-if="inbound.isSS2022">
|
||||||
|
<td>{{ i18n "password" }}</td>
|
||||||
|
<td><a-tag color="blue">[[ inbound.settings.password ]]</a-tag></td>
|
||||||
|
</tr><tr>
|
||||||
|
<td>{{ i18n "pages.inbounds.network" }}</td>
|
||||||
|
<td><a-tag color="green">[[ inbound.settings.network ]]</a-tag></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<template v-if="infoModal.clientSettings">
|
<template v-if="infoModal.clientSettings">
|
||||||
@@ -163,19 +174,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<a-divider></a-divider>
|
<template v-if="dbInbound.isSS && !inbound.isSSMultiUser">
|
||||||
<table v-if="inbound.protocol == Protocols.SHADOWSOCKS" style="margin-bottom: 10px; width: 100%;">
|
|
||||||
<tr>
|
|
||||||
<th>{{ i18n "encryption" }}</th>
|
|
||||||
<th>{{ i18n "password" }}</th>
|
|
||||||
<th>{{ i18n "pages.inbounds.network" }}</th>
|
|
||||||
</tr><tr>
|
|
||||||
<td><a-tag color="green">[[ inbound.settings.method ]]</a-tag></td>
|
|
||||||
<td><a-tag color="blue">[[ inbound.settings.password ]]</a-tag></td>
|
|
||||||
<td><a-tag color="green">[[ inbound.settings.network ]]</a-tag></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
<template v-if="inbound.protocol == Protocols.SHADOWSOCKS && !inbound.isSSMultiUser">
|
|
||||||
<a-divider>URL</a-divider>
|
<a-divider>URL</a-divider>
|
||||||
<a-row v-for="(link,index) in infoModal.links">
|
<a-row v-for="(link,index) in infoModal.links">
|
||||||
<a-col :span="22"><a-tag color="cyan">[[ link.remark ]]</a-tag><br />[[ link.link ]]</a-col>
|
<a-col :span="22"><a-tag color="cyan">[[ link.remark ]]</a-tag><br />[[ link.link ]]</a-col>
|
||||||
@@ -201,17 +200,19 @@
|
|||||||
<td><a-tag color="blue">[[ inbound.settings.followRedirect ]]</a-tag></td>
|
<td><a-tag color="blue">[[ inbound.settings.followRedirect ]]</a-tag></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</table>
|
<table v-if="dbInbound.isSocks" style="margin-bottom: 10px; width: 100%;">
|
||||||
<table v-if="inbound.protocol == Protocols.SOCKS" style="margin-bottom: 10px; width: 100%;">
|
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{ i18n "password" }} Auth</th>
|
<th>{{ i18n "password" }} Auth</th>
|
||||||
<th>{{ i18n "pages.inbounds.enable" }} udp</th>
|
<th>{{ i18n "pages.inbounds.enable" }} udp</th>
|
||||||
<th>IP</th>
|
<th>IP</th>
|
||||||
</tr><tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
<td><a-tag color="green">[[ inbound.settings.auth ]]</a-tag></td>
|
<td><a-tag color="green">[[ inbound.settings.auth ]]</a-tag></td>
|
||||||
<td><a-tag color="blue">[[ inbound.settings.udp]]</a-tag></td>
|
<td><a-tag color="blue">[[ inbound.settings.udp]]</a-tag></td>
|
||||||
<td><a-tag color="green">[[ inbound.settings.ip ]]</a-tag></td>
|
<td><a-tag color="green">[[ inbound.settings.ip ]]</a-tag></td>
|
||||||
</tr><tr v-if="inbound.settings.auth == 'password'">
|
</tr>
|
||||||
|
<template v-if="inbound.settings.auth == 'password'">
|
||||||
|
<tr>
|
||||||
<td> </td>
|
<td> </td>
|
||||||
<td>{{ i18n "username" }}</td>
|
<td>{{ i18n "username" }}</td>
|
||||||
<td>{{ i18n "password" }}</td>
|
<td>{{ i18n "password" }}</td>
|
||||||
@@ -220,9 +221,9 @@
|
|||||||
<td><a-tag color="blue">[[ account.user ]]</a-tag></td>
|
<td><a-tag color="blue">[[ account.user ]]</a-tag></td>
|
||||||
<td><a-tag color="green">[[ account.pass ]]</a-tag></td>
|
<td><a-tag color="green">[[ account.pass ]]</a-tag></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
</template>
|
||||||
</table>
|
</table>
|
||||||
</table>
|
<table v-if="dbInbound.isHTTP" style="margin-bottom: 10px; width: 100%;">
|
||||||
<table v-if="inbound.protocol == Protocols.HTTP" style="margin-bottom: 10px; width: 100%;">
|
|
||||||
<tr>
|
<tr>
|
||||||
<th> </th>
|
<th> </th>
|
||||||
<th>{{ i18n "username" }}</th>
|
<th>{{ i18n "username" }}</th>
|
||||||
@@ -233,7 +234,6 @@
|
|||||||
<td><a-tag color="green">[[ account.pass ]]</a-tag></td>
|
<td><a-tag color="green">[[ account.pass ]]</a-tag></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</table>
|
|
||||||
</template>
|
</template>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
@@ -110,6 +110,15 @@
|
|||||||
if (this.inModal.inbound.settings.shadowsockses.length ==0){
|
if (this.inModal.inbound.settings.shadowsockses.length ==0){
|
||||||
this.inModal.inbound.settings.shadowsockses = [new Inbound.ShadowsocksSettings.Shadowsocks()];
|
this.inModal.inbound.settings.shadowsockses = [new Inbound.ShadowsocksSettings.Shadowsocks()];
|
||||||
}
|
}
|
||||||
|
if (["aes-128-gcm", "aes-256-gcm", "chacha20-poly1305", "xchacha20-poly1305"].includes(this.inModal.inbound.settings.method)) {
|
||||||
|
this.inModal.inbound.settings.shadowsockses.forEach(client => {
|
||||||
|
client.method = this.inModal.inbound.settings.method;
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.inModal.inbound.settings.shadowsockses.forEach(client => {
|
||||||
|
client.method = "";
|
||||||
|
})
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (this.inModal.inbound.settings.shadowsockses.length > 0){
|
if (this.inModal.inbound.settings.shadowsockses.length > 0){
|
||||||
this.inModal.inbound.settings.shadowsockses = [];
|
this.inModal.inbound.settings.shadowsockses = [];
|
||||||
|
|||||||
@@ -11,6 +11,9 @@
|
|||||||
.ant-col-sm-24 {
|
.ant-col-sm-24 {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
tr.hideExpandIcon .ant-table-row-expand-icon {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@@ -20,9 +23,13 @@
|
|||||||
<a-layout-content>
|
<a-layout-content>
|
||||||
<a-spin :spinning="spinning" :delay="500" tip="loading">
|
<a-spin :spinning="spinning" :delay="500" tip="loading">
|
||||||
<transition name="list" appear>
|
<transition name="list" appear>
|
||||||
<a-tag v-if="false" color="red" style="margin-bottom: 10px">
|
<a-alert type="error" v-if="showAlert" style="margin-bottom: 10px"
|
||||||
Please go to the panel settings as soon as possible to modify the username and password, otherwise there may be a risk of leaking account information
|
message='{{ i18n "secAlertTitle" }}'
|
||||||
</a-tag>
|
color="red"
|
||||||
|
description='{{ i18n "secAlertSsl" }}'
|
||||||
|
show-icon closable
|
||||||
|
>
|
||||||
|
</a-alert>
|
||||||
</transition>
|
</transition>
|
||||||
<transition name="list" appear>
|
<transition name="list" appear>
|
||||||
<a-card hoverable style="margin-bottom: 20px;" :class="themeSwitcher.darkCardClass">
|
<a-card hoverable style="margin-bottom: 20px;" :class="themeSwitcher.darkCardClass">
|
||||||
@@ -117,8 +124,12 @@
|
|||||||
</a-radio-group>
|
</a-radio-group>
|
||||||
<a-table :columns="columns" :row-key="dbInbound => dbInbound.id"
|
<a-table :columns="columns" :row-key="dbInbound => dbInbound.id"
|
||||||
:data-source="searchedInbounds"
|
:data-source="searchedInbounds"
|
||||||
:loading="spinning" :scroll="{ x: 1300 }"
|
:loading="spinning" :scroll="{ x: 1200 }"
|
||||||
:pagination="false"
|
:pagination="false"
|
||||||
|
:expand-icon-as-cell="false"
|
||||||
|
:expand-row-by-click="false"
|
||||||
|
:expand-icon-column-index="0"
|
||||||
|
:row-class-name="dbInbound => (dbInbound.isTrojan || dbInbound.isVLess || dbInbound.isVMess || (dbInbound.isSS && dbInbound.toInbound().isSSMultiUser) ? '' : 'hideExpandIcon')"
|
||||||
style="margin-top: 20px"
|
style="margin-top: 20px"
|
||||||
@change="() => getDBInbounds()">
|
@change="() => getDBInbounds()">
|
||||||
<template slot="action" slot-scope="text, dbInbound">
|
<template slot="action" slot-scope="text, dbInbound">
|
||||||
@@ -133,7 +144,7 @@
|
|||||||
<a-icon type="qrcode"></a-icon>
|
<a-icon type="qrcode"></a-icon>
|
||||||
{{ i18n "qrCode" }}
|
{{ i18n "qrCode" }}
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
<template v-if="dbInbound.isTrojan || dbInbound.isVLess || dbInbound.isVMess || dbInbound.toInbound().isSSMultiUser">
|
<template v-if="dbInbound.isTrojan || dbInbound.isVLess || dbInbound.isVMess || (dbInbound.isSS && dbInbound.toInbound().isSSMultiUser)">
|
||||||
<a-menu-item key="addClient">
|
<a-menu-item key="addClient">
|
||||||
<a-icon type="user-add"></a-icon>
|
<a-icon type="user-add"></a-icon>
|
||||||
{{ i18n "pages.client.add"}}
|
{{ i18n "pages.client.add"}}
|
||||||
@@ -250,6 +261,7 @@
|
|||||||
:columns="innerColumns"
|
:columns="innerColumns"
|
||||||
:data-source="getInboundClients(record)"
|
:data-source="getInboundClients(record)"
|
||||||
:pagination="false"
|
:pagination="false"
|
||||||
|
style="margin-left: 20px;"
|
||||||
>
|
>
|
||||||
{{template "client_table"}}
|
{{template "client_table"}}
|
||||||
</a-table>
|
</a-table>
|
||||||
@@ -259,6 +271,7 @@
|
|||||||
:columns="innerTrojanColumns"
|
:columns="innerTrojanColumns"
|
||||||
:data-source="getInboundClients(record)"
|
:data-source="getInboundClients(record)"
|
||||||
:pagination="false"
|
:pagination="false"
|
||||||
|
style="margin-left: 20px;"
|
||||||
>
|
>
|
||||||
{{template "client_table"}}
|
{{template "client_table"}}
|
||||||
</a-table>
|
</a-table>
|
||||||
@@ -274,6 +287,11 @@
|
|||||||
{{template "component/themeSwitcher" .}}
|
{{template "component/themeSwitcher" .}}
|
||||||
<script>
|
<script>
|
||||||
const columns = [{
|
const columns = [{
|
||||||
|
title: "ID",
|
||||||
|
align: 'right',
|
||||||
|
dataIndex: "id",
|
||||||
|
width: 30,
|
||||||
|
}, {
|
||||||
title: '{{ i18n "pages.inbounds.operate" }}',
|
title: '{{ i18n "pages.inbounds.operate" }}',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
width: 30,
|
width: 30,
|
||||||
@@ -283,11 +301,6 @@
|
|||||||
align: 'center',
|
align: 'center',
|
||||||
width: 30,
|
width: 30,
|
||||||
scopedSlots: { customRender: 'enable' },
|
scopedSlots: { customRender: 'enable' },
|
||||||
}, {
|
|
||||||
title: "ID",
|
|
||||||
align: 'center',
|
|
||||||
dataIndex: "id",
|
|
||||||
width: 20,
|
|
||||||
}, {
|
}, {
|
||||||
title: '{{ i18n "pages.inbounds.remark" }}',
|
title: '{{ i18n "pages.inbounds.remark" }}',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
@@ -324,8 +337,8 @@
|
|||||||
{ title: '{{ i18n "pages.inbounds.operate" }}', width: 70, scopedSlots: { customRender: 'actions' } },
|
{ title: '{{ i18n "pages.inbounds.operate" }}', width: 70, scopedSlots: { customRender: 'actions' } },
|
||||||
{ title: '{{ i18n "pages.inbounds.enable" }}', width: 30, scopedSlots: { customRender: 'enable' } },
|
{ title: '{{ i18n "pages.inbounds.enable" }}', width: 30, scopedSlots: { customRender: 'enable' } },
|
||||||
{ title: '{{ i18n "pages.inbounds.client" }}', width: 80, scopedSlots: { customRender: 'client' } },
|
{ title: '{{ i18n "pages.inbounds.client" }}', width: 80, scopedSlots: { customRender: 'client' } },
|
||||||
{ title: '{{ i18n "pages.inbounds.traffic" }}', width: 50, scopedSlots: { customRender: 'traffic' } },
|
{ title: '{{ i18n "pages.inbounds.traffic" }}', width: 60, scopedSlots: { customRender: 'traffic' } },
|
||||||
{ title: '{{ i18n "pages.inbounds.expireDate" }}', width: 50, scopedSlots: { customRender: 'expiryTime' } },
|
{ title: '{{ i18n "pages.inbounds.expireDate" }}', width: 55, scopedSlots: { customRender: 'expiryTime' } },
|
||||||
{ title: 'UUID', width: 120, dataIndex: "id" },
|
{ title: 'UUID', width: 120, dataIndex: "id" },
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -334,8 +347,8 @@
|
|||||||
{ title: '{{ i18n "pages.inbounds.enable" }}', width: 30, scopedSlots: { customRender: 'enable' } },
|
{ title: '{{ i18n "pages.inbounds.enable" }}', width: 30, scopedSlots: { customRender: 'enable' } },
|
||||||
{ title: '{{ i18n "pages.inbounds.client" }}', width: 80, scopedSlots: { customRender: 'client' } },
|
{ title: '{{ i18n "pages.inbounds.client" }}', width: 80, scopedSlots: { customRender: 'client' } },
|
||||||
{ title: '{{ i18n "pages.inbounds.traffic" }}', width: 50, scopedSlots: { customRender: 'traffic' } },
|
{ title: '{{ i18n "pages.inbounds.traffic" }}', width: 50, scopedSlots: { customRender: 'traffic' } },
|
||||||
{ title: '{{ i18n "pages.inbounds.expireDate" }}', width: 50, scopedSlots: { customRender: 'expiryTime' } },
|
{ title: '{{ i18n "pages.inbounds.expireDate" }}', width: 55, scopedSlots: { customRender: 'expiryTime' } },
|
||||||
{ title: 'Password', width: 120, dataIndex: "password" },
|
{ title: '{{ i18n "password" }}', width: 165, dataIndex: "password" },
|
||||||
];
|
];
|
||||||
|
|
||||||
const app = new Vue({
|
const app = new Vue({
|
||||||
@@ -366,7 +379,8 @@
|
|||||||
domain: '',
|
domain: '',
|
||||||
tls: false
|
tls: false
|
||||||
},
|
},
|
||||||
tgBotEnable: false
|
tgBotEnable: false,
|
||||||
|
showAlert: false,
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
loading(spinning = true) {
|
loading(spinning = true) {
|
||||||
@@ -952,6 +966,9 @@
|
|||||||
}, 500)
|
}, 500)
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
if (window.location.protocol !== "https:") {
|
||||||
|
this.showAlert = true;
|
||||||
|
}
|
||||||
this.loading();
|
this.loading();
|
||||||
this.getDefaultSettings();
|
this.getDefaultSettings();
|
||||||
if (this.isRefreshEnabled) {
|
if (this.isRefreshEnabled) {
|
||||||
|
|||||||
@@ -22,6 +22,15 @@
|
|||||||
<a-layout id="content-layout" :style="themeSwitcher.bgStyle">
|
<a-layout id="content-layout" :style="themeSwitcher.bgStyle">
|
||||||
<a-layout-content>
|
<a-layout-content>
|
||||||
<a-spin :spinning="spinning" :delay="200" :tip="loadingTip"/>
|
<a-spin :spinning="spinning" :delay="200" :tip="loadingTip"/>
|
||||||
|
<transition name="list" appear>
|
||||||
|
<a-alert type="error" v-if="showAlert" style="margin-bottom: 10px"
|
||||||
|
message='{{ i18n "secAlertTitle" }}'
|
||||||
|
color="red"
|
||||||
|
description='{{ i18n "secAlertSsl" }}'
|
||||||
|
show-icon closable
|
||||||
|
>
|
||||||
|
</a-alert>
|
||||||
|
</transition>
|
||||||
<transition name="list" appear>
|
<transition name="list" appear>
|
||||||
<a-row>
|
<a-row>
|
||||||
<a-card hoverable :class="themeSwitcher.darkCardClass">
|
<a-card hoverable :class="themeSwitcher.darkCardClass">
|
||||||
@@ -33,7 +42,7 @@
|
|||||||
:stroke-color="status.cpu.color"
|
:stroke-color="status.cpu.color"
|
||||||
:class="themeSwitcher.darkCardClass"
|
:class="themeSwitcher.darkCardClass"
|
||||||
:percent="status.cpu.percent"></a-progress>
|
:percent="status.cpu.percent"></a-progress>
|
||||||
<div>CPU</div>
|
<div>CPU ([[ status.cpuCount ]]core)</div>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="12" style="text-align: center">
|
<a-col :span="12" style="text-align: center">
|
||||||
<a-progress type="dashboard" status="normal"
|
<a-progress type="dashboard" status="normal"
|
||||||
@@ -83,13 +92,16 @@
|
|||||||
<a-col :sm="24" :md="12">
|
<a-col :sm="24" :md="12">
|
||||||
<a-card hoverable :class="themeSwitcher.darkCardClass">
|
<a-card hoverable :class="themeSwitcher.darkCardClass">
|
||||||
{{ i18n "pages.index.operationHours" }}:
|
{{ i18n "pages.index.operationHours" }}:
|
||||||
<a-tag color="green">[[ formatSecond(status.uptime) ]]</a-tag>
|
xray
|
||||||
|
<a-tag color="green">[[ formatSecond(status.appStats.uptime) ]]</a-tag>
|
||||||
|
os
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
{{ i18n "pages.index.operationHoursDesc" }}
|
{{ i18n "pages.index.operationHoursDesc" }}
|
||||||
</template>
|
</template>
|
||||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
<a-icon type="question-circle" theme="filled"></a-icon>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
|
<a-tag color="green">[[ formatSecond(status.uptime) ]]</a-tag>
|
||||||
</a-card>
|
</a-card>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :sm="24" :md="12">
|
<a-col :sm="24" :md="12">
|
||||||
@@ -110,7 +122,7 @@
|
|||||||
<a-col :sm="24" :md="12">
|
<a-col :sm="24" :md="12">
|
||||||
<a-card hoverable :class="themeSwitcher.darkCardClass">
|
<a-card hoverable :class="themeSwitcher.darkCardClass">
|
||||||
{{ i18n "menu.link" }}:
|
{{ i18n "menu.link" }}:
|
||||||
<a-tag color="blue" style="cursor: pointer;" @click="openLogs(20)">{{ i18n "pages.index.logs" }}</a-tag>
|
<a-tag color="blue" style="cursor: pointer;" @click="openLogs()">{{ i18n "pages.index.logs" }}</a-tag>
|
||||||
<a-tag color="blue" style="cursor: pointer;" @click="openConfig">{{ i18n "pages.index.config" }}</a-tag>
|
<a-tag color="blue" style="cursor: pointer;" @click="openConfig">{{ i18n "pages.index.config" }}</a-tag>
|
||||||
<a-tag color="blue" style="cursor: pointer;" @click="openBackup">{{ i18n "pages.index.backup" }}</a-tag>
|
<a-tag color="blue" style="cursor: pointer;" @click="openBackup">{{ i18n "pages.index.backup" }}</a-tag>
|
||||||
</a-card>
|
</a-card>
|
||||||
@@ -122,7 +134,36 @@
|
|||||||
</a-col>
|
</a-col>
|
||||||
<a-col :sm="24" :md="12">
|
<a-col :sm="24" :md="12">
|
||||||
<a-card hoverable :class="themeSwitcher.darkCardClass">
|
<a-card hoverable :class="themeSwitcher.darkCardClass">
|
||||||
tcp / udp {{ i18n "pages.index.connectionCount" }}: [[ status.tcpCount ]] / [[ status.udpCount ]]
|
{{ i18n "usage"}}:
|
||||||
|
Memory [[ sizeFormat(status.appStats.mem) ]] -
|
||||||
|
Threads [[ status.appStats.threads ]]
|
||||||
|
</a-tooltip>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
<a-col :sm="24" :md="12">
|
||||||
|
<a-card hoverable :class="themeSwitcher.darkCardClass">
|
||||||
|
Host: [[ status.hostInfo.hostname ]] -
|
||||||
|
<template v-if="status.hostInfo.ipv4">IPv4:
|
||||||
|
<a-tooltip>
|
||||||
|
<template slot="title">
|
||||||
|
[[ status.hostInfo.ipv4 ]]
|
||||||
|
</template>
|
||||||
|
<a-icon type="question-circle" theme="filled"></a-icon>
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
<template v-if="status.hostInfo.ipv6">IPv6:
|
||||||
|
<a-tooltip>
|
||||||
|
<template slot="title">
|
||||||
|
[[ status.hostInfo.ipv6 ]]
|
||||||
|
</template>
|
||||||
|
<a-icon type="question-circle" theme="filled"></a-icon>
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
<a-col :sm="24" :md="12">
|
||||||
|
<a-card hoverable :class="themeSwitcher.darkCardClass">
|
||||||
|
{{ i18n "pages.index.connectionCount" }}: tcp: [[ status.tcpCount ]] udp: [[ status.udpCount ]]
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
{{ i18n "pages.index.connectionCountDesc" }}
|
{{ i18n "pages.index.connectionCountDesc" }}
|
||||||
@@ -211,7 +252,7 @@
|
|||||||
<a-form-item label="Count">
|
<a-form-item label="Count">
|
||||||
<a-select v-model="logModal.rows"
|
<a-select v-model="logModal.rows"
|
||||||
style="width: 80px"
|
style="width: 80px"
|
||||||
@change="openLogs(logModal.rows)"
|
@change="openLogs()"
|
||||||
:dropdown-class-name="themeSwitcher.darkCardClass">
|
:dropdown-class-name="themeSwitcher.darkCardClass">
|
||||||
<a-select-option value="10">10</a-select-option>
|
<a-select-option value="10">10</a-select-option>
|
||||||
<a-select-option value="20">20</a-select-option>
|
<a-select-option value="20">20</a-select-option>
|
||||||
@@ -219,8 +260,22 @@
|
|||||||
<a-select-option value="100">100</a-select-option>
|
<a-select-option value="100">100</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
<a-form-item label="Log Level">
|
||||||
|
<a-select v-model="logModal.level"
|
||||||
|
style="width: 120px"
|
||||||
|
@change="openLogs()"
|
||||||
|
:dropdown-class-name="themeSwitcher.darkCardClass">
|
||||||
|
<a-select-option value="debug">Debug</a-select-option>
|
||||||
|
<a-select-option value="info">Info</a-select-option>
|
||||||
|
<a-select-option value="warning">Warning</a-select-option>
|
||||||
|
<a-select-option value="err">Error</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="SysLog">
|
||||||
|
<a-checkbox v-model="logModal.syslog" @change="openLogs()"></a-checkbox>
|
||||||
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item>
|
||||||
<button class="ant-btn ant-btn-primary" @click="openLogs(logModal.rows)"><a-icon type="sync"></a-icon> Reload</button>
|
<button class="ant-btn ant-btn-primary" @click="openLogs()"><a-icon type="sync"></a-icon> Reload</button>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item>
|
||||||
<a-button type="primary" style="margin-bottom: 10px;"
|
<a-button type="primary" style="margin-bottom: 10px;"
|
||||||
@@ -292,6 +347,7 @@
|
|||||||
class Status {
|
class Status {
|
||||||
constructor(data) {
|
constructor(data) {
|
||||||
this.cpu = new CurTotal(0, 0);
|
this.cpu = new CurTotal(0, 0);
|
||||||
|
this.cpuCount = 0;
|
||||||
this.disk = new CurTotal(0, 0);
|
this.disk = new CurTotal(0, 0);
|
||||||
this.loads = [0, 0, 0];
|
this.loads = [0, 0, 0];
|
||||||
this.mem = new CurTotal(0, 0);
|
this.mem = new CurTotal(0, 0);
|
||||||
@@ -301,12 +357,16 @@
|
|||||||
this.tcpCount = 0;
|
this.tcpCount = 0;
|
||||||
this.udpCount = 0;
|
this.udpCount = 0;
|
||||||
this.uptime = 0;
|
this.uptime = 0;
|
||||||
|
this.appUptime = 0;
|
||||||
|
this.appStats = {threads: 0, mem: 0, uptime: 0};
|
||||||
|
this.hostInfo = {hostname:"", ipv4: "", ipv6: ""};
|
||||||
this.xray = {state: State.Stop, errorMsg: "", version: "", color: ""};
|
this.xray = {state: State.Stop, errorMsg: "", version: "", color: ""};
|
||||||
|
|
||||||
if (data == null) {
|
if (data == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.cpu = new CurTotal(data.cpu, 100);
|
this.cpu = new CurTotal(data.cpu, 100);
|
||||||
|
this.cpuCount = data.cpuCount;
|
||||||
this.disk = new CurTotal(data.disk.current, data.disk.total);
|
this.disk = new CurTotal(data.disk.current, data.disk.total);
|
||||||
this.loads = data.loads.map(load => toFixed(load, 2));
|
this.loads = data.loads.map(load => toFixed(load, 2));
|
||||||
this.mem = new CurTotal(data.mem.current, data.mem.total);
|
this.mem = new CurTotal(data.mem.current, data.mem.total);
|
||||||
@@ -316,6 +376,9 @@
|
|||||||
this.tcpCount = data.tcpCount;
|
this.tcpCount = data.tcpCount;
|
||||||
this.udpCount = data.udpCount;
|
this.udpCount = data.udpCount;
|
||||||
this.uptime = data.uptime;
|
this.uptime = data.uptime;
|
||||||
|
this.appUptime = data.appUptime;
|
||||||
|
this.appStats = data.appStats;
|
||||||
|
this.hostInfo = data.hostInfo;
|
||||||
this.xray = data.xray;
|
this.xray = data.xray;
|
||||||
switch (this.xray.state) {
|
switch (this.xray.state) {
|
||||||
case State.Running:
|
case State.Running:
|
||||||
@@ -349,10 +412,11 @@
|
|||||||
visible: false,
|
visible: false,
|
||||||
logs: '',
|
logs: '',
|
||||||
rows: 20,
|
rows: 20,
|
||||||
show(logs, rows) {
|
level: 'info',
|
||||||
|
syslog: false,
|
||||||
|
show(logs) {
|
||||||
this.visible = true;
|
this.visible = true;
|
||||||
this.rows = rows;
|
this.logs = logs? logs.join("\n"): "No Record...";
|
||||||
this.logs = logs.join("\n");
|
|
||||||
},
|
},
|
||||||
hide() {
|
hide() {
|
||||||
this.visible = false;
|
this.visible = false;
|
||||||
@@ -394,6 +458,7 @@
|
|||||||
backupModal,
|
backupModal,
|
||||||
spinning: false,
|
spinning: false,
|
||||||
loadingTip: '{{ i18n "loading"}}',
|
loadingTip: '{{ i18n "loading"}}',
|
||||||
|
showAlert: false,
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
loading(spinning, tip = '{{ i18n "loading"}}') {
|
loading(spinning, tip = '{{ i18n "loading"}}') {
|
||||||
@@ -449,14 +514,14 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async openLogs(rows){
|
async openLogs(){
|
||||||
this.loading(true);
|
this.loading(true);
|
||||||
const msg = await HttpUtil.post('server/logs/'+rows);
|
const msg = await HttpUtil.post('server/logs/'+logModal.rows,{level: logModal.level, syslog: logModal.syslog});
|
||||||
this.loading(false);
|
this.loading(false);
|
||||||
if (!msg.success) {
|
if (!msg.success) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
logModal.show(msg.obj, rows);
|
logModal.show(msg.obj);
|
||||||
},
|
},
|
||||||
async openConfig() {
|
async openConfig() {
|
||||||
this.loading(true);
|
this.loading(true);
|
||||||
@@ -512,6 +577,9 @@
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
|
if (window.location.protocol !== "https:") {
|
||||||
|
this.showAlert = true;
|
||||||
|
}
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
await this.getStatus();
|
await this.getStatus();
|
||||||
|
|||||||
@@ -52,6 +52,15 @@
|
|||||||
<a-layout id="content-layout" :style="themeSwitcher.bgStyle">
|
<a-layout id="content-layout" :style="themeSwitcher.bgStyle">
|
||||||
<a-layout-content>
|
<a-layout-content>
|
||||||
<a-spin :spinning="spinning" :delay="500" tip="loading">
|
<a-spin :spinning="spinning" :delay="500" tip="loading">
|
||||||
|
<transition name="list" appear>
|
||||||
|
<a-alert type="error" v-if="showAlert" style="margin-bottom: 10px"
|
||||||
|
message='{{ i18n "secAlertTitle" }}'
|
||||||
|
color="red"
|
||||||
|
description='{{ i18n "secAlertSsl" }}'
|
||||||
|
show-icon closable
|
||||||
|
>
|
||||||
|
</a-alert>
|
||||||
|
</transition>
|
||||||
<a-space direction="vertical">
|
<a-space direction="vertical">
|
||||||
<a-space direction="horizontal">
|
<a-space direction="horizontal">
|
||||||
<a-button type="primary" :disabled="saveBtnDisable" @click="updateAllSetting">{{ i18n "pages.settings.save" }}</a-button>
|
<a-button type="primary" :disabled="saveBtnDisable" @click="updateAllSetting">{{ i18n "pages.settings.save" }}</a-button>
|
||||||
@@ -228,19 +237,6 @@
|
|||||||
<setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigGoogleIPv4"}}' desc='{{ i18n "pages.settings.templates.xrayConfigGoogleIPv4Desc"}}' v-model="GoogleIPv4Settings"></setting-list-item>
|
<setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigGoogleIPv4"}}' desc='{{ i18n "pages.settings.templates.xrayConfigGoogleIPv4Desc"}}' v-model="GoogleIPv4Settings"></setting-list-item>
|
||||||
<setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigNetflixIPv4"}}' desc='{{ i18n "pages.settings.templates.xrayConfigNetflixIPv4Desc"}}' v-model="NetflixIPv4Settings"></setting-list-item>
|
<setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigNetflixIPv4"}}' desc='{{ i18n "pages.settings.templates.xrayConfigNetflixIPv4Desc"}}' v-model="NetflixIPv4Settings"></setting-list-item>
|
||||||
</a-collapse-panel>
|
</a-collapse-panel>
|
||||||
<a-collapse-panel header='{{ i18n "pages.settings.templates.manualLists"}}'>
|
|
||||||
<a-row :xs="24" :sm="24" :lg="12">
|
|
||||||
<h2 class="collapse-title">
|
|
||||||
<a-icon type="warning"></a-icon>
|
|
||||||
{{ i18n "pages.settings.templates.manualListsDesc" }}
|
|
||||||
</h2>
|
|
||||||
</a-row>
|
|
||||||
<setting-list-item type="textarea" title='{{ i18n "pages.settings.templates.manualBlockedIPs"}}' v-model="manualBlockedIPs"></setting-list-item>
|
|
||||||
<setting-list-item type="textarea" title='{{ i18n "pages.settings.templates.manualBlockedDomains"}}' v-model="manualBlockedDomains"></setting-list-item>
|
|
||||||
<setting-list-item type="textarea" title='{{ i18n "pages.settings.templates.manualDirectIPs"}}' v-model="manualDirectIPs"></setting-list-item>
|
|
||||||
<setting-list-item type="textarea" title='{{ i18n "pages.settings.templates.manualDirectDomains"}}' v-model="manualDirectDomains"></setting-list-item>
|
|
||||||
<setting-list-item type="textarea" title='{{ i18n "pages.settings.templates.manualIPv4Domains"}}' v-model="manualIPv4Domains"></setting-list-item>
|
|
||||||
</a-collapse-panel>
|
|
||||||
<a-collapse-panel header='{{ i18n "pages.settings.resetDefaultConfig"}}'>
|
<a-collapse-panel header='{{ i18n "pages.settings.resetDefaultConfig"}}'>
|
||||||
<a-space direction="horizontal" style="padding: 0 20px">
|
<a-space direction="horizontal" style="padding: 0 20px">
|
||||||
<a-button type="primary" @click="resetXrayConfigToDefault">{{ i18n "pages.settings.resetDefaultConfig" }}</a-button>
|
<a-button type="primary" @click="resetXrayConfigToDefault">{{ i18n "pages.settings.resetDefaultConfig" }}</a-button>
|
||||||
@@ -375,6 +371,7 @@
|
|||||||
saveBtnDisable: true,
|
saveBtnDisable: true,
|
||||||
user: {},
|
user: {},
|
||||||
lang: getLang(),
|
lang: getLang(),
|
||||||
|
showAlert: false,
|
||||||
ipv4Settings: {
|
ipv4Settings: {
|
||||||
tag: "IPv4",
|
tag: "IPv4",
|
||||||
protocol: "freedom",
|
protocol: "freedom",
|
||||||
@@ -563,6 +560,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
|
if (window.location.protocol !== "https:") {
|
||||||
|
this.showAlert = true;
|
||||||
|
}
|
||||||
await this.getAllSetting();
|
await this.getAllSetting();
|
||||||
while (true) {
|
while (true) {
|
||||||
await PromiseUtil.sleep(1000);
|
await PromiseUtil.sleep(1000);
|
||||||
|
|||||||
@@ -302,21 +302,17 @@ func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound,
|
|||||||
|
|
||||||
needRestart := false
|
needRestart := false
|
||||||
s.xrayApi.Init(p.GetAPIPort())
|
s.xrayApi.Init(p.GetAPIPort())
|
||||||
err1 := s.xrayApi.DelInbound(tag)
|
if s.xrayApi.DelInbound(tag) == nil {
|
||||||
if err1 != nil {
|
|
||||||
logger.Debug("Unable to delete old inbound by api:", err1)
|
|
||||||
needRestart = true
|
|
||||||
} else {
|
|
||||||
logger.Debug("Old inbound deleted by api:", tag)
|
logger.Debug("Old inbound deleted by api:", tag)
|
||||||
|
}
|
||||||
if inbound.Enable {
|
if inbound.Enable {
|
||||||
inboundJson, err2 := json.MarshalIndent(oldInbound.GenXrayInboundConfig(), "", " ")
|
inboundJson, err2 := json.MarshalIndent(oldInbound.GenXrayInboundConfig(), "", " ")
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
logger.Debug("Unable to marshal updated inbound config:", err2)
|
logger.Debug("Unable to marshal updated inbound config:", err2)
|
||||||
}
|
needRestart = true
|
||||||
|
} else {
|
||||||
err2 = s.xrayApi.AddInbound(inboundJson)
|
err2 = s.xrayApi.AddInbound(inboundJson)
|
||||||
if err1 == nil {
|
if err2 == nil {
|
||||||
logger.Debug("Updated inbound added by api:", oldInbound.Tag)
|
logger.Debug("Updated inbound added by api:", oldInbound.Tag)
|
||||||
} else {
|
} else {
|
||||||
logger.Debug("Unable to update inbound by api:", err2)
|
logger.Debug("Unable to update inbound by api:", err2)
|
||||||
@@ -447,15 +443,21 @@ func (s *InboundService) AddInboundClient(data *model.Inbound) (bool, error) {
|
|||||||
if len(client.Email) > 0 {
|
if len(client.Email) > 0 {
|
||||||
s.AddClientStat(tx, data.Id, &client)
|
s.AddClientStat(tx, data.Id, &client)
|
||||||
if client.Enable {
|
if client.Enable {
|
||||||
|
cipher := ""
|
||||||
|
if oldInbound.Protocol == "shadowsocks" {
|
||||||
|
cipher = oldSettings["method"].(string)
|
||||||
|
}
|
||||||
err1 := s.xrayApi.AddUser(string(oldInbound.Protocol), oldInbound.Tag, map[string]interface{}{
|
err1 := s.xrayApi.AddUser(string(oldInbound.Protocol), oldInbound.Tag, map[string]interface{}{
|
||||||
"email": client.Email,
|
"email": client.Email,
|
||||||
"id": client.ID,
|
"id": client.ID,
|
||||||
"flow": client.Flow,
|
"flow": client.Flow,
|
||||||
"password": client.Password,
|
"password": client.Password,
|
||||||
|
"cipher": cipher,
|
||||||
})
|
})
|
||||||
if err1 == nil {
|
if err1 == nil {
|
||||||
logger.Debug("Client added by api:", client.Email)
|
logger.Debug("Client added by api:", client.Email)
|
||||||
} else {
|
} else {
|
||||||
|
logger.Debug("Error in adding client by api:", err1)
|
||||||
needRestart = true
|
needRestart = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -516,15 +518,18 @@ func (s *InboundService) DelInboundClient(inboundId int, clientId string) (bool,
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
needRestart := false
|
needRestart := false
|
||||||
s.xrayApi.Init(p.GetAPIPort())
|
|
||||||
if len(email) > 0 {
|
if len(email) > 0 {
|
||||||
err = s.xrayApi.RemoveUser(oldInbound.Tag, email)
|
s.xrayApi.Init(p.GetAPIPort())
|
||||||
if err == nil {
|
err1 := s.xrayApi.RemoveUser(oldInbound.Tag, email)
|
||||||
|
if err1 == nil {
|
||||||
logger.Debug("Client deleted by api:", email)
|
logger.Debug("Client deleted by api:", email)
|
||||||
needRestart = false
|
needRestart = false
|
||||||
|
} else {
|
||||||
|
logger.Debug("Unable to del client by api:", err1)
|
||||||
|
needRestart = true
|
||||||
}
|
}
|
||||||
|
s.xrayApi.Close()
|
||||||
}
|
}
|
||||||
s.xrayApi.Close()
|
|
||||||
return needRestart, db.Save(oldInbound).Error
|
return needRestart, db.Save(oldInbound).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -622,26 +627,35 @@ func (s *InboundService) UpdateInboundClient(data *model.Inbound, clientId strin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
needRestart := false
|
needRestart := false
|
||||||
s.xrayApi.Init(p.GetAPIPort())
|
|
||||||
if len(oldEmail) > 0 {
|
if len(oldEmail) > 0 {
|
||||||
s.xrayApi.RemoveUser(oldInbound.Tag, oldEmail)
|
s.xrayApi.Init(p.GetAPIPort())
|
||||||
|
if s.xrayApi.RemoveUser(oldInbound.Tag, oldEmail) == nil {
|
||||||
|
logger.Debug("Old client deleted by api:", clients[0].Email)
|
||||||
|
}
|
||||||
if clients[0].Enable {
|
if clients[0].Enable {
|
||||||
|
cipher := ""
|
||||||
|
if oldInbound.Protocol == "shadowsocks" {
|
||||||
|
cipher = oldSettings["method"].(string)
|
||||||
|
}
|
||||||
err1 := s.xrayApi.AddUser(string(oldInbound.Protocol), oldInbound.Tag, map[string]interface{}{
|
err1 := s.xrayApi.AddUser(string(oldInbound.Protocol), oldInbound.Tag, map[string]interface{}{
|
||||||
"email": clients[0].Email,
|
"email": clients[0].Email,
|
||||||
"id": clients[0].ID,
|
"id": clients[0].ID,
|
||||||
"flow": clients[0].Flow,
|
"flow": clients[0].Flow,
|
||||||
"password": clients[0].Password,
|
"password": clients[0].Password,
|
||||||
|
"cipher": cipher,
|
||||||
})
|
})
|
||||||
if err1 == nil {
|
if err1 == nil {
|
||||||
logger.Debug("Client edited by api:", clients[0].Email)
|
logger.Debug("Client edited by api:", clients[0].Email)
|
||||||
needRestart = false
|
} else {
|
||||||
|
logger.Debug("Error in adding client by api:", err1)
|
||||||
|
needRestart = true
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
logger.Debug("Client disabled by api:", clients[0].Email)
|
|
||||||
needRestart = false
|
|
||||||
}
|
}
|
||||||
|
s.xrayApi.Close()
|
||||||
|
} else {
|
||||||
|
logger.Debug("Client old email not found")
|
||||||
|
needRestart = true
|
||||||
}
|
}
|
||||||
s.xrayApi.Close()
|
|
||||||
return needRestart, tx.Save(oldInbound).Error
|
return needRestart, tx.Save(oldInbound).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -695,6 +709,11 @@ func (s *InboundService) AddClientTraffic(traffics []*xray.ClientTraffic) (err e
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Avoid empty slice error
|
||||||
|
if len(dbClientTraffics) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
dbClientTraffics, err = s.adjustTraffics(tx, dbClientTraffics)
|
dbClientTraffics, err = s.adjustTraffics(tx, dbClientTraffics)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -786,10 +805,11 @@ func (s *InboundService) DisableInvalidInbounds() (bool, int64, error) {
|
|||||||
}
|
}
|
||||||
s.xrayApi.Init(p.GetAPIPort())
|
s.xrayApi.Init(p.GetAPIPort())
|
||||||
for _, tag := range tags {
|
for _, tag := range tags {
|
||||||
err = s.xrayApi.DelInbound(tag)
|
err1 := s.xrayApi.DelInbound(tag)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
logger.Debug("Inbound disabled by api:", tag)
|
logger.Debug("Inbound disabled by api:", tag)
|
||||||
} else {
|
} else {
|
||||||
|
logger.Debug("Error in disabling inbound by api:", err1)
|
||||||
needRestart = true
|
needRestart = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -825,10 +845,11 @@ func (s *InboundService) DisableInvalidClients() (bool, int64, error) {
|
|||||||
}
|
}
|
||||||
s.xrayApi.Init(p.GetAPIPort())
|
s.xrayApi.Init(p.GetAPIPort())
|
||||||
for _, result := range results {
|
for _, result := range results {
|
||||||
err = s.xrayApi.RemoveUser(result.Tag, result.Email)
|
err1 := s.xrayApi.RemoveUser(result.Tag, result.Email)
|
||||||
if err == nil {
|
if err1 == nil {
|
||||||
logger.Debug("Client disabled by api:", result.Email)
|
logger.Debug("Client disabled by api:", result.Email)
|
||||||
} else {
|
} else {
|
||||||
|
logger.Debug("Error in disabling client by api:", err1)
|
||||||
needRestart = true
|
needRestart = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -911,15 +932,26 @@ func (s *InboundService) ResetClientTraffic(id int, clientEmail string) (bool, e
|
|||||||
for _, client := range clients {
|
for _, client := range clients {
|
||||||
if client.Email == clientEmail {
|
if client.Email == clientEmail {
|
||||||
s.xrayApi.Init(p.GetAPIPort())
|
s.xrayApi.Init(p.GetAPIPort())
|
||||||
|
cipher := ""
|
||||||
|
if string(inbound.Protocol) == "shadowsocks" {
|
||||||
|
var oldSettings map[string]interface{}
|
||||||
|
err = json.Unmarshal([]byte(inbound.Settings), &oldSettings)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
cipher = oldSettings["method"].(string)
|
||||||
|
}
|
||||||
err1 := s.xrayApi.AddUser(string(inbound.Protocol), inbound.Tag, map[string]interface{}{
|
err1 := s.xrayApi.AddUser(string(inbound.Protocol), inbound.Tag, map[string]interface{}{
|
||||||
"email": client.Email,
|
"email": client.Email,
|
||||||
"id": client.ID,
|
"id": client.ID,
|
||||||
"flow": client.Flow,
|
"flow": client.Flow,
|
||||||
"password": client.Password,
|
"password": client.Password,
|
||||||
|
"cipher": cipher,
|
||||||
})
|
})
|
||||||
if err1 == nil {
|
if err1 == nil {
|
||||||
logger.Debug("Client enabled due to reset traffic:", clientEmail)
|
logger.Debug("Client enabled due to reset traffic:", clientEmail)
|
||||||
} else {
|
} else {
|
||||||
|
logger.Debug("Error in enabling client by api:", err1)
|
||||||
needRestart = true
|
needRestart = true
|
||||||
}
|
}
|
||||||
s.xrayApi.Close()
|
s.xrayApi.Close()
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
"x-ui/config"
|
"x-ui/config"
|
||||||
@@ -38,9 +39,10 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Status struct {
|
type Status struct {
|
||||||
T time.Time `json:"-"`
|
T time.Time `json:"-"`
|
||||||
Cpu float64 `json:"cpu"`
|
Cpu float64 `json:"cpu"`
|
||||||
Mem struct {
|
CpuCount int `json:"cpuCount"`
|
||||||
|
Mem struct {
|
||||||
Current uint64 `json:"current"`
|
Current uint64 `json:"current"`
|
||||||
Total uint64 `json:"total"`
|
Total uint64 `json:"total"`
|
||||||
} `json:"mem"`
|
} `json:"mem"`
|
||||||
@@ -69,6 +71,16 @@ type Status struct {
|
|||||||
Sent uint64 `json:"sent"`
|
Sent uint64 `json:"sent"`
|
||||||
Recv uint64 `json:"recv"`
|
Recv uint64 `json:"recv"`
|
||||||
} `json:"netTraffic"`
|
} `json:"netTraffic"`
|
||||||
|
AppStats struct {
|
||||||
|
Threads uint32 `json:"threads"`
|
||||||
|
Mem uint64 `json:"mem"`
|
||||||
|
Uptime uint64 `json:"uptime"`
|
||||||
|
} `json:"appStats"`
|
||||||
|
HostInfo struct {
|
||||||
|
HostName string `json:"hostname"`
|
||||||
|
Ipv4 string `json:"ipv4"`
|
||||||
|
Ipv6 string `json:"ipv6"`
|
||||||
|
} `json:"hostInfo"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Release struct {
|
type Release struct {
|
||||||
@@ -175,6 +187,36 @@ func (s *ServerService) GetStatus(lastStatus *Status) *Status {
|
|||||||
}
|
}
|
||||||
status.Xray.Version = s.xrayService.GetXrayVersion()
|
status.Xray.Version = s.xrayService.GetXrayVersion()
|
||||||
|
|
||||||
|
var rtm runtime.MemStats
|
||||||
|
runtime.ReadMemStats(&rtm)
|
||||||
|
|
||||||
|
status.AppStats.Mem = rtm.Sys
|
||||||
|
status.AppStats.Threads = uint32(runtime.NumGoroutine())
|
||||||
|
status.CpuCount = runtime.NumCPU()
|
||||||
|
if p.IsRunning() {
|
||||||
|
status.AppStats.Uptime = p.GetUptime()
|
||||||
|
} else {
|
||||||
|
status.AppStats.Uptime = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
status.HostInfo.HostName, _ = os.Hostname()
|
||||||
|
|
||||||
|
// get ip address
|
||||||
|
netInterfaces, _ := net.Interfaces()
|
||||||
|
for i := 0; i < len(netInterfaces); i++ {
|
||||||
|
if len(netInterfaces[i].Flags) > 2 && netInterfaces[i].Flags[0] == "up" && netInterfaces[i].Flags[1] != "loopback" {
|
||||||
|
addrs := netInterfaces[i].Addrs
|
||||||
|
|
||||||
|
for _, address := range addrs {
|
||||||
|
if strings.Contains(address.Addr, ".") {
|
||||||
|
status.HostInfo.Ipv4 += address.Addr + " "
|
||||||
|
} else if address.Addr[0:6] != "fe80::" {
|
||||||
|
status.HostInfo.Ipv6 += address.Addr + " "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return status
|
return status
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -334,27 +376,26 @@ func (s *ServerService) UpdateXray(version string) error {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ServerService) GetLogs(count string) ([]string, error) {
|
func (s *ServerService) GetLogs(count string, level string, syslog string) []string {
|
||||||
// Define the journalctl command and its arguments
|
c, _ := strconv.Atoi(count)
|
||||||
var cmdArgs []string
|
var lines []string
|
||||||
if runtime.GOOS == "linux" {
|
|
||||||
cmdArgs = []string{"journalctl", "-u", "x-ui", "--no-pager", "-n", count}
|
if syslog == "true" {
|
||||||
|
cmdArgs := []string{"journalctl", "-u", "x-ui", "--no-pager", "-n", count, "-p", level}
|
||||||
|
// Run the command
|
||||||
|
cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...)
|
||||||
|
var out bytes.Buffer
|
||||||
|
cmd.Stdout = &out
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return []string{"Failed to run journalctl command!"}
|
||||||
|
}
|
||||||
|
lines = strings.Split(out.String(), "\n")
|
||||||
} else {
|
} else {
|
||||||
return []string{"Unsupported operating system"}, nil
|
lines = logger.GetLogs(c, level)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the command
|
return lines
|
||||||
cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...)
|
|
||||||
var out bytes.Buffer
|
|
||||||
cmd.Stdout = &out
|
|
||||||
err := cmd.Run()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
lines := strings.Split(out.String(), "\n")
|
|
||||||
|
|
||||||
return lines, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ServerService) GetConfigJson() (interface{}, error) {
|
func (s *ServerService) GetConfigJson() (interface{}, error) {
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ func (s *XrayService) GetXrayConfig() (*xray.Config, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for key := range c {
|
for key := range c {
|
||||||
if key != "email" && key != "id" && key != "password" && key != "flow" {
|
if key != "email" && key != "id" && key != "password" && key != "flow" && key != "method" {
|
||||||
delete(c, key)
|
delete(c, key)
|
||||||
}
|
}
|
||||||
if c["flow"] == "xtls-rprx-vision-udp443" {
|
if c["flow"] == "xtls-rprx-vision-udp443" {
|
||||||
|
|||||||
@@ -48,6 +48,8 @@
|
|||||||
"clients" = "Clients"
|
"clients" = "Clients"
|
||||||
"usage" = "Usage"
|
"usage" = "Usage"
|
||||||
"remained" = "Remained"
|
"remained" = "Remained"
|
||||||
|
"secAlertTitle" = "Security Alert"
|
||||||
|
"secAlertSsl" = "This connection is not secure; Please refrain from entering sensitive information until TLS is activated for data protection"
|
||||||
|
|
||||||
[menu]
|
[menu]
|
||||||
"dashboard" = "System Status"
|
"dashboard" = "System Status"
|
||||||
|
|||||||
@@ -48,6 +48,8 @@
|
|||||||
"clients" = "کاربران"
|
"clients" = "کاربران"
|
||||||
"usage" = "استفاده"
|
"usage" = "استفاده"
|
||||||
"remained" = "باقیمانده"
|
"remained" = "باقیمانده"
|
||||||
|
"secAlertTitle" = "هشدار امنیتی"
|
||||||
|
"secAlertSsl" = "این اتصال امن نیست؛ لطفا تا زمانی که تیالاس برای حفاظت از داده ها فعال نشده است از وارد کردن اطلاعات حساس خودداری کنید"
|
||||||
|
|
||||||
[menu]
|
[menu]
|
||||||
"dashboard" = "وضعیت سیستم"
|
"dashboard" = "وضعیت سیستم"
|
||||||
|
|||||||
@@ -48,6 +48,8 @@
|
|||||||
"clients" = "клиенты"
|
"clients" = "клиенты"
|
||||||
"usage" = "использование"
|
"usage" = "использование"
|
||||||
"remained" = "остались"
|
"remained" = "остались"
|
||||||
|
"secAlertTitle" = "Предупреждение системы безопасности"
|
||||||
|
"secAlertSsl" = "Это соединение не защищено. Пожалуйста, воздержитесь от ввода конфиденциальной информации, пока TLS не будет активирован для защиты данных"
|
||||||
|
|
||||||
[menu]
|
[menu]
|
||||||
"dashboard" = "статус системы"
|
"dashboard" = "статус системы"
|
||||||
|
|||||||
@@ -48,6 +48,8 @@
|
|||||||
"clients" = "客户端"
|
"clients" = "客户端"
|
||||||
"usage" = "用法"
|
"usage" = "用法"
|
||||||
"remained" = "仍然存在"
|
"remained" = "仍然存在"
|
||||||
|
"secAlertTitle" = "安全警报"
|
||||||
|
"secAlertSsl" = "此连接不安全;在激活 TLS 进行数据保护之前,请勿输入敏感信息"
|
||||||
|
|
||||||
[menu]
|
[menu]
|
||||||
"dashboard" = "系统状态"
|
"dashboard" = "系统状态"
|
||||||
|
|||||||
154
x-ui.sh
154
x-ui.sh
@@ -403,7 +403,139 @@ show_xray_status() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
install_acme() {
|
||||||
|
cd ~
|
||||||
|
LOGI "install acme..."
|
||||||
|
curl https://get.acme.sh | sh
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
LOGE "install acme failed"
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
LOGI "install acme succeed"
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
ssl_cert_issue_main() {
|
||||||
|
echo -e "${green}\t1.${plain} Get SSL"
|
||||||
|
echo -e "${green}\t2.${plain} Revoke"
|
||||||
|
echo -e "${green}\t3.${plain} Force Renew"
|
||||||
|
read -p "Choose an option: " choice
|
||||||
|
case "$choice" in
|
||||||
|
1) ssl_cert_issue ;;
|
||||||
|
2)
|
||||||
|
local domain=""
|
||||||
|
read -p "Please enter your domain name to revoke the certificate: " domain
|
||||||
|
~/.acme.sh/acme.sh --revoke -d ${domain}
|
||||||
|
LOGI "Certificate revoked"
|
||||||
|
;;
|
||||||
|
3)
|
||||||
|
local domain=""
|
||||||
|
read -p "Please enter your domain name to forcefully renew an SSL certificate: " domain
|
||||||
|
~/.acme.sh/acme.sh --renew -d ${domain} --force ;;
|
||||||
|
*) echo "Invalid choice" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
ssl_cert_issue() {
|
ssl_cert_issue() {
|
||||||
|
# check for acme.sh first
|
||||||
|
if ! command -v ~/.acme.sh/acme.sh &>/dev/null; then
|
||||||
|
echo "acme.sh could not be found. we will install it"
|
||||||
|
install_acme
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
LOGE "install acme failed, please check logs"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
# install socat second
|
||||||
|
case "${release}" in
|
||||||
|
ubuntu|debian)
|
||||||
|
apt update && apt install socat -y ;;
|
||||||
|
centos)
|
||||||
|
yum -y update && yum -y install socat ;;
|
||||||
|
fedora)
|
||||||
|
dnf -y update && dnf -y install socat ;;
|
||||||
|
*)
|
||||||
|
echo -e "${red}Unsupported operating system. Please check the script and install the necessary packages manually.${plain}\n"
|
||||||
|
exit 1 ;;
|
||||||
|
esac
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
LOGE "install socat failed, please check logs"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
LOGI "install socat succeed..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# get the domain here,and we need verify it
|
||||||
|
local domain=""
|
||||||
|
read -p "Please enter your domain name:" domain
|
||||||
|
LOGD "your domain is:${domain},check it..."
|
||||||
|
# here we need to judge whether there exists cert already
|
||||||
|
local currentCert=$(~/.acme.sh/acme.sh --list | tail -1 | awk '{print $1}')
|
||||||
|
|
||||||
|
if [ ${currentCert} == ${domain} ]; then
|
||||||
|
local certInfo=$(~/.acme.sh/acme.sh --list)
|
||||||
|
LOGE "system already has certs here,can not issue again,current certs details:"
|
||||||
|
LOGI "$certInfo"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
LOGI "your domain is ready for issuing cert now..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# create a directory for install cert
|
||||||
|
certPath="/root/cert/${domain}"
|
||||||
|
if [ ! -d "$certPath" ]; then
|
||||||
|
mkdir -p "$certPath"
|
||||||
|
else
|
||||||
|
rm -rf "$certPath"
|
||||||
|
mkdir -p "$certPath"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# get needed port here
|
||||||
|
local WebPort=80
|
||||||
|
read -p "please choose which port do you use,default will be 80 port:" WebPort
|
||||||
|
if [[ ${WebPort} -gt 65535 || ${WebPort} -lt 1 ]]; then
|
||||||
|
LOGE "your input ${WebPort} is invalid,will use default port"
|
||||||
|
fi
|
||||||
|
LOGI "will use port:${WebPort} to issue certs,please make sure this port is open..."
|
||||||
|
# NOTE:This should be handled by user
|
||||||
|
# open the port and kill the occupied progress
|
||||||
|
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt
|
||||||
|
~/.acme.sh/acme.sh --issue -d ${domain} --standalone --httpport ${WebPort}
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
LOGE "issue certs failed,please check logs"
|
||||||
|
rm -rf ~/.acme.sh/${domain}
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
LOGE "issue certs succeed,installing certs..."
|
||||||
|
fi
|
||||||
|
# install cert
|
||||||
|
~/.acme.sh/acme.sh --installcert -d ${domain} \
|
||||||
|
--key-file /root/cert/${domain}/privkey.pem \
|
||||||
|
--fullchain-file /root/cert/${domain}/fullchain.pem
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
LOGE "install certs failed,exit"
|
||||||
|
rm -rf ~/.acme.sh/${domain}
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
LOGI "install certs succeed,enable auto renew..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
~/.acme.sh/acme.sh --upgrade --auto-upgrade
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
LOGE "auto renew failed, certs details:"
|
||||||
|
ls -lah cert/*
|
||||||
|
chmod 755 $certPath/*
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
LOGI "auto renew succeed, certs details:"
|
||||||
|
ls -lah cert/*
|
||||||
|
chmod 755 $certPath/*
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
ssl_cert_issue_CF() {
|
||||||
echo -E ""
|
echo -E ""
|
||||||
LOGD "******Instructions for use******"
|
LOGD "******Instructions for use******"
|
||||||
LOGI "This Acme script requires the following data:"
|
LOGI "This Acme script requires the following data:"
|
||||||
@@ -413,12 +545,14 @@ ssl_cert_issue() {
|
|||||||
LOGI "4.The script applies for a certificate. The default installation path is /root/cert "
|
LOGI "4.The script applies for a certificate. The default installation path is /root/cert "
|
||||||
confirm "Confirmed?[y/n]" "y"
|
confirm "Confirmed?[y/n]" "y"
|
||||||
if [ $? -eq 0 ]; then
|
if [ $? -eq 0 ]; then
|
||||||
cd ~
|
# check for acme.sh first
|
||||||
LOGI "Install Acme-Script"
|
if ! command -v ~/.acme.sh/acme.sh &>/dev/null; then
|
||||||
curl https://get.acme.sh | sh
|
echo "acme.sh could not be found. we will install it"
|
||||||
if [ $? -ne 0 ]; then
|
install_acme
|
||||||
LOGE "Failed to install acme script"
|
if [ $? -ne 0 ]; then
|
||||||
exit 1
|
LOGE "install acme failed, please check logs"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
CF_Domain=""
|
CF_Domain=""
|
||||||
CF_GlobalKey=""
|
CF_GlobalKey=""
|
||||||
@@ -520,7 +654,8 @@ show_menu() {
|
|||||||
${green}14.${plain} Cancel x-ui Autostart
|
${green}14.${plain} Cancel x-ui Autostart
|
||||||
————————————————
|
————————————————
|
||||||
${green}15.${plain} 一A key installation bbr (latest kernel)
|
${green}15.${plain} 一A key installation bbr (latest kernel)
|
||||||
${green}16.${plain} 一Apply for a SSL certificate with one click(acme script)
|
${green}16.${plain} 一SSL Certificate Management
|
||||||
|
${green}17.${plain} 一Cloudflare SSL Certificate
|
||||||
"
|
"
|
||||||
show_status
|
show_status
|
||||||
echo && read -p "Please enter your selection [0-16]: " num
|
echo && read -p "Please enter your selection [0-16]: " num
|
||||||
@@ -575,7 +710,10 @@ show_menu() {
|
|||||||
install_bbr
|
install_bbr
|
||||||
;;
|
;;
|
||||||
16)
|
16)
|
||||||
ssl_cert_issue
|
ssl_cert_issue_main
|
||||||
|
;;
|
||||||
|
17)
|
||||||
|
ssl_cert_issue_CF
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
LOGE "Please enter the correct number [0-16]"
|
LOGE "Please enter the correct number [0-16]"
|
||||||
|
|||||||
31
xray/api.go
31
xray/api.go
@@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/xtls/xray-core/common/serial"
|
"github.com/xtls/xray-core/common/serial"
|
||||||
"github.com/xtls/xray-core/infra/conf"
|
"github.com/xtls/xray-core/infra/conf"
|
||||||
"github.com/xtls/xray-core/proxy/shadowsocks"
|
"github.com/xtls/xray-core/proxy/shadowsocks"
|
||||||
|
"github.com/xtls/xray-core/proxy/shadowsocks_2022"
|
||||||
"github.com/xtls/xray-core/proxy/trojan"
|
"github.com/xtls/xray-core/proxy/trojan"
|
||||||
"github.com/xtls/xray-core/proxy/vless"
|
"github.com/xtls/xray-core/proxy/vless"
|
||||||
"github.com/xtls/xray-core/proxy/vmess"
|
"github.com/xtls/xray-core/proxy/vmess"
|
||||||
@@ -61,10 +62,12 @@ func (x *XrayAPI) AddInbound(inbound []byte) error {
|
|||||||
err := json.Unmarshal(inbound, conf)
|
err := json.Unmarshal(inbound, conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Debug("Failed to unmarshal inbound:", err)
|
logger.Debug("Failed to unmarshal inbound:", err)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
config, err := conf.Build()
|
config, err := conf.Build()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Debug("Failed to build inbound Detur:", err)
|
logger.Debug("Failed to build inbound Detur:", err)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
inboundConfig := command.AddInboundRequest{Inbound: config}
|
inboundConfig := command.AddInboundRequest{Inbound: config}
|
||||||
|
|
||||||
@@ -98,9 +101,31 @@ func (x *XrayAPI) AddUser(Protocol string, inboundTag string, user map[string]in
|
|||||||
Password: user["password"].(string),
|
Password: user["password"].(string),
|
||||||
})
|
})
|
||||||
case "shadowsocks":
|
case "shadowsocks":
|
||||||
account = serial.ToTypedMessage(&shadowsocks.Account{
|
var ssCipherType shadowsocks.CipherType
|
||||||
Password: user["password"].(string),
|
switch user["cipher"].(string) {
|
||||||
})
|
case "aes-128-gcm":
|
||||||
|
ssCipherType = shadowsocks.CipherType_AES_128_GCM
|
||||||
|
case "aes-256-gcm":
|
||||||
|
ssCipherType = shadowsocks.CipherType_AES_256_GCM
|
||||||
|
case "chacha20-poly1305":
|
||||||
|
ssCipherType = shadowsocks.CipherType_CHACHA20_POLY1305
|
||||||
|
case "xchacha20-poly1305":
|
||||||
|
ssCipherType = shadowsocks.CipherType_XCHACHA20_POLY1305
|
||||||
|
default:
|
||||||
|
ssCipherType = shadowsocks.CipherType_NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
if ssCipherType != shadowsocks.CipherType_NONE {
|
||||||
|
account = serial.ToTypedMessage(&shadowsocks.Account{
|
||||||
|
Password: user["password"].(string),
|
||||||
|
CipherType: ssCipherType,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
account = serial.ToTypedMessage(&shadowsocks_2022.User{
|
||||||
|
Key: user["password"].(string),
|
||||||
|
Email: user["email"].(string),
|
||||||
|
})
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
"time"
|
||||||
"x-ui/config"
|
"x-ui/config"
|
||||||
"x-ui/util/common"
|
"x-ui/util/common"
|
||||||
|
|
||||||
@@ -58,16 +59,18 @@ type process struct {
|
|||||||
version string
|
version string
|
||||||
apiPort int
|
apiPort int
|
||||||
|
|
||||||
config *Config
|
config *Config
|
||||||
lines *queue.Queue
|
lines *queue.Queue
|
||||||
exitErr error
|
exitErr error
|
||||||
|
startTime time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func newProcess(config *Config) *process {
|
func newProcess(config *Config) *process {
|
||||||
return &process{
|
return &process{
|
||||||
version: "Unknown",
|
version: "Unknown",
|
||||||
config: config,
|
config: config,
|
||||||
lines: queue.New(100),
|
lines: queue.New(100),
|
||||||
|
startTime: time.Now(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,6 +114,10 @@ func (p *Process) GetConfig() *Config {
|
|||||||
return p.config
|
return p.config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Process) GetUptime() uint64 {
|
||||||
|
return uint64(time.Since(p.startTime).Seconds())
|
||||||
|
}
|
||||||
|
|
||||||
func (p *process) refreshAPIPort() {
|
func (p *process) refreshAPIPort() {
|
||||||
for _, inbound := range p.config.InboundConfigs {
|
for _, inbound := range p.config.InboundConfigs {
|
||||||
if inbound.Tag == "api" {
|
if inbound.Tag == "api" {
|
||||||
|
|||||||
Reference in New Issue
Block a user