mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2025-04-20 05:52:24 +00:00
Expand multiDomain to externalProxy #1300
This commit is contained in:
parent
bcc897640e
commit
5fbf8f0d53
18 changed files with 452 additions and 365 deletions
|
@ -53,6 +53,7 @@ func (s *SubService) GetSubs(subId string, host string, showInfo bool) ([]string
|
||||||
json.Unmarshal([]byte(fallbackMaster.StreamSettings), &masterStream)
|
json.Unmarshal([]byte(fallbackMaster.StreamSettings), &masterStream)
|
||||||
stream["security"] = masterStream["security"]
|
stream["security"] = masterStream["security"]
|
||||||
stream["tlsSettings"] = masterStream["tlsSettings"]
|
stream["tlsSettings"] = masterStream["tlsSettings"]
|
||||||
|
stream["externalProxy"] = masterStream["externalProxy"]
|
||||||
modifiedStream, _ := json.MarshalIndent(stream, "", " ")
|
modifiedStream, _ := json.MarshalIndent(stream, "", " ")
|
||||||
inbound.StreamSettings = string(modifiedStream)
|
inbound.StreamSettings = string(modifiedStream)
|
||||||
}
|
}
|
||||||
|
@ -96,7 +97,14 @@ func (s *SubService) GetSubs(subId string, host string, showInfo bool) ([]string
|
||||||
func (s *SubService) getInboundsBySubId(subId string) ([]*model.Inbound, error) {
|
func (s *SubService) getInboundsBySubId(subId string) ([]*model.Inbound, error) {
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
var inbounds []*model.Inbound
|
var inbounds []*model.Inbound
|
||||||
err := db.Model(model.Inbound{}).Preload("ClientStats").Where("settings like ? and enable = ?", fmt.Sprintf(`%%"subId": "%s"%%`, subId), true).Find(&inbounds).Error
|
err := db.Model(model.Inbound{}).Preload("ClientStats").Where(`id in (
|
||||||
|
SELECT DISTINCT inbounds.id
|
||||||
|
FROM inbounds,
|
||||||
|
JSON_EACH(JSON_EXTRACT(inbounds.settings, '$.clients')) AS client
|
||||||
|
WHERE
|
||||||
|
protocol in ('vmess','vless','trojan','shadowsocks')
|
||||||
|
AND JSON_EXTRACT(client.value, '$.subId') = ? AND enable = ?
|
||||||
|
)`, subId, true).Find(&inbounds).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -196,7 +204,6 @@ func (s *SubService) genVmessLink(inbound *model.Inbound, email string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
security, _ := stream["security"].(string)
|
security, _ := stream["security"].(string)
|
||||||
var domains []interface{}
|
|
||||||
obj["tls"] = security
|
obj["tls"] = security
|
||||||
if security == "tls" {
|
if security == "tls" {
|
||||||
tlsSetting, _ := stream["tlsSettings"].(map[string]interface{})
|
tlsSetting, _ := stream["tlsSettings"].(map[string]interface{})
|
||||||
|
@ -208,24 +215,18 @@ func (s *SubService) genVmessLink(inbound *model.Inbound, email string) string {
|
||||||
}
|
}
|
||||||
obj["alpn"] = strings.Join(alpn, ",")
|
obj["alpn"] = strings.Join(alpn, ",")
|
||||||
}
|
}
|
||||||
tlsSettings, _ := searchKey(tlsSetting, "settings")
|
if sniValue, ok := searchKey(tlsSetting, "serverName"); ok {
|
||||||
if tlsSetting != nil {
|
|
||||||
if sniValue, ok := searchKey(tlsSettings, "serverName"); ok {
|
|
||||||
obj["sni"], _ = sniValue.(string)
|
obj["sni"], _ = sniValue.(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tlsSettings, _ := searchKey(tlsSetting, "settings")
|
||||||
|
if tlsSetting != nil {
|
||||||
if fpValue, ok := searchKey(tlsSettings, "fingerprint"); ok {
|
if fpValue, ok := searchKey(tlsSettings, "fingerprint"); ok {
|
||||||
obj["fp"], _ = fpValue.(string)
|
obj["fp"], _ = fpValue.(string)
|
||||||
}
|
}
|
||||||
if insecure, ok := searchKey(tlsSettings, "allowInsecure"); ok {
|
if insecure, ok := searchKey(tlsSettings, "allowInsecure"); ok {
|
||||||
obj["allowInsecure"], _ = insecure.(bool)
|
obj["allowInsecure"], _ = insecure.(bool)
|
||||||
}
|
}
|
||||||
if domainSettings, ok := searchKey(tlsSettings, "domains"); ok {
|
|
||||||
domains, _ = domainSettings.([]interface{})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
serverName, _ := tlsSetting["serverName"].(string)
|
|
||||||
if serverName != "" {
|
|
||||||
obj["add"] = serverName
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,16 +240,30 @@ func (s *SubService) genVmessLink(inbound *model.Inbound, email string) string {
|
||||||
}
|
}
|
||||||
obj["id"] = clients[clientIndex].ID
|
obj["id"] = clients[clientIndex].ID
|
||||||
|
|
||||||
if len(domains) > 0 {
|
externalProxies, _ := stream["externalProxy"].([]interface{})
|
||||||
|
|
||||||
|
if len(externalProxies) > 0 {
|
||||||
links := ""
|
links := ""
|
||||||
for index, d := range domains {
|
for index, externalProxy := range externalProxies {
|
||||||
domain := d.(map[string]interface{})
|
ep, _ := externalProxy.(map[string]interface{})
|
||||||
obj["ps"] = s.genRemark(inbound, email, domain["remark"].(string))
|
newSecurity, _ := ep["forceTls"].(string)
|
||||||
obj["add"] = domain["domain"].(string)
|
newObj := map[string]interface{}{}
|
||||||
|
for key, value := range obj {
|
||||||
|
if !(newSecurity == "none" && (key == "alpn" || key == "sni" || key == "fp" || key == "allowInsecure")) {
|
||||||
|
newObj[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newObj["ps"] = s.genRemark(inbound, email, ep["remark"].(string))
|
||||||
|
newObj["add"] = ep["dest"].(string)
|
||||||
|
newObj["port"] = int(ep["port"].(float64))
|
||||||
|
|
||||||
|
if newSecurity != "same" {
|
||||||
|
newObj["tls"] = newSecurity
|
||||||
|
}
|
||||||
if index > 0 {
|
if index > 0 {
|
||||||
links += "\n"
|
links += "\n"
|
||||||
}
|
}
|
||||||
jsonStr, _ := json.MarshalIndent(obj, "", " ")
|
jsonStr, _ := json.MarshalIndent(newObj, "", " ")
|
||||||
links += "vmess://" + base64.StdEncoding.EncodeToString(jsonStr)
|
links += "vmess://" + base64.StdEncoding.EncodeToString(jsonStr)
|
||||||
}
|
}
|
||||||
return links
|
return links
|
||||||
|
@ -323,7 +338,6 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
security, _ := stream["security"].(string)
|
security, _ := stream["security"].(string)
|
||||||
var domains []interface{}
|
|
||||||
if security == "tls" {
|
if security == "tls" {
|
||||||
params["security"] = "tls"
|
params["security"] = "tls"
|
||||||
tlsSetting, _ := stream["tlsSettings"].(map[string]interface{})
|
tlsSetting, _ := stream["tlsSettings"].(map[string]interface{})
|
||||||
|
@ -335,11 +349,12 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
|
||||||
if len(alpn) > 0 {
|
if len(alpn) > 0 {
|
||||||
params["alpn"] = strings.Join(alpn, ",")
|
params["alpn"] = strings.Join(alpn, ",")
|
||||||
}
|
}
|
||||||
tlsSettings, _ := searchKey(tlsSetting, "settings")
|
if sniValue, ok := searchKey(tlsSetting, "serverName"); ok {
|
||||||
if tlsSetting != nil {
|
|
||||||
if sniValue, ok := searchKey(tlsSettings, "serverName"); ok {
|
|
||||||
params["sni"], _ = sniValue.(string)
|
params["sni"], _ = sniValue.(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tlsSettings, _ := searchKey(tlsSetting, "settings")
|
||||||
|
if tlsSetting != nil {
|
||||||
if fpValue, ok := searchKey(tlsSettings, "fingerprint"); ok {
|
if fpValue, ok := searchKey(tlsSettings, "fingerprint"); ok {
|
||||||
params["fp"], _ = fpValue.(string)
|
params["fp"], _ = fpValue.(string)
|
||||||
}
|
}
|
||||||
|
@ -348,19 +363,11 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
|
||||||
params["allowInsecure"] = "1"
|
params["allowInsecure"] = "1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if domainSettings, ok := searchKey(tlsSettings, "domains"); ok {
|
|
||||||
domains, _ = domainSettings.([]interface{})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 {
|
if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 {
|
||||||
params["flow"] = clients[clientIndex].Flow
|
params["flow"] = clients[clientIndex].Flow
|
||||||
}
|
}
|
||||||
|
|
||||||
serverName, _ := tlsSetting["serverName"].(string)
|
|
||||||
if serverName != "" {
|
|
||||||
address = serverName
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if security == "reality" {
|
if security == "reality" {
|
||||||
|
@ -389,11 +396,6 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
|
||||||
params["spx"] = spx
|
params["spx"] = spx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if serverName, ok := searchKey(realitySettings, "serverName"); ok {
|
|
||||||
if sname, ok := serverName.(string); ok && len(sname) > 0 {
|
|
||||||
address = sname
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 {
|
if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 {
|
||||||
|
@ -412,7 +414,9 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
|
||||||
if len(alpn) > 0 {
|
if len(alpn) > 0 {
|
||||||
params["alpn"] = strings.Join(alpn, ",")
|
params["alpn"] = strings.Join(alpn, ",")
|
||||||
}
|
}
|
||||||
|
if sniValue, ok := searchKey(xtlsSetting, "serverName"); ok {
|
||||||
|
params["sni"], _ = sniValue.(string)
|
||||||
|
}
|
||||||
xtlsSettings, _ := searchKey(xtlsSetting, "settings")
|
xtlsSettings, _ := searchKey(xtlsSetting, "settings")
|
||||||
if xtlsSetting != nil {
|
if xtlsSetting != nil {
|
||||||
if fpValue, ok := searchKey(xtlsSettings, "fingerprint"); ok {
|
if fpValue, ok := searchKey(xtlsSettings, "fingerprint"); ok {
|
||||||
|
@ -423,25 +427,55 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
|
||||||
params["allowInsecure"] = "1"
|
params["allowInsecure"] = "1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if sniValue, ok := searchKey(xtlsSettings, "serverName"); ok {
|
|
||||||
params["sni"], _ = sniValue.(string)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 {
|
if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 {
|
||||||
params["flow"] = clients[clientIndex].Flow
|
params["flow"] = clients[clientIndex].Flow
|
||||||
}
|
}
|
||||||
|
|
||||||
serverName, _ := xtlsSetting["serverName"].(string)
|
|
||||||
if serverName != "" {
|
|
||||||
address = serverName
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if security != "tls" && security != "reality" && security != "xtls" {
|
if security != "tls" && security != "reality" && security != "xtls" {
|
||||||
params["security"] = "none"
|
params["security"] = "none"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
externalProxies, _ := stream["externalProxy"].([]interface{})
|
||||||
|
|
||||||
|
if len(externalProxies) > 0 {
|
||||||
|
links := ""
|
||||||
|
for index, externalProxy := range externalProxies {
|
||||||
|
ep, _ := externalProxy.(map[string]interface{})
|
||||||
|
newSecurity, _ := ep["forceTls"].(string)
|
||||||
|
dest, _ := ep["dest"].(string)
|
||||||
|
port := int(ep["port"].(float64))
|
||||||
|
link := fmt.Sprintf("vless://%s@%s:%d", uuid, dest, port)
|
||||||
|
|
||||||
|
if newSecurity != "same" {
|
||||||
|
params["security"] = newSecurity
|
||||||
|
} else {
|
||||||
|
params["security"] = security
|
||||||
|
}
|
||||||
|
url, _ := url.Parse(link)
|
||||||
|
q := url.Query()
|
||||||
|
|
||||||
|
for k, v := range params {
|
||||||
|
if !(newSecurity == "none" && (k == "alpn" || k == "sni" || k == "fp" || k == "allowInsecure")) {
|
||||||
|
q.Add(k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the new query values on the URL
|
||||||
|
url.RawQuery = q.Encode()
|
||||||
|
|
||||||
|
url.Fragment = s.genRemark(inbound, email, ep["remark"].(string))
|
||||||
|
|
||||||
|
if index > 0 {
|
||||||
|
links += "\n"
|
||||||
|
}
|
||||||
|
links += url.String()
|
||||||
|
}
|
||||||
|
return links
|
||||||
|
}
|
||||||
|
|
||||||
link := fmt.Sprintf("vless://%s@%s:%d", uuid, address, port)
|
link := fmt.Sprintf("vless://%s@%s:%d", uuid, address, port)
|
||||||
url, _ := url.Parse(link)
|
url, _ := url.Parse(link)
|
||||||
q := url.Query()
|
q := url.Query()
|
||||||
|
@ -453,20 +487,6 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
|
||||||
// Set the new query values on the URL
|
// Set the new query values on the URL
|
||||||
url.RawQuery = q.Encode()
|
url.RawQuery = q.Encode()
|
||||||
|
|
||||||
if len(domains) > 0 {
|
|
||||||
links := ""
|
|
||||||
for index, d := range domains {
|
|
||||||
domain := d.(map[string]interface{})
|
|
||||||
url.Fragment = s.genRemark(inbound, email, domain["remark"].(string))
|
|
||||||
url.Host = fmt.Sprintf("%s:%d", domain["domain"].(string), port)
|
|
||||||
if index > 0 {
|
|
||||||
links += "\n"
|
|
||||||
}
|
|
||||||
links += url.String()
|
|
||||||
}
|
|
||||||
return links
|
|
||||||
}
|
|
||||||
|
|
||||||
url.Fragment = s.genRemark(inbound, email, "")
|
url.Fragment = s.genRemark(inbound, email, "")
|
||||||
return url.String()
|
return url.String()
|
||||||
}
|
}
|
||||||
|
@ -534,7 +554,6 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string
|
||||||
}
|
}
|
||||||
|
|
||||||
security, _ := stream["security"].(string)
|
security, _ := stream["security"].(string)
|
||||||
var domains []interface{}
|
|
||||||
if security == "tls" {
|
if security == "tls" {
|
||||||
params["security"] = "tls"
|
params["security"] = "tls"
|
||||||
tlsSetting, _ := stream["tlsSettings"].(map[string]interface{})
|
tlsSetting, _ := stream["tlsSettings"].(map[string]interface{})
|
||||||
|
@ -546,11 +565,11 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string
|
||||||
if len(alpn) > 0 {
|
if len(alpn) > 0 {
|
||||||
params["alpn"] = strings.Join(alpn, ",")
|
params["alpn"] = strings.Join(alpn, ",")
|
||||||
}
|
}
|
||||||
tlsSettings, _ := searchKey(tlsSetting, "settings")
|
if sniValue, ok := searchKey(tlsSetting, "serverName"); ok {
|
||||||
if tlsSetting != nil {
|
|
||||||
if sniValue, ok := searchKey(tlsSettings, "serverName"); ok {
|
|
||||||
params["sni"], _ = sniValue.(string)
|
params["sni"], _ = sniValue.(string)
|
||||||
}
|
}
|
||||||
|
tlsSettings, _ := searchKey(tlsSetting, "settings")
|
||||||
|
if tlsSetting != nil {
|
||||||
if fpValue, ok := searchKey(tlsSettings, "fingerprint"); ok {
|
if fpValue, ok := searchKey(tlsSettings, "fingerprint"); ok {
|
||||||
params["fp"], _ = fpValue.(string)
|
params["fp"], _ = fpValue.(string)
|
||||||
}
|
}
|
||||||
|
@ -559,14 +578,6 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string
|
||||||
params["allowInsecure"] = "1"
|
params["allowInsecure"] = "1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if domainSettings, ok := searchKey(tlsSettings, "domains"); ok {
|
|
||||||
domains, _ = domainSettings.([]interface{})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
serverName, _ := tlsSetting["serverName"].(string)
|
|
||||||
if serverName != "" {
|
|
||||||
address = serverName
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -596,11 +607,6 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string
|
||||||
params["spx"] = spx
|
params["spx"] = spx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if serverName, ok := searchKey(realitySettings, "serverName"); ok {
|
|
||||||
if sname, ok := serverName.(string); ok && len(sname) > 0 {
|
|
||||||
address = sname
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 {
|
if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 {
|
||||||
|
@ -619,6 +625,9 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string
|
||||||
if len(alpn) > 0 {
|
if len(alpn) > 0 {
|
||||||
params["alpn"] = strings.Join(alpn, ",")
|
params["alpn"] = strings.Join(alpn, ",")
|
||||||
}
|
}
|
||||||
|
if sniValue, ok := searchKey(xtlsSetting, "serverName"); ok {
|
||||||
|
params["sni"], _ = sniValue.(string)
|
||||||
|
}
|
||||||
|
|
||||||
xtlsSettings, _ := searchKey(xtlsSetting, "settings")
|
xtlsSettings, _ := searchKey(xtlsSetting, "settings")
|
||||||
if xtlsSetting != nil {
|
if xtlsSetting != nil {
|
||||||
|
@ -630,25 +639,55 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string
|
||||||
params["allowInsecure"] = "1"
|
params["allowInsecure"] = "1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if sniValue, ok := searchKey(xtlsSettings, "serverName"); ok {
|
|
||||||
params["sni"], _ = sniValue.(string)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 {
|
if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 {
|
||||||
params["flow"] = clients[clientIndex].Flow
|
params["flow"] = clients[clientIndex].Flow
|
||||||
}
|
}
|
||||||
|
|
||||||
serverName, _ := xtlsSetting["serverName"].(string)
|
|
||||||
if serverName != "" {
|
|
||||||
address = serverName
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if security != "tls" && security != "reality" && security != "xtls" {
|
if security != "tls" && security != "reality" && security != "xtls" {
|
||||||
params["security"] = "none"
|
params["security"] = "none"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
externalProxies, _ := stream["externalProxy"].([]interface{})
|
||||||
|
|
||||||
|
if len(externalProxies) > 0 {
|
||||||
|
links := ""
|
||||||
|
for index, externalProxy := range externalProxies {
|
||||||
|
ep, _ := externalProxy.(map[string]interface{})
|
||||||
|
newSecurity, _ := ep["forceTls"].(string)
|
||||||
|
dest, _ := ep["dest"].(string)
|
||||||
|
port := int(ep["port"].(float64))
|
||||||
|
link := fmt.Sprintf("trojan://%s@%s:%d", password, dest, port)
|
||||||
|
|
||||||
|
if newSecurity != "same" {
|
||||||
|
params["security"] = newSecurity
|
||||||
|
} else {
|
||||||
|
params["security"] = security
|
||||||
|
}
|
||||||
|
url, _ := url.Parse(link)
|
||||||
|
q := url.Query()
|
||||||
|
|
||||||
|
for k, v := range params {
|
||||||
|
if !(newSecurity == "none" && (k == "alpn" || k == "sni" || k == "fp" || k == "allowInsecure")) {
|
||||||
|
q.Add(k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the new query values on the URL
|
||||||
|
url.RawQuery = q.Encode()
|
||||||
|
|
||||||
|
url.Fragment = s.genRemark(inbound, email, ep["remark"].(string))
|
||||||
|
|
||||||
|
if index > 0 {
|
||||||
|
links += "\n"
|
||||||
|
}
|
||||||
|
links += url.String()
|
||||||
|
}
|
||||||
|
return links
|
||||||
|
}
|
||||||
|
|
||||||
link := fmt.Sprintf("trojan://%s@%s:%d", password, address, port)
|
link := fmt.Sprintf("trojan://%s@%s:%d", password, address, port)
|
||||||
|
|
||||||
url, _ := url.Parse(link)
|
url, _ := url.Parse(link)
|
||||||
|
@ -661,20 +700,6 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string
|
||||||
// Set the new query values on the URL
|
// Set the new query values on the URL
|
||||||
url.RawQuery = q.Encode()
|
url.RawQuery = q.Encode()
|
||||||
|
|
||||||
if len(domains) > 0 {
|
|
||||||
links := ""
|
|
||||||
for index, d := range domains {
|
|
||||||
domain := d.(map[string]interface{})
|
|
||||||
url.Fragment = s.genRemark(inbound, email, domain["remark"].(string))
|
|
||||||
url.Host = fmt.Sprintf("%s:%d", domain["domain"].(string), port)
|
|
||||||
if index > 0 {
|
|
||||||
links += "\n"
|
|
||||||
}
|
|
||||||
links += url.String()
|
|
||||||
}
|
|
||||||
return links
|
|
||||||
}
|
|
||||||
|
|
||||||
url.Fragment = s.genRemark(inbound, email, "")
|
url.Fragment = s.genRemark(inbound, email, "")
|
||||||
return url.String()
|
return url.String()
|
||||||
}
|
}
|
||||||
|
@ -744,10 +769,78 @@ func (s *SubService) genShadowsocksLink(inbound *model.Inbound, email string) st
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
security, _ := stream["security"].(string)
|
||||||
|
if security == "tls" {
|
||||||
|
params["security"] = "tls"
|
||||||
|
tlsSetting, _ := stream["tlsSettings"].(map[string]interface{})
|
||||||
|
alpns, _ := tlsSetting["alpn"].([]interface{})
|
||||||
|
var alpn []string
|
||||||
|
for _, a := range alpns {
|
||||||
|
alpn = append(alpn, a.(string))
|
||||||
|
}
|
||||||
|
if len(alpn) > 0 {
|
||||||
|
params["alpn"] = strings.Join(alpn, ",")
|
||||||
|
}
|
||||||
|
if sniValue, ok := searchKey(tlsSetting, "serverName"); ok {
|
||||||
|
params["sni"], _ = sniValue.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsSettings, _ := searchKey(tlsSetting, "settings")
|
||||||
|
if tlsSetting != nil {
|
||||||
|
if fpValue, ok := searchKey(tlsSettings, "fingerprint"); ok {
|
||||||
|
params["fp"], _ = fpValue.(string)
|
||||||
|
}
|
||||||
|
if insecure, ok := searchKey(tlsSettings, "allowInsecure"); ok {
|
||||||
|
if insecure.(bool) {
|
||||||
|
params["allowInsecure"] = "1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
encPart := fmt.Sprintf("%s:%s", method, clients[clientIndex].Password)
|
encPart := fmt.Sprintf("%s:%s", method, clients[clientIndex].Password)
|
||||||
if method[0] == '2' {
|
if method[0] == '2' {
|
||||||
encPart = fmt.Sprintf("%s:%s:%s", method, inboundPassword, clients[clientIndex].Password)
|
encPart = fmt.Sprintf("%s:%s:%s", method, inboundPassword, clients[clientIndex].Password)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
externalProxies, _ := stream["externalProxy"].([]interface{})
|
||||||
|
|
||||||
|
if len(externalProxies) > 0 {
|
||||||
|
links := ""
|
||||||
|
for index, externalProxy := range externalProxies {
|
||||||
|
ep, _ := externalProxy.(map[string]interface{})
|
||||||
|
newSecurity, _ := ep["forceTls"].(string)
|
||||||
|
dest, _ := ep["dest"].(string)
|
||||||
|
port := int(ep["port"].(float64))
|
||||||
|
link := fmt.Sprintf("ss://%s@%s:%d", base64.StdEncoding.EncodeToString([]byte(encPart)), dest, port)
|
||||||
|
|
||||||
|
if newSecurity != "same" {
|
||||||
|
params["security"] = newSecurity
|
||||||
|
} else {
|
||||||
|
params["security"] = security
|
||||||
|
}
|
||||||
|
url, _ := url.Parse(link)
|
||||||
|
q := url.Query()
|
||||||
|
|
||||||
|
for k, v := range params {
|
||||||
|
if !(newSecurity == "none" && (k == "alpn" || k == "sni" || k == "fp" || k == "allowInsecure")) {
|
||||||
|
q.Add(k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the new query values on the URL
|
||||||
|
url.RawQuery = q.Encode()
|
||||||
|
|
||||||
|
url.Fragment = s.genRemark(inbound, email, ep["remark"].(string))
|
||||||
|
|
||||||
|
if index > 0 {
|
||||||
|
links += "\n"
|
||||||
|
}
|
||||||
|
links += url.String()
|
||||||
|
}
|
||||||
|
return links
|
||||||
|
}
|
||||||
|
|
||||||
link := fmt.Sprintf("ss://%s@%s:%d", base64.StdEncoding.EncodeToString([]byte(encPart)), address, inbound.Port)
|
link := fmt.Sprintf("ss://%s@%s:%d", base64.StdEncoding.EncodeToString([]byte(encPart)), address, inbound.Port)
|
||||||
url, _ := url.Parse(link)
|
url, _ := url.Parse(link)
|
||||||
q := url.Query()
|
q := url.Query()
|
||||||
|
@ -758,6 +851,7 @@ func (s *SubService) genShadowsocksLink(inbound *model.Inbound, email string) st
|
||||||
|
|
||||||
// Set the new query values on the URL
|
// Set the new query values on the URL
|
||||||
url.RawQuery = q.Encode()
|
url.RawQuery = q.Encode()
|
||||||
|
|
||||||
url.Fragment = s.genRemark(inbound, email, "")
|
url.Fragment = s.genRemark(inbound, email, "")
|
||||||
return url.String()
|
return url.String()
|
||||||
}
|
}
|
||||||
|
|
|
@ -578,27 +578,21 @@ TlsStreamSettings.Cert = class extends XrayCommonClass {
|
||||||
};
|
};
|
||||||
|
|
||||||
TlsStreamSettings.Settings = class extends XrayCommonClass {
|
TlsStreamSettings.Settings = class extends XrayCommonClass {
|
||||||
constructor(allowInsecure = false, fingerprint = '', serverName = '', domains = []) {
|
constructor(allowInsecure = false, fingerprint = '') {
|
||||||
super();
|
super();
|
||||||
this.allowInsecure = allowInsecure;
|
this.allowInsecure = allowInsecure;
|
||||||
this.fingerprint = fingerprint;
|
this.fingerprint = fingerprint;
|
||||||
this.serverName = serverName;
|
|
||||||
this.domains = domains;
|
|
||||||
}
|
}
|
||||||
static fromJson(json = {}) {
|
static fromJson(json = {}) {
|
||||||
return new TlsStreamSettings.Settings(
|
return new TlsStreamSettings.Settings(
|
||||||
json.allowInsecure,
|
json.allowInsecure,
|
||||||
json.fingerprint,
|
json.fingerprint,
|
||||||
json.serverName,
|
|
||||||
json.domains,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
toJson() {
|
toJson() {
|
||||||
return {
|
return {
|
||||||
allowInsecure: this.allowInsecure,
|
allowInsecure: this.allowInsecure,
|
||||||
fingerprint: this.fingerprint,
|
fingerprint: this.fingerprint,
|
||||||
serverName: this.serverName,
|
|
||||||
domains: this.domains,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -692,21 +686,18 @@ XtlsStreamSettings.Cert = class extends XrayCommonClass {
|
||||||
};
|
};
|
||||||
|
|
||||||
XtlsStreamSettings.Settings = class extends XrayCommonClass {
|
XtlsStreamSettings.Settings = class extends XrayCommonClass {
|
||||||
constructor(allowInsecure = false, serverName = '') {
|
constructor(allowInsecure = false) {
|
||||||
super();
|
super();
|
||||||
this.allowInsecure = allowInsecure;
|
this.allowInsecure = allowInsecure;
|
||||||
this.serverName = serverName;
|
|
||||||
}
|
}
|
||||||
static fromJson(json = {}) {
|
static fromJson(json = {}) {
|
||||||
return new XtlsStreamSettings.Settings(
|
return new XtlsStreamSettings.Settings(
|
||||||
json.allowInsecure,
|
json.allowInsecure,
|
||||||
json.servername,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
toJson() {
|
toJson() {
|
||||||
return {
|
return {
|
||||||
allowInsecure: this.allowInsecure,
|
allowInsecure: this.allowInsecure,
|
||||||
serverName: this.serverName,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -773,18 +764,16 @@ class RealityStreamSettings extends XrayCommonClass {
|
||||||
}
|
}
|
||||||
|
|
||||||
RealityStreamSettings.Settings = class extends XrayCommonClass {
|
RealityStreamSettings.Settings = class extends XrayCommonClass {
|
||||||
constructor(publicKey = '', fingerprint = UTLS_FINGERPRINT.UTLS_FIREFOX, serverName = '', spiderX= '/') {
|
constructor(publicKey = '', fingerprint = UTLS_FINGERPRINT.UTLS_FIREFOX, spiderX= '/') {
|
||||||
super();
|
super();
|
||||||
this.publicKey = publicKey;
|
this.publicKey = publicKey;
|
||||||
this.fingerprint = fingerprint;
|
this.fingerprint = fingerprint;
|
||||||
this.serverName = serverName;
|
|
||||||
this.spiderX = spiderX;
|
this.spiderX = spiderX;
|
||||||
}
|
}
|
||||||
static fromJson(json = {}) {
|
static fromJson(json = {}) {
|
||||||
return new RealityStreamSettings.Settings(
|
return new RealityStreamSettings.Settings(
|
||||||
json.publicKey,
|
json.publicKey,
|
||||||
json.fingerprint,
|
json.fingerprint,
|
||||||
json.serverName,
|
|
||||||
json.spiderX,
|
json.spiderX,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -792,7 +781,6 @@ RealityStreamSettings.Settings = class extends XrayCommonClass {
|
||||||
return {
|
return {
|
||||||
publicKey: this.publicKey,
|
publicKey: this.publicKey,
|
||||||
fingerprint: this.fingerprint,
|
fingerprint: this.fingerprint,
|
||||||
serverName: this.serverName,
|
|
||||||
spiderX: this.spiderX,
|
spiderX: this.spiderX,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -829,6 +817,7 @@ class SockoptStreamSettings extends XrayCommonClass {
|
||||||
class StreamSettings extends XrayCommonClass {
|
class StreamSettings extends XrayCommonClass {
|
||||||
constructor(network='tcp',
|
constructor(network='tcp',
|
||||||
security='none',
|
security='none',
|
||||||
|
externalProxy = [],
|
||||||
tlsSettings=new TlsStreamSettings(),
|
tlsSettings=new TlsStreamSettings(),
|
||||||
xtlsSettings=new XtlsStreamSettings(),
|
xtlsSettings=new XtlsStreamSettings(),
|
||||||
realitySettings = new RealityStreamSettings(),
|
realitySettings = new RealityStreamSettings(),
|
||||||
|
@ -843,6 +832,7 @@ class StreamSettings extends XrayCommonClass {
|
||||||
super();
|
super();
|
||||||
this.network = network;
|
this.network = network;
|
||||||
this.security = security;
|
this.security = security;
|
||||||
|
this.externalProxy = externalProxy;
|
||||||
this.tls = tlsSettings;
|
this.tls = tlsSettings;
|
||||||
this.xtls = xtlsSettings;
|
this.xtls = xtlsSettings;
|
||||||
this.reality = realitySettings;
|
this.reality = realitySettings;
|
||||||
|
@ -901,10 +891,10 @@ class StreamSettings extends XrayCommonClass {
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json={}) {
|
static fromJson(json={}) {
|
||||||
|
|
||||||
return new StreamSettings(
|
return new StreamSettings(
|
||||||
json.network,
|
json.network,
|
||||||
json.security,
|
json.security,
|
||||||
|
json.externalProxy,
|
||||||
TlsStreamSettings.fromJson(json.tlsSettings),
|
TlsStreamSettings.fromJson(json.tlsSettings),
|
||||||
XtlsStreamSettings.fromJson(json.xtlsSettings),
|
XtlsStreamSettings.fromJson(json.xtlsSettings),
|
||||||
RealityStreamSettings.fromJson(json.realitySettings),
|
RealityStreamSettings.fromJson(json.realitySettings),
|
||||||
|
@ -923,6 +913,7 @@ class StreamSettings extends XrayCommonClass {
|
||||||
return {
|
return {
|
||||||
network: network,
|
network: network,
|
||||||
security: this.security,
|
security: this.security,
|
||||||
|
externalProxy: this.externalProxy,
|
||||||
tlsSettings: this.isTls ? this.tls.toJson() : undefined,
|
tlsSettings: this.isTls ? this.tls.toJson() : undefined,
|
||||||
xtlsSettings: this.isXtls ? this.xtls.toJson() : undefined,
|
xtlsSettings: this.isXtls ? this.xtls.toJson() : undefined,
|
||||||
realitySettings: this.isReality ? this.reality.toJson() : undefined,
|
realitySettings: this.isReality ? this.reality.toJson() : undefined,
|
||||||
|
@ -982,6 +973,16 @@ class Inbound extends XrayCommonClass {
|
||||||
return this.clientStats;
|
return this.clientStats;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get clients() {
|
||||||
|
switch (this.protocol) {
|
||||||
|
case Protocols.VMESS: return this.settings.vmesses;
|
||||||
|
case Protocols.VLESS: return this.settings.vlesses;
|
||||||
|
case Protocols.TROJAN: return this.settings.trojans;
|
||||||
|
case Protocols.SHADOWSOCKS: return this.isSSMultiUser ? this.settings.shadowsockses : null;
|
||||||
|
default: return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
get protocol() {
|
get protocol() {
|
||||||
return this._protocol;
|
return this._protocol;
|
||||||
}
|
}
|
||||||
|
@ -1132,26 +1133,8 @@ class Inbound extends XrayCommonClass {
|
||||||
}
|
}
|
||||||
|
|
||||||
isExpiry(index) {
|
isExpiry(index) {
|
||||||
switch (this.protocol) {
|
let exp = this.clients[index].expiryTime;
|
||||||
case Protocols.VMESS:
|
return exp > 0 ? exp < new Date().getTime() : false;
|
||||||
if(this.settings.vmesses[index].expiryTime > 0)
|
|
||||||
return this.settings.vmesses[index].expiryTime < new Date().getTime();
|
|
||||||
return false
|
|
||||||
case Protocols.VLESS:
|
|
||||||
if(this.settings.vlesses[index].expiryTime > 0)
|
|
||||||
return this.settings.vlesses[index].expiryTime < new Date().getTime();
|
|
||||||
return false
|
|
||||||
case Protocols.TROJAN:
|
|
||||||
if(this.settings.trojans[index].expiryTime > 0)
|
|
||||||
return this.settings.trojans[index].expiryTime < new Date().getTime();
|
|
||||||
return false
|
|
||||||
case Protocols.SHADOWSOCKS:
|
|
||||||
if(this.settings.shadowsockses.length > 0 && this.settings.shadowsockses[index].expiryTime > 0)
|
|
||||||
return this.settings.shadowsockses[index].expiryTime < new Date().getTime();
|
|
||||||
return false
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
canEnableTls() {
|
canEnableTls() {
|
||||||
|
@ -1195,19 +1178,20 @@ class Inbound extends XrayCommonClass {
|
||||||
this.sniffing = new Sniffing();
|
this.sniffing = new Sniffing();
|
||||||
}
|
}
|
||||||
|
|
||||||
genVmessLink(address='', remark='', clientIndex=0) {
|
genVmessLink(address='', port=this.port, forceTls, remark='', clientId) {
|
||||||
if (this.protocol !== Protocols.VMESS) {
|
if (this.protocol !== Protocols.VMESS) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
const security = forceTls == 'same' ? this.stream.security : forceTls;
|
||||||
let obj = {
|
let obj = {
|
||||||
v: '2',
|
v: '2',
|
||||||
ps: remark,
|
ps: remark,
|
||||||
add: address,
|
add: address,
|
||||||
port: this.port,
|
port: port,
|
||||||
id: this.settings.vmesses[clientIndex].id,
|
id: clientId,
|
||||||
net: this.stream.network,
|
net: this.stream.network,
|
||||||
type: 'none',
|
type: 'none',
|
||||||
tls: this.stream.security,
|
tls: security,
|
||||||
};
|
};
|
||||||
let network = this.stream.network;
|
let network = this.stream.network;
|
||||||
if (network === 'tcp') {
|
if (network === 'tcp') {
|
||||||
|
@ -1247,12 +1231,9 @@ class Inbound extends XrayCommonClass {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.stream.security === 'tls') {
|
if (security === 'tls') {
|
||||||
if (!ObjectUtil.isEmpty(this.stream.tls.server)) {
|
if (!ObjectUtil.isEmpty(this.stream.tls.sni)){
|
||||||
obj.add = this.stream.tls.server;
|
obj.sni = this.stream.tls.server;
|
||||||
}
|
|
||||||
if (!ObjectUtil.isEmpty(this.stream.tls.settings.serverName)){
|
|
||||||
obj.sni = this.stream.tls.settings.serverName;
|
|
||||||
}
|
}
|
||||||
if (!ObjectUtil.isEmpty(this.stream.tls.settings.fingerprint)){
|
if (!ObjectUtil.isEmpty(this.stream.tls.settings.fingerprint)){
|
||||||
obj.fp = this.stream.tls.settings.fingerprint;
|
obj.fp = this.stream.tls.settings.fingerprint;
|
||||||
|
@ -1268,11 +1249,10 @@ class Inbound extends XrayCommonClass {
|
||||||
return 'vmess://' + base64(JSON.stringify(obj, null, 2));
|
return 'vmess://' + base64(JSON.stringify(obj, null, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
genVLESSLink(address = '', remark='', clientIndex=0) {
|
genVLESSLink(address = '', port=this.port, forceTls, remark='', clientId, flow) {
|
||||||
const settings = this.settings;
|
const uuid = clientId;
|
||||||
const uuid = settings.vlesses[clientIndex].id;
|
|
||||||
const port = this.port;
|
|
||||||
const type = this.stream.network;
|
const type = this.stream.network;
|
||||||
|
const security = forceTls == 'same' ? this.stream.security : forceTls;
|
||||||
const params = new Map();
|
const params = new Map();
|
||||||
params.set("type", this.stream.network);
|
params.set("type", this.stream.network);
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
@ -1323,58 +1303,51 @@ class Inbound extends XrayCommonClass {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.tls) {
|
if (security === 'tls') {
|
||||||
params.set("security", "tls");
|
params.set("security", "tls");
|
||||||
|
if (this.stream.isTls){
|
||||||
params.set("fp" , this.stream.tls.settings.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.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;
|
params.set("sni", this.stream.tls.server);
|
||||||
}
|
}
|
||||||
if (this.stream.tls.settings.serverName !== ''){
|
if (type == "tcp" && !ObjectUtil.isEmpty(flow)) {
|
||||||
params.set("sni", this.stream.tls.settings.serverName);
|
params.set("flow", flow);
|
||||||
}
|
}
|
||||||
if (type === "tcp" && this.settings.vlesses[clientIndex].flow.length > 0) {
|
|
||||||
params.set("flow", this.settings.vlesses[clientIndex].flow);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (this.xtls) {
|
else if (security === 'xtls') {
|
||||||
params.set("security", "xtls");
|
params.set("security", "xtls");
|
||||||
params.set("alpn", this.stream.xtls.alpn);
|
params.set("alpn", this.stream.xtls.alpn);
|
||||||
if(this.stream.xtls.settings.allowInsecure){
|
if(this.stream.xtls.settings.allowInsecure){
|
||||||
params.set("allowInsecure", "1");
|
params.set("allowInsecure", "1");
|
||||||
}
|
}
|
||||||
if (!ObjectUtil.isEmpty(this.stream.xtls.server)){
|
if (!ObjectUtil.isEmpty(this.stream.xtls.server)){
|
||||||
address = this.stream.xtls.server;
|
params.set("sni", this.stream.xtls.server);
|
||||||
}
|
|
||||||
if (this.stream.xtls.settings.serverName !== ''){
|
|
||||||
params.set("sni", this.stream.xtls.settings.serverName);
|
|
||||||
}
|
}
|
||||||
params.set("flow", this.settings.vlesses[clientIndex].flow);
|
params.set("flow", this.settings.vlesses[clientIndex].flow);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (this.reality) {
|
else if (security === 'reality') {
|
||||||
params.set("security", "reality");
|
params.set("security", "reality");
|
||||||
params.set("fp", this.stream.reality.settings.fingerprint);
|
|
||||||
params.set("pbk", this.stream.reality.settings.publicKey);
|
params.set("pbk", this.stream.reality.settings.publicKey);
|
||||||
|
params.set("fp", this.stream.reality.settings.fingerprint);
|
||||||
if (!ObjectUtil.isArrEmpty(this.stream.reality.serverNames)) {
|
if (!ObjectUtil.isArrEmpty(this.stream.reality.serverNames)) {
|
||||||
params.set("sni", this.stream.reality.serverNames.split(",")[0]);
|
params.set("sni", this.stream.reality.serverNames.split(",")[0]);
|
||||||
}
|
}
|
||||||
if (this.stream.network === 'tcp' && !ObjectUtil.isEmpty(this.settings.vlesses[clientIndex].flow)) {
|
|
||||||
params.set("flow", this.settings.vlesses[clientIndex].flow);
|
|
||||||
}
|
|
||||||
if (this.stream.reality.shortIds.length > 0) {
|
if (this.stream.reality.shortIds.length > 0) {
|
||||||
params.set("sid", this.stream.reality.shortIds.split(",")[0]);
|
params.set("sid", this.stream.reality.shortIds.split(",")[0]);
|
||||||
}
|
}
|
||||||
if (!ObjectUtil.isEmpty(this.stream.reality.settings.serverName)) {
|
|
||||||
address = this.stream.reality.settings.serverName;
|
|
||||||
}
|
|
||||||
if (!ObjectUtil.isEmpty(this.stream.reality.settings.spiderX)) {
|
if (!ObjectUtil.isEmpty(this.stream.reality.settings.spiderX)) {
|
||||||
params.set("spx", this.stream.reality.settings.spiderX);
|
params.set("spx", this.stream.reality.settings.spiderX);
|
||||||
}
|
}
|
||||||
|
if (type == 'tcp' && !ObjectUtil.isEmpty(flow)) {
|
||||||
|
params.set("flow", flow);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
|
@ -1390,10 +1363,10 @@ class Inbound extends XrayCommonClass {
|
||||||
return url.toString();
|
return url.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
genSSLink(address='', remark='', clientIndex = 0) {
|
genSSLink(address='', port=this.port, forceTls, remark='', clientPassword) {
|
||||||
let settings = this.settings;
|
let settings = this.settings;
|
||||||
const port = this.port;
|
|
||||||
const type = this.stream.network;
|
const type = this.stream.network;
|
||||||
|
const security = forceTls == 'same' ? this.stream.security : forceTls;
|
||||||
const params = new Map();
|
const params = new Map();
|
||||||
params.set("type", this.stream.network);
|
params.set("type", this.stream.network);
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
@ -1444,11 +1417,26 @@ class Inbound extends XrayCommonClass {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (security === 'tls') {
|
||||||
|
params.set("security", "tls");
|
||||||
|
if (this.stream.isTls){
|
||||||
|
params.set("fp" , this.stream.tls.settings.fingerprint);
|
||||||
|
params.set("alpn", this.stream.tls.alpn);
|
||||||
|
if(this.stream.tls.settings.allowInsecure){
|
||||||
|
params.set("allowInsecure", "1");
|
||||||
|
}
|
||||||
|
if (!ObjectUtil.isEmpty(this.stream.tls.server)){
|
||||||
|
params.set("sni", this.stream.tls.server);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
let password = new Array();
|
let password = new Array();
|
||||||
if (this.isSS2022) password.push(settings.password);
|
if (this.isSS2022) password.push(settings.password);
|
||||||
if (this.isSSMultiUser) password.push(settings.shadowsockses[clientIndex].password);
|
if (this.isSSMultiUser) password.push(clientPassword);
|
||||||
|
|
||||||
let link = `ss://${safeBase64(settings.method + ':' + password.join(':'))}@${address}:${this.port}`;
|
let link = `ss://${safeBase64(settings.method + ':' + password.join(':'))}@${address}:${port}`;
|
||||||
const url = new URL(link);
|
const url = new URL(link);
|
||||||
for (const [key, value] of params) {
|
for (const [key, value] of params) {
|
||||||
url.searchParams.set(key, value)
|
url.searchParams.set(key, value)
|
||||||
|
@ -1457,9 +1445,8 @@ class Inbound extends XrayCommonClass {
|
||||||
return url.toString();
|
return url.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
genTrojanLink(address = '', remark = '', clientIndex = 0) {
|
genTrojanLink(address = '', port=this.port, forceTls, remark = '', clientPassword) {
|
||||||
let settings = this.settings;
|
const security = forceTls == 'same' ? this.stream.security : forceTls;
|
||||||
const port = this.port;
|
|
||||||
const type = this.stream.network;
|
const type = this.stream.network;
|
||||||
const params = new Map();
|
const params = new Map();
|
||||||
params.set("type", this.stream.network);
|
params.set("type", this.stream.network);
|
||||||
|
@ -1511,48 +1498,41 @@ class Inbound extends XrayCommonClass {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.tls) {
|
if (security === 'tls') {
|
||||||
params.set("security", "tls");
|
params.set("security", "tls");
|
||||||
|
if (this.stream.isTls){
|
||||||
params.set("fp" , this.stream.tls.settings.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.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;
|
params.set("sni", this.stream.tls.server);
|
||||||
}
|
}
|
||||||
if (this.stream.tls.settings.serverName !== ''){
|
|
||||||
params.set("sni", this.stream.tls.settings.serverName);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (this.reality) {
|
else if (security === 'reality') {
|
||||||
params.set("security", "reality");
|
params.set("security", "reality");
|
||||||
params.set("fp", this.stream.reality.settings.fingerprint);
|
|
||||||
params.set("pbk", this.stream.reality.settings.publicKey);
|
params.set("pbk", this.stream.reality.settings.publicKey);
|
||||||
|
params.set("fp", this.stream.reality.settings.fingerprint);
|
||||||
if (!ObjectUtil.isArrEmpty(this.stream.reality.serverNames)) {
|
if (!ObjectUtil.isArrEmpty(this.stream.reality.serverNames)) {
|
||||||
params.set("sni", this.stream.reality.serverNames.split(",")[0]);
|
params.set("sni", this.stream.reality.serverNames.split(",")[0]);
|
||||||
}
|
}
|
||||||
if (this.stream.reality.shortIds.length > 0) {
|
if (this.stream.reality.shortIds.length > 0) {
|
||||||
params.set("sid", this.stream.reality.shortIds.split(",")[0]);
|
params.set("sid", this.stream.reality.shortIds.split(",")[0]);
|
||||||
}
|
}
|
||||||
if (!ObjectUtil.isEmpty(this.stream.reality.settings.serverName)) {
|
|
||||||
address = this.stream.reality.settings.serverName;
|
|
||||||
}
|
|
||||||
if (!ObjectUtil.isEmpty(this.stream.reality.settings.spiderX)) {
|
if (!ObjectUtil.isEmpty(this.stream.reality.settings.spiderX)) {
|
||||||
params.set("spx", this.stream.reality.settings.spiderX);
|
params.set("spx", this.stream.reality.settings.spiderX);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (this.xtls) {
|
else if (security === 'xtls') {
|
||||||
params.set("security", "xtls");
|
params.set("security", "xtls");
|
||||||
params.set("alpn", this.stream.xtls.alpn);
|
params.set("alpn", this.stream.xtls.alpn);
|
||||||
if(this.stream.xtls.settings.allowInsecure){
|
if(this.stream.xtls.settings.allowInsecure){
|
||||||
params.set("allowInsecure", "1");
|
params.set("allowInsecure", "1");
|
||||||
}
|
}
|
||||||
if (!ObjectUtil.isEmpty(this.stream.xtls.server)) {
|
|
||||||
address = this.stream.xtls.server;
|
|
||||||
}
|
|
||||||
if (this.stream.xtls.settings.serverName !== ''){
|
if (this.stream.xtls.settings.serverName !== ''){
|
||||||
params.set("sni", this.stream.xtls.settings.serverName);
|
params.set("sni", this.stream.xtls.settings.serverName);
|
||||||
}
|
}
|
||||||
|
@ -1563,7 +1543,7 @@ class Inbound extends XrayCommonClass {
|
||||||
params.set("security", "none");
|
params.set("security", "none");
|
||||||
}
|
}
|
||||||
|
|
||||||
const link = `trojan://${settings.trojans[clientIndex].password}@${address}:${this.port}`;
|
const link = `trojan://${clientPassword}@${address}:${port}`;
|
||||||
const url = new URL(link);
|
const url = new URL(link);
|
||||||
for (const [key, value] of params) {
|
for (const [key, value] of params) {
|
||||||
url.searchParams.set(key, value)
|
url.searchParams.set(key, value)
|
||||||
|
@ -1572,38 +1552,55 @@ class Inbound extends XrayCommonClass {
|
||||||
return url.toString();
|
return url.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
genLink(address='', remark='', clientIndex=0) {
|
genLink(address='', port=this.port, forceTls='same', remark='', client) {
|
||||||
switch (this.protocol) {
|
switch (this.protocol) {
|
||||||
case Protocols.VMESS:
|
case Protocols.VMESS:
|
||||||
return this.genVmessLink(address, remark, clientIndex);
|
return this.genVmessLink(address, port, forceTls, remark, client.id);
|
||||||
case Protocols.VLESS:
|
case Protocols.VLESS:
|
||||||
return this.genVLESSLink(address, remark, clientIndex);
|
return this.genVLESSLink(address, port, forceTls, remark, client.id, client.flow);
|
||||||
case Protocols.SHADOWSOCKS:
|
case Protocols.SHADOWSOCKS:
|
||||||
return this.genSSLink(address, remark, clientIndex);
|
return this.genSSLink(address, port, forceTls, remark, this.isSSMultiUser ? client.password : '');
|
||||||
case Protocols.TROJAN:
|
case Protocols.TROJAN:
|
||||||
return this.genTrojanLink(address, remark, clientIndex);
|
return this.genTrojanLink(address, port, forceTls, remark, client.password);
|
||||||
default: return '';
|
default: return '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
genInboundLinks(address = '', remark = '') {
|
genAllLinks(remark='', client){
|
||||||
let link = '';
|
let result = [];
|
||||||
switch (this.protocol) {
|
let email = client ? client.email : '';
|
||||||
case Protocols.VMESS:
|
let addr = !ObjectUtil.isEmpty(this.listen) && this.listen !== "0.0.0.0" ? this.listen : location.hostname;
|
||||||
case Protocols.VLESS:
|
let port = this.port
|
||||||
case Protocols.TROJAN:
|
if(ObjectUtil.isArrEmpty(this.stream.externalProxy)){
|
||||||
case Protocols.SHADOWSOCKS:
|
let r = [remark, email].filter(x => x.length > 0).join('-');
|
||||||
JSON.parse(this.settings).clients.forEach((client,index) => {
|
result.push({
|
||||||
if(this.tls && !ObjectUtil.isArrEmpty(this.stream.tls.settings.domains)){
|
remark: r,
|
||||||
this.stream.tls.settings.domains.forEach((domain) => {
|
link: this.genLink(addr, port, 'same', r, client)
|
||||||
link += this.genLink(domain.domain, [remark, client.email, domain.remark].filter(x => x.length > 0).join('-'), index) + '\r\n';
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
link += this.genLink(address, [remark, client.email].filter(x => x.length > 0).join('-'), index) + '\r\n';
|
this.stream.externalProxy.forEach((ep) => {
|
||||||
}
|
let r = [remark, email, ep.remark].filter(x => x.length > 0).join('-')
|
||||||
|
result.push({
|
||||||
|
remark: r,
|
||||||
|
link: this.genLink(ep.dest, ep.port, ep.forceTls, r, client)
|
||||||
});
|
});
|
||||||
return link;
|
});
|
||||||
default: return '';
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
genInboundLinks(remark = '') {
|
||||||
|
if(this.clients){
|
||||||
|
let links = [];
|
||||||
|
this.clients.forEach((client) => {
|
||||||
|
genAllLinks(remark,client).forEach(l => {
|
||||||
|
links.push(l.link);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
return links.join('\r\n');
|
||||||
|
} else {
|
||||||
|
if(this.protocol == Protocols.SHADOWSOCKS && !this.isSSMultiUser) return this.genSSLink(this.listen, this.port, remark);
|
||||||
|
return '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,39 +22,25 @@
|
||||||
|
|
||||||
const qrModal = {
|
const qrModal = {
|
||||||
title: '',
|
title: '',
|
||||||
clientIndex: 0,
|
|
||||||
inbound: new Inbound(),
|
|
||||||
dbInbound: new DBInbound(),
|
dbInbound: new DBInbound(),
|
||||||
client: null,
|
client: null,
|
||||||
qrcodes: [],
|
qrcodes: [],
|
||||||
clipboard: null,
|
clipboard: null,
|
||||||
visible: false,
|
visible: false,
|
||||||
subId: '',
|
subId: '',
|
||||||
show: function (title = '', dbInbound = new DBInbound(), clientIndex = 0) {
|
show: function (title = '', dbInbound, client) {
|
||||||
this.title = title;
|
this.title = title;
|
||||||
this.clientIndex = clientIndex;
|
|
||||||
this.dbInbound = dbInbound;
|
this.dbInbound = dbInbound;
|
||||||
this.inbound = dbInbound.toInbound();
|
this.inbound = dbInbound.toInbound();
|
||||||
settings = JSON.parse(this.inbound.settings);
|
this.client = client;
|
||||||
this.client = settings.clients[clientIndex];
|
|
||||||
remark = [this.dbInbound.remark, ( this.client ? this.client.email : '')].filter(Boolean).join('-');
|
|
||||||
address = this.dbInbound.address;
|
|
||||||
this.subId = '';
|
this.subId = '';
|
||||||
this.qrcodes = [];
|
this.qrcodes = [];
|
||||||
if (this.inbound.tls && !ObjectUtil.isArrEmpty(this.inbound.stream.tls.settings.domains)) {
|
this.inbound.genAllLinks(this.dbInbound.remark, client).forEach(l => {
|
||||||
this.inbound.stream.tls.settings.domains.forEach((domain) => {
|
|
||||||
remarkText = [remark, domain.remark].filter(Boolean).join('-');
|
|
||||||
this.qrcodes.push({
|
this.qrcodes.push({
|
||||||
remark: remarkText,
|
remark: l.remark,
|
||||||
link: this.inbound.genLink(domain.domain, remarkText, clientIndex)
|
link: l.link
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
this.qrcodes.push({
|
|
||||||
remark: remark,
|
|
||||||
link: this.inbound.genLink(address, remark, clientIndex)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this.visible = true;
|
this.visible = true;
|
||||||
},
|
},
|
||||||
close: function () {
|
close: function () {
|
||||||
|
|
|
@ -38,7 +38,7 @@
|
||||||
this.isEdit = isEdit;
|
this.isEdit = isEdit;
|
||||||
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.clients = this.inbound.clients;
|
||||||
this.index = index === null ? this.clients.length : index;
|
this.index = index === null ? this.clients.length : index;
|
||||||
this.delayedStart = false;
|
this.delayedStart = false;
|
||||||
if (isEdit) {
|
if (isEdit) {
|
||||||
|
@ -52,15 +52,6 @@
|
||||||
this.clientStats = this.dbInbound.clientStats.find(row => row.email === this.clients[this.index].email);
|
this.clientStats = this.dbInbound.clientStats.find(row => row.email === this.clients[this.index].email);
|
||||||
this.confirm = confirm;
|
this.confirm = confirm;
|
||||||
},
|
},
|
||||||
getClients(protocol, clientSettings) {
|
|
||||||
switch (protocol) {
|
|
||||||
case Protocols.VMESS: return clientSettings.vmesses;
|
|
||||||
case Protocols.VLESS: return clientSettings.vlesses;
|
|
||||||
case Protocols.TROJAN: return clientSettings.trojans;
|
|
||||||
case Protocols.SHADOWSOCKS: return clientSettings.shadowsockses;
|
|
||||||
default: return null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getClientId(protocol, client) {
|
getClientId(protocol, client) {
|
||||||
switch (protocol) {
|
switch (protocol) {
|
||||||
case Protocols.TROJAN: return client.password;
|
case Protocols.TROJAN: return client.password;
|
||||||
|
|
|
@ -96,6 +96,7 @@
|
||||||
<!-- stream settings -->
|
<!-- stream settings -->
|
||||||
<template v-if="inbound.canEnableStream()">
|
<template v-if="inbound.canEnableStream()">
|
||||||
{{template "form/streamSettings"}}
|
{{template "form/streamSettings"}}
|
||||||
|
{{template "form/externalProxy" }}
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- tls settings -->
|
<!-- tls settings -->
|
||||||
|
|
32
web/html/xui/form/stream/external_proxy.html
Normal file
32
web/html/xui/form/stream/external_proxy.html
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
{{define "form/externalProxy"}}
|
||||||
|
<a-form layout="inline">
|
||||||
|
<a-divider style="margin:0;"></a-divider>
|
||||||
|
<a-form-item label="External Proxy">
|
||||||
|
<a-switch v-model="externalProxy"></a-switch>
|
||||||
|
<a-button v-if="externalProxy" type="primary" style="margin-left: 10px" size="small" @click="inbound.stream.externalProxy.push({forceTls: 'same', dest: '', port: 443, remark: ''})">+</a-button>
|
||||||
|
</a-form-item>
|
||||||
|
<table width="100%" class="ant-table-tbody" v-if="externalProxy" style="margin-bottom:5px">
|
||||||
|
<tr style="line-height: 40px;">
|
||||||
|
<td width="100%">
|
||||||
|
<a-input-group style="margin: 0 5px;" compact v-for="(row, index) in inbound.stream.externalProxy">
|
||||||
|
<template>
|
||||||
|
<a-tooltip title="Force TLS">
|
||||||
|
<a-select v-model="row.forceTls" style="width:20%; margin: 0px" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
|
<a-select-option value="same">{{ i18n "pages.inbounds.same" }}</a-select-option>
|
||||||
|
<a-select-option value="none">{{ i18n "none" }}</a-select-option>
|
||||||
|
<a-select-option value="tls">TLS</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
<a-input style="width: 35%" v-model.trim="row.dest" placeholder='{{ i18n "host" }}'></a-input>
|
||||||
|
<a-tooltip title='{{ i18n "pages.inbounds.port" }}'>
|
||||||
|
<a-input-number style="width: 15%;" v-model.number="row.port" min="1" max="65531"></a-input-number>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-input style="width: 20%" v-model.trim="row.remark" placeholder='{{ i18n "remark" }}'></a-input>
|
||||||
|
<a-button style="width: 10%; margin: 0px" @click="inbound.stream.externalProxy.splice(index, 1)">-</a-button>
|
||||||
|
</a-input-group>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</a-form>
|
||||||
|
{{end}}
|
|
@ -24,26 +24,6 @@
|
||||||
|
|
||||||
<!-- tls settings -->
|
<!-- tls settings -->
|
||||||
<a-form v-if="inbound.tls" layout="inline">
|
<a-form v-if="inbound.tls" layout="inline">
|
||||||
<a-form-item label='Multi Domain'>
|
|
||||||
<a-switch v-model="multiDomain"></a-switch>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item v-if="multiDomain">
|
|
||||||
<a-row>
|
|
||||||
<span>Domains:</span>
|
|
||||||
<a-button v-if="multiDomain" type="primary" size="small" @click="inbound.stream.tls.settings.domains.push({remark: '', domain: ''})" style="margin-left: 10px">+</a-button>
|
|
||||||
</a-row>
|
|
||||||
<a-input-group v-for="(row, index) in inbound.stream.tls.settings.domains">
|
|
||||||
<a-input style="width: 40%" v-model.trim="row.remark" addon-before='{{ i18n "remark" }}'></a-input>
|
|
||||||
<a-input style="width: 60%" v-model.trim="row.domain" addon-before='{{ i18n "host" }}'>
|
|
||||||
<template slot="addonAfter">
|
|
||||||
<a-button type="primary" size="small" style="margin-left: 10px" @click="inbound.stream.tls.settings.domains.splice(index, 1)">-</a-button>
|
|
||||||
</template>
|
|
||||||
</a-input>
|
|
||||||
</a-input-group>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item v-else label='{{ i18n "domainName" }}'>
|
|
||||||
<a-input v-model.trim="inbound.stream.tls.server" style="width: 250px"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label="CipherSuites">
|
<a-form-item label="CipherSuites">
|
||||||
<a-select v-model="inbound.stream.tls.cipherSuites" style="width: 300px" :dropdown-class-name="themeSwitcher.currentTheme">
|
<a-select v-model="inbound.stream.tls.cipherSuites" style="width: 300px" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-select-option value="">auto</a-select-option>
|
<a-select-option value="">auto</a-select-option>
|
||||||
|
@ -61,7 +41,7 @@
|
||||||
</a-input-group>
|
</a-input-group>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="SNI" placeholder="Server Name Indication">
|
<a-form-item label="SNI" placeholder="Server Name Indication">
|
||||||
<a-input v-model.trim="inbound.stream.tls.settings.serverName" style="width: 250px"></a-input>
|
<a-input v-model.trim="inbound.stream.tls.server" style="width: 250px"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="uTLS">
|
<a-form-item label="uTLS">
|
||||||
<a-select v-model="inbound.stream.tls.settings.fingerprint"
|
<a-select v-model="inbound.stream.tls.settings.fingerprint"
|
||||||
|
@ -122,11 +102,8 @@
|
||||||
|
|
||||||
<!-- xtls settings -->
|
<!-- xtls settings -->
|
||||||
<a-form v-else-if="inbound.xtls" layout="inline">
|
<a-form v-else-if="inbound.xtls" layout="inline">
|
||||||
<a-form-item label='{{ i18n "domainName" }}'>
|
|
||||||
<a-input v-model.trim="inbound.stream.xtls.server"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label="SNI" placeholder="Server Name Indication">
|
<a-form-item label="SNI" placeholder="Server Name Indication">
|
||||||
<a-input v-model.trim="inbound.stream.xtls.settings.serverName" style="width: 250px"></a-input>
|
<a-input v-model.trim="inbound.stream.xtls.server" style="width: 250px"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="Alpn">
|
<a-form-item label="Alpn">
|
||||||
<a-checkbox-group v-model="inbound.stream.xtls.alpn" style="width:200px">
|
<a-checkbox-group v-model="inbound.stream.xtls.alpn" style="width:200px">
|
||||||
|
@ -179,9 +156,6 @@
|
||||||
style="width: 135px" :dropdown-class-name="themeSwitcher.currentTheme">
|
style="width: 135px" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
|
<a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label='{{ i18n "domainName" }}'>
|
|
||||||
<a-input v-model.trim="inbound.stream.reality.settings.serverName" style="width: 250px"></a-input>
|
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="Dest">
|
<a-form-item label="Dest">
|
||||||
<a-input v-model.trim="inbound.stream.reality.dest" style="width: 300px"></a-input>
|
<a-input v-model.trim="inbound.stream.reality.dest" style="width: 300px"></a-input>
|
||||||
|
|
|
@ -265,27 +265,10 @@
|
||||||
this.index = index;
|
this.index = index;
|
||||||
this.inbound = dbInbound.toInbound();
|
this.inbound = dbInbound.toInbound();
|
||||||
this.dbInbound = new DBInbound(dbInbound);
|
this.dbInbound = new DBInbound(dbInbound);
|
||||||
this.settings = JSON.parse(this.inbound.settings);
|
this.clientSettings = this.inbound.clients ? this.inbound.clients[index] : null;
|
||||||
this.clientSettings = this.settings.clients ? Object.values(this.settings.clients)[index] : null;
|
this.isExpired = this.inbound.clients ? this.inbound.isExpiry(index): this.dbInbound.isExpiry;
|
||||||
this.isExpired = this.inbound.isExpiry(index);
|
this.clientStats = this.inbound.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) : [];
|
this.links = this.inbound.genAllLinks(this.dbInbound.remark, this.clientSettings);
|
||||||
remark = [this.dbInbound.remark, ( this.clientSettings ? this.clientSettings.email : '')].filter(Boolean).join('-');
|
|
||||||
address = this.dbInbound.address;
|
|
||||||
this.links = [];
|
|
||||||
if (this.inbound.tls && !ObjectUtil.isArrEmpty(this.inbound.stream.tls.settings.domains)) {
|
|
||||||
this.inbound.stream.tls.settings.domains.forEach((domain) => {
|
|
||||||
remarkText = [remark, domain.remark].filter(Boolean).join('-');
|
|
||||||
this.links.push({
|
|
||||||
remark: remarkText,
|
|
||||||
link: this.inbound.genLink(domain.domain, remarkText, index)
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.links.push({
|
|
||||||
remark: remark,
|
|
||||||
link: this.inbound.genLink(address, remark, index)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (this.clientSettings) {
|
if (this.clientSettings) {
|
||||||
if (this.clientSettings.subId) {
|
if (this.clientSettings.subId) {
|
||||||
this.subLink = this.genSubLink(this.clientSettings.subId);
|
this.subLink = this.genSubLink(this.clientSettings.subId);
|
||||||
|
|
|
@ -43,15 +43,6 @@
|
||||||
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;
|
|
||||||
case Protocols.SHADOWSOCKS: return clientSettings.shadowsockses;
|
|
||||||
default: return null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
new Vue({
|
new Vue({
|
||||||
|
@ -70,7 +61,7 @@
|
||||||
return inModal.isEdit;
|
return inModal.isEdit;
|
||||||
},
|
},
|
||||||
get client() {
|
get client() {
|
||||||
return inModal.getClients(this.inbound.protocol, this.inbound.settings)[0];
|
return inModal.inbound.clients[0];
|
||||||
},
|
},
|
||||||
get delayedExpireDays() {
|
get delayedExpireDays() {
|
||||||
return this.client && this.client.expiryTime < 0 ? this.client.expiryTime / -86400000 : 0;
|
return this.client && this.client.expiryTime < 0 ? this.client.expiryTime / -86400000 : 0;
|
||||||
|
@ -78,16 +69,19 @@
|
||||||
set delayedExpireDays(days) {
|
set delayedExpireDays(days) {
|
||||||
this.client.expiryTime = -86400000 * days;
|
this.client.expiryTime = -86400000 * days;
|
||||||
},
|
},
|
||||||
get multiDomain() {
|
get externalProxy() {
|
||||||
return this.inbound.stream.tls.settings.domains.length > 0;
|
return this.inbound.stream.externalProxy.length > 0;
|
||||||
},
|
},
|
||||||
set multiDomain(value) {
|
set externalProxy(value) {
|
||||||
if (value) {
|
if (value) {
|
||||||
inModal.inbound.stream.tls.server = "";
|
inModal.inbound.stream.externalProxy = [{
|
||||||
inModal.inbound.stream.tls.settings.domains = [{ remark: "", domain: window.location.hostname }];
|
forceTls: "same",
|
||||||
|
dest: window.location.hostname,
|
||||||
|
port: inModal.inbound.port,
|
||||||
|
remark: ""
|
||||||
|
}];
|
||||||
} else {
|
} else {
|
||||||
inModal.inbound.stream.tls.server = "";
|
inModal.inbound.stream.externalProxy = [];
|
||||||
inModal.inbound.stream.tls.settings.domains = [];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -618,7 +618,7 @@
|
||||||
},
|
},
|
||||||
getClientCounts(dbInbound, inbound) {
|
getClientCounts(dbInbound, inbound) {
|
||||||
let clientCount = 0, active = [], deactive = [], depleted = [], expiring = [], online = [];
|
let clientCount = 0, active = [], deactive = [], depleted = [], expiring = [], online = [];
|
||||||
clients = this.getClients(dbInbound.protocol, inbound.settings);
|
clients = inbound.clients;
|
||||||
clientStats = dbInbound.clientStats
|
clientStats = dbInbound.clientStats
|
||||||
now = new Date().getTime()
|
now = new Date().getTime()
|
||||||
if (clients) {
|
if (clients) {
|
||||||
|
@ -968,15 +968,6 @@
|
||||||
this.submit(`/panel/inbound/${dbInboundId}/delClient/${clientId}`);
|
this.submit(`/panel/inbound/${dbInboundId}/delClient/${clientId}`);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getClients(protocol, clientSettings) {
|
|
||||||
switch (protocol) {
|
|
||||||
case Protocols.VMESS: return clientSettings.vmesses;
|
|
||||||
case Protocols.VLESS: return clientSettings.vlesses;
|
|
||||||
case Protocols.TROJAN: return clientSettings.trojans;
|
|
||||||
case Protocols.SHADOWSOCKS: return clientSettings.shadowsockses;
|
|
||||||
default: return null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getClientId(protocol, client) {
|
getClientId(protocol, client) {
|
||||||
switch (protocol) {
|
switch (protocol) {
|
||||||
case Protocols.TROJAN: return client.password;
|
case Protocols.TROJAN: return client.password;
|
||||||
|
@ -996,8 +987,9 @@
|
||||||
newDbInbound.listen = rootInbound.listen;
|
newDbInbound.listen = rootInbound.listen;
|
||||||
newDbInbound.port = rootInbound.port;
|
newDbInbound.port = rootInbound.port;
|
||||||
newInbound = newDbInbound.toInbound();
|
newInbound = newDbInbound.toInbound();
|
||||||
newInbound.stream.security = 'tls';
|
newInbound.stream.security = rootInbound.stream.security;
|
||||||
newInbound.stream.tls = rootInbound.stream.tls;
|
newInbound.stream.tls = rootInbound.stream.tls;
|
||||||
|
newInbound.stream.externalProxy = rootInbound.stream.externalProxy;
|
||||||
newDbInbound.streamSettings = newInbound.stream.toString();
|
newDbInbound.streamSettings = newInbound.stream.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1005,17 +997,17 @@
|
||||||
},
|
},
|
||||||
showQrcode(dbInboundId, client) {
|
showQrcode(dbInboundId, client) {
|
||||||
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
||||||
inbound = dbInbound.toInbound();
|
|
||||||
clients = this.getClients(dbInbound.protocol, inbound.settings);
|
|
||||||
index = this.findIndexOfClient(dbInbound.protocol, clients, client);
|
|
||||||
newDbInbound = this.checkFallback(dbInbound);
|
newDbInbound = this.checkFallback(dbInbound);
|
||||||
qrModal.show('{{ i18n "qrCode"}}', newDbInbound, index);
|
qrModal.show('{{ i18n "qrCode"}}', newDbInbound, client);
|
||||||
},
|
},
|
||||||
showInfo(dbInboundId, client) {
|
showInfo(dbInboundId, client) {
|
||||||
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
||||||
|
index=0;
|
||||||
|
if (dbInbound.isMultiUser()){
|
||||||
inbound = dbInbound.toInbound();
|
inbound = dbInbound.toInbound();
|
||||||
clients = this.getClients(dbInbound.protocol, inbound.settings);
|
clients = inbound.clients;
|
||||||
index = this.findIndexOfClient(dbInbound.protocol, clients, client);
|
index = this.findIndexOfClient(dbInbound.protocol, clients, client);
|
||||||
|
}
|
||||||
newDbInbound = this.checkFallback(dbInbound);
|
newDbInbound = this.checkFallback(dbInbound);
|
||||||
infoModal.show(newDbInbound, index);
|
infoModal.show(newDbInbound, index);
|
||||||
},
|
},
|
||||||
|
@ -1027,7 +1019,7 @@
|
||||||
this.loading()
|
this.loading()
|
||||||
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
||||||
inbound = dbInbound.toInbound();
|
inbound = dbInbound.toInbound();
|
||||||
clients = this.getClients(dbInbound.protocol, inbound.settings);
|
clients = inbound.clients;
|
||||||
index = this.findIndexOfClient(dbInbound.protocol, clients, client);
|
index = this.findIndexOfClient(dbInbound.protocol, clients, client);
|
||||||
clients[index].enable = !clients[index].enable;
|
clients[index].enable = !clients[index].enable;
|
||||||
clientId = this.getClientId(dbInbound.protocol, clients[index]);
|
clientId = this.getClientId(dbInbound.protocol, clients[index]);
|
||||||
|
@ -1041,15 +1033,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getInboundClients(dbInbound) {
|
getInboundClients(dbInbound) {
|
||||||
if (dbInbound.protocol == Protocols.VLESS) {
|
return dbInbound.toInbound().clients;
|
||||||
return dbInbound.toInbound().settings.vlesses;
|
|
||||||
} else if (dbInbound.protocol == Protocols.VMESS) {
|
|
||||||
return dbInbound.toInbound().settings.vmesses;
|
|
||||||
} else if (dbInbound.protocol == Protocols.TROJAN) {
|
|
||||||
return dbInbound.toInbound().settings.trojans;
|
|
||||||
} else if (dbInbound.protocol == Protocols.SHADOWSOCKS) {
|
|
||||||
return dbInbound.toInbound().settings.shadowsockses;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
resetClientTraffic(client, dbInboundId, confirmation = true) {
|
resetClientTraffic(client, dbInboundId, confirmation = true) {
|
||||||
if (confirmation){
|
if (confirmation){
|
||||||
|
@ -1179,11 +1163,11 @@
|
||||||
txtModal.show('{{ i18n "pages.inbounds.export"}}', newDbInbound.genInboundLinks, newDbInbound.remark);
|
txtModal.show('{{ i18n "pages.inbounds.export"}}', newDbInbound.genInboundLinks, newDbInbound.remark);
|
||||||
},
|
},
|
||||||
exportAllLinks() {
|
exportAllLinks() {
|
||||||
let copyText = '';
|
let copyText = [];
|
||||||
for (const dbInbound of this.dbInbounds) {
|
for (const dbInbound of this.dbInbounds) {
|
||||||
copyText += dbInbound.genInboundLinks;
|
copyText.push(dbInbound.genInboundLinks);
|
||||||
}
|
}
|
||||||
txtModal.show('{{ i18n "pages.inbounds.export"}}', copyText, 'All-Inbounds');
|
txtModal.show('{{ i18n "pages.inbounds.export"}}', copyText.join('\r\n'), 'All-Inbounds');
|
||||||
},
|
},
|
||||||
async startDataRefreshLoop() {
|
async startDataRefreshLoop() {
|
||||||
while (this.isRefreshEnabled) {
|
while (this.isRefreshEnabled) {
|
||||||
|
|
|
@ -1795,6 +1795,46 @@ func (s *InboundService) MigrationRequirements() {
|
||||||
|
|
||||||
// Remove orphaned traffics
|
// Remove orphaned traffics
|
||||||
tx.Where("inbound_id = 0").Delete(xray.ClientTraffic{})
|
tx.Where("inbound_id = 0").Delete(xray.ClientTraffic{})
|
||||||
|
|
||||||
|
// Migrate old MultiDomain to External Proxy
|
||||||
|
var externalProxy []struct {
|
||||||
|
Id int
|
||||||
|
Port int
|
||||||
|
StreamSettings []byte
|
||||||
|
}
|
||||||
|
err = tx.Raw(`select id, port, stream_settings
|
||||||
|
from inbounds
|
||||||
|
WHERE protocol in ('vmess','vless','trojan')
|
||||||
|
AND json_extract(stream_settings, '$.security') = 'tls'
|
||||||
|
AND json_extract(stream_settings, '$.tlsSettings.settings.domains') IS NOT NULL`).Scan(&externalProxy).Error
|
||||||
|
if err != nil || len(externalProxy) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ep := range externalProxy {
|
||||||
|
var reverses interface{}
|
||||||
|
var stream map[string]interface{}
|
||||||
|
json.Unmarshal(ep.StreamSettings, &stream)
|
||||||
|
if tlsSettings, ok := stream["tlsSettings"].(map[string]interface{}); ok {
|
||||||
|
if settings, ok := tlsSettings["settings"].(map[string]interface{}); ok {
|
||||||
|
if domains, ok := settings["domains"].([]interface{}); ok {
|
||||||
|
for _, domain := range domains {
|
||||||
|
if domainMap, ok := domain.(map[string]interface{}); ok {
|
||||||
|
domainMap["forceTls"] = "same"
|
||||||
|
domainMap["port"] = ep.Port
|
||||||
|
domainMap["dest"] = domainMap["domain"].(string)
|
||||||
|
delete(domainMap, "domain")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reverses = settings["domains"]
|
||||||
|
delete(settings, "domains")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stream["externalProxy"] = reverses
|
||||||
|
newStream, _ := json.MarshalIndent(stream, " ", " ")
|
||||||
|
tx.Model(model.Inbound{}).Where("id = ?", ep.Id).Update("stream_settings", newStream)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) MigrateDB() {
|
func (s *InboundService) MigrateDB() {
|
||||||
|
|
|
@ -135,6 +135,7 @@ func (s *XrayService) GetXrayConfig() (*xray.Config, error) {
|
||||||
inbound.Settings = string(modifiedSettings)
|
inbound.Settings = string(modifiedSettings)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(inbound.StreamSettings) > 0 {
|
||||||
// Unmarshal stream JSON
|
// Unmarshal stream JSON
|
||||||
var stream map[string]interface{}
|
var stream map[string]interface{}
|
||||||
json.Unmarshal([]byte(inbound.StreamSettings), &stream)
|
json.Unmarshal([]byte(inbound.StreamSettings), &stream)
|
||||||
|
@ -148,6 +149,10 @@ func (s *XrayService) GetXrayConfig() (*xray.Config, error) {
|
||||||
} else if ok2 {
|
} else if ok2 {
|
||||||
delete(realitySettings, "settings")
|
delete(realitySettings, "settings")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(stream, "externalProxy")
|
||||||
|
|
||||||
newStream, err := json.MarshalIndent(stream, "", " ")
|
newStream, err := json.MarshalIndent(stream, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -176,6 +176,7 @@
|
||||||
"telegramDesc" = "use Telegram ID without @ or chat IDs ( you can get it here @userinfobot or use '/id' command in bot )"
|
"telegramDesc" = "use Telegram ID without @ or chat IDs ( you can get it here @userinfobot or use '/id' command in bot )"
|
||||||
"subscriptionDesc" = "you can find your sub link on Details, also you can use the same name for several configurations"
|
"subscriptionDesc" = "you can find your sub link on Details, also you can use the same name for several configurations"
|
||||||
"info" = "Info"
|
"info" = "Info"
|
||||||
|
"same" = "Same"
|
||||||
|
|
||||||
[pages.client]
|
[pages.client]
|
||||||
"add" = "Add Client"
|
"add" = "Add Client"
|
||||||
|
|
|
@ -176,6 +176,7 @@
|
||||||
"telegramDesc" = "Utiliza el ID de Telegram sin @ o los IDs de chat (puedes obtenerlo aquí @userinfobot o usando el comando '/id' en el bot)."
|
"telegramDesc" = "Utiliza el ID de Telegram sin @ o los IDs de chat (puedes obtenerlo aquí @userinfobot o usando el comando '/id' en el bot)."
|
||||||
"subscriptionDesc" = "Puedes encontrar tu enlace de suscripción en Detalles, también puedes usar el mismo nombre para varias configuraciones."
|
"subscriptionDesc" = "Puedes encontrar tu enlace de suscripción en Detalles, también puedes usar el mismo nombre para varias configuraciones."
|
||||||
"info" = "Info"
|
"info" = "Info"
|
||||||
|
"same" = "misma"
|
||||||
|
|
||||||
[pages.client]
|
[pages.client]
|
||||||
"add" = "Agregar Cliente"
|
"add" = "Agregar Cliente"
|
||||||
|
|
|
@ -176,6 +176,7 @@
|
||||||
"telegramDesc" = "از آیدی تلگرام بدون @ یا آیدی چت استفاده کنید (می توانید آن را از اینجا دریافت کنید @userinfobot یا در ربات دستور '/id' را وارد کنید)"
|
"telegramDesc" = "از آیدی تلگرام بدون @ یا آیدی چت استفاده کنید (می توانید آن را از اینجا دریافت کنید @userinfobot یا در ربات دستور '/id' را وارد کنید)"
|
||||||
"subscriptionDesc" = "می توانید ساب لینک خود را در جزئیات پیدا کنید، همچنین می توانید از همین نام برای چندین کانفیگ استفاده کنید"
|
"subscriptionDesc" = "می توانید ساب لینک خود را در جزئیات پیدا کنید، همچنین می توانید از همین نام برای چندین کانفیگ استفاده کنید"
|
||||||
"info" = "اطلاعات"
|
"info" = "اطلاعات"
|
||||||
|
"same" = "همسان"
|
||||||
|
|
||||||
[pages.client]
|
[pages.client]
|
||||||
"add" = "کاربر جدید"
|
"add" = "کاربر جدید"
|
||||||
|
|
|
@ -176,6 +176,7 @@
|
||||||
"telegramDesc" = "Используйте идентификатор Telegram без символа @ или идентификатора чата (можно получить его здесь @userinfobot или использовать команду '/id' в боте)"
|
"telegramDesc" = "Используйте идентификатор Telegram без символа @ или идентификатора чата (можно получить его здесь @userinfobot или использовать команду '/id' в боте)"
|
||||||
"subscriptionDesc" = "Вы можете найти свою ссылку подписки в разделе 'Подробнее', также вы можете использовать одно и то же имя для нескольких конфигураций"
|
"subscriptionDesc" = "Вы можете найти свою ссылку подписки в разделе 'Подробнее', также вы можете использовать одно и то же имя для нескольких конфигураций"
|
||||||
"info" = "Информация"
|
"info" = "Информация"
|
||||||
|
"same" = "Тот же"
|
||||||
|
|
||||||
[pages.client]
|
[pages.client]
|
||||||
"add" = "Добавить пользователя"
|
"add" = "Добавить пользователя"
|
||||||
|
|
|
@ -176,6 +176,7 @@
|
||||||
"telegramDesc" = "Sử dụng Telegram ID mà không cần ký hiệu @ hoặc chat IDs (bạn có thể nhận được nó ở đây @userinfobot hoặc sử dụng lệnh '/id' trong bot)"
|
"telegramDesc" = "Sử dụng Telegram ID mà không cần ký hiệu @ hoặc chat IDs (bạn có thể nhận được nó ở đây @userinfobot hoặc sử dụng lệnh '/id' trong bot)"
|
||||||
"subscriptionDesc" = "Bạn có thể tìm liên kết đăng ký của mình trong Chi tiết, cũng như bạn có thể sử dụng cùng tên cho nhiều cấu hình khác nhau"
|
"subscriptionDesc" = "Bạn có thể tìm liên kết đăng ký của mình trong Chi tiết, cũng như bạn có thể sử dụng cùng tên cho nhiều cấu hình khác nhau"
|
||||||
"info" = "Thông tin"
|
"info" = "Thông tin"
|
||||||
|
"same" = "Giống nhau"
|
||||||
|
|
||||||
[pages.client]
|
[pages.client]
|
||||||
"add" = "Thêm Client"
|
"add" = "Thêm Client"
|
||||||
|
|
|
@ -176,6 +176,7 @@
|
||||||
"telegramDesc" = "使用 Telegram ID,不包含 @ 符号或聊天 ID(可以在 @userinfobot 处获取,或在机器人中使用'/id'命令)"
|
"telegramDesc" = "使用 Telegram ID,不包含 @ 符号或聊天 ID(可以在 @userinfobot 处获取,或在机器人中使用'/id'命令)"
|
||||||
"subscriptionDesc" = "您可以在详细信息上找到您的子链接,也可以对多个配置使用相同的名称"
|
"subscriptionDesc" = "您可以在详细信息上找到您的子链接,也可以对多个配置使用相同的名称"
|
||||||
"info" = "信息"
|
"info" = "信息"
|
||||||
|
"same" = "相同"
|
||||||
|
|
||||||
[pages.client]
|
[pages.client]
|
||||||
"add" = "添加客户端"
|
"add" = "添加客户端"
|
||||||
|
|
Loading…
Reference in a new issue