diff --git a/backend/api/api.go b/backend/api/api.go index 61db6cb..992139e 100644 --- a/backend/api/api.go +++ b/backend/api/api.go @@ -88,8 +88,7 @@ func (a *APIHandler) postHandler(c *gin.Context) { obj := c.Request.FormValue("object") act := c.Request.FormValue("action") data := c.Request.FormValue("data") - userLinks := c.Request.FormValue("userLinks") - objs, err := a.ConfigService.Save(obj, act, json.RawMessage(data), json.RawMessage(userLinks), loginUser, hostname) + objs, err := a.ConfigService.Save(obj, act, json.RawMessage(data), loginUser, hostname) if err != nil { jsonMsg(c, "save", err) return diff --git a/backend/cmd/migration/1_2.go b/backend/cmd/migration/1_2.go index 7d16d3f..f808db9 100644 --- a/backend/cmd/migration/1_2.go +++ b/backend/cmd/migration/1_2.go @@ -90,7 +90,25 @@ func moveJsonToDb(db *gorm.DB) error { db.Raw("select id,addrs,out_json from inbound_data where tag = ?", tag).Find(&inbData) if inbData.Id > 0 { inbObj["out_json"] = inbData.OutJson - inbObj["addrs"] = inbData.Addrs + var addrs []map[string]interface{} + json.Unmarshal(inbData.Addrs, &addrs) + for index, addr := range addrs { + if tlsEnable, ok := addr["tls"].(bool); ok { + newTls := map[string]interface{}{ + "enabled": tlsEnable, + } + if insecure, ok := addr["insecure"].(bool); ok { + newTls["insecure"] = insecure + delete(addrs[index], "insecure") + } + if sni, ok := addr["server_name"].(string); ok { + newTls["server_name"] = sni + delete(addrs[index], "server_name") + } + addrs[index]["tls"] = newTls + } + } + inbObj["addrs"] = addrs } else { inbObj["out_json"] = json.RawMessage("{}") inbObj["addrs"] = json.RawMessage("[]") diff --git a/backend/database/model/inbounds.go b/backend/database/model/inbounds.go index 4b3229b..72e09ec 100644 --- a/backend/database/model/inbounds.go +++ b/backend/database/model/inbounds.go @@ -90,7 +90,7 @@ func (i Inbound) MarshalFull() (*map[string]interface{}, error) { combined["out_json"] = i.OutJson if i.Options != nil { - var restFields map[string]json.RawMessage + var restFields map[string]interface{} if err := json.Unmarshal(i.Options, &restFields); err != nil { return nil, err } diff --git a/backend/service/client.go b/backend/service/client.go index d3798c4..317ff74 100644 --- a/backend/service/client.go +++ b/backend/service/client.go @@ -5,6 +5,7 @@ import ( "s-ui/database" "s-ui/database/model" "s-ui/logger" + "s-ui/util" "time" "gorm.io/gorm" @@ -24,7 +25,7 @@ func (s *ClientService) GetAll() ([]model.Client, error) { return clients, nil } -func (s *ClientService) Save(tx *gorm.DB, act string, data json.RawMessage) ([]uint, error) { +func (s *ClientService) Save(tx *gorm.DB, act string, data json.RawMessage, hostname string) ([]uint, error) { var err error var inboundIds []uint @@ -39,6 +40,10 @@ func (s *ClientService) Save(tx *gorm.DB, act string, data json.RawMessage) ([]u if err != nil { return nil, err } + err = s.updateLinksWithFixedInbounds(tx, []*model.Client{&client}, inboundIds, hostname) + if err != nil { + return nil, err + } err = tx.Save(&client).Error if err != nil { return nil, err @@ -67,33 +72,128 @@ func (s *ClientService) Save(tx *gorm.DB, act string, data json.RawMessage) ([]u return inboundIds, nil } -func (s *ClientService) UpdateLinks(tx *gorm.DB, links json.RawMessage) error { - var userLinks []interface{} - err := json.Unmarshal(links, &userLinks) +func (s *ClientService) updateLinksWithFixedInbounds(tx *gorm.DB, clients []*model.Client, inbounIds []uint, hostname string) error { + var err error + var inbounds []model.Inbound + + // Zero inbounds means removing local links only + if len(inbounIds) > 0 { + err = tx.Model(model.Inbound{}).Preload("Tls").Where("id in ? and type in ?", inbounIds, util.InboundTypeWithLink).Find(&inbounds).Error + if err != nil { + return err + } + } + for index, client := range clients { + var clientLinks, newClientLinks []map[string]string + json.Unmarshal(client.Links, &clientLinks) + + for _, inbound := range inbounds { + newLinks := util.LinkGenerator(client.Config, &inbound, hostname) + for _, newLink := range newLinks { + newClientLinks = append(newClientLinks, map[string]string{ + "remark": inbound.Tag, + "type": "local", + "uri": newLink, + }) + } + } + + // Add no local links + for _, clientLink := range clientLinks { + if clientLink["type"] != "local" { + newClientLinks = append(newClientLinks, clientLink) + } + } + + clients[index].Links, err = json.MarshalIndent(newClientLinks, "", " ") + if err != nil { + return err + } + } + return nil +} + +func (s *ClientService) UpdateClientsOnInboundDelete(tx *gorm.DB, id uint, tag string) error { + var clients []model.Client + err := tx.Table("clients"). + Where("EXISTS (SELECT 1 FROM json_each(clients.inbounds) WHERE json_each.value = ?)", id). + Find(&clients).Error if err != nil { return err } - for _, userLink := range userLinks { - userLinkData, _ := userLink.(map[string]interface{}) - userId, _ := userLinkData["id"].(float64) - links, err := json.MarshalIndent(userLinkData["links"], "", " ") + for _, client := range clients { + // Delete inbounds + var clientInbounds, newClientInbounds []uint + json.Unmarshal(client.Inbounds, &clientInbounds) + for _, clientInbound := range clientInbounds { + if clientInbound != id { + newClientInbounds = append(newClientInbounds, clientInbound) + } + } + client.Inbounds, err = json.MarshalIndent(newClientInbounds, "", " ") if err != nil { return err } - if inbounds, ok := userLinkData["inbounds"]; ok { - inbounds, err := json.MarshalIndent(inbounds, "", " ") - if err != nil { - return err - } - err = tx.Model(model.Client{}).Where("id = ?", uint(userId)).Update("inbounds", inbounds).Error - if err != nil { - return err + // Delete links + var clientLinks, newClientLinks []map[string]string + json.Unmarshal(client.Links, &clientLinks) + for _, clientLink := range clientLinks { + if clientLink["remark"] != tag { + newClientLinks = append(newClientLinks, clientLink) } } - err = tx.Model(model.Client{}).Where("id = ?", uint(userId)).Update("links", links).Error + client.Links, err = json.MarshalIndent(newClientLinks, "", " ") if err != nil { return err } + err = tx.Save(&client).Error + if err != nil { + return err + } + } + return nil +} + +func (s *ClientService) UpdateLinksByInboundChange(tx *gorm.DB, inbounIds []uint, hostname string) error { + var inbounds []model.Inbound + err := tx.Model(model.Inbound{}).Preload("Tls").Where("id in ? and type in ?", inbounIds, util.InboundTypeWithLink).Find(&inbounds).Error + if err != nil && err != gorm.ErrRecordNotFound { + return err + } + for _, inbound := range inbounds { + var clients []model.Client + err = tx.Table("clients"). + Where("EXISTS (SELECT 1 FROM json_each(clients.inbounds) WHERE json_each.value = ?)", inbound.Id). + Find(&clients).Error + if err != nil { + return err + } + for _, client := range clients { + var clientLinks, newClientLinks []map[string]string + json.Unmarshal(client.Links, &clientLinks) + newLinks := util.LinkGenerator(client.Config, &inbound, hostname) + for _, newLink := range newLinks { + newClientLinks = append(newClientLinks, map[string]string{ + "remark": inbound.Tag, + "type": "local", + "uri": newLink, + }) + } + for _, clientLink := range clientLinks { + if clientLink["remark"] != inbound.Tag { + newClientLinks = append(newClientLinks, clientLink) + } + } + + client.Links, err = json.MarshalIndent(newClientLinks, "", " ") + if err != nil { + return err + } + err = tx.Save(&client).Error + if err != nil { + return err + } + } } return nil } diff --git a/backend/service/config.go b/backend/service/config.go index a72d151..bca9fd2 100644 --- a/backend/service/config.go +++ b/backend/service/config.go @@ -123,9 +123,10 @@ func (s *ConfigService) StopCore() error { return nil } -func (s *ConfigService) Save(obj string, act string, data json.RawMessage, userLinks json.RawMessage, loginUser string, hostname string) ([]string, error) { +func (s *ConfigService) Save(obj string, act string, data json.RawMessage, loginUser string, hostname string) ([]string, error) { var err error var inboundIds []uint + var inboundId uint db := database.GetDB() tx := db.Begin() @@ -139,11 +140,11 @@ func (s *ConfigService) Save(obj string, act string, data json.RawMessage, userL switch obj { case "clients": - inboundIds, err = s.ClientService.Save(tx, act, data) + inboundIds, err = s.ClientService.Save(tx, act, data, hostname) case "tls": inboundIds, err = s.TlsService.Save(tx, act, data) case "inbounds": - err = s.InboundService.Save(tx, act, data, hostname) + inboundId, err = s.InboundService.Save(tx, act, data, hostname) case "outbounds": err = s.OutboundService.Save(tx, act, data) case "endpoints": @@ -161,13 +162,6 @@ func (s *ConfigService) Save(obj string, act string, data json.RawMessage, userL return nil, err } - if len(userLinks) > 0 { - err = s.ClientService.UpdateLinks(tx, userLinks) - if err != nil { - return nil, err - } - } - dt := time.Now().Unix() err = tx.Create(&model.Changes{ DateTime: dt, @@ -188,8 +182,25 @@ func (s *ConfigService) Save(obj string, act string, data json.RawMessage, userL // Update side changes // Update client links - if len(userLinks) > 0 { - err = s.ClientService.UpdateLinks(tx, userLinks) + if obj == "tls" && len(inboundIds) > 0 { + err = s.ClientService.UpdateLinksByInboundChange(tx, inboundIds, hostname) + if err != nil { + return nil, err + } + objs = append(objs, "clients") + } + if obj == "inbounds" && act != "add" { + switch act { + case "edit": + err = s.ClientService.UpdateLinksByInboundChange(tx, []uint{inboundId}, hostname) + case "del": + var tag string + err = json.Unmarshal(data, &tag) + if err != nil { + return nil, err + } + err = s.ClientService.UpdateClientsOnInboundDelete(tx, inboundId, tag) + } if err != nil { return nil, err } diff --git a/backend/sub/jsonService.go b/backend/sub/jsonService.go index 33db779..496f8d7 100644 --- a/backend/sub/jsonService.go +++ b/backend/sub/jsonService.go @@ -94,8 +94,13 @@ func (j *JsonService) getData(subId string) (*model.Client, []*model.Inbound, er if err != nil { return nil, nil, err } + var clientInbounds []uint + err = json.Unmarshal(client.Inbounds, &clientInbounds) + if err != nil { + return nil, nil, err + } var inbounds []*model.Inbound - err = db.Model(model.Inbound{}).Where("tag in ?", client.Inbounds).Find(&inbounds).Error + err = db.Model(model.Inbound{}).Where("id in ?", clientInbounds).Find(&inbounds).Error if err != nil { return nil, nil, err } @@ -156,22 +161,14 @@ func (j *JsonService) getOutbounds(clientConfig json.RawMessage, inbounds []*mod newOut["server_port"] = int(port) // Override TLS - newTls, overrideTls := addr["tls"].(bool) - if overrideTls { - tlsIf := map[string]interface{}{} - if newTls { - tlsIf["enabled"] = true - newSNI, overrideSNI := addr["server_name"].(string) - if overrideSNI { - tlsIf["server_name"] = newSNI - } - newInsecure, overrideInsecure := addr["insecure"].(bool) - if overrideInsecure { - tlsIf["insecure"] = newInsecure - } + outTls, _ := newOut["tls"].(map[string]interface{}) + if addrTls, ok := addr["tls"].(map[string]interface{}); ok { + for key, value := range addrTls { + outTls[key] = value } - newOut["tls"] = tlsIf } + newOut["tls"] = outTls + remark, _ := addr["remark"].(string) newTag := fmt.Sprintf("%d.%s%s", index+1, tag, remark) newOut["tag"] = newTag diff --git a/backend/util/genLink.go b/backend/util/genLink.go new file mode 100644 index 0000000..e113008 --- /dev/null +++ b/backend/util/genLink.go @@ -0,0 +1,510 @@ +package util + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "net/url" + "s-ui/database/model" + "strings" +) + +var InboundTypeWithLink = []string{"shadowsocks", "naive", "hysteria", "hysteria2", "tuic", "vless", "trojan", "vmess"} + +func LinkGenerator(clientConfig json.RawMessage, i *model.Inbound, hostname string) []string { + inbound, err := i.MarshalFull() + if err != nil { + return []string{} + } + + var tls map[string]interface{} + if i.TlsId > 0 { + json.Unmarshal(i.Tls.Client, &tls) + } + + var userConfig map[string]map[string]interface{} + if err := json.Unmarshal(clientConfig, &userConfig); err != nil { + return []string{} + } + + var Addrs []map[string]interface{} + json.Unmarshal(i.Addrs, &Addrs) + fmt.Printf("AddrsArray: %+v\n", Addrs) + if len(Addrs) == 0 { + Addrs = append(Addrs, map[string]interface{}{ + "server": hostname, + "server_port": (*inbound)["listen_port"], + "remark": i.Tag, + }) + if i.TlsId > 0 { + Addrs[0]["tls"] = tls + } + } else { + for index, addr := range Addrs { + addrRemark, _ := addr["remark"].(string) + Addrs[index]["remark"] = i.Tag + addrRemark + if addrTls, ok := addr["tls"].(map[string]interface{}); ok { + newTls := map[string]interface{}{} + if oldTls, hasOldTls := tls["tls"].(map[string]interface{}); hasOldTls { + for k, v := range oldTls { + newTls[k] = v + } + } + // Override tls + for k, v := range addrTls { + newTls[k] = v + } + Addrs[index]["tls"] = newTls + } + } + } + + switch i.Type { + case "shadowsocks": + return shadowsocksLink(userConfig, *inbound, Addrs) + case "naive": + return naiveLink(userConfig["naive"], *inbound, Addrs) + case "hysteria": + return hysteriaLink(userConfig["hysteria"], *inbound, Addrs) + case "hysteria2": + return hysteria2Link(userConfig["hysteria2"], *inbound, Addrs) + case "tuic": + return tuicLink(userConfig["tuic"], *inbound, Addrs) + case "vless": + return vlessLink(userConfig["vless"], *inbound, Addrs) + case "trojan": + return trojanLink(userConfig["trojan"], *inbound, Addrs) + case "vmess": + return vmessLink(userConfig["vmess"], *inbound, Addrs) + } + + return []string{} +} + +func shadowsocksLink( + userConfig map[string]map[string]interface{}, + inbound map[string]interface{}, + addrs []map[string]interface{}) []string { + + var userPass []string + method, _ := inbound["method"].(string) + var pass string + if method == "2022-blake3-aes-128-gcm" { + pass, _ = userConfig["shadowsocks16"]["password"].(string) + } else { + pass, _ = userConfig["shadowsocks"]["password"].(string) + } + userPass = append(userPass, pass) + + if strings.HasPrefix(method, "2022") { + inbPass, _ := inbound["password"].(string) + userPass = append(userPass, inbPass) + } + + uriBase := fmt.Sprintf("ss://%s", toBase64([]byte(fmt.Sprintf("%s:%s", method, strings.Join(userPass, ":"))))) + + var links []string + for _, addr := range addrs { + port, _ := addr["server_port"].(float64) + links = append(links, fmt.Sprintf("%s@%s:%d", uriBase, addr["server"].(string), uint(port))) + } + return links +} + +func naiveLink( + userConfig map[string]interface{}, + inbound map[string]interface{}, + addrs []map[string]interface{}) []string { + + password, _ := userConfig["password"].(string) + username, _ := userConfig["username"].(string) + + baseUri := "http2://" + var links []string + + for _, addr := range addrs { + params := map[string]string{} + params["padding"] = "1" + if tls, ok := addr["tls"].(map[string]interface{}); ok { + if sni, ok := tls["server_name"].(string); ok { + params["peer"] = sni + } + if alpn, ok := tls["alpn"].([]interface{}); ok { + alpnList := make([]string, len(alpn)) + for i, v := range alpn { + alpnList[i] = v.(string) + } + params["alpn"] = strings.Join(alpnList, ",") + } + if insecure, ok := tls["insecure"].(bool); ok && insecure { + params["allowInsecure"] = "1" + } + } + if tfo, ok := inbound["tcp_fast_open"].(bool); ok && tfo { + params["tfo"] = "1" + } else { + params["tfo"] = "0" + } + + port, _ := addr["server_port"].(float64) + uri := baseUri + toBase64([]byte(fmt.Sprintf("%s:%s@%s:%d", username, password, addr["server"].(string), uint(port)))) + links = append(links, addParams(uri, params, addr["remark"].(string))) + } + return links +} + +func hysteriaLink( + userConfig map[string]interface{}, + inbound map[string]interface{}, + addrs []map[string]interface{}) []string { + + baseUri := "hysteria://" + var links []string + + for _, addr := range addrs { + params := map[string]string{} + if upmbps, ok := inbound["up_mbps"].(string); ok { + params["up_mbps"] = upmbps + } + if downmbps, ok := inbound["down_mbps"].(string); ok { + params["down_mbps"] = downmbps + } + if auth, ok := userConfig["auth_str"].(string); ok { + params["auth"] = auth + } + if tls, ok := addr["tls"].(map[string]interface{}); ok { + if sni, ok := tls["server_name"].(string); ok { + params["peer"] = sni + } + if alpn, ok := tls["alpn"].([]interface{}); ok { + alpnList := make([]string, len(alpn)) + for i, v := range alpn { + alpnList[i] = v.(string) + } + params["alpn"] = strings.Join(alpnList, ",") + } + if insecure, ok := tls["insecure"].(bool); ok && insecure { + params["allowInsecure"] = "1" + } + } + if obfs, ok := inbound["obfs"].(string); ok { + params["obfs"] = obfs + } + if tfo, ok := inbound["tcp_fast_open"].(bool); ok && tfo { + params["fastopen"] = "1" + } else { + params["fastopen"] = "0" + } + + port, _ := addr["server_port"].(float64) + uri := fmt.Sprintf("%s%s:%d", baseUri, addr["server"].(string), uint(port)) + links = append(links, addParams(uri, params, addr["remark"].(string))) + } + + return links +} + +func hysteria2Link( + userConfig map[string]interface{}, + inbound map[string]interface{}, + addrs []map[string]interface{}) []string { + + password, _ := userConfig["password"].(string) + baseUri := fmt.Sprintf("%s%s@", "hysteria2://", password) + var links []string + + for _, addr := range addrs { + params := map[string]string{} + if upmbps, ok := inbound["up_mbps"].(string); ok { + params["up_mbps"] = upmbps + } + if downmbps, ok := inbound["down_mbps"].(string); ok { + params["down_mbps"] = downmbps + } + if tls, ok := addr["tls"].(map[string]interface{}); ok { + if sni, ok := tls["server_name"].(string); ok { + params["sni"] = sni + } + if alpn, ok := tls["alpn"].([]interface{}); ok { + alpnList := make([]string, len(alpn)) + for i, v := range alpn { + alpnList[i] = v.(string) + } + params["alpn"] = strings.Join(alpnList, ",") + } + if insecure, ok := tls["insecure"].(bool); ok && insecure { + params["allowInsecure"] = "1" + } + } + if obfs, ok := inbound["obfs"].(map[string]interface{}); ok { + if obfsType, ok := obfs["type"].(string); ok { + params["obfs"] = obfsType + } + if obfsPassword, ok := obfs["password"].(string); ok { + params["obfs-password"] = obfsPassword + } + } + if tfo, ok := inbound["tcp_fast_open"].(bool); ok && tfo { + params["fastopen"] = "1" + } else { + params["fastopen"] = "0" + } + + port, _ := addr["server_port"].(float64) + uri := fmt.Sprintf("%s%s:%d", baseUri, addr["server"].(string), uint(port)) + links = append(links, addParams(uri, params, addr["remark"].(string))) + } + + return links +} + +func tuicLink( + userConfig map[string]interface{}, + inbound map[string]interface{}, + addrs []map[string]interface{}) []string { + + password, _ := userConfig["password"].(string) + uuid, _ := userConfig["uuid"].(string) + baseUri := fmt.Sprintf("%s%s:%s@", "tuic://", uuid, password) + var links []string + + for _, addr := range addrs { + params := map[string]string{} + if tls, ok := addr["tls"].(map[string]interface{}); ok { + if sni, ok := tls["server_name"].(string); ok { + params["sni"] = sni + } + if alpn, ok := tls["alpn"].([]interface{}); ok { + alpnList := make([]string, len(alpn)) + for i, v := range alpn { + alpnList[i] = v.(string) + } + params["alpn"] = strings.Join(alpnList, ",") + } + if insecure, ok := tls["insecure"].(bool); ok && insecure { + params["allowInsecure"] = "1" + } + if disableSni, ok := tls["disable_sni"].(bool); ok && disableSni { + params["disableSni"] = "1" + } + } + if congestionControl, ok := inbound["congestion_control"].(string); ok { + params["congestion_control"] = congestionControl + } + + port, _ := addr["server_port"].(float64) + uri := fmt.Sprintf("%s%s:%d", baseUri, addr["server"].(string), uint(port)) + links = append(links, addParams(uri, params, addr["remark"].(string))) + } + + return links +} + +func vlessLink( + userConfig map[string]interface{}, + inbound map[string]interface{}, + addrs []map[string]interface{}) []string { + + uuid, _ := userConfig["uuid"].(string) + baseParams := getTransportParams(inbound["transport"]) + var links []string + + for _, addr := range addrs { + params := baseParams + if tls, ok := addr["tls"].(map[string]interface{}); ok && tls["enabled"].(bool) { + if reality, ok := tls["reality"].(map[string]interface{}); ok && reality["enabled"].(bool) { + params["security"] = "reality" + if pbk, ok := reality["public_key"].(string); ok { + params["pbk"] = pbk + } + if sid, ok := reality["short_id"].(string); ok { + params["sid"] = sid + } + } else { + params["security"] = "tls" + if insecure, ok := tls["insecure"].(bool); ok && insecure { + params["allowInsecure"] = "1" + } + if flow, ok := userConfig["flow"].(string); ok { + params["flow"] = flow + } + } + if sni, ok := tls["server_name"].(string); ok { + params["sni"] = sni + } + if alpn, ok := tls["alpn"].([]interface{}); ok { + alpnList := make([]string, len(alpn)) + for i, v := range alpn { + alpnList[i] = v.(string) + } + params["alpn"] = strings.Join(alpnList, ",") + } + } + port, _ := addr["server_port"].(float64) + uri := fmt.Sprintf("vless://%s@%s:%d", uuid, addr["server"].(string), uint(port)) + uri = addParams(uri, params, addr["remark"].(string)) + links = append(links, uri) + } + + return links +} + +func trojanLink( + userConfig map[string]interface{}, + inbound map[string]interface{}, + addrs []map[string]interface{}) []string { + password, _ := userConfig["password"].(string) + baseParams := getTransportParams(inbound["transport"]) + var links []string + + for _, addr := range addrs { + params := baseParams + if tls, ok := addr["tls"].(map[string]interface{}); ok && tls["enabled"].(bool) { + if reality, ok := tls["reality"].(map[string]interface{}); ok && reality["enabled"].(bool) { + params["security"] = "reality" + if pbk, ok := reality["public_key"].(string); ok { + params["pbk"] = pbk + } + if sid, ok := reality["short_id"].(string); ok { + params["sid"] = sid + } + } else { + params["security"] = "tls" + if insecure, ok := tls["insecure"].(bool); ok && insecure { + params["allowInsecure"] = "1" + } + } + if sni, ok := tls["server_name"].(string); ok { + params["sni"] = sni + } + if alpn, ok := tls["alpn"].([]interface{}); ok { + alpnList := make([]string, len(alpn)) + for i, v := range alpn { + alpnList[i] = v.(string) + } + params["alpn"] = strings.Join(alpnList, ",") + } + } + port, _ := addr["server_port"].(float64) + uri := fmt.Sprintf("trojan://%s@%s:%d", password, addr["server"].(string), uint(port)) + uri = addParams(uri, params, addr["remark"].(string)) + links = append(links, uri) + } + + return links +} + +func vmessLink( + userConfig map[string]interface{}, + inbound map[string]interface{}, + addrs []map[string]interface{}) []string { + + uuid, _ := userConfig["uuid"].(string) + trasportParams := getTransportParams(inbound["transport"]) + var links []string + + baseParams := map[string]interface{}{ + "v": 2, + "id": uuid, + "aid": 0, + } + if trasportParams["type"] == "http" || trasportParams["type"] == "tcp" { + baseParams["net"] = "tcp" + if trasportParams["type"] == "http" { + baseParams["type"] = "http" + } + } else { + baseParams["net"] = trasportParams["type"] + } + + for _, addr := range addrs { + obj := baseParams + obj["addr"], _ = addr["server"].(string) + port, _ := addr["server_port"].(float64) + obj["port"] = uint(port) + obj["ps"], _ = addr["remark"].(string) + if trasportParams["host"] != "" { + obj["host"] = trasportParams["host"] + } + if trasportParams["path"] != "" { + obj["path"] = trasportParams["path"] + } + if tls, ok := addr["tls"].(map[string]interface{}); ok && tls["enabled"].(bool) { + obj["tls"] = "tls" + if insecure, ok := tls["insecure"].(bool); ok && insecure { + obj["allowInsecure"] = 1 + } + if sni, ok := tls["server_name"].(string); ok { + obj["sni"] = sni + } + } else { + obj["tls"] = "none" + } + + jsonStr, _ := json.MarshalIndent(obj, "", " ") + + uri := fmt.Sprintf("vmess://%s", toBase64(jsonStr)) + links = append(links, uri) + } + return links +} + +func toBase64(d []byte) string { + return base64.StdEncoding.EncodeToString([]byte(d)) +} + +func addParams(uri string, params map[string]string, remark string) string { + URL, _ := url.Parse(uri) + q := URL.Query() + for k, v := range params { + q.Add(k, v) + } + URL.RawQuery = q.Encode() + URL.Fragment = remark + return URL.String() +} + +func getTransportParams(t interface{}) map[string]string { + params := map[string]string{} + trasport, _ := t.(map[string]interface{}) + if transportType, ok := trasport["type"].(string); ok { + params["type"] = transportType + } else { + params["type"] = "tcp" + return params + } + switch params["type"] { + case "http": + if host, ok := trasport["host"].([]interface{}); ok { + var hosts []string + for _, v := range host { + hosts = append(hosts, v.(string)) + } + params["host"] = strings.Join(hosts, ",") + } + if path, ok := trasport["path"].(string); ok { + params["path"] = path + } + case "ws": + if path, ok := trasport["path"].(string); ok { + params["path"] = path + } + if headers, ok := trasport["headers"].(map[string]interface{}); ok { + if host, ok := headers["Host"].(string); ok { + params["peer"] = host + } + } + case "grpc": + if serviceName, ok := trasport["service_name"].(string); ok { + params["serviceName"] = serviceName + } + case "httpupgrade": + if host, ok := trasport["host"].(string); ok { + params["peer"] = host + } + if path, ok := trasport["path"].(string); ok { + params["path"] = path + } + } + return params +} diff --git a/frontend/src/layouts/modals/Client.vue b/frontend/src/layouts/modals/Client.vue index f9143cd..0ccd734 100644 --- a/frontend/src/layouts/modals/Client.vue +++ b/frontend/src/layouts/modals/Client.vue @@ -178,8 +178,7 @@