mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2025-04-20 05:52:24 +00:00
[subJson] add mux and direct
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
This commit is contained in:
parent
fc23af5db6
commit
8b5fe0b018
7 changed files with 175 additions and 7 deletions
14
sub/sub.go
14
sub/sub.go
|
@ -92,9 +92,21 @@ func (s *Server) initRouter() (*gin.Engine, error) {
|
||||||
SubJsonFragment = ""
|
SubJsonFragment = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SubJsonMux, err := s.settingService.GetSubJsonMux()
|
||||||
|
if err != nil {
|
||||||
|
SubJsonMux = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
SubJsonRules, err := s.settingService.GetSubJsonRules()
|
||||||
|
if err != nil {
|
||||||
|
SubJsonRules = ""
|
||||||
|
}
|
||||||
|
|
||||||
g := engine.Group("/")
|
g := engine.Group("/")
|
||||||
|
|
||||||
s.sub = NewSUBController(g, LinksPath, JsonPath, Encrypt, ShowInfo, RemarkModel, SubUpdates, SubJsonFragment)
|
s.sub = NewSUBController(
|
||||||
|
g, LinksPath, JsonPath, Encrypt, ShowInfo, RemarkModel, SubUpdates,
|
||||||
|
SubJsonFragment, SubJsonMux, SubJsonRules)
|
||||||
|
|
||||||
return engine, nil
|
return engine, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,8 @@ func NewSUBController(
|
||||||
rModel string,
|
rModel string,
|
||||||
update string,
|
update string,
|
||||||
jsonFragment string,
|
jsonFragment string,
|
||||||
|
jsonMux string,
|
||||||
|
jsonRules string,
|
||||||
) *SUBController {
|
) *SUBController {
|
||||||
sub := NewSubService(showInfo, rModel)
|
sub := NewSubService(showInfo, rModel)
|
||||||
a := &SUBController{
|
a := &SUBController{
|
||||||
|
@ -35,7 +37,7 @@ func NewSUBController(
|
||||||
updateInterval: update,
|
updateInterval: update,
|
||||||
|
|
||||||
subService: sub,
|
subService: sub,
|
||||||
subJsonService: NewSubJsonService(jsonFragment, sub),
|
subJsonService: NewSubJsonService(jsonFragment, jsonMux, jsonRules, sub),
|
||||||
}
|
}
|
||||||
a.initRouter(g)
|
a.initRouter(g)
|
||||||
return a
|
return a
|
||||||
|
|
|
@ -21,12 +21,13 @@ type SubJsonService struct {
|
||||||
configJson map[string]interface{}
|
configJson map[string]interface{}
|
||||||
defaultOutbounds []json_util.RawMessage
|
defaultOutbounds []json_util.RawMessage
|
||||||
fragment string
|
fragment string
|
||||||
|
mux string
|
||||||
|
|
||||||
inboundService service.InboundService
|
inboundService service.InboundService
|
||||||
SubService *SubService
|
SubService *SubService
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSubJsonService(fragment string, subService *SubService) *SubJsonService {
|
func NewSubJsonService(fragment string, mux string, rules string, subService *SubService) *SubJsonService {
|
||||||
var configJson map[string]interface{}
|
var configJson map[string]interface{}
|
||||||
var defaultOutbounds []json_util.RawMessage
|
var defaultOutbounds []json_util.RawMessage
|
||||||
json.Unmarshal([]byte(defaultJson), &configJson)
|
json.Unmarshal([]byte(defaultJson), &configJson)
|
||||||
|
@ -37,6 +38,17 @@ func NewSubJsonService(fragment string, subService *SubService) *SubJsonService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if rules != "" {
|
||||||
|
var newRules []interface{}
|
||||||
|
routing, _ := configJson["routing"].(map[string]interface{})
|
||||||
|
defaultRules, _ := routing["rules"].([]interface{})
|
||||||
|
json.Unmarshal([]byte(rules), &newRules)
|
||||||
|
defaultRules = append(newRules, defaultRules...)
|
||||||
|
fmt.Printf("routing: %#v\n\nRules: %#v\n\n", routing, defaultRules)
|
||||||
|
routing["rules"] = defaultRules
|
||||||
|
configJson["routing"] = routing
|
||||||
|
}
|
||||||
|
|
||||||
if fragment != "" {
|
if fragment != "" {
|
||||||
defaultOutbounds = append(defaultOutbounds, json_util.RawMessage(fragment))
|
defaultOutbounds = append(defaultOutbounds, json_util.RawMessage(fragment))
|
||||||
}
|
}
|
||||||
|
@ -45,6 +57,7 @@ func NewSubJsonService(fragment string, subService *SubService) *SubJsonService
|
||||||
configJson: configJson,
|
configJson: configJson,
|
||||||
defaultOutbounds: defaultOutbounds,
|
defaultOutbounds: defaultOutbounds,
|
||||||
fragment: fragment,
|
fragment: fragment,
|
||||||
|
mux: mux,
|
||||||
SubService: subService,
|
SubService: subService,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -277,6 +290,9 @@ func (s *SubJsonService) genVnext(inbound *model.Inbound, streamSettings json_ut
|
||||||
|
|
||||||
outbound.Protocol = string(inbound.Protocol)
|
outbound.Protocol = string(inbound.Protocol)
|
||||||
outbound.Tag = "proxy"
|
outbound.Tag = "proxy"
|
||||||
|
if s.mux != "" {
|
||||||
|
outbound.Mux = json_util.RawMessage(s.mux)
|
||||||
|
}
|
||||||
outbound.StreamSettings = streamSettings
|
outbound.StreamSettings = streamSettings
|
||||||
outbound.Settings = OutboundSettings{
|
outbound.Settings = OutboundSettings{
|
||||||
Vnext: vnextData,
|
Vnext: vnextData,
|
||||||
|
@ -313,6 +329,9 @@ func (s *SubJsonService) genServer(inbound *model.Inbound, streamSettings json_u
|
||||||
|
|
||||||
outbound.Protocol = string(inbound.Protocol)
|
outbound.Protocol = string(inbound.Protocol)
|
||||||
outbound.Tag = "proxy"
|
outbound.Tag = "proxy"
|
||||||
|
if s.mux != "" {
|
||||||
|
outbound.Mux = json_util.RawMessage(s.mux)
|
||||||
|
}
|
||||||
outbound.StreamSettings = streamSettings
|
outbound.StreamSettings = streamSettings
|
||||||
outbound.Settings = OutboundSettings{
|
outbound.Settings = OutboundSettings{
|
||||||
Servers: serverData,
|
Servers: serverData,
|
||||||
|
@ -326,7 +345,7 @@ type Outbound struct {
|
||||||
Protocol string `json:"protocol"`
|
Protocol string `json:"protocol"`
|
||||||
Tag string `json:"tag"`
|
Tag string `json:"tag"`
|
||||||
StreamSettings json_util.RawMessage `json:"streamSettings"`
|
StreamSettings json_util.RawMessage `json:"streamSettings"`
|
||||||
Mux map[string]interface{} `json:"mux,omitempty"`
|
Mux json_util.RawMessage `json:"mux,omitempty"`
|
||||||
ProxySettings map[string]interface{} `json:"proxySettings,omitempty"`
|
ProxySettings map[string]interface{} `json:"proxySettings,omitempty"`
|
||||||
Settings OutboundSettings `json:"settings,omitempty"`
|
Settings OutboundSettings `json:"settings,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,9 +35,11 @@ class AllSetting {
|
||||||
this.subUpdates = 0;
|
this.subUpdates = 0;
|
||||||
this.subEncrypt = true;
|
this.subEncrypt = true;
|
||||||
this.subShowInfo = false;
|
this.subShowInfo = false;
|
||||||
this.subURI = '';
|
this.subURI = "";
|
||||||
this.subJsonURI = '';
|
this.subJsonURI = "";
|
||||||
this.subJsonFragment = '';
|
this.subJsonFragment = "";
|
||||||
|
this.subJsonMux = "";
|
||||||
|
this.subJsonRules = "";
|
||||||
|
|
||||||
this.timeLocation = "Asia/Tehran";
|
this.timeLocation = "Asia/Tehran";
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,8 @@ type AllSetting struct {
|
||||||
SubJsonPath string `json:"subJsonPath" form:"subJsonPath"`
|
SubJsonPath string `json:"subJsonPath" form:"subJsonPath"`
|
||||||
SubJsonURI string `json:"subJsonURI" form:"subJsonURI"`
|
SubJsonURI string `json:"subJsonURI" form:"subJsonURI"`
|
||||||
SubJsonFragment string `json:"subJsonFragment" form:"subJsonFragment"`
|
SubJsonFragment string `json:"subJsonFragment" form:"subJsonFragment"`
|
||||||
|
SubJsonMux string `json:"subJsonMux" form:"subJsonMux"`
|
||||||
|
SubJsonRules string `json:"subJsonRules" form:"subJsonRules"`
|
||||||
Datepicker string `json:"datepicker" form:"datepicker"`
|
Datepicker string `json:"datepicker" form:"datepicker"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -295,6 +295,8 @@
|
||||||
<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>
|
||||||
|
<setting-list-item type="switch" title='Mux' v-model="enableMux"></setting-list-item>
|
||||||
|
<setting-list-item type="switch" title='{{ i18n "pages.xray.directCountryConfigs"}}' desc='{{ i18n "pages.xray.directCountryConfigsDesc"}}' v-model="enableDirect"></setting-list-item>
|
||||||
</a-list>
|
</a-list>
|
||||||
<a-collapse v-if="fragment">
|
<a-collapse v-if="fragment">
|
||||||
<a-collapse-panel header='{{ i18n "pages.settings.fragment"}}'>
|
<a-collapse-panel header='{{ i18n "pages.settings.fragment"}}'>
|
||||||
|
@ -318,6 +320,36 @@
|
||||||
<setting-list-item type="text" title='Length' v-model="fragmentLength" placeholder="100-200"></setting-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>
|
<setting-list-item type="text" title='Interval' v-model="fragmentInterval" placeholder="10-20"></setting-list-item>
|
||||||
</a-collapse-panel>
|
</a-collapse-panel>
|
||||||
|
<a-collapse-panel header='Mux' v-if="enableMux">
|
||||||
|
<setting-list-item type="number" title='Concurrency' v-model="muxConcurrency" :min="-1" :max="1024"></setting-list-item>
|
||||||
|
<setting-list-item type="number" title='xudp Concurrency' v-model="muxXudpConcurrency" :min="-1" :max="1024"></setting-list-item>
|
||||||
|
<a-list-item style="padding: 20px">
|
||||||
|
<a-row>
|
||||||
|
<a-col :lg="24" :xl="12">
|
||||||
|
<a-list-item-meta title='xudp UDP 443'/>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="24" :xl="12">
|
||||||
|
<a-select
|
||||||
|
v-model="muxXudpProxyUDP443"
|
||||||
|
style="width: 100%"
|
||||||
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
|
<a-select-option :value="p" :label="p" v-for="p in ['reject', 'allow', 'skip']">
|
||||||
|
[[ p ]]
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-list-item>
|
||||||
|
</a-collapse-panel>
|
||||||
|
<a-collapse-panel header='{{ i18n "pages.xray.directCountryConfigs"}}' v-if="enableDirect">
|
||||||
|
<a-list-item style="padding: 20px">
|
||||||
|
<a-checkbox-group
|
||||||
|
v-model="directCountries"
|
||||||
|
name="Countries"
|
||||||
|
:options="countryOptions"
|
||||||
|
/>
|
||||||
|
</a-list-item>
|
||||||
|
</a-collapse-panel>
|
||||||
</a-collapse>
|
</a-collapse>
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
</a-tabs>
|
</a-tabs>
|
||||||
|
@ -367,6 +399,40 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
defaultMux: {
|
||||||
|
enabled: true,
|
||||||
|
concurrency: 8,
|
||||||
|
xudpConcurrency: 16,
|
||||||
|
xudpProxyUDP443: "reject"
|
||||||
|
},
|
||||||
|
defaultRules: [
|
||||||
|
{
|
||||||
|
type: "field",
|
||||||
|
outboundTag: "direct",
|
||||||
|
domain: [
|
||||||
|
"geosite:category-ir",
|
||||||
|
"geosite:cn"
|
||||||
|
],
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "field",
|
||||||
|
outboundTag: "direct",
|
||||||
|
ip: [
|
||||||
|
"geoip:private",
|
||||||
|
"geoip:ir",
|
||||||
|
"geoip:cn"
|
||||||
|
],
|
||||||
|
enabled: true
|
||||||
|
},
|
||||||
|
],
|
||||||
|
countryOptions: [
|
||||||
|
{ label: 'Private IP/Domain', value: 'private' },
|
||||||
|
{ label: '🇮🇷 Iran', value: 'ir' },
|
||||||
|
{ label: '🇨🇳 China', value: 'cn' },
|
||||||
|
{ label: '🇷🇺 Russia', value: 'ru' },
|
||||||
|
{ label: '🇻🇳 Vietnam', value: 'vn' },
|
||||||
|
],
|
||||||
get remarkModel() {
|
get remarkModel() {
|
||||||
rm = this.allSetting.remarkModel;
|
rm = this.allSetting.remarkModel;
|
||||||
return rm.length>1 ? rm.substring(1).split('') : [];
|
return rm.length>1 ? rm.substring(1).split('') : [];
|
||||||
|
@ -530,6 +596,61 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
enableMux: {
|
||||||
|
get: function() { return this.allSetting?.subJsonMux != ""; },
|
||||||
|
set: function (v) {
|
||||||
|
this.allSetting.subJsonMux = v ? JSON.stringify(this.defaultMux) : "";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
muxConcurrency: {
|
||||||
|
get: function() { return this.enableMux ? JSON.parse(this.allSetting.subJsonMux).concurrency : -1; },
|
||||||
|
set: function(v) {
|
||||||
|
newMux = JSON.parse(this.allSetting.subJsonMux);
|
||||||
|
newMux.concurrency = v;
|
||||||
|
this.allSetting.subJsonMux = JSON.stringify(newMux);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
muxXudpConcurrency: {
|
||||||
|
get: function() { return this.enableMux ? JSON.parse(this.allSetting.subJsonMux).xudpConcurrency : -1; },
|
||||||
|
set: function(v) {
|
||||||
|
newMux = JSON.parse(this.allSetting.subJsonMux);
|
||||||
|
newMux.xudpConcurrency = v;
|
||||||
|
this.allSetting.subJsonMux = JSON.stringify(newMux);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
muxXudpProxyUDP443: {
|
||||||
|
get: function() { return this.enableMux ? JSON.parse(this.allSetting.subJsonMux).xudpProxyUDP443 : "reject"; },
|
||||||
|
set: function(v) {
|
||||||
|
newMux = JSON.parse(this.allSetting.subJsonMux);
|
||||||
|
newMux.xudpProxyUDP443 = v;
|
||||||
|
this.allSetting.subJsonMux = JSON.stringify(newMux);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
enableDirect: {
|
||||||
|
get: function() { return this.allSetting?.subJsonRules != ""; },
|
||||||
|
set: function (v) {
|
||||||
|
this.allSetting.subJsonRules = v ? JSON.stringify(this.defaultRules) : "";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
directCountries: {
|
||||||
|
get: function() {
|
||||||
|
if (!this.enableDirect) return [];
|
||||||
|
rules = JSON.parse(this.allSetting.subJsonRules);
|
||||||
|
return Array.isArray(rules) ? rules[1].ip.map(d => d.replace("geoip:","")) : [];
|
||||||
|
},
|
||||||
|
set: function (v) {
|
||||||
|
rules = JSON.parse(this.allSetting.subJsonRules);
|
||||||
|
if (!Array.isArray(rules)) return;
|
||||||
|
rules[0].domain = [];
|
||||||
|
rules[1].ip = [];
|
||||||
|
v.forEach(d => {
|
||||||
|
category = ["cn","private"].includes(d) ? "" : "category-";
|
||||||
|
rules[0].domain.push("geosite:"+category+d);
|
||||||
|
rules[1].ip.push("geoip:"+d);
|
||||||
|
});
|
||||||
|
this.allSetting.subJsonRules = JSON.stringify(rules);
|
||||||
|
}
|
||||||
|
},
|
||||||
confAlerts: {
|
confAlerts: {
|
||||||
get: function() {
|
get: function() {
|
||||||
if (!this.allSetting) return [];
|
if (!this.allSetting) return [];
|
||||||
|
|
|
@ -61,6 +61,8 @@ var defaultValueMap = map[string]string{
|
||||||
"subJsonPath": "/json/",
|
"subJsonPath": "/json/",
|
||||||
"subJsonURI": "",
|
"subJsonURI": "",
|
||||||
"subJsonFragment": "",
|
"subJsonFragment": "",
|
||||||
|
"subJsonMux": "",
|
||||||
|
"subJsonRules": "",
|
||||||
"datepicker": "gregorian",
|
"datepicker": "gregorian",
|
||||||
"warp": "",
|
"warp": "",
|
||||||
}
|
}
|
||||||
|
@ -437,6 +439,14 @@ func (s *SettingService) GetSubJsonFragment() (string, error) {
|
||||||
return s.getString("subJsonFragment")
|
return s.getString("subJsonFragment")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SettingService) GetSubJsonMux() (string, error) {
|
||||||
|
return s.getString("subJsonMux")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SettingService) GetSubJsonRules() (string, error) {
|
||||||
|
return s.getString("subJsonRules")
|
||||||
|
}
|
||||||
|
|
||||||
func (s *SettingService) GetDatepicker() (string, error) {
|
func (s *SettingService) GetDatepicker() (string, error) {
|
||||||
return s.getString("datepicker")
|
return s.getString("datepicker")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue