mirror of
https://github.com/alireza0/x-ui.git
synced 2026-03-20 15:55:48 +00:00
Compare commits
20 Commits
0.5.1
...
1.0.0_beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
772a1b341e | ||
|
|
5923c765a9 | ||
|
|
d7074cc3c9 | ||
|
|
f4f8c0d6df | ||
|
|
0c755be648 | ||
|
|
c8dc4a96b4 | ||
|
|
8d9f6ccc11 | ||
|
|
3d7a8e372e | ||
|
|
2fe4f1ad07 | ||
|
|
04095b6ec4 | ||
|
|
6d404e4b27 | ||
|
|
f9f8431dd2 | ||
|
|
f996b9ee2b | ||
|
|
a3e1ee1f35 | ||
|
|
e16391ad05 | ||
|
|
48b543becb | ||
|
|
80f052697c | ||
|
|
fb15248030 | ||
|
|
6a18ba48aa | ||
|
|
c13b7b2a18 |
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
|||||||
mv xui-release x-ui
|
mv xui-release x-ui
|
||||||
mkdir bin
|
mkdir bin
|
||||||
cd bin
|
cd bin
|
||||||
wget https://github.com/xtls/xray-core/releases/latest/download/Xray-linux-64.zip
|
wget https://github.com/XTLS/Xray-core/releases/download/v1.8.1/Xray-linux-64.zip
|
||||||
unzip Xray-linux-64.zip
|
unzip Xray-linux-64.zip
|
||||||
rm -f Xray-linux-64.zip geoip.dat geosite.dat
|
rm -f Xray-linux-64.zip geoip.dat geosite.dat
|
||||||
wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat
|
wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat
|
||||||
@@ -66,7 +66,7 @@ jobs:
|
|||||||
mv xui-release x-ui
|
mv xui-release x-ui
|
||||||
mkdir bin
|
mkdir bin
|
||||||
cd bin
|
cd bin
|
||||||
wget https://github.com/xtls/xray-core/releases/latest/download/Xray-linux-arm64-v8a.zip
|
wget https://github.com/xtls/xray-core/releases/download/v1.8.1/Xray-linux-arm64-v8a.zip
|
||||||
unzip Xray-linux-arm64-v8a.zip
|
unzip Xray-linux-arm64-v8a.zip
|
||||||
rm -f Xray-linux-arm64-v8a.zip geoip.dat geosite.dat
|
rm -f Xray-linux-arm64-v8a.zip geoip.dat geosite.dat
|
||||||
wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat
|
wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat
|
||||||
@@ -106,7 +106,7 @@ jobs:
|
|||||||
mv xui-release x-ui
|
mv xui-release x-ui
|
||||||
mkdir bin
|
mkdir bin
|
||||||
cd bin
|
cd bin
|
||||||
wget https://github.com/xtls/xray-core/releases/latest/download/Xray-linux-s390x.zip
|
wget https://github.com/xtls/xray-core/releases/download/v1.8.1/Xray-linux-s390x.zip
|
||||||
unzip Xray-linux-s390x.zip
|
unzip Xray-linux-s390x.zip
|
||||||
rm -f Xray-linux-s390x.zip geoip.dat geosite.dat
|
rm -f Xray-linux-s390x.zip geoip.dat geosite.dat
|
||||||
wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat
|
wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat
|
||||||
|
|||||||
@@ -11,6 +11,12 @@ ENV TZ=Asia/Tehran
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
RUN apk add ca-certificates tzdata && mkdir bin
|
RUN apk add ca-certificates tzdata && mkdir bin
|
||||||
|
|
||||||
|
# Download latest rule files
|
||||||
|
ADD https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat \
|
||||||
|
https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat \
|
||||||
|
bin/
|
||||||
|
|
||||||
COPY --from=builder /app/main /app/x-ui
|
COPY --from=builder /app/main /app/x-ui
|
||||||
VOLUME [ "/etc/x-ui" ]
|
VOLUME [ "/etc/x-ui" ]
|
||||||
CMD [ "./x-ui" ]
|
CMD [ "./x-ui" ]
|
||||||
96
README.md
96
README.md
@@ -1,27 +1,29 @@
|
|||||||
# x-ui
|
# x-ui
|
||||||

|
|
||||||
|

|
||||||

|

|
||||||
[](https://goreportcard.com/report/github.com/alireza0/x-ui)
|
[](https://goreportcard.com/report/github.com/alireza0/x-ui)
|
||||||
[](https://img.shields.io/github/downloads/alireza0/x-ui/total.svg)
|
[](https://img.shields.io/github/downloads/alireza0/x-ui/total.svg)
|
||||||
[](https://www.gnu.org/licenses/gpl-3.0.en.html)
|
[](https://www.gnu.org/licenses/gpl-3.0.en.html)
|
||||||
|
|
||||||
> **Disclaimer: This project is only for personal learning and communication, please do not use it for illegal purposes, please do not use it in a production environment**
|
> **Disclaimer: This project is only for personal learning and communication, please do not use it for illegal purposes, please do not use it in a production environment**
|
||||||
|
|
||||||
xray panel supporting multi-protocol, **Multi-lang (English,Farsi,Chinese)**
|
xray panel supporting multi-protocol, **Multi-lang (English,Farsi,Chinese)**
|
||||||
|
|
||||||
| Features | Enable? |
|
| Features | Enable? |
|
||||||
| ------------- |:-------------:|
|
| ------------------------------------ | :----------------: |
|
||||||
| Multi-lang | :heavy_check_mark: |
|
| Multi-lang | :heavy_check_mark: |
|
||||||
| Dark/Light Theme | :heavy_check_mark: |
|
| Dark/Light Theme | :heavy_check_mark: |
|
||||||
| Search in deep | :heavy_check_mark: |
|
| Search in deep | :heavy_check_mark: |
|
||||||
| Inbound Multi User | :heavy_check_mark: |
|
| Inbound Multi User | :heavy_check_mark: |
|
||||||
| Multi User Traffic & Expiration time | :heavy_check_mark: |
|
| Multi User Traffic & Expiration time | :heavy_check_mark: |
|
||||||
| REST API | :heavy_check_mark: |
|
| REST API | :heavy_check_mark: |
|
||||||
| Telegram BOT (admin + clients) | :heavy_check_mark: |
|
| Telegram BOT (admin + clients) | :heavy_check_mark: |
|
||||||
| Backup database using Telegram BOT | :heavy_check_mark: |
|
| Backup database using Telegram BOT | :heavy_check_mark: |
|
||||||
| Subscription link | :heavy_check_mark: |
|
| Subscription link | :heavy_check_mark: |
|
||||||
| Calculate expire date on first usage | :heavy_check_mark: |
|
| Calculate expire date on first usage | :heavy_check_mark: |
|
||||||
|
|
||||||
**If you think this project is helpful to you, you may wish to give a** :star2:
|
**If you think this project is helpful to you, you may wish to give a** :star2:
|
||||||
|
|
||||||
# Features
|
# Features
|
||||||
|
|
||||||
@@ -44,19 +46,28 @@ xray panel supporting multi-protocol, **Multi-lang (English,Farsi,Chinese)**
|
|||||||
- `/login` with `PUSH` user data: `{username: '', password: ''}` for login
|
- `/login` with `PUSH` user data: `{username: '', password: ''}` for login
|
||||||
- `/xui/API/inbounds` base for following actions:
|
- `/xui/API/inbounds` base for following actions:
|
||||||
|
|
||||||
| Method | Path | Action |
|
| Method | Path | Action |
|
||||||
| ------------- | ------------- | ------------- |
|
| ------ | -------------------------------- | ------------------------------------------- |
|
||||||
| GET | "/" | Get all inbounds |
|
| GET | "/" | Get all inbounds |
|
||||||
| GET | "/get/:id" | Get inbound with inbound.id |
|
| GET | "/get/:id" | Get inbound with inbound.id |
|
||||||
| POST | "/add" | Add inbound |
|
| POST | "/add" | Add inbound |
|
||||||
| POST | "/del/:id" | Delete Inbound |
|
| POST | "/del/:id" | Delete Inbound |
|
||||||
| POST | "/update/:id" | Update Inbound |
|
| POST | "/update/:id" | Update Inbound |
|
||||||
| POST | "/addClient/" | Add Client to inbound |
|
| POST | "/addClient/" | Add Client to inbound |
|
||||||
| POST | "/delClient/:email" | Delete Client |
|
| POST | "/delClient/:email" | Delete Client |
|
||||||
| POST | "/updateClient/:index" | Update Client |
|
| POST | "/updateClient/:index" | Update Client |
|
||||||
| POST | "/:id/resetClientTraffic/:email" | Reset Client's Traffic |
|
| POST | "/:id/resetClientTraffic/:email" | Reset Client's Traffic |
|
||||||
| POST | "/resetAllTraffics" | Reset traffics of all inbounds |
|
| POST | "/resetAllTraffics" | Reset traffics of all inbounds |
|
||||||
| POST | "/resetAllClientTraffics/:id" | Reset traffics of all clients in an inbound |
|
| POST | "/resetAllClientTraffics/:id" | Reset traffics of all clients in an inbound |
|
||||||
|
|
||||||
|
# Environment Variables
|
||||||
|
|
||||||
|
| Variable | Type | Default |
|
||||||
|
| -------------- | :--------------------------------------------: | :------------ |
|
||||||
|
| XUI_LOG_LEVEL | `"debug"` \| `"info"` \| `"warn"` \| `"error"` | `"info"` |
|
||||||
|
| XUI_DEBUG | `boolean` | `false` |
|
||||||
|
| XUI_BIN_FOLDER | `string` | `"bin"` |
|
||||||
|
| XUI_DB_FOLDER | `string` | `"/etc/x-ui"` |
|
||||||
|
|
||||||
# Screenshot from Inbouds page
|
# Screenshot from Inbouds page
|
||||||
|
|
||||||
@@ -77,9 +88,9 @@ bash <(curl -Ls https://raw.githubusercontent.com/alireza0/x-ui/master/install.s
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Install custom version
|
## Install custom version
|
||||||
To install your desired version you can add the version to the end of install command. Example for ver `0.5.1`:
|
To install your desired version you can add the version to the end of install command. Example for ver `0.5.2`:
|
||||||
```
|
```
|
||||||
bash <(curl -Ls https://raw.githubusercontent.com/alireza0/x-ui/master/install.sh) 0.5.1
|
bash <(curl -Ls https://raw.githubusercontent.com/alireza0/x-ui/master/install.sh) 0.5.2
|
||||||
```
|
```
|
||||||
|
|
||||||
## Manual install & upgrade
|
## Manual install & upgrade
|
||||||
@@ -130,13 +141,10 @@ docker build -t x-ui .
|
|||||||
```
|
```
|
||||||
|
|
||||||
## SSL certificate application
|
## SSL certificate application
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Click for details</summary>
|
<summary>Click for details</summary>
|
||||||
|
|
||||||
### Cloudflare
|
|
||||||
|
|
||||||
> This feature and tutorial are provided by [FranzKafkaYu](https://github.com/FranzKafkaYu)
|
|
||||||
|
|
||||||
### Certbot
|
### Certbot
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -146,14 +154,14 @@ ln -s /snap/bin/certbot /usr/bin/certbot
|
|||||||
|
|
||||||
certbot certonly --standalone --register-unsafely-without-email --non-interactive --agree-tos -d <Your Domain Name>
|
certbot certonly --standalone --register-unsafely-without-email --non-interactive --agree-tos -d <Your Domain Name>
|
||||||
```
|
```
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
## Tg robot use
|
## Tg robot use
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Click for details</summary>
|
<summary>Click for details</summary>
|
||||||
|
|
||||||
> This feature and tutorial are provided by [FranzKafkaYu](https://github.com/FranzKafkaYu)
|
|
||||||
|
|
||||||
X-UI supports daily traffic notification, panel login reminder and other functions through the Tg robot. To use the Tg robot, you need to apply for the specific application tutorial. You can refer to the [blog](https://coderfan.net/how-to-use-telegram-bot-to-alarm-you-when-someone-login-into-your-vps.html)
|
X-UI supports daily traffic notification, panel login reminder and other functions through the Tg robot. To use the Tg robot, you need to apply for the specific application tutorial. You can refer to the [blog](https://coderfan.net/how-to-use-telegram-bot-to-alarm-you-when-someone-login-into-your-vps.html)
|
||||||
Set the robot-related parameters in the panel background, including:
|
Set the robot-related parameters in the panel background, including:
|
||||||
|
|
||||||
@@ -167,8 +175,8 @@ Set the robot-related parameters in the panel background, including:
|
|||||||
|
|
||||||
Reference syntax:
|
Reference syntax:
|
||||||
|
|
||||||
- 30 * * * * * //Notify at the 30s of each point
|
- 30 \* \* \* \* \* //Notify at the 30s of each point
|
||||||
- 0 */10 * * * * //Notify at the first second of each 10 minutes
|
- 0 */10 \* \* \* \* //Notify at the first second of each 10 minutes
|
||||||
- @hourly // hourly notification
|
- @hourly // hourly notification
|
||||||
- @daily // Daily notification (00:00 in the morning)
|
- @daily // Daily notification (00:00 in the morning)
|
||||||
- @every 8h // notify every 8 hours
|
- @every 8h // notify every 8 hours
|
||||||
@@ -190,6 +198,7 @@ Reference syntax:
|
|||||||
</details>
|
</details>
|
||||||
|
|
||||||
# Common problem
|
# Common problem
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Click for details</summary>
|
<summary>Click for details</summary>
|
||||||
## Migrating from v2-ui
|
## Migrating from v2-ui
|
||||||
@@ -206,12 +215,15 @@ x-ui v2-ui
|
|||||||
|
|
||||||
**If you upgrade from an old version or other forks, for enable traffic for users you should do :**
|
**If you upgrade from an old version or other forks, for enable traffic for users you should do :**
|
||||||
|
|
||||||
find this in config :
|
find this in config :
|
||||||
``` json
|
|
||||||
|
```json
|
||||||
"policy": {
|
"policy": {
|
||||||
"system": {
|
"system": {
|
||||||
```
|
```
|
||||||
**and add this just after ` "policy": {` :**
|
|
||||||
|
**and add this just after ` "policy": {` :**
|
||||||
|
|
||||||
```json
|
```json
|
||||||
"levels": {
|
"levels": {
|
||||||
"0": {
|
"0": {
|
||||||
@@ -222,6 +234,7 @@ find this in config :
|
|||||||
```
|
```
|
||||||
|
|
||||||
**the final output is like :**
|
**the final output is like :**
|
||||||
|
|
||||||
```json
|
```json
|
||||||
"policy": {
|
"policy": {
|
||||||
"levels": {
|
"levels": {
|
||||||
@@ -238,12 +251,15 @@ find this in config :
|
|||||||
},
|
},
|
||||||
"routing": {
|
"routing": {
|
||||||
```
|
```
|
||||||
restart panel
|
|
||||||
|
restart panel
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
# a special thanks to
|
# a special thanks to
|
||||||
|
|
||||||
- [HexaSoftwareTech](https://github.com/HexaSoftwareTech/)
|
- [HexaSoftwareTech](https://github.com/HexaSoftwareTech/)
|
||||||
- [MHSanaei](https://github.com/MHSanaei)
|
- [MHSanaei](https://github.com/MHSanaei)
|
||||||
|
|
||||||
## Stargazers over time
|
## Stargazers over time
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,22 @@ func IsDebug() bool {
|
|||||||
return os.Getenv("XUI_DEBUG") == "true"
|
return os.Getenv("XUI_DEBUG") == "true"
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetDBPath() string {
|
func GetBinFolderPath() string {
|
||||||
return fmt.Sprintf("/etc/%s/%s.db", GetName(), GetName())
|
binFolderPath := os.Getenv("XUI_BIN_FOLDER")
|
||||||
|
if binFolderPath == "" {
|
||||||
|
binFolderPath = "bin"
|
||||||
|
}
|
||||||
|
return binFolderPath
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDBFolderPath() string {
|
||||||
|
dbFolderPath := os.Getenv("XUI_DB_FOLDER")
|
||||||
|
if dbFolderPath == "" {
|
||||||
|
dbFolderPath = "/etc/x-ui"
|
||||||
|
}
|
||||||
|
return dbFolderPath
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDBPath() string {
|
||||||
|
return fmt.Sprintf("%s/%s.db", GetDBFolderPath(), GetName())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
0.5.1
|
1.0.0_beta1
|
||||||
2
main.go
2
main.go
@@ -217,7 +217,7 @@ func main() {
|
|||||||
|
|
||||||
v2uiCmd := flag.NewFlagSet("v2-ui", flag.ExitOnError)
|
v2uiCmd := flag.NewFlagSet("v2-ui", flag.ExitOnError)
|
||||||
var dbPath string
|
var dbPath string
|
||||||
v2uiCmd.StringVar(&dbPath, "db", "/etc/v2-ui/v2-ui.db", "set v2-ui db file path")
|
v2uiCmd.StringVar(&dbPath, "db", fmt.Sprintf("%s/v2-ui.db", config.GetDBFolderPath()), "set v2-ui db file path")
|
||||||
|
|
||||||
settingCmd := flag.NewFlagSet("setting", flag.ExitOnError)
|
settingCmd := flag.NewFlagSet("setting", flag.ExitOnError)
|
||||||
var port int
|
var port int
|
||||||
|
|||||||
@@ -246,6 +246,11 @@
|
|||||||
background-color: #2e3b52;
|
background-color: #2e3b52;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ant-card-dark .ant-select-disabled .ant-select-selection {
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
background-color: #242c3a;
|
||||||
|
}
|
||||||
|
|
||||||
.ant-card-dark .ant-collapse-item {
|
.ant-card-dark .ant-collapse-item {
|
||||||
color: hsla(0,0%,100%,.65);
|
color: hsla(0,0%,100%,.65);
|
||||||
background-color: #161b22;
|
background-color: #161b22;
|
||||||
|
|||||||
@@ -43,13 +43,9 @@ const RULE_DOMAIN = {
|
|||||||
SPEEDTEST: 'geosite:speedtest',
|
SPEEDTEST: 'geosite:speedtest',
|
||||||
};
|
};
|
||||||
|
|
||||||
const XTLS_FLOW_CONTROL = {
|
|
||||||
ORIGIN: "xtls-rprx-origin",
|
|
||||||
DIRECT: "xtls-rprx-direct",
|
|
||||||
};
|
|
||||||
|
|
||||||
const TLS_FLOW_CONTROL = {
|
const TLS_FLOW_CONTROL = {
|
||||||
VISION: "xtls-rprx-vision",
|
VISION: "xtls-rprx-vision",
|
||||||
|
VISION_UDP443: "xtls-rprx-vision-udp443",
|
||||||
};
|
};
|
||||||
|
|
||||||
const TLS_VERSION_OPTION = {
|
const TLS_VERSION_OPTION = {
|
||||||
@@ -103,7 +99,6 @@ Object.freeze(VmessMethods);
|
|||||||
Object.freeze(SSMethods);
|
Object.freeze(SSMethods);
|
||||||
Object.freeze(RULE_IP);
|
Object.freeze(RULE_IP);
|
||||||
Object.freeze(RULE_DOMAIN);
|
Object.freeze(RULE_DOMAIN);
|
||||||
Object.freeze(XTLS_FLOW_CONTROL);
|
|
||||||
Object.freeze(TLS_FLOW_CONTROL);
|
Object.freeze(TLS_FLOW_CONTROL);
|
||||||
Object.freeze(TLS_VERSION_OPTION);
|
Object.freeze(TLS_VERSION_OPTION);
|
||||||
Object.freeze(TLS_CIPHER_OPTION);
|
Object.freeze(TLS_CIPHER_OPTION);
|
||||||
@@ -479,7 +474,7 @@ class TlsStreamSettings extends XrayCommonClass {
|
|||||||
cipherSuites = '',
|
cipherSuites = '',
|
||||||
certificates=[new TlsStreamSettings.Cert()],
|
certificates=[new TlsStreamSettings.Cert()],
|
||||||
alpn=[],
|
alpn=[],
|
||||||
settings=[new TlsStreamSettings.Settings()]) {
|
settings=new TlsStreamSettings.Settings()) {
|
||||||
super();
|
super();
|
||||||
this.server = serverName;
|
this.server = serverName;
|
||||||
this.minVersion = minVersion;
|
this.minVersion = minVersion;
|
||||||
@@ -506,8 +501,7 @@ class TlsStreamSettings extends XrayCommonClass {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!ObjectUtil.isEmpty(json.settings)) {
|
if (!ObjectUtil.isEmpty(json.settings)) {
|
||||||
let values = json.settings[0];
|
settings = new TlsStreamSettings.Settings(json.settings.allowInsecure , json.settings.fingerprint, json.settings.serverName);
|
||||||
settings = [new TlsStreamSettings.Settings(values.allowInsecure , values.fingerprint, values.serverName)];
|
|
||||||
}
|
}
|
||||||
return new TlsStreamSettings(
|
return new TlsStreamSettings(
|
||||||
json.serverName,
|
json.serverName,
|
||||||
@@ -528,7 +522,7 @@ class TlsStreamSettings extends XrayCommonClass {
|
|||||||
cipherSuites: this.cipherSuites,
|
cipherSuites: this.cipherSuites,
|
||||||
certificates: TlsStreamSettings.toJsonArray(this.certs),
|
certificates: TlsStreamSettings.toJsonArray(this.certs),
|
||||||
alpn: this.alpn,
|
alpn: this.alpn,
|
||||||
settings: TlsStreamSettings.toJsonArray(this.settings),
|
settings: this.settings,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -597,10 +591,92 @@ TlsStreamSettings.Settings = class extends XrayCommonClass {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class RealityStreamSettings extends XrayCommonClass {
|
||||||
|
constructor(show = false, xver = 0,
|
||||||
|
dest = 'microsoft.com:443',
|
||||||
|
serverNames = 'microsoft.com,www.microsoft.com',
|
||||||
|
privateKey = '', minClient = '', maxClient = '',
|
||||||
|
maxTimediff = 0, shortIds = [],
|
||||||
|
settings= new RealityStreamSettings.Settings()) {
|
||||||
|
super();
|
||||||
|
this.show = show;
|
||||||
|
this.xver = xver;
|
||||||
|
this.dest = dest;
|
||||||
|
this.serverNames = serverNames instanceof Array ? serverNames.join(",") : serverNames;
|
||||||
|
this.privateKey = privateKey;
|
||||||
|
this.minClient = minClient;
|
||||||
|
this.maxClient = maxClient;
|
||||||
|
this.maxTimediff = maxTimediff;
|
||||||
|
this.shortIds = shortIds instanceof Array ? shortIds.join(",") : shortIds;
|
||||||
|
this.settings = settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromJson(json = {}) {
|
||||||
|
let settings;
|
||||||
|
if (!ObjectUtil.isEmpty(json.settings)) {
|
||||||
|
settings = new RealityStreamSettings.Settings(json.settings.publicKey , json.settings.fingerprint, json.settings.serverName, json.settings.spiderX);
|
||||||
|
}
|
||||||
|
return new RealityStreamSettings(
|
||||||
|
json.show,
|
||||||
|
json.xver,
|
||||||
|
json.dest,
|
||||||
|
json.serverNames,
|
||||||
|
json.privateKey,
|
||||||
|
json.minClient,
|
||||||
|
json.maxClient,
|
||||||
|
json.maxTimediff,
|
||||||
|
json.shortIds,
|
||||||
|
json.settings,
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
toJson() {
|
||||||
|
return {
|
||||||
|
show: this.show,
|
||||||
|
xver: this.xver,
|
||||||
|
dest: this.dest,
|
||||||
|
serverNames: this.serverNames.split(","),
|
||||||
|
privateKey: this.privateKey,
|
||||||
|
minClient: this.minClient,
|
||||||
|
maxClient: this.maxClient,
|
||||||
|
maxTimediff: this.maxTimediff,
|
||||||
|
shortIds: this.shortIds.split(","),
|
||||||
|
settings: this.settings,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RealityStreamSettings.Settings = class extends XrayCommonClass {
|
||||||
|
constructor(publicKey = '', fingerprint = '', serverName = '', spiderX= '/') {
|
||||||
|
super();
|
||||||
|
this.publicKey = publicKey;
|
||||||
|
this.fingerprint = fingerprint;
|
||||||
|
this.serverName = serverName;
|
||||||
|
this.spiderX = spiderX;
|
||||||
|
}
|
||||||
|
static fromJson(json = {}) {
|
||||||
|
return new RealityStreamSettings.Settings(
|
||||||
|
json.publicKey,
|
||||||
|
json.fingerprint,
|
||||||
|
json.serverName,
|
||||||
|
json.spiderX,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
toJson() {
|
||||||
|
return {
|
||||||
|
publicKey: this.publicKey,
|
||||||
|
fingerprint: this.fingerprint,
|
||||||
|
serverName: this.serverName,
|
||||||
|
spiderX: this.spiderX,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class StreamSettings extends XrayCommonClass {
|
class StreamSettings extends XrayCommonClass {
|
||||||
constructor(network='tcp',
|
constructor(network='tcp',
|
||||||
security='none',
|
security='none',
|
||||||
tlsSettings=new TlsStreamSettings(),
|
tlsSettings=new TlsStreamSettings(),
|
||||||
|
realitySettings = new RealityStreamSettings(),
|
||||||
tcpSettings=new TcpStreamSettings(),
|
tcpSettings=new TcpStreamSettings(),
|
||||||
kcpSettings=new KcpStreamSettings(),
|
kcpSettings=new KcpStreamSettings(),
|
||||||
wsSettings=new WsStreamSettings(),
|
wsSettings=new WsStreamSettings(),
|
||||||
@@ -612,6 +688,7 @@ class StreamSettings extends XrayCommonClass {
|
|||||||
this.network = network;
|
this.network = network;
|
||||||
this.security = security;
|
this.security = security;
|
||||||
this.tls = tlsSettings;
|
this.tls = tlsSettings;
|
||||||
|
this.reality = realitySettings;
|
||||||
this.tcp = tcpSettings;
|
this.tcp = tcpSettings;
|
||||||
this.kcp = kcpSettings;
|
this.kcp = kcpSettings;
|
||||||
this.ws = wsSettings;
|
this.ws = wsSettings;
|
||||||
@@ -632,29 +709,25 @@ class StreamSettings extends XrayCommonClass {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get isXTls() {
|
get isReality() {
|
||||||
return this.security === "xtls";
|
return this.security === "reality";
|
||||||
}
|
}
|
||||||
|
|
||||||
set isXTls(isXTls) {
|
set isReality(isReality) {
|
||||||
if (isXTls) {
|
if (isReality) {
|
||||||
this.security = 'xtls';
|
this.security = 'reality';
|
||||||
} else {
|
} else {
|
||||||
this.security = 'none';
|
this.security = 'none';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json={}) {
|
static fromJson(json={}) {
|
||||||
let tls;
|
|
||||||
if (json.security === "xtls") {
|
|
||||||
tls = TlsStreamSettings.fromJson(json.xtlsSettings);
|
|
||||||
} else {
|
|
||||||
tls = TlsStreamSettings.fromJson(json.tlsSettings);
|
|
||||||
}
|
|
||||||
return new StreamSettings(
|
return new StreamSettings(
|
||||||
json.network,
|
json.network,
|
||||||
json.security,
|
json.security,
|
||||||
tls,
|
TlsStreamSettings.fromJson(json.tlsSettings),
|
||||||
|
RealityStreamSettings.fromJson(json.realitySettings),
|
||||||
TcpStreamSettings.fromJson(json.tcpSettings),
|
TcpStreamSettings.fromJson(json.tcpSettings),
|
||||||
KcpStreamSettings.fromJson(json.kcpSettings),
|
KcpStreamSettings.fromJson(json.kcpSettings),
|
||||||
WsStreamSettings.fromJson(json.wsSettings),
|
WsStreamSettings.fromJson(json.wsSettings),
|
||||||
@@ -670,7 +743,7 @@ class StreamSettings extends XrayCommonClass {
|
|||||||
network: network,
|
network: network,
|
||||||
security: this.security,
|
security: this.security,
|
||||||
tlsSettings: this.isTls ? this.tls.toJson() : undefined,
|
tlsSettings: this.isTls ? this.tls.toJson() : undefined,
|
||||||
xtlsSettings: this.isXTls ? this.tls.toJson() : undefined,
|
realitySettings: this.isReality ? this.reality.toJson() : undefined,
|
||||||
tcpSettings: network === 'tcp' ? this.tcp.toJson() : undefined,
|
tcpSettings: network === 'tcp' ? this.tcp.toJson() : undefined,
|
||||||
kcpSettings: network === 'kcp' ? this.kcp.toJson() : undefined,
|
kcpSettings: network === 'kcp' ? this.kcp.toJson() : undefined,
|
||||||
wsSettings: network === 'ws' ? this.ws.toJson() : undefined,
|
wsSettings: network === 'ws' ? this.ws.toJson() : undefined,
|
||||||
@@ -750,13 +823,13 @@ class Inbound extends XrayCommonClass {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get xtls() {
|
get reality() {
|
||||||
return this.stream.security === 'xtls';
|
return this.stream.security === 'reality';
|
||||||
}
|
}
|
||||||
|
|
||||||
set xtls(isXTls) {
|
set reality(isReality) {
|
||||||
if (isXTls) {
|
if (isReality) {
|
||||||
this.stream.security = 'xtls';
|
this.stream.security = 'reality';
|
||||||
} else {
|
} else {
|
||||||
this.stream.security = 'none';
|
this.stream.security = 'none';
|
||||||
}
|
}
|
||||||
@@ -865,7 +938,7 @@ class Inbound extends XrayCommonClass {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get serverName() {
|
get serverName() {
|
||||||
if (this.stream.isTls || this.stream.isXTls) {
|
if (this.stream.isTls || this.stream.isReality) {
|
||||||
return this.stream.tls.server;
|
return this.stream.tls.server;
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
@@ -961,7 +1034,7 @@ class Inbound extends XrayCommonClass {
|
|||||||
|
|
||||||
//this is used for xtls-rprx-vision
|
//this is used for xtls-rprx-vision
|
||||||
canEnableTlsFlow() {
|
canEnableTlsFlow() {
|
||||||
if ((this.stream.security === 'tls') && (this.network === "tcp")) {
|
if ((this.stream.security != 'none') && (this.network === "tcp")) {
|
||||||
switch (this.protocol) {
|
switch (this.protocol) {
|
||||||
case Protocols.VLESS:
|
case Protocols.VLESS:
|
||||||
return true;
|
return true;
|
||||||
@@ -976,7 +1049,7 @@ class Inbound extends XrayCommonClass {
|
|||||||
return this.canEnableTls();
|
return this.canEnableTls();
|
||||||
}
|
}
|
||||||
|
|
||||||
canEnableXTls() {
|
canEnableReality() {
|
||||||
switch (this.protocol) {
|
switch (this.protocol) {
|
||||||
case Protocols.VLESS:
|
case Protocols.VLESS:
|
||||||
case Protocols.TROJAN:
|
case Protocols.TROJAN:
|
||||||
@@ -984,7 +1057,15 @@ class Inbound extends XrayCommonClass {
|
|||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return this.network === "tcp";
|
|
||||||
|
switch (this.network) {
|
||||||
|
case "tcp":
|
||||||
|
case "http":
|
||||||
|
case "grpc":
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
canEnableStream() {
|
canEnableStream() {
|
||||||
@@ -1081,10 +1162,10 @@ class Inbound extends XrayCommonClass {
|
|||||||
host: host,
|
host: host,
|
||||||
path: path,
|
path: path,
|
||||||
tls: this.stream.security,
|
tls: this.stream.security,
|
||||||
sni: this.stream.tls.settings[0]['serverName'],
|
sni: this.stream.tls.settings.serverName,
|
||||||
fp: this.stream.tls.settings[0]['fingerprint'],
|
fp: this.stream.tls.settings.fingerprint,
|
||||||
alpn: this.stream.tls.alpn.join(','),
|
alpn: this.stream.tls.alpn.join(','),
|
||||||
allowInsecure: this.stream.tls.settings[0].allowInsecure,
|
allowInsecure: this.stream.tls.settings.allowInsecure,
|
||||||
};
|
};
|
||||||
return 'vmess://' + base64(JSON.stringify(obj, null, 2));
|
return 'vmess://' + base64(JSON.stringify(obj, null, 2));
|
||||||
}
|
}
|
||||||
@@ -1143,32 +1224,43 @@ class Inbound extends XrayCommonClass {
|
|||||||
|
|
||||||
if (this.tls) {
|
if (this.tls) {
|
||||||
params.set("security", "tls");
|
params.set("security", "tls");
|
||||||
params.set("fp" , this.stream.tls.settings[0]['fingerprint']);
|
params.set("fp" , this.stream.tls.settings.fingerprint);
|
||||||
params.set("alpn", this.stream.tls.alpn);
|
params.set("alpn", this.stream.tls.alpn);
|
||||||
if(this.stream.tls.settings[0].allowInsecure){
|
if(this.stream.tls.settings.allowInsecure){
|
||||||
params.set("allowInsecure", "1");
|
params.set("allowInsecure", "1");
|
||||||
}
|
}
|
||||||
if (!ObjectUtil.isEmpty(this.stream.tls.server)) {
|
if (!ObjectUtil.isEmpty(this.stream.tls.server)) {
|
||||||
address = this.stream.tls.server;
|
address = this.stream.tls.server;
|
||||||
}
|
}
|
||||||
if (this.stream.tls.settings[0]['serverName'] !== ''){
|
if (this.stream.tls.settings.serverName !== ''){
|
||||||
params.set("sni", this.stream.tls.settings[0]['serverName']);
|
params.set("sni", this.stream.tls.settings.serverName);
|
||||||
}
|
}
|
||||||
if (type === "tcp" && this.settings.vlesses[clientIndex].flow.length > 0) {
|
if (type === "tcp" && this.settings.vlesses[clientIndex].flow.length > 0) {
|
||||||
params.set("flow", this.settings.vlesses[clientIndex].flow);
|
params.set("flow", this.settings.vlesses[clientIndex].flow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.xtls) {
|
if (this.reality) {
|
||||||
params.set("security", "xtls");
|
params.set("security", "reality");
|
||||||
params.set("alpn", this.stream.tls.alpn);
|
params.set("pbk", this.stream.reality.settings.publicKey);
|
||||||
if(this.stream.tls.settings[0].allowInsecure){
|
if (!ObjectUtil.isArrEmpty(this.stream.reality.serverNames)) {
|
||||||
params.set("allowInsecure", "1");
|
params.set("sni", this.stream.reality.serverNames.split(",")[0]);
|
||||||
}
|
}
|
||||||
if (!ObjectUtil.isEmpty(this.stream.tls.server)) {
|
if (this.stream.reality.shortIds != "") {
|
||||||
address = this.stream.tls.server;
|
params.set("sid", this.stream.reality.shortIds.split(",")[0]);
|
||||||
|
}
|
||||||
|
if (this.stream.reality.fingerprint != "") {
|
||||||
|
params.set("fp", this.stream.reality.settings.fingerprint);
|
||||||
|
}
|
||||||
|
if (!ObjectUtil.isEmpty(this.stream.reality.settings.serverName)) {
|
||||||
|
address = this.stream.reality.settings.serverName;
|
||||||
|
}
|
||||||
|
if (!ObjectUtil.isEmpty(this.stream.reality.settings.spiderX)) {
|
||||||
|
params.set("spx", this.stream.reality.settings.spiderX);
|
||||||
|
}
|
||||||
|
if (this.stream.network === 'tcp') {
|
||||||
|
params.set("flow", this.settings.vlesses[clientIndex].flow);
|
||||||
}
|
}
|
||||||
params.set("flow", this.settings.vlesses[clientIndex].flow);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const link = `vless://${uuid}@${address}:${port}`;
|
const link = `vless://${uuid}@${address}:${port}`;
|
||||||
@@ -1247,29 +1339,40 @@ class Inbound extends XrayCommonClass {
|
|||||||
|
|
||||||
if (this.tls) {
|
if (this.tls) {
|
||||||
params.set("security", "tls");
|
params.set("security", "tls");
|
||||||
params.set("fp" , this.stream.tls.settings[0]['fingerprint']);
|
params.set("fp" , this.stream.tls.settings.fingerprint);
|
||||||
params.set("alpn", this.stream.tls.alpn);
|
params.set("alpn", this.stream.tls.alpn);
|
||||||
if(this.stream.tls.settings[0].allowInsecure){
|
if(this.stream.tls.settings.allowInsecure){
|
||||||
params.set("allowInsecure", "1");
|
params.set("allowInsecure", "1");
|
||||||
}
|
}
|
||||||
if (!ObjectUtil.isEmpty(this.stream.tls.server)) {
|
if (!ObjectUtil.isEmpty(this.stream.tls.server)) {
|
||||||
address = this.stream.tls.server;
|
address = this.stream.tls.server;
|
||||||
}
|
}
|
||||||
if (this.stream.tls.settings[0]['serverName'] !== ''){
|
if (this.stream.tls.settings.serverName !== ''){
|
||||||
params.set("sni", this.stream.tls.settings[0]['serverName']);
|
params.set("sni", this.stream.tls.settings.serverName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.xtls) {
|
if (this.reality) {
|
||||||
params.set("security", "xtls");
|
params.set("security", "reality");
|
||||||
params.set("alpn", this.stream.tls.alpn);
|
params.set("pbk", this.stream.reality.settings.publicKey);
|
||||||
if(this.stream.tls.settings[0].allowInsecure){
|
if (!ObjectUtil.isArrEmpty(this.stream.reality.serverNames)) {
|
||||||
params.set("allowInsecure", "1");
|
params.set("sni", this.stream.reality.serverNames.split(",")[0]);
|
||||||
|
}
|
||||||
|
if (this.stream.reality.shortIds != "") {
|
||||||
|
params.set("sid", this.stream.reality.shortIds.split(",")[0]);
|
||||||
|
}
|
||||||
|
if (this.stream.reality.fingerprint != "") {
|
||||||
|
params.set("fp", this.stream.reality.settings.fingerprint);
|
||||||
|
}
|
||||||
|
if (!ObjectUtil.isEmpty(this.stream.reality.settings.serverName)) {
|
||||||
|
address = this.stream.reality.settings.serverName;
|
||||||
|
}
|
||||||
|
if (!ObjectUtil.isEmpty(this.stream.reality.settings.spiderX)) {
|
||||||
|
params.set("spx", this.stream.reality.settings.spiderX);
|
||||||
|
}
|
||||||
|
if (this.stream.network === 'tcp') {
|
||||||
|
params.set("flow", this.settings.vlesses[clientIndex].flow);
|
||||||
}
|
}
|
||||||
if (!ObjectUtil.isEmpty(this.stream.tls.server)) {
|
|
||||||
address = this.stream.tls.server;
|
|
||||||
}
|
|
||||||
params.set("flow", this.settings.trojans[clientIndex].flow);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const link = `trojan://${settings.trojans[clientIndex].password}@${address}:${this.port}#${encodeURIComponent(remark)}`;
|
const link = `trojan://${settings.trojans[clientIndex].password}@${address}:${this.port}#${encodeURIComponent(remark)}`;
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ func (a *InboundController) initRouter(g *gin.RouterGroup) {
|
|||||||
g.POST("/add", a.addInbound)
|
g.POST("/add", a.addInbound)
|
||||||
g.POST("/del/:id", a.delInbound)
|
g.POST("/del/:id", a.delInbound)
|
||||||
g.POST("/update/:id", a.updateInbound)
|
g.POST("/update/:id", a.updateInbound)
|
||||||
g.POST("/addClient/", a.addInboundClient)
|
g.POST("/addClient", a.addInboundClient)
|
||||||
g.POST("/delClient/:email", a.delInboundClient)
|
g.POST("/delClient/:email", a.delInboundClient)
|
||||||
g.POST("/updateClient/:index", a.updateInboundClient)
|
g.POST("/updateClient/:index", a.updateInboundClient)
|
||||||
g.POST("/:id/resetClientTraffic/:email", a.resetClientTraffic)
|
g.POST("/:id/resetClientTraffic/:email", a.resetClientTraffic)
|
||||||
@@ -129,19 +129,19 @@ func (a *InboundController) updateInbound(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *InboundController) addInboundClient(c *gin.Context) {
|
func (a *InboundController) addInboundClient(c *gin.Context) {
|
||||||
inbound := &model.Inbound{}
|
data := &model.Inbound{}
|
||||||
err := c.ShouldBind(inbound)
|
err := c.ShouldBind(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
jsonMsg(c, I18n(c, "pages.inbounds.revise"), err)
|
jsonMsg(c, I18n(c, "pages.inbounds.revise"), err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.inboundService.AddInboundClient(inbound)
|
err = a.inboundService.AddInboundClient(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
jsonMsg(c, "something worng!", err)
|
jsonMsg(c, "something worng!", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
jsonMsg(c, "Client added", nil)
|
jsonMsg(c, "Client(s) added", nil)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
a.xrayService.SetToNeedRestart()
|
a.xrayService.SetToNeedRestart()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ func (a *ServerController) initRouter(g *gin.RouterGroup) {
|
|||||||
g.POST("/logs/:count", a.getLogs)
|
g.POST("/logs/:count", a.getLogs)
|
||||||
g.POST("/getConfigJson", a.getConfigJson)
|
g.POST("/getConfigJson", a.getConfigJson)
|
||||||
g.GET("/getDb", a.getDb)
|
g.GET("/getDb", a.getDb)
|
||||||
|
g.POST("/getNewX25519Cert", a.getNewX25519Cert)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *ServerController) refreshStatus() {
|
func (a *ServerController) refreshStatus() {
|
||||||
@@ -114,7 +115,7 @@ func (a *ServerController) getLogs(c *gin.Context) {
|
|||||||
count := c.Param("count")
|
count := c.Param("count")
|
||||||
logs, err := a.serverService.GetLogs(count)
|
logs, err := a.serverService.GetLogs(count)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
jsonMsg(c, I18n(c, "getLogs"), err)
|
jsonMsg(c, "getLogs", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
jsonObj(c, logs, nil)
|
jsonObj(c, logs, nil)
|
||||||
@@ -123,7 +124,7 @@ func (a *ServerController) getLogs(c *gin.Context) {
|
|||||||
func (a *ServerController) getConfigJson(c *gin.Context) {
|
func (a *ServerController) getConfigJson(c *gin.Context) {
|
||||||
configJson, err := a.serverService.GetConfigJson()
|
configJson, err := a.serverService.GetConfigJson()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
jsonMsg(c, I18n(c, "getLogs"), err)
|
jsonMsg(c, "get config.json", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
jsonObj(c, configJson, nil)
|
jsonObj(c, configJson, nil)
|
||||||
@@ -132,13 +133,22 @@ func (a *ServerController) getConfigJson(c *gin.Context) {
|
|||||||
func (a *ServerController) getDb(c *gin.Context) {
|
func (a *ServerController) getDb(c *gin.Context) {
|
||||||
db, err := a.serverService.GetDb()
|
db, err := a.serverService.GetDb()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
jsonMsg(c, I18n(c, "getLogs"), err)
|
jsonMsg(c, "get Database", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Set the headers for the response
|
// Set the headers for the response
|
||||||
c.Header("Content-Type", "application/octet-stream")
|
c.Header("Content-Type", "application/octet-stream")
|
||||||
c.Header("Content-Disposition", "attachment; filename=xui.db")
|
c.Header("Content-Disposition", "attachment; filename=x-ui.db")
|
||||||
|
|
||||||
// Write the file contents to the response
|
// Write the file contents to the response
|
||||||
c.Writer.Write(db)
|
c.Writer.Write(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *ServerController) getNewX25519Cert(c *gin.Context) {
|
||||||
|
cert, err := a.serverService.GetNewX25519Cert()
|
||||||
|
if err != nil {
|
||||||
|
jsonMsg(c, "get x25519 certificate", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
jsonObj(c, cert, nil)
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,73 +4,140 @@
|
|||||||
:class="siderDrawer.isDarkTheme ? darkClass : ''"
|
:class="siderDrawer.isDarkTheme ? darkClass : ''"
|
||||||
:ok-text="clientsBulkModal.okText" cancel-text='{{ i18n "close" }}'>
|
:ok-text="clientsBulkModal.okText" cancel-text='{{ i18n "close" }}'>
|
||||||
<a-form layout="inline">
|
<a-form layout="inline">
|
||||||
<a-form-item label='{{ i18n "pages.client.method" }}'>
|
<table width="100%" class="ant-table-tbody">
|
||||||
<a-select v-model="clientsBulkModal.emailMethod" buttonStyle="solid" style="width: 350px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
<tr>
|
||||||
<a-select-option :value="0">Random</a-select-option>
|
<td>{{ i18n "pages.client.method" }}</td>
|
||||||
<a-select-option :value="1">Random+Prefix</a-select-option>
|
<td>
|
||||||
<a-select-option :value="2">Random+Prefix+Num</a-select-option>
|
<a-form-item>
|
||||||
<a-select-option :value="3">Random+Prefix+Num+Postfix</a-select-option>
|
<a-select v-model="clientsBulkModal.emailMethod" buttonStyle="solid" style="width: 250px"
|
||||||
<a-select-option :value="4">Prefix+Num+Postfix [ BE CAREFUL! ]</a-select-option>
|
:dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
||||||
</a-select>
|
<a-select-option :value="0">Random</a-select-option>
|
||||||
</a-form-item><br />
|
<a-select-option :value="1">Random+Prefix</a-select-option>
|
||||||
<a-form-item v-if="clientsBulkModal.emailMethod>1">
|
<a-select-option :value="2">Random+Prefix+Num</a-select-option>
|
||||||
<span slot="label">{{ i18n "pages.client.first" }}</span>
|
<a-select-option :value="3">Random+Prefix+Num+Postfix</a-select-option>
|
||||||
<a-input-number v-model="clientsBulkModal.firstNum" :min="1"></a-input-number>
|
<a-select-option :value="4">Prefix+Num+Postfix</a-select-option>
|
||||||
</a-form-item>
|
</a-select>
|
||||||
<a-form-item v-if="clientsBulkModal.emailMethod>1">
|
</a-form-item>
|
||||||
<span slot="label">{{ i18n "pages.client.last" }}</span>
|
</td>
|
||||||
<a-input-number v-model="clientsBulkModal.lastNum" :min="clientsBulkModal.firstNum"></a-input-number>
|
</tr>
|
||||||
</a-form-item>
|
<tr v-if="clientsBulkModal.emailMethod>1">
|
||||||
<a-form-item v-if="clientsBulkModal.emailMethod>0">
|
<td>{{ i18n "pages.client.first" }}</td>
|
||||||
<span slot="label">{{ i18n "pages.client.prefix" }}</span>
|
<td>
|
||||||
<a-input v-model="clientsBulkModal.emailPrefix" style="width: 120px"></a-input>
|
<a-form-item>
|
||||||
</a-form-item>
|
<a-input-number v-model="clientsBulkModal.firstNum" :min="1"></a-input-number>
|
||||||
<a-form-item v-if="clientsBulkModal.emailMethod>2">
|
</a-form-item>
|
||||||
<span slot="label">{{ i18n "pages.client.postfix" }}</span>
|
</td>
|
||||||
<a-input v-model="clientsBulkModal.emailPostfix" style="width: 120px"></a-input>
|
</tr>
|
||||||
</a-form-item>
|
<tr v-if="clientsBulkModal.emailMethod>1">
|
||||||
<a-form-item v-if="clientsBulkModal.emailMethod < 2">
|
<td>{{ i18n "pages.client.last" }}</td>
|
||||||
<span slot="label">{{ i18n "pages.client.clientCount" }}</span>
|
<td>
|
||||||
<a-input-number v-model="clientsBulkModal.quantity" :min="1" :max="100"></a-input-number>
|
<a-form-item>
|
||||||
</a-form-item>
|
<a-input-number v-model="clientsBulkModal.lastNum" :min="clientsBulkModal.firstNum"></a-input-number>
|
||||||
<a-form-item label="Subscription">
|
</a-form-item>
|
||||||
<a-input v-model.trim="clientsBulkModal.subId"></a-input>
|
</td>
|
||||||
</a-form-item>
|
</tr>
|
||||||
<a-form-item label="Telegram ID">
|
<tr v-if="clientsBulkModal.emailMethod>0">
|
||||||
<a-input v-model.trim="clientsBulkModal.tgId"></a-input>
|
<td>{{ i18n "pages.client.prefix" }}</td>
|
||||||
</a-form-item>
|
<td>
|
||||||
<a-form-item>
|
<a-form-item>
|
||||||
<span slot="label">
|
<a-input v-model="clientsBulkModal.emailPrefix" style="width: 250px"></a-input>
|
||||||
<span >{{ i18n "pages.inbounds.totalFlow" }}</span>(GB)
|
</a-form-item>
|
||||||
<a-tooltip>
|
</td>
|
||||||
<template slot="title">
|
</tr>
|
||||||
0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
|
<tr v-if="clientsBulkModal.emailMethod>2">
|
||||||
</template>
|
<td>{{ i18n "pages.client.postfix" }}</td>
|
||||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
<td>
|
||||||
</a-tooltip>
|
<a-form-item>
|
||||||
</span>
|
<a-input v-model="clientsBulkModal.emailPostfix" style="width: 250px"></a-input>
|
||||||
<a-input-number v-model="clientsBulkModal.totalGB" :min="0"></a-input-number>
|
</a-form-item>
|
||||||
</a-form-item>
|
</td>
|
||||||
<a-form-item label="{{ i18n "pages.client.delayedStart" }}">
|
</tr>
|
||||||
<a-switch v-model="clientsBulkModal.delayedStart" @click="clientsBulkModal.expiryTime=0"></a-switch>
|
<tr v-if="clientsBulkModal.emailMethod < 2">
|
||||||
</a-form-item>
|
<td>{{ i18n "pages.client.clientCount" }}</td>
|
||||||
<a-form-item label="{{ i18n "pages.client.expireDays" }}" v-if="clientsBulkModal.delayedStart">
|
<td>
|
||||||
<a-input type="number" v-model.number="delayedExpireDays" :min="0"></a-input>
|
<a-form-item>
|
||||||
</a-form-item>
|
<a-input-number v-model="clientsBulkModal.quantity" :min="1" :max="100"></a-input-number>
|
||||||
<a-form-item v-else>
|
</a-form-item>
|
||||||
<span slot="label">
|
</td>
|
||||||
<span >{{ i18n "pages.inbounds.expireDate" }}</span>
|
</tr>
|
||||||
<a-tooltip>
|
<tr v-if="clientsBulkModal.inbound.canEnableTlsFlow()">
|
||||||
<template slot="title">
|
<td>Flow</td>
|
||||||
<span>{{ i18n "pages.inbounds.leaveBlankToNeverExpire" }}</span>
|
<td>
|
||||||
</template>
|
<a-form-item>
|
||||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
<a-select v-model="clientsBulkModal.flow" style="width: 250px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
||||||
</a-tooltip>
|
<a-select-option value="" selected>{{ i18n "none" }}</a-select-option>
|
||||||
</span>
|
<a-select-option v-for="key in TLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
||||||
<a-date-picker :show-time="{ format: 'HH:mm' }" format="YYYY-MM-DD HH:mm"
|
</a-select>
|
||||||
:dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''"
|
</a-form-item>
|
||||||
v-model="clientsBulkModal.expiryTime" style="width: 300px;"></a-date-picker>
|
</td>
|
||||||
</a-form-item>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Subscription</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-input v-model.trim="clientsBulkModal.subId" style="width: 250px"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Telegram Username</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-input v-model.trim="clientsBulkModal.tgId" style="width: 250px"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<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>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-input-number v-model="clientsBulkModal.totalGB" :min="0"></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ i18n "pages.client.delayedStart" }}</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-switch v-model="clientsBulkModal.delayedStart" @click="clientsBulkModal.expiryTime=0"></a-switch>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr v-if="clientsBulkModal.delayedStart">
|
||||||
|
<td>{{ i18n "pages.client.expireDays" }}</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-input-number v-model.number="delayedExpireDays" :min="0"></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr v-else>
|
||||||
|
<td>
|
||||||
|
<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>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-date-picker :show-time="{ format: 'HH:mm' }" format="YYYY-MM-DD HH:mm"
|
||||||
|
:dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''"
|
||||||
|
v-model="clientsBulkModal.expiryTime" style="width: 250px;"></a-date-picker>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
</a-form>
|
</a-form>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
<script>
|
<script>
|
||||||
@@ -83,7 +150,6 @@
|
|||||||
confirm: null,
|
confirm: null,
|
||||||
dbInbound: new DBInbound(),
|
dbInbound: new DBInbound(),
|
||||||
inbound: new Inbound(),
|
inbound: new Inbound(),
|
||||||
clients: [],
|
|
||||||
quantity: 1,
|
quantity: 1,
|
||||||
totalGB: 0,
|
totalGB: 0,
|
||||||
expiryTime: '',
|
expiryTime: '',
|
||||||
@@ -94,8 +160,10 @@
|
|||||||
emailPostfix: "",
|
emailPostfix: "",
|
||||||
subId: "",
|
subId: "",
|
||||||
tgId: "",
|
tgId: "",
|
||||||
|
flow: "",
|
||||||
delayedStart: false,
|
delayedStart: false,
|
||||||
ok() {
|
ok() {
|
||||||
|
clients = [];
|
||||||
method=clientsBulkModal.emailMethod;
|
method=clientsBulkModal.emailMethod;
|
||||||
if(method>1){
|
if(method>1){
|
||||||
start=clientsBulkModal.firstNum;
|
start=clientsBulkModal.firstNum;
|
||||||
@@ -115,9 +183,12 @@
|
|||||||
newClient.tgId = clientsBulkModal.tgId;
|
newClient.tgId = clientsBulkModal.tgId;
|
||||||
newClient._totalGB = clientsBulkModal.totalGB;
|
newClient._totalGB = clientsBulkModal.totalGB;
|
||||||
newClient._expiryTime = clientsBulkModal.expiryTime;
|
newClient._expiryTime = clientsBulkModal.expiryTime;
|
||||||
clientsBulkModal.clients.push(newClient);
|
if(clientsBulkModal.inbound.canEnableTlsFlow()){
|
||||||
|
newClient.flow = clientsBulkModal.flow;
|
||||||
|
}
|
||||||
|
clients.push(newClient);
|
||||||
}
|
}
|
||||||
ObjectUtil.execute(clientsBulkModal.confirm, clientsBulkModal.inbound, clientsBulkModal.dbInbound);
|
ObjectUtil.execute(clientsBulkModal.confirm, clients, clientsBulkModal.dbInbound.id);
|
||||||
},
|
},
|
||||||
show({ title='', okText='{{ i18n "sure" }}', dbInbound=null, confirm=(inbound, dbInbound)=>{} }) {
|
show({ title='', okText='{{ i18n "sure" }}', dbInbound=null, confirm=(inbound, dbInbound)=>{} }) {
|
||||||
this.visible = true;
|
this.visible = true;
|
||||||
@@ -134,9 +205,9 @@
|
|||||||
this.emailPostfix= "";
|
this.emailPostfix= "";
|
||||||
this.subId= "";
|
this.subId= "";
|
||||||
this.tgId= "";
|
this.tgId= "";
|
||||||
|
this.flow= "";
|
||||||
this.dbInbound = new DBInbound(dbInbound);
|
this.dbInbound = new DBInbound(dbInbound);
|
||||||
this.inbound = dbInbound.toInbound();
|
this.inbound = dbInbound.toInbound();
|
||||||
this.clients = this.getClients(this.inbound.protocol, this.inbound.settings);
|
|
||||||
this.delayedStart = false;
|
this.delayedStart = false;
|
||||||
},
|
},
|
||||||
getClients(protocol, clientSettings) {
|
getClients(protocol, clientSettings) {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
confirmLoading: false,
|
confirmLoading: false,
|
||||||
title: '',
|
title: '',
|
||||||
okText: '',
|
okText: '',
|
||||||
|
isEdit: false,
|
||||||
dbInbound: new DBInbound(),
|
dbInbound: new DBInbound(),
|
||||||
inbound: new Inbound(),
|
inbound: new Inbound(),
|
||||||
clients: [],
|
clients: [],
|
||||||
@@ -20,9 +21,13 @@
|
|||||||
isExpired: false,
|
isExpired: false,
|
||||||
delayedStart: false,
|
delayedStart: false,
|
||||||
ok() {
|
ok() {
|
||||||
ObjectUtil.execute(clientModal.confirm, clientModal.inbound, clientModal.dbInbound, clientModal.index);
|
if(clientModal.isEdit){
|
||||||
|
ObjectUtil.execute(clientModal.confirm, clientModalApp.client, clientModal.dbInbound.id, clientModal.index);
|
||||||
|
} else {
|
||||||
|
ObjectUtil.execute(clientModal.confirm, clientModalApp.client, clientModal.dbInbound.id);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
show({ title='', okText='{{ i18n "sure" }}', index=null, dbInbound=null, confirm=(index, dbInbound)=>{}, isEdit=false }) {
|
show({ title='', okText='{{ i18n "sure" }}', index=null, dbInbound=null, confirm=()=>{}, isEdit=false }) {
|
||||||
this.visible = true;
|
this.visible = true;
|
||||||
this.title = title;
|
this.title = title;
|
||||||
this.okText = okText;
|
this.okText = okText;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
<a-input :value="value" @input="$emit('input', $event.target.value)"></a-input>
|
<a-input :value="value" @input="$emit('input', $event.target.value)"></a-input>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="type === 'number'">
|
<template v-else-if="type === 'number'">
|
||||||
<a-input type="number" :value="value" @input="$emit('input', $event.target.value)"></a-input>
|
<a-input-number :value="value" @input="$emit('input', $event.target.value)"></a-input-number>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="type === 'textarea'">
|
<template v-else-if="type === 'textarea'">
|
||||||
<a-textarea :value="value" @input="$emit('input', $event.target.value)" :auto-size="{ minRows: 10, maxRows: 10 }"></a-textarea>
|
<a-textarea :value="value" @input="$emit('input', $event.target.value)" :auto-size="{ minRows: 10, maxRows: 10 }"></a-textarea>
|
||||||
|
|||||||
@@ -3,88 +3,143 @@
|
|||||||
<template v-if="isEdit">
|
<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>
|
<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>
|
</template>
|
||||||
<a-form-item>
|
<table width="100%" class="ant-table-tbody">
|
||||||
<span slot="label">
|
<tr>
|
||||||
<span>{{ i18n "pages.inbounds.Email" }}</span>
|
<td>{{ i18n "pages.inbounds.enable" }}</td>
|
||||||
<a-tooltip>
|
<td>
|
||||||
<template slot="title">
|
<a-form-item>
|
||||||
<span>{{ i18n "pages.inbounds.EmailDesc" }}</span>
|
<a-switch v-model="client.enable"></a-switch>
|
||||||
</template>
|
</a-form-item>
|
||||||
<a-icon type="sync" @click="getNewEmail(client)"></a-icon>
|
</td>
|
||||||
</a-tooltip>
|
</tr>
|
||||||
</span>
|
<tr>
|
||||||
<a-input v-model.trim="client.email"></a-input>
|
<td>
|
||||||
</a-form-item>
|
<span>{{ i18n "pages.inbounds.Email" }}</span>
|
||||||
<a-form-item label="{{ i18n "pages.inbounds.enable" }}">
|
<a-tooltip>
|
||||||
<a-switch v-model="client.enable"></a-switch>
|
<template slot="title">
|
||||||
</a-form-item>
|
<span>{{ i18n "pages.inbounds.EmailDesc" }}</span>
|
||||||
<a-form-item label="password" v-if="inbound.protocol === Protocols.TROJAN">
|
</template>
|
||||||
<a-input v-model.trim="client.password"></a-input>
|
<a-icon type="sync" @click="getNewEmail(client)"></a-icon>
|
||||||
</a-form-item>
|
</a-tooltip>
|
||||||
<a-form-item label="id" v-if="inbound.protocol === Protocols.VMESS || inbound.protocol === Protocols.VLESS">
|
</td>
|
||||||
<a-input v-model.trim="client.id"></a-input>
|
<td>
|
||||||
</a-form-item>
|
<a-form-item>
|
||||||
<a-form-item label='{{ i18n "additional" }} ID' v-if="inbound.protocol === Protocols.VMESS">
|
<a-input v-model.trim="client.email" style="width: 250px"></a-input>
|
||||||
<a-input type="number" v-model.number="client.alterId"></a-input>
|
</a-form-item>
|
||||||
</a-form-item>
|
</td>
|
||||||
<a-form-item label="Subscription" v-if="client.email">
|
</tr>
|
||||||
<a-input v-model.trim="client.subId"></a-input>
|
<tr v-if="inbound.protocol === Protocols.TROJAN">
|
||||||
</a-form-item>
|
<td>password</td>
|
||||||
<a-form-item label="Telegram Username" v-if="client.email">
|
<td>
|
||||||
<a-input v-model.trim="client.tgId"></a-input>
|
<a-form-item>
|
||||||
</a-form-item>
|
<a-input v-model.trim="client.password" style="width: 250px"></a-input>
|
||||||
<a-form-item v-if="inbound.xtls" label="flow">
|
</a-form-item>
|
||||||
<a-select v-model="client.flow" style="width: 150px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
</td>
|
||||||
<a-select-option value="">{{ i18n "none" }}</a-select-option>
|
</tr>
|
||||||
<a-select-option v-for="key in XTLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
<tr v-if="inbound.protocol === Protocols.VMESS || inbound.protocol === Protocols.VLESS">
|
||||||
</a-select>
|
<td>ID</td>
|
||||||
</a-form-item>
|
<td>
|
||||||
<a-form-item v-else-if="inbound.canEnableTlsFlow()" label="flow" layout="inline">
|
<a-form-item>
|
||||||
<a-select v-model="client.flow" style="width: 150px">
|
<a-input v-model.trim="client.id" style="width: 250px"></a-input>
|
||||||
<a-select-option value="" selected>{{ i18n "none" }}</a-select-option>
|
</a-form-item>
|
||||||
<a-select-option v-for="key in TLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
</td>
|
||||||
</a-select>
|
</tr>
|
||||||
</a-form-item>
|
<tr v-if="inbound.protocol === Protocols.VMESS">
|
||||||
<a-form-item>
|
<td>{{ i18n "additional" }}</td>
|
||||||
<span slot="label">
|
<td>
|
||||||
<span >{{ i18n "pages.inbounds.totalFlow" }}</span>(GB)
|
<a-form-item>
|
||||||
<a-tooltip>
|
<a-input-number v-model.number="client.alterId"></a-input-number>
|
||||||
<template slot="title">
|
</a-form-item>
|
||||||
0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
|
</td>
|
||||||
</template>
|
</tr>
|
||||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
<tr v-if="client.email">
|
||||||
</a-tooltip>
|
<td>Subscription</td>
|
||||||
</span>
|
<td>
|
||||||
<a-input-number v-model="client._totalGB" :min="0"></a-input-number>
|
<a-form-item>
|
||||||
<template v-if="isEdit && clientStats">
|
<a-input v-model.trim="client.subId" style="width: 250px"></a-input>
|
||||||
<span>{{ i18n "usage" }}:</span>
|
</a-form-item>
|
||||||
<a-tag :color="statsColor">
|
</td>
|
||||||
[[ sizeFormat(clientStats.up) ]] /
|
</tr>
|
||||||
[[ sizeFormat(clientStats.down) ]]
|
<tr v-if="client.email">
|
||||||
([[ sizeFormat(clientStats.up + clientStats.down) ]])
|
<td>Telegram Username</td>
|
||||||
</a-tag>
|
<td>
|
||||||
</template>
|
<a-form-item>
|
||||||
</a-form-item>
|
<a-input v-model.trim="client.tgId" style="width: 250px"></a-input>
|
||||||
<a-form-item label="{{ i18n "pages.client.delayedStart" }}">
|
</a-form-item>
|
||||||
<a-switch v-model="clientModal.delayedStart" @click="client._expiryTime=0"></a-switch>
|
</td>
|
||||||
</a-form-item>
|
</tr>
|
||||||
<a-form-item label="{{ i18n "pages.client.expireDays" }}" v-if="clientModal.delayedStart">
|
<tr v-if="inbound.canEnableTlsFlow()">
|
||||||
<a-input type="number" v-model.number="delayedExpireDays" :min="0"></a-input>
|
<td>Flow</td>
|
||||||
</a-form-item>
|
<td>
|
||||||
<a-form-item v-else>
|
<a-form-item>
|
||||||
<span slot="label">
|
<a-select v-model="client.flow" style="width: 250px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
||||||
<span >{{ i18n "pages.inbounds.expireDate" }}</span>
|
<a-select-option value="" selected>{{ i18n "none" }}</a-select-option>
|
||||||
<a-tooltip>
|
<a-select-option v-for="key in TLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
||||||
<template slot="title">
|
</a-select>
|
||||||
<span>{{ i18n "pages.inbounds.leaveBlankToNeverExpire" }}</span>
|
</a-form-item>
|
||||||
</template>
|
</td>
|
||||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
</tr>
|
||||||
</a-tooltip>
|
<tr>
|
||||||
</span>
|
<td>
|
||||||
<a-date-picker :show-time="{ format: 'HH:mm' }" format="YYYY-MM-DD HH:mm"
|
<span >{{ i18n "pages.inbounds.totalFlow" }}</span>(GB)
|
||||||
:dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''"
|
<a-tooltip>
|
||||||
v-model="client._expiryTime" style="width: 300px;"></a-date-picker>
|
<template slot="title">
|
||||||
<a-tag color="red" v-if="isExpiry">Expired</a-tag>
|
0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
|
||||||
</a-form-item>
|
</template>
|
||||||
|
<a-icon type="question-circle" theme="filled"></a-icon>
|
||||||
|
</a-tooltip>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-input-number v-model="client._totalGB" :min="0"></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr v-if="isEdit && clientStats">
|
||||||
|
<td>{{ i18n "usage" }}</td>
|
||||||
|
<td>
|
||||||
|
<a-tag :color="statsColor">
|
||||||
|
[[ sizeFormat(clientStats.up) ]] /
|
||||||
|
[[ sizeFormat(clientStats.down) ]]
|
||||||
|
([[ sizeFormat(clientStats.up + clientStats.down) ]])
|
||||||
|
</a-tag>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ i18n "pages.client.delayedStart" }}</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-switch v-model="clientModal.delayedStart" @click="client._expiryTime=0"></a-switch>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr v-if="clientModal.delayedStart">
|
||||||
|
<td>{{ i18n "pages.client.expireDays" }}</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-input-number v-model.number="delayedExpireDays" :min="0"></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr v-else>
|
||||||
|
<td>
|
||||||
|
<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>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-date-picker :show-time="{ format: 'HH:mm' }" format="YYYY-MM-DD HH:mm"
|
||||||
|
:dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''"
|
||||||
|
v-model="client._expiryTime" style="width: 250px;"></a-date-picker>
|
||||||
|
<a-tag color="red" v-if="isExpiry">Expired</a-tag>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
</a-form>
|
</a-form>
|
||||||
{{end}}
|
{{end}}
|
||||||
@@ -1,58 +1,91 @@
|
|||||||
{{define "form/inbound"}}
|
{{define "form/inbound"}}
|
||||||
<!-- base -->
|
<!-- base -->
|
||||||
<a-form layout="inline">
|
<a-form layout="inline">
|
||||||
<a-form-item label='{{ i18n "remark" }}'>
|
<table width="100%" class="ant-table-tbody">
|
||||||
<a-input v-model.trim="dbInbound.remark"></a-input>
|
<tr>
|
||||||
</a-form-item>
|
<td>{{ i18n "enable" }}</td>
|
||||||
<a-form-item label='{{ i18n "enable" }}'>
|
<td>
|
||||||
<a-switch v-model="dbInbound.enable"></a-switch>
|
<a-form-item>
|
||||||
</a-form-item>
|
<a-switch v-model="dbInbound.enable"></a-switch>
|
||||||
<a-form-item label='{{ i18n "protocol" }}'>
|
</a-form-item>
|
||||||
<a-select v-model="inbound.protocol" style="width: 160px;" :disabled="isEdit" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
</td>
|
||||||
<a-select-option v-for="p in Protocols" :key="p" :value="p">[[ p ]]</a-select-option>
|
</tr>
|
||||||
</a-select>
|
<tr>
|
||||||
</a-form-item>
|
<td>{{ i18n "remark" }}</td>
|
||||||
<a-form-item>
|
<td>
|
||||||
<span slot="label">
|
<a-form-item>
|
||||||
{{ i18n "monitor" }}
|
<a-input v-model.trim="dbInbound.remark" style="width: 250px;"></a-input>
|
||||||
<a-tooltip>
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ i18n "protocol" }}</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-select v-model="inbound.protocol" style="width: 250px;" :disabled="isEdit" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
||||||
|
<a-select-option v-for="p in Protocols" :key="p" :value="p">[[ p ]]</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ i18n "monitor" }}
|
||||||
|
<a-tooltip>
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
<span>{{ i18n "pages.inbounds.monitorDesc" }}</span>
|
<span>{{ i18n "pages.inbounds.monitorDesc" }}</span>
|
||||||
</template>
|
</template>
|
||||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
<a-icon type="question-circle" theme="filled"></a-icon>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</span>
|
</td>
|
||||||
<a-input v-model.trim="inbound.listen"></a-input>
|
<td>
|
||||||
</a-form-item>
|
<a-form-item>
|
||||||
<a-form-item label='{{ i18n "pages.inbounds.port" }}'>
|
<a-input v-model.trim="inbound.listen" style="width: 250px;"></a-input>
|
||||||
<a-input type="number" v-model.number="inbound.port"></a-input>
|
</a-form-item>
|
||||||
</a-form-item>
|
</td>
|
||||||
<a-form-item>
|
</tr>
|
||||||
<span slot="label">
|
<tr>
|
||||||
<span >{{ i18n "pages.inbounds.totalFlow" }}</span>(GB)
|
<td>{{ i18n "pages.inbounds.port" }}</td>
|
||||||
<a-tooltip>
|
<td>
|
||||||
<template slot="title">
|
<a-form-item>
|
||||||
0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
|
<a-input-number v-model.number="inbound.port"></a-input-number>
|
||||||
</template>
|
</a-form-item>
|
||||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
</td>
|
||||||
</a-tooltip>
|
</tr>
|
||||||
</span>
|
<tr>
|
||||||
<a-input-number v-model="dbInbound.totalGB" :min="0"></a-input-number>
|
<td>
|
||||||
</a-form-item>
|
<span >{{ i18n "pages.inbounds.totalFlow" }}</span>(GB)
|
||||||
<a-form-item>
|
<a-tooltip>
|
||||||
<span slot="label">
|
<template slot="title">
|
||||||
<span >{{ i18n "pages.inbounds.expireDate" }}</span>
|
0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
|
||||||
<a-tooltip>
|
</template>
|
||||||
<template slot="title">
|
<a-icon type="question-circle" theme="filled"></a-icon>
|
||||||
<span>{{ i18n "pages.inbounds.leaveBlankToNeverExpire" }}</span>
|
</a-tooltip>
|
||||||
</template>
|
</td>
|
||||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
<td>
|
||||||
</a-tooltip>
|
<a-form-item>
|
||||||
</span>
|
<a-input-number v-model="dbInbound.totalGB" :min="0"></a-input-number>
|
||||||
<a-date-picker :show-time="{ format: 'HH:mm' }" format="YYYY-MM-DD HH:mm"
|
</a-form-item>
|
||||||
:dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''"
|
</td>
|
||||||
v-model="dbInbound._expiryTime" style="width: 300px;"></a-date-picker>
|
</tr>
|
||||||
</a-form-item>
|
<tr>
|
||||||
|
<td>
|
||||||
|
<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>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-date-picker :show-time="{ format: 'HH:mm' }" format="YYYY-MM-DD HH:mm"
|
||||||
|
:dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''"
|
||||||
|
v-model="dbInbound._expiryTime" style="width: 250px;"></a-date-picker>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
</a-form>
|
</a-form>
|
||||||
|
|
||||||
<!-- vmess settings -->
|
<!-- vmess settings -->
|
||||||
|
|||||||
@@ -1,20 +1,42 @@
|
|||||||
{{define "form/dokodemo"}}
|
{{define "form/dokodemo"}}
|
||||||
<a-form layout="inline">
|
<a-form layout="inline">
|
||||||
<a-form-item label='{{ i18n "pages.inbounds.targetAddress"}}'>
|
<table width="100%" class="ant-table-tbody">
|
||||||
<a-input v-model.trim="inbound.settings.address"></a-input>
|
<tr>
|
||||||
</a-form-item>
|
<td>{{ i18n "pages.inbounds.targetAddress"}}</td>
|
||||||
<a-form-item label='{{ i18n "pages.inbounds.destinationPort"}}'>
|
<td>
|
||||||
<a-input type="number" v-model.number="inbound.settings.port"></a-input>
|
<a-form-item>
|
||||||
</a-form-item>
|
<a-input v-model.trim="inbound.settings.address"></a-input>
|
||||||
<a-form-item label='{{ i18n "pages.inbounds.network"}}'>
|
</a-form-item>
|
||||||
<a-select v-model="inbound.settings.network" style="width: 100px;" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
</td>
|
||||||
<a-select-option value="tcp,udp">tcp+udp</a-select-option>
|
</tr>
|
||||||
<a-select-option value="tcp">tcp</a-select-option>
|
<tr>
|
||||||
<a-select-option value="udp">udp</a-select-option>
|
<td>{{ i18n "pages.inbounds.destinationPort"}}</td>
|
||||||
</a-select>
|
<td>
|
||||||
</a-form-item>
|
<a-form-item>
|
||||||
<a-form-item label="FollowRedirect">
|
<a-input-number v-model.number="inbound.settings.port"></a-input-number>
|
||||||
<a-switch v-model="inbound.settings.followRedirect"></a-switch>
|
</a-form-item>
|
||||||
</a-form-item>
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ i18n "pages.inbounds.network"}}</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-select v-model="inbound.settings.network" style="width: 100px;" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
||||||
|
<a-select-option value="tcp,udp">tcp+udp</a-select-option>
|
||||||
|
<a-select-option value="tcp">tcp</a-select-option>
|
||||||
|
<a-select-option value="udp">udp</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>FollowRedirect</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-switch v-model="inbound.settings.followRedirect"></a-switch>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
</a-form>
|
</a-form>
|
||||||
{{end}}
|
{{end}}
|
||||||
@@ -1,10 +1,22 @@
|
|||||||
{{define "form/http"}}
|
{{define "form/http"}}
|
||||||
<a-form layout="inline">
|
<a-form layout="inline">
|
||||||
<a-form-item label='{{ i18n "username"}}'>
|
<table width="100%" class="ant-table-tbody">
|
||||||
<a-input v-model.trim="inbound.settings.accounts[0].user"></a-input>
|
<tr>
|
||||||
</a-form-item>
|
<td>{{ i18n "username"}}</td>
|
||||||
<a-form-item label='{{ i18n "password" }}'>
|
<td>
|
||||||
<a-input v-model.trim="inbound.settings.accounts[0].pass"></a-input>
|
<a-form-item>
|
||||||
</a-form-item>
|
<a-input v-model.trim="inbound.settings.accounts[0].user"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</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>
|
||||||
|
</table>
|
||||||
</a-form>
|
</a-form>
|
||||||
{{end}}
|
{{end}}
|
||||||
@@ -1,19 +1,36 @@
|
|||||||
{{define "form/shadowsocks"}}
|
{{define "form/shadowsocks"}}
|
||||||
<a-form layout="inline">
|
<a-form layout="inline">
|
||||||
<a-form-item label='{{ i18n "encryption" }}'>
|
<table width="100%" class="ant-table-tbody">
|
||||||
<a-select v-model="inbound.settings.method" style="width: 165px;" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
<tr>
|
||||||
<a-select-option v-for="method in SSMethods" :value="method">[[ method ]]</a-select-option>
|
<td>{{ i18n "encryption" }}</td>
|
||||||
</a-select>
|
<td>
|
||||||
</a-form-item>
|
<a-form-item>
|
||||||
<a-form-item label='{{ i18n "password" }}'>
|
<a-select v-model="inbound.settings.method" style="width: 165px;" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
||||||
<a-input v-model.trim="inbound.settings.password"></a-input>
|
<a-select-option v-for="method in SSMethods" :value="method">[[ method ]]</a-select-option>
|
||||||
</a-form-item>
|
</a-select>
|
||||||
<a-form-item label='{{ i18n "pages.inbounds.network" }}'>
|
</a-form-item>
|
||||||
<a-select v-model="inbound.settings.network" style="width: 100px;" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
</td>
|
||||||
<a-select-option value="tcp,udp">tcp+udp</a-select-option>
|
</tr>
|
||||||
<a-select-option value="tcp">tcp</a-select-option>
|
<tr>
|
||||||
<a-select-option value="udp">udp</a-select-option>
|
<td>{{ i18n "password" }}</td>
|
||||||
</a-select>
|
<td>
|
||||||
</a-form-item>
|
<a-form-item>
|
||||||
|
<a-input v-model.trim="inbound.settings.password"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ i18n "pages.inbounds.network" }}</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-select v-model="inbound.settings.network" style="width: 100px;" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
||||||
|
<a-select-option value="tcp,udp">tcp+udp</a-select-option>
|
||||||
|
<a-select-option value="tcp">tcp</a-select-option>
|
||||||
|
<a-select-option value="udp">udp</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
</a-form>
|
</a-form>
|
||||||
{{end}}
|
{{end}}
|
||||||
@@ -1,24 +1,50 @@
|
|||||||
{{define "form/socks"}}
|
{{define "form/socks"}}
|
||||||
<a-form layout="inline">
|
<a-form layout="inline">
|
||||||
<!-- <a-form-item label="Password authentication">-->
|
<!-- <a-form-item label="Password authentication">-->
|
||||||
<a-form-item label='{{ i18n "password" }}'>
|
<table width="100%" class="ant-table-tbody">
|
||||||
<a-switch :checked="inbound.settings.auth === 'password'"
|
<tr>
|
||||||
@change="checked => inbound.settings.auth = checked ? 'password' : 'noauth'"></a-switch>
|
<td>{{ i18n "password" }}</td>
|
||||||
</a-form-item>
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-switch :checked="inbound.settings.auth === 'password'"
|
||||||
|
@change="checked => inbound.settings.auth = checked ? 'password' : 'noauth'"></a-switch>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
<template v-if="inbound.settings.auth === 'password'">
|
<template v-if="inbound.settings.auth === 'password'">
|
||||||
<a-form-item label='{{ i18n "username" }}'>
|
<tr>
|
||||||
<a-input v-model.trim="inbound.settings.accounts[0].user"></a-input>
|
<td>{{ i18n "username" }}</td>
|
||||||
</a-form-item>
|
<td>
|
||||||
<a-form-item label='{{ i18n "password" }}'>
|
<a-form-item>
|
||||||
<a-input v-model.trim="inbound.settings.accounts[0].pass"></a-input>
|
<a-input v-model.trim="inbound.settings.accounts[0].user"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</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>
|
</template>
|
||||||
<a-form-item label='{{ i18n "pages.inbounds.enable" }} udp'>
|
<tr>
|
||||||
<a-switch v-model="inbound.settings.udp"></a-switch>
|
<td>{{ i18n "pages.inbounds.enable" }} udp</td>
|
||||||
</a-form-item>
|
<td>
|
||||||
<a-form-item v-if="inbound.settings.udp"
|
<a-form-item>
|
||||||
label="IP">
|
<a-switch v-model="inbound.settings.udp"></a-switch>
|
||||||
<a-input v-model.trim="inbound.settings.ip"></a-input>
|
</a-form-item>
|
||||||
</a-form-item>
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>IP</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item v-if="inbound.settings.udp">
|
||||||
|
<a-input v-model.trim="inbound.settings.ip"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
</a-form>
|
</a-form>
|
||||||
{{end}}
|
{{end}}
|
||||||
@@ -2,9 +2,9 @@
|
|||||||
<a-form layout="inline">
|
<a-form layout="inline">
|
||||||
<a-collapse activeKey="0" v-for="(client, index) in inbound.settings.trojans.slice(0,1)" v-if="!isEdit">
|
<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-collapse-panel header="{{ i18n "pages.inbounds.client" }}">
|
||||||
<a-form layout="inline">
|
<table width="100%" class="ant-table-tbody">
|
||||||
<a-form-item>
|
<tr>
|
||||||
<span slot="label">
|
<td>
|
||||||
<span>{{ i18n "pages.inbounds.Email" }}</span>
|
<span>{{ i18n "pages.inbounds.Email" }}</span>
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
@@ -12,45 +12,88 @@
|
|||||||
</template>
|
</template>
|
||||||
<a-icon @click="getNewEmail(client)" type="sync"> </a-icon>
|
<a-icon @click="getNewEmail(client)" type="sync"> </a-icon>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</span>
|
</td>
|
||||||
<a-input v-model.trim="client.email"></a-input>
|
<td>
|
||||||
</a-form-item>
|
<a-form-item>
|
||||||
</a-form>
|
<a-input v-model.trim="client.email" style="width: 200px;"></a-input>
|
||||||
<a-form-item label="password">
|
</a-form-item>
|
||||||
<a-input v-model.trim="client.password"></a-input>
|
</td>
|
||||||
</a-form-item>
|
</tr>
|
||||||
<a-form-item v-if="inbound.xtls" label="flow">
|
<tr>
|
||||||
<a-select v-model="client.flow" style="width: 150px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
<td>password</td>
|
||||||
<a-select-option value="">{{ i18n "none" }}</a-select-option>
|
<td>
|
||||||
<a-select-option v-for="key in XTLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
<a-form-item>
|
||||||
</a-select>
|
<a-input v-model.trim="client.password" style="width: 200px;"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item>
|
</td>
|
||||||
<span slot="label">
|
</tr>
|
||||||
<span >{{ i18n "pages.inbounds.totalFlow" }}</span>(GB)
|
<tr>
|
||||||
<a-tooltip>
|
<td>Subscription</td>
|
||||||
<template slot="title">
|
<td>
|
||||||
0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
|
<a-form-item v-if="client.email">
|
||||||
</template>
|
<a-input v-model.trim="client.subId" style="width: 200px;"></a-input>
|
||||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
</a-form-item>
|
||||||
</a-tooltip>
|
</td>
|
||||||
</span>
|
</tr>
|
||||||
<a-input-number v-model="client._totalGB" :min="0"></a-input-number>
|
<tr>
|
||||||
</a-form-item>
|
<td>Telegram Username</td>
|
||||||
<a-form-item>
|
<td>
|
||||||
<span slot="label">
|
<a-form-item v-if="client.email">
|
||||||
<span >{{ i18n "pages.inbounds.expireDate" }}</span>
|
<a-input v-model.trim="client.tgId" style="width: 200px;"></a-input>
|
||||||
<a-tooltip>
|
</a-form-item>
|
||||||
<template slot="title">
|
</td>
|
||||||
<span>{{ i18n "pages.inbounds.leaveBlankToNeverExpire" }}</span>
|
</tr>
|
||||||
</template>
|
<tr>
|
||||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
<td>
|
||||||
</a-tooltip>
|
<span >{{ i18n "pages.inbounds.totalFlow" }}</span>(GB)
|
||||||
</span>
|
<a-tooltip>
|
||||||
<a-date-picker :show-time="{ format: 'HH:mm' }" format="YYYY-MM-DD HH:mm"
|
<template slot="title">
|
||||||
:dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''"
|
0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
|
||||||
v-model="client._expiryTime" style="width: 300px;"></a-date-picker>
|
</template>
|
||||||
</a-form-item>
|
<a-icon type="question-circle" theme="filled"></a-icon>
|
||||||
|
</a-tooltip>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-input-number v-model="client._totalGB" :min="0"></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ i18n "pages.client.delayedStart" }}</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-switch v-model="delayedStart" @click="client._expiryTime=0"></a-switch>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr v-if="delayedStart">
|
||||||
|
<td>{{ i18n "pages.client.expireDays" }}</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-input-number v-model.number="delayedExpireDays" :min="0"></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr v-else>
|
||||||
|
<td>
|
||||||
|
<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>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-date-picker :show-time="{ format: 'HH:mm' }" format="YYYY-MM-DD HH:mm"
|
||||||
|
:dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''"
|
||||||
|
v-model="client._expiryTime" style="width: 200px;"></a-date-picker>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
</a-collapse-panel>
|
</a-collapse-panel>
|
||||||
</a-collapse>
|
</a-collapse>
|
||||||
<a-collapse v-else>
|
<a-collapse v-else>
|
||||||
@@ -65,7 +108,7 @@
|
|||||||
</table>
|
</table>
|
||||||
</a-collapse-panel>
|
</a-collapse-panel>
|
||||||
</a-collapse>
|
</a-collapse>
|
||||||
<template v-if="inbound.isTcp && (inbound.tls || inbound.xtls)">
|
<template v-if="inbound.isTcp && inbound.tls">
|
||||||
<a-form layout="inline">
|
<a-form layout="inline">
|
||||||
<a-form-item label="Fallbacks">
|
<a-form-item label="Fallbacks">
|
||||||
<a-row>
|
<a-row>
|
||||||
@@ -97,7 +140,7 @@
|
|||||||
<a-input v-model="fallback.dest"></a-input>
|
<a-input v-model="fallback.dest"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="xver">
|
<a-form-item label="xver">
|
||||||
<a-input type="number" v-model.number="fallback.xver"></a-input>
|
<a-input-number v-model.number="fallback.xver"></a-input-number>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-divider v-if="inbound.settings.fallbacks.length - 1 === index"/>
|
<a-divider v-if="inbound.settings.fallbacks.length - 1 === index"/>
|
||||||
</a-form>
|
</a-form>
|
||||||
|
|||||||
@@ -2,61 +2,109 @@
|
|||||||
<a-form layout="inline">
|
<a-form layout="inline">
|
||||||
<a-collapse activeKey="0" v-for="(client, index) in inbound.settings.vlesses.slice(0,1)" v-if="!isEdit">
|
<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-collapse-panel header="{{ i18n "pages.inbounds.client" }}">
|
||||||
<a-form layout="inline">
|
<table width="100%" class="ant-table-tbody">
|
||||||
<a-form-item>
|
<tr>
|
||||||
<span slot="label">
|
<td>
|
||||||
<span>{{ i18n "pages.inbounds.Email" }}</span>
|
<span>{{ i18n "pages.inbounds.Email" }}</span>
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
<span>{{ i18n "pages.inbounds.EmailDesc" }}</span>
|
<span>{{ i18n "pages.inbounds.EmailDesc" }}</span>
|
||||||
</template>
|
</template>
|
||||||
<a-icon type="sync" @click="getNewEmail(client)"></a-icon>
|
<a-icon @click="getNewEmail(client)" type="sync"> </a-icon>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</span>
|
</td>
|
||||||
<a-input v-model.trim="client.email"></a-input>
|
<td>
|
||||||
</a-form-item>
|
<a-form-item>
|
||||||
</a-form>
|
<a-input v-model.trim="client.email" style="width: 200px;"></a-input>
|
||||||
<a-form-item label="id">
|
</a-form-item>
|
||||||
<a-input v-model.trim="client.id"></a-input>
|
</td>
|
||||||
</a-form-item>
|
</tr>
|
||||||
<a-form-item v-if="inbound.xtls" label="flow">
|
<tr>
|
||||||
<a-select v-model="inbound.settings.vlesses[index].flow" style="width: 150px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
<td>id</td>
|
||||||
<a-select-option value="" selected>{{ i18n "none" }}</a-select-option>
|
<td>
|
||||||
<a-select-option v-for="key in XTLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
<a-form-item>
|
||||||
</a-select>
|
<a-input v-model.trim="client.id" style="width: 200px;"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item v-else-if="inbound.canEnableTlsFlow()" label="flow" layout="inline">
|
</td>
|
||||||
<a-select v-model="inbound.settings.vlesses[index].flow" style="width: 150px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
</tr>
|
||||||
<a-select-option value="" selected>{{ i18n "none" }}</a-select-option>
|
<tr v-if="inbound.canEnableTlsFlow()">
|
||||||
<a-select-option v-for="key in TLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
<td>flow</td>
|
||||||
</a-select>
|
<td>
|
||||||
</a-form-item>
|
<a-form-item>
|
||||||
<a-form-item>
|
<a-select v-model="inbound.settings.vlesses[index].flow" style="width: 200px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
||||||
<span slot="label">
|
<a-select-option value="" selected>{{ i18n "none" }}</a-select-option>
|
||||||
<span >{{ i18n "pages.inbounds.totalFlow" }}</span>(GB)
|
<a-select-option v-for="key in TLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
||||||
<a-tooltip>
|
</a-select>
|
||||||
<template slot="title">
|
</a-form-item>
|
||||||
0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
|
</td>
|
||||||
</template>
|
</tr>
|
||||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
<tr>
|
||||||
</a-tooltip>
|
<td>Subscription</td>
|
||||||
</span>
|
<td>
|
||||||
<a-input-number v-model="client._totalGB" :min="0"></a-input-number>
|
<a-form-item v-if="client.email">
|
||||||
</a-form-item>
|
<a-input v-model.trim="client.subId" style="width: 200px;"></a-input>
|
||||||
<a-form-item>
|
</a-form-item>
|
||||||
<span slot="label">
|
</td>
|
||||||
<span >{{ i18n "pages.inbounds.expireDate" }}</span>
|
</tr>
|
||||||
<a-tooltip>
|
<tr>
|
||||||
<template slot="title">
|
<td>Telegram Username</td>
|
||||||
<span>{{ i18n "pages.inbounds.leaveBlankToNeverExpire" }}</span>
|
<td>
|
||||||
</template>
|
<a-form-item v-if="client.email">
|
||||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
<a-input v-model.trim="client.tgId" style="width: 200px;"></a-input>
|
||||||
</a-tooltip>
|
</a-form-item>
|
||||||
</span>
|
</td>
|
||||||
<a-date-picker :show-time="{ format: 'HH:mm' }" format="YYYY-MM-DD HH:mm"
|
</tr>
|
||||||
:dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''"
|
<tr>
|
||||||
v-model="client._expiryTime" style="width: 300px;"></a-date-picker>
|
<td>
|
||||||
</a-form-item>
|
<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>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-input-number v-model="client._totalGB" :min="0"></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ i18n "pages.client.delayedStart" }}</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-switch v-model="delayedStart" @click="client._expiryTime=0"></a-switch>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr v-if="delayedStart">
|
||||||
|
<td>{{ i18n "pages.client.expireDays" }}</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-input-number v-model.number="delayedExpireDays" :min="0"></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr v-else>
|
||||||
|
<td>
|
||||||
|
<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>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-date-picker :show-time="{ format: 'HH:mm' }" format="YYYY-MM-DD HH:mm"
|
||||||
|
:dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''"
|
||||||
|
v-model="client._expiryTime" style="width: 200px;"></a-date-picker>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
</a-collapse-panel>
|
</a-collapse-panel>
|
||||||
</a-collapse>
|
</a-collapse>
|
||||||
<a-collapse v-else>
|
<a-collapse v-else>
|
||||||
@@ -71,7 +119,7 @@
|
|||||||
</table>
|
</table>
|
||||||
</a-collapse-panel>
|
</a-collapse-panel>
|
||||||
</a-collapse>
|
</a-collapse>
|
||||||
<template v-if="inbound.isTcp && (inbound.tls || inbound.xtls)">
|
<template v-if="inbound.isTcp && inbound.tls">
|
||||||
<a-form layout="inline">
|
<a-form layout="inline">
|
||||||
<a-form-item label="Fallbacks">
|
<a-form-item label="Fallbacks">
|
||||||
<a-row>
|
<a-row>
|
||||||
@@ -103,7 +151,7 @@
|
|||||||
<a-input v-model="fallback.dest"></a-input>
|
<a-input v-model="fallback.dest"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="xver">
|
<a-form-item label="xver">
|
||||||
<a-input type="number" v-model.number="fallback.xver"></a-input>
|
<a-input-number v-model.number="fallback.xver"></a-input-number>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-divider v-if="inbound.settings.fallbacks.length - 1 === index"/>
|
<a-divider v-if="inbound.settings.fallbacks.length - 1 === index"/>
|
||||||
</a-form>
|
</a-form>
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
<a-form layout="inline">
|
<a-form layout="inline">
|
||||||
<a-collapse activeKey="0" v-for="(client, index) in inbound.settings.vmesses.slice(0,1)" v-if="!isEdit">
|
<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-collapse-panel header="{{ i18n "pages.inbounds.client" }}">
|
||||||
<a-form layout="inline">
|
<table width="100%" class="ant-table-tbody">
|
||||||
<a-form-item>
|
<tr>
|
||||||
<span slot="label">
|
<td>
|
||||||
<span>{{ i18n "pages.inbounds.Email" }}</span>
|
<span>{{ i18n "pages.inbounds.Email" }}</span>
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
@@ -12,42 +12,96 @@
|
|||||||
</template>
|
</template>
|
||||||
<a-icon type="sync" @click="getNewEmail(client)"></a-icon>
|
<a-icon type="sync" @click="getNewEmail(client)"></a-icon>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</span>
|
</td>
|
||||||
<a-input v-model.trim="client.email"></a-input>
|
<td>
|
||||||
</a-form-item>
|
<a-form-item>
|
||||||
</a-form>
|
<a-input v-model.trim="client.email" style="width: 200px;"></a-input>
|
||||||
<a-form-item label="id">
|
</a-form-item>
|
||||||
<a-input v-model.trim="client.id"></a-input>
|
</td>
|
||||||
</a-form-item>
|
</tr>
|
||||||
<a-form-item label='{{ i18n "additional" }} ID'>
|
<tr>
|
||||||
<a-input type="number" v-model.number="client.alterId"></a-input>
|
<td>id</td>
|
||||||
</a-form-item>
|
<td>
|
||||||
<a-form-item>
|
<a-form-item>
|
||||||
<span slot="label">
|
<a-input v-model.trim="client.id" style="width: 200px;"></a-input>
|
||||||
<span >{{ i18n "pages.inbounds.totalFlow" }}</span>(GB)
|
</a-form-item>
|
||||||
<a-tooltip>
|
</td>
|
||||||
<template slot="title">
|
</tr>
|
||||||
0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
|
<tr>
|
||||||
</template>
|
<td>{{ i18n "additional" }}</td>
|
||||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
<td>
|
||||||
</a-tooltip>
|
<a-form-item>
|
||||||
</span>
|
<a-input-number v-model.number="client.alterId"></a-input-number>
|
||||||
<a-input-number v-model="client._totalGB" :min="0"></a-input-number>
|
</a-form-item>
|
||||||
</a-form-item>
|
</td>
|
||||||
<a-form-item>
|
</tr>
|
||||||
<span slot="label">
|
<tr>
|
||||||
<span >{{ i18n "pages.inbounds.expireDate" }}</span>
|
<td>Subscription</td>
|
||||||
<a-tooltip>
|
<td>
|
||||||
<template slot="title">
|
<a-form-item v-if="client.email">
|
||||||
<span>{{ i18n "pages.inbounds.leaveBlankToNeverExpire" }}</span>
|
<a-input v-model.trim="client.subId" style="width: 200px;"></a-input>
|
||||||
</template>
|
</a-form-item>
|
||||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
</td>
|
||||||
</a-tooltip>
|
</tr>
|
||||||
</span>
|
<tr>
|
||||||
<a-date-picker :show-time="{ format: 'HH:mm' }" format="YYYY-MM-DD HH:mm"
|
<td>Telegram Username</td>
|
||||||
:dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''"
|
<td>
|
||||||
v-model="client._expiryTime" style="width: 300px;"></a-date-picker>
|
<a-form-item v-if="client.email">
|
||||||
</a-form-item>
|
<a-input v-model.trim="client.tgId" style="width: 200px;"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<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>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-input-number v-model="client._totalGB" :min="0"></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ i18n "pages.client.delayedStart" }}</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-switch v-model="delayedStart" @click="client._expiryTime=0"></a-switch>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr v-if="delayedStart">
|
||||||
|
<td>{{ i18n "pages.client.expireDays" }}</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-input-number v-model.number="delayedExpireDays" :min="0"></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr v-else>
|
||||||
|
<td>
|
||||||
|
<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>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-date-picker :show-time="{ format: 'HH:mm' }" format="YYYY-MM-DD HH:mm"
|
||||||
|
:dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''"
|
||||||
|
v-model="client._expiryTime" style="width: 200px;"></a-date-picker>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
</a-collapse-panel>
|
</a-collapse-panel>
|
||||||
</a-collapse>
|
</a-collapse>
|
||||||
<a-collapse v-else>
|
<a-collapse v-else>
|
||||||
@@ -64,8 +118,7 @@
|
|||||||
</a-collapse>
|
</a-collapse>
|
||||||
<a-form layout="inline">
|
<a-form layout="inline">
|
||||||
<a-form-item label='{{ i18n "pages.inbounds.disableInsecureEncryption" }}'>
|
<a-form-item label='{{ i18n "pages.inbounds.disableInsecureEncryption" }}'>
|
||||||
<a-switch v-model.number="inbound.settings.disableInsecure"></a-switch>
|
<a-switch v-model="inbound.settings.disableInsecure"></a-switch>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
|
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
{{define "form/streamGRPC"}}
|
{{define "form/streamGRPC"}}
|
||||||
<a-form layout="inline">
|
<a-form layout="inline">
|
||||||
<a-form-item label="serviceName">
|
<table width="100%" class="ant-table-tbody">
|
||||||
<a-input v-model.trim="inbound.stream.grpc.serviceName"></a-input>
|
<tr>
|
||||||
</a-form-item>
|
<td>serviceName</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-input v-model.trim="inbound.stream.grpc.serviceName" style="width: 250px;"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
</a-form>
|
</a-form>
|
||||||
{{end}}
|
{{end}}
|
||||||
@@ -1,12 +1,24 @@
|
|||||||
{{define "form/streamHTTP"}}
|
{{define "form/streamHTTP"}}
|
||||||
<a-form layout="inline">
|
<a-form layout="inline">
|
||||||
<a-form-item label='{{ i18n "path" }}'>
|
<table width="100%" class="ant-table-tbody">
|
||||||
<a-input v-model.trim="inbound.stream.http.path"></a-input>
|
<tr>
|
||||||
</a-form-item>
|
<td>{{ i18n "path" }}</td>
|
||||||
<a-form-item label="host">
|
<td>
|
||||||
<a-row v-for="(host, index) in inbound.stream.http.host">
|
<a-form-item>
|
||||||
<a-input v-model.trim="inbound.stream.http.host[index]"></a-input>
|
<a-input v-model.trim="inbound.stream.http.path" style="width: 250px;"></a-input>
|
||||||
</a-row>
|
</a-form-item>
|
||||||
</a-form-item>
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>host</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-row v-for="(host, index) in inbound.stream.http.host">
|
||||||
|
<a-input v-model.trim="inbound.stream.http.host[index]" style="width: 250px;"></a-input>
|
||||||
|
</a-row>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
</a-form>
|
</a-form>
|
||||||
{{end}}
|
{{end}}
|
||||||
@@ -1,38 +1,85 @@
|
|||||||
{{define "form/streamKCP"}}
|
{{define "form/streamKCP"}}
|
||||||
<a-form layout="inline">
|
<a-form layout="inline">
|
||||||
<a-form-item label='{{ i18n "camouflage" }}'>
|
<table width="100%" class="ant-table-tbody">
|
||||||
<a-select v-model="inbound.stream.kcp.type" style="width: 280px;" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
<tr>
|
||||||
<a-select-option value="none">none(not camouflage)</a-select-option>
|
<td>{{ i18n "camouflage" }}</td>
|
||||||
<a-select-option value="srtp">srtp(camouflage video call)</a-select-option>
|
<td>
|
||||||
<a-select-option value="utp">utp(camouflage BT download)</a-select-option>
|
<a-form-item>
|
||||||
<a-select-option value="wechat-video">wechat-video(camouflage WeChat video)</a-select-option>
|
<a-select v-model="inbound.stream.kcp.type" style="width: 250px;" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
||||||
<a-select-option value="dtls">dtls(camouflage DTLS 1.2 packages)</a-select-option>
|
<a-select-option value="none">none (not camouflage)</a-select-option>
|
||||||
<a-select-option value="wireguard">wireguard(camouflage wireguard packages)</a-select-option>
|
<a-select-option value="srtp">srtp (video call)</a-select-option>
|
||||||
</a-select>
|
<a-select-option value="utp">utp (BT download)</a-select-option>
|
||||||
</a-form-item>
|
<a-select-option value="wechat-video">wechat-video (WeChat video)</a-select-option>
|
||||||
<a-form-item label='{{ i18n "password" }}'>
|
<a-select-option value="dtls">dtls (DTLS 1.2 packages)</a-select-option>
|
||||||
<a-input v-model.number="inbound.stream.kcp.seed"></a-input>
|
<a-select-option value="wireguard">wireguard (wireguard packages)</a-select-option>
|
||||||
</a-form-item>
|
</a-select>
|
||||||
<a-form-item label="mtu">
|
</a-form-item>
|
||||||
<a-input type="number" v-model.number="inbound.stream.kcp.mtu"></a-input>
|
</td>
|
||||||
</a-form-item>
|
</tr>
|
||||||
<a-form-item label="tti (ms)">
|
<tr>
|
||||||
<a-input type="number" v-model.number="inbound.stream.kcp.tti"></a-input>
|
<td>{{ i18n "password" }}</td>
|
||||||
</a-form-item>
|
<td>
|
||||||
<a-form-item label="uplink capacity (MB/S)">
|
<a-form-item>
|
||||||
<a-input type="number" v-model.number="inbound.stream.kcp.upCap"></a-input>
|
<a-input v-model="inbound.stream.kcp.seed" style="width: 250px;"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="downlink capacity (MB/S)">
|
</td>
|
||||||
<a-input type="number" v-model.number="inbound.stream.kcp.downCap"></a-input>
|
</tr>
|
||||||
</a-form-item>
|
<tr>
|
||||||
<a-form-item label="congestion">
|
<td>mtu</td>
|
||||||
<a-switch v-model="inbound.stream.kcp.congestion"></a-switch>
|
<td>
|
||||||
</a-form-item>
|
<a-form-item>
|
||||||
<a-form-item label="read buffer size (MB)">
|
<a-input-number v-model.number="inbound.stream.kcp.mtu"></a-input-number>
|
||||||
<a-input type="number" v-model.number="inbound.stream.kcp.readBuffer"></a-input>
|
</a-form-item>
|
||||||
</a-form-item>
|
</td>
|
||||||
<a-form-item label="write buffer size (MB)">
|
</tr>
|
||||||
<a-input type="number" v-model.number="inbound.stream.kcp.writeBuffer"></a-input>
|
<tr>
|
||||||
</a-form-item>
|
<td>tti (ms)</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-input-number v-model.number="inbound.stream.kcp.tti"></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>uplink capacity (MB/S)</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-input-number v-model.number="inbound.stream.kcp.upCap"></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>downlink capacity (MB/S)</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-input-number v-model.number="inbound.stream.kcp.downCap"></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>congestion</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-switch v-model="inbound.stream.kcp.congestion"></a-switch>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>read buffer size (MB)</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-input-number v-model.number="inbound.stream.kcp.readBuffer"></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>write buffer size (MB)</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-input-number v-model.number="inbound.stream.kcp.writeBuffer"></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
</a-form>
|
</a-form>
|
||||||
{{end}}
|
{{end}}
|
||||||
@@ -1,24 +1,41 @@
|
|||||||
{{define "form/streamQUIC"}}
|
{{define "form/streamQUIC"}}
|
||||||
<a-form layout="inline">
|
<a-form layout="inline">
|
||||||
<a-form-item label='{{ i18n "pages.inbounds.stream.quic.encryption" }}'>
|
<table width="100%" class="ant-table-tbody">
|
||||||
<a-select v-model="inbound.stream.quic.security" style="width: 165px;" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
<tr>
|
||||||
<a-select-option value="none">none</a-select-option>
|
<td>{{ i18n "pages.inbounds.stream.quic.encryption" }}</td>
|
||||||
<a-select-option value="aes-128-gcm">aes-128-gcm</a-select-option>
|
<td>
|
||||||
<a-select-option value="chacha20-poly1305">chacha20-poly1305</a-select-option>
|
<a-form-item>
|
||||||
</a-select>
|
<a-select v-model="inbound.stream.quic.security" style="width: 200px;" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
||||||
</a-form-item>
|
<a-select-option value="none">none</a-select-option>
|
||||||
<a-form-item label='{{ i18n "password" }}'>
|
<a-select-option value="aes-128-gcm">aes-128-gcm</a-select-option>
|
||||||
<a-input v-model.trim="inbound.stream.quic.key"></a-input>
|
<a-select-option value="chacha20-poly1305">chacha20-poly1305</a-select-option>
|
||||||
</a-form-item>
|
</a-select>
|
||||||
<a-form-item label='{{ i18n "camouflage" }}'>
|
</a-form-item>
|
||||||
<a-select v-model="inbound.stream.quic.type" style="width: 280px;" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
</td>
|
||||||
<a-select-option value="none">none(not camouflage)</a-select-option>
|
</tr>
|
||||||
<a-select-option value="srtp">srtp(camouflage video call)</a-select-option>
|
<tr>
|
||||||
<a-select-option value="utp">utp(camouflage BT download)</a-select-option>
|
<td>{{ i18n "password" }}</td>
|
||||||
<a-select-option value="wechat-video">wechat-video(camouflage WeChat video)</a-select-option>
|
<td>
|
||||||
<a-select-option value="dtls">dtls(camouflage DTLS 1.2 packages)</a-select-option>
|
<a-form-item>
|
||||||
<a-select-option value="wireguard">wireguard(camouflage wireguard packages)</a-select-option>
|
<a-input v-model.trim="inbound.stream.quic.key" style="width: 200px;"></a-input>
|
||||||
</a-select>
|
</a-form-item>
|
||||||
</a-form-item>
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ i18n "camouflage" }}</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-select v-model="inbound.stream.quic.type" style="width: 200px;" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
||||||
|
<a-select-option value="none">none (not camouflage)</a-select-option>
|
||||||
|
<a-select-option value="srtp">srtp (video call)</a-select-option>
|
||||||
|
<a-select-option value="utp">utp (BT download)</a-select-option>
|
||||||
|
<a-select-option value="wechat-video">wechat-video (WeChat video)</a-select-option>
|
||||||
|
<a-select-option value="dtls">dtls (DTLS 1.2 packages)</a-select-option>
|
||||||
|
<a-select-option value="wireguard">wireguard (wireguard packages)</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
</a-form>
|
</a-form>
|
||||||
{{end}}
|
{{end}}
|
||||||
@@ -1,16 +1,24 @@
|
|||||||
{{define "form/streamSettings"}}
|
{{define "form/streamSettings"}}
|
||||||
<!-- select stream network -->
|
<!-- select stream network -->
|
||||||
<a-form layout="inline">
|
<a-form layout="inline">
|
||||||
<a-form-item label='{{ i18n "transmission" }}'>
|
<table width="100%" class="ant-table-tbody">
|
||||||
<a-select v-model="inbound.stream.network" @change="streamNetworkChange" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
<tr>
|
||||||
<a-select-option value="tcp">tcp</a-select-option>
|
<td>{{ i18n "transmission" }}</td>
|
||||||
<a-select-option value="kcp">kcp</a-select-option>
|
<td>
|
||||||
<a-select-option value="ws">ws</a-select-option>
|
<a-form-item>
|
||||||
<a-select-option value="http">http</a-select-option>
|
<a-select v-model="inbound.stream.network" @change="streamNetworkChange"
|
||||||
<a-select-option value="quic">quic</a-select-option>
|
:dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
||||||
<a-select-option value="grpc">grpc</a-select-option>
|
<a-select-option value="tcp">tcp</a-select-option>
|
||||||
</a-select>
|
<a-select-option value="kcp">kcp</a-select-option>
|
||||||
</a-form-item>
|
<a-select-option value="ws">ws</a-select-option>
|
||||||
|
<a-select-option value="http">http</a-select-option>
|
||||||
|
<a-select-option value="quic">quic</a-select-option>
|
||||||
|
<a-select-option value="grpc">grpc</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
</a-form>
|
</a-form>
|
||||||
|
|
||||||
<!-- tcp -->
|
<!-- tcp -->
|
||||||
|
|||||||
@@ -13,74 +13,109 @@
|
|||||||
</a-form>
|
</a-form>
|
||||||
|
|
||||||
<!-- tcp request -->
|
<!-- tcp request -->
|
||||||
<a-form v-if="inbound.stream.tcp.type === 'http'"
|
<a-form v-if="inbound.stream.tcp.type === 'http'" layout="inline">
|
||||||
layout="inline">
|
<table width="100%" class="ant-table-tbody">
|
||||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.requestVersion" }}'>
|
<tr>
|
||||||
<a-input v-model.trim="inbound.stream.tcp.request.version"></a-input>
|
<td>{{ i18n "pages.inbounds.stream.tcp.requestVersion" }}</td>
|
||||||
</a-form-item>
|
<td>
|
||||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.requestMethod" }}'>
|
<a-form-item>
|
||||||
<a-input v-model.trim="inbound.stream.tcp.request.method"></a-input>
|
<a-input v-model.trim="inbound.stream.tcp.request.version" style="width: 200px;"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.requestPath" }}'>
|
</td>
|
||||||
<a-row v-for="(path, index) in inbound.stream.tcp.request.path">
|
</tr>
|
||||||
<a-input v-model.trim="inbound.stream.tcp.request.path[index]"></a-input>
|
<tr>
|
||||||
</a-row>
|
<td>{{ i18n "pages.inbounds.stream.tcp.requestMethod" }}</td>
|
||||||
</a-form-item>
|
<td>
|
||||||
<a-form-item label='{{ i18n "pages.inbounds.stream.general.requestHeader" }}'>
|
<a-form-item>
|
||||||
<a-row>
|
<a-input v-model.trim="inbound.stream.tcp.request.method" style="width: 200px;"></a-input>
|
||||||
<a-button size="small"
|
</a-form-item>
|
||||||
@click="inbound.stream.tcp.request.addHeader('Host', 'xxx.com')">
|
</td>
|
||||||
+
|
</tr>
|
||||||
</a-button>
|
<tr>
|
||||||
</a-row>
|
<td>{{ i18n "pages.inbounds.stream.tcp.requestPath" }}</td>
|
||||||
<a-input-group v-for="(header, index) in inbound.stream.tcp.request.headers">
|
<td>
|
||||||
<a-input style="width: 50%" v-model.trim="header.name"
|
<a-form-item>
|
||||||
addon-before='{{ i18n "pages.inbounds.stream.general.name" }}'></a-input>
|
<a-row v-for="(path, index) in inbound.stream.tcp.request.path">
|
||||||
<a-input style="width: 50%" v-model.trim="header.value"
|
<a-input v-model.trim="inbound.stream.tcp.request.path[index]" style="width: 200px;"></a-input>
|
||||||
addon-before='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
</a-row>
|
||||||
<template slot="addonAfter">
|
</a-form-item>
|
||||||
<a-button size="small"
|
</td>
|
||||||
@click="inbound.stream.tcp.request.removeHeader(index)">
|
</tr>
|
||||||
-
|
<tr>
|
||||||
</a-button>
|
<td colspan="2">
|
||||||
</template>
|
<a-form-item label='{{ i18n "pages.inbounds.stream.general.requestHeader" }}'>
|
||||||
</a-input>
|
<a-row>
|
||||||
</a-input-group>
|
<a-button size="small"
|
||||||
</a-form-item>
|
@click="inbound.stream.tcp.request.addHeader('Host', 'xxx.com')">
|
||||||
</a-form>
|
+
|
||||||
|
</a-button>
|
||||||
<!-- tcp response -->
|
</a-row>
|
||||||
<a-form v-if="inbound.stream.tcp.type === 'http'"
|
<a-input-group v-for="(header, index) in inbound.stream.tcp.request.headers">
|
||||||
layout="inline">
|
<a-input style="width: 50%" v-model.trim="header.name"
|
||||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.responseVersion" }}'>
|
addon-before='{{ i18n "pages.inbounds.stream.general.name" }}'></a-input>
|
||||||
<a-input v-model.trim="inbound.stream.tcp.response.version"></a-input>
|
<a-input style="width: 50%" v-model.trim="header.value"
|
||||||
</a-form-item>
|
addon-before='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
||||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.responseStatus" }}'>
|
<template slot="addonAfter">
|
||||||
<a-input v-model.trim="inbound.stream.tcp.response.status"></a-input>
|
<a-button size="small"
|
||||||
</a-form-item>
|
@click="inbound.stream.tcp.request.removeHeader(index)">
|
||||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.responseStatusDescription" }}'>
|
-
|
||||||
<a-input v-model.trim="inbound.stream.tcp.response.reason"></a-input>
|
</a-button>
|
||||||
</a-form-item>
|
</template>
|
||||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.responseHeader" }}'>
|
</a-input>
|
||||||
<a-row>
|
</a-input-group>
|
||||||
<a-button size="small"
|
</a-form-item>
|
||||||
@click="inbound.stream.tcp.response.addHeader('Content-Type', 'application/octet-stream')">
|
</td>
|
||||||
+
|
</tr>
|
||||||
</a-button>
|
<!-- tcp response -->
|
||||||
</a-row>
|
<tr>
|
||||||
<a-input-group v-for="(header, index) in inbound.stream.tcp.response.headers">
|
<td>{{ i18n "pages.inbounds.stream.tcp.responseVersion" }}</td>
|
||||||
<a-input style="width: 50%" v-model.trim="header.name"
|
<td>
|
||||||
addon-before='{{ i18n "pages.inbounds.stream.general.name" }}'></a-input>
|
<a-form-item>
|
||||||
<a-input style="width: 50%" v-model.trim="header.value"
|
<a-input v-model.trim="inbound.stream.tcp.response.version" style="width: 200px;"></a-input>
|
||||||
addon-before='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
</a-form-item>
|
||||||
<template slot="addonAfter">
|
</td>
|
||||||
<a-button size="small"
|
</tr>
|
||||||
@click="inbound.stream.tcp.response.removeHeader(index)">
|
<tr>
|
||||||
-
|
<td>{{ i18n "pages.inbounds.stream.tcp.responseStatus" }}</td>
|
||||||
</a-button>
|
<td>
|
||||||
</template>
|
<a-form-item>
|
||||||
</a-input>
|
<a-input v-model.trim="inbound.stream.tcp.response.status" style="width: 200px;"></a-input>
|
||||||
</a-input-group>
|
</a-form-item>
|
||||||
</a-form-item>
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ i18n "pages.inbounds.stream.tcp.responseStatusDescription" }}</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-input v-model.trim="inbound.stream.tcp.response.reason" style="width: 200px;"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">
|
||||||
|
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.responseHeader" }}'>
|
||||||
|
<a-row>
|
||||||
|
<a-button size="small"
|
||||||
|
@click="inbound.stream.tcp.response.addHeader('Content-Type', 'application/octet-stream')">
|
||||||
|
+
|
||||||
|
</a-button>
|
||||||
|
</a-row>
|
||||||
|
<a-input-group v-for="(header, index) in inbound.stream.tcp.response.headers">
|
||||||
|
<a-input style="width: 50%" v-model.trim="header.name"
|
||||||
|
addon-before='{{ i18n "pages.inbounds.stream.general.name" }}'></a-input>
|
||||||
|
<a-input style="width: 50%" v-model.trim="header.value"
|
||||||
|
addon-before='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
||||||
|
<template slot="addonAfter">
|
||||||
|
<a-button size="small"
|
||||||
|
@click="inbound.stream.tcp.response.removeHeader(index)">
|
||||||
|
-
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
</a-input-group>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
</a-form>
|
</a-form>
|
||||||
{{end}}
|
{{end}}
|
||||||
@@ -1,33 +1,47 @@
|
|||||||
{{define "form/streamWS"}}
|
{{define "form/streamWS"}}
|
||||||
<a-form layout="inline">
|
<a-form layout="inline">
|
||||||
<a-form-item label="acceptProxyProtocol">
|
<table width="100%" class="ant-table-tbody">
|
||||||
<a-switch v-model="inbound.stream.ws.acceptProxyProtocol"></a-switch>
|
<tr>
|
||||||
</a-form-item>
|
<td>acceptProxyProtocol</td>
|
||||||
</a-form>
|
<td>
|
||||||
<a-form layout="inline">
|
<a-form-item>
|
||||||
<a-form-item label='{{ i18n "path" }}'>
|
<a-switch v-model="inbound.stream.ws.acceptProxyProtocol"></a-switch>
|
||||||
<a-input v-model.trim="inbound.stream.ws.path"></a-input>
|
</a-form-item>
|
||||||
</a-form-item>
|
</td>
|
||||||
<a-form-item label='{{ i18n "pages.inbounds.stream.general.requestHeader" }}'>
|
</tr>
|
||||||
<a-row>
|
<tr>
|
||||||
<a-button size="small"
|
<td>{{ i18n "path" }}</td>
|
||||||
@click="inbound.stream.ws.addHeader('Host', '')">
|
<td>
|
||||||
+
|
<a-form-item>
|
||||||
</a-button>
|
<a-input v-model.trim="inbound.stream.ws.path" style="width: 250px;"></a-input>
|
||||||
</a-row>
|
</a-form-item>
|
||||||
<a-input-group v-for="(header, index) in inbound.stream.ws.headers">
|
</td>
|
||||||
<a-input style="width: 50%" v-model.trim="header.name"
|
</tr>
|
||||||
addon-before='{{ i18n "pages.inbounds.stream.general.name"}}'></a-input>
|
<tr>
|
||||||
<a-input style="width: 50%" v-model.trim="header.value"
|
<td colspan="2">
|
||||||
addon-before='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
<a-form-item label='{{ i18n "pages.inbounds.stream.general.requestHeader" }}'>
|
||||||
<template slot="addonAfter">
|
<a-row>
|
||||||
<a-button size="small"
|
<a-button size="small"
|
||||||
@click="inbound.stream.ws.removeHeader(index)">
|
@click="inbound.stream.ws.addHeader('Host', '')">
|
||||||
-
|
+
|
||||||
</a-button>
|
</a-button>
|
||||||
</template>
|
</a-row>
|
||||||
</a-input>
|
<a-input-group v-for="(header, index) in inbound.stream.ws.headers">
|
||||||
</a-input-group>
|
<a-input style="width: 50%" v-model.trim="header.name"
|
||||||
</a-form-item>
|
addon-before='{{ i18n "pages.inbounds.stream.general.name"}}'></a-input>
|
||||||
|
<a-input style="width: 50%" v-model.trim="header.value"
|
||||||
|
addon-before='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
||||||
|
<template slot="addonAfter">
|
||||||
|
<a-button size="small"
|
||||||
|
@click="inbound.stream.ws.removeHeader(index)">
|
||||||
|
-
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
</a-input-group>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
</a-form>
|
</a-form>
|
||||||
{{end}}
|
{{end}}
|
||||||
@@ -1,75 +1,241 @@
|
|||||||
{{define "form/tlsSettings"}}
|
{{define "form/tlsSettings"}}
|
||||||
<!-- tls enable -->
|
<!-- tls enable -->
|
||||||
<a-form layout="inline" v-if="inbound.canSetTls()">
|
<a-form v-if="inbound.canSetTls()" layout="inline">
|
||||||
<a-form-item label="tls">
|
<a-form-item label="TLS">
|
||||||
<a-switch v-model="inbound.tls">
|
<a-switch v-model="inbound.tls">
|
||||||
</a-switch>
|
</a-switch>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item v-if="inbound.canEnableXTls()" label="xtls">
|
<a-form-item v-if="inbound.canEnableReality()" label="Reality">
|
||||||
<a-switch v-model="inbound.xtls"></a-switch>
|
<a-switch v-model="inbound.reality"></a-switch>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
|
|
||||||
<!-- tls settings -->
|
<!-- tls settings -->
|
||||||
<a-form v-if="inbound.tls || inbound.xtls" layout="inline">
|
<a-form v-if="inbound.tls" layout="inline">
|
||||||
<a-form-item label="SNI" placeholder="Server Name Indication" v-if="inbound.tls">
|
<table width="100%" class="ant-table-tbody">
|
||||||
<a-input v-model.trim="inbound.stream.tls.settings[0].serverName"></a-input>
|
<tr>
|
||||||
</a-form-item>
|
<td>SNI</td>
|
||||||
<a-form-item label="CipherSuites">
|
<td>
|
||||||
<a-select v-model="inbound.stream.tls.cipherSuites" style="width: 300px">
|
<a-form-item placeholder="Server Name Indication" v-if="inbound.tls">
|
||||||
<a-select-option value="">auto</a-select-option>
|
<a-input v-model.trim="inbound.stream.tls.settings.serverName" style="width: 250px"></a-input>
|
||||||
<a-select-option v-for="key in TLS_CIPHER_OPTION" :value="key">[[ key ]]</a-select-option>
|
</a-form-item>
|
||||||
</a-select>
|
</td>
|
||||||
</a-form-item>
|
</tr>
|
||||||
<a-form-item label="MinVersion">
|
<tr>
|
||||||
<a-select v-model="inbound.stream.tls.minVersion" style="width: 60px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
<td>CipherSuites</td>
|
||||||
<a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key ]]</a-select-option>
|
<td>
|
||||||
</a-select>
|
<a-form-item>
|
||||||
</a-form-item>
|
<a-select v-model="inbound.stream.tls.cipherSuites" style="width: 250px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
||||||
<a-form-item label="MaxVersion">
|
<a-select-option value="">auto</a-select-option>
|
||||||
<a-select v-model="inbound.stream.tls.maxVersion" style="width: 60px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
<a-select-option v-for="key in TLS_CIPHER_OPTION" :value="key">[[ key ]]</a-select-option>
|
||||||
<a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key ]]</a-select-option>
|
</a-select>
|
||||||
</a-select>
|
</a-form-item>
|
||||||
</a-form-item>
|
</td>
|
||||||
<a-form-item label="uTLS" v-if="inbound.tls" >
|
</tr>
|
||||||
<a-select v-model="inbound.stream.tls.settings[0].fingerprint" style="width: 135px">
|
<tr>
|
||||||
<a-select-option value=''>None</a-select-option>
|
<td>MinVersion</td>
|
||||||
<a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
|
<td>
|
||||||
</a-select>
|
<a-form-item>
|
||||||
</a-form-item>
|
<a-select v-model="inbound.stream.tls.minVersion" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
||||||
<a-form-item label='{{ i18n "domainName" }}'>
|
<a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key ]]</a-select-option>
|
||||||
<a-input v-model.trim="inbound.stream.tls.server"></a-input>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="Alpn">
|
</td>
|
||||||
<a-checkbox-group v-model="inbound.stream.tls.alpn" style="width:200px">
|
</tr>
|
||||||
<a-checkbox v-for="key in ALPN_OPTION" :value="key">[[ key ]]</a-checkbox>
|
<tr>
|
||||||
</a-checkbox-group>
|
<td>MaxVersion</td>
|
||||||
</a-form-item>
|
<td>
|
||||||
<a-form-item label="Allow insecure">
|
<a-form-item>
|
||||||
<a-switch v-model="inbound.stream.tls.settings[0].allowInsecure"></a-switch>
|
<a-select v-model="inbound.stream.tls.maxVersion" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
||||||
</a-form-item>
|
<a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key ]]</a-select-option>
|
||||||
<a-form-item label='{{ i18n "certificate" }}'>
|
</a-select>
|
||||||
<a-radio-group v-model="inbound.stream.tls.certs[0].useFile" button-style="solid">
|
</a-form-item>
|
||||||
<a-radio-button :value="true">{{ i18n "pages.inbounds.certificatePath" }}</a-radio-button>
|
</td>
|
||||||
<a-radio-button :value="false">{{ i18n "pages.inbounds.certificateContent" }}</a-radio-button>
|
</tr>
|
||||||
</a-radio-group>
|
<tr>
|
||||||
</a-form-item>
|
<td>uTLS</td>
|
||||||
<template v-if="inbound.stream.tls.certs[0].useFile">
|
<td>
|
||||||
<a-form-item label='{{ i18n "pages.inbounds.publicKeyPath" }}'>
|
<a-form-item>
|
||||||
<a-input v-model.trim="inbound.stream.tls.certs[0].certFile" style="width:300px;"></a-input>
|
<a-select v-model="inbound.stream.tls.settings.fingerprint" style="width: 250px">
|
||||||
</a-form-item>
|
<a-select-option value=''>None</a-select-option>
|
||||||
<a-form-item label='{{ i18n "pages.inbounds.keyPath" }}'>
|
<a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
|
||||||
<a-input v-model.trim="inbound.stream.tls.certs[0].keyFile" style="width:300px;"></a-input>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-button @click="setDefaultCertData">{{ i18n "pages.inbounds.setDefaultCert" }}</a-button>
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ i18n "domainName" }}</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-input v-model.trim="inbound.stream.tls.server" style="width: 250px"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Alpn</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-checkbox-group v-model="inbound.stream.tls.alpn">
|
||||||
|
<a-checkbox v-for="key in ALPN_OPTION" :value="key">[[ key ]]</a-checkbox>
|
||||||
|
</a-checkbox-group>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Allow insecure</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-switch v-model="inbound.stream.tls.settings.allowInsecure"></a-switch>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">
|
||||||
|
<a-form-item label="{{ i18n "certificate" }}">
|
||||||
|
<a-radio-group v-model="inbound.stream.tls.certs[0].useFile" button-style="solid">
|
||||||
|
<a-radio-button :value="true">{{ i18n "pages.inbounds.certificatePath" }}</a-radio-button>
|
||||||
|
<a-radio-button :value="false">{{ i18n "pages.inbounds.certificateContent" }}</a-radio-button>
|
||||||
|
</a-radio-group>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<template v-if="inbound.stream.tls.certs[0].useFile">
|
||||||
|
<tr>
|
||||||
|
<td>{{ i18n "pages.inbounds.publicKeyPath" }}</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-input v-model.trim="inbound.stream.tls.certs[0].certFile" style="width:250px;"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ i18n "pages.inbounds.keyPath" }}</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-input v-model.trim="inbound.stream.tls.certs[0].keyFile" style="width:250px;"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td>
|
||||||
|
<a-button type="primary" icon="import" @click="setDefaultCertData">{{ i18n "pages.inbounds.setDefaultCert" }}</a-button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<a-form-item label='{{ i18n "pages.inbounds.publicKeyContent" }}'>
|
<tr>
|
||||||
<a-input type="textarea" :rows="3" style="width:300px;" v-model="inbound.stream.tls.certs[0].cert"></a-input>
|
<td>{{ i18n "pages.inbounds.publicKeyContent" }}</td>
|
||||||
</a-form-item>
|
<td>
|
||||||
<a-form-item label='{{ i18n "pages.inbounds.keyContent" }}'>
|
<a-form-item>
|
||||||
<a-input type="textarea" :rows="3" style="width:300px;" v-model="inbound.stream.tls.certs[0].key"></a-input>
|
<a-input type="textarea" :rows="3" style="width:250px;" v-model="inbound.stream.tls.certs[0].cert"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ i18n "pages.inbounds.keyContent" }}</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-input type="textarea" :rows="3" style="width:250px;" v-model="inbound.stream.tls.certs[0].key"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</template>
|
</template>
|
||||||
|
</table>
|
||||||
|
</a-form>
|
||||||
|
|
||||||
|
<!-- reality settings -->
|
||||||
|
<a-form v-if="inbound.reality" layout="inline">
|
||||||
|
<table width="100%" class="ant-table-tbody">
|
||||||
|
<tr>
|
||||||
|
<td>{{ i18n "domainName" }}</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-input v-model.trim="inbound.stream.reality.settings.serverName" style="width: 250px"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Show</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-switch v-model="inbound.stream.reality.show"></a-switch>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Xver</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-input-number v-model.number="inbound.stream.reality.xver" :min="0"></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>uTLS</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item >
|
||||||
|
<a-select v-model="inbound.stream.reality.settings.fingerprint" style="width: 250px">
|
||||||
|
<a-select-option value=''>None</a-select-option>
|
||||||
|
<a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Dest</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-input v-model.trim="inbound.stream.reality.dest" style="width: 250px"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Server Names</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-input v-model.trim="inbound.stream.reality.serverNames" style="width: 250px"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Short Ids</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-input v-model.trim="inbound.stream.reality.shortIds" style="width:250px"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>SpiderX</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-input v-model.trim="inbound.stream.reality.settings.spiderX" style="width:250px"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Private Key</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-input v-model.trim="inbound.stream.reality.privateKey" style="width: 250px"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Public Key</td>
|
||||||
|
<td>
|
||||||
|
<a-form-item>
|
||||||
|
<a-input v-model.trim="inbound.stream.reality.settings.publicKey" style="width: 250px"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td>
|
||||||
|
<a-button type="primary" icon="import" @click="getNewX25519Cert">Get new cert</a-button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
</a-form>
|
</a-form>
|
||||||
{{end}}
|
{{end}}
|
||||||
@@ -49,9 +49,9 @@
|
|||||||
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>
|
||||||
<td v-else-if="inbound.xtls">
|
<td v-else-if="inbound.reality">
|
||||||
xtls: <a-tag color="green">{{ i18n "enabled" }}</a-tag><br />
|
reality: <a-tag color="green">{{ i18n "enabled" }}</a-tag><br />
|
||||||
xtls {{ i18n "domainName" }}: <a-tag :color="inbound.serverName ? 'green' : 'orange'">[[ inbound.serverName ? inbound.serverName : '' ]]</a-tag>
|
reality {{ i18n "domainName" }}: <a-tag :color="inbound.serverName ? 'green' : 'orange'">[[ inbound.serverName ? inbound.serverName : '' ]]</a-tag>
|
||||||
</td>
|
</td>
|
||||||
<td v-else>tls: <a-tag color="red">{{ i18n "disabled" }}</a-tag>
|
<td v-else>tls: <a-tag color="red">{{ i18n "disabled" }}</a-tag>
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
@@ -43,6 +43,14 @@
|
|||||||
loading(loading) {
|
loading(loading) {
|
||||||
inModal.confirmLoading = loading;
|
inModal.confirmLoading = loading;
|
||||||
},
|
},
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const protocols = {
|
const protocols = {
|
||||||
@@ -62,6 +70,7 @@
|
|||||||
inModal: inModal,
|
inModal: inModal,
|
||||||
Protocols: protocols,
|
Protocols: protocols,
|
||||||
SSMethods: SSMethods,
|
SSMethods: SSMethods,
|
||||||
|
delayedStart: false,
|
||||||
get inbound() {
|
get inbound() {
|
||||||
return inModal.inbound;
|
return inModal.inbound;
|
||||||
},
|
},
|
||||||
@@ -70,36 +79,40 @@
|
|||||||
},
|
},
|
||||||
get isEdit() {
|
get isEdit() {
|
||||||
return inModal.isEdit;
|
return inModal.isEdit;
|
||||||
}
|
},
|
||||||
|
get client() {
|
||||||
|
return inModal.getClients(this.inbound.protocol, this.inbound.settings)[0];
|
||||||
|
},
|
||||||
|
get delayedExpireDays() {
|
||||||
|
return this.client && this.client.expiryTime < 0 ? this.client.expiryTime / -86400000 : 0;
|
||||||
|
},
|
||||||
|
set delayedExpireDays(days){
|
||||||
|
this.client.expiryTime = -86400000 * days;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
streamNetworkChange(oldValue) {
|
streamNetworkChange() {
|
||||||
if (oldValue === 'kcp') {
|
if (!inModal.inbound.canSetTls()) {
|
||||||
this.inModal.inbound.tls = false;
|
this.inModal.inbound.stream.security = 'none';
|
||||||
}
|
}
|
||||||
},
|
if (!inModal.inbound.canEnableReality()) {
|
||||||
addClient(protocol, clients) {
|
this.inModal.inbound.reality = false;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
removeClient(index, clients) {
|
|
||||||
clients.splice(index, 1);
|
|
||||||
},
|
|
||||||
isExpiry(index) {
|
|
||||||
return this.inbound.isExpiry(index)
|
|
||||||
},
|
|
||||||
isClientEnable(email) {
|
|
||||||
clientStats = this.dbInbound.clientStats ? this.dbInbound.clientStats.find(stats => stats.email === email) : null
|
|
||||||
return clientStats ? clientStats['enable'] : true
|
|
||||||
},
|
|
||||||
setDefaultCertData(){
|
setDefaultCertData(){
|
||||||
inModal.inbound.stream.tls.certs[0].certFile = app.defaultCert;
|
inModal.inbound.stream.tls.certs[0].certFile = app.defaultCert;
|
||||||
inModal.inbound.stream.tls.certs[0].keyFile = app.defaultKey;
|
inModal.inbound.stream.tls.certs[0].keyFile = app.defaultKey;
|
||||||
},
|
},
|
||||||
|
async getNewX25519Cert(){
|
||||||
|
inModal.loading(true);
|
||||||
|
const msg = await HttpUtil.post('/server/getNewX25519Cert');
|
||||||
|
inModal.loading(false);
|
||||||
|
if (!msg.success) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
inModal.inbound.stream.reality.privateKey = msg.obj.privateKey;
|
||||||
|
inModal.inbound.stream.reality.settings.publicKey = msg.obj.publicKey;
|
||||||
|
},
|
||||||
getNewEmail(client) {
|
getNewEmail(client) {
|
||||||
var chars = 'abcdefghijklmnopqrstuvwxyz1234567890';
|
var chars = 'abcdefghijklmnopqrstuvwxyz1234567890';
|
||||||
var string = '';
|
var string = '';
|
||||||
|
|||||||
@@ -132,7 +132,7 @@
|
|||||||
<template v-if="dbInbound.isVMess || dbInbound.isVLess || dbInbound.isTrojan || dbInbound.isSS">
|
<template v-if="dbInbound.isVMess || dbInbound.isVLess || dbInbound.isTrojan || dbInbound.isSS">
|
||||||
<a-tag style="margin:0;" color="green">[[ dbInbound.toInbound().stream.network ]]</a-tag>
|
<a-tag style="margin:0;" color="green">[[ dbInbound.toInbound().stream.network ]]</a-tag>
|
||||||
<a-tag style="margin:0;" v-if="dbInbound.toInbound().stream.isTls" color="cyan">tls</a-tag>
|
<a-tag style="margin:0;" v-if="dbInbound.toInbound().stream.isTls" color="cyan">tls</a-tag>
|
||||||
<a-tag style="margin:0;" v-if="dbInbound.toInbound().stream.isXTls" color="cyan">xtls</a-tag>
|
<a-tag style="margin:0;" v-if="dbInbound.toInbound().stream.isReality" color="cyan">reality</a-tag>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<template slot="clients" slot-scope="text, dbInbound">
|
<template slot="clients" slot-scope="text, dbInbound">
|
||||||
@@ -209,11 +209,10 @@
|
|||||||
</a-layout>
|
</a-layout>
|
||||||
{{template "js" .}}
|
{{template "js" .}}
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
const columns = [{
|
const columns = [{
|
||||||
title: '{{ i18n "pages.inbounds.operate" }}',
|
title: '{{ i18n "pages.inbounds.operate" }}',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
width: 30,
|
width: 40,
|
||||||
scopedSlots: { customRender: 'action' },
|
scopedSlots: { customRender: 'action' },
|
||||||
}, {
|
}, {
|
||||||
title: '{{ i18n "pages.inbounds.enable" }}',
|
title: '{{ i18n "pages.inbounds.enable" }}',
|
||||||
@@ -221,7 +220,7 @@
|
|||||||
width: 40,
|
width: 40,
|
||||||
scopedSlots: { customRender: 'enable' },
|
scopedSlots: { customRender: 'enable' },
|
||||||
}, {
|
}, {
|
||||||
title: "Id",
|
title: "ID",
|
||||||
align: 'center',
|
align: 'center',
|
||||||
dataIndex: "id",
|
dataIndex: "id",
|
||||||
width: 30,
|
width: 30,
|
||||||
@@ -529,9 +528,9 @@
|
|||||||
title: '{{ i18n "pages.client.add"}}',
|
title: '{{ i18n "pages.client.add"}}',
|
||||||
okText: '{{ i18n "pages.client.submitAdd"}}',
|
okText: '{{ i18n "pages.client.submitAdd"}}',
|
||||||
dbInbound: dbInbound,
|
dbInbound: dbInbound,
|
||||||
confirm: async (inbound, dbInbound, index) => {
|
confirm: async (clients, dbInboundId) => {
|
||||||
clientModal.loading();
|
clientModal.loading();
|
||||||
await this.addClient(inbound, dbInbound);
|
await this.addClient(clients, dbInboundId);
|
||||||
clientModal.close();
|
clientModal.close();
|
||||||
},
|
},
|
||||||
isEdit: false
|
isEdit: false
|
||||||
@@ -543,9 +542,9 @@
|
|||||||
title: '{{ i18n "pages.client.bulk"}} ' + dbInbound.remark,
|
title: '{{ i18n "pages.client.bulk"}} ' + dbInbound.remark,
|
||||||
okText: '{{ i18n "pages.client.bulk"}}',
|
okText: '{{ i18n "pages.client.bulk"}}',
|
||||||
dbInbound: dbInbound,
|
dbInbound: dbInbound,
|
||||||
confirm: async (inbound, dbInbound) => {
|
confirm: async (clients, dbInboundId) => {
|
||||||
clientsBulkModal.loading();
|
clientsBulkModal.loading();
|
||||||
await this.addClient(inbound, dbInbound);
|
await this.addClient(clients, dbInboundId);
|
||||||
clientsBulkModal.close();
|
clientsBulkModal.close();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -559,9 +558,9 @@
|
|||||||
okText: '{{ i18n "pages.client.submitEdit"}}',
|
okText: '{{ i18n "pages.client.submitEdit"}}',
|
||||||
dbInbound: dbInbound,
|
dbInbound: dbInbound,
|
||||||
index: index,
|
index: index,
|
||||||
confirm: async (inbound, dbInbound, index) => {
|
confirm: async (client, dbInboundId, index) => {
|
||||||
clientModal.loading();
|
clientModal.loading();
|
||||||
await this.updateClient(inbound, dbInbound, index);
|
await this.updateClient(client, dbInboundId, index);
|
||||||
clientModal.close();
|
clientModal.close();
|
||||||
},
|
},
|
||||||
isEdit: true
|
isEdit: true
|
||||||
@@ -571,17 +570,17 @@
|
|||||||
firstKey = Object.keys(client)[0];
|
firstKey = Object.keys(client)[0];
|
||||||
return clients.findIndex(c => c[firstKey] === client[firstKey]);
|
return clients.findIndex(c => c[firstKey] === client[firstKey]);
|
||||||
},
|
},
|
||||||
async addClient(inbound, dbInbound) {
|
async addClient(clients, dbInboundId) {
|
||||||
const data = {
|
const data = {
|
||||||
id: dbInbound.id,
|
id: dbInboundId,
|
||||||
settings: inbound.settings.toString(),
|
settings: '{"clients": [' + clients.toString() +']}',
|
||||||
};
|
};
|
||||||
await this.submit('/xui/inbound/addClient/', data);
|
await this.submit(`/xui/inbound/addClient`, data);
|
||||||
},
|
},
|
||||||
async updateClient(inbound, dbInbound, index) {
|
async updateClient(client, dbInboundId, index) {
|
||||||
const data = {
|
const data = {
|
||||||
id: dbInbound.id,
|
id: dbInboundId,
|
||||||
settings: inbound.settings.toString(),
|
settings: '{"clients": [' + client.toString() +']}',
|
||||||
};
|
};
|
||||||
await this.submit(`/xui/inbound/updateClient/${index}`, data);
|
await this.submit(`/xui/inbound/updateClient/${index}`, data);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -64,28 +64,45 @@ func (s *InboundService) getClients(inbound *model.Inbound) ([]model.Client, err
|
|||||||
return clients, nil
|
return clients, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) checkEmailsExist(emails map[string]bool, ignoreId int) (string, error) {
|
func (s *InboundService) getAllEmails() ([]string, error) {
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
var inbounds []*model.Inbound
|
var emails []string
|
||||||
db = db.Model(model.Inbound{}).Where("Protocol in ?", []model.Protocol{model.VMess, model.VLESS, model.Trojan})
|
err := db.Raw(`
|
||||||
if ignoreId > 0 {
|
SELECT JSON_EXTRACT(client.value, '$.email')
|
||||||
db = db.Where("id != ?", ignoreId)
|
FROM inbounds,
|
||||||
}
|
JSON_EACH(JSON_EXTRACT(inbounds.settings, '$.clients')) AS client
|
||||||
db = db.Find(&inbounds)
|
`).Scan(&emails).Error
|
||||||
if db.Error != nil {
|
|
||||||
return "", db.Error
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, inbound := range inbounds {
|
if err != nil {
|
||||||
clients, err := s.getClients(inbound)
|
return nil, err
|
||||||
if err != nil {
|
}
|
||||||
return "", err
|
return emails, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *InboundService) contains(slice []string, str string) bool {
|
||||||
|
for _, s := range slice {
|
||||||
|
if s == str {
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
for _, client := range clients {
|
func (s *InboundService) checkEmailsExistForClients(clients []model.Client) (string, error) {
|
||||||
if emails[client.Email] {
|
allEmails, err := s.getAllEmails()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
var emails []string
|
||||||
|
for _, client := range clients {
|
||||||
|
if client.Email != "" {
|
||||||
|
if s.contains(emails, client.Email) {
|
||||||
return client.Email, nil
|
return client.Email, nil
|
||||||
}
|
}
|
||||||
|
if s.contains(allEmails, client.Email) {
|
||||||
|
return client.Email, nil
|
||||||
|
}
|
||||||
|
emails = append(emails, client.Email)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "", nil
|
return "", nil
|
||||||
@@ -96,16 +113,23 @@ func (s *InboundService) checkEmailExistForInbound(inbound *model.Inbound) (stri
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
emails := make(map[string]bool)
|
allEmails, err := s.getAllEmails()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
var emails []string
|
||||||
for _, client := range clients {
|
for _, client := range clients {
|
||||||
if client.Email != "" {
|
if client.Email != "" {
|
||||||
if emails[client.Email] {
|
if s.contains(emails, client.Email) {
|
||||||
return client.Email, nil
|
return client.Email, nil
|
||||||
}
|
}
|
||||||
emails[client.Email] = true
|
if s.contains(allEmails, client.Email) {
|
||||||
|
return client.Email, nil
|
||||||
|
}
|
||||||
|
emails = append(emails, client.Email)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return s.checkEmailsExist(emails, inbound.Id)
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) AddInbound(inbound *model.Inbound) (*model.Inbound, error) {
|
func (s *InboundService) AddInbound(inbound *model.Inbound) (*model.Inbound, error) {
|
||||||
@@ -201,14 +225,6 @@ func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound,
|
|||||||
return inbound, common.NewError("Port already exists:", inbound.Port)
|
return inbound, common.NewError("Port already exists:", inbound.Port)
|
||||||
}
|
}
|
||||||
|
|
||||||
existEmail, err := s.checkEmailExistForInbound(inbound)
|
|
||||||
if err != nil {
|
|
||||||
return inbound, err
|
|
||||||
}
|
|
||||||
if existEmail != "" {
|
|
||||||
return inbound, common.NewError("Duplicate email:", existEmail)
|
|
||||||
}
|
|
||||||
|
|
||||||
oldInbound, err := s.GetInbound(inbound.Id)
|
oldInbound, err := s.GetInbound(inbound.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return inbound, err
|
return inbound, err
|
||||||
@@ -231,8 +247,12 @@ func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound,
|
|||||||
return inbound, db.Save(oldInbound).Error
|
return inbound, db.Save(oldInbound).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) AddInboundClient(inbound *model.Inbound) error {
|
func (s *InboundService) AddInboundClient(data *model.Inbound) error {
|
||||||
existEmail, err := s.checkEmailExistForInbound(inbound)
|
clients, err := s.getClients(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
existEmail, err := s.checkEmailsExistForClients(clients)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -241,29 +261,35 @@ func (s *InboundService) AddInboundClient(inbound *model.Inbound) error {
|
|||||||
return common.NewError("Duplicate email:", existEmail)
|
return common.NewError("Duplicate email:", existEmail)
|
||||||
}
|
}
|
||||||
|
|
||||||
clients, err := s.getClients(inbound)
|
oldInbound, err := s.GetInbound(data.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
oldInbound, err := s.GetInbound(inbound.Id)
|
var settings map[string]interface{}
|
||||||
|
err = json.Unmarshal([]byte(oldInbound.Settings), &settings)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
oldClients, err := s.getClients(oldInbound)
|
oldClients := settings["clients"].([]interface{})
|
||||||
|
var newClients []interface{}
|
||||||
|
for _, client := range clients {
|
||||||
|
newClients = append(newClients, client)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings["clients"] = append(oldClients, newClients...)
|
||||||
|
|
||||||
|
newSettings, err := json.MarshalIndent(settings, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
oldInbound.Settings = inbound.Settings
|
oldInbound.Settings = string(newSettings)
|
||||||
|
|
||||||
if len(clients[len(clients)-1].Email) > 0 {
|
for _, client := range clients {
|
||||||
s.AddClientStat(inbound.Id, &clients[len(clients)-1])
|
if len(client.Email) > 0 {
|
||||||
}
|
s.AddClientStat(data.Id, &client)
|
||||||
for i := len(oldClients); i < len(clients); i++ {
|
|
||||||
if len(clients[i].Email) > 0 {
|
|
||||||
s.AddClientStat(inbound.Id, &clients[i])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
@@ -289,21 +315,13 @@ func (s *InboundService) DelInboundClient(inbound *model.Inbound, email string)
|
|||||||
return db.Save(oldInbound).Error
|
return db.Save(oldInbound).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) UpdateInboundClient(inbound *model.Inbound, index int) error {
|
func (s *InboundService) UpdateInboundClient(data *model.Inbound, index int) error {
|
||||||
existEmail, err := s.checkEmailExistForInbound(inbound)
|
clients, err := s.getClients(data)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if existEmail != "" {
|
|
||||||
return common.NewError("Duplicate email:", existEmail)
|
|
||||||
}
|
|
||||||
|
|
||||||
clients, err := s.getClients(inbound)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
oldInbound, err := s.GetInbound(inbound.Id)
|
oldInbound, err := s.GetInbound(data.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -313,18 +331,45 @@ func (s *InboundService) UpdateInboundClient(inbound *model.Inbound, index int)
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
oldInbound.Settings = inbound.Settings
|
if len(clients[0].Email) > 0 && clients[0].Email != oldClients[index].Email {
|
||||||
|
existEmail, err := s.checkEmailsExistForClients(clients)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if existEmail != "" {
|
||||||
|
return common.NewError("Duplicate email:", existEmail)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var settings map[string]interface{}
|
||||||
|
err = json.Unmarshal([]byte(oldInbound.Settings), &settings)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settingsClients := settings["clients"].([]interface{})
|
||||||
|
var newClients []interface{}
|
||||||
|
newClients = append(newClients, clients[0])
|
||||||
|
settingsClients[index] = newClients[0]
|
||||||
|
|
||||||
|
settings["clients"] = settingsClients
|
||||||
|
|
||||||
|
newSettings, err := json.MarshalIndent(settings, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
oldInbound.Settings = string(newSettings)
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
|
|
||||||
if len(clients[index].Email) > 0 {
|
if len(clients[0].Email) > 0 {
|
||||||
if len(oldClients[index].Email) > 0 {
|
if len(oldClients[index].Email) > 0 {
|
||||||
err = s.UpdateClientStat(oldClients[index].Email, &clients[index])
|
err = s.UpdateClientStat(oldClients[index].Email, &clients[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
s.AddClientStat(inbound.Id, &clients[index])
|
s.AddClientStat(data.Id, &clients[0])
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = s.DelClientStat(db, oldClients[index].Email)
|
err = s.DelClientStat(db, oldClients[index].Email)
|
||||||
@@ -405,7 +450,7 @@ func (s *InboundService) adjustTraffics(traffics []*xray.ClientTraffic) (full_tr
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
for traffic_index, traffic := range traffics {
|
for _, traffic := range traffics {
|
||||||
inbound := &model.Inbound{}
|
inbound := &model.Inbound{}
|
||||||
client_traffic := &xray.ClientTraffic{}
|
client_traffic := &xray.ClientTraffic{}
|
||||||
err := db.Model(xray.ClientTraffic{}).Where("email = ?", traffic.Email).First(client_traffic).Error
|
err := db.Model(xray.ClientTraffic{}).Where("email = ?", traffic.Email).First(client_traffic).Error
|
||||||
@@ -464,9 +509,9 @@ func (s *InboundService) adjustTraffics(traffics []*xray.ClientTraffic) (full_tr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
traffics[traffic_index] = client_traffic
|
full_traffics = append(full_traffics, client_traffic)
|
||||||
}
|
}
|
||||||
return traffics, nil
|
return full_traffics, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) DisableInvalidInbounds() (int64, error) {
|
func (s *InboundService) DisableInvalidInbounds() (int64, error) {
|
||||||
@@ -489,6 +534,17 @@ func (s *InboundService) DisableInvalidClients() (int64, error) {
|
|||||||
count := result.RowsAffected
|
count := result.RowsAffected
|
||||||
return count, err
|
return count, err
|
||||||
}
|
}
|
||||||
|
func (s *InboundService) RemoveOrphanedTraffics() {
|
||||||
|
db := database.GetDB()
|
||||||
|
db.Exec(`
|
||||||
|
DELETE FROM client_traffics
|
||||||
|
WHERE email NOT IN (
|
||||||
|
SELECT JSON_EXTRACT(client.value, '$.email')
|
||||||
|
FROM inbounds,
|
||||||
|
JSON_EACH(JSON_EXTRACT(inbounds.settings, '$.clients')) AS client
|
||||||
|
)
|
||||||
|
`)
|
||||||
|
}
|
||||||
func (s *InboundService) AddClientStat(inboundId int, client *model.Client) error {
|
func (s *InboundService) AddClientStat(inboundId int, client *model.Client) error {
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
|
|
||||||
|
|||||||
@@ -194,9 +194,11 @@ func (s *ServerService) GetXrayVersions() ([]string, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
versions := make([]string, 0, len(releases))
|
var versions []string
|
||||||
for _, release := range releases {
|
for _, release := range releases {
|
||||||
versions = append(versions, release.TagName)
|
if release.TagName >= "v1.8.0" {
|
||||||
|
versions = append(versions, release.TagName)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return versions, nil
|
return versions, nil
|
||||||
}
|
}
|
||||||
@@ -390,3 +392,29 @@ func (s *ServerService) GetDb() ([]byte, error) {
|
|||||||
|
|
||||||
return fileContents, nil
|
return fileContents, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *ServerService) GetNewX25519Cert() (interface{}, error) {
|
||||||
|
// Run the command
|
||||||
|
cmd := exec.Command(xray.GetBinaryPath(), "x25519")
|
||||||
|
var out bytes.Buffer
|
||||||
|
cmd.Stdout = &out
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
lines := strings.Split(out.String(), "\n")
|
||||||
|
|
||||||
|
privateKeyLine := strings.Split(lines[0], ":")
|
||||||
|
publicKeyLine := strings.Split(lines[1], ":")
|
||||||
|
|
||||||
|
privateKey := strings.TrimSpace(privateKeyLine[1])
|
||||||
|
publicKey := strings.TrimSpace(publicKeyLine[1])
|
||||||
|
|
||||||
|
keyPair := map[string]interface{}{
|
||||||
|
"privateKey": privateKey,
|
||||||
|
"publicKey": publicKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
return keyPair, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ func (t *Tgbot) asnwerCallback(callbackQuery *tgbotapi.CallbackQuery, isAdmin bo
|
|||||||
case "client_traffic":
|
case "client_traffic":
|
||||||
t.getClientUsage(callbackQuery.From.ID, callbackQuery.From.UserName)
|
t.getClientUsage(callbackQuery.From.ID, callbackQuery.From.UserName)
|
||||||
case "client_commands":
|
case "client_commands":
|
||||||
t.SendMsgToTgbot(callbackQuery.From.ID, "To search for statistics, just use folowing command:\r\n \r\n<code>/usage [UID|Passowrd]</code>\r\n \r\nUse UID for vmess/vless and Password for Trojan.")
|
t.SendMsgToTgbot(callbackQuery.From.ID, "To search for statistics, just use folowing command:\r\n \r\n<code>/usage [UID|Password]</code>\r\n \r\nUse UID for vmess/vless and Password for Trojan.")
|
||||||
case "commands":
|
case "commands":
|
||||||
t.SendMsgToTgbot(callbackQuery.From.ID, "Search for a client email:\r\n<code>/usage email</code>\r\n \r\nSearch for inbounds (with client stats):\r\n<code>/inbound [remark]</code>")
|
t.SendMsgToTgbot(callbackQuery.From.ID, "Search for a client email:\r\n<code>/usage email</code>\r\n \r\nSearch for inbounds (with client stats):\r\n<code>/inbound [remark]</code>")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ func (s *XrayService) GetXrayConfig() (*xray.Config, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
s.inboundService.DisableInvalidClients()
|
s.inboundService.DisableInvalidClients()
|
||||||
|
s.inboundService.RemoveOrphanedTraffics()
|
||||||
|
|
||||||
inbounds, err := s.inboundService.GetAllInbounds()
|
inbounds, err := s.inboundService.GetAllInbounds()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
"depleted" = "Depleted"
|
"depleted" = "Depleted"
|
||||||
"depletingSoon" = "Depleting soon"
|
"depletingSoon" = "Depleting soon"
|
||||||
"domainName" = "Domain name"
|
"domainName" = "Domain name"
|
||||||
"additional" = "Alter"
|
"additional" = "Alter ID"
|
||||||
"monitor" = "Listen IP"
|
"monitor" = "Listen IP"
|
||||||
"certificate" = "Certificat"
|
"certificate" = "Certificat"
|
||||||
"fail" = "Fail"
|
"fail" = "Fail"
|
||||||
@@ -106,8 +106,8 @@
|
|||||||
"expireDate" = "Expire date"
|
"expireDate" = "Expire date"
|
||||||
"resetTraffic" = "Reset traffic"
|
"resetTraffic" = "Reset traffic"
|
||||||
"addInbound" = "Add Inbound"
|
"addInbound" = "Add Inbound"
|
||||||
"addTo" = "Add To"
|
"addTo" = "Create"
|
||||||
"revise" = "Revise"
|
"revise" = "Update"
|
||||||
"modifyInbound" = "Modify InBound"
|
"modifyInbound" = "Modify InBound"
|
||||||
"deleteInbound" = "Delete Inbound"
|
"deleteInbound" = "Delete Inbound"
|
||||||
"deleteInboundContent" = "Are you sure you want to delete inbound?"
|
"deleteInboundContent" = "Are you sure you want to delete inbound?"
|
||||||
@@ -156,7 +156,7 @@
|
|||||||
"first" = "First"
|
"first" = "First"
|
||||||
"last" = "Last"
|
"last" = "Last"
|
||||||
"prefix" = "Prefix"
|
"prefix" = "Prefix"
|
||||||
"postfix" = "postfix"
|
"postfix" = "Postfix"
|
||||||
"delayedStart" = "Start after first use"
|
"delayedStart" = "Start after first use"
|
||||||
"expireDays" = "Expire days"
|
"expireDays" = "Expire days"
|
||||||
"days" = "day(s)"
|
"days" = "day(s)"
|
||||||
|
|||||||
@@ -117,7 +117,7 @@
|
|||||||
"network" = "شبکه"
|
"network" = "شبکه"
|
||||||
"destinationPort" = "پورت مقصد"
|
"destinationPort" = "پورت مقصد"
|
||||||
"targetAddress" = "آدرس مقصد"
|
"targetAddress" = "آدرس مقصد"
|
||||||
"disableInsecureEncryption" = "رمزگذاری ناامن را غیرفعال کنید"
|
"disableInsecureEncryption" = "غیرفعال سازی رمزگذاری ناامن"
|
||||||
"monitorDesc" = "به طور پیش فرض خالی بگذارید"
|
"monitorDesc" = "به طور پیش فرض خالی بگذارید"
|
||||||
"meansNoLimit" = "یعنی بدون محدودیت"
|
"meansNoLimit" = "یعنی بدون محدودیت"
|
||||||
"totalFlow" = "کل ترافیک"
|
"totalFlow" = "کل ترافیک"
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
"depleted" = "耗尽"
|
"depleted" = "耗尽"
|
||||||
"depletingSoon" = "即将耗尽"
|
"depletingSoon" = "即将耗尽"
|
||||||
"domainName" = "域名"
|
"domainName" = "域名"
|
||||||
"additional" = "额外"
|
"additional" = "额外 ID"
|
||||||
"monitor" = "监听"
|
"monitor" = "监听"
|
||||||
"certificate" = "证书"
|
"certificate" = "证书"
|
||||||
"fail" = "失败"
|
"fail" = "失败"
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
"x-ui/config"
|
||||||
"x-ui/util/common"
|
"x-ui/util/common"
|
||||||
|
|
||||||
"github.com/Workiva/go-datastructures/queue"
|
"github.com/Workiva/go-datastructures/queue"
|
||||||
@@ -29,19 +30,19 @@ func GetBinaryName() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GetBinaryPath() string {
|
func GetBinaryPath() string {
|
||||||
return "bin/" + GetBinaryName()
|
return config.GetBinFolderPath() + "/" + GetBinaryName()
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetConfigPath() string {
|
func GetConfigPath() string {
|
||||||
return "bin/config.json"
|
return config.GetBinFolderPath() + "/config.json"
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetGeositePath() string {
|
func GetGeositePath() string {
|
||||||
return "bin/geosite.dat"
|
return config.GetBinFolderPath() + "/geosite.dat"
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetGeoipPath() string {
|
func GetGeoipPath() string {
|
||||||
return "bin/geoip.dat"
|
return config.GetBinFolderPath() + "/geoip.dat"
|
||||||
}
|
}
|
||||||
|
|
||||||
func stopProcess(p *Process) {
|
func stopProcess(p *Process) {
|
||||||
|
|||||||
Reference in New Issue
Block a user