diff --git a/web/html/xui/xray.html b/web/html/xui/xray.html
index e8f2830a..ef1f48dd 100644
--- a/web/html/xui/xray.html
+++ b/web/html/xui/xray.html
@@ -104,8 +104,8 @@
- { if(activeKey == 'tpl-advanced') this.changeCode(); }">
-
+ { this.changePage(activeKey); }">
+
@@ -279,9 +279,7 @@
-
-
+
{{ i18n "pages.xray.rules.add" }}
-
+
{{ i18n "pages.xray.outbound.addOutbound" }}
WARP
-
+
{{ i18n "pages.xray.outbound.addReverse" }}
-
-
+
{{ i18n "pages.xray.balancer.addBalancer"}}
- Random
- Round Robin
+ Random
+ Round Robin
+ Least Ping
+ Least Load
[[ sel ]]
-
+
+
+ Observatory
+ Burst Observatory
+
+
-
+
@@ -695,6 +704,7 @@
showAlert: false,
isMobile: window.innerWidth <= 768,
advSettings: 'xraySetting',
+ obsSettings: '',
cm: null,
cmOptions: {
lineNumbers: true,
@@ -772,6 +782,22 @@
],
"queryStrategy": "UseIP"
},
+ },
+ defaultObservatory: {
+ subjectSelector: [],
+ probeURL: "http://www.google.com/gen_204",
+ probeInterval: "10m",
+ enableConcurrency: true
+ },
+ defaultBurstObservatory: {
+ subjectSelector: [],
+ pingConfig: {
+ destination: "http://www.google.com/gen_204",
+ interval: "30m",
+ connectivity: "http://connectivitycheck.platform.hicloud.com/generate_204",
+ timeout: "10s",
+ sampling: 2
+ }
}
},
methods: {
@@ -824,6 +850,10 @@
this.saveBtnDisable = true;
}
},
+ changePage(pageKey) {
+ if(pageKey == 'tpl-advanced') this.changeCode();
+ if(pageKey == 'tpl-balancer') this.changeObsCode();
+ },
syncRulesWithOutbound(tag, setting) {
const newTemplateSettings = this.templateSettings;
const haveRules = newTemplateSettings.routing.rules.some((r) => r?.outboundTag === tag);
@@ -906,6 +936,23 @@
}
});
},
+ changeObsCode() {
+ if (this.obsSettings == ''){
+ return
+ }
+ if(this.cm != null) {
+ this.cm.toTextArea();
+ }
+ textAreaObj = document.getElementById('obsSetting');
+ textAreaObj.value = this[this.obsSettings];
+ this.cm = CodeMirror.fromTextArea(textAreaObj, this.cmOptions);
+ this.cm.on('change',editor => {
+ value = editor.getValue();
+ if(this.isJsonString(value)){
+ this[this.obsSettings] = value;
+ }
+ });
+ },
isJsonString(str) {
try {
JSON.parse(str);
@@ -1081,14 +1128,27 @@
'tag': balancer.tag,
'selector': balancer.selector
};
- if (balancer.strategy == 'roundRobin') {
+ if (balancer.strategy && balancer.strategy != 'random') {
tmpBalancer.strategy = {
'type': balancer.strategy
};
+ if (balancer.strategy == 'leastPing'){
+ if (!newTemplateSettings.observatory)
+ newTemplateSettings.observatory = this.defaultObservatory;
+ if (!newTemplateSettings.observatory.subjectSelector.includes(balancer.tag))
+ newTemplateSettings.observatory.subjectSelector.push(balancer.tag);
+ }
+ if (balancer.strategy == 'leastLoad'){
+ if (!newTemplateSettings.burstObservatory)
+ newTemplateSettings.burstObservatory = this.defaultBurstObservatory;
+ if (!newTemplateSettings.burstObservatory.subjectSelector.includes(balancer.tag))
+ newTemplateSettings.burstObservatory.subjectSelector.push(balancer.tag);
+ }
}
newTemplateSettings.routing.balancers.push(tmpBalancer);
this.templateSettings = newTemplateSettings;
balancerModal.close();
+ this.changeObsCode();
},
isEdit: false
});
@@ -1108,10 +1168,31 @@
'tag': balancer.tag,
'selector': balancer.selector
};
- if (balancer.strategy == 'roundRobin') {
+
+ // Remove old tag
+ if (newTemplateSettings.observatory){
+ newTemplateSettings.observatory.subjectSelector = newTemplateSettings.observatory.subjectSelector.filter(s => s != oldTag);
+ }
+ if (newTemplateSettings.burstObservatory){
+ newTemplateSettings.burstObservatory.subjectSelector = newTemplateSettings.burstObservatory.subjectSelector.filter(s => s != oldTag);
+ }
+
+ if (balancer.strategy && balancer.strategy != 'random') {
tmpBalancer.strategy = {
'type': balancer.strategy
};
+ if (balancer.strategy == 'leastPing'){
+ if (!newTemplateSettings.observatory)
+ newTemplateSettings.observatory = this.defaultObservatory;
+ if (!newTemplateSettings.observatory.subjectSelector.includes(balancer.tag))
+ newTemplateSettings.observatory.subjectSelector.push(balancer.tag);
+ }
+ if (balancer.strategy == 'leastLoad'){
+ if (!newTemplateSettings.burstObservatory)
+ newTemplateSettings.burstObservatory = this.defaultBurstObservatory;
+ if (!newTemplateSettings.burstObservatory.subjectSelector.includes(balancer.tag))
+ newTemplateSettings.burstObservatory.subjectSelector.push(balancer.tag);
+ }
}
newTemplateSettings.routing.balancers[index] = tmpBalancer;
@@ -1125,6 +1206,7 @@
}
this.templateSettings = newTemplateSettings;
balancerModal.close();
+ this.changeObsCode();
},
isEdit: true
});
@@ -1139,11 +1221,20 @@
let realIndex = newTemplateSettings.routing.balancers.findIndex((b) => b.tag === removedBalancer.tag);
newTemplateSettings.routing.balancers.splice(realIndex, 1);
+ // Remove tag from observatory
+ if (newTemplateSettings.observatory){
+ newTemplateSettings.observatory.subjectSelector = newTemplateSettings.observatory.subjectSelector.filter(s => s != removedBalancer.tag);
+ }
+ if (newTemplateSettings.burstObservatory){
+ newTemplateSettings.burstObservatory.subjectSelector = newTemplateSettings.burstObservatory.subjectSelector.filter(s => s != removedBalancer.tag);
+ }
+
// Update balancers property to an empty array if there are no more balancers
if (newTemplateSettings.routing.balancers.length === 0) {
delete newTemplateSettings.routing.balancers;
}
this.templateSettings = newTemplateSettings;
+ this.changeObsCode()
},
addDNSServer(){
dnsModal.show({
@@ -1377,15 +1468,10 @@
data = []
if (this.templateSettings != null && this.templateSettings.routing != null && this.templateSettings.routing.balancers != null) {
this.templateSettings.routing.balancers.forEach((o, index) => {
- let strategy = "random"
- if (o.strategy && o.strategy.type == "roundRobin") {
- strategy = o.strategy.type
- }
-
data.push({
'key': index,
'tag': o.tag ? o.tag : "",
- 'strategy': strategy,
+ 'strategy': o.strategy?.type ?? "random",
'selector': o.selector ? o.selector : []
});
});
@@ -1393,6 +1479,42 @@
return data;
}
},
+ observatory: {
+ get: function () {
+ return this.templateSettings?.observatory ? JSON.stringify(this.templateSettings.observatory, null, 2) : null;
+ },
+ set: function (newValue) {
+ newTemplateSettings = this.templateSettings;
+ newTemplateSettings.observatory = JSON.parse(newValue);
+ this.templateSettings = newTemplateSettings;
+ },
+ },
+ burstObservatory: {
+ get: function () {
+ return this.templateSettings?.burstObservatory ? JSON.stringify(this.templateSettings.burstObservatory, null, 2) : null;
+ },
+ set: function (newValue) {
+ newTemplateSettings = this.templateSettings;
+ newTemplateSettings.burstObservatory = JSON.parse(newValue);
+ this.templateSettings = newTemplateSettings;
+ },
+ },
+ observatoryEnable: {
+ get: function () { return this.templateSettings != null && this.templateSettings.observatory },
+ set: function (v) {
+ newTemplateSettings = this.templateSettings;
+ newTemplateSettings.observatory = v ? this.defaultObservatory : undefined;
+ this.templateSettings = newTemplateSettings;
+ }
+ },
+ burstObservatoryEnable: {
+ get: function () { return this.templateSettings != null && this.templateSettings.burstObservatory },
+ set: function (v) {
+ newTemplateSettings = this.templateSettings;
+ newTemplateSettings.burstObservatory = v ? this.defaultBurstObservatory : undefined;
+ this.templateSettings = newTemplateSettings;
+ }
+ },
freedomStrategy: {
get: function () {
if (!this.templateSettings) return "AsIs";
diff --git a/web/html/xui/xray_balancer_modal.html b/web/html/xui/xray_balancer_modal.html
index f9f132d9..13e7257e 100644
--- a/web/html/xui/xray_balancer_modal.html
+++ b/web/html/xui/xray_balancer_modal.html
@@ -21,6 +21,8 @@
Random
Round Robin
+ Least Load
+ Least Ping