mirror of
https://github.com/alireza0/x-ui.git
synced 2026-03-14 13:31:41 +00:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
414669fa1b | ||
|
|
76f774fd6c | ||
|
|
b82ce31ec1 | ||
|
|
9bbd612327 | ||
|
|
a5922a8620 | ||
|
|
35e4b96e9e | ||
|
|
ceb7a7ac14 | ||
|
|
92dc390e58 | ||
|
|
a6edd1c631 | ||
|
|
146b4d7873 | ||
|
|
f3dac6395a | ||
|
|
b48707923c |
51
.github/workflows/release.yml
vendored
51
.github/workflows/release.yml
vendored
@@ -6,24 +6,8 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-18.04
|
||||
outputs:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
steps:
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: ${{ github.ref }}
|
||||
draft: true
|
||||
prerelease: true
|
||||
linuxamd64build:
|
||||
name: build x-ui amd64 version
|
||||
needs: release
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -53,17 +37,15 @@ jobs:
|
||||
- name: package
|
||||
run: tar -zcvf x-ui-linux-amd64.tar.gz x-ui
|
||||
- name: upload
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
uses: svenstaro/upload-release-action@2.5.0
|
||||
with:
|
||||
upload_url: ${{ needs.release.outputs.upload_url }}
|
||||
asset_path: x-ui-linux-amd64.tar.gz
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
tag: ${{ github.ref }}
|
||||
file: x-ui-linux-amd64.tar.gz
|
||||
asset_name: x-ui-linux-amd64.tar.gz
|
||||
asset_content_type: application/gzip
|
||||
prerelease: true
|
||||
linuxarm64build:
|
||||
name: build x-ui arm64 version
|
||||
needs: release
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -95,17 +77,15 @@ jobs:
|
||||
- name: package
|
||||
run: tar -zcvf x-ui-linux-arm64.tar.gz x-ui
|
||||
- name: upload
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
uses: svenstaro/upload-release-action@2.5.0
|
||||
with:
|
||||
upload_url: ${{ needs.release.outputs.upload_url }}
|
||||
asset_path: x-ui-linux-arm64.tar.gz
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
tag: ${{ github.ref }}
|
||||
file: x-ui-linux-arm64.tar.gz
|
||||
asset_name: x-ui-linux-arm64.tar.gz
|
||||
asset_content_type: application/gzip
|
||||
prerelease: true
|
||||
linuxs390xbuild:
|
||||
name: build x-ui s390x version
|
||||
needs: release
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -137,11 +117,10 @@ jobs:
|
||||
- name: package
|
||||
run: tar -zcvf x-ui-linux-s390x.tar.gz x-ui
|
||||
- name: upload
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
uses: svenstaro/upload-release-action@2.5.0
|
||||
with:
|
||||
upload_url: ${{ needs.release.outputs.upload_url }}
|
||||
asset_path: x-ui-linux-s390x.tar.gz
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
tag: ${{ github.ref }}
|
||||
file: x-ui-linux-s390x.tar.gz
|
||||
asset_name: x-ui-linux-s390x.tar.gz
|
||||
asset_content_type: application/gzip
|
||||
prerelease: true
|
||||
@@ -1 +1 @@
|
||||
0.2.4
|
||||
0.3.0
|
||||
|
||||
13
go.mod
13
go.mod
@@ -3,15 +3,15 @@ module x-ui
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.0.0
|
||||
github.com/Workiva/go-datastructures v1.0.53
|
||||
github.com/gin-contrib/sessions v0.0.3
|
||||
github.com/gin-contrib/sessions v0.0.4
|
||||
github.com/gin-gonic/gin v1.8.2
|
||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1
|
||||
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.6
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible
|
||||
github.com/shirou/gopsutil/v3 v3.23.1
|
||||
github.com/xtls/xray-core v1.7.5
|
||||
go.uber.org/atomic v1.10.0
|
||||
golang.org/x/text v0.7.0
|
||||
@@ -21,6 +21,7 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.2.1 // 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.0 // indirect
|
||||
@@ -30,20 +31,18 @@ require (
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/gorilla/context v1.1.1 // indirect
|
||||
github.com/gorilla/securecookie v1.1.1 // indirect
|
||||
github.com/gorilla/sessions v1.1.3 // indirect
|
||||
github.com/gorilla/sessions v1.2.1 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/leodido/go-urn v1.2.1 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.15 // 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/pelletier/go-toml/v2 v2.0.6 // indirect
|
||||
github.com/pires/go-proxyproto v0.6.2 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/shirou/gopsutil/v3 v3.23.1 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.11 // indirect
|
||||
github.com/tklauser/numcpus v0.6.0 // indirect
|
||||
github.com/ugorji/go/codec v1.2.7 // indirect
|
||||
|
||||
50
go.sum
50
go.sum
@@ -1,10 +1,12 @@
|
||||
github.com/BurntSushi/toml v1.0.0 h1:dtDWrepsVPfW9H/4y7dDgFc2MBUSeJhlaDtK13CxFlU=
|
||||
github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
|
||||
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/Workiva/go-datastructures v1.0.53 h1:J6Y/52yX10Xc5JjXmGtWoSSxs3mZnGSaq37xZZh7Yig=
|
||||
github.com/Workiva/go-datastructures v1.0.53/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A=
|
||||
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
||||
github.com/antonlindstrom/pgstore v0.0.0-20200229204646-b08ebf1105e0/go.mod h1:2Ti6VUHVxpC0VSmTZzEvpzysnaGAfGBOoMIz5ykPyyw=
|
||||
github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff/go.mod h1:+RTT1BOk5P97fT2CiHkbFQwkK3mjsFAP6zCYV2aXtjw=
|
||||
github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
|
||||
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/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -13,11 +15,11 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
||||
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 h1:y7y0Oa6UawqTFPCDw9JG6pdKt4F9pAhHv0B7FMGaGD0=
|
||||
github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
|
||||
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 h1:Arcl6UOIS/kgO2nW3A65HN+7CMjSDP/gofXL4CZt1V4=
|
||||
github.com/gin-contrib/sessions v0.0.3 h1:PoBXki+44XdJdlgDqDrY5nDVe3Wk7wDV/UCOuLP6fBI=
|
||||
github.com/gin-contrib/sessions v0.0.3/go.mod h1:8C/J6cad3Il1mWYYgtw0w+hqasmpvy25mPkXdOgeB9I=
|
||||
github.com/gin-contrib/sessions v0.0.4 h1:gq4fNa1Zmp564iHP5G6EBuktilEos8VKhe2sza1KMgo=
|
||||
github.com/gin-contrib/sessions v0.0.4/go.mod h1:pQ3sIyviBBGcxgyR8mkeJuXbeV3h3NYmhJADQTq5+Vo=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
|
||||
github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
|
||||
github.com/gin-gonic/gin v1.8.2 h1:UzKToD9/PoFj/V4rvlKqTRKnQYyz8Sc1MJlv4JHPtvY=
|
||||
github.com/gin-gonic/gin v1.8.2/go.mod h1:qw5AYuDrzRTnhvusDsrov+fDIxp9Dleuu12h8nfB398=
|
||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
@@ -25,12 +27,13 @@ github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
|
||||
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
||||
github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
|
||||
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
||||
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
||||
github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ=
|
||||
github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||
@@ -39,7 +42,7 @@ github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1/go.mod h1:A2S0CWkNylc2
|
||||
github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk=
|
||||
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
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=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
@@ -56,15 +59,16 @@ github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51
|
||||
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
|
||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||
github.com/gorilla/sessions v1.1.1/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w=
|
||||
github.com/gorilla/sessions v1.1.3 h1:uXoZdcdA5XdXF3QzuSlheVRUvjl+1rKY7zBXL68L9RU=
|
||||
github.com/gorilla/sessions v1.1.3/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w=
|
||||
github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||
github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
|
||||
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||
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.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kidstuff/mongostore v0.0.0-20181113001930-e650cd85ee4b/go.mod h1:g2nVr8KZVXJSS97Jo8pJ0jgq29P6H7dG0oplUA86MQw=
|
||||
@@ -78,16 +82,18 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
|
||||
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/lib/pq v1.10.3/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
|
||||
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=
|
||||
github.com/memcachier/mc v2.0.1+incompatible/go.mod h1:7bkvFE61leUBvXz+yxsOnGBQSZpBSPIMUQSmmSHvuXc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
@@ -107,11 +113,12 @@ github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG
|
||||
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/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
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=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/quasoft/memstore v0.0.0-20180925164028-84a050167438/go.mod h1:wTPjTepVu7uJBYgZ0SdWHQlIas582j6cn2jgk4DDdlg=
|
||||
github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b/go.mod h1:wTPjTepVu7uJBYgZ0SdWHQlIas582j6cn2jgk4DDdlg=
|
||||
github.com/quic-go/qtls-go1-18 v0.2.0 h1:5ViXqBZ90wpUcZS0ge79rf029yx0dYB0McyPJwqqj7U=
|
||||
github.com/quic-go/qtls-go1-19 v0.2.0 h1:Cvn2WdhyViFUHoOqK52i51k4nDX8EwIh5VJiVM4nttk=
|
||||
github.com/quic-go/qtls-go1-20 v0.1.0 h1:d1PK3ErFy9t7zxKsG3NXBJXZjp/kMLoIb3y/kV54oAI=
|
||||
@@ -127,8 +134,6 @@ github.com/sagernet/sing v0.1.6 h1:Qy63OUfKpcqKjfd5rPmUlj0RGjHZSK/PJn0duyCCsRg=
|
||||
github.com/sagernet/sing-shadowsocks v0.1.1-0.20230202035033-e3123545f2f7 h1:Plup6oEiyLzY3HDqQ+QsUBzgBGdVmcsgf3t8h940z9U=
|
||||
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.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shirou/gopsutil/v3 v3.23.1 h1:a9KKO+kGLKEvcPIs4W62v0nu3sciVDOOOPUD0Hz7z/4=
|
||||
github.com/shirou/gopsutil/v3 v3.23.1/go.mod h1:NN6mnm5/0k8jw4cBfCnJtr5L7ErOTg18tMNpgFkn0hA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@@ -143,12 +148,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg=
|
||||
github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4=
|
||||
github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI=
|
||||
github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM=
|
||||
github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI=
|
||||
github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA=
|
||||
github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM=
|
||||
github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms=
|
||||
github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4=
|
||||
github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q=
|
||||
@@ -193,12 +194,11 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -212,6 +212,7 @@ golang.org/x/sys v0.5.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=
|
||||
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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
@@ -241,9 +242,8 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
||||
gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
|
||||
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=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
|
||||
@@ -31,6 +31,9 @@ func (a *InboundController) initRouter(g *gin.RouterGroup) {
|
||||
g.POST("/add", a.addInbound)
|
||||
g.POST("/del/:id", a.delInbound)
|
||||
g.POST("/update/:id", a.updateInbound)
|
||||
g.POST("/addClient/", a.addInboundClient)
|
||||
g.POST("/delClient/:inboundId/:index", a.delInboundClient)
|
||||
g.POST("/updateClient/:index", a.updateInboundClient)
|
||||
g.POST("/resetClientTraffic/:email", a.resetClientTraffic)
|
||||
|
||||
}
|
||||
@@ -123,6 +126,65 @@ func (a *InboundController) updateInbound(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
func (a *InboundController) addInboundClient(c *gin.Context) {
|
||||
inbound := &model.Inbound{}
|
||||
err := c.ShouldBind(inbound)
|
||||
if err != nil {
|
||||
jsonMsg(c, I18n(c, "pages.inbounds.revise"), err)
|
||||
return
|
||||
}
|
||||
|
||||
err = a.inboundService.AddInboundClient(inbound)
|
||||
if err != nil {
|
||||
jsonMsg(c, "something worng!", err)
|
||||
return
|
||||
}
|
||||
jsonMsg(c, "Client added", nil)
|
||||
}
|
||||
|
||||
func (a *InboundController) delInboundClient(c *gin.Context) {
|
||||
inboundId, err := strconv.Atoi(c.Param("inboundId"))
|
||||
if err != nil {
|
||||
jsonMsg(c, I18n(c, "pages.inbounds.revise"), err)
|
||||
return
|
||||
}
|
||||
|
||||
index, err := strconv.Atoi(c.Param("index"))
|
||||
if err != nil {
|
||||
jsonMsg(c, I18n(c, "pages.inbounds.revise"), err)
|
||||
return
|
||||
}
|
||||
|
||||
err = a.inboundService.DelInboundClient(inboundId, index)
|
||||
if err != nil {
|
||||
jsonMsg(c, "something worng!", err)
|
||||
return
|
||||
}
|
||||
jsonMsg(c, "Client deleted", nil)
|
||||
}
|
||||
|
||||
func (a *InboundController) updateInboundClient(c *gin.Context) {
|
||||
index, err := strconv.Atoi(c.Param("index"))
|
||||
if err != nil {
|
||||
jsonMsg(c, I18n(c, "pages.inbounds.revise"), err)
|
||||
return
|
||||
}
|
||||
|
||||
inbound := &model.Inbound{}
|
||||
err = c.ShouldBind(inbound)
|
||||
if err != nil {
|
||||
jsonMsg(c, I18n(c, "pages.inbounds.revise"), err)
|
||||
return
|
||||
}
|
||||
|
||||
err = a.inboundService.UpdateInboundClient(inbound, index)
|
||||
if err != nil {
|
||||
jsonMsg(c, "something worng!", err)
|
||||
return
|
||||
}
|
||||
jsonMsg(c, "Client updated", nil)
|
||||
}
|
||||
|
||||
func (a *InboundController) resetClientTraffic(c *gin.Context) {
|
||||
email := c.Param("email")
|
||||
|
||||
|
||||
109
web/html/xui/client_modal.html
Normal file
109
web/html/xui/client_modal.html
Normal file
@@ -0,0 +1,109 @@
|
||||
{{define "clientsModal"}}
|
||||
<a-modal id="client-modal" v-model="clientModal.visible" :title="clientModal.title" @ok="clientModal.ok"
|
||||
:closable="true" :mask-closable="false"
|
||||
:ok-text="clientModal.okText" cancel-text='{{ i18n "close" }}'>
|
||||
{{template "form/client"}}
|
||||
</a-modal>
|
||||
<script>
|
||||
|
||||
const clientModal = {
|
||||
visible: false,
|
||||
title: '',
|
||||
okText: '',
|
||||
dbInbound: new DBInbound(),
|
||||
inbound: new Inbound(),
|
||||
clients: [],
|
||||
clientStats: [],
|
||||
index: null,
|
||||
isExpired: false,
|
||||
ok() {
|
||||
ObjectUtil.execute(clientModal.confirm, clientModal.inbound, clientModal.dbInbound, clientModal.index);
|
||||
},
|
||||
show({ title='', okText='{{ i18n "sure" }}', index=null, dbInbound=null, confirm=(index, dbInbound)=>{}, isEdit=false }) {
|
||||
this.visible = true;
|
||||
this.title = title;
|
||||
this.okText = okText;
|
||||
this.isEdit = isEdit;
|
||||
this.dbInbound = new DBInbound(dbInbound);
|
||||
this.inbound = dbInbound.toInbound();
|
||||
this.clients = this.getClients(this.inbound.protocol, this.inbound.settings);
|
||||
this.index = index === null ? this.clients.length : index;
|
||||
this.isExpired = isEdit ? this.inbound.isExpiry(this.index) : false;
|
||||
if (!isEdit){
|
||||
this.addClient(this.inbound.protocol, this.clients);
|
||||
}
|
||||
this.clientStats = this.dbInbound.clientStats.find(row => row.email === this.clients[this.index].email);
|
||||
this.confirm = confirm;
|
||||
},
|
||||
getClients(protocol, clientSettings) {
|
||||
switch(protocol){
|
||||
case Protocols.VMESS: return clientSettings.vmesses;
|
||||
case Protocols.VLESS: return clientSettings.vlesses;
|
||||
case Protocols.TROJAN: return clientSettings.trojans;
|
||||
default: return null;
|
||||
}
|
||||
},
|
||||
addClient(protocol, clients) {
|
||||
switch (protocol) {
|
||||
case Protocols.VMESS: return clients.push(new Inbound.VmessSettings.Vmess());
|
||||
case Protocols.VLESS: return clients.push(new Inbound.VLESSSettings.VLESS());
|
||||
case Protocols.TROJAN: return clients.push(new Inbound.TrojanSettings.Trojan());
|
||||
default: return null;
|
||||
}
|
||||
},
|
||||
close() {
|
||||
clientModal.visible = false;
|
||||
clientModal.loading(false);
|
||||
},
|
||||
loading(loading) {
|
||||
clientModal.confirmLoading = loading;
|
||||
},
|
||||
};
|
||||
|
||||
const clientModalApp = new Vue({
|
||||
delimiters: ['[[', ']]'],
|
||||
el: '#client-modal',
|
||||
data: {
|
||||
clientModal,
|
||||
get inbound() {
|
||||
return this.clientModal.inbound;
|
||||
},
|
||||
get client() {
|
||||
return this.clientModal.clients[this.clientModal.index];
|
||||
},
|
||||
get clientStats() {
|
||||
return this.clientModal.clientStats;
|
||||
},
|
||||
get isEdit() {
|
||||
return this.clientModal.isEdit;
|
||||
},
|
||||
get isTrafficExhausted() {
|
||||
if(!clientStats) return false
|
||||
if(clientStats.total == 0) return false
|
||||
if(clientStats.up + clientStats.down < clientStats.total) return false
|
||||
return true
|
||||
},
|
||||
get isExpiry() {
|
||||
return this.clientModal.isExpired
|
||||
},
|
||||
get statsColor() {
|
||||
if(!clientStats) return 'blue'
|
||||
if(clientStats.total === 0) return 'blue'
|
||||
else if(clientStats.total > 0 && (clientStats.down+clientStats.up) < clientStats.total) return 'cyan'
|
||||
else return 'red'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getNewEmail(client) {
|
||||
var chars = 'abcdefghijklmnopqrstuvwxyz1234567890';
|
||||
var string = '';
|
||||
var len = 6 + Math.floor(Math.random() * 5);
|
||||
for(var ii=0; ii<len; ii++){
|
||||
string += chars[Math.floor(Math.random() * chars.length)];
|
||||
}
|
||||
client.email = string;
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
{{end}}
|
||||
79
web/html/xui/form/client.html
Normal file
79
web/html/xui/form/client.html
Normal file
@@ -0,0 +1,79 @@
|
||||
{{define "form/client"}}
|
||||
<a-form layout="inline" v-if="client">
|
||||
<template v-if="isEdit">
|
||||
<a-tag v-if="isExpiry || isTrafficExhausted" color="red" style="margin-bottom: 10px;display: block;text-align: center;">Account is (Expired|Traffic Ended) And Disabled</a-tag>
|
||||
</template>
|
||||
<a-form-item>
|
||||
<span slot="label">
|
||||
Email
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
The email must be completely unique
|
||||
</template>
|
||||
<a-icon type="sync" @click="getNewEmail(client)"></a-icon>
|
||||
</a-tooltip>
|
||||
</span>
|
||||
<a-input v-model.trim="client.email"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="password" v-if="inbound.protocol === Protocols.TROJAN">
|
||||
<a-input v-model.trim="client.password"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="id" v-if="inbound.protocol === Protocols.VMESS || inbound.protocol === Protocols.VLESS">
|
||||
<a-input v-model.trim="client.id"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="inbound.tls" label="utls" layout="inline">
|
||||
<a-select v-model="client.fingerprint" label="utls" style="width: 150px">
|
||||
<a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "additional" }} ID' v-if="inbound.protocol === Protocols.VMESS">
|
||||
<a-input type="number" v-model.number="client.alterId"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="inbound.xtls" label="flow">
|
||||
<a-select v-model="client.flow" style="width: 150px">
|
||||
<a-select-option value="">{{ i18n "none" }}</a-select-option>
|
||||
<a-select-option v-for="key in XTLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item v-else-if="inbound.canEnableTlsFlow()" label="flow" layout="inline">
|
||||
<a-select v-model="client.flow" style="width: 150px">
|
||||
<a-select-option value="" selected>{{ i18n "none" }}</a-select-option>
|
||||
<a-select-option v-for="key in TLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<span slot="label">
|
||||
<span >{{ i18n "pages.inbounds.totalFlow" }}</span>(GB)
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
|
||||
</template>
|
||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
||||
</a-tooltip>
|
||||
</span>
|
||||
<a-input-number v-model="client._totalGB" :min="0"></a-input-number>
|
||||
<template v-if="isEdit && clientStats">
|
||||
{{ i18n "usage" }}:
|
||||
<a-tag :color="statsColor">
|
||||
[[ sizeFormat(clientStats.up) ]] /
|
||||
[[ sizeFormat(clientStats.down) ]]
|
||||
([[ sizeFormat(clientStats.up + clientStats.down) ]])
|
||||
</a-tag>
|
||||
</template>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<span slot="label">
|
||||
<span >{{ i18n "pages.inbounds.expireDate" }}</span>
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
<span>{{ i18n "pages.inbounds.leaveBlankToNeverExpire" }}</span>
|
||||
</template>
|
||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
||||
</a-tooltip>
|
||||
</span>
|
||||
<a-date-picker :show-time="{ format: 'HH:mm' }" format="YYYY-MM-DD HH:mm"
|
||||
v-model="client._expiryTime" style="width: 300px;"></a-date-picker>
|
||||
<a-tag color="red" v-if="isExpiry">Expired</a-tag>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
{{end}}
|
||||
@@ -8,7 +8,7 @@
|
||||
<a-switch v-model="dbInbound.enable"></a-switch>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "protocol" }}'>
|
||||
<a-select v-model="inbound.protocol" style="width: 160px;">
|
||||
<a-select v-model="inbound.protocol" style="width: 160px;" :disabled="isEdit">
|
||||
<a-select-option v-for="p in Protocols" :key="p" :value="p">[[ p ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
{{define "form/trojan"}}
|
||||
<a-form layout="inline">
|
||||
<label>{{ i18n "clients"}} </label>
|
||||
<a-collapse activeKey="0" v-for="(trojan, index) in inbound.settings.trojans"
|
||||
:key="`trojan-${index}`">
|
||||
|
||||
<a-collapse-panel :class="getHeaderStyle(trojan.email)" :header="getHeaderText(trojan.email)">
|
||||
<a-tag v-if="isExpiry(index) || ((getUpStats(trojan.email) + getDownStats(trojan.email)) > trojan.totalGB && trojan.totalGB != 0)" color="red" style="margin-bottom: 10px;display: block;text-align: center;">Account is (Expired|Traffic Ended) And Disabled</a-tag>
|
||||
<a-collapse activeKey="0" v-for="(client, index) in inbound.settings.trojans.slice(0,1)" v-if="!isEdit">
|
||||
<a-collapse-panel header="{{ i18n "pages.inbounds.client" }}">
|
||||
<a-form layout="inline">
|
||||
<a-form-item>
|
||||
<span slot="label">
|
||||
@@ -14,20 +10,17 @@
|
||||
<template slot="title">
|
||||
The email must be completely unique
|
||||
</template>
|
||||
<!--Renew Svg Icon-->
|
||||
<svg
|
||||
@click="getNewEmail(trojan)"
|
||||
xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="anticon anticon-question-circle" viewBox="0 0 16 16"> <path d="M11.534 7h3.932a.25.25 0 0 1 .192.41l-1.966 2.36a.25.25 0 0 1-.384 0l-1.966-2.36a.25.25 0 0 1 .192-.41zm-11 2h3.932a.25.25 0 0 0 .192-.41L2.692 6.23a.25.25 0 0 0-.384 0L.342 8.59A.25.25 0 0 0 .534 9z"/> <path fill-rule="evenodd" d="M8 3c-1.552 0-2.94.707-3.857 1.818a.5.5 0 1 1-.771-.636A6.002 6.002 0 0 1 13.917 7H12.9A5.002 5.002 0 0 0 8 3zM3.1 9a5.002 5.002 0 0 0 8.757 2.182.5.5 0 1 1 .771.636A6.002 6.002 0 0 1 2.083 9H3.1z"/> </svg>
|
||||
<a-icon @click="getNewEmail(client)" type="sync"> </a-icon>
|
||||
</a-tooltip>
|
||||
</span>
|
||||
<a-input v-model.trim="trojan.email"></a-input>
|
||||
<a-input v-model.trim="client.email"></a-input>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<a-form-item label="password">
|
||||
<a-input v-model.trim="trojan.password"></a-input>
|
||||
<a-input v-model.trim="client.password"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="inbound.xtls" label="flow">
|
||||
<a-select v-model="trojan.flow" style="width: 150px">
|
||||
<a-select v-model="client.flow" style="width: 150px">
|
||||
<a-select-option value="">{{ i18n "none" }}</a-select-option>
|
||||
<a-select-option v-for="key in XTLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
||||
</a-select>
|
||||
@@ -42,7 +35,7 @@
|
||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
||||
</a-tooltip>
|
||||
</span>
|
||||
<a-input-number v-model="trojan._totalGB" :min="0"></a-input-number>
|
||||
<a-input-number v-model="client._totalGB" :min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<span slot="label">
|
||||
@@ -55,39 +48,22 @@
|
||||
</a-tooltip>
|
||||
</span>
|
||||
<a-date-picker :show-time="{ format: 'HH:mm' }" format="YYYY-MM-DD HH:mm"
|
||||
v-model="trojan._expiryTime" style="width: 300px;"></a-date-picker>
|
||||
v-model="client._expiryTime" style="width: 300px;"></a-date-picker>
|
||||
</a-form-item>
|
||||
<a-form layout="inline">
|
||||
<a-tooltip v-if="trojan._totalGB > 0">
|
||||
<template slot="title">
|
||||
{{ i18n "pages.inbounds.resetTraffic" }}
|
||||
</template>
|
||||
<span style="color: #FF4D4F">
|
||||
<a-icon type="delete" @click="resetClientTraffic(trojan,$event)"></a-icon>
|
||||
</span>
|
||||
</a-tooltip>
|
||||
<a-tag color="blue">[[ sizeFormat(getUpStats(trojan.email)) ]] / [[ sizeFormat(getDownStats(trojan.email)) ]]</a-tag>
|
||||
<a-tag v-if="trojan._totalGB > 0" color="red">used : [[ sizeFormat(getUpStats(trojan.email) + getDownStats(trojan.email)) ]]</a-tag>
|
||||
<a-tag v-show="inbound.settings.trojans.length > 1" @click="removeClient(index, inbound.settings.trojans)">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22" width="22" height="22" class="mt-2 cursor-pointer">
|
||||
<path fill="none" d="M0 0h24v24H0z" />
|
||||
<path fill="#EC4899"
|
||||
d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16zm0-9.414l2.828-2.829 1.415 1.415L13.414 12l2.829 2.828-1.415 1.415L12 13.414l-2.828 2.829-1.415-1.415L10.586 12 7.757 9.172l1.415-1.415L12 10.586z"
|
||||
/>
|
||||
</svg>
|
||||
</a-tag>
|
||||
</a-form>
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
<a-tag @click="addClient(inbound.protocol, inbound.settings.trojans)">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" class="ml-2 cursor-pointer">
|
||||
<path fill="none" d="M0 0h24v24H0z" />
|
||||
<path fill="green"
|
||||
d="M11 11V7h2v4h4v2h-4v4h-2v-4H7v-2h4zm1 11C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16z"
|
||||
/>
|
||||
</svg>
|
||||
</a-tag>
|
||||
|
||||
<a-collapse v-else>
|
||||
<a-collapse-panel :header="'{{ i18n "pages.client.clientCount"}} : ' + inbound.settings.trojans.length">
|
||||
<table width="100%">
|
||||
<tr style="background-color: lightgrey;">
|
||||
<th v-for="col in Object.keys(inbound.settings.trojans[0]).slice(0, 3)">[[ col ]]</th>
|
||||
</tr>
|
||||
<tr v-for="(client, index) in inbound.settings.trojans" :style="[ index % 2 == 1 ? {'background-color': '#fafafa'} : {}]">
|
||||
<td v-for="col in Object.values(client).slice(0, 3)">[[ col ]]</td>
|
||||
</tr>
|
||||
</table>
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
<a-form layout="inline">
|
||||
<a-form-item label="fallbacks">
|
||||
<a-row>
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
{{define "form/vless"}}
|
||||
<a-form layout="inline">
|
||||
<label>{{ i18n "clients"}}</label>
|
||||
<a-collapse activeKey="0" v-for="(vless, index) in inbound.settings.vlesses"
|
||||
:key="`vless-${index}`">
|
||||
|
||||
<a-collapse-panel :class="getHeaderStyle(vless.email)" :header="getHeaderText(vless.email)">
|
||||
<a-tag v-if="isExpiry(index) || ((getUpStats(vless.email) + getDownStats(vless.email)) > vless.totalGB && vless.totalGB != 0)" color="red" style="margin-bottom: 10px;display: block;text-align: center;">Account is (Expired|Traffic Ended) And Disabled</a-tag>
|
||||
|
||||
<a-collapse activeKey="0" v-for="(client, index) in inbound.settings.vlesses.slice(0,1)" v-if="!isEdit">
|
||||
<a-collapse-panel header="{{ i18n "pages.inbounds.client" }}">
|
||||
<a-form layout="inline">
|
||||
<a-form-item>
|
||||
<span slot="label">
|
||||
@@ -15,17 +10,14 @@
|
||||
<template slot="title">
|
||||
The email must be completely unique
|
||||
</template>
|
||||
<!--Renew Svg Icon-->
|
||||
<svg
|
||||
@click="getNewEmail(vless)"
|
||||
xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="anticon anticon-question-circle" viewBox="0 0 16 16"> <path d="M11.534 7h3.932a.25.25 0 0 1 .192.41l-1.966 2.36a.25.25 0 0 1-.384 0l-1.966-2.36a.25.25 0 0 1 .192-.41zm-11 2h3.932a.25.25 0 0 0 .192-.41L2.692 6.23a.25.25 0 0 0-.384 0L.342 8.59A.25.25 0 0 0 .534 9z"/> <path fill-rule="evenodd" d="M8 3c-1.552 0-2.94.707-3.857 1.818a.5.5 0 1 1-.771-.636A6.002 6.002 0 0 1 13.917 7H12.9A5.002 5.002 0 0 0 8 3zM3.1 9a5.002 5.002 0 0 0 8.757 2.182.5.5 0 1 1 .771.636A6.002 6.002 0 0 1 2.083 9H3.1z"/> </svg>
|
||||
<a-icon type="sync" @click="getNewEmail(client)"></a-icon>
|
||||
</a-tooltip>
|
||||
</span>
|
||||
<a-input v-model.trim="vless.email"></a-input>
|
||||
<a-input v-model.trim="client.email"></a-input>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<a-form-item label="id">
|
||||
<a-input v-model.trim="vless.id"></a-input>
|
||||
<a-input v-model.trim="client.id"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="inbound.xtls" label="flow">
|
||||
<a-select v-model="inbound.settings.vlesses[index].flow" style="width: 150px">
|
||||
@@ -54,7 +46,7 @@
|
||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
||||
</a-tooltip>
|
||||
</span>
|
||||
<a-input-number v-model="vless._totalGB" :min="0"></a-input-number>
|
||||
<a-input-number v-model="client._totalGB" :min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<span slot="label">
|
||||
@@ -67,40 +59,22 @@
|
||||
</a-tooltip>
|
||||
</span>
|
||||
<a-date-picker :show-time="{ format: 'HH:mm' }" format="YYYY-MM-DD HH:mm"
|
||||
v-model="vless._expiryTime" style="width: 300px;"></a-date-picker>
|
||||
v-model="client._expiryTime" style="width: 300px;"></a-date-picker>
|
||||
</a-form-item>
|
||||
<a-form layout="inline">
|
||||
<a-tooltip v-if="vless._totalGB > 0">
|
||||
<template slot="title">
|
||||
{{ i18n "pages.inbounds.resetTraffic" }}
|
||||
</template>
|
||||
<span style="color: #FF4D4F">
|
||||
<a-icon type="delete" @click="resetClientTraffic(vless,$event)"></a-icon>
|
||||
</span>
|
||||
</a-tooltip>
|
||||
<a-tag color="blue">[[ sizeFormat(getUpStats(vless.email)) ]] / [[ sizeFormat(getDownStats(vless.email)) ]]</a-tag>
|
||||
<a-tag v-if="vless._totalGB > 0" color="red">used : [[ sizeFormat(getUpStats(vless.email) + getDownStats(vless.email)) ]]</a-tag>
|
||||
|
||||
<a-tag v-show="inbound.settings.vlesses.length > 1" @click="removeClient(index, inbound.settings.vlesses)">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22" width="22" height="22" class="mt-2 cursor-pointer">
|
||||
<path fill="none" d="M0 0h24v24H0z" />
|
||||
<path fill="#EC4899"
|
||||
d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16zm0-9.414l2.828-2.829 1.415 1.415L13.414 12l2.829 2.828-1.415 1.415L12 13.414l-2.828 2.829-1.415-1.415L10.586 12 7.757 9.172l1.415-1.415L12 10.586z"
|
||||
/>
|
||||
</svg>
|
||||
</a-tag>
|
||||
</a-form>
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
<a-tag @click="addClient(inbound.protocol, inbound.settings.vlesses)">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" class="ml-2 cursor-pointer">
|
||||
<path fill="none" d="M0 0h24v24H0z" />
|
||||
<path fill="green"
|
||||
d="M11 11V7h2v4h4v2h-4v4h-2v-4H7v-2h4zm1 11C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16z"
|
||||
/>
|
||||
</svg>
|
||||
</a-tag>
|
||||
|
||||
<a-collapse v-else>
|
||||
<a-collapse-panel :header="'{{ i18n "pages.client.clientCount"}} : ' + inbound.settings.vlesses.length">
|
||||
<table width="100%">
|
||||
<tr style="background-color: lightgrey;">
|
||||
<th v-for="col in Object.keys(inbound.settings.vlesses[0]).slice(0, 3)">[[ col ]]</th>
|
||||
</tr>
|
||||
<tr v-for="(client, index) in inbound.settings.vlesses" :style="[ index % 2 == 1 ? {'background-color': '#fafafa'} : {}]">
|
||||
<td v-for="col in Object.values(client).slice(0, 3)">[[ col ]]</td>
|
||||
</tr>
|
||||
</table>
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
<a-form layout="inline">
|
||||
<a-form-item label="fallbacks">
|
||||
<a-row>
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
{{define "form/vmess"}}
|
||||
<a-form layout="inline">
|
||||
<label>{{ i18n "clients"}}</label>
|
||||
<a-collapse activeKey="0" v-for="(vmess, index) in inbound.settings.vmesses"
|
||||
:key="`vmess-${index}`">
|
||||
<a-collapse-panel :class="getHeaderStyle(vmess.email)" :header="getHeaderText(vmess.email)">
|
||||
<a-tag v-if="isExpiry(index) || ((getUpStats(vmess.email) + getDownStats(vmess.email)) > vmess.totalGB && vmess.totalGB != 0)" color="red" style="margin-bottom: 10px;display: block;text-align: center;">Account is (Expired|Traffic Ended) And Disabled</a-tag>
|
||||
|
||||
<a-collapse activeKey="0" v-for="(client, index) in inbound.settings.vmesses.slice(0,1)" v-if="!isEdit">
|
||||
<a-collapse-panel header="{{ i18n "pages.inbounds.client" }}">
|
||||
<a-form layout="inline">
|
||||
<a-form-item>
|
||||
<span slot="label">
|
||||
@@ -14,20 +10,17 @@
|
||||
<template slot="title">
|
||||
The email must be completely unique
|
||||
</template>
|
||||
<!--Renew Svg Icon-->
|
||||
<svg
|
||||
@click="getNewEmail(vmess)"
|
||||
xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="anticon anticon-question-circle" viewBox="0 0 16 16"> <path d="M11.534 7h3.932a.25.25 0 0 1 .192.41l-1.966 2.36a.25.25 0 0 1-.384 0l-1.966-2.36a.25.25 0 0 1 .192-.41zm-11 2h3.932a.25.25 0 0 0 .192-.41L2.692 6.23a.25.25 0 0 0-.384 0L.342 8.59A.25.25 0 0 0 .534 9z"/> <path fill-rule="evenodd" d="M8 3c-1.552 0-2.94.707-3.857 1.818a.5.5 0 1 1-.771-.636A6.002 6.002 0 0 1 13.917 7H12.9A5.002 5.002 0 0 0 8 3zM3.1 9a5.002 5.002 0 0 0 8.757 2.182.5.5 0 1 1 .771.636A6.002 6.002 0 0 1 2.083 9H3.1z"/> </svg>
|
||||
<a-icon type="sync" @click="getNewEmail(client)"></a-icon>
|
||||
</a-tooltip>
|
||||
</span>
|
||||
<a-input v-model.trim="vmess.email"></a-input>
|
||||
<a-input v-model.trim="client.email"></a-input>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<a-form-item label="id">
|
||||
<a-input v-model.trim="vmess.id"></a-input>
|
||||
<a-input v-model.trim="client.id"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "additional" }} ID'>
|
||||
<a-input type="number" v-model.number="vmess.alterId"></a-input>
|
||||
<a-input type="number" v-model.number="client.alterId"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<span slot="label">
|
||||
@@ -39,7 +32,7 @@
|
||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
||||
</a-tooltip>
|
||||
</span>
|
||||
<a-input-number v-model="vmess._totalGB" :min="0"></a-input-number>
|
||||
<a-input-number v-model="client._totalGB" :min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<span slot="label">
|
||||
@@ -52,43 +45,22 @@
|
||||
</a-tooltip>
|
||||
</span>
|
||||
<a-date-picker :show-time="{ format: 'HH:mm' }" format="YYYY-MM-DD HH:mm"
|
||||
v-model="vmess._expiryTime" style="width: 300px;"></a-date-picker>
|
||||
v-model="client._expiryTime" style="width: 300px;"></a-date-picker>
|
||||
</a-form-item>
|
||||
<a-form layout="inline">
|
||||
<a-tooltip v-if="vmess._totalGB > 0">
|
||||
<template slot="title">
|
||||
{{ i18n "pages.inbounds.resetTraffic" }}
|
||||
</template>
|
||||
<span style="color: #FF4D4F">
|
||||
<a-icon type="delete" @click="resetClientTraffic(vmess,$event)"></a-icon>
|
||||
</span>
|
||||
</a-tooltip>
|
||||
<a-tag color="blue">[[ sizeFormat(getUpStats(vmess.email)) ]] / [[ sizeFormat(getDownStats(vmess.email)) ]]</a-tag>
|
||||
<a-tag v-if="vmess._totalGB > 0" color="red">used : [[ sizeFormat(getUpStats(vmess.email) + getDownStats(vmess.email)) ]]</a-tag>
|
||||
<a-tag v-show="inbound.settings.vmesses.length > 1" @click="removeClient(index, inbound.settings.vmesses)">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22" width="22" height="22" class="mt-2 cursor-pointer">
|
||||
<path fill="none" d="M0 0h24v24H0z" />
|
||||
<path fill="#EC4899"
|
||||
d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16zm0-9.414l2.828-2.829 1.415 1.415L13.414 12l2.829 2.828-1.415 1.415L12 13.414l-2.828 2.829-1.415-1.415L10.586 12 7.757 9.172l1.415-1.415L12 10.586z"
|
||||
/>
|
||||
</svg>
|
||||
</a-tag>
|
||||
</a-form>
|
||||
|
||||
|
||||
</a-collapse-panel>
|
||||
|
||||
</a-collapse>
|
||||
|
||||
<a-tag @click="addClient(inbound.protocol, inbound.settings.vmesses)">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" class="ml-2 cursor-pointer">
|
||||
<path fill="none" d="M0 0h24v24H0z" />
|
||||
<path fill="green"
|
||||
d="M11 11V7h2v4h4v2h-4v4h-2v-4H7v-2h4zm1 11C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16z"
|
||||
/>
|
||||
</svg>
|
||||
</a-tag>
|
||||
|
||||
<a-collapse v-else>
|
||||
<a-collapse-panel :header="'{{ i18n "pages.client.clientCount"}} : ' + inbound.settings.vmesses.length">
|
||||
<table width="100%">
|
||||
<tr style="background-color: lightgrey;">
|
||||
<th v-for="col in Object.keys(inbound.settings.vmesses[0]).slice(0, 3)">[[ col ]]</th>
|
||||
</tr>
|
||||
<tr v-for="(client, index) in inbound.settings.vmesses" :style="[ index % 2 == 1 ? {'background-color': '#fafafa'} : {}]">
|
||||
<td v-for="col in Object.values(client).slice(0, 3)">[[ col ]]</td>
|
||||
</tr>
|
||||
</table>
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
<a-form layout="inline">
|
||||
<a-form-item label='{{ i18n "pages.inbounds.disableInsecureEncryption" }}'>
|
||||
<a-switch v-model.number="inbound.settings.disableInsecure"></a-switch>
|
||||
|
||||
@@ -1,15 +1,25 @@
|
||||
{{define "client_table"}}
|
||||
<template slot="actions" slot-scope="text, client, index">
|
||||
<a-dropdown>
|
||||
<a-icon @click="e => e.preventDefault()" type="menu"></a-icon>
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item v-if="record.hasLink()" @click="showQrcode(record,index);"><a-icon type="qrcode"></a-icon>{{ i18n "qrCode" }}</a-menu-item>
|
||||
<a-menu-item @click="showInfo(record,index);"><a-icon type="info-circle"></a-icon>{{ i18n "info" }}</a-menu-item>
|
||||
<a-menu-item @click="resetClientTraffic(client,record,$event)" v-if="client.email != ''"><a-icon type="retweet"></a-icon>{{ i18n "pages.inbounds.resetTraffic" }}</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
<a-tooltip>
|
||||
<template slot="title">{{ i18n "qrCode" }}</template>
|
||||
<a-icon style="font-size: 24px;" type="qrcode" v-if="record.hasLink()" @click="showQrcode(record,index);"></a-icon>
|
||||
</a-tooltip>
|
||||
<a-tooltip>
|
||||
<template slot="title">{{ i18n "pages.client.edit" }}</template>
|
||||
<a-icon style="font-size: 24px;" type="edit" @click="openEditClient(record.id,index);"></a-icon>
|
||||
</a-tooltip>
|
||||
<a-tooltip>
|
||||
<template slot="title">{{ i18n "info" }}</template>
|
||||
<a-icon style="font-size: 24px;" type="info-circle" @click="showInfo(record,index);"></a-icon>
|
||||
</a-tooltip>
|
||||
<a-tooltip>
|
||||
<template slot="title">{{ i18n "pages.inbounds.resetTraffic" }}</template>
|
||||
<a-icon style="font-size: 24px;" type="retweet" @click="resetClientTraffic(client,record,$event)" v-if="client.email.length > 0"></a-icon>
|
||||
</a-tooltip>
|
||||
<a-tooltip>
|
||||
<template slot="title"><span style="color: #FF4D4F"> {{ i18n "delete"}}</span></template>
|
||||
<a-icon style="font-size: 24px;" type="delete" v-if="isRemovable(record.id)" @click="delClient(record.id,index)"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template slot="client" slot-scope="text, client">
|
||||
[[ client.email ]]
|
||||
|
||||
@@ -58,18 +58,17 @@
|
||||
</table>
|
||||
<a-divider>{{ i18n "pages.inbounds.client" }}</a-divider>
|
||||
<table style="margin-bottom: 10px; width: 100%;">
|
||||
<tr><th>[[ Object.keys(infoModal.clientSettings)[0] ]]</th><th>[[ Object.keys(infoModal.clientSettings)[1] ]]</th><th>[[ Object.keys(infoModal.clientSettings)[2] ]]</th></tr>
|
||||
<tr>
|
||||
<td><a-tag color="green">[[ Object.values(infoModal.clientSettings)[0] ]]</a-tag></td>
|
||||
<td><a-tag color="green">[[ Object.values(infoModal.clientSettings)[1] ]]</a-tag></td>
|
||||
<td><a-tag color="green">[[ Object.values(infoModal.clientSettings)[2] ]]</a-tag></td>
|
||||
<th v-for="col in Object.keys(infoModal.clientSettings).slice(0, 3)">[[ col ]]</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td v-for="col in Object.values(infoModal.clientSettings).slice(0, 3)"><a-tag color="green">[[ col ]]</a-tag></td>
|
||||
</table>
|
||||
<table style="margin-bottom: 10px; width: 100%;">
|
||||
<tr><th>{{ i18n "usage" }}</th><th>{{ i18n "pages.inbounds.totalFlow" }}</th><th>{{ i18n "pages.inbounds.expireDate" }}</th><th>{{ i18n "enable" }}</th></tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a-tag :color="statsColor(infoModal.clientStats)">
|
||||
<a-tag v-if="infoModal.clientStats" :color="statsColor(infoModal.clientStats)">
|
||||
[[ sizeFormat(infoModal.clientStats['up']) ]] /
|
||||
[[ sizeFormat(infoModal.clientStats['down']) ]]
|
||||
([[ sizeFormat(infoModal.clientStats['up'] + infoModal.clientStats['down']) ]])
|
||||
@@ -88,7 +87,7 @@
|
||||
<a-tag v-else color="green">{{ i18n "indefinite" }}</a-tag>
|
||||
</td>
|
||||
<td>
|
||||
<a-tag v-if="infoModal.clientStats.enable" color="blue">{{ i18n "enabled" }}</a-tag>
|
||||
<a-tag v-if="isEnable" color="blue">{{ i18n "enabled" }}</a-tag>
|
||||
<a-tag v-else color="red">{{ i18n "disabled" }}</a-tag>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -119,18 +118,8 @@
|
||||
this.dbInbound = new DBInbound(dbInbound);
|
||||
this.link = dbInbound.genLink(index);
|
||||
this.clientSettings = Object.values(JSON.parse(this.inbound.settings).clients)[index];
|
||||
this.clientStats = dbInbound.clientStats;
|
||||
this.isExpired = this.inbound.isExpiry(index);
|
||||
if(dbInbound.clientStats.length > 0)
|
||||
{
|
||||
for (const key in dbInbound.clientStats) {
|
||||
if (Object.hasOwnProperty.call(dbInbound.clientStats, key)) {
|
||||
if(dbInbound.clientStats[key]['email'] == this.clientSettings.email)
|
||||
this.clientStats = dbInbound.clientStats[key];
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
this.clientStats = this.dbInbound.clientStats.find(row => row.email === this.clientSettings.email);
|
||||
this.visible = true;
|
||||
infoModalApp.$nextTick(() => {
|
||||
if (this.clipboard === null) {
|
||||
@@ -156,6 +145,12 @@
|
||||
},
|
||||
get inbound() {
|
||||
return this.infoModal.inbound;
|
||||
},
|
||||
get isEnable() {
|
||||
if(infoModal.clientStats){
|
||||
return infoModal.clientStats.enable;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -178,6 +173,7 @@
|
||||
});
|
||||
},
|
||||
statsColor(stats) {
|
||||
if(!stats) return 'blue'
|
||||
if(stats['total'] === 0) return 'blue'
|
||||
else if(stats['total'] > 0 && (stats['down']+stats['up']) < stats['total']) return 'cyan'
|
||||
else return 'red'
|
||||
|
||||
@@ -109,53 +109,18 @@
|
||||
isExpiry(index) {
|
||||
return this.inbound.isExpiry(index)
|
||||
},
|
||||
getUpStats(email) {
|
||||
clientStats = this.inbound.clientStats
|
||||
if(clientStats.length > 0)
|
||||
{
|
||||
for (const key in clientStats) {
|
||||
if (Object.hasOwnProperty.call(clientStats, key)) {
|
||||
if(clientStats[key]['email'] == email)
|
||||
return clientStats[key]['up']
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
getDownStats(email) {
|
||||
clientStats = this.inbound.clientStats
|
||||
if(clientStats.length > 0)
|
||||
{
|
||||
for (const key in clientStats) {
|
||||
if (Object.hasOwnProperty.call(clientStats, key)) {
|
||||
if(clientStats[key]['email'] == email)
|
||||
return clientStats[key]['down']
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
isClientEnable(email) {
|
||||
clientStats = this.dbInbound.clientStats ? this.dbInbound.clientStats.find(stats => stats.email === email) : null
|
||||
return clientStats ? clientStats['enable'] : true
|
||||
},
|
||||
getHeaderText(email) {
|
||||
if(email == "")
|
||||
return "Add Client"
|
||||
|
||||
return email + (this.isClientEnable(email) == true ? ' Active' : ' Deactive')
|
||||
},
|
||||
getHeaderStyle(email) {
|
||||
return (this.isClientEnable(email) == true ? '' : 'deactive-client')
|
||||
},
|
||||
getNewEmail(client) {
|
||||
var chars = 'abcdefghijklmnopqrstuvwxyz1234567890';
|
||||
var string = '';
|
||||
var len = 6 + Math.floor(Math.random() * 5)
|
||||
var len = 6 + Math.floor(Math.random() * 5);
|
||||
for(var ii=0; ii<len; ii++){
|
||||
string += chars[Math.floor(Math.random() * chars.length)];
|
||||
}
|
||||
client.email = string + "@gmail.com"
|
||||
client.email = string;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
<transition name="list" appear>
|
||||
<a-card hoverable>
|
||||
<div slot="title">
|
||||
<a-button type="primary" icon="plus" @click="openAddInbound"></a-button>
|
||||
<a-button type="primary" icon="plus" @click="openAddInbound">{{ i18n "pages.inbounds.addInbound" }}</a-button>
|
||||
</div>
|
||||
<a-input v-model.lazy="searchKey" placeholder="{{ i18n "search" }}" autofocus style="max-width: 300px"></a-input>
|
||||
<a-table :columns="columns" :row-key="dbInbound => dbInbound.id"
|
||||
@@ -65,6 +65,10 @@
|
||||
<a-icon type="edit"></a-icon>
|
||||
{{ i18n "edit" }}
|
||||
</a-menu-item>
|
||||
<a-menu-item v-if="dbInbound.isTrojan || dbInbound.isVLess || dbInbound.isVMess" key="addClient">
|
||||
<a-icon type="user"></a-icon>
|
||||
{{ i18n "pages.client.add"}}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="resetTraffic">
|
||||
<a-icon type="retweet"></a-icon> {{ i18n "pages.inbounds.resetTraffic" }}
|
||||
</a-menu-item>
|
||||
@@ -128,15 +132,6 @@
|
||||
>
|
||||
{{template "client_table"}}
|
||||
</a-table>
|
||||
<a-table
|
||||
v-else
|
||||
:row-key="client => client.id"
|
||||
:columns="innerOneColumns"
|
||||
:data-source="record"
|
||||
:pagination="false"
|
||||
>
|
||||
{{template "client_table"}}
|
||||
</a-table>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-card>
|
||||
@@ -196,7 +191,7 @@
|
||||
}];
|
||||
|
||||
const innerColumns = [
|
||||
{ title: '', width: 20, scopedSlots: { customRender: 'actions' } },
|
||||
{ title: '{{ i18n "pages.inbounds.operate" }}', width: 60, scopedSlots: { customRender: 'actions' } },
|
||||
{ title: '{{ i18n "pages.inbounds.client" }}', width: 80, scopedSlots: { customRender: 'client' } },
|
||||
{ title: '{{ i18n "pages.inbounds.traffic" }}↑|↓', width: 80, scopedSlots: { customRender: 'traffic' } },
|
||||
{ title: '{{ i18n "pages.inbounds.expireDate" }}', width: 70, scopedSlots: { customRender: 'expiryTime' } },
|
||||
@@ -204,17 +199,13 @@
|
||||
];
|
||||
|
||||
const innerTrojanColumns = [
|
||||
{ title: '', width: 20, scopedSlots: { customRender: 'actions' } },
|
||||
{ title: '{{ i18n "pages.inbounds.operate" }}', width: 60, scopedSlots: { customRender: 'actions' } },
|
||||
{ title: '{{ i18n "pages.inbounds.client" }}', width: 80, scopedSlots: { customRender: 'client' } },
|
||||
{ title: '{{ i18n "pages.inbounds.traffic" }}↑|↓', width: 80, scopedSlots: { customRender: 'traffic' } },
|
||||
{ title: '{{ i18n "pages.inbounds.expireDate" }}', width: 70, scopedSlots: { customRender: 'expiryTime' } },
|
||||
{ title: 'Password', width: 150, dataIndex: "password" },
|
||||
];
|
||||
|
||||
const innerOneColumns = [
|
||||
{ title: '', width: 50, scopedSlots: { customRender: 'actions' } },
|
||||
];
|
||||
|
||||
const app = new Vue({
|
||||
delimiters: ['[[', ']]'],
|
||||
el: '#app',
|
||||
@@ -238,6 +229,7 @@
|
||||
return;
|
||||
}
|
||||
this.setInbounds(msg.obj);
|
||||
this.searchKey = '';
|
||||
},
|
||||
setInbounds(dbInbounds) {
|
||||
this.inbounds.splice(0);
|
||||
@@ -281,6 +273,9 @@
|
||||
case "edit":
|
||||
this.openEditInbound(dbInbound.id);
|
||||
break;
|
||||
case "addClient":
|
||||
this.openAddClient(dbInbound)
|
||||
break;
|
||||
case "resetTraffic":
|
||||
this.resetTraffic(dbInbound);
|
||||
break;
|
||||
@@ -355,6 +350,72 @@
|
||||
};
|
||||
await this.submit(`/xui/inbound/update/${dbInbound.id}`, data, inModal);
|
||||
},
|
||||
openAddClient(inbound) {
|
||||
clientModal.show({
|
||||
title: '{{ i18n "pages.client.add"}}',
|
||||
okText: '{{ i18n "pages.client.submitAdd"}}',
|
||||
dbInbound: inbound,
|
||||
confirm: async (inbound, dbInbound, index) => {
|
||||
clientModal.loading();
|
||||
await this.addClient(inbound, dbInbound);
|
||||
clientModal.close();
|
||||
},
|
||||
isEdit: false
|
||||
});
|
||||
},
|
||||
openEditClient(dbInbound_id, index) {
|
||||
dbInbound = this.dbInbounds.find(row => row.id === dbInbound_id);
|
||||
clientModal.show({
|
||||
title: '{{ i18n "pages.client.edit"}}',
|
||||
okText: '{{ i18n "pages.client.submitEdit"}}',
|
||||
dbInbound: dbInbound,
|
||||
index: index,
|
||||
confirm: async (inbound, dbInbound, index) => {
|
||||
clientModal.loading();
|
||||
await this.updateClient(inbound, dbInbound, index);
|
||||
clientModal.close();
|
||||
},
|
||||
isEdit: true
|
||||
});
|
||||
},
|
||||
async addClient(inbound, dbInbound) {
|
||||
const data = {
|
||||
id: dbInbound.id,
|
||||
up: dbInbound.up,
|
||||
down: dbInbound.down,
|
||||
total: dbInbound.total,
|
||||
remark: dbInbound.remark,
|
||||
enable: dbInbound.enable,
|
||||
expiryTime: dbInbound.expiryTime,
|
||||
|
||||
listen: inbound.listen,
|
||||
port: inbound.port,
|
||||
protocol: inbound.protocol,
|
||||
settings: inbound.settings.toString(),
|
||||
streamSettings: inbound.stream.toString(),
|
||||
sniffing: inbound.canSniffing() ? inbound.sniffing.toString() : '{}',
|
||||
};
|
||||
await this.submit('/xui/inbound/addClient', data, clientModal);
|
||||
},
|
||||
async updateClient(inbound, dbInbound, index) {
|
||||
const data = {
|
||||
id: dbInbound.id,
|
||||
up: dbInbound.up,
|
||||
down: dbInbound.down,
|
||||
total: dbInbound.total,
|
||||
remark: dbInbound.remark,
|
||||
enable: dbInbound.enable,
|
||||
expiryTime: dbInbound.expiryTime,
|
||||
|
||||
listen: inbound.listen,
|
||||
port: inbound.port,
|
||||
protocol: inbound.protocol,
|
||||
settings: inbound.settings.toString(),
|
||||
streamSettings: inbound.stream.toString(),
|
||||
sniffing: inbound.canSniffing() ? inbound.sniffing.toString() : '{}',
|
||||
};
|
||||
await this.submit(`/xui/inbound/updateClient/${index}`, data, clientModal);
|
||||
},
|
||||
resetTraffic(dbInbound) {
|
||||
this.$confirm({
|
||||
title: '{{ i18n "pages.inbounds.resetTraffic"}}',
|
||||
@@ -378,6 +439,15 @@
|
||||
onOk: () => this.submit('/xui/inbound/del/' + dbInbound.id),
|
||||
});
|
||||
},
|
||||
delClient(dbInboundId,index) {
|
||||
this.$confirm({
|
||||
title: '{{ i18n "pages.inbounds.deleteInbound"}}',
|
||||
content: '{{ i18n "pages.inbounds.deleteInboundContent"}}',
|
||||
okText: '{{ i18n "delete"}}',
|
||||
cancelText: '{{ i18n "cancel"}}',
|
||||
onOk: () => this.submit('/xui/inbound/delClient/' + dbInboundId + '/' + index),
|
||||
});
|
||||
},
|
||||
showQrcode(dbInbound, clientIndex) {
|
||||
const link = dbInbound.genLink(clientIndex);
|
||||
qrModal.show('{{ i18n "qrCode"}}', link, dbInbound);
|
||||
@@ -419,65 +489,38 @@
|
||||
if (!msg.success) {
|
||||
return;
|
||||
}
|
||||
clientStats = inbound.clientStats
|
||||
clientStats = inbound.clientStats.find(stats => stats.email == client.email)
|
||||
if(clientStats.length > 0)
|
||||
{
|
||||
for (const key in clientStats) {
|
||||
if (Object.hasOwnProperty.call(clientStats, key)) {
|
||||
if(clientStats[key]['email'] == client.email){
|
||||
clientStats[key]['up'] = 0
|
||||
clientStats[key]['down'] = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
clientStats.up = 0
|
||||
clientStats.down = 0
|
||||
}
|
||||
},
|
||||
isExpiry(dbInbound, index) {
|
||||
return dbInbound.toInbound().isExpiry(index)
|
||||
},
|
||||
getUpStats(dbInbound, email) {
|
||||
clientStats = dbInbound.clientStats
|
||||
if(clientStats.length > 0)
|
||||
{
|
||||
for (const key in clientStats) {
|
||||
if (Object.hasOwnProperty.call(clientStats, key)) {
|
||||
if(clientStats[key]['email'] == email)
|
||||
return clientStats[key]['up']
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
if(email.length == 0) return 0
|
||||
clientStats = dbInbound.clientStats.find(stats => stats.email === email)
|
||||
return clientStats ? clientStats.up : 0
|
||||
},
|
||||
getDownStats(dbInbound, email) {
|
||||
clientStats = dbInbound.clientStats
|
||||
if(clientStats.length > 0)
|
||||
{
|
||||
for (const key in clientStats) {
|
||||
if (Object.hasOwnProperty.call(clientStats, key)) {
|
||||
if(clientStats[key]['email'] == email)
|
||||
return clientStats[key]['down']
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
if(email.length == 0) return 0
|
||||
clientStats = dbInbound.clientStats.find(stats => stats.email === email)
|
||||
return clientStats ? clientStats.down : 0
|
||||
},
|
||||
isTrafficExhausted(dbInbound, email) {
|
||||
clientStats = dbInbound.clientStats
|
||||
if(clientStats.length > 0)
|
||||
{
|
||||
for (const key in clientStats) {
|
||||
if (Object.hasOwnProperty.call(clientStats, key)) {
|
||||
if(clientStats[key]['email'] == email)
|
||||
return clientStats[key]['down']+clientStats[key]['up'] > clientStats[key]['total']
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
if(email.length == 0) return false
|
||||
clientStats = dbInbound.clientStats.find(stats => stats.email === email)
|
||||
return clientStats.down + clientStats.up > clientStats.total
|
||||
},
|
||||
isClientEnabled(dbInbound, email) {
|
||||
clientStats = dbInbound.clientStats ? dbInbound.clientStats.find(stats => stats.email === email) : null
|
||||
return clientStats ? clientStats['enable'] : true
|
||||
},
|
||||
isRemovable(dbInbound_id){
|
||||
return this.getInboundClients(this.dbInbounds.find(row => row.id === dbInbound_id)).length > 1
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
searchKey: debounce(function (newVal) {
|
||||
@@ -509,5 +552,6 @@
|
||||
{{template "qrcodeModal"}}
|
||||
{{template "textModal"}}
|
||||
{{template "inboundInfoModal"}}
|
||||
{{template "clientsModal"}}
|
||||
</body>
|
||||
</html>
|
||||
@@ -125,11 +125,16 @@ func (s *InboundService) AddInbound(inbound *model.Inbound) (*model.Inbound, err
|
||||
return inbound, common.NewError("Duplicate email:", existEmail)
|
||||
}
|
||||
|
||||
clients, err := s.getClients(inbound)
|
||||
if err != nil {
|
||||
return inbound, err
|
||||
}
|
||||
|
||||
db := database.GetDB()
|
||||
|
||||
err = db.Save(inbound).Error
|
||||
if err == nil {
|
||||
s.UpdateClientStat(inbound.Id, inbound.Settings)
|
||||
s.AddClientStat(inbound.Id, &clients[0])
|
||||
}
|
||||
return inbound, err
|
||||
}
|
||||
@@ -168,6 +173,10 @@ func (s *InboundService) AddInbounds(inbounds []*model.Inbound) error {
|
||||
|
||||
func (s *InboundService) DelInbound(id int) error {
|
||||
db := database.GetDB()
|
||||
err := db.Where("inbound_id = ?", id).Delete(xray.ClientTraffic{}).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return db.Delete(model.Inbound{}, id).Error
|
||||
}
|
||||
|
||||
@@ -216,11 +225,104 @@ func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound,
|
||||
oldInbound.Sniffing = inbound.Sniffing
|
||||
oldInbound.Tag = fmt.Sprintf("inbound-%v", inbound.Port)
|
||||
|
||||
s.UpdateClientStat(inbound.Id, inbound.Settings)
|
||||
db := database.GetDB()
|
||||
return inbound, db.Save(oldInbound).Error
|
||||
}
|
||||
|
||||
func (s *InboundService) AddInboundClient(inbound *model.Inbound) error {
|
||||
existEmail, err := s.checkEmailExistForInbound(inbound)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if existEmail != "" {
|
||||
return common.NewError("Duplicate email:", existEmail)
|
||||
}
|
||||
|
||||
clients, err := s.getClients(inbound)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
oldInbound, err := s.GetInbound(inbound.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
oldInbound.Settings = inbound.Settings
|
||||
|
||||
if len(clients[len(clients)-1].Email) > 0 {
|
||||
s.AddClientStat(inbound.Id, &clients[len(clients)-1])
|
||||
}
|
||||
db := database.GetDB()
|
||||
return db.Save(oldInbound).Error
|
||||
}
|
||||
|
||||
func (s *InboundService) DelInboundClient(inboundId int, index int) error {
|
||||
oldInbound, err := s.GetInbound(inboundId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
settings := map[string][]model.Client{}
|
||||
json.Unmarshal([]byte(oldInbound.Settings), &settings)
|
||||
clients := settings["clients"]
|
||||
email := clients[index].Email
|
||||
settings["clients"] = append(clients[:index], clients[index+1:]...)
|
||||
|
||||
newSetting, err := json.Marshal(settings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
oldInbound.Settings = string(newSetting)
|
||||
|
||||
db := database.GetDB()
|
||||
err = s.DelClientStat(db, email)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return db.Save(oldInbound).Error
|
||||
}
|
||||
|
||||
func (s *InboundService) UpdateInboundClient(inbound *model.Inbound, index int) error {
|
||||
existEmail, err := s.checkEmailExistForInbound(inbound)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existEmail != "" {
|
||||
return common.NewError("Duplicate email:", existEmail)
|
||||
}
|
||||
|
||||
clients, err := s.getClients(inbound)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
oldInbound, err := s.GetInbound(inbound.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
oldClients, err := s.getClients(oldInbound)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
oldInbound.Settings = inbound.Settings
|
||||
|
||||
db := database.GetDB()
|
||||
|
||||
if len(clients[index].Email) > 0 {
|
||||
if len(oldClients[index].Email) > 0 {
|
||||
s.UpdateClientStat(oldClients[index].Email, &clients[index])
|
||||
} else {
|
||||
s.AddClientStat(inbound.Id, &clients[index])
|
||||
}
|
||||
} else {
|
||||
s.DelClientStat(db, oldClients[index].Email)
|
||||
}
|
||||
return db.Save(oldInbound).Error
|
||||
}
|
||||
|
||||
func (s *InboundService) AddTraffic(traffics []*xray.Traffic) (err error) {
|
||||
if len(traffics) == 0 {
|
||||
return nil
|
||||
@@ -297,7 +399,7 @@ func (s *InboundService) AddClientTraffic(traffics []*xray.ClientTraffic) (err e
|
||||
traffic.Total = client.TotalGB
|
||||
}
|
||||
}
|
||||
if tx.Where("inbound_id = ?", inbound.Id).Where("email = ?", traffic.Email).
|
||||
if tx.Where("inbound_id = ? and email = ?", inbound.Id, traffic.Email).
|
||||
UpdateColumns(map[string]interface{}{
|
||||
"enable": true,
|
||||
"expiry_time": traffic.ExpiryTime,
|
||||
@@ -336,33 +438,37 @@ func (s *InboundService) DisableInvalidClients() (int64, error) {
|
||||
count := result.RowsAffected
|
||||
return count, err
|
||||
}
|
||||
func (s *InboundService) UpdateClientStat(inboundId int, inboundSettings string) error {
|
||||
func (s *InboundService) AddClientStat(inboundId int, client *model.Client) error {
|
||||
db := database.GetDB()
|
||||
|
||||
// get settings clients
|
||||
settings := map[string][]model.Client{}
|
||||
json.Unmarshal([]byte(inboundSettings), &settings)
|
||||
clients := settings["clients"]
|
||||
for _, client := range clients {
|
||||
result := db.Model(xray.ClientTraffic{}).
|
||||
Where("inbound_id = ? and email = ?", inboundId, client.Email).
|
||||
Updates(map[string]interface{}{"enable": true, "total": client.TotalGB, "expiry_time": client.ExpiryTime})
|
||||
if result.RowsAffected == 0 {
|
||||
clientTraffic := xray.ClientTraffic{}
|
||||
clientTraffic.InboundId = inboundId
|
||||
clientTraffic.Email = client.Email
|
||||
clientTraffic.Total = client.TotalGB
|
||||
clientTraffic.ExpiryTime = client.ExpiryTime
|
||||
clientTraffic.Enable = true
|
||||
clientTraffic.Up = 0
|
||||
clientTraffic.Down = 0
|
||||
db.Create(&clientTraffic)
|
||||
}
|
||||
err := result.Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
clientTraffic := xray.ClientTraffic{}
|
||||
clientTraffic.InboundId = inboundId
|
||||
clientTraffic.Email = client.Email
|
||||
clientTraffic.Total = client.TotalGB
|
||||
clientTraffic.ExpiryTime = client.ExpiryTime
|
||||
clientTraffic.Enable = true
|
||||
clientTraffic.Up = 0
|
||||
clientTraffic.Down = 0
|
||||
result := db.Create(&clientTraffic)
|
||||
err := result.Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (s *InboundService) UpdateClientStat(email string, client *model.Client) error {
|
||||
db := database.GetDB()
|
||||
|
||||
result := db.Model(xray.ClientTraffic{}).
|
||||
Where("email = ?", email).
|
||||
Updates(map[string]interface{}{
|
||||
"enable": true,
|
||||
"email": client.Email,
|
||||
"total": client.TotalGB,
|
||||
"expiry_time": client.ExpiryTime})
|
||||
err := result.Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -375,7 +481,7 @@ func (s *InboundService) ResetClientTraffic(clientEmail string) error {
|
||||
|
||||
result := db.Model(xray.ClientTraffic{}).
|
||||
Where("email = ?", clientEmail).
|
||||
Updates(map[string]interface{}{"up": 0, "down": 0})
|
||||
Updates(map[string]interface{}{"enable": true, "up": 0, "down": 0})
|
||||
|
||||
err := result.Error
|
||||
|
||||
|
||||
@@ -160,5 +160,5 @@ func (s *XrayService) SetToNeedRestart() {
|
||||
}
|
||||
|
||||
func (s *XrayService) IsNeedRestartAndSetFalse() bool {
|
||||
return isNeedXrayRestart.CAS(true, false)
|
||||
return isNeedXrayRestart.CompareAndSwap(true, false)
|
||||
}
|
||||
|
||||
@@ -127,6 +127,13 @@
|
||||
"clickOnQRcode" = "Click on QR Code to Copy"
|
||||
"client" = "Client"
|
||||
|
||||
[pages.client]
|
||||
"add" = "Add client"
|
||||
"edit" = "Edit client"
|
||||
"submitAdd" = "Add client"
|
||||
"submitEdit" = "Save changes"
|
||||
"clientCount" = "Number of clients"
|
||||
|
||||
[pages.inbounds.toasts]
|
||||
"obtain" = "Obtain"
|
||||
|
||||
|
||||
@@ -127,6 +127,13 @@
|
||||
"clickOnQRcode" = "برای کپی بر روی کد تصویری کلیک کنید"
|
||||
"client" = "کاربر"
|
||||
|
||||
[pages.client]
|
||||
"add" = "کاربر جدید"
|
||||
"edit" = "ویرایش کاربر"
|
||||
"submitAdd" = "اضافه کردن"
|
||||
"submitEdit" = "ذخیره تغییرات"
|
||||
"clientCount" = "تعداد کاربران"
|
||||
|
||||
[pages.inbounds.toasts]
|
||||
"obtain" = "Obtain"
|
||||
|
||||
|
||||
@@ -127,6 +127,13 @@
|
||||
"clickOnQRcode" = "点击二维码复制"
|
||||
"client" = "客户"
|
||||
|
||||
[pages.client]
|
||||
"add" = "添加客户端"
|
||||
"edit" = "编辑客户"
|
||||
"submitAdd" = "添加客户端"
|
||||
"submitEdit" = "保存修改"
|
||||
"clientCount" = "客户数量"
|
||||
|
||||
[pages.inbounds.toasts]
|
||||
"obtain" = "获取"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user