pagination and sub URI support #1300

This commit is contained in:
Alireza Ahmadi 2023-12-08 17:18:51 +01:00
parent 549f230221
commit 5e47b4e949
14 changed files with 142 additions and 59 deletions

View file

@ -56,44 +56,11 @@ func (a *SettingController) getAllSetting(c *gin.Context) {
} }
func (a *SettingController) getDefaultSettings(c *gin.Context) { func (a *SettingController) getDefaultSettings(c *gin.Context) {
type settingFunc func() (interface{}, error) result, err := a.settingService.GetDefaultSettings(c.Request.Host)
if err != nil {
settings := map[string]settingFunc{ jsonMsg(c, I18nWeb(c, "pages.settings.toasts.getSettings"), err)
"expireDiff": func() (interface{}, error) { return a.settingService.GetExpireDiff() }, return
"trafficDiff": func() (interface{}, error) { return a.settingService.GetTrafficDiff() },
"defaultCert": func() (interface{}, error) { return a.settingService.GetCertFile() },
"defaultKey": func() (interface{}, error) { return a.settingService.GetKeyFile() },
"tgBotEnable": func() (interface{}, error) { return a.settingService.GetTgbotenabled() },
"subEnable": func() (interface{}, error) { return a.settingService.GetSubEnable() },
"subPort": func() (interface{}, error) { return a.settingService.GetSubPort() },
"subPath": func() (interface{}, error) { return a.settingService.GetSubPath() },
"subDomain": func() (interface{}, error) { return a.settingService.GetSubDomain() },
"subKeyFile": func() (interface{}, error) { return a.settingService.GetSubKeyFile() },
"subCertFile": func() (interface{}, error) { return a.settingService.GetSubCertFile() },
"subEncrypt": func() (interface{}, error) { return a.settingService.GetSubEncrypt() },
"subShowInfo": func() (interface{}, error) { return a.settingService.GetSubShowInfo() },
} }
result := make(map[string]interface{})
for key, fn := range settings {
value, err := fn()
if err != nil {
jsonMsg(c, I18nWeb(c, "pages.settings.toasts.getSettings"), err)
return
}
result[key] = value
}
subTLS := false
if result["subKeyFile"] != "" || result["subCertFile"] != "" {
subTLS = true
}
result["subTLS"] = subTLS
delete(result, "subKeyFile")
delete(result, "subCertFile")
jsonObj(c, result, nil) jsonObj(c, result, nil)
} }

View file

@ -22,6 +22,7 @@ type AllSetting struct {
WebKeyFile string `json:"webKeyFile" form:"webKeyFile"` WebKeyFile string `json:"webKeyFile" form:"webKeyFile"`
WebBasePath string `json:"webBasePath" form:"webBasePath"` WebBasePath string `json:"webBasePath" form:"webBasePath"`
SessionMaxAge int `json:"sessionMaxAge" form:"sessionMaxAge"` SessionMaxAge int `json:"sessionMaxAge" form:"sessionMaxAge"`
PageSize int `json:"pageSize" form:"pageSize"`
ExpireDiff int `json:"expireDiff" form:"expireDiff"` ExpireDiff int `json:"expireDiff" form:"expireDiff"`
TrafficDiff int `json:"trafficDiff" form:"trafficDiff"` TrafficDiff int `json:"trafficDiff" form:"trafficDiff"`
TgBotEnable bool `json:"tgBotEnable" form:"tgBotEnable"` TgBotEnable bool `json:"tgBotEnable" form:"tgBotEnable"`
@ -44,6 +45,7 @@ type AllSetting struct {
SubUpdates int `json:"subUpdates" form:"subUpdates"` SubUpdates int `json:"subUpdates" form:"subUpdates"`
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"`
} }
func (s *AllSetting) CheckValid() error { func (s *AllSetting) CheckValid() error {
@ -93,6 +95,12 @@ func (s *AllSetting) CheckValid() error {
if !strings.HasSuffix(s.WebBasePath, "/") { if !strings.HasSuffix(s.WebBasePath, "/") {
s.WebBasePath += "/" s.WebBasePath += "/"
} }
if !strings.HasPrefix(s.SubPath, "/") {
s.SubPath = "/" + s.SubPath
}
if !strings.HasSuffix(s.SubPath, "/") {
s.SubPath += "/"
}
_, err := time.LoadLocation(s.TimeLocation) _, err := time.LoadLocation(s.TimeLocation)
if err != nil { if err != nil {

View file

@ -86,8 +86,7 @@
}); });
}, },
genSubLink(subID) { genSubLink(subID) {
const { domain: host, port, tls: isTLS, path: base } = app.subSettings; return app.subSettings.subURI+subID+'?name='+subID;
return buildURL({ host, port, isTLS, base, path: subID+'?name='+remark });
} }
}, },
updated() { updated() {

View file

@ -13,10 +13,10 @@
</a-col> </a-col>
<a-col :lg="24" :xl="12"> <a-col :lg="24" :xl="12">
<template v-if="type === 'text'"> <template v-if="type === 'text'">
<a-input :value="value" @input="$emit('input', $event.target.value)"></a-input> <a-input :value="value" @input="$emit('input', $event.target.value)" :placeholder="placeholder"></a-input>
</template> </template>
<template v-else-if="type === 'number'"> <template v-else-if="type === 'number'">
<a-input-number :value="value" @change="value => $emit('input', value)" :min="min" style="width: 100%;"></a-input-number> <a-input-number :value="value" :step="step" @change="value => $emit('input', value)" :min="min" style="width: 100%;"></a-input-number>
</template> </template>
<template v-else-if="type === 'switch'"> <template v-else-if="type === 'switch'">
<a-switch :checked="value" @change="value => $emit('input', value)"></a-switch> <a-switch :checked="value" @change="value => $emit('input', value)"></a-switch>
@ -29,7 +29,7 @@
{{define "component/setting"}} {{define "component/setting"}}
<script> <script>
Vue.component('setting-list-item', { Vue.component('setting-list-item', {
props: ["type", "title", "desc", "value", "min"], props: ["type", "title", "desc", "value", "min", "step", "placeholder"],
template: `{{template "component/settingListItem"}}`, template: `{{template "component/settingListItem"}}`,
}); });
</script> </script>

View file

@ -300,8 +300,7 @@
infoModal.visible = false; infoModal.visible = false;
}, },
genSubLink(subID) { genSubLink(subID) {
const { domain: host, port, tls: isTLS, path: base } = app.subSettings; return app.subSettings.subURI+subID+'?name='+subID;
return buildURL({ host, port, isTLS, base, path: subID+'?name='+remark });
} }
}; };

View file

@ -173,14 +173,13 @@
<a-table :columns="isMobile ? mobileColums : columns" :row-key="dbInbound => dbInbound.id" <a-table :columns="isMobile ? mobileColums : columns" :row-key="dbInbound => dbInbound.id"
:data-source="searchedInbounds" :data-source="searchedInbounds"
:scroll="isMobile ? {} : { x: 1000 }" :scroll="isMobile ? {} : { x: 1000 }"
:pagination="false" :pagination=pagination(searchedInbounds)
:expand-icon-as-cell="false" :expand-icon-as-cell="false"
:expand-row-by-click="false" :expand-row-by-click="false"
:expand-icon-column-index="0" :expand-icon-column-index="0"
:indent-size="0" :indent-size="0"
:row-class-name="dbInbound => (dbInbound.isMultiUser() ? '' : 'hideExpandIcon')" :row-class-name="dbInbound => (dbInbound.isMultiUser() ? '' : 'hideExpandIcon')"
style="margin-top: 10px" style="margin-top: 10px">
@change="() => getDBInbounds()">
<template slot="action" slot-scope="text, dbInbound"> <template slot="action" slot-scope="text, dbInbound">
<a-icon type="edit" style="font-size: 22px" @click="openEditInbound(dbInbound.id);"></a-icon> <a-icon type="edit" style="font-size: 22px" @click="openEditInbound(dbInbound.id);"></a-icon>
<a-dropdown :trigger="['click']"> <a-dropdown :trigger="['click']">
@ -416,7 +415,7 @@
:row-key="client => client.id" :row-key="client => client.id"
:columns="isMobile ? innerMobileColumns : innerColumns" :columns="isMobile ? innerMobileColumns : innerColumns"
:data-source="getInboundClients(record)" :data-source="getInboundClients(record)"
:pagination="false" :pagination=pagination(getInboundClients(record))
:style="isMobile ? 'margin: -16px -5px -17px;' : 'margin-left: 10px;'"> :style="isMobile ? 'margin: -16px -5px -17px;' : 'margin-left: 10px;'">
{{template "client_table"}} {{template "client_table"}}
</a-table> </a-table>
@ -547,12 +546,10 @@
refreshInterval: Number(localStorage.getItem("refreshInterval")) || 5000, refreshInterval: Number(localStorage.getItem("refreshInterval")) || 5000,
subSettings: { subSettings: {
enable : false, enable : false,
port: 0, subURI : ''
path: '',
domain: '',
tls: false
}, },
tgBotEnable: false, tgBotEnable: false,
pageSize: 0,
isMobile: window.innerWidth <= 768, isMobile: window.innerWidth <= 768,
}, },
methods: { methods: {
@ -592,11 +589,9 @@
this.tgBotEnable = tgBotEnable; this.tgBotEnable = tgBotEnable;
this.subSettings = { this.subSettings = {
enable : subEnable, enable : subEnable,
port: subPort, subURI: subURI
path: subPath,
domain: subDomain,
tls: subTLS
}; };
this.pageSize = pageSize;
} }
}, },
setInbounds(dbInbounds) { setInbounds(dbInbounds) {
@ -1216,6 +1211,27 @@
this.spinning = false; this.spinning = false;
} }
}, },
pagination(obj){
if (this.pageSize > 0 && obj.length>this.pageSize) {
// Set page options based on object size
sizeOptions = []
for (i=this.pageSize;i<=obj.length;i=i+this.pageSize) {
sizeOptions.push(i.toString());
}
// Add option to see all in one page
sizeOptions.push(i.toString());
p = {
showSizeChanger: true,
size: 'small',
position: 'bottom',
pageSize: this.pageSize,
pageSizeOptions: sizeOptions
}
return p
}
return false
},
onResize() { onResize() {
this.isMobile = window.innerWidth <= 768; this.isMobile = window.innerWidth <= 768;
} }

View file

@ -112,6 +112,7 @@
<setting-list-item type="text" title='{{ i18n "pages.settings.privateKeyPath"}}' desc='{{ i18n "pages.settings.privateKeyPathDesc"}}' v-model="allSetting.webKeyFile"></setting-list-item> <setting-list-item type="text" title='{{ i18n "pages.settings.privateKeyPath"}}' desc='{{ i18n "pages.settings.privateKeyPathDesc"}}' v-model="allSetting.webKeyFile"></setting-list-item>
<setting-list-item type="text" title='{{ i18n "pages.settings.panelUrlPath"}}' desc='{{ i18n "pages.settings.panelUrlPathDesc"}}' v-model="allSetting.webBasePath"></setting-list-item> <setting-list-item type="text" title='{{ i18n "pages.settings.panelUrlPath"}}' desc='{{ i18n "pages.settings.panelUrlPathDesc"}}' v-model="allSetting.webBasePath"></setting-list-item>
<setting-list-item type="number" title='{{ i18n "pages.settings.sessionMaxAge" }}' desc='{{ i18n "pages.settings.sessionMaxAgeDesc" }}' v-model="allSetting.sessionMaxAge" :min="0"></setting-list-item> <setting-list-item type="number" title='{{ i18n "pages.settings.sessionMaxAge" }}' desc='{{ i18n "pages.settings.sessionMaxAgeDesc" }}' v-model="allSetting.sessionMaxAge" :min="0"></setting-list-item>
<setting-list-item type="number" title='{{ i18n "pages.settings.pageSize" }}' desc='{{ i18n "pages.settings.pageSizeDesc" }}' v-model="allSetting.pageSize" :min="0" :step="5"></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.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>
@ -243,6 +244,7 @@
<setting-list-item type="text" title='{{ i18n "pages.settings.subPath"}}' desc='{{ i18n "pages.settings.subPathDesc"}}' v-model="allSetting.subPath"></setting-list-item> <setting-list-item type="text" title='{{ i18n "pages.settings.subPath"}}' desc='{{ i18n "pages.settings.subPathDesc"}}' v-model="allSetting.subPath"></setting-list-item>
<setting-list-item type="text" title='{{ i18n "pages.settings.subCertPath"}}' desc='{{ i18n "pages.settings.subCertPathDesc"}}' v-model="allSetting.subCertFile"></setting-list-item> <setting-list-item type="text" title='{{ i18n "pages.settings.subCertPath"}}' desc='{{ i18n "pages.settings.subCertPathDesc"}}' v-model="allSetting.subCertFile"></setting-list-item>
<setting-list-item type="text" title='{{ i18n "pages.settings.subKeyPath"}}' desc='{{ i18n "pages.settings.subKeyPathDesc"}}' v-model="allSetting.subKeyFile"></setting-list-item> <setting-list-item type="text" title='{{ i18n "pages.settings.subKeyPath"}}' desc='{{ i18n "pages.settings.subKeyPathDesc"}}' v-model="allSetting.subKeyFile"></setting-list-item>
<setting-list-item type="text" title='{{ i18n "pages.settings.subURI"}}' desc='{{ i18n "pages.settings.subURIDesc"}}' v-model="allSetting.subURI" placeholder="(http|https)://domain[:port]/path/"></setting-list-item>
<setting-list-item type="number" title='{{ i18n "pages.settings.subUpdates"}}' desc='{{ i18n "pages.settings.subUpdatesDesc"}}' v-model="allSetting.subUpdates"></setting-list-item> <setting-list-item type="number" title='{{ i18n "pages.settings.subUpdates"}}' desc='{{ i18n "pages.settings.subUpdatesDesc"}}' v-model="allSetting.subUpdates"></setting-list-item>
</a-list> </a-list>
</a-tab-pane> </a-tab-pane>

View file

@ -31,6 +31,7 @@ var defaultValueMap = map[string]string{
"secret": random.Seq(32), "secret": random.Seq(32),
"webBasePath": "/", "webBasePath": "/",
"sessionMaxAge": "0", "sessionMaxAge": "0",
"pageSize": "0",
"expireDiff": "0", "expireDiff": "0",
"trafficDiff": "0", "trafficDiff": "0",
"timeLocation": "Asia/Tehran", "timeLocation": "Asia/Tehran",
@ -53,6 +54,7 @@ var defaultValueMap = map[string]string{
"subUpdates": "12", "subUpdates": "12",
"subEncrypt": "true", "subEncrypt": "true",
"subShowInfo": "true", "subShowInfo": "true",
"subURI": "",
} }
type SettingService struct { type SettingService struct {
@ -406,6 +408,14 @@ func (s *SettingService) GetSubShowInfo() (bool, error) {
return s.getBool("subShowInfo") return s.getBool("subShowInfo")
} }
func (s *SettingService) GetSubURI() (string, error) {
return s.getString("subURI")
}
func (s *SettingService) GetPageSize() (int, error) {
return s.getInt("pageSize")
}
func (s *SettingService) UpdateAllSetting(allSetting *entity.AllSetting) error { func (s *SettingService) UpdateAllSetting(allSetting *entity.AllSetting) error {
if err := allSetting.CheckValid(); err != nil { if err := allSetting.CheckValid(); err != nil {
return err return err
@ -435,3 +445,61 @@ func (s *SettingService) GetDefaultXrayConfig() (interface{}, error) {
} }
return jsonData, nil return jsonData, nil
} }
func (s *SettingService) GetDefaultSettings(host string) (interface{}, error) {
type settingFunc func() (interface{}, error)
settings := map[string]settingFunc{
"expireDiff": func() (interface{}, error) { return s.GetExpireDiff() },
"trafficDiff": func() (interface{}, error) { return s.GetTrafficDiff() },
"pageSize": func() (interface{}, error) { return s.GetPageSize() },
"defaultCert": func() (interface{}, error) { return s.GetCertFile() },
"defaultKey": func() (interface{}, error) { return s.GetKeyFile() },
"tgBotEnable": func() (interface{}, error) { return s.GetTgbotenabled() },
"subEnable": func() (interface{}, error) { return s.GetSubEnable() },
"subURI": func() (interface{}, error) { return s.GetSubURI() },
}
result := make(map[string]interface{})
for key, fn := range settings {
value, err := fn()
if err != nil {
return "", err
}
result[key] = value
}
if result["subEnable"].(bool) && result["subURI"].(string) == "" {
subURI := ""
subPort, _ := s.GetSubPort()
subPath, _ := s.GetSubPath()
subDomain, _ := s.GetSubDomain()
subKeyFile, _ := s.GetSubKeyFile()
subCertFile, _ := s.GetSubCertFile()
subTLS := false
if subKeyFile != "" && subCertFile != "" {
subTLS = true
}
if subDomain == "" {
subDomain = strings.Split(host, ":")[0]
}
if subTLS {
subURI = "https://"
} else {
subURI = "http://"
}
if (subPort == 443 && subTLS) || (subPort == 80 && !subTLS) {
subURI += subDomain
} else {
subURI += fmt.Sprintf("%s:%d", subDomain, subPort)
}
if subPath[0] == byte('/') {
subURI += subPath
} else {
subURI += "/" + subPath
}
result["subURI"] = subURI
}
return result, nil
}

View file

@ -238,6 +238,8 @@
"privateKeyPathDesc" = "Fill in an absolute path starting with." "privateKeyPathDesc" = "Fill in an absolute path starting with."
"panelUrlPath" = "Panel URL Root Path" "panelUrlPath" = "Panel URL Root Path"
"panelUrlPathDesc" = "Must start with '/' and end with." "panelUrlPathDesc" = "Must start with '/' and end with."
"pageSize" = "Pagination size"
"pageSizeDesc" = "Define page size for inbounds table. Set 0 to disable"
"oldUsername" = "Current Username" "oldUsername" = "Current Username"
"currentPassword" = "Current Password" "currentPassword" = "Current Password"
"newUsername" = "New Username" "newUsername" = "New Username"
@ -285,6 +287,8 @@
"subEncryptDesc" = "Encrypt the returned configs in subscription" "subEncryptDesc" = "Encrypt the returned configs in subscription"
"subShowInfo" = "Show usage info" "subShowInfo" = "Show usage info"
"subShowInfoDesc" = "Show remianed traffic and date after config name" "subShowInfoDesc" = "Show remianed traffic and date after config name"
"subURI" = "Reverse Proxy URI"
"subURIDesc" = "Change base URI of subscription URL for using on behind of proxies"
[pages.xray] [pages.xray]
"title" = "Xray Settings" "title" = "Xray Settings"

View file

@ -238,6 +238,8 @@
"privateKeyPathDesc" = "Complete con una ruta absoluta que comience con." "privateKeyPathDesc" = "Complete con una ruta absoluta que comience con."
"panelUrlPath" = "Ruta Raíz de la URL del Panel" "panelUrlPath" = "Ruta Raíz de la URL del Panel"
"panelUrlPathDesc" = "Debe empezar con '/' y terminar con." "panelUrlPathDesc" = "Debe empezar con '/' y terminar con."
"pageSize" = "Tamaño de paginación"
"pageSizeDesc" = "Defina el tamaño de página para la tabla de entradas. Establezca 0 para desactivar"
"oldUsername" = "Nombre de Usuario Actual" "oldUsername" = "Nombre de Usuario Actual"
"currentPassword" = "Contraseña Actual" "currentPassword" = "Contraseña Actual"
"newUsername" = "Nuevo Nombre de Usuario" "newUsername" = "Nuevo Nombre de Usuario"
@ -285,6 +287,8 @@
"subEncryptDesc" = "Encriptar las configuraciones devueltas en la suscripción." "subEncryptDesc" = "Encriptar las configuraciones devueltas en la suscripción."
"subShowInfo" = "Mostrar información de uso" "subShowInfo" = "Mostrar información de uso"
"subShowInfoDesc" = "Mostrar tráfico restante y fecha después del nombre de configuración." "subShowInfoDesc" = "Mostrar tráfico restante y fecha después del nombre de configuración."
"subURI" = "URI de proxy inverso"
"subURIDesc" = "Cambiar el URI base de la URL de suscripción para usar detrás de los servidores proxy"
[pages.xray] [pages.xray]
"title" = "Xray Configuración" "title" = "Xray Configuración"

View file

@ -238,6 +238,8 @@
"privateKeyPathDesc" = "باید یک مسیر مطلق باشد که با / شروع می شود " "privateKeyPathDesc" = "باید یک مسیر مطلق باشد که با / شروع می شود "
"panelUrlPath" = "آدرس روت پنل" "panelUrlPath" = "آدرس روت پنل"
"panelUrlPathDesc" = "باید با '/' شروع شود و با '/' تمام شود" "panelUrlPathDesc" = "باید با '/' شروع شود و با '/' تمام شود"
"pageSize" = "اندازه صفحه بندی جدول"
"pageSizeDesc" = "اندازه صفحه را برای جدول سرویس ها تعریف کنید. 0: غیرفعال"
"oldUsername" = "نام کاربری فعلی" "oldUsername" = "نام کاربری فعلی"
"currentPassword" = "رمز عبور فعلی" "currentPassword" = "رمز عبور فعلی"
"newUsername" = "نام کاربری جدید" "newUsername" = "نام کاربری جدید"
@ -285,6 +287,8 @@
"subEncryptDesc" = "رمزگذاری کانفیگ های بازگشتی سابسکریپشن" "subEncryptDesc" = "رمزگذاری کانفیگ های بازگشتی سابسکریپشن"
"subShowInfo" = "نمایش اطلاعات مصرف" "subShowInfo" = "نمایش اطلاعات مصرف"
"subShowInfoDesc" = "ترافیک و زمان باقیمانده را در هر کانفیگ نمایش میدهد" "subShowInfoDesc" = "ترافیک و زمان باقیمانده را در هر کانفیگ نمایش میدهد"
"subURI" = "آدرس پایه پروکسی معکوس"
"subURIDesc" = "آدرس پایه سابسکریپشن را برای استفاده در پشت پراکسی ها تغییر میدهد"
[pages.xray] [pages.xray]
"title" = "الگوها" "title" = "الگوها"

View file

@ -233,11 +233,13 @@
"panelPort" = "Порт панели" "panelPort" = "Порт панели"
"panelPortDesc" = "Порт, используемый для отображения этой панели" "panelPortDesc" = "Порт, используемый для отображения этой панели"
"publicKeyPath" = "Путь к файлу публичного ключа сертификата панели" "publicKeyPath" = "Путь к файлу публичного ключа сертификата панели"
"publicKeyPathDesc" = "Введите полный путь, начинающийся с " "publicKeyPathDesc" = "Введите полный путь, начинающийся с"
"privateKeyPath" = "Путь к файлу приватного ключа сертификата панели" "privateKeyPath" = "Путь к файлу приватного ключа сертификата панели"
"privateKeyPathDesc" = "Введите полный путь, начинающийся с " "privateKeyPathDesc" = "Введите полный путь, начинающийся с"
"panelUrlPath" = "Корневой путь URL адреса панели" "panelUrlPath" = "Корневой путь URL адреса панели"
"panelUrlPathDesc" = "Должен начинаться с '/' и заканчиваться на " "panelUrlPathDesc" = "Должен начинаться с '/' и заканчиваться на"
"pageSize" = "Размер нумерации страниц"
"pageSizeDesc" = "Определить размер страницы для входящей таблицы. Установите 0, чтобы отключить"
"oldUsername" = "Текущее имя пользователя" "oldUsername" = "Текущее имя пользователя"
"currentPassword" = "Текущий пароль" "currentPassword" = "Текущий пароль"
"newUsername" = "Новое имя пользователя" "newUsername" = "Новое имя пользователя"
@ -285,6 +287,8 @@
"subEncryptDesc" = "Шифровать возвращенные конфиги в подписке" "subEncryptDesc" = "Шифровать возвращенные конфиги в подписке"
"subShowInfo" = "Показать информацию об использовании" "subShowInfo" = "Показать информацию об использовании"
"subShowInfoDesc" = "Показывать восстановленный трафик и дату после имени конфигурации" "subShowInfoDesc" = "Показывать восстановленный трафик и дату после имени конфигурации"
"subURI" = "URI обратного прокси"
"subURIDesc" = "Изменить базовый URI URL-адреса подписки для использования за прокси-серверами"
[pages.xray] [pages.xray]
"title" = "Xray Настройки" "title" = "Xray Настройки"

View file

@ -238,6 +238,8 @@
"privateKeyPathDesc" = "Điền vào đường dẫn tuyệt đối bắt đầu với." "privateKeyPathDesc" = "Điền vào đường dẫn tuyệt đối bắt đầu với."
"panelUrlPath" = "Đường dẫn gốc URL Bảng điều khiển" "panelUrlPath" = "Đường dẫn gốc URL Bảng điều khiển"
"panelUrlPathDesc" = "Phải bắt đầu bằng '/' và kết thúc bằng." "panelUrlPathDesc" = "Phải bắt đầu bằng '/' và kết thúc bằng."
"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"
"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"
"newUsername" = "Tên người dùng mới" "newUsername" = "Tên người dùng mới"
@ -285,6 +287,8 @@
"subEncryptDesc" = "Mã hóa các cấu hình được trả về trong đăng ký" "subEncryptDesc" = "Mã hóa các cấu hình được trả về trong đăng ký"
"subShowInfo" = "Hiển thị thông tin sử dụng" "subShowInfo" = "Hiển thị thông tin sử dụng"
"subShowInfoDesc" = "Hiển thị lưu lượng truy cập còn lại và ngày sau tên cấu hình" "subShowInfoDesc" = "Hiển thị lưu lượng truy cập còn lại và ngày sau tên cấu hình"
"subURI" = "URI proxy ngược"
"subURIDesc" = "Thay đổi URI cơ sở của URL đăng ký để sử dụng ở phía sau proxy"
[pages.xray] [pages.xray]
"title" = "Xray Cài đặt" "title" = "Xray Cài đặt"

View file

@ -238,6 +238,8 @@
"privateKeyPathDesc" = "填写一个 '/' 开头的绝对路径" "privateKeyPathDesc" = "填写一个 '/' 开头的绝对路径"
"panelUrlPath" = "面板 url 根路径" "panelUrlPath" = "面板 url 根路径"
"panelUrlPathDesc" = "必须以 '/' 开头,以 '/' 结尾" "panelUrlPathDesc" = "必须以 '/' 开头,以 '/' 结尾"
"pageSize" = "分页大小"
"pageSizeDesc" = "定义入站表的页面大小。设置 0 表示禁用"
"oldUsername" = "原用户名" "oldUsername" = "原用户名"
"currentPassword" = "原密码" "currentPassword" = "原密码"
"newUsername" = "新用户名" "newUsername" = "新用户名"
@ -285,6 +287,8 @@
"subEncryptDesc" = "在订阅中加密返回的配置" "subEncryptDesc" = "在订阅中加密返回的配置"
"subShowInfo" = "显示使用信息" "subShowInfo" = "显示使用信息"
"subShowInfoDesc" = "在配置名称后显示剩余流量和日期" "subShowInfoDesc" = "在配置名称后显示剩余流量和日期"
"subURI" = "反向代理 URI"
"subURIDesc" = "更改订阅 URL 的基本 URI 以在代理后面使用"
[pages.xray] [pages.xray]
"title" = "Xray 设置" "title" = "Xray 设置"