Compare commits

...

8 Commits

Author SHA1 Message Date
MHSanaei
d03e049320 v1.3.3 2023-04-28 01:03:59 +03:30
MHSanaei
957d9e24fb Revert "grpc.WithInsecure is deprecated"
This reverts commit 0b896d9c31.
2023-04-28 00:47:56 +03:30
MHSanaei
865e47e9a6 Update check_client_ip_job.go 2023-04-28 00:30:49 +03:30
MHSanaei
607c5d3598 [feature] add grpc multiMode 2023-04-28 00:15:06 +03:30
MHSanaei
8879541999 dark mode - default 2023-04-27 23:48:58 +03:30
MHSanaei
0b896d9c31 grpc.WithInsecure is deprecated 2023-04-27 23:48:22 +03:30
MHSanaei
6f4a2809e2 tls for ss - remove unused 2023-04-27 19:25:48 +03:30
MHSanaei
103a26edb6 [migrate] remove orphaned traffics
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2023-04-27 19:05:36 +03:30
12 changed files with 151 additions and 135 deletions

View File

@@ -20,10 +20,10 @@ bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.
## Install custom version ## Install custom version
To install your desired version you can add the version to the end of install command. Example for ver `v1.3.2`: To install your desired version you can add the version to the end of install command. Example for ver `v1.3.3`:
``` ```
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) v1.3.2 bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) v1.3.3
``` ```
# SSL # SSL

View File

@@ -1 +1 @@
1.3.2 1.3.3

2
go.sum
View File

@@ -9,8 +9,6 @@ github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff/go.mod h1:+RTT1BOk5P
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
github.com/bradleypeabody/gorilla-sessions-memcache v0.0.0-20181103040241-659414f458e1/go.mod h1:dkChI7Tbtx7H1Tj7TqGSZMOeGpMP5gLHtjroHd4agiI= github.com/bradleypeabody/gorilla-sessions-memcache v0.0.0-20181103040241-659414f458e1/go.mod h1:dkChI7Tbtx7H1Tj7TqGSZMOeGpMP5gLHtjroHd4agiI=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.8.7 h1:d3sry5vGgVq/OpgozRUNP6xBsSo0mtNdwliApw+SAMQ=
github.com/bytedance/sonic v1.8.7/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
github.com/bytedance/sonic v1.8.8 h1:Kj4AYbZSeENfyXicsYppYKO0K2YWab+i2UTSY7Ukz9Q= github.com/bytedance/sonic v1.8.8 h1:Kj4AYbZSeENfyXicsYppYKO0K2YWab+i2UTSY7Ukz9Q=
github.com/bytedance/sonic v1.8.8/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= github.com/bytedance/sonic v1.8.8/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=

View File

@@ -1,8 +1,9 @@
package logger package logger
import ( import (
"github.com/op/go-logging"
"os" "os"
"github.com/op/go-logging"
) )
var logger *logging.Logger var logger *logging.Logger

View File

@@ -28,20 +28,6 @@ const SSMethods = {
BLAKE3_CHACHA20_POLY1305: '2022-blake3-chacha20-poly1305', BLAKE3_CHACHA20_POLY1305: '2022-blake3-chacha20-poly1305',
}; };
const RULE_IP = {
PRIVATE: 'geoip:private',
CN: 'geoip:cn',
};
const RULE_DOMAIN = {
ADS: 'geosite:category-ads',
ADS_ALL: 'geosite:category-ads-all',
CN: 'geosite:cn',
GOOGLE: 'geosite:google',
FACEBOOK: 'geosite:facebook',
SPEEDTEST: 'geosite:speedtest',
};
const XTLS_FLOW_CONTROL = { const XTLS_FLOW_CONTROL = {
ORIGIN: "xtls-rprx-origin", ORIGIN: "xtls-rprx-origin",
DIRECT: "xtls-rprx-direct", DIRECT: "xtls-rprx-direct",
@@ -101,8 +87,6 @@ const ALPN_OPTION = {
Object.freeze(Protocols); Object.freeze(Protocols);
Object.freeze(VmessMethods); Object.freeze(VmessMethods);
Object.freeze(SSMethods); Object.freeze(SSMethods);
Object.freeze(RULE_IP);
Object.freeze(RULE_DOMAIN);
Object.freeze(XTLS_FLOW_CONTROL); Object.freeze(XTLS_FLOW_CONTROL);
Object.freeze(TLS_FLOW_CONTROL); Object.freeze(TLS_FLOW_CONTROL);
Object.freeze(TLS_VERSION_OPTION); Object.freeze(TLS_VERSION_OPTION);
@@ -456,18 +440,26 @@ class QuicStreamSettings extends XrayCommonClass {
} }
class GrpcStreamSettings extends XrayCommonClass { class GrpcStreamSettings extends XrayCommonClass {
constructor(serviceName="") { constructor(
serviceName="",
multiMode=false
) {
super(); super();
this.serviceName = serviceName; this.serviceName = serviceName;
this.multiMode = multiMode;
} }
static fromJson(json={}) { static fromJson(json={}) {
return new GrpcStreamSettings(json.serviceName); return new GrpcStreamSettings(
json.serviceName,
json.multiMode
);
} }
toJson() { toJson() {
return { return {
serviceName: this.serviceName, serviceName: this.serviceName,
multiMode: this.multiMode
} }
} }
} }
@@ -1160,6 +1152,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:
break; break;
default: default:
return false; return false;
@@ -1261,50 +1254,6 @@ class Inbound extends XrayCommonClass {
if (this.protocol !== Protocols.VMESS) { if (this.protocol !== Protocols.VMESS) {
return ''; return '';
} }
let network = this.stream.network;
let type = 'none';
let host = '';
let path = '';
if (network === 'tcp') {
let tcp = this.stream.tcp;
type = tcp.type;
if (type === 'http') {
let request = tcp.request;
path = request.path.join(',');
let index = request.headers.findIndex(header => header.name.toLowerCase() === 'host');
if (index >= 0) {
host = request.headers[index].value;
}
}
} else if (network === 'kcp') {
let kcp = this.stream.kcp;
type = kcp.type;
path = kcp.seed;
} else if (network === 'ws') {
let ws = this.stream.ws;
path = ws.path;
let index = ws.headers.findIndex(header => header.name.toLowerCase() === 'host');
if (index >= 0) {
host = ws.headers[index].value;
}
} else if (network === 'http') {
network = 'h2';
path = this.stream.http.path;
host = this.stream.http.host.join(',');
} else if (network === 'quic') {
type = this.stream.quic.type;
host = this.stream.quic.security;
path = this.stream.quic.key;
} else if (network === 'grpc') {
path = this.stream.grpc.serviceName;
}
if (this.stream.security === 'tls') {
if (!ObjectUtil.isEmpty(this.stream.tls.server)) {
address = this.stream.tls.server;
}
}
let obj = { let obj = {
v: '2', v: '2',
ps: remark, ps: remark,
@@ -1312,16 +1261,66 @@ class Inbound extends XrayCommonClass {
port: this.port, port: this.port,
id: this.settings.vmesses[clientIndex].id, id: this.settings.vmesses[clientIndex].id,
aid: this.settings.vmesses[clientIndex].alterId, aid: this.settings.vmesses[clientIndex].alterId,
net: network, net: this.stream.network,
type: type, type: 'none',
host: host,
path: path,
tls: this.stream.security, tls: this.stream.security,
sni: this.stream.tls.settings.serverName,
fp: this.stream.tls.settings.fingerprint,
alpn: this.stream.tls.alpn.join(','),
allowInsecure: this.stream.tls.settings.allowInsecure,
}; };
let network = this.stream.network;
if (network === 'tcp') {
let tcp = this.stream.tcp;
obj.type = tcp.type;
if (tcp.type === 'http') {
let request = tcp.request;
obj.path = request.path.join(',');
let index = request.headers.findIndex(header => header.name.toLowerCase() === 'host');
if (index >= 0) {
obj.host = request.headers[index].value;
}
}
} else if (network === 'kcp') {
let kcp = this.stream.kcp;
obj.type = kcp.type;
obj.path = kcp.seed;
} else if (network === 'ws') {
let ws = this.stream.ws;
obj.path = ws.path;
let index = ws.headers.findIndex(header => header.name.toLowerCase() === 'host');
if (index >= 0) {
obj.host = ws.headers[index].value;
}
} else if (network === 'http') {
obj.net = 'h2';
obj.path = this.stream.http.path;
obj.host = this.stream.http.host.join(',');
} else if (network === 'quic') {
obj.type = this.stream.quic.type;
obj.host = this.stream.quic.security;
obj.path = this.stream.quic.key;
} else if (network === 'grpc') {
obj.path = this.stream.grpc.serviceName;
if (this.stream.grpc.multiMode){
obj.type = 'multi'
}
}
if (this.stream.security === 'tls') {
if (!ObjectUtil.isEmpty(this.stream.tls.server)) {
obj.add = 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)){
obj.fp = this.stream.tls.settings.fingerprint;
}
if (this.stream.tls.alpn.length>0){
obj.alpn = this.stream.tls.alpn.join(',');
}
if (this.stream.tls.settings.allowInsecure){
obj.allowInsecure = this.stream.tls.settings.allowInsecure;
}
}
return 'vmess://' + base64(JSON.stringify(obj, null, 2)); return 'vmess://' + base64(JSON.stringify(obj, null, 2));
} }
@@ -1374,6 +1373,9 @@ class Inbound extends XrayCommonClass {
case "grpc": case "grpc":
const grpc = this.stream.grpc; const grpc = this.stream.grpc;
params.set("serviceName", grpc.serviceName); params.set("serviceName", grpc.serviceName);
if(grpc.multiMode){
params.set("mode", "multi");
}
break; break;
} }
@@ -1491,6 +1493,9 @@ class Inbound extends XrayCommonClass {
case "grpc": case "grpc":
const grpc = this.stream.grpc; const grpc = this.stream.grpc;
params.set("serviceName", grpc.serviceName); params.set("serviceName", grpc.serviceName);
if(grpc.multiMode){
params.set("mode", "multi");
}
break; break;
} }

View File

@@ -2,8 +2,9 @@ package global
import ( import (
"context" "context"
"github.com/robfig/cron/v3"
_ "unsafe" _ "unsafe"
"github.com/robfig/cron/v3"
) )
var webServer WebServer var webServer WebServer

View File

@@ -66,7 +66,7 @@
const siderDrawer = { const siderDrawer = {
visible: false, visible: false,
collapsed: false, collapsed: false,
isDarkTheme: localStorage.getItem("dark-mode") === 'true' ? true : false, isDarkTheme: localStorage.getItem("dark-mode") === 'false' ? false : true,
show() { show() {
this.visible = true; this.visible = true;
}, },

View File

@@ -3,5 +3,8 @@
<a-form-item label="ServiceName"> <a-form-item label="ServiceName">
<a-input v-model.trim="inbound.stream.grpc.serviceName"></a-input> <a-input v-model.trim="inbound.stream.grpc.serviceName"></a-input>
</a-form-item> </a-form-item>
<a-form-item label="Multi Mode">
<a-switch v-model="inbound.stream.grpc.multiMode"></a-switch>
</a-form-item>
</a-form> </a-form>
{{end}} {{end}}

View File

@@ -41,6 +41,7 @@
<template v-if="inbound.isGrpc"> <template v-if="inbound.isGrpc">
<tr><td>grpc serviceName</td><td><a-tag color="green">[[ inbound.serviceName ]]</a-tag></td></tr> <tr><td>grpc serviceName</td><td><a-tag color="green">[[ inbound.serviceName ]]</a-tag></td></tr>
<tr><td>grpc multiMode</td><td><a-tag color="green">[[ inbound.stream.grpc.multiMode ]]</a-tag></td></tr>
</template> </template>
</table> </table>
</td></tr> </td></tr>

View File

@@ -4,23 +4,22 @@ import (
"encoding/json" "encoding/json"
"os" "os"
"regexp" "regexp"
ss "strings"
"x-ui/database" "x-ui/database"
"x-ui/database/model" "x-ui/database/model"
"x-ui/logger" "x-ui/logger"
"x-ui/web/service" "x-ui/web/service"
"x-ui/xray" "x-ui/xray"
// "strconv"
"github.com/go-cmd/cmd"
"net" "net"
"sort" "sort"
"strings" "strings"
"time" "time"
"github.com/go-cmd/cmd"
) )
type CheckClientIpJob struct { type CheckClientIpJob struct {
xrayService service.XrayService xrayService service.XrayService
inboundService service.InboundService
} }
var job *CheckClientIpJob var job *CheckClientIpJob
@@ -36,7 +35,7 @@ func (j *CheckClientIpJob) Run() {
processLogFile() processLogFile()
// disAllowedIps = []string{"192.168.1.183","192.168.1.197"} // disAllowedIps = []string{"192.168.1.183","192.168.1.197"}
blockedIps := []byte(ss.Join(disAllowedIps, ",")) blockedIps := []byte(strings.Join(disAllowedIps, ","))
err := os.WriteFile(xray.GetBlockedIPsPath(), blockedIps, 0755) err := os.WriteFile(xray.GetBlockedIPsPath(), blockedIps, 0755)
checkError(err) checkError(err)
@@ -58,7 +57,7 @@ func processLogFile() {
checkError(err) checkError(err)
} }
lines := ss.Split(string(data), "\n") lines := strings.Split(string(data), "\n")
for _, line := range lines { for _, line := range lines {
ipRegx, _ := regexp.Compile(`[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+`) ipRegx, _ := regexp.Compile(`[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+`)
emailRegx, _ := regexp.Compile(`email:.+`) emailRegx, _ := regexp.Compile(`email:.+`)
@@ -74,7 +73,7 @@ func processLogFile() {
if matchesEmail == "" { if matchesEmail == "" {
continue continue
} }
matchesEmail = ss.Split(matchesEmail, "email: ")[1] matchesEmail = strings.Split(matchesEmail, "email: ")[1]
if InboundClientIps[matchesEmail] != nil { if InboundClientIps[matchesEmail] != nil {
if contains(InboundClientIps[matchesEmail], ip) { if contains(InboundClientIps[matchesEmail], ip) {
@@ -92,14 +91,12 @@ func processLogFile() {
for clientEmail, ips := range InboundClientIps { for clientEmail, ips := range InboundClientIps {
inboundClientIps, err := GetInboundClientIps(clientEmail) inboundClientIps, err := GetInboundClientIps(clientEmail)
sort.Sort(sort.StringSlice(ips)) sort.Strings(ips)
if err != nil { if err != nil {
addInboundClientIps(clientEmail, ips) addInboundClientIps(clientEmail, ips)
} else { } else {
updateInboundClientIps(inboundClientIps, clientEmail, ips) updateInboundClientIps(inboundClientIps, clientEmail, ips)
} }
} }
// check if inbound connection is more than limited ip and drop connection // check if inbound connection is more than limited ip and drop connection
@@ -202,6 +199,8 @@ func updateInboundClientIps(inboundClientIps *model.InboundClientIps, clientEmai
json.Unmarshal([]byte(inbound.Settings), &settings) json.Unmarshal([]byte(inbound.Settings), &settings)
clients := settings["clients"] clients := settings["clients"]
var disAllowedIps []string // initialize the slice
for _, client := range clients { for _, client := range clients {
if client.Email == clientEmail { if client.Email == clientEmail {
@@ -214,7 +213,7 @@ func updateInboundClientIps(inboundClientIps *model.InboundClientIps, clientEmai
} }
} }
logger.Debug("disAllowedIps ", disAllowedIps) logger.Debug("disAllowedIps ", disAllowedIps)
sort.Sort(sort.StringSlice(disAllowedIps)) sort.Strings(disAllowedIps)
db := database.GetDB() db := database.GetDB()
err = db.Save(inboundClientIps).Error err = db.Save(inboundClientIps).Error
@@ -223,6 +222,7 @@ func updateInboundClientIps(inboundClientIps *model.InboundClientIps, clientEmai
} }
return nil return nil
} }
func DisableInbound(id int) error { func DisableInbound(id int) error {
db := database.GetDB() db := database.GetDB()
result := db.Model(model.Inbound{}). result := db.Model(model.Inbound{}).

View File

@@ -912,6 +912,8 @@ func (s *InboundService) SearchInbounds(query string) ([]*model.Inbound, error)
func (s *InboundService) MigrationRequirements() { func (s *InboundService) MigrationRequirements() {
db := database.GetDB() db := database.GetDB()
// Fix inbounds based problems
var inbounds []*model.Inbound var inbounds []*model.Inbound
err := db.Model(model.Inbound{}).Where("protocol IN (?)", []string{"vmess", "vless", "trojan"}).Find(&inbounds).Error err := db.Model(model.Inbound{}).Where("protocol IN (?)", []string{"vmess", "vless", "trojan"}).Find(&inbounds).Error
if err != nil && err != gorm.ErrRecordNotFound { if err != nil && err != gorm.ErrRecordNotFound {
@@ -922,6 +924,7 @@ func (s *InboundService) MigrationRequirements() {
json.Unmarshal([]byte(inbounds[inbound_index].Settings), &settings) json.Unmarshal([]byte(inbounds[inbound_index].Settings), &settings)
clients, ok := settings["clients"].([]interface{}) clients, ok := settings["clients"].([]interface{})
if ok { if ok {
// Fix Clinet configuration problems
var newClients []interface{} var newClients []interface{}
for client_index := range clients { for client_index := range clients {
c := clients[client_index].(map[string]interface{}) c := clients[client_index].(map[string]interface{})
@@ -947,6 +950,7 @@ func (s *InboundService) MigrationRequirements() {
inbounds[inbound_index].Settings = string(modifiedSettings) inbounds[inbound_index].Settings = string(modifiedSettings)
} }
// Add client traffic row for all clients which has email
modelClients, err := s.getClients(inbounds[inbound_index]) modelClients, err := s.getClients(inbounds[inbound_index])
if err != nil { if err != nil {
return return
@@ -962,4 +966,7 @@ func (s *InboundService) MigrationRequirements() {
} }
} }
db.Save(inbounds) db.Save(inbounds)
// Remove orphaned traffics
db.Where("inbound_id = 0").Delete(xray.ClientTraffic{})
} }

View File

@@ -102,80 +102,89 @@ func (s *SubService) getLink(inbound *model.Inbound, email string) string {
} }
func (s *SubService) genVmessLink(inbound *model.Inbound, email string) string { func (s *SubService) genVmessLink(inbound *model.Inbound, email string) string {
address := s.address
if inbound.Protocol != model.VMess { if inbound.Protocol != model.VMess {
return "" return ""
} }
obj := map[string]interface{}{
"v": "2",
"ps": email,
"add": s.address,
"port": inbound.Port,
"type": "none",
}
var stream map[string]interface{} var stream map[string]interface{}
json.Unmarshal([]byte(inbound.StreamSettings), &stream) json.Unmarshal([]byte(inbound.StreamSettings), &stream)
network, _ := stream["network"].(string) network, _ := stream["network"].(string)
typeStr := "none" obj["net"] = network
host := ""
path := ""
sni := ""
fp := ""
var alpn []string
allowInsecure := false
switch network { switch network {
case "tcp": case "tcp":
tcp, _ := stream["tcpSettings"].(map[string]interface{}) tcp, _ := stream["tcpSettings"].(map[string]interface{})
header, _ := tcp["header"].(map[string]interface{}) header, _ := tcp["header"].(map[string]interface{})
typeStr, _ = header["type"].(string) typeStr, _ := header["type"].(string)
obj["type"] = typeStr
if typeStr == "http" { if typeStr == "http" {
request := header["request"].(map[string]interface{}) request := header["request"].(map[string]interface{})
requestPath, _ := request["path"].([]interface{}) requestPath, _ := request["path"].([]interface{})
path = requestPath[0].(string) obj["path"] = requestPath[0].(string)
headers, _ := request["headers"].(map[string]interface{}) headers, _ := request["headers"].(map[string]interface{})
host = searchHost(headers) obj["host"] = searchHost(headers)
} }
case "kcp": case "kcp":
kcp, _ := stream["kcpSettings"].(map[string]interface{}) kcp, _ := stream["kcpSettings"].(map[string]interface{})
header, _ := kcp["header"].(map[string]interface{}) header, _ := kcp["header"].(map[string]interface{})
typeStr, _ = header["type"].(string) obj["type"], _ = header["type"].(string)
path, _ = kcp["seed"].(string) obj["path"], _ = kcp["seed"].(string)
case "ws": case "ws":
ws, _ := stream["wsSettings"].(map[string]interface{}) ws, _ := stream["wsSettings"].(map[string]interface{})
path = ws["path"].(string) obj["path"] = ws["path"].(string)
headers, _ := ws["headers"].(map[string]interface{}) headers, _ := ws["headers"].(map[string]interface{})
host = searchHost(headers) obj["host"] = searchHost(headers)
case "http": case "http":
network = "h2" obj["net"] = "h2"
http, _ := stream["httpSettings"].(map[string]interface{}) http, _ := stream["httpSettings"].(map[string]interface{})
path, _ = http["path"].(string) obj["path"], _ = http["path"].(string)
host = searchHost(http) obj["host"] = searchHost(http)
case "quic": case "quic":
quic, _ := stream["quicSettings"].(map[string]interface{}) quic, _ := stream["quicSettings"].(map[string]interface{})
header := quic["header"].(map[string]interface{}) header := quic["header"].(map[string]interface{})
typeStr, _ = header["type"].(string) obj["type"], _ = header["type"].(string)
host, _ = quic["security"].(string) obj["host"], _ = quic["security"].(string)
path, _ = quic["key"].(string) obj["path"], _ = quic["key"].(string)
case "grpc": case "grpc":
grpc, _ := stream["grpcSettings"].(map[string]interface{}) grpc, _ := stream["grpcSettings"].(map[string]interface{})
path = grpc["serviceName"].(string) obj["path"] = grpc["serviceName"].(string)
if grpc["multiMode"].(bool) {
obj["type"] = "multi"
}
} }
security, _ := stream["security"].(string) security, _ := stream["security"].(string)
obj["tls"] = security
if security == "tls" { if security == "tls" {
tlsSetting, _ := stream["tlsSettings"].(map[string]interface{}) tlsSetting, _ := stream["tlsSettings"].(map[string]interface{})
alpns, _ := tlsSetting["alpn"].([]interface{}) alpns, _ := tlsSetting["alpn"].([]interface{})
for _, a := range alpns { if len(alpns) > 0 {
alpn = append(alpn, a.(string)) var alpn []string
for _, a := range alpns {
alpn = append(alpn, a.(string))
}
obj["alpn"] = strings.Join(alpn, ",")
} }
tlsSettings, _ := searchKey(tlsSetting, "settings") tlsSettings, _ := searchKey(tlsSetting, "settings")
if tlsSetting != nil { if tlsSetting != nil {
if sniValue, ok := searchKey(tlsSettings, "serverName"); ok { if sniValue, ok := searchKey(tlsSettings, "serverName"); ok {
sni, _ = sniValue.(string) obj["sni"], _ = sniValue.(string)
} }
if fpValue, ok := searchKey(tlsSettings, "fingerprint"); ok { if fpValue, ok := searchKey(tlsSettings, "fingerprint"); ok {
fp, _ = fpValue.(string) obj["fp"], _ = fpValue.(string)
} }
if insecure, ok := searchKey(tlsSettings, "allowInsecure"); ok { if insecure, ok := searchKey(tlsSettings, "allowInsecure"); ok {
allowInsecure, _ = insecure.(bool) obj["allowInsecure"], _ = insecure.(bool)
} }
} }
serverName, _ := tlsSetting["serverName"].(string) serverName, _ := tlsSetting["serverName"].(string)
if serverName != "" { if serverName != "" {
address = serverName obj["add"] = serverName
} }
} }
@@ -187,24 +196,9 @@ func (s *SubService) genVmessLink(inbound *model.Inbound, email string) string {
break break
} }
} }
obj["id"] = clients[clientIndex].ID
obj["aid"] = clients[clientIndex].AlterIds
obj := map[string]interface{}{
"v": "2",
"ps": email,
"add": address,
"port": inbound.Port,
"id": clients[clientIndex].ID,
"aid": clients[clientIndex].AlterIds,
"net": network,
"type": typeStr,
"host": host,
"path": path,
"tls": security,
"sni": sni,
"fp": fp,
"alpn": strings.Join(alpn, ","),
"allowInsecure": allowInsecure,
}
jsonStr, _ := json.MarshalIndent(obj, "", " ") jsonStr, _ := json.MarshalIndent(obj, "", " ")
return "vmess://" + base64.StdEncoding.EncodeToString(jsonStr) return "vmess://" + base64.StdEncoding.EncodeToString(jsonStr)
} }
@@ -266,6 +260,9 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
case "grpc": case "grpc":
grpc, _ := stream["grpcSettings"].(map[string]interface{}) grpc, _ := stream["grpcSettings"].(map[string]interface{})
params["serviceName"] = grpc["serviceName"].(string) params["serviceName"] = grpc["serviceName"].(string)
if grpc["multiMode"].(bool) {
params["mode"] = "multi"
}
} }
security, _ := stream["security"].(string) security, _ := stream["security"].(string)
@@ -444,6 +441,9 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string
case "grpc": case "grpc":
grpc, _ := stream["grpcSettings"].(map[string]interface{}) grpc, _ := stream["grpcSettings"].(map[string]interface{})
params["serviceName"] = grpc["serviceName"].(string) params["serviceName"] = grpc["serviceName"].(string)
if grpc["multiMode"].(bool) {
params["mode"] = "multi"
}
} }
security, _ := stream["security"].(string) security, _ := stream["security"].(string)