[sub] JSON sub enhancement + minor changes

Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
This commit is contained in:
MHSanaei 2024-03-11 16:14:24 +03:30
parent 8908e8b16a
commit 5b87b12535
8 changed files with 115 additions and 101 deletions

View file

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

View file

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

View file

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

View file

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

View file

@ -1,5 +1,6 @@
{{define "qrcodeModal"}} {{define "qrcodeModal"}}
<a-modal id="qrcode-modal" v-model="qrModal.visible" :title="qrModal.title" <a-modal id="qrcode-modal" v-model="qrModal.visible" :title="qrModal.title"
:dialog-style="{ top: '20px' }"
:closable="true" :closable="true"
:class="themeSwitcher.currentTheme" :class="themeSwitcher.currentTheme"
:footer="null" :footer="null"

View file

@ -1,5 +1,6 @@
{{define "inboundModal"}} {{define "inboundModal"}}
<a-modal id="inbound-modal" v-model="inModal.visible" :title="inModal.title" @ok="inModal.ok" <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" :confirm-loading="inModal.confirmLoading" :closable="true" :mask-closable="false"
:class="themeSwitcher.currentTheme" :class="themeSwitcher.currentTheme"
:ok-text="inModal.okText" cancel-text='{{ i18n "close" }}'> :ok-text="inModal.okText" cancel-text='{{ i18n "close" }}'>

View file

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

View file

@ -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.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="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> <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-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-tab-pane>
</a-tabs> </a-tabs>
</a-space> </a-space>
@ -483,6 +502,16 @@
this.allSetting.subJsonFragment = v ? JSON.stringify(this.defaultFragment) : ""; 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: { fragmentLength: {
get: function() { return this.fragment ? JSON.parse(this.allSetting.subJsonFragment).settings.fragment.length : ""; }, get: function() { return this.fragment ? JSON.parse(this.allSetting.subJsonFragment).settings.fragment.length : ""; },
set: function(v) { set: function(v) {