| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388 | package subimport (	_ "embed"	"encoding/json"	"fmt"	"strings"	"x-ui/database/model"	"x-ui/logger"	"x-ui/util/json_util"	"x-ui/util/random"	"x-ui/web/service"	"x-ui/xray")//go:embed default.jsonvar defaultJson stringtype SubJsonService struct {	configJson       map[string]interface{}	defaultOutbounds []json_util.RawMessage	fragment         string	mux              string	inboundService service.InboundService	SubService     *SubService}func NewSubJsonService(fragment string, mux string, rules string, subService *SubService) *SubJsonService {	var configJson map[string]interface{}	var defaultOutbounds []json_util.RawMessage	json.Unmarshal([]byte(defaultJson), &configJson)	if outboundSlices, ok := configJson["outbounds"].([]interface{}); ok {		for _, defaultOutbound := range outboundSlices {			jsonBytes, _ := json.Marshal(defaultOutbound)			defaultOutbounds = append(defaultOutbounds, jsonBytes)		}	}	if rules != "" {		var newRules []interface{}		routing, _ := configJson["routing"].(map[string]interface{})		defaultRules, _ := routing["rules"].([]interface{})		json.Unmarshal([]byte(rules), &newRules)		defaultRules = append(newRules, defaultRules...)		routing["rules"] = defaultRules		configJson["routing"] = routing	}	if fragment != "" {		defaultOutbounds = append(defaultOutbounds, json_util.RawMessage(fragment))	}	return &SubJsonService{		configJson:       configJson,		defaultOutbounds: defaultOutbounds,		fragment:         fragment,		mux:              mux,		SubService:       subService,	}}func (s *SubJsonService) GetJson(subId string, host string) (string, string, error) {	inbounds, err := s.SubService.getInboundsBySubId(subId)	if err != nil || len(inbounds) == 0 {		return "", "", err	}	var header string	var traffic xray.ClientTraffic	var clientTraffics []xray.ClientTraffic	var configArray []json_util.RawMessage	// Prepare Inbounds	for _, inbound := range inbounds {		clients, err := s.inboundService.GetClients(inbound)		if err != nil {			logger.Error("SubJsonService - GetClients: Unable to get clients from inbound")		}		if clients == nil {			continue		}		if len(inbound.Listen) > 0 && inbound.Listen[0] == '@' {			listen, port, streamSettings, err := s.SubService.getFallbackMaster(inbound.Listen, inbound.StreamSettings)			if err == nil {				inbound.Listen = listen				inbound.Port = port				inbound.StreamSettings = streamSettings			}		}		for _, client := range clients {			if client.Enable && client.SubID == subId {				clientTraffics = append(clientTraffics, s.SubService.getClientTraffics(inbound.ClientStats, client.Email))				newConfigs := s.getConfig(inbound, client, host)				configArray = append(configArray, newConfigs...)			}		}	}	if len(configArray) == 0 {		return "", "", nil	}	// Prepare statistics	for index, clientTraffic := range clientTraffics {		if index == 0 {			traffic.Up = clientTraffic.Up			traffic.Down = clientTraffic.Down			traffic.Total = clientTraffic.Total			if clientTraffic.ExpiryTime > 0 {				traffic.ExpiryTime = clientTraffic.ExpiryTime			}		} else {			traffic.Up += clientTraffic.Up			traffic.Down += clientTraffic.Down			if traffic.Total == 0 || clientTraffic.Total == 0 {				traffic.Total = 0			} else {				traffic.Total += clientTraffic.Total			}			if clientTraffic.ExpiryTime != traffic.ExpiryTime {				traffic.ExpiryTime = 0			}		}	}	// Combile outbounds	var finalJson []byte	if len(configArray) == 1 {		finalJson, _ = json.MarshalIndent(configArray[0], "", "  ")	} else {		finalJson, _ = json.MarshalIndent(configArray, "", "  ")	}	header = fmt.Sprintf("upload=%d; download=%d; total=%d; expire=%d", traffic.Up, traffic.Down, traffic.Total, traffic.ExpiryTime/1000)	return string(finalJson), header, nil}func (s *SubJsonService) getConfig(inbound *model.Inbound, client model.Client, host string) []json_util.RawMessage {	var newJsonArray []json_util.RawMessage	stream := s.streamData(inbound.StreamSettings)	externalProxies, ok := stream["externalProxy"].([]interface{})	if !ok || len(externalProxies) == 0 {		externalProxies = []interface{}{			map[string]interface{}{				"forceTls": "same",				"dest":     host,				"port":     float64(inbound.Port),				"remark":   "",			},		}	}	delete(stream, "externalProxy")	for _, ep := range externalProxies {		extPrxy := ep.(map[string]interface{})		inbound.Listen = extPrxy["dest"].(string)		inbound.Port = int(extPrxy["port"].(float64))		newStream := stream		switch extPrxy["forceTls"].(string) {		case "tls":			if newStream["security"] != "tls" {				newStream["security"] = "tls"				newStream["tslSettings"] = map[string]interface{}{}			}		case "none":			if newStream["security"] != "none" {				newStream["security"] = "none"				delete(newStream, "tslSettings")			}		}		streamSettings, _ := json.MarshalIndent(newStream, "", "  ")		var newOutbounds []json_util.RawMessage		switch inbound.Protocol {		case "vmess", "vless":			newOutbounds = append(newOutbounds, s.genVnext(inbound, streamSettings, client))		case "trojan", "shadowsocks":			newOutbounds = append(newOutbounds, s.genServer(inbound, streamSettings, client))		}		newOutbounds = append(newOutbounds, s.defaultOutbounds...)		newConfigJson := make(map[string]interface{})		for key, value := range s.configJson {			newConfigJson[key] = value		}		newConfigJson["outbounds"] = newOutbounds		newConfigJson["remarks"] = s.SubService.genRemark(inbound, client.Email, extPrxy["remark"].(string))		newConfig, _ := json.MarshalIndent(newConfigJson, "", "  ")		newJsonArray = append(newJsonArray, newConfig)	}	return newJsonArray}func (s *SubJsonService) streamData(stream string) map[string]interface{} {	var streamSettings map[string]interface{}	json.Unmarshal([]byte(stream), &streamSettings)	security, _ := streamSettings["security"].(string)	if security == "tls" {		streamSettings["tlsSettings"] = s.tlsData(streamSettings["tlsSettings"].(map[string]interface{}))	} else if security == "reality" {		streamSettings["realitySettings"] = s.realityData(streamSettings["realitySettings"].(map[string]interface{}))	}	delete(streamSettings, "sockopt")	if s.fragment != "" {		streamSettings["sockopt"] = json_util.RawMessage(`{"dialerProxy": "fragment", "tcpKeepAliveIdle": 100, "tcpMptcp": true, "tcpNoDelay": true}`)	}	// remove proxy protocol	network, _ := streamSettings["network"].(string)	switch network {	case "tcp":		streamSettings["tcpSettings"] = s.removeAcceptProxy(streamSettings["tcpSettings"])	case "ws":		streamSettings["wsSettings"] = s.removeAcceptProxy(streamSettings["wsSettings"])	case "httpupgrade":		streamSettings["httpupgradeSettings"] = s.removeAcceptProxy(streamSettings["httpupgradeSettings"])	}	return streamSettings}func (s *SubJsonService) removeAcceptProxy(setting interface{}) map[string]interface{} {	netSettings, ok := setting.(map[string]interface{})	if ok {		delete(netSettings, "acceptProxyProtocol")	}	return netSettings}func (s *SubJsonService) tlsData(tData map[string]interface{}) map[string]interface{} {	tlsData := make(map[string]interface{}, 1)	tlsClientSettings, _ := tData["settings"].(map[string]interface{})	tlsData["serverName"] = tData["serverName"]	tlsData["alpn"] = tData["alpn"]	if allowInsecure, ok := tlsClientSettings["allowInsecure"].(bool); ok {		tlsData["allowInsecure"] = allowInsecure	}	if fingerprint, ok := tlsClientSettings["fingerprint"].(string); ok {		tlsData["fingerprint"] = fingerprint	}	return tlsData}func (s *SubJsonService) realityData(rData map[string]interface{}) map[string]interface{} {	rltyData := make(map[string]interface{}, 1)	rltyClientSettings, _ := rData["settings"].(map[string]interface{})	rltyData["show"] = false	rltyData["publicKey"] = rltyClientSettings["publicKey"]	rltyData["fingerprint"] = rltyClientSettings["fingerprint"]	// Set random data	rltyData["spiderX"] = "/" + random.Seq(15)	shortIds, ok := rData["shortIds"].([]interface{})	if ok && len(shortIds) > 0 {		rltyData["shortId"] = shortIds[random.Num(len(shortIds))].(string)	} else {		rltyData["shortId"] = ""	}	serverNames, ok := rData["serverNames"].([]interface{})	if ok && len(serverNames) > 0 {		rltyData["serverName"] = serverNames[random.Num(len(serverNames))].(string)	} else {		rltyData["serverName"] = ""	}	return rltyData}func (s *SubJsonService) genVnext(inbound *model.Inbound, streamSettings json_util.RawMessage, client model.Client) json_util.RawMessage {	outbound := Outbound{}	usersData := make([]UserVnext, 1)	usersData[0].ID = client.ID	usersData[0].Level = 8	if inbound.Protocol == model.VMESS {		usersData[0].Security = client.Security	}	if inbound.Protocol == model.VLESS {		usersData[0].Flow = client.Flow		usersData[0].Encryption = "none"	}	vnextData := make([]VnextSetting, 1)	vnextData[0] = VnextSetting{		Address: inbound.Listen,		Port:    inbound.Port,		Users:   usersData,	}	outbound.Protocol = string(inbound.Protocol)	outbound.Tag = "proxy"	if s.mux != "" {		outbound.Mux = json_util.RawMessage(s.mux)	}	outbound.StreamSettings = streamSettings	outbound.Settings = OutboundSettings{		Vnext: vnextData,	}	result, _ := json.MarshalIndent(outbound, "", "  ")	return result}func (s *SubJsonService) genServer(inbound *model.Inbound, streamSettings json_util.RawMessage, client model.Client) json_util.RawMessage {	outbound := Outbound{}	serverData := make([]ServerSetting, 1)	serverData[0] = ServerSetting{		Address:  inbound.Listen,		Port:     inbound.Port,		Level:    8,		Password: client.Password,	}	if inbound.Protocol == model.Shadowsocks {		var inboundSettings map[string]interface{}		json.Unmarshal([]byte(inbound.Settings), &inboundSettings)		method, _ := inboundSettings["method"].(string)		serverData[0].Method = method		// server password in multi-user 2022 protocols		if strings.HasPrefix(method, "2022") {			if serverPassword, ok := inboundSettings["password"].(string); ok {				serverData[0].Password = fmt.Sprintf("%s:%s", serverPassword, client.Password)			}		}	}	outbound.Protocol = string(inbound.Protocol)	outbound.Tag = "proxy"	if s.mux != "" {		outbound.Mux = json_util.RawMessage(s.mux)	}	outbound.StreamSettings = streamSettings	outbound.Settings = OutboundSettings{		Servers: serverData,	}	result, _ := json.MarshalIndent(outbound, "", "  ")	return result}type Outbound struct {	Protocol       string                 `json:"protocol"`	Tag            string                 `json:"tag"`	StreamSettings json_util.RawMessage   `json:"streamSettings"`	Mux            json_util.RawMessage   `json:"mux,omitempty"`	ProxySettings  map[string]interface{} `json:"proxySettings,omitempty"`	Settings       OutboundSettings       `json:"settings,omitempty"`}type OutboundSettings struct {	Vnext   []VnextSetting  `json:"vnext,omitempty"`	Servers []ServerSetting `json:"servers,omitempty"`}type VnextSetting struct {	Address string      `json:"address"`	Port    int         `json:"port"`	Users   []UserVnext `json:"users"`}type UserVnext struct {	Encryption string `json:"encryption,omitempty"`	Flow       string `json:"flow,omitempty"`	ID         string `json:"id"`	Security   string `json:"security,omitempty"`	Level      int    `json:"level"`}type ServerSetting struct {	Password string `json:"password"`	Level    int    `json:"level"`	Address  string `json:"address"`	Port     int    `json:"port"`	Flow     string `json:"flow,omitempty"`	Method   string `json:"method,omitempty"`}
 |