Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/MHSanaei/3x-ui.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMHSanaei <ho3ein.sanaei@gmail.com>2024-03-11 15:44:24 +0300
committerMHSanaei <ho3ein.sanaei@gmail.com>2024-03-11 18:10:11 +0300
commit5b87b1253555b7e932f79bd86795e350f9f3de2e (patch)
tree711ca9b226f2cf9d041db50f81c975913c76ea23
parent8908e8b16a3457c1353cec163a79cd82338be336 (diff)
[sub] JSON sub enhancement + minor changes
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
-rw-r--r--sub/default.json22
-rw-r--r--sub/subController.go5
-rw-r--r--sub/subJsonService.go117
-rw-r--r--web/assets/js/model/outbound.js3
-rw-r--r--web/html/common/qrcode_modal.html9
-rw-r--r--web/html/xui/inbound_modal.html9
-rw-r--r--web/html/xui/index.html14
-rw-r--r--web/html/xui/settings.html37
8 files changed, 115 insertions, 101 deletions
diff --git a/sub/default.json b/sub/default.json
index ba13f6fb..d98a03ef 100644
--- a/sub/default.json
+++ b/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": {}
} \ No newline at end of file
diff --git a/sub/subController.go b/sub/subController.go
index 8de5b5df..dc66c892 100644
--- a/sub/subController.go
+++ b/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
diff --git a/sub/subJsonService.go b/sub/subJsonService.go
index de4be680..d50e9644 100644
--- a/sub/subJsonService.go
+++ b/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,
}
diff --git a/web/assets/js/model/outbound.js b/web/assets/js/model/outbound.js
index f62b06b9..0b699f49 100644
--- a/web/assets/js/model/outbound.js
+++ b/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),
);
}
diff --git a/web/html/common/qrcode_modal.html b/web/html/common/qrcode_modal.html
index 3fcfdfc1..0e2b3a63 100644
--- a/web/html/common/qrcode_modal.html
+++ b/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>
diff --git a/web/html/xui/inbound_modal.html b/web/html/xui/inbound_modal.html
index fa89fada..f605b4fd 100644
--- a/web/html/xui/inbound_modal.html
+++ b/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>
diff --git a/web/html/xui/index.html b/web/html/xui/index.html
index fe867214..157563c0 100644
--- a/web/html/xui/index.html
+++ b/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>
diff --git a/web/html/xui/settings.html b/web/html/xui/settings.html
index 507a93c7..aac10f2a 100644
--- a/web/html/xui/settings.html
+++ b/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) {