diff --git a/sub/default.json b/sub/default.json deleted file mode 100644 index fff1b3ad..00000000 --- a/sub/default.json +++ /dev/null @@ -1,90 +0,0 @@ -{ - "remarks": "", - "dns": { - "tag": "dns_out", - "queryStrategy": "UseIP", - "servers": [ - { - "address": "8.8.8.8", - "skipFallback": false - } - ] - }, - "inbounds": [ - { - "port": 10808, - "protocol": "socks", - "settings": { - "auth": "noauth", - "udp": true, - "userLevel": 8 - }, - "sniffing": { - "destOverride": [ - "http", - "tls", - "quic", - "fakedns" - ], - "enabled": true - }, - "tag": "socks" - }, - { - "port": 10809, - "protocol": "http", - "settings": { - "userLevel": 8 - }, - "tag": "http" - } - ], - "log": { - "loglevel": "warning" - }, - "outbounds": [ - { - "tag": "direct", - "protocol": "freedom", - "settings": { - "domainStrategy": "AsIs", - "redirect": "", - "noises": [] - } - }, - { - "tag": "block", - "protocol": "blackhole", - "settings": { - "response": { - "type": "http" - } - } - } - ], - "policy": { - "levels": { - "8": { - "connIdle": 300, - "downlinkOnly": 1, - "handshake": 4, - "uplinkOnly": 1 - } - }, - "system": { - "statsOutboundUplink": true, - "statsOutboundDownlink": true - } - }, - "routing": { - "domainStrategy": "AsIs", - "rules": [ - { - "type": "field", - "network": "tcp,udp", - "outboundTag": "proxy" - } - ] - }, - "stats": {} -} \ No newline at end of file diff --git a/sub/sub.go b/sub/sub.go deleted file mode 100644 index db582e8d..00000000 --- a/sub/sub.go +++ /dev/null @@ -1,208 +0,0 @@ -package sub - -import ( - "context" - "crypto/tls" - "io" - "net" - "net/http" - "strconv" - - "x-ui/config" - "x-ui/logger" - "x-ui/util/common" - "x-ui/web/middleware" - "x-ui/web/network" - "x-ui/web/service" - - "github.com/gin-gonic/gin" -) - -type Server struct { - httpServer *http.Server - listener net.Listener - - sub *SUBController - settingService service.SettingService - - ctx context.Context - cancel context.CancelFunc -} - -func NewServer() *Server { - ctx, cancel := context.WithCancel(context.Background()) - return &Server{ - ctx: ctx, - cancel: cancel, - } -} - -func (s *Server) initRouter() (*gin.Engine, error) { - if config.IsDebug() { - gin.SetMode(gin.DebugMode) - } else { - gin.DefaultWriter = io.Discard - gin.DefaultErrorWriter = io.Discard - gin.SetMode(gin.ReleaseMode) - } - - engine := gin.Default() - - subDomain, err := s.settingService.GetSubDomain() - if err != nil { - return nil, err - } - - if subDomain != "" { - engine.Use(middleware.DomainValidatorMiddleware(subDomain)) - } - - LinksPath, err := s.settingService.GetSubPath() - if err != nil { - return nil, err - } - - JsonPath, err := s.settingService.GetSubJsonPath() - if err != nil { - return nil, err - } - - Encrypt, err := s.settingService.GetSubEncrypt() - if err != nil { - return nil, err - } - - ShowInfo, err := s.settingService.GetSubShowInfo() - if err != nil { - return nil, err - } - - RemarkModel, err := s.settingService.GetRemarkModel() - if err != nil { - RemarkModel = "-ieo" - } - - SubUpdates, err := s.settingService.GetSubUpdates() - if err != nil { - SubUpdates = "10" - } - - SubJsonFragment, err := s.settingService.GetSubJsonFragment() - if err != nil { - SubJsonFragment = "" - } - - SubJsonNoises, err := s.settingService.GetSubJsonNoises() - if err != nil { - SubJsonNoises = "" - } - - SubJsonMux, err := s.settingService.GetSubJsonMux() - if err != nil { - SubJsonMux = "" - } - - SubJsonRules, err := s.settingService.GetSubJsonRules() - if err != nil { - SubJsonRules = "" - } - - g := engine.Group("/") - - s.sub = NewSUBController( - g, LinksPath, JsonPath, Encrypt, ShowInfo, RemarkModel, SubUpdates, - SubJsonFragment, SubJsonNoises, SubJsonMux, SubJsonRules) - - return engine, nil -} - -func (s *Server) Start() (err error) { - // This is an anonymous function, no function name - defer func() { - if err != nil { - s.Stop() - } - }() - - subEnable, err := s.settingService.GetSubEnable() - if err != nil { - return err - } - if !subEnable { - return nil - } - - engine, err := s.initRouter() - if err != nil { - return err - } - - certFile, err := s.settingService.GetSubCertFile() - if err != nil { - return err - } - keyFile, err := s.settingService.GetSubKeyFile() - if err != nil { - return err - } - listen, err := s.settingService.GetSubListen() - if err != nil { - return err - } - port, err := s.settingService.GetSubPort() - if err != nil { - return err - } - - listenAddr := net.JoinHostPort(listen, strconv.Itoa(port)) - listener, err := net.Listen("tcp", listenAddr) - if err != nil { - return err - } - - if certFile != "" || keyFile != "" { - cert, err := tls.LoadX509KeyPair(certFile, keyFile) - if err == nil { - c := &tls.Config{ - Certificates: []tls.Certificate{cert}, - } - listener = network.NewAutoHttpsListener(listener) - listener = tls.NewListener(listener, c) - logger.Info("Sub server running HTTPS on", listener.Addr()) - } else { - logger.Error("Error loading certificates:", err) - logger.Info("Sub server running HTTP on", listener.Addr()) - } - } else { - logger.Info("Sub server running HTTP on", listener.Addr()) - } - s.listener = listener - - s.httpServer = &http.Server{ - Handler: engine, - } - - go func() { - s.httpServer.Serve(listener) - }() - - return nil -} - -func (s *Server) Stop() error { - s.cancel() - - var err1 error - var err2 error - if s.httpServer != nil { - err1 = s.httpServer.Shutdown(s.ctx) - } - if s.listener != nil { - err2 = s.listener.Close() - } - return common.Combine(err1, err2) -} - -func (s *Server) GetCtx() context.Context { - return s.ctx -} diff --git a/sub/subController.go b/sub/subController.go deleted file mode 100644 index 9afbc8da..00000000 --- a/sub/subController.go +++ /dev/null @@ -1,134 +0,0 @@ -package sub - -import ( - "encoding/base64" - "net" - "strings" - - "github.com/gin-gonic/gin" -) - -type SUBController struct { - subPath string - subJsonPath string - subEncrypt bool - updateInterval string - - subService *SubService - subJsonService *SubJsonService -} - -func NewSUBController( - g *gin.RouterGroup, - subPath string, - jsonPath string, - encrypt bool, - showInfo bool, - rModel string, - update string, - jsonFragment string, - jsonNoise string, - jsonMux string, - jsonRules string, -) *SUBController { - sub := NewSubService(showInfo, rModel) - a := &SUBController{ - subPath: subPath, - subJsonPath: jsonPath, - subEncrypt: encrypt, - updateInterval: update, - - subService: sub, - subJsonService: NewSubJsonService(jsonFragment, jsonNoise, jsonMux, jsonRules, sub), - } - a.initRouter(g) - return a -} - -func (a *SUBController) initRouter(g *gin.RouterGroup) { - gLink := g.Group(a.subPath) - gJson := g.Group(a.subJsonPath) - - gLink.GET(":subid", a.subs) - - gJson.GET(":subid", a.subJsons) -} - -func (a *SUBController) subs(c *gin.Context) { - subId := c.Param("subid") - var host string - if h, err := getHostFromXFH(c.GetHeader("X-Forwarded-Host")); err == nil { - host = h - } - if host == "" { - host = c.GetHeader("X-Real-IP") - } - if host == "" { - var err error - host, _, err = net.SplitHostPort(c.Request.Host) - if err != nil { - host = c.Request.Host - } - } - subs, header, err := a.subService.GetSubs(subId, host) - if err != nil || len(subs) == 0 { - c.String(400, "Error!") - } else { - result := "" - for _, sub := range subs { - result += sub + "\n" - } - - // Add headers - c.Writer.Header().Set("Subscription-Userinfo", header) - c.Writer.Header().Set("Profile-Update-Interval", a.updateInterval) - c.Writer.Header().Set("Profile-Title", subId) - - if a.subEncrypt { - c.String(200, base64.StdEncoding.EncodeToString([]byte(result))) - } else { - c.String(200, result) - } - } -} - -func (a *SUBController) subJsons(c *gin.Context) { - subId := c.Param("subid") - var host string - if h, err := getHostFromXFH(c.GetHeader("X-Forwarded-Host")); err == nil { - host = h - } - if host == "" { - host = c.GetHeader("X-Real-IP") - } - if host == "" { - var err error - host, _, err = net.SplitHostPort(c.Request.Host) - if err != nil { - host = c.Request.Host - } - } - jsonSub, header, err := a.subJsonService.GetJson(subId, host) - if err != nil || len(jsonSub) == 0 { - c.String(400, "Error!") - } else { - - // Add headers - c.Writer.Header().Set("Subscription-Userinfo", header) - c.Writer.Header().Set("Profile-Update-Interval", a.updateInterval) - c.Writer.Header().Set("Profile-Title", subId) - - c.String(200, jsonSub) - } -} - -func getHostFromXFH(s string) (string, error) { - if strings.Contains(s, ":") { - realHost, _, err := net.SplitHostPort(s) - if err != nil { - return "", err - } - return realHost, nil - } - return s, nil -} diff --git a/sub/subJsonService.go b/sub/subJsonService.go deleted file mode 100644 index 8786541e..00000000 --- a/sub/subJsonService.go +++ /dev/null @@ -1,394 +0,0 @@ -package sub - -import ( - _ "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.json -var defaultJson string - -type SubJsonService struct { - configJson map[string]interface{} - defaultOutbounds []json_util.RawMessage - fragment string - noises string - mux string - - inboundService service.InboundService - SubService *SubService -} - -func NewSubJsonService(fragment string, noises 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)) - } - - if noises != "" { - defaultOutbounds = append(defaultOutbounds, json_util.RawMessage(noises)) - } - - return &SubJsonService{ - configJson: configJson, - defaultOutbounds: defaultOutbounds, - fragment: fragment, - noises: noises, - 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, "penetrate": 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"` -} diff --git a/sub/subService.go b/sub/subService.go deleted file mode 100644 index f52d4b67..00000000 --- a/sub/subService.go +++ /dev/null @@ -1,987 +0,0 @@ -package sub - -import ( - "encoding/base64" - "fmt" - "net/url" - "strings" - "time" - - "x-ui/database" - "x-ui/database/model" - "x-ui/logger" - "x-ui/util/common" - "x-ui/util/random" - "x-ui/web/service" - "x-ui/xray" - - "github.com/goccy/go-json" -) - -type SubService struct { - address string - showInfo bool - remarkModel string - datepicker string - inboundService service.InboundService - settingService service.SettingService -} - -func NewSubService(showInfo bool, remarkModel string) *SubService { - return &SubService{ - showInfo: showInfo, - remarkModel: remarkModel, - } -} - -func (s *SubService) GetSubs(subId string, host string) ([]string, string, error) { - s.address = host - var result []string - var header string - var traffic xray.ClientTraffic - var clientTraffics []xray.ClientTraffic - inbounds, err := s.getInboundsBySubId(subId) - if err != nil { - return nil, "", err - } - - if len(inbounds) == 0 { - return nil, "", common.NewError("No inbounds found with ", subId) - } - - s.datepicker, err = s.settingService.GetDatepicker() - if err != nil { - s.datepicker = "gregorian" - } - for _, inbound := range inbounds { - clients, err := s.inboundService.GetClients(inbound) - if err != nil { - logger.Error("SubService - GetClients: Unable to get clients from inbound") - } - if clients == nil { - continue - } - if len(inbound.Listen) > 0 && inbound.Listen[0] == '@' { - listen, port, streamSettings, err := s.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 { - link := s.getLink(inbound, client.Email) - result = append(result, link) - clientTraffics = append(clientTraffics, s.getClientTraffics(inbound.ClientStats, client.Email)) - } - } - } - - // 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 - } - } - } - header = fmt.Sprintf("upload=%d; download=%d; total=%d; expire=%d", traffic.Up, traffic.Down, traffic.Total, traffic.ExpiryTime/1000) - return result, header, nil -} - -func (s *SubService) getInboundsBySubId(subId string) ([]*model.Inbound, error) { - db := database.GetDB() - var inbounds []*model.Inbound - 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 { - return nil, err - } - return inbounds, nil -} - -func (s *SubService) getClientTraffics(traffics []xray.ClientTraffic, email string) xray.ClientTraffic { - for _, traffic := range traffics { - if traffic.Email == email { - return traffic - } - } - return xray.ClientTraffic{} -} - -func (s *SubService) getFallbackMaster(dest string, streamSettings string) (string, int, string, error) { - db := database.GetDB() - var inbound *model.Inbound - err := db.Model(model.Inbound{}). - Where("JSON_TYPE(settings, '$.fallbacks') = 'array'"). - Where("EXISTS (SELECT * FROM json_each(settings, '$.fallbacks') WHERE json_extract(value, '$.dest') = ?)", dest). - Find(&inbound).Error - if err != nil { - return "", 0, "", err - } - - var stream map[string]interface{} - json.Unmarshal([]byte(streamSettings), &stream) - var masterStream map[string]interface{} - json.Unmarshal([]byte(inbound.StreamSettings), &masterStream) - stream["security"] = masterStream["security"] - stream["tlsSettings"] = masterStream["tlsSettings"] - stream["externalProxy"] = masterStream["externalProxy"] - modifiedStream, _ := json.MarshalIndent(stream, "", " ") - - return inbound.Listen, inbound.Port, string(modifiedStream), nil -} - -func (s *SubService) getLink(inbound *model.Inbound, email string) string { - switch inbound.Protocol { - case "vmess": - return s.genVmessLink(inbound, email) - case "vless": - return s.genVlessLink(inbound, email) - case "trojan": - return s.genTrojanLink(inbound, email) - case "shadowsocks": - return s.genShadowsocksLink(inbound, email) - } - return "" -} - -func (s *SubService) genVmessLink(inbound *model.Inbound, email string) string { - if inbound.Protocol != model.VMESS { - return "" - } - obj := map[string]interface{}{ - "v": "2", - "add": s.address, - "port": inbound.Port, - "type": "none", - } - var stream map[string]interface{} - json.Unmarshal([]byte(inbound.StreamSettings), &stream) - network, _ := stream["network"].(string) - obj["net"] = network - switch network { - case "tcp": - tcp, _ := stream["tcpSettings"].(map[string]interface{}) - header, _ := tcp["header"].(map[string]interface{}) - typeStr, _ := header["type"].(string) - obj["type"] = typeStr - if typeStr == "http" { - request := header["request"].(map[string]interface{}) - requestPath, _ := request["path"].([]interface{}) - obj["path"] = requestPath[0].(string) - headers, _ := request["headers"].(map[string]interface{}) - obj["host"] = searchHost(headers) - } - case "kcp": - kcp, _ := stream["kcpSettings"].(map[string]interface{}) - header, _ := kcp["header"].(map[string]interface{}) - obj["type"], _ = header["type"].(string) - obj["path"], _ = kcp["seed"].(string) - case "ws": - ws, _ := stream["wsSettings"].(map[string]interface{}) - obj["path"] = ws["path"].(string) - if host, ok := ws["host"].(string); ok && len(host) > 0 { - obj["host"] = host - } else { - headers, _ := ws["headers"].(map[string]interface{}) - obj["host"] = searchHost(headers) - } - case "grpc": - grpc, _ := stream["grpcSettings"].(map[string]interface{}) - obj["path"] = grpc["serviceName"].(string) - obj["authority"] = grpc["authority"].(string) - if grpc["multiMode"].(bool) { - obj["type"] = "multi" - } - case "httpupgrade": - httpupgrade, _ := stream["httpupgradeSettings"].(map[string]interface{}) - obj["path"] = httpupgrade["path"].(string) - if host, ok := httpupgrade["host"].(string); ok && len(host) > 0 { - obj["host"] = host - } else { - headers, _ := httpupgrade["headers"].(map[string]interface{}) - obj["host"] = searchHost(headers) - } - case "xhttp": - xhttp, _ := stream["xhttpSettings"].(map[string]interface{}) - obj["path"] = xhttp["path"].(string) - if host, ok := xhttp["host"].(string); ok && len(host) > 0 { - obj["host"] = host - } else { - headers, _ := xhttp["headers"].(map[string]interface{}) - obj["host"] = searchHost(headers) - } - obj["mode"] = xhttp["mode"].(string) - } - security, _ := stream["security"].(string) - obj["tls"] = security - if security == "tls" { - tlsSetting, _ := stream["tlsSettings"].(map[string]interface{}) - alpns, _ := tlsSetting["alpn"].([]interface{}) - if len(alpns) > 0 { - var alpn []string - for _, a := range alpns { - alpn = append(alpn, a.(string)) - } - obj["alpn"] = strings.Join(alpn, ",") - } - if sniValue, ok := searchKey(tlsSetting, "serverName"); ok { - obj["sni"], _ = sniValue.(string) - } - - tlsSettings, _ := searchKey(tlsSetting, "settings") - if tlsSetting != nil { - if fpValue, ok := searchKey(tlsSettings, "fingerprint"); ok { - obj["fp"], _ = fpValue.(string) - } - if insecure, ok := searchKey(tlsSettings, "allowInsecure"); ok { - obj["allowInsecure"], _ = insecure.(bool) - } - } - } - - clients, _ := s.inboundService.GetClients(inbound) - clientIndex := -1 - for i, client := range clients { - if client.Email == email { - clientIndex = i - break - } - } - obj["id"] = clients[clientIndex].ID - obj["scy"] = clients[clientIndex].Security - - externalProxies, _ := stream["externalProxy"].([]interface{}) - - if len(externalProxies) > 0 { - links := "" - for index, externalProxy := range externalProxies { - ep, _ := externalProxy.(map[string]interface{}) - newSecurity, _ := ep["forceTls"].(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 { - links += "\n" - } - jsonStr, _ := json.MarshalIndent(newObj, "", " ") - links += "vmess://" + base64.StdEncoding.EncodeToString(jsonStr) - } - return links - } - - obj["ps"] = s.genRemark(inbound, email, "") - - jsonStr, _ := json.MarshalIndent(obj, "", " ") - return "vmess://" + base64.StdEncoding.EncodeToString(jsonStr) -} - -func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string { - address := s.address - if inbound.Protocol != model.VLESS { - return "" - } - var stream map[string]interface{} - json.Unmarshal([]byte(inbound.StreamSettings), &stream) - clients, _ := s.inboundService.GetClients(inbound) - clientIndex := -1 - for i, client := range clients { - if client.Email == email { - clientIndex = i - break - } - } - uuid := clients[clientIndex].ID - port := inbound.Port - streamNetwork := stream["network"].(string) - params := make(map[string]string) - params["type"] = streamNetwork - - switch streamNetwork { - case "tcp": - tcp, _ := stream["tcpSettings"].(map[string]interface{}) - header, _ := tcp["header"].(map[string]interface{}) - typeStr, _ := header["type"].(string) - if typeStr == "http" { - request := header["request"].(map[string]interface{}) - requestPath, _ := request["path"].([]interface{}) - params["path"] = requestPath[0].(string) - headers, _ := request["headers"].(map[string]interface{}) - params["host"] = searchHost(headers) - params["headerType"] = "http" - } - case "kcp": - kcp, _ := stream["kcpSettings"].(map[string]interface{}) - header, _ := kcp["header"].(map[string]interface{}) - params["headerType"] = header["type"].(string) - params["seed"] = kcp["seed"].(string) - case "ws": - ws, _ := stream["wsSettings"].(map[string]interface{}) - params["path"] = ws["path"].(string) - if host, ok := ws["host"].(string); ok && len(host) > 0 { - params["host"] = host - } else { - headers, _ := ws["headers"].(map[string]interface{}) - params["host"] = searchHost(headers) - } - case "grpc": - grpc, _ := stream["grpcSettings"].(map[string]interface{}) - params["serviceName"] = grpc["serviceName"].(string) - params["authority"], _ = grpc["authority"].(string) - if grpc["multiMode"].(bool) { - params["mode"] = "multi" - } - case "httpupgrade": - httpupgrade, _ := stream["httpupgradeSettings"].(map[string]interface{}) - params["path"] = httpupgrade["path"].(string) - if host, ok := httpupgrade["host"].(string); ok && len(host) > 0 { - params["host"] = host - } else { - headers, _ := httpupgrade["headers"].(map[string]interface{}) - params["host"] = searchHost(headers) - } - case "xhttp": - xhttp, _ := stream["xhttpSettings"].(map[string]interface{}) - params["path"] = xhttp["path"].(string) - if host, ok := xhttp["host"].(string); ok && len(host) > 0 { - params["host"] = host - } else { - headers, _ := xhttp["headers"].(map[string]interface{}) - params["host"] = searchHost(headers) - } - params["mode"] = xhttp["mode"].(string) - } - 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" - } - } - } - - if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 { - params["flow"] = clients[clientIndex].Flow - } - } - - if security == "reality" { - params["security"] = "reality" - realitySetting, _ := stream["realitySettings"].(map[string]interface{}) - realitySettings, _ := searchKey(realitySetting, "settings") - if realitySetting != nil { - if sniValue, ok := searchKey(realitySetting, "serverNames"); ok { - sNames, _ := sniValue.([]interface{}) - params["sni"] = sNames[random.Num(len(sNames))].(string) - } - if pbkValue, ok := searchKey(realitySettings, "publicKey"); ok { - params["pbk"], _ = pbkValue.(string) - } - if sidValue, ok := searchKey(realitySetting, "shortIds"); ok { - shortIds, _ := sidValue.([]interface{}) - params["sid"] = shortIds[random.Num(len(shortIds))].(string) - } - if fpValue, ok := searchKey(realitySettings, "fingerprint"); ok { - if fp, ok := fpValue.(string); ok && len(fp) > 0 { - params["fp"] = fp - } - } - params["spx"] = "/" + random.Seq(15) - } - - if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 { - params["flow"] = clients[clientIndex].Flow - } - } - - if security != "tls" && security != "reality" { - 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) - url, _ := url.Parse(link) - q := url.Query() - - for k, v := range params { - q.Add(k, v) - } - - // Set the new query values on the URL - url.RawQuery = q.Encode() - - url.Fragment = s.genRemark(inbound, email, "") - return url.String() -} - -func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string { - address := s.address - if inbound.Protocol != model.Trojan { - return "" - } - var stream map[string]interface{} - json.Unmarshal([]byte(inbound.StreamSettings), &stream) - clients, _ := s.inboundService.GetClients(inbound) - clientIndex := -1 - for i, client := range clients { - if client.Email == email { - clientIndex = i - break - } - } - password := clients[clientIndex].Password - port := inbound.Port - streamNetwork := stream["network"].(string) - params := make(map[string]string) - params["type"] = streamNetwork - - switch streamNetwork { - case "tcp": - tcp, _ := stream["tcpSettings"].(map[string]interface{}) - header, _ := tcp["header"].(map[string]interface{}) - typeStr, _ := header["type"].(string) - if typeStr == "http" { - request := header["request"].(map[string]interface{}) - requestPath, _ := request["path"].([]interface{}) - params["path"] = requestPath[0].(string) - headers, _ := request["headers"].(map[string]interface{}) - params["host"] = searchHost(headers) - params["headerType"] = "http" - } - case "kcp": - kcp, _ := stream["kcpSettings"].(map[string]interface{}) - header, _ := kcp["header"].(map[string]interface{}) - params["headerType"] = header["type"].(string) - params["seed"] = kcp["seed"].(string) - case "ws": - ws, _ := stream["wsSettings"].(map[string]interface{}) - params["path"] = ws["path"].(string) - if host, ok := ws["host"].(string); ok && len(host) > 0 { - params["host"] = host - } else { - headers, _ := ws["headers"].(map[string]interface{}) - params["host"] = searchHost(headers) - } - case "grpc": - grpc, _ := stream["grpcSettings"].(map[string]interface{}) - params["serviceName"] = grpc["serviceName"].(string) - params["authority"], _ = grpc["authority"].(string) - if grpc["multiMode"].(bool) { - params["mode"] = "multi" - } - case "httpupgrade": - httpupgrade, _ := stream["httpupgradeSettings"].(map[string]interface{}) - params["path"] = httpupgrade["path"].(string) - if host, ok := httpupgrade["host"].(string); ok && len(host) > 0 { - params["host"] = host - } else { - headers, _ := httpupgrade["headers"].(map[string]interface{}) - params["host"] = searchHost(headers) - } - case "xhttp": - xhttp, _ := stream["xhttpSettings"].(map[string]interface{}) - params["path"] = xhttp["path"].(string) - if host, ok := xhttp["host"].(string); ok && len(host) > 0 { - params["host"] = host - } else { - headers, _ := xhttp["headers"].(map[string]interface{}) - params["host"] = searchHost(headers) - } - params["mode"] = xhttp["mode"].(string) - } - 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" - } - } - } - } - - if security == "reality" { - params["security"] = "reality" - realitySetting, _ := stream["realitySettings"].(map[string]interface{}) - realitySettings, _ := searchKey(realitySetting, "settings") - if realitySetting != nil { - if sniValue, ok := searchKey(realitySetting, "serverNames"); ok { - sNames, _ := sniValue.([]interface{}) - params["sni"] = sNames[random.Num(len(sNames))].(string) - } - if pbkValue, ok := searchKey(realitySettings, "publicKey"); ok { - params["pbk"], _ = pbkValue.(string) - } - if sidValue, ok := searchKey(realitySetting, "shortIds"); ok { - shortIds, _ := sidValue.([]interface{}) - params["sid"] = shortIds[random.Num(len(shortIds))].(string) - } - if fpValue, ok := searchKey(realitySettings, "fingerprint"); ok { - if fp, ok := fpValue.(string); ok && len(fp) > 0 { - params["fp"] = fp - } - } - params["spx"] = "/" + random.Seq(15) - } - - if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 { - params["flow"] = clients[clientIndex].Flow - } - } - - if security != "tls" && security != "reality" { - 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) - - url, _ := url.Parse(link) - q := url.Query() - - for k, v := range params { - q.Add(k, v) - } - - // Set the new query values on the URL - url.RawQuery = q.Encode() - - url.Fragment = s.genRemark(inbound, email, "") - return url.String() -} - -func (s *SubService) genShadowsocksLink(inbound *model.Inbound, email string) string { - address := s.address - if inbound.Protocol != model.Shadowsocks { - return "" - } - var stream map[string]interface{} - json.Unmarshal([]byte(inbound.StreamSettings), &stream) - clients, _ := s.inboundService.GetClients(inbound) - - var settings map[string]interface{} - json.Unmarshal([]byte(inbound.Settings), &settings) - inboundPassword := settings["password"].(string) - method := settings["method"].(string) - clientIndex := -1 - for i, client := range clients { - if client.Email == email { - clientIndex = i - break - } - } - streamNetwork := stream["network"].(string) - params := make(map[string]string) - params["type"] = streamNetwork - - switch streamNetwork { - case "tcp": - tcp, _ := stream["tcpSettings"].(map[string]interface{}) - header, _ := tcp["header"].(map[string]interface{}) - typeStr, _ := header["type"].(string) - if typeStr == "http" { - request := header["request"].(map[string]interface{}) - requestPath, _ := request["path"].([]interface{}) - params["path"] = requestPath[0].(string) - headers, _ := request["headers"].(map[string]interface{}) - params["host"] = searchHost(headers) - params["headerType"] = "http" - } - case "kcp": - kcp, _ := stream["kcpSettings"].(map[string]interface{}) - header, _ := kcp["header"].(map[string]interface{}) - params["headerType"] = header["type"].(string) - params["seed"] = kcp["seed"].(string) - case "ws": - ws, _ := stream["wsSettings"].(map[string]interface{}) - params["path"] = ws["path"].(string) - if host, ok := ws["host"].(string); ok && len(host) > 0 { - params["host"] = host - } else { - headers, _ := ws["headers"].(map[string]interface{}) - params["host"] = searchHost(headers) - } - case "grpc": - grpc, _ := stream["grpcSettings"].(map[string]interface{}) - params["serviceName"] = grpc["serviceName"].(string) - params["authority"], _ = grpc["authority"].(string) - if grpc["multiMode"].(bool) { - params["mode"] = "multi" - } - case "httpupgrade": - httpupgrade, _ := stream["httpupgradeSettings"].(map[string]interface{}) - params["path"] = httpupgrade["path"].(string) - if host, ok := httpupgrade["host"].(string); ok && len(host) > 0 { - params["host"] = host - } else { - headers, _ := httpupgrade["headers"].(map[string]interface{}) - params["host"] = searchHost(headers) - } - case "xhttp": - xhttp, _ := stream["xhttpSettings"].(map[string]interface{}) - params["path"] = xhttp["path"].(string) - if host, ok := xhttp["host"].(string); ok && len(host) > 0 { - params["host"] = host - } else { - headers, _ := xhttp["headers"].(map[string]interface{}) - params["host"] = searchHost(headers) - } - params["mode"] = xhttp["mode"].(string) - } - - 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) - if method[0] == '2' { - 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) - url, _ := url.Parse(link) - q := url.Query() - - for k, v := range params { - q.Add(k, v) - } - - // Set the new query values on the URL - url.RawQuery = q.Encode() - - url.Fragment = s.genRemark(inbound, email, "") - return url.String() -} - -func (s *SubService) genRemark(inbound *model.Inbound, email string, extra string) string { - separationChar := string(s.remarkModel[0]) - orderChars := s.remarkModel[1:] - orders := map[byte]string{ - 'i': "", - 'e': "", - 'o': "", - } - if len(email) > 0 { - orders['e'] = email - } - if len(inbound.Remark) > 0 { - orders['i'] = inbound.Remark - } - if len(extra) > 0 { - orders['o'] = extra - } - - var remark []string - for i := 0; i < len(orderChars); i++ { - char := orderChars[i] - order, exists := orders[char] - if exists && order != "" { - remark = append(remark, order) - } - } - - if s.showInfo { - statsExist := false - var stats xray.ClientTraffic - for _, clientStat := range inbound.ClientStats { - if clientStat.Email == email { - stats = clientStat - statsExist = true - break - } - } - - // Get remained days - if statsExist { - if !stats.Enable { - return fmt.Sprintf("⛔️N/A%s%s", separationChar, strings.Join(remark, separationChar)) - } - if vol := stats.Total - (stats.Up + stats.Down); vol > 0 { - remark = append(remark, fmt.Sprintf("%s%s", common.FormatTraffic(vol), "📊")) - } - now := time.Now().Unix() - switch exp := stats.ExpiryTime / 1000; { - case exp > 0: - remainingSeconds := exp - now - days := remainingSeconds / 86400 - hours := (remainingSeconds % 86400) / 3600 - minutes := (remainingSeconds % 3600) / 60 - if days > 0 { - if hours > 0 { - remark = append(remark, fmt.Sprintf("%dD,%dH⏳", days, hours)) - } else { - remark = append(remark, fmt.Sprintf("%dD⏳", days)) - } - } else if hours > 0 { - remark = append(remark, fmt.Sprintf("%dH⏳", hours)) - } else { - remark = append(remark, fmt.Sprintf("%dM⏳", minutes)) - } - case exp < 0: - days := exp / -86400 - hours := (exp % -86400) / 3600 - minutes := (exp % -3600) / 60 - if days > 0 { - if hours > 0 { - remark = append(remark, fmt.Sprintf("%dD,%dH⏳", days, hours)) - } else { - remark = append(remark, fmt.Sprintf("%dD⏳", days)) - } - } else if hours > 0 { - remark = append(remark, fmt.Sprintf("%dH⏳", hours)) - } else { - remark = append(remark, fmt.Sprintf("%dM⏳", minutes)) - } - } - } - } - return strings.Join(remark, separationChar) -} - -func searchKey(data interface{}, key string) (interface{}, bool) { - switch val := data.(type) { - case map[string]interface{}: - for k, v := range val { - if k == key { - return v, true - } - if result, ok := searchKey(v, key); ok { - return result, true - } - } - case []interface{}: - for _, v := range val { - if result, ok := searchKey(v, key); ok { - return result, true - } - } - } - return nil, false -} - -func searchHost(headers interface{}) string { - data, _ := headers.(map[string]interface{}) - for k, v := range data { - if strings.EqualFold(k, "host") { - switch v.(type) { - case []interface{}: - hosts, _ := v.([]interface{}) - if len(hosts) > 0 { - return hosts[0].(string) - } else { - return "" - } - case interface{}: - return v.(string) - } - } - } - - return "" -}