diff --git a/web/assets/js/util/common.js b/web/assets/js/util/common.js
index fb58da96..f411148b 100644
--- a/web/assets/js/util/common.js
+++ b/web/assets/js/util/common.js
@@ -104,4 +104,13 @@ function usageColor(data, threshold, total) {
default:
return 'red';
}
-}
\ No newline at end of file
+}
+
+function areAllItemsExist(array1, array2) {
+ for (let i = 0; i < array1.length; i++) {
+ if (!array2.includes(array1[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
\ No newline at end of file
diff --git a/web/html/xui/component/setting.html b/web/html/xui/component/setting.html
index 00eeb259..741c7d9f 100644
--- a/web/html/xui/component/setting.html
+++ b/web/html/xui/component/setting.html
@@ -1,6 +1,12 @@
{{define "component/settingListItem"}}
-
+
+
+
+
+
+
+
@@ -9,10 +15,7 @@
-
-
-
-
+ $emit('input', value)" :min="min" style="width: 100%;">
$emit('input', value)">
diff --git a/web/html/xui/setting.html b/web/html/xui/setting.html
index 7917f23d..4e8d720f 100644
--- a/web/html/xui/setting.html
+++ b/web/html/xui/setting.html
@@ -36,6 +36,23 @@
{{ i18n "pages.settings.restartPanel" }}
+
+
+
+
+ {{ i18n "pages.settings.infoDesc" }}
+
+
+
+
+
+
+
+
+
+
+
+
@@ -59,7 +76,6 @@
-
@@ -80,45 +96,159 @@
style="max-width: 300px">
-
{{ i18n "confirm" }}
-
-
-
-
- {{ i18n "pages.setting.advancedTemplate"}}
-
-
-
-
-
-
-
-
-
-
-
- {{ i18n "pages.setting.completeTemplate"}}
-
- {{ i18n "pages.setting.resetDefaultConfig" }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+ {{ i18n "pages.settings.infoDesc" }}
+
+
+
+
+
+
+
+
+
+ {{ i18n "pages.settings.templates.generalConfigsDesc" }}
+
+
+
+
+
+
+
+
+
+
+ [[ s ]]
+
+
+
+
+
+
+
+
+
+
+
+
+ [[ s ]]
+
+
+
+
+
+
+
+
+
+
+ {{ i18n "pages.settings.templates.blockConfigsDesc" }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ i18n "pages.settings.templates.countryConfigsDesc" }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ i18n "pages.settings.templates.ipv4ConfigsDesc" }}
+
+
+
+
+
+
+
+
+
+ {{ i18n "pages.settings.templates.manualListsDesc" }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ i18n "pages.settings.resetDefaultConfig" }}
+
+
+
+
+
+
+
+
+
+
+ {{ i18n "pages.settings.infoDesc" }}
+
+
+
+
+
+
+
+
+
@@ -143,11 +273,73 @@
allSetting: new AllSetting(),
saveBtnDisable: true,
user: {},
- lang : getLang()
+ lang : getLang(),
+ ipv4Settings: {
+ tag: "IPv4",
+ protocol: "freedom",
+ settings: {
+ domainStrategy: "UseIPv4"
+ }
+ },
+ directSettings: {
+ tag: "direct",
+ protocol: "freedom"
+ },
+ outboundDomainStrategies: ["AsIs", "UseIP", "UseIPv4", "UseIPv6"],
+ routingDomainStrategies: ["AsIs", "IPIfNonMatch", "IPOnDemand"],
+ settingsData: {
+ protocols: {
+ bittorrent: ["bittorrent"],
+ },
+ ips: {
+ local: ["geoip:private"],
+ google: ["geoip:google"],
+ cn: ["geoip:cn"],
+ ir: ["geoip:ir"],
+ ru: ["geoip:ru"],
+ },
+ domains: {
+ ads: [
+ "geosite:category-ads-all",
+ "geosite:category-ads",
+ "geosite:google-ads",
+ "geosite:spotify-ads"
+ ],
+ porn: ["geosite:category-porn"],
+ openai: ["geosite:openai"],
+ google: ["geosite:google"],
+ spotify: ["geosite:spotify"],
+ netflix: ["geosite:netflix"],
+ cn: [
+ "geosite:cn",
+ "regexp:.*\\.cn$"
+ ],
+ ru: [
+ "geosite:category-gov-ru",
+ "regexp:.*\\.ru$"
+ ],
+ ir: [
+ "regexp:.*\\.ir$",
+ "ext:iran.dat:ir",
+ "ext:iran.dat:other",
+ "ext:iran.dat:ads",
+ "geosite:category-ir"
+ ]
+ },
+ familyProtectDNS: {
+ "servers": [
+ "1.1.1.2",
+ "1.0.0.2",
+ "94.140.14.14",
+ "94.140.15.15"
+ ],
+ "queryStrategy": "UseIPv4"
+ },
+ }
},
methods: {
- loading(spinning = true) {
- this.spinning = spinning;
+ loading(spinning = true, obj) {
+ if (obj == null) this.spinning = spinning;
},
async getAllSetting() {
this.loading(true);
@@ -204,6 +396,75 @@
this.saveBtnDisable = true;
}
},
+ syncRulesWithOutbound(tag, setting) {
+ const newTemplateSettings = this.templateSettings;
+ const haveRules = newTemplateSettings.routing.rules.some((r) => r?.outboundTag === tag);
+ const outboundIndex = newTemplateSettings.outbounds.findIndex((o) => o.tag === tag);
+ if (!haveRules && outboundIndex > 0){
+ newTemplateSettings.outbounds.splice(outboundIndex);
+ }
+ if (haveRules && outboundIndex < 0) {
+
+ newTemplateSettings.outbounds.push(setting);
+ }
+ this.templateSettings = newTemplateSettings;
+ },
+ templateRuleGetter(routeSettings) {
+ const { property, outboundTag } = routeSettings;
+ let result = [];
+ if (this.templateSettings != null) {
+ this.templateSettings.routing.rules.forEach(
+ (routingRule) => {
+ if (
+ routingRule.hasOwnProperty(property) &&
+ routingRule.hasOwnProperty("outboundTag") &&
+ routingRule.outboundTag === outboundTag
+ ) {
+ result.push(...routingRule[property]);
+ }
+ }
+ );
+ }
+ return result;
+ },
+ templateRuleSetter(routeSettings) {
+ const { data, property, outboundTag } = routeSettings;
+ const oldTemplateSettings = this.templateSettings;
+ const newTemplateSettings = oldTemplateSettings;
+ currentProperty = this.templateRuleGetter({outboundTag: outboundTag, property: property})
+ if (currentProperty.length == 0) {
+ const propertyRule = {
+ type: "field",
+ outboundTag,
+ [property]: data
+ };
+ newTemplateSettings.routing.rules.push(propertyRule);
+ }
+ else {
+ const newRules = [];
+ insertedOnce = false;
+ newTemplateSettings.routing.rules.forEach(
+ (routingRule) => {
+ if (
+ routingRule.hasOwnProperty(property) &&
+ routingRule.hasOwnProperty("outboundTag") &&
+ routingRule.outboundTag === outboundTag
+ ) {
+ if (!insertedOnce && data.length>0){
+ insertedOnce = true;
+ routingRule[property] = data;
+ newRules.push(routingRule);
+ }
+ }
+ else {
+ newRules.push(routingRule);
+ }
+ }
+ );
+ newTemplateSettings.routing.rules = newRules;
+ }
+ this.templateSettings = newTemplateSettings;
+ }
},
async mounted() {
await this.getAllSetting();
@@ -214,7 +475,7 @@
},
computed: {
templateSettings: {
- get: function () { return this.allSetting.xrayTemplateConfig ? JSON.parse(this.allSetting.xrayTemplateConfig) : null ; },
+ get: function () { return this.allSetting.xrayTemplateConfig ? JSON.parse(this.allSetting.xrayTemplateConfig) : null; },
set: function (newValue) { this.allSetting.xrayTemplateConfig = JSON.stringify(newValue, null, 2) },
},
inboundSettings: {
@@ -241,75 +502,329 @@
this.templateSettings = newTemplateSettings
},
},
- torrentSettings: {
+ freedomStrategy: {
get: function () {
- torrentFilter = false
- if(this.templateSettings != null){
- this.templateSettings.routing.rules.forEach(routingRule => {
- if(routingRule.hasOwnProperty("protocol")){
- if (routingRule.protocol[0] === "bittorrent" && routingRule.outboundTag == "blocked"){
- torrentFilter = true
- }
- }
- });
- }
- return torrentFilter
+ if (!this.templateSettings) return "AsIs";
+ freedomOutbound = this.templateSettings.outbounds.find((o) => o.tag === "direct");
+ if (!freedomOutbound) return "AsIs";
+ if (!freedomOutbound.settings || !freedomOutbound.settings.domainStrategy) return "AsIs";
+ return freedomOutbound.settings.domainStrategy;
},
set: function (newValue) {
- newTemplateSettings = JSON.parse(this.allSetting.xrayTemplateConfig);
- if (newValue){
- newTemplateSettings.routing.rules.push(JSON.parse("{\"outboundTag\": \"blocked\",\"protocol\": [\"bittorrent\"],\"type\": \"field\"}"))
+ newTemplateSettings = this.templateSettings;
+ freedomOutboundIndex = newTemplateSettings.outbounds.findIndex((o) => o.protocol === "freedom" && !o.tag);
+ if (!newTemplateSettings.outbounds[freedomOutboundIndex].settings) {
+ newTemplateSettings.outbounds[freedomOutboundIndex].settings = {"domainStrategy": newValue};
+ } else {
+ newTemplateSettings.outbounds[freedomOutboundIndex].settings.domainStrategy = newValue;
}
- else {
- newTemplateSettings.routing.rules = [];
- this.templateSettings.routing.rules.forEach(routingRule => {
- if (routingRule.hasOwnProperty('protocol')){
- if (routingRule.protocol[0] === "bittorrent" && routingRule.outboundTag == "blocked"){
- return;
- }
- }
- newTemplateSettings.routing.rules.push(routingRule);
- });
+ this.templateSettings = newTemplateSettings;
+ }
+ },
+ routingStrategy: {
+ get: function () {
+ if (!this.templateSettings || !this.templateSettings.routing || !this.templateSettings.routing.domainStrategy) return "AsIs";
+ return this.templateSettings.routing.domainStrategy;
+ },
+ set: function (newValue) {
+ newTemplateSettings = this.templateSettings;
+ newTemplateSettings.routing.domainStrategy = newValue;
+ this.templateSettings = newTemplateSettings;
+ }
+ },
+ blockedIPs: {
+ get: function() {
+ return this.templateRuleGetter({outboundTag: "blocked", property: "ip"});
+ },
+ set: function(newValue) {
+ this.templateRuleSetter({outboundTag: "blocked", property: "ip", data: newValue});
+ }
+ },
+ blockedDomains: {
+ get: function() {
+ return this.templateRuleGetter({outboundTag: "blocked", property: "domain"});
+ },
+ set: function(newValue) {
+ this.templateRuleSetter({outboundTag: "blocked", property: "domain", data: newValue});
+ }
+ },
+ blockedProtocols: {
+ get: function() {
+ return this.templateRuleGetter({outboundTag: "blocked", property: "protocol"});
+ },
+ set: function(newValue) {
+ this.templateRuleSetter({outboundTag: "blocked", property: "protocol", data: newValue});
+ }
+ },
+ directIPs: {
+ get: function() {
+ return this.templateRuleGetter({outboundTag: "direct", property: "ip"});
+ },
+ set: function(newValue) {
+ this.templateRuleSetter({outboundTag: "direct", property: "ip", data: newValue});
+ this.syncRulesWithOutbound("direct",this.directSettings);
+ }
+ },
+ directDomains: {
+ get: function() {
+ return this.templateRuleGetter({outboundTag: "direct", property: "domain"});
+ },
+ set: function(newValue) {
+ this.templateRuleSetter({outboundTag: "direct", property: "domain", data: newValue});
+ this.syncRulesWithOutbound("direct",this.directSettings);
+ }
+ },
+ manualBlockedIPs: {
+ get: function() { return JSON.stringify(this.blockedIPs, null, 2); },
+ set: debounce(function(value) { this.blockedIPs = JSON.parse(value); } , 1000)
+ },
+ manualBlockedDomains: {
+ get: function() { return JSON.stringify(this.blockedDomains, null, 2); },
+ set: debounce(function(value) { this.blockedDomains = JSON.parse(value); } , 1000)
+ },
+ manualDirectIPs: {
+ get: function() { return JSON.stringify(this.directIPs, null, 2); },
+ set: debounce(function(value) { this.directIPs = JSON.parse(value); } , 1000)
+ },
+ manualDirectDomains: {
+ get: function() { return JSON.stringify(this.directDomains, null, 2); },
+ set: debounce(function(value) { this.directDomains = JSON.parse(value); } , 1000)
+ },
+ torrentSettings: {
+ get: function () {
+ return areAllItemsExist(this.settingsData.protocols.bittorrent,this.blockedProtocols);
+ },
+ set: function (newValue) {
+ if (newValue) {
+ this.blockedProtocols = [...this.blockedProtocols, ...this.settingsData.protocols.bittorrent];
+ } else {
+ this.blockedProtocols = this.blockedProtocols.filter(data => !this.settingsData.protocols.bittorrent.includes(data));
}
- this.templateSettings = newTemplateSettings
},
},
privateIpSettings: {
get: function () {
- localIpFilter = false
- if(this.templateSettings != null){
- this.templateSettings.routing.rules.forEach(routingRule => {
- if(routingRule.hasOwnProperty("ip")){
- if (routingRule.ip[0] === "geoip:private" && routingRule.outboundTag == "blocked"){
- localIpFilter = true
- }
- }
- });
- }
- return localIpFilter
+ return areAllItemsExist(this.settingsData.ips.local,this.blockedIPs);
},
set: function (newValue) {
- newTemplateSettings = JSON.parse(this.allSetting.xrayTemplateConfig);
- if (newValue){
- newTemplateSettings.routing.rules.push(JSON.parse("{\"outboundTag\": \"blocked\",\"ip\": [\"geoip:private\"],\"type\": \"field\"}"))
+ if (newValue) {
+ this.blockedIPs = [...this.blockedIPs, ...this.settingsData.ips.local];
+ } else {
+ this.blockedIPs = this.blockedIPs.filter(data => !this.settingsData.ips.local.includes(data));
}
- else {
- newTemplateSettings.routing.rules = [];
- this.templateSettings.routing.rules.forEach(routingRule => {
- if (routingRule.hasOwnProperty('ip')){
- if (routingRule.ip[0] === "geoip:private" && routingRule.outboundTag == "blocked"){
- return;
- }
- }
- newTemplateSettings.routing.rules.push(routingRule);
- });
- }
- this.templateSettings = newTemplateSettings
},
},
- }
- });
-
+ AdsSettings: {
+ get: function () {
+ return areAllItemsExist(this.settingsData.domains.ads,this.blockedDomains);
+ },
+ set: function (newValue) {
+ if (newValue) {
+ this.blockedDomains = [...this.blockedDomains, ...this.settingsData.domains.ads];
+ } else {
+ this.blockedDomains = this.blockedDomains.filter(data => !this.settingsData.domains.ads.includes(data));
+ }
+ },
+ },
+ familyProtectSettings: {
+ get: function () {
+ if (!this.templateSettings || !this.templateSettings.dns || !this.templateSettings.dns.servers) return false;
+ return areAllItemsExist(this.templateSettings.dns.servers,this.settingsData.familyProtectDNS.servers);
+ },
+ set: function (newValue) {
+ newTemplateSettings = this.templateSettings;
+ if (newValue) {
+ newTemplateSettings.dns = this.settingsData.familyProtectDNS;
+ } else {
+ delete newTemplateSettings.dns;
+ }
+ this.templateSettings = newTemplateSettings;
+ },
+ },
+ GoogleIPv4Settings: {
+ get: function () {
+ return areAllItemsExist(this.settingsData.domains.google, this.templateRuleGetter({outboundTag: "IPv4", property: "domain"}));
+ },
+ set: function (newValue) {
+ oldData = this.templateRuleGetter({outboundTag: "IPv4", property: "domain"});
+ if (newValue) {
+ oldData = [...oldData, ...this.settingsData.domains.google];
+ } else {
+ oldData = oldData.filter(data => !this.settingsData.domains.google.includes(data))
+ }
+ this.templateRuleSetter({
+ outboundTag: "IPv4",
+ property: "domain",
+ data: oldData
+ });
+ this.syncRulesWithOutbound("IPv4", this.ipv4Settings);
+ },
+ },
+ NetflixIPv4Settings: {
+ get: function () {
+ return areAllItemsExist(this.settingsData.domains.netflix, this.templateRuleGetter({outboundTag: "IPv4", property: "domain"}));
+ },
+ set: function (newValue) {
+ oldData = this.templateRuleGetter({outboundTag: "IPv4", property: "domain"});
+ if (newValue) {
+ oldData = [...oldData, ...this.settingsData.domains.netflix];
+ } else {
+ oldData = oldData.filter(data => !this.settingsData.domains.netflix.includes(data))
+ }
+ this.templateRuleSetter({
+ outboundTag: "IPv4",
+ property: "domain",
+ data: oldData
+ });
+ this.syncRulesWithOutbound("IPv4", this.ipv4Settings);
+ },
+ },
+ IRIpSettings: {
+ get: function () {
+ return areAllItemsExist(this.settingsData.ips.ir,this.blockedIPs);
+ },
+ set: function (newValue) {
+ if (newValue) {
+ this.blockedIPs = [...this.blockedIPs, ...this.settingsData.ips.ir];
+ } else {
+ this.blockedIPs = this.blockedIPs.filter(data => !this.settingsData.ips.ir.includes(data));
+ }
+ }
+ },
+ IRDomainSettings: {
+ get: function () {
+ return areAllItemsExist(this.settingsData.domains.ir,this.blockedDomains);
+ },
+ set: function (newValue) {
+ if (newValue) {
+ this.blockedDomains = [...this.blockedDomains, ...this.settingsData.domains.ir];
+ } else {
+ this.blockedDomains = this.blockedDomains.filter(data => !this.settingsData.domains.ir.includes(data));
+ }
+ }
+ },
+ ChinaIpSettings: {
+ get: function () {
+ return areAllItemsExist(this.settingsData.ips.cn,this.blockedIPs);
+ },
+ set: function (newValue) {
+ if (newValue) {
+ this.blockedIPs = [...this.blockedIPs, ...this.settingsData.ips.cn];
+ } else {
+ this.blockedIPs = this.blockedIPs.filter(data => !this.settingsData.ips.cn.includes(data));
+ }
+ }
+ },
+ ChinaDomainSettings: {
+ get: function () {
+ return areAllItemsExist(this.settingsData.domains.cn,this.blockedDomains);
+ },
+ set: function (newValue) {
+ if (newValue) {
+ this.blockedDomains = [...this.blockedDomains, ...this.settingsData.domains.cn];
+ } else {
+ this.blockedDomains = this.blockedDomains.filter(data => !this.settingsData.domains.cn.includes(data));
+ }
+ }
+ },
+ RussiaIpSettings: {
+ get: function () {
+ return areAllItemsExist(this.settingsData.ips.ru,this.blockedIPs);
+ },
+ set: function (newValue) {
+ if (newValue) {
+ this.blockedIPs = [...this.blockedIPs, ...this.settingsData.ips.ru];
+ } else {
+ this.blockedIPs = this.blockedIPs.filter(data => !this.settingsData.ips.ru.includes(data));
+ }
+ }
+ },
+ RussiaDomainSettings: {
+ get: function () {
+ return areAllItemsExist(this.settingsData.domains.ru,this.blockedDomains);
+ },
+ set: function (newValue) {
+ if (newValue) {
+ this.blockedDomains = [...this.blockedDomains, ...this.settingsData.domains.ru];
+ } else {
+ this.blockedDomains = this.blockedDomains.filter(data => !this.settingsData.domains.ru.includes(data));
+ }
+ }
+ },
+ IRIpDirectSettings: {
+ get: function () {
+ return areAllItemsExist(this.settingsData.ips.ir,this.directIPs);
+ },
+ set: function (newValue) {
+ if (newValue) {
+ this.directIPs = [...this.directIPs, ...this.settingsData.ips.ir];
+ } else {
+ this.directIPs = this.directIPs.filter(data => !this.settingsData.ips.ir.includes(data));
+ }
+ }
+ },
+ IRDomainDirectSettings: {
+ get: function () {
+ return areAllItemsExist(this.settingsData.domains.ir,this.directDomains);
+ },
+ set: function (newValue) {
+ if (newValue) {
+ this.directDomains = [...this.directDomains, ...this.settingsData.domains.ir];
+ } else {
+ this.directDomains = this.directDomains.filter(data => !this.settingsData.domains.ir.includes(data));
+ }
+ }
+ },
+ ChinaIpDirectSettings: {
+ get: function () {
+ return areAllItemsExist(this.settingsData.ips.cn,this.directIPs);
+ },
+ set: function (newValue) {
+ if (newValue) {
+ this.directIPs = [...this.directIPs, ...this.settingsData.ips.cn];
+ } else {
+ this.directIPs = this.directIPs.filter(data => !this.settingsData.ips.cn.includes(data));
+ }
+ }
+ },
+ ChinaDomainDirectSettings: {
+ get: function () {
+ return areAllItemsExist(this.settingsData.domains.cn,this.directDomains);
+ },
+ set: function (newValue) {
+ if (newValue) {
+ this.directDomains = [...this.directDomains, ...this.settingsData.domains.cn];
+ } else {
+ this.directDomains = this.directDomains.filter(data => !this.settingsData.domains.cn.includes(data));
+ }
+ }
+ },
+ RussiaIpDirectSettings: {
+ get: function () {
+ return areAllItemsExist(this.settingsData.ips.ru,this.directIPs);
+ },
+ set: function (newValue) {
+ if (newValue) {
+ this.directIPs = [...this.directIPs, ...this.settingsData.ips.ru];
+ } else {
+ this.directIPs = this.directIPs.filter(data => !this.settingsData.ips.ru.includes(data));
+ }
+ }
+ },
+ RussiaDomainDirectSettings: {
+ get: function () {
+ return areAllItemsExist(this.settingsData.domains.ru,this.directDomains);
+ },
+ set: function (newValue) {
+ if (newValue) {
+ this.directDomains = [...this.directDomains, ...this.settingsData.domains.ru];
+ } else {
+ this.directDomains = this.directDomains.filter(data => !this.settingsData.domains.ru.includes(data));
+ }
+ }
+ },
+ },
+ });