This commit is contained in:
Alireza Ahmand 2025-01-28 22:43:45 +03:30 committed by GitHub
commit f0f00706b4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 832 additions and 665 deletions

View file

@ -9,10 +9,10 @@
**Un Panel Web Avanzado • Construido sobre Xray Core** **Un Panel Web Avanzado • Construido sobre Xray Core**
[![](https://img.shields.io/github/v/release/mhsanaei/3x-ui.svg)](https://github.com/MHSanaei/3x-ui/releases) [![](https://img.shields.io/github/v/release/mhsanaei/3x-ui.svg)](https://github.com/Incognito-Coder/3xui-LTS/releases)
[![](https://img.shields.io/github/actions/workflow/status/mhsanaei/3x-ui/release.yml.svg)](#) [![](https://img.shields.io/github/actions/workflow/status/Incognito-Coder/3xui-LTS/release.yml.svg)](#)
[![GO Version](https://img.shields.io/github/go-mod/go-version/mhsanaei/3x-ui.svg)](#) [![GO Version](https://img.shields.io/github/go-mod/go-version/mhsanaei/3x-ui.svg)](#)
[![Downloads](https://img.shields.io/github/downloads/mhsanaei/3x-ui/total.svg)](#) [![Downloads](https://img.shields.io/github/downloads/Incognito-Coder/3xui-LTS/total.svg)](#)
[![License](https://img.shields.io/badge/license-GPL%20V3-blue.svg?longCache=true)](https://www.gnu.org/licenses/gpl-3.0.en.html) [![License](https://img.shields.io/badge/license-GPL%20V3-blue.svg?longCache=true)](https://www.gnu.org/licenses/gpl-3.0.en.html)
> **Descargo de responsabilidad:** Este proyecto es solo para aprendizaje personal y comunicación, por favor no lo uses con fines ilegales, por favor no lo uses en un entorno de producción > **Descargo de responsabilidad:** Este proyecto es solo para aprendizaje personal y comunicación, por favor no lo uses con fines ilegales, por favor no lo uses en un entorno de producción
@ -32,7 +32,7 @@
## Instalar y Actualizar ## Instalar y Actualizar
``` ```
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) bash <(curl -Ls https://raw.githubusercontent.com/Incognito-Coder/3xui-LTS/master/install.sh)
``` ```
## Instalar versión antigua (no recomendamos) ## Instalar versión antigua (no recomendamos)
@ -40,7 +40,7 @@ bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.
Para instalar la versión deseada, utiliza el siguiente comando de instalación. Por ejemplo, ver `v1.7.9`: Para instalar la versión deseada, utiliza el siguiente comando de instalación. Por ejemplo, ver `v1.7.9`:
``` ```
VERSION=v1.7.9 && <(curl -Ls "https://raw.githubusercontent.com/mhsanaei/3x-ui/$VERSION/install.sh") $VERSION VERSION=v1.7.9 && <(curl -Ls "https://raw.githubusercontent.com/Incognito-Coder/3xui-LTS/$VERSION/install.sh") $VERSION
``` ```
## Certificado SSL ## Certificado SSL
@ -116,7 +116,7 @@ case "${ARCH}" in
esac esac
wget https://github.com/MHSanaei/3x-ui/releases/latest/download/x-ui-linux-${XUI_ARCH}.tar.gz wget https://github.com/Incognito-Coder/3xui-LTS/releases/latest/download/x-ui-linux-${XUI_ARCH}.tar.gz
``` ```
2. Una vez que se haya descargado el paquete comprimido, ejecuta los siguientes comandos para instalar o actualizar x-ui: 2. Una vez que se haya descargado el paquete comprimido, ejecuta los siguientes comandos para instalar o actualizar x-ui:

View file

@ -9,10 +9,10 @@
**An Advanced Web Panel • Built on Xray Core** **An Advanced Web Panel • Built on Xray Core**
[![](https://img.shields.io/github/v/release/mhsanaei/3x-ui.svg)](https://github.com/MHSanaei/3x-ui/releases) [![](https://img.shields.io/github/v/release/mhsanaei/3x-ui.svg)](https://github.com/Incognito-Coder/3xui-LTS/releases)
[![](https://img.shields.io/github/actions/workflow/status/mhsanaei/3x-ui/release.yml.svg)](#) [![](https://img.shields.io/github/actions/workflow/status/Incognito-Coder/3xui-LTS/release.yml.svg)](#)
[![GO Version](https://img.shields.io/github/go-mod/go-version/mhsanaei/3x-ui.svg)](#) [![GO Version](https://img.shields.io/github/go-mod/go-version/mhsanaei/3x-ui.svg)](#)
[![Downloads](https://img.shields.io/github/downloads/mhsanaei/3x-ui/total.svg)](#) [![Downloads](https://img.shields.io/github/downloads/Incognito-Coder/3xui-LTS/total.svg)](#)
[![License](https://img.shields.io/badge/license-GPL%20V3-blue.svg?longCache=true)](https://www.gnu.org/licenses/gpl-3.0.en.html) [![License](https://img.shields.io/badge/license-GPL%20V3-blue.svg?longCache=true)](https://www.gnu.org/licenses/gpl-3.0.en.html)
> **Disclaimer:** This project is only for personal learning and communication, please do not use it for illegal purposes, please do not use it in a production environment > **Disclaimer:** This project is only for personal learning and communication, please do not use it for illegal purposes, please do not use it in a production environment
@ -32,7 +32,7 @@
## Install & Upgrade ## Install & Upgrade
``` ```
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) bash <(curl -Ls https://raw.githubusercontent.com/Incognito-Coder/3xui-LTS/master/install.sh)
``` ```
## Install legacy Version (we don't recommend) ## Install legacy Version (we don't recommend)
@ -40,7 +40,7 @@ bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.
To install your desired version, use following installation command. e.g., ver `v1.7.9`: To install your desired version, use following installation command. e.g., ver `v1.7.9`:
``` ```
VERSION=v1.7.9 && bash <(curl -Ls "https://raw.githubusercontent.com/mhsanaei/3x-ui/$VERSION/install.sh") $VERSION VERSION=v1.7.9 && bash <(curl -Ls "https://raw.githubusercontent.com/Incognito-Coder/3xui-LTS/$VERSION/install.sh") $VERSION
``` ```
## SSL Certificate ## SSL Certificate
@ -118,7 +118,7 @@ case "${ARCH}" in
esac esac
wget https://github.com/MHSanaei/3x-ui/releases/latest/download/x-ui-linux-${XUI_ARCH}.tar.gz wget https://github.com/Incognito-Coder/3xui-LTS/releases/latest/download/x-ui-linux-${XUI_ARCH}.tar.gz
``` ```
2. Once the compressed package is downloaded, execute the following commands to install or upgrade x-ui: 2. Once the compressed package is downloaded, execute the following commands to install or upgrade x-ui:

View file

@ -9,10 +9,10 @@
**Продвинутая веб-панель • Построена на основе Xray Core** **Продвинутая веб-панель • Построена на основе Xray Core**
[![](https://img.shields.io/github/v/release/mhsanaei/3x-ui.svg)](https://github.com/MHSanaei/3x-ui/releases) [![](https://img.shields.io/github/v/release/mhsanaei/3x-ui.svg)](https://github.com/Incognito-Coder/3xui-LTS/releases)
[![](https://img.shields.io/github/actions/workflow/status/mhsanaei/3x-ui/release.yml.svg)](#) [![](https://img.shields.io/github/actions/workflow/status/Incognito-Coder/3xui-LTS/release.yml.svg)](#)
[![GO Version](https://img.shields.io/github/go-mod/go-version/mhsanaei/3x-ui.svg)](#) [![GO Version](https://img.shields.io/github/go-mod/go-version/mhsanaei/3x-ui.svg)](#)
[![Downloads](https://img.shields.io/github/downloads/mhsanaei/3x-ui/total.svg)](#) [![Downloads](https://img.shields.io/github/downloads/Incognito-Coder/3xui-LTS/total.svg)](#)
[![License](https://img.shields.io/badge/license-GPL%20V3-blue.svg?longCache=true)](https://www.gnu.org/licenses/gpl-3.0.en.html) [![License](https://img.shields.io/badge/license-GPL%20V3-blue.svg?longCache=true)](https://www.gnu.org/licenses/gpl-3.0.en.html)
> **Отказ от ответственности:** Этот проект предназначен только для личного обучения и общения. Пожалуйста, не используйте его в незаконных целях и не применяйте в производственной среде. > **Отказ от ответственности:** Этот проект предназначен только для личного обучения и общения. Пожалуйста, не используйте его в незаконных целях и не применяйте в производственной среде.
@ -32,7 +32,7 @@
## Установка и обновление ## Установка и обновление
``` ```
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) bash <(curl -Ls https://raw.githubusercontent.com/Incognito-Coder/3xui-LTS/master/install.sh)
``` ```
## Установить старую версию (мы не рекомендуем) ## Установить старую версию (мы не рекомендуем)
@ -40,7 +40,7 @@ bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.
Чтобы установить желаемую версию, используйте следующую команду установки. Например, ver `v1.7.9`: Чтобы установить желаемую версию, используйте следующую команду установки. Например, ver `v1.7.9`:
``` ```
VERSION=v1.7.9 && <(curl -Ls "https://raw.githubusercontent.com/mhsanaei/3x-ui/$VERSION/install.sh") $VERSION VERSION=v1.7.9 && <(curl -Ls "https://raw.githubusercontent.com/Incognito-Coder/3xui-LTS/$VERSION/install.sh") $VERSION
``` ```
## SSL Сертификат ## SSL Сертификат
@ -117,7 +117,7 @@ case "${ARCH}" in
esac esac
wget https://github.com/MHSanaei/3x-ui/releases/latest/download/x-ui-linux-${XUI_ARCH}.tar.gz wget https://github.com/Incognito-Coder/3xui-LTS/releases/latest/download/x-ui-linux-${XUI_ARCH}.tar.gz
``` ```
2. После загрузки архива выполните следующие команды для установки или обновления x-ui: 2. После загрузки архива выполните следующие команды для установки или обновления x-ui:

View file

@ -9,10 +9,10 @@
**一个更好的面板 • 基于Xray Core构建** **一个更好的面板 • 基于Xray Core构建**
[![](https://img.shields.io/github/v/release/mhsanaei/3x-ui.svg)](https://github.com/MHSanaei/3x-ui/releases) [![](https://img.shields.io/github/v/release/mhsanaei/3x-ui.svg)](https://github.com/Incognito-Coder/3xui-LTS/releases)
[![](https://img.shields.io/github/actions/workflow/status/mhsanaei/3x-ui/release.yml.svg)](#) [![](https://img.shields.io/github/actions/workflow/status/Incognito-Coder/3xui-LTS/release.yml.svg)](#)
[![GO Version](https://img.shields.io/github/go-mod/go-version/mhsanaei/3x-ui.svg)](#) [![GO Version](https://img.shields.io/github/go-mod/go-version/mhsanaei/3x-ui.svg)](#)
[![Downloads](https://img.shields.io/github/downloads/mhsanaei/3x-ui/total.svg)](#) [![Downloads](https://img.shields.io/github/downloads/Incognito-Coder/3xui-LTS/total.svg)](#)
[![License](https://img.shields.io/badge/license-GPL%20V3-blue.svg?longCache=true)](https://www.gnu.org/licenses/gpl-3.0.en.html) [![License](https://img.shields.io/badge/license-GPL%20V3-blue.svg?longCache=true)](https://www.gnu.org/licenses/gpl-3.0.en.html)
> **Disclaimer:** 此项目仅供个人学习交流,请不要用于非法目的,请不要在生产环境中使用。 > **Disclaimer:** 此项目仅供个人学习交流,请不要用于非法目的,请不要在生产环境中使用。
@ -32,7 +32,7 @@
## 安装 & 升级 ## 安装 & 升级
``` ```
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) bash <(curl -Ls https://raw.githubusercontent.com/Incognito-Coder/3xui-LTS/master/install.sh)
``` ```
## 安装旧版本 (我们不建议) ## 安装旧版本 (我们不建议)
@ -40,7 +40,7 @@ bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.
要安装您想要的版本请使用以下安装命令。例如ver `v1.7.9`: 要安装您想要的版本请使用以下安装命令。例如ver `v1.7.9`:
``` ```
VERSION=v1.7.9 && <(curl -Ls "https://raw.githubusercontent.com/mhsanaei/3x-ui/$VERSION/install.sh") $VERSION VERSION=v1.7.9 && <(curl -Ls "https://raw.githubusercontent.com/Incognito-Coder/3xui-LTS/$VERSION/install.sh") $VERSION
``` ```
### SSL证书 ### SSL证书
@ -116,7 +116,7 @@ case "${ARCH}" in
esac esac
wget https://github.com/MHSanaei/3x-ui/releases/latest/download/x-ui-linux-${XUI_ARCH}.tar.gz wget https://github.com/Incognito-Coder/3xui-LTS/releases/latest/download/x-ui-linux-${XUI_ARCH}.tar.gz
``` ```
2. 下载压缩包后,执行以下命令安装或升级 x-ui 2. 下载压缩包后,执行以下命令安装或升级 x-ui

View file

@ -1 +1 @@
2.5.0 2.5.1

View file

@ -203,13 +203,13 @@ install_x-ui() {
cd /usr/local/ cd /usr/local/
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/Incognito-Coder/3xui-LTS/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
if [[ ! -n "$tag_version" ]]; then if [[ ! -n "$tag_version" ]]; then
echo -e "${red}Failed to fetch x-ui version, it may be due to GitHub API restrictions, please try it later${plain}" echo -e "${red}Failed to fetch x-ui version, it may be due to GitHub API restrictions, please try it later${plain}"
exit 1 exit 1
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 -N --no-check-certificate -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 wget -N --no-check-certificate -O /usr/local/x-ui-linux-$(arch).tar.gz https://github.com/Incognito-Coder/3xui-LTS/releases/download/${tag_version}/x-ui-linux-$(arch).tar.gz
if [[ $? -ne 0 ]]; then if [[ $? -ne 0 ]]; then
echo -e "${red}Downloading x-ui failed, please be sure that your server can access GitHub ${plain}" echo -e "${red}Downloading x-ui failed, please be sure that your server can access GitHub ${plain}"
exit 1 exit 1
@ -224,7 +224,7 @@ install_x-ui() {
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/Incognito-Coder/3xui-LTS/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 -N --no-check-certificate -O /usr/local/x-ui-linux-$(arch).tar.gz ${url} wget -N --no-check-certificate -O /usr/local/x-ui-linux-$(arch).tar.gz ${url}
if [[ $? -ne 0 ]]; then if [[ $? -ne 0 ]]; then
@ -251,7 +251,7 @@ install_x-ui() {
chmod +x x-ui bin/xray-linux-$(arch) chmod +x x-ui bin/xray-linux-$(arch)
cp -f x-ui.service /etc/systemd/system/ cp -f x-ui.service /etc/systemd/system/
wget --no-check-certificate -O /usr/bin/x-ui https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.sh wget --no-check-certificate -O /usr/bin/x-ui https://raw.githubusercontent.com/Incognito-Coder/3xui-LTS/main/x-ui.sh
chmod +x /usr/local/x-ui/x-ui.sh chmod +x /usr/local/x-ui/x-ui.sh
chmod +x /usr/bin/x-ui chmod +x /usr/bin/x-ui
config_after_install config_after_install

View file

@ -8,71 +8,90 @@
border-radius: 1rem; border-radius: 1rem;
overflow-x: hidden; overflow-x: hidden;
} }
.dark .ant-table:not(.ant-table-expanded-row .ant-table) { .dark .ant-table:not(.ant-table-expanded-row .ant-table) {
outline-color: var(--dark-color-table-ring); outline-color: var(--dark-color-table-ring);
} }
.ant-table .ant-table-content .ant-table-scroll .ant-table-body { .ant-table .ant-table-content .ant-table-scroll .ant-table-body {
overflow-y: hidden; overflow-y: hidden;
} }
.ant-table .ant-table-content .ant-table-tbody tr:last-child .ant-table-wrapper { .ant-table .ant-table-content .ant-table-tbody tr:last-child .ant-table-wrapper {
margin: -10px 22px !important; margin: -10px 22px !important;
} }
.ant-table .ant-table-content .ant-table-tbody tr:last-child .ant-table-wrapper .ant-table { .ant-table .ant-table-content .ant-table-tbody tr:last-child .ant-table-wrapper .ant-table {
border-bottom-left-radius: 1rem; border-bottom-left-radius: 1rem;
border-bottom-right-radius: 1rem; border-bottom-right-radius: 1rem;
} }
.ant-table .ant-table-content .ant-table-tbody tr:last-child tr:last-child td { .ant-table .ant-table-content .ant-table-tbody tr:last-child tr:last-child td {
border-bottom-color: transparent; border-bottom-color: transparent;
} }
.ant-table .ant-table-tbody tr:last-child.ant-table-expanded-row .ant-table-wrapper .ant-table-tbody > tr:last-child > td:first-child { .ant-table .ant-table-tbody tr:last-child.ant-table-expanded-row .ant-table-wrapper .ant-table-tbody > tr:last-child > td:first-child {
border-bottom-left-radius: 6px; border-bottom-left-radius: 6px;
} }
.ant-table .ant-table-tbody tr:last-child.ant-table-expanded-row .ant-table-wrapper .ant-table-tbody > tr:last-child > td:last-child { .ant-table .ant-table-tbody tr:last-child.ant-table-expanded-row .ant-table-wrapper .ant-table-tbody > tr:last-child > td:last-child {
border-bottom-right-radius: 6px; border-bottom-right-radius: 6px;
} }
@media (min-width: 769px) { @media (min-width: 769px) {
.ant-layout-content { .ant-layout-content {
margin: 24px 16px; margin: 24px 16px;
} }
} }
@media (max-width: 768px) { @media (max-width: 768px) {
.ant-card-body { .ant-card-body {
padding: .5rem; padding: .5rem;
} }
.ant-table .ant-table-content .ant-table-tbody tr:last-child .ant-table-wrapper { .ant-table .ant-table-content .ant-table-tbody tr:last-child .ant-table-wrapper {
margin: -10px 2px !important; margin: -10px 2px !important;
} }
} }
.ant-col-sm-24 { .ant-col-sm-24 {
margin: 0.5rem -2rem 0.5rem 2rem; margin: 0.5rem -2rem 0.5rem 2rem;
} }
tr.hideExpandIcon .ant-table-row-expand-icon { tr.hideExpandIcon .ant-table-row-expand-icon {
display: none; display: none;
} }
.infinite-tag { .infinite-tag {
padding: 0 5px; padding: 0 5px;
border-radius: 2rem; border-radius: 2rem;
min-width: 50px; min-width: 50px;
min-height: 22px; min-height: 22px;
} }
.infinite-bar .ant-progress-inner .ant-progress-bg { .infinite-bar .ant-progress-inner .ant-progress-bg {
background-color: #F2EAF1; background-color: #F2EAF1;
border: #D5BED2 solid 1px; border: #D5BED2 solid 1px;
} }
.dark .infinite-bar .ant-progress-inner .ant-progress-bg { .dark .infinite-bar .ant-progress-inner .ant-progress-bg {
background-color: #7a316f !important; background-color: #7a316f !important;
border: #7a316f solid 1px; border: #7a316f solid 1px;
} }
.ant-collapse { .ant-collapse {
margin: 5px 0; margin: 5px 0;
} }
.info-large-tag { .info-large-tag {
max-width: 200px; max-width: 200px;
overflow: hidden; overflow: hidden;
} }
.online-animation .ant-badge-status-dot { .online-animation .ant-badge-status-dot {
animation: onlineAnimation 1.2s linear infinite; animation: onlineAnimation 1.2s linear infinite;
} }
@keyframes onlineAnimation { @keyframes onlineAnimation {
0%, 0%,
50%, 50%,
@ -85,38 +104,46 @@
opacity: .2; opacity: .2;
} }
} }
.tr-table-box { .tr-table-box {
display: flex; display: flex;
gap: 4px; gap: 4px;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
} }
.tr-table-rt { .tr-table-rt {
flex-basis: 70px; flex-basis: 70px;
min-width: 70px; min-width: 70px;
text-align: end; text-align: end;
} }
.tr-table-lt { .tr-table-lt {
flex-basis: 70px; flex-basis: 70px;
min-width: 70px; min-width: 70px;
text-align: start; text-align: start;
} }
.tr-table-bar { .tr-table-bar {
flex-basis: 160px; flex-basis: 160px;
min-width: 60px; min-width: 60px;
} }
.tr-infinity-ch { .tr-infinity-ch {
font-size: 14pt; font-size: 14pt;
max-height: 24px; max-height: 24px;
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
} }
.ant-table-expanded-row .ant-table .ant-table-body { .ant-table-expanded-row .ant-table .ant-table-body {
overflow-x: hidden; overflow-x: hidden;
} }
.ant-table-expanded-row .ant-table-tbody > tr > td { .ant-table-expanded-row .ant-table-tbody > tr > td {
padding: 10px 2px; padding: 10px 2px;
} }
.ant-table-expanded-row .ant-table-thead > tr > th { .ant-table-expanded-row .ant-table-thead > tr > th {
padding: 12px 2px; padding: 12px 2px;
} }
@ -154,32 +181,42 @@
<a-col :xs="24" :sm="24" :lg="12"> <a-col :xs="24" :sm="24" :lg="12">
<template> <template>
<div> <div>
<a-back-top :target="() => document.getElementById('content-layout')" visibility-height="200"></a-back-top> <a-back-top :target="() => document.getElementById('content-layout')"
visibility-height="200"></a-back-top>
{{ i18n "clients" }}: {{ i18n "clients" }}:
<a-tag color="green">[[ total.clients ]]</a-tag> <a-tag color="green">[[ total.clients ]]</a-tag>
<a-popover title='{{ i18n "disabled" }}' :overlay-class-name="themeSwitcher.currentTheme"> <a-popover title='{{ i18n "disabled" }}'
:overlay-class-name="themeSwitcher.currentTheme">
<template slot="content"> <template slot="content">
<p v-for="clientEmail in total.deactive">[[ clientEmail ]]</p> <p v-for="clientEmail in total.deactive">[[ clientEmail ]]</p>
</template> </template>
<a-tag v-if="total.deactive.length">[[ total.deactive.length ]]</a-tag> <a-tag v-if="total.deactive.length">[[ total.deactive.length ]]</a-tag>
</a-popover> </a-popover>
<a-popover title='{{ i18n "depleted" }}' :overlay-class-name="themeSwitcher.currentTheme"> <a-popover title='{{ i18n "depleted" }}'
:overlay-class-name="themeSwitcher.currentTheme">
<template slot="content"> <template slot="content">
<p v-for="clientEmail in total.depleted">[[ clientEmail ]]</p> <p v-for="clientEmail in total.depleted">[[ clientEmail ]]</p>
</template> </template>
<a-tag color="red" v-if="total.depleted.length">[[ total.depleted.length ]]</a-tag> <a-tag color="red" v-if="total.depleted.length">[[ total.depleted.length
]]
</a-tag>
</a-popover> </a-popover>
<a-popover title='{{ i18n "depletingSoon" }}' :overlay-class-name="themeSwitcher.currentTheme"> <a-popover title='{{ i18n "depletingSoon" }}'
:overlay-class-name="themeSwitcher.currentTheme">
<template slot="content"> <template slot="content">
<p v-for="clientEmail in total.expiring">[[ clientEmail ]]</p> <p v-for="clientEmail in total.expiring">[[ clientEmail ]]</p>
</template> </template>
<a-tag color="orange" v-if="total.expiring.length">[[ total.expiring.length ]]</a-tag> <a-tag color="orange" v-if="total.expiring.length">[[ total.expiring.length
]]
</a-tag>
</a-popover> </a-popover>
<a-popover title='{{ i18n "online" }}' :overlay-class-name="themeSwitcher.currentTheme"> <a-popover title='{{ i18n "online" }}'
:overlay-class-name="themeSwitcher.currentTheme">
<template slot="content"> <template slot="content">
<p v-for="clientEmail in onlineClients">[[ clientEmail ]]</p> <p v-for="clientEmail in onlineClients">[[ clientEmail ]]</p>
</template> </template>
<a-tag color="blue" v-if="onlineClients.length">[[ onlineClients.length ]]</a-tag> <a-tag color="blue" v-if="onlineClients.length">[[ onlineClients.length ]]
</a-tag>
</a-popover> </a-popover>
</div> </div>
</template> </template>
@ -197,9 +234,11 @@
</a-button> </a-button>
<a-dropdown :trigger="['click']"> <a-dropdown :trigger="['click']">
<a-button type="primary" icon="menu"> <a-button type="primary" icon="menu">
<template v-if="!isMobile">{{ i18n "pages.inbounds.generalActions" }}</template> <template v-if="!isMobile">{{ i18n "pages.inbounds.generalActions" }}
</template>
</a-button> </a-button>
<a-menu slot="overlay" @click="a => generalActions(a)" :theme="themeSwitcher.currentTheme"> <a-menu slot="overlay" @click="a => generalActions(a)"
:theme="themeSwitcher.currentTheme">
<a-menu-item key="import"> <a-menu-item key="import">
<a-icon type="import"></a-icon> <a-icon type="import"></a-icon>
{{ i18n "pages.inbounds.importInbound" }} {{ i18n "pages.inbounds.importInbound" }}
@ -210,7 +249,8 @@
</a-menu-item> </a-menu-item>
<a-menu-item key="subs" v-if="subSettings.enable"> <a-menu-item key="subs" v-if="subSettings.enable">
<a-icon type="export"></a-icon> <a-icon type="export"></a-icon>
{{ i18n "pages.inbounds.export" }} - {{ i18n "pages.settings.subSettings" }} {{ i18n "pages.inbounds.export" }} - {{ i18n
"pages.settings.subSettings" }}
</a-menu-item> </a-menu-item>
<a-menu-item key="resetInbounds"> <a-menu-item key="resetInbounds">
<a-icon type="reload"></a-icon> <a-icon type="reload"></a-icon>
@ -224,7 +264,8 @@
<a-icon type="rest"></a-icon> <a-icon type="rest"></a-icon>
{{ i18n "pages.inbounds.delDepletedClients" }} {{ i18n "pages.inbounds.delDepletedClients" }}
</a-menu-item> </a-menu-item>
<a-menu-item v-if="subSettings.enable && dbInbounds.length > 0" key="addGroupClient"> <a-menu-item v-if="subSettings.enable && dbInbounds.length > 0"
key="addGroupClient">
<a-icon type="usergroup-add"></a-icon> <a-icon type="usergroup-add"></a-icon>
{{ i18n "pages.client.groupAdd"}} {{ i18n "pages.client.groupAdd"}}
</a-menu-item> </a-menu-item>
@ -237,9 +278,11 @@
v-if="isRefreshEnabled" v-if="isRefreshEnabled"
@change="changeRefreshInterval" @change="changeRefreshInterval"
:dropdown-class-name="themeSwitcher.currentTheme"> :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option v-for="key in [5,10,30,60]" :value="key*1000">[[ key ]]s</a-select-option> <a-select-option v-for="key in [5,10,30,60]" :value="key*1000">[[ key ]]s
</a-select-option>
</a-select> </a-select>
<a-icon type="sync" :spin="refreshing" @click="manualRefresh" style="margin: 0 5px;"></a-icon> <a-icon type="sync" :spin="refreshing" @click="manualRefresh"
style="margin: 0 5px;"></a-icon>
<a-switch v-model="isRefreshEnabled" @change="toggleRefresh"></a-switch> <a-switch v-model="isRefreshEnabled" @change="toggleRefresh"></a-switch>
</a-col> </a-col>
</a-row> </a-row>
@ -251,8 +294,10 @@
<a-icon slot="checkedChildren" type="search"></a-icon> <a-icon slot="checkedChildren" type="search"></a-icon>
<a-icon slot="unCheckedChildren" type="filter"></a-icon> <a-icon slot="unCheckedChildren" type="filter"></a-icon>
</a-switch> </a-switch>
<a-input v-if="!enableFilter" v-model.lazy="searchKey" placeholder='{{ i18n "search" }}' autofocus style="max-width: 300px" :size="isMobile ? 'small' : ''"></a-input> <a-input v-if="!enableFilter" v-model.lazy="searchKey" placeholder='{{ i18n "search" }}'
<a-radio-group v-if="enableFilter" v-model="filterBy" @change="filterInbounds" button-style="solid" :size="isMobile ? 'small' : ''"> autofocus style="max-width: 300px" :size="isMobile ? 'small' : ''"></a-input>
<a-radio-group v-if="enableFilter" v-model="filterBy" @change="filterInbounds"
button-style="solid" :size="isMobile ? 'small' : ''">
<a-radio-button value="">{{ i18n "none" }}</a-radio-button> <a-radio-button value="">{{ i18n "none" }}</a-radio-button>
<a-radio-button value="deactive">{{ i18n "disabled" }}</a-radio-button> <a-radio-button value="deactive">{{ i18n "disabled" }}</a-radio-button>
<a-radio-button value="depleted">{{ i18n "depleted" }}</a-radio-button> <a-radio-button value="depleted">{{ i18n "depleted" }}</a-radio-button>
@ -273,13 +318,16 @@
style="margin-top: 10px"> style="margin-top: 10px">
<template slot="action" slot-scope="text, dbInbound"> <template slot="action" slot-scope="text, dbInbound">
<a-dropdown :trigger="['click']"> <a-dropdown :trigger="['click']">
<a-icon @click="e => e.preventDefault()" type="more" style="font-size: 20px; text-decoration: solid;"></a-icon> <a-icon @click="e => e.preventDefault()" type="more"
<a-menu slot="overlay" @click="a => clickAction(a, dbInbound)" :theme="themeSwitcher.currentTheme"> style="font-size: 20px; text-decoration: solid;"></a-icon>
<a-menu slot="overlay" @click="a => clickAction(a, dbInbound)"
:theme="themeSwitcher.currentTheme">
<a-menu-item key="edit"> <a-menu-item key="edit">
<a-icon type="edit"></a-icon> <a-icon type="edit"></a-icon>
{{ i18n "edit" }} {{ i18n "edit" }}
</a-menu-item> </a-menu-item>
<a-menu-item key="qrcode" v-if="(dbInbound.isSS && !dbInbound.toInbound().isSSMultiUser) || dbInbound.isWireguard"> <a-menu-item key="qrcode"
v-if="(dbInbound.isSS && !dbInbound.toInbound().isSSMultiUser) || dbInbound.isWireguard">
<a-icon type="qrcode"></a-icon> <a-icon type="qrcode"></a-icon>
{{ i18n "qrCode" }} {{ i18n "qrCode" }}
</a-menu-item> </a-menu-item>
@ -302,7 +350,8 @@
</a-menu-item> </a-menu-item>
<a-menu-item key="subs" v-if="subSettings.enable"> <a-menu-item key="subs" v-if="subSettings.enable">
<a-icon type="export"></a-icon> <a-icon type="export"></a-icon>
{{ i18n "pages.inbounds.export"}} - {{ i18n "pages.settings.subSettings" }} {{ i18n "pages.inbounds.export"}} - {{ i18n "pages.settings.subSettings"
}}
</a-menu-item> </a-menu-item>
<a-menu-item key="delDepletedClients" style="color: #FF4D4F;"> <a-menu-item key="delDepletedClients" style="color: #FF4D4F;">
<a-icon type="rest"></a-icon> <a-icon type="rest"></a-icon>
@ -320,10 +369,12 @@
{{ i18n "pages.inbounds.exportInbound" }} {{ i18n "pages.inbounds.exportInbound" }}
</a-menu-item> </a-menu-item>
<a-menu-item key="resetTraffic"> <a-menu-item key="resetTraffic">
<a-icon type="retweet"></a-icon> {{ i18n "pages.inbounds.resetTraffic" }} <a-icon type="retweet"></a-icon>
{{ i18n "pages.inbounds.resetTraffic" }}
</a-menu-item> </a-menu-item>
<a-menu-item key="clone"> <a-menu-item key="clone">
<a-icon type="block"></a-icon> {{ i18n "pages.inbounds.clone"}} <a-icon type="block"></a-icon>
{{ i18n "pages.inbounds.clone"}}
</a-menu-item> </a-menu-item>
<a-menu-item key="delete"> <a-menu-item key="delete">
<span style="color: #FF4D4F"> <span style="color: #FF4D4F">
@ -331,7 +382,8 @@
</span> </span>
</a-menu-item> </a-menu-item>
<a-menu-item v-if="isMobile"> <a-menu-item v-if="isMobile">
<a-switch size="small" v-model="dbInbound.enable" @change="switchEnable(dbInbound.id,dbInbound.enable)"></a-switch> <a-switch size="small" v-model="dbInbound.enable"
@change="switchEnable(dbInbound.id,dbInbound.enable)"></a-switch>
{{ i18n "pages.inbounds.enable" }} {{ i18n "pages.inbounds.enable" }}
</a-menu-item> </a-menu-item>
</a-menu> </a-menu>
@ -339,38 +391,65 @@
</template> </template>
<template slot="protocol" slot-scope="text, dbInbound"> <template slot="protocol" slot-scope="text, dbInbound">
<a-tag style="margin:0;" color="purple">[[ dbInbound.protocol ]]</a-tag> <a-tag style="margin:0;" color="purple">[[ dbInbound.protocol ]]</a-tag>
<template v-if="dbInbound.isVMess || dbInbound.isVLess || dbInbound.isTrojan || dbInbound.isSS"> <template
<a-tag style="margin:0;" color="green">[[ dbInbound.toInbound().stream.network ]]</a-tag> v-if="dbInbound.isVMess || dbInbound.isVLess || dbInbound.isTrojan || dbInbound.isSS">
<a-tag style="margin:0;" v-if="dbInbound.toInbound().stream.isTls" color="blue">TLS</a-tag> <a-tag style="margin:0;" color="green">[[ dbInbound.toInbound().stream.network ]]
<a-tag style="margin:0;" v-if="dbInbound.toInbound().stream.isReality" color="blue">Reality</a-tag> </a-tag>
<a-tag style="margin:0;" v-if="dbInbound.toInbound().stream.isTls" color="blue">
TLS
</a-tag>
<a-tag style="margin:0;" v-if="dbInbound.toInbound().stream.isReality" color="blue">
Reality
</a-tag>
</template> </template>
</template> </template>
<template slot="clients" slot-scope="text, dbInbound"> <template slot="clients" slot-scope="text, dbInbound">
<template v-if="clientCount[dbInbound.id]"> <template v-if="clientCount[dbInbound.id]">
<a-tag style="margin:0;" color="green">[[ clientCount[dbInbound.id].clients ]]</a-tag> <a-tag style="margin:0;" color="green">[[ clientCount[dbInbound.id].clients ]]
<a-popover title='{{ i18n "disabled" }}' :overlay-class-name="themeSwitcher.currentTheme"> </a-tag>
<a-popover title='{{ i18n "disabled" }}'
:overlay-class-name="themeSwitcher.currentTheme">
<template slot="content"> <template slot="content">
<p v-for="clientEmail in clientCount[dbInbound.id].deactive">[[ clientEmail ]]</p> <p v-for="clientEmail in clientCount[dbInbound.id].deactive">[[ clientEmail
]]</p>
</template> </template>
<a-tag style="margin:0; padding: 0 2px;" v-if="clientCount[dbInbound.id].deactive.length">[[ clientCount[dbInbound.id].deactive.length ]]</a-tag> <a-tag style="margin:0; padding: 0 2px;"
v-if="clientCount[dbInbound.id].deactive.length">[[
clientCount[dbInbound.id].deactive.length ]]
</a-tag>
</a-popover> </a-popover>
<a-popover title='{{ i18n "depleted" }}' :overlay-class-name="themeSwitcher.currentTheme"> <a-popover title='{{ i18n "depleted" }}'
:overlay-class-name="themeSwitcher.currentTheme">
<template slot="content"> <template slot="content">
<p v-for="clientEmail in clientCount[dbInbound.id].depleted">[[ clientEmail ]]</p> <p v-for="clientEmail in clientCount[dbInbound.id].depleted">[[ clientEmail
]]</p>
</template> </template>
<a-tag style="margin:0; padding: 0 2px;" color="red" v-if="clientCount[dbInbound.id].depleted.length">[[ clientCount[dbInbound.id].depleted.length ]]</a-tag> <a-tag style="margin:0; padding: 0 2px;" color="red"
v-if="clientCount[dbInbound.id].depleted.length">[[
clientCount[dbInbound.id].depleted.length ]]
</a-tag>
</a-popover> </a-popover>
<a-popover title='{{ i18n "depletingSoon" }}' :overlay-class-name="themeSwitcher.currentTheme"> <a-popover title='{{ i18n "depletingSoon" }}'
:overlay-class-name="themeSwitcher.currentTheme">
<template slot="content"> <template slot="content">
<p v-for="clientEmail in clientCount[dbInbound.id].expiring">[[ clientEmail ]]</p> <p v-for="clientEmail in clientCount[dbInbound.id].expiring">[[ clientEmail
]]</p>
</template> </template>
<a-tag style="margin:0; padding: 0 2px;" color="orange" v-if="clientCount[dbInbound.id].expiring.length">[[ clientCount[dbInbound.id].expiring.length ]]</a-tag> <a-tag style="margin:0; padding: 0 2px;" color="orange"
v-if="clientCount[dbInbound.id].expiring.length">[[
clientCount[dbInbound.id].expiring.length ]]
</a-tag>
</a-popover> </a-popover>
<a-popover title='{{ i18n "online" }}' :overlay-class-name="themeSwitcher.currentTheme"> <a-popover title='{{ i18n "online" }}'
:overlay-class-name="themeSwitcher.currentTheme">
<template slot="content"> <template slot="content">
<p v-for="clientEmail in clientCount[dbInbound.id].online">[[ clientEmail ]]</p> <p v-for="clientEmail in clientCount[dbInbound.id].online">[[ clientEmail
]]</p>
</template> </template>
<a-tag style="margin:0; padding: 0 2px;" color="blue" v-if="clientCount[dbInbound.id].online.length">[[ clientCount[dbInbound.id].online.length ]]</a-tag> <a-tag style="margin:0; padding: 0 2px;" color="blue"
v-if="clientCount[dbInbound.id].online.length">[[
clientCount[dbInbound.id].online.length ]]
</a-tag>
</a-popover> </a-popover>
</template> </template>
</template> </template>
@ -384,7 +463,8 @@
</tr> </tr>
<tr v-if="dbInbound.total > 0 && dbInbound.up + dbInbound.down < dbInbound.total"> <tr v-if="dbInbound.total > 0 && dbInbound.up + dbInbound.down < dbInbound.total">
<td>{{ i18n "remained" }}</td> <td>{{ i18n "remained" }}</td>
<td>[[ sizeFormat(dbInbound.total - dbInbound.up - dbInbound.down) ]]</td> <td>[[ sizeFormat(dbInbound.total - dbInbound.up - dbInbound.down) ]]
</td>
</tr> </tr>
</table> </table>
</template> </template>
@ -395,79 +475,119 @@
</template> </template>
<template v-else> <template v-else>
<svg height="10px" width="14px" viewBox="0 0 640 512" fill="currentColor"> <svg height="10px" width="14px" viewBox="0 0 640 512" fill="currentColor">
<path d="M484.4 96C407 96 349.2 164.1 320 208.5C290.8 164.1 233 96 155.6 96C69.75 96 0 167.8 0 256s69.75 160 155.6 160C233.1 416 290.8 347.9 320 303.5C349.2 347.9 407 416 484.4 416C570.3 416 640 344.2 640 256S570.3 96 484.4 96zM155.6 368C96.25 368 48 317.8 48 256s48.25-112 107.6-112c67.75 0 120.5 82.25 137.1 112C276 285.8 223.4 368 155.6 368zM484.4 368c-67.75 0-120.5-82.25-137.1-112C364 226.2 416.6 144 484.4 144C543.8 144 592 194.2 592 256S543.8 368 484.4 368z" fill="currentColor"></path> <path d="M484.4 96C407 96 349.2 164.1 320 208.5C290.8 164.1 233 96 155.6 96C69.75 96 0 167.8 0 256s69.75 160 155.6 160C233.1 416 290.8 347.9 320 303.5C349.2 347.9 407 416 484.4 416C570.3 416 640 344.2 640 256S570.3 96 484.4 96zM155.6 368C96.25 368 48 317.8 48 256s48.25-112 107.6-112c67.75 0 120.5 82.25 137.1 112C276 285.8 223.4 368 155.6 368zM484.4 368c-67.75 0-120.5-82.25-137.1-112C364 226.2 416.6 144 484.4 144C543.8 144 592 194.2 592 256S543.8 368 484.4 368z"
fill="currentColor"></path>
</svg> </svg>
</template> </template>
</a-tag> </a-tag>
</a-popover> </a-popover>
</template> </template>
<template slot="enable" slot-scope="text, dbInbound"> <template slot="enable" slot-scope="text, dbInbound">
<a-switch v-model="dbInbound.enable" @change="switchEnable(dbInbound.id,dbInbound.enable)"></a-switch> <a-switch v-model="dbInbound.enable"
@change="switchEnable(dbInbound.id,dbInbound.enable)"></a-switch>
</template> </template>
<template slot="expiryTime" slot-scope="text, dbInbound"> <template slot="expiryTime" slot-scope="text, dbInbound">
<a-popover v-if="dbInbound.expiryTime > 0" :overlay-class-name="themeSwitcher.currentTheme"> <a-popover v-if="dbInbound.expiryTime > 0"
:overlay-class-name="themeSwitcher.currentTheme">
<template slot="content" v-if="app.datepicker === 'gregorian'"> <template slot="content" v-if="app.datepicker === 'gregorian'">
[[ DateUtil.formatMillis(dbInbound.expiryTime) ]] [[ DateUtil.formatMillis(dbInbound.expiryTime) ]]
</template> </template>
<template v-else slot="content"> <template v-else slot="content">
[[ DateUtil.convertToJalalian(moment(dbInbound.expiryTime)) ]] [[ DateUtil.convertToJalalian(moment(dbInbound.expiryTime)) ]]
</template> </template>
<a-tag style="min-width: 50px;" :color="usageColor(new Date().getTime(), app.expireDiff, dbInbound._expiryTime)"> <a-tag style="min-width: 50px;"
:color="usageColor(new Date().getTime(), app.expireDiff, dbInbound._expiryTime)">
[[ remainedDays(dbInbound._expiryTime) ]] [[ remainedDays(dbInbound._expiryTime) ]]
</a-tag> </a-tag>
</a-popover> </a-popover>
<a-tag v-else color="purple" class="infinite-tag"> <a-tag v-else color="purple" class="infinite-tag">
<svg height="10px" width="14px" viewBox="0 0 640 512" fill="currentColor"> <svg height="10px" width="14px" viewBox="0 0 640 512" fill="currentColor">
<path d="M484.4 96C407 96 349.2 164.1 320 208.5C290.8 164.1 233 96 155.6 96C69.75 96 0 167.8 0 256s69.75 160 155.6 160C233.1 416 290.8 347.9 320 303.5C349.2 347.9 407 416 484.4 416C570.3 416 640 344.2 640 256S570.3 96 484.4 96zM155.6 368C96.25 368 48 317.8 48 256s48.25-112 107.6-112c67.75 0 120.5 82.25 137.1 112C276 285.8 223.4 368 155.6 368zM484.4 368c-67.75 0-120.5-82.25-137.1-112C364 226.2 416.6 144 484.4 144C543.8 144 592 194.2 592 256S543.8 368 484.4 368z" fill="currentColor"></path> <path d="M484.4 96C407 96 349.2 164.1 320 208.5C290.8 164.1 233 96 155.6 96C69.75 96 0 167.8 0 256s69.75 160 155.6 160C233.1 416 290.8 347.9 320 303.5C349.2 347.9 407 416 484.4 416C570.3 416 640 344.2 640 256S570.3 96 484.4 96zM155.6 368C96.25 368 48 317.8 48 256s48.25-112 107.6-112c67.75 0 120.5 82.25 137.1 112C276 285.8 223.4 368 155.6 368zM484.4 368c-67.75 0-120.5-82.25-137.1-112C364 226.2 416.6 144 484.4 144C543.8 144 592 194.2 592 256S543.8 368 484.4 368z"
fill="currentColor"></path>
</svg> </svg>
</a-tag> </a-tag>
</template> </template>
<template slot="info" slot-scope="text, dbInbound"> <template slot="info" slot-scope="text, dbInbound">
<a-popover placement="bottomRight" :overlay-class-name="themeSwitcher.currentTheme" trigger="click"> <a-popover placement="bottomRight" :overlay-class-name="themeSwitcher.currentTheme"
trigger="click">
<template slot="content"> <template slot="content">
<table cellpadding="2"> <table cellpadding="2">
<tr> <tr>
<td>{{ i18n "pages.inbounds.protocol" }}</td> <td>{{ i18n "pages.inbounds.protocol" }}</td>
<td> <td>
<a-tag style="margin:0;" color="purple">[[ dbInbound.protocol ]]</a-tag> <a-tag style="margin:0;" color="purple">[[ dbInbound.protocol ]]
<template v-if="dbInbound.isVMess || dbInbound.isVLess || dbInbound.isTrojan || dbInbound.isSS"> </a-tag>
<a-tag style="margin:0;" color="blue">[[ dbInbound.toInbound().stream.network ]]</a-tag> <template
<a-tag style="margin:0;" v-if="dbInbound.toInbound().stream.isTls" color="green">tls</a-tag> v-if="dbInbound.isVMess || dbInbound.isVLess || dbInbound.isTrojan || dbInbound.isSS">
<a-tag style="margin:0;" v-if="dbInbound.toInbound().stream.isReality" color="green">reality</a-tag> <a-tag style="margin:0;" color="blue">[[
dbInbound.toInbound().stream.network ]]
</a-tag>
<a-tag style="margin:0;"
v-if="dbInbound.toInbound().stream.isTls" color="green">
tls
</a-tag>
<a-tag style="margin:0;"
v-if="dbInbound.toInbound().stream.isReality"
color="green">reality
</a-tag>
</template> </template>
</td> </td>
</tr> </tr>
<tr> <tr>
<td>{{ i18n "pages.inbounds.port" }}</td> <td>{{ i18n "pages.inbounds.port" }}</td>
<td><a-tag>[[ dbInbound.port ]]</a-tag></td> <td>
<a-tag>[[ dbInbound.port ]]</a-tag>
</td>
</tr> </tr>
<tr v-if="clientCount[dbInbound.id]"> <tr v-if="clientCount[dbInbound.id]">
<td>{{ i18n "clients" }}</td> <td>{{ i18n "clients" }}</td>
<td> <td>
<a-tag style="margin:0;" color="blue">[[ clientCount[dbInbound.id].clients ]]</a-tag> <a-tag style="margin:0;" color="blue">[[
<a-popover title='{{ i18n "disabled" }}' :overlay-class-name="themeSwitcher.currentTheme"> clientCount[dbInbound.id].clients ]]
</a-tag>
<a-popover title='{{ i18n "disabled" }}'
:overlay-class-name="themeSwitcher.currentTheme">
<template slot="content"> <template slot="content">
<p v-for="clientEmail in clientCount[dbInbound.id].deactive">[[ clientEmail ]]</p> <p v-for="clientEmail in clientCount[dbInbound.id].deactive">
[[ clientEmail ]]</p>
</template> </template>
<a-tag style="margin:0; padding: 0 2px;" v-if="clientCount[dbInbound.id].deactive.length">[[ clientCount[dbInbound.id].deactive.length ]]</a-tag> <a-tag style="margin:0; padding: 0 2px;"
v-if="clientCount[dbInbound.id].deactive.length">[[
clientCount[dbInbound.id].deactive.length ]]
</a-tag>
</a-popover> </a-popover>
<a-popover title='{{ i18n "depleted" }}' :overlay-class-name="themeSwitcher.currentTheme"> <a-popover title='{{ i18n "depleted" }}'
:overlay-class-name="themeSwitcher.currentTheme">
<template slot="content"> <template slot="content">
<p v-for="clientEmail in clientCount[dbInbound.id].depleted">[[ clientEmail ]]</p> <p v-for="clientEmail in clientCount[dbInbound.id].depleted">
[[ clientEmail ]]</p>
</template> </template>
<a-tag style="margin:0; padding: 0 2px;" color="red" v-if="clientCount[dbInbound.id].depleted.length">[[ clientCount[dbInbound.id].depleted.length ]]</a-tag> <a-tag style="margin:0; padding: 0 2px;" color="red"
v-if="clientCount[dbInbound.id].depleted.length">[[
clientCount[dbInbound.id].depleted.length ]]
</a-tag>
</a-popover> </a-popover>
<a-popover title='{{ i18n "depletingSoon" }}' :overlay-class-name="themeSwitcher.currentTheme"> <a-popover title='{{ i18n "depletingSoon" }}'
:overlay-class-name="themeSwitcher.currentTheme">
<template slot="content"> <template slot="content">
<p v-for="clientEmail in clientCount[dbInbound.id].expiring">[[ clientEmail ]]</p> <p v-for="clientEmail in clientCount[dbInbound.id].expiring">
[[ clientEmail ]]</p>
</template> </template>
<a-tag style="margin:0; padding: 0 2px;" color="orange" v-if="clientCount[dbInbound.id].expiring.length">[[ clientCount[dbInbound.id].expiring.length ]]</a-tag> <a-tag style="margin:0; padding: 0 2px;" color="orange"
v-if="clientCount[dbInbound.id].expiring.length">[[
clientCount[dbInbound.id].expiring.length ]]
</a-tag>
</a-popover> </a-popover>
<a-popover title='{{ i18n "online" }}' :overlay-class-name="themeSwitcher.currentTheme"> <a-popover title='{{ i18n "online" }}'
:overlay-class-name="themeSwitcher.currentTheme">
<template slot="content"> <template slot="content">
<p v-for="clientEmail in clientCount[dbInbound.id].online">[[ clientEmail ]]</p> <p v-for="clientEmail in clientCount[dbInbound.id].online">
[[ clientEmail ]]</p>
</template> </template>
<a-tag style="margin:0; padding: 0 2px;" color="green" v-if="clientCount[dbInbound.id].online.length">[[ clientCount[dbInbound.id].online.length ]]</a-tag> <a-tag style="margin:0; padding: 0 2px;" color="green"
v-if="clientCount[dbInbound.id].online.length">[[
clientCount[dbInbound.id].online.length ]]
</a-tag>
</a-popover> </a-popover>
</td> </td>
</tr> </tr>
@ -483,7 +603,9 @@
</tr> </tr>
<tr v-if="dbInbound.total > 0 && dbInbound.up + dbInbound.down < dbInbound.total"> <tr v-if="dbInbound.total > 0 && dbInbound.up + dbInbound.down < dbInbound.total">
<td>{{ i18n "remained" }}</td> <td>{{ i18n "remained" }}</td>
<td>[[ sizeFormat(dbInbound.total - dbInbound.up - dbInbound.down) ]]</td> <td>[[ sizeFormat(dbInbound.total - dbInbound.up -
dbInbound.down) ]]
</td>
</tr> </tr>
</table> </table>
</template> </template>
@ -493,8 +615,10 @@
[[ sizeFormat(dbInbound.total) ]] [[ sizeFormat(dbInbound.total) ]]
</template> </template>
<template v-else> <template v-else>
<svg height="10px" width="14px" viewBox="0 0 640 512" fill="currentColor"> <svg height="10px" width="14px" viewBox="0 0 640 512"
<path d="M484.4 96C407 96 349.2 164.1 320 208.5C290.8 164.1 233 96 155.6 96C69.75 96 0 167.8 0 256s69.75 160 155.6 160C233.1 416 290.8 347.9 320 303.5C349.2 347.9 407 416 484.4 416C570.3 416 640 344.2 640 256S570.3 96 484.4 96zM155.6 368C96.25 368 48 317.8 48 256s48.25-112 107.6-112c67.75 0 120.5 82.25 137.1 112C276 285.8 223.4 368 155.6 368zM484.4 368c-67.75 0-120.5-82.25-137.1-112C364 226.2 416.6 144 484.4 144C543.8 144 592 194.2 592 256S543.8 368 484.4 368z" fill="currentColor"></path> fill="currentColor">
<path d="M484.4 96C407 96 349.2 164.1 320 208.5C290.8 164.1 233 96 155.6 96C69.75 96 0 167.8 0 256s69.75 160 155.6 160C233.1 416 290.8 347.9 320 303.5C349.2 347.9 407 416 484.4 416C570.3 416 640 344.2 640 256S570.3 96 484.4 96zM155.6 368C96.25 368 48 317.8 48 256s48.25-112 107.6-112c67.75 0 120.5 82.25 137.1 112C276 285.8 223.4 368 155.6 368zM484.4 368c-67.75 0-120.5-82.25-137.1-112C364 226.2 416.6 144 484.4 144C543.8 144 592 194.2 592 256S543.8 368 484.4 368z"
fill="currentColor"></path>
</svg> </svg>
</template> </template>
</a-tag> </a-tag>
@ -504,18 +628,23 @@
<tr> <tr>
<td>{{ i18n "pages.inbounds.expireDate" }}</td> <td>{{ i18n "pages.inbounds.expireDate" }}</td>
<td> <td>
<a-tag style="min-width: 50px; text-align: center;" v-if="dbInbound.expiryTime > 0" <a-tag style="min-width: 50px; text-align: center;"
v-if="dbInbound.expiryTime > 0"
:color="dbInbound.isExpiry? 'red': 'blue'"> :color="dbInbound.isExpiry? 'red': 'blue'">
<template v-if="app.datepicker === 'gregorian'"> <template v-if="app.datepicker === 'gregorian'">
[[ DateUtil.formatMillis(dbInbound.expiryTime) ]] [[ DateUtil.formatMillis(dbInbound.expiryTime) ]]
</template> </template>
<template v-else> <template v-else>
[[ DateUtil.convertToJalalian(moment(dbInbound.expiryTime)) ]] [[ DateUtil.convertToJalalian(moment(dbInbound.expiryTime))
]]
</template> </template>
</a-tag> </a-tag>
<a-tag v-else style="text-align: center;" color="purple" class="infinite-tag"> <a-tag v-else style="text-align: center;" color="purple"
<svg height="10px" width="14px" viewBox="0 0 640 512" fill="currentColor"> class="infinite-tag">
<path d="M484.4 96C407 96 349.2 164.1 320 208.5C290.8 164.1 233 96 155.6 96C69.75 96 0 167.8 0 256s69.75 160 155.6 160C233.1 416 290.8 347.9 320 303.5C349.2 347.9 407 416 484.4 416C570.3 416 640 344.2 640 256S570.3 96 484.4 96zM155.6 368C96.25 368 48 317.8 48 256s48.25-112 107.6-112c67.75 0 120.5 82.25 137.1 112C276 285.8 223.4 368 155.6 368zM484.4 368c-67.75 0-120.5-82.25-137.1-112C364 226.2 416.6 144 484.4 144C543.8 144 592 194.2 592 256S543.8 368 484.4 368z" fill="currentColor"></path> <svg height="10px" width="14px" viewBox="0 0 640 512"
fill="currentColor">
<path d="M484.4 96C407 96 349.2 164.1 320 208.5C290.8 164.1 233 96 155.6 96C69.75 96 0 167.8 0 256s69.75 160 155.6 160C233.1 416 290.8 347.9 320 303.5C349.2 347.9 407 416 484.4 416C570.3 416 640 344.2 640 256S570.3 96 484.4 96zM155.6 368C96.25 368 48 317.8 48 256s48.25-112 107.6-112c67.75 0 120.5 82.25 137.1 112C276 285.8 223.4 368 155.6 368zM484.4 368c-67.75 0-120.5-82.25-137.1-112C364 226.2 416.6 144 484.4 144C543.8 144 592 194.2 592 256S543.8 368 484.4 368z"
fill="currentColor"></path>
</svg> </svg>
</a-tag> </a-tag>
</td> </td>
@ -523,7 +652,8 @@
</table> </table>
</template> </template>
<a-badge> <a-badge>
<a-icon v-if="!dbInbound.enable" slot="count" type="pause-circle" :style="'color: ' + themeSwitcher.isDarkTheme ? '#2c3950' : '#bcbcbc'"></a-icon> <a-icon v-if="!dbInbound.enable" slot="count" type="pause-circle"
:style="'color: ' + themeSwitcher.isDarkTheme ? '#2c3950' : '#bcbcbc'"></a-icon>
<a-button shape="round" size="small" style="font-size: 14px; padding: 0 10px;"> <a-button shape="round" size="small" style="font-size: 14px; padding: 0 10px;">
<a-icon type="info"></a-icon> <a-icon type="info"></a-icon>
</a-button> </a-button>
@ -633,12 +763,27 @@
{title: '{{ i18n "pages.inbounds.enable" }}', width: 30, scopedSlots: {customRender: 'enable'}}, {title: '{{ i18n "pages.inbounds.enable" }}', width: 30, scopedSlots: {customRender: 'enable'}},
{title: '{{ i18n "online" }}', width: 30, scopedSlots: {customRender: 'online'}}, {title: '{{ i18n "online" }}', width: 30, scopedSlots: {customRender: 'online'}},
{title: '{{ i18n "pages.inbounds.client" }}', width: 80, scopedSlots: {customRender: 'client'}}, {title: '{{ i18n "pages.inbounds.client" }}', width: 80, scopedSlots: {customRender: 'client'}},
{ title: '{{ i18n "pages.inbounds.traffic" }}', width: 80, align: 'center', scopedSlots: { customRender: 'traffic' } }, {
{ title: '{{ i18n "pages.inbounds.expireDate" }}', width: 80, align: 'center', scopedSlots: { customRender: 'expiryTime' } }, title: '{{ i18n "pages.inbounds.traffic" }}',
width: 80,
align: 'center',
scopedSlots: {customRender: 'traffic'}
},
{
title: '{{ i18n "pages.inbounds.expireDate" }}',
width: 80,
align: 'center',
scopedSlots: {customRender: 'expiryTime'}
},
]; ];
const innerMobileColumns = [ const innerMobileColumns = [
{ title: '{{ i18n "pages.inbounds.operate" }}', width: 10, align: 'center', scopedSlots: { customRender: 'actionMenu' } }, {
title: '{{ i18n "pages.inbounds.operate" }}',
width: 10,
align: 'center',
scopedSlots: {customRender: 'actionMenu'}
},
{title: '{{ i18n "pages.inbounds.client" }}', width: 90, align: 'left', scopedSlots: {customRender: 'client'}}, {title: '{{ i18n "pages.inbounds.client" }}', width: 90, align: 'left', scopedSlots: {customRender: 'client'}},
{title: '{{ i18n "pages.inbounds.info" }}', width: 10, align: 'center', scopedSlots: {customRender: 'info'}}, {title: '{{ i18n "pages.inbounds.info" }}', width: 10, align: 'center', scopedSlots: {customRender: 'info'}},
]; ];
@ -1076,7 +1221,8 @@
case Protocols.TROJAN: case Protocols.TROJAN:
case Protocols.SHADOWSOCKS: case Protocols.SHADOWSOCKS:
return clients.findIndex(item => item.password === client.password && item.email === client.email); return clients.findIndex(item => item.password === client.password && item.email === client.email);
default: return clients.findIndex(item => item.id === client.id && item.email === client.email); default:
return clients.findIndex(item => item.id === client.id && item.email === client.email);
} }
}, },
async addClient(clients, dbInboundId, modal) { async addClient(clients, dbInboundId, modal) {
@ -1198,37 +1344,59 @@
const response = { const response = {
inbounds: [], inbounds: [],
clients: [], clients: [],
editIds: [] editIds: [],
} };
if (dbInbounds && dbInbounds.length > 0 && currentClient) {
dbInbounds.forEach((dbInboundItem) => { if (!Array.isArray(dbInbounds) || dbInbounds.length === 0) {
const dbInbound = new DBInbound(dbInboundItem); console.warn("dbInbounds is empty or not an array");
if (dbInbound) {
const inbound = dbInbound.toInbound();
if (inbound) {
const clients = inbound.clients;
if (clients.length > 0) {
clients.forEach((client) => {
if (client['subId'] === currentClient['subId']) {
client['inboundId'] = dbInboundItem.id
client['clientId'] = this.getClientId(dbInbound.protocol, client)
response.inbounds.push(dbInboundItem.id)
response.clients.push(client)
response.editIds.push(client['clientId'])
}
})
}
}
}
})
}
return response; return response;
}, }
if (!currentClient || !currentClient.subId) {
console.warn("Invalid currentClient or missing subId:", currentClient);
return response;
}
dbInbounds.forEach((dbInboundItem) => {
try {
const dbInbound = new DBInbound(dbInboundItem);
if (!dbInbound) {
console.warn("Invalid DBInbound instance for item:", dbInboundItem);
return;
}
const inbound = dbInbound.toInbound();
if (!inbound || !Array.isArray(inbound.clients)) {
console.warn("Invalid inbound or clients missing:", inbound);
return;
}
inbound.clients.forEach((client) => {
if (client.subId === currentClient.subId) {
console.log("Matching client found:", client);
client.inboundId = dbInboundItem.id;
client.clientId = this.getClientId(dbInbound.protocol, client);
response.inbounds.push(dbInboundItem.id);
response.clients.push(client);
response.editIds.push(client.clientId);
}
});
} catch (error) {
console.error("Error processing dbInboundItem:", dbInboundItem, error);
}
});
return response;
}
,
getClientId(protocol, client) { getClientId(protocol, client) {
switch (protocol) { switch (protocol) {
case Protocols.TROJAN: return client.password; case Protocols.TROJAN:
case Protocols.SHADOWSOCKS: return client.email; return client.password;
default: return client.id; case Protocols.SHADOWSOCKS:
return client.email;
default:
return client.id;
} }
}, },
checkFallback(dbInbound) { checkFallback(dbInbound) {
@ -1569,8 +1737,7 @@
this.getDefaultSettings(); this.getDefaultSettings();
if (this.isRefreshEnabled) { if (this.isRefreshEnabled) {
this.startDataRefreshLoop(); this.startDataRefreshLoop();
} } else {
else {
this.getDBInbounds(); this.getDBInbounds();
} }
this.loading(false); this.loading(false);

View file

@ -92,7 +92,7 @@
<a-col :sm="24" :lg="12"> <a-col :sm="24" :lg="12">
<a-card hoverable> <a-card hoverable>
<b>3X-UI:</b> <b>3X-UI:</b>
<a rel="noopener" href="https://github.com/MHSanaei/3x-ui/releases" target="_blank"><a-tag color="green">v{{ .cur_ver }}</a-tag></a> <a rel="noopener" href="https://github.com/Incognito-Coder/3xui-LTS/releases" target="_blank"><a-tag color="green">v{{ .cur_ver }}</a-tag></a>
<a rel="noopener" href="https://t.me/XrayUI" target="_blank"><a-tag color="green">@XrayUI</a-tag></a> <a rel="noopener" href="https://t.me/XrayUI" target="_blank"><a-tag color="green">@XrayUI</a-tag></a>
</a-card> </a-card>
</a-col> </a-col>

12
x-ui.sh
View file

@ -143,7 +143,7 @@ before_show_menu() {
} }
install() { install() {
bash <(curl -Ls https://raw.githubusercontent.com/MHSanaei/3x-ui/main/install.sh) bash <(curl -Ls https://raw.githubusercontent.com/Incognito-Coder/3xui-LTS/main/install.sh)
if [[ $? == 0 ]]; then if [[ $? == 0 ]]; then
if [[ $# == 0 ]]; then if [[ $# == 0 ]]; then
start start
@ -162,7 +162,7 @@ update() {
fi fi
return 0 return 0
fi fi
bash <(curl -Ls https://raw.githubusercontent.com/MHSanaei/3x-ui/main/install.sh) bash <(curl -Ls https://raw.githubusercontent.com/Incognito-Coder/3xui-LTS/main/install.sh)
if [[ $? == 0 ]]; then if [[ $? == 0 ]]; then
LOGI "Update is complete, Panel has automatically restarted " LOGI "Update is complete, Panel has automatically restarted "
before_show_menu before_show_menu
@ -180,7 +180,7 @@ update_menu() {
return 0 return 0
fi fi
wget --no-check-certificate -O /usr/bin/x-ui https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.sh wget --no-check-certificate -O /usr/bin/x-ui https://raw.githubusercontent.com/Incognito-Coder/3xui-LTS/main/x-ui.sh
chmod +x /usr/local/x-ui/x-ui.sh chmod +x /usr/local/x-ui/x-ui.sh
chmod +x /usr/bin/x-ui chmod +x /usr/bin/x-ui
@ -202,7 +202,7 @@ legacy_version() {
exit 1 exit 1
fi fi
# Use the entered panel version in the download link # Use the entered panel version in the download link
install_command="bash <(curl -Ls "https://raw.githubusercontent.com/mhsanaei/3x-ui/v$tag_version/install.sh") v$tag_version" install_command="bash <(curl -Ls "https://raw.githubusercontent.com/Incognito-Coder/3xui-LTS/v$tag_version/install.sh") v$tag_version"
echo "Downloading and installing panel version $tag_version..." echo "Downloading and installing panel version $tag_version..."
eval $install_command eval $install_command
@ -233,7 +233,7 @@ uninstall() {
echo "" echo ""
echo -e "Uninstalled Successfully.\n" echo -e "Uninstalled Successfully.\n"
echo "If you need to install this panel again, you can use below command:" echo "If you need to install this panel again, you can use below command:"
echo -e "${green}bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)${plain}" echo -e "${green}bash <(curl -Ls https://raw.githubusercontent.com/Incognito-Coder/3xui-LTS/master/install.sh)${plain}"
echo "" echo ""
# Trap the SIGTERM signal # Trap the SIGTERM signal
trap delete_script SIGTERM trap delete_script SIGTERM
@ -575,7 +575,7 @@ enable_bbr() {
} }
update_shell() { update_shell() {
wget -O /usr/bin/x-ui -N --no-check-certificate https://github.com/MHSanaei/3x-ui/raw/main/x-ui.sh wget -O /usr/bin/x-ui -N --no-check-certificate https://github.com/Incognito-Coder/3xui-LTS/raw/main/x-ui.sh
if [[ $? != 0 ]]; then if [[ $? != 0 ]]; then
echo "" echo ""
LOGE "Failed to download script, Please check whether the machine can connect Github" LOGE "Failed to download script, Please check whether the machine can connect Github"