Initial release: soft-tariff multi-server VPN aggregator with Happ support
This commit is contained in:
@@ -0,0 +1,7 @@
|
|||||||
|
motd.txt
|
||||||
|
servers.conf
|
||||||
|
settings.conf
|
||||||
|
users.db
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
.env
|
||||||
@@ -0,0 +1,181 @@
|
|||||||
|
# Py-3XUI-multiserver
|
||||||
|
|
||||||
|
Объединение нескольких серверов 3x-UI в единую VPN-подписку. Soft-tariff система с веб-панелью, донатной оплатой и Happ-совместимостью.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
servers.conf ──→ 3x-UI сервера (API + sub URL)
|
||||||
|
settings.conf ──→ тарифы, платежи, админка
|
||||||
|
users.db ───────→ пользователи (SQLite)
|
||||||
|
↓
|
||||||
|
aggregator.py ──→ FastAPI сервер
|
||||||
|
↓
|
||||||
|
/sub/{id} ────→ подписка для клиента (VLESS links)
|
||||||
|
/admin/* ─────→ веб-панель управления
|
||||||
|
```
|
||||||
|
|
||||||
|
**Soft-tariff**: пользователь создаётся на всех inbounds один раз, tier (free/test/paid) меняется локально в БД, без дёргания 3x-UI API.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Multi-server: объединение любого количества 3x-UI серверов
|
||||||
|
- Multi-inbound: поддержка нескольких inbound на сервере
|
||||||
|
- Soft-tariff: free / test / paid tier без пересоздания клиентов
|
||||||
|
- DonationAlerts: приём платежей (50₽ → test 7д, 150₽ → paid 30д, 990₽ → paid 365д)
|
||||||
|
- Веб-панель: дашборд, CRUD пользователей, просмотр трафика, QR-коды
|
||||||
|
- Happ.su совместимость: hide-settings, sub-expire, announce через HTTP-хедеры
|
||||||
|
- In-memory кеш: ссылки кешируются на 7 дней, трафик — на 2 минуты
|
||||||
|
- ShortID ротация: автоматическая смена shortId на серверах каждые N часов
|
||||||
|
- MOTD: объявления из motd.txt или settings
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install fastapi uvicorn httpx py3xui qrcode[pil] Pillow
|
||||||
|
git clone https://github.com/SashegDev/Py-3XUI-multiserver.git
|
||||||
|
cd Py-3XUI-multiserver
|
||||||
|
# настроить servers.conf, settings.conf (см. ниже)
|
||||||
|
python3 aggregator.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### servers.conf
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"name": "ru-1",
|
||||||
|
"subscription_url": "https://panel.domain.com:2096",
|
||||||
|
"api_base_url": "https://panel.domain.com:65431/PATH",
|
||||||
|
"country": "RU",
|
||||||
|
"sub_path": "/sub/{sub_id}",
|
||||||
|
"inbounds": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "Reality",
|
||||||
|
"api_host": "https://panel.domain.com:65431/PATH",
|
||||||
|
"api_user": "admin",
|
||||||
|
"api_pass": "password"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `name` | string | Unique server name |
|
||||||
|
| `subscription_url` | string | Base URL for subscription links |
|
||||||
|
| `api_base_url` | string | API URL for traffic stats |
|
||||||
|
| `country` | string | 2-letter country code (RU, DE, NL...) |
|
||||||
|
| `sub_path` | string | Sub endpoint path on the server (`/sub/{sub_id}`) |
|
||||||
|
| `inbounds[].id` | int | Inbound ID in 3x-UI |
|
||||||
|
| `inbounds[].name` | string | Display name |
|
||||||
|
| `inbounds[].api_host` | string | 3x-UI API URL for this inbound |
|
||||||
|
| `inbounds[].api_user` | string | API login |
|
||||||
|
| `inbounds[].api_pass` | string | API password |
|
||||||
|
|
||||||
|
### settings.conf
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"general": {
|
||||||
|
"title": "MyVPN",
|
||||||
|
"host": "conn.example.com",
|
||||||
|
"support_url": "https://t.me/support"
|
||||||
|
},
|
||||||
|
"announcement": "base64 enc or plain text",
|
||||||
|
"shortid_rotation_hours": 11,
|
||||||
|
"tiers": {
|
||||||
|
"free": {
|
||||||
|
"name": "Free",
|
||||||
|
"servers": ["ru-1"],
|
||||||
|
"traffic_limit_gb": 0
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"name": "Test 7 days",
|
||||||
|
"servers": ["ru-1", "nl-1"],
|
||||||
|
"traffic_limit_gb": 5,
|
||||||
|
"price": 50,
|
||||||
|
"duration_days": 7
|
||||||
|
},
|
||||||
|
"paid": {
|
||||||
|
"name": "Premium",
|
||||||
|
"servers": ["ru-1", "nl-1", "de-1"],
|
||||||
|
"traffic_limit_gb": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"payments": {
|
||||||
|
"donationalerts": {
|
||||||
|
"enabled": true,
|
||||||
|
"token": "your_donationalerts_token",
|
||||||
|
"url": "https://www.donationalerts.com/r/you"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"admin": {
|
||||||
|
"username": "admin",
|
||||||
|
"password": "secure_password"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tiers
|
||||||
|
|
||||||
|
| Tier | Servers | Traffic | Price | Duration |
|
||||||
|
|------|---------|---------|-------|----------|
|
||||||
|
| free | subset marked `is_free: true` in config | unlimited (0 = ∞) | free | forever |
|
||||||
|
| test | all servers | 5 GB | 50₽ | 7 days |
|
||||||
|
| paid | all servers | unlimited | 150₽/30d, 990₽/365d | configurable |
|
||||||
|
|
||||||
|
Free servers are defined in `servers.conf` per-server via `is_free: true`.
|
||||||
|
|
||||||
|
### MOTD
|
||||||
|
|
||||||
|
Two ways:
|
||||||
|
1. `motd.txt` — plain text file in project root
|
||||||
|
2. `settings.conf → announcement` — string (plain or base64)
|
||||||
|
|
||||||
|
## Endpoints
|
||||||
|
|
||||||
|
| Path | Method | Description |
|
||||||
|
|------|--------|-------------|
|
||||||
|
| `/sub/{id}` | GET | Subscription (format: base64/json/raw) |
|
||||||
|
| `/sub/{id}` | GET (HTML accept) | Web page with QR and info |
|
||||||
|
| `/admin/login` | POST | Admin auth |
|
||||||
|
| `/admin/users` | GET | User management panel |
|
||||||
|
| `/admin/dashboard` | GET | Stats dashboard |
|
||||||
|
| `/admin/api/users` | POST | Create user |
|
||||||
|
| `/admin/api/users/update` | POST | Update user |
|
||||||
|
| `/admin/api/users/delete` | POST | Delete user |
|
||||||
|
| `/admin/api/reload` | POST | Reload configs + clear cache |
|
||||||
|
| `/admin/api/rotate-shortids` | GET | Rotate short IDs on all servers |
|
||||||
|
| `/` | GET | Landing page |
|
||||||
|
|
||||||
|
## DonationAlerts
|
||||||
|
|
||||||
|
Polling-based (every ~2 seconds). Amount mapping:
|
||||||
|
- 50₽ → test tier (7 days)
|
||||||
|
- 150₽ → paid tier (30 days)
|
||||||
|
- 990₽ → paid tier (365 days)
|
||||||
|
|
||||||
|
Other amounts are ignored. Donor identified by `id` (priority) or `username` from message parts.
|
||||||
|
|
||||||
|
## Happ.su Support
|
||||||
|
|
||||||
|
Subscription response includes Happ-compatible headers:
|
||||||
|
- `Profile-Title`, `Profile-Update-Interval`, `Profile-Web-Page-Url`
|
||||||
|
- `Subscription-Userinfo` (upload/download/total/expire)
|
||||||
|
- `Announce`, `Support-Url`, `Hide-Settings`
|
||||||
|
- `Sub-Expire`, `Sub-Expire-Button-Link`
|
||||||
|
|
||||||
|
## Tech
|
||||||
|
|
||||||
|
- **FastAPI** + uvicorn
|
||||||
|
- **SQLite** (users only)
|
||||||
|
- **py3xui** — 3x-UI API client
|
||||||
|
- **httpx** — async HTTP for sub links
|
||||||
|
- **qrcode** — QR generation
|
||||||
+1438
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user