- [](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
-Утилита для автогенерации инбаундов к панели [3x-ui](https://github.com/MHSanaei/3x-ui), формирования единой подписки и настройки перенаправления трафика с промежуточного сервера на основной. Утилита, начиная с версии 2.0.0 имеет графический интерфейс и простые пользовательские настройки.
+Утилита для автогенерации инбаундов к панели [3x-ui](https://github.com/MHSanaei/3x-ui), формирования единых подписок, управления несколькими 3x-ui нодами и настройки relay-перенаправления трафика с промежуточных серверов на основные. Начиная с версии 2.0.0 проект имеет графический интерфейс и простые пользовательские настройки.
**Поддержать проект**
@@ -25,18 +25,19 @@
## Описание
-Главная цель утилиты — сделать так, чтобы ваш трафик не выглядел одинаковым. Бот генерирует по заданному интервалу 10 подключений с разными параметрами:
+Главная цель утилиты — сделать так, чтобы ваш трафик не выглядел одинаковым. 3DP-MANAGER генерирует по заданному интервалу набор подключений с разными параметрами:
- протоколы: `vless`, `vmess`, `shadowsocks`, `hysteria2`, `trojan`;
- порты: `443`, `8443` (фиксированные) и случайные из диапазона `10000-60000`;
- транспорт: `tcp`, `websocket`, `grpc`, `xhttp`;
-- SNI берутся из белого списка доменов (whitelist); можно использовать свой список.
+- SNI берутся из белого списка доменов (whitelist) или задаются вручную;
+- нода, relay-сервер, порт и флаг могут настраиваться отдельно для каждого инбаунда.
-Все подключения объединяются в одну подписку со статичным URL. Бот работает с панелью `3x-ui` и не вмешивается в её работу напрямую, используя открытое API панели.
+Все подключения объединяются в одну подписку со статичным URL. 3DP-MANAGER работает с панелью `3x-ui` и не вмешивается в её работу напрямую, используя открытое API панели.
-Вторичная цель — стабильность подключения: клиент получает 10 вариантов подключений и может выбрать любое из них.
+Вторичная цель — стабильность подключения: клиент получает несколько вариантов подключений и может выбрать любой из них. В одну подписку можно добавлять инбаунды с разных нод, использовать relay-серверы и добавлять собственные внешние ссылки как кастомные подключения.
-Дополнительно: бот можно использовать в каскадной схеме. Сервис перенаправления автоматически настроит переадресацию подписки и трафика к основному серверу.
+Дополнительно: 3DP-MANAGER можно использовать в каскадной схеме. Сервис перенаправления автоматически настроит переадресацию подписки и трафика к выбранной ноде.
Рекомендации:
@@ -46,15 +47,25 @@
## Возможности
-- Генерация 10 разнообразных подключений
+- Генерация разнообразных подключений для одной или нескольких подписок
- Формирование единой подписки со статичным URL
+- Управление несколькими нодами `3x-ui`
+- Авторизация нод по логину/паролю или токену
+- Автоопределение IP, страны и флага ноды по URL панели
+- Выбор ноды, relay-сервера, флага, SNI и порта для каждого инбаунда
+- Поддержка фиксированного порта или значения `random`
+- Кастомные подключения: можно добавить готовую внешнюю ссылку в подписку
+- Отдельные пути к `fullchain.pem` и `privkey.pem` для Hysteria2 UDP
- Поддержка кастомного `whitelist` доменов
-- Автоматическая настройка перенаправления трафика (опционально)
+- Автоматическая настройка relay-перенаправления трафика (опционально)
+- Установка Web UI через HTTP или HTTPS: Let's Encrypt, self-signed или свои сертификаты
## Требования
- Ubuntu 20.04 (и выше), Debian 12.11 (и выше)
-- Панель `3x-ui` v2.8.4 (и выше)
+- Панель `3x-ui` v2.9.4 (и выше)
+- Root-доступ на сервере
+- Docker и Docker Compose; если их нет, установочный скрипт попробует установить их автоматически
- Домен + SSL сертификат (опционально)
---
@@ -62,13 +73,29 @@
## Установка
У вас должна быть установлена панель управления `3x-ui`, которую можно поставить командой: `bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)`
+
Установите проект на сервер командой:
```bash
bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/install.sh)
```
-Краткое описание: запускает скрипт установки и разворачивает контейнеры и сервисы.
+Во время установки скрипт:
+
+- проверит ОС, root-доступ, Docker и Docker Compose;
+- при нехватке RAM создаст swap-файл 2 GB, если swap отсутствует;
+- предложит выбрать режим доступа к Web UI:
+ - HTTPS через Let's Encrypt;
+ - HTTPS с self-signed сертификатом;
+ - HTTPS со своими сертификатами;
+ - HTTP без шифрования;
+- сгенерирует случайный порт Web UI;
+- сгенерирует логин и пароль администратора;
+- развернёт контейнеры `postgres`, `backend` и `frontend`.
+
+После завершения установки в терминале будут показаны URL, логин и пароль. Сразу смените пароль в настройках 3DP-MANAGER.
+
+Краткое описание: запускает интерактивный скрипт установки, подготавливает окружение и разворачивает контейнеры проекта.
## Обновление
@@ -78,7 +105,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)
```
-Краткое описание: подтягивает последние изменения и перезапускает контейнеры.
+Краткое описание: подтягивает последние изменения, применяет совместимые исправления конфигурации, обновляет контейнеры и перезапускает сервис.
## Удаление
@@ -92,12 +119,76 @@ bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main
---
+## Первый вход и доступы
+
+Логин и пароль администратора выводятся в конце установки. Если нужно посмотреть их повторно:
+
+```bash
+grep -E "ADMIN_LOGIN|ADMIN_PASSWORD" /opt/3dp-manager/docker-compose.yml | sed 's/^[ \t]*//; s/^- //'
+```
+
+Web UI доступен по адресу, который показал установщик, например:
+
+- `https://example.com:PORT` для HTTPS;
+- `http://SERVER_IP:PORT` для HTTP.
+
+Если включён `ufw`, установщик открывает рабочие диапазоны портов для инбаундов: `443`, `8443` и `10000-60000` для TCP/UDP.
+
+---
+
+## Ноды
+
+Раздел **Ноды** позволяет подключить одну или несколько панелей `3x-ui`.
+
+Для каждой ноды указываются:
+
+- название;
+- URL панели `3x-ui`;
+- IP ноды;
+- флаг/страна;
+- тип авторизации: `password` или `token`;
+- признак основной ноды.
+
+После ввода URL приложение пытается определить IP и страну ноды автоматически. Флаг используется в названиях подключений внутри подписки, чтобы в клиенте было проще отличать серверы.
+
+> [!NOTE]
+> Основная нода используется как значение по умолчанию для подписок, инбаундов и relay-серверов.
+
+---
+
+## Подписки
+
+Раздел **Подписки** управляет статичными URL подписок и составом инбаундов.
+
+Для каждого инбаунда можно настроить:
+
+- тип подключения;
+- ноду;
+- relay-сервер;
+- флаг;
+- порт: конкретное число `1-65535` или `random`;
+- SNI: конкретный домен или `random`;
+- готовую внешнюю ссылку для типа `custom`.
+
+Для `hysteria2-udp` можно указать отдельные пути к сертификату и ключу:
+
+- `certificateFile`, например `/root/cert/example.com/fullchain.pem`;
+- `keyFile`, например `/root/cert/example.com/privkey.pem`.
+
+Если порт или SNI указаны как `random`, значение будет выбрано при ротации из доступного диапазона или whitelist доменов.
+
+---
+
## Установка сервиса перенаправления (forwarding)
> [!WARNING]
> Сервис перенаправления работает на промежуточном сервере
-Сервис перенаправления позволяет проксировать входящие порты с промежуточного сервера на основной. IP адрес замените на IP адрес основного сервера (не домен! только IP адрес):
+Сервис перенаправления позволяет проксировать входящие порты с промежуточного сервера на выбранную ноду. В Web UI такие серверы настраиваются в разделе **Relay серверы** и могут быть привязаны к конкретной ноде.
+
+Relay-сервер можно добавить по IP или домену. Если указан домен, backend попытается определить IP автоматически.
+
+Для ручной установки forwarding на промежуточном сервере замените `IP_ADDRESS` на IP основной ноды:
```bash
sudo ORIGIN_IP="IP_ADDRESS" bash -c "$(curl -sSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/forwarding_install.sh)"
@@ -115,16 +206,6 @@ bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main
---
-## Посмотреть логин и пароль в терминале
-
-Команда позволяет посмотреть логин и пароль от личного кабинета 3DP-MANAGER.
-
-```bash
-grep -E "ADMIN_LOGIN|ADMIN_PASSWORD" /opt/3dp-manager/docker-compose.yml | sed 's/^[ \t]*//; s/^- //'
-```
-
----
-
## Сбор доменов из мульти-подписок
Утилита извлекает домены из подписок и формирует `whitelist` для генератора.
@@ -160,4 +241,4 @@ node get_domains.js
## Обсуждение
- Телеграм: [@denpiligrim_web](https://t.me/denpiligrim_web)
-- Раздел Issues в данном репозитории
\ No newline at end of file
+- Раздел Issues в данном репозитории
diff --git a/README_CN.md b/README_CN.md
index 2d79f3b..c9d787a 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -2,69 +2,100 @@
- [](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
-A utility for auto-generating inbound connections for the [3x-ui](https://github.com/MHSanaei/3x-ui) panel, creating a single subscription URL, and configuring traffic forwarding from an intermediate server to the main server.
+A utility for automatically generating inbounds for the [3x-ui](https://github.com/MHSanaei/3x-ui) panel, creating unified subscriptions, managing multiple 3x-ui nodes, and configuring relay forwarding from intermediate servers to origin servers. Since version 2.0.0, the project has a graphical interface and simple user-facing settings.
**Support the project**
-- Donation / payment details:
- - MIR card: `2204320436318077`
- - MasterCard: `5395452209474530`
- - PayPal: `vasiljevdenisx@gmail.com`
- - USDT | ETH (ERC20 | BEP20): `0x6fe140040f6Cdc1E1Ff2136cd1d60C0165809463`
- - USDT | TRX (TRC20): `TEWxXmJxvkAmhshp7E61XJGHB3VyM9hNAb`
- - Bitcoin: `bc1qctntwncsv2yn02x2vgnkrqm00c4h04c0afkgpl`
- - TON: `UQCZ3MiwyYHXftPItMMzJRYRiKHugr16jFMq2nfOQOOoemLy`
- - Bybit ID: `165292278`
+- Bank transfer:
+ - :credit_card: MIR card: `2204320436318077`
+ - :credit_card: MasterCard: `5395452209474530`
+- E-wallet:
+ - :moneybag: YooMoney: `4100116897060652`
+ - :moneybag: PayPal: `vasiljevdenisx@gmail.com`
+- Crypto:
+ - :coin: USDT | ETH (ERC20 | BEP20): `0x6fe140040f6Cdc1E1Ff2136cd1d60C0165809463`
+ - :coin: USDT | TRX (TRC20): `TEWxXmJxvkAmhshp7E61XJGHB3VyM9hNAb`
+ - :coin: Bitcoin: `bc1qctntwncsv2yn02x2vgnkrqm00c4h04c0afkgpl`
+ - :coin: TON: `UQCZ3MiwyYHXftPItMMzJRYRiKHugr16jFMq2nfOQOOoemLy`
+ - :coin: Bybit ID: `165292278`
## Description
-The main goal of the utility is to make your traffic appear non-uniform. The bot generates 10 connection entries at a configured interval with varying parameters:
+The main goal of the utility is to make your traffic look less uniform. 3DP-MANAGER generates a set of connections at a configured interval with different parameters:
-- Protocols: `vless`, `vmess`, `shadowsocks`, `trojan`;
-- Ports: `443`, `8443` (fixed) and random ports from the range `10000-60000`;
-- Transport: `tcp`, `websocket`, `grpc`, `xhttp`;
-- SNI values are taken from a whitelist of domains (`whitelist`); you can use your own list.
+- protocols: `vless`, `vmess`, `shadowsocks`, `hysteria2`, `trojan`;
+- ports: fixed `443`, `8443`, plus random ports from `10000-60000`;
+- transport: `tcp`, `websocket`, `grpc`, `xhttp`;
+- SNI values are taken from the domain whitelist or set manually;
+- node, relay server, port, and flag can be configured separately for each inbound.
-All generated connections are combined into a single subscription with a static URL. The bot works with the `3x-ui` panel using its public API and does not directly modify the panel internals.
+All connections are combined into one subscription with a static URL. 3DP-MANAGER works with the `3x-ui` panel through its public API and does not directly interfere with the panel internals.
-The secondary goal is connection stability: the client receives 10 alternate connection options and can choose any of them.
+The secondary goal is connection stability: the client receives several connection options and can choose any of them. One subscription can contain inbounds from different nodes, relay servers, and your own external links as custom connections.
-Additionally, the bot can be used in a cascading setup. The forwarding service will automatically configure the subscription and traffic redirection to the main server.
+Additionally, 3DP-MANAGER can be used in a cascading setup. The forwarding service configures subscription and traffic forwarding to the selected node.
Recommendations:
-- Use HTTPS for the subscription (domain + SSL certificate).
-- Set the generation interval to ≥ 10 minutes; for stability, once per day (1440 minutes) is recommended.
-- Configure the client to check for updates more frequently (for example, hourly) to stay synchronized with the server.
+- Use HTTPS for subscriptions (domain + SSL certificate).
+- Set the generation interval to at least 10 minutes; for stability, once per day (1440 minutes) is recommended.
+- Configure the client to refresh more frequently, for example every hour, so it stays synchronized with the server.
## Features
-- Generates 10 diverse connection entries
-- Creates a single subscription with a static URL
-- Supports custom `whitelist` domains
-- Optional automatic configuration of traffic forwarding
+- Generates diverse connections for one or more subscriptions
+- Creates a unified subscription with a static URL
+- Manages multiple `3x-ui` nodes
+- Supports node authentication by login/password or token
+- Automatically detects node IP, country, and flag from the panel URL
+- Lets you choose node, relay server, flag, SNI, and port per inbound
+- Supports a fixed port or `random`
+- Supports custom connections: add a ready-made external link to a subscription
+- Allows separate `fullchain.pem` and `privkey.pem` paths for Hysteria2 UDP
+- Supports a custom domain `whitelist`
+- Optional automatic relay forwarding setup
+- Installs the Web UI over HTTP or HTTPS: Let's Encrypt, self-signed, or custom certificates
## Requirements
-- Ubuntu 20.04 or newer
-- `3x-ui` panel
+- Ubuntu 20.04 or newer, Debian 12.11 or newer
+- `3x-ui` panel v2.8.4 or newer
+- Root access on the server
+- Docker and Docker Compose; if missing, the installer will try to install them automatically
- Domain + SSL certificate (optional)
---
## Installation
-Install the project on your server with:
+The `3x-ui` control panel must already be installed. You can install it with: `bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)`
+
+Install the project on the server:
```bash
bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/install.sh)
```
-Short description: runs the installer script and deploys containers and services.
+During installation, the script will:
+
+- check the OS, root access, Docker, and Docker Compose;
+- create a 2 GB swap file if RAM is low and swap is missing;
+- ask which Web UI access mode to use:
+ - HTTPS via Let's Encrypt;
+ - HTTPS with a self-signed certificate;
+ - HTTPS with your own certificates;
+ - HTTP without encryption;
+- generate a random Web UI port;
+- generate the administrator login and password;
+- deploy the `postgres`, `backend`, and `frontend` containers.
+
+After installation, the terminal will show the URL, login, and password. Change the password immediately in the 3DP-MANAGER settings.
+
+Short description: runs the interactive installer, prepares the environment, and deploys the project containers.
## Update
@@ -74,7 +105,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, applies compatible configuration fixes, updates containers, and restarts the service.
## Removal
@@ -88,37 +119,94 @@ bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main
---
-## Install forwarding service
+## First Login
-The forwarding service allows proxying incoming ports from the intermediate server to the main server.
+The administrator login and password are printed at the end of installation. To view them again:
```bash
-bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/forwarding_install.sh)
+grep -E "ADMIN_LOGIN|ADMIN_PASSWORD" /opt/3dp-manager/docker-compose.yml | sed 's/^[ \t]*//; s/^- //'
```
-Short description: adds forwarding rules and creates a service to update the subscription.
+The Web UI is available at the address shown by the installer, for example:
-## Remove forwarding
+- `https://example.com:PORT` for HTTPS;
+- `http://SERVER_IP:PORT` for HTTP.
+
+If `ufw` is active, the installer opens the working inbound port ranges: `443`, `8443`, and `10000-60000` for TCP/UDP.
+
+---
+
+## Nodes
+
+The **Nodes** section lets you connect one or more `3x-ui` panels.
+
+For each node, you specify:
+
+- name;
+- `3x-ui` panel URL;
+- node IP;
+- flag/country;
+- authentication type: `password` or `token`;
+- whether this node is the main node.
+
+After you enter the URL, the application tries to detect the node IP and country automatically. The flag is used in connection names inside the subscription so clients can distinguish servers more easily.
+
+> [!NOTE]
+> The main node is used as the default value for subscriptions, inbounds, and relay servers.
+
+---
+
+## Subscriptions
+
+The **Subscriptions** section manages static subscription URLs and inbound composition.
+
+For each inbound, you can configure:
+
+- connection type;
+- node;
+- relay server;
+- flag;
+- port: a concrete number from `1-65535` or `random`;
+- SNI: a concrete domain or `random`;
+- a ready-made external link for the `custom` type.
+
+For `hysteria2-udp`, you can specify separate certificate and key paths:
+
+- `certificateFile`, for example `/root/cert/example.com/fullchain.pem`;
+- `keyFile`, for example `/root/cert/example.com/privkey.pem`.
+
+If port or SNI is set to `random`, the value will be selected during rotation from the available range or domain whitelist.
+
+---
+
+## Install Forwarding Service
+
+> [!WARNING]
+> The forwarding service runs on an intermediate server
+
+The forwarding service proxies incoming ports from an intermediate server to the selected node. In the Web UI, these servers are configured in **Relay servers** and can be linked to a specific node.
+
+A relay server can be added by IP or domain. If a domain is provided, the backend will try to resolve its IP automatically.
+
+For manual forwarding installation on the intermediate server, replace `IP_ADDRESS` with the origin node IP:
+
+```bash
+sudo ORIGIN_IP="IP_ADDRESS" bash -c "$(curl -sSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/forwarding_install.sh)"
+```
+
+Short description: adds forwarding rules.
+
+## Remove Forwarding
```bash
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 forwarding rules. After removal, reload the firewall with `ufw reload` and reboot the system with `reboot` for changes to take effect.
---
-## Show subscription URL
-
-Command to print the current subscription URL from the container environment:
-
-```bash
-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.
-
-## Collect domains from multi-subscriptions
+## Collect Domains From Multi-Subscriptions
The utility extracts domains from subscriptions and builds a `whitelist` for the generator.
@@ -126,24 +214,13 @@ 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.
-
-## Use your own whitelist
-
-1. Prepare a file in the format of `whitelist.txt`.
-2. Rename it to `my_whitelist.txt` and copy it into `/opt/3dp-manager/app`.
-
-```bash
-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: add a multi-subscription link to the script and run the command. The output will be a domain list. `Node.js` is required.
---
-## Notes and current limitations
+## Notes And Current Limitations
-- The shared domain list may not work with all providers; it is recommended to prepare and use your own `whitelist`.
+- The shared domain list may not work with all providers, so it is recommended to prepare and use your own whitelist.
---
@@ -152,16 +229,16 @@ cd /opt/3dp-manager && docker cp ./app/my_whitelist.txt node:/app/my_whitelist.t
Contributions are welcome! Simple contributor workflow:
1. Fork the repository on GitHub.
-2. Create a branch with a meaningful name, e.g. `feature/add-README` or `fix/whitelist-load`.
-3. Make changes and add a short commit message.
+2. Create a branch with a meaningful name, for example `feature/add-README` or `fix/whitelist-load`.
+3. Make changes and add a short description in the commit.
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 your changes in the PR, include the goal and test steps. If the changes are large, split them into small commits.
---
## Discussion
- Telegram: [@denpiligrim_web](https://t.me/denpiligrim_web)
-- Issues
\ No newline at end of file
+- Issues section in this repository
diff --git a/README_IR.md b/README_IR.md
index fe06e89..a302936 100644
--- a/README_IR.md
+++ b/README_IR.md
@@ -2,166 +2,243 @@
- [](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
-ابزاری برای تولید خودکار اتصالهای inbound برای پنل `3x-ui`، ساخت یک اشتراک یکپارچه و هدایت ترافیک از سرور واسط به سرور اصلی.
+ابزاری برای تولید خودکار inbound برای پنل [3x-ui](https://github.com/MHSanaei/3x-ui)، ساخت اشتراکهای یکپارچه، مدیریت چندین نود 3x-ui و پیکربندی relay forwarding از سرورهای واسط به سرورهای اصلی. از نسخه 2.0.0 به بعد، پروژه دارای رابط گرافیکی و تنظیمات ساده کاربری است.
**حمایت از پروژه**
-- اطلاعات پرداخت/اهدا:
- - کارت MIR: `2204320436318077`
- - کارت MasterCard: `5395452209474530`
- - PayPal: `vasiljevdenisx@gmail.com`
- - USDT | ETH (ERC20 | BEP20): `0x6fe140040f6Cdc1E1Ff2136cd1d60C0165809463`
- - USDT | TRX (TRC20): `TEWxXmJxvkAmhshp7E61XJGHB3VyM9hNAb`
- - بیتکوین: `bc1qctntwncsv2yn02x2vgnkrqm00c4h04c0afkgpl`
- - TON: `UQCZ3MiwyYHXftPItMMzJRYRiKHugr16jFMq2nfOQOOoemLy`
- - Bybit ID: `165292278`
+- انتقال بانکی:
+ - :credit_card: کارت MIR: `2204320436318077`
+ - :credit_card: کارت MasterCard: `5395452209474530`
+- کیف پول الکترونیکی:
+ - :moneybag: YooMoney: `4100116897060652`
+ - :moneybag: PayPal: `vasiljevdenisx@gmail.com`
+- رمزارز:
+ - :coin: USDT | ETH (ERC20 | BEP20): `0x6fe140040f6Cdc1E1Ff2136cd1d60C0165809463`
+ - :coin: USDT | TRX (TRC20): `TEWxXmJxvkAmhshp7E61XJGHB3VyM9hNAb`
+ - :coin: Bitcoin: `bc1qctntwncsv2yn02x2vgnkrqm00c4h04c0afkgpl`
+ - :coin: TON: `UQCZ3MiwyYHXftPItMMzJRYRiKHugr16jFMq2nfOQOOoemLy`
+ - :coin: Bybit ID: `165292278`
## توضیحات
-هدف اصلی این ابزار این است که ترافیک شما یکسان به نظر نرسد. ربات در فواصل زمانی مشخص 10 اتصال با پارامترهای مختلف تولید میکند:
+هدف اصلی ابزار این است که ترافیک شما یکسان به نظر نرسد. 3DP-MANAGER در بازه زمانی تعیینشده، مجموعهای از اتصالها با پارامترهای متفاوت تولید میکند:
-- پروتکلها: `vless`, `vmess`, `shadowsocks`, `trojan`
-- پورتها: `443`, `8443` (ثابت) و پورتهای تصادفی در بازه `10000-60000`
-- لایه انتقال: `tcp`, `websocket`, `grpc`, `xhttp`
-- SNI از لیست سفید دامنهها گرفته میشود (`whitelist`); میتوانید از لیست خود استفاده کنید.
+- پروتکلها: `vless`, `vmess`, `shadowsocks`, `hysteria2`, `trojan`;
+- پورتها: `443` و `8443` بهصورت ثابت، و پورتهای تصادفی از بازه `10000-60000`;
+- transport: `tcp`, `websocket`, `grpc`, `xhttp`;
+- SNI از whitelist دامنهها انتخاب میشود یا بهصورت دستی تنظیم میگردد؛
+- نود، relay server، پورت و پرچم را میتوان برای هر inbound جداگانه تنظیم کرد.
-تمام اتصالات در یک اشتراک با URL ثابت تجمیع میشوند. ربات با پنل `3x-ui` از طریق API باز کار میکند و بهطور مستقیم در عملکرد داخلی پنل دخالت نمیکند.
+همه اتصالها در یک اشتراک با URL ثابت ترکیب میشوند. 3DP-MANAGER با پنل `3x-ui` از طریق API عمومی آن کار میکند و مستقیماً در بخش داخلی پنل دخالت نمیکند.
-هدف ثانویه افزایش پایداری اتصال است: مشتری 10 گزینه اتصال دریافت میکند و میتواند هر کدام را انتخاب کند.
+هدف دوم پایداری اتصال است: کلاینت چند گزینه اتصال دریافت میکند و میتواند هرکدام را انتخاب کند. در یک اشتراک میتوان inboundهای مربوط به نودهای مختلف، relay serverها و لینکهای خارجی آماده را بهعنوان اتصال custom قرار داد.
-علاوه بر این، ربات میتواند در طرحهای آبشاری استفاده شود. سرویس فورواردینگ بهطور خودکار بازنشانی اشتراک و ترافیک به سرور اصلی را تنظیم میکند.
+همچنین 3DP-MANAGER را میتوان در سناریوی زنجیرهای استفاده کرد. سرویس forwarding، هدایت اشتراک و ترافیک را به نود انتخابشده پیکربندی میکند.
توصیهها:
- برای اشتراک از HTTPS استفاده کنید (دامنه + گواهی SSL).
-- فاصله تولید را ≥ 10 دقیقه در نظر بگیرید؛ برای پایداری توصیه میشود یکبار در روز (1440 دقیقه) تنظیم شود.
-- در کلاینت بهروزرسانی خودکار را بیشتر تنظیم کنید (مثلاً هر ساعت) تا همگامسازی با سرور انجام شود.
+- فاصله تولید را حداقل 10 دقیقه تنظیم کنید؛ برای پایداری، یکبار در روز (1440 دقیقه) توصیه میشود.
+- در کلاینت، بهروزرسانی خودکار را دفعات بیشتری تنظیم کنید، مثلاً هر ساعت، تا با سرور همگام بماند.
## قابلیتها
-- تولید 10 اتصال متنوع
-- تشکیل یک اشتراک یکپارچه با URL ثابت
-- پشتیبانی از دامنههای `whitelist` سفارشی
-- پیکربندی خودکار فورواردینگ ترافیک (اختیاری)
+- تولید اتصالهای متنوع برای یک یا چند اشتراک
+- ساخت اشتراک یکپارچه با URL ثابت
+- مدیریت چندین نود `3x-ui`
+- پشتیبانی از احراز هویت نود با login/password یا token
+- تشخیص خودکار IP، کشور و پرچم نود از URL پنل
+- انتخاب نود، relay server، پرچم، SNI و پورت برای هر inbound
+- پشتیبانی از پورت ثابت یا مقدار `random`
+- پشتیبانی از اتصالهای custom: افزودن لینک خارجی آماده به اشتراک
+- امکان تعیین مسیرهای جداگانه `fullchain.pem` و `privkey.pem` برای Hysteria2 UDP
+- پشتیبانی از `whitelist` دامنه سفارشی
+- تنظیم خودکار relay forwarding بهصورت اختیاری
+- نصب Web UI از طریق HTTP یا HTTPS: Let's Encrypt، self-signed یا گواهیهای خودتان
## نیازمندیها
-- Ubuntu 20.04 یا بالاتر
-- پنل `3x-ui`
+- Ubuntu 20.04 یا جدیدتر، Debian 12.11 یا جدیدتر
+- پنل `3x-ui` نسخه v2.8.4 یا جدیدتر
+- دسترسی root روی سرور
+- Docker و Docker Compose؛ اگر نصب نباشند، اسکریپت نصب تلاش میکند آنها را خودکار نصب کند
- دامنه + گواهی SSL (اختیاری)
---
## نصب
-برای نصب پروژه در سرور، دستور زیر را اجرا کنید:
+پنل مدیریتی `3x-ui` باید از قبل نصب شده باشد. میتوانید آن را با این دستور نصب کنید: `bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)`
+
+پروژه را روی سرور نصب کنید:
```bash
bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/install.sh)
```
-توضیح کوتاه: اسکریپت نصب را اجرا کرده و کانتینرها و سرویسها را راهاندازی میکند.
+در زمان نصب، اسکریپت:
+
+- سیستمعامل، دسترسی root، Docker و Docker Compose را بررسی میکند؛
+- اگر RAM کم باشد و swap وجود نداشته باشد، یک فایل swap با حجم 2 GB میسازد؛
+- حالت دسترسی Web UI را میپرسد:
+ - HTTPS با Let's Encrypt؛
+ - HTTPS با گواهی self-signed؛
+ - HTTPS با گواهیهای خودتان؛
+ - HTTP بدون رمزنگاری؛
+- یک پورت تصادفی برای Web UI ایجاد میکند؛
+- login و password مدیر را ایجاد میکند؛
+- کانتینرهای `postgres`, `backend` و `frontend` را اجرا میکند.
+
+پس از پایان نصب، URL، login و password در ترمینال نمایش داده میشود. بلافاصله password را در تنظیمات 3DP-MANAGER تغییر دهید.
+
+توضیح کوتاه: اسکریپت نصب تعاملی را اجرا میکند، محیط را آماده میسازد و کانتینرهای پروژه را راهاندازی میکند.
## بهروزرسانی
-برای بهروزرسانی به آخرین نسخه:
+بهروزرسانی به آخرین نسخه:
```bash
bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/update.sh)
```
-توضیح کوتاه: تغییرات جدید را میکشد و کانتینرها را ریاستارت میکند.
+توضیح کوتاه: آخرین تغییرات را دریافت میکند، اصلاحات سازگار پیکربندی را اعمال میکند، کانتینرها را بهروزرسانی کرده و سرویس را راهاندازی مجدد میکند.
## حذف
-برای حذف کامل سرویس:
+حذف کامل سرویس:
```bash
bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/delete.sh)
```
-توضیح کوتاه: کانتینرها و فایلهای پیکربندی را حذف میکند و سیستم را به وضعیت پیش از نصب برمیگرداند.
+توضیح کوتاه: کانتینرها و فایلهای پیکربندی را حذف میکند و سیستم را به وضعیت پیش از نصب بازمیگرداند.
---
-## نصب سرویس فورواردینگ (forwarding)
+## اولین ورود
-سرویس فورواردینگ امکان پراکسی کردن پورتهای ورودی از سرور واسط به سرور اصلی را فراهم میکند.
+login و password مدیر در پایان نصب نمایش داده میشود. برای مشاهده دوباره:
```bash
-bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/forwarding_install.sh)
+grep -E "ADMIN_LOGIN|ADMIN_PASSWORD" /opt/3dp-manager/docker-compose.yml | sed 's/^[ \t]*//; s/^- //'
```
-توضیح کوتاه: قوانین فورواردینگ را اضافه کرده و سرویسی برای بهروزرسانی اشتراک ایجاد میکند.
+Web UI در آدرسی که نصبکننده نمایش داده قابل دسترسی است، برای مثال:
-## حذف فورواردینگ
+- `https://example.com:PORT` برای HTTPS؛
+- `http://SERVER_IP:PORT` برای HTTP.
+
+اگر `ufw` فعال باشد، نصبکننده بازههای پورت کاری inbound را برای TCP/UDP باز میکند: `443`, `8443` و `10000-60000`.
+
+---
+
+## نودها
+
+بخش **نودها** امکان اتصال یک یا چند پنل `3x-ui` را فراهم میکند.
+
+برای هر نود مشخص میشود:
+
+- نام؛
+- URL پنل `3x-ui`؛
+- IP نود؛
+- پرچم/کشور؛
+- نوع احراز هویت: `password` یا `token`؛
+- اینکه نود اصلی است یا خیر.
+
+پس از وارد کردن URL، برنامه تلاش میکند IP و کشور نود را خودکار تشخیص دهد. پرچم در نام اتصالها داخل اشتراک استفاده میشود تا تشخیص سرورها در کلاینت سادهتر باشد.
+
+> [!NOTE]
+> نود اصلی بهعنوان مقدار پیشفرض برای اشتراکها، inboundها و relay serverها استفاده میشود.
+
+---
+
+## اشتراکها
+
+بخش **اشتراکها** URLهای ثابت اشتراک و ترکیب inboundها را مدیریت میکند.
+
+برای هر inbound میتوانید تنظیم کنید:
+
+- نوع اتصال؛
+- نود؛
+- relay server؛
+- پرچم؛
+- پورت: عدد مشخص از `1-65535` یا `random`؛
+- SNI: دامنه مشخص یا `random`؛
+- لینک خارجی آماده برای نوع `custom`.
+
+برای `hysteria2-udp` میتوانید مسیرهای جداگانه گواهی و کلید را مشخص کنید:
+
+- `certificateFile`، برای مثال `/root/cert/example.com/fullchain.pem`;
+- `keyFile`، برای مثال `/root/cert/example.com/privkey.pem`.
+
+اگر پورت یا SNI برابر `random` باشد، مقدار هنگام روتیشن از بازه موجود یا whitelist دامنهها انتخاب میشود.
+
+---
+
+## نصب سرویس forwarding
+
+> [!WARNING]
+> سرویس forwarding روی سرور واسط اجرا میشود
+
+سرویس forwarding پورتهای ورودی سرور واسط را به نود انتخابشده proxy میکند. در Web UI این سرورها در بخش **Relay serverها** تنظیم میشوند و میتوانند به یک نود مشخص متصل شوند.
+
+Relay server را میتوان با IP یا دامنه اضافه کرد. اگر دامنه وارد شود، backend تلاش میکند IP آن را خودکار resolve کند.
+
+برای نصب دستی forwarding روی سرور واسط، `IP_ADDRESS` را با IP نود اصلی جایگزین کنید:
+
+```bash
+sudo ORIGIN_IP="IP_ADDRESS" bash -c "$(curl -sSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/forwarding_install.sh)"
+```
+
+توضیح کوتاه: قوانین forwarding را اضافه میکند.
+
+## حذف forwarding
```bash
bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/forwarding_delete.sh)
```
-توضیح کوتاه: قوانین را حذف کرده و سرویس فورواردینگ را غیرفعال میکند.
+توضیح کوتاه: قوانین forwarding را حذف میکند. پس از حذف، برای اعمال تغییرات firewall را با `ufw reload` بارگذاری مجدد کنید و سیستم را با `reboot` ریاستارت کنید.
---
-## نمایش URL اشتراک
+## جمعآوری دامنهها از چند اشتراک
-دستور برای نمایش URL اشتراک فعلی در محیط کانتینر:
-
-```bash
-cd /opt/3dp-manager && docker compose exec node env | grep SUB_URL | cut -d'=' -f2
-```
-
-توضیح کوتاه: URL ثابت اشتراک را نمایش میدهد که میتوان در کلاینتها استفاده کرد. هم در سرور اصلی و هم در سرور واسط کار میکند.
-
-## جمعآوری دامنهها از چندین اشتراک
-
-ابزار دامنهها را از اشتراکهای دارای چندین پیکربندی استخراج کرده و `whitelist` تولید میکند.
+این ابزار دامنهها را از اشتراکها استخراج میکند و برای generator یک `whitelist` میسازد.
```bash
node get_domains.js
```
-توضیح کوتاه: لینک چندین اشتراک را در اسکریپت قرار داده و فرمان را اجرا کنید — خروجی فهرست دامنهها خواهد بود. نیازمند `Node.js` است.
-
-## استفاده از whitelist شخصی
-
-1. فایلی با ساختار مشابه `whitelist.txt` آماده کنید.
-2. آن را به `my_whitelist.txt` تغییر نام دهید و در پوشه `/opt/3dp-manager/app` کپی کنید.
-
-```bash
-cd /opt/3dp-manager && docker cp ./app/my_whitelist.txt node:/app/my_whitelist.txt
-```
-
-توضیح کوتاه: فایل دامنه شما را به کانتینر برنامه اضافه میکند.
+توضیح کوتاه: لینک چند اشتراک را در اسکریپت اضافه کنید و دستور را اجرا کنید. خروجی، فهرست دامنهها خواهد بود. برای اجرای اسکریپت به `Node.js` نیاز است.
---
## نکات و محدودیتهای فعلی
-- لیست عمومی دامنهها برای همه ارائهدهندگان کار نمیکند؛ پیشنهاد میشود `whitelist` خود را تهیه و استفاده کنید.
+- فهرست عمومی دامنهها با همه ارائهدهندگان کار نمیکند، بنابراین توصیه میشود whitelist خودتان را بسازید و استفاده کنید.
---
## مشارکت
-از هرگونه مشارکت در توسعه پروژه خوشحال میشوم! روند ساده برای مشارکتکنندگان:
+از هرگونه مشارکت در توسعه پروژه استقبال میشود! روند ساده برای مشارکتکنندگان:
1. مخزن را در GitHub fork کنید.
-2. شاخهای با نام معنادار ایجاد کنید مانند `feature/add-README` یا `fix/whitelist-load`.
-3. تغییرات را اعمال کرده و یک پیام کوتاه در کامیت اضافه کنید.
+2. شاخهای با نام معنادار بسازید، مثل `feature/add-README` یا `fix/whitelist-load`.
+3. تغییرات را اعمال کنید و توضیح کوتاهی در commit بنویسید.
4. در صورت وجود، بررسیهای محلی را اجرا کنید.
-5. شاخه را به fork خود پوش کرده و Pull Request ایجاد کنید.
+5. شاخه را به fork خود push کنید و در مخزن اصلی Pull Request بسازید.
-نکات: تغییرات را در PR شرح دهید و مراحل تست را بنویسید. در صورت تغییرات بزرگ، آنها را به کامیتهای کوچک تقسیم کنید.
+نکته: تغییرات، هدف و مراحل تست را در PR توضیح دهید. اگر تغییرات بزرگ هستند، آنها را به commitهای کوچک تقسیم کنید.
---
-## بحث
+## گفتگو
- تلگرام: [@denpiligrim_web](https://t.me/denpiligrim_web)
-- Issues
\ No newline at end of file
+- بخش Issues در همین مخزن
diff --git a/README_TK.md b/README_TK.md
index 25ff2eb..b49e66b 100644
--- a/README_TK.md
+++ b/README_TK.md
@@ -2,154 +2,243 @@
- [](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
-Bu gural `3x-ui` paneli üçin awtomatiki ýagdaýda inbound baglanyşlaryny döredýär, birleşdirilen abunany (subscription) döretmäge we aragatnaşyk serwerinden esasy servere trafigiň ugradylyşyny sazlamaga kömek edýär.
+[3x-ui](https://github.com/MHSanaei/3x-ui) paneli üçin inbound-lary awtomatiki döretmäge, birleşdirilen abunalary düzmäge, birnäçe 3x-ui node-ny dolandyrmaga we aralyk serwerlerden esasy serwerlere relay forwarding sazlamaga niýetlenen gural. 2.0.0 wersiýadan bäri taslamada grafiki interfeýs we ýönekeý ulanyjy sazlamalary bar.
**Taslamany goldaň**
-- Töleg / goşant maglumatlary:
- - MIR kart: `2204320436318077`
- - MasterCard: `5395452209474530`
- - PayPal: `vasiljevdenisx@gmail.com`
- - USDT | ETH (ERC20 | BEP20): `0x6fe140040f6Cdc1E1Ff2136cd1d60C0165809463`
- - USDT | TRX (TRC20): `TEWxXmJxvkAmhshp7E61XJGHB3VyM9hNAb`
- - Bitcoin: `bc1qctntwncsv2yn02x2vgnkrqm00c4h04c0afkgpl`
- - TON: `UQCZ3MiwyYHXftPItMMzJRYRiKHugr16jFMq2nfOQOOoemLy`
- - Bybit ID: `165292278`
+- Bank geçirimi:
+ - :credit_card: MIR kart: `2204320436318077`
+ - :credit_card: MasterCard: `5395452209474530`
+- Elektron gapjyk:
+ - :moneybag: YooMoney: `4100116897060652`
+ - :moneybag: PayPal: `vasiljevdenisx@gmail.com`
+- Kriptowalýuta:
+ - :coin: USDT | ETH (ERC20 | BEP20): `0x6fe140040f6Cdc1E1Ff2136cd1d60C0165809463`
+ - :coin: USDT | TRX (TRC20): `TEWxXmJxvkAmhshp7E61XJGHB3VyM9hNAb`
+ - :coin: Bitcoin: `bc1qctntwncsv2yn02x2vgnkrqm00c4h04c0afkgpl`
+ - :coin: TON: `UQCZ3MiwyYHXftPItMMzJRYRiKHugr16jFMq2nfOQOOoemLy`
+ - :coin: Bybit ID: `165292278`
## Düşündiriş
-Esasy maksady trafigiňizi birmeňzeş bolmaz ýaly etmekdir. Bot bellenen aralykda dürli parametrler bilen 10 sany baglanyşygy döredýär:
+Guralyň esasy maksady trafigiňiziň birmeňzeş görünmezligini gazanmakdyr. 3DP-MANAGER bellenen aralykda dürli parametrli baglanyşyklar toplumyny döredýär:
-- Protokollar: `vless`, `vmess`, `shadowsocks`, `trojan`;
-- Portlar: `443`, `8443` (hemişelik) we `10000-60000` aralygyndan tötänleýin portlar;
-- Transport: `tcp`, `websocket`, `grpc`, `xhttp`;
-- SNI `whitelist` domen sanawyndan alynýar; öz sanawyňyzy hem ulanyp bilersiňiz.
+- protokollar: `vless`, `vmess`, `shadowsocks`, `hysteria2`, `trojan`;
+- portlar: hemişelik `443`, `8443` we `10000-60000` aralygyndaky tötänleýin portlar;
+- transport: `tcp`, `websocket`, `grpc`, `xhttp`;
+- SNI domen whitelist-den alynýar ýa-da el bilen berilýär;
+- her inbound üçin node, relay serwer, port we baýdak aýratyn sazlanýar.
-Ähli döredilen baglanyşyklary biri-birine birleşdirip, statik URL bilen bir abuna döredilýär. Bot `3x-ui` paneliniň açyk API-sini ulanýar we paneliň içki konfigurasiýasyna gönüden-göni aralaşmaýar.
+Ähli baglanyşyklar statik URL-li bir abuna birleşdirilýär. 3DP-MANAGER `3x-ui` paneli bilen onuň açyk API-si arkaly işleýär we paneliň içki işine göni gatyşmaýar.
-Ikinji maksady baglanyşygyň durnuklylygyny ýokarlandyrmakdyr: müşderi 10 dürli baglanyşyk opsiýasyny alýar we islese haýsyny islese saýlap bilýär.
+Ikinji maksat baglanyşygyň durnuklylygydyr: müşderi birnäçe baglanyşyk wariantyny alýar we islänini saýlap bilýär. Bir abuna dürli node-lardan inbound-lary, relay serwerleri we custom baglanyşyk hökmünde taýýar daşarky linkleri goşup bolýar.
-Göçürme (forwarding) hyzmaty bilen birleşdirilende, bot abonnament we trafik diňe esasy servere ugradylyşyny awtomatiki sazlar.
+Mundan başga-da, 3DP-MANAGER kaskadly shemada ulanylyp bilner. Forwarding hyzmaty abunany we trafigi saýlanan node-a ugrukdyrmagy sazlaýar.
-Maslahatchylyklar:
+Maslahatlar:
-- Abuna üçin HTTPS ulanyň (domain + SSL şahadatnamasy).
-- Dörediş aralygyny ≥ 10 minut goýuň; durnuklylyk üçin her gün bir gezek (1440 minut) maslahat berilýär.
-- Müşderide awtomatiki täzelenmäni ýygylaşdyryň (meselem, her sagat) — serwera sazlaşyklylyk üçin.
+- Abuna üçin HTTPS ulanyň (domen + SSL şahadatnamasy).
+- Döretmek aralygyny azyndan 10 minut goýuň; durnuklylyk üçin günde bir gezek (1440 minut) maslahat berilýär.
+- Müşderide awtomatiki täzelenmäni ýygy goýuň, meselem her sagat, serwer bilen sazlaşyk saklansyn.
-## Imkançylyklar
+## Mümkinçilikler
-- 10 dürli baglanyşygy öndürýär
-- Statik URL bilen bir abuna döredýär
-- Öz `whitelist` domenleriňizi goldaýar
-- Trafigiň awtomatik forwarding sazlamalary (islege bagly)
+- Bir ýa-da birnäçe abuna üçin dürli baglanyşyklar döredýär
+- Statik URL bilen birleşdirilen abuna döredýär
+- Birnäçe `3x-ui` node-ny dolandyrýar
+- Node autentifikasiýasyny login/password ýa-da token arkaly goldaýar
+- Panel URL-den node IP-sini, ýurduny we baýdagyny awtomatiki kesgitleýär
+- Her inbound üçin node, relay serwer, baýdak, SNI we port saýlamaga mümkinçilik berýär
+- Hemişelik porty ýa-da `random` bahasyny goldaýar
+- Custom baglanyşyklary goldaýar: taýýar daşarky linki abuna goşup bolýar
+- Hysteria2 UDP üçin `fullchain.pem` we `privkey.pem` ýollaryny aýratyn görkezmäge mümkinçilik berýär
+- Öz domen `whitelist` sanawyňyzy goldaýar
+- Relay forwarding-i awtomatiki sazlamak mümkinçiligi bar
+- Web UI HTTP ýa-da HTTPS arkaly gurnalýar: Let's Encrypt, self-signed ýa-da öz şahadatnamalaryňyz
## Talaplar
-- Ubuntu 20.04 ýa-da täze
-- `3x-ui` paneli
-- Domain + SSL şahadatnamasy (islege bagly)
+- Ubuntu 20.04 ýa-da täze, Debian 12.11 ýa-da täze
+- `3x-ui` paneli v2.8.4 ýa-da täze
+- Serwerde root elýeterliligi
+- Docker we Docker Compose; ýok bolsa, gurnama skripti olary awtomatiki gurnamaga synanyşar
+- Domen + SSL şahadatnamasy (islege bagly)
---
## Gurnama
-Taslamany serwera gurnamak üçin şu komandany ýerine ýetiriň:
+`3x-ui` dolandyryş paneli öňünden gurnalan bolmaly. Ony şu komanda bilen gurnap bilersiňiz: `bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)`
+
+Taslamany serwere gurnaň:
```bash
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.
+Gurnama wagtynda skript:
-## Täzelik
+- OS, root elýeterliligi, Docker we Docker Compose-y barlaýar;
+- RAM az we swap ýok bolsa, 2 GB swap faýl döredýär;
+- Web UI elýeterlilik görnüşini saýladýar:
+ - Let's Encrypt arkaly HTTPS;
+ - self-signed şahadatnama bilen HTTPS;
+ - öz şahadatnamalaryňyz bilen HTTPS;
+ - şifrlemesiz HTTP;
+- Web UI üçin tötänleýin port döredýär;
+- administrator login we password döredýär;
+- `postgres`, `backend` we `frontend` konteýnerlerini işe goýberýär.
-Soňky wersiýa çenli täzeläň:
+Gurnama tamamlanandan soň terminalda URL, login we password görkeziler. Password-y derrew 3DP-MANAGER sazlamalarynda üýtgediň.
+
+Gysgaça: interaktiw gurnama skriptini işledýär, gurşawy taýýarlaýar we taslamanyň konteýnerlerini ýerleşdirýär.
+
+## Täzelemek
+
+Soňky wersiýa çenli täzelemek:
```bash
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: soňky üýtgeşmeleri alýar, laýyk konfigurasiýa düzedişlerini ulanýar, konteýnerleri täzeleýär we hyzmaty gaýtadan işledýär.
## Pozmak
-Hyzmaty doly pozmak üçin:
+Hyzmaty doly pozmak:
```bash
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 pozýar, ulgamy gurnamadan öňki ýagdaýa getirýär.
---
-## Forwarding hyzmatyny gurnamak
+## Ilkinji Giriş
-Forwarding hyzmaty aragatnaşyk serwerinden esasy servere gelýän portlary proxy arkaly geçirýär.
+Administrator login we password gurnamanyň ahyrynda görkezilýär. Olary gaýtadan görmek üçin:
```bash
-bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/forwarding_install.sh)
+grep -E "ADMIN_LOGIN|ADMIN_PASSWORD" /opt/3dp-manager/docker-compose.yml | sed 's/^[ \t]*//; s/^- //'
```
-## Forwarding-i pozmak
+Web UI gurnawçynyň görkezen salgysy boýunça elýeterlidir, mysal üçin:
+
+- HTTPS üçin `https://example.com:PORT`;
+- HTTP üçin `http://SERVER_IP:PORT`.
+
+Eger `ufw` işjeň bolsa, gurnawçy inbound üçin iş portlaryny açýar: TCP/UDP üçin `443`, `8443` we `10000-60000`.
+
+---
+
+## Node-lar
+
+**Node-lar** bölümi bir ýa-da birnäçe `3x-ui` panelini birikdirmäge mümkinçilik berýär.
+
+Her node üçin görkezilýär:
+
+- ady;
+- `3x-ui` panel URL-i;
+- node IP-si;
+- baýdak/ýurt;
+- autentifikasiýa görnüşi: `password` ýa-da `token`;
+- esasy node bolup durýandygy.
+
+URL girizilenden soň programma node IP-sini we ýurduny awtomatiki kesgitlemäge synanyşýar. Baýdak abunanyň içindäki baglanyşyk atlarynda ulanylýar, müşderide serwerleri tapawutlandyrmak aňsat bolýar.
+
+> [!NOTE]
+> Esasy node abunalar, inbound-lar we relay serwerler üçin deslapky baha hökmünde ulanylýar.
+
+---
+
+## Abunalar
+
+**Abunalar** bölümi statik abuna URL-lerini we inbound düzümini dolandyrýar.
+
+Her inbound üçin sazlap bolýar:
+
+- baglanyşyk görnüşi;
+- node;
+- relay serwer;
+- baýdak;
+- port: `1-65535` aralygyndaky anyk san ýa-da `random`;
+- SNI: anyk domen ýa-da `random`;
+- `custom` görnüşi üçin taýýar daşarky link.
+
+`hysteria2-udp` üçin şahadatnama we açar ýollaryny aýratyn görkezmek bolýar:
+
+- `certificateFile`, mysal üçin `/root/cert/example.com/fullchain.pem`;
+- `keyFile`, mysal üçin `/root/cert/example.com/privkey.pem`.
+
+Port ýa-da SNI `random` bolsa, baha rotasiýa wagtynda elýeterli aralykdan ýa-da domen whitelist-den saýlanar.
+
+---
+
+## Forwarding Hyzmatyny Gurnamak
+
+> [!WARNING]
+> Forwarding hyzmaty aralyk serwerde işleýär
+
+Forwarding hyzmaty aralyk serweriň gelýän portlaryny saýlanan node-a proxy edýär. Web UI-de şeýle serwerler **Relay serwerler** bölüminde sazlanýar we belli bir node bilen baglanyşdyrylyp bilýär.
+
+Relay serwer IP ýa-da domen boýunça goşulyp bilner. Domen görkezilse, backend onuň IP-sini awtomatiki çözmäge synanyşar.
+
+Aralyk serwerde forwarding-i el bilen gurnamak üçin `IP_ADDRESS` ýerine esasy node IP-sini goýuň:
+
+```bash
+sudo ORIGIN_IP="IP_ADDRESS" bash -c "$(curl -sSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/forwarding_install.sh)"
+```
+
+Gysgaça: forwarding düzgünlerini goşýar.
+
+## Forwarding-i Pozmak
```bash
bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/forwarding_delete.sh)
```
+Gysgaça: forwarding düzgünlerini pozýar. Pozlandan soň üýtgeşmeler güýje girmegi üçin firewall-y `ufw reload` bilen täzeden ýükläň we ulgamy `reboot` bilen täzeden işlediň.
+
---
-## Abuna URL-ni görkezmek
+## Multi-Abunalardan Domenleri Toplamak
-Konteýner gurşawynda häzirki abuna URL-ni görkezýän komanda:
-
-```bash
-cd /opt/3dp-manager && docker compose exec node env | grep SUB_URL | cut -d'=' -f2
-```
-
-## Multi-abunalardan domenleri toplamak
-
-Gural birden köp konfigurasiýa içeren abunalardan domenleri çykaryp `whitelist` düzýär.
+Gural abunalardan domenleri çykaryp, generator üçin `whitelist` düzýär.
```bash
node get_domains.js
```
-## Öz `whitelist`-iňizi ulanmak
-
-1. `whitelist.txt` bilen meňzeş struktura eýe faýl taýýarlaň.
-2. Ony `my_whitelist.txt` diýip atlandyryp `/opt/3dp-manager/app` içine göçüriň.
-
-```bash
-cd /opt/3dp-manager && docker cp ./app/my_whitelist.txt node:/app/my_whitelist.txt
-```
+Gysgaça: skripte multi-abuna linkini goşuň we komandany işlediň. Netijede domenleriň sanawy çykýar. Skript üçin `Node.js` gerek.
---
-## Bellikler we çäklendirmeler
+## Bellikler We Häzirki Çäklendirmeler
-- Umumy domen sanawy ähli üpjün edijilerde işlemeýär; öz `whitelist`-iňizi düzmegiňizi maslahat berýäris.
+- Umumy domen sanawy ähli prowaýderlerde işlemeýär, şonuň üçin öz whitelist sanawyňyzy taýýarlamak we ulanmak maslahat berilýär.
---
-## Goşanty
+## Goşant
-Proýekte goşant bermäge hoş geldiňiz! Goşant prosesi ýönekeý:
+Taslama goşantlaryňyza hoşal bolarys! Goşantçylar üçin ýönekeý proses:
-1. GitHub-da repo-ny fork ediň.
-2. Meni manly at bilen şahamça (branch) dörediň, meselem `feature/add-README` ýa-da `fix/whitelist-load`.
-3. Üýtgeşmeleri giriziň we gysga commit ýazmagy unutmaň.
-4. Lokal barlaglary işlediň (bardy bolsa).
-5. Şahamçany fork-a iteriň we Pull Request dörediň.
+1. GitHub-da repozitoriýany fork ediň.
+2. Manyly at bilen branch dörediň, mysal üçin `feature/add-README` ýa-da `fix/whitelist-load`.
+3. Üýtgeşmeleri giriziň we commit-de gysga düşündiriş ýazyň.
+4. Bar bolsa, lokal barlaglary işlediň.
+5. Branch-i öz fork-uňyza push ediň we esasy repozitoriýa Pull Request dörediň.
+
+Maslahat: PR-de üýtgeşmeleri, maksady we test ädimlerini ýazyň. Üýtgeşmeler uly bolsa, olary kiçi commit-lere bölüň.
---
-## Çeperdeşlik
+## Ara Alyp Maslahatlaşmak
- Telegram: [@denpiligrim_web](https://t.me/denpiligrim_web)
-- Issues
\ No newline at end of file
+- Şu repozitoriýanyň Issues bölümi
diff --git a/client/Dockerfile b/client/Dockerfile
index d61145f..b665e3d 100644
--- a/client/Dockerfile
+++ b/client/Dockerfile
@@ -10,7 +10,7 @@ COPY . .
ENV VITE_API_URL=/api
ENV VITE_LOG_LEVEL=debug
ENV VITE_SEND_LOGS_TO_BACKEND=true
-ENV VITE_APP_VERSION=2.1.0
+ENV VITE_APP_VERSION=2.2.0
RUN npm run build
diff --git a/client/index.html b/client/index.html
index c34b7e7..691b0d6 100644
--- a/client/index.html
+++ b/client/index.html
@@ -1,9 +1,12 @@
-
+
+
+
+
3DP-MANAGER
diff --git a/client/src/App.tsx b/client/src/App.tsx
index 64e1760..698250b 100644
--- a/client/src/App.tsx
+++ b/client/src/App.tsx
@@ -11,6 +11,7 @@ import NotFoundPage from './pages/NotFoundPage';
import { AxiosInterceptor } from './auth/AxiosInterceptor';
import PublicRoute from './auth/PublicRoute';
import TunnelsPage from './pages/TunnelsPage';
+import NodesPage from './pages/NodesPage';
function App() {
return (
@@ -31,6 +32,7 @@ function App() {
} />
} />
} />
+ } />
} />
} />
@@ -41,4 +43,4 @@ function App() {
);
}
-export default App;
\ No newline at end of file
+export default App;
diff --git a/client/src/components/Layout.tsx b/client/src/components/Layout.tsx
index 29b7269..d9c982a 100644
--- a/client/src/components/Layout.tsx
+++ b/client/src/components/Layout.tsx
@@ -1,9 +1,17 @@
import {
- Toolbar, Drawer, List, ListItem,
- ListItemButton, ListItemIcon, ListItemText, Box, useMediaQuery, useTheme
+ Box,
+ Drawer,
+ List,
+ ListItem,
+ ListItemButton,
+ ListItemIcon,
+ ListItemText,
+ Toolbar,
+ useMediaQuery,
+ useTheme,
} from '@mui/material';
-import { People, Settings, Dns, SwapHoriz } from '@mui/icons-material';
-import { useNavigate, useLocation, Outlet } from 'react-router-dom';
+import { Dns, Hub, People, Settings, SwapHoriz } from '@mui/icons-material';
+import { Outlet, useLocation, useNavigate } from 'react-router-dom';
import { useState } from 'react';
import Header from './Header';
@@ -13,6 +21,14 @@ import { useSecureConnection } from '../utils/useSecureConnection';
const drawerWidth = 240;
+const menuItems = [
+ { text: 'Подписки', icon: , path: '/' },
+ { text: 'Ноды', icon: , path: '/nodes' },
+ { text: 'Relay серверы', icon: , path: '/tunnels' },
+ { text: 'Домены', icon: , path: '/domains' },
+ { text: 'Настройки', icon: , path: '/settings' },
+];
+
export default function Layout() {
const navigate = useNavigate();
const location = useLocation();
@@ -22,23 +38,16 @@ export default function Layout() {
const { isSecure } = useSecureConnection();
const handleDrawerToggle = () => {
- setMobileOpen(!mobileOpen);
+ setMobileOpen((prev) => !prev);
};
- const menuItems = [
- { text: 'Подписки', icon: , path: '/' },
- { text: 'Домены', icon: , path: '/domains' },
- { text: 'Перенаправление', icon: , path: '/tunnels' },
- { text: 'Настройки', icon: , path: '/settings' },
- ];
-
const drawerContent = (
{menuItems.map((item) => (
- {
navigate(item.path);
@@ -56,11 +65,10 @@ export default function Layout() {
return (
- {/* Передаем функцию открытия в Header */}
-
@@ -92,4 +100,4 @@ export default function Layout() {
);
-}
\ No newline at end of file
+}
diff --git a/client/src/features/nodes/api.ts b/client/src/features/nodes/api.ts
new file mode 100644
index 0000000..21be4e6
--- /dev/null
+++ b/client/src/features/nodes/api.ts
@@ -0,0 +1,62 @@
+import api from '../../api';
+import type { NodePayload, NodeRecord } from '../../types/node';
+
+export const nodesApi = {
+ async list() {
+ const { data } = await api.get('/nodes');
+ return data;
+ },
+
+ async create(payload: NodePayload) {
+ const { data } = await api.post('/nodes', payload);
+ return data;
+ },
+
+ async update(id: string, payload: Partial) {
+ const { data } = await api.put(`/nodes/${id}`, payload);
+ return data;
+ },
+
+ async remove(id: string) {
+ const { data } = await api.delete<{ success: boolean }>(`/nodes/${id}`);
+ return data;
+ },
+
+ async setMain(id: string) {
+ const { data } = await api.post(`/nodes/${id}/main`);
+ return data;
+ },
+
+ async check(id: string) {
+ const { data } = await api.post<{ success: boolean; version?: string }>(
+ `/nodes/${id}/check`,
+ );
+ return data;
+ },
+
+ async checkPayload(payload: NodePayload) {
+ const { data } = await api.post<{ success: boolean; version?: string }>(
+ '/nodes/check',
+ payload,
+ );
+ return data;
+ },
+
+ async detectLocation(url: string) {
+ const { data } = await api.post<{
+ ip?: string;
+ host?: string;
+ flag?: string;
+ country?: string;
+ countryCode?: string;
+ }>('/nodes/detect-location', { url });
+ return data;
+ },
+
+ async syncFromMain() {
+ const { data } = await api.post<{ success: boolean; count: number }>(
+ '/nodes/sync/main',
+ );
+ return data;
+ },
+};
diff --git a/client/src/index.css b/client/src/index.css
index 108ad08..55729c6 100644
--- a/client/src/index.css
+++ b/client/src/index.css
@@ -1,3 +1,16 @@
+html {
+ font-family: "Inter", "Roboto", "Helvetica", "Arial", sans-serif;
+ font-synthesis: none;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+body {
+ margin: 0;
+ font-family: inherit;
+}
+
.animated-container {
background: linear-gradient(-45deg, #111827, #15122c, #24243e, #0B0F19);
background-size: 400% 400%;
@@ -32,4 +45,4 @@
input:autofill {
background: transparent;
-}
\ No newline at end of file
+}
diff --git a/client/src/main.tsx b/client/src/main.tsx
index bc6bfb9..05194bc 100644
--- a/client/src/main.tsx
+++ b/client/src/main.tsx
@@ -4,6 +4,7 @@ import './index.css'
import '@fontsource/inter/300.css';
import '@fontsource/inter/400.css';
import '@fontsource/inter/500.css';
+import '@fontsource/inter/600.css';
import '@fontsource/inter/700.css';
import App from './App.tsx'
diff --git a/client/src/pages/DomainsPage.tsx b/client/src/pages/DomainsPage.tsx
index 6335498..70d5e62 100644
--- a/client/src/pages/DomainsPage.tsx
+++ b/client/src/pages/DomainsPage.tsx
@@ -50,6 +50,7 @@ export default function DomainsPage() {
const [domains, setDomains] = useState([]);
const [newDomain, setNewDomain] = useState('');
const fileInputRef = useRef(null);
+ const emptyDomainsNotified = useRef(false);
const [totalCount, setTotalCount] = useState(0);
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
@@ -203,6 +204,13 @@ export default function DomainsPage() {
setDomains(data.data);
setTotalCount(data.total);
+ if (data.total === 0 && !emptyDomainsNotified.current) {
+ emptyDomainsNotified.current = true;
+ setSnackbar({ open: true, type: 'error', message: 'Создайте хотя бы один домен!' });
+ }
+ if (data.total > 0) {
+ emptyDomainsNotified.current = false;
+ }
Logger.debug(`Loaded ${data.data.length} domains (total: ${data.total})`, 'Domains');
} catch (error) {
Logger.error('Failed to load', 'Domains', error);
diff --git a/client/src/pages/NodesPage.tsx b/client/src/pages/NodesPage.tsx
new file mode 100644
index 0000000..361dfa0
--- /dev/null
+++ b/client/src/pages/NodesPage.tsx
@@ -0,0 +1,471 @@
+import { useCallback, useEffect, useState } from 'react';
+import {
+ Alert,
+ Box,
+ Button,
+ Chip,
+ CircularProgress,
+ Dialog,
+ DialogActions,
+ DialogContent,
+ DialogTitle,
+ FormControl,
+ FormHelperText,
+ IconButton,
+ InputLabel,
+ MenuItem,
+ Paper,
+ Select,
+ Snackbar,
+ Stack,
+ Switch,
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableRow,
+ TextField,
+ Typography,
+} from '@mui/material';
+import {
+ Add,
+ CheckCircle,
+ Delete,
+ Edit,
+ Star,
+ StarBorder,
+ Sync,
+} from '@mui/icons-material';
+import api from '../api';
+import { nodesApi } from '../features/nodes/api';
+import type { NodeAuthType, NodePayload, NodeRecord } from '../types/node';
+import { getApiErrorMessage } from '../utils/errorHandlers';
+import { FlagIcon, FlagOptionLabel } from '../utils/flags';
+
+const emptyForm: NodePayload = {
+ name: '',
+ url: '',
+ ip: '',
+ flag: '',
+ authType: 'token',
+ login: '',
+ password: '',
+ token: '',
+ isMain: false,
+};
+
+interface CountryOption {
+ name: string;
+ code: string;
+ emoji: string;
+}
+
+const isValidIp = (value?: string) =>
+ !!value &&
+ (/^(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}$/.test(value.trim()) ||
+ /^([0-9a-f]{1,4}:){2,7}[0-9a-f]{1,4}$/i.test(value.trim()));
+
+export default function NodesPage() {
+ const [nodes, setNodes] = useState([]);
+ const [open, setOpen] = useState(false);
+ const [editing, setEditing] = useState(null);
+ const [form, setForm] = useState(emptyForm);
+ const [checkingId, setCheckingId] = useState(null);
+ const [countries, setCountries] = useState([]);
+ const [detectingLocation, setDetectingLocation] = useState(false);
+ const [deleteTarget, setDeleteTarget] = useState(null);
+ const [formErrors, setFormErrors] = useState>>({});
+ const [message, setMessage] = useState({
+ open: false,
+ type: 'success' as 'success' | 'error',
+ text: '',
+ });
+
+ const loadNodes = useCallback(async () => {
+ const data = await nodesApi.list();
+ setNodes(Array.isArray(data) ? data : []);
+ }, []);
+
+ useEffect(() => {
+ loadNodes();
+ api.get('/settings/countries').then((res) => setCountries(Array.isArray(res.data) ? res.data : []));
+ }, [loadNodes]);
+
+ const openCreate = () => {
+ setEditing(null);
+ setForm(emptyForm);
+ setFormErrors({});
+ setOpen(true);
+ };
+
+ const openEdit = (node: NodeRecord) => {
+ setEditing(node);
+ setForm({
+ name: node.name,
+ url: node.url || '',
+ ip: node.ip || '',
+ flag: node.flag || '',
+ authType: node.authType,
+ login: node.login || '',
+ password: '',
+ token: '',
+ isMain: node.isMain,
+ version: node.version || '',
+ });
+ setFormErrors({});
+ setOpen(true);
+ };
+
+ const updateField = (key: K, value: NodePayload[K]) => {
+ setForm((prev) => ({ ...prev, [key]: value }));
+ setFormErrors((prev) => ({ ...prev, [key]: undefined }));
+ };
+
+ const validateForm = (requireSecrets = !editing) => {
+ const errors: Partial> = {};
+
+ if (!form.name.trim()) errors.name = 'Введите название ноды';
+ if (!form.url.trim()) errors.url = 'Введите URL панели 3x-ui';
+ if (!isValidIp(form.ip)) errors.ip = 'Введите корректный IP ноды';
+ if (!form.flag) errors.flag = 'Выберите флаг ноды';
+
+ if (form.authType === 'password') {
+ if (!form.login?.trim()) errors.login = 'Введите логин';
+ if (requireSecrets && !form.password?.trim()) errors.password = 'Введите пароль';
+ }
+
+ if (form.authType === 'token' && requireSecrets && !form.token?.trim()) {
+ errors.token = 'Введите токен';
+ }
+
+ setFormErrors(errors);
+ return Object.keys(errors).length === 0;
+ };
+
+ const detectNodeLocation = async () => {
+ const url = form.url.trim();
+ if (!url) return;
+
+ setDetectingLocation(true);
+ try {
+ const result = await nodesApi.detectLocation(url.replace(/\/+$/, ''));
+ setForm((prev) => ({
+ ...prev,
+ ip: result.ip || prev.ip,
+ flag: result.flag || prev.flag,
+ }));
+ if (result.country || result.ip) {
+ setMessage({
+ open: true,
+ type: 'success',
+ text: `Определено: ${result.country || 'страна неизвестна'}${result.ip ? `, IP ${result.ip}` : ''}`,
+ });
+ }
+ } catch {
+ setMessage({ open: true, type: 'error', text: 'Не удалось определить страну ноды' });
+ } finally {
+ setDetectingLocation(false);
+ }
+ };
+
+ const saveNode = async () => {
+ if (!validateForm(!editing)) {
+ setMessage({ open: true, type: 'error', text: 'Заполните обязательные поля' });
+ return;
+ }
+
+ const payload: NodePayload = {
+ ...form,
+ url: form.url.replace(/\/+$/, ''),
+ ip: form.ip || undefined,
+ flag: form.flag || undefined,
+ login: form.authType === 'password' ? form.login : undefined,
+ password: form.authType === 'password' && form.password ? form.password : undefined,
+ token: form.authType === 'token' && form.token ? form.token : undefined,
+ };
+
+ try {
+ if (editing) {
+ await nodesApi.update(editing.id, payload);
+ } else {
+ await nodesApi.create(payload);
+ }
+
+ setOpen(false);
+ setMessage({
+ open: true,
+ type: 'success',
+ text: editing ? 'Нода обновлена' : 'Нода добавлена',
+ });
+ loadNodes();
+ } catch (error: unknown) {
+ const text =
+ (error as { response?: { data?: { message?: string } } })?.response?.data?.message ||
+ 'Не удалось сохранить ноду';
+ setMessage({ open: true, type: 'error', text });
+ }
+ };
+
+ const checkFormConnection = async () => {
+ if (!validateForm(true)) {
+ setMessage({ open: true, type: 'error', text: 'Заполните обязательные поля для проверки подключения' });
+ return;
+ }
+
+ const result = await nodesApi.checkPayload({
+ ...form,
+ url: form.url.replace(/\/+$/, ''),
+ login: form.authType === 'password' ? form.login : undefined,
+ password: form.authType === 'password' ? form.password : undefined,
+ token: form.authType === 'token' ? form.token : undefined,
+ });
+
+ setMessage({
+ open: true,
+ type: result.success ? 'success' : 'error',
+ text: result.success ? 'Подключение успешно' : 'Не удалось подключиться',
+ });
+ };
+
+ const checkNode = async (node: NodeRecord) => {
+ setCheckingId(node.id);
+ try {
+ const result = await nodesApi.check(node.id);
+ setMessage({
+ open: true,
+ type: result.success ? 'success' : 'error',
+ text: result.success ? 'Подключение успешно' : 'Не удалось подключиться',
+ });
+ loadNodes();
+ } finally {
+ setCheckingId(null);
+ }
+ };
+
+ const syncNodes = async () => {
+ const result = await nodesApi.syncFromMain();
+ setMessage({
+ open: true,
+ type: 'success',
+ text: `Синхронизировано нод: ${result.count}`,
+ });
+ loadNodes();
+ };
+
+ const removeNode = async () => {
+ if (!deleteTarget) return;
+ try {
+ await nodesApi.remove(deleteTarget.id);
+ setDeleteTarget(null);
+ setMessage({ open: true, type: 'success', text: 'Нода удалена' });
+ loadNodes();
+ } catch (error) {
+ setMessage({
+ open: true,
+ type: 'error',
+ text: getApiErrorMessage(error, 'Ошибка удаления ноды'),
+ });
+ }
+ };
+
+ return (
+
+
+
+ Ноды
+
+
+
+ {/* } variant="outlined" onClick={syncNodes}>
+ Синхронизировать
+ */}
+ } variant="contained" onClick={openCreate}>
+ Добавить
+
+
+
+
+
+
+