Compare commits

..

9 Commits
1.3.0 ... 1.3.1

Author SHA1 Message Date
Alireza Ahmadi
7f20b71c45 v1.3.1 2023-05-17 10:23:59 +02:00
Alireza Ahmadi
4779b37e6e correction: tiny changes 2023-05-17 10:23:21 +02:00
Alireza Ahmadi
7a20d2c83c [feature] filter inbound clients #301 2023-05-16 23:10:39 +02:00
Alireza Ahmadi
7ead999f12 remove github link 2023-05-16 21:22:21 +02:00
Alireza Ahmadi
812c9fbe17 Change place of reset to default button 2023-05-16 20:35:03 +02:00
Alireza Ahmadi
5adbfd9528 Correction: change/remove setting data 2023-05-16 20:34:30 +02:00
Alireza Ahmadi
1b93c7c0f2 Correction: freedom strategies 2023-05-16 20:21:59 +02:00
Alireza Ahmadi
58e7f51313 correction: family Protect settings 2023-05-16 20:18:02 +02:00
Alireza Ahmadi
78e97af4db Fix setting bgcolor 2023-05-16 20:16:00 +02:00
10 changed files with 72 additions and 38 deletions

View File

@@ -1 +1 @@
1.3.0 1.3.1

View File

@@ -11,16 +11,6 @@
<a-icon type="setting"></a-icon> <a-icon type="setting"></a-icon>
<span>{{ i18n "menu.settings"}}</span> <span>{{ i18n "menu.settings"}}</span>
</a-menu-item> </a-menu-item>
<a-sub-menu>
<template slot="title">
<a-icon type="link"></a-icon>
<span>{{ i18n "menu.link"}}</span>
</template>
<a-menu-item key="https://github.com/alireza0/x-ui/">
<a-icon type="github"></a-icon>
<span>Github</span>
</a-menu-item>
</a-sub-menu>
<a-menu-item key="{{ .base_path }}logout"> <a-menu-item key="{{ .base_path }}logout">
<a-icon type="logout"></a-icon> <a-icon type="logout"></a-icon>
<span>{{ i18n "menu.logout"}}</span> <span>{{ i18n "menu.logout"}}</span>

View File

@@ -97,9 +97,9 @@
<a-radio-group v-model="cert.useFile" button-style="solid"> <a-radio-group v-model="cert.useFile" button-style="solid">
<a-radio-button :value="true">{{ i18n "pages.inbounds.certificatePath" }}</a-radio-button> <a-radio-button :value="true">{{ i18n "pages.inbounds.certificatePath" }}</a-radio-button>
<a-radio-button :value="false">{{ i18n "pages.inbounds.certificateContent" }}</a-radio-button> <a-radio-button :value="false">{{ i18n "pages.inbounds.certificateContent" }}</a-radio-button>
<a-button type="primary" size="small" @click="inbound.stream.tls.addCert()" style="margin: 0 10px">+</a-button>
<a-button v-if="inbound.stream.tls.certs.length>1" type="primary" size="small" @click="inbound.stream.tls.removeCert(index)">-</a-button>
</a-radio-group> </a-radio-group>
<a-button v-if="index === 0" type="primary" size="small" @click="inbound.stream.tls.addCert()" style="margin-left: 10px">+</a-button>
<a-button v-if="inbound.stream.tls.certs.length>1" type="primary" size="small" @click="inbound.stream.tls.removeCert(index)" style="margin-left: 10px">-</a-button>
</a-form-item> </a-form-item>
</td> </td>
</tr> </tr>

View File

@@ -104,7 +104,17 @@
</a-col> </a-col>
</a-row> </a-row>
</div> </div>
<a-input v-model.lazy="searchKey" placeholder='{{ i18n "search" }}' autofocus style="max-width: 300px"></a-input> <a-input v-if="!enableFilter" v-model.lazy="searchKey" placeholder='{{ i18n "search" }}' autofocus style="max-width: 300px"></a-input>
<a-radio-group v-if="enableFilter" v-model="filterBy" @change="filterInbounds" button-style="solid">
<a-radio-button value="">{{ i18n "none" }}</a-radio-button>
<a-radio-button value="deactive">{{ i18n "disabled" }}</a-radio-button>
<a-radio-button value="depleted">{{ i18n "depleted" }}</a-radio-button>
<a-radio-button value="expiring">{{ i18n "depletingSoon" }}</a-radio-button>
</a-radio-group>
<a-switch v-model="enableFilter"
checked-children="{{ i18n "search" }}" un-checked-children="{{ i18n "filter" }}"
@change="toggleFilter">
</a-switch>
<a-table :columns="columns" :row-key="dbInbound => dbInbound.id" <a-table :columns="columns" :row-key="dbInbound => dbInbound.id"
:data-source="searchedInbounds" :data-source="searchedInbounds"
:loading="spinning" :scroll="{ x: 1300 }" :loading="spinning" :scroll="{ x: 1300 }"
@@ -319,6 +329,8 @@
inbounds: [], inbounds: [],
dbInbounds: [], dbInbounds: [],
searchKey: '', searchKey: '',
enableFilter: false,
filterBy: '',
searchedInbounds: [], searchedInbounds: [],
expireDiff: 0, expireDiff: 0,
trafficDiff: 0, trafficDiff: 0,
@@ -327,7 +339,7 @@
clientCount: {}, clientCount: {},
isRefreshEnabled: localStorage.getItem("isRefreshEnabled") === "true" ? true : false, isRefreshEnabled: localStorage.getItem("isRefreshEnabled") === "true" ? true : false,
refreshing: false, refreshing: false,
refreshInterval: Number(localStorage.getItem("refreshInterval")) || 5000, refreshInterval: Number(localStorage.getItem("refreshInterval")) || 5000
}, },
methods: { methods: {
loading(spinning = true) { loading(spinning = true) {
@@ -362,11 +374,15 @@
to_inbound = dbInbound.toInbound() to_inbound = dbInbound.toInbound()
this.inbounds.push(to_inbound); this.inbounds.push(to_inbound);
this.dbInbounds.push(dbInbound); this.dbInbounds.push(dbInbound);
if ([Protocols.VMESS, Protocols.VLESS, Protocols.TROJAN].includes(inbound.protocol)) { if ([Protocols.VMESS, Protocols.VLESS, Protocols.TROJAN, Protocols.SHADOWSOCKS].includes(inbound.protocol)) {
this.clientCount[inbound.id] = this.getClientCounts(inbound, to_inbound); this.clientCount[inbound.id] = this.getClientCounts(inbound, to_inbound);
} }
} }
this.searchInbounds(this.searchKey); if(this.enableFilter){
this.filterInbounds();
} else {
this.searchInbounds(this.searchKey);
}
}, },
getClientCounts(dbInbound, inbound) { getClientCounts(dbInbound, inbound) {
let clientCount = 0, active = [], deactive = [], depleted = [], expiring = []; let clientCount = 0, active = [], deactive = [], depleted = [], expiring = [];
@@ -424,6 +440,38 @@
}); });
} }
}, },
filterInbounds() {
if (ObjectUtil.isEmpty(this.filterBy)) {
this.searchedInbounds = this.dbInbounds.slice();
} else {
this.searchedInbounds.splice(0, this.searchedInbounds.length);
this.dbInbounds.forEach(inbound => {
const newInbound = new DBInbound(inbound);
const inboundSettings = JSON.parse(inbound.settings);
if (this.clientCount[inbound.id] && this.clientCount[inbound.id].hasOwnProperty(this.filterBy)){
const list = this.clientCount[inbound.id][this.filterBy];
if (list.length > 0) {
const filteredSettings = { "clients": [] };
inboundSettings.clients.forEach(client => {
if (list.includes(client.email)) {
filteredSettings.clients.push(client);
}
});
newInbound.settings = Inbound.Settings.fromJson(inbound.protocol, filteredSettings);
this.searchedInbounds.push(newInbound);
}
}
});
}
},
toggleFilter(){
if(this.enableFilter) {
this.searchKey = '';
} else {
this.filterBy = '';
this.searchedInbounds = this.dbInbounds.slice();
}
},
generalActions(action) { generalActions(action) {
switch (action.key) { switch (action.key) {
case "export": case "export":

View File

@@ -19,10 +19,6 @@
.ant-list-item { .ant-list-item {
display: block; display: block;
} }
:not(.ant-card-dark)>.ant-tabs-top-bar {
background: white;
}
</style> </style>
<body> <body>
<a-layout id="app" v-cloak> <a-layout id="app" v-cloak>
@@ -35,7 +31,7 @@
<a-button type="primary" :disabled="saveBtnDisable" @click="updateAllSetting">{{ i18n "pages.settings.save" }}</a-button> <a-button type="primary" :disabled="saveBtnDisable" @click="updateAllSetting">{{ i18n "pages.settings.save" }}</a-button>
<a-button type="danger" :disabled="!saveBtnDisable" @click="restartPanel">{{ i18n "pages.settings.restartPanel" }}</a-button> <a-button type="danger" :disabled="!saveBtnDisable" @click="restartPanel">{{ i18n "pages.settings.restartPanel" }}</a-button>
</a-space> </a-space>
<a-tabs default-active-key="1" :class="themeSwitcher.darkCardClass"> <a-tabs default-active-key="1" :class="themeSwitcher.darkCardClass" :style="!themeSwitcher.isDarkTheme? 'background: white':''">
<a-tab-pane key="1" tab='{{ i18n "pages.settings.panelConfig"}}'> <a-tab-pane key="1" tab='{{ i18n "pages.settings.panelConfig"}}'>
<a-row :xs="24" :sm="24" :lg="12"> <a-row :xs="24" :sm="24" :lg="12">
<h2 style="color: inherit; font-weight: bold; font-size: 18px; padding: 20px 20px; text-align: center;"> <h2 style="color: inherit; font-weight: bold; font-size: 18px; padding: 20px 20px; text-align: center;">
@@ -217,6 +213,11 @@
<setting-list-item type="textarea" title='{{ i18n "pages.settings.templates.manualDirectIPs"}}' v-model="manualDirectIPs"></setting-list-item> <setting-list-item type="textarea" title='{{ i18n "pages.settings.templates.manualDirectIPs"}}' v-model="manualDirectIPs"></setting-list-item>
<setting-list-item type="textarea" title='{{ i18n "pages.settings.templates.manualDirectDomains"}}' v-model="manualDirectDomains"></setting-list-item> <setting-list-item type="textarea" title='{{ i18n "pages.settings.templates.manualDirectDomains"}}' v-model="manualDirectDomains"></setting-list-item>
</a-collapse-panel> </a-collapse-panel>
<a-collapse-panel header='{{ i18n "pages.settings.resetDefaultConfig"}}'>
<a-space direction="horizontal" style="padding: 0 20px">
<a-button type="primary" @click="resetXrayConfigToDefault">{{ i18n "pages.settings.resetDefaultConfig" }}</a-button>
</a-space>
</a-collapse-panel>
</a-collapse> </a-collapse>
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="tpl-2" tab='{{ i18n "pages.settings.templates.advancedTemplate"}}' style="padding-top: 20px;"> <a-tab-pane key="tpl-2" tab='{{ i18n "pages.settings.templates.advancedTemplate"}}' style="padding-top: 20px;">
@@ -233,9 +234,6 @@
</a-collapse> </a-collapse>
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="tpl-3" tab='{{ i18n "pages.settings.templates.completeTemplate"}}' style="padding-top: 20px;"> <a-tab-pane key="tpl-3" tab='{{ i18n "pages.settings.templates.completeTemplate"}}' style="padding-top: 20px;">
<a-space direction="horizontal" style="padding: 0 20px">
<a-button type="primary" @click="resetXrayConfigToDefault">{{ i18n "pages.settings.resetDefaultConfig" }}</a-button>
</a-space>
<setting-list-item type="textarea" title='{{ i18n "pages.settings.templates.xrayConfigTemplate"}}' desc='{{ i18n "pages.settings.templates.xrayConfigTemplateDesc"}}' v-model="allSetting.xrayTemplateConfig"></setting-list-item> <setting-list-item type="textarea" title='{{ i18n "pages.settings.templates.xrayConfigTemplate"}}' desc='{{ i18n "pages.settings.templates.xrayConfigTemplateDesc"}}' v-model="allSetting.xrayTemplateConfig"></setting-list-item>
</a-tab-pane> </a-tab-pane>
</a-tabs> </a-tabs>
@@ -300,7 +298,6 @@
}, },
ips: { ips: {
local: ["geoip:private"], local: ["geoip:private"],
google: ["geoip:google"],
cn: ["geoip:cn"], cn: ["geoip:cn"],
ir: ["geoip:ir"], ir: ["geoip:ir"],
ru: ["geoip:ru"], ru: ["geoip:ru"],
@@ -310,12 +307,10 @@
"geosite:category-ads-all", "geosite:category-ads-all",
"geosite:category-ads", "geosite:category-ads",
"geosite:google-ads", "geosite:google-ads",
"geosite:spotify-ads" "geosite:spotify-ads",
"ext:iran.dat:ads"
], ],
porn: ["geosite:category-porn"],
openai: ["geosite:openai"],
google: ["geosite:google"], google: ["geosite:google"],
spotify: ["geosite:spotify"],
netflix: ["geosite:netflix"], netflix: ["geosite:netflix"],
cn: [ cn: [
"geosite:cn", "geosite:cn",
@@ -329,16 +324,15 @@
"regexp:.*\\.ir$", "regexp:.*\\.ir$",
"ext:iran.dat:ir", "ext:iran.dat:ir",
"ext:iran.dat:other", "ext:iran.dat:other",
"ext:iran.dat:ads",
"geosite:category-ir" "geosite:category-ir"
] ]
}, },
familyProtectDNS: { familyProtectDNS: {
"servers": [ "servers": [
"1.1.1.2", "1.1.1.3",
"1.0.0.2", "1.0.0.3",
"94.140.14.14", "94.140.14.15",
"94.140.15.15" "94.140.15.16"
], ],
"queryStrategy": "UseIPv4" "queryStrategy": "UseIPv4"
}, },
@@ -511,7 +505,7 @@
freedomStrategy: { freedomStrategy: {
get: function () { get: function () {
if (!this.templateSettings) return "AsIs"; if (!this.templateSettings) return "AsIs";
freedomOutbound = this.templateSettings.outbounds.find((o) => o.tag === "direct"); freedomOutbound = this.templateSettings.outbounds.find((o) => o.protocol === "freedom" && !o.tag);
if (!freedomOutbound) return "AsIs"; if (!freedomOutbound) return "AsIs";
if (!freedomOutbound.settings || !freedomOutbound.settings.domainStrategy) return "AsIs"; if (!freedomOutbound.settings || !freedomOutbound.settings.domainStrategy) return "AsIs";
return freedomOutbound.settings.domainStrategy; return freedomOutbound.settings.domainStrategy;

View File

@@ -11,6 +11,7 @@
"enable" = "Enable" "enable" = "Enable"
"protocol" = "Protocol" "protocol" = "Protocol"
"search" = "Search" "search" = "Search"
"filter" = "Filter"
"loading" = "Loading" "loading" = "Loading"
"second" = "Second" "second" = "Second"

View File

@@ -11,6 +11,7 @@
"enable" = "فعال" "enable" = "فعال"
"protocol" = "پروتکل" "protocol" = "پروتکل"
"search" = "جستجو" "search" = "جستجو"
"filter" = "فیلتر"
"loading" = "در حال بروزرسانی.." "loading" = "در حال بروزرسانی.."
"second" = "ثانیه" "second" = "ثانیه"

View File

@@ -11,6 +11,7 @@
"enable" = "включить" "enable" = "включить"
"protocol" = "протокол" "protocol" = "протокол"
"search" = "поиск" "search" = "поиск"
"filter" = "Фильтр"
"loading" = "загрузка" "loading" = "загрузка"
"second" = "секунда" "second" = "секунда"

View File

@@ -11,6 +11,7 @@
"enable" = "启用" "enable" = "启用"
"protocol" = "协议" "protocol" = "协议"
"search" = "搜尋" "search" = "搜尋"
"filter" = "过滤器"
"loading" = "加载中" "loading" = "加载中"
"second" = "秒" "second" = "秒"

View File

@@ -276,8 +276,6 @@ func (s *Server) initI18n(engine *gin.Engine) error {
engine.FuncMap["i18n"] = I18n engine.FuncMap["i18n"] = I18n
engine.Use(func(c *gin.Context) { engine.Use(func(c *gin.Context) {
//accept := c.GetHeader("Accept-Language")
var lang string var lang string
if cookie, err := c.Request.Cookie("lang"); err == nil { if cookie, err := c.Request.Cookie("lang"); err == nil {