diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..e602ad9
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,11 @@
+# Внести вклад
+
+Буду рад любому вкладу в разработку проекта! Простой процесс для контрибьюторов:
+
+1. Форкните репозиторий на GitHub.
+2. Создайте ветку с осмысленным именем, например `feature/add-README` или `fix/whitelist-load`.
+3. Внесите изменения и добавьте короткое описание в коммите.
+4. Запустите локально базовые проверки (если есть).
+5. Отправьте ветку в ваш форк и создайте Pull Request в основной репозиторий.
+
+Советы: описывайте изменения в PR, указывайте цель и тестовые шаги. Если изменения большие — разделяйте на маленькие коммиты.
\ No newline at end of file
diff --git a/README.md b/README.md
index c40ac99..b1f97c7 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@

- [](https://www.gnu.org/licenses/gpl-3.0) [](https://t.me/denpiligrim_web) [](https://www.youtube.com/@denpiligrim)
+ [](https://www.gnu.org/licenses/gpl-3.0) [](https://t.me/denpiligrim_web) [](https://www.youtube.com/@denpiligrim)
# 3DP-MANAGER
@@ -61,6 +61,9 @@
## Установка
+> [!WARNING]
+> Утилита работает на основном сервере
+
У вас должны быть установлены пакеты на сервере `curl`, `jq` командой: `apt install curl jq` и панель управления `3x-ui`, которую можно установить командой: `bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)`
Установите проект на сервер командой:
@@ -68,7 +71,13 @@
bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/install.sh)
```
-Краткое описание: запускает скрипт установки и разворачивает контейнеры и сервисы.
+Краткое описание: запускает скрипт установки и разворачивает контейнеры и сервисы.
+
+Если панель 3x-ui находится в Docker контейнере, установите командой:
+
+```bash
+REMOTE_PANEL=true bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/install.sh)
+```
## Обновление
@@ -78,7 +87,7 @@ bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main
bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/update.sh)
```
-Краткое описание: подтягивает последние изменения и перезапускает контейнеры.
+Краткое описание: подтягивает последние изменения и перезапускает контейнеры.
## Удаление
@@ -88,14 +97,14 @@ bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main
bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/delete.sh)
```
-Краткое описание: удаляет контейнеры и файлы конфигурации, возвращая систему к состоянию до установки.
+Краткое описание: удаляет контейнеры и файлы конфигурации, возвращая систему к состоянию до установки.
---
## Установка сервиса перенаправления (forwarding)
> [!WARNING]
-> Сервис перенаправления работает на промежуточном сервере.
+> Сервис перенаправления работает на промежуточном сервере
Сервис перенаправления позволяет проксировать входящие порты с промежуточного сервера на основной.
@@ -103,7 +112,7 @@ bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main
bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/forwarding_install.sh)
```
-Краткое описание: добавляет правила перенаправления и создает сервис для обновления подписки.
+Краткое описание: добавляет правила перенаправления и создает сервис для обновления подписки.
## Удаление перенаправления
@@ -111,7 +120,7 @@ bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main
bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/forwarding_delete.sh)
```
-Краткое описание: удаляет правила и отключает сервис перенаправления.
+Краткое описание: удаляет правила и отключает сервис перенаправления.
---
@@ -123,7 +132,7 @@ bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main
cd /opt/3dp-manager && docker compose exec node env | grep SUB_URL | cut -d'=' -f2
```
-Краткое описание: выводит статичный URL подписки, который можно использовать в клиентах. Работает как на основном, так и на промежуточном сервере.
+Краткое описание: выводит статичный URL подписки, который можно использовать в клиентах. Работает как на основном, так и на промежуточном сервере.
## Сбор доменов из мульти-подписок
@@ -133,7 +142,7 @@ cd /opt/3dp-manager && docker compose exec node env | grep SUB_URL | cut -d'=' -
node get_domains.js
```
-Краткое описание: добавьте ссылку на мульти-подписку в скрипт и запустите команду — на выходе получите список доменов. Необходим `Node.js` для работы скрипта.
+Краткое описание: добавьте ссылку на мульти-подписку в скрипт и запустите команду — на выходе получите список доменов. Необходим `Node.js` для работы скрипта.
## Использование собственного белого списка
@@ -144,7 +153,19 @@ node get_domains.js
cd /opt/3dp-manager && docker cp ./app/my_whitelist.txt node:/app/my_whitelist.txt
```
-Краткое описание: добавляет ваш файл доменов в контейнер приложения. Чтобы сразу же сгенерировать инбаунды с новым списком, выполните `docker exec -it node sh` и затем `node index.js`.
+Краткое описание: добавляет ваш файл доменов в контейнер приложения. Чтобы сразу же сгенерировать инбаунды с новым списком, выполните `docker exec -it node sh` и затем `node rotate.js`.
+
+## Ручной запуск генерации
+
+Чтобы создать новые инбаунды, выполните команды поочередно:
+
+```bash
+cd /opt/3dp-manager
+docker exec -it node sh
+node rotate.js
+```
+
+Краткое описание: выполняет немедленную генерацию инбаундов, не влияя на заданный интервал ротации.
---
@@ -164,7 +185,7 @@ cd /opt/3dp-manager && docker cp ./app/my_whitelist.txt node:/app/my_whitelist.t
4. Запустите локально базовые проверки (если есть).
5. Отправьте ветку в ваш форк и создайте Pull Request в основной репозиторий.
-Советы: описывайте изменения в PR, указывайте цель и тестовые шаги. Если изменения большие — разделяйте на маленькие коммиты.
+Советы: описывайте изменения в PR, указывайте цель и тестовые шаги. Если изменения большие — разделяйте на маленькие коммиты.
---
diff --git a/README_CN.md b/README_CN.md
index dbd90c4..ffbeb83 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -2,7 +2,7 @@

- [](https://www.gnu.org/licenses/gpl-3.0) [](https://t.me/denpiligrim_web) [](https://www.youtube.com/@denpiligrim)
+ [](https://www.gnu.org/licenses/gpl-3.0) [](https://t.me/denpiligrim_web) [](https://www.youtube.com/@denpiligrim)
# 3DP-MANAGER
@@ -64,7 +64,7 @@
bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/install.sh)
```
-简要说明:运行安装脚本并部署容器和服务。
+简要说明:运行安装脚本并部署容器和服务。
## 更新
@@ -74,7 +74,7 @@ bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main
bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/update.sh)
```
-简要说明:拉取最新更改并重启容器。
+简要说明:拉取最新更改并重启容器。
## 删除
@@ -84,7 +84,7 @@ bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main
bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/delete.sh)
```
-简要说明:删除容器和配置文件,恢复到安装前状态。
+简要说明:删除容器和配置文件,恢复到安装前状态。
---
@@ -96,7 +96,7 @@ bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main
bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/forwarding_install.sh)
```
-简要说明:添加转发规则并创建用于更新订阅的服务。
+简要说明:添加转发规则并创建用于更新订阅的服务。
## 删除转发
@@ -104,7 +104,7 @@ bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main
bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/forwarding_delete.sh)
```
-简要说明:删除规则并禁用转发服务。
+简要说明:删除规则并禁用转发服务。
---
@@ -116,7 +116,7 @@ bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main
cd /opt/3dp-manager && docker compose exec node env | grep SUB_URL | cut -d'=' -f2
```
-简要说明:打印可在客户端使用的静态订阅 URL,适用于主服务器和中继服务器。
+简要说明:打印可在客户端使用的静态订阅 URL,适用于主服务器和中继服务器。
## 从多订阅收集域名
@@ -126,7 +126,7 @@ cd /opt/3dp-manager && docker compose exec node env | grep SUB_URL | cut -d'=' -
node get_domains.js
```
-简要说明:在脚本中添加多订阅链接并运行命令 — 输出为域名列表。运行脚本需要 `Node.js`。
+简要说明:在脚本中添加多订阅链接并运行命令 — 输出为域名列表。运行脚本需要 `Node.js`。
## 使用自定义白名单
@@ -137,7 +137,7 @@ node get_domains.js
cd /opt/3dp-manager && docker cp ./app/my_whitelist.txt node:/app/my_whitelist.txt
```
-简要说明:将您的域名文件添加到应用容器中。
+简要说明:将您的域名文件添加到应用容器中。
---
@@ -157,7 +157,7 @@ cd /opt/3dp-manager && docker cp ./app/my_whitelist.txt node:/app/my_whitelist.t
4. 在本地运行检查(如有)。
5. 推送分支到您的 fork 并创建 Pull Request。
-建议:在 PR 中说明更改和测试步骤;较大更改请拆成小提交。
+建议:在 PR 中说明更改和测试步骤;较大更改请拆成小提交。
---
diff --git a/README_EN.md b/README_EN.md
index d6de078..6f86786 100644
--- a/README_EN.md
+++ b/README_EN.md
@@ -2,7 +2,7 @@

- [](https://www.gnu.org/licenses/gpl-3.0) [](https://t.me/denpiligrim_web) [](https://www.youtube.com/@denpiligrim)
+ [](https://www.gnu.org/licenses/gpl-3.0) [](https://t.me/denpiligrim_web) [](https://www.youtube.com/@denpiligrim)
# 3DP-MANAGER
@@ -64,7 +64,7 @@ Install the project on your server with:
bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/install.sh)
```
-Short description: runs the installer script and deploys containers and services.
+Short description: runs the installer script and deploys containers and services.
## Update
@@ -74,7 +74,7 @@ Update to the latest version:
bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/update.sh)
```
-Short description: pulls the latest changes and restarts containers.
+Short description: pulls the latest changes and restarts containers.
## Removal
@@ -84,7 +84,7 @@ Remove the service completely:
bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/delete.sh)
```
-Short description: removes containers and configuration files, restoring the system to the pre-install state.
+Short description: removes containers and configuration files, restoring the system to the pre-install state.
---
@@ -96,7 +96,7 @@ The forwarding service allows proxying incoming ports from the intermediate serv
bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/forwarding_install.sh)
```
-Short description: adds forwarding rules and creates a service to update the subscription.
+Short description: adds forwarding rules and creates a service to update the subscription.
## Remove forwarding
@@ -104,7 +104,7 @@ bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main
bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/forwarding_delete.sh)
```
-Short description: removes rules and disables the forwarding service.
+Short description: removes rules and disables the forwarding service.
---
@@ -116,7 +116,7 @@ Command to print the current subscription URL from the container environment:
cd /opt/3dp-manager && docker compose exec node env | grep SUB_URL | cut -d'=' -f2
```
-Short description: prints the static subscription URL that can be used in clients. Works on both main and intermediate servers.
+Short description: prints the static subscription URL that can be used in clients. Works on both main and intermediate servers.
## Collect domains from multi-subscriptions
@@ -126,7 +126,7 @@ The utility extracts domains from subscriptions and builds a `whitelist` for the
node get_domains.js
```
-Short description: add a multi-subscription link to the script and run the command — the output will be a list of domains. `Node.js` is required to run the script.
+Short description: add a multi-subscription link to the script and run the command — the output will be a list of domains. `Node.js` is required to run the script.
## Use your own whitelist
@@ -137,7 +137,7 @@ node get_domains.js
cd /opt/3dp-manager && docker cp ./app/my_whitelist.txt node:/app/my_whitelist.txt
```
-Short description: adds your domain file into the application container.
+Short description: adds your domain file into the application container.
---
@@ -157,7 +157,7 @@ Contributions are welcome! Simple contributor workflow:
4. Run local checks if available.
5. Push the branch to your fork and create a Pull Request to the main repository.
-Tips: describe changes and testing steps in the PR. For large changes, split into smaller commits.
+Tips: describe changes and testing steps in the PR. For large changes, split into smaller commits.
---
diff --git a/README_IR.md b/README_IR.md
index e573987..fa84383 100644
--- a/README_IR.md
+++ b/README_IR.md
@@ -2,7 +2,7 @@

- [](https://www.gnu.org/licenses/gpl-3.0) [](https://t.me/denpiligrim_web) [](https://www.youtube.com/@denpiligrim)
+ [](https://www.gnu.org/licenses/gpl-3.0) [](https://t.me/denpiligrim_web) [](https://www.youtube.com/@denpiligrim)
# 3DP-MANAGER
@@ -64,7 +64,7 @@
bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/install.sh)
```
-توضیح کوتاه: اسکریپت نصب را اجرا کرده و کانتینرها و سرویسها را راهاندازی میکند.
+توضیح کوتاه: اسکریپت نصب را اجرا کرده و کانتینرها و سرویسها را راهاندازی میکند.
## بهروزرسانی
@@ -74,7 +74,7 @@ bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main
bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/update.sh)
```
-توضیح کوتاه: تغییرات جدید را میکشد و کانتینرها را ریاستارت میکند.
+توضیح کوتاه: تغییرات جدید را میکشد و کانتینرها را ریاستارت میکند.
## حذف
@@ -84,7 +84,7 @@ bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main
bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/delete.sh)
```
-توضیح کوتاه: کانتینرها و فایلهای پیکربندی را حذف میکند و سیستم را به وضعیت پیش از نصب برمیگرداند.
+توضیح کوتاه: کانتینرها و فایلهای پیکربندی را حذف میکند و سیستم را به وضعیت پیش از نصب برمیگرداند.
---
@@ -96,7 +96,7 @@ bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main
bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/forwarding_install.sh)
```
-توضیح کوتاه: قوانین فورواردینگ را اضافه کرده و سرویسی برای بهروزرسانی اشتراک ایجاد میکند.
+توضیح کوتاه: قوانین فورواردینگ را اضافه کرده و سرویسی برای بهروزرسانی اشتراک ایجاد میکند.
## حذف فورواردینگ
@@ -104,7 +104,7 @@ bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main
bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/forwarding_delete.sh)
```
-توضیح کوتاه: قوانین را حذف کرده و سرویس فورواردینگ را غیرفعال میکند.
+توضیح کوتاه: قوانین را حذف کرده و سرویس فورواردینگ را غیرفعال میکند.
---
@@ -116,7 +116,7 @@ bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main
cd /opt/3dp-manager && docker compose exec node env | grep SUB_URL | cut -d'=' -f2
```
-توضیح کوتاه: URL ثابت اشتراک را نمایش میدهد که میتوان در کلاینتها استفاده کرد. هم در سرور اصلی و هم در سرور واسط کار میکند.
+توضیح کوتاه: URL ثابت اشتراک را نمایش میدهد که میتوان در کلاینتها استفاده کرد. هم در سرور اصلی و هم در سرور واسط کار میکند.
## جمعآوری دامنهها از چندین اشتراک
@@ -126,7 +126,7 @@ cd /opt/3dp-manager && docker compose exec node env | grep SUB_URL | cut -d'=' -
node get_domains.js
```
-توضیح کوتاه: لینک چندین اشتراک را در اسکریپت قرار داده و فرمان را اجرا کنید — خروجی فهرست دامنهها خواهد بود. نیازمند `Node.js` است.
+توضیح کوتاه: لینک چندین اشتراک را در اسکریپت قرار داده و فرمان را اجرا کنید — خروجی فهرست دامنهها خواهد بود. نیازمند `Node.js` است.
## استفاده از whitelist شخصی
@@ -137,7 +137,7 @@ node get_domains.js
cd /opt/3dp-manager && docker cp ./app/my_whitelist.txt node:/app/my_whitelist.txt
```
-توضیح کوتاه: فایل دامنه شما را به کانتینر برنامه اضافه میکند.
+توضیح کوتاه: فایل دامنه شما را به کانتینر برنامه اضافه میکند.
---
@@ -157,7 +157,7 @@ cd /opt/3dp-manager && docker cp ./app/my_whitelist.txt node:/app/my_whitelist.t
4. در صورت وجود، بررسیهای محلی را اجرا کنید.
5. شاخه را به fork خود پوش کرده و Pull Request ایجاد کنید.
-نکات: تغییرات را در PR شرح دهید و مراحل تست را بنویسید. در صورت تغییرات بزرگ، آنها را به کامیتهای کوچک تقسیم کنید.
+نکات: تغییرات را در PR شرح دهید و مراحل تست را بنویسید. در صورت تغییرات بزرگ، آنها را به کامیتهای کوچک تقسیم کنید.
---
diff --git a/README_TK.md b/README_TK.md
index 18545bc..70cf6d8 100644
--- a/README_TK.md
+++ b/README_TK.md
@@ -2,7 +2,7 @@

- [](https://www.gnu.org/licenses/gpl-3.0) [](https://t.me/denpiligrim_web) [](https://www.youtube.com/@denpiligrim)
+ [](https://www.gnu.org/licenses/gpl-3.0) [](https://t.me/denpiligrim_web) [](https://www.youtube.com/@denpiligrim)
# 3DP-MANAGER
@@ -64,7 +64,7 @@ Taslamany serwera gurnamak üçin şu komandany ýerine ýetiriň:
bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/install.sh)
```
-Gysgaça: gurnama skripti işledilýär we konteýnerler we hyzmatlar döredilýär.
+Gysgaça: gurnama skripti işledilýär we konteýnerler we hyzmatlar döredilýär.
## Täzelik
@@ -74,7 +74,7 @@ Soňky wersiýa çenli täzeläň:
bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/update.sh)
```
-Gysgaça: iň soňky üýtgeşmeleri çekýär we konteýnerleri gaýtadan işledýär.
+Gysgaça: iň soňky üýtgeşmeleri çekýär we konteýnerleri gaýtadan işledýär.
## Pozmak
@@ -84,7 +84,7 @@ Hyzmaty doly pozmak üçin:
bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/delete.sh)
```
-Gysgaça: konteýnerleri we konfigurasiýa faýllaryny pozup, systemany gurnamadan öňki ýagdaýyna getirer.
+Gysgaça: konteýnerleri we konfigurasiýa faýllaryny pozup, systemany gurnamadan öňki ýagdaýyna getirer.
---
diff --git a/app/Dockerfile b/app/Dockerfile
index 4a63d4e..d50bd86 100644
--- a/app/Dockerfile
+++ b/app/Dockerfile
@@ -6,6 +6,7 @@ COPY package.json ./
RUN npm install --production
COPY index.js .
+COPY rotate.js .
COPY builders ./builders
CMD ["node", "index.js"]
\ No newline at end of file
diff --git a/app/index.js b/app/index.js
index 0ef17cc..510e2ed 100644
--- a/app/index.js
+++ b/app/index.js
@@ -1,5 +1,5 @@
import axios from "axios";
-import cron from "node-cron";
+import https from "https";
import fs from "fs";
import net from "net";
import { buildVlessRealityTcp } from "./builders/buildVlessRealityTcp.js";
@@ -16,7 +16,10 @@ const SUB_FILE = "/subscriptions/list.txt";
const LAST_INBOUNDS = '/subscriptions/latestInbounds.json';
const cookieJar = {};
-const api = axios.create({ baseURL: UI_URL, timeout: 15000, withCredentials: true });
+const agent = new https.Agent({
+ rejectUnauthorized: false
+});
+const api = axios.create({ baseURL: UI_URL, timeout: 15000, withCredentials: true, httpsAgent: agent });
api.interceptors.request.use(config => {
if (cookieJar.value) config.headers['Cookie'] = cookieJar.value;
@@ -121,12 +124,12 @@ async function login() {
} catch (e) { console.error("Login failed:", e.message); throw e; }
}
-async function getInbounds() {
- try {
- const res = await api.get("/panel/api/inbounds/list");
- return res.status === 200 ? res.data.obj || [] : [];
- } catch (e) { console.error("Get inbounds failed:", e.message); return []; }
-}
+// async function getInbounds() {
+// try {
+// const res = await api.get("/panel/api/inbounds/list");
+// return res.status === 200 ? res.data.obj || [] : [];
+// } catch (e) { console.error("Get inbounds failed:", e.message); return []; }
+// }
async function deleteInbounds() {
try {
diff --git a/app/rotate.js b/app/rotate.js
new file mode 100644
index 0000000..7f583de
--- /dev/null
+++ b/app/rotate.js
@@ -0,0 +1,216 @@
+import axios from "axios";
+import https from "https";
+import fs from "fs";
+import net from "net";
+import { buildVlessRealityTcp } from "./builders/buildVlessRealityTcp.js";
+import { buildVlessRealityXhttp } from "./builders/buildVlessRealityXhttp.js";
+import { buildTrojanRealityTcp } from "./builders/buildTrojanRealityTcp.js";
+import { buildShadowsocksTcp } from "./builders/buildShadowsocksTcp.js";
+import { buildVmessTcp } from "./builders/buildVmessTcp.js";
+import { buildVlessRealityGrpc } from "./builders/buildVlessRealityGrpc.js";
+import { buildVlessWs } from "./builders/buildVlessWs.js";
+import { buildInboundLink } from "./builders/buildInboundLink.js";
+
+const { UI_URL, UI_LOGIN, UI_PASSWORD, UI_HOST } = process.env;
+const SUB_FILE = "/subscriptions/list.txt";
+const LAST_INBOUNDS = '/subscriptions/latestInbounds.json';
+
+const cookieJar = {};
+const agent = new https.Agent({
+ rejectUnauthorized: false
+});
+const api = axios.create({ baseURL: UI_URL, timeout: 15000, withCredentials: true, httpsAgent: agent });
+
+api.interceptors.request.use(config => {
+ if (cookieJar.value) config.headers['Cookie'] = cookieJar.value;
+ return config;
+});
+
+const WHITELIST_FILE = "/app/whitelist.txt";
+const WHITELIST_REPO_URL = "https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/whitelist.txt";
+
+// Helper functions
+async function updateWhitelist() {
+ try {
+ const res = await axios.get(WHITELIST_REPO_URL, { timeout: 10000 });
+ fs.writeFileSync(WHITELIST_FILE, res.data, "utf8");
+ console.log("✔ whitelist.txt обновлен из репозитория");
+ } catch (err) {
+ console.warn("⚠ Не удалось обновить whitelist.txt, будет использоваться локальный файл:", err.message);
+ }
+}
+
+function loadWhitelist() {
+ let fileToUse = WHITELIST_FILE;
+
+ if (fs.existsSync("/app/my_whitelist.txt")) {
+ console.log('Найден кастомный whitelist: /app/my_whitelist.txt. Используется он.');
+ fileToUse = "/app/my_whitelist.txt";
+ } else {
+ console.log(`Кастомный whitelist не найден. Используется дефолтный: ${WHITELIST_FILE}`);
+ if (!fs.existsSync(WHITELIST_FILE)) {
+ throw new Error("Дефолтный whitelist.txt не найден");
+ }
+ }
+
+ return fs.readFileSync(fileToUse, "utf8")
+ .split("\n")
+ .map(v => v.trim())
+ .filter(Boolean);
+}
+
+function pickDomain(list) {
+ return list[Math.floor(Math.random() * list.length)];
+}
+
+async function isPortFree(port) {
+ return new Promise(resolve => {
+ const s = net.createServer()
+ .once("error", () => resolve(false))
+ .once("listening", () => {
+ s.close();
+ resolve(true);
+ })
+ .listen(port, "0.0.0.0");
+ });
+}
+
+async function getFreePort(used) {
+ while (true) {
+ const p = Math.floor(Math.random() * (60000 - 10000)) + 10000;
+ if (used.has(p)) continue;
+ if (await isPortFree(p)) {
+ used.add(p);
+ return p;
+ }
+ }
+}
+
+async function generateRealityKeys() {
+ try {
+ const res = await api.get("/panel/api/server/getNewX25519Cert");
+ if (res.data?.success && res.data?.obj) {
+ return {
+ privateKey: res.data.obj.privateKey,
+ publicKey: res.data.obj.publicKey
+ };
+ }
+ throw new Error("Invalid key response");
+ } catch (e) {
+ console.error("Failed to get Reality keys:", e.message);
+ throw e;
+ }
+}
+
+async function uuid() {
+ try {
+ const res = await api.get("/panel/api/server/getNewUUID");
+ if (res.data?.success && res.data?.obj?.uuid) {
+ return res.data.obj.uuid;
+ }
+ throw new Error("Invalid UUID response");
+ } catch (e) {
+ console.error("Failed to get UUID:", e.message);
+ throw e;
+ }
+}
+
+// API 3x-ui
+async function login() {
+ try {
+ const res = await api.post("/login", { username: UI_LOGIN, password: UI_PASSWORD });
+ if (res.headers['set-cookie']) cookieJar.value = res.headers['set-cookie'].join('; ');
+ console.log("Login success");
+ } catch (e) { console.error("Login failed:", e.message); throw e; }
+}
+
+// async function getInbounds() {
+// try {
+// const res = await api.get("/panel/api/inbounds/list");
+// return res.status === 200 ? res.data.obj || [] : [];
+// } catch (e) { console.error("Get inbounds failed:", e.message); return []; }
+// }
+
+async function deleteInbounds() {
+ try {
+ const data = fs.readFileSync(LAST_INBOUNDS, 'utf-8');
+ const oldIds = JSON.parse(data);
+
+ if (Array.isArray(oldIds) && oldIds.length > 0) {
+ console.log(`Удаляем старые инбаунды: ${oldIds.length} шт.`);
+ for (const id of oldIds) {
+ try {
+ await deleteInbound(id);
+ } catch (err) {
+ console.error(`Ошибка при удалении инбаунда ${id}:`, err.message);
+ }
+ }
+ }
+ } catch (error) {
+ console.log("Старых ID не найдено, пропускаем удаление.");
+ }
+}
+
+async function deleteInbound(id) {
+ try { await api.post(`/panel/api/inbounds/del/${id}`); } catch { }
+}
+
+async function addInbound(config) {
+ try {
+ const res = await api.post("/panel/api/inbounds/add", config);
+ return res.data?.obj?.id || null;
+ } catch (e) {
+ console.error("Add inbound failed:", e.message);
+ return null;
+ }
+}
+
+// Main rotation of inbound entries
+async function rotate() {
+ await login();
+
+ await updateWhitelist();
+ const whitelist = loadWhitelist();
+ const usedPorts = new Set();
+ const subs = [];
+
+ await deleteInbounds();
+
+ const builders = [
+ async (d) => buildVlessRealityTcp({ port: await isPortFree(8443) ? 8443 : await getFreePort(usedPorts), uuid: await uuid(), domain: d, keys: await generateRealityKeys() }),
+ async (d) => buildVlessRealityXhttp({ port: await isPortFree(443) ? 443 : await getFreePort(usedPorts), uuid: await uuid(), domain: d, keys: await generateRealityKeys() }),
+ async (d) => buildVlessRealityGrpc({ port: await getFreePort(usedPorts), uuid: await uuid(), domain: d, keys: await generateRealityKeys() }),
+ async (d) => buildVlessWs({ port: await getFreePort(usedPorts), uuid: await uuid(), domain: d }),
+ async (d) => buildVlessRealityTcp({ port: await getFreePort(usedPorts), uuid: await uuid(), domain: d, keys: await generateRealityKeys() }),
+ async (d) => buildVlessRealityTcp({ port: await getFreePort(usedPorts), uuid: await uuid(), domain: d, keys: await generateRealityKeys() }),
+ async (d) => buildVlessRealityTcp({ port: await getFreePort(usedPorts), uuid: await uuid(), domain: d, keys: await generateRealityKeys() }),
+ async (d) => buildVmessTcp({ port: await getFreePort(usedPorts), uuid: await uuid() }),
+ async (d) => buildShadowsocksTcp({ port: await getFreePort(usedPorts), uuid: await uuid() }),
+ async (d) => buildTrojanRealityTcp({ port: await getFreePort(usedPorts), uuid: await uuid(), domain: d, keys: await generateRealityKeys() }),
+ ];
+
+ const inboundIds = [];
+
+ for (const b of builders) {
+ const domain = pickDomain(whitelist);
+ const inbound = await b(domain);
+ const idOrPass = inbound.settings ? JSON.parse(inbound.settings).clients?.[0]?.id || JSON.parse(inbound.settings).clients?.[0]?.password : "";
+
+ // Build the link depending on the protocol
+ const link = buildInboundLink(inbound, UI_HOST, idOrPass);
+ if (link) subs.push(link);
+ const id = await addInbound(inbound);
+ inboundIds.push(id);
+ }
+
+ if (inboundIds.length > 0) {
+ fs.writeFileSync(LAST_INBOUNDS, JSON.stringify(inboundIds, null, 2));
+ console.log(`Новые ID (${inboundIds.length} шт.) сохранены в ${LAST_INBOUNDS}`);
+ }
+
+ fs.writeFileSync(SUB_FILE, subs.join("\n") + "\n", "utf8");
+ console.log("✔ 10 inbound created, подписка обновлена");
+}
+
+// Initial run
+rotate();
\ No newline at end of file
diff --git a/install.sh b/install.sh
index c5733aa..21b4c24 100644
--- a/install.sh
+++ b/install.sh
@@ -31,14 +31,17 @@ if [[ "$ID" != "ubuntu" && "$ID" != "debian" ]]; then
die "Этот скрипт поддерживает только Ubuntu или Debian: $ID"
fi
-if ! x-ui status >/dev/null 2>&1; then
- echo "❌ Панель 3x-ui не найдена или не работает."
- echo " Чтобы установить, выполните:"
- echo "bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)"
- exit 1
-fi
+REMOTE_PANEL=${REMOTE_PANEL:-false}
-echo "✔ Панель 3x-ui найдена и работает"
+if [[ "$REMOTE_PANEL" != "true" ]]; then
+ if ! x-ui status >/dev/null 2>&1; then
+ echo "❌ Панель 3x-ui не найдена или не работает."
+ echo " Чтобы установить, выполните:"
+ echo " bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)"
+ exit 1
+ fi
+ echo "✔ Панель 3x-ui найдена и работает"
+fi
#################################
# ASCII-баннер
@@ -60,6 +63,11 @@ echo ""
#################################
# Function to get panel URL from 3x-ui
get_xui_url() {
+ if [[ "$REMOTE_PANEL" == "true" ]]; then
+ echo ""
+ return
+ fi
+
local output=$(x-ui settings 2>/dev/null || true)
echo "$output" | sed 's/\x1b\[[0-9;]*m//g' | grep "Access URL:" | grep -oE 'https?://[^[:space:]]+' | head -n1 || true
@@ -83,6 +91,9 @@ echo "URL панели 3x-ui: $UI_URL"
read -rp "Логин 3x-ui: " UI_LOGIN
read -rsp "Пароль 3x-ui: " UI_PASSWORD
echo
+
+UI_LOGIN=$(echo "$UI_LOGIN" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
+UI_PASSWORD=$(echo "$UI_PASSWORD" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
[[ -z "$UI_LOGIN" || -z "$UI_PASSWORD" ]] && die "Логин/пароль обязательны"
# Check login
@@ -92,7 +103,7 @@ if ! command -v curl >/dev/null 2>&1; then
exit 1
fi
-LOGIN_RESPONSE=$(curl -s -X POST "$UI_URL/login" -H "Content-Type: application/json" -d "{\"username\":\"$UI_LOGIN\",\"password\":\"$UI_PASSWORD\"}" || true)
+LOGIN_RESPONSE=$(curl -s -k --connect-timeout 10 -X POST "$UI_URL/login" -H "Content-Type: application/json" -d "{\"username\":\"$UI_LOGIN\",\"password\":\"$UI_PASSWORD\"}" || true)
if ! echo "$LOGIN_RESPONSE" | grep -q '"success":true'; then
echo "Не удалось залогиниться в 3x-ui. Проверьте URL, логин и пароль."
@@ -117,8 +128,8 @@ if [[ "$UI_PROTO" == "https" ]]; then
log "Найдены сертификаты Let's Encrypt для $UI_HOST"
else
log "⚠ Сертификаты Let's Encrypt для $UI_HOST не найдены"
- read -rp "Введите полный путь к SSL сертификату (fullchain.pem): " CERT_PATH
- read -rp "Введите полный путь к SSL ключу (privkey.pem): " KEY_PATH
+ read -rp "Введите полный путь к сертификату (публичный ключ): " CERT_PATH
+ read -rp "Введите полный путь к ключу (приватный ключ): " KEY_PATH
[[ -f "$CERT_PATH" ]] || die "Файл сертификата не найден: $CERT_PATH"
[[ -f "$KEY_PATH" ]] || die "Файл ключа не найден: $KEY_PATH"
@@ -274,6 +285,7 @@ curl -fsSL "$REPO_BASE/app/package.json" -o app/package.json
# JS Files
#################################
curl -fsSL "$REPO_BASE/app/index.js" -o app/index.js
+curl -fsSL "$REPO_BASE/app/rotate.js" -o app/rotate.js
curl -fsSL "$REPO_BASE/app/builders/buildVlessRealityTcp.js" -o app/builders/buildVlessRealityTcp.js
curl -fsSL "$REPO_BASE/app/builders/buildVlessRealityXhttp.js" -o app/builders/buildVlessRealityXhttp.js
curl -fsSL "$REPO_BASE/app/builders/buildTrojanRealityTcp.js" -o app/builders/buildTrojanRealityTcp.js
diff --git a/update.sh b/update.sh
index 3b4eda0..6c7c399 100644
--- a/update.sh
+++ b/update.sh
@@ -80,6 +80,11 @@ docker compose build node
log "Перезапускаем контейнеры"
docker compose up -d
+if [ -f "app/my_whitelist.txt" ]; then
+ log "✔ Копируем my_whitelist.txt в контейнер..."
+ docker cp app/my_whitelist.txt node:/app/my_whitelist.txt
+fi
+
#################################
# HEALTH CHECK
#################################