mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2025-07-02 21:12:07 +00:00
Merge branch 'main' of https://github.com/Torotin/3x-ui
This commit is contained in:
commit
f910aa814a
50 changed files with 3296 additions and 2748 deletions
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
|
@ -97,7 +97,7 @@ jobs:
|
||||||
cd x-ui/bin
|
cd x-ui/bin
|
||||||
|
|
||||||
# Download dependencies
|
# Download dependencies
|
||||||
Xray_URL="https://github.com/XTLS/Xray-core/releases/download/v25.5.16/"
|
Xray_URL="https://github.com/XTLS/Xray-core/releases/download/v25.6.8/"
|
||||||
if [ "${{ matrix.platform }}" == "amd64" ]; then
|
if [ "${{ matrix.platform }}" == "amd64" ]; then
|
||||||
wget -q ${Xray_URL}Xray-linux-64.zip
|
wget -q ${Xray_URL}Xray-linux-64.zip
|
||||||
unzip Xray-linux-64.zip
|
unzip Xray-linux-64.zip
|
||||||
|
|
133
DockerInit.sh
133
DockerInit.sh
|
@ -1,12 +1,21 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# POSIX-compatible script to download Xray binary and GeoIP/GeoSite databases
|
# POSIX-compatible script to download Xray binary and GeoIP/GeoSite databases
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
XRAY_VERSION="v25.6.8"
|
XRAY_VERSION="v25.6.8"
|
||||||
BASE_URL="https://github.com/XTLS/Xray-core/releases/download/${XRAY_VERSION}"
|
BASE_URL="https://github.com/XTLS/Xray-core/releases/download/${XRAY_VERSION}"
|
||||||
DAT_DIR="build/bin"
|
DAT_DIR="build/bin"
|
||||||
ARCH="${1:-amd64}" # Default to amd64 if not provided
|
ARCH="${1:-amd64}" # Default to amd64 if not provided
|
||||||
|
=======
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# DockerInit.sh <20> download and prepare Xray binaries and geolocation databases
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Xray version
|
||||||
|
readonly XRAY_VERSION="v25.6.8"
|
||||||
|
>>>>>>> f7a3ebf2f3c28d40c1ae126f73ac6a8c9e22c2c6
|
||||||
|
|
||||||
# Map architecture
|
# Map architecture
|
||||||
case "$ARCH" in
|
case "$ARCH" in
|
||||||
|
@ -51,5 +60,129 @@ download_variant() {
|
||||||
download_variant "IR" "https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download"
|
download_variant "IR" "https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download"
|
||||||
download_variant "RU" "https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download"
|
download_variant "RU" "https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download"
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
echo "All files downloaded successfully."
|
echo "All files downloaded successfully."
|
||||||
cd ../../
|
cd ../../
|
||||||
|
=======
|
||||||
|
Supported architectures:
|
||||||
|
amd64 ? 64-bit x86
|
||||||
|
i386 ? 32-bit x86
|
||||||
|
armv8 ? ARM64-v8a (also accepts arm64, aarch64)
|
||||||
|
armv7 ? ARM32-v7a (also accepts arm, arm32)
|
||||||
|
armv6 ? ARM32-v6
|
||||||
|
|
||||||
|
If no argument is provided or the argument is not recognized, 'amd64' will be used by default.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
$0 armv7
|
||||||
|
EOF
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Determine ARCH and FNAME based on input argument
|
||||||
|
detect_arch() {
|
||||||
|
local input="$1"
|
||||||
|
|
||||||
|
case "$input" in
|
||||||
|
amd64)
|
||||||
|
ARCH="64"
|
||||||
|
FNAME="amd64"
|
||||||
|
;;
|
||||||
|
i386)
|
||||||
|
ARCH="32"
|
||||||
|
FNAME="i386"
|
||||||
|
;;
|
||||||
|
armv8 | arm64 | aarch64)
|
||||||
|
ARCH="arm64-v8a"
|
||||||
|
FNAME="arm64"
|
||||||
|
;;
|
||||||
|
armv7 | arm | arm32)
|
||||||
|
ARCH="arm32-v7a"
|
||||||
|
FNAME="arm32"
|
||||||
|
;;
|
||||||
|
armv6)
|
||||||
|
ARCH="arm32-v6"
|
||||||
|
FNAME="armv6"
|
||||||
|
;;
|
||||||
|
"")
|
||||||
|
# If argument is empty, default to amd64
|
||||||
|
ARCH="64"
|
||||||
|
FNAME="amd64"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Warning: Architecture '$input' not recognized. Defaulting to 'amd64'." >&2
|
||||||
|
ARCH="64"
|
||||||
|
FNAME="amd64"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# Generic function to download a file by URL (with error handling)
|
||||||
|
download_file() {
|
||||||
|
local url="$1"
|
||||||
|
local output="$2"
|
||||||
|
|
||||||
|
echo "Downloading: $url"
|
||||||
|
if ! wget -q -O "$output" "$url"; then
|
||||||
|
echo "Error: Failed to download '$url'" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main function: create directory, download and unpack Xray, then geolocation databases
|
||||||
|
main() {
|
||||||
|
# Check dependencies
|
||||||
|
check_dependencies
|
||||||
|
|
||||||
|
# Get architecture from argument
|
||||||
|
local ARCH_ARG="${1-}"
|
||||||
|
detect_arch "$ARCH_ARG"
|
||||||
|
|
||||||
|
# Construct URL for Xray download
|
||||||
|
local xray_url
|
||||||
|
printf -v xray_url "$XRAY_URL_TEMPLATE" "$ARCH"
|
||||||
|
|
||||||
|
# Create build directory
|
||||||
|
echo "Creating directory: $BUILD_DIR"
|
||||||
|
mkdir -p "$BUILD_DIR"
|
||||||
|
cd "$BUILD_DIR" || exit 1
|
||||||
|
|
||||||
|
# Download and unpack Xray
|
||||||
|
local xray_zip="Xray-linux-${ARCH}.zip"
|
||||||
|
download_file "$xray_url" "$xray_zip"
|
||||||
|
echo "Unpacking $xray_zip"
|
||||||
|
unzip -q "$xray_zip"
|
||||||
|
rm -f "$xray_zip" geoip.dat geosite.dat
|
||||||
|
|
||||||
|
# Rename binary according to target architecture
|
||||||
|
mv xray "xray-linux-${FNAME}"
|
||||||
|
chmod +x "xray-linux-${FNAME}"
|
||||||
|
|
||||||
|
# Return to project root
|
||||||
|
cd "$ROOT_DIR" || exit 1
|
||||||
|
|
||||||
|
# Download standard GeoIP and GeoSite databases
|
||||||
|
echo "Downloading default GeoIP and GeoSite databases"
|
||||||
|
download_file "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat" "geoip.dat"
|
||||||
|
download_file "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat" "geosite.dat"
|
||||||
|
|
||||||
|
# Download alternative GeoIP/GeoSite for Iran (IR) and Russia (RU)
|
||||||
|
echo "Downloading alternative GeoIP/GeoSite for Iran"
|
||||||
|
download_file "https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geoip.dat" "geoip_IR.dat"
|
||||||
|
download_file "https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geosite.dat" "geosite_IR.dat"
|
||||||
|
|
||||||
|
echo "Downloading alternative GeoIP/GeoSite for Russia"
|
||||||
|
download_file "https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geoip.dat" "geoip_RU.dat"
|
||||||
|
download_file "https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geosite.dat" "geosite_RU.dat"
|
||||||
|
|
||||||
|
echo "Done."
|
||||||
|
}
|
||||||
|
|
||||||
|
# If -h or --help is passed, show usage
|
||||||
|
if [[ "${1-}" == "-h" || "${1-}" == "--help" ]]; then
|
||||||
|
usage
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Run main with the provided argument (if any)
|
||||||
|
main "${1-}"
|
||||||
|
>>>>>>> f7a3ebf2f3c28d40c1ae126f73ac6a8c9e22c2c6
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
## البدء السريع
|
## البدء السريع
|
||||||
|
|
||||||
```
|
```
|
||||||
$ bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
|
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
|
||||||
```
|
```
|
||||||
|
|
||||||
للحصول على الوثائق الكاملة، يرجى زيارة [ويكي المشروع](https://github.com/MHSanaei/3x-ui/wiki).
|
للحصول على الوثائق الكاملة، يرجى زيارة [ويكي المشروع](https://github.com/MHSanaei/3x-ui/wiki).
|
||||||
|
|
|
@ -23,7 +23,7 @@ Como una versión mejorada del proyecto X-UI original, 3X-UI proporciona mayor e
|
||||||
## Inicio Rápido
|
## Inicio Rápido
|
||||||
|
|
||||||
```
|
```
|
||||||
$ bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
|
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
|
||||||
```
|
```
|
||||||
|
|
||||||
Para documentación completa, visita la [Wiki del proyecto](https://github.com/MHSanaei/3x-ui/wiki).
|
Para documentación completa, visita la [Wiki del proyecto](https://github.com/MHSanaei/3x-ui/wiki).
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
## شروع سریع
|
## شروع سریع
|
||||||
|
|
||||||
```
|
```
|
||||||
$ bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
|
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
|
||||||
```
|
```
|
||||||
|
|
||||||
برای مستندات کامل، لطفاً به [ویکی پروژه](https://github.com/MHSanaei/3x-ui/wiki) مراجعه کنید.
|
برای مستندات کامل، لطفاً به [ویکی پروژه](https://github.com/MHSanaei/3x-ui/wiki) مراجعه کنید.
|
||||||
|
|
|
@ -23,7 +23,7 @@ As an enhanced fork of the original X-UI project, 3X-UI provides improved stabil
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
|
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
|
||||||
```
|
```
|
||||||
|
|
||||||
For full documentation, please visit the [project Wiki](https://github.com/MHSanaei/3x-ui/wiki).
|
For full documentation, please visit the [project Wiki](https://github.com/MHSanaei/3x-ui/wiki).
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
## Быстрый старт
|
## Быстрый старт
|
||||||
|
|
||||||
```
|
```
|
||||||
$ bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
|
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
|
||||||
```
|
```
|
||||||
|
|
||||||
Полную документацию смотрите в [вики проекта](https://github.com/MHSanaei/3x-ui/wiki).
|
Полную документацию смотрите в [вики проекта](https://github.com/MHSanaei/3x-ui/wiki).
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
## 快速开始
|
## 快速开始
|
||||||
|
|
||||||
```
|
```
|
||||||
$ bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
|
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
|
||||||
```
|
```
|
||||||
|
|
||||||
完整文档请参阅 [项目Wiki](https://github.com/MHSanaei/3x-ui/wiki)。
|
完整文档请参阅 [项目Wiki](https://github.com/MHSanaei/3x-ui/wiki)。
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
services:
|
services:
|
||||||
3x-ui:
|
3xui:
|
||||||
image: ghcr.io/mhsanaei/3x-ui:latest
|
build:
|
||||||
container_name: 3x-ui
|
context: .
|
||||||
hostname: yourhostname
|
dockerfile: ./Dockerfile
|
||||||
|
container_name: 3xui_app
|
||||||
|
# hostname: yourhostname <- optional
|
||||||
volumes:
|
volumes:
|
||||||
- $PWD/db/:/etc/x-ui/
|
- $PWD/db/:/etc/x-ui/
|
||||||
- $PWD/cert/:/root/cert/
|
- $PWD/cert/:/root/cert/
|
||||||
|
|
|
@ -57,7 +57,7 @@ install_base() {
|
||||||
ubuntu | debian | armbian)
|
ubuntu | debian | armbian)
|
||||||
apt-get update && apt-get install -y -q wget curl tar tzdata
|
apt-get update && apt-get install -y -q wget curl tar tzdata
|
||||||
;;
|
;;
|
||||||
centos | almalinux | rocky | ol)
|
centos | rhel | almalinux | rocky | ol)
|
||||||
yum -y update && yum install -y -q wget curl tar tzdata
|
yum -y update && yum install -y -q wget curl tar tzdata
|
||||||
;;
|
;;
|
||||||
fedora | amzn | virtuozzo)
|
fedora | amzn | virtuozzo)
|
||||||
|
|
|
@ -2150,7 +2150,7 @@ Inbound.TrojanSettings.Fallback = class extends XrayCommonClass {
|
||||||
Inbound.ShadowsocksSettings = class extends Inbound.Settings {
|
Inbound.ShadowsocksSettings = class extends Inbound.Settings {
|
||||||
constructor(protocol,
|
constructor(protocol,
|
||||||
method = SSMethods.BLAKE3_AES_256_GCM,
|
method = SSMethods.BLAKE3_AES_256_GCM,
|
||||||
password = RandomUtil.randomShadowsocksPassword(),
|
password = '',
|
||||||
network = 'tcp,udp',
|
network = 'tcp,udp',
|
||||||
shadowsockses = [new Inbound.ShadowsocksSettings.Shadowsocks()],
|
shadowsockses = [new Inbound.ShadowsocksSettings.Shadowsocks()],
|
||||||
ivCheck = false,
|
ivCheck = false,
|
||||||
|
@ -2188,7 +2188,7 @@ Inbound.ShadowsocksSettings = class extends Inbound.Settings {
|
||||||
Inbound.ShadowsocksSettings.Shadowsocks = class extends XrayCommonClass {
|
Inbound.ShadowsocksSettings.Shadowsocks = class extends XrayCommonClass {
|
||||||
constructor(
|
constructor(
|
||||||
method = '',
|
method = '',
|
||||||
password = RandomUtil.randomShadowsocksPassword(),
|
password = '',
|
||||||
email = RandomUtil.randomLowerAndNum(8),
|
email = RandomUtil.randomLowerAndNum(8),
|
||||||
limitIp = 0,
|
limitIp = 0,
|
||||||
totalGB = 0,
|
totalGB = 0,
|
||||||
|
|
|
@ -138,8 +138,14 @@ class RandomUtil {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static randomShadowsocksPassword() {
|
static randomShadowsocksPassword(method = SSMethods.BLAKE3_AES_256_GCM) {
|
||||||
const array = new Uint8Array(32);
|
let length = 32;
|
||||||
|
|
||||||
|
if ([SSMethods.BLAKE3_AES_128_GCM].includes(method)) {
|
||||||
|
length = 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
const array = new Uint8Array(length);
|
||||||
|
|
||||||
window.crypto.getRandomValues(array);
|
window.crypto.getRandomValues(array);
|
||||||
|
|
||||||
|
@ -789,6 +795,25 @@ class LanguageManager {
|
||||||
if (window.navigator) {
|
if (window.navigator) {
|
||||||
lang = window.navigator.language || window.navigator.userLanguage;
|
lang = window.navigator.language || window.navigator.userLanguage;
|
||||||
|
|
||||||
|
const simularLangs = [
|
||||||
|
["ar", this.supportedLanguages[0].value],
|
||||||
|
["fa", this.supportedLanguages[2].value],
|
||||||
|
["ja", this.supportedLanguages[5].value],
|
||||||
|
["ru", this.supportedLanguages[6].value],
|
||||||
|
["vi", this.supportedLanguages[7].value],
|
||||||
|
["es", this.supportedLanguages[8].value],
|
||||||
|
["id", this.supportedLanguages[9].value],
|
||||||
|
["uk", this.supportedLanguages[10].value],
|
||||||
|
["tr", this.supportedLanguages[11].value],
|
||||||
|
["pt", this.supportedLanguages[12].value],
|
||||||
|
]
|
||||||
|
|
||||||
|
simularLangs.forEach((pair) => {
|
||||||
|
if (lang === pair[0]) {
|
||||||
|
lang = pair[1];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (LanguageManager.isSupportLanguage(lang)) {
|
if (LanguageManager.isSupportLanguage(lang)) {
|
||||||
CookieManager.setCookie("lang", lang, 150);
|
CookieManager.setCookie("lang", lang, 150);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
"math"
|
||||||
|
|
||||||
"x-ui/util/common"
|
"x-ui/util/common"
|
||||||
)
|
)
|
||||||
|
@ -78,11 +79,11 @@ func (s *AllSetting) CheckValid() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.WebPort <= 0 || s.WebPort > 65535 {
|
if s.WebPort <= 0 || s.WebPort > math.MaxUint16 {
|
||||||
return common.NewError("web port is not a valid port:", s.WebPort)
|
return common.NewError("web port is not a valid port:", s.WebPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.SubPort <= 0 || s.SubPort > 65535 {
|
if s.SubPort <= 0 || s.SubPort > math.MaxUint16 {
|
||||||
return common.NewError("Sub port is not a valid port:", s.SubPort)
|
return common.NewError("Sub port is not a valid port:", s.SubPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
{{define "head"}}
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="renderer" content="webkit">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<meta name="robots" content="noindex,nofollow">
|
|
||||||
<link rel="stylesheet" href="{{ .base_path }}assets/ant-design-vue/antd.min.css">
|
|
||||||
<link rel="stylesheet" href="{{ .base_path }}assets/css/custom.min.css?{{ .cur_ver }}">
|
|
||||||
<style>
|
|
||||||
[v-cloak] {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
/* vazirmatn-regular - arabic_latin_latin-ext */
|
|
||||||
@font-face {
|
|
||||||
font-display: swap;
|
|
||||||
font-family: 'Vazirmatn';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
src: url('{{ .base_path }}assets/Vazirmatn-UI-NL-Regular.woff2') format('woff2');
|
|
||||||
unicode-range: U+0600-06FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE80-FEFC, U+0030-0039;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Vazirmatn', 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB',
|
|
||||||
'Microsoft YaHei', 'Helvetica Neue', Helvetica, Arial, sans-serif, 'Apple Color Emoji',
|
|
||||||
'Segoe UI Emoji', 'Segoe UI Symbol';
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<title>{{ .host }} – {{ i18n .title}}</title>
|
|
||||||
</head>
|
|
||||||
<div id="message"></div>
|
|
||||||
{{end}}
|
|
|
@ -1,14 +0,0 @@
|
||||||
{{define "js"}}
|
|
||||||
<script src="{{ .base_path }}assets/vue/vue.min.js?{{ .cur_ver }}"></script>
|
|
||||||
<script src="{{ .base_path }}assets/moment/moment.min.js"></script>
|
|
||||||
<script src="{{ .base_path }}assets/ant-design-vue/antd.min.js"></script>
|
|
||||||
<script src="{{ .base_path }}assets/axios/axios.min.js?{{ .cur_ver }}"></script>
|
|
||||||
<script src="{{ .base_path }}assets/qs/qs.min.js"></script>
|
|
||||||
<script src="{{ .base_path }}assets/js/axios-init.js?{{ .cur_ver }}"></script>
|
|
||||||
<script src="{{ .base_path }}assets/js/util/date-util.js?{{ .cur_ver }}"></script>
|
|
||||||
<script src="{{ .base_path }}assets/js/util/index.js?{{ .cur_ver }}"></script>
|
|
||||||
<script>
|
|
||||||
const basePath = '{{ .base_path }}';
|
|
||||||
axios.defaults.baseURL = basePath;
|
|
||||||
</script>
|
|
||||||
{{end}}
|
|
58
web/html/common/page.html
Normal file
58
web/html/common/page.html
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
{{ define "page/head_start" }}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="renderer" content="webkit">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta name="robots" content="noindex,nofollow">
|
||||||
|
<link rel="stylesheet" href="{{ .base_path }}assets/ant-design-vue/antd.min.css">
|
||||||
|
<link rel="stylesheet" href="{{ .base_path }}assets/css/custom.min.css?{{ .cur_ver }}">
|
||||||
|
<style>
|
||||||
|
[v-cloak] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
/* vazirmatn-regular - arabic_latin_latin-ext */
|
||||||
|
@font-face {
|
||||||
|
font-display: swap;
|
||||||
|
font-family: 'Vazirmatn';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
src: url('{{ .base_path }}assets/Vazirmatn-UI-NL-Regular.woff2') format('woff2');
|
||||||
|
unicode-range: U+0600-06FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE80-FEFC, U+0030-0039;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Vazirmatn', 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<title>{{ .host }} – {{ i18n .title}}</title>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ define "page/head_end" }}
|
||||||
|
</head>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ define "page/body_start" }}
|
||||||
|
<body>
|
||||||
|
<div id="message"></div>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ define "page/body_scripts" }}
|
||||||
|
<script src="{{ .base_path }}assets/vue/vue.min.js?{{ .cur_ver }}"></script>
|
||||||
|
<script src="{{ .base_path }}assets/moment/moment.min.js"></script>
|
||||||
|
<script src="{{ .base_path }}assets/ant-design-vue/antd.min.js"></script>
|
||||||
|
<script src="{{ .base_path }}assets/axios/axios.min.js?{{ .cur_ver }}"></script>
|
||||||
|
<script src="{{ .base_path }}assets/qs/qs.min.js"></script>
|
||||||
|
<script src="{{ .base_path }}assets/js/axios-init.js?{{ .cur_ver }}"></script>
|
||||||
|
<script src="{{ .base_path }}assets/js/util/date-util.js?{{ .cur_ver }}"></script>
|
||||||
|
<script src="{{ .base_path }}assets/js/util/index.js?{{ .cur_ver }}"></script>
|
||||||
|
<script>
|
||||||
|
const basePath = '{{ .base_path }}';
|
||||||
|
axios.defaults.baseURL = basePath;
|
||||||
|
</script>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ define "page/body_end" }}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
{{ end }}
|
|
@ -41,6 +41,7 @@
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<template slot="client" slot-scope="text, client">
|
<template slot="client" slot-scope="text, client">
|
||||||
|
<a-space direction="horizontal" :size="2">
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
<template v-if="!isClientEnabled(record, client.email)">{{ i18n "depleted" }}</template>
|
<template v-if="!isClientEnabled(record, client.email)">{{ i18n "depleted" }}</template>
|
||||||
|
@ -48,7 +49,20 @@
|
||||||
<template v-else-if="client.enable && isClientOnline(client.email)">{{ i18n "online" }}</template>
|
<template v-else-if="client.enable && isClientOnline(client.email)">{{ i18n "online" }}</template>
|
||||||
</template>
|
</template>
|
||||||
<a-badge :class="isClientOnline(client.email)? 'online-animation' : ''" :color="client.enable ? statsExpColor(record, client.email) : themeSwitcher.isDarkTheme ? '#2c3950' : '#bcbcbc'"></a-badge>
|
<a-badge :class="isClientOnline(client.email)? 'online-animation' : ''" :color="client.enable ? statsExpColor(record, client.email) : themeSwitcher.isDarkTheme ? '#2c3950' : '#bcbcbc'"></a-badge>
|
||||||
</a-tooltip> [[ client.email ]]
|
</a-tooltip>
|
||||||
|
<a-space direction="vertical" :size="2">
|
||||||
|
<span class="client-email">[[ client.email ]]</span>
|
||||||
|
<template v-if="client.comment && client.comment.trim()">
|
||||||
|
<a-tooltip v-if="client.comment.length > 50" :overlay-class-name="themeSwitcher.currentTheme">
|
||||||
|
<template slot="title">
|
||||||
|
[[ client.comment ]]
|
||||||
|
</template>
|
||||||
|
<span class="client-comment">[[ client.comment.substring(0, 47) + '...' ]]</span>
|
||||||
|
</a-tooltip>
|
||||||
|
<span v-else class="client-comment">[[ client.comment ]]</span>
|
||||||
|
</template>
|
||||||
|
</a-space>
|
||||||
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
<template slot="traffic" slot-scope="text, client">
|
<template slot="traffic" slot-scope="text, client">
|
||||||
<a-popover :overlay-class-name="themeSwitcher.currentTheme">
|
<a-popover :overlay-class-name="themeSwitcher.currentTheme">
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
<span>{{ i18n "reset" }}</span>
|
<span>{{ i18n "reset" }}</span>
|
||||||
</template>
|
</template>
|
||||||
{{ i18n "password" }}
|
{{ i18n "password" }}
|
||||||
<a-icon v-if="inbound.protocol === Protocols.SHADOWSOCKS"@click="client.password = RandomUtil.randomShadowsocksPassword()" type="sync"></a-icon>
|
<a-icon v-if="inbound.protocol === Protocols.SHADOWSOCKS" @click="client.password = RandomUtil.randomShadowsocksPassword(inbound.settings.method)" type="sync"></a-icon>
|
||||||
<a-icon v-if="inbound.protocol === Protocols.TROJAN" @click="client.password = RandomUtil.randomSeq(10)"type="sync"> </a-icon>
|
<a-icon v-if="inbound.protocol === Protocols.TROJAN" @click="client.password = RandomUtil.randomSeq(10)"type="sync"> </a-icon>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
<span>{{ i18n "reset" }}</span>
|
<span>{{ i18n "reset" }}</span>
|
||||||
</template> Password <a-icon @click="inbound.settings.password = RandomUtil.randomShadowsocksPassword()" type="sync"></a-icon>
|
</template> Password <a-icon @click="inbound.settings.password = RandomUtil.randomShadowsocksPassword(inbound.settings.method)" type="sync"></a-icon>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</template>
|
</template>
|
||||||
<a-input v-model.trim="inbound.settings.password"></a-input>
|
<a-input v-model.trim="inbound.settings.password"></a-input>
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
<!DOCTYPE html>
|
{{ template "page/head_start" .}}
|
||||||
<html lang="en">
|
|
||||||
{{template "head" .}}
|
|
||||||
<style>
|
<style>
|
||||||
.ant-table:not(.ant-table-expanded-row .ant-table) {
|
.ant-table:not(.ant-table-expanded-row .ant-table) {
|
||||||
outline: 1px solid #f0f0f0;
|
outline: 1px solid #f0f0f0;
|
||||||
|
@ -79,6 +77,19 @@
|
||||||
max-width: 200px;
|
max-width: 200px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
.client-comment {
|
||||||
|
font-size: 12px;
|
||||||
|
opacity: 0.75;
|
||||||
|
cursor: help;
|
||||||
|
}
|
||||||
|
.client-email {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.client-popup-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
.online-animation .ant-badge-status-dot {
|
.online-animation .ant-badge-status-dot {
|
||||||
animation: onlineAnimation 1.2s linear infinite;
|
animation: onlineAnimation 1.2s linear infinite;
|
||||||
}
|
}
|
||||||
|
@ -130,15 +141,16 @@
|
||||||
padding: 12px 2px;
|
padding: 12px 2px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
{{ template "page/head_end" .}}
|
||||||
|
|
||||||
<body>
|
{{ template "page/body_start" .}}
|
||||||
<a-layout id="app" v-cloak :class="themeSwitcher.currentTheme">
|
<a-layout id="app" v-cloak :class="themeSwitcher.currentTheme">
|
||||||
<a-sidebar></a-sidebar>
|
<a-sidebar></a-sidebar>
|
||||||
<a-layout id="content-layout">
|
<a-layout id="content-layout">
|
||||||
<a-layout-content>
|
<a-layout-content>
|
||||||
<a-spin :spinning="spinning" :delay="500" tip='{{ i18n "loading"}}'>
|
<a-spin :spinning="loadingStates.spinning" :delay="500" tip='{{ i18n "loading"}}'>
|
||||||
<transition name="list" appear>
|
<transition name="list" appear>
|
||||||
<a-alert type="error" v-if="showAlert" :style="{ marginBottom: '10px' }"
|
<a-alert type="error" v-if="showAlert && loadingStates.fetched" :style="{ marginBottom: '10px' }"
|
||||||
message='{{ i18n "secAlertTitle" }}'
|
message='{{ i18n "secAlertTitle" }}'
|
||||||
color="red"
|
color="red"
|
||||||
description='{{ i18n "secAlertSsl" }}'
|
description='{{ i18n "secAlertSsl" }}'
|
||||||
|
@ -146,6 +158,13 @@
|
||||||
</a-alert>
|
</a-alert>
|
||||||
</transition>
|
</transition>
|
||||||
<transition name="list" appear>
|
<transition name="list" appear>
|
||||||
|
<a-row v-if="!loadingStates.fetched">
|
||||||
|
<a-card :style="{ textAlign: 'center', padding: '30px 0', marginTop: '10px', background: 'transparent', border: 'none' }">
|
||||||
|
<a-spin tip='{{ i18n "loading" }}'></a-spin>
|
||||||
|
</a-card>
|
||||||
|
</a-row>
|
||||||
|
<a-row :gutter="[isMobile ? 8 : 16, isMobile ? 0 : 12]" v-else>
|
||||||
|
<a-col>
|
||||||
<a-card size="small" :style="{ padding: '16px' }" hoverable>
|
<a-card size="small" :style="{ padding: '16px' }" hoverable>
|
||||||
<a-row>
|
<a-row>
|
||||||
<a-col :sm="12" :md="6">
|
<a-col :sm="12" :md="6">
|
||||||
|
@ -208,8 +227,8 @@
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
</a-card>
|
</a-card>
|
||||||
</transition>
|
</a-col>
|
||||||
<transition name="list" appear>
|
<a-col>
|
||||||
<a-card hoverable>
|
<a-card hoverable>
|
||||||
<template #title>
|
<template #title>
|
||||||
<a-space direction="horizontal">
|
<a-space direction="horizontal">
|
||||||
|
@ -382,25 +401,57 @@
|
||||||
<a-tag :style="{ margin: '0' }" color="green">[[ clientCount[dbInbound.id].clients ]]</a-tag>
|
<a-tag :style="{ margin: '0' }" color="green">[[ clientCount[dbInbound.id].clients ]]</a-tag>
|
||||||
<a-popover title='{{ i18n "disabled" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
<a-popover title='{{ i18n "disabled" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
||||||
<template slot="content">
|
<template slot="content">
|
||||||
<div v-for="clientEmail in clientCount[dbInbound.id].deactive"><span>[[ clientEmail ]]</span></div>
|
<div v-for="clientEmail in clientCount[dbInbound.id].deactive" :key="clientEmail" class="client-popup-item">
|
||||||
|
<span>[[ clientEmail ]]</span>
|
||||||
|
<a-tooltip :overlay-class-name="themeSwitcher.currentTheme">
|
||||||
|
<template #title>
|
||||||
|
[[ getClientWithComment(clientEmail, dbInbound.id).comment ]]
|
||||||
|
</template>
|
||||||
|
<a-icon type="message" v-if="getClientWithComment(clientEmail, dbInbound.id).comment"></a-icon>
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<a-tag :style="{ margin: '0', padding: '0 2px' }" v-if="clientCount[dbInbound.id].deactive.length">[[ clientCount[dbInbound.id].deactive.length ]]</a-tag>
|
<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>
|
||||||
<a-popover title='{{ i18n "depleted" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
<a-popover title='{{ i18n "depleted" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
||||||
<template slot="content">
|
<template slot="content">
|
||||||
<div v-for="clientEmail in clientCount[dbInbound.id].depleted"><span>[[ clientEmail ]]</span></div>
|
<div v-for="clientEmail in clientCount[dbInbound.id].depleted" :key="clientEmail" class="client-popup-item">
|
||||||
|
<span>[[ clientEmail ]]</span>
|
||||||
|
<a-tooltip :overlay-class-name="themeSwitcher.currentTheme">
|
||||||
|
<template #title>
|
||||||
|
[[ getClientWithComment(clientEmail, dbInbound.id).comment ]]
|
||||||
|
</template>
|
||||||
|
<a-icon type="message" v-if="getClientWithComment(clientEmail, dbInbound.id).comment"></a-icon>
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
</template>
|
</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-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>
|
||||||
<a-popover title='{{ i18n "depletingSoon" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
<a-popover title='{{ i18n "depletingSoon" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
||||||
<template slot="content">
|
<template slot="content">
|
||||||
<div v-for="clientEmail in clientCount[dbInbound.id].expiring"><span>[[ clientEmail ]]</span></div>
|
<div v-for="clientEmail in clientCount[dbInbound.id].expiring" :key="clientEmail" class="client-popup-item">
|
||||||
|
<span>[[ clientEmail ]]</span>
|
||||||
|
<a-tooltip :overlay-class-name="themeSwitcher.currentTheme">
|
||||||
|
<template #title>
|
||||||
|
[[ getClientWithComment(clientEmail, dbInbound.id).comment ]]
|
||||||
|
</template>
|
||||||
|
<a-icon type="message" v-if="getClientWithComment(clientEmail, dbInbound.id).comment"></a-icon>
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<a-tag :style="{ margin: '0', padding: '0 2px' }" color="orange" v-if="clientCount[dbInbound.id].expiring.length">[[ clientCount[dbInbound.id].expiring.length ]]</a-tag>
|
<a-tag :style="{ margin: '0', padding: '0 2px' }" color="orange" v-if="clientCount[dbInbound.id].expiring.length">[[ clientCount[dbInbound.id].expiring.length ]]</a-tag>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
<a-popover title='{{ i18n "online" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
<a-popover title='{{ i18n "online" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
||||||
<template slot="content">
|
<template slot="content">
|
||||||
<div v-for="clientEmail in clientCount[dbInbound.id].online"><span>[[ clientEmail ]]</span></div>
|
<div v-for="clientEmail in clientCount[dbInbound.id].online" :key="clientEmail" class="client-popup-item">
|
||||||
|
<span>[[ clientEmail ]]</span>
|
||||||
|
<a-tooltip :overlay-class-name="themeSwitcher.currentTheme">
|
||||||
|
<template #title>
|
||||||
|
[[ getClientWithComment(clientEmail, dbInbound.id).comment ]]
|
||||||
|
</template>
|
||||||
|
<a-icon type="message" v-if="getClientWithComment(clientEmail, dbInbound.id).comment"></a-icon>
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<a-tag :style="{ margin: '0', padding: '0 2px' }" color="blue" v-if="clientCount[dbInbound.id].online.length">[[ clientCount[dbInbound.id].online.length ]]</a-tag>
|
<a-tag :style="{ margin: '0', padding: '0 2px' }" color="blue" v-if="clientCount[dbInbound.id].online.length">[[ clientCount[dbInbound.id].online.length ]]</a-tag>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
|
@ -479,25 +530,57 @@
|
||||||
<a-tag :style="{ margin: '0' }" color="blue">[[ clientCount[dbInbound.id].clients ]]</a-tag>
|
<a-tag :style="{ margin: '0' }" color="blue">[[ clientCount[dbInbound.id].clients ]]</a-tag>
|
||||||
<a-popover title='{{ i18n "disabled" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
<a-popover title='{{ i18n "disabled" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
||||||
<template slot="content">
|
<template slot="content">
|
||||||
<p v-for="clientEmail in clientCount[dbInbound.id].deactive">[[ clientEmail ]]</p>
|
<div v-for="clientEmail in clientCount[dbInbound.id].deactive" :key="clientEmail" class="client-popup-item">
|
||||||
|
<span>[[ clientEmail ]]</span>
|
||||||
|
<a-tooltip :overlay-class-name="themeSwitcher.currentTheme">
|
||||||
|
<template #title>
|
||||||
|
[[ getClientWithComment(clientEmail, dbInbound.id).comment ]]
|
||||||
|
</template>
|
||||||
|
<a-icon type="message" v-if="getClientWithComment(clientEmail, dbInbound.id).comment"></a-icon>
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<a-tag :style="{ margin: '0', padding: '0 2px' }" v-if="clientCount[dbInbound.id].deactive.length">[[ clientCount[dbInbound.id].deactive.length ]]</a-tag>
|
<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>
|
||||||
<a-popover title='{{ i18n "depleted" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
<a-popover title='{{ i18n "depleted" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
||||||
<template slot="content">
|
<template slot="content">
|
||||||
<p v-for="clientEmail in clientCount[dbInbound.id].depleted">[[ clientEmail ]]</p>
|
<div v-for="clientEmail in clientCount[dbInbound.id].depleted" :key="clientEmail" class="client-popup-item">
|
||||||
|
<span>[[ clientEmail ]]</span>
|
||||||
|
<a-tooltip :overlay-class-name="themeSwitcher.currentTheme">
|
||||||
|
<template #title>
|
||||||
|
[[ getClientWithComment(clientEmail, dbInbound.id).comment ]]
|
||||||
|
</template>
|
||||||
|
<a-icon type="message" v-if="getClientWithComment(clientEmail, dbInbound.id).comment"></a-icon>
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
</template>
|
</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-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>
|
||||||
<a-popover title='{{ i18n "depletingSoon" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
<a-popover title='{{ i18n "depletingSoon" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
||||||
<template slot="content">
|
<template slot="content">
|
||||||
<p v-for="clientEmail in clientCount[dbInbound.id].expiring">[[ clientEmail ]]</p>
|
<div v-for="clientEmail in clientCount[dbInbound.id].expiring" :key="clientEmail" class="client-popup-item">
|
||||||
|
<span>[[ clientEmail ]]</span>
|
||||||
|
<a-tooltip :overlay-class-name="themeSwitcher.currentTheme">
|
||||||
|
<template #title>
|
||||||
|
[[ getClientWithComment(clientEmail, dbInbound.id).comment ]]
|
||||||
|
</template>
|
||||||
|
<a-icon type="message" v-if="getClientWithComment(clientEmail, dbInbound.id).comment"></a-icon>
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<a-tag :style="{ margin: '0', padding: '0 2px' }" color="orange" v-if="clientCount[dbInbound.id].expiring.length">[[ clientCount[dbInbound.id].expiring.length ]]</a-tag>
|
<a-tag :style="{ margin: '0', padding: '0 2px' }" color="orange" v-if="clientCount[dbInbound.id].expiring.length">[[ clientCount[dbInbound.id].expiring.length ]]</a-tag>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
<a-popover title='{{ i18n "online" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
<a-popover title='{{ i18n "online" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
||||||
<template slot="content">
|
<template slot="content">
|
||||||
<p v-for="clientEmail in clientCount[dbInbound.id].online">[[ clientEmail ]]</p>
|
<div v-for="clientEmail in clientCount[dbInbound.id].online" :key="clientEmail" class="client-popup-item">
|
||||||
|
<span>[[ clientEmail ]]</span>
|
||||||
|
<a-tooltip :overlay-class-name="themeSwitcher.currentTheme">
|
||||||
|
<template #title>
|
||||||
|
[[ getClientWithComment(clientEmail, dbInbound.id).comment ]]
|
||||||
|
</template>
|
||||||
|
<a-icon type="message" v-if="getClientWithComment(clientEmail, dbInbound.id).comment"></a-icon>
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<a-tag :style="{ margin: '0', padding: '0 2px' }" color="green" v-if="clientCount[dbInbound.id].online.length">[[ clientCount[dbInbound.id].online.length ]]</a-tag>
|
<a-tag :style="{ margin: '0', padding: '0 2px' }" color="green" v-if="clientCount[dbInbound.id].online.length">[[ clientCount[dbInbound.id].online.length ]]</a-tag>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
|
@ -575,12 +658,14 @@
|
||||||
</a-table>
|
</a-table>
|
||||||
</a-space>
|
</a-space>
|
||||||
</a-card>
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
</transition>
|
</transition>
|
||||||
</a-spin>
|
</a-spin>
|
||||||
</a-layout-content>
|
</a-layout-content>
|
||||||
</a-layout>
|
</a-layout>
|
||||||
</a-layout>
|
</a-layout>
|
||||||
{{template "js" .}}
|
{{template "page/body_scripts" .}}
|
||||||
<script src="{{ .base_path }}assets/qrcode/qrious2.min.js?{{ .cur_ver }}"></script>
|
<script src="{{ .base_path }}assets/qrcode/qrious2.min.js?{{ .cur_ver }}"></script>
|
||||||
<script src="{{ .base_path }}assets/uri/URI.min.js?{{ .cur_ver }}"></script>
|
<script src="{{ .base_path }}assets/uri/URI.min.js?{{ .cur_ver }}"></script>
|
||||||
<script src="{{ .base_path }}assets/js/model/inbound.js?{{ .cur_ver }}"></script>
|
<script src="{{ .base_path }}assets/js/model/inbound.js?{{ .cur_ver }}"></script>
|
||||||
|
@ -589,6 +674,13 @@
|
||||||
{{template "component/aThemeSwitch" .}}
|
{{template "component/aThemeSwitch" .}}
|
||||||
{{template "component/aCustomStatistic" .}}
|
{{template "component/aCustomStatistic" .}}
|
||||||
{{template "component/aPersianDatepicker" .}}
|
{{template "component/aPersianDatepicker" .}}
|
||||||
|
{{template "modals/inboundModal"}}
|
||||||
|
{{template "modals/promptModal"}}
|
||||||
|
{{template "modals/qrcodeModal"}}
|
||||||
|
{{template "modals/textModal"}}
|
||||||
|
{{template "modals/inboundInfoModal"}}
|
||||||
|
{{template "modals/clientsModal"}}
|
||||||
|
{{template "modals/clientsBulkModal"}}
|
||||||
<script>
|
<script>
|
||||||
const columns = [{
|
const columns = [{
|
||||||
title: "ID",
|
title: "ID",
|
||||||
|
@ -683,7 +775,10 @@
|
||||||
data: {
|
data: {
|
||||||
themeSwitcher,
|
themeSwitcher,
|
||||||
persianDatepicker,
|
persianDatepicker,
|
||||||
spinning: false,
|
loadingStates: {
|
||||||
|
fetched: false,
|
||||||
|
spinning: false
|
||||||
|
},
|
||||||
inbounds: [],
|
inbounds: [],
|
||||||
dbInbounds: [],
|
dbInbounds: [],
|
||||||
searchKey: '',
|
searchKey: '',
|
||||||
|
@ -714,7 +809,18 @@
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
loading(spinning = true) {
|
loading(spinning = true) {
|
||||||
this.spinning = spinning;
|
this.loadingStates.spinning = spinning;
|
||||||
|
},
|
||||||
|
getClientWithComment(email, inboundId) {
|
||||||
|
const dbInbound = this.dbInbounds.find(inbound => inbound.id === inboundId);
|
||||||
|
if (!dbInbound) return { email, comment: '' };
|
||||||
|
|
||||||
|
const inboundSettings = JSON.parse(dbInbound.settings);
|
||||||
|
if (inboundSettings.clients) {
|
||||||
|
const client = inboundSettings.clients.find(c => c.email === email);
|
||||||
|
return client ? { email: client.email, comment: client.comment || '' } : { email, comment: '' };
|
||||||
|
}
|
||||||
|
return { email, comment: '' };
|
||||||
},
|
},
|
||||||
async getDBInbounds() {
|
async getDBInbounds() {
|
||||||
this.refreshing = true;
|
this.refreshing = true;
|
||||||
|
@ -725,6 +831,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.getOnlineUsers();
|
await this.getOnlineUsers();
|
||||||
|
|
||||||
this.setInbounds(msg.obj);
|
this.setInbounds(msg.obj);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.refreshing = false;
|
this.refreshing = false;
|
||||||
|
@ -776,6 +883,9 @@
|
||||||
this.clientCount[inbound.id] = this.getClientCounts(inbound, to_inbound);
|
this.clientCount[inbound.id] = this.getClientCounts(inbound, to_inbound);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!this.loadingStates.fetched) {
|
||||||
|
this.loadingStates.fetched = true
|
||||||
|
}
|
||||||
if(this.enableFilter){
|
if(this.enableFilter){
|
||||||
this.filterInbounds();
|
this.filterInbounds();
|
||||||
} else {
|
} else {
|
||||||
|
@ -1447,9 +1557,9 @@
|
||||||
},
|
},
|
||||||
async manualRefresh() {
|
async manualRefresh() {
|
||||||
if (!this.refreshing) {
|
if (!this.refreshing) {
|
||||||
this.spinning = true;
|
this.loadingStates.spinning = true;
|
||||||
await this.getDBInbounds();
|
await this.getDBInbounds();
|
||||||
this.spinning = false;
|
this.loadingStates.spinning = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
pagination(obj){
|
pagination(obj){
|
||||||
|
@ -1519,13 +1629,4 @@
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
{{ template "page/body_end" .}}
|
||||||
{{template "modals/inboundModal"}}
|
|
||||||
{{template "modals/promptModal"}}
|
|
||||||
{{template "modals/qrcodeModal"}}
|
|
||||||
{{template "modals/textModal"}}
|
|
||||||
{{template "modals/inboundInfoModal"}}
|
|
||||||
{{template "modals/clientsModal"}}
|
|
||||||
{{template "modals/clientsBulkModal"}}
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,20 +1,9 @@
|
||||||
<!DOCTYPE html>
|
{{ template "page/head_start" .}}
|
||||||
<html lang="en">
|
|
||||||
{{template "head" .}}
|
|
||||||
<style>
|
<style>
|
||||||
@media (min-width: 769px) {
|
@media (min-width: 769px) {
|
||||||
.ant-layout-content {
|
.ant-layout-content {
|
||||||
margin: 24px 16px;
|
margin: 24px 16px;
|
||||||
}
|
}
|
||||||
.ant-card-hoverable {
|
|
||||||
margin-inline: 0.3rem;
|
|
||||||
}
|
|
||||||
.ant-alert-error {
|
|
||||||
margin-inline: 0.3rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.ant-col-sm-24 {
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
}
|
||||||
.ant-card-dark h2 {
|
.ant-card-dark h2 {
|
||||||
color: var(--dark-color-text-primary);
|
color: var(--dark-color-text-primary);
|
||||||
|
@ -79,15 +68,16 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
{{ template "page/head_end" .}}
|
||||||
|
|
||||||
<body>
|
{{ template "page/body_start" .}}
|
||||||
<a-layout id="app" v-cloak :class="themeSwitcher.currentTheme">
|
<a-layout id="app" v-cloak :class="themeSwitcher.currentTheme">
|
||||||
<a-sidebar></a-sidebar>
|
<a-sidebar></a-sidebar>
|
||||||
<a-layout id="content-layout">
|
<a-layout id="content-layout">
|
||||||
<a-layout-content>
|
<a-layout-content>
|
||||||
<a-spin :spinning="spinning" :delay="200" :tip="loadingTip">
|
<a-spin :spinning="loadingStates.spinning" :delay="200" :tip="loadingTip">
|
||||||
<transition name="list" appear>
|
<transition name="list" appear>
|
||||||
<a-alert type="error" v-if="showAlert" :style="{ marginBottom: '10px' }"
|
<a-alert type="error" v-if="showAlert && loadingStates.fetched" :style="{ marginBottom: '10px' }"
|
||||||
message='{{ i18n "secAlertTitle" }}'
|
message='{{ i18n "secAlertTitle" }}'
|
||||||
color="red"
|
color="red"
|
||||||
description='{{ i18n "secAlertSsl" }}'
|
description='{{ i18n "secAlertSsl" }}'
|
||||||
|
@ -96,15 +86,15 @@
|
||||||
</transition>
|
</transition>
|
||||||
<transition name="list" appear>
|
<transition name="list" appear>
|
||||||
<template>
|
<template>
|
||||||
<a-row v-if="!status.isLoaded">
|
<a-row v-if="!loadingStates.fetched">
|
||||||
<a-card :style="{ textAlign: 'center', padding: '30px 0', marginTop: '10px', background: 'transparent', border: 'none' }">
|
<a-card :style="{ textAlign: 'center', padding: '30px 0', marginTop: '10px', background: 'transparent', border: 'none' }">
|
||||||
<a-spin tip='{{ i18n "loading" }}'></a-spin>
|
<a-spin tip='{{ i18n "loading" }}'></a-spin>
|
||||||
</a-card>
|
</a-card>
|
||||||
</a-row>
|
</a-row>
|
||||||
<a-row v-else>
|
<a-row :gutter="[isMobile ? 8 : 16, isMobile ? 0 : 12]" v-else>
|
||||||
<a-row>
|
<a-col>
|
||||||
<a-card hoverable>
|
<a-card hoverable>
|
||||||
<a-row>
|
<a-row :gutter="[0, isMobile ? 16 : 0]">
|
||||||
<a-col :sm="24" :md="12">
|
<a-col :sm="24" :md="12">
|
||||||
<a-row>
|
<a-row>
|
||||||
<a-col :span="12" :style="{ textAlign: 'center' }">
|
<a-col :span="12" :style="{ textAlign: 'center' }">
|
||||||
|
@ -154,7 +144,7 @@
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
</a-card>
|
</a-card>
|
||||||
</a-row>
|
</a-col>
|
||||||
<a-col :sm="24" :lg="12">
|
<a-col :sm="24" :lg="12">
|
||||||
<a-card hoverable>
|
<a-card hoverable>
|
||||||
<template #title>
|
<template #title>
|
||||||
|
@ -237,7 +227,7 @@
|
||||||
</a-tag>
|
</a-tag>
|
||||||
</a>
|
</a>
|
||||||
<a rel="noopener" href="https://github.com/MHSanaei/3x-ui/wiki" target="_blank">
|
<a rel="noopener" href="https://github.com/MHSanaei/3x-ui/wiki" target="_blank">
|
||||||
<a-tag>
|
<a-tag color="purple">
|
||||||
<span>{{ i18n "pages.index.documentation" }}</span>
|
<span>{{ i18n "pages.index.documentation" }}</span>
|
||||||
</a-tag>
|
</a-tag>
|
||||||
</a>
|
</a>
|
||||||
|
@ -455,8 +445,8 @@
|
||||||
</a-list-item>
|
</a-list-item>
|
||||||
</a-list>
|
</a-list>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
</a-layout>
|
</a-layout>
|
||||||
{{template "js" .}}
|
{{template "page/body_scripts" .}}
|
||||||
{{template "component/aSidebar" .}}
|
{{template "component/aSidebar" .}}
|
||||||
{{template "component/aThemeSwitch" .}}
|
{{template "component/aThemeSwitch" .}}
|
||||||
{{template "component/aCustomStatistic" .}}
|
{{template "component/aCustomStatistic" .}}
|
||||||
|
@ -489,7 +479,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
class Status {
|
class Status {
|
||||||
constructor(data, isLoaded = false) {
|
constructor(data) {
|
||||||
this.cpu = new CurTotal(0, 0);
|
this.cpu = new CurTotal(0, 0);
|
||||||
this.cpuCores = 0;
|
this.cpuCores = 0;
|
||||||
this.logicalPro = 0;
|
this.logicalPro = 0;
|
||||||
|
@ -513,7 +503,6 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isLoaded = isLoaded;
|
|
||||||
this.cpu = new CurTotal(data.cpu, 100);
|
this.cpu = new CurTotal(data.cpu, 100);
|
||||||
this.cpuCores = data.cpuCores;
|
this.cpuCores = data.cpuCores;
|
||||||
this.logicalPro = data.logicalPro;
|
this.logicalPro = data.logicalPro;
|
||||||
|
@ -633,32 +622,39 @@
|
||||||
mixins: [MediaQueryMixin],
|
mixins: [MediaQueryMixin],
|
||||||
data: {
|
data: {
|
||||||
themeSwitcher,
|
themeSwitcher,
|
||||||
|
loadingStates: {
|
||||||
|
fetched: false,
|
||||||
|
spinning: false
|
||||||
|
},
|
||||||
status: new Status(),
|
status: new Status(),
|
||||||
versionModal,
|
versionModal,
|
||||||
logModal,
|
logModal,
|
||||||
backupModal,
|
backupModal,
|
||||||
spinning: false,
|
|
||||||
loadingTip: '{{ i18n "loading"}}',
|
loadingTip: '{{ i18n "loading"}}',
|
||||||
showAlert: false,
|
showAlert: false,
|
||||||
showIp: false
|
showIp: false
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
loading(spinning, tip = '{{ i18n "loading"}}') {
|
loading(spinning, tip = '{{ i18n "loading"}}') {
|
||||||
this.spinning = spinning;
|
this.loadingStates.spinning = spinning;
|
||||||
this.loadingTip = tip;
|
this.loadingTip = tip;
|
||||||
},
|
},
|
||||||
async getStatus() {
|
async getStatus() {
|
||||||
try {
|
try {
|
||||||
const msg = await HttpUtil.post('/server/status');
|
const msg = await HttpUtil.post('/server/status');
|
||||||
if (msg.success) {
|
if (msg.success) {
|
||||||
|
if (!this.loadingStates.fetched) {
|
||||||
|
this.loadingStates.fetched = true;
|
||||||
|
}
|
||||||
|
|
||||||
this.setStatus(msg.obj, true);
|
this.setStatus(msg.obj, true);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Failed to get status:", e);
|
console.error("Failed to get status:", e);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setStatus(data, isLoaded = false) {
|
setStatus(data) {
|
||||||
this.status = new Status(data, isLoaded);
|
this.status = new Status(data);
|
||||||
},
|
},
|
||||||
async openSelectV2rayVersion() {
|
async openSelectV2rayVersion() {
|
||||||
this.loading(true);
|
this.loading(true);
|
||||||
|
@ -788,5 +784,4 @@
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
{{ template "page/body_end" .}}
|
||||||
</html>
|
|
|
@ -1,6 +1,4 @@
|
||||||
<!DOCTYPE html>
|
{{ template "page/head_start" .}}
|
||||||
<html lang="en">
|
|
||||||
{{template "head" .}}
|
|
||||||
<style>
|
<style>
|
||||||
html * {
|
html * {
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
|
@ -300,6 +298,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.title {
|
||||||
|
font-size: 1.4rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.words-wrapper {
|
.words-wrapper {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
@ -447,9 +451,10 @@
|
||||||
margin: 2px 0 4px;
|
margin: 2px 0 4px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
{{ template "page/head_end" .}}
|
||||||
|
|
||||||
<body>
|
{{ template "page/body_start" .}}
|
||||||
<a-layout id="app" v-cloak :class="themeSwitcher.currentTheme">
|
<a-layout id="app" v-cloak :class="themeSwitcher.currentTheme">
|
||||||
<transition name="list" appear>
|
<transition name="list" appear>
|
||||||
<a-layout-content class="under" :style="{ minHeight: '0' }">
|
<a-layout-content class="under" :style="{ minHeight: '0' }">
|
||||||
<div class="waves-header">
|
<div class="waves-header">
|
||||||
|
@ -536,10 +541,10 @@
|
||||||
</a-row>
|
</a-row>
|
||||||
</a-layout-content>
|
</a-layout-content>
|
||||||
</transition>
|
</transition>
|
||||||
</a-layout>
|
</a-layout>
|
||||||
{{template "js" .}}
|
{{template "page/body_scripts" .}}
|
||||||
{{template "component/aThemeSwitch" .}}
|
{{template "component/aThemeSwitch" .}}
|
||||||
<script>
|
<script>
|
||||||
const app = new Vue({
|
const app = new Vue({
|
||||||
delimiters: ['[[', ']]'],
|
delimiters: ['[[', ']]'],
|
||||||
el: '#app',
|
el: '#app',
|
||||||
|
@ -615,6 +620,5 @@
|
||||||
newWord.classList.add('is-visible');
|
newWord.classList.add('is-visible');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
{{ template "page/body_end" .}}
|
||||||
</html>
|
|
110
web/html/modals/dns_presets_modal.html
Normal file
110
web/html/modals/dns_presets_modal.html
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
{{define "modals/dnsPresetsModal"}}
|
||||||
|
<a-modal id="dnsPresets-modal" v-model="dnsPresetsModal.visible" :title="dnsPresetsModal.title" :closable="true"
|
||||||
|
:mask-closable="false" :footer="null" :class="themeSwitcher.currentTheme">
|
||||||
|
<a-list class="ant-dns-presets-list" bordered :style="{ width: '100%' }">
|
||||||
|
<a-list-item v-for="dns in dnsPresetsDatabase" :style="{ padding: '12px 16px' }">
|
||||||
|
<a-row justify="space-between" align="middle">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-space direction="vertical" size="small">
|
||||||
|
<span class="ant-dns-presets-list-name">[[ dns.name ]]</span>
|
||||||
|
<a-tag :color="dns.family ? 'purple' : 'green'">[[ dns.family ? '{{ i18n "pages.xray.dns.dnsPresetFamily" }}' : 'DNS' ]]</a-tag>
|
||||||
|
</a-space>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12" :style="{ textAlign: 'right' }">
|
||||||
|
<a-button type="primary" @click="dnsPresetsModal.install(dns.data)">{{ i18n "install" }}</a-button>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-list-item>
|
||||||
|
</a-list>
|
||||||
|
</a-modal>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.dark .ant-dns-presets-list {
|
||||||
|
border-color: var(--dark-color-stroke)
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .ant-dns-presets-list-name {
|
||||||
|
color: var(--dark-color-text-primary);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const dnsPresetsDatabase = [
|
||||||
|
{
|
||||||
|
name: 'Google DNS',
|
||||||
|
family: false,
|
||||||
|
data: [
|
||||||
|
"8.8.8.8",
|
||||||
|
"8.8.4.4",
|
||||||
|
"2001:4860:4860::8888",
|
||||||
|
"2001:4860:4860::8844"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Cloudflare DNS',
|
||||||
|
family: false,
|
||||||
|
data: [
|
||||||
|
"1.1.1.1",
|
||||||
|
"1.0.0.1",
|
||||||
|
"2606:4700:4700::1111",
|
||||||
|
"2606:4700:4700::1001"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Adguard DNS',
|
||||||
|
family: false,
|
||||||
|
data: [
|
||||||
|
"94.140.14.14",
|
||||||
|
"94.140.15.15",
|
||||||
|
"2a10:50c0::ad1:ff",
|
||||||
|
"2a10:50c0::ad2:ff"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Adguard Family DNS',
|
||||||
|
family: true,
|
||||||
|
data: [
|
||||||
|
"94.140.14.14",
|
||||||
|
"94.140.15.15",
|
||||||
|
"2a10:50c0::ad1:ff",
|
||||||
|
"2a10:50c0::ad2:ff"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Cloudflare Family DNS',
|
||||||
|
family: true,
|
||||||
|
data: [
|
||||||
|
"1.1.1.3",
|
||||||
|
"1.0.0.3",
|
||||||
|
"2606:4700:4700::1113",
|
||||||
|
"2606:4700:4700::1003"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const dnsPresetsModal = {
|
||||||
|
title: '',
|
||||||
|
visible: false,
|
||||||
|
selected: null,
|
||||||
|
install(selectedPreset) {
|
||||||
|
return ObjectUtil.execute(dnsPresetsModal.selected, selectedPreset);
|
||||||
|
},
|
||||||
|
show({ title = '', selected = (selectedPreset) => { }, isEdit = false }) {
|
||||||
|
this.title = title;
|
||||||
|
this.selected = selected;
|
||||||
|
this.visible = true;
|
||||||
|
},
|
||||||
|
close() {
|
||||||
|
dnsPresetsModal.visible = false;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
new Vue({
|
||||||
|
delimiters: ['[[', ']]'],
|
||||||
|
el: '#dnsPresets-modal',
|
||||||
|
data: {
|
||||||
|
dnsPresetsModal: dnsPresetsModal,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{{end}}
|
|
@ -1,57 +0,0 @@
|
||||||
{{define "modals/fakednsModal"}}
|
|
||||||
<a-modal id="fakedns-modal" v-model="fakednsModal.visible" :title="fakednsModal.title" @ok="fakednsModal.ok"
|
|
||||||
:closable="true" :mask-closable="false"
|
|
||||||
:ok-text="fakednsModal.okText" cancel-text='{{ i18n "close" }}' :class="themeSwitcher.currentTheme">
|
|
||||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
|
||||||
<a-form-item label='{{ i18n "pages.xray.fakedns.ipPool" }}'>
|
|
||||||
<a-input v-model.trim="fakednsModal.fakeDns.ipPool"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label='{{ i18n "pages.xray.fakedns.poolSize" }}'>
|
|
||||||
<a-input-number v-model.number="fakednsModal.fakeDns.poolSize" :min="1"></a-input-number>
|
|
||||||
</a-form-item>
|
|
||||||
</a-form>
|
|
||||||
</a-modal>
|
|
||||||
<script>
|
|
||||||
const fakednsModal = {
|
|
||||||
title: '',
|
|
||||||
visible: false,
|
|
||||||
okText: '{{ i18n "confirm" }}',
|
|
||||||
isEdit: false,
|
|
||||||
confirm: null,
|
|
||||||
fakeDns: {
|
|
||||||
ipPool: "198.18.0.0/16",
|
|
||||||
poolSize: 65535,
|
|
||||||
},
|
|
||||||
ok() {
|
|
||||||
ObjectUtil.execute(fakednsModal.confirm, fakednsModal.fakeDns);
|
|
||||||
},
|
|
||||||
show({ title='', okText='{{ i18n "confirm" }}', fakeDns, confirm=(fakeDns)=>{}, isEdit=false }) {
|
|
||||||
this.title = title;
|
|
||||||
this.okText = okText;
|
|
||||||
this.confirm = confirm;
|
|
||||||
this.visible = true;
|
|
||||||
if(isEdit) {
|
|
||||||
this.fakeDns = fakeDns;
|
|
||||||
} else {
|
|
||||||
this.fakeDns = {
|
|
||||||
ipPool: "198.18.0.0/16",
|
|
||||||
poolSize: 65535,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.isEdit = isEdit;
|
|
||||||
},
|
|
||||||
close() {
|
|
||||||
fakednsModal.visible = false;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
new Vue({
|
|
||||||
delimiters: ['[[', ']]'],
|
|
||||||
el: '#fakedns-modal',
|
|
||||||
data: {
|
|
||||||
fakednsModal: fakednsModal,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
|
||||||
{{end}}
|
|
|
@ -433,6 +433,9 @@
|
||||||
<a-tooltip title='{{ i18n "copy" }}'>
|
<a-tooltip title='{{ i18n "copy" }}'>
|
||||||
<a-button :style="{ minWidth: '24px' }" size="small" icon="snippets" @click="copy(infoModal.links[index])"></a-button>
|
<a-button :style="{ minWidth: '24px' }" size="small" icon="snippets" @click="copy(infoModal.links[index])"></a-button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
|
<a-tooltip title='{{ i18n "download" }}'>
|
||||||
|
<a-button :style="{ minWidth: '24px' }" size="small" icon="download" @click="FileManager.downloadTextFile(infoModal.links[index], `peer-${index + 1}.conf`)"></a-button>
|
||||||
|
</a-tooltip>
|
||||||
</tr-info-title>
|
</tr-info-title>
|
||||||
<div v-html="infoModal.links[index].replaceAll(`\n`,`<br />`)" :style="{ borderRadius: '1rem', padding: '0.5rem' }" class="client-table-odd-row">
|
<div v-html="infoModal.links[index].replaceAll(`\n`,`<br />`)" :style="{ borderRadius: '1rem', padding: '0.5rem' }" class="client-table-odd-row">
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,6 +6,16 @@
|
||||||
<a-form-item label='{{ i18n "pages.xray.outbound.address" }}'>
|
<a-form-item label='{{ i18n "pages.xray.outbound.address" }}'>
|
||||||
<a-input v-model.trim="dnsModal.dnsServer.address"></a-input>
|
<a-input v-model.trim="dnsModal.dnsServer.address"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
<a-form-item label='{{ i18n "pages.inbounds.port" }}'>
|
||||||
|
<a-input-number v-model.number="dnsModal.dnsServer.port" :min="1" :max="65531"></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label='{{ i18n "pages.xray.dns.strategy" }}'>
|
||||||
|
<a-select v-model="dnsModal.dnsServer.queryStrategy" :style="{ width: '100%' }"
|
||||||
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
|
<a-select-option :value="l" :label="l" v-for="l in ['UseSystem', 'UseIP', 'UseIPv4', 'UseIPv6']"> [[ l ]] </a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<a-divider :style="{ margin: '5px 0' }"></a-divider>
|
||||||
<a-form-item label='{{ i18n "pages.xray.dns.domains" }}'>
|
<a-form-item label='{{ i18n "pages.xray.dns.domains" }}'>
|
||||||
<a-button icon="plus" size="small" type="primary" @click="dnsModal.dnsServer.domains.push('')"></a-button>
|
<a-button icon="plus" size="small" type="primary" @click="dnsModal.dnsServer.domains.push('')"></a-button>
|
||||||
<template v-for="(domain, index) in dnsModal.dnsServer.domains">
|
<template v-for="(domain, index) in dnsModal.dnsServer.domains">
|
||||||
|
@ -15,15 +25,6 @@
|
||||||
</a-input>
|
</a-input>
|
||||||
</template>
|
</template>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label='{{ i18n "pages.xray.dns.strategy" }}' v-if="isAdvanced">
|
|
||||||
<a-select v-model="dnsModal.dnsServer.queryStrategy" :style="{ width: '100%' }"
|
|
||||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
|
||||||
<a-select-option :value="l" :label="l" v-for="l in ['UseIP', 'UseIPv4', 'UseIPv6']"> [[ l ]] </a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label='Skip Fallback' v-if="isAdvanced">
|
|
||||||
<a-switch v-model="dnsModal.dnsServer.skipFallback"></a-switch>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label='{{ i18n "pages.xray.dns.expectIPs"}}'>
|
<a-form-item label='{{ i18n "pages.xray.dns.expectIPs"}}'>
|
||||||
<a-button icon="plus" size="small" type="primary" @click="dnsModal.dnsServer.expectIPs.push('')"></a-button>
|
<a-button icon="plus" size="small" type="primary" @click="dnsModal.dnsServer.expectIPs.push('')"></a-button>
|
||||||
<template v-for="(domain, index) in dnsModal.dnsServer.expectIPs">
|
<template v-for="(domain, index) in dnsModal.dnsServer.expectIPs">
|
||||||
|
@ -33,31 +34,50 @@
|
||||||
</a-input>
|
</a-input>
|
||||||
</template>
|
</template>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
<a-form-item label='{{ i18n "pages.xray.dns.unexpectIPs"}}'>
|
||||||
|
<a-button icon="plus" size="small" type="primary" @click="dnsModal.dnsServer.unexpectedIPs.push('')"></a-button>
|
||||||
|
<template v-for="(domain, index) in dnsModal.dnsServer.unexpectedIPs">
|
||||||
|
<a-input v-model.trim="dnsModal.dnsServer.unexpectedIPs[index]">
|
||||||
|
<a-button icon="minus" size="small" slot="addonAfter"
|
||||||
|
@click="dnsModal.dnsServer.unexpectedIPs.splice(index,1)"></a-button>
|
||||||
|
</a-input>
|
||||||
|
</template>
|
||||||
|
</a-form-item>
|
||||||
|
<a-divider :style="{ margin: '5px 0' }"></a-divider>
|
||||||
|
<a-form-item label='Skip Fallback'>
|
||||||
|
<a-switch v-model="dnsModal.dnsServer.skipFallback"></a-switch>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label='Disable Cache'>
|
||||||
|
<a-switch v-model="dnsModal.dnsServer.disableCache"></a-switch>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label='Final Query'>
|
||||||
|
<a-switch v-model="dnsModal.dnsServer.finalQuery"></a-switch>
|
||||||
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
<script>
|
<script>
|
||||||
|
const defaultDnsObject = {
|
||||||
|
address: "localhost",
|
||||||
|
port: 53,
|
||||||
|
domains: [],
|
||||||
|
expectIPs: [],
|
||||||
|
unexpectedIPs: [],
|
||||||
|
queryStrategy: 'UseIP',
|
||||||
|
skipFallback: true,
|
||||||
|
disableCache: false,
|
||||||
|
finalQuery: false
|
||||||
|
}
|
||||||
|
|
||||||
const dnsModal = {
|
const dnsModal = {
|
||||||
title: '',
|
title: '',
|
||||||
visible: false,
|
visible: false,
|
||||||
okText: '{{ i18n "confirm" }}',
|
okText: '{{ i18n "confirm" }}',
|
||||||
isEdit: false,
|
isEdit: false,
|
||||||
confirm: null,
|
confirm: null,
|
||||||
dnsServer: {
|
dnsServer: { ...defaultDnsObject },
|
||||||
address: "localhost",
|
|
||||||
domains: [],
|
|
||||||
expectIPs: [],
|
|
||||||
queryStrategy: 'UseIP',
|
|
||||||
skipFallback: true,
|
|
||||||
},
|
|
||||||
ok() {
|
ok() {
|
||||||
domains = dnsModal.dnsServer.domains.filter(d => d.length > 0);
|
ObjectUtil.execute(dnsModal.confirm, { ...dnsModal.dnsServer });
|
||||||
expectIPs = dnsModal.dnsServer.expectIPs.filter(ip => ip.length > 0);
|
|
||||||
dnsModal.dnsServer.domains = domains;
|
|
||||||
dnsModal.dnsServer.expectIPs = expectIPs;
|
|
||||||
newDnsServer = (domains.length > 0 || expectIPs.length > 0) ? dnsModal.dnsServer : dnsModal.dnsServer.address;
|
|
||||||
ObjectUtil.execute(dnsModal.confirm, newDnsServer);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
show({
|
show({
|
||||||
title = '',
|
title = '',
|
||||||
okText = '{{ i18n "confirm" }}',
|
okText = '{{ i18n "confirm" }}',
|
||||||
|
@ -69,28 +89,28 @@
|
||||||
this.okText = okText;
|
this.okText = okText;
|
||||||
this.confirm = confirm;
|
this.confirm = confirm;
|
||||||
this.visible = true;
|
this.visible = true;
|
||||||
if (isEdit) {
|
|
||||||
if (typeof dnsServer == 'object') {
|
|
||||||
this.dnsServer = dnsServer;
|
|
||||||
} else {
|
|
||||||
this.dnsServer = {
|
|
||||||
address: dnsServer ?? "",
|
|
||||||
domains: [],
|
|
||||||
expectIPs: [],
|
|
||||||
queryStrategy: 'UseIP',
|
|
||||||
skipFallback: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.dnsServer = {
|
|
||||||
address: "localhost",
|
|
||||||
domains: [],
|
|
||||||
expectIPs: [],
|
|
||||||
queryStrategy: 'UseIP',
|
|
||||||
skipFallback: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.isEdit = isEdit;
|
this.isEdit = isEdit;
|
||||||
|
|
||||||
|
if (isEdit) {
|
||||||
|
switch (typeof dnsServer) {
|
||||||
|
case 'string':
|
||||||
|
const dnsObj = { ...defaultDnsObject };
|
||||||
|
|
||||||
|
dnsObj.address = dnsServer;
|
||||||
|
|
||||||
|
this.dnsServer = dnsObj;
|
||||||
|
break;
|
||||||
|
case 'object':
|
||||||
|
this.dnsServer = dnsServer;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.dnsServer = { ...defaultDnsObject };
|
||||||
|
|
||||||
|
this.dnsServer.domains = [];
|
||||||
|
this.dnsServer.expectIPs = [];
|
||||||
|
this.dnsServer.unexpectedIPs = [];
|
||||||
|
}
|
||||||
},
|
},
|
||||||
close() {
|
close() {
|
||||||
dnsModal.visible = false;
|
dnsModal.visible = false;
|
||||||
|
@ -101,13 +121,6 @@
|
||||||
el: '#dns-modal',
|
el: '#dns-modal',
|
||||||
data: {
|
data: {
|
||||||
dnsModal: dnsModal,
|
dnsModal: dnsModal,
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
isAdvanced: {
|
|
||||||
get: function () {
|
|
||||||
return dnsModal.dnsServer.domains.length > 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
56
web/html/modals/xray_fakedns_modal.html
Normal file
56
web/html/modals/xray_fakedns_modal.html
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
{{define "modals/fakednsModal"}}
|
||||||
|
<a-modal id="fakedns-modal" v-model="fakednsModal.visible" :title="fakednsModal.title" @ok="fakednsModal.ok"
|
||||||
|
:closable="true" :mask-closable="false" :ok-text="fakednsModal.okText" cancel-text='{{ i18n "close" }}'
|
||||||
|
:class="themeSwitcher.currentTheme">
|
||||||
|
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||||
|
<a-form-item label='{{ i18n "pages.xray.fakedns.ipPool" }}'>
|
||||||
|
<a-input v-model.trim="fakednsModal.fakeDns.ipPool"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label='{{ i18n "pages.xray.fakedns.poolSize" }}'>
|
||||||
|
<a-input-number v-model.number="fakednsModal.fakeDns.poolSize" :min="1"></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-modal>
|
||||||
|
<script>
|
||||||
|
const fakednsDefaultData = {
|
||||||
|
ipPool: "198.18.0.0/16",
|
||||||
|
poolSize: 65535,
|
||||||
|
}
|
||||||
|
|
||||||
|
const fakednsModal = {
|
||||||
|
title: '',
|
||||||
|
visible: false,
|
||||||
|
okText: '{{ i18n "confirm" }}',
|
||||||
|
isEdit: false,
|
||||||
|
confirm: null,
|
||||||
|
fakeDns: { ...fakednsDefaultData },
|
||||||
|
ok() {
|
||||||
|
ObjectUtil.execute(fakednsModal.confirm, fakednsModal.fakeDns);
|
||||||
|
},
|
||||||
|
show({ title = '', okText = '{{ i18n "confirm" }}', fakeDns, confirm = (fakeDns) => { }, isEdit = false }) {
|
||||||
|
this.title = title;
|
||||||
|
this.okText = okText;
|
||||||
|
this.confirm = confirm;
|
||||||
|
this.visible = true;
|
||||||
|
if (isEdit) {
|
||||||
|
this.fakeDns = fakeDns;
|
||||||
|
} else {
|
||||||
|
this.fakeDns = { ...fakednsDefaultData }
|
||||||
|
}
|
||||||
|
this.isEdit = isEdit;
|
||||||
|
},
|
||||||
|
close() {
|
||||||
|
fakednsModal.visible = false;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
new Vue({
|
||||||
|
delimiters: ['[[', ']]'],
|
||||||
|
el: '#fakedns-modal',
|
||||||
|
data: {
|
||||||
|
fakednsModal: fakednsModal,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
{{end}}
|
|
@ -1,6 +1,4 @@
|
||||||
<!DOCTYPE html>
|
{{ template "page/head_start" .}}
|
||||||
<html lang="en">
|
|
||||||
{{template "head" .}}
|
|
||||||
<style>
|
<style>
|
||||||
@media (min-width: 769px) {
|
@media (min-width: 769px) {
|
||||||
.ant-layout-content {
|
.ant-layout-content {
|
||||||
|
@ -60,14 +58,16 @@
|
||||||
margin-block-end: 12px;
|
margin-block-end: 12px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<body>
|
{{ template "page/head_end" .}}
|
||||||
<a-layout id="app" v-cloak :class="themeSwitcher.currentTheme">
|
|
||||||
|
{{ template "page/body_start" .}}
|
||||||
|
<a-layout id="app" v-cloak :class="themeSwitcher.currentTheme">
|
||||||
<a-sidebar></a-sidebar>
|
<a-sidebar></a-sidebar>
|
||||||
<a-layout id="content-layout">
|
<a-layout id="content-layout">
|
||||||
<a-layout-content>
|
<a-layout-content>
|
||||||
<a-spin :spinning="spinning" :delay="500" tip='{{ i18n "loading"}}'>
|
<a-spin :spinning="loadingStates.spinning" :delay="500" tip='{{ i18n "loading"}}'>
|
||||||
<transition name="list" appear>
|
<transition name="list" appear>
|
||||||
<a-alert type="error" v-if="confAlerts.length>0" :style="{ marginBottom: '10px' }"
|
<a-alert type="error" v-if="confAlerts.length>0 && loadingStates.fetched" :style="{ marginBottom: '10px' }"
|
||||||
message='{{ i18n "secAlertTitle" }}'
|
message='{{ i18n "secAlertTitle" }}'
|
||||||
color="red"
|
color="red"
|
||||||
show-icon closable>
|
show-icon closable>
|
||||||
|
@ -77,8 +77,16 @@
|
||||||
</template>
|
</template>
|
||||||
</a-alert>
|
</a-alert>
|
||||||
</transition>
|
</transition>
|
||||||
<a-space direction="vertical">
|
<transition name="list" appear>
|
||||||
<a-card hoverable :style="{ marginBottom: '.5rem', overflowX: 'hidden' }">
|
<template>
|
||||||
|
<a-row v-if="!loadingStates.fetched">
|
||||||
|
<a-card :style="{ textAlign: 'center', padding: '30px 0', marginTop: '10px', background: 'transparent', border: 'none' }">
|
||||||
|
<a-spin tip='{{ i18n "loading" }}'></a-spin>
|
||||||
|
</a-card>
|
||||||
|
</a-row>
|
||||||
|
<a-row :gutter="[isMobile ? 8 : 16, isMobile ? 0 : 12]" v-else>
|
||||||
|
<a-col>
|
||||||
|
<a-card hoverable>
|
||||||
<a-row :style="{ display: 'flex', flexWrap: 'wrap', alignItems: 'center' }">
|
<a-row :style="{ display: 'flex', flexWrap: 'wrap', alignItems: 'center' }">
|
||||||
<a-col :xs="24" :sm="10" :style="{ padding: '4px' }">
|
<a-col :xs="24" :sm="10" :style="{ padding: '4px' }">
|
||||||
<a-space direction="horizontal">
|
<a-space direction="horizontal">
|
||||||
|
@ -99,29 +107,54 @@
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
</a-card>
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
<a-col>
|
||||||
<a-tabs default-active-key="1">
|
<a-tabs default-active-key="1">
|
||||||
<a-tab-pane key="1" tab='{{ i18n "pages.settings.panelSettings" }}' :style="{ paddingTop: '20px' }">
|
<a-tab-pane key="1" :style="{ paddingTop: '20px' }">
|
||||||
|
<template #tab>
|
||||||
|
<a-icon type="setting"></a-icon>
|
||||||
|
<span>{{ i18n "pages.settings.panelSettings" }}</span>
|
||||||
|
</template>
|
||||||
{{ template "settings/panel/general" . }}
|
{{ template "settings/panel/general" . }}
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane key="2" tab='{{ i18n "pages.settings.securitySettings" }}' :style="{ paddingTop: '20px' }">
|
<a-tab-pane key="2" :style="{ paddingTop: '20px' }">
|
||||||
|
<template #tab>
|
||||||
|
<a-icon type="safety"></a-icon>
|
||||||
|
<span>{{ i18n "pages.settings.securitySettings" }}</span>
|
||||||
|
</template>
|
||||||
{{ template "settings/panel/security" . }}
|
{{ template "settings/panel/security" . }}
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane key="3" tab='{{ i18n "pages.settings.TGBotSettings" }}' :style="{ paddingTop: '20px' }">
|
<a-tab-pane key="3" :style="{ paddingTop: '20px' }">
|
||||||
|
<template #tab>
|
||||||
|
<a-icon type="message"></a-icon>
|
||||||
|
<span>{{ i18n "pages.settings.TGBotSettings" }}</span>
|
||||||
|
</template>
|
||||||
{{ template "settings/panel/telegram" . }}
|
{{ template "settings/panel/telegram" . }}
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane key="4" tab='{{ i18n "pages.settings.subSettings" }}' :style="{ paddingTop: '20px' }">
|
<a-tab-pane key="4" :style="{ paddingTop: '20px' }">
|
||||||
|
<template #tab>
|
||||||
|
<a-icon type="cloud-server"></a-icon>
|
||||||
|
<span>{{ i18n "pages.settings.subSettings" }}</span>
|
||||||
|
</template>
|
||||||
{{ template "settings/panel/subscription/general" . }}
|
{{ template "settings/panel/subscription/general" . }}
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane key="5" tab='{{ i18n "pages.settings.subSettings" }} Json' v-if="allSetting.subEnable" :style="{ paddingTop: '20px' }">
|
<a-tab-pane key="5" v-if="allSetting.subEnable" :style="{ paddingTop: '20px' }">
|
||||||
|
<template #tab>
|
||||||
|
<a-icon type="code"></a-icon>
|
||||||
|
<span>{{ i18n "pages.settings.subSettings" }} (JSON)</span>
|
||||||
|
</template>
|
||||||
{{ template "settings/panel/subscription/json" . }}
|
{{ template "settings/panel/subscription/json" . }}
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
</a-tabs>
|
</a-tabs>
|
||||||
</a-space>
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</template>
|
||||||
|
</transition>
|
||||||
</a-spin>
|
</a-spin>
|
||||||
</a-layout-content>
|
</a-layout-content>
|
||||||
</a-layout>
|
</a-layout>
|
||||||
</a-layout>
|
</a-layout>
|
||||||
{{template "js" .}}
|
{{template "page/body_scripts" .}}
|
||||||
<script src="{{ .base_path }}assets/qrcode/qrious2.min.js?{{ .cur_ver }}"></script>
|
<script src="{{ .base_path }}assets/qrcode/qrious2.min.js?{{ .cur_ver }}"></script>
|
||||||
<script src="{{ .base_path }}assets/otpauth/otpauth.umd.min.js?{{ .cur_ver }}"></script>
|
<script src="{{ .base_path }}assets/otpauth/otpauth.umd.min.js?{{ .cur_ver }}"></script>
|
||||||
<script src="{{ .base_path }}assets/js/model/setting.js?{{ .cur_ver }}"></script>
|
<script src="{{ .base_path }}assets/js/model/setting.js?{{ .cur_ver }}"></script>
|
||||||
|
@ -132,10 +165,14 @@
|
||||||
<script>
|
<script>
|
||||||
const app = new Vue({
|
const app = new Vue({
|
||||||
delimiters: ['[[', ']]'],
|
delimiters: ['[[', ']]'],
|
||||||
|
mixins: [MediaQueryMixin],
|
||||||
el: '#app',
|
el: '#app',
|
||||||
data: {
|
data: {
|
||||||
themeSwitcher,
|
themeSwitcher,
|
||||||
spinning: false,
|
loadingStates: {
|
||||||
|
fetched: false,
|
||||||
|
spinning: false
|
||||||
|
},
|
||||||
oldAllSetting: new AllSetting(),
|
oldAllSetting: new AllSetting(),
|
||||||
allSetting: new AllSetting(),
|
allSetting: new AllSetting(),
|
||||||
saveBtnDisable: true,
|
saveBtnDisable: true,
|
||||||
|
@ -248,13 +285,16 @@
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
loading(spinning = true) {
|
loading(spinning = true) {
|
||||||
this.spinning = spinning;
|
this.loadingStates.spinning = spinning;
|
||||||
},
|
},
|
||||||
async getAllSetting() {
|
async getAllSetting() {
|
||||||
this.loading(true);
|
|
||||||
const msg = await HttpUtil.post("/panel/setting/all");
|
const msg = await HttpUtil.post("/panel/setting/all");
|
||||||
this.loading(false);
|
|
||||||
if (msg.success) {
|
if (msg.success) {
|
||||||
|
if (!this.loadingStates.fetched) {
|
||||||
|
this.loadingStates.fetched = true
|
||||||
|
}
|
||||||
|
|
||||||
this.oldAllSetting = new AllSetting(msg.obj);
|
this.oldAllSetting = new AllSetting(msg.obj);
|
||||||
this.allSetting = new AllSetting(msg.obj);
|
this.allSetting = new AllSetting(msg.obj);
|
||||||
app.changeRemarkSample();
|
app.changeRemarkSample();
|
||||||
|
@ -508,7 +548,7 @@
|
||||||
if (!this.allSetting) return [];
|
if (!this.allSetting) return [];
|
||||||
var alerts = []
|
var alerts = []
|
||||||
if (window.location.protocol !== "https:") alerts.push('{{ i18n "secAlertSSL" }}');
|
if (window.location.protocol !== "https:") alerts.push('{{ i18n "secAlertSSL" }}');
|
||||||
if (this.allSetting.webPort == 54321) alerts.push('{{ i18n "secAlertPanelPort" }}');
|
if (this.allSetting.webPort === 2053) alerts.push('{{ i18n "secAlertPanelPort" }}');
|
||||||
panelPath = window.location.pathname.split('/').length < 4
|
panelPath = window.location.pathname.split('/').length < 4
|
||||||
if (panelPath && this.allSetting.webBasePath == '/') alerts.push('{{ i18n "secAlertPanelURI" }}');
|
if (panelPath && this.allSetting.webBasePath == '/') alerts.push('{{ i18n "secAlertPanelURI" }}');
|
||||||
if (this.allSetting.subEnable) {
|
if (this.allSetting.subEnable) {
|
||||||
|
@ -531,5 +571,4 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
{{ template "page/body_end" .}}
|
||||||
</html>
|
|
|
@ -1,5 +1,5 @@
|
||||||
{{define "settings/xray/advanced"}}
|
{{define "settings/xray/advanced"}}
|
||||||
<a-space direction="vertical" size="small">
|
<a-space direction="vertical" size="small" :style="{ marginTop: '20px' }">
|
||||||
<a-list-item-meta title='{{ i18n "pages.xray.Template"}}'
|
<a-list-item-meta title='{{ i18n "pages.xray.Template"}}'
|
||||||
description='{{ i18n "pages.xray.TemplateDesc"}}'></a-list-item-meta>
|
description='{{ i18n "pages.xray.TemplateDesc"}}'></a-list-item-meta>
|
||||||
<a-radio-group v-model="advSettings" @change="changeCode" button-style="solid" :style="{ margin: '10px 0' }"
|
<a-radio-group v-model="advSettings" @change="changeCode" button-style="solid" :style="{ margin: '10px 0' }"
|
||||||
|
|
|
@ -135,7 +135,7 @@
|
||||||
</template>
|
</template>
|
||||||
</a-setting-list-item>
|
</a-setting-list-item>
|
||||||
</a-collapse-panel>
|
</a-collapse-panel>
|
||||||
<a-collapse-panel key="4" header='{{ i18n "pages.xray.blockConfigs"}}'>
|
<a-collapse-panel key="4" header='{{ i18n "pages.xray.basicRouting"}}'>
|
||||||
<a-row :xs="24" :sm="24" :lg="12">
|
<a-row :xs="24" :sm="24" :lg="12">
|
||||||
<a-alert type="warning" :style="{ textAlign: 'center' }">
|
<a-alert type="warning" :style="{ textAlign: 'center' }">
|
||||||
<template slot="message">
|
<template slot="message">
|
||||||
|
@ -144,22 +144,12 @@
|
||||||
</template>
|
</template>
|
||||||
</a-alert>
|
</a-alert>
|
||||||
</a-row>
|
</a-row>
|
||||||
<a-setting-list-item paddings="small">
|
<a-setting-list-item paddings="small" :style="{ marginBottom: '20px' }">
|
||||||
<template #title>{{ i18n "pages.xray.Torrent"}}</template>
|
<template #title>{{ i18n "pages.xray.Torrent"}}</template>
|
||||||
<template #description>{{ i18n "pages.xray.TorrentDesc"}}</template>
|
|
||||||
<template #control>
|
<template #control>
|
||||||
<a-switch v-model="torrentSettings"></a-switch>
|
<a-switch v-model="torrentSettings"></a-switch>
|
||||||
</template>
|
</template>
|
||||||
</a-setting-list-item>
|
</a-setting-list-item>
|
||||||
<a-setting-list-item paddings="small">
|
|
||||||
<template #title>{{ i18n "pages.xray.Family"}}</template>
|
|
||||||
<template #description>{{ i18n "pages.xray.FamilyDesc"}}</template>
|
|
||||||
<template #control>
|
|
||||||
<a-switch v-model="familyProtectSettings"></a-switch>
|
|
||||||
</template>
|
|
||||||
</a-setting-list-item>
|
|
||||||
</a-collapse-panel>
|
|
||||||
<a-collapse-panel key="5" header='{{ i18n "pages.xray.basicRouting"}}'>
|
|
||||||
<a-row :xs="24" :sm="24" :lg="12">
|
<a-row :xs="24" :sm="24" :lg="12">
|
||||||
<a-alert type="warning" :style="{ textAlign: 'center' }">
|
<a-alert type="warning" :style="{ textAlign: 'center' }">
|
||||||
<template slot="message">
|
<template slot="message">
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
<template #control>
|
<template #control>
|
||||||
<a-select v-model="dnsStrategy" :style="{ width: '100%' }"
|
<a-select v-model="dnsStrategy" :style="{ width: '100%' }"
|
||||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-select-option :value="l" :label="l" v-for="l in ['UseIP', 'UseIPv4', 'UseIPv6']">
|
<a-select-option :value="l" :label="l" v-for="l in ['UseSystem', 'UseIP', 'UseIPv4', 'UseIPv6']">
|
||||||
<span>[[ l ]]</span>
|
<span>[[ l ]]</span>
|
||||||
</a-select-option>
|
</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
|
@ -56,6 +56,14 @@
|
||||||
<a-switch v-model="dnsDisableFallbackIfMatch"></a-switch>
|
<a-switch v-model="dnsDisableFallbackIfMatch"></a-switch>
|
||||||
</template>
|
</template>
|
||||||
</a-setting-list-item>
|
</a-setting-list-item>
|
||||||
|
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.xray.dns.useSystemHosts" }}</template>
|
||||||
|
<template #description>{{ i18n "pages.xray.dns.useSystemHostsDesc" }}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-switch v-model="dnsUseSystemHosts"></a-switch>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
</template>
|
</template>
|
||||||
</a-collapse-panel>
|
</a-collapse-panel>
|
||||||
<template v-if="enableDNS">
|
<template v-if="enableDNS">
|
||||||
|
@ -102,9 +110,12 @@
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<a-empty description='{{ i18n "emptyDnsDesc" }}' :style="{ margin: '10px' }">
|
<a-empty description='{{ i18n "emptyDnsDesc" }}' :style="{ margin: '10px' }">
|
||||||
<a-button type="primary" icon="plus" @click="addDNSServer()" :style="{ marginTop: '10px' }">
|
<a-button-group>
|
||||||
|
<a-button type="primary" icon="plus" @click="addDNSServer()">
|
||||||
<span>{{ i18n "pages.xray.dns.add" }}</span>
|
<span>{{ i18n "pages.xray.dns.add" }}</span>
|
||||||
</a-button>
|
</a-button>
|
||||||
|
<a-button type="primary" icon="menu" @click="openDNSPresets()"></a-button>
|
||||||
|
</a-button-group>
|
||||||
</a-empty>
|
</a-empty>
|
||||||
</template>
|
</template>
|
||||||
</a-collapse-panel>
|
</a-collapse-panel>
|
||||||
|
|
|
@ -1,74 +1,75 @@
|
||||||
<!DOCTYPE html>
|
{{ template "page/head_start" .}}
|
||||||
<html lang="en">
|
|
||||||
{{template "head" .}}
|
|
||||||
<link rel="stylesheet" href="{{ .base_path }}assets/codemirror/codemirror.min.css?{{ .cur_ver }}">
|
<link rel="stylesheet" href="{{ .base_path }}assets/codemirror/codemirror.min.css?{{ .cur_ver }}">
|
||||||
<link rel="stylesheet" href="{{ .base_path }}assets/codemirror/fold/foldgutter.css">
|
<link rel="stylesheet" href="{{ .base_path }}assets/codemirror/fold/foldgutter.css">
|
||||||
<link rel="stylesheet" href="{{ .base_path }}assets/codemirror/xq.min.css?{{ .cur_ver }}">
|
<link rel="stylesheet" href="{{ .base_path }}assets/codemirror/xq.min.css?{{ .cur_ver }}">
|
||||||
<link rel="stylesheet" href="{{ .base_path }}assets/codemirror/lint/lint.css">
|
<link rel="stylesheet" href="{{ .base_path }}assets/codemirror/lint/lint.css">
|
||||||
|
|
||||||
<script src="{{ .base_path }}assets/js/model/outbound.js?{{ .cur_ver }}"></script>
|
|
||||||
<script src="{{ .base_path }}assets/codemirror/codemirror.min.js?{{ .cur_ver }}"></script>
|
|
||||||
<script src="{{ .base_path }}assets/codemirror/javascript.js"></script>
|
|
||||||
<script src="{{ .base_path }}assets/codemirror/jshint.js"></script>
|
|
||||||
<script src="{{ .base_path }}assets/codemirror/jsonlint.js"></script>
|
|
||||||
<script src="{{ .base_path }}assets/codemirror/lint/lint.js"></script>
|
|
||||||
<script src="{{ .base_path }}assets/codemirror/lint/javascript-lint.js"></script>
|
|
||||||
<script src="{{ .base_path }}assets/codemirror/hint/javascript-hint.js"></script>
|
|
||||||
<script src="{{ .base_path }}assets/codemirror/fold/foldcode.js"></script>
|
|
||||||
<script src="{{ .base_path }}assets/codemirror/fold/foldgutter.js"></script>
|
|
||||||
<script src="{{ .base_path }}assets/codemirror/fold/brace-fold.js"></script>
|
|
||||||
<style>
|
<style>
|
||||||
@media (min-width: 769px) {
|
@media (min-width: 769px) {
|
||||||
.ant-layout-content {
|
.ant-layout-content {
|
||||||
margin: 24px 16px;
|
margin: 24px 16px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.ant-tabs-nav .ant-tabs-tab {
|
.ant-tabs-nav .ant-tabs-tab {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 12px .5rem;
|
padding: 12px .5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-table-thead>tr>th,
|
.ant-table-thead>tr>th,
|
||||||
.ant-table-tbody>tr>td {
|
.ant-table-tbody>tr>td {
|
||||||
padding: 10px 0px;
|
padding: 10px 0px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-tabs-bar {
|
.ant-tabs-bar {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-list-item {
|
.ant-list-item {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-list-item>li {
|
.ant-list-item>li {
|
||||||
padding: 10px 20px !important;
|
padding: 10px 20px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-collapse-content-box .ant-alert {
|
.ant-collapse-content-box .ant-alert {
|
||||||
margin-block-end: 12px;
|
margin-block-end: 12px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<body>
|
{{ template "page/head_end" .}}
|
||||||
<a-layout id="app" v-cloak :class="themeSwitcher.currentTheme">
|
|
||||||
|
{{ template "page/body_start" .}}
|
||||||
|
<a-layout id="app" v-cloak :class="themeSwitcher.currentTheme">
|
||||||
<a-sidebar></a-sidebar>
|
<a-sidebar></a-sidebar>
|
||||||
<a-layout id="content-layout">
|
<a-layout id="content-layout">
|
||||||
<a-layout-content>
|
<a-layout-content>
|
||||||
<a-spin :spinning="spinning" :delay="500" tip='{{ i18n "loading"}}'>
|
<a-spin :spinning="loadingStates.spinning" :delay="500" tip='{{ i18n "loading"}}'>
|
||||||
<transition name="list" appear>
|
<transition name="list" appear>
|
||||||
<a-alert type="error" v-if="showAlert" :style="{ marginBottom: '10px' }"
|
<a-alert type="error" v-if="showAlert && loadingStates.fetched" :style="{ marginBottom: '10px' }" message='{{ i18n "secAlertTitle" }}'
|
||||||
message='{{ i18n "secAlertTitle" }}'
|
color="red" description='{{ i18n "secAlertSsl" }}' show-icon closable>
|
||||||
color="red"
|
|
||||||
description='{{ i18n "secAlertSsl" }}'
|
|
||||||
show-icon closable>
|
|
||||||
</a-alert>
|
</a-alert>
|
||||||
</transition>
|
</transition>
|
||||||
<a-space direction="vertical">
|
<transition name="list" appear>
|
||||||
<a-card hoverable :style="{ marginBottom: '.5rem' }">
|
<a-row v-if="!loadingStates.fetched">
|
||||||
|
<a-card :style="{ textAlign: 'center', padding: '30px 0', marginTop: '10px', background: 'transparent', border: 'none' }">
|
||||||
|
<a-spin tip='{{ i18n "loading" }}'></a-spin>
|
||||||
|
</a-card>
|
||||||
|
</a-row>
|
||||||
|
<a-row :gutter="[isMobile ? 8 : 16, isMobile ? 0 : 12]" v-else>
|
||||||
|
<a-col>
|
||||||
|
<a-card hoverable>
|
||||||
<a-row :style="{ display: 'flex', flexWrap: 'wrap', alignItems: 'center' }">
|
<a-row :style="{ display: 'flex', flexWrap: 'wrap', alignItems: 'center' }">
|
||||||
<a-col :xs="24" :sm="10" :style="{ padding: '4px' }">
|
<a-col :xs="24" :sm="10" :style="{ padding: '4px' }">
|
||||||
<a-space direction="horizontal">
|
<a-space direction="horizontal">
|
||||||
<a-button type="primary" :disabled="saveBtnDisable" @click="updateXraySetting">{{ i18n "pages.xray.save" }}</a-button>
|
<a-button type="primary" :disabled="saveBtnDisable" @click="updateXraySetting">
|
||||||
<a-button type="danger" :disabled="!saveBtnDisable" @click="restartXray">{{ i18n "pages.xray.restart" }}</a-button>
|
{{ i18n "pages.xray.save" }}
|
||||||
<a-popover v-if="restartResult"
|
</a-button>
|
||||||
:overlay-class-name="themeSwitcher.currentTheme">
|
<a-button type="danger" :disabled="!saveBtnDisable" @click="restartXray">
|
||||||
|
{{ i18n "pages.xray.restart" }}
|
||||||
|
</a-button>
|
||||||
|
<a-popover v-if="restartResult" :overlay-class-name="themeSwitcher.currentTheme">
|
||||||
<span slot="title">{{ i18n "pages.index.xrayErrorPopoverTitle" }}</span>
|
<span slot="title">{{ i18n "pages.index.xrayErrorPopoverTitle" }}</span>
|
||||||
<template slot="content">
|
<template slot="content">
|
||||||
<span :style="{ maxWidth: '400px' }" v-for="line in restartResult.split('\n')">[[ line ]]</span>
|
<span :style="{ maxWidth: '400px' }" v-for="line in restartResult.split('\n')">[[ line ]]</span>
|
||||||
|
@ -80,45 +81,89 @@
|
||||||
<a-col :xs="24" :sm="14">
|
<a-col :xs="24" :sm="14">
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<a-back-top :target="() => document.getElementById('content-layout')" visibility-height="200"></a-back-top>
|
<a-back-top :target="() => document.getElementById('content-layout')"
|
||||||
<a-alert type="warning" :style="{ float: 'right', width: 'fit-content' }" message='{{ i18n "pages.settings.infoDesc" }}' show-icon>
|
visibility-height="200"></a-back-top>
|
||||||
|
<a-alert type="warning" :style="{ float: 'right', width: 'fit-content' }"
|
||||||
|
message='{{ i18n "pages.settings.infoDesc" }}' show-icon>
|
||||||
</a-alert>
|
</a-alert>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
</a-card>
|
</a-card>
|
||||||
<a-tabs class="ant-card-dark-box-nohover" default-active-key="1"
|
</a-col>
|
||||||
@change="(activeKey) => { this.changePage(activeKey); }"
|
<a-col>
|
||||||
|
<a-tabs default-active-key="tpl-basic" @change="(activeKey) => { this.changePage(activeKey); }"
|
||||||
:class="themeSwitcher.currentTheme">
|
:class="themeSwitcher.currentTheme">
|
||||||
<a-tab-pane key="tpl-basic" tab='{{ i18n "pages.xray.basicTemplate"}}' :style="{ paddingTop: '20px' }">
|
<a-tab-pane key="tpl-basic" :style="{ paddingTop: '20px' }">
|
||||||
|
<template #tab>
|
||||||
|
<a-icon type="setting"></a-icon>
|
||||||
|
<span>{{ i18n "pages.xray.basicTemplate"}}</span>
|
||||||
|
</template>
|
||||||
{{ template "settings/xray/basics" . }}
|
{{ template "settings/xray/basics" . }}
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane key="tpl-routing" tab='{{ i18n "pages.xray.Routings"}}' :style="{ paddingTop: '20px' }">
|
<a-tab-pane key="tpl-routing" :style="{ paddingTop: '20px' }">
|
||||||
|
<template #tab>
|
||||||
|
<a-icon type="swap"></a-icon>
|
||||||
|
<span>{{ i18n "pages.xray.Routings"}}</span>
|
||||||
|
</template>
|
||||||
{{ template "settings/xray/routing" . }}
|
{{ template "settings/xray/routing" . }}
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane key="tpl-outbound" tab='{{ i18n "pages.xray.Outbounds"}}' force-render="true">
|
<a-tab-pane key="tpl-outbound" force-render="true">
|
||||||
|
<template #tab>
|
||||||
|
<a-icon type="upload"></a-icon>
|
||||||
|
<span>{{ i18n "pages.xray.Outbounds"}}</span>
|
||||||
|
</template>
|
||||||
{{ template "settings/xray/outbounds" . }}
|
{{ template "settings/xray/outbounds" . }}
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane key="tpl-reverse" tab='{{ i18n "pages.xray.outbound.reverse"}}' :style="{ paddingTop: '20px' }" force-render="true">
|
<a-tab-pane key="tpl-reverse" :style="{ paddingTop: '20px' }" force-render="true">
|
||||||
|
<template #tab>
|
||||||
|
<a-icon type="import"></a-icon>
|
||||||
|
<span>{{ i18n "pages.xray.outbound.reverse"}}</span>
|
||||||
|
</template>
|
||||||
{{ template "settings/xray/reverse" . }}
|
{{ template "settings/xray/reverse" . }}
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane key="tpl-balancer" tab='{{ i18n "pages.xray.Balancers"}}' :style="{ paddingTop: '20px' }" force-render="true">
|
<a-tab-pane key="tpl-balancer" :style="{ paddingTop: '20px' }" force-render="true">
|
||||||
|
<template #tab>
|
||||||
|
<a-icon type="cluster"></a-icon>
|
||||||
|
<span>{{ i18n "pages.xray.Balancers"}}</span>
|
||||||
|
</template>
|
||||||
{{ template "settings/xray/balancers" . }}
|
{{ template "settings/xray/balancers" . }}
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane key="tpl-dns" tab='DNS' :style="{ paddingTop: '20px' }" force-render="true">
|
<a-tab-pane key="tpl-dns" :style="{ paddingTop: '20px' }" force-render="true">
|
||||||
|
<template #tab>
|
||||||
|
<a-icon type="database"></a-icon>
|
||||||
|
<span>DNS</span>
|
||||||
|
</template>
|
||||||
{{ template "settings/xray/dns" . }}
|
{{ template "settings/xray/dns" . }}
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane key="tpl-advanced" tab='{{ i18n "pages.xray.advancedTemplate"}}' :style="{ paddingTop: '20px' }" force-render="true">
|
<a-tab-pane key="tpl-advanced" force-render="true">
|
||||||
|
<template #tab>
|
||||||
|
<a-icon type="code"></a-icon>
|
||||||
|
<span>{{ i18n "pages.xray.advancedTemplate"}}</span>
|
||||||
|
</template>
|
||||||
{{ template "settings/xray/advanced" . }}
|
{{ template "settings/xray/advanced" . }}
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
</a-tabs>
|
</a-tabs>
|
||||||
</a-space>
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</transition>
|
||||||
</a-spin>
|
</a-spin>
|
||||||
</a-layout-content>
|
</a-layout-content>
|
||||||
</a-layout>
|
</a-layout>
|
||||||
</a-layout>
|
</a-layout>
|
||||||
{{template "js" .}}
|
{{template "page/body_scripts" .}}
|
||||||
|
<script src="{{ .base_path }}assets/js/model/outbound.js?{{ .cur_ver }}"></script>
|
||||||
|
<script src="{{ .base_path }}assets/codemirror/codemirror.min.js?{{ .cur_ver }}"></script>
|
||||||
|
<script src="{{ .base_path }}assets/codemirror/javascript.js"></script>
|
||||||
|
<script src="{{ .base_path }}assets/codemirror/jshint.js"></script>
|
||||||
|
<script src="{{ .base_path }}assets/codemirror/jsonlint.js"></script>
|
||||||
|
<script src="{{ .base_path }}assets/codemirror/lint/lint.js"></script>
|
||||||
|
<script src="{{ .base_path }}assets/codemirror/lint/javascript-lint.js"></script>
|
||||||
|
<script src="{{ .base_path }}assets/codemirror/hint/javascript-hint.js"></script>
|
||||||
|
<script src="{{ .base_path }}assets/codemirror/fold/foldcode.js"></script>
|
||||||
|
<script src="{{ .base_path }}assets/codemirror/fold/foldgutter.js"></script>
|
||||||
|
<script src="{{ .base_path }}assets/codemirror/fold/brace-fold.js"></script>
|
||||||
{{template "component/aSidebar" .}}
|
{{template "component/aSidebar" .}}
|
||||||
{{template "component/aThemeSwitch" .}}
|
{{template "component/aThemeSwitch" .}}
|
||||||
{{template "component/aTableSortable" .}}
|
{{template "component/aTableSortable" .}}
|
||||||
|
@ -128,25 +173,34 @@
|
||||||
{{template "modals/reverseModal"}}
|
{{template "modals/reverseModal"}}
|
||||||
{{template "modals/balancerModal"}}
|
{{template "modals/balancerModal"}}
|
||||||
{{template "modals/dnsModal"}}
|
{{template "modals/dnsModal"}}
|
||||||
|
{{template "modals/dnsPresetsModal"}}
|
||||||
{{template "modals/fakednsModal"}}
|
{{template "modals/fakednsModal"}}
|
||||||
{{template "modals/warpModal"}}
|
{{template "modals/warpModal"}}
|
||||||
<script>
|
<script>
|
||||||
const rulesColumns = [
|
const rulesColumns = [
|
||||||
{ title: "#", align: 'center', width: 15, scopedSlots: { customRender: 'action' } },
|
{ title: "#", align: 'center', width: 15, scopedSlots: { customRender: 'action' } },
|
||||||
{ title: '{{ i18n "pages.xray.rules.source"}}', children: [
|
{
|
||||||
|
title: '{{ i18n "pages.xray.rules.source"}}', children: [
|
||||||
{ title: 'IP', dataIndex: "source", align: 'center', width: 20, ellipsis: true },
|
{ title: 'IP', dataIndex: "source", align: 'center', width: 20, ellipsis: true },
|
||||||
{ title: 'Port', dataIndex: 'sourcePort', align: 'center', width: 10, ellipsis: true } ]},
|
{ title: '{{ i18n "pages.inbounds.port" }}', dataIndex: 'sourcePort', align: 'center', width: 10, ellipsis: true }]
|
||||||
{ title: '{{ i18n "pages.inbounds.network"}}', children: [
|
},
|
||||||
|
{
|
||||||
|
title: '{{ i18n "pages.inbounds.network"}}', children: [
|
||||||
{ title: 'L4', dataIndex: 'network', align: 'center', width: 10 },
|
{ title: 'L4', dataIndex: 'network', align: 'center', width: 10 },
|
||||||
{ title: 'Protocol', dataIndex: 'protocol', align: 'center', width: 15, ellipsis: true },
|
{ title: '{{ i18n "protocol" }}', dataIndex: 'protocol', align: 'center', width: 15, ellipsis: true },
|
||||||
{ title: 'Attrs', dataIndex: 'attrs', align: 'center', width: 10, ellipsis: true } ]},
|
{ title: 'Attrs', dataIndex: 'attrs', align: 'center', width: 10, ellipsis: true }]
|
||||||
{ title: '{{ i18n "pages.xray.rules.dest"}}', children: [
|
},
|
||||||
|
{
|
||||||
|
title: '{{ i18n "pages.xray.rules.dest"}}', children: [
|
||||||
{ title: 'IP', dataIndex: 'ip', align: 'center', width: 20, ellipsis: true },
|
{ title: 'IP', dataIndex: 'ip', align: 'center', width: 20, ellipsis: true },
|
||||||
{ title: 'Domain', dataIndex: 'domain', align: 'center', width: 20, ellipsis: true },
|
{ title: '{{ i18n "pages.xray.outbound.domain" }}', dataIndex: 'domain', align: 'center', width: 20, ellipsis: true },
|
||||||
{ title: 'Port', dataIndex: 'port', align: 'center', width: 10, ellipsis: true }]},
|
{ title: '{{ i18n "pages.inbounds.port" }}', dataIndex: 'port', align: 'center', width: 10, ellipsis: true }]
|
||||||
{ title: '{{ i18n "pages.xray.rules.inbound"}}', children: [
|
},
|
||||||
{ title: 'Tag', dataIndex: 'inboundTag', align: 'center', width: 15, ellipsis: true },
|
{
|
||||||
{ title: 'Client Email', dataIndex: 'user', align: 'center', width: 20, ellipsis: true }]},
|
title: '{{ i18n "pages.xray.rules.inbound"}}', children: [
|
||||||
|
{ title: '{{ i18n "pages.xray.outbound.tag" }}', dataIndex: 'inboundTag', align: 'center', width: 15, ellipsis: true },
|
||||||
|
{ title: '{{ i18n "pages.inbounds.client" }}', dataIndex: 'user', align: 'center', width: 20, ellipsis: true }]
|
||||||
|
},
|
||||||
{ title: '{{ i18n "pages.xray.rules.outbound"}}', dataIndex: 'outboundTag', align: 'center', width: 17 },
|
{ title: '{{ i18n "pages.xray.rules.outbound"}}', dataIndex: 'outboundTag', align: 'center', width: 17 },
|
||||||
{ title: '{{ i18n "pages.xray.rules.balancer"}}', dataIndex: 'balancerTag', align: 'center', width: 15 },
|
{ title: '{{ i18n "pages.xray.rules.balancer"}}', dataIndex: 'balancerTag', align: 'center', width: 15 },
|
||||||
];
|
];
|
||||||
|
@ -176,8 +230,8 @@
|
||||||
const balancerColumns = [
|
const balancerColumns = [
|
||||||
{ title: "#", align: 'center', width: 20, scopedSlots: { customRender: 'action' } },
|
{ title: "#", align: 'center', width: 20, scopedSlots: { customRender: 'action' } },
|
||||||
{ title: '{{ i18n "pages.xray.balancer.tag"}}', dataIndex: 'tag', align: 'center', width: 50 },
|
{ title: '{{ i18n "pages.xray.balancer.tag"}}', dataIndex: 'tag', align: 'center', width: 50 },
|
||||||
{ title: '{{ i18n "pages.xray.balancer.balancerStrategy"}}', align: 'center', width: 50, scopedSlots: { customRender: 'strategy' }},
|
{ title: '{{ i18n "pages.xray.balancer.balancerStrategy"}}', align: 'center', width: 50, scopedSlots: { customRender: 'strategy' } },
|
||||||
{ title: '{{ i18n "pages.xray.balancer.balancerSelectors"}}', align: 'center', width: 100, scopedSlots: { customRender: 'selector' }},
|
{ title: '{{ i18n "pages.xray.balancer.balancerSelectors"}}', align: 'center', width: 100, scopedSlots: { customRender: 'selector' } },
|
||||||
];
|
];
|
||||||
|
|
||||||
const dnsColumns = [
|
const dnsColumns = [
|
||||||
|
@ -200,7 +254,10 @@
|
||||||
data: {
|
data: {
|
||||||
themeSwitcher,
|
themeSwitcher,
|
||||||
isDarkTheme: themeSwitcher.isDarkTheme,
|
isDarkTheme: themeSwitcher.isDarkTheme,
|
||||||
spinning: false,
|
loadingStates: {
|
||||||
|
fetched: false,
|
||||||
|
spinning: false
|
||||||
|
},
|
||||||
oldXraySetting: '',
|
oldXraySetting: '',
|
||||||
xraySetting: '',
|
xraySetting: '',
|
||||||
inboundTags: [],
|
inboundTags: [],
|
||||||
|
@ -308,16 +365,7 @@
|
||||||
{ label: 'Netflix', value: 'geosite:netflix' },
|
{ label: 'Netflix', value: 'geosite:netflix' },
|
||||||
{ label: 'Reddit', value: 'geosite:reddit' },
|
{ label: 'Reddit', value: 'geosite:reddit' },
|
||||||
{ label: 'Speedtest', value: 'geosite:speedtest' },
|
{ label: 'Speedtest', value: 'geosite:speedtest' },
|
||||||
],
|
]
|
||||||
familyProtectDNS: {
|
|
||||||
"servers": [
|
|
||||||
"1.1.1.3", // https://developers.cloudflare.com/1.1.1.1/setup/
|
|
||||||
"1.0.0.3",
|
|
||||||
"2606:4700:4700::1113",
|
|
||||||
"2606:4700:4700::1003"
|
|
||||||
],
|
|
||||||
"queryStrategy": "UseIP"
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
defaultObservatory: {
|
defaultObservatory: {
|
||||||
subjectSelector: [],
|
subjectSelector: [],
|
||||||
|
@ -338,7 +386,7 @@
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
loading(spinning = true) {
|
loading(spinning = true) {
|
||||||
this.spinning = spinning;
|
this.loadingStates.spinning = spinning;
|
||||||
},
|
},
|
||||||
async getOutboundsTraffic() {
|
async getOutboundsTraffic() {
|
||||||
const msg = await HttpUtil.get("/panel/xray/getOutboundsTraffic");
|
const msg = await HttpUtil.get("/panel/xray/getOutboundsTraffic");
|
||||||
|
@ -347,10 +395,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async getXraySetting() {
|
async getXraySetting() {
|
||||||
this.loading(true);
|
|
||||||
const msg = await HttpUtil.post("/panel/xray/");
|
const msg = await HttpUtil.post("/panel/xray/");
|
||||||
this.loading(false);
|
|
||||||
if (msg.success) {
|
if (msg.success) {
|
||||||
|
if (!this.loadingStates.fetched) {
|
||||||
|
this.loadingStates.fetched = true
|
||||||
|
}
|
||||||
|
|
||||||
result = JSON.parse(msg.obj);
|
result = JSON.parse(msg.obj);
|
||||||
xs = JSON.stringify(result.xraySetting, null, 2);
|
xs = JSON.stringify(result.xraySetting, null, 2);
|
||||||
this.oldXraySetting = xs;
|
this.oldXraySetting = xs;
|
||||||
|
@ -361,7 +412,7 @@
|
||||||
},
|
},
|
||||||
async updateXraySetting() {
|
async updateXraySetting() {
|
||||||
this.loading(true);
|
this.loading(true);
|
||||||
const msg = await HttpUtil.post("/panel/xray/update", {xraySetting : this.xraySetting});
|
const msg = await HttpUtil.post("/panel/xray/update", { xraySetting: this.xraySetting });
|
||||||
this.loading(false);
|
this.loading(false);
|
||||||
if (msg.success) {
|
if (msg.success) {
|
||||||
await this.getXraySetting();
|
await this.getXraySetting();
|
||||||
|
@ -380,8 +431,8 @@
|
||||||
async getXrayResult() {
|
async getXrayResult() {
|
||||||
const msg = await HttpUtil.get("/panel/xray/getXrayResult");
|
const msg = await HttpUtil.get("/panel/xray/getXrayResult");
|
||||||
if (msg.success) {
|
if (msg.success) {
|
||||||
this.restartResult=msg.obj;
|
this.restartResult = msg.obj;
|
||||||
if(msg.obj.length > 1) Vue.prototype.$message.error(msg.obj);
|
if (msg.obj.length > 1) Vue.prototype.$message.error(msg.obj);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async resetXrayConfigToDefault() {
|
async resetXrayConfigToDefault() {
|
||||||
|
@ -394,8 +445,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
changePage(pageKey) {
|
changePage(pageKey) {
|
||||||
if(pageKey == 'tpl-advanced') this.changeCode();
|
if (pageKey == 'tpl-advanced') this.changeCode();
|
||||||
if(pageKey == 'tpl-balancer') this.changeObsCode();
|
if (pageKey == 'tpl-balancer') this.changeObsCode();
|
||||||
},
|
},
|
||||||
syncRulesWithOutbound(tag, setting) {
|
syncRulesWithOutbound(tag, setting) {
|
||||||
const newTemplateSettings = this.templateSettings;
|
const newTemplateSettings = this.templateSettings;
|
||||||
|
@ -466,13 +517,13 @@
|
||||||
this.templateSettings = newTemplateSettings;
|
this.templateSettings = newTemplateSettings;
|
||||||
},
|
},
|
||||||
changeCode() {
|
changeCode() {
|
||||||
if(this.cm != null) {
|
if (this.cm != null) {
|
||||||
this.cm.toTextArea();
|
this.cm.toTextArea();
|
||||||
}
|
}
|
||||||
textAreaObj = document.getElementById('xraySetting');
|
textAreaObj = document.getElementById('xraySetting');
|
||||||
textAreaObj.value = this[this.advSettings];
|
textAreaObj.value = this[this.advSettings];
|
||||||
this.cm = CodeMirror.fromTextArea(textAreaObj, this.cmOptions);
|
this.cm = CodeMirror.fromTextArea(textAreaObj, this.cmOptions);
|
||||||
this.cm.on('change',editor => {
|
this.cm.on('change', editor => {
|
||||||
value = editor.getValue();
|
value = editor.getValue();
|
||||||
if (this.isJsonString(value)) {
|
if (this.isJsonString(value)) {
|
||||||
this[this.advSettings] = value;
|
this[this.advSettings] = value;
|
||||||
|
@ -480,19 +531,19 @@
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
changeObsCode() {
|
changeObsCode() {
|
||||||
if(this.cm != null) {
|
if (this.cm != null) {
|
||||||
this.cm.toTextArea();
|
this.cm.toTextArea();
|
||||||
}
|
}
|
||||||
if (this.obsSettings == ''){
|
if (this.obsSettings == '') {
|
||||||
this.cm = null;
|
this.cm = null;
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
textAreaObj = document.getElementById('obsSetting');
|
textAreaObj = document.getElementById('obsSetting');
|
||||||
textAreaObj.value = this[this.obsSettings];
|
textAreaObj.value = this[this.obsSettings];
|
||||||
this.cm = CodeMirror.fromTextArea(textAreaObj, this.cmOptions);
|
this.cm = CodeMirror.fromTextArea(textAreaObj, this.cmOptions);
|
||||||
this.cm.on('change',editor => {
|
this.cm.on('change', editor => {
|
||||||
value = editor.getValue();
|
value = editor.getValue();
|
||||||
if(this.isJsonString(value)){
|
if (this.isJsonString(value)) {
|
||||||
this[this.obsSettings] = value;
|
this[this.obsSettings] = value;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -515,7 +566,7 @@
|
||||||
},
|
},
|
||||||
findOutboundAddress(o) {
|
findOutboundAddress(o) {
|
||||||
serverObj = null;
|
serverObj = null;
|
||||||
switch(o.protocol){
|
switch (o.protocol) {
|
||||||
case Protocols.VMess:
|
case Protocols.VMess:
|
||||||
case Protocols.VLESS:
|
case Protocols.VLESS:
|
||||||
serverObj = o.settings.vnext;
|
serverObj = o.settings.vnext;
|
||||||
|
@ -535,13 +586,13 @@
|
||||||
}
|
}
|
||||||
return serverObj ? serverObj.map(obj => obj.address + ':' + obj.port) : null;
|
return serverObj ? serverObj.map(obj => obj.address + ':' + obj.port) : null;
|
||||||
},
|
},
|
||||||
addOutbound(){
|
addOutbound() {
|
||||||
outModal.show({
|
outModal.show({
|
||||||
title: '{{ i18n "pages.xray.outbound.addOutbound"}}',
|
title: '{{ i18n "pages.xray.outbound.addOutbound"}}',
|
||||||
okText: '{{ i18n "pages.xray.outbound.addOutbound" }}',
|
okText: '{{ i18n "pages.xray.outbound.addOutbound" }}',
|
||||||
confirm: (outbound) => {
|
confirm: (outbound) => {
|
||||||
outModal.loading();
|
outModal.loading();
|
||||||
if(outbound.tag.length > 0){
|
if (outbound.tag.length > 0) {
|
||||||
this.templateSettings.outbounds.push(outbound);
|
this.templateSettings.outbounds.push(outbound);
|
||||||
this.outboundSettings = JSON.stringify(this.templateSettings.outbounds);
|
this.outboundSettings = JSON.stringify(this.templateSettings.outbounds);
|
||||||
}
|
}
|
||||||
|
@ -551,9 +602,9 @@
|
||||||
tags: this.templateSettings.outbounds.map(obj => obj.tag)
|
tags: this.templateSettings.outbounds.map(obj => obj.tag)
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
editOutbound(index){
|
editOutbound(index) {
|
||||||
outModal.show({
|
outModal.show({
|
||||||
title: '{{ i18n "pages.xray.outbound.editOutbound"}} ' + (index+1),
|
title: '{{ i18n "pages.xray.outbound.editOutbound"}} ' + (index + 1),
|
||||||
outbound: app.templateSettings.outbounds[index],
|
outbound: app.templateSettings.outbounds[index],
|
||||||
confirm: (outbound) => {
|
confirm: (outbound) => {
|
||||||
outModal.loading();
|
outModal.loading();
|
||||||
|
@ -562,30 +613,30 @@
|
||||||
outModal.close();
|
outModal.close();
|
||||||
},
|
},
|
||||||
isEdit: true,
|
isEdit: true,
|
||||||
tags: this.outboundData.filter((o) => o.key != index ).map(obj => obj.tag)
|
tags: this.outboundData.filter((o) => o.key != index).map(obj => obj.tag)
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
deleteOutbound(index){
|
deleteOutbound(index) {
|
||||||
outbounds = this.templateSettings.outbounds;
|
outbounds = this.templateSettings.outbounds;
|
||||||
outbounds.splice(index,1);
|
outbounds.splice(index, 1);
|
||||||
this.outboundSettings = JSON.stringify(outbounds);
|
this.outboundSettings = JSON.stringify(outbounds);
|
||||||
},
|
},
|
||||||
setFirstOutbound(index){
|
setFirstOutbound(index) {
|
||||||
outbounds = this.templateSettings.outbounds;
|
outbounds = this.templateSettings.outbounds;
|
||||||
outbounds.splice(0, 0, outbounds.splice(index, 1)[0]);
|
outbounds.splice(0, 0, outbounds.splice(index, 1)[0]);
|
||||||
this.outboundSettings = JSON.stringify(outbounds);
|
this.outboundSettings = JSON.stringify(outbounds);
|
||||||
},
|
},
|
||||||
addReverse(){
|
addReverse() {
|
||||||
reverseModal.show({
|
reverseModal.show({
|
||||||
title: '{{ i18n "pages.xray.outbound.addReverse"}}',
|
title: '{{ i18n "pages.xray.outbound.addReverse"}}',
|
||||||
okText: '{{ i18n "pages.xray.outbound.addReverse" }}',
|
okText: '{{ i18n "pages.xray.outbound.addReverse" }}',
|
||||||
confirm: (reverse, rules) => {
|
confirm: (reverse, rules) => {
|
||||||
reverseModal.loading();
|
reverseModal.loading();
|
||||||
if(reverse.tag.length > 0){
|
if (reverse.tag.length > 0) {
|
||||||
newTemplateSettings = this.templateSettings;
|
newTemplateSettings = this.templateSettings;
|
||||||
if(newTemplateSettings.reverse == undefined) newTemplateSettings.reverse = {};
|
if (newTemplateSettings.reverse == undefined) newTemplateSettings.reverse = {};
|
||||||
if(newTemplateSettings.reverse[reverse.type+'s'] == undefined) newTemplateSettings.reverse[reverse.type+'s'] = [];
|
if (newTemplateSettings.reverse[reverse.type + 's'] == undefined) newTemplateSettings.reverse[reverse.type + 's'] = [];
|
||||||
newTemplateSettings.reverse[reverse.type+'s'].push({ tag: reverse.tag, domain: reverse.domain });
|
newTemplateSettings.reverse[reverse.type + 's'].push({ tag: reverse.tag, domain: reverse.domain });
|
||||||
this.templateSettings = newTemplateSettings;
|
this.templateSettings = newTemplateSettings;
|
||||||
|
|
||||||
// Add related rules
|
// Add related rules
|
||||||
|
@ -597,33 +648,33 @@
|
||||||
isEdit: false
|
isEdit: false
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
editReverse(index){
|
editReverse(index) {
|
||||||
if(this.reverseData[index].type == "bridge") {
|
if (this.reverseData[index].type == "bridge") {
|
||||||
oldRules = this.templateSettings.routing.rules.filter(r => r.inboundTag && r.inboundTag[0] == this.reverseData[index].tag);
|
oldRules = this.templateSettings.routing.rules.filter(r => r.inboundTag && r.inboundTag[0] == this.reverseData[index].tag);
|
||||||
} else {
|
} else {
|
||||||
oldRules = this.templateSettings.routing.rules.filter(r => r.outboundTag && r.outboundTag == this.reverseData[index].tag);
|
oldRules = this.templateSettings.routing.rules.filter(r => r.outboundTag && r.outboundTag == this.reverseData[index].tag);
|
||||||
}
|
}
|
||||||
reverseModal.show({
|
reverseModal.show({
|
||||||
title: '{{ i18n "pages.xray.outbound.editReverse"}} ' + (index+1),
|
title: '{{ i18n "pages.xray.outbound.editReverse"}} ' + (index + 1),
|
||||||
reverse: this.reverseData[index],
|
reverse: this.reverseData[index],
|
||||||
rules: oldRules,
|
rules: oldRules,
|
||||||
confirm: (reverse, rules) => {
|
confirm: (reverse, rules) => {
|
||||||
reverseModal.loading();
|
reverseModal.loading();
|
||||||
if(reverse.tag.length > 0){
|
if (reverse.tag.length > 0) {
|
||||||
oldData = this.reverseData[index];
|
oldData = this.reverseData[index];
|
||||||
newTemplateSettings = this.templateSettings;
|
newTemplateSettings = this.templateSettings;
|
||||||
oldReverseIndex = newTemplateSettings.reverse[oldData.type+'s'].findIndex(rs => rs.tag == oldData.tag);
|
oldReverseIndex = newTemplateSettings.reverse[oldData.type + 's'].findIndex(rs => rs.tag == oldData.tag);
|
||||||
oldRuleIndex0 = oldRules.length>0 ? newTemplateSettings.routing.rules.findIndex(r => JSON.stringify(r) == JSON.stringify(oldRules[0])) : -1;
|
oldRuleIndex0 = oldRules.length > 0 ? newTemplateSettings.routing.rules.findIndex(r => JSON.stringify(r) == JSON.stringify(oldRules[0])) : -1;
|
||||||
oldRuleIndex1 = oldRules.length==2 ? newTemplateSettings.routing.rules.findIndex(r => JSON.stringify(r) == JSON.stringify(oldRules[1])) : -1;
|
oldRuleIndex1 = oldRules.length == 2 ? newTemplateSettings.routing.rules.findIndex(r => JSON.stringify(r) == JSON.stringify(oldRules[1])) : -1;
|
||||||
if(oldData.type == reverse.type){
|
if (oldData.type == reverse.type) {
|
||||||
newTemplateSettings.reverse[oldData.type + 's'][oldReverseIndex] = { tag: reverse.tag, domain: reverse.domain };
|
newTemplateSettings.reverse[oldData.type + 's'][oldReverseIndex] = { tag: reverse.tag, domain: reverse.domain };
|
||||||
} else {
|
} else {
|
||||||
newTemplateSettings.reverse[oldData.type+'s'].splice(oldReverseIndex,1);
|
newTemplateSettings.reverse[oldData.type + 's'].splice(oldReverseIndex, 1);
|
||||||
// delete empty object
|
// delete empty object
|
||||||
if(newTemplateSettings.reverse[oldData.type+'s'].length == 0) Reflect.deleteProperty(newTemplateSettings.reverse, oldData.type+'s');
|
if (newTemplateSettings.reverse[oldData.type + 's'].length == 0) Reflect.deleteProperty(newTemplateSettings.reverse, oldData.type + 's');
|
||||||
// add other type of reverse if it is not exist
|
// add other type of reverse if it is not exist
|
||||||
if(!newTemplateSettings.reverse[reverse.type+'s']) newTemplateSettings.reverse[reverse.type+'s'] = [];
|
if (!newTemplateSettings.reverse[reverse.type + 's']) newTemplateSettings.reverse[reverse.type + 's'] = [];
|
||||||
newTemplateSettings.reverse[reverse.type+'s'].push({ tag: reverse.tag, domain: reverse.domain });
|
newTemplateSettings.reverse[reverse.type + 's'].push({ tag: reverse.tag, domain: reverse.domain });
|
||||||
}
|
}
|
||||||
this.templateSettings = newTemplateSettings;
|
this.templateSettings = newTemplateSettings;
|
||||||
|
|
||||||
|
@ -638,22 +689,22 @@
|
||||||
isEdit: true
|
isEdit: true
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
deleteReverse(index){
|
deleteReverse(index) {
|
||||||
oldData = this.reverseData[index];
|
oldData = this.reverseData[index];
|
||||||
newTemplateSettings = this.templateSettings;
|
newTemplateSettings = this.templateSettings;
|
||||||
reverseTypeObj = newTemplateSettings.reverse[oldData.type+'s'];
|
reverseTypeObj = newTemplateSettings.reverse[oldData.type + 's'];
|
||||||
realIndex = reverseTypeObj.findIndex(r => r.tag==oldData.tag && r.domain==oldData.domain);
|
realIndex = reverseTypeObj.findIndex(r => r.tag == oldData.tag && r.domain == oldData.domain);
|
||||||
newTemplateSettings.reverse[oldData.type+'s'].splice(realIndex,1);
|
newTemplateSettings.reverse[oldData.type + 's'].splice(realIndex, 1);
|
||||||
|
|
||||||
// delete empty objects
|
// delete empty objects
|
||||||
if(reverseTypeObj.length == 0) Reflect.deleteProperty(newTemplateSettings.reverse, oldData.type+'s');
|
if (reverseTypeObj.length == 0) Reflect.deleteProperty(newTemplateSettings.reverse, oldData.type + 's');
|
||||||
if(Object.keys(newTemplateSettings.reverse).length === 0) Reflect.deleteProperty(newTemplateSettings, 'reverse');
|
if (Object.keys(newTemplateSettings.reverse).length === 0) Reflect.deleteProperty(newTemplateSettings, 'reverse');
|
||||||
|
|
||||||
// delete related routing rules
|
// delete related routing rules
|
||||||
newRules = newTemplateSettings.routing.rules;
|
newRules = newTemplateSettings.routing.rules;
|
||||||
if(oldData.type == "bridge"){
|
if (oldData.type == "bridge") {
|
||||||
newRules = newTemplateSettings.routing.rules.filter(r => !( r.inboundTag && r.inboundTag.length == 1 && r.inboundTag[0] == oldData.tag));
|
newRules = newTemplateSettings.routing.rules.filter(r => !(r.inboundTag && r.inboundTag.length == 1 && r.inboundTag[0] == oldData.tag));
|
||||||
} else if(oldData.type == "portal"){
|
} else if (oldData.type == "portal") {
|
||||||
newRules = newTemplateSettings.routing.rules.filter(r => r.outboundTag != oldData.tag);
|
newRules = newTemplateSettings.routing.rules.filter(r => r.outboundTag != oldData.tag);
|
||||||
}
|
}
|
||||||
newTemplateSettings.routing.rules = newRules;
|
newTemplateSettings.routing.rules = newRules;
|
||||||
|
@ -668,7 +719,7 @@
|
||||||
data = []
|
data = []
|
||||||
if (this.templateSettings != null) {
|
if (this.templateSettings != null) {
|
||||||
this.templateSettings.outbounds.forEach((o, index) => {
|
this.templateSettings.outbounds.forEach((o, index) => {
|
||||||
data.push({'key': index, ...o});
|
data.push({ 'key': index, ...o });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -740,10 +791,10 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
// Remove old tag
|
// Remove old tag
|
||||||
if (newTemplateSettings.observatory){
|
if (newTemplateSettings.observatory) {
|
||||||
newTemplateSettings.observatory.subjectSelector = newTemplateSettings.observatory.subjectSelector.filter(s => s != oldTag);
|
newTemplateSettings.observatory.subjectSelector = newTemplateSettings.observatory.subjectSelector.filter(s => s != oldTag);
|
||||||
}
|
}
|
||||||
if (newTemplateSettings.burstObservatory){
|
if (newTemplateSettings.burstObservatory) {
|
||||||
newTemplateSettings.burstObservatory.subjectSelector = newTemplateSettings.burstObservatory.subjectSelector.filter(s => s != oldTag);
|
newTemplateSettings.burstObservatory.subjectSelector = newTemplateSettings.burstObservatory.subjectSelector.filter(s => s != oldTag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -770,7 +821,7 @@
|
||||||
isEdit: true
|
isEdit: true
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
updateObservatorySelectors(){
|
updateObservatorySelectors() {
|
||||||
newTemplateSettings = this.templateSettings;
|
newTemplateSettings = this.templateSettings;
|
||||||
const leastPings = this.balancersData.filter((b) => b.strategy == 'leastPing');
|
const leastPings = this.balancersData.filter((b) => b.strategy == 'leastPing');
|
||||||
const leastLoads = this.balancersData.filter((b) =>
|
const leastLoads = this.balancersData.filter((b) =>
|
||||||
|
@ -778,7 +829,7 @@
|
||||||
b.strategy === 'roundRobin' ||
|
b.strategy === 'roundRobin' ||
|
||||||
b.strategy === 'random'
|
b.strategy === 'random'
|
||||||
);
|
);
|
||||||
if (leastPings.length>0){
|
if (leastPings.length > 0) {
|
||||||
if (!newTemplateSettings.observatory)
|
if (!newTemplateSettings.observatory)
|
||||||
newTemplateSettings.observatory = this.defaultObservatory;
|
newTemplateSettings.observatory = this.defaultObservatory;
|
||||||
newTemplateSettings.observatory.subjectSelector = [];
|
newTemplateSettings.observatory.subjectSelector = [];
|
||||||
|
@ -791,7 +842,7 @@
|
||||||
} else {
|
} else {
|
||||||
delete newTemplateSettings.observatory
|
delete newTemplateSettings.observatory
|
||||||
}
|
}
|
||||||
if (leastLoads.length>0){
|
if (leastLoads.length > 0) {
|
||||||
if (!newTemplateSettings.burstObservatory)
|
if (!newTemplateSettings.burstObservatory)
|
||||||
newTemplateSettings.burstObservatory = this.defaultBurstObservatory;
|
newTemplateSettings.burstObservatory = this.defaultBurstObservatory;
|
||||||
newTemplateSettings.burstObservatory.subjectSelector = [];
|
newTemplateSettings.burstObservatory.subjectSelector = [];
|
||||||
|
@ -826,7 +877,17 @@
|
||||||
this.obsSettings = '';
|
this.obsSettings = '';
|
||||||
this.changeObsCode()
|
this.changeObsCode()
|
||||||
},
|
},
|
||||||
addDNSServer(){
|
openDNSPresets() {
|
||||||
|
dnsPresetsModal.show({
|
||||||
|
title: '{{ i18n "pages.xray.dns.dnsPresetTitle" }}',
|
||||||
|
selected: (selectedPreset) => {
|
||||||
|
this.dnsServers = selectedPreset;
|
||||||
|
|
||||||
|
dnsPresetsModal.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
addDNSServer() {
|
||||||
dnsModal.show({
|
dnsModal.show({
|
||||||
title: '{{ i18n "pages.xray.dns.add" }}',
|
title: '{{ i18n "pages.xray.dns.add" }}',
|
||||||
confirm: (dnsServer) => {
|
confirm: (dnsServer) => {
|
||||||
|
@ -838,9 +899,9 @@
|
||||||
isEdit: false
|
isEdit: false
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
editDNSServer(index){
|
editDNSServer(index) {
|
||||||
dnsModal.show({
|
dnsModal.show({
|
||||||
title: '{{ i18n "pages.xray.dns.edit" }} #' + (index+1),
|
title: '{{ i18n "pages.xray.dns.edit" }} #' + (index + 1),
|
||||||
dnsServer: this.dnsServers[index],
|
dnsServer: this.dnsServers[index],
|
||||||
confirm: (dnsServer) => {
|
confirm: (dnsServer) => {
|
||||||
dnsServers = this.dnsServers;
|
dnsServers = this.dnsServers;
|
||||||
|
@ -851,16 +912,16 @@
|
||||||
isEdit: true
|
isEdit: true
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
deleteDNSServer(index){
|
deleteDNSServer(index) {
|
||||||
newDnsServers = this.dnsServers;
|
newDnsServers = this.dnsServers;
|
||||||
newDnsServers.splice(index,1);
|
newDnsServers.splice(index, 1);
|
||||||
this.dnsServers = newDnsServers;
|
this.dnsServers = newDnsServers;
|
||||||
},
|
},
|
||||||
addFakedns() {
|
addFakedns() {
|
||||||
fakednsModal.show({
|
fakednsModal.show({
|
||||||
title: '{{ i18n "pages.xray.fakedns.add" }}',
|
title: '{{ i18n "pages.xray.fakedns.add" }}',
|
||||||
confirm: (item) => {
|
confirm: (item) => {
|
||||||
fakeDns = this.fakeDns?? [];
|
fakeDns = this.fakeDns ?? [];
|
||||||
fakeDns.push(item);
|
fakeDns.push(item);
|
||||||
this.fakeDns = fakeDns;
|
this.fakeDns = fakeDns;
|
||||||
fakednsModal.close();
|
fakednsModal.close();
|
||||||
|
@ -868,9 +929,9 @@
|
||||||
isEdit: false
|
isEdit: false
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
editFakedns(index){
|
editFakedns(index) {
|
||||||
fakednsModal.show({
|
fakednsModal.show({
|
||||||
title: '{{ i18n "pages.xray.fakedns.edit" }} #' + (index+1),
|
title: '{{ i18n "pages.xray.fakedns.edit" }} #' + (index + 1),
|
||||||
fakeDns: this.fakeDns[index],
|
fakeDns: this.fakeDns[index],
|
||||||
confirm: (item) => {
|
confirm: (item) => {
|
||||||
fakeDns = this.fakeDns;
|
fakeDns = this.fakeDns;
|
||||||
|
@ -881,18 +942,18 @@
|
||||||
isEdit: true
|
isEdit: true
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
deleteFakedns(index){
|
deleteFakedns(index) {
|
||||||
fakeDns = this.fakeDns;
|
fakeDns = this.fakeDns;
|
||||||
fakeDns.splice(index,1);
|
fakeDns.splice(index, 1);
|
||||||
this.fakeDns = fakeDns;
|
this.fakeDns = fakeDns;
|
||||||
},
|
},
|
||||||
addRule(){
|
addRule() {
|
||||||
ruleModal.show({
|
ruleModal.show({
|
||||||
title: '{{ i18n "pages.xray.rules.add"}}',
|
title: '{{ i18n "pages.xray.rules.add"}}',
|
||||||
okText: '{{ i18n "pages.xray.rules.add" }}',
|
okText: '{{ i18n "pages.xray.rules.add" }}',
|
||||||
confirm: (rule) => {
|
confirm: (rule) => {
|
||||||
ruleModal.loading();
|
ruleModal.loading();
|
||||||
if(JSON.stringify(rule).length > 3){
|
if (JSON.stringify(rule).length > 3) {
|
||||||
this.templateSettings.routing.rules.push(rule);
|
this.templateSettings.routing.rules.push(rule);
|
||||||
this.routingRuleSettings = JSON.stringify(this.templateSettings.routing.rules);
|
this.routingRuleSettings = JSON.stringify(this.templateSettings.routing.rules);
|
||||||
}
|
}
|
||||||
|
@ -901,13 +962,13 @@
|
||||||
isEdit: false
|
isEdit: false
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
editRule(index){
|
editRule(index) {
|
||||||
ruleModal.show({
|
ruleModal.show({
|
||||||
title: '{{ i18n "pages.xray.rules.edit"}} ' + (index+1),
|
title: '{{ i18n "pages.xray.rules.edit"}} ' + (index + 1),
|
||||||
rule: app.templateSettings.routing.rules[index],
|
rule: app.templateSettings.routing.rules[index],
|
||||||
confirm: (rule) => {
|
confirm: (rule) => {
|
||||||
ruleModal.loading();
|
ruleModal.loading();
|
||||||
if(JSON.stringify(rule).length > 3){
|
if (JSON.stringify(rule).length > 3) {
|
||||||
this.templateSettings.routing.rules[index] = rule;
|
this.templateSettings.routing.rules[index] = rule;
|
||||||
this.routingRuleSettings = JSON.stringify(this.templateSettings.routing.rules);
|
this.routingRuleSettings = JSON.stringify(this.templateSettings.routing.rules);
|
||||||
}
|
}
|
||||||
|
@ -916,18 +977,18 @@
|
||||||
isEdit: true
|
isEdit: true
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
replaceRule(old_index,new_index){
|
replaceRule(old_index, new_index) {
|
||||||
rules = this.templateSettings.routing.rules;
|
rules = this.templateSettings.routing.rules;
|
||||||
if (new_index >= rules.length) rules.push(undefined);
|
if (new_index >= rules.length) rules.push(undefined);
|
||||||
rules.splice(new_index, 0, rules.splice(old_index, 1)[0]);
|
rules.splice(new_index, 0, rules.splice(old_index, 1)[0]);
|
||||||
this.routingRuleSettings = JSON.stringify(rules);
|
this.routingRuleSettings = JSON.stringify(rules);
|
||||||
},
|
},
|
||||||
deleteRule(index){
|
deleteRule(index) {
|
||||||
rules = this.templateSettings.routing.rules;
|
rules = this.templateSettings.routing.rules;
|
||||||
rules.splice(index,1);
|
rules.splice(index, 1);
|
||||||
this.routingRuleSettings = JSON.stringify(rules);
|
this.routingRuleSettings = JSON.stringify(rules);
|
||||||
},
|
},
|
||||||
showWarp(){
|
showWarp() {
|
||||||
warpModal.show();
|
warpModal.show();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -976,7 +1037,7 @@
|
||||||
data = []
|
data = []
|
||||||
if (this.templateSettings != null) {
|
if (this.templateSettings != null) {
|
||||||
this.templateSettings.outbounds.forEach((o, index) => {
|
this.templateSettings.outbounds.forEach((o, index) => {
|
||||||
data.push({'key': index, ...o});
|
data.push({ 'key': index, ...o });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
|
@ -986,14 +1047,14 @@
|
||||||
get: function () {
|
get: function () {
|
||||||
data = []
|
data = []
|
||||||
if (this.templateSettings != null && this.templateSettings.reverse != null) {
|
if (this.templateSettings != null && this.templateSettings.reverse != null) {
|
||||||
if(this.templateSettings.reverse.bridges) {
|
if (this.templateSettings.reverse.bridges) {
|
||||||
this.templateSettings.reverse.bridges.forEach((o, index) => {
|
this.templateSettings.reverse.bridges.forEach((o, index) => {
|
||||||
data.push({'key': index, 'type':'bridge', ...o});
|
data.push({ 'key': index, 'type': 'bridge', ...o });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if(this.templateSettings.reverse.portals){
|
if (this.templateSettings.reverse.portals) {
|
||||||
this.templateSettings.reverse.portals.forEach((o, index) => {
|
this.templateSettings.reverse.portals.forEach((o, index) => {
|
||||||
data.push({'key': index, 'type':'portal', ...o});
|
data.push({ 'key': index, 'type': 'portal', ...o });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1013,17 +1074,17 @@
|
||||||
data = [];
|
data = [];
|
||||||
if (this.templateSettings != null) {
|
if (this.templateSettings != null) {
|
||||||
this.templateSettings.routing.rules.forEach((r, index) => {
|
this.templateSettings.routing.rules.forEach((r, index) => {
|
||||||
data.push({'key': index, ...r});
|
data.push({ 'key': index, ...r });
|
||||||
});
|
});
|
||||||
// Make rules readable
|
// Make rules readable
|
||||||
data.forEach(r => {
|
data.forEach(r => {
|
||||||
if(r.domain) r.domain = r.domain.join(',')
|
if (r.domain) r.domain = r.domain.join(',')
|
||||||
if(r.ip) r.ip = r.ip.join(',')
|
if (r.ip) r.ip = r.ip.join(',')
|
||||||
if(r.source) r.source = r.source.join(',');
|
if (r.source) r.source = r.source.join(',');
|
||||||
if(r.user) r.user = r.user.join(',')
|
if (r.user) r.user = r.user.join(',')
|
||||||
if(r.inboundTag) r.inboundTag = r.inboundTag.join(',')
|
if (r.inboundTag) r.inboundTag = r.inboundTag.join(',')
|
||||||
if(r.protocol) r.protocol = r.protocol.join(',')
|
if (r.protocol) r.protocol = r.protocol.join(',')
|
||||||
if(r.attrs) r.attrs = JSON.stringify(r.attrs, null, 2)
|
if (r.attrs) r.attrs = JSON.stringify(r.attrs, null, 2)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
|
@ -1039,7 +1100,7 @@
|
||||||
'tag': o.tag ? o.tag : "",
|
'tag': o.tag ? o.tag : "",
|
||||||
'strategy': o.strategy?.type ?? "random",
|
'strategy': o.strategy?.type ?? "random",
|
||||||
'selector': o.selector ? o.selector : [],
|
'selector': o.selector ? o.selector : [],
|
||||||
'fallbackTag': o.fallbackTag?? '',
|
'fallbackTag': o.fallbackTag ?? '',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1079,10 +1140,10 @@
|
||||||
set: function (newValue) {
|
set: function (newValue) {
|
||||||
newTemplateSettings = this.templateSettings;
|
newTemplateSettings = this.templateSettings;
|
||||||
freedomOutboundIndex = newTemplateSettings.outbounds.findIndex((o) => o.protocol === "freedom" && o.tag == "direct");
|
freedomOutboundIndex = newTemplateSettings.outbounds.findIndex((o) => o.protocol === "freedom" && o.tag == "direct");
|
||||||
if(freedomOutboundIndex == -1){
|
if (freedomOutboundIndex == -1) {
|
||||||
newTemplateSettings.outbounds.push({protocol: "freedom", tag: "direct", settings: { "domainStrategy": newValue }});
|
newTemplateSettings.outbounds.push({ protocol: "freedom", tag: "direct", settings: { "domainStrategy": newValue } });
|
||||||
} else if (!newTemplateSettings.outbounds[freedomOutboundIndex].settings) {
|
} else if (!newTemplateSettings.outbounds[freedomOutboundIndex].settings) {
|
||||||
newTemplateSettings.outbounds[freedomOutboundIndex].settings = {"domainStrategy": newValue};
|
newTemplateSettings.outbounds[freedomOutboundIndex].settings = { "domainStrategy": newValue };
|
||||||
} else {
|
} else {
|
||||||
newTemplateSettings.outbounds[freedomOutboundIndex].settings.domainStrategy = newValue;
|
newTemplateSettings.outbounds[freedomOutboundIndex].settings.domainStrategy = newValue;
|
||||||
}
|
}
|
||||||
|
@ -1270,24 +1331,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
familyProtectSettings: {
|
|
||||||
get: function () {
|
|
||||||
if (!this.templateSettings || !this.templateSettings.dns || !this.templateSettings.dns.servers) return false;
|
|
||||||
return ArrayUtils.doAllItemsExist(this.settingsData.familyProtectDNS.servers, this.templateSettings.dns.servers);
|
|
||||||
},
|
|
||||||
set: function (newValue) {
|
|
||||||
newTemplateSettings = this.templateSettings;
|
|
||||||
if (newValue) {
|
|
||||||
newTemplateSettings.dns = this.settingsData.familyProtectDNS;
|
|
||||||
} else {
|
|
||||||
newTemplateSettings.dns.servers = newTemplateSettings.dns?.servers?.filter(data => !this.settingsData.familyProtectDNS.servers.includes(data))
|
|
||||||
}
|
|
||||||
this.templateSettings = newTemplateSettings;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
WarpExist: {
|
WarpExist: {
|
||||||
get: function() {
|
get: function () {
|
||||||
return this.templateSettings ? this.templateSettings.outbounds.findIndex((o) => o.tag == "warp")>=0 : false;
|
return this.templateSettings ? this.templateSettings.outbounds.findIndex((o) => o.tag == "warp") >= 0 : false;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
enableDNS: {
|
enableDNS: {
|
||||||
|
@ -1376,6 +1422,20 @@
|
||||||
this.templateSettings = newTemplateSettings;
|
this.templateSettings = newTemplateSettings;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
dnsUseSystemHosts: {
|
||||||
|
get: function () {
|
||||||
|
return this.enableDNS ? this.templateSettings.dns.useSystemHosts : false;
|
||||||
|
},
|
||||||
|
set: function (newValue) {
|
||||||
|
newTemplateSettings = this.templateSettings;
|
||||||
|
if (newValue) {
|
||||||
|
newTemplateSettings.dns.useSystemHosts = newValue;
|
||||||
|
} else {
|
||||||
|
delete newTemplateSettings.dns.useSystemHosts
|
||||||
|
}
|
||||||
|
this.templateSettings = newTemplateSettings;
|
||||||
|
}
|
||||||
|
},
|
||||||
dnsStrategy: {
|
dnsStrategy: {
|
||||||
get: function () {
|
get: function () {
|
||||||
return this.enableDNS ? this.templateSettings.dns.queryStrategy : null;
|
return this.enableDNS ? this.templateSettings.dns.queryStrategy : null;
|
||||||
|
@ -1409,5 +1469,4 @@
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
{{ template "page/body_end" .}}
|
||||||
</html>
|
|
|
@ -81,5 +81,9 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"stats": {}
|
"stats": {},
|
||||||
|
"metrics": {
|
||||||
|
"tag": "metrics_out",
|
||||||
|
"listen": "127.0.0.1:11111"
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -295,7 +295,7 @@ func (s *ServerService) GetXrayVersions() ([]string, error) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if major > 25 || (major == 25 && minor > 3) || (major == 25 && minor == 3 && patch >= 3) {
|
if major > 25 || (major == 25 && minor > 6) || (major == 25 && minor == 6 && patch >= 8) {
|
||||||
versions = append(versions, release.TagName)
|
versions = append(versions, release.TagName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -400,7 +400,6 @@
|
||||||
"generalConfigsDesc" = "الخيارات دي هتحدد التعديلات العامة."
|
"generalConfigsDesc" = "الخيارات دي هتحدد التعديلات العامة."
|
||||||
"logConfigs" = "السجلات"
|
"logConfigs" = "السجلات"
|
||||||
"logConfigsDesc" = "السجلات ممكن تأثر على كفاءة السيرفر. ننصح بتفعيلها بحكمة لما تكون محتاجها."
|
"logConfigsDesc" = "السجلات ممكن تأثر على كفاءة السيرفر. ننصح بتفعيلها بحكمة لما تكون محتاجها."
|
||||||
"blockConfigs" = "درع الحماية"
|
|
||||||
"blockConfigsDesc" = "الخيارات دي هتحجب الترافيك بناءً على بروتوكولات ومواقع محددة."
|
"blockConfigsDesc" = "الخيارات دي هتحجب الترافيك بناءً على بروتوكولات ومواقع محددة."
|
||||||
"basicRouting" = "توجيه أساسي"
|
"basicRouting" = "توجيه أساسي"
|
||||||
"blockConnectionsConfigsDesc" = "الخيارات دي هتحجب الترافيك بناءً على الدولة المطلوبة."
|
"blockConnectionsConfigsDesc" = "الخيارات دي هتحجب الترافيك بناءً على الدولة المطلوبة."
|
||||||
|
@ -420,9 +419,6 @@
|
||||||
"RoutingStrategy" = "استراتيجية التوجيه العامة"
|
"RoutingStrategy" = "استراتيجية التوجيه العامة"
|
||||||
"RoutingStrategyDesc" = "حدد استراتيجية التوجيه الإجمالية لحل كل الطلبات."
|
"RoutingStrategyDesc" = "حدد استراتيجية التوجيه الإجمالية لحل كل الطلبات."
|
||||||
"Torrent" = "حظر بروتوكول التورنت"
|
"Torrent" = "حظر بروتوكول التورنت"
|
||||||
"TorrentDesc" = "بيحجب بروتوكول التورنت."
|
|
||||||
"Family" = "حماية العيلة"
|
|
||||||
"FamilyDesc" = "بيحجب المحتويات الكبار وعناوين المواقع الضارة."
|
|
||||||
"Inbounds" = "الإدخالات"
|
"Inbounds" = "الإدخالات"
|
||||||
"InboundsDesc" = "قبول العملاء المعينين."
|
"InboundsDesc" = "قبول العملاء المعينين."
|
||||||
"Outbounds" = "المخرجات"
|
"Outbounds" = "المخرجات"
|
||||||
|
@ -522,6 +518,12 @@
|
||||||
"edit" = "عدل السيرفر"
|
"edit" = "عدل السيرفر"
|
||||||
"domains" = "الدومينات"
|
"domains" = "الدومينات"
|
||||||
"expectIPs" = "العناوين المتوقعة"
|
"expectIPs" = "العناوين المتوقعة"
|
||||||
|
"unexpectIPs" = "عناوين IP غير متوقعة"
|
||||||
|
"useSystemHosts" = "استخدام ملف Hosts الخاص بالنظام"
|
||||||
|
"useSystemHostsDesc" = "استخدام ملف hosts من نظام مثبت"
|
||||||
|
"usePreset" = "استخدام النموذج"
|
||||||
|
"dnsPresetTitle" = "قوالب DNS"
|
||||||
|
"dnsPresetFamily" = "العائلي"
|
||||||
|
|
||||||
[pages.xray.fakedns]
|
[pages.xray.fakedns]
|
||||||
"add" = "أضف Fake DNS"
|
"add" = "أضف Fake DNS"
|
||||||
|
|
|
@ -399,7 +399,6 @@
|
||||||
"generalConfigsDesc" = "These options will determine general adjustments."
|
"generalConfigsDesc" = "These options will determine general adjustments."
|
||||||
"logConfigs" = "Log"
|
"logConfigs" = "Log"
|
||||||
"logConfigsDesc" = "Logs may affect your server's efficiency. It is recommended to enable it wisely only in case of your needs"
|
"logConfigsDesc" = "Logs may affect your server's efficiency. It is recommended to enable it wisely only in case of your needs"
|
||||||
"blockConfigs" = "Protection Shield"
|
|
||||||
"blockConfigsDesc" = "These options will block traffic based on specific requested protocols and websites."
|
"blockConfigsDesc" = "These options will block traffic based on specific requested protocols and websites."
|
||||||
"basicRouting" = "Basic Routing"
|
"basicRouting" = "Basic Routing"
|
||||||
"blockConnectionsConfigsDesc" = "These options will block traffic based on the specific requested country."
|
"blockConnectionsConfigsDesc" = "These options will block traffic based on the specific requested country."
|
||||||
|
@ -419,9 +418,6 @@
|
||||||
"RoutingStrategy" = "Overall Routing Strategy"
|
"RoutingStrategy" = "Overall Routing Strategy"
|
||||||
"RoutingStrategyDesc" = "Set the overall traffic routing strategy for resolving all requests."
|
"RoutingStrategyDesc" = "Set the overall traffic routing strategy for resolving all requests."
|
||||||
"Torrent" = "Block BitTorrent Protocol"
|
"Torrent" = "Block BitTorrent Protocol"
|
||||||
"TorrentDesc" = "Blocks BitTorrent protocol."
|
|
||||||
"Family" = "Family Protection"
|
|
||||||
"FamilyDesc" = "Blocks adult content, and malware websites."
|
|
||||||
"Inbounds" = "Inbounds"
|
"Inbounds" = "Inbounds"
|
||||||
"InboundsDesc" = "Accepting the specific clients."
|
"InboundsDesc" = "Accepting the specific clients."
|
||||||
"Outbounds" = "Outbounds"
|
"Outbounds" = "Outbounds"
|
||||||
|
@ -521,6 +517,12 @@
|
||||||
"edit" = "Edit Server"
|
"edit" = "Edit Server"
|
||||||
"domains" = "Domains"
|
"domains" = "Domains"
|
||||||
"expectIPs" = "Expect IPs"
|
"expectIPs" = "Expect IPs"
|
||||||
|
"unexpectIPs" = "Unexpect IPs"
|
||||||
|
"useSystemHosts" = "Use System Hosts"
|
||||||
|
"useSystemHostsDesc" = "Use the hosts file from an installed system"
|
||||||
|
"usePreset" = "Use Preset"
|
||||||
|
"dnsPresetTitle" = "DNS Presets"
|
||||||
|
"dnsPresetFamily" = "Family"
|
||||||
|
|
||||||
[pages.xray.fakedns]
|
[pages.xray.fakedns]
|
||||||
"add" = "Add Fake DNS"
|
"add" = "Add Fake DNS"
|
||||||
|
|
|
@ -402,7 +402,6 @@
|
||||||
"generalConfigsDesc" = "Estas opciones proporcionarán ajustes generales."
|
"generalConfigsDesc" = "Estas opciones proporcionarán ajustes generales."
|
||||||
"logConfigs" = "Registro"
|
"logConfigs" = "Registro"
|
||||||
"logConfigsDesc" = "Los registros pueden afectar la eficiencia de su servidor. Se recomienda habilitarlos sabiamente solo en caso de sus necesidades."
|
"logConfigsDesc" = "Los registros pueden afectar la eficiencia de su servidor. Se recomienda habilitarlos sabiamente solo en caso de sus necesidades."
|
||||||
"blockConfigs" = "Configuraciones de Bloqueo"
|
|
||||||
"blockConfigsDesc" = "Estas opciones evitarán que los usuarios se conecten a protocolos y sitios web específicos."
|
"blockConfigsDesc" = "Estas opciones evitarán que los usuarios se conecten a protocolos y sitios web específicos."
|
||||||
"basicRouting" = "Enrutamiento Básico"
|
"basicRouting" = "Enrutamiento Básico"
|
||||||
"blockConnectionsConfigsDesc" = "Estas opciones bloquearán el tráfico según el país solicitado específico."
|
"blockConnectionsConfigsDesc" = "Estas opciones bloquearán el tráfico según el país solicitado específico."
|
||||||
|
@ -422,9 +421,6 @@
|
||||||
"RoutingStrategy" = "Configurar Estrategia de Enrutamiento de Dominios"
|
"RoutingStrategy" = "Configurar Estrategia de Enrutamiento de Dominios"
|
||||||
"RoutingStrategyDesc" = "Establece la estrategia general de enrutamiento para la resolución de DNS."
|
"RoutingStrategyDesc" = "Establece la estrategia general de enrutamiento para la resolución de DNS."
|
||||||
"Torrent" = "Prohibir Uso de BitTorrent"
|
"Torrent" = "Prohibir Uso de BitTorrent"
|
||||||
"TorrentDesc" = "Cambia la plantilla de configuración para evitar el uso de BitTorrent por parte de los usuarios."
|
|
||||||
"Family" = "Bloquee malware y contenido para adultos"
|
|
||||||
"FamilyDesc" = "Resolutores de DNS de Cloudflare para bloquear malware y contenido para adultos para protección familiar."
|
|
||||||
"Inbounds" = "Entrante"
|
"Inbounds" = "Entrante"
|
||||||
"InboundsDesc" = "Cambia la plantilla de configuración para aceptar clientes específicos."
|
"InboundsDesc" = "Cambia la plantilla de configuración para aceptar clientes específicos."
|
||||||
"Outbounds" = "Salidas"
|
"Outbounds" = "Salidas"
|
||||||
|
@ -524,6 +520,12 @@
|
||||||
"edit" = "Editar Servidor"
|
"edit" = "Editar Servidor"
|
||||||
"domains" = "Dominios"
|
"domains" = "Dominios"
|
||||||
"expectIPs" = "IPs esperadas"
|
"expectIPs" = "IPs esperadas"
|
||||||
|
"unexpectIPs" = "IPs inesperadas"
|
||||||
|
"useSystemHosts" = "Usar Hosts del sistema"
|
||||||
|
"useSystemHostsDesc" = "Usar el archivo hosts de un sistema instalado"
|
||||||
|
"usePreset" = "Usar plantilla"
|
||||||
|
"dnsPresetTitle" = "Plantillas DNS"
|
||||||
|
"dnsPresetFamily" = "Familiar"
|
||||||
|
|
||||||
[pages.xray.fakedns]
|
[pages.xray.fakedns]
|
||||||
"add" = "Agregar DNS Falso"
|
"add" = "Agregar DNS Falso"
|
||||||
|
|
|
@ -402,7 +402,6 @@
|
||||||
"generalConfigsDesc" = "این گزینهها استراتژی کلی ترافیک را تعیین میکنند"
|
"generalConfigsDesc" = "این گزینهها استراتژی کلی ترافیک را تعیین میکنند"
|
||||||
"logConfigs" = "گزارش"
|
"logConfigs" = "گزارش"
|
||||||
"logConfigsDesc" = "گزارشها ممکن است بر کارایی سرور شما تأثیر بگذارد. توصیه می شود فقط در صورت نیاز آن را عاقلانه فعال کنید"
|
"logConfigsDesc" = "گزارشها ممکن است بر کارایی سرور شما تأثیر بگذارد. توصیه می شود فقط در صورت نیاز آن را عاقلانه فعال کنید"
|
||||||
"blockConfigs" = "سپر محافظ"
|
|
||||||
"blockConfigsDesc" = "این گزینهها ترافیک را بر اساس پروتکلهای درخواستی خاص، و وب سایتها مسدود میکند"
|
"blockConfigsDesc" = "این گزینهها ترافیک را بر اساس پروتکلهای درخواستی خاص، و وب سایتها مسدود میکند"
|
||||||
"basicRouting" = "مسیریابی پایه"
|
"basicRouting" = "مسیریابی پایه"
|
||||||
"blockConnectionsConfigsDesc" = "این گزینهها ترافیک را بر اساس کشور درخواستشده خاص مسدود میکنند."
|
"blockConnectionsConfigsDesc" = "این گزینهها ترافیک را بر اساس کشور درخواستشده خاص مسدود میکنند."
|
||||||
|
@ -422,9 +421,6 @@
|
||||||
"RoutingStrategy" = "استراتژی کلی مسیریابی"
|
"RoutingStrategy" = "استراتژی کلی مسیریابی"
|
||||||
"RoutingStrategyDesc" = "استراتژی کلی مسیریابی برای حل تمام درخواستها را تعیین میکند"
|
"RoutingStrategyDesc" = "استراتژی کلی مسیریابی برای حل تمام درخواستها را تعیین میکند"
|
||||||
"Torrent" = "مسدودسازی پروتکل بیتتورنت"
|
"Torrent" = "مسدودسازی پروتکل بیتتورنت"
|
||||||
"TorrentDesc" = "پروتکل بیت تورنت را مسدود میکند"
|
|
||||||
"Family" = "محافظت خانواده"
|
|
||||||
"FamilyDesc" = "محتوای مخصوص بزرگسالان، و وبسایتهای ناامن را مسدود میکند"
|
|
||||||
"Inbounds" = "ورودیها"
|
"Inbounds" = "ورودیها"
|
||||||
"InboundsDesc" = "پذیرش کلاینت خاص"
|
"InboundsDesc" = "پذیرش کلاینت خاص"
|
||||||
"Outbounds" = "خروجیها"
|
"Outbounds" = "خروجیها"
|
||||||
|
@ -524,6 +520,12 @@
|
||||||
"edit" = "ویرایش سرور"
|
"edit" = "ویرایش سرور"
|
||||||
"domains" = "دامنهها"
|
"domains" = "دامنهها"
|
||||||
"expectIPs" = "آیپیهای مورد انتظار"
|
"expectIPs" = "آیپیهای مورد انتظار"
|
||||||
|
"unexpectIPs" = "آیپیهای غیرمنتظره"
|
||||||
|
"useSystemHosts" = "استفاده از Hosts سیستم"
|
||||||
|
"useSystemHostsDesc" = "استفاده از فایل hosts یک سیستم نصبشده"
|
||||||
|
"usePreset" = "استفاده از پیشتنظیم"
|
||||||
|
"dnsPresetTitle" = "پیشتنظیمهای DNS"
|
||||||
|
"dnsPresetFamily" = "خانوادگی"
|
||||||
|
|
||||||
[pages.xray.fakedns]
|
[pages.xray.fakedns]
|
||||||
"add" = "افزودن دیاناس جعلی"
|
"add" = "افزودن دیاناس جعلی"
|
||||||
|
|
|
@ -402,7 +402,6 @@
|
||||||
"generalConfigsDesc" = "Opsi ini akan menentukan penyesuaian strategi umum."
|
"generalConfigsDesc" = "Opsi ini akan menentukan penyesuaian strategi umum."
|
||||||
"logConfigs" = "Catatan"
|
"logConfigs" = "Catatan"
|
||||||
"logConfigsDesc" = "Log dapat mempengaruhi efisiensi server Anda. Disarankan untuk mengaktifkannya dengan bijak hanya jika diperlukan"
|
"logConfigsDesc" = "Log dapat mempengaruhi efisiensi server Anda. Disarankan untuk mengaktifkannya dengan bijak hanya jika diperlukan"
|
||||||
"blockConfigs" = "Pelindung"
|
|
||||||
"blockConfigsDesc" = "Opsi ini akan memblokir lalu lintas berdasarkan protokol dan situs web yang diminta."
|
"blockConfigsDesc" = "Opsi ini akan memblokir lalu lintas berdasarkan protokol dan situs web yang diminta."
|
||||||
"basicRouting" = "Perutean Dasar"
|
"basicRouting" = "Perutean Dasar"
|
||||||
"blockConnectionsConfigsDesc" = "Opsi ini akan memblokir lalu lintas berdasarkan negara yang diminta."
|
"blockConnectionsConfigsDesc" = "Opsi ini akan memblokir lalu lintas berdasarkan negara yang diminta."
|
||||||
|
@ -423,8 +422,6 @@
|
||||||
"RoutingStrategyDesc" = "Atur strategi pengalihan lalu lintas keseluruhan untuk menyelesaikan semua permintaan."
|
"RoutingStrategyDesc" = "Atur strategi pengalihan lalu lintas keseluruhan untuk menyelesaikan semua permintaan."
|
||||||
"Torrent" = "Blokir Protokol BitTorrent"
|
"Torrent" = "Blokir Protokol BitTorrent"
|
||||||
"TorrentDesc" = "Memblokir protokol BitTorrent."
|
"TorrentDesc" = "Memblokir protokol BitTorrent."
|
||||||
"Family" = "Proteksi Keluarga"
|
|
||||||
"FamilyDesc" = "Memblokir konten dewasa dan situs web berbahaya."
|
|
||||||
"Inbounds" = "Masuk"
|
"Inbounds" = "Masuk"
|
||||||
"InboundsDesc" = "Menerima klien tertentu."
|
"InboundsDesc" = "Menerima klien tertentu."
|
||||||
"Outbounds" = "Keluar"
|
"Outbounds" = "Keluar"
|
||||||
|
@ -524,6 +521,12 @@
|
||||||
"edit" = "Sunting Server"
|
"edit" = "Sunting Server"
|
||||||
"domains" = "Domains"
|
"domains" = "Domains"
|
||||||
"expectIPs" = "IP yang Diharapkan"
|
"expectIPs" = "IP yang Diharapkan"
|
||||||
|
"unexpectIPs" = "IP tak terduga"
|
||||||
|
"useSystemHosts" = "Gunakan Hosts Sistem"
|
||||||
|
"useSystemHostsDesc" = "Gunakan file hosts dari sistem yang terinstal"
|
||||||
|
"usePreset" = "Gunakan templat"
|
||||||
|
"dnsPresetTitle" = "Templat DNS"
|
||||||
|
"dnsPresetFamily" = "Keluarga"
|
||||||
|
|
||||||
[pages.xray.fakedns]
|
[pages.xray.fakedns]
|
||||||
"add" = "Tambahkan DNS Palsu"
|
"add" = "Tambahkan DNS Palsu"
|
||||||
|
|
|
@ -402,7 +402,6 @@
|
||||||
"generalConfigsDesc" = "これらのオプションは一般設定を決定します"
|
"generalConfigsDesc" = "これらのオプションは一般設定を決定します"
|
||||||
"logConfigs" = "ログ"
|
"logConfigs" = "ログ"
|
||||||
"logConfigsDesc" = "ログはサーバーのパフォーマンスに影響を与える可能性があるため、必要な場合にのみ有効にすることをお勧めします"
|
"logConfigsDesc" = "ログはサーバーのパフォーマンスに影響を与える可能性があるため、必要な場合にのみ有効にすることをお勧めします"
|
||||||
"blockConfigs" = "防御フィルター"
|
|
||||||
"blockConfigsDesc" = "これらのオプションは、特定のプロトコルやウェブサイトへのユーザー接続をブロックします"
|
"blockConfigsDesc" = "これらのオプションは、特定のプロトコルやウェブサイトへのユーザー接続をブロックします"
|
||||||
"basicRouting" = "基本ルーティング"
|
"basicRouting" = "基本ルーティング"
|
||||||
"blockConnectionsConfigsDesc" = "これらのオプションにより、特定のリクエスト元の国に基づいてトラフィックをブロックします。"
|
"blockConnectionsConfigsDesc" = "これらのオプションにより、特定のリクエスト元の国に基づいてトラフィックをブロックします。"
|
||||||
|
@ -422,9 +421,6 @@
|
||||||
"RoutingStrategy" = "ルーティングドメイン戦略設定"
|
"RoutingStrategy" = "ルーティングドメイン戦略設定"
|
||||||
"RoutingStrategyDesc" = "DNS解決の全体的なルーティング戦略を設定する"
|
"RoutingStrategyDesc" = "DNS解決の全体的なルーティング戦略を設定する"
|
||||||
"Torrent" = "BitTorrent プロトコルをブロック"
|
"Torrent" = "BitTorrent プロトコルをブロック"
|
||||||
"TorrentDesc" = "BitTorrentの使用を禁止する"
|
|
||||||
"Family" = "ファミリー保護"
|
|
||||||
"FamilyDesc" = "アダルトコンテンツや悪意のあるサイトをブロックする"
|
|
||||||
"Inbounds" = "インバウンドルール"
|
"Inbounds" = "インバウンドルール"
|
||||||
"InboundsDesc" = "特定のクライアントからのトラフィックを受け入れる"
|
"InboundsDesc" = "特定のクライアントからのトラフィックを受け入れる"
|
||||||
"Outbounds" = "アウトバウンドルール"
|
"Outbounds" = "アウトバウンドルール"
|
||||||
|
@ -524,6 +520,12 @@
|
||||||
"edit" = "サーバー編集"
|
"edit" = "サーバー編集"
|
||||||
"domains" = "ドメイン"
|
"domains" = "ドメイン"
|
||||||
"expectIPs" = "期待されるIP"
|
"expectIPs" = "期待されるIP"
|
||||||
|
"unexpectIPs" = "予期しないIP"
|
||||||
|
"useSystemHosts" = "システムのHostsを使用"
|
||||||
|
"useSystemHostsDesc" = "インストール済みシステムのhostsファイルを使用する"
|
||||||
|
"usePreset" = "テンプレートを使用"
|
||||||
|
"dnsPresetTitle" = "DNSテンプレート"
|
||||||
|
"dnsPresetFamily" = "ファミリー"
|
||||||
|
|
||||||
[pages.xray.fakedns]
|
[pages.xray.fakedns]
|
||||||
"add" = "フェイクDNS追加"
|
"add" = "フェイクDNS追加"
|
||||||
|
|
|
@ -402,7 +402,6 @@
|
||||||
"generalConfigsDesc" = "Essas opções determinam ajustes gerais."
|
"generalConfigsDesc" = "Essas opções determinam ajustes gerais."
|
||||||
"logConfigs" = "Log"
|
"logConfigs" = "Log"
|
||||||
"logConfigsDesc" = "Os logs podem afetar a eficiência do servidor. É recomendável habilitá-los com sabedoria apenas se necessário."
|
"logConfigsDesc" = "Os logs podem afetar a eficiência do servidor. É recomendável habilitá-los com sabedoria apenas se necessário."
|
||||||
"blockConfigs" = "Escudo de Proteção"
|
|
||||||
"blockConfigsDesc" = "Essas opções bloqueiam tráfego com base em protocolos e sites específicos solicitados."
|
"blockConfigsDesc" = "Essas opções bloqueiam tráfego com base em protocolos e sites específicos solicitados."
|
||||||
"basicRouting" = "Roteamento Básico"
|
"basicRouting" = "Roteamento Básico"
|
||||||
"blockConnectionsConfigsDesc" = "Essas opções bloquearão o tráfego com base no país solicitado."
|
"blockConnectionsConfigsDesc" = "Essas opções bloquearão o tráfego com base no país solicitado."
|
||||||
|
@ -422,9 +421,6 @@
|
||||||
"RoutingStrategy" = "Estratégia Geral de Roteamento"
|
"RoutingStrategy" = "Estratégia Geral de Roteamento"
|
||||||
"RoutingStrategyDesc" = "Definir a estratégia geral de roteamento de tráfego para resolver todas as solicitações."
|
"RoutingStrategyDesc" = "Definir a estratégia geral de roteamento de tráfego para resolver todas as solicitações."
|
||||||
"Torrent" = "Bloquear Protocolo BitTorrent"
|
"Torrent" = "Bloquear Protocolo BitTorrent"
|
||||||
"TorrentDesc" = "Bloqueia o protocolo BitTorrent."
|
|
||||||
"Family" = "Proteção Familiar"
|
|
||||||
"FamilyDesc" = "Bloqueia conteúdo adulto e sites maliciosos."
|
|
||||||
"Inbounds" = "Inbounds"
|
"Inbounds" = "Inbounds"
|
||||||
"InboundsDesc" = "Aceitar clientes específicos."
|
"InboundsDesc" = "Aceitar clientes específicos."
|
||||||
"Outbounds" = "Outbounds"
|
"Outbounds" = "Outbounds"
|
||||||
|
@ -524,6 +520,12 @@
|
||||||
"edit" = "Editar Servidor"
|
"edit" = "Editar Servidor"
|
||||||
"domains" = "Domínios"
|
"domains" = "Domínios"
|
||||||
"expectIPs" = "IPs Esperadas"
|
"expectIPs" = "IPs Esperadas"
|
||||||
|
"unexpectIPs" = "IPs inesperados"
|
||||||
|
"useSystemHosts" = "Usar Hosts do sistema"
|
||||||
|
"useSystemHostsDesc" = "Usar o arquivo hosts de um sistema instalado"
|
||||||
|
"usePreset" = "Usar modelo"
|
||||||
|
"dnsPresetTitle" = "Modelos DNS"
|
||||||
|
"dnsPresetFamily" = "Familiar"
|
||||||
|
|
||||||
[pages.xray.fakedns]
|
[pages.xray.fakedns]
|
||||||
"add" = "Adicionar Fake DNS"
|
"add" = "Adicionar Fake DNS"
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
"sure" = "Да"
|
"sure" = "Да"
|
||||||
"encryption" = "Шифрование"
|
"encryption" = "Шифрование"
|
||||||
"useIPv4ForHost" = "Использовать IPv4 для хоста"
|
"useIPv4ForHost" = "Использовать IPv4 для хоста"
|
||||||
"transmission" = "Протокол"
|
"transmission" = "Транспорт"
|
||||||
"host" = "Хост"
|
"host" = "Хост"
|
||||||
"path" = "Путь"
|
"path" = "Путь"
|
||||||
"camouflage" = "Маскировка"
|
"camouflage" = "Маскировка"
|
||||||
|
@ -59,24 +59,24 @@
|
||||||
"security" = "Безопасность"
|
"security" = "Безопасность"
|
||||||
"secAlertTitle" = "Предупреждение системы безопасности"
|
"secAlertTitle" = "Предупреждение системы безопасности"
|
||||||
"secAlertSsl" = "Это соединение не защищено. Пожалуйста, не вводите конфиденциальную информацию, пока не установите SSL сертификат для защиты соединения"
|
"secAlertSsl" = "Это соединение не защищено. Пожалуйста, не вводите конфиденциальную информацию, пока не установите SSL сертификат для защиты соединения"
|
||||||
"secAlertConf" = "Некоторые настройки уязвимы для атак. Рекомендуем усилить протоколы безопасности, чтобы предотвратить проблемы в будущем."
|
"secAlertConf" = "Некоторые настройки уязвимы для атак. Чтобы в будущем не было проблем, нужно усилить защиту."
|
||||||
"secAlertSSL" = "Ваше подключение к панели небезопасно. Пожалуйста, установите SSL сертификат для защиты данных."
|
"secAlertSSL" = "Ваше подключение к панели не защищено. Установите SSL сертификат для защиты данных."
|
||||||
"secAlertPanelPort" = "Порт, на котором работает панель небезопасен. Пожалуйста, установите случайный или просто другой порт."
|
"secAlertPanelPort" = "Порт панели по умолчанию небезопасен. Установите случайный или просто другой порт."
|
||||||
"secAlertPanelURI" = "URI-адрес панели по умолчанию небезопасен. Пожалуйста, настройте сложный URI-адрес."
|
"secAlertPanelURI" = "Адрес панели по умолчанию небезопасен. Сделайте адрес сложным."
|
||||||
"secAlertSubURI" = "URI-адрес подписки по умолчанию небезопасен. Пожалуйста, настройте сложный URI-адрес."
|
"secAlertSubURI" = "URI-адрес подписки по умолчанию небезопасен. Пожалуйста, настройте сложный URI-адрес."
|
||||||
"secAlertSubJsonURI" = "URI-адрес по умолчанию для JSON подписки небезопасен. Пожалуйста, настройте сложный URI-адрес."
|
"secAlertSubJsonURI" = "URI-адрес по умолчанию для JSON подписки небезопасен. Пожалуйста, настройте сложный URI-адрес."
|
||||||
"emptyDnsDesc" = "Нет добавленных DNS-серверов."
|
"emptyDnsDesc" = "Нет добавленных DNS-серверов."
|
||||||
"emptyFakeDnsDesc" = "Нет добавленных Fake DNS-серверов."
|
"emptyFakeDnsDesc" = "Нет добавленных Fake DNS-серверов."
|
||||||
"emptyBalancersDesc" = "Нет добавленных балансировщиков."
|
"emptyBalancersDesc" = "Нет добавленных балансировщиков."
|
||||||
"emptyReverseDesc" = "Нет добавленных обратных прокси."
|
"emptyReverseDesc" = "Нет добавленных реверс-прокси."
|
||||||
"somethingWentWrong" = "Что-то пошло не так"
|
"somethingWentWrong" = "Что-то пошло не так"
|
||||||
|
|
||||||
[menu]
|
[menu]
|
||||||
"theme" = "Тема"
|
"theme" = "Тема"
|
||||||
"dark" = "Темная"
|
"dark" = "Темная"
|
||||||
"ultraDark" = "Очень темная"
|
"ultraDark" = "Очень темная"
|
||||||
"dashboard" = "Статус системы"
|
"dashboard" = "Дашборд"
|
||||||
"inbounds" = "Входящие подключения"
|
"inbounds" = "Инбаунды"
|
||||||
"settings" = "Настройки"
|
"settings" = "Настройки"
|
||||||
"xray" = "Настройки Xray"
|
"xray" = "Настройки Xray"
|
||||||
"logout" = "Выход"
|
"logout" = "Выход"
|
||||||
|
@ -91,15 +91,15 @@
|
||||||
"invalidFormData" = "Недопустимый формат данных"
|
"invalidFormData" = "Недопустимый формат данных"
|
||||||
"emptyUsername" = "Введите имя пользователя"
|
"emptyUsername" = "Введите имя пользователя"
|
||||||
"emptyPassword" = "Введите пароль"
|
"emptyPassword" = "Введите пароль"
|
||||||
"wrongUsernameOrPassword" = "Неверное имя пользователя, пароль или код двухфакторной аутентификации."
|
"wrongUsernameOrPassword" = "Неверные данные учетной записи."
|
||||||
"successLogin" = "Успешный вход"
|
"successLogin" = "Вы успешно вошли в аккаунт"
|
||||||
|
|
||||||
[pages.index]
|
[pages.index]
|
||||||
"title" = "Статус системы"
|
"title" = "Дашборд"
|
||||||
"cpu" = "ЦП"
|
"cpu" = "ЦП"
|
||||||
"logicalProcessors" = "Логические процессоры"
|
"logicalProcessors" = "Логические процессоры"
|
||||||
"frequency" = "Частота"
|
"frequency" = "Частота"
|
||||||
"swap" = "Swap"
|
"swap" = "Файл подкачки"
|
||||||
"storage" = "Диск"
|
"storage" = "Диск"
|
||||||
"memory" = "ОЗУ"
|
"memory" = "ОЗУ"
|
||||||
"threads" = "Потоки"
|
"threads" = "Потоки"
|
||||||
|
@ -136,7 +136,7 @@
|
||||||
"geofileUpdateDialogDesc" = "Это обновит файл #filename#."
|
"geofileUpdateDialogDesc" = "Это обновит файл #filename#."
|
||||||
"geofileUpdatePopover" = "Геофайл успешно обновлён"
|
"geofileUpdatePopover" = "Геофайл успешно обновлён"
|
||||||
"dontRefresh" = "Установка в процессе. Не обновляйте страницу"
|
"dontRefresh" = "Установка в процессе. Не обновляйте страницу"
|
||||||
"logs" = "Логи"
|
"logs" = "Журнал"
|
||||||
"config" = "Конфигурация"
|
"config" = "Конфигурация"
|
||||||
"backup" = "Резервная копия"
|
"backup" = "Резервная копия"
|
||||||
"backupTitle" = "Резервная копия базы данных"
|
"backupTitle" = "Резервная копия базы данных"
|
||||||
|
@ -151,10 +151,10 @@
|
||||||
"getConfigError" = "Произошла ошибка при получении конфигурационного файла"
|
"getConfigError" = "Произошла ошибка при получении конфигурационного файла"
|
||||||
|
|
||||||
[pages.inbounds]
|
[pages.inbounds]
|
||||||
"title" = "Входящие подключения"
|
"title" = "Инбаунды"
|
||||||
"totalDownUp" = "Объем отправленного/полученного трафика"
|
"totalDownUp" = "Объем отправленного/полученного трафика"
|
||||||
"totalUsage" = "Всего трафика"
|
"totalUsage" = "Всего трафика"
|
||||||
"inboundCount" = "Всего подключений"
|
"inboundCount" = "Всего инбаундов"
|
||||||
"operate" = "Меню"
|
"operate" = "Меню"
|
||||||
"enable" = "Включить"
|
"enable" = "Включить"
|
||||||
"remark" = "Примечание"
|
"remark" = "Примечание"
|
||||||
|
@ -165,18 +165,18 @@
|
||||||
"transportConfig" = "Транспорт"
|
"transportConfig" = "Транспорт"
|
||||||
"expireDate" = "Дата окончания"
|
"expireDate" = "Дата окончания"
|
||||||
"resetTraffic" = "Сброс трафика"
|
"resetTraffic" = "Сброс трафика"
|
||||||
"addInbound" = "Создать новое подключение"
|
"addInbound" = "Создать инбаунд"
|
||||||
"generalActions" = "Общие действия"
|
"generalActions" = "Общие действия"
|
||||||
"autoRefresh" = "Автообновление"
|
"autoRefresh" = "Автообновление"
|
||||||
"autoRefreshInterval" = "Интервал"
|
"autoRefreshInterval" = "Интервал"
|
||||||
"modifyInbound" = "Изменить входящее подключение"
|
"modifyInbound" = "Изменить инбаунд"
|
||||||
"deleteInbound" = "Удалить входящее подключение"
|
"deleteInbound" = "Удалить инбаунд"
|
||||||
"deleteInboundContent" = "Вы уверены, что хотите удалить входящее подключение?"
|
"deleteInboundContent" = "Вы уверены, что хотите удалить инбаунд?"
|
||||||
"deleteClient" = "Удалить клиента"
|
"deleteClient" = "Удалить клиента"
|
||||||
"deleteClientContent" = "Вы уверены, что хотите удалить клиента?"
|
"deleteClientContent" = "Вы уверены, что хотите удалить клиента?"
|
||||||
"resetTrafficContent" = "Вы уверены, что хотите сбросить трафик?"
|
"resetTrafficContent" = "Вы уверены, что хотите сбросить трафик?"
|
||||||
"inboundUpdateSuccess" = "Входящее подключение успешно обновлено."
|
"inboundUpdateSuccess" = "Инбаунд успешно обновлен."
|
||||||
"inboundCreateSuccess" = "Входящее подключение успешно создано."
|
"inboundCreateSuccess" = "Инбаунд успешно создан."
|
||||||
"copyLink" = "Копировать ссылку"
|
"copyLink" = "Копировать ссылку"
|
||||||
"address" = "Адрес"
|
"address" = "Адрес"
|
||||||
"network" = "Сеть"
|
"network" = "Сеть"
|
||||||
|
@ -196,11 +196,11 @@
|
||||||
"export" = "Экспорт ссылок"
|
"export" = "Экспорт ссылок"
|
||||||
"clone" = "Клонировать"
|
"clone" = "Клонировать"
|
||||||
"cloneInbound" = "Клонировать"
|
"cloneInbound" = "Клонировать"
|
||||||
"cloneInboundContent" = "Будут клонированы все настройки входящих подключений, кроме списка клиентов, порта и IP-адреса прослушивания"
|
"cloneInboundContent" = "Будут клонированы все настройки инбаундов, кроме списка клиентов, порта и IP-адреса прослушивания"
|
||||||
"cloneInboundOk" = "Клонировано"
|
"cloneInboundOk" = "Клонировано"
|
||||||
"resetAllTraffic" = "Сброс трафика всех подключений"
|
"resetAllTraffic" = "Сброс трафика всех инбаундов"
|
||||||
"resetAllTrafficTitle" = "Сброс трафика всех подключений"
|
"resetAllTrafficTitle" = "Сброс трафика всех инбаундов"
|
||||||
"resetAllTrafficContent" = "Вы уверены, что хотите сбросить трафик всех подключений?"
|
"resetAllTrafficContent" = "Вы уверены, что хотите сбросить трафик всех инбаундов?"
|
||||||
"resetInboundClientTraffics" = "Сброс трафика клиента"
|
"resetInboundClientTraffics" = "Сброс трафика клиента"
|
||||||
"resetInboundClientTrafficTitle" = "Сброс трафика клиентов"
|
"resetInboundClientTrafficTitle" = "Сброс трафика клиентов"
|
||||||
"resetInboundClientTrafficContent" = "Вы уверены, что хотите сбросить трафик для этих клиентов?"
|
"resetInboundClientTrafficContent" = "Вы уверены, что хотите сбросить трафик для этих клиентов?"
|
||||||
|
@ -222,10 +222,10 @@
|
||||||
"subscriptionDesc" = "Вы можете найти свою ссылку подписки в разделе 'Подробнее'"
|
"subscriptionDesc" = "Вы можете найти свою ссылку подписки в разделе 'Подробнее'"
|
||||||
"info" = "Информация"
|
"info" = "Информация"
|
||||||
"same" = "Тот же"
|
"same" = "Тот же"
|
||||||
"inboundData" = "Данные подключений"
|
"inboundData" = "Данные инбаундов"
|
||||||
"exportInbound" = "Экспорт входящих подключений"
|
"exportInbound" = "Экспорт инбаундов"
|
||||||
"import" = "Импортировать"
|
"import" = "Импортировать"
|
||||||
"importInbound" = "Импорт входящих подключений"
|
"importInbound" = "Импорт инбаундов"
|
||||||
|
|
||||||
[pages.client]
|
[pages.client]
|
||||||
"add" = "Создать клиента"
|
"add" = "Создать клиента"
|
||||||
|
@ -249,13 +249,13 @@
|
||||||
"obtain" = "Получить"
|
"obtain" = "Получить"
|
||||||
"updateSuccess" = "Обновление прошло успешно"
|
"updateSuccess" = "Обновление прошло успешно"
|
||||||
"logCleanSuccess" = "Лог был очищен"
|
"logCleanSuccess" = "Лог был очищен"
|
||||||
"inboundsUpdateSuccess" = "Входящие подключения успешно обновлены"
|
"inboundsUpdateSuccess" = "Инбаунды успешно обновлены"
|
||||||
"inboundUpdateSuccess" = "Входящее подключение успешно обновлено"
|
"inboundUpdateSuccess" = "Инбаунд успешно обновлено"
|
||||||
"inboundCreateSuccess" = "Входящее подключение успешно создано"
|
"inboundCreateSuccess" = "Инбаунд успешно создано"
|
||||||
"inboundDeleteSuccess" = "Входящее подключение успешно удалено"
|
"inboundDeleteSuccess" = "Инбаунд успешно удалено"
|
||||||
"inboundClientAddSuccess" = "Клиент(ы) входящего подключения добавлен(ы)"
|
"inboundClientAddSuccess" = "Клиент(ы) инбаунда добавлен(ы)"
|
||||||
"inboundClientDeleteSuccess" = "Клиент входящего подключения удалён"
|
"inboundClientDeleteSuccess" = "Клиент инбаунда удалён"
|
||||||
"inboundClientUpdateSuccess" = "Клиент входящего подключения обновлён"
|
"inboundClientUpdateSuccess" = "Клиент инбаунда обновлён"
|
||||||
"delDepletedClientsSuccess" = "Все исчерпанные клиенты удалены"
|
"delDepletedClientsSuccess" = "Все исчерпанные клиенты удалены"
|
||||||
"resetAllClientTrafficSuccess" = "Весь трафик клиента сброшен"
|
"resetAllClientTrafficSuccess" = "Весь трафик клиента сброшен"
|
||||||
"resetAllTrafficSuccess" = "Весь трафик сброшен"
|
"resetAllTrafficSuccess" = "Весь трафик сброшен"
|
||||||
|
@ -281,15 +281,15 @@
|
||||||
[pages.settings]
|
[pages.settings]
|
||||||
"title" = "Настройки"
|
"title" = "Настройки"
|
||||||
"save" = "Сохранить"
|
"save" = "Сохранить"
|
||||||
"infoDesc" = "Каждое выполненное изменение необходимо сохранить. Пожалуйста, перезапустите панель, чтобы изменения вступили в силу"
|
"infoDesc" = "Каждое внесённое изменение должно быть сохранено. Пожалуйста, перезапустите панель, чтобы изменения вступили в силу."
|
||||||
"restartPanel" = "Перезапуск панели"
|
"restartPanel" = "Перезапуск панели"
|
||||||
"restartPanelDesc" = "Вы уверены, что хотите перезапустить панель? Подтвердите, и перезапуск произойдёт через 3 секунды. Если панель будет недоступна, проверьте лог сервера"
|
"restartPanelDesc" = "Вы уверены, что хотите перезапустить панель? Подтвердите, и перезапуск произойдёт через 3 секунды. Если панель будет недоступна, проверьте лог сервера"
|
||||||
"restartPanelSuccess" = "Панель успешно перезапущена"
|
"restartPanelSuccess" = "Панель успешно перезапущена"
|
||||||
"actions" = "Действия"
|
"actions" = "Действия"
|
||||||
"resetDefaultConfig" = "Восстановить настройки по умолчанию"
|
"resetDefaultConfig" = "Восстановить настройки по умолчанию"
|
||||||
"panelSettings" = "Настройки панели"
|
"panelSettings" = "Панель"
|
||||||
"securitySettings" = "Настройки безопасности"
|
"securitySettings" = "Учетная запись"
|
||||||
"TGBotSettings" = "Настройки Telegram бота"
|
"TGBotSettings" = "Telegram"
|
||||||
"panelListeningIP" = "IP-адрес для управления панелью"
|
"panelListeningIP" = "IP-адрес для управления панелью"
|
||||||
"panelListeningIPDesc" = "Оставьте пустым для подключения с любого IP"
|
"panelListeningIPDesc" = "Оставьте пустым для подключения с любого IP"
|
||||||
"panelListeningDomain" = "Домен панели"
|
"panelListeningDomain" = "Домен панели"
|
||||||
|
@ -303,7 +303,7 @@
|
||||||
"panelUrlPath" = "Корневой путь URL адреса панели"
|
"panelUrlPath" = "Корневой путь URL адреса панели"
|
||||||
"panelUrlPathDesc" = "Должен начинаться с '/' и заканчиваться '/'"
|
"panelUrlPathDesc" = "Должен начинаться с '/' и заканчиваться '/'"
|
||||||
"pageSize" = "Размер нумерации страниц"
|
"pageSize" = "Размер нумерации страниц"
|
||||||
"pageSizeDesc" = "Определить размер страницы для таблицы входящих подключений. Установите 0, чтобы отключить"
|
"pageSizeDesc" = "Определить размер страницы для таблицы инбаундов. Установите 0, чтобы отключить"
|
||||||
"remarkModel" = "Модель примечания и символ разделения"
|
"remarkModel" = "Модель примечания и символ разделения"
|
||||||
"datepicker" = "Выбор даты"
|
"datepicker" = "Выбор даты"
|
||||||
"datepickerPlaceholder" = "Выберите дату"
|
"datepickerPlaceholder" = "Выберите дату"
|
||||||
|
@ -391,19 +391,18 @@
|
||||||
[pages.xray]
|
[pages.xray]
|
||||||
"title" = "Настройки Xray"
|
"title" = "Настройки Xray"
|
||||||
"save" = "Сохранить"
|
"save" = "Сохранить"
|
||||||
"restart" = "Перезапустить Xray"
|
"restart" = "Перезапуск Xray"
|
||||||
"restartSuccess" = "Xray успешно перезапущен"
|
"restartSuccess" = "Xray успешно перезапущен"
|
||||||
"stopSuccess" = "Xray успешно остановлен"
|
"stopSuccess" = "Xray успешно остановлен"
|
||||||
"restartError" = "Произошла ошибка при перезапуске Xray."
|
"restartError" = "Произошла ошибка при перезапуске Xray."
|
||||||
"stopError" = "Произошла ошибка при остановке Xray."
|
"stopError" = "Произошла ошибка при остановке Xray."
|
||||||
"basicTemplate" = "Базовый шаблон"
|
"basicTemplate" = "Основное"
|
||||||
"advancedTemplate" = "Расширенный шаблон"
|
"advancedTemplate" = "Расширенный шаблон"
|
||||||
"generalConfigs" = "Основные настройки"
|
"generalConfigs" = "Основные настройки"
|
||||||
"generalConfigsDesc" = "Эти параметры описывают общие настройки"
|
"generalConfigsDesc" = "Эти параметры описывают общие настройки"
|
||||||
"logConfigs" = "Логи"
|
"logConfigs" = "Логи"
|
||||||
"logConfigsDesc" = "Логи могут замедлять работу сервера. Включайте только нужные вам виды логов при необходимости!"
|
"logConfigsDesc" = "Логи могут замедлять работу сервера. Включайте только нужные вам виды логов при необходимости!"
|
||||||
"blockConfigs" = "Блокировка подключений"
|
"blockConfigsDesc" = "Настройте, чтобы клиенты не имели доступа к определенным протоколам"
|
||||||
"blockConfigsDesc" = "Настройте, чтобы клиенты не имели доступа к определенным протоколам и веб-сайтами"
|
|
||||||
"basicRouting" = "Базовые соединения"
|
"basicRouting" = "Базовые соединения"
|
||||||
"blockConnectionsConfigsDesc" = "Эти параметры будут блокировать трафик в зависимости от страны назначения."
|
"blockConnectionsConfigsDesc" = "Эти параметры будут блокировать трафик в зависимости от страны назначения."
|
||||||
"directConnectionsConfigsDesc" = "Прямое соединение означает, что определенный трафик не будет перенаправлен через другой сервер."
|
"directConnectionsConfigsDesc" = "Прямое соединение означает, что определенный трафик не будет перенаправлен через другой сервер."
|
||||||
|
@ -414,22 +413,19 @@
|
||||||
"ipv4Routing" = "Правила IPv4"
|
"ipv4Routing" = "Правила IPv4"
|
||||||
"ipv4RoutingDesc" = "Эти параметры позволят клиентам маршрутизироваться к целевым доменам только через IPv4"
|
"ipv4RoutingDesc" = "Эти параметры позволят клиентам маршрутизироваться к целевым доменам только через IPv4"
|
||||||
"warpRouting" = "Правила WARP"
|
"warpRouting" = "Правила WARP"
|
||||||
"warpRoutingDesc" = "Внимание: перед использованием этих параметров установите WARP в режиме прокси-сервера socks5 на свой сервер, следуя инструкциям на GitHub панели. WARP будет направлять трафик на веб-сайты через серверы Cloudflare"
|
"warpRoutingDesc" = " Эти опции будут направлять трафик в зависимости от конкретного пункта назначения через WARP."
|
||||||
"Template" = "Шаблон конфигурации Xray"
|
"Template" = "Шаблон конфигурации Xray"
|
||||||
"TemplateDesc" = "Создание файла конфигурации Xray на основе этого шаблона"
|
"TemplateDesc" = "На основе шаблона создаётся конфигурационный файл Xray."
|
||||||
"FreedomStrategy" = "Настройка стратегии протокола Freedom"
|
"FreedomStrategy" = "Настройка стратегии протокола Freedom"
|
||||||
"FreedomStrategyDesc" = "Установка стратегии вывода сети в протоколе Freedom"
|
"FreedomStrategyDesc" = "Установка стратегии вывода сети в протоколе Freedom"
|
||||||
"RoutingStrategy" = "Настройка маршрутизации доменов"
|
"RoutingStrategy" = "Настройка маршрутизации доменов"
|
||||||
"RoutingStrategyDesc" = "Установка общей стратегии маршрутизации разрешения DNS"
|
"RoutingStrategyDesc" = "Установка общей стратегии маршрутизации разрешения DNS"
|
||||||
"Torrent" = "Заблокировать BitTorrent"
|
"Torrent" = "Заблокировать BitTorrent"
|
||||||
"TorrentDesc" = "Запретить входящий/исходящий трафик, в котором фигурирует протокол BitTorrent"
|
"Inbounds" = "Инбаунды"
|
||||||
"Family" = "Семейный режим"
|
|
||||||
"FamilyDesc" = "Использовать DNS-сервера Cloudflare для блокировки вредоносного ПО и контента для взрослых в целях защиты семьи."
|
|
||||||
"Inbounds" = "Входящее соединение"
|
|
||||||
"InboundsDesc" = "Изменение шаблона конфигурации для подключения определенных клиентов"
|
"InboundsDesc" = "Изменение шаблона конфигурации для подключения определенных клиентов"
|
||||||
"Outbounds" = "Исходящее соединение"
|
"Outbounds" = "Аутбаунды"
|
||||||
"Balancers" = "Балансировщик"
|
"Balancers" = "Балансировщик"
|
||||||
"OutboundsDesc" = "Изменение шаблона конфигурации, чтобы определить исходящие соединения для этого сервера"
|
"OutboundsDesc" = "Изменение шаблона конфигурации, чтобы определить аутбаунды для этого сервера"
|
||||||
"Routings" = "Маршрутизация"
|
"Routings" = "Маршрутизация"
|
||||||
"RoutingsDesc" = "Важен приоритет каждого правила!"
|
"RoutingsDesc" = "Важен приоритет каждого правила!"
|
||||||
"completeTemplate" = "Все"
|
"completeTemplate" = "Все"
|
||||||
|
@ -460,8 +456,8 @@
|
||||||
"down" = "Опустить вниз"
|
"down" = "Опустить вниз"
|
||||||
"source" = "Источник"
|
"source" = "Источник"
|
||||||
"dest" = "Пункт назначения"
|
"dest" = "Пункт назначения"
|
||||||
"inbound" = "Входящее соединение"
|
"inbound" = "Инбаунд"
|
||||||
"outbound" = "Исходящее соединение"
|
"outbound" = "Аутбаунд"
|
||||||
"balancer" = "Балансировщик"
|
"balancer" = "Балансировщик"
|
||||||
"info" = "Информация"
|
"info" = "Информация"
|
||||||
"add" = "Создать правило"
|
"add" = "Создать правило"
|
||||||
|
@ -469,14 +465,14 @@
|
||||||
"useComma" = "Элементы, разделённые запятыми"
|
"useComma" = "Элементы, разделённые запятыми"
|
||||||
|
|
||||||
[pages.xray.outbound]
|
[pages.xray.outbound]
|
||||||
"addOutbound" = "Создать исходящее соединение"
|
"addOutbound" = "Создать аутбаунд"
|
||||||
"addReverse" = "Создать обратный прокси"
|
"addReverse" = "Создать реверс-прокси"
|
||||||
"editOutbound" = "Изменить исходящее соединение"
|
"editOutbound" = "Изменить аутбаунд"
|
||||||
"editReverse" = "Редактировать обратное прокси"
|
"editReverse" = "Редактировать реверс-прокси"
|
||||||
"tag" = "Тег"
|
"tag" = "Тег"
|
||||||
"tagDesc" = "Уникальный тег"
|
"tagDesc" = "Уникальный тег"
|
||||||
"address" = "Адрес"
|
"address" = "Адрес"
|
||||||
"reverse" = "Обратный"
|
"reverse" = "Реверс-прокси"
|
||||||
"domain" = "Домен"
|
"domain" = "Домен"
|
||||||
"type" = "Тип"
|
"type" = "Тип"
|
||||||
"bridge" = "Мост"
|
"bridge" = "Мост"
|
||||||
|
@ -485,7 +481,7 @@
|
||||||
"intercon" = "Соединение"
|
"intercon" = "Соединение"
|
||||||
"settings" = "Настройки"
|
"settings" = "Настройки"
|
||||||
"accountInfo" = "Информация об учетной записи"
|
"accountInfo" = "Информация об учетной записи"
|
||||||
"outboundStatus" = "Исходящий статус"
|
"outboundStatus" = "Статус аутбаунда"
|
||||||
"sendThrough" = "Отправить через"
|
"sendThrough" = "Отправить через"
|
||||||
|
|
||||||
[pages.xray.balancer]
|
[pages.xray.balancer]
|
||||||
|
@ -498,7 +494,7 @@
|
||||||
"balancerDesc" = "Невозможно одновременно использовать balancerTag и outboundTag. При одновременном использовании будет работать только outboundTag."
|
"balancerDesc" = "Невозможно одновременно использовать balancerTag и outboundTag. При одновременном использовании будет работать только outboundTag."
|
||||||
|
|
||||||
[pages.xray.wireguard]
|
[pages.xray.wireguard]
|
||||||
"secretKey" = "Приватный ключ"
|
"secretKey" = "Секретный ключ"
|
||||||
"publicKey" = "Публичный ключ"
|
"publicKey" = "Публичный ключ"
|
||||||
"allowedIPs" = "Разрешенные IP-адреса"
|
"allowedIPs" = "Разрешенные IP-адреса"
|
||||||
"endpoint" = "Конечная точка"
|
"endpoint" = "Конечная точка"
|
||||||
|
@ -524,6 +520,12 @@
|
||||||
"edit" = "Редактировать DNS"
|
"edit" = "Редактировать DNS"
|
||||||
"domains" = "Домены"
|
"domains" = "Домены"
|
||||||
"expectIPs" = "Ожидаемые IP"
|
"expectIPs" = "Ожидаемые IP"
|
||||||
|
"unexpectIPs" = "Неожидаемые IP"
|
||||||
|
"useSystemHosts" = "Использовать системные Hosts"
|
||||||
|
"useSystemHostsDesc" = "Использовать файл hosts из установленной системы"
|
||||||
|
"usePreset" = "Использовать шаблон"
|
||||||
|
"dnsPresetTitle" = "Шаблоны DNS"
|
||||||
|
"dnsPresetFamily" = "Семейный"
|
||||||
|
|
||||||
[pages.xray.fakedns]
|
[pages.xray.fakedns]
|
||||||
"add" = "Создать Fake DNS"
|
"add" = "Создать Fake DNS"
|
||||||
|
@ -553,8 +555,8 @@
|
||||||
"modifyUser" = "Вы успешно изменили учетные данные администратора."
|
"modifyUser" = "Вы успешно изменили учетные данные администратора."
|
||||||
"originalUserPassIncorrect" = "Неверное имя пользователя или пароль"
|
"originalUserPassIncorrect" = "Неверное имя пользователя или пароль"
|
||||||
"userPassMustBeNotEmpty" = "Новое имя пользователя и новый пароль должны быть заполнены"
|
"userPassMustBeNotEmpty" = "Новое имя пользователя и новый пароль должны быть заполнены"
|
||||||
"getOutboundTrafficError" = "Ошибка получения исходящего трафика"
|
"getOutboundTrafficError" = "Ошибка получения трафика аутбаунда"
|
||||||
"resetOutboundTrafficError" = "Ошибка сброса исходящего трафика"
|
"resetOutboundTrafficError" = "Ошибка сброса трафика аутбаунда"
|
||||||
|
|
||||||
[tgbot]
|
[tgbot]
|
||||||
"keyboardClosed" = "❌ Клавиатура закрыта."
|
"keyboardClosed" = "❌ Клавиатура закрыта."
|
||||||
|
@ -562,7 +564,7 @@
|
||||||
"noQuery" = "❌ Запрос не найден. Пожалуйста, повторите команду."
|
"noQuery" = "❌ Запрос не найден. Пожалуйста, повторите команду."
|
||||||
"wentWrong" = "❌ Что-то пошло не так..."
|
"wentWrong" = "❌ Что-то пошло не так..."
|
||||||
"noIpRecord" = "❗ Нет записей об IP-адресе."
|
"noIpRecord" = "❗ Нет записей об IP-адресе."
|
||||||
"noInbounds" = "❗ У вас не настроено ни одного подключения."
|
"noInbounds" = "❗ У вас не настроено ни одного инбаунда."
|
||||||
"unlimited" = "♾ Безлимит"
|
"unlimited" = "♾ Безлимит"
|
||||||
"add" = "Добавить"
|
"add" = "Добавить"
|
||||||
"month" = "Месяц"
|
"month" = "Месяц"
|
||||||
|
@ -571,7 +573,7 @@
|
||||||
"days" = "Дней"
|
"days" = "Дней"
|
||||||
"hours" = "Часов"
|
"hours" = "Часов"
|
||||||
"unknown" = "Неизвестно"
|
"unknown" = "Неизвестно"
|
||||||
"inbounds" = "Подключения"
|
"inbounds" = "Инбаунды"
|
||||||
"clients" = "Клиенты"
|
"clients" = "Клиенты"
|
||||||
"offline" = "🔴 Офлайн"
|
"offline" = "🔴 Офлайн"
|
||||||
"online" = "🟢 Онлайн"
|
"online" = "🟢 Онлайн"
|
||||||
|
@ -585,7 +587,7 @@
|
||||||
"status" = "✅ Бот функционирует нормально."
|
"status" = "✅ Бот функционирует нормально."
|
||||||
"usage" = "❗ Пожалуйста, укажите email для поиска."
|
"usage" = "❗ Пожалуйста, укажите email для поиска."
|
||||||
"getID" = "🆔 Ваш User ID: <code>{{ .ID }}</code>"
|
"getID" = "🆔 Ваш User ID: <code>{{ .ID }}</code>"
|
||||||
"helpAdminCommands" = "🔃 Для перезапуска Xray Core:\r\n<code>/restart</code>\r\n\r\n🔎 Для поиска клиента по email:\r\n<code>/usage [Email]</code>\r\n\r\n📊 Для поиска подключений (со статистикой клиентов):\r\n<code>/inbound [имя подключения]</code>\r\n\r\n🆔 Ваш Telegram User ID:\r\n<code>/id</code>"
|
"helpAdminCommands" = "🔃 Для перезапуска Xray Core:\r\n<code>/restart</code>\r\n\r\n🔎 Для поиска клиента по email:\r\n<code>/usage [Email]</code>\r\n\r\n📊 Для поиска инбаундов (со статистикой клиентов):\r\n<code>/inbound [имя подключения]</code>\r\n\r\n🆔 Ваш Telegram User ID:\r\n<code>/id</code>"
|
||||||
"helpClientCommands" = "💲 Для просмотра информации о вашей подписке используйте команду:\r\n<code>/usage [Email]</code>\r\n\r\n🆔 Ваш Telegram User ID:\r\n<code>/id</code>"
|
"helpClientCommands" = "💲 Для просмотра информации о вашей подписке используйте команду:\r\n<code>/usage [Email]</code>\r\n\r\n🆔 Ваш Telegram User ID:\r\n<code>/id</code>"
|
||||||
"restartUsage" = "\r\n\r\n<code>/restart</code>"
|
"restartUsage" = "\r\n\r\n<code>/restart</code>"
|
||||||
"restartSuccess" = "✅ Ядро Xray успешно перезапущено."
|
"restartSuccess" = "✅ Ядро Xray успешно перезапущено."
|
||||||
|
@ -651,8 +653,8 @@
|
||||||
"pass_prompt" = "🔑 Стандартный пароль: {{ .ClientPassword }}\n\nВведите ваш пароль."
|
"pass_prompt" = "🔑 Стандартный пароль: {{ .ClientPassword }}\n\nВведите ваш пароль."
|
||||||
"email_prompt" = "📧 Стандартный email: {{ .ClientEmail }}\n\nВведите ваш email."
|
"email_prompt" = "📧 Стандартный email: {{ .ClientEmail }}\n\nВведите ваш email."
|
||||||
"comment_prompt" = "💬 Стандартный комментарий: {{ .ClientComment }}\n\nВведите ваш комментарий."
|
"comment_prompt" = "💬 Стандартный комментарий: {{ .ClientComment }}\n\nВведите ваш комментарий."
|
||||||
"inbound_client_data_id" = "🔄 Подключения: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Email: {{ .ClientEmail }}\n📊 Трафик: {{ .ClientTraffic }}\n📅 Дата исчерпания: {{ .ClientExp }}\n💬 Комментарий: {{ .ClientComment }}\n\nТеперь вы можете добавить клиента в подключение!"
|
"inbound_client_data_id" = "🔄 Инбаунды: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Email: {{ .ClientEmail }}\n📊 Трафик: {{ .ClientTraffic }}\n📅 Дата исчерпания: {{ .ClientExp }}\n💬 Комментарий: {{ .ClientComment }}\n\nТеперь вы можете добавить клиента в инбаунд!"
|
||||||
"inbound_client_data_pass" = "🔄 Подключения: {{ .InboundRemark }}\n\n🔑 Пароль: {{ .ClientPass }}\n📧 Email: {{ .ClientEmail }}\n📊 Трафик: {{ .ClientTraffic }}\n📅 Дата исчерпания: {{ .ClientExp }}\n💬 Комментарий: {{ .ClientComment }}\n\nТеперь вы можете добавить клиента в подключение!"
|
"inbound_client_data_pass" = "🔄 Инбаунды: {{ .InboundRemark }}\n\n🔑 Пароль: {{ .ClientPass }}\n📧 Email: {{ .ClientEmail }}\n📊 Трафик: {{ .ClientTraffic }}\n📅 Дата исчерпания: {{ .ClientExp }}\n💬 Комментарий: {{ .ClientComment }}\n\nТеперь вы можете добавить клиента в инбаунд!"
|
||||||
"cancel" = "❌ Процесс отменён! \n\nВы можете снова начать с /start в любое время. 🔄"
|
"cancel" = "❌ Процесс отменён! \n\nВы можете снова начать с /start в любое время. 🔄"
|
||||||
"error_add_client" = "⚠️ Ошибка:\n\n {{ .error }}"
|
"error_add_client" = "⚠️ Ошибка:\n\n {{ .error }}"
|
||||||
"using_default_value" = "Используется значение по умолчанию👌"
|
"using_default_value" = "Используется значение по умолчанию👌"
|
||||||
|
@ -674,7 +676,7 @@
|
||||||
"confirmToggle" = "✅ Подтвердить вкл/выкл пользователя?"
|
"confirmToggle" = "✅ Подтвердить вкл/выкл пользователя?"
|
||||||
"dbBackup" = "📂 Бэкап БД"
|
"dbBackup" = "📂 Бэкап БД"
|
||||||
"serverUsage" = "💻 Состояние сервера"
|
"serverUsage" = "💻 Состояние сервера"
|
||||||
"getInbounds" = "🔌 Подключения"
|
"getInbounds" = "🔌 Инбаунды"
|
||||||
"depleteSoon" = "⚠️ Скоро конец"
|
"depleteSoon" = "⚠️ Скоро конец"
|
||||||
"clientUsage" = "Статистика клиента"
|
"clientUsage" = "Статистика клиента"
|
||||||
"onlines" = "🟢 Онлайн"
|
"onlines" = "🟢 Онлайн"
|
||||||
|
@ -712,7 +714,7 @@
|
||||||
[tgbot.answers]
|
[tgbot.answers]
|
||||||
"successfulOperation" = "✅ Успешно!"
|
"successfulOperation" = "✅ Успешно!"
|
||||||
"errorOperation" = "❗ Ошибка в операции."
|
"errorOperation" = "❗ Ошибка в операции."
|
||||||
"getInboundsFailed" = "❌ Не удалось получить подключения."
|
"getInboundsFailed" = "❌ Не удалось получить инбаунды."
|
||||||
"getClientsFailed" = "❌ Не удалось получить клиентов."
|
"getClientsFailed" = "❌ Не удалось получить клиентов."
|
||||||
"canceled" = "❌ {{ .Email }}: Операция отменена."
|
"canceled" = "❌ {{ .Email }}: Операция отменена."
|
||||||
"clientRefreshSuccess" = "✅ {{ .Email }}: Клиент успешно обновлен."
|
"clientRefreshSuccess" = "✅ {{ .Email }}: Клиент успешно обновлен."
|
||||||
|
@ -729,5 +731,5 @@
|
||||||
"enableSuccess" = "✅ {{ .Email }}: Включено успешно."
|
"enableSuccess" = "✅ {{ .Email }}: Включено успешно."
|
||||||
"disableSuccess" = "✅ {{ .Email }}: Отключено успешно."
|
"disableSuccess" = "✅ {{ .Email }}: Отключено успешно."
|
||||||
"askToAddUserId" = "❌ Ваша конфигурация не найдена!\r\n💭 Пожалуйста, попросите администратора использовать ваш Telegram User ID в конфигурации.\r\n\r\n🆔 Ваш User ID: <code>{{ .TgUserID }}</code>"
|
"askToAddUserId" = "❌ Ваша конфигурация не найдена!\r\n💭 Пожалуйста, попросите администратора использовать ваш Telegram User ID в конфигурации.\r\n\r\n🆔 Ваш User ID: <code>{{ .TgUserID }}</code>"
|
||||||
"chooseClient" = "Выберите клиента для подключения {{ .Inbound }}"
|
"chooseClient" = "Выберите клиента для инбаунда {{ .Inbound }}"
|
||||||
"chooseInbound" = "Выберите подключение"
|
"chooseInbound" = "Выберите инбаунд"
|
||||||
|
|
|
@ -402,7 +402,6 @@
|
||||||
"generalConfigsDesc" = "Bu seçenekler genel ayarlamaları belirler."
|
"generalConfigsDesc" = "Bu seçenekler genel ayarlamaları belirler."
|
||||||
"logConfigs" = "Günlük"
|
"logConfigs" = "Günlük"
|
||||||
"logConfigsDesc" = "Günlükler sunucunuzun verimliliğini etkileyebilir. Yalnızca ihtiyaç durumunda akıllıca etkinleştirmeniz önerilir"
|
"logConfigsDesc" = "Günlükler sunucunuzun verimliliğini etkileyebilir. Yalnızca ihtiyaç durumunda akıllıca etkinleştirmeniz önerilir"
|
||||||
"blockConfigs" = "Koruma Kalkanı"
|
|
||||||
"blockConfigsDesc" = "Bu seçenekler belirli istek protokolleri ve web siteleri temelinde trafiği engeller."
|
"blockConfigsDesc" = "Bu seçenekler belirli istek protokolleri ve web siteleri temelinde trafiği engeller."
|
||||||
"basicRouting" = "Temel Yönlendirme"
|
"basicRouting" = "Temel Yönlendirme"
|
||||||
"blockConnectionsConfigsDesc" = "Bu seçenekler belirli bir istenen ülkeye göre trafiği engelleyecektir."
|
"blockConnectionsConfigsDesc" = "Bu seçenekler belirli bir istenen ülkeye göre trafiği engelleyecektir."
|
||||||
|
@ -422,9 +421,6 @@
|
||||||
"RoutingStrategy" = "Genel Yönlendirme Stratejisi"
|
"RoutingStrategy" = "Genel Yönlendirme Stratejisi"
|
||||||
"RoutingStrategyDesc" = "Tüm istekleri çözmek için genel trafik yönlendirme stratejisini ayarlayın."
|
"RoutingStrategyDesc" = "Tüm istekleri çözmek için genel trafik yönlendirme stratejisini ayarlayın."
|
||||||
"Torrent" = "BitTorrent Protokolünü Engelle"
|
"Torrent" = "BitTorrent Protokolünü Engelle"
|
||||||
"TorrentDesc" = "BitTorrent protokolünü engeller."
|
|
||||||
"Family" = "Aile Koruması"
|
|
||||||
"FamilyDesc" = "Yetişkin içerikli ve kötü amaçlı yazılım web sitelerini engeller."
|
|
||||||
"Inbounds" = "Gelenler"
|
"Inbounds" = "Gelenler"
|
||||||
"InboundsDesc" = "Belirli müşterileri kabul eder."
|
"InboundsDesc" = "Belirli müşterileri kabul eder."
|
||||||
"Outbounds" = "Gidenler"
|
"Outbounds" = "Gidenler"
|
||||||
|
@ -524,6 +520,12 @@
|
||||||
"edit" = "Sunucuyu Düzenle"
|
"edit" = "Sunucuyu Düzenle"
|
||||||
"domains" = "Alan Adları"
|
"domains" = "Alan Adları"
|
||||||
"expectIPs" = "Beklenen IP'ler"
|
"expectIPs" = "Beklenen IP'ler"
|
||||||
|
"unexpectIPs" = "Beklenmeyen IP'ler"
|
||||||
|
"useSystemHosts" = "Sistem Hosts'larını Kullan"
|
||||||
|
"useSystemHostsDesc" = "Yüklü bir sistemden hosts dosyasını kullan"
|
||||||
|
"usePreset" = "Şablon kullan"
|
||||||
|
"dnsPresetTitle" = "DNS Şablonları"
|
||||||
|
"dnsPresetFamily" = "Aile"
|
||||||
|
|
||||||
[pages.xray.fakedns]
|
[pages.xray.fakedns]
|
||||||
"add" = "Sahte DNS Ekle"
|
"add" = "Sahte DNS Ekle"
|
||||||
|
|
|
@ -402,7 +402,6 @@
|
||||||
"generalConfigsDesc" = "Ці параметри визначатимуть загальні налаштування."
|
"generalConfigsDesc" = "Ці параметри визначатимуть загальні налаштування."
|
||||||
"logConfigs" = "Журнал"
|
"logConfigs" = "Журнал"
|
||||||
"logConfigsDesc" = "Журнали можуть вплинути на ефективність вашого сервера. Рекомендується вмикати його з розумом лише у випадку ваших потреб"
|
"logConfigsDesc" = "Журнали можуть вплинути на ефективність вашого сервера. Рекомендується вмикати його з розумом лише у випадку ваших потреб"
|
||||||
"blockConfigs" = "Захисний екран"
|
|
||||||
"blockConfigsDesc" = "Ці параметри блокуватимуть трафік на основі конкретних запитуваних протоколів і веб-сайтів."
|
"blockConfigsDesc" = "Ці параметри блокуватимуть трафік на основі конкретних запитуваних протоколів і веб-сайтів."
|
||||||
"basicRouting" = "Основна Маршрутизація"
|
"basicRouting" = "Основна Маршрутизація"
|
||||||
"blockConnectionsConfigsDesc" = "Ці параметри блокуватимуть трафік на основі запитаних країн."
|
"blockConnectionsConfigsDesc" = "Ці параметри блокуватимуть трафік на основі запитаних країн."
|
||||||
|
@ -422,9 +421,6 @@
|
||||||
"RoutingStrategy" = "Загальна стратегія маршрутизації"
|
"RoutingStrategy" = "Загальна стратегія маршрутизації"
|
||||||
"RoutingStrategyDesc" = "Установити загальну стратегію маршрутизації трафіку для вирішення всіх запитів."
|
"RoutingStrategyDesc" = "Установити загальну стратегію маршрутизації трафіку для вирішення всіх запитів."
|
||||||
"Torrent" = "Блокувати протокол BitTorrent"
|
"Torrent" = "Блокувати протокол BitTorrent"
|
||||||
"TorrentDesc" = "Блокує протокол BitTorrent."
|
|
||||||
"Family" = "Захист сім'ї"
|
|
||||||
"FamilyDesc" = "Блокує вміст для дорослих і веб-сайти з шкідливими програмами."
|
|
||||||
"Inbounds" = "Вхідні"
|
"Inbounds" = "Вхідні"
|
||||||
"InboundsDesc" = "Прийняття певних клієнтів."
|
"InboundsDesc" = "Прийняття певних клієнтів."
|
||||||
"Outbounds" = "Вихід"
|
"Outbounds" = "Вихід"
|
||||||
|
@ -524,6 +520,12 @@
|
||||||
"edit" = "Редагувати сервер"
|
"edit" = "Редагувати сервер"
|
||||||
"domains" = "Домени"
|
"domains" = "Домени"
|
||||||
"expectIPs" = "Очікувані IP"
|
"expectIPs" = "Очікувані IP"
|
||||||
|
"unexpectIPs" = "Неочікувані IP"
|
||||||
|
"useSystemHosts" = "Використовувати системні Hosts"
|
||||||
|
"useSystemHostsDesc" = "Використовувати файл hosts з встановленої системи"
|
||||||
|
"usePreset" = "Використати шаблон"
|
||||||
|
"dnsPresetTitle" = "Шаблони DNS"
|
||||||
|
"dnsPresetFamily" = "Сімейний"
|
||||||
|
|
||||||
[pages.xray.fakedns]
|
[pages.xray.fakedns]
|
||||||
"add" = "Додати підроблений DNS"
|
"add" = "Додати підроблений DNS"
|
||||||
|
|
|
@ -402,7 +402,6 @@
|
||||||
"generalConfigsDesc" = "Những tùy chọn này sẽ cung cấp điều chỉnh tổng quát."
|
"generalConfigsDesc" = "Những tùy chọn này sẽ cung cấp điều chỉnh tổng quát."
|
||||||
"logConfigs" = "Nhật ký"
|
"logConfigs" = "Nhật ký"
|
||||||
"logConfigsDesc" = "Nhật ký có thể ảnh hưởng đến hiệu suất máy chủ của bạn. Bạn chỉ nên kích hoạt nó một cách khôn ngoan trong trường hợp bạn cần"
|
"logConfigsDesc" = "Nhật ký có thể ảnh hưởng đến hiệu suất máy chủ của bạn. Bạn chỉ nên kích hoạt nó một cách khôn ngoan trong trường hợp bạn cần"
|
||||||
"blockConfigs" = "Cấu hình Chặn"
|
|
||||||
"blockConfigsDesc" = "Những tùy chọn này sẽ ngăn người dùng kết nối đến các giao thức và trang web cụ thể."
|
"blockConfigsDesc" = "Những tùy chọn này sẽ ngăn người dùng kết nối đến các giao thức và trang web cụ thể."
|
||||||
"basicRouting" = "Định tuyến Cơ bản"
|
"basicRouting" = "Định tuyến Cơ bản"
|
||||||
"blockConnectionsConfigsDesc" = "Các tùy chọn này sẽ chặn lưu lượng truy cập dựa trên quốc gia được yêu cầu cụ thể."
|
"blockConnectionsConfigsDesc" = "Các tùy chọn này sẽ chặn lưu lượng truy cập dựa trên quốc gia được yêu cầu cụ thể."
|
||||||
|
@ -422,9 +421,6 @@
|
||||||
"RoutingStrategy" = "Cấu hình Chiến lược Định tuyến Tên miền"
|
"RoutingStrategy" = "Cấu hình Chiến lược Định tuyến Tên miền"
|
||||||
"RoutingStrategyDesc" = "Đặt chiến lược định tuyến tổng thể cho việc giải quyết DNS."
|
"RoutingStrategyDesc" = "Đặt chiến lược định tuyến tổng thể cho việc giải quyết DNS."
|
||||||
"Torrent" = "Cấu hình sử dụng BitTorrent"
|
"Torrent" = "Cấu hình sử dụng BitTorrent"
|
||||||
"TorrentDesc" = "Thay đổi mẫu cấu hình để tránh việc người dùng sử dụng BitTorrent."
|
|
||||||
"Family" = "Chặn phần mềm độc hại và nội dung người lớn"
|
|
||||||
"FamilyDesc" = "Trình phân giải DNS của Cloudflare để chặn phần mềm độc hại và nội dung người lớn để bảo vệ gia đình."
|
|
||||||
"Inbounds" = "Đầu vào"
|
"Inbounds" = "Đầu vào"
|
||||||
"InboundsDesc" = "Thay đổi mẫu cấu hình để chấp nhận các máy khách cụ thể."
|
"InboundsDesc" = "Thay đổi mẫu cấu hình để chấp nhận các máy khách cụ thể."
|
||||||
"Outbounds" = "Đầu ra"
|
"Outbounds" = "Đầu ra"
|
||||||
|
@ -524,6 +520,12 @@
|
||||||
"edit" = "Chỉnh sửa máy chủ"
|
"edit" = "Chỉnh sửa máy chủ"
|
||||||
"domains" = "Tên miền"
|
"domains" = "Tên miền"
|
||||||
"expectIPs" = "Các IP Dự Kiến"
|
"expectIPs" = "Các IP Dự Kiến"
|
||||||
|
"unexpectIPs" = "IP không mong muốn"
|
||||||
|
"useSystemHosts" = "Sử dụng Hosts hệ thống"
|
||||||
|
"useSystemHostsDesc" = "Sử dụng file hosts từ hệ thống đã cài đặt"
|
||||||
|
"usePreset" = "Dùng mẫu"
|
||||||
|
"dnsPresetTitle" = "Mẫu DNS"
|
||||||
|
"dnsPresetFamily" = "Gia đình"
|
||||||
|
|
||||||
[pages.xray.fakedns]
|
[pages.xray.fakedns]
|
||||||
"add" = "Thêm DNS giả"
|
"add" = "Thêm DNS giả"
|
||||||
|
|
|
@ -402,7 +402,6 @@
|
||||||
"generalConfigsDesc" = "这些选项将决定常规配置"
|
"generalConfigsDesc" = "这些选项将决定常规配置"
|
||||||
"logConfigs" = "日志"
|
"logConfigs" = "日志"
|
||||||
"logConfigsDesc" = "日志可能会影响服务器的性能,建议仅在需要时启用"
|
"logConfigsDesc" = "日志可能会影响服务器的性能,建议仅在需要时启用"
|
||||||
"blockConfigs" = "防护屏蔽"
|
|
||||||
"blockConfigsDesc" = "这些选项将阻止用户连接到特定协议和网站"
|
"blockConfigsDesc" = "这些选项将阻止用户连接到特定协议和网站"
|
||||||
"basicRouting" = "基本路由"
|
"basicRouting" = "基本路由"
|
||||||
"blockConnectionsConfigsDesc" = "这些选项将根据特定的请求国家阻止流量。"
|
"blockConnectionsConfigsDesc" = "这些选项将根据特定的请求国家阻止流量。"
|
||||||
|
@ -422,9 +421,6 @@
|
||||||
"RoutingStrategy" = "配置路由域策略"
|
"RoutingStrategy" = "配置路由域策略"
|
||||||
"RoutingStrategyDesc" = "设置 DNS 解析的整体路由策略"
|
"RoutingStrategyDesc" = "设置 DNS 解析的整体路由策略"
|
||||||
"Torrent" = "屏蔽 BitTorrent 协议"
|
"Torrent" = "屏蔽 BitTorrent 协议"
|
||||||
"TorrentDesc" = "禁止使用 BitTorrent"
|
|
||||||
"Family" = "家庭保护"
|
|
||||||
"FamilyDesc" = "屏蔽成人内容和恶意网站"
|
|
||||||
"Inbounds" = "入站规则"
|
"Inbounds" = "入站规则"
|
||||||
"InboundsDesc" = "接受来自特定客户端的流量"
|
"InboundsDesc" = "接受来自特定客户端的流量"
|
||||||
"Outbounds" = "出站规则"
|
"Outbounds" = "出站规则"
|
||||||
|
@ -524,6 +520,12 @@
|
||||||
"edit" = "编辑服务器"
|
"edit" = "编辑服务器"
|
||||||
"domains" = "域"
|
"domains" = "域"
|
||||||
"expectIPs" = "预期 IP"
|
"expectIPs" = "预期 IP"
|
||||||
|
"unexpectIPs" = "意外IP"
|
||||||
|
"useSystemHosts" = "使用系统Hosts"
|
||||||
|
"useSystemHostsDesc" = "使用已安装系统的hosts文件"
|
||||||
|
"usePreset" = "使用模板"
|
||||||
|
"dnsPresetTitle" = "DNS模板"
|
||||||
|
"dnsPresetFamily" = "家庭"
|
||||||
|
|
||||||
[pages.xray.fakedns]
|
[pages.xray.fakedns]
|
||||||
"add" = "添加假 DNS"
|
"add" = "添加假 DNS"
|
||||||
|
|
|
@ -404,7 +404,6 @@
|
||||||
"generalConfigsDesc" = "這些選項將決定常規配置"
|
"generalConfigsDesc" = "這些選項將決定常規配置"
|
||||||
"logConfigs" = "日誌"
|
"logConfigs" = "日誌"
|
||||||
"logConfigsDesc" = "日誌可能會影響伺服器的效能,建議僅在需要時啟用"
|
"logConfigsDesc" = "日誌可能會影響伺服器的效能,建議僅在需要時啟用"
|
||||||
"blockConfigs" = "防護遮蔽"
|
|
||||||
"blockConfigsDesc" = "這些選項將阻止使用者連線到特定協議和網站"
|
"blockConfigsDesc" = "這些選項將阻止使用者連線到特定協議和網站"
|
||||||
"basicRouting" = "基本路由"
|
"basicRouting" = "基本路由"
|
||||||
"blockConnectionsConfigsDesc" = "這些選項將根據特定的請求國家阻止流量。"
|
"blockConnectionsConfigsDesc" = "這些選項將根據特定的請求國家阻止流量。"
|
||||||
|
@ -424,9 +423,6 @@
|
||||||
"RoutingStrategy" = "配置路由域策略"
|
"RoutingStrategy" = "配置路由域策略"
|
||||||
"RoutingStrategyDesc" = "設定 DNS 解析的整體路由策略"
|
"RoutingStrategyDesc" = "設定 DNS 解析的整體路由策略"
|
||||||
"Torrent" = "遮蔽 BitTorrent 協議"
|
"Torrent" = "遮蔽 BitTorrent 協議"
|
||||||
"TorrentDesc" = "禁止使用 BitTorrent"
|
|
||||||
"Family" = "家庭保護"
|
|
||||||
"FamilyDesc" = "遮蔽成人內容和惡意網站"
|
|
||||||
"Inbounds" = "入站規則"
|
"Inbounds" = "入站規則"
|
||||||
"InboundsDesc" = "接受來自特定客戶端的流量"
|
"InboundsDesc" = "接受來自特定客戶端的流量"
|
||||||
"Outbounds" = "出站規則"
|
"Outbounds" = "出站規則"
|
||||||
|
@ -526,6 +522,12 @@
|
||||||
"edit" = "編輯伺服器"
|
"edit" = "編輯伺服器"
|
||||||
"domains" = "域"
|
"domains" = "域"
|
||||||
"expectIPs" = "預期 IP"
|
"expectIPs" = "預期 IP"
|
||||||
|
"unexpectIPs" = "意外IP"
|
||||||
|
"useSystemHosts" = "使用系統Hosts"
|
||||||
|
"useSystemHostsDesc" = "使用已安裝系統的hosts檔案"
|
||||||
|
"usePreset" = "使用範本"
|
||||||
|
"dnsPresetTitle" = "DNS範本"
|
||||||
|
"dnsPresetFamily" = "家庭"
|
||||||
|
|
||||||
[pages.xray.fakedns]
|
[pages.xray.fakedns]
|
||||||
"add" = "新增假 DNS"
|
"add" = "新增假 DNS"
|
||||||
|
|
8
x-ui.sh
8
x-ui.sh
|
@ -474,7 +474,7 @@ enable_bbr() {
|
||||||
ubuntu | debian | armbian)
|
ubuntu | debian | armbian)
|
||||||
apt-get update && apt-get install -yqq --no-install-recommends ca-certificates
|
apt-get update && apt-get install -yqq --no-install-recommends ca-certificates
|
||||||
;;
|
;;
|
||||||
centos | almalinux | rocky | ol)
|
centos | rhel | almalinux | rocky | ol)
|
||||||
yum -y update && yum -y install ca-certificates
|
yum -y update && yum -y install ca-certificates
|
||||||
;;
|
;;
|
||||||
fedora | amzn | virtuozzo)
|
fedora | amzn | virtuozzo)
|
||||||
|
@ -995,7 +995,7 @@ ssl_cert_issue() {
|
||||||
ubuntu | debian | armbian)
|
ubuntu | debian | armbian)
|
||||||
apt update && apt install socat -y
|
apt update && apt install socat -y
|
||||||
;;
|
;;
|
||||||
centos | almalinux | rocky | ol)
|
centos | rhel | almalinux | rocky | ol)
|
||||||
yum -y update && yum -y install socat
|
yum -y update && yum -y install socat
|
||||||
;;
|
;;
|
||||||
fedora | amzn | virtuozzo)
|
fedora | amzn | virtuozzo)
|
||||||
|
@ -1511,7 +1511,7 @@ install_iplimit() {
|
||||||
debian | armbian)
|
debian | armbian)
|
||||||
apt update && apt install fail2ban -y
|
apt update && apt install fail2ban -y
|
||||||
;;
|
;;
|
||||||
centos | almalinux | rocky | ol)
|
centos | rhel | almalinux | rocky | ol)
|
||||||
yum update -y && yum install epel-release -y
|
yum update -y && yum install epel-release -y
|
||||||
yum -y install fail2ban
|
yum -y install fail2ban
|
||||||
;;
|
;;
|
||||||
|
@ -1591,7 +1591,7 @@ remove_iplimit() {
|
||||||
apt-get purge -y fail2ban -y
|
apt-get purge -y fail2ban -y
|
||||||
apt-get autoremove -y
|
apt-get autoremove -y
|
||||||
;;
|
;;
|
||||||
centos | almalinux | rocky | ol)
|
centos | rhel | almalinux | rocky | ol)
|
||||||
yum remove fail2ban -y
|
yum remove fail2ban -y
|
||||||
yum autoremove -y
|
yum autoremove -y
|
||||||
;;
|
;;
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
"time"
|
"time"
|
||||||
|
"math"
|
||||||
|
|
||||||
"x-ui/logger"
|
"x-ui/logger"
|
||||||
"x-ui/util/common"
|
"x-ui/util/common"
|
||||||
|
@ -32,7 +33,7 @@ type XrayAPI struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *XrayAPI) Init(apiPort int) error {
|
func (x *XrayAPI) Init(apiPort int) error {
|
||||||
if apiPort <= 0 {
|
if apiPort <= 0 || apiPort > math.MaxUint16 {
|
||||||
return fmt.Errorf("invalid Xray API port: %d", apiPort)
|
return fmt.Errorf("invalid Xray API port: %d", apiPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ type Config struct {
|
||||||
FakeDNS json_util.RawMessage `json:"fakedns"`
|
FakeDNS json_util.RawMessage `json:"fakedns"`
|
||||||
Observatory json_util.RawMessage `json:"observatory"`
|
Observatory json_util.RawMessage `json:"observatory"`
|
||||||
BurstObservatory json_util.RawMessage `json:"burstObservatory"`
|
BurstObservatory json_util.RawMessage `json:"burstObservatory"`
|
||||||
|
Metrics json_util.RawMessage `json:"metrics"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) Equals(other *Config) bool {
|
func (c *Config) Equals(other *Config) bool {
|
||||||
|
@ -61,5 +62,8 @@ func (c *Config) Equals(other *Config) bool {
|
||||||
if !bytes.Equal(c.FakeDNS, other.FakeDNS) {
|
if !bytes.Equal(c.FakeDNS, other.FakeDNS) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if !bytes.Equal(c.Metrics, other.Metrics) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue