mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-03-03 14:43:01 +00:00
Compare commits
15 commits
8d182f2c40
...
cd4be57191
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd4be57191 | ||
|
|
e8c509c720 | ||
|
|
83a1c721c7 | ||
|
|
7ccc0877a1 | ||
|
|
ad659e48cf | ||
|
|
3b262cf180 | ||
|
|
4c7249c451 | ||
|
|
edd8b12988 | ||
|
|
5e953bae45 | ||
|
|
747af376f2 | ||
|
|
a3ccccfe52 | ||
|
|
3299d15f28 | ||
|
|
ae82373457 | ||
|
|
d65233cc2c | ||
|
|
11dc06863e |
33 changed files with 989 additions and 352 deletions
|
|
@ -38,6 +38,7 @@ func initModels() error {
|
||||||
&model.InboundClientIps{},
|
&model.InboundClientIps{},
|
||||||
&xray.ClientTraffic{},
|
&xray.ClientTraffic{},
|
||||||
&model.HistoryOfSeeders{},
|
&model.HistoryOfSeeders{},
|
||||||
|
&model.Server{},
|
||||||
}
|
}
|
||||||
for _, model := range models {
|
for _, model := range models {
|
||||||
if err := db.AutoMigrate(model); err != nil {
|
if err := db.AutoMigrate(model); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -119,3 +119,12 @@ type Client struct {
|
||||||
CreatedAt int64 `json:"created_at,omitempty"` // Creation timestamp
|
CreatedAt int64 `json:"created_at,omitempty"` // Creation timestamp
|
||||||
UpdatedAt int64 `json:"updated_at,omitempty"` // Last update timestamp
|
UpdatedAt int64 `json:"updated_at,omitempty"` // Last update timestamp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
Id int `json:"id" gorm:"primaryKey;autoIncrement"`
|
||||||
|
Name string `json:"name" gorm:"unique;not null"`
|
||||||
|
Address string `json:"address" gorm:"not null"`
|
||||||
|
Port int `json:"port" gorm:"not null"`
|
||||||
|
APIKey string `json:"apiKey" gorm:"not null"`
|
||||||
|
Enable bool `json:"enable" gorm:"default:true"`
|
||||||
|
}
|
||||||
|
|
|
||||||
1
go.mod
1
go.mod
|
|
@ -68,6 +68,7 @@ require (
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/pires/go-proxyproto v0.8.1 // indirect
|
github.com/pires/go-proxyproto v0.8.1 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||||
github.com/quic-go/qpack v0.5.1 // indirect
|
github.com/quic-go/qpack v0.5.1 // indirect
|
||||||
github.com/quic-go/quic-go v0.56.0 // indirect
|
github.com/quic-go/quic-go v0.56.0 // indirect
|
||||||
|
|
|
||||||
97
install.sh
97
install.sh
|
|
@ -15,7 +15,7 @@ cur_dir=$(pwd)
|
||||||
if [[ -f /etc/os-release ]]; then
|
if [[ -f /etc/os-release ]]; then
|
||||||
source /etc/os-release
|
source /etc/os-release
|
||||||
release=$ID
|
release=$ID
|
||||||
elif [[ -f /usr/lib/os-release ]]; then
|
elif [[ -f /usr/lib/os-release ]]; then
|
||||||
source /usr/lib/os-release
|
source /usr/lib/os-release
|
||||||
release=$ID
|
release=$ID
|
||||||
else
|
else
|
||||||
|
|
@ -26,14 +26,14 @@ echo "The OS release is: $release"
|
||||||
|
|
||||||
arch() {
|
arch() {
|
||||||
case "$(uname -m)" in
|
case "$(uname -m)" in
|
||||||
x86_64 | x64 | amd64) echo 'amd64' ;;
|
x86_64 | x64 | amd64) echo 'amd64' ;;
|
||||||
i*86 | x86) echo '386' ;;
|
i*86 | x86) echo '386' ;;
|
||||||
armv8* | armv8 | arm64 | aarch64) echo 'arm64' ;;
|
armv8* | armv8 | arm64 | aarch64) echo 'arm64' ;;
|
||||||
armv7* | armv7 | arm) echo 'armv7' ;;
|
armv7* | armv7 | arm) echo 'armv7' ;;
|
||||||
armv6* | armv6) echo 'armv6' ;;
|
armv6* | armv6) echo 'armv6' ;;
|
||||||
armv5* | armv5) echo 'armv5' ;;
|
armv5* | armv5) echo 'armv5' ;;
|
||||||
s390x) echo 's390x' ;;
|
s390x) echo 's390x' ;;
|
||||||
*) echo -e "${green}Unsupported CPU architecture! ${plain}" && rm -f install.sh && exit 1 ;;
|
*) echo -e "${green}Unsupported CPU architecture! ${plain}" && rm -f install.sh && exit 1 ;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -41,26 +41,30 @@ echo "Arch: $(arch)"
|
||||||
|
|
||||||
install_base() {
|
install_base() {
|
||||||
case "${release}" in
|
case "${release}" in
|
||||||
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 | rhel | almalinux | rocky | ol)
|
fedora | amzn | virtuozzo | rhel | almalinux | rocky | ol)
|
||||||
yum -y update && yum install -y -q wget curl tar tzdata
|
dnf -y update && dnf install -y -q wget curl tar tzdata
|
||||||
;;
|
;;
|
||||||
fedora | amzn | virtuozzo)
|
centos)
|
||||||
dnf -y update && dnf install -y -q wget curl tar tzdata
|
if [[ "${VERSION_ID}" =~ ^7 ]]; then
|
||||||
|
yum -y update && yum install -y wget curl tar tzdata
|
||||||
|
else
|
||||||
|
dnf -y update && dnf install -y -q wget curl tar tzdata
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
arch | manjaro | parch)
|
arch | manjaro | parch)
|
||||||
pacman -Syu && pacman -Syu --noconfirm wget curl tar tzdata
|
pacman -Syu && pacman -Syu --noconfirm wget curl tar tzdata
|
||||||
;;
|
;;
|
||||||
opensuse-tumbleweed | opensuse-leap)
|
opensuse-tumbleweed | opensuse-leap)
|
||||||
zypper refresh && zypper -q install -y wget curl tar timezone
|
zypper refresh && zypper -q install -y wget curl tar timezone
|
||||||
;;
|
;;
|
||||||
alpine)
|
alpine)
|
||||||
apk update && apk add wget curl tar tzdata
|
apk update && apk add wget curl tar tzdata
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
apt-get update && apt-get install -y -q wget curl tar tzdata
|
apt-get update && apt-get install -y -q wget curl tar tzdata
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
@ -77,11 +81,11 @@ config_after_install() {
|
||||||
local existing_port=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'port: .+' | awk '{print $2}')
|
local existing_port=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'port: .+' | awk '{print $2}')
|
||||||
local URL_lists=(
|
local URL_lists=(
|
||||||
"https://api4.ipify.org"
|
"https://api4.ipify.org"
|
||||||
"https://ipv4.icanhazip.com"
|
"https://ipv4.icanhazip.com"
|
||||||
"https://v4.api.ipinfo.io/ip"
|
"https://v4.api.ipinfo.io/ip"
|
||||||
"https://ipv4.myexternalip.com/raw"
|
"https://ipv4.myexternalip.com/raw"
|
||||||
"https://4.ident.me"
|
"https://4.ident.me"
|
||||||
"https://check-host.net/ip"
|
"https://check-host.net/ip"
|
||||||
)
|
)
|
||||||
local server_ip=""
|
local server_ip=""
|
||||||
for ip_address in "${URL_lists[@]}"; do
|
for ip_address in "${URL_lists[@]}"; do
|
||||||
|
|
@ -90,13 +94,13 @@ config_after_install() {
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
if [[ ${#existing_webBasePath} -lt 4 ]]; then
|
if [[ ${#existing_webBasePath} -lt 4 ]]; then
|
||||||
if [[ "$existing_hasDefaultCredential" == "true" ]]; then
|
if [[ "$existing_hasDefaultCredential" == "true" ]]; then
|
||||||
local config_webBasePath=$(gen_random_string 18)
|
local config_webBasePath=$(gen_random_string 18)
|
||||||
local config_username=$(gen_random_string 10)
|
local config_username=$(gen_random_string 10)
|
||||||
local config_password=$(gen_random_string 10)
|
local config_password=$(gen_random_string 10)
|
||||||
|
|
||||||
read -rp "Would you like to customize the Panel Port settings? (If not, a random port will be applied) [y/n]: " config_confirm
|
read -rp "Would you like to customize the Panel Port settings? (If not, a random port will be applied) [y/n]: " config_confirm
|
||||||
if [[ "${config_confirm}" == "y" || "${config_confirm}" == "Y" ]]; then
|
if [[ "${config_confirm}" == "y" || "${config_confirm}" == "Y" ]]; then
|
||||||
read -rp "Please set up the panel port: " config_port
|
read -rp "Please set up the panel port: " config_port
|
||||||
|
|
@ -105,7 +109,7 @@ config_after_install() {
|
||||||
local config_port=$(shuf -i 1024-62000 -n 1)
|
local config_port=$(shuf -i 1024-62000 -n 1)
|
||||||
echo -e "${yellow}Generated random port: ${config_port}${plain}"
|
echo -e "${yellow}Generated random port: ${config_port}${plain}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
/usr/local/x-ui/x-ui setting -username "${config_username}" -password "${config_password}" -port "${config_port}" -webBasePath "${config_webBasePath}"
|
/usr/local/x-ui/x-ui setting -username "${config_username}" -password "${config_password}" -port "${config_port}" -webBasePath "${config_webBasePath}"
|
||||||
echo -e "This is a fresh installation, generating random login info for security concerns:"
|
echo -e "This is a fresh installation, generating random login info for security concerns:"
|
||||||
echo -e "###############################################"
|
echo -e "###############################################"
|
||||||
|
|
@ -126,7 +130,7 @@ config_after_install() {
|
||||||
if [[ "$existing_hasDefaultCredential" == "true" ]]; then
|
if [[ "$existing_hasDefaultCredential" == "true" ]]; then
|
||||||
local config_username=$(gen_random_string 10)
|
local config_username=$(gen_random_string 10)
|
||||||
local config_password=$(gen_random_string 10)
|
local config_password=$(gen_random_string 10)
|
||||||
|
|
||||||
echo -e "${yellow}Default credentials detected. Security update required...${plain}"
|
echo -e "${yellow}Default credentials detected. Security update required...${plain}"
|
||||||
/usr/local/x-ui/x-ui setting -username "${config_username}" -password "${config_password}"
|
/usr/local/x-ui/x-ui setting -username "${config_username}" -password "${config_password}"
|
||||||
echo -e "Generated new random login credentials:"
|
echo -e "Generated new random login credentials:"
|
||||||
|
|
@ -138,13 +142,20 @@ config_after_install() {
|
||||||
echo -e "${green}Username, Password, and WebBasePath are properly set. Exiting...${plain}"
|
echo -e "${green}Username, Password, and WebBasePath are properly set. Exiting...${plain}"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
/usr/local/x-ui/x-ui migrate
|
/usr/local/x-ui/x-ui migrate
|
||||||
|
|
||||||
|
local existing_apiKey=$(/usr/local/x-ui/x-ui setting -show true | grep -oP 'ApiKey: \K.*')
|
||||||
|
if [[ -z "$existing_apiKey" ]]; then
|
||||||
|
local config_apiKey=$(gen_random_string 32)
|
||||||
|
/usr/local/x-ui/x-ui setting -apiKey "${config_apiKey}"
|
||||||
|
echo -e "${green}Generated random API Key: ${config_apiKey}${plain}"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
install_x-ui() {
|
install_x-ui() {
|
||||||
cd /usr/local/
|
cd /usr/local/
|
||||||
|
|
||||||
# Download resources
|
# Download resources
|
||||||
if [ $# == 0 ]; then
|
if [ $# == 0 ]; then
|
||||||
tag_version=$(curl -Ls "https://api.github.com/repos/MHSanaei/3x-ui/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
|
tag_version=$(curl -Ls "https://api.github.com/repos/MHSanaei/3x-ui/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
|
||||||
|
|
@ -166,12 +177,12 @@ install_x-ui() {
|
||||||
tag_version=$1
|
tag_version=$1
|
||||||
tag_version_numeric=${tag_version#v}
|
tag_version_numeric=${tag_version#v}
|
||||||
min_version="2.3.5"
|
min_version="2.3.5"
|
||||||
|
|
||||||
if [[ "$(printf '%s\n' "$min_version" "$tag_version_numeric" | sort -V | head -n1)" != "$min_version" ]]; then
|
if [[ "$(printf '%s\n' "$min_version" "$tag_version_numeric" | sort -V | head -n1)" != "$min_version" ]]; then
|
||||||
echo -e "${red}Please use a newer version (at least v2.3.5). Exiting installation.${plain}"
|
echo -e "${red}Please use a newer version (at least v2.3.5). Exiting installation.${plain}"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
url="https://github.com/MHSanaei/3x-ui/releases/download/${tag_version}/x-ui-linux-$(arch).tar.gz"
|
url="https://github.com/MHSanaei/3x-ui/releases/download/${tag_version}/x-ui-linux-$(arch).tar.gz"
|
||||||
echo -e "Beginning to install x-ui $1"
|
echo -e "Beginning to install x-ui $1"
|
||||||
wget --inet4-only -N -O /usr/local/x-ui-linux-$(arch).tar.gz ${url}
|
wget --inet4-only -N -O /usr/local/x-ui-linux-$(arch).tar.gz ${url}
|
||||||
|
|
@ -185,7 +196,7 @@ install_x-ui() {
|
||||||
echo -e "${red}Failed to download x-ui.sh${plain}"
|
echo -e "${red}Failed to download x-ui.sh${plain}"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Stop x-ui service and remove old resources
|
# Stop x-ui service and remove old resources
|
||||||
if [[ -e /usr/local/x-ui/ ]]; then
|
if [[ -e /usr/local/x-ui/ ]]; then
|
||||||
if [[ $release == "alpine" ]]; then
|
if [[ $release == "alpine" ]]; then
|
||||||
|
|
@ -195,7 +206,7 @@ install_x-ui() {
|
||||||
fi
|
fi
|
||||||
rm /usr/local/x-ui/ -rf
|
rm /usr/local/x-ui/ -rf
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Extract resources and set permissions
|
# Extract resources and set permissions
|
||||||
tar zxvf x-ui-linux-$(arch).tar.gz
|
tar zxvf x-ui-linux-$(arch).tar.gz
|
||||||
rm x-ui-linux-$(arch).tar.gz -f
|
rm x-ui-linux-$(arch).tar.gz -f
|
||||||
|
|
@ -203,19 +214,19 @@ install_x-ui() {
|
||||||
cd x-ui
|
cd x-ui
|
||||||
chmod +x x-ui
|
chmod +x x-ui
|
||||||
chmod +x x-ui.sh
|
chmod +x x-ui.sh
|
||||||
|
|
||||||
# Check the system's architecture and rename the file accordingly
|
# Check the system's architecture and rename the file accordingly
|
||||||
if [[ $(arch) == "armv5" || $(arch) == "armv6" || $(arch) == "armv7" ]]; then
|
if [[ $(arch) == "armv5" || $(arch) == "armv6" || $(arch) == "armv7" ]]; then
|
||||||
mv bin/xray-linux-$(arch) bin/xray-linux-arm
|
mv bin/xray-linux-$(arch) bin/xray-linux-arm
|
||||||
chmod +x bin/xray-linux-arm
|
chmod +x bin/xray-linux-arm
|
||||||
fi
|
fi
|
||||||
chmod +x x-ui bin/xray-linux-$(arch)
|
chmod +x x-ui bin/xray-linux-$(arch)
|
||||||
|
|
||||||
# Update x-ui cli and se set permission
|
# Update x-ui cli and se set permission
|
||||||
mv -f /usr/bin/x-ui-temp /usr/bin/x-ui
|
mv -f /usr/bin/x-ui-temp /usr/bin/x-ui
|
||||||
chmod +x /usr/bin/x-ui
|
chmod +x /usr/bin/x-ui
|
||||||
config_after_install
|
config_after_install
|
||||||
|
|
||||||
if [[ $release == "alpine" ]]; then
|
if [[ $release == "alpine" ]]; then
|
||||||
wget --inet4-only -O /etc/init.d/x-ui https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.rc
|
wget --inet4-only -O /etc/init.d/x-ui https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.rc
|
||||||
if [[ $? -ne 0 ]]; then
|
if [[ $? -ne 0 ]]; then
|
||||||
|
|
@ -231,7 +242,7 @@ install_x-ui() {
|
||||||
systemctl enable x-ui
|
systemctl enable x-ui
|
||||||
systemctl start x-ui
|
systemctl start x-ui
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo -e "${green}x-ui ${tag_version}${plain} installation finished, it is running now..."
|
echo -e "${green}x-ui ${tag_version}${plain} installation finished, it is running now..."
|
||||||
echo -e ""
|
echo -e ""
|
||||||
echo -e "┌───────────────────────────────────────────────────────┐
|
echo -e "┌───────────────────────────────────────────────────────┐
|
||||||
|
|
@ -251,7 +262,7 @@ install_x-ui() {
|
||||||
│ ${blue}x-ui legacy${plain} - Legacy version │
|
│ ${blue}x-ui legacy${plain} - Legacy version │
|
||||||
│ ${blue}x-ui install${plain} - Install │
|
│ ${blue}x-ui install${plain} - Install │
|
||||||
│ ${blue}x-ui uninstall${plain} - Uninstall │
|
│ ${blue}x-ui uninstall${plain} - Uninstall │
|
||||||
└───────────────────────────────────────────────────────┘"
|
└───────────────────────────────────────────────────────┘"
|
||||||
}
|
}
|
||||||
|
|
||||||
echo -e "${green}Running...${plain}"
|
echo -e "${green}Running...${plain}"
|
||||||
|
|
|
||||||
16
main.go
16
main.go
|
|
@ -248,7 +248,8 @@ func updateTgbotSetting(tgBotToken string, tgBotChatid string, tgBotRuntime stri
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateSetting updates various panel settings including port, credentials, base path, listen IP, and two-factor authentication.
|
// updateSetting updates various panel settings including port, credentials, base path, listen IP, and two-factor authentication.
|
||||||
func updateSetting(port int, username string, password string, webBasePath string, listenIP string, resetTwoFactor bool) {
|
func updateSetting(port int, username string, password string, webBasePath string, listenIP string, resetTwoFactor bool, apiKey string) {
|
||||||
|
|
||||||
err := database.InitDB(config.GetDBPath())
|
err := database.InitDB(config.GetDBPath())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Database initialization failed:", err)
|
fmt.Println("Database initialization failed:", err)
|
||||||
|
|
@ -258,6 +259,15 @@ func updateSetting(port int, username string, password string, webBasePath strin
|
||||||
settingService := service.SettingService{}
|
settingService := service.SettingService{}
|
||||||
userService := service.UserService{}
|
userService := service.UserService{}
|
||||||
|
|
||||||
|
if apiKey != "" {
|
||||||
|
err := settingService.SetAPIKey(apiKey)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Failed to set API Key:", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("API Key set successfully: %v\n", apiKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if port > 0 {
|
if port > 0 {
|
||||||
err := settingService.SetPort(port)
|
err := settingService.SetPort(port)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -424,9 +434,11 @@ func main() {
|
||||||
var show bool
|
var show bool
|
||||||
var getCert bool
|
var getCert bool
|
||||||
var resetTwoFactor bool
|
var resetTwoFactor bool
|
||||||
|
var apiKey string
|
||||||
settingCmd.BoolVar(&reset, "reset", false, "Reset all settings")
|
settingCmd.BoolVar(&reset, "reset", false, "Reset all settings")
|
||||||
settingCmd.BoolVar(&show, "show", false, "Display current settings")
|
settingCmd.BoolVar(&show, "show", false, "Display current settings")
|
||||||
settingCmd.IntVar(&port, "port", 0, "Set panel port number")
|
settingCmd.IntVar(&port, "port", 0, "Set panel port number")
|
||||||
|
settingCmd.StringVar(&apiKey, "apiKey", "", "Set API Key")
|
||||||
settingCmd.StringVar(&username, "username", "", "Set login username")
|
settingCmd.StringVar(&username, "username", "", "Set login username")
|
||||||
settingCmd.StringVar(&password, "password", "", "Set login password")
|
settingCmd.StringVar(&password, "password", "", "Set login password")
|
||||||
settingCmd.StringVar(&webBasePath, "webBasePath", "", "Set base path for Panel")
|
settingCmd.StringVar(&webBasePath, "webBasePath", "", "Set base path for Panel")
|
||||||
|
|
@ -476,7 +488,7 @@ func main() {
|
||||||
if reset {
|
if reset {
|
||||||
resetSetting()
|
resetSetting()
|
||||||
} else {
|
} else {
|
||||||
updateSetting(port, username, password, webBasePath, listenIP, resetTwoFactor)
|
updateSetting(port, username, password, webBasePath, listenIP, resetTwoFactor, apiKey)
|
||||||
}
|
}
|
||||||
if show {
|
if show {
|
||||||
showSetting(show)
|
showSetting(show)
|
||||||
|
|
|
||||||
|
|
@ -162,26 +162,43 @@ func (s *SubService) getFallbackMaster(dest string, streamSettings string) (stri
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SubService) getLink(inbound *model.Inbound, email string) string {
|
func (s *SubService) getLink(inbound *model.Inbound, email string) string {
|
||||||
switch inbound.Protocol {
|
serverService := service.MultiServerService{}
|
||||||
case "vmess":
|
servers, err := serverService.GetServers()
|
||||||
return s.genVmessLink(inbound, email)
|
if err != nil {
|
||||||
case "vless":
|
logger.Warning("Failed to get servers for subscription:", err)
|
||||||
return s.genVlessLink(inbound, email)
|
return ""
|
||||||
case "trojan":
|
|
||||||
return s.genTrojanLink(inbound, email)
|
|
||||||
case "shadowsocks":
|
|
||||||
return s.genShadowsocksLink(inbound, email)
|
|
||||||
}
|
}
|
||||||
return ""
|
|
||||||
|
var links []string
|
||||||
|
for _, server := range servers {
|
||||||
|
if !server.Enable {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var link string
|
||||||
|
switch inbound.Protocol {
|
||||||
|
case "vmess":
|
||||||
|
link = s.genVmessLink(inbound, email, server)
|
||||||
|
case "vless":
|
||||||
|
link = s.genVlessLink(inbound, email, server)
|
||||||
|
case "trojan":
|
||||||
|
link = s.genTrojanLink(inbound, email, server)
|
||||||
|
case "shadowsocks":
|
||||||
|
link = s.genShadowsocksLink(inbound, email, server)
|
||||||
|
}
|
||||||
|
if link != "" {
|
||||||
|
links = append(links, link)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strings.Join(links, "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SubService) genVmessLink(inbound *model.Inbound, email string) string {
|
func (s *SubService) genVmessLink(inbound *model.Inbound, email string, server *model.Server) string {
|
||||||
if inbound.Protocol != model.VMESS {
|
if inbound.Protocol != model.VMESS {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
obj := map[string]any{
|
obj := map[string]any{
|
||||||
"v": "2",
|
"v": "2",
|
||||||
"add": s.address,
|
"add": server.Address,
|
||||||
"port": inbound.Port,
|
"port": inbound.Port,
|
||||||
"type": "none",
|
"type": "none",
|
||||||
}
|
}
|
||||||
|
|
@ -294,7 +311,7 @@ func (s *SubService) genVmessLink(inbound *model.Inbound, email string) string {
|
||||||
newObj[key] = value
|
newObj[key] = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
newObj["ps"] = s.genRemark(inbound, email, ep["remark"].(string))
|
newObj["ps"] = s.genRemark(inbound, email, ep["remark"].(string), server.Name)
|
||||||
newObj["add"] = ep["dest"].(string)
|
newObj["add"] = ep["dest"].(string)
|
||||||
newObj["port"] = int(ep["port"].(float64))
|
newObj["port"] = int(ep["port"].(float64))
|
||||||
|
|
||||||
|
|
@ -310,14 +327,14 @@ func (s *SubService) genVmessLink(inbound *model.Inbound, email string) string {
|
||||||
return links
|
return links
|
||||||
}
|
}
|
||||||
|
|
||||||
obj["ps"] = s.genRemark(inbound, email, "")
|
obj["ps"] = s.genRemark(inbound, email, "", server.Name)
|
||||||
|
|
||||||
jsonStr, _ := json.MarshalIndent(obj, "", " ")
|
jsonStr, _ := json.MarshalIndent(obj, "", " ")
|
||||||
return "vmess://" + base64.StdEncoding.EncodeToString(jsonStr)
|
return "vmess://" + base64.StdEncoding.EncodeToString(jsonStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
|
func (s *SubService) genVlessLink(inbound *model.Inbound, email string, server *model.Server) string {
|
||||||
address := s.address
|
address := server.Address
|
||||||
if inbound.Protocol != model.VLESS {
|
if inbound.Protocol != model.VLESS {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
@ -497,7 +514,7 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
|
||||||
// Set the new query values on the URL
|
// Set the new query values on the URL
|
||||||
url.RawQuery = q.Encode()
|
url.RawQuery = q.Encode()
|
||||||
|
|
||||||
url.Fragment = s.genRemark(inbound, email, ep["remark"].(string))
|
url.Fragment = s.genRemark(inbound, email, ep["remark"].(string), server.Name)
|
||||||
|
|
||||||
if index > 0 {
|
if index > 0 {
|
||||||
links += "\n"
|
links += "\n"
|
||||||
|
|
@ -518,12 +535,12 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
|
||||||
// Set the new query values on the URL
|
// Set the new query values on the URL
|
||||||
url.RawQuery = q.Encode()
|
url.RawQuery = q.Encode()
|
||||||
|
|
||||||
url.Fragment = s.genRemark(inbound, email, "")
|
url.Fragment = s.genRemark(inbound, email, "", server.Name)
|
||||||
return url.String()
|
return url.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string {
|
func (s *SubService) genTrojanLink(inbound *model.Inbound, email string, server *model.Server) string {
|
||||||
address := s.address
|
address := server.Address
|
||||||
if inbound.Protocol != model.Trojan {
|
if inbound.Protocol != model.Trojan {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
@ -692,7 +709,7 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string
|
||||||
// Set the new query values on the URL
|
// Set the new query values on the URL
|
||||||
url.RawQuery = q.Encode()
|
url.RawQuery = q.Encode()
|
||||||
|
|
||||||
url.Fragment = s.genRemark(inbound, email, ep["remark"].(string))
|
url.Fragment = s.genRemark(inbound, email, ep["remark"].(string), server.Name)
|
||||||
|
|
||||||
if index > 0 {
|
if index > 0 {
|
||||||
links += "\n"
|
links += "\n"
|
||||||
|
|
@ -714,12 +731,12 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string
|
||||||
// Set the new query values on the URL
|
// Set the new query values on the URL
|
||||||
url.RawQuery = q.Encode()
|
url.RawQuery = q.Encode()
|
||||||
|
|
||||||
url.Fragment = s.genRemark(inbound, email, "")
|
url.Fragment = s.genRemark(inbound, email, "", server.Name)
|
||||||
return url.String()
|
return url.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SubService) genShadowsocksLink(inbound *model.Inbound, email string) string {
|
func (s *SubService) genShadowsocksLink(inbound *model.Inbound, email string, server *model.Server) string {
|
||||||
address := s.address
|
address := server.Address
|
||||||
if inbound.Protocol != model.Shadowsocks {
|
if inbound.Protocol != model.Shadowsocks {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
@ -859,7 +876,7 @@ func (s *SubService) genShadowsocksLink(inbound *model.Inbound, email string) st
|
||||||
// Set the new query values on the URL
|
// Set the new query values on the URL
|
||||||
url.RawQuery = q.Encode()
|
url.RawQuery = q.Encode()
|
||||||
|
|
||||||
url.Fragment = s.genRemark(inbound, email, ep["remark"].(string))
|
url.Fragment = s.genRemark(inbound, email, ep["remark"].(string), server.Name)
|
||||||
|
|
||||||
if index > 0 {
|
if index > 0 {
|
||||||
links += "\n"
|
links += "\n"
|
||||||
|
|
@ -880,17 +897,18 @@ func (s *SubService) genShadowsocksLink(inbound *model.Inbound, email string) st
|
||||||
// Set the new query values on the URL
|
// Set the new query values on the URL
|
||||||
url.RawQuery = q.Encode()
|
url.RawQuery = q.Encode()
|
||||||
|
|
||||||
url.Fragment = s.genRemark(inbound, email, "")
|
url.Fragment = s.genRemark(inbound, email, "", server.Name)
|
||||||
return url.String()
|
return url.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SubService) genRemark(inbound *model.Inbound, email string, extra string) string {
|
func (s *SubService) genRemark(inbound *model.Inbound, email string, extra string, serverName string) string {
|
||||||
separationChar := string(s.remarkModel[0])
|
separationChar := string(s.remarkModel[0])
|
||||||
orderChars := s.remarkModel[1:]
|
orderChars := s.remarkModel[1:]
|
||||||
orders := map[byte]string{
|
orders := map[byte]string{
|
||||||
'i': "",
|
'i': "",
|
||||||
'e': "",
|
'e': "",
|
||||||
'o': "",
|
'o': "",
|
||||||
|
's': "",
|
||||||
}
|
}
|
||||||
if len(email) > 0 {
|
if len(email) > 0 {
|
||||||
orders['e'] = email
|
orders['e'] = email
|
||||||
|
|
@ -901,6 +919,9 @@ func (s *SubService) genRemark(inbound *model.Inbound, email string, extra strin
|
||||||
if len(extra) > 0 {
|
if len(extra) > 0 {
|
||||||
orders['o'] = extra
|
orders['o'] = extra
|
||||||
}
|
}
|
||||||
|
if len(serverName) > 0 {
|
||||||
|
orders['s'] = serverName
|
||||||
|
}
|
||||||
|
|
||||||
var remark []string
|
var remark []string
|
||||||
for i := 0; i < len(orderChars); i++ {
|
for i := 0; i < len(orderChars); i++ {
|
||||||
|
|
|
||||||
376
update.sh
376
update.sh
|
|
@ -9,233 +9,237 @@ plain='\033[0m'
|
||||||
# Don't edit this config
|
# Don't edit this config
|
||||||
b_source="${BASH_SOURCE[0]}"
|
b_source="${BASH_SOURCE[0]}"
|
||||||
while [ -h "$b_source" ]; do
|
while [ -h "$b_source" ]; do
|
||||||
b_dir="$(cd -P "$(dirname "$b_source")" >/dev/null 2>&1 && pwd || pwd -P)"
|
b_dir="$(cd -P "$(dirname "$b_source")" >/dev/null 2>&1 && pwd || pwd -P)"
|
||||||
b_source="$(readlink "$b_source")"
|
b_source="$(readlink "$b_source")"
|
||||||
[[ $b_source != /* ]] && b_source="$b_dir/$b_source"
|
[[ $b_source != /* ]] && b_source="$b_dir/$b_source"
|
||||||
done
|
done
|
||||||
cur_dir="$(cd -P "$(dirname "$b_source")" >/dev/null 2>&1 && pwd || pwd -P)"
|
cur_dir="$(cd -P "$(dirname "$b_source")" >/dev/null 2>&1 && pwd || pwd -P)"
|
||||||
script_name=$(basename "$0")
|
script_name=$(basename "$0")
|
||||||
|
|
||||||
# Check command exist function
|
# Check command exist function
|
||||||
_command_exists() {
|
_command_exists() {
|
||||||
type "$1" &>/dev/null
|
type "$1" &>/dev/null
|
||||||
}
|
}
|
||||||
|
|
||||||
# Fail, log and exit script function
|
# Fail, log and exit script function
|
||||||
_fail() {
|
_fail() {
|
||||||
local msg=${1}
|
local msg=${1}
|
||||||
echo -e "${red}${msg}${plain}"
|
echo -e "${red}${msg}${plain}"
|
||||||
exit 2
|
exit 2
|
||||||
}
|
}
|
||||||
|
|
||||||
# check root
|
# check root
|
||||||
[[ $EUID -ne 0 ]] && _fail "FATAL ERROR: Please run this script with root privilege."
|
[[ $EUID -ne 0 ]] && _fail "FATAL ERROR: Please run this script with root privilege."
|
||||||
|
|
||||||
if _command_exists wget; then
|
if _command_exists wget; then
|
||||||
wget_bin=$(which wget)
|
wget_bin=$(which wget)
|
||||||
else
|
else
|
||||||
_fail "ERROR: Command 'wget' not found."
|
_fail "ERROR: Command 'wget' not found."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if _command_exists curl; then
|
if _command_exists curl; then
|
||||||
curl_bin=$(which curl)
|
curl_bin=$(which curl)
|
||||||
else
|
else
|
||||||
_fail "ERROR: Command 'curl' not found."
|
_fail "ERROR: Command 'curl' not found."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check OS and set release variable
|
# Check OS and set release variable
|
||||||
if [[ -f /etc/os-release ]]; then
|
if [[ -f /etc/os-release ]]; then
|
||||||
source /etc/os-release
|
source /etc/os-release
|
||||||
release=$ID
|
release=$ID
|
||||||
elif [[ -f /usr/lib/os-release ]]; then
|
elif [[ -f /usr/lib/os-release ]]; then
|
||||||
source /usr/lib/os-release
|
source /usr/lib/os-release
|
||||||
release=$ID
|
release=$ID
|
||||||
else
|
else
|
||||||
_fail "Failed to check the system OS, please contact the author!"
|
_fail "Failed to check the system OS, please contact the author!"
|
||||||
fi
|
fi
|
||||||
echo "The OS release is: $release"
|
echo "The OS release is: $release"
|
||||||
|
|
||||||
arch() {
|
arch() {
|
||||||
case "$(uname -m)" in
|
case "$(uname -m)" in
|
||||||
x86_64 | x64 | amd64) echo 'amd64' ;;
|
x86_64 | x64 | amd64) echo 'amd64' ;;
|
||||||
i*86 | x86) echo '386' ;;
|
i*86 | x86) echo '386' ;;
|
||||||
armv8* | armv8 | arm64 | aarch64) echo 'arm64' ;;
|
armv8* | armv8 | arm64 | aarch64) echo 'arm64' ;;
|
||||||
armv7* | armv7 | arm) echo 'armv7' ;;
|
armv7* | armv7 | arm) echo 'armv7' ;;
|
||||||
armv6* | armv6) echo 'armv6' ;;
|
armv6* | armv6) echo 'armv6' ;;
|
||||||
armv5* | armv5) echo 'armv5' ;;
|
armv5* | armv5) echo 'armv5' ;;
|
||||||
s390x) echo 's390x' ;;
|
s390x) echo 's390x' ;;
|
||||||
*) echo -e "${red}Unsupported CPU architecture!${plain}" && rm -f "${cur_dir}/${script_name}" >/dev/null 2>&1 && exit 2;;
|
*) echo -e "${red}Unsupported CPU architecture!${plain}" && rm -f "${cur_dir}/${script_name}" >/dev/null 2>&1 && exit 2;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
echo "Arch: $(arch)"
|
echo "Arch: $(arch)"
|
||||||
|
|
||||||
install_base() {
|
install_base() {
|
||||||
echo -e "${green}Updating and install dependency packages...${plain}"
|
echo -e "${green}Updating and install dependency packages...${plain}"
|
||||||
case "${release}" in
|
case "${release}" in
|
||||||
ubuntu | debian | armbian)
|
ubuntu | debian | armbian)
|
||||||
apt-get update >/dev/null 2>&1 && apt-get install -y -q wget curl tar tzdata >/dev/null 2>&1
|
apt-get update >/dev/null 2>&1 && apt-get install -y -q wget curl tar tzdata >/dev/null 2>&1
|
||||||
;;
|
;;
|
||||||
centos | rhel | almalinux | rocky | ol)
|
fedora | amzn | virtuozzo | rhel | almalinux | rocky | ol)
|
||||||
yum -y update >/dev/null 2>&1 && yum install -y -q wget curl tar tzdata >/dev/null 2>&1
|
dnf -y update >/dev/null 2>&1 && dnf install -y -q wget curl tar tzdata >/dev/null 2>&1
|
||||||
;;
|
;;
|
||||||
fedora | amzn | virtuozzo)
|
centos)
|
||||||
dnf -y update >/dev/null 2>&1 && dnf install -y -q wget curl tar tzdata >/dev/null 2>&1
|
if [[ "${VERSION_ID}" =~ ^7 ]]; then
|
||||||
;;
|
yum -y update >/dev/null 2>&1 && yum install -y -q wget curl tar tzdata >/dev/null 2>&1
|
||||||
arch | manjaro | parch)
|
else
|
||||||
pacman -Syu >/dev/null 2>&1 && pacman -Syu --noconfirm wget curl tar tzdata >/dev/null 2>&1
|
dnf -y update >/dev/null 2>&1 && dnf install -y -q wget curl tar tzdata >/dev/null 2>&1
|
||||||
;;
|
fi
|
||||||
opensuse-tumbleweed | opensuse-leap)
|
;;
|
||||||
zypper refresh >/dev/null 2>&1 && zypper -q install -y wget curl tar timezone >/dev/null 2>&1
|
arch | manjaro | parch)
|
||||||
;;
|
pacman -Syu >/dev/null 2>&1 && pacman -Syu --noconfirm wget curl tar tzdata >/dev/null 2>&1
|
||||||
alpine)
|
;;
|
||||||
apk update >/dev/null 2>&1 && apk add wget curl tar tzdata >/dev/null 2>&1
|
opensuse-tumbleweed | opensuse-leap)
|
||||||
;;
|
zypper refresh >/dev/null 2>&1 && zypper -q install -y wget curl tar timezone >/dev/null 2>&1
|
||||||
*)
|
;;
|
||||||
apt-get update >/dev/null 2>&1 && apt install -y -q wget curl tar tzdata >/dev/null 2>&1
|
alpine)
|
||||||
;;
|
apk update >/dev/null 2>&1 && apk add wget curl tar tzdata >/dev/null 2>&1
|
||||||
esac
|
;;
|
||||||
|
*)
|
||||||
|
apt-get update >/dev/null 2>&1 && apt install -y -q wget curl tar tzdata >/dev/null 2>&1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
config_after_update() {
|
config_after_update() {
|
||||||
echo -e "${yellow}x-ui settings:${plain}"
|
echo -e "${yellow}x-ui settings:${plain}"
|
||||||
/usr/local/x-ui/x-ui setting -show true
|
/usr/local/x-ui/x-ui setting -show true
|
||||||
/usr/local/x-ui/x-ui migrate
|
/usr/local/x-ui/x-ui migrate
|
||||||
}
|
}
|
||||||
|
|
||||||
update_x-ui() {
|
update_x-ui() {
|
||||||
cd /usr/local/
|
cd /usr/local/
|
||||||
|
|
||||||
if [ -f "/usr/local/x-ui/x-ui" ]; then
|
if [ -f "/usr/local/x-ui/x-ui" ]; then
|
||||||
current_xui_version=$(/usr/local/x-ui/x-ui -v)
|
current_xui_version=$(/usr/local/x-ui/x-ui -v)
|
||||||
echo -e "${green}Current x-ui version: ${current_xui_version}${plain}"
|
echo -e "${green}Current x-ui version: ${current_xui_version}${plain}"
|
||||||
else
|
else
|
||||||
_fail "ERROR: Current x-ui version: unknown"
|
_fail "ERROR: Current x-ui version: unknown"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo -e "${green}Downloading new x-ui version...${plain}"
|
echo -e "${green}Downloading new x-ui version...${plain}"
|
||||||
|
|
||||||
tag_version=$(${curl_bin} -Ls "https://api.github.com/repos/MHSanaei/3x-ui/releases/latest" 2>/dev/null | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
|
tag_version=$(${curl_bin} -Ls "https://api.github.com/repos/MHSanaei/3x-ui/releases/latest" 2>/dev/null | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
|
||||||
if [[ ! -n "$tag_version" ]]; then
|
if [[ ! -n "$tag_version" ]]; then
|
||||||
echo -e "${yellow}Trying to fetch version with IPv4...${plain}"
|
echo -e "${yellow}Trying to fetch version with IPv4...${plain}"
|
||||||
tag_version=$(${curl_bin} -4 -Ls "https://api.github.com/repos/MHSanaei/3x-ui/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
|
tag_version=$(${curl_bin} -4 -Ls "https://api.github.com/repos/MHSanaei/3x-ui/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
|
||||||
if [[ ! -n "$tag_version" ]]; then
|
if [[ ! -n "$tag_version" ]]; then
|
||||||
_fail "ERROR: Failed to fetch x-ui version, it may be due to GitHub API restrictions, please try it later"
|
_fail "ERROR: Failed to fetch x-ui version, it may be due to GitHub API restrictions, please try it later"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
echo -e "Got x-ui latest version: ${tag_version}, beginning the installation..."
|
echo -e "Got x-ui latest version: ${tag_version}, beginning the installation..."
|
||||||
${wget_bin} -N -O /usr/local/x-ui-linux-$(arch).tar.gz https://github.com/MHSanaei/3x-ui/releases/download/${tag_version}/x-ui-linux-$(arch).tar.gz 2>/dev/null
|
${wget_bin} -N -O /usr/local/x-ui-linux-$(arch).tar.gz https://github.com/MHSanaei/3x-ui/releases/download/${tag_version}/x-ui-linux-$(arch).tar.gz 2>/dev/null
|
||||||
if [[ $? -ne 0 ]]; then
|
if [[ $? -ne 0 ]]; then
|
||||||
echo -e "${yellow}Trying to fetch version with IPv4...${plain}"
|
echo -e "${yellow}Trying to fetch version with IPv4...${plain}"
|
||||||
${wget_bin} --inet4-only -N -O /usr/local/x-ui-linux-$(arch).tar.gz https://github.com/MHSanaei/3x-ui/releases/download/${tag_version}/x-ui-linux-$(arch).tar.gz 2>/dev/null
|
${wget_bin} --inet4-only -N -O /usr/local/x-ui-linux-$(arch).tar.gz https://github.com/MHSanaei/3x-ui/releases/download/${tag_version}/x-ui-linux-$(arch).tar.gz 2>/dev/null
|
||||||
if [[ $? -ne 0 ]]; then
|
if [[ $? -ne 0 ]]; then
|
||||||
_fail "ERROR: Failed to download x-ui, please be sure that your server can access GitHub"
|
_fail "ERROR: Failed to download x-ui, please be sure that your server can access GitHub"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -e /usr/local/x-ui/ ]]; then
|
if [[ -e /usr/local/x-ui/ ]]; then
|
||||||
echo -e "${green}Stopping x-ui...${plain}"
|
echo -e "${green}Stopping x-ui...${plain}"
|
||||||
if [[ $release == "alpine" ]]; then
|
if [[ $release == "alpine" ]]; then
|
||||||
if [ -f "/etc/init.d/x-ui" ]; then
|
if [ -f "/etc/init.d/x-ui" ]; then
|
||||||
rc-service x-ui stop >/dev/null 2>&1
|
rc-service x-ui stop >/dev/null 2>&1
|
||||||
rc-update del x-ui >/dev/null 2>&1
|
rc-update del x-ui >/dev/null 2>&1
|
||||||
echo -e "${green}Removing old service unit version...${plain}"
|
echo -e "${green}Removing old service unit version...${plain}"
|
||||||
rm -f /etc/init.d/x-ui >/dev/null 2>&1
|
rm -f /etc/init.d/x-ui >/dev/null 2>&1
|
||||||
else
|
else
|
||||||
rm x-ui-linux-$(arch).tar.gz -f >/dev/null 2>&1
|
rm x-ui-linux-$(arch).tar.gz -f >/dev/null 2>&1
|
||||||
_fail "ERROR: x-ui service unit not installed."
|
_fail "ERROR: x-ui service unit not installed."
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
if [ -f "/etc/systemd/system/x-ui.service" ]; then
|
if [ -f "/etc/systemd/system/x-ui.service" ]; then
|
||||||
systemctl stop x-ui >/dev/null 2>&1
|
systemctl stop x-ui >/dev/null 2>&1
|
||||||
systemctl disable x-ui >/dev/null 2>&1
|
systemctl disable x-ui >/dev/null 2>&1
|
||||||
echo -e "${green}Removing old systemd unit version...${plain}"
|
echo -e "${green}Removing old systemd unit version...${plain}"
|
||||||
rm /etc/systemd/system/x-ui.service -f >/dev/null 2>&1
|
rm /etc/systemd/system/x-ui.service -f >/dev/null 2>&1
|
||||||
systemctl daemon-reload >/dev/null 2>&1
|
systemctl daemon-reload >/dev/null 2>&1
|
||||||
else
|
else
|
||||||
rm x-ui-linux-$(arch).tar.gz -f >/dev/null 2>&1
|
rm x-ui-linux-$(arch).tar.gz -f >/dev/null 2>&1
|
||||||
_fail "ERROR: x-ui systemd unit not installed."
|
_fail "ERROR: x-ui systemd unit not installed."
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
echo -e "${green}Removing old x-ui version...${plain}"
|
echo -e "${green}Removing old x-ui version...${plain}"
|
||||||
rm /usr/bin/x-ui -f >/dev/null 2>&1
|
rm /usr/bin/x-ui -f >/dev/null 2>&1
|
||||||
rm /usr/local/x-ui/x-ui.service -f >/dev/null 2>&1
|
rm /usr/local/x-ui/x-ui.service -f >/dev/null 2>&1
|
||||||
rm /usr/local/x-ui/x-ui -f >/dev/null 2>&1
|
rm /usr/local/x-ui/x-ui -f >/dev/null 2>&1
|
||||||
rm /usr/local/x-ui/x-ui.sh -f >/dev/null 2>&1
|
rm /usr/local/x-ui/x-ui.sh -f >/dev/null 2>&1
|
||||||
echo -e "${green}Removing old xray version...${plain}"
|
echo -e "${green}Removing old xray version...${plain}"
|
||||||
rm /usr/local/x-ui/bin/xray-linux-amd64 -f >/dev/null 2>&1
|
rm /usr/local/x-ui/bin/xray-linux-amd64 -f >/dev/null 2>&1
|
||||||
echo -e "${green}Removing old README and LICENSE file...${plain}"
|
echo -e "${green}Removing old README and LICENSE file...${plain}"
|
||||||
rm /usr/local/x-ui/bin/README.md -f >/dev/null 2>&1
|
rm /usr/local/x-ui/bin/README.md -f >/dev/null 2>&1
|
||||||
rm /usr/local/x-ui/bin/LICENSE -f >/dev/null 2>&1
|
rm /usr/local/x-ui/bin/LICENSE -f >/dev/null 2>&1
|
||||||
else
|
else
|
||||||
rm x-ui-linux-$(arch).tar.gz -f >/dev/null 2>&1
|
rm x-ui-linux-$(arch).tar.gz -f >/dev/null 2>&1
|
||||||
_fail "ERROR: x-ui not installed."
|
_fail "ERROR: x-ui not installed."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo -e "${green}Installing new x-ui version...${plain}"
|
echo -e "${green}Installing new x-ui version...${plain}"
|
||||||
tar zxvf x-ui-linux-$(arch).tar.gz >/dev/null 2>&1
|
tar zxvf x-ui-linux-$(arch).tar.gz >/dev/null 2>&1
|
||||||
rm x-ui-linux-$(arch).tar.gz -f >/dev/null 2>&1
|
rm x-ui-linux-$(arch).tar.gz -f >/dev/null 2>&1
|
||||||
cd x-ui >/dev/null 2>&1
|
cd x-ui >/dev/null 2>&1
|
||||||
chmod +x x-ui >/dev/null 2>&1
|
chmod +x x-ui >/dev/null 2>&1
|
||||||
|
|
||||||
# Check the system's architecture and rename the file accordingly
|
# Check the system's architecture and rename the file accordingly
|
||||||
if [[ $(arch) == "armv5" || $(arch) == "armv6" || $(arch) == "armv7" ]]; then
|
if [[ $(arch) == "armv5" || $(arch) == "armv6" || $(arch) == "armv7" ]]; then
|
||||||
mv bin/xray-linux-$(arch) bin/xray-linux-arm >/dev/null 2>&1
|
mv bin/xray-linux-$(arch) bin/xray-linux-arm >/dev/null 2>&1
|
||||||
chmod +x bin/xray-linux-arm >/dev/null 2>&1
|
chmod +x bin/xray-linux-arm >/dev/null 2>&1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
chmod +x x-ui bin/xray-linux-$(arch) >/dev/null 2>&1
|
chmod +x x-ui bin/xray-linux-$(arch) >/dev/null 2>&1
|
||||||
|
|
||||||
echo -e "${green}Downloading and installing x-ui.sh script...${plain}"
|
echo -e "${green}Downloading and installing x-ui.sh script...${plain}"
|
||||||
${wget_bin} -O /usr/bin/x-ui https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.sh >/dev/null 2>&1
|
${wget_bin} -O /usr/bin/x-ui https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.sh >/dev/null 2>&1
|
||||||
if [[ $? -ne 0 ]]; then
|
if [[ $? -ne 0 ]]; then
|
||||||
echo -e "${yellow}Trying to fetch x-ui with IPv4...${plain}"
|
echo -e "${yellow}Trying to fetch x-ui with IPv4...${plain}"
|
||||||
${wget_bin} --inet4-only -O /usr/bin/x-ui https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.sh >/dev/null 2>&1
|
${wget_bin} --inet4-only -O /usr/bin/x-ui https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.sh >/dev/null 2>&1
|
||||||
if [[ $? -ne 0 ]]; then
|
if [[ $? -ne 0 ]]; then
|
||||||
_fail "ERROR: Failed to download x-ui.sh script, please be sure that your server can access GitHub"
|
_fail "ERROR: Failed to download x-ui.sh script, please be sure that your server can access GitHub"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
chmod +x /usr/local/x-ui/x-ui.sh >/dev/null 2>&1
|
chmod +x /usr/local/x-ui/x-ui.sh >/dev/null 2>&1
|
||||||
chmod +x /usr/bin/x-ui >/dev/null 2>&1
|
chmod +x /usr/bin/x-ui >/dev/null 2>&1
|
||||||
|
|
||||||
echo -e "${green}Changing owner...${plain}"
|
echo -e "${green}Changing owner...${plain}"
|
||||||
chown -R root:root /usr/local/x-ui >/dev/null 2>&1
|
chown -R root:root /usr/local/x-ui >/dev/null 2>&1
|
||||||
|
|
||||||
if [ -f "/usr/local/x-ui/bin/config.json" ]; then
|
if [ -f "/usr/local/x-ui/bin/config.json" ]; then
|
||||||
echo -e "${green}Changing on config file permissions...${plain}"
|
echo -e "${green}Changing on config file permissions...${plain}"
|
||||||
chmod 640 /usr/local/x-ui/bin/config.json >/dev/null 2>&1
|
chmod 640 /usr/local/x-ui/bin/config.json >/dev/null 2>&1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ $release == "alpine" ]]; then
|
if [[ $release == "alpine" ]]; then
|
||||||
echo -e "${green}Downloading and installing startup unit x-ui.rc...${plain}"
|
echo -e "${green}Downloading and installing startup unit x-ui.rc...${plain}"
|
||||||
${wget_bin} -O /etc/init.d/x-ui https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.rc >/dev/null 2>&1
|
${wget_bin} -O /etc/init.d/x-ui https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.rc >/dev/null 2>&1
|
||||||
if [[ $? -ne 0 ]]; then
|
if [[ $? -ne 0 ]]; then
|
||||||
${wget_bin} --inet4-only -O /etc/init.d/x-ui https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.rc >/dev/null 2>&1
|
${wget_bin} --inet4-only -O /etc/init.d/x-ui https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.rc >/dev/null 2>&1
|
||||||
if [[ $? -ne 0 ]]; then
|
if [[ $? -ne 0 ]]; then
|
||||||
_fail "ERROR: Failed to download startup unit x-ui.rc, please be sure that your server can access GitHub"
|
_fail "ERROR: Failed to download startup unit x-ui.rc, please be sure that your server can access GitHub"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
chmod +x /etc/init.d/x-ui >/dev/null 2>&1
|
chmod +x /etc/init.d/x-ui >/dev/null 2>&1
|
||||||
chown root:root /etc/init.d/x-ui >/dev/null 2>&1
|
chown root:root /etc/init.d/x-ui >/dev/null 2>&1
|
||||||
rc-update add x-ui >/dev/null 2>&1
|
rc-update add x-ui >/dev/null 2>&1
|
||||||
rc-service x-ui start >/dev/null 2>&1
|
rc-service x-ui start >/dev/null 2>&1
|
||||||
else
|
else
|
||||||
echo -e "${green}Installing systemd unit...${plain}"
|
echo -e "${green}Installing systemd unit...${plain}"
|
||||||
cp -f x-ui.service /etc/systemd/system/ >/dev/null 2>&1
|
cp -f x-ui.service /etc/systemd/system/ >/dev/null 2>&1
|
||||||
chown root:root /etc/systemd/system/x-ui.service >/dev/null 2>&1
|
chown root:root /etc/systemd/system/x-ui.service >/dev/null 2>&1
|
||||||
systemctl daemon-reload >/dev/null 2>&1
|
systemctl daemon-reload >/dev/null 2>&1
|
||||||
systemctl enable x-ui >/dev/null 2>&1
|
systemctl enable x-ui >/dev/null 2>&1
|
||||||
systemctl start x-ui >/dev/null 2>&1
|
systemctl start x-ui >/dev/null 2>&1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
config_after_update
|
config_after_update
|
||||||
|
|
||||||
echo -e "${green}x-ui ${tag_version}${plain} updating finished, it is running now..."
|
echo -e "${green}x-ui ${tag_version}${plain} updating finished, it is running now..."
|
||||||
echo -e ""
|
echo -e ""
|
||||||
echo -e "┌───────────────────────────────────────────────────────┐
|
echo -e "┌───────────────────────────────────────────────────────┐
|
||||||
│ ${blue}x-ui control menu usages (subcommands):${plain} │
|
│ ${blue}x-ui control menu usages (subcommands):${plain} │
|
||||||
│ │
|
│ │
|
||||||
│ ${blue}x-ui${plain} - Admin Management Script │
|
│ ${blue}x-ui${plain} - Admin Management Script │
|
||||||
│ ${blue}x-ui start${plain} - Start │
|
│ ${blue}x-ui start${plain} - Start │
|
||||||
│ ${blue}x-ui stop${plain} - Stop │
|
│ ${blue}x-ui stop${plain} - Stop │
|
||||||
|
|
@ -250,7 +254,7 @@ update_x-ui() {
|
||||||
│ ${blue}x-ui legacy${plain} - Legacy version │
|
│ ${blue}x-ui legacy${plain} - Legacy version │
|
||||||
│ ${blue}x-ui install${plain} - Install │
|
│ ${blue}x-ui install${plain} - Install │
|
||||||
│ ${blue}x-ui uninstall${plain} - Uninstall │
|
│ ${blue}x-ui uninstall${plain} - Uninstall │
|
||||||
└───────────────────────────────────────────────────────┘"
|
└───────────────────────────────────────────────────────┘"
|
||||||
}
|
}
|
||||||
|
|
||||||
echo -e "${green}Running...${plain}"
|
echo -e "${green}Running...${plain}"
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
|
||||||
"github.com/mhsanaei/3x-ui/v2/database/model"
|
"github.com/mhsanaei/3x-ui/v2/database/model"
|
||||||
"github.com/mhsanaei/3x-ui/v2/web/service"
|
"github.com/mhsanaei/3x-ui/v2/web/service"
|
||||||
"github.com/mhsanaei/3x-ui/v2/web/session"
|
"github.com/mhsanaei/3x-ui/v2/web/session"
|
||||||
|
|
|
||||||
89
web/controller/multi_server_controller.go
Normal file
89
web/controller/multi_server_controller.go
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
package controller
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"x-ui/database/model"
|
||||||
|
"x-ui/web/service"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MultiServerController struct {
|
||||||
|
multiServerService service.MultiServerService
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMultiServerController(g *gin.RouterGroup) *MultiServerController {
|
||||||
|
c := &MultiServerController{}
|
||||||
|
c.initRouter(g)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *MultiServerController) initRouter(g *gin.RouterGroup) {
|
||||||
|
g = g.Group("/server")
|
||||||
|
|
||||||
|
g.GET("/list", c.getServers)
|
||||||
|
g.POST("/add", c.addServer)
|
||||||
|
g.POST("/del/:id", c.delServer)
|
||||||
|
g.POST("/update/:id", c.updateServer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *MultiServerController) getServers(ctx *gin.Context) {
|
||||||
|
servers, err := c.multiServerService.GetServers()
|
||||||
|
if err != nil {
|
||||||
|
jsonMsg(ctx, "Failed to get servers", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
jsonObj(ctx, servers, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *MultiServerController) addServer(ctx *gin.Context) {
|
||||||
|
server := &model.Server{}
|
||||||
|
err := ctx.ShouldBind(server)
|
||||||
|
if err != nil {
|
||||||
|
jsonMsg(ctx, "Invalid data", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = c.multiServerService.AddServer(server)
|
||||||
|
if err != nil {
|
||||||
|
jsonMsg(ctx, "Failed to add server", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
jsonMsg(ctx, "Server added successfully", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *MultiServerController) delServer(ctx *gin.Context) {
|
||||||
|
id, err := strconv.Atoi(ctx.Param("id"))
|
||||||
|
if err != nil {
|
||||||
|
jsonMsg(ctx, "Invalid ID", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = c.multiServerService.DeleteServer(id)
|
||||||
|
if err != nil {
|
||||||
|
jsonMsg(ctx, "Failed to delete server", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
jsonMsg(ctx, "Server deleted successfully", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *MultiServerController) updateServer(ctx *gin.Context) {
|
||||||
|
id, err := strconv.Atoi(ctx.Param("id"))
|
||||||
|
if err != nil {
|
||||||
|
jsonMsg(ctx, "Invalid ID", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
server := &model.Server{
|
||||||
|
Id: id,
|
||||||
|
}
|
||||||
|
err = ctx.ShouldBind(server)
|
||||||
|
if err != nil {
|
||||||
|
jsonMsg(ctx, "Invalid data", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = c.multiServerService.UpdateServer(server)
|
||||||
|
if err != nil {
|
||||||
|
jsonMsg(ctx, "Failed to update server", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
jsonMsg(ctx, "Server updated successfully", nil)
|
||||||
|
}
|
||||||
|
|
@ -26,6 +26,7 @@ func (a *XUIController) initRouter(g *gin.RouterGroup) {
|
||||||
|
|
||||||
g.GET("/", a.index)
|
g.GET("/", a.index)
|
||||||
g.GET("/inbounds", a.inbounds)
|
g.GET("/inbounds", a.inbounds)
|
||||||
|
g.GET("/servers", a.servers)
|
||||||
g.GET("/settings", a.settings)
|
g.GET("/settings", a.settings)
|
||||||
g.GET("/xray", a.xraySettings)
|
g.GET("/xray", a.xraySettings)
|
||||||
|
|
||||||
|
|
@ -52,3 +53,7 @@ func (a *XUIController) settings(c *gin.Context) {
|
||||||
func (a *XUIController) xraySettings(c *gin.Context) {
|
func (a *XUIController) xraySettings(c *gin.Context) {
|
||||||
html(c, "xray.html", "pages.xray.title", nil)
|
html(c, "xray.html", "pages.xray.title", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *XUIController) servers(c *gin.Context) {
|
||||||
|
html(c, "servers.html", "Servers", nil)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,11 @@
|
||||||
icon: 'user',
|
icon: 'user',
|
||||||
title: '{{ i18n "menu.inbounds"}}'
|
title: '{{ i18n "menu.inbounds"}}'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: '{{ .base_path }}panel/servers',
|
||||||
|
icon: 'cloud-server',
|
||||||
|
title: 'Servers'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: '{{ .base_path }}panel/settings',
|
key: '{{ .base_path }}panel/settings',
|
||||||
icon: 'setting',
|
icon: 'setting',
|
||||||
|
|
|
||||||
165
web/html/servers.html
Normal file
165
web/html/servers.html
Normal file
|
|
@ -0,0 +1,165 @@
|
||||||
|
{{template "header" .}}
|
||||||
|
|
||||||
|
<div id="app" class="row" v-cloak>
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h3 class="card-title">Server Management</h3>
|
||||||
|
<div class="card-tools">
|
||||||
|
<button class="btn btn-primary" @click="showAddModal">Add Server</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<table class="table table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>#</th>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Address</th>
|
||||||
|
<th>Port</th>
|
||||||
|
<th>Enabled</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="(server, index) in servers">
|
||||||
|
<td>{{index + 1}}</td>
|
||||||
|
<td>{{server.name}}</td>
|
||||||
|
<td>{{server.address}}</td>
|
||||||
|
<td>{{server.port}}</td>
|
||||||
|
<td>
|
||||||
|
<span v-if="server.enable" class="badge bg-success">Yes</span>
|
||||||
|
<span v-else class="badge bg-danger">No</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<button class="btn btn-info btn-sm" @click="showEditModal(server)">Edit</button>
|
||||||
|
<button class="btn btn-danger btn-sm" @click="deleteServer(server.id)">Delete</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Add/Edit Modal -->
|
||||||
|
<div class="modal fade" id="serverModal" tabindex="-1" role="dialog">
|
||||||
|
<div class="modal-dialog" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">{{modal.title}}</h5>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Name</label>
|
||||||
|
<input type="text" class="form-control" v-model="modal.server.name">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Address (IP or Domain)</label>
|
||||||
|
<input type="text" class="form-control" v-model="modal.server.address">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Port</label>
|
||||||
|
<input type="number" class="form-control" v-model.number="modal.server.port">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>API Key</label>
|
||||||
|
<input type="text" class="form-control" v-model="modal.server.apiKey">
|
||||||
|
</div>
|
||||||
|
<div class="form-check">
|
||||||
|
<input type="checkbox" class="form-check-input" v-model="modal.server.enable">
|
||||||
|
<label class="form-check-label">Enabled</label>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||||
|
<button type="button" class="btn btn-primary" @click="saveServer">Save</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const app = new Vue({
|
||||||
|
el: '#app',
|
||||||
|
data: {
|
||||||
|
servers: [],
|
||||||
|
modal: {
|
||||||
|
title: '',
|
||||||
|
server: {
|
||||||
|
name: '',
|
||||||
|
address: '',
|
||||||
|
port: 0,
|
||||||
|
apiKey: '',
|
||||||
|
enable: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
loadServers() {
|
||||||
|
axios.get('{{.base_path}}server/list')
|
||||||
|
.then(response => {
|
||||||
|
this.servers = response.data.obj;
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
alert(error.response.data.msg);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
showAddModal() {
|
||||||
|
this.modal.title = 'Add Server';
|
||||||
|
this.modal.server = {
|
||||||
|
name: '',
|
||||||
|
address: '',
|
||||||
|
port: 0,
|
||||||
|
apiKey: '',
|
||||||
|
enable: true
|
||||||
|
};
|
||||||
|
$('#serverModal').modal('show');
|
||||||
|
},
|
||||||
|
showEditModal(server) {
|
||||||
|
this.modal.title = 'Edit Server';
|
||||||
|
this.modal.server = Object.assign({}, server);
|
||||||
|
$('#serverModal').modal('show');
|
||||||
|
},
|
||||||
|
saveServer() {
|
||||||
|
let url = '{{.base_path}}server/add';
|
||||||
|
if (this.modal.server.id) {
|
||||||
|
url = `{{.base_path}}server/update/${this.modal.server.id}`;
|
||||||
|
}
|
||||||
|
axios.post(url, this.modal.server)
|
||||||
|
.then(response => {
|
||||||
|
alert(response.data.msg);
|
||||||
|
$('#serverModal').modal('hide');
|
||||||
|
this.loadServers();
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
alert(error.response.data.msg);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
deleteServer(id) {
|
||||||
|
if (!confirm('Are you sure you want to delete this server?')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
axios.post(`{{.base_path}}server/del/${id}`)
|
||||||
|
.then(response => {
|
||||||
|
alert(response.data.msg);
|
||||||
|
this.loadServers();
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
alert(error.response.data.msg);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.loadServers();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{{template "footer" .}}
|
||||||
34
web/middleware/auth.go
Normal file
34
web/middleware/auth.go
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"x-ui/web/service"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ApiAuth() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
apiKey := c.GetHeader("Api-Key")
|
||||||
|
if apiKey == "" {
|
||||||
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "API key is required"})
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
settingService := service.SettingService{}
|
||||||
|
panelAPIKey, err := settingService.GetAPIKey()
|
||||||
|
if err != nil || panelAPIKey == "" {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "API key not configured on the panel"})
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if apiKey != panelAPIKey {
|
||||||
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid API key"})
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,8 +3,11 @@
|
||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -673,6 +676,11 @@ func (s *InboundService) AddInboundClient(data *model.Inbound) (bool, error) {
|
||||||
}
|
}
|
||||||
s.xrayApi.Close()
|
s.xrayApi.Close()
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
body, _ := json.Marshal(data)
|
||||||
|
s.syncWithSlaves("POST", "/panel/inbound/api/addClient", bytes.NewReader(body))
|
||||||
|
}
|
||||||
|
|
||||||
return needRestart, tx.Save(oldInbound).Error
|
return needRestart, tx.Save(oldInbound).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -761,6 +769,11 @@ func (s *InboundService) DelInboundClient(inboundId int, clientId string) (bool,
|
||||||
s.xrayApi.Close()
|
s.xrayApi.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
s.syncWithSlaves("POST", fmt.Sprintf("/panel/inbound/api/%d/delClient/%s", inboundId, clientId), nil)
|
||||||
|
}
|
||||||
|
|
||||||
return needRestart, db.Save(oldInbound).Error
|
return needRestart, db.Save(oldInbound).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -936,6 +949,12 @@ func (s *InboundService) UpdateInboundClient(data *model.Inbound, clientId strin
|
||||||
logger.Debug("Client old email not found")
|
logger.Debug("Client old email not found")
|
||||||
needRestart = true
|
needRestart = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
body, _ := json.Marshal(data)
|
||||||
|
s.syncWithSlaves("POST", fmt.Sprintf("/panel/inbound/api/updateClient/%s", clientId), bytes.NewReader(body))
|
||||||
|
}
|
||||||
|
|
||||||
return needRestart, tx.Save(oldInbound).Error
|
return needRestart, tx.Save(oldInbound).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2379,6 +2398,44 @@ func (s *InboundService) FilterAndSortClientEmails(emails []string) ([]string, [
|
||||||
|
|
||||||
return validEmails, extraEmails, nil
|
return validEmails, extraEmails, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *InboundService) syncWithSlaves(method string, path string, body io.Reader) {
|
||||||
|
serverService := MultiServerService{}
|
||||||
|
servers, err := serverService.GetServers()
|
||||||
|
if err != nil {
|
||||||
|
logger.Warning("Failed to get servers for syncing:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, server := range servers {
|
||||||
|
if !server.Enable {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
url := fmt.Sprintf("http://%s:%d%s", server.Address, server.Port, path)
|
||||||
|
req, err := http.NewRequest(method, url, body)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warningf("Failed to create request for server %s: %v", server.Name, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
req.Header.Set("Api-Key", server.APIKey)
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warningf("Failed to send request to server %s: %v", server.Name, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
bodyBytes, _ := io.ReadAll(resp.Body)
|
||||||
|
logger.Warningf("Failed to sync with server %s. Status: %s, Body: %s", server.Name, resp.Status, string(bodyBytes))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *InboundService) DelInboundClientByEmail(inboundId int, email string) (bool, error) {
|
func (s *InboundService) DelInboundClientByEmail(inboundId int, email string) (bool, error) {
|
||||||
oldInbound, err := s.GetInbound(inboundId)
|
oldInbound, err := s.GetInbound(inboundId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -2470,4 +2527,5 @@ func (s *InboundService) DelInboundClientByEmail(inboundId int, email string) (b
|
||||||
}
|
}
|
||||||
|
|
||||||
return needRestart, db.Save(oldInbound).Error
|
return needRestart, db.Save(oldInbound).Error
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
72
web/service/inbound_service_sync_test.go
Normal file
72
web/service/inbound_service_sync_test.go
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
"x-ui/database"
|
||||||
|
"x-ui/database/model"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestInboundServiceSync(t *testing.T) {
|
||||||
|
setup()
|
||||||
|
defer teardown()
|
||||||
|
|
||||||
|
// Mock server to simulate a slave
|
||||||
|
var receivedApiKey string
|
||||||
|
var receivedBody []byte
|
||||||
|
mockSlave := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
receivedApiKey = r.Header.Get("Api-Key")
|
||||||
|
receivedBody, _ = io.ReadAll(r.Body)
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
}))
|
||||||
|
defer mockSlave.Close()
|
||||||
|
|
||||||
|
// Add the mock slave to the database
|
||||||
|
multiServerService := MultiServerService{}
|
||||||
|
mockSlaveURL, _ := url.Parse(mockSlave.URL)
|
||||||
|
mockSlavePort, _ := strconv.Atoi(mockSlaveURL.Port())
|
||||||
|
slaveServer := &model.Server{
|
||||||
|
Name: "mock-slave",
|
||||||
|
Address: mockSlaveURL.Hostname(),
|
||||||
|
Port: mockSlavePort,
|
||||||
|
APIKey: "slave-api-key",
|
||||||
|
Enable: true,
|
||||||
|
}
|
||||||
|
multiServerService.AddServer(slaveServer)
|
||||||
|
|
||||||
|
// Create a test inbound and client
|
||||||
|
inboundService := InboundService{}
|
||||||
|
db := database.GetDB()
|
||||||
|
testInbound := &model.Inbound{
|
||||||
|
UserId: 1,
|
||||||
|
Remark: "test-inbound",
|
||||||
|
Enable: true,
|
||||||
|
Settings: `{"clients":[]}`,
|
||||||
|
}
|
||||||
|
db.Create(testInbound)
|
||||||
|
|
||||||
|
clientData := model.Client{
|
||||||
|
Email: "test@example.com",
|
||||||
|
ID: "test-id",
|
||||||
|
}
|
||||||
|
clientBytes, _ := json.Marshal([]model.Client{clientData})
|
||||||
|
inboundData := &model.Inbound{
|
||||||
|
Id: testInbound.Id,
|
||||||
|
Settings: string(clientBytes),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test AddInboundClient sync
|
||||||
|
inboundService.AddInboundClient(inboundData)
|
||||||
|
|
||||||
|
assert.Equal(t, "slave-api-key", receivedApiKey)
|
||||||
|
var receivedInbound model.Inbound
|
||||||
|
json.Unmarshal(receivedBody, &receivedInbound)
|
||||||
|
assert.Equal(t, 1, receivedInbound.Id)
|
||||||
|
}
|
||||||
37
web/service/multi_server_service.go
Normal file
37
web/service/multi_server_service.go
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"x-ui/database"
|
||||||
|
"x-ui/database/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MultiServerService struct{}
|
||||||
|
|
||||||
|
func (s *MultiServerService) GetServers() ([]*model.Server, error) {
|
||||||
|
db := database.GetDB()
|
||||||
|
var servers []*model.Server
|
||||||
|
err := db.Find(&servers).Error
|
||||||
|
return servers, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MultiServerService) GetServer(id int) (*model.Server, error) {
|
||||||
|
db := database.GetDB()
|
||||||
|
var server model.Server
|
||||||
|
err := db.First(&server, id).Error
|
||||||
|
return &server, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MultiServerService) AddServer(server *model.Server) error {
|
||||||
|
db := database.GetDB()
|
||||||
|
return db.Create(server).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MultiServerService) UpdateServer(server *model.Server) error {
|
||||||
|
db := database.GetDB()
|
||||||
|
return db.Save(server).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MultiServerService) DeleteServer(id int) error {
|
||||||
|
db := database.GetDB()
|
||||||
|
return db.Delete(&model.Server{}, id).Error
|
||||||
|
}
|
||||||
63
web/service/multi_server_service_test.go
Normal file
63
web/service/multi_server_service_test.go
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
"x-ui/database"
|
||||||
|
"x-ui/database/model"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func setup() {
|
||||||
|
dbPath := "test.db"
|
||||||
|
os.Remove(dbPath)
|
||||||
|
database.InitDB(dbPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func teardown() {
|
||||||
|
db, _ := database.GetDB().DB()
|
||||||
|
db.Close()
|
||||||
|
os.Remove("test.db")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMultiServerService(t *testing.T) {
|
||||||
|
setup()
|
||||||
|
defer teardown()
|
||||||
|
|
||||||
|
service := MultiServerService{}
|
||||||
|
|
||||||
|
// Test AddServer
|
||||||
|
server := &model.Server{
|
||||||
|
Name: "test-server",
|
||||||
|
Address: "127.0.0.1",
|
||||||
|
Port: 54321,
|
||||||
|
APIKey: "test-key",
|
||||||
|
Enable: true,
|
||||||
|
}
|
||||||
|
err := service.AddServer(server)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Test GetServer
|
||||||
|
retrievedServer, err := service.GetServer(server.Id)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, server.Name, retrievedServer.Name)
|
||||||
|
|
||||||
|
// Test GetServers
|
||||||
|
servers, err := service.GetServers()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, servers, 1)
|
||||||
|
|
||||||
|
// Test UpdateServer
|
||||||
|
retrievedServer.Name = "updated-server"
|
||||||
|
err = service.UpdateServer(retrievedServer)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
updatedServer, _ := service.GetServer(server.Id)
|
||||||
|
assert.Equal(t, "updated-server", updatedServer.Name)
|
||||||
|
|
||||||
|
// Test DeleteServer
|
||||||
|
err = service.DeleteServer(server.Id)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
_, err = service.GetServer(server.Id)
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
@ -204,6 +204,21 @@ func (s *SettingService) getSetting(key string) (*model.Setting, error) {
|
||||||
return setting, nil
|
return setting, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SettingService) GetAPIKey() (string, error) {
|
||||||
|
setting, err := s.getSetting("ApiKey")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if setting == nil {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
return setting.Value, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SettingService) SetAPIKey(apiKey string) error {
|
||||||
|
return s.saveSetting("ApiKey", apiKey)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *SettingService) saveSetting(key string, value string) error {
|
func (s *SettingService) saveSetting(key string, value string) error {
|
||||||
setting, err := s.getSetting(key)
|
setting, err := s.getSetting(key)
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
|
|
|
||||||
|
|
@ -210,7 +210,7 @@ func (t *Tgbot) Start(i18nFS embed.FS) error {
|
||||||
// Parse admin IDs from comma-separated string
|
// Parse admin IDs from comma-separated string
|
||||||
if tgBotID != "" {
|
if tgBotID != "" {
|
||||||
for _, adminID := range strings.Split(tgBotID, ",") {
|
for _, adminID := range strings.Split(tgBotID, ",") {
|
||||||
id, err := strconv.Atoi(adminID)
|
id, err := strconv.ParseInt(adminID, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warning("Failed to parse admin ID from Telegram bot chat ID:", err)
|
logger.Warning("Failed to parse admin ID from Telegram bot chat ID:", err)
|
||||||
return err
|
return err
|
||||||
|
|
@ -905,8 +905,8 @@ func (t *Tgbot) answerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool
|
||||||
t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.errorOperation"))
|
t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.errorOperation"))
|
||||||
t.searchClient(chatId, email, callbackQuery.Message.GetMessageID())
|
t.searchClient(chatId, email, callbackQuery.Message.GetMessageID())
|
||||||
case "add_client_limit_traffic_c":
|
case "add_client_limit_traffic_c":
|
||||||
limitTraffic, _ := strconv.Atoi(dataArray[1])
|
limitTraffic, _ := strconv.ParseInt(dataArray[1], 10, 64)
|
||||||
client_TotalGB = int64(limitTraffic) * 1024 * 1024 * 1024
|
client_TotalGB = limitTraffic * 1024 * 1024 * 1024
|
||||||
messageId := callbackQuery.Message.GetMessageID()
|
messageId := callbackQuery.Message.GetMessageID()
|
||||||
inbound, err := t.inboundService.GetInbound(receiver_inbound_ID)
|
inbound, err := t.inboundService.GetInbound(receiver_inbound_ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -1010,7 +1010,7 @@ func (t *Tgbot) answerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool
|
||||||
t.editMessageCallbackTgBot(chatId, callbackQuery.Message.GetMessageID(), inlineKeyboard)
|
t.editMessageCallbackTgBot(chatId, callbackQuery.Message.GetMessageID(), inlineKeyboard)
|
||||||
case "reset_exp_c":
|
case "reset_exp_c":
|
||||||
if len(dataArray) == 3 {
|
if len(dataArray) == 3 {
|
||||||
days, err := strconv.Atoi(dataArray[2])
|
days, err := strconv.ParseInt(dataArray[2], 10, 64)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
var date int64
|
var date int64
|
||||||
if days > 0 {
|
if days > 0 {
|
||||||
|
|
@ -1115,7 +1115,7 @@ func (t *Tgbot) answerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool
|
||||||
t.searchClient(chatId, email, callbackQuery.Message.GetMessageID())
|
t.searchClient(chatId, email, callbackQuery.Message.GetMessageID())
|
||||||
case "add_client_reset_exp_c":
|
case "add_client_reset_exp_c":
|
||||||
client_ExpiryTime = 0
|
client_ExpiryTime = 0
|
||||||
days, _ := strconv.Atoi(dataArray[1])
|
days, _ := strconv.ParseInt(dataArray[1], 10, 64)
|
||||||
var date int64
|
var date int64
|
||||||
if client_ExpiryTime > 0 {
|
if client_ExpiryTime > 0 {
|
||||||
if client_ExpiryTime-time.Now().Unix()*1000 < 0 {
|
if client_ExpiryTime-time.Now().Unix()*1000 < 0 {
|
||||||
|
|
@ -2952,10 +2952,12 @@ func (t *Tgbot) clientInfoMsg(
|
||||||
}
|
}
|
||||||
|
|
||||||
status := t.I18nBot("tgbot.offline")
|
status := t.I18nBot("tgbot.offline")
|
||||||
|
isOnline := false
|
||||||
if p.IsRunning() {
|
if p.IsRunning() {
|
||||||
for _, online := range p.GetOnlineClients() {
|
for _, online := range p.GetOnlineClients() {
|
||||||
if online == traffic.Email {
|
if online == traffic.Email {
|
||||||
status = t.I18nBot("tgbot.online")
|
status = t.I18nBot("tgbot.online")
|
||||||
|
isOnline = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2968,6 +2970,9 @@ func (t *Tgbot) clientInfoMsg(
|
||||||
}
|
}
|
||||||
if printOnline {
|
if printOnline {
|
||||||
output += t.I18nBot("tgbot.messages.online", "Status=="+status)
|
output += t.I18nBot("tgbot.messages.online", "Status=="+status)
|
||||||
|
if !isOnline && traffic.LastOnline > 0 {
|
||||||
|
output += t.I18nBot("tgbot.messages.lastOnline", "Time=="+time.UnixMilli(traffic.LastOnline).Format("2006-01-02 15:04:05"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if printActive {
|
if printActive {
|
||||||
output += t.I18nBot("tgbot.messages.active", "Enable=="+active)
|
output += t.I18nBot("tgbot.messages.active", "Enable=="+active)
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@
|
||||||
"invalidFormData" = "تنسيق البيانات المدخلة مش صحيح."
|
"invalidFormData" = "تنسيق البيانات المدخلة مش صحيح."
|
||||||
"emptyUsername" = "اسم المستخدم مطلوب"
|
"emptyUsername" = "اسم المستخدم مطلوب"
|
||||||
"emptyPassword" = "الباسورد مطلوب"
|
"emptyPassword" = "الباسورد مطلوب"
|
||||||
"wrongUsernameOrPassword" = "اسم المستخدم أو كلمة المرور أو كود المصادقة الثنائية غير صحيح."
|
"wrongUsernameOrPassword" = "اسم المستخدم أو كلمة المرور أو كود المصادقة الثنائية غير صحيح."
|
||||||
"successLogin" = "لقد تم تسجيل الدخول إلى حسابك بنجاح."
|
"successLogin" = "لقد تم تسجيل الدخول إلى حسابك بنجاح."
|
||||||
|
|
||||||
[pages.index]
|
[pages.index]
|
||||||
|
|
@ -565,9 +565,9 @@
|
||||||
|
|
||||||
[pages.settings.security]
|
[pages.settings.security]
|
||||||
"admin" = "بيانات الأدمن"
|
"admin" = "بيانات الأدمن"
|
||||||
"twoFactor" = "المصادقة الثنائية"
|
"twoFactor" = "المصادقة الثنائية"
|
||||||
"twoFactorEnable" = "تفعيل المصادقة الثنائية"
|
"twoFactorEnable" = "تفعيل المصادقة الثنائية"
|
||||||
"twoFactorEnableDesc" = "يضيف طبقة إضافية من المصادقة لتعزيز الأمان."
|
"twoFactorEnableDesc" = "يضيف طبقة إضافية من المصادقة لتعزيز الأمان."
|
||||||
"twoFactorModalSetTitle" = "تفعيل المصادقة الثنائية"
|
"twoFactorModalSetTitle" = "تفعيل المصادقة الثنائية"
|
||||||
"twoFactorModalDeleteTitle" = "تعطيل المصادقة الثنائية"
|
"twoFactorModalDeleteTitle" = "تعطيل المصادقة الثنائية"
|
||||||
"twoFactorModalSteps" = "لإعداد المصادقة الثنائية، قم ببعض الخطوات:"
|
"twoFactorModalSteps" = "لإعداد المصادقة الثنائية، قم ببعض الخطوات:"
|
||||||
|
|
@ -663,6 +663,7 @@
|
||||||
"active" = "💡 مفعل: {{ .Enable }}\r\n"
|
"active" = "💡 مفعل: {{ .Enable }}\r\n"
|
||||||
"enabled" = "🚨 مفعل: {{ .Enable }}\r\n"
|
"enabled" = "🚨 مفعل: {{ .Enable }}\r\n"
|
||||||
"online" = "🌐 حالة الاتصال: {{ .Status }}\r\n"
|
"online" = "🌐 حالة الاتصال: {{ .Status }}\r\n"
|
||||||
|
"lastOnline" = "🔙 آخر متصل: {{ .Time }}\r\n"
|
||||||
"email" = "📧 الإيميل: {{ .Email }}\r\n"
|
"email" = "📧 الإيميل: {{ .Email }}\r\n"
|
||||||
"upload" = "🔼 رفع: ↑{{ .Upload }}\r\n"
|
"upload" = "🔼 رفع: ↑{{ .Upload }}\r\n"
|
||||||
"download" = "🔽 تنزيل: ↓{{ .Download }}\r\n"
|
"download" = "🔽 تنزيل: ↓{{ .Download }}\r\n"
|
||||||
|
|
|
||||||
|
|
@ -663,6 +663,7 @@
|
||||||
"active" = "💡 Active: {{ .Enable }}\r\n"
|
"active" = "💡 Active: {{ .Enable }}\r\n"
|
||||||
"enabled" = "🚨 Enabled: {{ .Enable }}\r\n"
|
"enabled" = "🚨 Enabled: {{ .Enable }}\r\n"
|
||||||
"online" = "🌐 Connection status: {{ .Status }}\r\n"
|
"online" = "🌐 Connection status: {{ .Status }}\r\n"
|
||||||
|
"lastOnline" = "🔙 Last online: {{ .Time }}\r\n"
|
||||||
"email" = "📧 Email: {{ .Email }}\r\n"
|
"email" = "📧 Email: {{ .Email }}\r\n"
|
||||||
"upload" = "🔼 Upload: ↑{{ .Upload }}\r\n"
|
"upload" = "🔼 Upload: ↑{{ .Upload }}\r\n"
|
||||||
"download" = "🔽 Download: ↓{{ .Download }}\r\n"
|
"download" = "🔽 Download: ↓{{ .Download }}\r\n"
|
||||||
|
|
@ -688,9 +689,9 @@
|
||||||
"inbound_client_data_id" = "🔄 Inbound: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Email: {{ .ClientEmail }}\n📊 Traffic: {{ .ClientTraffic }}\n📅 Expire Date: {{ .ClientExp }}\n🌐 IP Limit: {{ .IpLimit }}\n💬 Comment: {{ .ClientComment }}\n\nYou can add the client to inbound now!"
|
"inbound_client_data_id" = "🔄 Inbound: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Email: {{ .ClientEmail }}\n📊 Traffic: {{ .ClientTraffic }}\n📅 Expire Date: {{ .ClientExp }}\n🌐 IP Limit: {{ .IpLimit }}\n💬 Comment: {{ .ClientComment }}\n\nYou can add the client to inbound now!"
|
||||||
"inbound_client_data_pass" = "🔄 Inbound: {{ .InboundRemark }}\n\n🔑 Password: {{ .ClientPass }}\n📧 Email: {{ .ClientEmail }}\n📊 Traffic: {{ .ClientTraffic }}\n📅 Expire Date: {{ .ClientExp }}\n🌐 IP Limit: {{ .IpLimit }}\n💬 Comment: {{ .ClientComment }}\n\nYou can add the client to inbound now!"
|
"inbound_client_data_pass" = "🔄 Inbound: {{ .InboundRemark }}\n\n🔑 Password: {{ .ClientPass }}\n📧 Email: {{ .ClientEmail }}\n📊 Traffic: {{ .ClientTraffic }}\n📅 Expire Date: {{ .ClientExp }}\n🌐 IP Limit: {{ .IpLimit }}\n💬 Comment: {{ .ClientComment }}\n\nYou can add the client to inbound now!"
|
||||||
"cancel" = "❌ Process Canceled! \n\nYou can /start again anytime. 🔄"
|
"cancel" = "❌ Process Canceled! \n\nYou can /start again anytime. 🔄"
|
||||||
"error_add_client" = "⚠️ Error:\n\n {{ .error }}"
|
"error_add_client" = "⚠️ Error:\n\n {{ .error }}"
|
||||||
"using_default_value" = "Okay, I'll stick with the default value. 😊"
|
"using_default_value" = "Okay, I'll stick with the default value. 😊"
|
||||||
"incorrect_input" ="Your input is not valid.\nThe phrases should be continuous without spaces.\nCorrect example: aaaaaa\nIncorrect example: aaa aaa 🚫"
|
"incorrect_input" = "Your input is not valid.\nThe phrases should be continuous without spaces.\nCorrect example: aaaaaa\nIncorrect example: aaa aaa 🚫"
|
||||||
"AreYouSure" = "Are you sure? 🤔"
|
"AreYouSure" = "Are you sure? 🤔"
|
||||||
"SuccessResetTraffic" = "📧 Email: {{ .ClientEmail }}\n🏁 Result: ✅ Success"
|
"SuccessResetTraffic" = "📧 Email: {{ .ClientEmail }}\n🏁 Result: ✅ Success"
|
||||||
"FailedResetTraffic" = "📧 Email: {{ .ClientEmail }}\n🏁 Result: ❌ Failed \n\n🛠️ Error: [ {{ .ErrorMessage }} ]"
|
"FailedResetTraffic" = "📧 Email: {{ .ClientEmail }}\n🏁 Result: ❌ Failed \n\n🛠️ Error: [ {{ .ErrorMessage }} ]"
|
||||||
|
|
|
||||||
|
|
@ -663,6 +663,7 @@
|
||||||
"active" = "💡 Activo: {{ .Enable }}\r\n"
|
"active" = "💡 Activo: {{ .Enable }}\r\n"
|
||||||
"enabled" = "🚨 Habilitado: {{ .Enable }}\r\n"
|
"enabled" = "🚨 Habilitado: {{ .Enable }}\r\n"
|
||||||
"online" = "🌐 Estado de conexión: {{ .Status }}\r\n"
|
"online" = "🌐 Estado de conexión: {{ .Status }}\r\n"
|
||||||
|
"lastOnline" = "🔙 Última conexión: {{ .Time }}\r\n"
|
||||||
"email" = "📧 Email: {{ .Email }}\r\n"
|
"email" = "📧 Email: {{ .Email }}\r\n"
|
||||||
"upload" = "🔼 Subida: ↑{{ .Upload }}\r\n"
|
"upload" = "🔼 Subida: ↑{{ .Upload }}\r\n"
|
||||||
"download" = "🔽 Bajada: ↓{{ .Download }}\r\n"
|
"download" = "🔽 Bajada: ↓{{ .Download }}\r\n"
|
||||||
|
|
@ -688,9 +689,9 @@
|
||||||
"inbound_client_data_id" = "🔄 Entrada: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Correo: {{ .ClientEmail }}\n📊 Tráfico: {{ .ClientTraffic }}\n📅 Fecha de expiración: {{ .ClientExp }}\n🌐 Límite de IP: {{ .IpLimit }}\n💬 Comentario: {{ .ClientComment }}\n\n¡Ahora puedes agregar al cliente a la entrada!"
|
"inbound_client_data_id" = "🔄 Entrada: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Correo: {{ .ClientEmail }}\n📊 Tráfico: {{ .ClientTraffic }}\n📅 Fecha de expiración: {{ .ClientExp }}\n🌐 Límite de IP: {{ .IpLimit }}\n💬 Comentario: {{ .ClientComment }}\n\n¡Ahora puedes agregar al cliente a la entrada!"
|
||||||
"inbound_client_data_pass" = "🔄 Entrada: {{ .InboundRemark }}\n\n🔑 Contraseña: {{ .ClientPass }}\n📧 Correo: {{ .ClientEmail }}\n📊 Tráfico: {{ .ClientTraffic }}\n📅 Fecha de expiración: {{ .ClientExp }}\n🌐 Límite de IP: {{ .IpLimit }}\n💬 Comentario: {{ .ClientComment }}\n\n¡Ahora puedes agregar al cliente a la entrada!"
|
"inbound_client_data_pass" = "🔄 Entrada: {{ .InboundRemark }}\n\n🔑 Contraseña: {{ .ClientPass }}\n📧 Correo: {{ .ClientEmail }}\n📊 Tráfico: {{ .ClientTraffic }}\n📅 Fecha de expiración: {{ .ClientExp }}\n🌐 Límite de IP: {{ .IpLimit }}\n💬 Comentario: {{ .ClientComment }}\n\n¡Ahora puedes agregar al cliente a la entrada!"
|
||||||
"cancel" = "❌ ¡Proceso cancelado! \n\nPuedes /start de nuevo en cualquier momento. 🔄"
|
"cancel" = "❌ ¡Proceso cancelado! \n\nPuedes /start de nuevo en cualquier momento. 🔄"
|
||||||
"error_add_client" = "⚠️ Error:\n\n {{ .error }}"
|
"error_add_client" = "⚠️ Error:\n\n {{ .error }}"
|
||||||
"using_default_value" = "Está bien, me quedaré con el valor predeterminado. 😊"
|
"using_default_value" = "Está bien, me quedaré con el valor predeterminado. 😊"
|
||||||
"incorrect_input" ="Tu entrada no es válida.\nLas frases deben ser continuas sin espacios.\nEjemplo correcto: aaaaaa\nEjemplo incorrecto: aaa aaa 🚫"
|
"incorrect_input" = "Tu entrada no es válida.\nLas frases deben ser continuas sin espacios.\nEjemplo correcto: aaaaaa\nEjemplo incorrecto: aaa aaa 🚫"
|
||||||
"AreYouSure" = "¿Estás seguro? 🤔"
|
"AreYouSure" = "¿Estás seguro? 🤔"
|
||||||
"SuccessResetTraffic" = "📧 Correo: {{ .ClientEmail }}\n🏁 Resultado: ✅ Éxito"
|
"SuccessResetTraffic" = "📧 Correo: {{ .ClientEmail }}\n🏁 Resultado: ✅ Éxito"
|
||||||
"FailedResetTraffic" = "📧 Correo: {{ .ClientEmail }}\n🏁 Resultado: ❌ Fallido \n\n🛠️ Error: [ {{ .ErrorMessage }} ]"
|
"FailedResetTraffic" = "📧 Correo: {{ .ClientEmail }}\n🏁 Resultado: ❌ Fallido \n\n🛠️ Error: [ {{ .ErrorMessage }} ]"
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@
|
||||||
"invalidFormData" = "اطلاعات بهدرستی وارد نشدهاست"
|
"invalidFormData" = "اطلاعات بهدرستی وارد نشدهاست"
|
||||||
"emptyUsername" = "لطفا یک نامکاربری وارد کنید"
|
"emptyUsername" = "لطفا یک نامکاربری وارد کنید"
|
||||||
"emptyPassword" = "لطفا یک رمزعبور وارد کنید"
|
"emptyPassword" = "لطفا یک رمزعبور وارد کنید"
|
||||||
"wrongUsernameOrPassword" = "نام کاربری، رمز عبور یا کد دو مرحلهای نامعتبر است."
|
"wrongUsernameOrPassword" = "نام کاربری، رمز عبور یا کد دو مرحلهای نامعتبر است."
|
||||||
"successLogin" = "شما با موفقیت به حساب کاربری خود وارد شدید."
|
"successLogin" = "شما با موفقیت به حساب کاربری خود وارد شدید."
|
||||||
|
|
||||||
[pages.index]
|
[pages.index]
|
||||||
|
|
@ -565,9 +565,9 @@
|
||||||
|
|
||||||
[pages.settings.security]
|
[pages.settings.security]
|
||||||
"admin" = "اعتبارنامههای ادمین"
|
"admin" = "اعتبارنامههای ادمین"
|
||||||
"twoFactor" = "احراز هویت دو مرحلهای"
|
"twoFactor" = "احراز هویت دو مرحلهای"
|
||||||
"twoFactorEnable" = "فعالسازی 2FA"
|
"twoFactorEnable" = "فعالسازی 2FA"
|
||||||
"twoFactorEnableDesc" = "یک لایه اضافی امنیتی برای احراز هویت فراهم میکند."
|
"twoFactorEnableDesc" = "یک لایه اضافی امنیتی برای احراز هویت فراهم میکند."
|
||||||
"twoFactorModalSetTitle" = "فعالسازی احراز هویت دو مرحلهای"
|
"twoFactorModalSetTitle" = "فعالسازی احراز هویت دو مرحلهای"
|
||||||
"twoFactorModalDeleteTitle" = "غیرفعالسازی احراز هویت دو مرحلهای"
|
"twoFactorModalDeleteTitle" = "غیرفعالسازی احراز هویت دو مرحلهای"
|
||||||
"twoFactorModalSteps" = "برای راهاندازی احراز هویت دو مرحلهای، مراحل زیر را انجام دهید:"
|
"twoFactorModalSteps" = "برای راهاندازی احراز هویت دو مرحلهای، مراحل زیر را انجام دهید:"
|
||||||
|
|
@ -663,6 +663,7 @@
|
||||||
"active" = "💡 فعال: {{ .Enable }}\r\n"
|
"active" = "💡 فعال: {{ .Enable }}\r\n"
|
||||||
"enabled" = "🚨 وضعیت: {{ .Enable }}\r\n"
|
"enabled" = "🚨 وضعیت: {{ .Enable }}\r\n"
|
||||||
"online" = "🌐 وضعیت اتصال: {{ .Status }}\r\n"
|
"online" = "🌐 وضعیت اتصال: {{ .Status }}\r\n"
|
||||||
|
"lastOnline" = "🔙 آخرین فعالیت: {{ .Time }}\r\n"
|
||||||
"email" = "📧 ایمیل: {{ .Email }}\r\n"
|
"email" = "📧 ایمیل: {{ .Email }}\r\n"
|
||||||
"upload" = "🔼 آپلود↑: {{ .Upload }}\r\n"
|
"upload" = "🔼 آپلود↑: {{ .Upload }}\r\n"
|
||||||
"download" = "🔽 دانلود↓: {{ .Download }}\r\n"
|
"download" = "🔽 دانلود↓: {{ .Download }}\r\n"
|
||||||
|
|
@ -688,9 +689,9 @@
|
||||||
"inbound_client_data_id" = "🔄 ورودی: {{ .InboundRemark }}\n\n🔑 شناسه: {{ .ClientId }}\n📧 ایمیل: {{ .ClientEmail }}\n📊 ترافیک: {{ .ClientTraffic }}\n📅 تاریخ انقضا: {{ .ClientExp }}\n🌐 محدودیت IP: {{ .IpLimit }}\n💬 توضیح: {{ .ClientComment }}\n\nاکنون میتونی مشتری را به ورودی اضافه کنی!"
|
"inbound_client_data_id" = "🔄 ورودی: {{ .InboundRemark }}\n\n🔑 شناسه: {{ .ClientId }}\n📧 ایمیل: {{ .ClientEmail }}\n📊 ترافیک: {{ .ClientTraffic }}\n📅 تاریخ انقضا: {{ .ClientExp }}\n🌐 محدودیت IP: {{ .IpLimit }}\n💬 توضیح: {{ .ClientComment }}\n\nاکنون میتونی مشتری را به ورودی اضافه کنی!"
|
||||||
"inbound_client_data_pass" = "🔄 ورودی: {{ .InboundRemark }}\n\n🔑 رمز عبور: {{ .ClientPass }}\n📧 ایمیل: {{ .ClientEmail }}\n📊 ترافیک: {{ .ClientTraffic }}\n📅 تاریخ انقضا: {{ .ClientExp }}\n🌐 محدودیت IP: {{ .IpLimit }}\n💬 توضیح: {{ .ClientComment }}\n\nاکنون میتونی مشتری را به ورودی اضافه کنی!"
|
"inbound_client_data_pass" = "🔄 ورودی: {{ .InboundRemark }}\n\n🔑 رمز عبور: {{ .ClientPass }}\n📧 ایمیل: {{ .ClientEmail }}\n📊 ترافیک: {{ .ClientTraffic }}\n📅 تاریخ انقضا: {{ .ClientExp }}\n🌐 محدودیت IP: {{ .IpLimit }}\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" = "باشه، از مقدار پیشفرض استفاده میکنم. 😊"
|
||||||
"incorrect_input" ="ورودی شما معتبر نیست.\nعبارتها باید بدون فاصله باشند.\nمثال صحیح: aaaaaa\nمثال نادرست: aaa aaa 🚫"
|
"incorrect_input" = "ورودی شما معتبر نیست.\nعبارتها باید بدون فاصله باشند.\nمثال صحیح: aaaaaa\nمثال نادرست: aaa aaa 🚫"
|
||||||
"AreYouSure" = "مطمئنی؟ 🤔"
|
"AreYouSure" = "مطمئنی؟ 🤔"
|
||||||
"SuccessResetTraffic" = "📧 ایمیل: {{ .ClientEmail }}\n🏁 نتیجه: ✅ موفقیتآمیز"
|
"SuccessResetTraffic" = "📧 ایمیل: {{ .ClientEmail }}\n🏁 نتیجه: ✅ موفقیتآمیز"
|
||||||
"FailedResetTraffic" = "📧 ایمیل: {{ .ClientEmail }}\n🏁 نتیجه: ❌ ناموفق \n\n🛠️ خطا: [ {{ .ErrorMessage }} ]"
|
"FailedResetTraffic" = "📧 ایمیل: {{ .ClientEmail }}\n🏁 نتیجه: ❌ ناموفق \n\n🛠️ خطا: [ {{ .ErrorMessage }} ]"
|
||||||
|
|
|
||||||
|
|
@ -106,12 +106,12 @@
|
||||||
"invalidFormData" = "Format data input tidak valid."
|
"invalidFormData" = "Format data input tidak valid."
|
||||||
"emptyUsername" = "Nama Pengguna diperlukan"
|
"emptyUsername" = "Nama Pengguna diperlukan"
|
||||||
"emptyPassword" = "Kata Sandi diperlukan"
|
"emptyPassword" = "Kata Sandi diperlukan"
|
||||||
"wrongUsernameOrPassword" = "Username, kata sandi, atau kode dua faktor tidak valid."
|
"wrongUsernameOrPassword" = "Username, kata sandi, atau kode dua faktor tidak valid."
|
||||||
"successLogin" = "Anda telah berhasil masuk ke akun Anda."
|
"successLogin" = "Anda telah berhasil masuk ke akun Anda."
|
||||||
|
|
||||||
[pages.index]
|
[pages.index]
|
||||||
"title" = "Ikhtisar"
|
"title" = "Ikhtisar"
|
||||||
"cpu" = "CPU"
|
"cpu" = "CPU"
|
||||||
"logicalProcessors" = "Prosesor logis"
|
"logicalProcessors" = "Prosesor logis"
|
||||||
"frequency" = "Frekuensi"
|
"frequency" = "Frekuensi"
|
||||||
"swap" = "Swap"
|
"swap" = "Swap"
|
||||||
|
|
@ -663,6 +663,7 @@
|
||||||
"active" = "💡 Aktif: {{ .Enable }}\r\n"
|
"active" = "💡 Aktif: {{ .Enable }}\r\n"
|
||||||
"enabled" = "🚨 Diaktifkan: {{ .Enable }}\r\n"
|
"enabled" = "🚨 Diaktifkan: {{ .Enable }}\r\n"
|
||||||
"online" = "🌐 Status Koneksi: {{ .Status }}\r\n"
|
"online" = "🌐 Status Koneksi: {{ .Status }}\r\n"
|
||||||
|
"lastOnline" = "🔙 Terakhir online: {{ .Time }}\r\n"
|
||||||
"email" = "📧 Email: {{ .Email }}\r\n"
|
"email" = "📧 Email: {{ .Email }}\r\n"
|
||||||
"upload" = "🔼 Unggah: ↑{{ .Upload }}\r\n"
|
"upload" = "🔼 Unggah: ↑{{ .Upload }}\r\n"
|
||||||
"download" = "🔽 Unduh: ↓{{ .Download }}\r\n"
|
"download" = "🔽 Unduh: ↓{{ .Download }}\r\n"
|
||||||
|
|
@ -688,9 +689,9 @@
|
||||||
"inbound_client_data_id" = "🔄 Masuk: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Email: {{ .ClientEmail }}\n📊 Lalu lintas: {{ .ClientTraffic }}\n📅 Tanggal Kedaluwarsa: {{ .ClientExp }}\n🌐 Batas IP: {{ .IpLimit }}\n💬 Komentar: {{ .ClientComment }}\n\nSekarang kamu bisa menambahkan klien ke inbound!"
|
"inbound_client_data_id" = "🔄 Masuk: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Email: {{ .ClientEmail }}\n📊 Lalu lintas: {{ .ClientTraffic }}\n📅 Tanggal Kedaluwarsa: {{ .ClientExp }}\n🌐 Batas IP: {{ .IpLimit }}\n💬 Komentar: {{ .ClientComment }}\n\nSekarang kamu bisa menambahkan klien ke inbound!"
|
||||||
"inbound_client_data_pass" = "🔄 Masuk: {{ .InboundRemark }}\n\n🔑 Kata sandi: {{ .ClientPass }}\n📧 Email: {{ .ClientEmail }}\n📊 Lalu lintas: {{ .ClientTraffic }}\n📅 Tanggal Kedaluwarsa: {{ .ClientExp }}\n🌐 Batas IP: {{ .IpLimit }}\n💬 Komentar: {{ .ClientComment }}\n\nSekarang kamu bisa menambahkan klien ke inbound!"
|
"inbound_client_data_pass" = "🔄 Masuk: {{ .InboundRemark }}\n\n🔑 Kata sandi: {{ .ClientPass }}\n📧 Email: {{ .ClientEmail }}\n📊 Lalu lintas: {{ .ClientTraffic }}\n📅 Tanggal Kedaluwarsa: {{ .ClientExp }}\n🌐 Batas IP: {{ .IpLimit }}\n💬 Komentar: {{ .ClientComment }}\n\nSekarang kamu bisa menambahkan klien ke inbound!"
|
||||||
"cancel" = "❌ Proses Dibatalkan! \n\nAnda dapat /start lagi kapan saja. 🔄"
|
"cancel" = "❌ Proses Dibatalkan! \n\nAnda dapat /start lagi kapan saja. 🔄"
|
||||||
"error_add_client" = "⚠️ Kesalahan:\n\n {{ .error }}"
|
"error_add_client" = "⚠️ Kesalahan:\n\n {{ .error }}"
|
||||||
"using_default_value" = "Oke, saya akan tetap menggunakan nilai default. 😊"
|
"using_default_value" = "Oke, saya akan tetap menggunakan nilai default. 😊"
|
||||||
"incorrect_input" ="Masukan Anda tidak valid.\nFrasa harus berlanjut tanpa spasi.\nContoh benar: aaaaaa\nContoh salah: aaa aaa 🚫"
|
"incorrect_input" = "Masukan Anda tidak valid.\nFrasa harus berlanjut tanpa spasi.\nContoh benar: aaaaaa\nContoh salah: aaa aaa 🚫"
|
||||||
"AreYouSure" = "Apakah kamu yakin? 🤔"
|
"AreYouSure" = "Apakah kamu yakin? 🤔"
|
||||||
"SuccessResetTraffic" = "📧 Email: {{ .ClientEmail }}\n🏁 Hasil: ✅ Berhasil"
|
"SuccessResetTraffic" = "📧 Email: {{ .ClientEmail }}\n🏁 Hasil: ✅ Berhasil"
|
||||||
"FailedResetTraffic" = "📧 Email: {{ .ClientEmail }}\n🏁 Hasil: ❌ Gagal \n\n🛠️ Kesalahan: [ {{ .ErrorMessage }} ]"
|
"FailedResetTraffic" = "📧 Email: {{ .ClientEmail }}\n🏁 Hasil: ❌ Gagal \n\n🛠️ Kesalahan: [ {{ .ErrorMessage }} ]"
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@
|
||||||
"invalidFormData" = "データ形式エラー"
|
"invalidFormData" = "データ形式エラー"
|
||||||
"emptyUsername" = "ユーザー名を入力してください"
|
"emptyUsername" = "ユーザー名を入力してください"
|
||||||
"emptyPassword" = "パスワードを入力してください"
|
"emptyPassword" = "パスワードを入力してください"
|
||||||
"wrongUsernameOrPassword" = "ユーザー名、パスワード、または二段階認証コードが無効です。"
|
"wrongUsernameOrPassword" = "ユーザー名、パスワード、または二段階認証コードが無効です。"
|
||||||
"successLogin" = "アカウントに正常にログインしました。"
|
"successLogin" = "アカウントに正常にログインしました。"
|
||||||
|
|
||||||
[pages.index]
|
[pages.index]
|
||||||
|
|
@ -565,9 +565,9 @@
|
||||||
|
|
||||||
[pages.settings.security]
|
[pages.settings.security]
|
||||||
"admin" = "管理者の資格情報"
|
"admin" = "管理者の資格情報"
|
||||||
"twoFactor" = "二段階認証"
|
"twoFactor" = "二段階認証"
|
||||||
"twoFactorEnable" = "2FAを有効化"
|
"twoFactorEnable" = "2FAを有効化"
|
||||||
"twoFactorEnableDesc" = "セキュリティを強化するために追加の認証層を追加します。"
|
"twoFactorEnableDesc" = "セキュリティを強化するために追加の認証層を追加します。"
|
||||||
"twoFactorModalSetTitle" = "二段階認証を有効にする"
|
"twoFactorModalSetTitle" = "二段階認証を有効にする"
|
||||||
"twoFactorModalDeleteTitle" = "二段階認証を無効にする"
|
"twoFactorModalDeleteTitle" = "二段階認証を無効にする"
|
||||||
"twoFactorModalSteps" = "二段階認証を設定するには、次の手順を実行してください:"
|
"twoFactorModalSteps" = "二段階認証を設定するには、次の手順を実行してください:"
|
||||||
|
|
@ -663,6 +663,7 @@
|
||||||
"active" = "💡 有効:{{ .Enable }}\r\n"
|
"active" = "💡 有効:{{ .Enable }}\r\n"
|
||||||
"enabled" = "🚨 有効化済み:{{ .Enable }}\r\n"
|
"enabled" = "🚨 有効化済み:{{ .Enable }}\r\n"
|
||||||
"online" = "🌐 接続ステータス:{{ .Status }}\r\n"
|
"online" = "🌐 接続ステータス:{{ .Status }}\r\n"
|
||||||
|
"lastOnline" = "🔙 最終オンライン: {{ .Time }}\r\n"
|
||||||
"email" = "📧 メール:{{ .Email }}\r\n"
|
"email" = "📧 メール:{{ .Email }}\r\n"
|
||||||
"upload" = "🔼 アップロード↑:{{ .Upload }}\r\n"
|
"upload" = "🔼 アップロード↑:{{ .Upload }}\r\n"
|
||||||
"download" = "🔽 ダウンロード↓:{{ .Download }}\r\n"
|
"download" = "🔽 ダウンロード↓:{{ .Download }}\r\n"
|
||||||
|
|
@ -688,9 +689,9 @@
|
||||||
"inbound_client_data_id" = "🔄 インバウンド: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 メール: {{ .ClientEmail }}\n📊 トラフィック: {{ .ClientTraffic }}\n📅 有効期限: {{ .ClientExp }}\n🌐 IP制限: {{ .IpLimit }}\n💬 コメント: {{ .ClientComment }}\n\n今すぐこのクライアントをインバウンドに追加できます!"
|
"inbound_client_data_id" = "🔄 インバウンド: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 メール: {{ .ClientEmail }}\n📊 トラフィック: {{ .ClientTraffic }}\n📅 有効期限: {{ .ClientExp }}\n🌐 IP制限: {{ .IpLimit }}\n💬 コメント: {{ .ClientComment }}\n\n今すぐこのクライアントをインバウンドに追加できます!"
|
||||||
"inbound_client_data_pass" = "🔄 インバウンド: {{ .InboundRemark }}\n\n🔑 パスワード: {{ .ClientPass }}\n📧 メール: {{ .ClientEmail }}\n📊 トラフィック: {{ .ClientTraffic }}\n📅 有効期限: {{ .ClientExp }}\n🌐 IP制限: {{ .IpLimit }}\n💬 コメント: {{ .ClientComment }}\n\n今すぐこのクライアントをインバウンドに追加できます!"
|
"inbound_client_data_pass" = "🔄 インバウンド: {{ .InboundRemark }}\n\n🔑 パスワード: {{ .ClientPass }}\n📧 メール: {{ .ClientEmail }}\n📊 トラフィック: {{ .ClientTraffic }}\n📅 有効期限: {{ .ClientExp }}\n🌐 IP制限: {{ .IpLimit }}\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" = "わかりました、デフォルト値を使用します。 😊"
|
||||||
"incorrect_input" ="入力が無効です。\nフレーズはスペースなしで続けて入力してください。\n正しい例: aaaaaa\n間違った例: aaa aaa 🚫"
|
"incorrect_input" = "入力が無効です。\nフレーズはスペースなしで続けて入力してください。\n正しい例: aaaaaa\n間違った例: aaa aaa 🚫"
|
||||||
"AreYouSure" = "本当にいいですか?🤔"
|
"AreYouSure" = "本当にいいですか?🤔"
|
||||||
"SuccessResetTraffic" = "📧 メール: {{ .ClientEmail }}\n🏁 結果: ✅ 成功"
|
"SuccessResetTraffic" = "📧 メール: {{ .ClientEmail }}\n🏁 結果: ✅ 成功"
|
||||||
"FailedResetTraffic" = "📧 メール: {{ .ClientEmail }}\n🏁 結果: ❌ 失敗 \n\n🛠️ エラー: [ {{ .ErrorMessage }} ]"
|
"FailedResetTraffic" = "📧 メール: {{ .ClientEmail }}\n🏁 結果: ❌ 失敗 \n\n🛠️ エラー: [ {{ .ErrorMessage }} ]"
|
||||||
|
|
|
||||||
|
|
@ -106,12 +106,12 @@
|
||||||
"invalidFormData" = "O formato dos dados de entrada é inválido."
|
"invalidFormData" = "O formato dos dados de entrada é inválido."
|
||||||
"emptyUsername" = "Nome de usuário é obrigatório"
|
"emptyUsername" = "Nome de usuário é obrigatório"
|
||||||
"emptyPassword" = "Senha é obrigatória"
|
"emptyPassword" = "Senha é obrigatória"
|
||||||
"wrongUsernameOrPassword" = "Nome de usuário, senha ou código de dois fatores inválido."
|
"wrongUsernameOrPassword" = "Nome de usuário, senha ou código de dois fatores inválido."
|
||||||
"successLogin" = "Você entrou na sua conta com sucesso."
|
"successLogin" = "Você entrou na sua conta com sucesso."
|
||||||
|
|
||||||
[pages.index]
|
[pages.index]
|
||||||
"title" = "Visão Geral"
|
"title" = "Visão Geral"
|
||||||
"cpu" = "CPU"
|
"cpu" = "CPU"
|
||||||
"logicalProcessors" = "Processadores lógicos"
|
"logicalProcessors" = "Processadores lógicos"
|
||||||
"frequency" = "Frequência"
|
"frequency" = "Frequência"
|
||||||
"swap" = "Swap"
|
"swap" = "Swap"
|
||||||
|
|
@ -565,9 +565,9 @@
|
||||||
|
|
||||||
[pages.settings.security]
|
[pages.settings.security]
|
||||||
"admin" = "Credenciais de administrador"
|
"admin" = "Credenciais de administrador"
|
||||||
"twoFactor" = "Autenticação de dois fatores"
|
"twoFactor" = "Autenticação de dois fatores"
|
||||||
"twoFactorEnable" = "Ativar 2FA"
|
"twoFactorEnable" = "Ativar 2FA"
|
||||||
"twoFactorEnableDesc" = "Adiciona uma camada extra de autenticação para mais segurança."
|
"twoFactorEnableDesc" = "Adiciona uma camada extra de autenticação para mais segurança."
|
||||||
"twoFactorModalSetTitle" = "Ativar autenticação de dois fatores"
|
"twoFactorModalSetTitle" = "Ativar autenticação de dois fatores"
|
||||||
"twoFactorModalDeleteTitle" = "Desativar autenticação de dois fatores"
|
"twoFactorModalDeleteTitle" = "Desativar autenticação de dois fatores"
|
||||||
"twoFactorModalSteps" = "Para configurar a autenticação de dois fatores, siga alguns passos:"
|
"twoFactorModalSteps" = "Para configurar a autenticação de dois fatores, siga alguns passos:"
|
||||||
|
|
@ -663,6 +663,7 @@
|
||||||
"active" = "💡 Ativo: {{ .Enable }}\r\n"
|
"active" = "💡 Ativo: {{ .Enable }}\r\n"
|
||||||
"enabled" = "🚨 Ativado: {{ .Enable }}\r\n"
|
"enabled" = "🚨 Ativado: {{ .Enable }}\r\n"
|
||||||
"online" = "🌐 Status da conexão: {{ .Status }}\r\n"
|
"online" = "🌐 Status da conexão: {{ .Status }}\r\n"
|
||||||
|
"lastOnline" = "🔙 Última vez online: {{ .Time }}\r\n"
|
||||||
"email" = "📧 Email: {{ .Email }}\r\n"
|
"email" = "📧 Email: {{ .Email }}\r\n"
|
||||||
"upload" = "🔼 Upload: ↑{{ .Upload }}\r\n"
|
"upload" = "🔼 Upload: ↑{{ .Upload }}\r\n"
|
||||||
"download" = "🔽 Download: ↓{{ .Download }}\r\n"
|
"download" = "🔽 Download: ↓{{ .Download }}\r\n"
|
||||||
|
|
@ -688,9 +689,9 @@
|
||||||
"inbound_client_data_id" = "🔄 Entrada: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Email: {{ .ClientEmail }}\n📊 Tráfego: {{ .ClientTraffic }}\n📅 Data de expiração: {{ .ClientExp }}\n🌐 Limite de IP: {{ .IpLimit }}\n💬 Comentário: {{ .ClientComment }}\n\nAgora você pode adicionar o cliente à entrada!"
|
"inbound_client_data_id" = "🔄 Entrada: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Email: {{ .ClientEmail }}\n📊 Tráfego: {{ .ClientTraffic }}\n📅 Data de expiração: {{ .ClientExp }}\n🌐 Limite de IP: {{ .IpLimit }}\n💬 Comentário: {{ .ClientComment }}\n\nAgora você pode adicionar o cliente à entrada!"
|
||||||
"inbound_client_data_pass" = "🔄 Entrada: {{ .InboundRemark }}\n\n🔑 Senha: {{ .ClientPass }}\n📧 Email: {{ .ClientEmail }}\n📊 Tráfego: {{ .ClientTraffic }}\n📅 Data de expiração: {{ .ClientExp }}\n🌐 Limite de IP: {{ .IpLimit }}\n💬 Comentário: {{ .ClientComment }}\n\nAgora você pode adicionar o cliente à entrada!"
|
"inbound_client_data_pass" = "🔄 Entrada: {{ .InboundRemark }}\n\n🔑 Senha: {{ .ClientPass }}\n📧 Email: {{ .ClientEmail }}\n📊 Tráfego: {{ .ClientTraffic }}\n📅 Data de expiração: {{ .ClientExp }}\n🌐 Limite de IP: {{ .IpLimit }}\n💬 Comentário: {{ .ClientComment }}\n\nAgora você pode adicionar o cliente à entrada!"
|
||||||
"cancel" = "❌ Processo Cancelado! \n\nVocê pode iniciar novamente a qualquer momento com /start. 🔄"
|
"cancel" = "❌ Processo Cancelado! \n\nVocê pode iniciar novamente a qualquer momento com /start. 🔄"
|
||||||
"error_add_client" = "⚠️ Erro:\n\n {{ .error }}"
|
"error_add_client" = "⚠️ Erro:\n\n {{ .error }}"
|
||||||
"using_default_value" = "Tudo bem, vou manter o valor padrão. 😊"
|
"using_default_value" = "Tudo bem, vou manter o valor padrão. 😊"
|
||||||
"incorrect_input" ="Sua entrada não é válida.\nAs frases devem ser contínuas, sem espaços.\nExemplo correto: aaaaaa\nExemplo incorreto: aaa aaa 🚫"
|
"incorrect_input" = "Sua entrada não é válida.\nAs frases devem ser contínuas, sem espaços.\nExemplo correto: aaaaaa\nExemplo incorreto: aaa aaa 🚫"
|
||||||
"AreYouSure" = "Você tem certeza? 🤔"
|
"AreYouSure" = "Você tem certeza? 🤔"
|
||||||
"SuccessResetTraffic" = "📧 Email: {{ .ClientEmail }}\n🏁 Resultado: ✅ Sucesso"
|
"SuccessResetTraffic" = "📧 Email: {{ .ClientEmail }}\n🏁 Resultado: ✅ Sucesso"
|
||||||
"FailedResetTraffic" = "📧 Email: {{ .ClientEmail }}\n🏁 Resultado: ❌ Falhou \n\n🛠️ Erro: [ {{ .ErrorMessage }} ]"
|
"FailedResetTraffic" = "📧 Email: {{ .ClientEmail }}\n🏁 Resultado: ❌ Falhou \n\n🛠️ Erro: [ {{ .ErrorMessage }} ]"
|
||||||
|
|
|
||||||
|
|
@ -663,6 +663,7 @@
|
||||||
"active" = "💡 Активен: {{ .Enable }}\r\n"
|
"active" = "💡 Активен: {{ .Enable }}\r\n"
|
||||||
"enabled" = "🚨 Активен: {{ .Enable }}\r\n"
|
"enabled" = "🚨 Активен: {{ .Enable }}\r\n"
|
||||||
"online" = "🌐 Статус соединения: {{ .Status }}\r\n"
|
"online" = "🌐 Статус соединения: {{ .Status }}\r\n"
|
||||||
|
"lastOnline" = "🔙 Был(а) в сети: {{ .Time }}\r\n"
|
||||||
"email" = "📧 Email: {{ .Email }}\r\n"
|
"email" = "📧 Email: {{ .Email }}\r\n"
|
||||||
"upload" = "🔼 Исходящий трафик: ↑{{ .Upload }}\r\n"
|
"upload" = "🔼 Исходящий трафик: ↑{{ .Upload }}\r\n"
|
||||||
"download" = "🔽 Входящий трафик: ↓{{ .Download }}\r\n"
|
"download" = "🔽 Входящий трафик: ↓{{ .Download }}\r\n"
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@
|
||||||
"invalidFormData" = "Girdi verisi formatı geçersiz."
|
"invalidFormData" = "Girdi verisi formatı geçersiz."
|
||||||
"emptyUsername" = "Kullanıcı adı gerekli"
|
"emptyUsername" = "Kullanıcı adı gerekli"
|
||||||
"emptyPassword" = "Şifre gerekli"
|
"emptyPassword" = "Şifre gerekli"
|
||||||
"wrongUsernameOrPassword" = "Geçersiz kullanıcı adı, şifre veya iki adımlı doğrulama kodu."
|
"wrongUsernameOrPassword" = "Geçersiz kullanıcı adı, şifre veya iki adımlı doğrulama kodu."
|
||||||
"successLogin" = "Hesabınıza başarıyla giriş yaptınız."
|
"successLogin" = "Hesabınıza başarıyla giriş yaptınız."
|
||||||
|
|
||||||
[pages.index]
|
[pages.index]
|
||||||
|
|
@ -565,9 +565,9 @@
|
||||||
|
|
||||||
[pages.settings.security]
|
[pages.settings.security]
|
||||||
"admin" = "Yönetici kimlik bilgileri"
|
"admin" = "Yönetici kimlik bilgileri"
|
||||||
"twoFactor" = "İki adımlı doğrulama"
|
"twoFactor" = "İki adımlı doğrulama"
|
||||||
"twoFactorEnable" = "2FA'yı Etkinleştir"
|
"twoFactorEnable" = "2FA'yı Etkinleştir"
|
||||||
"twoFactorEnableDesc" = "Daha fazla güvenlik için ek bir doğrulama katmanı ekler."
|
"twoFactorEnableDesc" = "Daha fazla güvenlik için ek bir doğrulama katmanı ekler."
|
||||||
"twoFactorModalSetTitle" = "İki adımlı doğrulamayı etkinleştir"
|
"twoFactorModalSetTitle" = "İki adımlı doğrulamayı etkinleştir"
|
||||||
"twoFactorModalDeleteTitle" = "İki adımlı doğrulamayı devre dışı bırak"
|
"twoFactorModalDeleteTitle" = "İki adımlı doğrulamayı devre dışı bırak"
|
||||||
"twoFactorModalSteps" = "İki adımlı doğrulamayı ayarlamak için şu adımları izleyin:"
|
"twoFactorModalSteps" = "İki adımlı doğrulamayı ayarlamak için şu adımları izleyin:"
|
||||||
|
|
@ -663,6 +663,7 @@
|
||||||
"active" = "💡 Aktif: {{ .Enable }}\r\n"
|
"active" = "💡 Aktif: {{ .Enable }}\r\n"
|
||||||
"enabled" = "🚨 Etkin: {{ .Enable }}\r\n"
|
"enabled" = "🚨 Etkin: {{ .Enable }}\r\n"
|
||||||
"online" = "🌐 Bağlantı durumu: {{ .Status }}\r\n"
|
"online" = "🌐 Bağlantı durumu: {{ .Status }}\r\n"
|
||||||
|
"lastOnline" = "🔙 Son çevrimiçi: {{ .Time }}\r\n"
|
||||||
"email" = "📧 E-posta: {{ .Email }}\r\n"
|
"email" = "📧 E-posta: {{ .Email }}\r\n"
|
||||||
"upload" = "🔼 Yükleme: ↑{{ .Upload }}\r\n"
|
"upload" = "🔼 Yükleme: ↑{{ .Upload }}\r\n"
|
||||||
"download" = "🔽 İndirme: ↓{{ .Download }}\r\n"
|
"download" = "🔽 İndirme: ↓{{ .Download }}\r\n"
|
||||||
|
|
@ -688,9 +689,9 @@
|
||||||
"inbound_client_data_id" = "🔄 Giriş: {{ .InboundRemark }}\n\n🔑 Kimlik: {{ .ClientId }}\n📧 E-posta: {{ .ClientEmail }}\n📊 Trafik: {{ .ClientTraffic }}\n📅 Bitiş Tarihi: {{ .ClientExp }}\n🌐 IP Sınırı: {{ .IpLimit }}\n💬 Yorum: {{ .ClientComment }}\n\nArtık bu müşteriyi girişe ekleyebilirsin!"
|
"inbound_client_data_id" = "🔄 Giriş: {{ .InboundRemark }}\n\n🔑 Kimlik: {{ .ClientId }}\n📧 E-posta: {{ .ClientEmail }}\n📊 Trafik: {{ .ClientTraffic }}\n📅 Bitiş Tarihi: {{ .ClientExp }}\n🌐 IP Sınırı: {{ .IpLimit }}\n💬 Yorum: {{ .ClientComment }}\n\nArtık bu müşteriyi girişe ekleyebilirsin!"
|
||||||
"inbound_client_data_pass" = "🔄 Giriş: {{ .InboundRemark }}\n\n🔑 Şifre: {{ .ClientPass }}\n📧 E-posta: {{ .ClientEmail }}\n📊 Trafik: {{ .ClientTraffic }}\n📅 Bitiş Tarihi: {{ .ClientExp }}\n🌐 IP Sınırı: {{ .IpLimit }}\n💬 Yorum: {{ .ClientComment }}\n\nArtık bu müşteriyi girişe ekleyebilirsin!"
|
"inbound_client_data_pass" = "🔄 Giriş: {{ .InboundRemark }}\n\n🔑 Şifre: {{ .ClientPass }}\n📧 E-posta: {{ .ClientEmail }}\n📊 Trafik: {{ .ClientTraffic }}\n📅 Bitiş Tarihi: {{ .ClientExp }}\n🌐 IP Sınırı: {{ .IpLimit }}\n💬 Yorum: {{ .ClientComment }}\n\nArtık bu müşteriyi girişe ekleyebilirsin!"
|
||||||
"cancel" = "❌ İşlem iptal edildi! \n\nİstediğiniz zaman /start ile yeniden başlayabilirsiniz. 🔄"
|
"cancel" = "❌ İşlem iptal edildi! \n\nİstediğiniz zaman /start ile yeniden başlayabilirsiniz. 🔄"
|
||||||
"error_add_client" = "⚠️ Hata:\n\n {{ .error }}"
|
"error_add_client" = "⚠️ Hata:\n\n {{ .error }}"
|
||||||
"using_default_value" = "Tamam, varsayılan değeri kullanacağım. 😊"
|
"using_default_value" = "Tamam, varsayılan değeri kullanacağım. 😊"
|
||||||
"incorrect_input" ="Girdiğiniz değer geçerli değil.\nKelime öbekleri boşluk olmadan devam etmelidir.\nDoğru örnek: aaaaaa\nYanlış örnek: aaa aaa 🚫"
|
"incorrect_input" = "Girdiğiniz değer geçerli değil.\nKelime öbekleri boşluk olmadan devam etmelidir.\nDoğru örnek: aaaaaa\nYanlış örnek: aaa aaa 🚫"
|
||||||
"AreYouSure" = "Emin misin? 🤔"
|
"AreYouSure" = "Emin misin? 🤔"
|
||||||
"SuccessResetTraffic" = "📧 E-posta: {{ .ClientEmail }}\n🏁 Sonuç: ✅ Başarılı"
|
"SuccessResetTraffic" = "📧 E-posta: {{ .ClientEmail }}\n🏁 Sonuç: ✅ Başarılı"
|
||||||
"FailedResetTraffic" = "📧 E-posta: {{ .ClientEmail }}\n🏁 Sonuç: ❌ Başarısız \n\n🛠️ Hata: [ {{ .ErrorMessage }} ]"
|
"FailedResetTraffic" = "📧 E-posta: {{ .ClientEmail }}\n🏁 Sonuç: ❌ Başarısız \n\n🛠️ Hata: [ {{ .ErrorMessage }} ]"
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@
|
||||||
"invalidFormData" = "Формат вхідних даних недійсний."
|
"invalidFormData" = "Формат вхідних даних недійсний."
|
||||||
"emptyUsername" = "Потрібне ім'я користувача"
|
"emptyUsername" = "Потрібне ім'я користувача"
|
||||||
"emptyPassword" = "Потрібен пароль"
|
"emptyPassword" = "Потрібен пароль"
|
||||||
"wrongUsernameOrPassword" = "Невірне ім’я користувача, пароль або код двофакторної аутентифікації."
|
"wrongUsernameOrPassword" = "Невірне ім’я користувача, пароль або код двофакторної аутентифікації."
|
||||||
"successLogin" = "Ви успішно увійшли до свого облікового запису."
|
"successLogin" = "Ви успішно увійшли до свого облікового запису."
|
||||||
|
|
||||||
[pages.index]
|
[pages.index]
|
||||||
|
|
@ -565,9 +565,9 @@
|
||||||
|
|
||||||
[pages.settings.security]
|
[pages.settings.security]
|
||||||
"admin" = "Облікові дані адміністратора"
|
"admin" = "Облікові дані адміністратора"
|
||||||
"twoFactor" = "Двофакторна аутентифікація"
|
"twoFactor" = "Двофакторна аутентифікація"
|
||||||
"twoFactorEnable" = "Увімкнути 2FA"
|
"twoFactorEnable" = "Увімкнути 2FA"
|
||||||
"twoFactorEnableDesc" = "Додає додатковий рівень аутентифікації для підвищення безпеки."
|
"twoFactorEnableDesc" = "Додає додатковий рівень аутентифікації для підвищення безпеки."
|
||||||
"twoFactorModalSetTitle" = "Увімкнути двофакторну аутентифікацію"
|
"twoFactorModalSetTitle" = "Увімкнути двофакторну аутентифікацію"
|
||||||
"twoFactorModalDeleteTitle" = "Вимкнути двофакторну аутентифікацію"
|
"twoFactorModalDeleteTitle" = "Вимкнути двофакторну аутентифікацію"
|
||||||
"twoFactorModalSteps" = "Щоб налаштувати двофакторну аутентифікацію, виконайте кілька кроків:"
|
"twoFactorModalSteps" = "Щоб налаштувати двофакторну аутентифікацію, виконайте кілька кроків:"
|
||||||
|
|
@ -663,6 +663,7 @@
|
||||||
"active" = "💡 Активний: {{ .Enable }}\r\n"
|
"active" = "💡 Активний: {{ .Enable }}\r\n"
|
||||||
"enabled" = "🚨 Увімкнено: {{ .Enable }}\r\n"
|
"enabled" = "🚨 Увімкнено: {{ .Enable }}\r\n"
|
||||||
"online" = "🌐 Стан підключення: {{ .Status }}\r\n"
|
"online" = "🌐 Стан підключення: {{ .Status }}\r\n"
|
||||||
|
"lastOnline" = "🔙 Був(ла) онлайн: {{ .Time }}\r\n"
|
||||||
"email" = "📧 Електронна пошта: {{ .Email }}\r\n"
|
"email" = "📧 Електронна пошта: {{ .Email }}\r\n"
|
||||||
"upload" = "🔼 Upload: ↑{{ .Upload }}\r\n"
|
"upload" = "🔼 Upload: ↑{{ .Upload }}\r\n"
|
||||||
"download" = "🔽 Download: ↓{{ .Download }}\r\n"
|
"download" = "🔽 Download: ↓{{ .Download }}\r\n"
|
||||||
|
|
@ -688,9 +689,9 @@
|
||||||
"inbound_client_data_id" = "🔄 Вхід: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Електронна пошта: {{ .ClientEmail }}\n📊 Трафік: {{ .ClientTraffic }}\n📅 Дата завершення: {{ .ClientExp }}\n🌐 Обмеження IP: {{ .IpLimit }}\n💬 Коментар: {{ .ClientComment }}\n\nТепер ви можете додати клієнта до вхідного з'єднання!"
|
"inbound_client_data_id" = "🔄 Вхід: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Електронна пошта: {{ .ClientEmail }}\n📊 Трафік: {{ .ClientTraffic }}\n📅 Дата завершення: {{ .ClientExp }}\n🌐 Обмеження IP: {{ .IpLimit }}\n💬 Коментар: {{ .ClientComment }}\n\nТепер ви можете додати клієнта до вхідного з'єднання!"
|
||||||
"inbound_client_data_pass" = "🔄 Вхід: {{ .InboundRemark }}\n\n🔑 Пароль: {{ .ClientPass }}\n📧 Електронна пошта: {{ .ClientEmail }}\n📊 Трафік: {{ .ClientTraffic }}\n📅 Дата завершення: {{ .ClientExp }}\n🌐 Обмеження IP: {{ .IpLimit }}\n💬 Коментар: {{ .ClientComment }}\n\nТепер ви можете додати клієнта до вхідного з'єднання!"
|
"inbound_client_data_pass" = "🔄 Вхід: {{ .InboundRemark }}\n\n🔑 Пароль: {{ .ClientPass }}\n📧 Електронна пошта: {{ .ClientEmail }}\n📊 Трафік: {{ .ClientTraffic }}\n📅 Дата завершення: {{ .ClientExp }}\n🌐 Обмеження IP: {{ .IpLimit }}\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" = "Гаразд, залишу значення за замовчуванням. 😊"
|
||||||
"incorrect_input" ="Ваш ввід невірний.\nФрази повинні бути без пробілів.\nПравильний приклад: aaaaaa\nНеправильний приклад: aaa aaa 🚫"
|
"incorrect_input" = "Ваш ввід невірний.\nФрази повинні бути без пробілів.\nПравильний приклад: aaaaaa\nНеправильний приклад: aaa aaa 🚫"
|
||||||
"AreYouSure" = "Ви впевнені? 🤔"
|
"AreYouSure" = "Ви впевнені? 🤔"
|
||||||
"SuccessResetTraffic" = "📧 Електронна пошта: {{ .ClientEmail }}\n🏁 Результат: ✅ Успішно"
|
"SuccessResetTraffic" = "📧 Електронна пошта: {{ .ClientEmail }}\n🏁 Результат: ✅ Успішно"
|
||||||
"FailedResetTraffic" = "📧 Електронна пошта: {{ .ClientEmail }}\n🏁 Результат: ❌ Невдача \n\n🛠️ Помилка: [ {{ .ErrorMessage }} ]"
|
"FailedResetTraffic" = "📧 Електронна пошта: {{ .ClientEmail }}\n🏁 Результат: ❌ Невдача \n\n🛠️ Помилка: [ {{ .ErrorMessage }} ]"
|
||||||
|
|
|
||||||
|
|
@ -663,6 +663,7 @@
|
||||||
"active" = "💡 Đang hoạt động: {{ .Enable }}\r\n"
|
"active" = "💡 Đang hoạt động: {{ .Enable }}\r\n"
|
||||||
"enabled" = "🚨 Đã bật: {{ .Enable }}\r\n"
|
"enabled" = "🚨 Đã bật: {{ .Enable }}\r\n"
|
||||||
"online" = "🌐 Trạng thái kết nối: {{ .Status }}\r\n"
|
"online" = "🌐 Trạng thái kết nối: {{ .Status }}\r\n"
|
||||||
|
"lastOnline" = "🔙 Lần online gần nhất: {{ .Time }}\r\n"
|
||||||
"email" = "📧 Email: {{ .Email }}\r\n"
|
"email" = "📧 Email: {{ .Email }}\r\n"
|
||||||
"upload" = "🔼 Tải lên: ↑{{ .Upload }}\r\n"
|
"upload" = "🔼 Tải lên: ↑{{ .Upload }}\r\n"
|
||||||
"download" = "🔽 Tải xuống: ↓{{ .Download }}\r\n"
|
"download" = "🔽 Tải xuống: ↓{{ .Download }}\r\n"
|
||||||
|
|
@ -688,9 +689,9 @@
|
||||||
"inbound_client_data_id" = "🔄 Kết nối vào: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Email: {{ .ClientEmail }}\n📊 Dung lượng: {{ .ClientTraffic }}\n📅 Ngày hết hạn: {{ .ClientExp }}\n🌐 Giới hạn IP: {{ .IpLimit }}\n💬 Ghi chú: {{ .ClientComment }}\n\nBây giờ bạn có thể thêm khách hàng vào inbound!"
|
"inbound_client_data_id" = "🔄 Kết nối vào: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Email: {{ .ClientEmail }}\n📊 Dung lượng: {{ .ClientTraffic }}\n📅 Ngày hết hạn: {{ .ClientExp }}\n🌐 Giới hạn IP: {{ .IpLimit }}\n💬 Ghi chú: {{ .ClientComment }}\n\nBây giờ bạn có thể thêm khách hàng vào inbound!"
|
||||||
"inbound_client_data_pass" = "🔄 Kết nối vào: {{ .InboundRemark }}\n\n🔑 Mật khẩu: {{ .ClientPass }}\n📧 Email: {{ .ClientEmail }}\n📊 Dung lượng: {{ .ClientTraffic }}\n📅 Ngày hết hạn: {{ .ClientExp }}\n🌐 Giới hạn IP: {{ .IpLimit }}\n💬 Ghi chú: {{ .ClientComment }}\n\nBây giờ bạn có thể thêm khách hàng vào inbound!"
|
"inbound_client_data_pass" = "🔄 Kết nối vào: {{ .InboundRemark }}\n\n🔑 Mật khẩu: {{ .ClientPass }}\n📧 Email: {{ .ClientEmail }}\n📊 Dung lượng: {{ .ClientTraffic }}\n📅 Ngày hết hạn: {{ .ClientExp }}\n🌐 Giới hạn IP: {{ .IpLimit }}\n💬 Ghi chú: {{ .ClientComment }}\n\nBây giờ bạn có thể thêm khách hàng vào inbound!"
|
||||||
"cancel" = "❌ Quá trình đã bị hủy! \n\nBạn có thể bắt đầu lại bất cứ lúc nào bằng cách nhập /start. 🔄"
|
"cancel" = "❌ Quá trình đã bị hủy! \n\nBạn có thể bắt đầu lại bất cứ lúc nào bằng cách nhập /start. 🔄"
|
||||||
"error_add_client" = "⚠️ Lỗi:\n\n {{ .error }}"
|
"error_add_client" = "⚠️ Lỗi:\n\n {{ .error }}"
|
||||||
"using_default_value" = "Được rồi, tôi sẽ sử dụng giá trị mặc định. 😊"
|
"using_default_value" = "Được rồi, tôi sẽ sử dụng giá trị mặc định. 😊"
|
||||||
"incorrect_input" ="Dữ liệu bạn nhập không hợp lệ.\nCác chuỗi phải liền mạch và không có dấu cách.\nVí dụ đúng: aaaaaa\nVí dụ sai: aaa aaa 🚫"
|
"incorrect_input" = "Dữ liệu bạn nhập không hợp lệ.\nCác chuỗi phải liền mạch và không có dấu cách.\nVí dụ đúng: aaaaaa\nVí dụ sai: aaa aaa 🚫"
|
||||||
"AreYouSure" = "Bạn có chắc không? 🤔"
|
"AreYouSure" = "Bạn có chắc không? 🤔"
|
||||||
"SuccessResetTraffic" = "📧 Email: {{ .ClientEmail }}\n🏁 Kết quả: ✅ Thành công"
|
"SuccessResetTraffic" = "📧 Email: {{ .ClientEmail }}\n🏁 Kết quả: ✅ Thành công"
|
||||||
"FailedResetTraffic" = "📧 Email: {{ .ClientEmail }}\n🏁 Kết quả: ❌ Thất bại \n\n🛠️ Lỗi: [ {{ .ErrorMessage }} ]"
|
"FailedResetTraffic" = "📧 Email: {{ .ClientEmail }}\n🏁 Kết quả: ❌ Thất bại \n\n🛠️ Lỗi: [ {{ .ErrorMessage }} ]"
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@
|
||||||
"invalidFormData" = "数据格式错误"
|
"invalidFormData" = "数据格式错误"
|
||||||
"emptyUsername" = "请输入用户名"
|
"emptyUsername" = "请输入用户名"
|
||||||
"emptyPassword" = "请输入密码"
|
"emptyPassword" = "请输入密码"
|
||||||
"wrongUsernameOrPassword" = "用户名、密码或双重验证码无效。"
|
"wrongUsernameOrPassword" = "用户名、密码或双重验证码无效。"
|
||||||
"successLogin" = "您已成功登录您的账户。"
|
"successLogin" = "您已成功登录您的账户。"
|
||||||
|
|
||||||
[pages.index]
|
[pages.index]
|
||||||
|
|
@ -242,7 +242,7 @@
|
||||||
"same" = "相同"
|
"same" = "相同"
|
||||||
"inboundData" = "入站数据"
|
"inboundData" = "入站数据"
|
||||||
"exportInbound" = "导出入站规则"
|
"exportInbound" = "导出入站规则"
|
||||||
"import"="导入"
|
"import" = "导入"
|
||||||
"importInbound" = "导入入站规则"
|
"importInbound" = "导入入站规则"
|
||||||
"periodicTrafficResetTitle" = "流量重置"
|
"periodicTrafficResetTitle" = "流量重置"
|
||||||
"periodicTrafficResetDesc" = "按指定间隔自动重置流量计数器"
|
"periodicTrafficResetDesc" = "按指定间隔自动重置流量计数器"
|
||||||
|
|
@ -565,9 +565,9 @@
|
||||||
|
|
||||||
[pages.settings.security]
|
[pages.settings.security]
|
||||||
"admin" = "管理员凭据"
|
"admin" = "管理员凭据"
|
||||||
"twoFactor" = "双重验证"
|
"twoFactor" = "双重验证"
|
||||||
"twoFactorEnable" = "启用2FA"
|
"twoFactorEnable" = "启用2FA"
|
||||||
"twoFactorEnableDesc" = "增加额外的验证层以提高安全性。"
|
"twoFactorEnableDesc" = "增加额外的验证层以提高安全性。"
|
||||||
"twoFactorModalSetTitle" = "启用双重认证"
|
"twoFactorModalSetTitle" = "启用双重认证"
|
||||||
"twoFactorModalDeleteTitle" = "停用双重认证"
|
"twoFactorModalDeleteTitle" = "停用双重认证"
|
||||||
"twoFactorModalSteps" = "要设定双重认证,请执行以下步骤:"
|
"twoFactorModalSteps" = "要设定双重认证,请执行以下步骤:"
|
||||||
|
|
@ -663,6 +663,7 @@
|
||||||
"active" = "💡 激活:{{ .Enable }}\r\n"
|
"active" = "💡 激活:{{ .Enable }}\r\n"
|
||||||
"enabled" = "🚨 已启用:{{ .Enable }}\r\n"
|
"enabled" = "🚨 已启用:{{ .Enable }}\r\n"
|
||||||
"online" = "🌐 连接状态:{{ .Status }}\r\n"
|
"online" = "🌐 连接状态:{{ .Status }}\r\n"
|
||||||
|
"lastOnline" = "🔙 上次在线: {{ .Time }}\r\n"
|
||||||
"email" = "📧 邮箱:{{ .Email }}\r\n"
|
"email" = "📧 邮箱:{{ .Email }}\r\n"
|
||||||
"upload" = "🔼 上传↑:{{ .Upload }}\r\n"
|
"upload" = "🔼 上传↑:{{ .Upload }}\r\n"
|
||||||
"download" = "🔽 下载↓:{{ .Download }}\r\n"
|
"download" = "🔽 下载↓:{{ .Download }}\r\n"
|
||||||
|
|
@ -688,9 +689,9 @@
|
||||||
"inbound_client_data_id" = "🔄 入站: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 邮箱: {{ .ClientEmail }}\n📊 流量: {{ .ClientTraffic }}\n📅 到期日期: {{ .ClientExp }}\n🌐 IP 限制: {{ .IpLimit }}\n💬 备注: {{ .ClientComment }}\n\n你现在可以将客户添加到入站了!"
|
"inbound_client_data_id" = "🔄 入站: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 邮箱: {{ .ClientEmail }}\n📊 流量: {{ .ClientTraffic }}\n📅 到期日期: {{ .ClientExp }}\n🌐 IP 限制: {{ .IpLimit }}\n💬 备注: {{ .ClientComment }}\n\n你现在可以将客户添加到入站了!"
|
||||||
"inbound_client_data_pass" = "🔄 入站: {{ .InboundRemark }}\n\n🔑 密码: {{ .ClientPass }}\n📧 邮箱: {{ .ClientEmail }}\n📊 流量: {{ .ClientTraffic }}\n📅 到期日期: {{ .ClientExp }}\n🌐 IP 限制: {{ .IpLimit }}\n💬 备注: {{ .ClientComment }}\n\n你现在可以将客户添加到入站了!"
|
"inbound_client_data_pass" = "🔄 入站: {{ .InboundRemark }}\n\n🔑 密码: {{ .ClientPass }}\n📧 邮箱: {{ .ClientEmail }}\n📊 流量: {{ .ClientTraffic }}\n📅 到期日期: {{ .ClientExp }}\n🌐 IP 限制: {{ .IpLimit }}\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" = "好的,我会使用默认值。 😊"
|
||||||
"incorrect_input" ="您的输入无效。\n短语应连续输入,不能有空格。\n正确示例: aaaaaa\n错误示例: aaa aaa 🚫"
|
"incorrect_input" = "您的输入无效。\n短语应连续输入,不能有空格。\n正确示例: aaaaaa\n错误示例: aaa aaa 🚫"
|
||||||
"AreYouSure" = "你确定吗?🤔"
|
"AreYouSure" = "你确定吗?🤔"
|
||||||
"SuccessResetTraffic" = "📧 邮箱: {{ .ClientEmail }}\n🏁 结果: ✅ 成功"
|
"SuccessResetTraffic" = "📧 邮箱: {{ .ClientEmail }}\n🏁 结果: ✅ 成功"
|
||||||
"FailedResetTraffic" = "📧 邮箱: {{ .ClientEmail }}\n🏁 结果: ❌ 失败 \n\n🛠️ 错误: [ {{ .ErrorMessage }} ]"
|
"FailedResetTraffic" = "📧 邮箱: {{ .ClientEmail }}\n🏁 结果: ❌ 失败 \n\n🛠️ 错误: [ {{ .ErrorMessage }} ]"
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@
|
||||||
"invalidFormData" = "資料格式錯誤"
|
"invalidFormData" = "資料格式錯誤"
|
||||||
"emptyUsername" = "請輸入使用者名稱"
|
"emptyUsername" = "請輸入使用者名稱"
|
||||||
"emptyPassword" = "請輸入密碼"
|
"emptyPassword" = "請輸入密碼"
|
||||||
"wrongUsernameOrPassword" = "用戶名、密碼或雙重驗證碼無效。"
|
"wrongUsernameOrPassword" = "用戶名、密碼或雙重驗證碼無效。"
|
||||||
"successLogin" = "您已成功登入您的帳戶。"
|
"successLogin" = "您已成功登入您的帳戶。"
|
||||||
|
|
||||||
[pages.index]
|
[pages.index]
|
||||||
|
|
@ -242,7 +242,7 @@
|
||||||
"same" = "相同"
|
"same" = "相同"
|
||||||
"inboundData" = "入站資料"
|
"inboundData" = "入站資料"
|
||||||
"exportInbound" = "匯出入站規則"
|
"exportInbound" = "匯出入站規則"
|
||||||
"import"="匯入"
|
"import" = "匯入"
|
||||||
"importInbound" = "匯入入站規則"
|
"importInbound" = "匯入入站規則"
|
||||||
"periodicTrafficResetTitle" = "流量重置"
|
"periodicTrafficResetTitle" = "流量重置"
|
||||||
"periodicTrafficResetDesc" = "按指定間隔自動重置流量計數器"
|
"periodicTrafficResetDesc" = "按指定間隔自動重置流量計數器"
|
||||||
|
|
@ -565,9 +565,9 @@
|
||||||
|
|
||||||
[pages.settings.security]
|
[pages.settings.security]
|
||||||
"admin" = "管理員憑證"
|
"admin" = "管理員憑證"
|
||||||
"twoFactor" = "雙重驗證"
|
"twoFactor" = "雙重驗證"
|
||||||
"twoFactorEnable" = "啟用2FA"
|
"twoFactorEnable" = "啟用2FA"
|
||||||
"twoFactorEnableDesc" = "增加額外的驗證層以提高安全性。"
|
"twoFactorEnableDesc" = "增加額外的驗證層以提高安全性。"
|
||||||
"twoFactorModalSetTitle" = "啟用雙重認證"
|
"twoFactorModalSetTitle" = "啟用雙重認證"
|
||||||
"twoFactorModalDeleteTitle" = "停用雙重認證"
|
"twoFactorModalDeleteTitle" = "停用雙重認證"
|
||||||
"twoFactorModalSteps" = "要設定雙重認證,請執行以下步驟:"
|
"twoFactorModalSteps" = "要設定雙重認證,請執行以下步驟:"
|
||||||
|
|
@ -663,6 +663,7 @@
|
||||||
"active" = "💡 啟用:{{ .Enable }}\r\n"
|
"active" = "💡 啟用:{{ .Enable }}\r\n"
|
||||||
"enabled" = "🚨 已啟用:{{ .Enable }}\r\n"
|
"enabled" = "🚨 已啟用:{{ .Enable }}\r\n"
|
||||||
"online" = "🌐 連線狀態:{{ .Status }}\r\n"
|
"online" = "🌐 連線狀態:{{ .Status }}\r\n"
|
||||||
|
"lastOnline" = "🔙 上次上線: {{ .Time }}\r\n"
|
||||||
"email" = "📧 郵箱:{{ .Email }}\r\n"
|
"email" = "📧 郵箱:{{ .Email }}\r\n"
|
||||||
"upload" = "🔼 上傳↑:{{ .Upload }}\r\n"
|
"upload" = "🔼 上傳↑:{{ .Upload }}\r\n"
|
||||||
"download" = "🔽 下載↓:{{ .Download }}\r\n"
|
"download" = "🔽 下載↓:{{ .Download }}\r\n"
|
||||||
|
|
@ -688,9 +689,9 @@
|
||||||
"inbound_client_data_id" = "🔄 入站: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 電子郵件: {{ .ClientEmail }}\n📊 流量: {{ .ClientTraffic }}\n📅 到期日: {{ .ClientExp }}\n🌐 IP 限制: {{ .IpLimit }}\n💬 備註: {{ .ClientComment }}\n\n你現在可以將客戶加入入站了!"
|
"inbound_client_data_id" = "🔄 入站: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 電子郵件: {{ .ClientEmail }}\n📊 流量: {{ .ClientTraffic }}\n📅 到期日: {{ .ClientExp }}\n🌐 IP 限制: {{ .IpLimit }}\n💬 備註: {{ .ClientComment }}\n\n你現在可以將客戶加入入站了!"
|
||||||
"inbound_client_data_pass" = "🔄 入站: {{ .InboundRemark }}\n\n🔑 密碼: {{ .ClientPass }}\n📧 電子郵件: {{ .ClientEmail }}\n📊 流量: {{ .ClientTraffic }}\n📅 到期日: {{ .ClientExp }}\n🌐 IP 限制: {{ .IpLimit }}\n💬 備註: {{ .ClientComment }}\n\n你現在可以將客戶加入入站了!"
|
"inbound_client_data_pass" = "🔄 入站: {{ .InboundRemark }}\n\n🔑 密碼: {{ .ClientPass }}\n📧 電子郵件: {{ .ClientEmail }}\n📊 流量: {{ .ClientTraffic }}\n📅 到期日: {{ .ClientExp }}\n🌐 IP 限制: {{ .IpLimit }}\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" = "好的,我會使用預設值。 😊"
|
||||||
"incorrect_input" ="您的輸入無效。\n短語應連續輸入,不能有空格。\n正確示例: aaaaaa\n錯誤示例: aaa aaa 🚫"
|
"incorrect_input" = "您的輸入無效。\n短語應連續輸入,不能有空格。\n正確示例: aaaaaa\n錯誤示例: aaa aaa 🚫"
|
||||||
"AreYouSure" = "你確定嗎?🤔"
|
"AreYouSure" = "你確定嗎?🤔"
|
||||||
"SuccessResetTraffic" = "📧 電子郵件: {{ .ClientEmail }}\n🏁 結果: ✅ 成功"
|
"SuccessResetTraffic" = "📧 電子郵件: {{ .ClientEmail }}\n🏁 結果: ✅ 成功"
|
||||||
"FailedResetTraffic" = "📧 電子郵件: {{ .ClientEmail }}\n🏁 結果: ❌ 失敗 \n\n🛠️ 錯誤: [ {{ .ErrorMessage }} ]"
|
"FailedResetTraffic" = "📧 電子郵件: {{ .ClientEmail }}\n🏁 結果: ❌ 失敗 \n\n🛠️ 錯誤: [ {{ .ErrorMessage }} ]"
|
||||||
|
|
|
||||||
54
x-ui.sh
54
x-ui.sh
|
|
@ -509,12 +509,16 @@ 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 | rhel | almalinux | rocky | ol)
|
fedora | amzn | virtuozzo | rhel | almalinux | rocky | ol)
|
||||||
yum -y update && yum -y install ca-certificates
|
|
||||||
;;
|
|
||||||
fedora | amzn | virtuozzo)
|
|
||||||
dnf -y update && dnf -y install ca-certificates
|
dnf -y update && dnf -y install ca-certificates
|
||||||
;;
|
;;
|
||||||
|
centos)
|
||||||
|
if [[ "${VERSION_ID}" =~ ^7 ]]; then
|
||||||
|
yum -y update && yum -y install ca-certificates
|
||||||
|
else
|
||||||
|
dnf -y update && dnf -y install ca-certificates
|
||||||
|
fi
|
||||||
|
;;
|
||||||
arch | manjaro | parch)
|
arch | manjaro | parch)
|
||||||
pacman -Sy --noconfirm ca-certificates
|
pacman -Sy --noconfirm ca-certificates
|
||||||
;;
|
;;
|
||||||
|
|
@ -1073,12 +1077,15 @@ ssl_cert_issue() {
|
||||||
ubuntu | debian | armbian)
|
ubuntu | debian | armbian)
|
||||||
apt-get update && apt-get install socat -y
|
apt-get update && apt-get install socat -y
|
||||||
;;
|
;;
|
||||||
centos | rhel | almalinux | rocky | ol)
|
fedora | amzn | virtuozzo | rhel | almalinux | rocky | ol)
|
||||||
yum -y update && yum -y install socat
|
|
||||||
;;
|
|
||||||
fedora | amzn | virtuozzo)
|
|
||||||
dnf -y update && dnf -y install socat
|
dnf -y update && dnf -y install socat
|
||||||
;;
|
;;
|
||||||
|
centos)
|
||||||
|
if [[ "${VERSION_ID}" =~ ^7 ]]; then
|
||||||
|
yum -y update && yum -y install socat
|
||||||
|
else
|
||||||
|
dnf -y update && dnf -y install socat
|
||||||
|
fi
|
||||||
arch | manjaro | parch)
|
arch | manjaro | parch)
|
||||||
pacman -Sy --noconfirm socat
|
pacman -Sy --noconfirm socat
|
||||||
;;
|
;;
|
||||||
|
|
@ -1086,7 +1093,7 @@ ssl_cert_issue() {
|
||||||
zypper refresh && zypper -q install -y socat
|
zypper refresh && zypper -q install -y socat
|
||||||
;;
|
;;
|
||||||
alpine)
|
alpine)
|
||||||
apk add socat
|
apk add socat curl openssl
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo -e "${red}Unsupported operating system. Please check the script and install the necessary packages manually.${plain}\n"
|
echo -e "${red}Unsupported operating system. Please check the script and install the necessary packages manually.${plain}\n"
|
||||||
|
|
@ -1537,13 +1544,17 @@ install_iplimit() {
|
||||||
armbian)
|
armbian)
|
||||||
apt-get update && apt-get install fail2ban -y
|
apt-get update && apt-get install fail2ban -y
|
||||||
;;
|
;;
|
||||||
centos | rhel | almalinux | rocky | ol)
|
fedora | amzn | virtuozzo | rhel | almalinux | rocky | ol)
|
||||||
yum update -y && yum install epel-release -y
|
|
||||||
yum -y install fail2ban
|
|
||||||
;;
|
|
||||||
fedora | amzn | virtuozzo)
|
|
||||||
dnf -y update && dnf -y install fail2ban
|
dnf -y update && dnf -y install fail2ban
|
||||||
;;
|
;;
|
||||||
|
centos)
|
||||||
|
if [[ "${VERSION_ID}" =~ ^7 ]]; then
|
||||||
|
yum update -y && yum install epel-release -y
|
||||||
|
yum -y install fail2ban
|
||||||
|
else
|
||||||
|
dnf -y update && dnf -y install fail2ban
|
||||||
|
fi
|
||||||
|
;;
|
||||||
arch | manjaro | parch)
|
arch | manjaro | parch)
|
||||||
pacman -Syu --noconfirm fail2ban
|
pacman -Syu --noconfirm fail2ban
|
||||||
;;
|
;;
|
||||||
|
|
@ -1637,14 +1648,19 @@ remove_iplimit() {
|
||||||
apt-get purge -y fail2ban -y
|
apt-get purge -y fail2ban -y
|
||||||
apt-get autoremove -y
|
apt-get autoremove -y
|
||||||
;;
|
;;
|
||||||
centos | rhel | almalinux | rocky | ol)
|
fedora | amzn | virtuozzo | rhel | almalinux | rocky | ol)
|
||||||
yum remove fail2ban -y
|
|
||||||
yum autoremove -y
|
|
||||||
;;
|
|
||||||
fedora | amzn | virtuozzo)
|
|
||||||
dnf remove fail2ban -y
|
dnf remove fail2ban -y
|
||||||
dnf autoremove -y
|
dnf autoremove -y
|
||||||
;;
|
;;
|
||||||
|
centos)
|
||||||
|
if [[ "${VERSION_ID}" =~ ^7 ]]; then
|
||||||
|
yum remove fail2ban -y
|
||||||
|
yum autoremove -y
|
||||||
|
else
|
||||||
|
dnf remove fail2ban -y
|
||||||
|
dnf autoremove -y
|
||||||
|
fi
|
||||||
|
;;
|
||||||
arch | manjaro | parch)
|
arch | manjaro | parch)
|
||||||
pacman -Rns --noconfirm fail2ban
|
pacman -Rns --noconfirm fail2ban
|
||||||
;;
|
;;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue