mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2025-03-01 01:20:49 +03:00
update - shadowsocks
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
This commit is contained in:
parent
1f78842b70
commit
c2e9ee3665
@ -3,17 +3,18 @@ package sub
|
|||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
ptime "github.com/yaa110/go-persian-calendar"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
"x-ui/database"
|
"x-ui/database"
|
||||||
"x-ui/database/model"
|
"x-ui/database/model"
|
||||||
"x-ui/logger"
|
"x-ui/logger"
|
||||||
|
"x-ui/util/common"
|
||||||
"x-ui/web/service"
|
"x-ui/web/service"
|
||||||
"x-ui/xray"
|
"x-ui/xray"
|
||||||
"x-ui/util/common"
|
|
||||||
"github.com/goccy/go-json"
|
"github.com/goccy/go-json"
|
||||||
|
ptime "github.com/yaa110/go-persian-calendar"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SubService struct {
|
type SubService struct {
|
||||||
@ -57,7 +58,7 @@ func (s *SubService) GetSubs(subId string, host string) ([]string, []string, err
|
|||||||
}
|
}
|
||||||
for _, client := range clients {
|
for _, client := range clients {
|
||||||
if client.Enable && client.SubID == subId {
|
if client.Enable && client.SubID == subId {
|
||||||
link := s.getLink(inbound, client.Email,client.ExpiryTime)
|
link := s.getLink(inbound, client.Email, client.ExpiryTime)
|
||||||
result = append(result, link)
|
result = append(result, link)
|
||||||
clientTraffics = append(clientTraffics, s.getClientTraffics(inbound.ClientStats, client.Email))
|
clientTraffics = append(clientTraffics, s.getClientTraffics(inbound.ClientStats, client.Email))
|
||||||
}
|
}
|
||||||
@ -143,7 +144,7 @@ func (s *SubService) genVmessLink(inbound *model.Inbound, email string, expiryTi
|
|||||||
}
|
}
|
||||||
|
|
||||||
remainedTraffic := s.getRemainedTraffic(email)
|
remainedTraffic := s.getRemainedTraffic(email)
|
||||||
expiryTimeString := getExpiryTime(expiryTime)
|
expiryTimeString := getExpiryTime(expiryTime)
|
||||||
|
|
||||||
remark := fmt.Sprintf("%s: %s- %s", email, remainedTraffic, expiryTimeString)
|
remark := fmt.Sprintf("%s: %s- %s", email, remainedTraffic, expiryTimeString)
|
||||||
obj := map[string]interface{}{
|
obj := map[string]interface{}{
|
||||||
@ -456,7 +457,7 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string, expiryTi
|
|||||||
url.RawQuery = q.Encode()
|
url.RawQuery = q.Encode()
|
||||||
|
|
||||||
remainedTraffic := s.getRemainedTraffic(email)
|
remainedTraffic := s.getRemainedTraffic(email)
|
||||||
expiryTimeString := getExpiryTime(expiryTime)
|
expiryTimeString := getExpiryTime(expiryTime)
|
||||||
|
|
||||||
remark := fmt.Sprintf("%s: %s- %s", email, remainedTraffic, expiryTimeString)
|
remark := fmt.Sprintf("%s: %s- %s", email, remainedTraffic, expiryTimeString)
|
||||||
|
|
||||||
@ -668,7 +669,7 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string, expiryT
|
|||||||
url.RawQuery = q.Encode()
|
url.RawQuery = q.Encode()
|
||||||
|
|
||||||
remainedTraffic := s.getRemainedTraffic(email)
|
remainedTraffic := s.getRemainedTraffic(email)
|
||||||
expiryTimeString := getExpiryTime(expiryTime)
|
expiryTimeString := getExpiryTime(expiryTime)
|
||||||
|
|
||||||
remark := fmt.Sprintf("%s: %s- %s", email, remainedTraffic, expiryTimeString)
|
remark := fmt.Sprintf("%s: %s- %s", email, remainedTraffic, expiryTimeString)
|
||||||
|
|
||||||
@ -695,6 +696,8 @@ func (s *SubService) genShadowsocksLink(inbound *model.Inbound, email string, ex
|
|||||||
if inbound.Protocol != model.Shadowsocks {
|
if inbound.Protocol != model.Shadowsocks {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
var stream map[string]interface{}
|
||||||
|
json.Unmarshal([]byte(inbound.StreamSettings), &stream)
|
||||||
clients, _ := s.inboundService.GetClients(inbound)
|
clients, _ := s.inboundService.GetClients(inbound)
|
||||||
|
|
||||||
var settings map[string]interface{}
|
var settings map[string]interface{}
|
||||||
@ -708,13 +711,69 @@ func (s *SubService) genShadowsocksLink(inbound *model.Inbound, email string, ex
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
streamNetwork := stream["network"].(string)
|
||||||
|
params := make(map[string]string)
|
||||||
|
params["type"] = streamNetwork
|
||||||
|
|
||||||
|
switch streamNetwork {
|
||||||
|
case "tcp":
|
||||||
|
tcp, _ := stream["tcpSettings"].(map[string]interface{})
|
||||||
|
header, _ := tcp["header"].(map[string]interface{})
|
||||||
|
typeStr, _ := header["type"].(string)
|
||||||
|
if typeStr == "http" {
|
||||||
|
request := header["request"].(map[string]interface{})
|
||||||
|
requestPath, _ := request["path"].([]interface{})
|
||||||
|
params["path"] = requestPath[0].(string)
|
||||||
|
headers, _ := request["headers"].(map[string]interface{})
|
||||||
|
params["host"] = searchHost(headers)
|
||||||
|
params["headerType"] = "http"
|
||||||
|
}
|
||||||
|
case "kcp":
|
||||||
|
kcp, _ := stream["kcpSettings"].(map[string]interface{})
|
||||||
|
header, _ := kcp["header"].(map[string]interface{})
|
||||||
|
params["headerType"] = header["type"].(string)
|
||||||
|
params["seed"] = kcp["seed"].(string)
|
||||||
|
case "ws":
|
||||||
|
ws, _ := stream["wsSettings"].(map[string]interface{})
|
||||||
|
params["path"] = ws["path"].(string)
|
||||||
|
headers, _ := ws["headers"].(map[string]interface{})
|
||||||
|
params["host"] = searchHost(headers)
|
||||||
|
case "http":
|
||||||
|
http, _ := stream["httpSettings"].(map[string]interface{})
|
||||||
|
params["path"] = http["path"].(string)
|
||||||
|
params["host"] = searchHost(http)
|
||||||
|
case "quic":
|
||||||
|
quic, _ := stream["quicSettings"].(map[string]interface{})
|
||||||
|
params["quicSecurity"] = quic["security"].(string)
|
||||||
|
params["key"] = quic["key"].(string)
|
||||||
|
header := quic["header"].(map[string]interface{})
|
||||||
|
params["headerType"] = header["type"].(string)
|
||||||
|
case "grpc":
|
||||||
|
grpc, _ := stream["grpcSettings"].(map[string]interface{})
|
||||||
|
params["serviceName"] = grpc["serviceName"].(string)
|
||||||
|
if grpc["multiMode"].(bool) {
|
||||||
|
params["mode"] = "multi"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
encPart := fmt.Sprintf("%s:%s:%s", method, inboundPassword, clients[clientIndex].Password)
|
encPart := fmt.Sprintf("%s:%s:%s", method, inboundPassword, clients[clientIndex].Password)
|
||||||
|
link := fmt.Sprintf("ss://%s@%s:%d", base64.StdEncoding.EncodeToString([]byte(encPart)), address, inbound.Port)
|
||||||
|
url, _ := url.Parse(link)
|
||||||
|
q := url.Query()
|
||||||
|
|
||||||
remainedTraffic := s.getRemainedTraffic(clients[clientIndex].Email)
|
for k, v := range params {
|
||||||
expiryTimeString := getExpiryTime(expiryTime)
|
q.Add(k, v)
|
||||||
|
}
|
||||||
|
|
||||||
remark := fmt.Sprintf("%s: %s- %s", clients[clientIndex].Email, remainedTraffic ,expiryTimeString)
|
// Set the new query values on the URL
|
||||||
return fmt.Sprintf("ss://%s@%s:%d#%s", base64.StdEncoding.EncodeToString([]byte(encPart)), address, inbound.Port, remark)
|
url.RawQuery = q.Encode()
|
||||||
|
|
||||||
|
remainedTraffic := s.getRemainedTraffic(email)
|
||||||
|
expiryTimeString := getExpiryTime(expiryTime)
|
||||||
|
|
||||||
|
remark := fmt.Sprintf("%s: %s- %s", clients[clientIndex].Email, remainedTraffic, expiryTimeString)
|
||||||
|
url.Fragment = remark
|
||||||
|
return url.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func searchKey(data interface{}, key string) (interface{}, bool) {
|
func searchKey(data interface{}, key string) (interface{}, bool) {
|
||||||
@ -759,26 +818,26 @@ func searchHost(headers interface{}) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func getExpiryTime(expiryTime int64) string{
|
func getExpiryTime(expiryTime int64) string {
|
||||||
now := time.Now().Unix()
|
now := time.Now().Unix()
|
||||||
expiryString := ""
|
expiryString := ""
|
||||||
|
|
||||||
timeDifference := expiryTime/1000 - now
|
timeDifference := expiryTime/1000 - now
|
||||||
|
|
||||||
if expiryTime == 0 {
|
if expiryTime == 0 {
|
||||||
expiryString = "♾ ⏳"
|
expiryString = "♾ ⏳"
|
||||||
} else if timeDifference > 172800 {
|
} else if timeDifference > 172800 {
|
||||||
expiryString = fmt.Sprintf("%s ⏳", ptime.Unix((expiryTime / 1000), 0).Format("yy-MM-dd hh:mm"))
|
expiryString = fmt.Sprintf("%s ⏳", ptime.Unix((expiryTime/1000), 0).Format("yy-MM-dd hh:mm"))
|
||||||
} else if expiryTime < 0 {
|
} else if expiryTime < 0 {
|
||||||
expiryString = fmt.Sprintf("%d ⏳", expiryTime/-86400000)
|
expiryString = fmt.Sprintf("%d ⏳", expiryTime/-86400000)
|
||||||
} else {
|
} else {
|
||||||
expiryString = fmt.Sprintf("%s %d ⏳", "ساعت", timeDifference/3600)
|
expiryString = fmt.Sprintf("%s %d ⏳", "ساعت", timeDifference/3600)
|
||||||
}
|
}
|
||||||
|
|
||||||
return expiryString
|
return expiryString
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SubService) getRemainedTraffic( email string) string{
|
func (s *SubService) getRemainedTraffic(email string) string {
|
||||||
traffic, err := s.inboundService.GetClientTrafficByEmail(email)
|
traffic, err := s.inboundService.GetClientTrafficByEmail(email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warning(err)
|
logger.Warning(err)
|
||||||
@ -788,7 +847,7 @@ func (s *SubService) getRemainedTraffic( email string) string{
|
|||||||
if traffic.Total == 0 {
|
if traffic.Total == 0 {
|
||||||
remainedTraffic = "♾ 📊"
|
remainedTraffic = "♾ 📊"
|
||||||
} else {
|
} else {
|
||||||
remainedTraffic = fmt.Sprintf("%s%s" ,common.FormatTraffic(traffic.Total-(traffic.Up+traffic.Down)), "📊")
|
remainedTraffic = fmt.Sprintf("%s%s", common.FormatTraffic(traffic.Total-(traffic.Up+traffic.Down)), "📊")
|
||||||
}
|
}
|
||||||
|
|
||||||
return remainedTraffic
|
return remainedTraffic
|
||||||
|
@ -16,8 +16,12 @@ const VmessMethods = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const SSMethods = {
|
const SSMethods = {
|
||||||
BLAKE3_AES_128_GCM: '2022-blake3-aes-128-gcm',
|
CHACHA20_POLY1305: 'chacha20-poly1305',
|
||||||
BLAKE3_AES_256_GCM: '2022-blake3-aes-256-gcm',
|
AES_256_GCM: 'aes-256-gcm',
|
||||||
|
AES_128_GCM: 'aes-128-gcm',
|
||||||
|
BLAKE3_AES_128_GCM: '2022-blake3-aes-128-gcm',
|
||||||
|
BLAKE3_AES_256_GCM: '2022-blake3-aes-256-gcm',
|
||||||
|
BLAKE3_CHACHA20_POLY1305: '2022-blake3-chacha20-poly1305',
|
||||||
};
|
};
|
||||||
|
|
||||||
const XTLS_FLOW_CONTROL = {
|
const XTLS_FLOW_CONTROL = {
|
||||||
@ -511,7 +515,8 @@ class TlsStreamSettings extends XrayCommonClass {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!ObjectUtil.isEmpty(json.settings)) {
|
if (!ObjectUtil.isEmpty(json.settings)) {
|
||||||
settings = new TlsStreamSettings.Settings(json.settings.allowInsecure , json.settings.fingerprint, json.settings.serverName, json.settings.domains); }
|
settings = new TlsStreamSettings.Settings(json.settings.allowInsecure , json.settings.fingerprint, json.settings.serverName, json.settings.domains);
|
||||||
|
}
|
||||||
return new TlsStreamSettings(
|
return new TlsStreamSettings(
|
||||||
json.serverName,
|
json.serverName,
|
||||||
json.minVersion,
|
json.minVersion,
|
||||||
@ -980,7 +985,6 @@ class Inbound extends XrayCommonClass {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//for Reality
|
|
||||||
get reality() {
|
get reality() {
|
||||||
return this.stream.security === 'reality';
|
return this.stream.security === 'reality';
|
||||||
}
|
}
|
||||||
@ -1034,6 +1038,9 @@ class Inbound extends XrayCommonClass {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
get isSSMultiUser() {
|
||||||
|
return [SSMethods.BLAKE3_AES_128_GCM,SSMethods.BLAKE3_AES_256_GCM].includes(this.method);
|
||||||
|
}
|
||||||
|
|
||||||
get serverName() {
|
get serverName() {
|
||||||
if (this.stream.isTls || this.stream.isXtls || this.stream.isReality) {
|
if (this.stream.isTls || this.stream.isXtls || this.stream.isReality) {
|
||||||
@ -1103,7 +1110,7 @@ class Inbound extends XrayCommonClass {
|
|||||||
return this.settings.trojans[index].expiryTime < new Date().getTime();
|
return this.settings.trojans[index].expiryTime < new Date().getTime();
|
||||||
return false
|
return false
|
||||||
case Protocols.SHADOWSOCKS:
|
case Protocols.SHADOWSOCKS:
|
||||||
if(this.settings.shadowsockses[index].expiryTime > 0)
|
if(this.settings.shadowsockses.length > 0 && this.settings.shadowsockses[index].expiryTime > 0)
|
||||||
return this.settings.shadowsockses[index].expiryTime < new Date().getTime();
|
return this.settings.shadowsockses[index].expiryTime < new Date().getTime();
|
||||||
return false
|
return false
|
||||||
default:
|
default:
|
||||||
@ -1184,6 +1191,7 @@ class Inbound extends XrayCommonClass {
|
|||||||
case Protocols.VMESS:
|
case Protocols.VMESS:
|
||||||
case Protocols.VLESS:
|
case Protocols.VLESS:
|
||||||
case Protocols.TROJAN:
|
case Protocols.TROJAN:
|
||||||
|
case Protocols.SHADOWSOCKS:
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
@ -1410,8 +1418,66 @@ class Inbound extends XrayCommonClass {
|
|||||||
genSSLink(address='', remark='', clientIndex = 0) {
|
genSSLink(address='', remark='', clientIndex = 0) {
|
||||||
let settings = this.settings;
|
let settings = this.settings;
|
||||||
const port = this.port;
|
const port = this.port;
|
||||||
|
const type = this.stream.network;
|
||||||
|
const params = new Map();
|
||||||
|
params.set("type", this.stream.network);
|
||||||
|
switch (type) {
|
||||||
|
case "tcp":
|
||||||
|
const tcp = this.stream.tcp;
|
||||||
|
if (tcp.type === 'http') {
|
||||||
|
const request = tcp.request;
|
||||||
|
params.set("path", request.path.join(','));
|
||||||
|
const index = request.headers.findIndex(header => header.name.toLowerCase() === 'host');
|
||||||
|
if (index >= 0) {
|
||||||
|
const host = request.headers[index].value;
|
||||||
|
params.set("host", host);
|
||||||
|
}
|
||||||
|
params.set("headerType", 'http');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "kcp":
|
||||||
|
const kcp = this.stream.kcp;
|
||||||
|
params.set("headerType", kcp.type);
|
||||||
|
params.set("seed", kcp.seed);
|
||||||
|
break;
|
||||||
|
case "ws":
|
||||||
|
const ws = this.stream.ws;
|
||||||
|
params.set("path", ws.path);
|
||||||
|
const index = ws.headers.findIndex(header => header.name.toLowerCase() === 'host');
|
||||||
|
if (index >= 0) {
|
||||||
|
const host = ws.headers[index].value;
|
||||||
|
params.set("host", host);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "http":
|
||||||
|
const http = this.stream.http;
|
||||||
|
params.set("path", http.path);
|
||||||
|
params.set("host", http.host);
|
||||||
|
break;
|
||||||
|
case "quic":
|
||||||
|
const quic = this.stream.quic;
|
||||||
|
params.set("quicSecurity", quic.security);
|
||||||
|
params.set("key", quic.key);
|
||||||
|
params.set("headerType", quic.type);
|
||||||
|
break;
|
||||||
|
case "grpc":
|
||||||
|
const grpc = this.stream.grpc;
|
||||||
|
params.set("serviceName", grpc.serviceName);
|
||||||
|
if(grpc.multiMode){
|
||||||
|
params.set("mode", "multi");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return 'ss://' + safeBase64(settings.method + ':' + settings.password + ':' +settings.shadowsockses[clientIndex].password) + '@' + address + ':' + this.port + '#' + encodeURIComponent(remark);
|
let clientPassword = this.isSSMultiUser ? ':' + settings.shadowsockses[clientIndex].password : '';
|
||||||
|
|
||||||
|
let link = `ss://${safeBase64(settings.method + ':' + settings.password + clientPassword)}@${address}:${this.port}`;
|
||||||
|
const url = new URL(link);
|
||||||
|
for (const [key, value] of params) {
|
||||||
|
url.searchParams.set(key, value)
|
||||||
|
}
|
||||||
|
url.hash = encodeURIComponent(remark);
|
||||||
|
return url.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
genTrojanLink(address = '', remark = '', clientIndex = 0) {
|
genTrojanLink(address = '', remark = '', clientIndex = 0) {
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
this.inbound = dbInbound.toInbound();
|
this.inbound = dbInbound.toInbound();
|
||||||
settings = JSON.parse(this.inbound.settings);
|
settings = JSON.parse(this.inbound.settings);
|
||||||
this.client = settings.clients[clientIndex];
|
this.client = settings.clients[clientIndex];
|
||||||
remark = this.dbInbound.remark + "-" + this.client.email;
|
remark = this.dbInbound.remark + ( this.client ? "-" + this.client.email : '');
|
||||||
address = this.dbInbound.address;
|
address = this.dbInbound.address;
|
||||||
this.subId = '';
|
this.subId = '';
|
||||||
this.qrcodes = [];
|
this.qrcodes = [];
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
{{define "form/shadowsocks"}}
|
{{define "form/shadowsocks"}}
|
||||||
<a-form layout="inline" style="padding: 10px 0px;">
|
<a-form layout="inline" style="padding: 10px 0px;">
|
||||||
|
<template v-if="inbound.isSSMultiUser">
|
||||||
<a-collapse activeKey="0" v-for="(client, index) in inbound.settings.shadowsockses.slice(0,1)" v-if="!isEdit">
|
<a-collapse activeKey="0" v-for="(client, index) in inbound.settings.shadowsockses.slice(0,1)" v-if="!isEdit">
|
||||||
<a-collapse-panel header='{{ i18n "pages.inbounds.client" }}'>
|
<a-collapse-panel header='{{ i18n "pages.inbounds.client" }}'>
|
||||||
<a-form-item>
|
<a-form-item>
|
||||||
@ -106,10 +107,11 @@
|
|||||||
</table>
|
</table>
|
||||||
</a-collapse-panel>
|
</a-collapse-panel>
|
||||||
</a-collapse>
|
</a-collapse>
|
||||||
|
</template>
|
||||||
</a-form>
|
</a-form>
|
||||||
<a-form layout="inline">
|
<a-form layout="inline">
|
||||||
<a-form-item label='{{ i18n "encryption" }}'>
|
<a-form-item label='{{ i18n "encryption" }}'>
|
||||||
<a-select v-model="inbound.settings.method" style="width: 250px;" :dropdown-class-name="themeSwitcher.darkCardClass">
|
<a-select v-model="inbound.settings.method" style="width: 250px;" :dropdown-class-name="themeSwitcher.darkCardClass" @change="SSMethodChange">
|
||||||
<a-select-option v-for="method in SSMethods" :value="method">[[ method ]]</a-select-option>
|
<a-select-option v-for="method in SSMethods" :value="method">[[ method ]]</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
@ -179,6 +179,19 @@
|
|||||||
<td><a-tag color="green">[[ inbound.settings.network ]]</a-tag></td>
|
<td><a-tag color="green">[[ inbound.settings.network ]]</a-tag></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
<template v-if="inbound.protocol == Protocols.SHADOWSOCKS && !inbound.isSSMultiUser">
|
||||||
|
<a-divider>URL</a-divider>
|
||||||
|
<a-row v-for="(link,index) in infoModal.links">
|
||||||
|
<a-col :span="22"><a-tag color="cyan">[[ link.remark ]]</a-tag><br />[[ link.link ]]</a-col>
|
||||||
|
<a-col :span="2" style="text-align: right;">
|
||||||
|
<a-tooltip title='{{ i18n "copy" }}'>
|
||||||
|
<button class="ant-btn ant-btn-primary" :id="'copy-url-link-'+index" @click="copyToClipboard('copy-url-link-'+index, link.link)">
|
||||||
|
<a-icon type="snippets"></a-icon>
|
||||||
|
</button>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</template>
|
||||||
<table v-if="inbound.protocol == Protocols.DOKODEMO" style="margin-bottom: 10px; width: 100%;">
|
<table v-if="inbound.protocol == Protocols.DOKODEMO" style="margin-bottom: 10px; width: 100%;">
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{ i18n "pages.inbounds.targetAddress" }}</th>
|
<th>{{ i18n "pages.inbounds.targetAddress" }}</th>
|
||||||
@ -251,7 +264,7 @@
|
|||||||
this.clientSettings = this.settings.clients ? Object.values(this.settings.clients)[index] : null;
|
this.clientSettings = this.settings.clients ? Object.values(this.settings.clients)[index] : null;
|
||||||
this.isExpired = this.inbound.isExpiry(index);
|
this.isExpired = this.inbound.isExpiry(index);
|
||||||
this.clientStats = this.settings.clients ? this.dbInbound.clientStats.find(row => row.email === this.clientSettings.email) : [];
|
this.clientStats = this.settings.clients ? this.dbInbound.clientStats.find(row => row.email === this.clientSettings.email) : [];
|
||||||
remark = this.dbInbound.remark + "-" + this.clientSettings.email;
|
remark = this.dbInbound.remark + ( this.clientSettings ? "-" + this.clientSettings.email : '');
|
||||||
address = this.dbInbound.address;
|
address = this.dbInbound.address;
|
||||||
this.links = [];
|
this.links = [];
|
||||||
if (this.inbound.tls && !ObjectUtil.isArrEmpty(this.inbound.stream.tls.settings.domains)) {
|
if (this.inbound.tls && !ObjectUtil.isArrEmpty(this.inbound.stream.tls.settings.domains)) {
|
||||||
|
@ -54,23 +54,11 @@
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const protocols = {
|
|
||||||
VMESS: Protocols.VMESS,
|
|
||||||
VLESS: Protocols.VLESS,
|
|
||||||
TROJAN: Protocols.TROJAN,
|
|
||||||
SHADOWSOCKS: Protocols.SHADOWSOCKS,
|
|
||||||
DOKODEMO: Protocols.DOKODEMO,
|
|
||||||
SOCKS: Protocols.SOCKS,
|
|
||||||
HTTP: Protocols.HTTP,
|
|
||||||
};
|
|
||||||
|
|
||||||
new Vue({
|
new Vue({
|
||||||
delimiters: ['[[', ']]'],
|
delimiters: ['[[', ']]'],
|
||||||
el: '#inbound-modal',
|
el: '#inbound-modal',
|
||||||
data: {
|
data: {
|
||||||
inModal: inModal,
|
inModal: inModal,
|
||||||
Protocols: protocols,
|
|
||||||
SSMethods: SSMethods,
|
|
||||||
delayedStart: false,
|
delayedStart: false,
|
||||||
get inbound() {
|
get inbound() {
|
||||||
return inModal.inbound;
|
return inModal.inbound;
|
||||||
@ -117,6 +105,17 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
SSMethodChange() {
|
||||||
|
if (this.inModal.inbound.isSSMultiUser) {
|
||||||
|
if (this.inModal.inbound.settings.shadowsockses.length ==0){
|
||||||
|
this.inModal.inbound.settings.shadowsockses = [new Inbound.ShadowsocksSettings.Shadowsocks()];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this.inModal.inbound.settings.shadowsockses.length > 0){
|
||||||
|
this.inModal.inbound.settings.shadowsockses = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
setDefaultCertData(index) {
|
setDefaultCertData(index) {
|
||||||
inModal.inbound.stream.tls.certs[index].certFile = app.defaultCert;
|
inModal.inbound.stream.tls.certs[index].certFile = app.defaultCert;
|
||||||
inModal.inbound.stream.tls.certs[index].keyFile = app.defaultKey;
|
inModal.inbound.stream.tls.certs[index].keyFile = app.defaultKey;
|
||||||
|
@ -131,7 +131,11 @@
|
|||||||
<a-icon type="edit"></a-icon>
|
<a-icon type="edit"></a-icon>
|
||||||
{{ i18n "edit" }}
|
{{ i18n "edit" }}
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
<template v-if="dbInbound.isTrojan || dbInbound.isVLess || dbInbound.isVMess || dbInbound.isSS">
|
<a-menu-item key="qrcode" v-if="dbInbound.isSS && !dbInbound.toInbound().isSSMultiUser">
|
||||||
|
<a-icon type="qrcode"></a-icon>
|
||||||
|
{{ i18n "qrCode" }}
|
||||||
|
</a-menu-item>
|
||||||
|
<template v-if="dbInbound.isTrojan || dbInbound.isVLess || dbInbound.isVMess || dbInbound.toInbound().isSSMultiUser">
|
||||||
<a-menu-item key="addClient">
|
<a-menu-item key="addClient">
|
||||||
<a-icon type="user-add"></a-icon>
|
<a-icon type="user-add"></a-icon>
|
||||||
{{ i18n "pages.client.add"}}
|
{{ i18n "pages.client.add"}}
|
||||||
@ -255,7 +259,7 @@
|
|||||||
{{template "client_table"}}
|
{{template "client_table"}}
|
||||||
</a-table>
|
</a-table>
|
||||||
<a-table
|
<a-table
|
||||||
v-else-if="record.protocol === Protocols.TROJAN || record.protocol === Protocols.SHADOWSOCKS"
|
v-else-if="record.protocol === Protocols.TROJAN || record.toInbound().isSSMultiUser"
|
||||||
:row-key="client => client.id"
|
:row-key="client => client.id"
|
||||||
:columns="innerTrojanColumns"
|
:columns="innerTrojanColumns"
|
||||||
:data-source="getInboundClients(record)"
|
:data-source="getInboundClients(record)"
|
||||||
@ -274,7 +278,6 @@
|
|||||||
{{template "js" .}}
|
{{template "js" .}}
|
||||||
{{template "component/themeSwitcher" .}}
|
{{template "component/themeSwitcher" .}}
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
const columns = [{
|
const columns = [{
|
||||||
title: '{{ i18n "pages.inbounds.operate" }}',
|
title: '{{ i18n "pages.inbounds.operate" }}',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
@ -357,7 +360,7 @@
|
|||||||
trafficDiff: 0,
|
trafficDiff: 0,
|
||||||
defaultCert: '',
|
defaultCert: '',
|
||||||
defaultKey: '',
|
defaultKey: '',
|
||||||
clientCount: {},
|
clientCount: [],
|
||||||
isRefreshEnabled: localStorage.getItem("isRefreshEnabled") === "true" ? true : false,
|
isRefreshEnabled: localStorage.getItem("isRefreshEnabled") === "true" ? true : false,
|
||||||
refreshing: false,
|
refreshing: false,
|
||||||
refreshInterval: Number(localStorage.getItem("refreshInterval")) || 5000,
|
refreshInterval: Number(localStorage.getItem("refreshInterval")) || 5000,
|
||||||
@ -409,12 +412,16 @@
|
|||||||
setInbounds(dbInbounds) {
|
setInbounds(dbInbounds) {
|
||||||
this.inbounds.splice(0);
|
this.inbounds.splice(0);
|
||||||
this.dbInbounds.splice(0);
|
this.dbInbounds.splice(0);
|
||||||
|
this.clientCount.splice(0);
|
||||||
for (const inbound of dbInbounds) {
|
for (const inbound of dbInbounds) {
|
||||||
const dbInbound = new DBInbound(inbound);
|
const dbInbound = new DBInbound(inbound);
|
||||||
to_inbound = dbInbound.toInbound()
|
to_inbound = dbInbound.toInbound()
|
||||||
this.inbounds.push(to_inbound);
|
this.inbounds.push(to_inbound);
|
||||||
this.dbInbounds.push(dbInbound);
|
this.dbInbounds.push(dbInbound);
|
||||||
if ([Protocols.VMESS, Protocols.VLESS, Protocols.TROJAN, Protocols.SHADOWSOCKS].includes(inbound.protocol)) {
|
if ([Protocols.VMESS, Protocols.VLESS, Protocols.TROJAN, Protocols.SHADOWSOCKS].includes(inbound.protocol)) {
|
||||||
|
if (inbound.protocol === Protocols.SHADOWSOCKS && (!to_inbound.isSSMultiUser)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
this.clientCount[inbound.id] = this.getClientCounts(inbound, to_inbound);
|
this.clientCount[inbound.id] = this.getClientCounts(inbound, to_inbound);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user