This commit is contained in:
Ali Rahimi 2023-12-26 15:08:30 +00:00 committed by GitHub
commit cdc9231929
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 1521 additions and 162 deletions

View file

@ -20,6 +20,7 @@ type SubService struct {
address string address string
showInfo bool showInfo bool
remarkModel string remarkModel string
datepicker string
inboundService service.InboundService inboundService service.InboundService
settingService service.SettingService settingService service.SettingService
} }
@ -39,6 +40,10 @@ func (s *SubService) GetSubs(subId string, host string, showInfo bool) ([]string
if err != nil { if err != nil {
s.remarkModel = "-ieo" s.remarkModel = "-ieo"
} }
s.datepicker, err = s.settingService.GetDatepicker()
if err != nil {
s.datepicker = "gregorian"
}
for _, inbound := range inbounds { for _, inbound := range inbounds {
clients, err := s.inboundService.GetClients(inbound) clients, err := s.inboundService.GetClients(inbound)
if err != nil { if err != nil {

View file

@ -12,6 +12,7 @@ class AllSetting {
this.expireDiff = ""; this.expireDiff = "";
this.trafficDiff = ""; this.trafficDiff = "";
this.remarkModel = "-ieo"; this.remarkModel = "-ieo";
this.datepicker = "gregorian";
this.tgBotEnable = false; this.tgBotEnable = false;
this.tgBotToken = ""; this.tgBotToken = "";
this.tgBotChatId = ""; this.tgBotChatId = "";

1252
web/assets/moment/moment-jalali.min.js vendored Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -47,6 +47,7 @@ type AllSetting struct {
SubEncrypt bool `json:"subEncrypt" form:"subEncrypt"` SubEncrypt bool `json:"subEncrypt" form:"subEncrypt"`
SubShowInfo bool `json:"subShowInfo" form:"subShowInfo"` SubShowInfo bool `json:"subShowInfo" form:"subShowInfo"`
SubURI string `json:"subURI" form:"subURI"` SubURI string `json:"subURI" form:"subURI"`
Datepicker string `json:"datepicker" form:"datepicker"`
} }
func (s *AllSetting) CheckValid() error { func (s *AllSetting) CheckValid() error {

View file

@ -104,8 +104,10 @@
<a-icon type="question-circle"></a-icon> <a-icon type="question-circle"></a-icon>
</a-tooltip> </a-tooltip>
</template> </template>
<a-date-picker :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss" <a-date-picker v-if="datepicker == 'gregorian'" :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
:dropdown-class-name="themeSwitcher.currentTheme" v-model="clientsBulkModal.expiryTime"></a-date-picker> :dropdown-class-name="themeSwitcher.currentTheme" v-model="clientsBulkModal.expiryTime"></a-date-picker>
<persian-datepicker v-else :dropdown-class-name="themeSwitcher.currentTheme"
value="clientsBulkModal.expiryTime" v-model="clientsBulkModal.expiryTime"></persian-datepicker>
</a-form-item> </a-form-item>
<a-form-item v-if="clientsBulkModal.expiryTime != 0"> <a-form-item v-if="clientsBulkModal.expiryTime != 0">
<template slot="label"> <template slot="label">
@ -234,6 +236,9 @@
get delayedExpireDays() { get delayedExpireDays() {
return this.clientsBulkModal.expiryTime < 0 ? this.clientsBulkModal.expiryTime / -86400000 : 0; return this.clientsBulkModal.expiryTime < 0 ? this.clientsBulkModal.expiryTime / -86400000 : 0;
}, },
get datepicker() {
return app.datepicker;
},
set delayedExpireDays(days) { set delayedExpireDays(days) {
this.clientsBulkModal.expiryTime = -86400000 * days; this.clientsBulkModal.expiryTime = -86400000 * days;
}, },

View file

@ -94,6 +94,9 @@
get isEdit() { get isEdit() {
return this.clientModal.isEdit; return this.clientModal.isEdit;
}, },
get datepicker() {
return app.datepicker;
},
get isTrafficExhausted() { get isTrafficExhausted() {
if (!clientStats) return false if (!clientStats) return false
if (clientStats.total <= 0) return false if (clientStats.total <= 0) return false

View file

@ -0,0 +1,59 @@
{{define "component/persianDatepickerTemplate"}}
<template>
<div>
<a-input :value="value" type="text" v-model="date" data-jdp class="persian-datepicker"
@input="$emit('input', convertToGregorian($event.target.value)); jalaliDatepicker.hide();"
placeholder="انتخاب تاریخ">
<template #addonAfter>
<a-icon type="calendar" style="font-size: 16px;"/>
</template>
</a-input>
</div>
</template>
{{end}}
{{define "component/persianDatepicker"}}
<link rel="stylesheet" href="{{ .base_path }}assets/persian-datepicker/persian-datepicker.min.css"/>
<script src="{{ .base_path }}assets/moment/moment-jalali.min.js"></script>
<script src="{{ .base_path }}assets/persian-datepicker/persian-datepicker.min.js"></script>
<script>
const persianDatepicker = {};
Vue.component('persian-datepicker', {
props: ['dropdown-class-name', 'format', 'value'],
template: `{{template "component/persianDatepickerTemplate"}}`,
data() {
return {
date: '',
persianDatepicker,
};
},
watch: {
value: function (date) {
this.date = this.convertToJalalian(date)
}
},
mounted() {
this.date = this.convertToJalalian(this.value)
this.listenToDatepicker()
},
methods: {
convertToGregorian(date) {
return date ? moment(moment(date, 'jYYYY/jMM/jDD HH:mm:ss').format('YYYY-MM-DD HH:mm:ss')) : null
},
convertToJalalian(date) {
return date && moment.isMoment(date) ? date.format('jYYYY/jMM/jDD HH:mm:ss') : null
},
listenToDatepicker() {
jalaliDatepicker.startWatch({
time: true,
hideAfterChange: true,
useDropDownYears: false,
changeMonthRotateYear: true,
});
},
}
});
</script>
{{end}}

View file

@ -150,8 +150,10 @@
<a-icon type="question-circle"></a-icon> <a-icon type="question-circle"></a-icon>
</a-tooltip> </a-tooltip>
</template> </template>
<a-date-picker :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss" <a-date-picker v-if="datepicker == 'gregorian'" :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
:dropdown-class-name="themeSwitcher.currentTheme" v-model="client._expiryTime"></a-date-picker> :dropdown-class-name="themeSwitcher.currentTheme" v-model="client._expiryTime"></a-date-picker>
<persian-datepicker v-else :dropdown-class-name="themeSwitcher.currentTheme"
value="client._expiryTime" v-model="client._expiryTime"></persian-datepicker>
<a-tag color="red" v-if="isEdit && isExpiry">Expired</a-tag> <a-tag color="red" v-if="isEdit && isExpiry">Expired</a-tag>
</a-form-item> </a-form-item>
<a-form-item v-if="client.expiryTime != 0"> <a-form-item v-if="client.expiryTime != 0">

View file

@ -54,9 +54,11 @@
<a-icon type="question-circle"></a-icon> <a-icon type="question-circle"></a-icon>
</a-tooltip> </a-tooltip>
</template> </template>
<a-date-picker :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss" <a-date-picker v-if="datepicker == 'gregorian'" :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
:dropdown-class-name="themeSwitcher.currentTheme" :dropdown-class-name="themeSwitcher.currentTheme"
v-model="dbInbound._expiryTime"></a-date-picker> v-model="dbInbound._expiryTime"></a-date-picker>
<persian-datepicker v-else :dropdown-class-name="themeSwitcher.currentTheme"
value="dbInbound._expiryTime" v-model="dbInbound._expiryTime"></persian-datepicker>
</a-form-item> </a-form-item>
</a-form> </a-form>

View file

@ -63,6 +63,9 @@
get client() { get client() {
return inModal.inbound.clients[0]; return inModal.inbound.clients[0];
}, },
get datepicker() {
return app.datepicker;
},
get delayedExpireDays() { get delayedExpireDays() {
return this.client && this.client.expiryTime < 0 ? this.client.expiryTime / -86400000 : 0; return this.client && this.client.expiryTime < 0 ? this.client.expiryTime / -86400000 : 0;
}, },

View file

@ -446,6 +446,7 @@
<script src="{{ .base_path }}assets/js/model/xray.js?{{ .cur_ver }}"></script> <script src="{{ .base_path }}assets/js/model/xray.js?{{ .cur_ver }}"></script>
<script src="{{ .base_path }}assets/js/model/dbinbound.js?{{ .cur_ver }}"></script> <script src="{{ .base_path }}assets/js/model/dbinbound.js?{{ .cur_ver }}"></script>
{{template "component/themeSwitcher" .}} {{template "component/themeSwitcher" .}}
{{template "component/persianDatepicker" .}}
<script> <script>
const columns = [{ const columns = [{
title: "ID", title: "ID",
@ -539,6 +540,7 @@
data: { data: {
siderDrawer, siderDrawer,
themeSwitcher, themeSwitcher,
persianDatepicker,
spinning: false, spinning: false,
inbounds: [], inbounds: [],
dbInbounds: [], dbInbounds: [],
@ -560,6 +562,7 @@
subURI : '' subURI : ''
}, },
remarkModel: '-ieo', remarkModel: '-ieo',
datepicker: 'gregorian',
tgBotEnable: false, tgBotEnable: false,
pageSize: 0, pageSize: 0,
isMobile: window.innerWidth <= 768, isMobile: window.innerWidth <= 768,
@ -605,6 +608,7 @@
}; };
this.pageSize = pageSize; this.pageSize = pageSize;
this.remarkModel = remarkModel; this.remarkModel = remarkModel;
this.datepicker = datepicker;
} }
}, },
setInbounds(dbInbounds) { setInbounds(dbInbounds) {

View file

@ -138,6 +138,27 @@
<setting-list-item type="number" title='{{ i18n "pages.settings.expireTimeDiff" }}' desc='{{ i18n "pages.settings.expireTimeDiffDesc" }}' v-model="allSetting.expireDiff" :min="0"></setting-list-item> <setting-list-item type="number" title='{{ i18n "pages.settings.expireTimeDiff" }}' desc='{{ i18n "pages.settings.expireTimeDiffDesc" }}' v-model="allSetting.expireDiff" :min="0"></setting-list-item>
<setting-list-item type="number" title='{{ i18n "pages.settings.trafficDiff" }}' desc='{{ i18n "pages.settings.trafficDiffDesc" }}' v-model="allSetting.trafficDiff" :min="0"></setting-list-item> <setting-list-item type="number" title='{{ i18n "pages.settings.trafficDiff" }}' desc='{{ i18n "pages.settings.trafficDiffDesc" }}' v-model="allSetting.trafficDiff" :min="0"></setting-list-item>
<setting-list-item type="text" title='{{ i18n "pages.settings.timeZone"}}' desc='{{ i18n "pages.settings.timeZoneDesc"}}' v-model="allSetting.timeLocation"></setting-list-item> <setting-list-item type="text" title='{{ i18n "pages.settings.timeZone"}}' desc='{{ i18n "pages.settings.timeZoneDesc"}}' v-model="allSetting.timeLocation"></setting-list-item>
<a-list-item>
<a-row style="padding: 20px">
<a-col :lg="24" :xl="12">
<a-list-item-meta title='{{ i18n "pages.settings.datepicker"}}'>
<template slot="description">{{ i18n "pages.settings.datepickerDescription"}}</template>
</a-list-item-meta>
</a-col>
<a-col :lg="24" :xl="12">
<template>
<a-select style="width: 100%"
:dropdown-class-name="themeSwitcher.currentTheme"
v-model="datepicker">
<a-select-option v-for="item in datepickerList" :value="item.value">
<span v-text="item.name"></span>
</a-select-option>
</a-select>
</template>
</a-col>
</a-row>
</a-list-item>
<a-list-item> <a-list-item>
<a-row style="padding: 20px"> <a-row style="padding: 20px">
<a-col :lg="24" :xl="12"> <a-col :lg="24" :xl="12">
@ -197,7 +218,7 @@
<td> <td>
<a-form-item> <a-form-item>
<password-input v-model="user.newPassword" style="width: 200px"></password-input> <password-input v-model="user.newPassword" style="width: 200px"></password-input>
</a-form-item> </a-form-item>
</td> </td>
</tr> </tr>
</table> </table>
@ -311,6 +332,7 @@
showAlert: false, showAlert: false,
remarkModels: {i:'Inbound',e:'Email',o:'Other'}, remarkModels: {i:'Inbound',e:'Email',o:'Other'},
remarkSeparators: [' ','-','_','@',':','~','|',',','.','/'], remarkSeparators: [' ','-','_','@',':','~','|',',','.','/'],
datepickerList: [{name:'Gregorian (Standard)', value: 'gregorian'}, {name:'Jalalian (شمسی)', value: 'jalalian'}],
remarkSample: '', remarkSample: '',
get remarkModel() { get remarkModel() {
rm = this.allSetting.remarkModel; rm = this.allSetting.remarkModel;
@ -328,11 +350,17 @@
this.allSetting.remarkModel = value + this.allSetting.remarkModel.substring(1); this.allSetting.remarkModel = value + this.allSetting.remarkModel.substring(1);
this.changeRemarkSample(); this.changeRemarkSample();
}, },
get datepicker() {
return this.allSetting.datepicker ? this.allSetting.datepicker : 'gregorian';
},
set datepicker(value) {
this.allSetting.datepicker = value;
},
changeRemarkSample(){ changeRemarkSample(){
sample = [] sample = []
this.remarkModel.forEach(r => sample.push(this.remarkModels[r])); this.remarkModel.forEach(r => sample.push(this.remarkModels[r]));
this.remarkSample = sample.length == 0 ? '' : sample.join(this.remarkSeparator); this.remarkSample = sample.length == 0 ? '' : sample.join(this.remarkSeparator);
} }
}, },
methods: { methods: {
loading(spinning = true) { loading(spinning = true) {

View file

@ -70,7 +70,7 @@
<a-button type="primary" :disabled="saveBtnDisable" @click="updateXraySetting">{{ i18n "pages.xray.save" }}</a-button> <a-button type="primary" :disabled="saveBtnDisable" @click="updateXraySetting">{{ i18n "pages.xray.save" }}</a-button>
<a-button type="danger" :disabled="!saveBtnDisable" @click="restartXray">{{ i18n "pages.xray.restart" }}</a-button> <a-button type="danger" :disabled="!saveBtnDisable" @click="restartXray">{{ i18n "pages.xray.restart" }}</a-button>
<a-popover v-if="restartResult" <a-popover v-if="restartResult"
:overlay-class-name="themeSwitcher.currentTheme"> :overlay-class-name="themeSwitcher.currentTheme">
<span slot="title" style="font-size: 12pt">Error in running xray-core</span> <span slot="title" style="font-size: 12pt">Error in running xray-core</span>
<template slot="content"> <template slot="content">
<p style="max-width: 400px" v-for="line in restartResult.split('\n')">[[ line ]]</p> <p style="max-width: 400px" v-for="line in restartResult.split('\n')">[[ line ]]</p>
@ -83,15 +83,15 @@
<a-back-top :target="() => document.getElementById('content-layout')" visibility-height="200"> <a-back-top :target="() => document.getElementById('content-layout')" visibility-height="200">
</a-back-top> </a-back-top>
<a-alert type="warning" style="float: right; width: fit-content" <a-alert type="warning" style="float: right; width: fit-content"
message='{{ i18n "pages.settings.infoDesc" }}' message='{{ i18n "pages.settings.infoDesc" }}'
show-icon show-icon
> >
</a-col> </a-col>
</a-row> </a-row>
</a-card> </a-card>
<a-tabs class="ant-card-dark-box-nohover" default-active-key="tpl-1" <a-tabs class="ant-card-dark-box-nohover" default-active-key="tpl-1"
@change="(activeKey) => { if(activeKey == 'tpl-4') this.changeCode(); }" @change="(activeKey) => { if(activeKey == 'tpl-4') this.changeCode(); }"
:class="themeSwitcher.currentTheme"> :class="themeSwitcher.currentTheme">
<a-tab-pane key="tpl-1" tab='{{ i18n "pages.xray.basicTemplate"}}'> <a-tab-pane key="tpl-1" tab='{{ i18n "pages.xray.basicTemplate"}}'>
<a-space direction="horizontal" style="padding: 20px 20px"> <a-space direction="horizontal" style="padding: 20px 20px">
<a-button type="primary" @click="resetXrayConfigToDefault">{{ i18n "pages.settings.resetDefaultConfig" }}</a-button> <a-button type="primary" @click="resetXrayConfigToDefault">{{ i18n "pages.settings.resetDefaultConfig" }}</a-button>
@ -109,16 +109,16 @@
<a-list-item> <a-list-item>
<a-row style="padding: 20px"> <a-row style="padding: 20px">
<a-col :lg="24" :xl="12"> <a-col :lg="24" :xl="12">
<a-list-item-meta <a-list-item-meta
title='{{ i18n "pages.xray.FreedomStrategy" }}' title='{{ i18n "pages.xray.FreedomStrategy" }}'
description='{{ i18n "pages.xray.FreedomStrategyDesc" }}'/> description='{{ i18n "pages.xray.FreedomStrategyDesc" }}'/>
</a-col> </a-col>
<a-col :lg="24" :xl="12"> <a-col :lg="24" :xl="12">
<template> <template>
<a-select <a-select
v-model="freedomStrategy" v-model="freedomStrategy"
:dropdown-class-name="themeSwitcher.currentTheme" :dropdown-class-name="themeSwitcher.currentTheme"
style="width: 100%"> style="width: 100%">
<a-select-option v-for="s in outboundDomainStrategies" :value="s">[[ s ]]</a-select-option> <a-select-option v-for="s in outboundDomainStrategies" :value="s">[[ s ]]</a-select-option>
</a-select> </a-select>
</template> </template>
@ -127,22 +127,22 @@
</a-list-item> </a-list-item>
<a-row style="padding: 20px"> <a-row style="padding: 20px">
<a-col :lg="24" :xl="12"> <a-col :lg="24" :xl="12">
<a-list-item-meta <a-list-item-meta
title='{{ i18n "pages.xray.RoutingStrategy" }}' title='{{ i18n "pages.xray.RoutingStrategy" }}'
description='{{ i18n "pages.xray.RoutingStrategyDesc" }}'/> description='{{ i18n "pages.xray.RoutingStrategyDesc" }}'/>
</a-col> </a-col>
<a-col :lg="24" :xl="12"> <a-col :lg="24" :xl="12">
<template> <template>
<a-select <a-select
v-model="routingStrategy" v-model="routingStrategy"
:dropdown-class-name="themeSwitcher.currentTheme" :dropdown-class-name="themeSwitcher.currentTheme"
style="width: 100%"> style="width: 100%">
<a-select-option v-for="s in routingDomainStrategies" :value="s">[[ s ]]</a-select-option> <a-select-option v-for="s in routingDomainStrategies" :value="s">[[ s ]]</a-select-option>
</a-select> </a-select>
</template> </template>
</a-col> </a-col>
</a-row> </a-row>
</a-list-item> </a-list-item>
</a-collapse-panel> </a-collapse-panel>
<a-collapse-panel header='{{ i18n "pages.xray.blockConfigs"}}'> <a-collapse-panel header='{{ i18n "pages.xray.blockConfigs"}}'>
<a-row :xs="24" :sm="24" :lg="12"> <a-row :xs="24" :sm="24" :lg="12">
@ -175,7 +175,7 @@
<setting-list-item type="switch" title='{{ i18n "pages.xray.ChinaDomain"}}' desc='{{ i18n "pages.xray.ChinaDomainDesc"}}' v-model="ChinaDomainSettings"></setting-list-item> <setting-list-item type="switch" title='{{ i18n "pages.xray.ChinaDomain"}}' desc='{{ i18n "pages.xray.ChinaDomainDesc"}}' v-model="ChinaDomainSettings"></setting-list-item>
<setting-list-item type="switch" title='{{ i18n "pages.xray.RussiaIp"}}' desc='{{ i18n "pages.xray.RussiaIpDesc"}}' v-model="RussiaIpSettings"></setting-list-item> <setting-list-item type="switch" title='{{ i18n "pages.xray.RussiaIp"}}' desc='{{ i18n "pages.xray.RussiaIpDesc"}}' v-model="RussiaIpSettings"></setting-list-item>
<setting-list-item type="switch" title='{{ i18n "pages.xray.RussiaDomain"}}' desc='{{ i18n "pages.xray.RussiaDomainDesc"}}' v-model="RussiaDomainSettings"></setting-list-item> <setting-list-item type="switch" title='{{ i18n "pages.xray.RussiaDomain"}}' desc='{{ i18n "pages.xray.RussiaDomainDesc"}}' v-model="RussiaDomainSettings"></setting-list-item>
<setting-list-item type="switch" title='{{ i18n "pages.xray.VNIp"}}' desc='{{ i18n "pages.xray.VNIpDesc"}}' v-model="VNIpSettings"></setting-list-item> <setting-list-item type="switch" title='{{ i18n "pages.xray.VNIp"}}' desc='{{ i18n "pages.xray.VNIpDesc"}}' v-model="VNIpSettings"></setting-list-item>
<setting-list-item type="switch" title='{{ i18n "pages.xray.VNDomain"}}' desc='{{ i18n "pages.xray.VNDomainDesc"}}' v-model="VNDomainSettings"></setting-list-item> <setting-list-item type="switch" title='{{ i18n "pages.xray.VNDomain"}}' desc='{{ i18n "pages.xray.VNDomainDesc"}}' v-model="VNDomainSettings"></setting-list-item>
</a-collapse-panel> </a-collapse-panel>
<a-collapse-panel header='{{ i18n "pages.xray.directCountryConfigs"}}'> <a-collapse-panel header='{{ i18n "pages.xray.directCountryConfigs"}}'>
@ -193,8 +193,8 @@
<setting-list-item type="switch" title='{{ i18n "pages.xray.DirectChinaDomain"}}' desc='{{ i18n "pages.xray.DirectChinaDomainDesc"}}' v-model="ChinaDomainDirectSettings"></setting-list-item> <setting-list-item type="switch" title='{{ i18n "pages.xray.DirectChinaDomain"}}' desc='{{ i18n "pages.xray.DirectChinaDomainDesc"}}' v-model="ChinaDomainDirectSettings"></setting-list-item>
<setting-list-item type="switch" title='{{ i18n "pages.xray.DirectRussiaIp"}}' desc='{{ i18n "pages.xray.DirectRussiaIpDesc"}}' v-model="RussiaIpDirectSettings"></setting-list-item> <setting-list-item type="switch" title='{{ i18n "pages.xray.DirectRussiaIp"}}' desc='{{ i18n "pages.xray.DirectRussiaIpDesc"}}' v-model="RussiaIpDirectSettings"></setting-list-item>
<setting-list-item type="switch" title='{{ i18n "pages.xray.DirectRussiaDomain"}}' desc='{{ i18n "pages.xray.DirectRussiaDomainDesc"}}' v-model="RussiaDomainDirectSettings"></setting-list-item> <setting-list-item type="switch" title='{{ i18n "pages.xray.DirectRussiaDomain"}}' desc='{{ i18n "pages.xray.DirectRussiaDomainDesc"}}' v-model="RussiaDomainDirectSettings"></setting-list-item>
<setting-list-item type="switch" title='{{ i18n "pages.xray.DirectVNIp"}}' desc='{{ i18n "pages.xray.DirectVNIpDesc"}}' v-model="VNIpDirectSettings"></setting-list-item> <setting-list-item type="switch" title='{{ i18n "pages.xray.DirectVNIp"}}' desc='{{ i18n "pages.xray.DirectVNIpDesc"}}' v-model="VNIpDirectSettings"></setting-list-item>
<setting-list-item type="switch" title='{{ i18n "pages.xray.DirectVNDomain"}}' desc='{{ i18n "pages.xray.DirectVNDomainDesc"}}' v-model="VNDomainDirectSettings"></setting-list-item> <setting-list-item type="switch" title='{{ i18n "pages.xray.DirectVNDomain"}}' desc='{{ i18n "pages.xray.DirectVNDomainDesc"}}' v-model="VNDomainDirectSettings"></setting-list-item>
</a-collapse-panel> </a-collapse-panel>
<a-collapse-panel header='{{ i18n "pages.xray.ipv4Configs"}}'> <a-collapse-panel header='{{ i18n "pages.xray.ipv4Configs"}}'>
<a-row :xs="24" :sm="24" :lg="12"> <a-row :xs="24" :sm="24" :lg="12">
@ -226,15 +226,15 @@
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="tpl-2" tab='{{ i18n "pages.xray.Routings"}}' style="padding-top: 20px;"> <a-tab-pane key="tpl-2" tab='{{ i18n "pages.xray.Routings"}}' style="padding-top: 20px;">
<a-alert type="warning" style="margin-bottom: 10px; width: fit-content" <a-alert type="warning" style="margin-bottom: 10px; width: fit-content"
message='{{ i18n "pages.xray.RoutingsDesc"}}' show-icon></a-alert> message='{{ i18n "pages.xray.RoutingsDesc"}}' show-icon></a-alert>
<a-button type="primary" icon="plus" @click="addRule">{{ i18n "pages.xray.rules.add" }}</a-button> <a-button type="primary" icon="plus" @click="addRule">{{ i18n "pages.xray.rules.add" }}</a-button>
<a-table :columns="isMobile ? rulesMobileColumns : rulesColumns" bordered <a-table :columns="isMobile ? rulesMobileColumns : rulesColumns" bordered
:row-key="r => r.key" :row-key="r => r.key"
:data-source="routingRuleData" :data-source="routingRuleData"
:scroll="isMobile ? {} : { x: 1000 }" :scroll="isMobile ? {} : { x: 1000 }"
:pagination="false" :pagination="false"
:indent-size="0" :indent-size="0"
:style="isMobile ? 'padding: 5px 0' : 'margin-top: 10px;'"> :style="isMobile ? 'padding: 5px 0' : 'margin-top: 10px;'">
<template slot="action" slot-scope="text, rule, index"> <template slot="action" slot-scope="text, rule, index">
[[ index+1 ]] [[ index+1 ]]
<a-dropdown :trigger="['click']"> <a-dropdown :trigger="['click']">
@ -287,8 +287,8 @@
</template> </template>
<template slot="info" slot-scope="text, rule, index"> <template slot="info" slot-scope="text, rule, index">
<a-popover placement="bottomRight" <a-popover placement="bottomRight"
v-if="(rule.source+rule.sourcePort+rule.network+rule.protocol+rule.attrs+rule.ip+rule.domain+rule.port).length>0" v-if="(rule.source+rule.sourcePort+rule.network+rule.protocol+rule.attrs+rule.ip+rule.domain+rule.port).length>0"
:overlay-class-name="themeSwitcher.currentTheme" trigger="click"> :overlay-class-name="themeSwitcher.currentTheme" trigger="click">
<template slot="content"> <template slot="content">
<table cellpadding="2" style="max-width: 300px;"> <table cellpadding="2" style="max-width: 300px;">
<tr v-if="rule.source"> <tr v-if="rule.source">
@ -339,12 +339,12 @@
<a-col :sm="24" :md="12"> <a-col :sm="24" :md="12">
<p style="margin: 10px;">{{ i18n "pages.xray.Outbounds"}}</p> <p style="margin: 10px;">{{ i18n "pages.xray.Outbounds"}}</p>
<a-table :columns="outboundColumns" bordered <a-table :columns="outboundColumns" bordered
:row-key="r => r.key" :row-key="r => r.key"
:data-source="outboundData" :data-source="outboundData"
:scroll="isMobile ? {} : { x: 200 }" :scroll="isMobile ? {} : { x: 200 }"
:pagination="false" :pagination="false"
:indent-size="0" :indent-size="0"
:style="isMobile ? 'padding: 5px 5px' : 'margin-right: 1px;'"> :style="isMobile ? 'padding: 5px 5px' : 'margin-right: 1px;'">
<template slot="action" slot-scope="text, outbound, index"> <template slot="action" slot-scope="text, outbound, index">
[[ index+1 ]] [[ index+1 ]]
<a-dropdown :trigger="['click']"> <a-dropdown :trigger="['click']">
@ -378,12 +378,12 @@
<a-col :sm="24" :md="12" v-if="reverseData.length>0"> <a-col :sm="24" :md="12" v-if="reverseData.length>0">
<p style="margin: 10px;">{{ i18n "pages.xray.outbound.reverse"}}</p> <p style="margin: 10px;">{{ i18n "pages.xray.outbound.reverse"}}</p>
<a-table :columns="reverseColumns" bordered <a-table :columns="reverseColumns" bordered
:row-key="r => r.key" :row-key="r => r.key"
:data-source="reverseData" :data-source="reverseData"
:scroll="isMobile ? {} : { x: 200 }" :scroll="isMobile ? {} : { x: 200 }"
:pagination="false" :pagination="false"
:indent-size="0" :indent-size="0"
:style="isMobile ? 'padding: 5px 0' : 'margin-left: 1px;'"> :style="isMobile ? 'padding: 5px 0' : 'margin-left: 1px;'">
<template slot="action" slot-scope="text, reverse, index"> <template slot="action" slot-scope="text, reverse, index">
[[ index+1 ]] [[ index+1 ]]
<a-dropdown :trigger="['click']"> <a-dropdown :trigger="['click']">
@ -428,22 +428,22 @@
{{template "outModal"}} {{template "outModal"}}
{{template "reverseModal"}} {{template "reverseModal"}}
<script> <script>
const rulesColumns = [ const rulesColumns = [
{ title: "#", align: 'center', width: 15, scopedSlots: { customRender: 'action' } }, { title: "#", align: 'center', width: 15, scopedSlots: { customRender: 'action' } },
{ title: '{{ i18n "pages.xray.rules.source"}}', children: [ { title: '{{ i18n "pages.xray.rules.source"}}', children: [
{ title: 'IP', dataIndex: "source", align: 'center', width: 20, ellipsis: true }, { title: 'IP', dataIndex: "source", align: 'center', width: 20, ellipsis: true },
{ title: 'port', dataIndex: 'sourcePort', align: 'center', width: 10, ellipsis: true } ]}, { title: 'port', dataIndex: 'sourcePort', align: 'center', width: 10, ellipsis: true } ]},
{ title: '{{ i18n "pages.inbounds.network"}}', children: [ { title: '{{ i18n "pages.inbounds.network"}}', children: [
{ title: 'L4', dataIndex: 'network', align: 'center', width: 10 }, { title: 'L4', dataIndex: 'network', align: 'center', width: 10 },
{ title: 'Protocol', dataIndex: 'protocol', align: 'center', width: 10, ellipsis: true }, { title: 'Protocol', dataIndex: 'protocol', align: 'center', width: 10, ellipsis: true },
{ title: 'Attrs', dataIndex: 'attrs', align: 'center', width: 20, ellipsis: true } ]}, { title: 'Attrs', dataIndex: 'attrs', align: 'center', width: 20, ellipsis: true } ]},
{ title: '{{ i18n "pages.xray.rules.dest"}}', children: [ { title: '{{ i18n "pages.xray.rules.dest"}}', children: [
{ title: 'IP', dataIndex: 'ip', align: 'center', width: 20, ellipsis: true }, { title: 'IP', dataIndex: 'ip', align: 'center', width: 20, ellipsis: true },
{ title: 'Domain', dataIndex: 'domain', align: 'center', width: 20, ellipsis: true }, { title: 'Domain', dataIndex: 'domain', align: 'center', width: 20, ellipsis: true },
{ title: 'Port', dataIndex: 'port', align: 'center', width: 10, ellipsis: true }]}, { title: 'Port', dataIndex: 'port', align: 'center', width: 10, ellipsis: true }]},
{ title: '{{ i18n "pages.xray.rules.inbound"}}', children: [ { title: '{{ i18n "pages.xray.rules.inbound"}}', children: [
{ title: 'Inbound Tag', dataIndex: 'inboundTag', align: 'center', width: 20, ellipsis: true }, { title: 'Inbound Tag', dataIndex: 'inboundTag', align: 'center', width: 20, ellipsis: true },
{ title: 'Client Email', dataIndex: 'user', align: 'center', width: 20, ellipsis: true }]}, { title: 'Client Email', dataIndex: 'user', align: 'center', width: 20, ellipsis: true }]},
{ title: '{{ i18n "pages.xray.rules.outbound"}}', dataIndex: 'outboundTag', align: 'center', width: 20 }, { title: '{{ i18n "pages.xray.rules.outbound"}}', dataIndex: 'outboundTag', align: 'center', width: 20 },
]; ];
@ -538,7 +538,7 @@
cn: ["geoip:cn"], cn: ["geoip:cn"],
ir: ["ext:geoip_IR.dat:ir"], ir: ["ext:geoip_IR.dat:ir"],
ru: ["geoip:ru"], ru: ["geoip:ru"],
vn: ["ext:geoip_VN.dat:vn"], vn: ["ext:geoip_VN.dat:vn"],
}, },
domains: { domains: {
ads: [ ads: [
@ -568,11 +568,11 @@
"regexp:.*\\.xn--mgba3a4f16a$", // .ایران "regexp:.*\\.xn--mgba3a4f16a$", // .ایران
"ext:geosite_IR.dat:ir" "ext:geosite_IR.dat:ir"
], ],
vn: [ vn: [
"regexp:.*\\.vn$", "regexp:.*\\.vn$",
"ext:geosite_VN.dat:vn", "ext:geosite_VN.dat:vn",
"ext:geosite_VN.dat:ads" "ext:geosite_VN.dat:ads"
] ]
}, },
familyProtectDNS: { familyProtectDNS: {
"servers": [ "servers": [
@ -620,10 +620,10 @@
}, },
async getXrayResult() { async getXrayResult() {
const msg = await HttpUtil.get("/panel/xray/getXrayResult"); const msg = await HttpUtil.get("/panel/xray/getXrayResult");
if(msg.success){ if(msg.success){
this.restartResult=msg.obj; this.restartResult=msg.obj;
if(msg.obj.length > 1) Vue.prototype.$message.error(msg.obj); if(msg.obj.length > 1) Vue.prototype.$message.error(msg.obj);
} }
}, },
async fetchUserSecret() { async fetchUserSecret() {
this.loading(true); this.loading(true);
@ -661,9 +661,9 @@
}, },
async toggleToken(value) { async toggleToken(value) {
if (value) { if (value) {
await this.getNewSecret(); await this.getNewSecret();
} else { } else {
this.user.loginSecret = ""; this.user.loginSecret = "";
} }
}, },
async resetXrayConfigToDefault() { async resetXrayConfigToDefault() {
@ -902,7 +902,7 @@
newRules = newTemplateSettings.routing.rules.filter(r => r.outboundTag != oldData.tag); newRules = newTemplateSettings.routing.rules.filter(r => r.outboundTag != oldData.tag);
} }
newTemplateSettings.routing.rules = newRules; newTemplateSettings.routing.rules = newRules;
this.templateSettings = newTemplateSettings; this.templateSettings = newTemplateSettings;
}, },
addRule(){ addRule(){
@ -1321,7 +1321,7 @@
} }
} }
}, },
VNIpSettings: { VNIpSettings: {
get: function () { get: function () {
return doAllItemsExist(this.settingsData.ips.vn, this.blockedIPs); return doAllItemsExist(this.settingsData.ips.vn, this.blockedIPs);
}, },
@ -1417,7 +1417,7 @@
} }
} }
}, },
VNIpDirectSettings: { VNIpDirectSettings: {
get: function () { get: function () {
return doAllItemsExist(this.settingsData.ips.vn, this.directIPs); return doAllItemsExist(this.settingsData.ips.vn, this.directIPs);
}, },
@ -1440,7 +1440,7 @@
this.directDomains = this.directDomains.filter(data => !this.settingsData.domains.vn.includes(data)); this.directDomains = this.directDomains.filter(data => !this.settingsData.domains.vn.includes(data));
} }
} }
}, },
GoogleWARPSettings: { GoogleWARPSettings: {
get: function () { get: function () {
return doAllItemsExist(this.settingsData.domains.google, this.warpDomains); return doAllItemsExist(this.settingsData.domains.google, this.warpDomains);
@ -1493,4 +1493,4 @@
}); });
</script> </script>
</body> </body>
</html> </html>

View file

@ -4,9 +4,9 @@
:ok-text="ruleModal.okText" cancel-text='{{ i18n "close" }}' :class="themeSwitcher.currentTheme"> :ok-text="ruleModal.okText" cancel-text='{{ i18n "close" }}' :class="themeSwitcher.currentTheme">
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }"> <a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }">
<a-form-item label='Domain Matcher'> <a-form-item label='Domain Matcher'>
<a-select v-model="ruleModal.rule.domainMatcher" :dropdown-class-name="themeSwitcher.currentTheme"> <a-select v-model="ruleModal.rule.domainMatcher" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option v-for="dm in ['','hybrid','linear']" :value="dm">[[ dm ]]</a-select-option> <a-select-option v-for="dm in ['','hybrid','linear']" :value="dm">[[ dm ]]</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-form-item> <a-form-item>
<template slot="label"> <template slot="label">
@ -107,7 +107,7 @@
<a-select-option v-for="tag in ruleModal.outboundTags" :value="tag">[[ tag ]]</a-select-option> <a-select-option v-for="tag in ruleModal.outboundTags" :value="tag">[[ tag ]]</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
</table> </table>
</a-form> </a-form>
</a-modal> </a-modal>
<script> <script>
@ -214,11 +214,11 @@
for (const [key, value] of Object.entries(rule)) { for (const [key, value] of Object.entries(rule)) {
if ( if (
value !== null && value !== null &&
value !== undefined && value !== undefined &&
!(Array.isArray(value) && value.length === 0) && !(Array.isArray(value) && value.length === 0) &&
!(typeof value === 'object' && Object.keys(value).length === 0) && !(typeof value === 'object' && Object.keys(value).length === 0) &&
value !== '' value !== ''
) { ) {
newRule[key] = value; newRule[key] = value;
} }
@ -236,4 +236,4 @@
}); });
</script> </script>
{{end}} {{end}}

View file

@ -56,6 +56,7 @@ var defaultValueMap = map[string]string{
"subEncrypt": "true", "subEncrypt": "true",
"subShowInfo": "true", "subShowInfo": "true",
"subURI": "", "subURI": "",
"datepicker": "gregorian",
} }
type SettingService struct { type SettingService struct {
@ -417,6 +418,10 @@ func (s *SettingService) GetSubURI() (string, error) {
return s.getString("subURI") return s.getString("subURI")
} }
func (s *SettingService) GetDatepicker() (string, error) {
return s.getString("datepicker")
}
func (s *SettingService) GetPageSize() (int, error) { func (s *SettingService) GetPageSize() (int, error) {
return s.getInt("pageSize") return s.getInt("pageSize")
} }
@ -463,6 +468,7 @@ func (s *SettingService) GetDefaultSettings(host string) (interface{}, error) {
"subEnable": func() (interface{}, error) { return s.GetSubEnable() }, "subEnable": func() (interface{}, error) { return s.GetSubEnable() },
"subURI": func() (interface{}, error) { return s.GetSubURI() }, "subURI": func() (interface{}, error) { return s.GetSubURI() },
"remarkModel": func() (interface{}, error) { return s.GetRemarkModel() }, "remarkModel": func() (interface{}, error) { return s.GetRemarkModel() },
"datepicker": func() (interface{}, error) { return s.GetDatepicker() },
} }
result := make(map[string]interface{}) result := make(map[string]interface{})

View file

@ -246,6 +246,8 @@
"pageSize" = "Pagination Size" "pageSize" = "Pagination Size"
"pageSizeDesc" = "Define page size for inbounds table. Set 0 to disable" "pageSizeDesc" = "Define page size for inbounds table. Set 0 to disable"
"remarkModel" = "Remark Model and Seperation Charachter" "remarkModel" = "Remark Model and Seperation Charachter"
"datepicker" = "Datepicker"
"datepickerDescription" = "Selector calendar type specifies the expiration date"
"sampleRemark" = "Sample Remark" "sampleRemark" = "Sample Remark"
"oldUsername" = "Current Username" "oldUsername" = "Current Username"
"currentPassword" = "Current Password" "currentPassword" = "Current Password"
@ -549,4 +551,4 @@
"enableSuccess" = "✅ {{ .Email }} : Enabled successfully." "enableSuccess" = "✅ {{ .Email }} : Enabled successfully."
"disableSuccess" = "✅ {{ .Email }} : Disabled successfully." "disableSuccess" = "✅ {{ .Email }} : Disabled successfully."
"askToAddUserId" = "Your configuration is not found!\r\nPlease ask your Admin to use your telegram user id in your configuration(s).\r\n\r\nYour user id: <b>{{ .TgUserID }}</b>" "askToAddUserId" = "Your configuration is not found!\r\nPlease ask your Admin to use your telegram user id in your configuration(s).\r\n\r\nYour user id: <b>{{ .TgUserID }}</b>"
"askToAddUserName" = "Your configuration is not found!\r\nPlease ask your Admin to use your telegram username or user id in your configuration(s).\r\n\r\nYour username: <b>@{{ .TgUserName }}</b>\r\n\r\nYour user id: <b>{{ .TgUserID }}</b>" "askToAddUserName" = "Your configuration is not found!\r\nPlease ask your Admin to use your telegram username or user id in your configuration(s).\r\n\r\nYour username: <b>@{{ .TgUserName }}</b>\r\n\r\nYour user id: <b>{{ .TgUserID }}</b>"

View file

@ -246,6 +246,8 @@
"pageSize" = "Tamaño de paginación" "pageSize" = "Tamaño de paginación"
"pageSizeDesc" = "Defina el tamaño de página para la tabla de entradas. Establezca 0 para desactivar" "pageSizeDesc" = "Defina el tamaño de página para la tabla de entradas. Establezca 0 para desactivar"
"remarkModel" = "Modelo de observación y carácter de separación" "remarkModel" = "Modelo de observación y carácter de separación"
"datepicker" = "selector de fechas"
"datepickerDescription" = "El tipo de calendario selector especifica la fecha de vencimiento"
"sampleRemark" = "Observación de muestra" "sampleRemark" = "Observación de muestra"
"oldUsername" = "Nombre de Usuario Actual" "oldUsername" = "Nombre de Usuario Actual"
"currentPassword" = "Contraseña Actual" "currentPassword" = "Contraseña Actual"
@ -549,4 +551,4 @@
"enableSuccess" = "✅ {{ .Email }} : Habilitado exitosamente." "enableSuccess" = "✅ {{ .Email }} : Habilitado exitosamente."
"disableSuccess" = "✅ {{ .Email }} : Deshabilitado exitosamente." "disableSuccess" = "✅ {{ .Email }} : Deshabilitado exitosamente."
"askToAddUserId" = "¡No se encuentra su configuración!\r\nPor favor, pídale a su administrador que use su ID de usuario de Telegram en su(s) configuración(es).\r\n\r\nSu ID de usuario: <b>{{ .TgUserID }}</b>" "askToAddUserId" = "¡No se encuentra su configuración!\r\nPor favor, pídale a su administrador que use su ID de usuario de Telegram en su(s) configuración(es).\r\n\r\nSu ID de usuario: <b>{{ .TgUserID }}</b>"
"askToAddUserName" = "¡No se encuentra su configuración!\r\nPor favor, pídale a su administrador que use su nombre de usuario o ID de usuario de Telegram en su(s) configuración(es).\r\n\r\nSu nombre de usuario: <b>@{{ .TgUserName }}</b>\r\n\r\nSu ID de usuario: <b>{{ .TgUserID }}</b>" "askToAddUserName" = "¡No se encuentra su configuración!\r\nPor favor, pídale a su administrador que use su nombre de usuario o ID de usuario de Telegram en su(s) configuración(es).\r\n\r\nSu nombre de usuario: <b>@{{ .TgUserName }}</b>\r\n\r\nSu ID de usuario: <b>{{ .TgUserID }}</b>"

View file

@ -246,6 +246,8 @@
"pageSize" = "اندازه صفحه بندی جدول" "pageSize" = "اندازه صفحه بندی جدول"
"pageSizeDesc" = "اندازه صفحه را برای جدول سرویس ها تعریف کنید. 0: غیرفعال" "pageSizeDesc" = "اندازه صفحه را برای جدول سرویس ها تعریف کنید. 0: غیرفعال"
"remarkModel" = "نام کانفیگ و جداکننده" "remarkModel" = "نام کانفیگ و جداکننده"
"datepicker" = "انتخاب کننده تاریخ"
"datepickerDescription" = "نوع تقویم انتخاب کننده تاریخ انقضا را مشخص میکند "
"sampleRemark" = "نمونه نام" "sampleRemark" = "نمونه نام"
"oldUsername" = "نام کاربری فعلی" "oldUsername" = "نام کاربری فعلی"
"currentPassword" = "رمز عبور فعلی" "currentPassword" = "رمز عبور فعلی"
@ -549,4 +551,4 @@
"enableSuccess" = "✅ {{ .Email }} : با موفقیت فعال شد." "enableSuccess" = "✅ {{ .Email }} : با موفقیت فعال شد."
"disableSuccess" = "✅ {{ .Email }} : با موفقیت غیرفعال شد." "disableSuccess" = "✅ {{ .Email }} : با موفقیت غیرفعال شد."
"askToAddUserId" = "پیکربندی شما یافت نشد!\r\nلطفاً از مدیر خود بخواهید که شناسه کاربر تلگرام خود را در پیکربندی (های) خود استفاده کند.\r\n\r\nشناسه کاربری شما: <b>{{ .TgUserID }}</b>" "askToAddUserId" = "پیکربندی شما یافت نشد!\r\nلطفاً از مدیر خود بخواهید که شناسه کاربر تلگرام خود را در پیکربندی (های) خود استفاده کند.\r\n\r\nشناسه کاربری شما: <b>{{ .TgUserID }}</b>"
"askToAddUserName" = "پیکربندی شما یافت نشد!\r\nلطفاً از مدیر خود بخواهید که نام کاربری یا شناسه کاربر تلگرام خود را در پیکربندی (های) خود استفاده کند.\r\n\r\nنام کاربری شما: <b>@{{ .TgUserName }}</b>\r\n\r\nشناسه کاربری شما: <b>{{ .TgUserID }}</b>" "askToAddUserName" = "پیکربندی شما یافت نشد!\r\nلطفاً از مدیر خود بخواهید که نام کاربری یا شناسه کاربر تلگرام خود را در پیکربندی (های) خود استفاده کند.\r\n\r\nنام کاربری شما: <b>@{{ .TgUserName }}</b>\r\n\r\nشناسه کاربری شما: <b>{{ .TgUserID }}</b>"

View file

@ -246,6 +246,8 @@
"pageSize" = "Размер нумерации страниц" "pageSize" = "Размер нумерации страниц"
"pageSizeDesc" = "Определить размер страницы для входящей таблицы. Установите 0, чтобы отключить" "pageSizeDesc" = "Определить размер страницы для входящей таблицы. Установите 0, чтобы отключить"
"remarkModel" = "Модель примечания и символ разделения" "remarkModel" = "Модель примечания и символ разделения"
"datepicker" = "выбор даты"
"datepickerDescription" = "Тип календаря выбора указывает дату истечения срока действия."
"sampleRemark" = "Пример замечания" "sampleRemark" = "Пример замечания"
"oldUsername" = "Текущее имя пользователя" "oldUsername" = "Текущее имя пользователя"
"currentPassword" = "Текущий пароль" "currentPassword" = "Текущий пароль"
@ -549,4 +551,4 @@
"enableSuccess" = "✅ {{ .Email }}: Включено успешно." "enableSuccess" = "✅ {{ .Email }}: Включено успешно."
"disableSuccess" = "✅ {{ .Email }}: Отключено успешно." "disableSuccess" = "✅ {{ .Email }}: Отключено успешно."
"askToAddUserId" = "Ваша конфигурация не найдена!\r\nПожалуйста, попросите администратора использовать ваш идентификатор пользователя Telegram в ваших конфигурациях.\r\n\r\nВаш идентификатор пользователя: <b>{{ .TgUserID }}</b>" "askToAddUserId" = "Ваша конфигурация не найдена!\r\nПожалуйста, попросите администратора использовать ваш идентификатор пользователя Telegram в ваших конфигурациях.\r\n\r\nВаш идентификатор пользователя: <b>{{ .TgUserID }}</b>"
"askToAddUserName" = "Ваша конфигурация не найдена!\r\nПожалуйста, попросите администратора использовать ваше имя пользователя или идентификатор пользователя Telegram в ваших конфигурациях.\r\n\r\nВаше имя пользователя: <b>@{{ .TgUserName }}</b>\r\n\r\nВаш идентификатор пользователя: <b>{{ .TgUserID }}</b>" "askToAddUserName" = "Ваша конфигурация не найдена!\r\nПожалуйста, попросите администратора использовать ваше имя пользователя или идентификатор пользователя Telegram в ваших конфигурациях.\r\n\r\nВаше имя пользователя: <b>@{{ .TgUserName }}</b>\r\n\r\nВаш идентификатор пользователя: <b>{{ .TgUserID }}</b>"

View file

@ -246,6 +246,8 @@
"pageSize" = "Kích thước phân trang" "pageSize" = "Kích thước phân trang"
"pageSizeDesc" = "Xác định kích thước trang cho bảng gửi đến. Đặt 0 để tắt" "pageSizeDesc" = "Xác định kích thước trang cho bảng gửi đến. Đặt 0 để tắt"
"remarkModel" = "Ghi chú mô hình và ký tự phân tách" "remarkModel" = "Ghi chú mô hình và ký tự phân tách"
"datepicker" = "bảng chọn ngày"
"datepickerDescription" = "Loại lịch chọn chỉ định ngày hết hạn"
"sampleRemark" = "Nhận xét mẫu" "sampleRemark" = "Nhận xét mẫu"
"oldUsername" = "Tên người dùng hiện tại" "oldUsername" = "Tên người dùng hiện tại"
"currentPassword" = "Mật khẩu hiện tại" "currentPassword" = "Mật khẩu hiện tại"
@ -549,4 +551,4 @@
"enableSuccess" = "✅ {{ .Email }} : Đã Bật Thành Công." "enableSuccess" = "✅ {{ .Email }} : Đã Bật Thành Công."
"disableSuccess" = "✅ {{ .Email }} : Đã Tắt Thành Công." "disableSuccess" = "✅ {{ .Email }} : Đã Tắt Thành Công."
"askToAddUserId" = "Cấu hình của bạn không được tìm thấy!\r\nVui lòng yêu cầu Quản trị viên sử dụng ID người dùng telegram của bạn trong cấu hình của bạn.\r\n\r\nID người dùng của bạn: <b>{{ .TgUserID }}</b>" "askToAddUserId" = "Cấu hình của bạn không được tìm thấy!\r\nVui lòng yêu cầu Quản trị viên sử dụng ID người dùng telegram của bạn trong cấu hình của bạn.\r\n\r\nID người dùng của bạn: <b>{{ .TgUserID }}</b>"
"askToAddUserName" = "Cấu hình của bạn không được tìm thấy!\r\nVui lòng yêu cầu Quản trị viên sử dụng tên người dùng hoặc ID người dùng telegram của bạn trong cấu hình của bạn.\r\n\r\nTên người dùng của bạn: <b>@{{ .TgUserName }}</b>\r\n\r\nID người dùng của bạn: <b>{{ .TgUserID }}</b>" "askToAddUserName" = "Cấu hình của bạn không được tìm thấy!\r\nVui lòng yêu cầu Quản trị viên sử dụng tên người dùng hoặc ID người dùng telegram của bạn trong cấu hình của bạn.\r\n\r\nTên người dùng của bạn: <b>@{{ .TgUserName }}</b>\r\n\r\nID người dùng của bạn: <b>{{ .TgUserID }}</b>"

View file

@ -246,6 +246,8 @@
"pageSize" = "分页大小" "pageSize" = "分页大小"
"pageSizeDesc" = "定义入站表的页面大小。设置 0 表示禁用" "pageSizeDesc" = "定义入站表的页面大小。设置 0 表示禁用"
"remarkModel" = "备注模型和分隔符" "remarkModel" = "备注模型和分隔符"
"datepicker" = "日期选择器"
"datepickerDescription" = "选择器日历类型指定到期日期"
"sampleRemark" = "备注示例" "sampleRemark" = "备注示例"
"oldUsername" = "原用户名" "oldUsername" = "原用户名"
"currentPassword" = "原密码" "currentPassword" = "原密码"
@ -422,7 +424,7 @@
"loginSecurity" = "登录安全" "loginSecurity" = "登录安全"
"loginSecurityDesc" = "在用户登录页面中切换附加步骤" "loginSecurityDesc" = "在用户登录页面中切换附加步骤"
"secretToken" = "密钥" "secretToken" = "密钥"
"secretTokenDesc" = "复制此密钥并将其保存在安全的地方;没有这个你将无法登录。这也无法从 x-ui 命令工具中恢复" "secretTokenDesc" = "复制此密钥并将其保存在安全的地方;没有这个你将无法登录。这也无法从 x-ui 命令工具中恢复"
[pages.settings.toasts] [pages.settings.toasts]
"modifySettings" = "修改设置" "modifySettings" = "修改设置"
@ -549,4 +551,4 @@
"enableSuccess" = "✅ {{ .Email }}:已成功启用。" "enableSuccess" = "✅ {{ .Email }}:已成功启用。"
"disableSuccess" = "✅ {{ .Email }}:已成功禁用。" "disableSuccess" = "✅ {{ .Email }}:已成功禁用。"
"askToAddUserId" = "未找到您的配置!\r\n请向管理员询问在您的配置中使用您的 Telegram 用户ID。\r\n\r\n您的用户ID<b>{{ .TgUserID }}</b>" "askToAddUserId" = "未找到您的配置!\r\n请向管理员询问在您的配置中使用您的 Telegram 用户ID。\r\n\r\n您的用户ID<b>{{ .TgUserID }}</b>"
"askToAddUserName" = "未找到您的配置!\r\n请向管理员询问在您的配置中使用您的 Telegram 用户名或用户ID。\r\n\r\n您的用户名<b>@{{ .TgUserName }}</b>\r\n\r\n您的用户ID<b>{{ .TgUserID }}</b>" "askToAddUserName" = "未找到您的配置!\r\n请向管理员询问在您的配置中使用您的 Telegram 用户名或用户ID。\r\n\r\n您的用户名<b>@{{ .TgUserName }}</b>\r\n\r\n您的用户ID<b>{{ .TgUserID }}</b>"

118
x-ui.sh
View file

@ -54,10 +54,6 @@ elif [[ "${release}" == "debian" ]]; then
if [[ ${os_version} -lt 10 ]]; then if [[ ${os_version} -lt 10 ]]; then
echo -e "${red} Please use Debian 10 or higher ${plain}\n" && exit 1 echo -e "${red} Please use Debian 10 or higher ${plain}\n" && exit 1
fi fi
elif [[ "${release}" == "almalinux" ]]; then
if [[ ${os_version} -lt 9 ]]; then
echo -e "${red} Please use Almalinux 9 or higher ${plain}\n" && exit 1
fi
elif [[ "${release}" == "arch" ]]; then elif [[ "${release}" == "arch" ]]; then
echo "Your OS is ArchLinux" echo "Your OS is ArchLinux"
elif [[ "${release}" == "manjaro" ]]; then elif [[ "${release}" == "manjaro" ]]; then
@ -104,7 +100,7 @@ before_show_menu() {
} }
install() { install() {
bash <(curl -Ls https://raw.githubusercontent.com/MHSanaei/3x-ui/main/install.sh) bash <(curl -Ls https://raw.githubusercontent.com/alirahimi818/3x-ui/main/install.sh)
if [[ $? == 0 ]]; then if [[ $? == 0 ]]; then
if [[ $# == 0 ]]; then if [[ $# == 0 ]]; then
start start
@ -123,31 +119,13 @@ update() {
fi fi
return 0 return 0
fi fi
bash <(curl -Ls https://raw.githubusercontent.com/MHSanaei/3x-ui/main/install.sh) bash <(curl -Ls https://raw.githubusercontent.com/alirahimi818/3x-ui/main/install.sh)
if [[ $? == 0 ]]; then if [[ $? == 0 ]]; then
LOGI "Update is complete, Panel has automatically restarted " LOGI "Update is complete, Panel has automatically restarted "
exit 0 exit 0
fi fi
} }
custom_version() {
echo "Enter the panel version (like 2.0.0):"
read panel_version
if [ -z "$panel_version" ]; then
echo "Panel version cannot be empty. Exiting."
exit 1
fi
download_link="https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh"
# Use the entered panel version in the download link
install_command="bash <(curl -Ls $download_link) v$panel_version"
echo "Downloading and installing panel version $panel_version..."
eval $install_command
}
uninstall() { uninstall() {
confirm "Are you sure you want to uninstall the panel? xray will also uninstalled!" "n" confirm "Are you sure you want to uninstall the panel? xray will also uninstalled!" "n"
if [[ $? != 0 ]]; then if [[ $? != 0 ]]; then
@ -375,7 +353,7 @@ enable_bbr() {
} }
update_shell() { update_shell() {
wget -O /usr/bin/x-ui -N --no-check-certificate https://github.com/MHSanaei/3x-ui/raw/main/x-ui.sh wget -O /usr/bin/x-ui -N --no-check-certificate https://github.com/alirahimi818/3x-ui/raw/main/x-ui.sh
if [[ $? != 0 ]]; then if [[ $? != 0 ]]; then
echo "" echo ""
LOGE "Failed to download script, Please check whether the machine can connect Github" LOGE "Failed to download script, Please check whether the machine can connect Github"
@ -1057,37 +1035,36 @@ show_menu() {
${green}3X-ui Panel Management Script${plain} ${green}3X-ui Panel Management Script${plain}
${green}0.${plain} Exit Script ${green}0.${plain} Exit Script
———————————————— ————————————————
${green}1.${plain} Install ${green}1.${plain} Install x-ui
${green}2.${plain} Update ${green}2.${plain} Update x-ui
${green}3.${plain} Custom Version ${green}3.${plain} Uninstall x-ui
${green}4.${plain} Uninstall
———————————————— ————————————————
${green}5.${plain} Reset Username & Password & Secret Token ${green}4.${plain} Reset Username & Password & Secret Token
${green}6.${plain} Reset Settings ${green}5.${plain} Reset Panel Settings
${green}7.${plain} Change Port ${green}6.${plain} Change Panel Port
${green}8.${plain} View Current Settings ${green}7.${plain} View Current Panel Settings
———————————————— ————————————————
${green}9.${plain} Start ${green}8.${plain} Start x-ui
${green}10.${plain} Stop ${green}9.${plain} Stop x-ui
${green}11.${plain} Restart ${green}10.${plain} Restart x-ui
${green}12.${plain} Check Status ${green}11.${plain} Check x-ui Status
${green}13.${plain} Check Logs ${green}12.${plain} Check x-ui Logs
———————————————— ————————————————
${green}14.${plain} Enable x-ui On System Startup ${green}13.${plain} Enable x-ui On System Startup
${green}15.${plain} Disable x-ui On System Startup ${green}14.${plain} Disable x-ui On System Startup
———————————————— ————————————————
${green}16.${plain} SSL Certificate Management ${green}15.${plain} SSL Certificate Management
${green}17.${plain} Cloudflare SSL Certificate ${green}16.${plain} Cloudflare SSL Certificate
${green}18.${plain} IP Limit Management ${green}17.${plain} IP Limit Management
${green}19.${plain} WARP Management ${green}18.${plain} WARP Management
———————————————— ————————————————
${green}20.${plain} Enable BBR ${green}19.${plain} Enable BBR
${green}21.${plain} Update Geo Files ${green}20.${plain} Update Geo Files
${green}22.${plain} Active Firewall and open ports ${green}21.${plain} Active Firewall and open ports
${green}23.${plain} Speedtest by Ookla ${green}22.${plain} Speedtest by Ookla
" "
show_status show_status
echo && read -p "Please enter your selection [0-23]: " num echo && read -p "Please enter your selection [0-22]: " num
case "${num}" in case "${num}" in
0) 0)
@ -1100,70 +1077,67 @@ show_menu() {
check_install && update check_install && update
;; ;;
3) 3)
check_install && custom_version
;;
4)
check_install && uninstall check_install && uninstall
;; ;;
5) 4)
check_install && reset_user check_install && reset_user
;; ;;
6) 5)
check_install && reset_config check_install && reset_config
;; ;;
7) 6)
check_install && set_port check_install && set_port
;; ;;
8) 7)
check_install && check_config check_install && check_config
;; ;;
9) 8)
check_install && start check_install && start
;; ;;
10) 9)
check_install && stop check_install && stop
;; ;;
11) 10)
check_install && restart check_install && restart
;; ;;
12) 11)
check_install && status check_install && status
;; ;;
13) 12)
check_install && show_log check_install && show_log
;; ;;
14) 13)
check_install && enable check_install && enable
;; ;;
15) 14)
check_install && disable check_install && disable
;; ;;
16) 15)
ssl_cert_issue_main ssl_cert_issue_main
;; ;;
17) 16)
ssl_cert_issue_CF ssl_cert_issue_CF
;; ;;
18) 17)
iplimit_main iplimit_main
;; ;;
19) 18)
warp_cloudflare warp_cloudflare
;; ;;
20) 19)
enable_bbr enable_bbr
;; ;;
21) 20)
update_geo update_geo
;; ;;
22) 21)
open_ports open_ports
;; ;;
23) 22)
run_speedtest run_speedtest
;; ;;
*) *)
LOGE "Please enter the correct number [0-23]" LOGE "Please enter the correct number [0-22]"
;; ;;
esac esac
} }