diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 790c3a1a..feed7051 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -77,7 +77,7 @@ jobs: cd x-ui/bin # Download dependencies - Xray_URL="https://github.com/XTLS/Xray-core/releases/download/v1.8.8/" + Xray_URL="https://github.com/XTLS/Xray-core/releases/download/v1.8.9/" if [ "${{ matrix.platform }}" == "amd64" ]; then wget ${Xray_URL}Xray-linux-64.zip unzip Xray-linux-64.zip diff --git a/.gitignore b/.gitignore index 158f4ee1..781a633d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ .cache .sync* *.tar.gz +*.log access.log error.log tmp diff --git a/DockerInit.sh b/DockerInit.sh index e9cccf49..aa1c856d 100755 --- a/DockerInit.sh +++ b/DockerInit.sh @@ -27,7 +27,7 @@ case $1 in esac mkdir -p build/bin cd build/bin -wget "https://github.com/XTLS/Xray-core/releases/download/v1.8.8/Xray-linux-${ARCH}.zip" +wget "https://github.com/XTLS/Xray-core/releases/download/v1.8.9/Xray-linux-${ARCH}.zip" unzip "Xray-linux-${ARCH}.zip" rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat mv xray "xray-linux-${FNAME}" diff --git a/README.md b/README.md index 90f23701..5c42c9e0 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # 3X-UI +[English](/README.md) | [Chinese](/README.zh.md) +

Image

**An Advanced Web Panel • Built on Xray Core** @@ -26,10 +28,10 @@ bash <(curl -Ls https://raw.githubusercontent.com/maple367/3x-ui/main/install.sh ## Install Custom Version -To install your desired version, add the version to the end of the installation command. e.g., ver `v2.2.1`: +To install your desired version, add the version to the end of the installation command. e.g., ver `v2.2.6`: ``` -bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) v2.2.1 +bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) v2.2.6 ``` ## SSL Certificate @@ -311,9 +313,9 @@ If you want to use routing to WARP before v2.1.0 follow steps as below: ```sh "log": { - "access": "./access.log", - "dnsLog": false, - "loglevel": "warning" + "access": "./access.log", + "dnsLog": false, + "loglevel": "warning" }, ``` diff --git a/README.zh.md b/README.zh.md new file mode 100644 index 00000000..fd7f7691 --- /dev/null +++ b/README.zh.md @@ -0,0 +1,473 @@ +# 3X-UI + +[English](/README.md) | [Chinese](/README.zh.md) + +

Image

+ +**一个更好的面板 • 基于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/actions/workflow/status/mhsanaei/3x-ui/release.yml.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)](#) +[![License](https://img.shields.io/badge/license-GPL%20V3-blue.svg?longCache=true)](https://www.gnu.org/licenses/gpl-3.0.en.html) + +> **Disclaimer:** 此项目仅供个人学习交流,请不要用于非法目的,请不要在生产环境中使用。 + +**如果此项目对你有用,请给一个**:star2: + +

Image

+ +- USDT (TRC20): `TXncxkvhkDWGts487Pjqq1qT9JmwRUz8CC` + +## 安装 & 升级 + +``` +bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) +``` + +## 安装指定版本 + +要安装所需的版本,请将该版本添加到安装命令的末尾。 e.g., ver `v2.2.6`: + +``` +bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) v2.2.6 +``` + +## SSL 认证 + +
+ 点击查看 SSL 认证 + +### Cloudflare + +管理脚本具有用于 Cloudflare 的内置 SSL 证书应用程序。若要使用此脚本申请证书,需要满足以下条件: + +- Cloudflare 邮箱地址 +- Cloudflare Global API Key +- 域名已通过 cloudflare 解析到当前服务器 + +**1:** 在终端中运行`x-ui`, 选择 `Cloudflare SSL Certificate`. + + +### Certbot +``` +apt-get install certbot -y +certbot certonly --standalone --agree-tos --register-unsafely-without-email -d yourdomain.com +certbot renew --dry-run +``` + +***Tip:*** *管理脚本具有 Certbot 。使用 `x-ui` 命令, 选择 `SSL Certificate Management`.* + +
+ +## 手动安装 & 升级 + +
+ 点击查看 手动安装 & 升级 + +#### 使用 + +1. 若要将最新版本的压缩包直接下载到服务器,请运行以下命令: + +```sh +ARCH=$(uname -m) +case "${ARCH}" in + x86_64 | x64 | amd64) XUI_ARCH="amd64" ;; + i*86 | x86) XUI_ARCH="386" ;; + armv8* | armv8 | arm64 | aarch64) XUI_ARCH="arm64" ;; + armv7* | armv7) XUI_ARCH="armv7" ;; + armv6* | armv6) XUI_ARCH="armv6" ;; + armv5* | armv5) XUI_ARCH="armv5" ;; + *) XUI_ARCH="amd64" ;; +esac + + +wget https://github.com/MHSanaei/3x-ui/releases/latest/download/x-ui-linux-${XUI_ARCH}.tar.gz +``` + +2. 下载压缩包后,执行以下命令安装或升级 x-ui: + +```sh +ARCH=$(uname -m) +case "${ARCH}" in + x86_64 | x64 | amd64) XUI_ARCH="amd64" ;; + i*86 | x86) XUI_ARCH="386" ;; + armv8* | armv8 | arm64 | aarch64) XUI_ARCH="arm64" ;; + armv7* | armv7) XUI_ARCH="armv7" ;; + armv6* | armv6) XUI_ARCH="armv6" ;; + armv5* | armv5) XUI_ARCH="armv5" ;; + *) XUI_ARCH="amd64" ;; +esac + +cd /root/ +rm -rf x-ui/ /usr/local/x-ui/ /usr/bin/x-ui +tar zxvf x-ui-linux-${XUI_ARCH}.tar.gz +chmod +x x-ui/x-ui x-ui/bin/xray-linux-* x-ui/x-ui.sh +cp x-ui/x-ui.sh /usr/bin/x-ui +cp -f x-ui/x-ui.service /etc/systemd/system/ +mv x-ui/ /usr/local/ +systemctl daemon-reload +systemctl enable x-ui +systemctl restart x-ui +``` + +
+ +## 通过Docker安装 + +
+ 点击查看 通过Docker安装 + +#### 使用 + +1. 安装Docker: + + ```sh + bash <(curl -sSL https://get.docker.com) + ``` + +2. 克隆仓库: + + ```sh + git clone https://github.com/MHSanaei/3x-ui.git + cd 3x-ui + ``` + +3. 运行服务: + + ```sh + docker compose up -d + ``` + + 或 + + ```sh + docker run -itd \ + -e XRAY_VMESS_AEAD_FORCED=false \ + -v $PWD/db/:/etc/x-ui/ \ + -v $PWD/cert/:/root/cert/ \ + --network=host \ + --restart=unless-stopped \ + --name 3x-ui \ + ghcr.io/mhsanaei/3x-ui:latest + ``` + +更新至最新版本 + + ```sh + cd 3x-ui + docker compose down + docker compose pull 3x-ui + docker compose up -d + ``` + +从Docker中删除3x-ui + + ```sh + docker stop 3x-ui + docker rm 3x-ui + cd -- + rm -r 3x-ui + ``` + +
+ + +## 建议使用的操作系统 + +- Ubuntu 20.04+ +- Debian 11+ +- CentOS 8+ +- Fedora 36+ +- Arch Linux +- Manjaro +- Armbian +- AlmaLinux 9+ +- Rockylinux 9+ + +## 支持的架构和设备 +
+ 点击查看 支持的架构和设备 + +我们的平台提供与各种架构和设备的兼容性,确保在各种计算环境中的灵活性。以下是我们支持的关键架构: + +- **amd64**: 这种流行的架构是个人计算机和服务器的标准,可以无缝地适应大多数现代操作系统。 + +- **x86 / i386**: 这种架构在台式机和笔记本电脑中被广泛采用,得到了众多操作系统和应用程序的广泛支持,包括但不限于 Windows、macOS 和 Linux 系统。 + +- **armv8 / arm64 / aarch64**: 这种架构专为智能手机和平板电脑等当代移动和嵌入式设备量身定制,以 Raspberry Pi 4、Raspberry Pi 3、Raspberry Pi Zero 2/Zero 2 W、Orange Pi 3 LTS 等设备为例。 + +- **armv7 / arm / arm32**: 作为较旧的移动和嵌入式设备的架构,它仍然广泛用于Orange Pi Zero LTS、Orange Pi PC Plus、Raspberry Pi 2等设备。 + +- **armv6 / arm / arm32**: 这种架构面向非常老旧的嵌入式设备,虽然不太普遍,但仍在使用中。Raspberry Pi 1、Raspberry Pi Zero/Zero W 等设备都依赖于这种架构。 + +- **armv5 / arm / arm32**: 它是一种主要与早期嵌入式系统相关的旧架构,目前不太常见,但仍可能出现在早期 Raspberry Pi 版本和一些旧智能手机等传统设备中。 +
+ +## Languages + +- English(英语) +- Farsi(伊朗语) +- Chinese(中文) +- Russian(俄语) +- Vietnamese(越南语) +- Spanish(西班牙语) +- Indonesian (印度尼西亚语) +- Ukrainian(乌克兰语) + + +## Features + +- 系统状态监控 +- 在所有入站和客户端中搜索 +- 深色/浅色主题 +- 支持多用户和多协议 +- 支持多种协议,包括 VMess、VLESS、Trojan、Shadowsocks、Dokodemo-door、Socks、HTTP、wireguard +- 支持 XTLS 原生协议,包括 RPRX-Direct、Vision、REALITY +- 流量统计、流量限制、过期时间限制 +- 可自定义的 Xray配置模板 +- 支持HTTPS访问面板(自建域名+SSL证书) +- 支持一键式SSL证书申请和自动续费 +- 更多高级配置项目请参考面板 +- 修复了 API 路由(用户设置将使用 API 创建) +- 支持通过面板中提供的不同项目更改配置。 +- 支持从面板导出/导入数据库 + + +## 默认设置 + +
+ 点击查看 默认设置 + + ### 信息 + +- **端口:** 2053 +- **用户名 & 密码:** 当您跳过设置时,此项会随机生成。 +- **数据库路径:** + - /etc/x-ui/x-ui.db +- **Xray 配置路径:** + - /usr/local/x-ui/bin/config.json +- **面板链接(无SSL):** + - http://ip:2053/panel + - http://domain:2053/panel +- **面板链接(有SSL):** + - https://domain:2053/panel + +
+ +## [WARP 配置](https://gitlab.com/fscarmen/warp) + +
+ 点击查看 WARP 配置 + +#### 使用 + +如果要在 v2.1.0 之前使用 WARP 路由,请按照以下步骤操作: + +**1.** 在 **SOCKS Proxy Mode** 模式中安装Wrap + + ```sh + bash <(curl -sSL https://raw.githubusercontent.com/hamid-gh98/x-ui-scripts/main/install_warp_proxy.sh) + ``` + +**2.** 如果您已经安装了 warp,您可以使用以下命令卸载: + + ```sh + warp u + ``` + +**3.** 在面板中打开您需要的配置 + + 配置: + + - Block Ads + - Route Google + Netflix + Spotify + OpenAI (ChatGPT) to WARP + - Fix Google 403 error + +
+ +## IP 限制 + +
+ 点击查看 IP 限制 + +#### 使用 + +**注意:** 使用 IP 隧道时,IP 限制无法正常工作。 + +- 适用于最高 `v1.6.1` : + + - IP 限制 已被集成在面板中。 + +- 适用于 `v1.7.0` 以及更新的版本: + + - 要使 IP 限制正常工作,您需要按照以下步骤安装 fail2ban 及其所需的文件: + + 1. 使用面板内置的 `x-ui` 指令 + 2. 选择 `IP Limit Management`. + 3. 根据您的需要选择合适的选项。 + + - 确保您的 Xray 配置上有 ./access.log 。在 v2.1.3 之后,我们有一个选项。 + + ```sh + "log": { + "access": "./access.log", + "dnsLog": false, + "loglevel": "warning" + }, + ``` + +
+ +## Telegram 机器人 + +
+ 点击查看 Telegram 机器人 + +#### 使用 + +Web 面板通过 Telegram Bot 支持每日流量、面板登录、数据库备份、系统状态、客户端信息等通知和功能。要使用机器人,您需要在面板中设置机器人相关参数,包括: + +- 电报令牌 +- 管理员聊天 ID +- 通知时间(cron 语法) +- 到期日期通知 +- 流量上限通知 +- 数据库备份 +- CPU 负载通知 + + +**参考:** + +- `30 \* \* \* \* \*` - 在每个点的 30 秒处通知 +- `0 \*/10 \* \* \* \*` - 每 10 分钟的第一秒通知 +- `@hourly` - 每小时通知 +- `@daily` - 每天通知 (00:00) +- `@weekly` - 每周通知 +- `@every 8h` - 每8小时通知 + +### Telegram Bot 功能 + +- 定期报告 +- 登录通知 +- CPU 阈值通知 +- 提前报告的过期时间和流量阈值 +- 如果将客户的电报用户名添加到用户的配置中,则支持客户端报告菜单 +- 支持使用UUID(VMESS/VLESS)或密码(TROJAN)搜索报文流量报告 - 匿名 +- 基于菜单的机器人 +- 通过电子邮件搜索客户端(仅限管理员) +- 检查所有入库 +- 检查服务器状态 +- 检查耗尽的用户 +- 根据请求和定期报告接收备份 +- 多语言机器人 + +### 注册 Telegram bot + +- 与 [Botfather](https://t.me/BotFather) 对话: + ![Botfather](./media/botfather.png) + +- 使用 /newbot 创建新机器人:你需要提供机器人名称以及用户名,注意名称中末尾要包含“bot” + ![创建机器人](./media/newbot.png) + +- 启动您刚刚创建的机器人。可以在此处找到机器人的链接。 + ![令牌](./media/token.png) + +- 输入您的面板并配置 Telegram 机器人设置,如下所示: + ![面板设置](./media/panel-bot-config.png) + +在输入字段编号 3 中输入机器人令牌。 +在输入字段编号 4 中输入用户 ID。具有此 id 的 Telegram 帐户将是机器人管理员。 (您可以输入多个,只需将它们用“ ,”分开即可) + +- 如何获取TG ID? 使用 [bot](https://t.me/useridinfobot), 启动机器人,它会给你 Telegram 用户 ID。 +![用户 ID](./media/user-id.png) + +
+ +## API 路由 + +
+ 点击查看 API 路由 + +#### 使用 + +- `/login` 使用 `POST` 用户名称 & 密码: `{username: '', password: ''}` 登录 +- `/panel/api/inbounds` 以下操作的基础: + +| 方法 | 路径 | 操作 | +| :----: | ---------------------------------- | ------------------------------------------- | +| `GET` | `"/list"` | 获取所有入站 | +| `GET` | `"/get/:id"` | 获取所有入站以及inbound.id | +| `GET` | `"/getClientTraffics/:email"` | 通过电子邮件获取客户端流量 | +| `GET` | `"/createbackup"` | Telegram 机器人向管理员发送备份 | +| `POST` | `"/add"` | 添加入站 | +| `POST` | `"/del/:id"` | 删除入站 | +| `POST` | `"/update/:id"` | 更新入站 | +| `POST` | `"/clientIps/:email"` | 客户端 IP 地址 | +| `POST` | `"/clearClientIps/:email"` | 清除客户端 IP 地址 | +| `POST` | `"/addClient"` | 将客户端添加到入站 | +| `POST` | `"/:id/delClient/:clientId"` | 通过 clientId\* 删除客户端 | +| `POST` | `"/updateClient/:clientId"` | 通过 clientId\* 更新客户端 | +| `POST` | `"/:id/resetClientTraffic/:email"` | 重置客户端的流量 | +| `POST` | `"/resetAllTraffics"` | 重置所有入站的流量 | +| `POST` | `"/resetAllClientTraffics/:id"` | 重置入站中所有客户端的流量 | +| `POST` | `"/delDepletedClients/:id"` | 删除入站耗尽的客户端 (-1: all) | +| `POST` | `"/onlines"` | 获取在线用户 ( 电子邮件列表 ) | + +\*- `clientId` 项应该使用下列数据 + +- `client.id` VMESS and VLESS +- `client.password` TROJAN +- `client.email` Shadowsocks + + +- [API 文档](https://documenter.getpostman.com/view/16802678/2s9YkgD5jm) +- [Run In Postman](https://app.getpostman.com/run-collection/16802678-1a4c9270-ac77-40ed-959a-7aa56dc4a415?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D16802678-1a4c9270-ac77-40ed-959a-7aa56dc4a415%26entityType%3Dcollection%26workspaceId%3D2cd38c01-c851-4a15-a972-f181c23359d9) +
+ +## 环境变量 + +
+ 点击查看 环境变量 + +#### Usage + +| 变量 | Type | 默认 | +| -------------- | :--------------------------------------------: | :------------ | +| XUI_LOG_LEVEL | `"debug"` \| `"info"` \| `"warn"` \| `"error"` | `"info"` | +| XUI_DEBUG | `boolean` | `false` | +| XUI_BIN_FOLDER | `string` | `"bin"` | +| XUI_DB_FOLDER | `string` | `"/etc/x-ui"` | +| XUI_LOG_FOLDER | `string` | `"/var/log"` | + +例子: + +```sh +XUI_BIN_FOLDER="bin" XUI_DB_FOLDER="/etc/x-ui" go build main.go +``` + +
+ +## 预览 + +![1](./media/1.png) +![2](./media/2.png) +![3](./media/3.png) +![4](./media/4.png) +![5](./media/5.png) +![6](./media/6.png) +![7](./media/7.png) + +## 特别感谢 + +- [alireza0](https://github.com/alireza0/) + +## 致谢 + +- [Iran v2ray rules](https://github.com/chocolate4u/Iran-v2ray-rules) (License: **GPL-3.0**): _Enhanced v2ray/xray and v2ray/xray-clients routing rules with built-in Iranian domains and a focus on security and adblocking._ +- [Vietnam Adblock rules](https://github.com/vuong2023/vn-v2ray-rules) (License: **GPL-3.0**): _A hosted domain hosted in Vietnam and blocklist with the most efficiency for Vietnamese._ + +## Star趋势 + +[![Stargazers over time](https://starchart.cc/MHSanaei/3x-ui.svg)](https://starchart.cc/MHSanaei/3x-ui) diff --git a/config/version b/config/version index fae692e4..1506473e 100644 --- a/config/version +++ b/config/version @@ -1 +1 @@ -2.2.1 \ No newline at end of file +2.2.6 \ No newline at end of file diff --git a/database/model/model.go b/database/model/model.go index 32ab255f..df41237d 100644 --- a/database/model/model.go +++ b/database/model/model.go @@ -2,6 +2,7 @@ package model import ( "fmt" + "x-ui/util/json_util" "x-ui/xray" ) diff --git a/go.mod b/go.mod index f6566e55..4a680303 100644 --- a/go.mod +++ b/go.mod @@ -12,35 +12,35 @@ require ( github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 github.com/pelletier/go-toml/v2 v2.1.1 github.com/robfig/cron/v3 v3.0.1 - github.com/shirou/gopsutil/v3 v3.24.1 + github.com/shirou/gopsutil/v3 v3.24.2 github.com/valyala/fasthttp v1.52.0 - github.com/xtls/xray-core v1.8.8 + github.com/xtls/xray-core v1.8.9 go.uber.org/atomic v1.11.0 golang.org/x/text v0.14.0 - google.golang.org/grpc v1.62.0 + google.golang.org/grpc v1.62.1 gorm.io/driver/sqlite v1.5.5 gorm.io/gorm v1.25.7 ) require ( github.com/andybalholm/brotli v1.1.0 // indirect - github.com/bytedance/sonic v1.10.2 // indirect + github.com/bytedance/sonic v1.11.2 // indirect github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect github.com/chenzhuoyu/iasm v0.9.1 // indirect github.com/cloudflare/circl v1.3.7 // indirect github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect - github.com/fasthttp/router v1.4.22 // indirect + github.com/fasthttp/router v1.5.0 // indirect github.com/francoispqt/gojay v1.2.13 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.17.0 // indirect + github.com/go-playground/validator/v10 v10.19.0 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/google/btree v1.1.2 // indirect - github.com/google/pprof v0.0.0-20240225044709-fd706174c886 // indirect + github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7 // indirect github.com/gorilla/context v1.1.2 // indirect github.com/gorilla/securecookie v1.1.2 // indirect github.com/gorilla/sessions v1.2.2 // indirect @@ -51,22 +51,22 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.17.7 // indirect github.com/klauspost/cpuid/v2 v2.2.7 // indirect - github.com/leodido/go-urn v1.2.4 // indirect + github.com/leodido/go-urn v1.4.0 // indirect github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed // indirect github.com/mattn/go-isatty v0.0.19 // indirect - github.com/mattn/go-sqlite3 v1.14.19 // indirect + github.com/mattn/go-sqlite3 v1.14.22 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/onsi/ginkgo/v2 v2.15.0 // indirect + github.com/onsi/ginkgo/v2 v2.16.0 // indirect github.com/pires/go-proxyproto v0.7.0 // indirect github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect github.com/quic-go/quic-go v0.41.0 // indirect github.com/refraction-networking/utls v1.6.3 // indirect github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect github.com/rogpeppe/go-internal v1.11.0 // indirect - github.com/sagernet/sing v0.3.2 // indirect + github.com/sagernet/sing v0.3.6 // indirect github.com/sagernet/sing-shadowsocks v0.2.6 // indirect - github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect + github.com/savsgio/gotils v0.0.0-20240303185622-093b76447511 // indirect github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect @@ -79,21 +79,21 @@ require ( github.com/vishvananda/netlink v1.2.1-beta.2.0.20230316163032-ced5aaba43e3 // indirect github.com/vishvananda/netns v0.0.4 // indirect github.com/xtls/reality v0.0.0-20231112171332-de1173cf2b19 // indirect - github.com/yusufpapurcu/wmi v1.2.3 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect go.uber.org/mock v0.4.0 // indirect go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect golang.org/x/arch v0.6.0 // indirect - golang.org/x/crypto v0.19.0 // indirect + golang.org/x/crypto v0.21.0 // indirect golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect - golang.org/x/mod v0.15.0 // indirect - golang.org/x/net v0.21.0 // indirect - golang.org/x/sys v0.17.0 // indirect + golang.org/x/mod v0.16.0 // indirect + golang.org/x/net v0.22.0 // indirect + golang.org/x/sys v0.18.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.18.0 // indirect + golang.org/x/tools v0.19.0 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c // indirect - google.golang.org/protobuf v1.32.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240308144416-29370a3891b7 // indirect + google.golang.org/protobuf v1.33.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gvisor.dev/gvisor v0.0.0-20231104011432-48a6d7d5bd0b // indirect lukechampine.com/blake3 v1.2.1 // indirect diff --git a/go.sum b/go.sum index 5a92f611..505b5b1b 100644 --- a/go.sum +++ b/go.sum @@ -20,8 +20,8 @@ github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBT github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= -github.com/bytedance/sonic v1.10.2 h1:GQebETVBxYB7JGWJtLBi07OVzWwt+8dWA00gEVW2ZFE= -github.com/bytedance/sonic v1.10.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= +github.com/bytedance/sonic v1.11.2 h1:ywfwo0a/3j9HR8wsYGWsIWl2mvRsI950HyoxiBERw5A= +github.com/bytedance/sonic v1.11.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= @@ -41,8 +41,8 @@ github.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165/go.mod h1:c9O8+fp github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 h1:y7y0Oa6UawqTFPCDw9JG6pdKt4F9pAhHv0B7FMGaGD0= github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/fasthttp/router v1.4.22 h1:qwWcYBbndVDwts4dKaz+A2ehsnbKilmiP6pUhXBfYKo= -github.com/fasthttp/router v1.4.22/go.mod h1:KeMvHLqhlB9vyDWD5TSvTccl9qeWrjSSiTJrJALHKV0= +github.com/fasthttp/router v1.5.0 h1:3Qbbo27HAPzwbpRzgiV5V9+2faPkPt3eNuRaDV6LYDA= +github.com/fasthttp/router v1.5.0/go.mod h1:FddcKNXFZg1imHcy+uKB0oo/o6yE9zD3wNguqlhWDak= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= @@ -61,8 +61,8 @@ github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= -github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= -github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= @@ -76,8 +76,8 @@ github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= -github.com/go-playground/validator/v10 v10.17.0 h1:SmVVlfAOtlZncTxRuinDPomC2DkXJ4E5T9gDA0AIH74= -github.com/go-playground/validator/v10 v10.17.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4= +github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= @@ -93,8 +93,8 @@ github.com/golang/mock v1.7.0-rc.1/go.mod h1:s42URUywIqd+OcERslBJvOjepvNymP31m3q github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= @@ -111,8 +111,8 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20240225044709-fd706174c886 h1:JSJUTZTQT1Gzb2ROdAKOY3HwzBYcclS2GgumhMfHqjw= -github.com/google/pprof v0.0.0-20240225044709-fd706174c886/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= +github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7 h1:y3N7Bm7Y9/CtpiVkw/ZWj6lSlDF3F74SfKwfTCer72Q= +github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -155,8 +155,8 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= -github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= -github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed h1:036IscGBfJsFIgJQzlui7nK1Ncm0tp2ktmPj8xO4N/0= github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k= @@ -165,8 +165,8 @@ github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-sqlite3 v1.14.19 h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbWfGI= -github.com/mattn/go-sqlite3 v1.14.19/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= @@ -183,8 +183,8 @@ github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJE github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/nicksnyder/go-i18n/v2 v2.4.0 h1:3IcvPOAvnCKwNm0TB0dLDTuawWEj+ax/RERNC+diLMM= github.com/nicksnyder/go-i18n/v2 v2.4.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4= -github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY= -github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM= +github.com/onsi/ginkgo/v2 v2.16.0 h1:7q1w9frJDzninhXxjZd+Y/x54XNjG/UlRLIYPZafsPM= +github.com/onsi/ginkgo/v2 v2.16.0/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88= @@ -221,17 +221,17 @@ github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6po github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/sagernet/sing v0.3.2 h1:CwWcxUBPkMvwgfe2/zUgY5oHG9qOL8Aob/evIFYK9jo= -github.com/sagernet/sing v0.3.2/go.mod h1:qHySJ7u8po9DABtMYEkNBcOumx7ZZJf/fbv2sfTkNHE= +github.com/sagernet/sing v0.3.6 h1:dsEdYLKBQlrxUfw1a92x0VdPvR1/BOxQ+HIMyaoEJsQ= +github.com/sagernet/sing v0.3.6/go.mod h1:+60H3Cm91RnL9dpVGWDPHt0zTQImO9Vfqt9a4rSambI= github.com/sagernet/sing-shadowsocks v0.2.6 h1:xr7ylAS/q1cQYS8oxKKajhuQcchd5VJJ4K4UZrrpp0s= github.com/sagernet/sing-shadowsocks v0.2.6/go.mod h1:j2YZBIpWIuElPFL/5sJAj470bcn/3QQ5lxZUNKLDNAM= -github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk= -github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g= +github.com/savsgio/gotils v0.0.0-20240303185622-093b76447511 h1:KanIMPX0QdEdB4R3CiimCAbxFrhB3j7h0/OvpYGVQa8= +github.com/savsgio/gotils v0.0.0-20240303185622-093b76447511/go.mod h1:sM7Mt7uEoCeFSCBM+qBrqvEo+/9vdmj19wzp3yzUhmg= github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb h1:XfLJSPIOUX+osiMraVgIrMR27uMXnRJWGm1+GL8/63U= github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/shirou/gopsutil/v3 v3.24.1 h1:R3t6ondCEvmARp3wxODhXMTLC/klMa87h2PHUw5m7QI= -github.com/shirou/gopsutil/v3 v3.24.1/go.mod h1:UU7a2MSBQa+kW1uuDq8DeEBS8kmrnQwsv2b5O513rwU= +github.com/shirou/gopsutil/v3 v3.24.2 h1:kcR0erMbLg5/3LcInpw0X/rrPSqq4CDPyI6A6ZRC18Y= +github.com/shirou/gopsutil/v3 v3.24.2/go.mod h1:tSg/594BcA+8UdQU2XcW803GWYgdtauFFPgJCJKZlVk= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= @@ -270,9 +270,9 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= @@ -301,10 +301,10 @@ github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1Y github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= github.com/xtls/reality v0.0.0-20231112171332-de1173cf2b19 h1:capMfFYRgH9BCLd6A3Er/cH3A9Nz3CU2KwxwOQZIePI= github.com/xtls/reality v0.0.0-20231112171332-de1173cf2b19/go.mod h1:dm4y/1QwzjGaK17ofi0Vs6NpKAHegZky8qk6J2JJZAE= -github.com/xtls/xray-core v1.8.8 h1:6ApBa5pNkPZ+I7jyJDZod3v5sadizS/BZr0pW7zcs8o= -github.com/xtls/xray-core v1.8.8/go.mod h1:Zp33A8cxnhP5Kt6nguQrMgNH4A/tgq7LE8cBedeNje8= -github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= -github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +github.com/xtls/xray-core v1.8.9 h1:wefcON0behu4DoQvCKJYZKsJlSvNhyq2I7vC2fxLFcY= +github.com/xtls/xray-core v1.8.9/go.mod h1:XDE4f422qJKAU3hNDSNZyWrOHvn9kF8UHVdyOzU38rc= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= @@ -321,16 +321,16 @@ golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= -golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= +golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -340,8 +340,8 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -371,9 +371,9 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -390,8 +390,8 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= -golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= +golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= +golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= @@ -409,19 +409,18 @@ google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoA google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c h1:NUsgEN92SQQqzfA+YtqYNqYmB3DMMYLlIwUZAQFVFbo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240308144416-29370a3891b7 h1:em/y72n4XlYRtayY/cVj6pnVzHa//BDA1BdoO+z9mdE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240308144416-29370a3891b7/go.mod h1:UCOku4NytXMJuLQE5VuqA5lX3PcHCBo8pxNyvkf4xBs= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.62.0 h1:HQKZ/fa1bXkX1oFOvSjmZEUL8wLSaZTjCcLAlmZRtdk= -google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= +google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk= +google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= -google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= diff --git a/logger/logger.go b/logger/logger.go index ca047cbc..35c5c0ac 100644 --- a/logger/logger.go +++ b/logger/logger.go @@ -8,12 +8,14 @@ import ( "github.com/op/go-logging" ) -var logger *logging.Logger -var logBuffer []struct { - time string - level logging.Level - log string -} +var ( + logger *logging.Logger + logBuffer []struct { + time string + level logging.Level + log string + } +) func init() { InitLogger(logging.INFO) diff --git a/main.go b/main.go index fdf54089..80111aae 100644 --- a/main.go +++ b/main.go @@ -8,6 +8,7 @@ import ( "os/signal" "syscall" _ "unsafe" + "x-ui/config" "x-ui/database" "x-ui/logger" @@ -243,21 +244,33 @@ func migrateDb() { } func removeSecret() { - err := database.InitDB(config.GetDBPath()) + userService := service.UserService{} + + secretExists, err := userService.CheckSecretExistence() if err != nil { - fmt.Println(err) + fmt.Println("Error checking secret existence:", err) return } - userService := service.UserService{} + + if !secretExists { + fmt.Println("No secret exists to remove.") + return + } + err = userService.RemoveUserSecret() if err != nil { - fmt.Println(err) + fmt.Println("Error removing secret:", err) + return } + settingService := service.SettingService{} err = settingService.SetSecretStatus(false) if err != nil { - fmt.Println(err) + fmt.Println("Error updating secret status:", err) + return } + + fmt.Println("Secret removed successfully.") } func main() { @@ -284,6 +297,7 @@ func main() { var remove_secret bool settingCmd.BoolVar(&reset, "reset", false, "reset all settings") settingCmd.BoolVar(&show, "show", false, "show current settings") + settingCmd.BoolVar(&remove_secret, "remove_secret", false, "remove secret") settingCmd.IntVar(&port, "port", 0, "set panel port") settingCmd.StringVar(&username, "username", "", "set login username") settingCmd.StringVar(&password, "password", "", "set login password") @@ -342,7 +356,7 @@ func main() { updateTgbotEnableSts(enabletgbot) } default: - fmt.Println("except 'run' or 'setting' subcommands") + fmt.Println("Invalid subcommands") fmt.Println() runCmd.Usage() fmt.Println() diff --git a/sub/default.json b/sub/default.json index ba13f6fb..d98a03ef 100644 --- a/sub/default.json +++ b/sub/default.json @@ -1,4 +1,5 @@ { + "remarks": "", "dns": { "tag": "dns_out", "queryStrategy": "UseIP", @@ -78,28 +79,9 @@ { "type": "field", "network": "tcp,udp", - "balancerTag": "all" - } - ], - "balancers": [ - { - "tag": "all", - "selector": [ - "proxy" - ], - "strategy": { - "type": "leastPing" - } + "outboundTag": "proxy" } ] }, - "observatory": { - "probeInterval": "5m", - "probeURL": "https://api.github.com/_private/browser/stats", - "subjectSelector": [ - "proxy" - ], - "EnableConcurrency": true - }, "stats": {} } \ No newline at end of file diff --git a/sub/sub.go b/sub/sub.go index 2a4a37f4..8fe025bc 100644 --- a/sub/sub.go +++ b/sub/sub.go @@ -7,6 +7,7 @@ import ( "net" "net/http" "strconv" + "x-ui/config" "x-ui/logger" "x-ui/util/common" @@ -91,15 +92,27 @@ func (s *Server) initRouter() (*gin.Engine, error) { SubJsonFragment = "" } + SubJsonMux, err := s.settingService.GetSubJsonMux() + if err != nil { + SubJsonMux = "" + } + + SubJsonRules, err := s.settingService.GetSubJsonRules() + if err != nil { + SubJsonRules = "" + } + g := engine.Group("/") - s.sub = NewSUBController(g, LinksPath, JsonPath, Encrypt, ShowInfo, RemarkModel, SubUpdates, SubJsonFragment) + s.sub = NewSUBController( + g, LinksPath, JsonPath, Encrypt, ShowInfo, RemarkModel, SubUpdates, + SubJsonFragment, SubJsonMux, SubJsonRules) return engine, nil } func (s *Server) Start() (err error) { - //This is an anonymous function, no function name + // This is an anonymous function, no function name defer func() { if err != nil { s.Stop() @@ -144,21 +157,19 @@ func (s *Server) Start() (err error) { if certFile != "" || keyFile != "" { cert, err := tls.LoadX509KeyPair(certFile, keyFile) - if err != nil { - listener.Close() - return err + if err == nil { + c := &tls.Config{ + Certificates: []tls.Certificate{cert}, + } + listener = network.NewAutoHttpsListener(listener) + listener = tls.NewListener(listener, c) + logger.Info("sub server run https on", listener.Addr()) + } else { + logger.Error("error in loading certificates: ", err) + logger.Info("sub server run http on", listener.Addr()) } - c := &tls.Config{ - Certificates: []tls.Certificate{cert}, - } - listener = network.NewAutoHttpsListener(listener) - listener = tls.NewListener(listener, c) - } - - if certFile != "" || keyFile != "" { - logger.Info("Sub server run https on", listener.Addr()) } else { - logger.Info("Sub server run http on", listener.Addr()) + logger.Info("sub server run http on", listener.Addr()) } s.listener = listener diff --git a/sub/subController.go b/sub/subController.go index b5c5cbac..8fd39efa 100644 --- a/sub/subController.go +++ b/sub/subController.go @@ -25,16 +25,19 @@ func NewSUBController( showInfo bool, rModel string, update string, - jsonFragment string) *SUBController { - + jsonFragment string, + jsonMux string, + jsonRules string, +) *SUBController { + sub := NewSubService(showInfo, rModel) a := &SUBController{ subPath: subPath, subJsonPath: jsonPath, subEncrypt: encrypt, updateInterval: update, - subService: NewSubService(showInfo, rModel), - subJsonService: NewSubJsonService(jsonFragment), + subService: sub, + subJsonService: NewSubJsonService(jsonFragment, jsonMux, jsonRules, sub), } a.initRouter(g) return a diff --git a/sub/subJsonService.go b/sub/subJsonService.go index 8bc98dea..c037eb00 100644 --- a/sub/subJsonService.go +++ b/sub/subJsonService.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "strings" + "x-ui/database/model" "x-ui/logger" "x-ui/util/json_util" @@ -17,15 +18,47 @@ import ( var defaultJson string type SubJsonService struct { - fragmanet string + configJson map[string]interface{} + defaultOutbounds []json_util.RawMessage + fragment string + mux string inboundService service.InboundService - SubService + SubService *SubService } -func NewSubJsonService(fragment string) *SubJsonService { +func NewSubJsonService(fragment string, mux string, rules string, subService *SubService) *SubJsonService { + var configJson map[string]interface{} + var defaultOutbounds []json_util.RawMessage + json.Unmarshal([]byte(defaultJson), &configJson) + if outboundSlices, ok := configJson["outbounds"].([]interface{}); ok { + for _, defaultOutbound := range outboundSlices { + jsonBytes, _ := json.Marshal(defaultOutbound) + defaultOutbounds = append(defaultOutbounds, jsonBytes) + } + } + + if rules != "" { + var newRules []interface{} + routing, _ := configJson["routing"].(map[string]interface{}) + defaultRules, _ := routing["rules"].([]interface{}) + json.Unmarshal([]byte(rules), &newRules) + defaultRules = append(newRules, defaultRules...) + fmt.Printf("routing: %#v\n\nRules: %#v\n\n", routing, defaultRules) + routing["rules"] = defaultRules + configJson["routing"] = routing + } + + if fragment != "" { + defaultOutbounds = append(defaultOutbounds, json_util.RawMessage(fragment)) + } + return &SubJsonService{ - fragmanet: fragment, + configJson: configJson, + defaultOutbounds: defaultOutbounds, + fragment: fragment, + mux: mux, + SubService: subService, } } @@ -38,19 +71,8 @@ func (s *SubJsonService) GetJson(subId string, host string) (string, string, err var header string var traffic xray.ClientTraffic var clientTraffics []xray.ClientTraffic - var configJson map[string]interface{} - var defaultOutbounds []json_util.RawMessage + var configArray []json_util.RawMessage - json.Unmarshal([]byte(defaultJson), &configJson) - if outboundSlices, ok := configJson["outbounds"].([]interface{}); ok { - for _, defaultOutbound := range outboundSlices { - jsonBytes, _ := json.Marshal(defaultOutbound) - defaultOutbounds = append(defaultOutbounds, jsonBytes) - } - } - - outbounds := []json_util.RawMessage{} - startIndex := 0 // Prepare Inbounds for _, inbound := range inbounds { clients, err := s.inboundService.GetClients(inbound) @@ -61,7 +83,7 @@ func (s *SubJsonService) GetJson(subId string, host string) (string, string, err continue } if len(inbound.Listen) > 0 && inbound.Listen[0] == '@' { - listen, port, streamSettings, err := s.getFallbackMaster(inbound.Listen, inbound.StreamSettings) + listen, port, streamSettings, err := s.SubService.getFallbackMaster(inbound.Listen, inbound.StreamSettings) if err == nil { inbound.Listen = listen inbound.Port = port @@ -69,22 +91,16 @@ func (s *SubJsonService) GetJson(subId string, host string) (string, string, err } } - var subClients []model.Client for _, client := range clients { if client.Enable && client.SubID == subId { - subClients = append(subClients, client) clientTraffics = append(clientTraffics, s.SubService.getClientTraffics(inbound.ClientStats, client.Email)) + newConfigs := s.getConfig(inbound, client, host) + configArray = append(configArray, newConfigs...) } } - - outbound := s.getOutbound(inbound, subClients, host, startIndex) - if outbound != nil { - outbounds = append(outbounds, outbound...) - startIndex += len(outbound) - } } - if len(outbounds) == 0 { + if len(configArray) == 0 { return "", "", nil } @@ -111,21 +127,15 @@ func (s *SubJsonService) GetJson(subId string, host string) (string, string, err } } - if s.fragmanet != "" { - outbounds = append(outbounds, json_util.RawMessage(s.fragmanet)) - } - // Combile outbounds - outbounds = append(outbounds, defaultOutbounds...) - configJson["outbounds"] = outbounds - finalJson, _ := json.MarshalIndent(configJson, "", " ") + finalJson, _ := json.MarshalIndent(configArray, "", " ") header = fmt.Sprintf("upload=%d; download=%d; total=%d; expire=%d", traffic.Up, traffic.Down, traffic.Total, traffic.ExpiryTime/1000) return string(finalJson), header, nil } -func (s *SubJsonService) getOutbound(inbound *model.Inbound, clients []model.Client, host string, startIndex int) []json_util.RawMessage { - var newOutbounds []json_util.RawMessage +func (s *SubJsonService) getConfig(inbound *model.Inbound, client model.Client, host string) []json_util.RawMessage { + var newJsonArray []json_util.RawMessage stream := s.streamData(inbound.StreamSettings) externalProxies, ok := stream["externalProxy"].([]interface{}) @@ -135,13 +145,13 @@ func (s *SubJsonService) getOutbound(inbound *model.Inbound, clients []model.Cli "forceTls": "same", "dest": host, "port": float64(inbound.Port), + "remark": "", }, } } delete(stream, "externalProxy") - config_index := startIndex for _, ep := range externalProxies { extPrxy := ep.(map[string]interface{}) inbound.Listen = extPrxy["dest"].(string) @@ -160,21 +170,28 @@ func (s *SubJsonService) getOutbound(inbound *model.Inbound, clients []model.Cli } } streamSettings, _ := json.MarshalIndent(newStream, "", " ") - inbound.StreamSettings = string(streamSettings) - for _, client := range clients { - inbound.Tag = fmt.Sprintf("proxy_%d", config_index) - switch inbound.Protocol { - case "vmess", "vless": - newOutbounds = append(newOutbounds, s.genVnext(inbound, client)) - case "trojan", "shadowsocks": - newOutbounds = append(newOutbounds, s.genServer(inbound, client)) - } - config_index += 1 + var newOutbounds []json_util.RawMessage + + switch inbound.Protocol { + case "vmess", "vless": + newOutbounds = append(newOutbounds, s.genVnext(inbound, streamSettings, client)) + case "trojan", "shadowsocks": + newOutbounds = append(newOutbounds, s.genServer(inbound, streamSettings, client)) } + + newOutbounds = append(newOutbounds, s.defaultOutbounds...) + newConfigJson := make(map[string]interface{}) + for key, value := range s.configJson { + newConfigJson[key] = value + } + newConfigJson["outbounds"] = newOutbounds + newConfigJson["remarks"] = s.SubService.genRemark(inbound, client.Email, extPrxy["remark"].(string)) + newConfig, _ := json.MarshalIndent(newConfigJson, "", " ") + newJsonArray = append(newJsonArray, newConfig) } - return newOutbounds + return newJsonArray } func (s *SubJsonService) streamData(stream string) map[string]interface{} { @@ -188,7 +205,7 @@ func (s *SubJsonService) streamData(stream string) map[string]interface{} { } delete(streamSettings, "sockopt") - if s.fragmanet != "" { + if s.fragment != "" { streamSettings["sockopt"] = json_util.RawMessage(`{"dialerProxy": "fragment", "tcpKeepAliveIdle": 100, "tcpNoDelay": true}`) } @@ -214,7 +231,7 @@ func (s *SubJsonService) removeAcceptProxy(setting interface{}) map[string]inter func (s *SubJsonService) tlsData(tData map[string]interface{}) map[string]interface{} { tlsData := make(map[string]interface{}, 1) - tlsClientSettings := tData["settings"].(map[string]interface{}) + tlsClientSettings, _ := tData["settings"].(map[string]interface{}) tlsData["serverName"] = tData["serverName"] tlsData["alpn"] = tData["alpn"] @@ -229,7 +246,7 @@ func (s *SubJsonService) tlsData(tData map[string]interface{}) map[string]interf func (s *SubJsonService) realityData(rData map[string]interface{}) map[string]interface{} { rltyData := make(map[string]interface{}, 1) - rltyClientSettings := rData["settings"].(map[string]interface{}) + rltyClientSettings, _ := rData["settings"].(map[string]interface{}) rltyData["show"] = false rltyData["publicKey"] = rltyClientSettings["publicKey"] @@ -253,7 +270,7 @@ func (s *SubJsonService) realityData(rData map[string]interface{}) map[string]in return rltyData } -func (s *SubJsonService) genVnext(inbound *model.Inbound, client model.Client) json_util.RawMessage { +func (s *SubJsonService) genVnext(inbound *model.Inbound, streamSettings json_util.RawMessage, client model.Client) json_util.RawMessage { outbound := Outbound{} usersData := make([]UserVnext, 1) @@ -272,8 +289,11 @@ func (s *SubJsonService) genVnext(inbound *model.Inbound, client model.Client) j } outbound.Protocol = string(inbound.Protocol) - outbound.Tag = inbound.Tag - outbound.StreamSettings = json_util.RawMessage(inbound.StreamSettings) + outbound.Tag = "proxy" + if s.mux != "" { + outbound.Mux = json_util.RawMessage(s.mux) + } + outbound.StreamSettings = streamSettings outbound.Settings = OutboundSettings{ Vnext: vnextData, } @@ -282,7 +302,7 @@ func (s *SubJsonService) genVnext(inbound *model.Inbound, client model.Client) j return result } -func (s *SubJsonService) genServer(inbound *model.Inbound, client model.Client) json_util.RawMessage { +func (s *SubJsonService) genServer(inbound *model.Inbound, streamSettings json_util.RawMessage, client model.Client) json_util.RawMessage { outbound := Outbound{} serverData := make([]ServerSetting, 1) @@ -308,8 +328,11 @@ func (s *SubJsonService) genServer(inbound *model.Inbound, client model.Client) } outbound.Protocol = string(inbound.Protocol) - outbound.Tag = inbound.Tag - outbound.StreamSettings = json_util.RawMessage(inbound.StreamSettings) + outbound.Tag = "proxy" + if s.mux != "" { + outbound.Mux = json_util.RawMessage(s.mux) + } + outbound.StreamSettings = streamSettings outbound.Settings = OutboundSettings{ Servers: serverData, } @@ -322,7 +345,7 @@ type Outbound struct { Protocol string `json:"protocol"` Tag string `json:"tag"` StreamSettings json_util.RawMessage `json:"streamSettings"` - Mux map[string]interface{} `json:"mux,omitempty"` + Mux json_util.RawMessage `json:"mux,omitempty"` ProxySettings map[string]interface{} `json:"proxySettings,omitempty"` Settings OutboundSettings `json:"settings,omitempty"` } diff --git a/sub/subService.go b/sub/subService.go index 06d1ed0a..0ed1d454 100644 --- a/sub/subService.go +++ b/sub/subService.go @@ -6,10 +6,12 @@ import ( "net/url" "strings" "time" + "x-ui/database" "x-ui/database/model" "x-ui/logger" "x-ui/util/common" + "x-ui/util/random" "x-ui/web/service" "x-ui/xray" @@ -212,9 +214,14 @@ func (s *SubService) genVmessLink(inbound *model.Inbound, email string) string { case "grpc": grpc, _ := stream["grpcSettings"].(map[string]interface{}) obj["path"] = grpc["serviceName"].(string) + obj["authority"] = grpc["authority"].(string) if grpc["multiMode"].(bool) { obj["type"] = "multi" } + case "httpupgrade": + httpupgrade, _ := stream["httpupgradeSettings"].(map[string]interface{}) + obj["path"] = httpupgrade["path"].(string) + obj["host"] = httpupgrade["host"].(string) } security, _ := stream["security"].(string) @@ -346,9 +353,14 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string { case "grpc": grpc, _ := stream["grpcSettings"].(map[string]interface{}) params["serviceName"] = grpc["serviceName"].(string) + params["authority"] = grpc["authority"].(string) if grpc["multiMode"].(bool) { params["mode"] = "multi" } + case "httpupgrade": + httpupgrade, _ := stream["httpupgradeSettings"].(map[string]interface{}) + params["path"] = httpupgrade["path"].(string) + params["host"] = httpupgrade["host"].(string) } security, _ := stream["security"].(string) @@ -391,25 +403,21 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string { if realitySetting != nil { if sniValue, ok := searchKey(realitySetting, "serverNames"); ok { sNames, _ := sniValue.([]interface{}) - params["sni"], _ = sNames[0].(string) + params["sni"] = sNames[random.Num(len(sNames))].(string) } if pbkValue, ok := searchKey(realitySettings, "publicKey"); ok { params["pbk"], _ = pbkValue.(string) } if sidValue, ok := searchKey(realitySetting, "shortIds"); ok { shortIds, _ := sidValue.([]interface{}) - params["sid"], _ = shortIds[0].(string) + params["sid"] = shortIds[random.Num(len(shortIds))].(string) } if fpValue, ok := searchKey(realitySettings, "fingerprint"); ok { if fp, ok := fpValue.(string); ok && len(fp) > 0 { params["fp"] = fp } } - if spxValue, ok := searchKey(realitySettings, "spiderX"); ok { - if spx, ok := spxValue.(string); ok && len(spx) > 0 { - params["spx"] = spx - } - } + params["spx"] = "/" + random.Seq(15) } if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 { @@ -562,9 +570,14 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string case "grpc": grpc, _ := stream["grpcSettings"].(map[string]interface{}) params["serviceName"] = grpc["serviceName"].(string) + params["authority"] = grpc["authority"].(string) if grpc["multiMode"].(bool) { params["mode"] = "multi" } + case "httpupgrade": + httpupgrade, _ := stream["httpupgradeSettings"].(map[string]interface{}) + params["path"] = httpupgrade["path"].(string) + params["host"] = httpupgrade["host"].(string) } security, _ := stream["security"].(string) @@ -603,25 +616,21 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string if realitySetting != nil { if sniValue, ok := searchKey(realitySetting, "serverNames"); ok { sNames, _ := sniValue.([]interface{}) - params["sni"], _ = sNames[0].(string) + params["sni"] = sNames[random.Num(len(sNames))].(string) } if pbkValue, ok := searchKey(realitySettings, "publicKey"); ok { params["pbk"], _ = pbkValue.(string) } if sidValue, ok := searchKey(realitySetting, "shortIds"); ok { shortIds, _ := sidValue.([]interface{}) - params["sid"], _ = shortIds[0].(string) + params["sid"] = shortIds[random.Num(len(shortIds))].(string) } if fpValue, ok := searchKey(realitySettings, "fingerprint"); ok { if fp, ok := fpValue.(string); ok && len(fp) > 0 { params["fp"] = fp } } - if spxValue, ok := searchKey(realitySettings, "spiderX"); ok { - if spx, ok := spxValue.(string); ok && len(spx) > 0 { - params["spx"] = spx - } - } + params["spx"] = "/" + random.Seq(15) } if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 { @@ -779,9 +788,14 @@ func (s *SubService) genShadowsocksLink(inbound *model.Inbound, email string) st case "grpc": grpc, _ := stream["grpcSettings"].(map[string]interface{}) params["serviceName"] = grpc["serviceName"].(string) + params["authority"] = grpc["authority"].(string) if grpc["multiMode"].(bool) { params["mode"] = "multi" } + case "httpupgrade": + httpupgrade, _ := stream["httpupgradeSettings"].(map[string]interface{}) + params["path"] = httpupgrade["path"].(string) + params["host"] = httpupgrade["host"].(string) } security, _ := stream["security"].(string) diff --git a/util/common/err.go b/util/common/err.go index f6078c33..494e5c82 100644 --- a/util/common/err.go +++ b/util/common/err.go @@ -3,6 +3,7 @@ package common import ( "errors" "fmt" + "x-ui/logger" ) diff --git a/util/random/random.go b/util/random/random.go index 1dd47ec9..67ee0691 100644 --- a/util/random/random.go +++ b/util/random/random.go @@ -4,12 +4,14 @@ import ( "math/rand" ) -var numSeq [10]rune -var lowerSeq [26]rune -var upperSeq [26]rune -var numLowerSeq [36]rune -var numUpperSeq [36]rune -var allSeq [62]rune +var ( + numSeq [10]rune + lowerSeq [26]rune + upperSeq [26]rune + numLowerSeq [36]rune + numUpperSeq [36]rune + allSeq [62]rune +) func init() { for i := 0; i < 10; i++ { diff --git a/web/assets/css/custom.css b/web/assets/css/custom.css index 5f957cce..3be33763 100644 --- a/web/assets/css/custom.css +++ b/web/assets/css/custom.css @@ -80,6 +80,11 @@ html[data-theme='ultra-dark'] { .dark .waves-header { background-color: #0a2227; } + .dark .ant-calendar-year-panel-year:hover, + .dark .ant-calendar-month-panel-month:hover, + .dark .ant-calendar-decade-panel-decade:hover { + background-color: var(--dark-color-surface-600); + } } html, @@ -175,7 +180,7 @@ style attribute { position: relative; clear: both; } -.ant-table-body { +.ant-table-wrapper > div > div > div > div > div > div { overflow-x: auto !important; } .ant-card-hoverable { @@ -750,8 +755,8 @@ style attribute { .dark .ant-btn-danger[disabled], .dark .ant-calendar-ok-btn-disabled { color: rgb(255 255 255 / 35%); - background-color: var(--dark-color-surface-300); - border-color: #42516c; + background-color: var(--dark-color-surface-200); + border-color: var(--dark-color-surface-300); } .dark @@ -791,9 +796,17 @@ style attribute { border-color: var(--dark-color-surface-500); } +@media (max-width: 768px) { + .dark .ant-popover-inner { + background-color: var(--dark-color-surface-200); + } + .dark > .ant-popover-content > .ant-popover-arrow { + border-color: var(--dark-color-surface-200); + } +} + .ant-dropdown-menu-dark .ant-dropdown-menu-item:hover, .dark .ant-select-dropdown-menu-item-selected, -.dark .ant-select-dropdown-menu-item:hover, .dark .ant-calendar-time-picker-select-option-selected { background-color: var(--dark-color-surface-600); } @@ -1249,3 +1262,17 @@ b, strong { .dark .ant-alert-close-icon .anticon-close:hover { color: rgb(255 255 255); } + +.ant-empty-small { + margin: 4px 0; + background-color: transparent !important; +} + +.ant-empty-small .ant-empty-image { + height: 20px; +} + +.ant-menu-theme-switch:hover { + background-color: transparent !important; + cursor: default !important; +} diff --git a/web/assets/js/axios-init.js b/web/assets/js/axios-init.js index b864b714..f0b0f4be 100644 --- a/web/assets/js/axios-init.js +++ b/web/assets/js/axios-init.js @@ -14,3 +14,17 @@ axios.interceptors.request.use( }, (error) => Promise.reject(error), ); + +axios.interceptors.response.use( + (response) => response, + (error) => { + if (error.response) { + const statusCode = error.response.status; + // Check the status code + if (statusCode === 401) { // Unauthorized + return window.location.reload(); + } + } + return Promise.reject(error); + } +); diff --git a/web/assets/js/model/outbound.js b/web/assets/js/model/outbound.js index 05248b77..c3008e18 100644 --- a/web/assets/js/model/outbound.js +++ b/web/assets/js/model/outbound.js @@ -51,7 +51,14 @@ const OutboundDomainStrategies = [ "AsIs", "UseIP", "UseIPv4", - "UseIPv6" + "UseIPv6", + "UseIPv6v4", + "UseIPv4v6", + "ForceIP", + "ForceIPv6v4", + "ForceIPv6", + "ForceIPv4v6", + "ForceIPv4" ]; const WireguardDomainStrategy = [ @@ -250,24 +257,48 @@ class QuicStreamSettings extends CommonClass { } class GrpcStreamSettings extends CommonClass { - constructor(serviceName="", multiMode=false) { + constructor(serviceName="", multiMode=false, authority="") { super(); this.serviceName = serviceName; this.multiMode = multiMode; + this.authority = authority; } static fromJson(json={}) { - return new GrpcStreamSettings(json.serviceName, json.multiMode); + return new GrpcStreamSettings(json.serviceName, json.multiMode,json.authority); } toJson() { return { serviceName: this.serviceName, multiMode: this.multiMode, + authority: this.authority } } } +class HttpUpgradeStreamSettings extends CommonClass { + constructor(path='/', host='') { + super(); + this.path = path; + this.host = host; + } + + static fromJson(json={}) { + return new HttpUpgradeStreamSettings( + json.path, + json.Host, + ); + } + + toJson() { + return { + path: this.path, + host: this.host, + }; + } +} + class TlsStreamSettings extends CommonClass { constructor(serverName='', alpn=[], @@ -327,6 +358,34 @@ class RealityStreamSettings extends CommonClass { }; } }; +class SockoptStreamSettings extends CommonClass { + constructor(dialerProxy = "", tcpFastOpen = false, tcpKeepAliveInterval = 0, tcpNoDelay = false) { + super(); + this.dialerProxy = dialerProxy; + this.tcpFastOpen = tcpFastOpen; + this.tcpKeepAliveInterval = tcpKeepAliveInterval; + this.tcpNoDelay = tcpNoDelay; + } + + static fromJson(json = {}) { + if (Object.keys(json).length === 0) return undefined; + return new SockoptStreamSettings( + json.dialerProxy, + json.tcpFastOpen, + json.tcpKeepAliveInterval, + json.tcpNoDelay, + ); + } + + toJson() { + return { + dialerProxy: this.dialerProxy, + tcpFastOpen: this.tcpFastOpen, + tcpKeepAliveInterval: this.tcpKeepAliveInterval, + tcpNoDelay: this.tcpNoDelay, + }; + } +} class StreamSettings extends CommonClass { constructor(network='tcp', @@ -339,6 +398,8 @@ class StreamSettings extends CommonClass { httpSettings=new HttpStreamSettings(), quicSettings=new QuicStreamSettings(), grpcSettings=new GrpcStreamSettings(), + httpupgradeSettings=new HttpUpgradeStreamSettings(), + sockopt = undefined, ) { super(); this.network = network; @@ -351,6 +412,8 @@ class StreamSettings extends CommonClass { this.http = httpSettings; this.quic = quicSettings; this.grpc = grpcSettings; + this.httpupgrade = httpupgradeSettings; + this.sockopt = sockopt; } get isTls() { @@ -361,6 +424,14 @@ class StreamSettings extends CommonClass { return this.security === "reality"; } + get sockoptSwitch() { + return this.sockopt != undefined; + } + + set sockoptSwitch(value) { + this.sockopt = value ? new SockoptStreamSettings() : undefined; + } + static fromJson(json={}) { return new StreamSettings( json.network, @@ -373,6 +444,8 @@ class StreamSettings extends CommonClass { HttpStreamSettings.fromJson(json.httpSettings), QuicStreamSettings.fromJson(json.quicSettings), GrpcStreamSettings.fromJson(json.grpcSettings), + HttpUpgradeStreamSettings.fromJson(json.httpupgradeSettings), + SockoptStreamSettings.fromJson(json.sockopt), ); } @@ -389,6 +462,37 @@ class StreamSettings extends CommonClass { httpSettings: network === 'http' ? this.http.toJson() : undefined, quicSettings: network === 'quic' ? this.quic.toJson() : undefined, grpcSettings: network === 'grpc' ? this.grpc.toJson() : undefined, + httpupgradeSettings: network === 'httpupgrade' ? this.httpupgrade.toJson() : undefined, + sockopt: this.sockopt != undefined ? this.sockopt.toJson() : undefined, + }; + } +} + +class Mux extends CommonClass { + constructor(enabled = false, concurrency = 8, xudpConcurrency = 16, xudpProxyUDP443 = "reject") { + super(); + this.enabled = enabled; + this.concurrency = concurrency; + this.xudpConcurrency = xudpConcurrency; + this.xudpProxyUDP443 = xudpProxyUDP443; + } + + static fromJson(json = {}) { + if (Object.keys(json).length === 0) return undefined; + return new Mux( + json.enabled, + json.concurrency, + json.xudpConcurrency, + json.xudpProxyUDP443, + ); + } + + toJson() { + return { + enabled: this.enabled, + concurrency: this.concurrency, + xudpConcurrency: this.xudpConcurrency, + xudpProxyUDP443: this.xudpProxyUDP443, }; } } @@ -399,12 +503,16 @@ class Outbound extends CommonClass { protocol=Protocols.VMess, settings=null, streamSettings = new StreamSettings(), + sendThrough, + mux = new Mux(), ) { super(); this.tag = tag; this._protocol = protocol; this.settings = settings == null ? Outbound.Settings.getSettings(protocol) : settings; this.stream = streamSettings; + this.sendThrough = sendThrough; + this.mux = mux; } get protocol() { @@ -419,7 +527,7 @@ class Outbound extends CommonClass { canEnableTls() { if (![Protocols.VMess, Protocols.VLESS, Protocols.Trojan, Protocols.Shadowsocks].includes(this.protocol)) return false; - return ["tcp", "ws", "http", "quic", "grpc"].includes(this.stream.network); + return ["tcp", "ws", "http", "quic", "grpc", "httpupgrade"].includes(this.stream.network); } //this is used for xtls-rprx-vision @@ -439,6 +547,10 @@ class Outbound extends CommonClass { return [Protocols.VMess, Protocols.VLESS, Protocols.Trojan, Protocols.Shadowsocks].includes(this.protocol); } + canEnableMux() { + return [Protocols.VMess, Protocols.VLESS, Protocols.Trojan, Protocols.Shadowsocks, Protocols.HTTP, Protocols.Socks].includes(this.protocol); + } + hasVnext() { return [Protocols.VMess, Protocols.VLESS].includes(this.protocol); } @@ -469,15 +581,26 @@ class Outbound extends CommonClass { json.protocol, Outbound.Settings.fromJson(json.protocol, json.settings), StreamSettings.fromJson(json.streamSettings), + json.sendThrough, + Mux.fromJson(json.mux), ) } toJson() { + var stream; + if (this.canEnableStream()) { + stream = this.stream.toJson(); + } else { + if (this.stream?.sockopt) + stream = { sockopt: this.stream.sockopt.toJson() }; + } return { tag: this.tag == '' ? undefined : this.tag, protocol: this.protocol, settings: this.settings instanceof CommonClass ? this.settings.toJson() : this.settings, - streamSettings: this.canEnableStream() ? this.stream.toJson() : undefined, + streamSettings: stream, + sendThrough: this.sendThrough != "" ? this.sendThrough : undefined, + mux: this.mux?.enabled ? this.mux : undefined, }; } @@ -523,6 +646,8 @@ class Outbound extends CommonClass { json.type ? json.type : 'none'); } else if (network === 'grpc') { stream.grpc = new GrpcStreamSettings(json.path, json.type == 'multi'); + } else if (network === 'httpupgrade') { + stream.httpupgrade = new HttpUpgradeStreamSettings(json.path,json.host); } if(json.tls && json.tls == 'tls'){ @@ -533,7 +658,6 @@ class Outbound extends CommonClass { json.allowInsecure); } - return new Outbound(json.ps, Protocols.VMess, new Outbound.VmessSettings(json.add, json.port, json.id), stream); } @@ -564,6 +688,8 @@ class Outbound extends CommonClass { headerType ?? 'none'); } else if (type === 'grpc') { stream.grpc = new GrpcStreamSettings(url.searchParams.get('serviceName') ?? '', url.searchParams.get('mode') == 'multi'); + } else if (type === 'httpupgrade') { + stream.httpupgrade = new HttpUpgradeStreamSettings(path,host); } if(security == 'tls'){ @@ -580,13 +706,13 @@ class Outbound extends CommonClass { let sni=url.searchParams.get('sni') ?? ''; let sid=url.searchParams.get('sid') ?? ''; let spx=url.searchParams.get('spx') ?? ''; - stream.reality = new RealityStreamSettings(pbk, fp, sni, sid, spx); + stream.reality = new RealityStreamSettings(pbk, fp, sni, sid, spx); } let data = link.split('?'); if(data.length != 2) return null; - const regex = /([^@]+):\/\/([^@]+)@([^:]+):(\d+)\?(.*)$/; + const regex = /([^@]+):\/\/([^@]+)@(.+):(\d+)\?(.*)$/; const match = link.match(regex); if (!match) return null; diff --git a/web/assets/js/model/setting.js b/web/assets/js/model/setting.js index 637830e8..d9d0f4d4 100644 --- a/web/assets/js/model/setting.js +++ b/web/assets/js/model/setting.js @@ -35,9 +35,11 @@ class AllSetting { this.subUpdates = 0; this.subEncrypt = true; this.subShowInfo = false; - this.subURI = ''; - this.subJsonURI = ''; - this.subJsonFragment = ''; + this.subURI = ""; + this.subJsonURI = ""; + this.subJsonFragment = ""; + this.subJsonMux = ""; + this.subJsonRules = ""; this.timeLocation = "Asia/Tehran"; diff --git a/web/assets/js/model/xray.js b/web/assets/js/model/xray.js index 791e8533..9d863798 100644 --- a/web/assets/js/model/xray.js +++ b/web/assets/js/model/xray.js @@ -446,16 +446,19 @@ class QuicStreamSettings extends XrayCommonClass { class GrpcStreamSettings extends XrayCommonClass { constructor( serviceName="", + authority="", multiMode=false, ) { super(); this.serviceName = serviceName; + this.authority = authority; this.multiMode = multiMode; } static fromJson(json={}) { return new GrpcStreamSettings( json.serviceName, + json.authority, json.multiMode ); } @@ -463,11 +466,36 @@ class GrpcStreamSettings extends XrayCommonClass { toJson() { return { serviceName: this.serviceName, + authority: this.authority, multiMode: this.multiMode, } } } +class HttpUpgradeStreamSettings extends XrayCommonClass { + constructor(acceptProxyProtocol=false, path='/', host='') { + super(); + this.acceptProxyProtocol = acceptProxyProtocol; + this.path = path; + this.host = host; + } + + static fromJson(json={}) { + return new HttpUpgradeStreamSettings( + json.acceptProxyProtocol, + json.path, + json.host, + ); + } + + toJson() { + return { + acceptProxyProtocol: this.acceptProxyProtocol, + path: this.path, + host: this.host, + }; + } +} class TlsStreamSettings extends XrayCommonClass { constructor(serverName='', @@ -833,6 +861,7 @@ class StreamSettings extends XrayCommonClass { httpSettings=new HttpStreamSettings(), quicSettings=new QuicStreamSettings(), grpcSettings=new GrpcStreamSettings(), + httpupgradeSettings=new HttpUpgradeStreamSettings(), sockopt = undefined, ) { super(); @@ -848,6 +877,7 @@ class StreamSettings extends XrayCommonClass { this.http = httpSettings; this.quic = quicSettings; this.grpc = grpcSettings; + this.httpupgrade = httpupgradeSettings; this.sockopt = sockopt; } @@ -910,6 +940,7 @@ class StreamSettings extends XrayCommonClass { HttpStreamSettings.fromJson(json.httpSettings), QuicStreamSettings.fromJson(json.quicSettings), GrpcStreamSettings.fromJson(json.grpcSettings), + HttpUpgradeStreamSettings.fromJson(json.httpupgradeSettings), SockoptStreamSettings.fromJson(json.sockopt), ); } @@ -929,6 +960,7 @@ class StreamSettings extends XrayCommonClass { httpSettings: network === 'http' ? this.http.toJson() : undefined, quicSettings: network === 'quic' ? this.quic.toJson() : undefined, grpcSettings: network === 'grpc' ? this.grpc.toJson() : undefined, + httpupgradeSettings: network === 'httpupgrade' ? this.httpupgrade.toJson() : undefined, sockopt: this.sockopt != undefined ? this.sockopt.toJson() : undefined, }; } @@ -1045,6 +1077,10 @@ class Inbound extends XrayCommonClass { return this.network === "http"; } + get isHttpupgrade() { + return this.network === "httpupgrade"; + } + // Shadowsocks get method() { switch (this.protocol) { @@ -1075,6 +1111,8 @@ class Inbound extends XrayCommonClass { return this.stream.ws.getHeader("Host"); } else if (this.isH2) { return this.stream.http.host[0]; + } else if (this.isHttpupgrade) { + return this.stream.httpupgrade.host; } return null; } @@ -1086,6 +1124,8 @@ class Inbound extends XrayCommonClass { return this.stream.ws.path; } else if (this.isH2) { return this.stream.http.path; + } else if (this.isHttpupgrade) { + return this.stream.httpupgrade.path; } return null; } @@ -1121,7 +1161,7 @@ class Inbound extends XrayCommonClass { canEnableTls() { if(![Protocols.VMESS, Protocols.VLESS, Protocols.TROJAN, Protocols.SHADOWSOCKS].includes(this.protocol)) return false; - return ["tcp", "ws", "http", "quic", "grpc"].includes(this.network); + return ["tcp", "ws", "http", "quic", "grpc", "httpupgrade"].includes(this.network); } //this is used for xtls-rprx-vision @@ -1204,9 +1244,14 @@ class Inbound extends XrayCommonClass { obj.path = this.stream.quic.key; } else if (network === 'grpc') { obj.path = this.stream.grpc.serviceName; + obj.authority = this.stream.grpc.authority; if (this.stream.grpc.multiMode){ obj.type = 'multi' } + } else if (network === 'httpupgrade') { + let httpupgrade = this.stream.httpupgrade; + obj.path = httpupgrade.path; + obj.host = httpupgrade.host; } if (security === 'tls') { @@ -1275,10 +1320,16 @@ class Inbound extends XrayCommonClass { case "grpc": const grpc = this.stream.grpc; params.set("serviceName", grpc.serviceName); + params.set("authority", grpc.authority); if(grpc.multiMode){ params.set("mode", "multi"); } break; + case "httpupgrade": + const httpupgrade = this.stream.httpupgrade; + params.set("path", httpupgrade.path); + params.set("host", httpupgrade.host); + break; } if (security === 'tls') { @@ -1389,10 +1440,16 @@ class Inbound extends XrayCommonClass { case "grpc": const grpc = this.stream.grpc; params.set("serviceName", grpc.serviceName); + params.set("authority", grpc.authority); if(grpc.multiMode){ params.set("mode", "multi"); } break; + case "httpupgrade": + const httpupgrade = this.stream.httpupgrade; + params.set("path", httpupgrade.path); + params.set("host", httpupgrade.host); + break; } if (security === 'tls') { @@ -1470,10 +1527,16 @@ class Inbound extends XrayCommonClass { case "grpc": const grpc = this.stream.grpc; params.set("serviceName", grpc.serviceName); + params.set("authority", grpc.authority); if(grpc.multiMode){ params.set("mode", "multi"); } break; + case "httpupgrade": + const httpupgrade = this.stream.httpupgrade; + params.set("path", httpupgrade.path); + params.set("host", httpupgrade.host); + break; } if (security === 'tls') { diff --git a/web/assets/js/util/utils.js b/web/assets/js/util/utils.js index 61b322bd..f2f05f01 100644 --- a/web/assets/js/util/utils.js +++ b/web/assets/js/util/utils.js @@ -131,11 +131,11 @@ class RandomUtil { static randomUUID() { const template = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'; return template.replace(/[xy]/g, function (c) { - const randomValues = new Uint8Array(1); - crypto.getRandomValues(randomValues); - let randomValue = randomValues[0] % 16; - let calculatedValue = (c === 'x') ? randomValue : (randomValue & 0x3 | 0x8); - return calculatedValue.toString(16); + const randomValues = new Uint8Array(1); + crypto.getRandomValues(randomValues); + let randomValue = randomValues[0] % 16; + let calculatedValue = (c === 'x') ? randomValue : (randomValue & 0x3 | 0x8); + return calculatedValue.toString(16); }); } diff --git a/web/controller/api.go b/web/controller/api.go index 73c36787..6281097b 100644 --- a/web/controller/api.go +++ b/web/controller/api.go @@ -22,91 +22,37 @@ func (a *APIController) initRouter(g *gin.RouterGroup) { g = g.Group("/panel/api/inbounds") g.Use(a.checkLogin) - g.GET("/list", a.getAllInbounds) - g.GET("/get/:id", a.getSingleInbound) - g.GET("/getClientTraffics/:email", a.getClientTraffics) - g.POST("/add", a.addInbound) - g.POST("/del/:id", a.delInbound) - g.POST("/update/:id", a.updateInbound) - g.POST("/clientIps/:email", a.getClientIps) - g.POST("/clearClientIps/:email", a.clearClientIps) - g.POST("/addClient", a.addInboundClient) - g.POST("/:id/delClient/:clientId", a.delInboundClient) - g.POST("/updateClient/:clientId", a.updateInboundClient) - g.POST("/:id/resetClientTraffic/:email", a.resetClientTraffic) - g.POST("/resetAllTraffics", a.resetAllTraffics) - g.POST("/resetAllClientTraffics/:id", a.resetAllClientTraffics) - g.POST("/delDepletedClients/:id", a.delDepletedClients) - g.GET("/createbackup", a.createBackup) - g.POST("/onlines", a.onlines) - a.inboundController = NewInboundController(g) -} -func (a *APIController) getAllInbounds(c *gin.Context) { - a.inboundController.getInbounds(c) -} + inboundRoutes := []struct { + Method string + Path string + Handler gin.HandlerFunc + }{ + {"GET", "/createbackup", a.createBackup}, + {"GET", "/list", a.inboundController.getInbounds}, + {"GET", "/get/:id", a.inboundController.getInbound}, + {"GET", "/getClientTraffics/:email", a.inboundController.getClientTraffics}, + {"POST", "/add", a.inboundController.addInbound}, + {"POST", "/del/:id", a.inboundController.delInbound}, + {"POST", "/update/:id", a.inboundController.updateInbound}, + {"POST", "/clientIps/:email", a.inboundController.getClientIps}, + {"POST", "/clearClientIps/:email", a.inboundController.clearClientIps}, + {"POST", "/addClient", a.inboundController.addInboundClient}, + {"POST", "/:id/delClient/:clientId", a.inboundController.delInboundClient}, + {"POST", "/updateClient/:clientId", a.inboundController.updateInboundClient}, + {"POST", "/:id/resetClientTraffic/:email", a.inboundController.resetClientTraffic}, + {"POST", "/resetAllTraffics", a.inboundController.resetAllTraffics}, + {"POST", "/resetAllClientTraffics/:id", a.inboundController.resetAllClientTraffics}, + {"POST", "/delDepletedClients/:id", a.inboundController.delDepletedClients}, + {"POST", "/onlines", a.inboundController.onlines}, + } -func (a *APIController) getSingleInbound(c *gin.Context) { - a.inboundController.getInbound(c) -} - -func (a *APIController) getClientTraffics(c *gin.Context) { - a.inboundController.getClientTraffics(c) -} - -func (a *APIController) addInbound(c *gin.Context) { - a.inboundController.addInbound(c) -} - -func (a *APIController) delInbound(c *gin.Context) { - a.inboundController.delInbound(c) -} - -func (a *APIController) updateInbound(c *gin.Context) { - a.inboundController.updateInbound(c) -} - -func (a *APIController) getClientIps(c *gin.Context) { - a.inboundController.getClientIps(c) -} - -func (a *APIController) clearClientIps(c *gin.Context) { - a.inboundController.clearClientIps(c) -} - -func (a *APIController) addInboundClient(c *gin.Context) { - a.inboundController.addInboundClient(c) -} - -func (a *APIController) delInboundClient(c *gin.Context) { - a.inboundController.delInboundClient(c) -} - -func (a *APIController) updateInboundClient(c *gin.Context) { - a.inboundController.updateInboundClient(c) -} - -func (a *APIController) resetClientTraffic(c *gin.Context) { - a.inboundController.resetClientTraffic(c) -} - -func (a *APIController) resetAllTraffics(c *gin.Context) { - a.inboundController.resetAllTraffics(c) -} - -func (a *APIController) resetAllClientTraffics(c *gin.Context) { - a.inboundController.resetAllClientTraffics(c) -} - -func (a *APIController) delDepletedClients(c *gin.Context) { - a.inboundController.delDepletedClients(c) + for _, route := range inboundRoutes { + g.Handle(route.Method, route.Path, route.Handler) + } } func (a *APIController) createBackup(c *gin.Context) { a.Tgbot.SendBackupToAdmins() } - -func (a *APIController) onlines(c *gin.Context) { - a.inboundController.onlines(c) -} diff --git a/web/controller/base.go b/web/controller/base.go index 674a195d..492fc2dc 100644 --- a/web/controller/base.go +++ b/web/controller/base.go @@ -2,6 +2,7 @@ package controller import ( "net/http" + "x-ui/logger" "x-ui/web/locale" "x-ui/web/session" @@ -9,13 +10,12 @@ import ( "github.com/gin-gonic/gin" ) -type BaseController struct { -} +type BaseController struct{} func (a *BaseController) checkLogin(c *gin.Context) { if !session.IsLogin(c) { if isAjax(c) { - pureJsonMsg(c, false, I18nWeb(c, "pages.login.loginAgain")) + pureJsonMsg(c, http.StatusUnauthorized, false, I18nWeb(c, "pages.login.loginAgain")) } else { c.Redirect(http.StatusTemporaryRedirect, c.GetString("base_path")) } diff --git a/web/controller/inbound.go b/web/controller/inbound.go index d613453f..511afd64 100644 --- a/web/controller/inbound.go +++ b/web/controller/inbound.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "strconv" + "x-ui/database/model" "x-ui/web/service" "x-ui/web/session" @@ -174,7 +175,7 @@ func (a *InboundController) addInboundClient(c *gin.Context) { return } jsonMsg(c, "Client(s) added", nil) - if err == nil && needRestart { + if needRestart { a.xrayService.SetToNeedRestart() } } @@ -195,7 +196,7 @@ func (a *InboundController) delInboundClient(c *gin.Context) { return } jsonMsg(c, "Client deleted", nil) - if err == nil && needRestart { + if needRestart { a.xrayService.SetToNeedRestart() } } @@ -218,7 +219,7 @@ func (a *InboundController) updateInboundClient(c *gin.Context) { return } jsonMsg(c, "Client updated", nil) - if err == nil && needRestart { + if needRestart { a.xrayService.SetToNeedRestart() } } @@ -239,7 +240,7 @@ func (a *InboundController) resetClientTraffic(c *gin.Context) { return } jsonMsg(c, "traffic reseted", nil) - if err == nil && needRestart { + if needRestart { a.xrayService.SetToNeedRestart() } } diff --git a/web/controller/index.go b/web/controller/index.go index 9be88273..bc3c4204 100644 --- a/web/controller/index.go +++ b/web/controller/index.go @@ -3,6 +3,7 @@ package controller import ( "net/http" "time" + "x-ui/logger" "x-ui/web/service" "x-ui/web/session" @@ -49,15 +50,15 @@ func (a *IndexController) login(c *gin.Context) { var form LoginForm err := c.ShouldBind(&form) if err != nil { - pureJsonMsg(c, false, I18nWeb(c, "pages.login.toasts.invalidFormData")) + pureJsonMsg(c, http.StatusOK, false, I18nWeb(c, "pages.login.toasts.invalidFormData")) return } if form.Username == "" { - pureJsonMsg(c, false, I18nWeb(c, "pages.login.toasts.emptyUsername")) + pureJsonMsg(c, http.StatusOK, false, I18nWeb(c, "pages.login.toasts.emptyUsername")) return } if form.Password == "" { - pureJsonMsg(c, false, I18nWeb(c, "pages.login.toasts.emptyPassword")) + pureJsonMsg(c, http.StatusOK, false, I18nWeb(c, "pages.login.toasts.emptyPassword")) return } @@ -66,7 +67,7 @@ func (a *IndexController) login(c *gin.Context) { if user == nil { logger.Warningf("wrong username or password: \"%s\" \"%s\"", form.Username, form.Password) a.tgbot.UserLoginNotify(form.Username, getRemoteIp(c), timeStr, 0) - pureJsonMsg(c, false, I18nWeb(c, "pages.login.toasts.wrongUsernameOrPassword")) + pureJsonMsg(c, http.StatusOK, false, I18nWeb(c, "pages.login.toasts.wrongUsernameOrPassword")) return } else { logger.Infof("%s login success, Ip Address: %s\n", form.Username, getRemoteIp(c)) diff --git a/web/controller/server.go b/web/controller/server.go index 10baacb9..0eeca71c 100644 --- a/web/controller/server.go +++ b/web/controller/server.go @@ -5,6 +5,7 @@ import ( "net/http" "regexp" "time" + "x-ui/web/global" "x-ui/web/service" diff --git a/web/controller/setting.go b/web/controller/setting.go index 64cae71b..d04969dc 100644 --- a/web/controller/setting.go +++ b/web/controller/setting.go @@ -3,6 +3,7 @@ package controller import ( "errors" "time" + "x-ui/web/entity" "x-ui/web/service" "x-ui/web/session" diff --git a/web/controller/util.go b/web/controller/util.go index da77189b..a32c9270 100644 --- a/web/controller/util.go +++ b/web/controller/util.go @@ -4,6 +4,7 @@ import ( "net" "net/http" "strings" + "x-ui/config" "x-ui/logger" "x-ui/web/entity" @@ -48,18 +49,11 @@ func jsonMsgObj(c *gin.Context, msg string, obj interface{}, err error) { c.JSON(http.StatusOK, m) } -func pureJsonMsg(c *gin.Context, success bool, msg string) { - if success { - c.JSON(http.StatusOK, entity.Msg{ - Success: true, - Msg: msg, - }) - } else { - c.JSON(http.StatusOK, entity.Msg{ - Success: false, - Msg: msg, - }) - } +func pureJsonMsg(c *gin.Context, statusCode int, success bool, msg string) { + c.JSON(statusCode, entity.Msg{ + Success: success, + Msg: msg, + }) } func html(c *gin.Context, name string, title string, data gin.H) { diff --git a/web/entity/entity.go b/web/entity/entity.go index 06850128..533fe973 100644 --- a/web/entity/entity.go +++ b/web/entity/entity.go @@ -5,6 +5,7 @@ import ( "net" "strings" "time" + "x-ui/util/common" ) @@ -51,6 +52,8 @@ type AllSetting struct { SubJsonPath string `json:"subJsonPath" form:"subJsonPath"` SubJsonURI string `json:"subJsonURI" form:"subJsonURI"` SubJsonFragment string `json:"subJsonFragment" form:"subJsonFragment"` + SubJsonMux string `json:"subJsonMux" form:"subJsonMux"` + SubJsonRules string `json:"subJsonRules" form:"subJsonRules"` Datepicker string `json:"datepicker" form:"datepicker"` } diff --git a/web/global/global.go b/web/global/global.go index 7d0b4e1f..e92c375b 100644 --- a/web/global/global.go +++ b/web/global/global.go @@ -7,8 +7,10 @@ import ( "github.com/robfig/cron/v3" ) -var webServer WebServer -var subServer SubServer +var ( + webServer WebServer + subServer SubServer +) type WebServer interface { GetCron() *cron.Cron diff --git a/web/html/common/qrcode_modal.html b/web/html/common/qrcode_modal.html index 3fcfdfc1..0e2b3a63 100644 --- a/web/html/common/qrcode_modal.html +++ b/web/html/common/qrcode_modal.html @@ -1,9 +1,10 @@ {{define "qrcodeModal"}} + :dialog-style="{ top: '20px' }" + :closable="true" + :class="themeSwitcher.currentTheme" + :footer="null" + width="300px"> {{ i18n "pages.inbounds.clickOnQRcode" }} diff --git a/web/html/login.html b/web/html/login.html index 7655c66f..dfb01cea 100644 --- a/web/html/login.html +++ b/web/html/login.html @@ -374,6 +374,12 @@ transform: translateZ(-100px); } } + .ant-menu-item .anticon { + margin-right: 4px; + } + .ant-menu-inline .ant-menu-item { + padding: 0 16px !important; + } @@ -410,19 +416,19 @@ - - - @@ -455,17 +461,7 @@ - -   - - - - - Ultra - - + diff --git a/web/html/xui/common_sider.html b/web/html/xui/common_sider.html index 78b833b9..bc8f4608 100644 --- a/web/html/xui/common_sider.html +++ b/web/html/xui/common_sider.html @@ -24,18 +24,7 @@ {{define "commonSider"}} - - - - - - - Ultra - - - + {{template "menuItems" .}} @@ -49,18 +38,7 @@
- - - - - - - Ultra - - - + {{template "menuItems" .}} diff --git a/web/html/xui/component/setting.html b/web/html/xui/component/setting.html index 82c0ae75..8adc000c 100644 --- a/web/html/xui/component/setting.html +++ b/web/html/xui/component/setting.html @@ -16,7 +16,7 @@ - + @@ -235,7 +233,7 @@ [[ DateUtil.formatMillis(client._expiryTime) ]] diff --git a/web/html/xui/inbound_info_modal.html b/web/html/xui/inbound_info_modal.html index c8341651..f85fc7d3 100644 --- a/web/html/xui/inbound_info_modal.html +++ b/web/html/xui/inbound_info_modal.html @@ -25,7 +25,7 @@ {{ i18n "transmission" }}[[ inbound.network ]] - + + Observatory + Burst Observatory + + - + -