Browse Source

[sub] JSON sub enhancement + minor changes

Co-Authored-By: Alireza Ahmadi <[email protected]>
MHSanaei 1 year ago
parent
commit
5b87b12535

+ 2 - 20
sub/default.json

@@ -1,4 +1,5 @@
 {
+  "remarks": "",
   "dns": {
     "tag": "dns_out",
     "queryStrategy": "UseIP",
@@ -78,28 +79,9 @@
       {
         "type": "field",
         "network": "tcp,udp",
-        "balancerTag": "all"
-      }
-    ],
-    "balancers": [
-      {
-        "tag": "all",
-        "selector": [
-          "proxy"
-        ],
-        "strategy": {
-          "type": "leastPing"
-        }
+        "outboundTag": "proxy"
       }
     ]
   },
-  "observatory": {
-    "probeInterval": "5m",
-    "probeURL": "https://api.github.com/_private/browser/stats",
-    "subjectSelector": [
-      "proxy"
-    ],
-    "EnableConcurrency": true
-  },
   "stats": {}
 }

+ 3 - 2
sub/subController.go

@@ -27,14 +27,15 @@ func NewSUBController(
 	update string,
 	jsonFragment string,
 ) *SUBController {
+	sub := NewSubService(showInfo, rModel)
 	a := &SUBController{
 		subPath:        subPath,
 		subJsonPath:    jsonPath,
 		subEncrypt:     encrypt,
 		updateInterval: update,
 
-		subService:     NewSubService(showInfo, rModel),
-		subJsonService: NewSubJsonService(jsonFragment),
+		subService:     sub,
+		subJsonService: NewSubJsonService(jsonFragment, sub),
 	}
 	a.initRouter(g)
 	return a

+ 59 - 58
sub/subJsonService.go

@@ -18,15 +18,34 @@ import (
 var defaultJson string
 
 type SubJsonService struct {
-	fragmanet string
+	configJson       map[string]interface{}
+	defaultOutbounds []json_util.RawMessage
+	fragment         string
 
 	inboundService service.InboundService
-	SubService
+	SubService     *SubService
 }
 
-func NewSubJsonService(fragment string) *SubJsonService {
+func NewSubJsonService(fragment 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 fragment != "" {
+		defaultOutbounds = append(defaultOutbounds, json_util.RawMessage(fragment))
+	}
+
 	return &SubJsonService{
-		fragmanet: fragment,
+		configJson:       configJson,
+		defaultOutbounds: defaultOutbounds,
+		fragment:         fragment,
+		SubService:       subService,
 	}
 }
 
@@ -39,19 +58,8 @@ func (s *SubJsonService) GetJson(subId string, host string) (string, string, err
 	var header string
 	var traffic xray.ClientTraffic
 	var clientTraffics []xray.ClientTraffic
-	var configJson map[string]interface{}
-	var defaultOutbounds []json_util.RawMessage
+	var configArray []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)
-		}
-	}
-
-	outbounds := []json_util.RawMessage{}
-	startIndex := 0
 	// Prepare Inbounds
 	for _, inbound := range inbounds {
 		clients, err := s.inboundService.GetClients(inbound)
@@ -62,7 +70,7 @@ func (s *SubJsonService) GetJson(subId string, host string) (string, string, err
 			continue
 		}
 		if len(inbound.Listen) > 0 && inbound.Listen[0] == '@' {
-			listen, port, streamSettings, err := s.getFallbackMaster(inbound.Listen, inbound.StreamSettings)
+			listen, port, streamSettings, err := s.SubService.getFallbackMaster(inbound.Listen, inbound.StreamSettings)
 			if err == nil {
 				inbound.Listen = listen
 				inbound.Port = port
@@ -70,22 +78,16 @@ func (s *SubJsonService) GetJson(subId string, host string) (string, string, err
 			}
 		}
 
-		var subClients []model.Client
 		for _, client := range clients {
 			if client.Enable && client.SubID == subId {
-				subClients = append(subClients, client)
 				clientTraffics = append(clientTraffics, s.SubService.getClientTraffics(inbound.ClientStats, client.Email))
+				newConfigs := s.getConfig(inbound, client, host)
+				configArray = append(configArray, newConfigs...)
 			}
 		}
-
-		outbound := s.getOutbound(inbound, subClients, host, startIndex)
-		if outbound != nil {
-			outbounds = append(outbounds, outbound...)
-			startIndex += len(outbound)
-		}
 	}
 
-	if len(outbounds) == 0 {
+	if len(configArray) == 0 {
 		return "", "", nil
 	}
 
@@ -112,21 +114,15 @@ func (s *SubJsonService) GetJson(subId string, host string) (string, string, err
 		}
 	}
 
-	if s.fragmanet != "" {
-		outbounds = append(outbounds, json_util.RawMessage(s.fragmanet))
-	}
-
 	// Combile outbounds
-	outbounds = append(outbounds, defaultOutbounds...)
-	configJson["outbounds"] = outbounds
-	finalJson, _ := json.MarshalIndent(configJson, "", "  ")
+	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) getOutbound(inbound *model.Inbound, clients []model.Client, host string, startIndex int) []json_util.RawMessage {
-	var newOutbounds []json_util.RawMessage
+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{})
@@ -136,13 +132,13 @@ func (s *SubJsonService) getOutbound(inbound *model.Inbound, clients []model.Cli
 				"forceTls": "same",
 				"dest":     host,
 				"port":     float64(inbound.Port),
+				"remark":   "",
 			},
 		}
 	}
 
 	delete(stream, "externalProxy")
 
-	config_index := startIndex
 	for _, ep := range externalProxies {
 		extPrxy := ep.(map[string]interface{})
 		inbound.Listen = extPrxy["dest"].(string)
@@ -161,21 +157,28 @@ func (s *SubJsonService) getOutbound(inbound *model.Inbound, clients []model.Cli
 			}
 		}
 		streamSettings, _ := json.MarshalIndent(newStream, "", "  ")
-		inbound.StreamSettings = string(streamSettings)
 
-		for _, client := range clients {
-			inbound.Tag = fmt.Sprintf("proxy_%d", config_index)
-			switch inbound.Protocol {
-			case "vmess", "vless":
-				newOutbounds = append(newOutbounds, s.genVnext(inbound, client))
-			case "trojan", "shadowsocks":
-				newOutbounds = append(newOutbounds, s.genServer(inbound, client))
-			}
-			config_index += 1
+		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 newOutbounds
+	return newJsonArray
 }
 
 func (s *SubJsonService) streamData(stream string) map[string]interface{} {
@@ -189,7 +192,7 @@ func (s *SubJsonService) streamData(stream string) map[string]interface{} {
 	}
 	delete(streamSettings, "sockopt")
 
-	if s.fragmanet != "" {
+	if s.fragment != "" {
 		streamSettings["sockopt"] = json_util.RawMessage(`{"dialerProxy": "fragment", "tcpKeepAliveIdle": 100, "tcpNoDelay": true}`)
 	}
 
@@ -200,8 +203,6 @@ func (s *SubJsonService) streamData(stream string) map[string]interface{} {
 		streamSettings["tcpSettings"] = s.removeAcceptProxy(streamSettings["tcpSettings"])
 	case "ws":
 		streamSettings["wsSettings"] = s.removeAcceptProxy(streamSettings["wsSettings"])
-	case "httpupgrade":
-		streamSettings["httpupgradeSettings"] = s.removeAcceptProxy(streamSettings["httpupgradeSettings"])
 	}
 
 	return streamSettings
@@ -217,7 +218,7 @@ func (s *SubJsonService) removeAcceptProxy(setting interface{}) map[string]inter
 
 func (s *SubJsonService) tlsData(tData map[string]interface{}) map[string]interface{} {
 	tlsData := make(map[string]interface{}, 1)
-	tlsClientSettings := tData["settings"].(map[string]interface{})
+	tlsClientSettings, _ := tData["settings"].(map[string]interface{})
 
 	tlsData["serverName"] = tData["serverName"]
 	tlsData["alpn"] = tData["alpn"]
@@ -232,7 +233,7 @@ func (s *SubJsonService) tlsData(tData map[string]interface{}) map[string]interf
 
 func (s *SubJsonService) realityData(rData map[string]interface{}) map[string]interface{} {
 	rltyData := make(map[string]interface{}, 1)
-	rltyClientSettings := rData["settings"].(map[string]interface{})
+	rltyClientSettings, _ := rData["settings"].(map[string]interface{})
 
 	rltyData["show"] = false
 	rltyData["publicKey"] = rltyClientSettings["publicKey"]
@@ -256,7 +257,7 @@ func (s *SubJsonService) realityData(rData map[string]interface{}) map[string]in
 	return rltyData
 }
 
-func (s *SubJsonService) genVnext(inbound *model.Inbound, client model.Client) json_util.RawMessage {
+func (s *SubJsonService) genVnext(inbound *model.Inbound, streamSettings json_util.RawMessage, client model.Client) json_util.RawMessage {
 	outbound := Outbound{}
 	usersData := make([]UserVnext, 1)
 
@@ -275,8 +276,8 @@ func (s *SubJsonService) genVnext(inbound *model.Inbound, client model.Client) j
 	}
 
 	outbound.Protocol = string(inbound.Protocol)
-	outbound.Tag = inbound.Tag
-	outbound.StreamSettings = json_util.RawMessage(inbound.StreamSettings)
+	outbound.Tag = "proxy"
+	outbound.StreamSettings = streamSettings
 	outbound.Settings = OutboundSettings{
 		Vnext: vnextData,
 	}
@@ -285,7 +286,7 @@ func (s *SubJsonService) genVnext(inbound *model.Inbound, client model.Client) j
 	return result
 }
 
-func (s *SubJsonService) genServer(inbound *model.Inbound, client model.Client) json_util.RawMessage {
+func (s *SubJsonService) genServer(inbound *model.Inbound, streamSettings json_util.RawMessage, client model.Client) json_util.RawMessage {
 	outbound := Outbound{}
 
 	serverData := make([]ServerSetting, 1)
@@ -311,8 +312,8 @@ func (s *SubJsonService) genServer(inbound *model.Inbound, client model.Client)
 	}
 
 	outbound.Protocol = string(inbound.Protocol)
-	outbound.Tag = inbound.Tag
-	outbound.StreamSettings = json_util.RawMessage(inbound.StreamSettings)
+	outbound.Tag = "proxy"
+	outbound.StreamSettings = streamSettings
 	outbound.Settings = OutboundSettings{
 		Servers: serverData,
 	}

+ 3 - 0
web/assets/js/model/outbound.js

@@ -397,6 +397,7 @@ class StreamSettings extends CommonClass {
                 quicSettings=new QuicStreamSettings(),
                 grpcSettings=new GrpcStreamSettings(),
                 httpupgradeSettings=new HttpUpgradeStreamSettings(),
+                sockopt = undefined,
                 ) {
         super();
         this.network = network;
@@ -410,6 +411,7 @@ class StreamSettings extends CommonClass {
         this.quic = quicSettings;
         this.grpc = grpcSettings;
         this.httpupgrade = httpupgradeSettings;
+        this.sockopt = sockopt;
     }
     
     get isTls() {
@@ -441,6 +443,7 @@ class StreamSettings extends CommonClass {
             QuicStreamSettings.fromJson(json.quicSettings),
             GrpcStreamSettings.fromJson(json.grpcSettings),
             HttpUpgradeStreamSettings.fromJson(json.httpupgradeSettings),
+            SockoptStreamSettings.fromJson(json.sockopt),
         );
     }
 

+ 5 - 4
web/html/common/qrcode_modal.html

@@ -1,9 +1,10 @@
 {{define "qrcodeModal"}}
 <a-modal id="qrcode-modal" v-model="qrModal.visible" :title="qrModal.title"
-         :closable="true"
-         :class="themeSwitcher.currentTheme"
-         :footer="null"
-         width="300px">
+        :dialog-style="{ top: '20px' }"
+        :closable="true"
+        :class="themeSwitcher.currentTheme"
+        :footer="null"
+        width="300px">
     <a-tag color="green" style="margin-bottom: 10px;display: block;text-align: center;">
         {{ i18n "pages.inbounds.clickOnQRcode" }}
     </a-tag>

+ 5 - 4
web/html/xui/inbound_modal.html

@@ -1,8 +1,9 @@
 {{define "inboundModal"}}
-<a-modal id="inbound-modal" v-model="inModal.visible" :title="inModal.title" @ok="inModal.ok"
-         :confirm-loading="inModal.confirmLoading" :closable="true" :mask-closable="false"
-         :class="themeSwitcher.currentTheme"
-         :ok-text="inModal.okText" cancel-text='{{ i18n "close" }}'>
+<a-modal id="inbound-modal" v-model="inModal.visible" :title="inModal.title"
+        :dialog-style="{ top: '20px' }" @ok="inModal.ok"
+        :confirm-loading="inModal.confirmLoading" :closable="true" :mask-closable="false"
+        :class="themeSwitcher.currentTheme"
+        :ok-text="inModal.okText" cancel-text='{{ i18n "close" }}'>
     {{template "form/inbound"}}
 </a-modal>
 <script>

+ 5 - 9
web/html/xui/index.html

@@ -259,17 +259,13 @@
         </a-layout-content>
     </a-layout>
 
-    <a-modal id="version-modal" v-model="versionModal.visible" title='{{ i18n "pages.index.xraySwitch" }}'
-             :closable="true" @ok="() => versionModal.visible = false"
-             :class="themeSwitcher.currentTheme"
-             footer="">
+    <a-modal id="version-modal" v-model="versionModal.visible" title='{{ i18n "pages.index.xraySwitch" }}' :closable="true"
+        @ok="() => versionModal.visible = false" :class="themeSwitcher.currentTheme" footer="">
         <a-alert type="warning" style="margin-bottom: 12px; width: fit-content"
-        message='{{ i18n "pages.index.xraySwitchClickDesk" }}'
-        show-icon
-        ></a-alert>
+            message='{{ i18n "pages.index.xraySwitchClickDesk" }}' show-icon></a-alert>
         <template v-for="version, index in versionModal.versions">
-            <a-tag :color="index % 2 == 0 ? 'purple' : 'green'"
-                   style="margin-right: 10px" @click="switchV2rayVersion(version)">
+            <a-tag :color="index % 2 == 0 ? 'purple' : 'green'" style="margin-right: 12px; margin-bottom: 12px"
+                @click="switchV2rayVersion(version)">
                 [[ version ]]
             </a-tag>
         </template>

+ 33 - 4
web/html/xui/settings.html

@@ -295,11 +295,30 @@
                                 <setting-list-item type="text" title='{{ i18n "pages.settings.subPath"}}' desc='{{ i18n "pages.settings.subPathDesc"}}' v-model="allSetting.subJsonPath"></setting-list-item>
                                 <setting-list-item type="text" title='{{ i18n "pages.settings.subURI"}}' desc='{{ i18n "pages.settings.subURIDesc"}}' v-model="allSetting.subJsonURI" placeholder="(http|https)://domain[:port]/path/"></setting-list-item>
                                 <setting-list-item type="switch" title='{{ i18n "pages.settings.fragment"}}' desc='{{ i18n "pages.settings.fragmentDesc"}}' v-model="fragment"></setting-list-item>
-                                <template v-if="fragment">
-                                    <setting-list-item type="text" title='length' v-model="fragmentLength" placeholder="100-200"></setting-list-item>
-                                    <setting-list-item type="text" title='Interval' v-model="fragmentInterval" placeholder="10-20"></setting-list-item>
-                                </template>
                             </a-list>
+                            <a-collapse v-if="fragment">
+                                <a-collapse-panel header='{{ i18n "pages.settings.fragment"}}'>
+                                    <a-list-item style="padding: 20px">
+                                        <a-row>
+                                            <a-col :lg="24" :xl="12">
+                                                <a-list-item-meta title='Packets'/>
+                                            </a-col>
+                                            <a-col :lg="24" :xl="12">
+                                                <a-select
+                                                    v-model="fragmentPackets"
+                                                    style="width: 100%"
+                                                    :dropdown-class-name="themeSwitcher.currentTheme">
+                                                    <a-select-option :value="p" :label="p" v-for="p in ['1-1', '1-3', 'tlshello']">
+                                                        [[ p ]]
+                                                    </a-select-option>
+                                                </a-select>
+                                            </a-col>
+                                        </a-row>
+                                    </a-list-item>
+                                    <setting-list-item type="text" title='Length' v-model="fragmentLength" placeholder="100-200"></setting-list-item>
+                                    <setting-list-item type="text" title='Interval' v-model="fragmentInterval" placeholder="10-20"></setting-list-item>
+                                </a-collapse-panel>
+                            </a-collapse>
                         </a-tab-pane>
                     </a-tabs>
                 </a-space>
@@ -483,6 +502,16 @@
                     this.allSetting.subJsonFragment = v ? JSON.stringify(this.defaultFragment) : "";
                 }
             },
+            fragmentPackets: {
+                get: function() { return this.fragment ? JSON.parse(this.allSetting.subJsonFragment).settings.fragment.packets : ""; },
+                set: function(v) {
+                    if (v != ""){
+                        newFragment = JSON.parse(this.allSetting.subJsonFragment);
+                        newFragment.settings.fragment.packets = v;
+                        this.allSetting.subJsonFragment = JSON.stringify(newFragment);
+                    }
+                }
+            },
             fragmentLength: {
                 get: function() { return this.fragment ? JSON.parse(this.allSetting.subJsonFragment).settings.fragment.length : ""; },
                 set: function(v) {