Compare commits

...

1 Commits

+108 -52
View File
@@ -1,44 +1,70 @@
# Py-3XUI-multiserver # ZernProxy Manager (Py-3XUI-multiserver)
Объединение нескольких серверов 3x-UI в единую VPN-подписку. Soft-tariff система с веб-панелью, донатной оплатой и Happ-совместимостью. Объединение нескольких серверов 3x-UI в единую VPN-подписку. Soft-tariff система с веб-панелью, донатной оплатой, Happ и WDTT совместимостью.
## Architecture ## Architecture
``` ```
servers.conf ──→ 3x-UI сервера (API + sub URL) servers.conf ──→ 3x-UI сервера (API + sub URL + status URL)
settings.conf ──→ тарифы, платежи, админка settings.conf ──→ тарифы, платежи, админка, auto-propagate
users.db ───────→ пользователи (SQLite) users.db ───────→ пользователи (SQLite)
aggregator.py ──→ FastAPI сервер aggregator.py ──→ FastAPI сервер
/sub/{id} ────→ подписка для клиента (VLESS links) /sub/{id} ────→ подписка для клиента (VLESS / Trojan / VMess / Shadowsocks)
/admin/* ─────→ веб-панель управления /admin/* ─────→ веб-панель управления
/admin/api/propagate/{server} → синхронизация пользователей между серверами
/ ────────────→ статус-пейдж с health-check серверов
``` ```
**Soft-tariff**: пользователь создаётся на всех inbounds один раз, tier (free/test/paid) меняется локально в БД, без дёргания 3x-UI API. **Soft-tariff**: пользователь создаётся на всех inbounds один раз, tier (free/test/paid) меняется локально в БД, без дёргания 3x-UI API.
## Features ## Features
- Multi-server: объединение любого количества 3x-UI серверов - **Multi-server**: объединение любого количества 3x-UI серверов
- Multi-inbound: поддержка нескольких inbound на сервере - **Multi-inbound**: поддержка нескольких inbound на сервере (разные протоколы)
- Soft-tariff: free / test / paid tier без пересоздания клиентов - **Soft-tariff**: free / test / paid tier без пересоздания клиентов
- DonationAlerts: приём платежей (50₽ → test 7д, 150₽ → paid 30д, 990₽ → paid 365д) - **Propagate**: синхронизация пользователей между серверами по strict majority (>50%)
- Веб-панель: дашборд, CRUD пользователей, просмотр трафика, QR-коды - **Auto-propagate**: фоновая синхронизация по расписанию (по умолчанию 30 мин)
- Happ.su совместимость: hide-settings, sub-expire, announce через HTTP-хедеры - **DonationAlerts**: приём платежей с поиском донатера по ID/username
- In-memory кеш: ссылки кешируются на 7 дней, трафик — на 2 минуты - **Веб-панель**: дашборд со статистикой, CRUD пользователей, просмотр трафика, QR-коды
- ShortID ротация: автоматическая смена shortId на серверах каждые N часов - **Статус-пейдж**: публичная страница с health-check серверов (CPU/RAM/Disk/Net/I/O)
- MOTD: объявления из motd.txt или settings - **Happ.su / WDTT совместимость**: заголовки profile-title, sub-expire, announce, hide-settings
- **Абсолютный expire**: `tariff_end_at` (Unix timestamp) — дни считаются динамически, автопонижение tier при истечении
- **In-memory кеш**: ссылки кешируются на 7 дней, трафик — на 2 минуты
- **ShortID ротация**: автоматическая смена shortId на серверах каждые N часов
- **MOTD**: объявления из motd.txt или settings
- **Health scoring**: взвешенная оценка здоровья сервера (CPU 30%, RAM 25%, Disk 20%, Net 15%, I/O 10%)
- **Trojan support**: создание клиентов на Trojan инбаундах (password вместо uuid)
## wdtt (Zern-BlackOut) Compatibility
Проект полностью совместим с клиентом **Zern-BlackOut** (форк wdtt), доступным по адресу:
[https://git.swe.zernmc.ru/sasheg/Zern-BlackOut](https://git.swe.zernmc.ru/sasheg/Zern-BlackOut)
> **Важно:** Клиент wdtt был модифицирован для совместимости с данным агрегатором.
> Использование оригинального wdtt без модификаций может привести к некорректной работе.
> Репозиторий Zern-BlackOut находится в активной разработке для полной интеграции.
Поддерживаемые возможности:
- VLESS + XTLS Vision / Reality
- Trojan + HTTP + Reality
- Автообновление подписки
- Отображение expire-даты и лимитов трафика
- Hide-settings, announce, support-url через subscription-заголовки
## Quick Start ## Quick Start
```bash ```bash
pip install fastapi uvicorn httpx py3xui qrcode[pil] Pillow pip install fastapi uvicorn httpx py3xui qrcode[pil] Pillow
git clone https://github.com/SashegDev/Py-3XUI-multiserver.git git clone ssh://git@git.swe.zernmc.ru:2222/sasheg/Py-3XUI-multiserver.git
cd Py-3XUI-multiserver cd Py-3XUI-multiserver
# настроить servers.conf, settings.conf (см. ниже) # настроить servers.conf, settings.conf (см. ниже)
python3 aggregator.py python3 aggregator.py
``` ```
Для production: `screen -S aggregator python3 aggregator.py`
## Configuration ## Configuration
### servers.conf ### servers.conf
@@ -48,17 +74,27 @@ python3 aggregator.py
"servers": [ "servers": [
{ {
"name": "ru-1", "name": "ru-1",
"subscription_url": "https://panel.domain.com:2096", "subscription_url": "http://panel.domain.com:2096",
"api_base_url": "https://panel.domain.com:65431/PATH",
"country": "RU",
"sub_path": "/sub/{sub_id}", "sub_path": "/sub/{sub_id}",
"country": "RU",
"status_url": "http://domain.com:18765/status?secret=...",
"inbounds": [ "inbounds": [
{ {
"id": 1, "id": 1,
"name": "Reality", "name": "Reality",
"api_host": "https://panel.domain.com:65431/PATH", "api_host": "http://panel.domain.com:22881/PATH",
"api_user": "admin", "api_user": "admin",
"api_pass": "password" "api_pass": "password",
"is_free": true
},
{
"id": 2,
"name": "Trojan-Inbound",
"protocol": "trojan",
"api_host": "http://panel.domain.com:22881/PATH",
"api_user": "admin",
"api_pass": "password",
"is_free": false
} }
] ]
} }
@@ -70,14 +106,16 @@ python3 aggregator.py
|-------|------|-------------| |-------|------|-------------|
| `name` | string | Unique server name | | `name` | string | Unique server name |
| `subscription_url` | string | Base URL for subscription links | | `subscription_url` | string | Base URL for subscription links |
| `api_base_url` | string | API URL for traffic stats | | `sub_path` | string | Sub endpoint path (`/sub/{sub_id}`) |
| `country` | string | 2-letter country code (RU, DE, NL...) | | `country` | string | 2-letter country code (RU, DE, PL, SE...) |
| `sub_path` | string | Sub endpoint path on the server (`/sub/{sub_id}`) | | `status_url` | string | URL for server health data (3x-UI status endpoint) |
| `inbounds[].id` | int | Inbound ID in 3x-UI | | `inbounds[].id` | int | Inbound ID in 3x-UI |
| `inbounds[].name` | string | Display name | | `inbounds[].name` | string | Display name |
| `inbounds[].protocol` | string | *(optional)* protocol type (`trojan`) |
| `inbounds[].api_host` | string | 3x-UI API URL for this inbound | | `inbounds[].api_host` | string | 3x-UI API URL for this inbound |
| `inbounds[].api_user` | string | API login | | `inbounds[].api_user` | string | API login |
| `inbounds[].api_pass` | string | API password | | `inbounds[].api_pass` | string | API password |
| `inbounds[].is_free` | bool | Доступен ли inbound для free-тарифа |
### settings.conf ### settings.conf
@@ -93,32 +131,46 @@ python3 aggregator.py
"tiers": { "tiers": {
"free": { "free": {
"name": "Free", "name": "Free",
"servers": ["ru-1"],
"traffic_limit_gb": 0 "traffic_limit_gb": 0
}, },
"test": { "test": {
"name": "Test 7 days", "name": "Test",
"servers": ["ru-1", "nl-1"],
"traffic_limit_gb": 5, "traffic_limit_gb": 5,
"price": 50, "prices": {
"duration_days": 7 "test": 50
},
"days": {
"test": 7
}
}, },
"paid": { "paid": {
"name": "Premium", "name": "Premium",
"servers": ["ru-1", "nl-1", "de-1"], "traffic_limit_gb": 0,
"traffic_limit_gb": 0 "prices": {
"monthly": 150,
"yearly": 990
},
"days": {
"monthly": 30,
"yearly": 365
}
} }
}, },
"payments": { "payments": {
"donationalerts": { "donationalerts": {
"enabled": true, "enabled": true,
"token": "your_donationalerts_token", "api_token": "...",
"webhook_secret": "...",
"url": "https://www.donationalerts.com/r/you" "url": "https://www.donationalerts.com/r/you"
} }
}, },
"admin": { "admin": {
"username": "admin", "username": "admin",
"password": "secure_password" "password": "secure_password"
},
"auto_propagate": {
"enabled": true,
"interval_minutes": 30
} }
} }
``` ```
@@ -127,46 +179,48 @@ python3 aggregator.py
| Tier | Servers | Traffic | Price | Duration | | Tier | Servers | Traffic | Price | Duration |
|------|---------|---------|-------|----------| |------|---------|---------|-------|----------|
| free | subset marked `is_free: true` in config | unlimited (0 = ∞) | free | forever | | free | inbounds with `is_free: true` | unlimited (0 = ∞) | free | forever |
| test | all servers | 5 GB | 50₽ | 7 days | | test | all inbounds | 5 GB | 50₽ | 7 days |
| paid | all servers | unlimited | 150₽/30d, 990₽/365d | configurable | | paid | all inbounds | unlimited | 150₽/30d, 990₽/365d | configurable |
Free servers are defined in `servers.conf` per-server via `is_free: true`. ### Propagate
### MOTD При добавлении нового сервера или инбаунда пользователи НЕ создаются автоматически на нём.
Для синхронизации используется **strict majority**: пользователь добавляется на целевой сервер только если он существует на >50% других активных серверов.
Two ways: ```bash
1. `motd.txt` — plain text file in project root curl -X POST http://localhost:8000/admin/api/propagate/{server_name} -b "admin_token=..."
2. `settings.conf → announcement` — string (plain or base64) ```
В админ-панели кнопка 🔄 на каждом сервере. Auto-propagate запускается фоном каждые N минут.
## Endpoints ## Endpoints
| Path | Method | Description | | Path | Method | Description |
|------|--------|-------------| |------|--------|-------------|
| `/sub/{id}` | GET | Subscription (format: base64/json/raw) | | `/sub/{id}` | GET | Subscription (format: base64/json/raw) |
| `/sub/{id}` | GET (HTML accept) | Web page with QR and info | | `/sub/{id}` | GET (HTML) | Web page with QR and info |
| `/admin/login` | POST | Admin auth | | `/admin/login` | GET/POST | Admin auth |
| `/admin/users` | GET | User management panel | | `/admin/users` | GET | User management panel |
| `/admin/dashboard` | GET | Stats dashboard | | `/admin/dashboard` | GET | Stats dashboard with server health |
| `/admin/api/users` | POST | Create user | | `/admin/api/users` | POST | Create user |
| `/admin/api/users/update` | POST | Update user | | `/admin/api/users/update` | POST | Update user |
| `/admin/api/users/delete` | POST | Delete user | | `/admin/api/users/delete` | POST | Delete user (with 3x-UI) |
| `/admin/api/reload` | POST | Reload configs + clear cache | | `/admin/api/reload` | POST | Reload configs + clear cache |
| `/admin/api/propagate/{server}` | POST | Sync users to server |
| `/admin/api/rotate-shortids` | GET | Rotate short IDs on all servers | | `/admin/api/rotate-shortids` | GET | Rotate short IDs on all servers |
| `/` | GET | Landing page | | `/` | GET | Landing page with server status |
## DonationAlerts ## DonationAlerts
Polling-based (every ~2 seconds). Amount mapping: Polling-based (every ~2 seconds). Amount mapping from `settings.conf → tiers → prices`:
- 50₽ → test tier (7 days) - Any configured amount → corresponding tier + days
- 150₽ → paid tier (30 days) - Donor identified by `id` or `username` from donation message
- 990₽ → paid tier (365 days) - `tariff_end_at` extends dynamically (MAX of current end, now + days)
Other amounts are ignored. Donor identified by `id` (priority) or `username` from message parts. ## Happ.su / WDTT Support
## Happ.su Support Subscription response includes compatible headers:
Subscription response includes Happ-compatible headers:
- `Profile-Title`, `Profile-Update-Interval`, `Profile-Web-Page-Url` - `Profile-Title`, `Profile-Update-Interval`, `Profile-Web-Page-Url`
- `Subscription-Userinfo` (upload/download/total/expire) - `Subscription-Userinfo` (upload/download/total/expire)
- `Announce`, `Support-Url`, `Hide-Settings` - `Announce`, `Support-Url`, `Hide-Settings`
@@ -179,3 +233,5 @@ Subscription response includes Happ-compatible headers:
- **py3xui** — 3x-UI API client - **py3xui** — 3x-UI API client
- **httpx** — async HTTP for sub links - **httpx** — async HTTP for sub links
- **qrcode** — QR generation - **qrcode** — QR generation
- **Plus Jakarta Sans / JetBrains Mono** — UI typography
- **Glassmorphism** — design system