mirror of
https://github.com/alireza0/x-ui.git
synced 2026-03-19 15:25:49 +00:00
Merge pull request #311 from hamid-gh98/main
[FIX] themeSwitch class + [ADD] password component + ...
This commit is contained in:
@@ -1,12 +1,16 @@
|
||||
version: '3.9'
|
||||
---
|
||||
version: "3.9"
|
||||
|
||||
services:
|
||||
xui:
|
||||
image: alireza7/x-ui
|
||||
container_name: x-ui
|
||||
hostname: yourhostname
|
||||
volumes:
|
||||
- $PWD/db/:/etc/x-ui/
|
||||
- $PWD/cert/:/root/cert/
|
||||
environment:
|
||||
XRAY_VMESS_AEAD_FORCED: "false"
|
||||
tty: true
|
||||
network_mode: host
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 54321:54321
|
||||
- 443:443
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
#app {
|
||||
html,
|
||||
body {
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#app {
|
||||
height: 100%;
|
||||
min-height: 100vh;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.ant-space {
|
||||
@@ -10,6 +28,12 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.ant-layout-sider {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-card {
|
||||
border-radius: 30px;
|
||||
}
|
||||
@@ -216,6 +240,7 @@
|
||||
.ant-card-dark .ant-input-group-addon {
|
||||
color: hsla(0,0%,100%,.65);
|
||||
background-color: #262f3d;
|
||||
border: 1px solid rgb(0 150 112 / 0%);
|
||||
}
|
||||
|
||||
.ant-card-dark .ant-list-item-meta-title,
|
||||
@@ -253,6 +278,10 @@
|
||||
background-color: #1a212a;
|
||||
}
|
||||
|
||||
.ant-input-number {
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
.ant-card-dark .ant-input,
|
||||
.ant-card-dark .ant-input-number,
|
||||
.ant-card-dark .ant-input-number-handler-wrap,
|
||||
@@ -262,6 +291,7 @@
|
||||
.ant-card-dark .ant-calendar-picker-clear {
|
||||
color: hsla(0,0%,100%,.65);
|
||||
background-color: #193752;
|
||||
border: 1px solid rgba(0, 65, 150, 0);
|
||||
}
|
||||
|
||||
.ant-card-dark .ant-select-disabled .ant-select-selection {
|
||||
|
||||
@@ -2,7 +2,7 @@ axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded
|
||||
axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
|
||||
|
||||
axios.interceptors.request.use(
|
||||
config => {
|
||||
(config) => {
|
||||
if (config.data instanceof FormData) {
|
||||
config.headers['Content-Type'] = 'multipart/form-data';
|
||||
} else {
|
||||
@@ -12,5 +12,5 @@ axios.interceptors.request.use(
|
||||
}
|
||||
return config;
|
||||
},
|
||||
error => Promise.reject(error)
|
||||
(error) => Promise.reject(error),
|
||||
);
|
||||
|
||||
@@ -69,48 +69,48 @@ function debounce(fn, delay) {
|
||||
}
|
||||
|
||||
function getCookie(cname) {
|
||||
let name = cname + '=';
|
||||
let name = cname + "=";
|
||||
let decodedCookie = decodeURIComponent(document.cookie);
|
||||
let ca = decodedCookie.split(';');
|
||||
let ca = decodedCookie.split(";");
|
||||
for (let i = 0; i < ca.length; i++) {
|
||||
let c = ca[i];
|
||||
while (c.charAt(0) == ' ') {
|
||||
while (c.charAt(0) == " ") {
|
||||
c = c.substring(1);
|
||||
}
|
||||
if (c.indexOf(name) == 0) {
|
||||
return c.substring(name.length, c.length);
|
||||
}
|
||||
}
|
||||
return '';
|
||||
return "";
|
||||
}
|
||||
|
||||
function setCookie(cname, cvalue, exdays) {
|
||||
const d = new Date();
|
||||
d.setTime(d.getTime() + exdays * 24 * 60 * 60 * 1000);
|
||||
let expires = 'expires=' + d.toUTCString();
|
||||
document.cookie = cname + '=' + cvalue + ';' + expires + ';path=/';
|
||||
let expires = "expires=" + d.toUTCString();
|
||||
document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
|
||||
}
|
||||
|
||||
function usageColor(data, threshold, total) {
|
||||
switch (true) {
|
||||
case data === null:
|
||||
return 'blue';
|
||||
return "blue";
|
||||
case total <= 0:
|
||||
return 'blue';
|
||||
return "blue";
|
||||
case data < total - threshold:
|
||||
return 'cyan';
|
||||
return "cyan";
|
||||
case data < total:
|
||||
return 'orange';
|
||||
return "orange";
|
||||
default:
|
||||
return 'red';
|
||||
return "red";
|
||||
}
|
||||
}
|
||||
|
||||
function areAllItemsExist(array1, array2) {
|
||||
function doAllItemsExist(array1, array2) {
|
||||
for (let i = 0; i < array1.length; i++) {
|
||||
if (!array2.includes(array1[i])) {
|
||||
return false;
|
||||
}
|
||||
if (!array2.includes(array1[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +128,6 @@ Date.prototype.formatDateTime = function (split = ' ') {
|
||||
};
|
||||
|
||||
class DateUtil {
|
||||
|
||||
// String string to date object
|
||||
static parseDate(str) {
|
||||
return new Date(str.replace(/-/g, '/'));
|
||||
@@ -144,4 +143,4 @@ class DateUtil {
|
||||
date.setMinTime();
|
||||
return date;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@ class RandomUtil {
|
||||
return string;
|
||||
}
|
||||
|
||||
static randomShadowsocksPassword(){
|
||||
static randomShadowsocksPassword() {
|
||||
let array = new Uint8Array(32);
|
||||
window.crypto.getRandomValues(array);
|
||||
return btoa(String.fromCharCode.apply(null, array));
|
||||
@@ -299,4 +299,4 @@ class ObjectUtil {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ func (a *SettingController) initRouter(g *gin.RouterGroup) {
|
||||
func (a *SettingController) getAllSetting(c *gin.Context) {
|
||||
allSetting, err := a.settingService.GetAllSetting()
|
||||
if err != nil {
|
||||
jsonMsg(c, I18n(c, "pages.settings.toasts.getSetting"), err)
|
||||
jsonMsg(c, I18n(c, "pages.settings.toasts.getSettings"), err)
|
||||
return
|
||||
}
|
||||
jsonObj(c, allSetting, nil)
|
||||
@@ -52,22 +52,22 @@ func (a *SettingController) getAllSetting(c *gin.Context) {
|
||||
func (a *SettingController) getDefaultSettings(c *gin.Context) {
|
||||
expireDiff, err := a.settingService.GetExpireDiff()
|
||||
if err != nil {
|
||||
jsonMsg(c, I18n(c, "pages.settings.toasts.getSetting"), err)
|
||||
jsonMsg(c, I18n(c, "pages.settings.toasts.getSettings"), err)
|
||||
return
|
||||
}
|
||||
trafficDiff, err := a.settingService.GetTrafficDiff()
|
||||
if err != nil {
|
||||
jsonMsg(c, I18n(c, "pages.settings.toasts.getSetting"), err)
|
||||
jsonMsg(c, I18n(c, "pages.settings.toasts.getSettings"), err)
|
||||
return
|
||||
}
|
||||
defaultCert, err := a.settingService.GetCertFile()
|
||||
if err != nil {
|
||||
jsonMsg(c, I18n(c, "pages.settings.toasts.getSetting"), err)
|
||||
jsonMsg(c, I18n(c, "pages.settings.toasts.getSettings"), err)
|
||||
return
|
||||
}
|
||||
defaultKey, err := a.settingService.GetKeyFile()
|
||||
if err != nil {
|
||||
jsonMsg(c, I18n(c, "pages.settings.toasts.getSetting"), err)
|
||||
jsonMsg(c, I18n(c, "pages.settings.toasts.getSettings"), err)
|
||||
return
|
||||
}
|
||||
result := map[string]interface{}{
|
||||
@@ -83,18 +83,18 @@ func (a *SettingController) updateSetting(c *gin.Context) {
|
||||
allSetting := &entity.AllSetting{}
|
||||
err := c.ShouldBind(allSetting)
|
||||
if err != nil {
|
||||
jsonMsg(c, I18n(c, "pages.settings.toasts.modifySetting"), err)
|
||||
jsonMsg(c, I18n(c, "pages.settings.toasts.modifySettings"), err)
|
||||
return
|
||||
}
|
||||
err = a.settingService.UpdateAllSetting(allSetting)
|
||||
jsonMsg(c, I18n(c, "pages.settings.toasts.modifySetting"), err)
|
||||
jsonMsg(c, I18n(c, "pages.settings.toasts.modifySettings"), err)
|
||||
}
|
||||
|
||||
func (a *SettingController) updateUser(c *gin.Context) {
|
||||
form := &updateUserForm{}
|
||||
err := c.ShouldBind(form)
|
||||
if err != nil {
|
||||
jsonMsg(c, I18n(c, "pages.settings.toasts.modifySetting"), err)
|
||||
jsonMsg(c, I18n(c, "pages.settings.toasts.modifySettings"), err)
|
||||
return
|
||||
}
|
||||
user := session.GetLoginUser(c)
|
||||
@@ -123,7 +123,7 @@ func (a *SettingController) restartPanel(c *gin.Context) {
|
||||
func (a *SettingController) getDefaultXrayConfig(c *gin.Context) {
|
||||
defaultJsonConfig, err := a.settingService.GetDefaultXrayConfig()
|
||||
if err != nil {
|
||||
jsonMsg(c, I18n(c, "pages.settings.toasts.getSetting"), err)
|
||||
jsonMsg(c, I18n(c, "pages.settings.toasts.getSettings"), err)
|
||||
return
|
||||
}
|
||||
jsonObj(c, defaultJsonConfig, nil)
|
||||
|
||||
@@ -23,7 +23,7 @@ func (a *XUIController) initRouter(g *gin.RouterGroup) {
|
||||
|
||||
g.GET("/", a.index)
|
||||
g.GET("/inbounds", a.inbounds)
|
||||
g.GET("/setting", a.setting)
|
||||
g.GET("/settings", a.settings)
|
||||
|
||||
a.inboundController = NewInboundController(g)
|
||||
a.settingController = NewSettingController(g)
|
||||
@@ -37,6 +37,6 @@ func (a *XUIController) inbounds(c *gin.Context) {
|
||||
html(c, "inbounds.html", "pages.inbounds.title", nil)
|
||||
}
|
||||
|
||||
func (a *XUIController) setting(c *gin.Context) {
|
||||
html(c, "setting.html", "pages.settings.title", nil)
|
||||
func (a *XUIController) settings(c *gin.Context) {
|
||||
html(c, "settings.html", "pages.settings.title", nil)
|
||||
}
|
||||
|
||||
@@ -36,11 +36,11 @@
|
||||
},
|
||||
confirm() {},
|
||||
open({
|
||||
title='',
|
||||
type='text',
|
||||
value='',
|
||||
okText='{{ i18n "sure"}}',
|
||||
confirm=() => {},
|
||||
title = '',
|
||||
type = 'text',
|
||||
value = '',
|
||||
okText = '{{ i18n "sure"}}',
|
||||
confirm = () => {},
|
||||
}) {
|
||||
this.title = title;
|
||||
this.type = type;
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
qrcode: null,
|
||||
clipboard: null,
|
||||
visible: false,
|
||||
show: function (title='', content='', dbInbound=new DBInbound(), copyText='', clientName = null) {
|
||||
show: function (title = '', content = '', dbInbound = new DBInbound(), copyText = '', clientName = null) {
|
||||
this.title = title;
|
||||
this.content = content;
|
||||
this.dbInbound = dbInbound;
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
qrcode: null,
|
||||
clipboard: null,
|
||||
visible: false,
|
||||
show: function (title='', content='', fileName='') {
|
||||
show: function (title = '', content = '', fileName = '') {
|
||||
this.title = title;
|
||||
this.content = content;
|
||||
this.fileName = fileName;
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
<a-layout-content>
|
||||
<a-row type="flex" justify="center">
|
||||
<a-col :xs="22" :sm="20" :md="16" :lg="12" :xl="8">
|
||||
<h1 :style="themeSwitcher.textStyle">{{ i18n "pages.login.title" }}</h1>
|
||||
<h1 class="title" :style="themeSwitcher.textStyle">{{ i18n "pages.login.title" }}</h1>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row type="flex" justify="center">
|
||||
@@ -63,10 +63,9 @@
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-input type="password" v-model.trim="user.password"
|
||||
placeholder='{{ i18n "password" }}' @keydown.enter.native="login">
|
||||
<a-icon slot="prefix" type="lock" :style="themeSwitcher.textStyle"/>
|
||||
</a-input>
|
||||
<password-input icon="lock" v-model.trim="user.password"
|
||||
placeholder='{{ i18n "password" }}' @keydown.enter.native="login">
|
||||
</password-input>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-row justify="center" class="centered">
|
||||
@@ -101,6 +100,7 @@
|
||||
</a-layout>
|
||||
{{template "js" .}}
|
||||
{{template "component/themeSwitcher" .}}
|
||||
{{template "component/password" .}}
|
||||
<script>
|
||||
const app = new Vue({
|
||||
delimiters: ['[[', ']]'],
|
||||
@@ -125,7 +125,7 @@
|
||||
}
|
||||
},
|
||||
updateBackground() {
|
||||
document.querySelector('#app').style.background = this.themeSwitcher.isDarkTheme ? colors.dark.bg : 'white';
|
||||
document.querySelector('#app').style.background = colors[this.themeSwitcher.currentTheme].bg;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
<a-icon type="user"></a-icon>
|
||||
<span>{{ i18n "menu.inbounds"}}</span>
|
||||
</a-menu-item>
|
||||
<a-menu-item key="{{ .base_path }}xui/setting">
|
||||
<a-menu-item key="{{ .base_path }}xui/settings">
|
||||
<a-icon type="setting"></a-icon>
|
||||
<span>{{ i18n "menu.setting"}}</span>
|
||||
<span>{{ i18n "menu.settings"}}</span>
|
||||
</a-menu-item>
|
||||
<a-sub-menu>
|
||||
<template slot="title">
|
||||
|
||||
35
web/html/xui/component/password.html
Normal file
35
web/html/xui/component/password.html
Normal file
@@ -0,0 +1,35 @@
|
||||
{{define "component/passwordInput"}}
|
||||
<template>
|
||||
<a-input :value="value" :type="showPassword ? 'text' : 'password'"
|
||||
:placeholder="placeholder"
|
||||
@input="$emit('input', $event.target.value)">
|
||||
<template v-if="icon" #prefix>
|
||||
<a-icon :type="icon" :style="'font-size: 16px;' + themeSwitcher.textStyle" />
|
||||
</template>
|
||||
<template #addonAfter>
|
||||
<a-icon :type="showPassword ? 'eye-invisible' : 'eye'"
|
||||
@click="toggleShowPassword"
|
||||
:style="'font-size: 16px;' + themeSwitcher.textStyle" />
|
||||
</template>
|
||||
</a-input>
|
||||
</template>
|
||||
{{end}}
|
||||
|
||||
{{define "component/password"}}
|
||||
<script>
|
||||
Vue.component('password-input', {
|
||||
props: ["title", "value", "placeholder", "icon"],
|
||||
template: `{{template "component/passwordInput"}}`,
|
||||
data() {
|
||||
return {
|
||||
showPassword: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
toggleShowPassword() {
|
||||
this.showPassword = !this.showPassword;
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
{{end}}
|
||||
@@ -1,7 +1,7 @@
|
||||
{{define "form/shadowsocks"}}
|
||||
<a-form layout="inline">
|
||||
<a-collapse activeKey="0" v-for="(client, index) in inbound.settings.shadowsockses.slice(0,1)" v-if="!isEdit">
|
||||
<a-collapse-panel header="{{ i18n "pages.inbounds.client" }}">
|
||||
<a-collapse-panel header='{{ i18n "pages.inbounds.client" }}'>
|
||||
<table width="100%" class="ant-table-tbody">
|
||||
<tr>
|
||||
<td>
|
||||
@@ -47,7 +47,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<span>{{ i18n "pages.inbounds.totalFlow" }}</span>(GB)
|
||||
<span>{{ i18n "pages.inbounds.totalFlow" }}</span> (GB)
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
{{define "form/socks"}}
|
||||
<a-form layout="inline">
|
||||
<!-- <a-form-item label="Password authentication">-->
|
||||
<table width="100%" class="ant-table-tbody">
|
||||
<tr>
|
||||
<td>{{ i18n "password" }}</td>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{{define "form/trojan"}}
|
||||
<a-form layout="inline">
|
||||
<a-collapse activeKey="0" v-for="(client, index) in inbound.settings.trojans.slice(0,1)" v-if="!isEdit">
|
||||
<a-collapse-panel header="{{ i18n "pages.inbounds.client" }}">
|
||||
<a-collapse-panel header='{{ i18n "pages.inbounds.client" }}'>
|
||||
<table width="100%" class="ant-table-tbody">
|
||||
<tr>
|
||||
<td>
|
||||
@@ -45,7 +45,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<span>{{ i18n "pages.inbounds.totalFlow" }}</span>(GB)
|
||||
<span>{{ i18n "pages.inbounds.totalFlow" }}</span> (GB)
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
<template v-for="cert,index in inbound.stream.tls.certs">
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<a-form-item label="{{ i18n "certificate" }}">
|
||||
<a-form-item label='{{ i18n "certificate" }}'>
|
||||
<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="false">{{ i18n "pages.inbounds.certificateContent" }}</a-radio-button>
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<body>
|
||||
<a-layout id="app" v-cloak>
|
||||
{{ template "commonSider" . }}
|
||||
@@ -41,19 +42,19 @@
|
||||
<a-col :xs="24" :sm="24" :lg="12">
|
||||
{{ i18n "clients" }}:
|
||||
<a-tag color="green">[[ total.clients ]]</a-tag>
|
||||
<a-popover title="{{ i18n "disabled" }}" :overlay-class-name="themeSwitcher.darkClass">
|
||||
<a-popover title='{{ i18n "disabled" }}' :overlay-class-name="themeSwitcher.darkClass">
|
||||
<template slot="content">
|
||||
<p v-for="clientEmail in total.deactive">[[ clientEmail ]]</p>
|
||||
</template>
|
||||
<a-tag v-if="total.deactive.length">[[ total.deactive.length ]]</a-tag>
|
||||
</a-popover>
|
||||
<a-popover title="{{ i18n "depleted" }}" :overlay-class-name="themeSwitcher.darkClass">
|
||||
<a-popover title='{{ i18n "depleted" }}' :overlay-class-name="themeSwitcher.darkClass">
|
||||
<template slot="content">
|
||||
<p v-for="clientEmail in total.depleted">[[ clientEmail ]]</p>
|
||||
</template>
|
||||
<a-tag color="red" v-if="total.depleted.length">[[ total.depleted.length ]]</a-tag>
|
||||
</a-popover>
|
||||
<a-popover title="{{ i18n "depletingSoon" }}" :overlay-class-name="themeSwitcher.darkClass">
|
||||
<a-popover title='{{ i18n "depletingSoon" }}' :overlay-class-name="themeSwitcher.darkClass">
|
||||
<template slot="content">
|
||||
<p v-for="clientEmail in total.expiring">[[ clientEmail ]]</p>
|
||||
</template>
|
||||
@@ -103,7 +104,7 @@
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
<a-input v-model.lazy="searchKey" placeholder="{{ i18n "search" }}" autofocus style="max-width: 300px"></a-input>
|
||||
<a-input v-model.lazy="searchKey" placeholder='{{ i18n "search" }}' autofocus style="max-width: 300px"></a-input>
|
||||
<a-table :columns="columns" :row-key="dbInbound => dbInbound.id"
|
||||
:data-source="searchedInbounds"
|
||||
:loading="spinning" :scroll="{ x: 1300 }"
|
||||
@@ -150,7 +151,7 @@
|
||||
<a-icon type="retweet"></a-icon> {{ i18n "pages.inbounds.resetTraffic" }}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="clone">
|
||||
<a-icon type="block"></a-icon> {{ i18n "pages.inbounds.Clone"}}
|
||||
<a-icon type="block"></a-icon> {{ i18n "pages.inbounds.clone"}}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="delete">
|
||||
<span style="color: #FF4D4F">
|
||||
@@ -171,19 +172,19 @@
|
||||
<template slot="clients" slot-scope="text, dbInbound">
|
||||
<template v-if="clientCount[dbInbound.id]">
|
||||
<a-tag style="margin:0;" color="green">[[ clientCount[dbInbound.id].clients ]]</a-tag>
|
||||
<a-popover title="{{ i18n "disabled" }}" :overlay-class-name="themeSwitcher.darkClass">
|
||||
<a-popover title='{{ i18n "disabled" }}' :overlay-class-name="themeSwitcher.darkClass">
|
||||
<template slot="content">
|
||||
<p v-for="clientEmail in clientCount[dbInbound.id].deactive">[[ clientEmail ]]</p>
|
||||
</template>
|
||||
<a-tag style="margin:0; padding: 0 2px;" v-if="clientCount[dbInbound.id].deactive.length">[[ clientCount[dbInbound.id].deactive.length ]]</a-tag>
|
||||
</a-popover>
|
||||
<a-popover title="{{ i18n "depleted" }}" :overlay-class-name="themeSwitcher.darkClass">
|
||||
<a-popover title='{{ i18n "depleted" }}' :overlay-class-name="themeSwitcher.darkClass">
|
||||
<template slot="content">
|
||||
<p v-for="clientEmail in clientCount[dbInbound.id].depleted">[[ clientEmail ]]</p>
|
||||
</template>
|
||||
<a-tag style="margin:0; padding: 0 2px;" color="red" v-if="clientCount[dbInbound.id].depleted.length">[[ clientCount[dbInbound.id].depleted.length ]]</a-tag>
|
||||
</a-popover>
|
||||
<a-popover title="{{ i18n "depletingSoon" }}" :overlay-class-name="themeSwitcher.darkClass">
|
||||
<a-popover title='{{ i18n "depletingSoon" }}' :overlay-class-name="themeSwitcher.darkClass">
|
||||
<template slot="content">
|
||||
<p v-for="clientEmail in clientCount[dbInbound.id].expiring">[[ clientEmail ]]</p>
|
||||
</template>
|
||||
@@ -215,20 +216,20 @@
|
||||
</template>
|
||||
<template slot="expandedRowRender" slot-scope="record">
|
||||
<a-table
|
||||
v-if="(record.protocol === Protocols.VLESS) || (record.protocol === Protocols.VMESS)"
|
||||
:row-key="client => client.id"
|
||||
:columns="innerColumns"
|
||||
:data-source="getInboundClients(record)"
|
||||
:pagination="false"
|
||||
v-if="(record.protocol === Protocols.VLESS) || (record.protocol === Protocols.VMESS)"
|
||||
:row-key="client => client.id"
|
||||
:columns="innerColumns"
|
||||
:data-source="getInboundClients(record)"
|
||||
:pagination="false"
|
||||
>
|
||||
{{template "client_table"}}
|
||||
</a-table>
|
||||
<a-table
|
||||
v-else-if="record.protocol === Protocols.TROJAN || record.protocol === Protocols.SHADOWSOCKS"
|
||||
:row-key="client => client.id"
|
||||
:columns="innerTrojanColumns"
|
||||
:data-source="getInboundClients(record)"
|
||||
:pagination="false"
|
||||
v-else-if="record.protocol === Protocols.TROJAN || record.protocol === Protocols.SHADOWSOCKS"
|
||||
:row-key="client => client.id"
|
||||
:columns="innerTrojanColumns"
|
||||
:data-source="getInboundClients(record)"
|
||||
:pagination="false"
|
||||
>
|
||||
{{template "client_table"}}
|
||||
</a-table>
|
||||
@@ -329,7 +330,7 @@
|
||||
refreshInterval: Number(localStorage.getItem("refreshInterval")) || 5000,
|
||||
},
|
||||
methods: {
|
||||
loading(spinning=true) {
|
||||
loading(spinning = true) {
|
||||
this.spinning = spinning;
|
||||
},
|
||||
async getDBInbounds() {
|
||||
@@ -361,29 +362,29 @@
|
||||
to_inbound = dbInbound.toInbound()
|
||||
this.inbounds.push(to_inbound);
|
||||
this.dbInbounds.push(dbInbound);
|
||||
if([Protocols.VMESS, Protocols.VLESS, Protocols.TROJAN].includes(inbound.protocol) ){
|
||||
this.clientCount[inbound.id] = this.getClientCounts(inbound,to_inbound);
|
||||
if ([Protocols.VMESS, Protocols.VLESS, Protocols.TROJAN].includes(inbound.protocol)) {
|
||||
this.clientCount[inbound.id] = this.getClientCounts(inbound, to_inbound);
|
||||
}
|
||||
}
|
||||
this.searchInbounds(this.searchKey);
|
||||
},
|
||||
getClientCounts(dbInbound,inbound){
|
||||
let clientCount = 0,active = [], deactive = [], depleted = [], expiring = [];
|
||||
getClientCounts(dbInbound, inbound) {
|
||||
let clientCount = 0, active = [], deactive = [], depleted = [], expiring = [];
|
||||
clients = this.getClients(dbInbound.protocol, inbound.settings);
|
||||
clientStats = dbInbound.clientStats
|
||||
now = new Date().getTime()
|
||||
if(clients){
|
||||
if (clients) {
|
||||
clientCount = clients.length;
|
||||
if(dbInbound.enable){
|
||||
if (dbInbound.enable) {
|
||||
clients.forEach(client => {
|
||||
client.enable ? active.push(client.email) : deactive.push(client.email);
|
||||
});
|
||||
clientStats.forEach(client => {
|
||||
if(!client.enable) {
|
||||
if (!client.enable) {
|
||||
depleted.push(client.email);
|
||||
} else {
|
||||
if ((client.expiryTime > 0 && (client.expiryTime-now < this.expireDiff)) ||
|
||||
(client.total > 0 && (client.total-(client.up+client.down) < this.trafficDiff ))) expiring.push(client.email);
|
||||
if ((client.expiryTime > 0 && (client.expiryTime - now < this.expireDiff)) ||
|
||||
(client.total > 0 && (client.total - (client.up + client.down) < this.trafficDiff))) expiring.push(client.email);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@@ -409,10 +410,10 @@
|
||||
if (ObjectUtil.deepSearch(inbound, key)) {
|
||||
const newInbound = new DBInbound(inbound);
|
||||
const inboundSettings = JSON.parse(inbound.settings);
|
||||
if (inboundSettings.hasOwnProperty('clients')){
|
||||
if (inboundSettings.hasOwnProperty('clients')) {
|
||||
const searchedSettings = { "clients": [] };
|
||||
inboundSettings.clients.forEach(client => {
|
||||
if (ObjectUtil.deepSearch(client, key)){
|
||||
if (ObjectUtil.deepSearch(client, key)) {
|
||||
searchedSettings.clients.push(client);
|
||||
}
|
||||
});
|
||||
@@ -423,7 +424,7 @@
|
||||
});
|
||||
}
|
||||
},
|
||||
generalActions(action){
|
||||
generalActions(action) {
|
||||
switch (action.key) {
|
||||
case "export":
|
||||
this.exportAllLinks();
|
||||
@@ -508,7 +509,7 @@
|
||||
},
|
||||
openCloneInbound(dbInbound) {
|
||||
this.$confirm({
|
||||
title: '{{ i18n "pages.inbounds.cloneInbound"}} ' + dbInbound.remark,
|
||||
title: '{{ i18n "pages.inbounds.cloneInbound"}} \"' + dbInbound.remark + '\"',
|
||||
content: '{{ i18n "pages.inbounds.cloneInboundContent"}}',
|
||||
okText: '{{ i18n "pages.inbounds.update"}}',
|
||||
cancelText: '{{ i18n "cancel" }}',
|
||||
@@ -620,21 +621,21 @@
|
||||
isEdit: true
|
||||
});
|
||||
},
|
||||
findIndexOfClient(clients,client) {
|
||||
findIndexOfClient(clients, client) {
|
||||
firstKey = Object.keys(client)[0];
|
||||
return clients.findIndex(c => c[firstKey] === client[firstKey]);
|
||||
},
|
||||
async addClient(clients, dbInboundId) {
|
||||
const data = {
|
||||
id: dbInboundId,
|
||||
settings: '{"clients": [' + clients.toString() +']}',
|
||||
settings: '{"clients": [' + clients.toString() + ']}',
|
||||
};
|
||||
await this.submit(`/xui/inbound/addClient`, data);
|
||||
},
|
||||
async updateClient(client, dbInboundId, clientId) {
|
||||
const data = {
|
||||
id: dbInboundId,
|
||||
settings: '{"clients": [' + client.toString() +']}',
|
||||
settings: '{"clients": [' + client.toString() + ']}',
|
||||
};
|
||||
await this.submit(`/xui/inbound/updateClient/${clientId}`, data);
|
||||
},
|
||||
@@ -664,9 +665,9 @@
|
||||
onOk: () => this.submit('/xui/inbound/del/' + dbInboundId),
|
||||
});
|
||||
},
|
||||
delClient(dbInboundId,client) {
|
||||
delClient(dbInboundId, client) {
|
||||
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
||||
clientId = this.getClientId(dbInbound.protocol,client);
|
||||
clientId = this.getClientId(dbInbound.protocol, client);
|
||||
this.$confirm({
|
||||
title: '{{ i18n "pages.inbounds.deleteInbound"}}',
|
||||
content: '{{ i18n "pages.inbounds.deleteInboundContent"}}',
|
||||
@@ -677,7 +678,7 @@
|
||||
});
|
||||
},
|
||||
getClients(protocol, clientSettings) {
|
||||
switch(protocol){
|
||||
switch (protocol) {
|
||||
case Protocols.VMESS: return clientSettings.vmesses;
|
||||
case Protocols.VLESS: return clientSettings.vlesses;
|
||||
case Protocols.TROJAN: return clientSettings.trojans;
|
||||
@@ -686,7 +687,7 @@
|
||||
}
|
||||
},
|
||||
getClientId(protocol, client) {
|
||||
switch(protocol){
|
||||
switch (protocol) {
|
||||
case Protocols.TROJAN: return client.password;
|
||||
case Protocols.SHADOWSOCKS: return client.email;
|
||||
default: return client.id;
|
||||
@@ -711,8 +712,8 @@
|
||||
clients = this.getClients(dbInbound.protocol, inbound.settings);
|
||||
index = this.findIndexOfClient(clients, client);
|
||||
clients[index].enable = !clients[index].enable;
|
||||
clientId = this.getClientId(dbInbound.protocol,clients[index]);
|
||||
await this.updateClient(clients[index],dbInboundId, clientId);
|
||||
clientId = this.getClientId(dbInbound.protocol, clients[index]);
|
||||
await this.updateClient(clients[index], dbInboundId, clientId);
|
||||
this.loading(false);
|
||||
},
|
||||
async submit(url, data) {
|
||||
@@ -722,24 +723,24 @@
|
||||
}
|
||||
},
|
||||
getInboundClients(dbInbound) {
|
||||
if(dbInbound.protocol == Protocols.VLESS) {
|
||||
if (dbInbound.protocol == Protocols.VLESS) {
|
||||
return dbInbound.toInbound().settings.vlesses;
|
||||
} else if(dbInbound.protocol == Protocols.VMESS) {
|
||||
} else if (dbInbound.protocol == Protocols.VMESS) {
|
||||
return dbInbound.toInbound().settings.vmesses;
|
||||
} else if(dbInbound.protocol == Protocols.TROJAN) {
|
||||
} else if (dbInbound.protocol == Protocols.TROJAN) {
|
||||
return dbInbound.toInbound().settings.trojans;
|
||||
} else if(dbInbound.protocol == Protocols.SHADOWSOCKS) {
|
||||
} else if (dbInbound.protocol == Protocols.SHADOWSOCKS) {
|
||||
return dbInbound.toInbound().settings.shadowsockses;
|
||||
}
|
||||
},
|
||||
resetClientTraffic(client,dbInboundId) {
|
||||
resetClientTraffic(client, dbInboundId) {
|
||||
this.$confirm({
|
||||
title: '{{ i18n "pages.inbounds.resetTraffic"}}',
|
||||
content: '{{ i18n "pages.inbounds.resetTrafficContent"}}',
|
||||
class: themeSwitcher.darkCardClass,
|
||||
okText: '{{ i18n "reset"}}',
|
||||
cancelText: '{{ i18n "cancel"}}',
|
||||
onOk: () => this.submit('/xui/inbound/' + dbInboundId + '/resetClientTraffic/'+ client.email),
|
||||
onOk: () => this.submit('/xui/inbound/' + dbInboundId + '/resetClientTraffic/' + client.email),
|
||||
})
|
||||
},
|
||||
resetAllTraffic() {
|
||||
@@ -754,8 +755,8 @@
|
||||
},
|
||||
resetAllClientTraffics(dbInboundId) {
|
||||
this.$confirm({
|
||||
title: dbInboundId>0 ? '{{ i18n "pages.inbounds.resetInboundClientTrafficTitle"}}' : '{{ i18n "pages.inbounds.resetAllClientTrafficTitle"}}',
|
||||
content: dbInboundId>0 ? '{{ i18n "pages.inbounds.resetInboundClientTrafficContent"}}' : '{{ i18n "pages.inbounds.resetAllClientTrafficContent"}}',
|
||||
title: dbInboundId > 0 ? '{{ i18n "pages.inbounds.resetInboundClientTrafficTitle"}}' : '{{ i18n "pages.inbounds.resetAllClientTrafficTitle"}}',
|
||||
content: dbInboundId > 0 ? '{{ i18n "pages.inbounds.resetInboundClientTrafficContent"}}' : '{{ i18n "pages.inbounds.resetAllClientTrafficContent"}}',
|
||||
class: themeSwitcher.darkCardClass,
|
||||
okText: '{{ i18n "reset"}}',
|
||||
cancelText: '{{ i18n "cancel"}}',
|
||||
@@ -776,17 +777,17 @@
|
||||
return dbInbound.toInbound().isExpiry(index)
|
||||
},
|
||||
getUpStats(dbInbound, email) {
|
||||
if(email.length == 0) return 0
|
||||
if (email.length == 0) return 0
|
||||
clientStats = dbInbound.clientStats.find(stats => stats.email === email)
|
||||
return clientStats ? clientStats.up : 0
|
||||
},
|
||||
getDownStats(dbInbound, email) {
|
||||
if(email.length == 0) return 0
|
||||
if (email.length == 0) return 0
|
||||
clientStats = dbInbound.clientStats.find(stats => stats.email === email)
|
||||
return clientStats ? clientStats.down : 0
|
||||
},
|
||||
statsColor(dbInbound, email) {
|
||||
if(email.length == 0) return 'blue';
|
||||
if (email.length == 0) return 'blue';
|
||||
clientStats = dbInbound.clientStats.find(stats => stats.email === email);
|
||||
return usageColor(clientStats.down + clientStats.up, this.trafficDiff, clientStats.total);
|
||||
},
|
||||
@@ -794,19 +795,19 @@
|
||||
clientStats = dbInbound.clientStats ? dbInbound.clientStats.find(stats => stats.email === email) : null
|
||||
return clientStats ? clientStats['enable'] : true
|
||||
},
|
||||
isRemovable(dbInbound_id){
|
||||
isRemovable(dbInbound_id) {
|
||||
return this.getInboundClients(this.dbInbounds.find(row => row.id === dbInbound_id)).length > 1
|
||||
},
|
||||
inboundLinks(dbInboundId) {
|
||||
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
||||
txtModal.show('{{ i18n "pages.inbounds.export"}}',dbInbound.genInboundLinks,dbInbound.remark);
|
||||
txtModal.show('{{ i18n "pages.inbounds.export"}}', dbInbound.genInboundLinks, dbInbound.remark);
|
||||
},
|
||||
exportAllLinks() {
|
||||
let copyText = '';
|
||||
for (const dbInbound of this.dbInbounds) {
|
||||
copyText += dbInbound.genInboundLinks
|
||||
}
|
||||
txtModal.show('{{ i18n "pages.inbounds.export"}}',copyText,'All-Inbounds');
|
||||
txtModal.show('{{ i18n "pages.inbounds.export"}}', copyText, 'All-Inbounds');
|
||||
},
|
||||
async startDataRefreshLoop() {
|
||||
while (this.isRefreshEnabled) {
|
||||
@@ -824,11 +825,11 @@
|
||||
this.startDataRefreshLoop();
|
||||
}
|
||||
},
|
||||
changeRefreshInterval(){
|
||||
changeRefreshInterval() {
|
||||
localStorage.setItem("refreshInterval", this.refreshInterval);
|
||||
},
|
||||
async manualRefresh(){
|
||||
if(!this.refreshing){
|
||||
async manualRefresh() {
|
||||
if (!this.refreshing) {
|
||||
this.spinning = true;
|
||||
await this.getDBInbounds();
|
||||
this.spinning = false;
|
||||
|
||||
@@ -427,7 +427,7 @@
|
||||
cancelText: '{{ i18n "cancel"}}',
|
||||
onOk: async () => {
|
||||
versionModal.hide();
|
||||
this.loading(true, '{{ i18n "pages.index.dontRefreshh"}}');
|
||||
this.loading(true, '{{ i18n "pages.index.dontRefresh"}}');
|
||||
await HttpUtil.post(`/server/installXray/${version}`);
|
||||
this.loading(false);
|
||||
},
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
{{ i18n "pages.settings.infoDesc" }}
|
||||
</h2>
|
||||
</a-row>
|
||||
<a-list item-layout="horizontal" :style="themeSwitcher.isDarkTheme ? 'color: hsla(0,0%,100%,.65);': 'background: white;'">
|
||||
<a-list item-layout="horizontal" :style="themeSwitcher.textStyle">
|
||||
<setting-list-item type="text" title='{{ i18n "pages.settings.panelListeningIP"}}' desc='{{ i18n "pages.settings.panelListeningIPDesc"}}' v-model="allSetting.webListen"></setting-list-item>
|
||||
<setting-list-item type="number" title='{{ i18n "pages.settings.panelPort"}}' desc='{{ i18n "pages.settings.panelPortDesc"}}' v-model.number="allSetting.webPort"></setting-list-item>
|
||||
<setting-list-item type="text" title='{{ i18n "pages.settings.publicKeyPath"}}' desc='{{ i18n "pages.settings.publicKeyPathDesc"}}' v-model="allSetting.webCertFile"></setting-list-item>
|
||||
@@ -79,21 +79,19 @@
|
||||
</a-list-item>
|
||||
</a-list>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="2" tab='{{ i18n "pages.settings.userSetting"}}'>
|
||||
<a-form :style="themeSwitcher.isDarkTheme ? 'color: hsla(0,0%,100%,.65); padding: 20px;': 'background: white; padding: 20px;'">
|
||||
<a-tab-pane key="2" tab='{{ i18n "pages.settings.userSettings"}}'>
|
||||
<a-form :style="'padding: 20px;' + themeSwitcher.textStyle">
|
||||
<a-form-item label='{{ i18n "pages.settings.oldUsername"}}'>
|
||||
<a-input v-model="user.oldUsername" style="max-width: 300px"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.settings.currentPassword"}}'>
|
||||
<a-input type="password" v-model="user.oldPassword"
|
||||
style="max-width: 300px"></a-input>
|
||||
<password-input v-model="user.oldPassword" style="max-width: 300px"></password-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.settings.newUsername"}}'>
|
||||
<a-input v-model="user.newUsername" style="max-width: 300px"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.settings.newPassword"}}'>
|
||||
<a-input type="password" v-model="user.newPassword"
|
||||
style="max-width: 300px"></a-input>
|
||||
<password-input v-model="user.newPassword" style="max-width: 300px"></password-input>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-button type="primary" @click="updateUser">{{ i18n "confirm" }}</a-button>
|
||||
@@ -108,7 +106,7 @@
|
||||
{{ i18n "pages.settings.infoDesc" }}
|
||||
</h2>
|
||||
</a-row>
|
||||
<a-list item-layout="horizontal" :style="themeSwitcher.isDarkTheme ? 'color: hsla(0,0%,100%,.65);': 'background: white;'">
|
||||
<a-list item-layout="horizontal" :style="themeSwitcher.textStyle">
|
||||
<a-tabs class="ant-card-dark-box-nohover" default-active-key="tpl-1" :class="themeSwitcher.darkCardClass" style="padding: 20px 20px;">
|
||||
<a-tab-pane key="tpl-1" tab='{{ i18n "pages.settings.templates.basicTemplate"}}' style="padding-top: 20px;">
|
||||
<a-collapse>
|
||||
@@ -169,11 +167,11 @@
|
||||
<setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigAds"}}' desc='{{ i18n "pages.settings.templates.xrayConfigAdsDesc"}}' v-model="AdsSettings"></setting-list-item>
|
||||
<setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigFamily"}}' desc='{{ i18n "pages.settings.templates.xrayConfigFamilyDesc"}}' v-model="familyProtectSettings"></setting-list-item>
|
||||
</a-collapse-panel>
|
||||
<a-collapse-panel header='{{ i18n "pages.settings.templates.countryConfigs"}}'>
|
||||
<a-collapse-panel header='{{ i18n "pages.settings.templates.blockCountryConfigs"}}'>
|
||||
<a-row :xs="24" :sm="24" :lg="12">
|
||||
<h2 style="color: inherit; font-weight: bold; font-size: 18px; padding: 10px 20px; border-bottom: 2px solid;">
|
||||
<a-icon type="warning" style="color: inherit; font-size: 24px;"></a-icon>
|
||||
{{ i18n "pages.settings.templates.countryConfigsDesc" }}
|
||||
{{ i18n "pages.settings.templates.blockCountryConfigsDesc" }}
|
||||
</h2>
|
||||
</a-row>
|
||||
<setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigIRIp"}}' desc='{{ i18n "pages.settings.templates.xrayConfigIRIpDesc"}}' v-model="IRIpSettings"></setting-list-item>
|
||||
@@ -182,6 +180,14 @@
|
||||
<setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigChinaDomain"}}' desc='{{ i18n "pages.settings.templates.xrayConfigChinaDomainDesc"}}' v-model="ChinaDomainSettings"></setting-list-item>
|
||||
<setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigRussiaIp"}}' desc='{{ i18n "pages.settings.templates.xrayConfigRussiaIpDesc"}}' v-model="RussiaIpSettings"></setting-list-item>
|
||||
<setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigRussiaDomain"}}' desc='{{ i18n "pages.settings.templates.xrayConfigRussiaDomainDesc"}}' v-model="RussiaDomainSettings"></setting-list-item>
|
||||
</a-collapse-panel>
|
||||
<a-collapse-panel header='{{ i18n "pages.settings.templates.directCountryConfigs"}}'>
|
||||
<a-row :xs="24" :sm="24" :lg="12">
|
||||
<h2 style="color: inherit; font-weight: bold; font-size: 18px; padding: 10px 20px; border-bottom: 2px solid;">
|
||||
<a-icon type="warning" style="color: inherit; font-size: 24px;"></a-icon>
|
||||
{{ i18n "pages.settings.templates.directCountryConfigsDesc" }}
|
||||
</h2>
|
||||
</a-row>
|
||||
<setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigDirectIRIp"}}' desc='{{ i18n "pages.settings.templates.xrayConfigDirectIRIpDesc"}}' v-model="IRIpDirectSettings"></setting-list-item>
|
||||
<setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigDirectIRDomain"}}' desc='{{ i18n "pages.settings.templates.xrayConfigDirectIRDomainDesc"}}' v-model="IRDomainDirectSettings"></setting-list-item>
|
||||
<setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigDirectChinaIp"}}' desc='{{ i18n "pages.settings.templates.xrayConfigDirectChinaIpDesc"}}' v-model="ChinaIpDirectSettings"></setting-list-item>
|
||||
@@ -235,14 +241,14 @@
|
||||
</a-tabs>
|
||||
</a-list>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="4" tab='{{ i18n "pages.settings.TGReminder"}}'>
|
||||
<a-tab-pane key="4" tab='{{ i18n "pages.settings.TGBotSettings"}}'>
|
||||
<a-row :xs="24" :sm="24" :lg="12">
|
||||
<h2 style="color: inherit; font-weight: bold; font-size: 18px; padding: 20px 20px; text-align: center;">
|
||||
<a-icon type="warning" style="color: inherit; font-size: 24px;"></a-icon>
|
||||
{{ i18n "pages.settings.infoDesc" }}
|
||||
</h2>
|
||||
</a-row>
|
||||
<a-list item-layout="horizontal" :style="themeSwitcher.isDarkTheme ? 'color: hsla(0,0%,100%,.65);': 'background: white;'">
|
||||
<a-list item-layout="horizontal" :style="themeSwitcher.textStyle">
|
||||
<setting-list-item type="switch" title='{{ i18n "pages.settings.telegramBotEnable" }}' desc='{{ i18n "pages.settings.telegramBotEnableDesc" }}' v-model="allSetting.tgBotEnable"></setting-list-item>
|
||||
<setting-list-item type="text" title='{{ i18n "pages.settings.telegramToken"}}' desc='{{ i18n "pages.settings.telegramTokenDesc"}}' v-model="allSetting.tgBotToken"></setting-list-item>
|
||||
<setting-list-item type="text" title='{{ i18n "pages.settings.telegramChatId"}}' desc='{{ i18n "pages.settings.telegramChatIdDesc"}}' v-model="allSetting.tgBotChatId"></setting-list-item>
|
||||
@@ -259,6 +265,7 @@
|
||||
</a-layout>
|
||||
{{template "js" .}}
|
||||
{{template "component/themeSwitcher" .}}
|
||||
{{template "component/password" .}}
|
||||
{{template "component/setting"}}
|
||||
<script>
|
||||
|
||||
@@ -273,7 +280,7 @@
|
||||
allSetting: new AllSetting(),
|
||||
saveBtnDisable: true,
|
||||
user: {},
|
||||
lang : getLang(),
|
||||
lang: getLang(),
|
||||
ipv4Settings: {
|
||||
tag: "IPv4",
|
||||
protocol: "freedom",
|
||||
@@ -338,8 +345,8 @@
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
loading(spinning = true, obj) {
|
||||
if (obj == null) this.spinning = spinning;
|
||||
loading(spinning = true) {
|
||||
this.spinning = spinning;
|
||||
},
|
||||
async getAllSetting() {
|
||||
this.loading(true);
|
||||
@@ -400,11 +407,10 @@
|
||||
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){
|
||||
if (!haveRules && outboundIndex > 0) {
|
||||
newTemplateSettings.outbounds.splice(outboundIndex);
|
||||
}
|
||||
if (haveRules && outboundIndex < 0) {
|
||||
|
||||
newTemplateSettings.outbounds.push(setting);
|
||||
}
|
||||
this.templateSettings = newTemplateSettings;
|
||||
@@ -431,7 +437,7 @@
|
||||
const { data, property, outboundTag } = routeSettings;
|
||||
const oldTemplateSettings = this.templateSettings;
|
||||
const newTemplateSettings = oldTemplateSettings;
|
||||
currentProperty = this.templateRuleGetter({outboundTag: outboundTag, property: property})
|
||||
currentProperty = this.templateRuleGetter({ outboundTag, property })
|
||||
if (currentProperty.length == 0) {
|
||||
const propertyRule = {
|
||||
type: "field",
|
||||
@@ -450,7 +456,7 @@
|
||||
routingRule.hasOwnProperty("outboundTag") &&
|
||||
routingRule.outboundTag === outboundTag
|
||||
) {
|
||||
if (!insertedOnce && data.length>0){
|
||||
if (!insertedOnce && data.length > 0) {
|
||||
insertedOnce = true;
|
||||
routingRule[property] = data;
|
||||
newRules.push(routingRule);
|
||||
@@ -514,7 +520,7 @@
|
||||
newTemplateSettings = this.templateSettings;
|
||||
freedomOutboundIndex = newTemplateSettings.outbounds.findIndex((o) => o.protocol === "freedom" && !o.tag);
|
||||
if (!newTemplateSettings.outbounds[freedomOutboundIndex].settings) {
|
||||
newTemplateSettings.outbounds[freedomOutboundIndex].settings = {"domainStrategy": newValue};
|
||||
newTemplateSettings.outbounds[freedomOutboundIndex].settings = { "domainStrategy": newValue };
|
||||
} else {
|
||||
newTemplateSettings.outbounds[freedomOutboundIndex].settings.domainStrategy = newValue;
|
||||
}
|
||||
@@ -533,66 +539,66 @@
|
||||
}
|
||||
},
|
||||
blockedIPs: {
|
||||
get: function() {
|
||||
return this.templateRuleGetter({outboundTag: "blocked", property: "ip"});
|
||||
get: function () {
|
||||
return this.templateRuleGetter({ outboundTag: "blocked", property: "ip" });
|
||||
},
|
||||
set: function(newValue) {
|
||||
this.templateRuleSetter({outboundTag: "blocked", property: "ip", data: newValue});
|
||||
set: function (newValue) {
|
||||
this.templateRuleSetter({ outboundTag: "blocked", property: "ip", data: newValue });
|
||||
}
|
||||
},
|
||||
blockedDomains: {
|
||||
get: function() {
|
||||
return this.templateRuleGetter({outboundTag: "blocked", property: "domain"});
|
||||
get: function () {
|
||||
return this.templateRuleGetter({ outboundTag: "blocked", property: "domain" });
|
||||
},
|
||||
set: function(newValue) {
|
||||
this.templateRuleSetter({outboundTag: "blocked", property: "domain", data: newValue});
|
||||
set: function (newValue) {
|
||||
this.templateRuleSetter({ outboundTag: "blocked", property: "domain", data: newValue });
|
||||
}
|
||||
},
|
||||
blockedProtocols: {
|
||||
get: function() {
|
||||
return this.templateRuleGetter({outboundTag: "blocked", property: "protocol"});
|
||||
get: function () {
|
||||
return this.templateRuleGetter({ outboundTag: "blocked", property: "protocol" });
|
||||
},
|
||||
set: function(newValue) {
|
||||
this.templateRuleSetter({outboundTag: "blocked", property: "protocol", data: newValue});
|
||||
set: function (newValue) {
|
||||
this.templateRuleSetter({ outboundTag: "blocked", property: "protocol", data: newValue });
|
||||
}
|
||||
},
|
||||
directIPs: {
|
||||
get: function() {
|
||||
return this.templateRuleGetter({outboundTag: "direct", property: "ip"});
|
||||
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);
|
||||
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"});
|
||||
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);
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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);
|
||||
return doAllItemsExist(this.settingsData.protocols.bittorrent, this.blockedProtocols);
|
||||
},
|
||||
set: function (newValue) {
|
||||
if (newValue) {
|
||||
@@ -604,7 +610,7 @@
|
||||
},
|
||||
privateIpSettings: {
|
||||
get: function () {
|
||||
return areAllItemsExist(this.settingsData.ips.local,this.blockedIPs);
|
||||
return doAllItemsExist(this.settingsData.ips.local, this.blockedIPs);
|
||||
},
|
||||
set: function (newValue) {
|
||||
if (newValue) {
|
||||
@@ -616,7 +622,7 @@
|
||||
},
|
||||
AdsSettings: {
|
||||
get: function () {
|
||||
return areAllItemsExist(this.settingsData.domains.ads,this.blockedDomains);
|
||||
return doAllItemsExist(this.settingsData.domains.ads, this.blockedDomains);
|
||||
},
|
||||
set: function (newValue) {
|
||||
if (newValue) {
|
||||
@@ -629,7 +635,7 @@
|
||||
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);
|
||||
return doAllItemsExist(this.templateSettings.dns.servers, this.settingsData.familyProtectDNS.servers);
|
||||
},
|
||||
set: function (newValue) {
|
||||
newTemplateSettings = this.templateSettings;
|
||||
@@ -643,10 +649,10 @@
|
||||
},
|
||||
GoogleIPv4Settings: {
|
||||
get: function () {
|
||||
return areAllItemsExist(this.settingsData.domains.google, this.templateRuleGetter({outboundTag: "IPv4", property: "domain"}));
|
||||
return doAllItemsExist(this.settingsData.domains.google, this.templateRuleGetter({ outboundTag: "IPv4", property: "domain" }));
|
||||
},
|
||||
set: function (newValue) {
|
||||
oldData = this.templateRuleGetter({outboundTag: "IPv4", property: "domain"});
|
||||
oldData = this.templateRuleGetter({ outboundTag: "IPv4", property: "domain" });
|
||||
if (newValue) {
|
||||
oldData = [...oldData, ...this.settingsData.domains.google];
|
||||
} else {
|
||||
@@ -662,10 +668,10 @@
|
||||
},
|
||||
NetflixIPv4Settings: {
|
||||
get: function () {
|
||||
return areAllItemsExist(this.settingsData.domains.netflix, this.templateRuleGetter({outboundTag: "IPv4", property: "domain"}));
|
||||
return doAllItemsExist(this.settingsData.domains.netflix, this.templateRuleGetter({ outboundTag: "IPv4", property: "domain" }));
|
||||
},
|
||||
set: function (newValue) {
|
||||
oldData = this.templateRuleGetter({outboundTag: "IPv4", property: "domain"});
|
||||
oldData = this.templateRuleGetter({ outboundTag: "IPv4", property: "domain" });
|
||||
if (newValue) {
|
||||
oldData = [...oldData, ...this.settingsData.domains.netflix];
|
||||
} else {
|
||||
@@ -681,7 +687,7 @@
|
||||
},
|
||||
IRIpSettings: {
|
||||
get: function () {
|
||||
return areAllItemsExist(this.settingsData.ips.ir,this.blockedIPs);
|
||||
return doAllItemsExist(this.settingsData.ips.ir, this.blockedIPs);
|
||||
},
|
||||
set: function (newValue) {
|
||||
if (newValue) {
|
||||
@@ -693,7 +699,7 @@
|
||||
},
|
||||
IRDomainSettings: {
|
||||
get: function () {
|
||||
return areAllItemsExist(this.settingsData.domains.ir,this.blockedDomains);
|
||||
return doAllItemsExist(this.settingsData.domains.ir, this.blockedDomains);
|
||||
},
|
||||
set: function (newValue) {
|
||||
if (newValue) {
|
||||
@@ -705,7 +711,7 @@
|
||||
},
|
||||
ChinaIpSettings: {
|
||||
get: function () {
|
||||
return areAllItemsExist(this.settingsData.ips.cn,this.blockedIPs);
|
||||
return doAllItemsExist(this.settingsData.ips.cn, this.blockedIPs);
|
||||
},
|
||||
set: function (newValue) {
|
||||
if (newValue) {
|
||||
@@ -717,7 +723,7 @@
|
||||
},
|
||||
ChinaDomainSettings: {
|
||||
get: function () {
|
||||
return areAllItemsExist(this.settingsData.domains.cn,this.blockedDomains);
|
||||
return doAllItemsExist(this.settingsData.domains.cn, this.blockedDomains);
|
||||
},
|
||||
set: function (newValue) {
|
||||
if (newValue) {
|
||||
@@ -729,7 +735,7 @@
|
||||
},
|
||||
RussiaIpSettings: {
|
||||
get: function () {
|
||||
return areAllItemsExist(this.settingsData.ips.ru,this.blockedIPs);
|
||||
return doAllItemsExist(this.settingsData.ips.ru, this.blockedIPs);
|
||||
},
|
||||
set: function (newValue) {
|
||||
if (newValue) {
|
||||
@@ -741,7 +747,7 @@
|
||||
},
|
||||
RussiaDomainSettings: {
|
||||
get: function () {
|
||||
return areAllItemsExist(this.settingsData.domains.ru,this.blockedDomains);
|
||||
return doAllItemsExist(this.settingsData.domains.ru, this.blockedDomains);
|
||||
},
|
||||
set: function (newValue) {
|
||||
if (newValue) {
|
||||
@@ -753,7 +759,7 @@
|
||||
},
|
||||
IRIpDirectSettings: {
|
||||
get: function () {
|
||||
return areAllItemsExist(this.settingsData.ips.ir,this.directIPs);
|
||||
return doAllItemsExist(this.settingsData.ips.ir, this.directIPs);
|
||||
},
|
||||
set: function (newValue) {
|
||||
if (newValue) {
|
||||
@@ -765,7 +771,7 @@
|
||||
},
|
||||
IRDomainDirectSettings: {
|
||||
get: function () {
|
||||
return areAllItemsExist(this.settingsData.domains.ir,this.directDomains);
|
||||
return doAllItemsExist(this.settingsData.domains.ir, this.directDomains);
|
||||
},
|
||||
set: function (newValue) {
|
||||
if (newValue) {
|
||||
@@ -777,7 +783,7 @@
|
||||
},
|
||||
ChinaIpDirectSettings: {
|
||||
get: function () {
|
||||
return areAllItemsExist(this.settingsData.ips.cn,this.directIPs);
|
||||
return doAllItemsExist(this.settingsData.ips.cn, this.directIPs);
|
||||
},
|
||||
set: function (newValue) {
|
||||
if (newValue) {
|
||||
@@ -789,7 +795,7 @@
|
||||
},
|
||||
ChinaDomainDirectSettings: {
|
||||
get: function () {
|
||||
return areAllItemsExist(this.settingsData.domains.cn,this.directDomains);
|
||||
return doAllItemsExist(this.settingsData.domains.cn, this.directDomains);
|
||||
},
|
||||
set: function (newValue) {
|
||||
if (newValue) {
|
||||
@@ -801,7 +807,7 @@
|
||||
},
|
||||
RussiaIpDirectSettings: {
|
||||
get: function () {
|
||||
return areAllItemsExist(this.settingsData.ips.ru,this.directIPs);
|
||||
return doAllItemsExist(this.settingsData.ips.ru, this.directIPs);
|
||||
},
|
||||
set: function (newValue) {
|
||||
if (newValue) {
|
||||
@@ -813,7 +819,7 @@
|
||||
},
|
||||
RussiaDomainDirectSettings: {
|
||||
get: function () {
|
||||
return areAllItemsExist(this.settingsData.domains.ru,this.directDomains);
|
||||
return doAllItemsExist(this.settingsData.domains.ru, this.directDomains);
|
||||
},
|
||||
set: function (newValue) {
|
||||
if (newValue) {
|
||||
@@ -822,9 +828,9 @@
|
||||
this.directDomains = this.directDomains.filter(data => !this.settingsData.domains.ru.includes(data));
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -3,22 +3,18 @@
|
||||
"loglevel": "warning"
|
||||
},
|
||||
"api": {
|
||||
"services": [
|
||||
"HandlerService",
|
||||
"LoggerService",
|
||||
"StatsService"
|
||||
],
|
||||
"tag": "api"
|
||||
"tag": "api",
|
||||
"services": ["HandlerService", "LoggerService", "StatsService"]
|
||||
},
|
||||
"inbounds": [
|
||||
{
|
||||
"tag": "api",
|
||||
"listen": "127.0.0.1",
|
||||
"port": 62789,
|
||||
"protocol": "dokodemo-door",
|
||||
"settings": {
|
||||
"address": "127.0.0.1"
|
||||
},
|
||||
"tag": "api"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
@@ -27,16 +23,16 @@
|
||||
"settings": {}
|
||||
},
|
||||
{
|
||||
"tag": "blocked",
|
||||
"protocol": "blackhole",
|
||||
"settings": {},
|
||||
"tag": "blocked"
|
||||
"settings": {}
|
||||
}
|
||||
],
|
||||
"policy": {
|
||||
"levels": {
|
||||
"0": {
|
||||
"statsUserUplink": true,
|
||||
"statsUserDownlink": true
|
||||
"statsUserDownlink": true,
|
||||
"statsUserUplink": true
|
||||
}
|
||||
},
|
||||
"system": {
|
||||
@@ -48,25 +44,19 @@
|
||||
"domainStrategy": "IPIfNonMatch",
|
||||
"rules": [
|
||||
{
|
||||
"inboundTag": [
|
||||
"api"
|
||||
],
|
||||
"outboundTag": "api",
|
||||
"type": "field"
|
||||
"type": "field",
|
||||
"inboundTag": ["api"],
|
||||
"outboundTag": "api"
|
||||
},
|
||||
{
|
||||
"ip": [
|
||||
"geoip:private"
|
||||
],
|
||||
"type": "field",
|
||||
"outboundTag": "blocked",
|
||||
"type": "field"
|
||||
"ip": ["geoip:private"]
|
||||
},
|
||||
{
|
||||
"type": "field",
|
||||
"outboundTag": "blocked",
|
||||
"protocol": [
|
||||
"bittorrent"
|
||||
],
|
||||
"type": "field"
|
||||
"protocol": ["bittorrent"]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -38,7 +38,7 @@ func (s *SubService) GetSubs(subId string, host string) ([]string, string, error
|
||||
continue
|
||||
}
|
||||
for _, client := range clients {
|
||||
if client.SubID == subId {
|
||||
if client.Enable && client.SubID == subId {
|
||||
link := s.getLink(inbound, client.Email)
|
||||
result = append(result, link)
|
||||
clientTraffics = append(clientTraffics, s.getClientTraffics(inbound.ClientStats, client.Email))
|
||||
@@ -73,7 +73,7 @@ func (s *SubService) GetSubs(subId string, host string) ([]string, string, error
|
||||
func (s *SubService) getInboundsBySubId(subId string) ([]*model.Inbound, error) {
|
||||
db := database.GetDB()
|
||||
var inbounds []*model.Inbound
|
||||
err := db.Model(model.Inbound{}).Preload("ClientStats").Where("settings like ?", fmt.Sprintf(`%%"subId": "%s"%%`, subId)).Find(&inbounds).Error
|
||||
err := db.Model(model.Inbound{}).Preload("ClientStats").Where("settings like ? and enable = ?", fmt.Sprintf(`%%"subId": "%s"%%`, subId), true).Find(&inbounds).Error
|
||||
if err != nil && err != gorm.ErrRecordNotFound {
|
||||
return nil, err
|
||||
}
|
||||
@@ -107,9 +107,10 @@ func (s *SubService) genVmessLink(inbound *model.Inbound, email string) string {
|
||||
if inbound.Protocol != model.VMess {
|
||||
return ""
|
||||
}
|
||||
remark := fmt.Sprintf("%s-%s", inbound.Remark, email)
|
||||
obj := map[string]interface{}{
|
||||
"v": "2",
|
||||
"ps": email,
|
||||
"ps": remark,
|
||||
"add": s.address,
|
||||
"port": inbound.Port,
|
||||
"type": "none",
|
||||
@@ -353,7 +354,8 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
|
||||
// Set the new query values on the URL
|
||||
url.RawQuery = q.Encode()
|
||||
|
||||
url.Fragment = email
|
||||
remark := fmt.Sprintf("%s-%s", inbound.Remark, email)
|
||||
url.Fragment = remark
|
||||
return url.String()
|
||||
}
|
||||
|
||||
@@ -502,7 +504,8 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string
|
||||
// Set the new query values on the URL
|
||||
url.RawQuery = q.Encode()
|
||||
|
||||
url.Fragment = email
|
||||
remark := fmt.Sprintf("%s-%s", inbound.Remark, email)
|
||||
url.Fragment = remark
|
||||
return url.String()
|
||||
}
|
||||
|
||||
@@ -583,7 +586,8 @@ func (s *SubService) genShadowsocksLink(inbound *model.Inbound, email string) st
|
||||
// Set the new query values on the URL
|
||||
url.RawQuery = q.Encode()
|
||||
|
||||
url.Fragment = clients[clientIndex].Email
|
||||
remark := fmt.Sprintf("%s-%s", inbound.Remark, clients[clientIndex].Email)
|
||||
url.Fragment = remark
|
||||
return url.String()
|
||||
}
|
||||
|
||||
|
||||
@@ -22,11 +22,11 @@
|
||||
"unlimited" = "Unlimited"
|
||||
"none" = "None"
|
||||
"qrCode" = "QR Code"
|
||||
"info" = "More information"
|
||||
"info" = "More Information"
|
||||
"edit" = "Edit"
|
||||
"delete" = "Delete"
|
||||
"reset" = "Reset"
|
||||
"copySuccess" = "Copy successfully"
|
||||
"copySuccess" = "Copied successfully"
|
||||
"sure" = "Sure"
|
||||
"encryption" = "Encryption"
|
||||
"transmission" = "Transmission"
|
||||
@@ -42,7 +42,7 @@
|
||||
"additional" = "Alter ID"
|
||||
"monitor" = "Listen IP"
|
||||
"certificate" = "Certificate"
|
||||
"fail" = "Fail"
|
||||
"fail" = " Fail"
|
||||
"success" = " Success"
|
||||
"getVersion" = "Get version"
|
||||
"install" = "Install"
|
||||
@@ -52,8 +52,8 @@
|
||||
[menu]
|
||||
"dashboard" = "System Status"
|
||||
"inbounds" = "Inbounds"
|
||||
"setting" = "Panel Setting"
|
||||
"logout" = "LogOut"
|
||||
"settings" = "Panel Settings"
|
||||
"logout" = "Logout"
|
||||
"link" = "Other"
|
||||
|
||||
[pages.login]
|
||||
@@ -61,113 +61,114 @@
|
||||
"loginAgain" = "The login time limit has expired, please log in again"
|
||||
|
||||
[pages.login.toasts]
|
||||
"invalidFormData" = "Input Data Format Is Invalid"
|
||||
"emptyUsername" = "Please Enter Username"
|
||||
"emptyPassword" = "Please Enter Password"
|
||||
"wrongUsernameOrPassword" = "Invalid username or password"
|
||||
"invalidFormData" = "Input data format is invalid."
|
||||
"emptyUsername" = "Please enter username."
|
||||
"emptyPassword" = "Please enter password."
|
||||
"wrongUsernameOrPassword" = "Invalid username or password."
|
||||
"successLogin" = "Login"
|
||||
|
||||
[pages.index]
|
||||
"title" = "System status"
|
||||
"title" = "System Status"
|
||||
"memory" = "Memory"
|
||||
"hard" = "Hard disk"
|
||||
"xrayStatus" = "xray Status"
|
||||
"hard" = "Hard Disk"
|
||||
"xrayStatus" = "Xray Status"
|
||||
"stopXray" = "Stop"
|
||||
"restartXray" = "Restart"
|
||||
"xraySwitch" = "Switch Version"
|
||||
"xraySwitchClick" = "Click on the version you want to switch"
|
||||
"xraySwitchClickDesk" = "Please choose carefully, older versions may have incompatible configurations"
|
||||
"xraySwitchClick" = "Choose the version you want to switch to."
|
||||
"xraySwitchClickDesk" = "Choose wisely, as older versions may not be compatible with current configurations."
|
||||
"operationHours" = "Operation Hours"
|
||||
"operationHoursDesc" = "The running time of the system since it was started"
|
||||
"operationHoursDesc" = "System uptime: time since startup."
|
||||
"systemLoad" = "System Load"
|
||||
"connectionCount" = "Connection Count"
|
||||
"connectionCountDesc" = "The total number of connections for all network cards"
|
||||
"upSpeed" = "Total upload speed for all network cards"
|
||||
"downSpeed" = "Total download speed for all network cards"
|
||||
"totalSent" = "Total upload traffic of all network cards since system startup"
|
||||
"totalReceive" = "Total download traffic of all network cards since system startup"
|
||||
"xraySwitchVersionDialog" = "Switch xray version"
|
||||
"xraySwitchVersionDialogDesc" = "Whether to switch the xray version to"
|
||||
"dontRefreshh" = "Installation is in progress, please do not refresh this page"
|
||||
"connectionCountDesc" = "Total connections across all network cards."
|
||||
"upSpeed" = "Total upload speed for all network cards."
|
||||
"downSpeed" = "Total download speed for all network cards."
|
||||
"totalSent" = "Total upload traffic of all network cards since system startup."
|
||||
"totalReceive" = "Total download data across all network cards since system startup."
|
||||
"xraySwitchVersionDialog" = "Switch Xray Version"
|
||||
"xraySwitchVersionDialogDesc" = "Are you sure you want to switch the Xray version to"
|
||||
"dontRefresh" = "Installation is in progress, please do not refresh this page."
|
||||
"logs" = "Logs"
|
||||
"config" = "Config"
|
||||
"backup" = "Backup"
|
||||
"backupTitle" = "Backup Database"
|
||||
"backupDescription" = "Remember to backup before importing a new database"
|
||||
"backup" = "Backup & Restore"
|
||||
"backupTitle" = "Backup & Restore Database"
|
||||
"backupDescription" = "Remember to backup before importing a new database."
|
||||
"exportDatabase" = "Download Database"
|
||||
"importDatabase" = "Upload Database"
|
||||
|
||||
[pages.inbounds]
|
||||
"title" = "Inbounds"
|
||||
"totalDownUp" = "Total uploads/downloads"
|
||||
"totalUsage" = "Total usage"
|
||||
"inboundCount" = "Number of inbound"
|
||||
"operate" = "Operate"
|
||||
"totalDownUp" = "Total Uploads/Downloads"
|
||||
"totalUsage" = "Total Usage"
|
||||
"inboundCount" = "Number of Inbounds"
|
||||
"operate" = "Menu"
|
||||
"enable" = "Enable"
|
||||
"remark" = "Remark"
|
||||
"protocol" = "Protocol"
|
||||
"port" = "Port"
|
||||
"traffic" = "Traffic"
|
||||
"details" = "Details"
|
||||
"transportConfig" = "Transport config"
|
||||
"expireDate" = "Expire date"
|
||||
"resetTraffic" = "Reset traffic"
|
||||
"transportConfig" = "Transport Config"
|
||||
"expireDate" = "Expire Date"
|
||||
"resetTraffic" = "Reset Traffic"
|
||||
"addInbound" = "Add Inbound"
|
||||
"generalActions" = "General Actions"
|
||||
"create" = "Create"
|
||||
"update" = "Update"
|
||||
"modifyInbound" = "Modify InBound"
|
||||
"modifyInbound" = "Modify Inbound"
|
||||
"deleteInbound" = "Delete Inbound"
|
||||
"deleteInboundContent" = "Are you sure you want to delete inbound?"
|
||||
"resetTrafficContent" = "Are you sure you want to reset traffic?"
|
||||
"copyLink" = "Copy Link"
|
||||
"address" = "Address"
|
||||
"network" = "Network"
|
||||
"destinationPort" = "Destination port"
|
||||
"targetAddress" = "Target address"
|
||||
"disableInsecureEncryption" = "Disable insecure encryption"
|
||||
"destinationPort" = "Destination Port"
|
||||
"targetAddress" = "Target Address"
|
||||
"disableInsecureEncryption" = "Disable Insecure Encryption"
|
||||
"monitorDesc" = "Leave blank by default"
|
||||
"meansNoLimit" = "Means no limit"
|
||||
"totalFlow" = "Total flow"
|
||||
"meansNoLimit" = "Means No Limit"
|
||||
"totalFlow" = "Total Flow"
|
||||
"leaveBlankToNeverExpire" = "Leave blank to never expire"
|
||||
"noRecommendKeepDefault" = "There are no special requirements to keep the default"
|
||||
"certificatePath" = "Certificate file path"
|
||||
"certificateContent" = "Certificate file content"
|
||||
"publicKeyPath" = "Public key file path"
|
||||
"publicKeyContent" = "Public key content"
|
||||
"keyPath" = "Key file path"
|
||||
"keyContent" = "Key content"
|
||||
"noRecommendKeepDefault" = "No special requirements to keep the default"
|
||||
"certificatePath" = "Certificate File Path"
|
||||
"certificateContent" = "Certificate File Content"
|
||||
"publicKeyPath" = "Public Key Path"
|
||||
"publicKeyContent" = "Public Key Content"
|
||||
"keyPath" = "Private Key Path"
|
||||
"keyContent" = "Private Key Content"
|
||||
"clickOnQRcode" = "Click on QR Code to Copy"
|
||||
"client" = "Client"
|
||||
"export" = "Export Links"
|
||||
"Clone" = "Clone"
|
||||
"cloneInbound" = "Create"
|
||||
"cloneInboundContent" = "All items of this inbound except Port, Listening IP, Clients will be applied to the clone"
|
||||
"clone" = "Clone"
|
||||
"cloneInbound" = "Clone"
|
||||
"cloneInboundContent" = "All settings of this inbound, except for Port, Listening IP, and Clients, will be applied to the clone."
|
||||
"cloneInboundOk" = "Clone"
|
||||
"resetAllTraffic" = "Reset All Inbounds Traffic"
|
||||
"resetAllTrafficTitle" = "Reset all inbounds traffic"
|
||||
"resetAllTrafficContent" = "Are you sure to reset all inbounds traffic ?"
|
||||
"resetAllTrafficContent" = "Are you sure you want to reset all inbounds traffic?"
|
||||
"resetInboundClientTraffics" = "Reset Clients Traffic"
|
||||
"resetInboundClientTrafficTitle" = "Reset all clients traffic"
|
||||
"resetInboundClientTrafficContent" = "Are you sure to reset all traffics of this inbound's clients ?"
|
||||
"resetInboundClientTrafficContent" = "Are you sure you want to reset all traffic for this inbound's clients?"
|
||||
"resetAllClientTraffics" = "Reset All Clients Traffic"
|
||||
"resetAllClientTrafficTitle" = "Reset all clients traffic"
|
||||
"resetAllClientTrafficContent" = "Are you sure to reset all traffics of all clients ?"
|
||||
"delDepletedClients" = "Delete depleted clients"
|
||||
"resetAllClientTrafficContent" = "Are you sure you want to reset all traffics for all clients?"
|
||||
"delDepletedClients" = "Delete Depleted Clients"
|
||||
"delDepletedClientsTitle" = "Delete depleted clients"
|
||||
"delDepletedClientsContent" = "Are you sure to delete all depleted clients ?"
|
||||
"delDepletedClientsContent" = "Are you sure you want to delete all depleted clients?"
|
||||
"email" = "Email"
|
||||
"emailDesc" = "The Email Must Be Completely Unique"
|
||||
"emailDesc" = "Please provide a unique email address."
|
||||
"setDefaultCert" = "Set cert from panel"
|
||||
"telegramDesc" = "use Telegram ID without @ or chat IDs ( you can get it here @userinfobot )"
|
||||
"subscriptionDesc" = "you can find your sub link on Details, also ou can use the same name for several configurations"
|
||||
"subscriptionDesc" = "you can find your sub link on Details, also you can use the same name for several configurations"
|
||||
|
||||
[pages.client]
|
||||
"add" = "Add client"
|
||||
"edit" = "Edit client"
|
||||
"submitAdd" = "Add client"
|
||||
"add" = "Add Client"
|
||||
"edit" = "Edit Client"
|
||||
"submitAdd" = "Add Client"
|
||||
"submitEdit" = "Save changes"
|
||||
"clientCount" = "Number of clients"
|
||||
"bulk" = "Add bulk"
|
||||
"clientCount" = "Number of Clients"
|
||||
"bulk" = "Add Bulk"
|
||||
"method" = "Method"
|
||||
"first" = "First"
|
||||
"last" = "Last"
|
||||
@@ -198,50 +199,50 @@
|
||||
"encryption" = "Encryption"
|
||||
|
||||
[pages.settings]
|
||||
"title" = "Setting"
|
||||
"title" = "Settings"
|
||||
"save" = "Save"
|
||||
"infoDesc" = "Every change here needs to be saved and restart panel to take effect"
|
||||
"restartPanel" = "Restart Panel"
|
||||
"restartPanelDesc" = "Are you sure you want to restart the panel? Click OK to restart after 3 seconds. If you cannot access the panel after restarting, please go to the server to view the panel log information"
|
||||
"infoDesc" = "Every change made here needs to be saved. Please restart the panel for the changes to take effect."
|
||||
"restartPanel" = "Restart Panel "
|
||||
"restartPanelDesc" = "Are you sure you want to restart the panel? Click OK to restart after 3 seconds. If you cannot access the panel after restarting, please view the panel log information on the server."
|
||||
"resetDefaultConfig" = "Reset to default config"
|
||||
"panelConfig" = "Panel Configuration"
|
||||
"userSetting" = "User Setting"
|
||||
"xrayConfiguration" = "xray Configuration"
|
||||
"TGReminder" = "TG Reminder Related Settings"
|
||||
"panelListeningIP" = "Panel listening IP"
|
||||
"panelListeningIPDesc" = "Leave blank by default to monitor all IPs"
|
||||
"panelConfig" = "Panel Configurations"
|
||||
"userSettings" = "User Settings"
|
||||
"xrayConfiguration" = "Xray Configurations"
|
||||
"TGBotSettings" = "Telegram Bot Settings"
|
||||
"panelListeningIP" = "Panel Listening IP"
|
||||
"panelListeningIPDesc" = "Leave blank by default to monitor all IPs."
|
||||
"panelPort" = "Panel Port"
|
||||
"panelPortDesc" = "Port number for serving this panel"
|
||||
"publicKeyPath" = "Panel certificate public key file path"
|
||||
"panelPortDesc" = "Port number for serving the panel."
|
||||
"publicKeyPath" = "Panel Certificate Public Key File Path"
|
||||
"publicKeyPathDesc" = "Fill in an absolute path starting with '/'"
|
||||
"privateKeyPath" = "Panel certificate private key file path"
|
||||
"privateKeyPath" = "Panel Certificate Private Key File Path"
|
||||
"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 '/'"
|
||||
"oldUsername" = "Current Username"
|
||||
"currentPassword" = "Current Password"
|
||||
"newUsername" = "New Username"
|
||||
"newPassword" = "New Password"
|
||||
"telegramBotEnable" = "Enable telegram bot"
|
||||
"telegramBotEnableDesc" = "Your telegram bot will interact with panel"
|
||||
"telegramBotEnable" = "Enable Telegram bot"
|
||||
"telegramBotEnableDesc" = "Your telegram bot will interact with the panel"
|
||||
"telegramToken" = "Telegram Token"
|
||||
"telegramTokenDesc" = "The Token you have got from BotFather"
|
||||
"telegramChatId" = "Telegram Admin ChatIds"
|
||||
"telegramChatIdDesc" = "Multi chatIDs separated by comma"
|
||||
"telegramTokenDesc" = "The Token you have got from @BotFather"
|
||||
"telegramChatId" = "Telegram Admin ChatIDs"
|
||||
"telegramChatIdDesc" = "Multi chatIDs separated by comma."
|
||||
"telegramNotifyTime" = "Telegram bot notification time"
|
||||
"telegramNotifyTimeDesc" = "Using Crontab timing format"
|
||||
"tgNotifyBackup" = "Database backup"
|
||||
"tgNotifyBackupDesc" = "Sending database backup file with report notification"
|
||||
"telegramNotifyTimeDesc" = "Use Crontab timing format."
|
||||
"tgNotifyBackup" = "Database Backup"
|
||||
"tgNotifyBackupDesc" = "Send database backup file with report notification"
|
||||
"sessionMaxAge" = "Session maximum age"
|
||||
"sessionMaxAgeDesc" = "The time that you can stay login (unit: minute)"
|
||||
"expireTimeDiff" = "Exhaustion time threshold"
|
||||
"expireTimeDiffDesc" = "Detect exhaustion before expiration (unit:day)"
|
||||
"trafficDiff" = "Exhaustion traffic threshold"
|
||||
"trafficDiffDesc" = "Detect exhaustion before finishing traffic (unit:GB)"
|
||||
"expireTimeDiff" = "Expiration threshold for notification"
|
||||
"expireTimeDiffDesc" = "Get notified about account expiration before the threshold (unit: day)"
|
||||
"trafficDiff" = "Traffic threshold for notification"
|
||||
"trafficDiffDesc" = "Get notified about traffic exhaustion before reaching the threshold (unit: GB)"
|
||||
"tgNotifyCpu" = "CPU percentage alert threshold"
|
||||
"tgNotifyCpuDesc" = "This telegram bot will send you a notification if CPU usage is more than this percentage (unit:%)"
|
||||
"tgNotifyCpuDesc" = "Receive notification if CPU usage exceeds this threshold (unit: %)"
|
||||
"timeZone" = "Time Zone"
|
||||
"timeZoneDesc" = "The scheduled task runs according to the time in the time zone"
|
||||
"timeZoneDesc" = "Scheduled tasks run according to the time in this time zone."
|
||||
|
||||
[pages.settings.templates]
|
||||
"title" = "Templates"
|
||||
@@ -249,71 +250,73 @@
|
||||
"advancedTemplate" = "Advanced Template"
|
||||
"completeTemplate" = "Complete Template"
|
||||
"generalConfigs" = "General Configs"
|
||||
"generalConfigsDesc" = "These options will provide general adjustmets"
|
||||
"generalConfigsDesc" = "These options will provide general adjustments."
|
||||
"blockConfigs" = "Blocking Configs"
|
||||
"blockConfigsDesc" = "These options will prevent users from connecting to specific protocols and websites"
|
||||
"countryConfigs" = "Country Configs"
|
||||
"countryConfigsDesc" = "These options will configure connecting to specific country domains"
|
||||
"blockConfigsDesc" = "These options will prevent users from connecting to specific protocols and websites."
|
||||
"blockCountryConfigs" = "Block Country Configs"
|
||||
"blockCountryConfigsDesc" = "These options will prevent users from connecting to specific country domains."
|
||||
"directCountryConfigs" = "Direct Country Configs"
|
||||
"directCountryConfigsDesc" = "These options will connect users directly to specific country domains."
|
||||
"ipv4Configs" = "IPv4 Configs"
|
||||
"ipv4ConfigsDesc" = "These options will route to target domains only via IPv4"
|
||||
"ipv4ConfigsDesc" = "These options will route to target domains only via IPv4."
|
||||
"xrayConfigTemplate" = "Xray Configuration Template"
|
||||
"xrayConfigTemplateDesc" = "Generate the final Xray configuration file based on this template"
|
||||
"xrayConfigFreedomStrategy" = "Configure Freedom protocol's strategy"
|
||||
"xrayConfigFreedomStrategyDesc" = "Set output strategy of network in freedom protocol"
|
||||
"xrayConfigRoutingStrategy" = "Configure domain strategy of Routing"
|
||||
"xrayConfigRoutingStrategyDesc" = "Set overall Routing strategy of DNS resolving"
|
||||
"xrayConfigTemplateDesc" = "Generate the final Xray configuration file based on this template."
|
||||
"xrayConfigFreedomStrategy" = "Configure Strategy for Freedom Protocol"
|
||||
"xrayConfigFreedomStrategyDesc" = "Set the output strategy of the network in the Freedom Protocol."
|
||||
"xrayConfigRoutingStrategy" = "Configure Domains Routing Strategy"
|
||||
"xrayConfigRoutingStrategyDesc" = "Set the overall routing strategy for DNS resolving."
|
||||
"xrayConfigTorrent" = "Ban BitTorrent Usage"
|
||||
"xrayConfigTorrentDesc" = "Change the configuration template to avoid using BitTorrent by users"
|
||||
"xrayConfigTorrentDesc" = "Change the configuration template to avoid using BitTorrent by users."
|
||||
"xrayConfigPrivateIp" = "Ban Private IP Ranges to Connect"
|
||||
"xrayConfigPrivateIpDesc" = "Change the configuration template to avoid connecting to private IP ranges"
|
||||
"xrayConfigPrivateIpDesc" = "Change the configuration template to avoid connecting to private IP ranges."
|
||||
"xrayConfigAds" = "Block Ads"
|
||||
"xrayConfigAdsDesc" = "Change the configuration template to block ads"
|
||||
"xrayConfigFamily" = "Enable Family friendly config"
|
||||
"xrayConfigFamilyDesc" = "Avoid connecting to unsafe websites for family"
|
||||
"xrayConfigFamily" = "Enable Family-Friendly Configuration"
|
||||
"xrayConfigFamilyDesc" = "Avoid connecting to unsafe websites for family protection."
|
||||
"xrayConfigIRIp" = "Disable connection to Iran IP ranges"
|
||||
"xrayConfigIRIpDesc" = "Change the configuration template to avoid connecting to Iran IP ranges"
|
||||
"xrayConfigIRIpDesc" = "Change the configuration template to avoid connecting to Iran IP ranges."
|
||||
"xrayConfigIRDomain" = "Disable connection to Iran domains"
|
||||
"xrayConfigIRDomainDesc" = "Change the configuration template to avoid connecting to Iran domains"
|
||||
"xrayConfigIRDomainDesc" = "Change the configuration template to avoid connecting to Iran domains."
|
||||
"xrayConfigChinaIp" = "Disable connection to China IP ranges"
|
||||
"xrayConfigChinaIpDesc" = "Change the configuration template to avoid connecting to China IP ranges"
|
||||
"xrayConfigChinaIpDesc" = "Change the configuration template to avoid connecting to China IP ranges."
|
||||
"xrayConfigChinaDomain" = "Disable connection to China domains"
|
||||
"xrayConfigChinaDomainDesc" = "Change the configuration template to avoid connecting to China domains"
|
||||
"xrayConfigChinaDomainDesc" = "Change the configuration template to avoid connecting to China domains."
|
||||
"xrayConfigRussiaIp" = "Disable connection to Russia IP ranges"
|
||||
"xrayConfigRussiaIpDesc" = "Change the configuration template to avoid connecting to Russia IP ranges"
|
||||
"xrayConfigRussiaIpDesc" = "Change the configuration template to avoid connecting to Russia IP ranges."
|
||||
"xrayConfigRussiaDomain" = "Disable connection to Russia domains"
|
||||
"xrayConfigRussiaDomainDesc" = "Change the configuration template to avoid connecting to Russia domains"
|
||||
"xrayConfigRussiaDomainDesc" = "Change the configuration template to avoid connecting to Russia domains."
|
||||
"xrayConfigDirectIRIp" = "Direct connection to Iran IP ranges"
|
||||
"xrayConfigDirectIRIpDesc" = "Change the configuration template for direct connecting to Iran IP ranges"
|
||||
"xrayConfigDirectIRIpDesc" = "Change the configuration template for direct connecting to Iran IP ranges."
|
||||
"xrayConfigDirectIRDomain" = "Direct connection to Iran domains"
|
||||
"xrayConfigDirectIRDomainDesc" = "Change the configuration template for direct connecting to Iran domains"
|
||||
"xrayConfigDirectIRDomainDesc" = "Change the configuration template for direct connecting to Iran domains."
|
||||
"xrayConfigDirectChinaIp" = "Direct connection to China IP ranges"
|
||||
"xrayConfigDirectChinaIpDesc" = "Change the configuration template for direct connecting to China IP ranges"
|
||||
"xrayConfigDirectChinaIpDesc" = "Change the configuration template for direct connecting to China IP ranges."
|
||||
"xrayConfigDirectChinaDomain" = "Direct connection to China domains"
|
||||
"xrayConfigDirectChinaDomainDesc" = "Change the configuration template for direct connecting to China domains"
|
||||
"xrayConfigDirectChinaDomainDesc" = "Change the configuration template for direct connecting to China domains."
|
||||
"xrayConfigDirectRussiaIp" = "Direct connection to Russia IP ranges"
|
||||
"xrayConfigDirectRussiaIpDesc" = "Change the configuration template for direct connecting to Russia IP ranges"
|
||||
"xrayConfigDirectRussiaIpDesc" = "Change the configuration template for direct connecting to Russia IP ranges."
|
||||
"xrayConfigDirectRussiaDomain" = "Direct connection to Russia domains"
|
||||
"xrayConfigDirectRussiaDomainDesc" = "Change the configuration template for direct connecting to Russia domains"
|
||||
"xrayConfigDirectRussiaDomainDesc" = "Change the configuration template for direct connecting to Russia domains."
|
||||
"xrayConfigGoogleIPv4" = "Use IPv4 for Google"
|
||||
"xrayConfigGoogleIPv4Desc" = "Add routing for Google to connect with IPv4"
|
||||
"xrayConfigGoogleIPv4Desc" = "Add routing for Google to connect with IPv4."
|
||||
"xrayConfigNetflixIPv4" = "Use IPv4 for Netflix"
|
||||
"xrayConfigNetflixIPv4Desc" = "Add routing for Netflix to connect with IPv4"
|
||||
"xrayConfigNetflixIPv4Desc" = "Add routing for Netflix to connect with IPv4."
|
||||
"xrayConfigInbounds" = "Configuration of Inbounds"
|
||||
"xrayConfigInboundsDesc" = "Change the configuration template to accept specific clients"
|
||||
"xrayConfigInboundsDesc" = "Change the configuration template to accept specific clients."
|
||||
"xrayConfigOutbounds" = "Configuration of Outbounds"
|
||||
"xrayConfigOutboundsDesc" = "Change the configuration template to define outgoing ways for this server"
|
||||
"xrayConfigOutboundsDesc" = "Change the configuration template to define outgoing ways for this server."
|
||||
"xrayConfigRoutings" = "Configuration of routing rules"
|
||||
"xrayConfigRoutingsDesc" = "Change the configuration template to define routing rules for this server"
|
||||
"manualLists" = "Manual lists"
|
||||
"manualListsDesc" = "PLease use JSON array format"
|
||||
"manualBlockedIPs" = "List of blocked IPs"
|
||||
"manualBlockedDomains" = "List of blocked domains"
|
||||
"manualDirectIPs" = "List of direct IPs"
|
||||
"manualDirectDomains" = "List of direct domains"
|
||||
"xrayConfigRoutingsDesc" = "Change the configuration template to define routing rules for this server."
|
||||
"manualLists" = "Manual Lists"
|
||||
"manualListsDesc" = "Please use the JSON array format."
|
||||
"manualBlockedIPs" = "List of Blocked IPs"
|
||||
"manualBlockedDomains" = "List of Blocked Domains"
|
||||
"manualDirectIPs" = "List of Direct IPs"
|
||||
"manualDirectDomains" = "List of Direct Domains"
|
||||
|
||||
[pages.settings.toasts]
|
||||
"modifySetting" = "Modify setting"
|
||||
"getSetting" = "Get setting"
|
||||
"modifyUser" = "Modify user"
|
||||
"originalUserPassIncorrect" = "The original user name or original password is incorrect"
|
||||
"userPassMustBeNotEmpty" = "New username and new password cannot be empty"
|
||||
"modifySettings" = "Modify Settings "
|
||||
"getSettings" = "Get Settings "
|
||||
"modifyUser" = "Modify User "
|
||||
"originalUserPassIncorrect" = "Incorrect original username or password"
|
||||
"userPassMustBeNotEmpty" = "New username and new password cannot be empty"
|
||||
|
||||
@@ -51,8 +51,8 @@
|
||||
|
||||
[menu]
|
||||
"dashboard" = "وضعیت سیستم"
|
||||
"inbounds" = "سرویس ها"
|
||||
"setting" = "تنظیمات پنل"
|
||||
"inbounds" = "سرویس ها"
|
||||
"settings" = "تنظیمات پنل"
|
||||
"logout" = "خروج"
|
||||
"link" = "دیگر"
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
"totalReceive" = "جمع کل ترافیک دانلود مصرفی"
|
||||
"xraySwitchVersionDialog" = "تغییر ورژن Xray"
|
||||
"xraySwitchVersionDialogDesc" = "آیا از تغییر ورژن مطمئن هستین"
|
||||
"dontRefreshh" = "در حال نصب ، لطفا رفرش نکنید "
|
||||
"dontRefresh" = "در حال نصب ، لطفا رفرش نکنید "
|
||||
"logs" = "گزارش ها"
|
||||
"config" = "تنظیمات"
|
||||
"backup" = "پشتیبان گیری"
|
||||
@@ -140,7 +140,7 @@
|
||||
"clickOnQRcode" = "برای کپی بر روی کد تصویری کلیک کنید"
|
||||
"client" = "کاربر"
|
||||
"export" = "استخراج لینکها"
|
||||
"Clone" = "شبیه سازی"
|
||||
"clone" = "شبیه سازی"
|
||||
"cloneInbound" = "ایجاد"
|
||||
"cloneInboundContent" = "همه موارد این ورودی بجز پورت ، ای پی و کلاینت ها شبیه سازی خواهند شد"
|
||||
"resetAllTraffic" = "ریست ترافیک کل سرویس ها"
|
||||
@@ -205,9 +205,9 @@
|
||||
"restartPanelDesc" = "آیا مطمئن هستید که می خواهید پنل را دوباره راه اندازی کنید؟ برای راه اندازی مجدد روی OK کلیک کنید. اگر بعد از 3 ثانیه نمی توانید به پنل دسترسی پیدا کنید، لطفاً برای مشاهده اطلاعات گزارش پانل به سرور برگردید"
|
||||
"resetDefaultConfig" = "برگشت به تنظیمات پیشفرض"
|
||||
"panelConfig" = "تنظیمات پنل"
|
||||
"userSetting" = "تنظیمات مدیر"
|
||||
"userSettings" = "تنظیمات مدیر"
|
||||
"xrayConfiguration" = "تنظیمات Xray"
|
||||
"TGReminder" = "تنظیمات ربات تلگرام"
|
||||
"TGBotSettings" = "تنظیمات ربات تلگرام"
|
||||
"panelListeningIP" = "محدودیت آی پی پنل"
|
||||
"panelListeningIPDesc" = "برای استفاده از تمام آیپیها به طور پیش فرض خالی بگذارید"
|
||||
"panelPort" = "پورت پنل"
|
||||
@@ -252,8 +252,10 @@
|
||||
"generalConfigsDesc" = "این تنظیمات میتواند ترافیک کلی سرویس را متاثر کند"
|
||||
"blockConfigs" = "مسدود سازی"
|
||||
"blockConfigsDesc" = "این گزینه ها از اتصال کاربران به پروتکل ها و وب سایت های خاص جلوگیری می کند"
|
||||
"countryConfigs" = "تنظیمات برای کشورها"
|
||||
"countryConfigsDesc" = "این گزینه اتصال کاربران به دامنه های کشوری خاص را تنظیم می کند"
|
||||
"blockCountryConfigs" = "تنظیمات برای مسدودسازی کشورها"
|
||||
"blockCountryConfigsDesc" = "این گزینه اتصال کاربران به دامنه های کشوری خاص را مسدود می کند"
|
||||
"directCountryConfigs" = "تنظیمات برای اتصال مستقیم کشورها"
|
||||
"directCountryConfigsDesc" = "این گزینه کاربران را به دامنه های کشوری خاص را به طور مستقیم، متصل می کند"
|
||||
"ipv4Configs" = "تنظیمات برای IPv4"
|
||||
"ipv4ConfigsDesc" = "این گزینه فقط از طریق آیپی ورژن ۴ به دامنه های هدف هدایت می شود"
|
||||
"xrayConfigTemplate" = "تنظیمات الگو ایکس ری"
|
||||
@@ -312,8 +314,8 @@
|
||||
"manualDirectDomains" = "لیست دامنه های مستقیم"
|
||||
|
||||
[pages.settings.toasts]
|
||||
"modifySetting" = "ویرایش تنظیمات"
|
||||
"getSetting" = "دریافت تنظیمات"
|
||||
"modifySettings" = "ویرایش تنظیمات"
|
||||
"getSettings" = "دریافت تنظیمات"
|
||||
"modifyUser" = "ویرایش کاربر"
|
||||
"originalUserPassIncorrect" = "نام کاربری و رمز عبور فعلی اشتباه می باشد "
|
||||
"userPassMustBeNotEmpty" = "نام کاربری و رمز عبور جدید نمیتواند خالی باشد "
|
||||
"userPassMustBeNotEmpty" = "نام کاربری و رمز عبور جدید نمیتواند خالی باشد "
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
[menu]
|
||||
"dashboard" = "статус системы"
|
||||
"inbounds" = "пользователи"
|
||||
"setting" = "настройки"
|
||||
"settings" = "настройки"
|
||||
"logout" = "выход"
|
||||
"link" = "другое"
|
||||
|
||||
@@ -129,7 +129,7 @@
|
||||
"monitorDesc" = "Оставьте пустым по умолчанию"
|
||||
"meansNoLimit" = "Значит без ограничений"
|
||||
"totalFlow" = "Общий расход"
|
||||
"leaveBlankToNeverExpire" = "Оставьте пустым = бессрочно"
|
||||
"leaveBlankToNeverExpire" = "Оставьте пустым, чтобы никогда не истекать"
|
||||
"noRecommendKeepDefault" = "Нет требований для сохранения настроек по умолчанию"
|
||||
"certificatePath" = "Путь файла сертификата"
|
||||
"certificateContent" = "Содержимое файла сертификата"
|
||||
@@ -143,6 +143,7 @@
|
||||
"clone" = "Клонировать"
|
||||
"cloneInbound" = "Клонировать пользователя"
|
||||
"cloneInboundContent" = "Все настройки этого пользователя, кроме порта, порт прослушки и клиентов, будут клонированы"
|
||||
"cloneInboundOk" = "Клонировать"
|
||||
"resetAllTraffic" = "Обнулить весь траффик"
|
||||
"resetAllTrafficTitle" = "Обнуление всего траффика"
|
||||
"resetAllTrafficContent" = "Подтверждаете обнуление всего траффика пользователей?"
|
||||
@@ -205,9 +206,9 @@
|
||||
"restartPanelDesc" = "Подтвердите рестарт панели? ОК для рестарта панели через 3 сек. Если вы не можете пользоваться панелью после рестарта, пожалуйста, посмотрите лог панели на сервере"
|
||||
"resetDefaultConfig" = "Сбросить всё по-умолчанию"
|
||||
"panelConfig" = "Настройки панели"
|
||||
"userSetting" = "Настройки безопасности"
|
||||
"userSettings" = "Настройки безопасности"
|
||||
"xrayConfiguration" = "Конфигурация Xray"
|
||||
"TGReminder" = "Настройки Телеграм-бота"
|
||||
"TGBotSettings" = "Настройки Телеграм-бота"
|
||||
"panelListeningIP" = "IP-порт панели"
|
||||
"panelListeningIPDesc" = "Оставьте пустым для работы с любого IP. Перезагрузите панель для применения настроек"
|
||||
"panelPort" = "Порт панели"
|
||||
@@ -250,8 +251,12 @@
|
||||
"completeTemplate" = "Конфигурация шаблона"
|
||||
"generalConfigs" = "Основные настройки"
|
||||
"generalConfigsDesc" = "Эти параметры не позволят пользователям подключаться к определенным протоколам и веб-сайтам"
|
||||
"countryConfigs" = "Настройки по гео"
|
||||
"countryConfigsDesc" = "Эти параметры не позволят пользователям подключаться к доменам определенной страны"
|
||||
"blockConfigs" = "Блокировка конфигураций"
|
||||
"blockConfigsDesc" = "Эти параметры не позволят пользователям подключаться к определенным протоколам и веб-сайтам."
|
||||
"blockCountryConfigs" = "Заблокировать конфигурации страны"
|
||||
"blockCountryConfigsDesc" = "Эти параметры не позволят пользователям подключаться к доменам определенной страны."
|
||||
"directCountryConfigs" = "Прямые настройки страны"
|
||||
"directCountryConfigsDesc" = "Эти параметры будут подключать пользователей напрямую к доменам определенной страны."
|
||||
"ipv4Configs" = "Настройки IPv4 "
|
||||
"ipv4ConfigsDesc" = "Эти параметры будут маршрутизироваться к целевым доменам только через IPv4"
|
||||
"xrayConfigTemplate" = "Шаблон конфигурации Xray"
|
||||
@@ -280,7 +285,7 @@
|
||||
"xrayConfigRussiaIpDesc" = "Измените конфигурацию, чтобы отключить соединения с диапазонами IP-адресов России. Перезагрузите панель для применения настроек"
|
||||
"xrayConfigRussiaDomain" = "Отключить подключение к доменам России"
|
||||
"xrayConfigRussiaDomainDesc" = "Измените конфигурацию, чтобы избежать подключения к доменам России. Перезагрузите панель для применения настроек"
|
||||
"xrayConfigDirectIRIP" = "Прямое подключение к диапазонам IP-адресов Ирана"
|
||||
"xrayConfigDirectIRIp" = "Прямое подключение к диапазонам IP-адресов Ирана"
|
||||
"xrayConfigDirectIRIpDesc" = "Измените шаблон конфигурации для прямого подключения к диапазонам IP-адресов Ирана"
|
||||
"xrayConfigDirectIRDomain" = "Прямое подключение к доменам Ирана"
|
||||
"xrayConfigDirectIRDomainDesc" = "Измените шаблон конфигурации для прямого подключения к доменам Ирана"
|
||||
@@ -314,4 +319,4 @@
|
||||
"getSettings" = "Просмотр настроек"
|
||||
"modifyUser" = "Изменение пользователя "
|
||||
"originalUserPassIncorrect" = "Неверное имя пользователя или пароль"
|
||||
"userPassMustBeNotEmpty" = "Новое имя пользователя и новый пароль должны быть заполнены"
|
||||
"userPassMustBeNotEmpty" = "Новое имя пользователя и новый пароль должны быть заполнены"
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
[menu]
|
||||
"dashboard" = "系统状态"
|
||||
"inbounds" = "入站列表"
|
||||
"setting" = "面板设置"
|
||||
"settings" = "面板设置"
|
||||
"logout" = "退出登录"
|
||||
"link" = "其他"
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
"title" = "系统状态"
|
||||
"memory" = "内存"
|
||||
"hard" = "硬盘"
|
||||
"xrayStatus" = "xray 状态"
|
||||
"xrayStatus" = "Xray 状态"
|
||||
"stopXray" = "停止"
|
||||
"restartXray" = "重启"
|
||||
"xraySwitch" = "切换版本"
|
||||
@@ -86,9 +86,9 @@
|
||||
"downSpeed" = "所有网卡的总下载速度"
|
||||
"totalSent" = "系统启动以来所有网卡的总上传流量"
|
||||
"totalReceive" = "系统启动以来所有网卡的总下载流量"
|
||||
"xraySwitchVersionDialog" = "切换 xray 版本"
|
||||
"xraySwitchVersionDialogDesc" = "是否切换 xray 版本至"
|
||||
"dontRefreshh" = "安装中,请不要刷新此页面"
|
||||
"xraySwitchVersionDialog" = "切换 Xray 版本"
|
||||
"xraySwitchVersionDialogDesc" = "是否切换 Xray 版本至"
|
||||
"dontRefresh" = "安装中,请不要刷新此页面"
|
||||
"logs" = "日志"
|
||||
"config" = "配置"
|
||||
"backup" = "备份"
|
||||
@@ -140,9 +140,10 @@
|
||||
"clickOnQRcode" = "点击二维码复制"
|
||||
"client" = "客户"
|
||||
"export" = "导出链接"
|
||||
"Clone" = "克隆"
|
||||
"clone" = "克隆"
|
||||
"cloneInbound" = "创造"
|
||||
"cloneInboundContent" = "此入站的所有项目除 Port、Listening IP、Clients 将应用于克隆"
|
||||
"cloneInboundOk" = "创造"
|
||||
"resetAllTraffic" = "重置所有入站流量"
|
||||
"resetAllTrafficTitle" = "重置所有入站流量"
|
||||
"resetAllTrafficContent" = "您确定要重置所有入站流量吗?"
|
||||
@@ -205,9 +206,9 @@
|
||||
"restartPanelDesc" = "确定要重启面板吗?点击确定将于 3 秒后重启,若重启后无法访问面板,请前往服务器查看面板日志信息"
|
||||
"resetDefaultConfig" = "重置为默认配置"
|
||||
"panelConfig" = "面板配置"
|
||||
"userSetting" = "用户设置"
|
||||
"xrayConfiguration" = "xray 相关设置"
|
||||
"TGReminder" = "TG提醒相关设置"
|
||||
"userSettings" = "用户设置"
|
||||
"xrayConfiguration" = "Xray 相关设置"
|
||||
"TGBotSettings" = "TG提醒相关设置"
|
||||
"panelListeningIP" = "面板监听 IP"
|
||||
"panelListeningIPDesc" = "默认留空监听所有 IP"
|
||||
"panelPort" = "面板监听端口"
|
||||
@@ -252,14 +253,14 @@
|
||||
"generalConfigsDesc" = "这些选项将提供一般调整"
|
||||
"blockConfigs" = "阻塞配置"
|
||||
"blockConfigsDesc" = "这些选项将阻止用户连接到特定协议和网站"
|
||||
"directConfigs" = "直接配置"
|
||||
"directConfigsDesc" = "这些选项将使用直接输出到特定的协议和网站"
|
||||
"countryConfigs" = "国家配置"
|
||||
"countryConfigsDesc" = "此选项将阻止用户连接到特定国家/地区的域"
|
||||
"blockCountryConfigs" = "阻止国家配置"
|
||||
"blockCountryConfigsDesc" = "这些选项将阻止用户连接到特定国家/地区的域。"
|
||||
"directCountryConfigs" = "直接国家配置"
|
||||
"directCountryConfigsDesc" = "这些选项会将用户直接连接到特定国家/地区的域。"
|
||||
"ipv4Configs" = "IPv4 配置"
|
||||
"ipv4ConfigsDesc" = "此选项将仅通过 IPv4 路由到目标域"
|
||||
"xrayConfigTemplate" = "xray 配置模板"
|
||||
"xrayConfigTemplateDesc" = "以该模型为基础生成最终的xray配置文件,重新启动面板生成效率"
|
||||
"xrayConfigTemplate" = "Xray 配置模板"
|
||||
"xrayConfigTemplateDesc" = "以该模型为基础生成最终的Xray配置文件,重新启动面板生成效率"
|
||||
"xrayConfigFreedomStrategy" = "配置自由协议的策略"
|
||||
"xrayConfigFreedomStrategyDesc" = "在自由协议中设置网络输出策略"
|
||||
"xrayConfigRoutingStrategy" = "配置路由域策略"
|
||||
@@ -284,8 +285,8 @@
|
||||
"xrayConfigRussiaIpDesc" = "修改配置模板避免连接俄罗斯IP范围"
|
||||
"xrayConfigRussiaDomain" = "禁止俄罗斯域连接"
|
||||
"xrayConfigRussiaDomainDesc" = "更改配置模板避免连接俄罗斯域"
|
||||
"xrayConfigDirectIRIP" = "直接连接到伊朗 IP 范围"
|
||||
"xrayConfigDirectIRIPDesc" = "更改直接连接到伊朗 IP 范围的配置模板"
|
||||
"xrayConfigDirectIRIp" = "直接连接到伊朗 IP 范围"
|
||||
"xrayConfigDirectIRIpDesc" = "更改直接连接到伊朗 IP 范围的配置模板"
|
||||
"xrayConfigDirectIRDomain" = "直接连接到伊朗域"
|
||||
"xrayConfigDirectIRDomainDesc" = "更改直接连接到伊朗域的配置模板"
|
||||
"xrayConfigDirectChinaIp" = "直连中国IP范围"
|
||||
@@ -314,8 +315,8 @@
|
||||
"manualDirectDomains" = "直接域列表"
|
||||
|
||||
[pages.settings.toasts]
|
||||
"modifySetting" = "修改设置"
|
||||
"getSetting" = "获取设置"
|
||||
"modifySettings" = "修改设置"
|
||||
"getSettings" = "获取设置"
|
||||
"modifyUser" = "修改用户"
|
||||
"originalUserPassIncorrect" = "原用户名或原密码错误"
|
||||
"userPassMustBeNotEmpty" = "新用户名和新密码不能为空"
|
||||
"userPassMustBeNotEmpty" = "新用户名和新密码不能为空"
|
||||
|
||||
Reference in New Issue
Block a user