sync: bring api.md to dp-custom state

This commit is contained in:
iqubik
2026-04-05 00:48:46 +03:00
parent 4335b28d65
commit 9e87741e33
+878
View File
@@ -0,0 +1,878 @@
# API документация 3dp-manager
**Версия API:** v1
**Базовый URL:** `http://<host>:3100/api`
**Префикс:** Все endpoints начинаются с `/api`, кроме публичных подписок (`/bus/:uuid`)
---
## Содержание
1. [Аутентификация](#аутентификация)
2. [Подписки](#подписки)
3. [Домены](#домены)
4. [Туннели (перенаправление)](#туннели-перенаправление)
5. [Настройки](#настройки)
6. [Ротация](#ротация)
7. [Клиентские endpoints (публичные)](#клиентские-endpoints-публичные)
---
## Аутентификация
Все endpoints требуют JWT-аутентификации, кроме помеченных как **Public**.
### POST `/api/auth/login`
Получение JWT-токена.
**Body:**
```json
{
"login": "admin",
"password": "your_password"
}
```
**Response:**
```json
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
```
---
### POST `/api/auth/change-password`
Смена пароля администратора.
**Headers:** `Authorization: Bearer <token>`
**Body:**
```json
{
"password": "newSecurePassword"
}
```
**Response:**
```json
{
"success": true
}
```
---
### POST `/api/auth/update-profile`
Обновление профиля администратора (логин + опционально пароль).
**Headers:** `Authorization: Bearer <token>`
**Body:**
```json
{
"login": "newAdmin",
"password": "newPassword"
}
```
**Response:**
```json
{
"success": true
}
```
---
## Подписки
### GET `/api/subscriptions`
Получить все подписки.
**Headers:** `Authorization: Bearer <token>`
**Response:**
```json
[
{
"id": 1,
"uuid": "abc-123-def",
"name": "My Subscription",
"isEnabled": true,
"isAutoRotationEnabled": true,
"inbounds": [...],
"createdAt": "2024-01-01T00:00:00Z",
"updatedAt": "2024-01-01T00:00:00Z"
}
]
```
---
### POST `/api/subscriptions`
Создать новую подписку.
**Headers:** `Authorization: Bearer <token>`
**Body:**
```json
{
"name": "My Subscription",
"inboundsConfig": [
{
"type": "vless",
"port": 443,
"sni": "example.com"
},
{
"type": "vmess",
"port": 8443,
"transport": "websocket"
}
],
"isAutoRotationEnabled": true
}
```
**Response:**
```json
{
"id": 1,
"uuid": "abc-123-def",
"name": "My Subscription",
...
}
```
---
### PUT `/api/subscriptions/:id`
Обновить подписку.
**Headers:** `Authorization: Bearer <token>`
**Body:**
```json
{
"name": "Updated Name",
"isEnabled": false,
"isAutoRotationEnabled": true
}
```
**Response:**
```json
{
"id": 1,
"name": "Updated Name",
...
}
```
---
### PUT `/api/subscriptions/bulk-auto-rotation`
Массовое включение/отключение авто-ротации.
**Headers:** `Authorization: Bearer <token>`
**Body:**
```json
{
"subscriptionIds": ["1", "2", "3"],
"enabled": true
}
```
**Response:**
```json
{
"success": true,
"message": "Обновлено 3 подписок",
"updatedCount": 3,
"notFound": []
}
```
---
### DELETE `/api/subscriptions/:id`
Удалить подписку.
**Headers:** `Authorization: Bearer <token>`
**Response:**
```json
{
"success": true
}
```
---
## Домены
### GET `/api/domains/all`
Получить все домены без пагинации.
**Headers:** `Authorization: Bearer <token>`
**Response:**
```json
[
{ "id": 1, "name": "ya.ru" },
{ "id": 2, "name": "vk.com" }
]
```
---
### GET `/api/domains?page=1&limit=10`
Получить домены с пагинацией.
**Headers:** `Authorization: Bearer <token>`
**Query Parameters:**
- `page` (default: 1)
- `limit` (default: 10)
**Response:**
```json
{
"data": [
{ "id": 1, "name": "ya.ru" }
],
"total": 100
}
```
---
### GET `/api/domains/:id`
Получить домен по ID.
**Headers:** `Authorization: Bearer <token>`
**Response:**
```json
{
"id": 1,
"name": "ya.ru"
}
```
---
### POST `/api/domains`
Создать один домен.
**Headers:** `Authorization: Bearer <token>`
**Body:**
```json
{
"name": "example.com"
}
```
**Response:**
```json
{
"id": 1,
"name": "example.com"
}
```
---
### POST `/api/domains/upload`
Загрузить несколько доменов сразу.
**Headers:** `Authorization: Bearer <token>`
**Body:**
```json
{
"domains": ["ya.ru", "vk.com", "example.com"]
}
```
**Response:**
```json
{
"count": 3
}
```
---
### DELETE `/api/domains/:id`
Удалить домен по ID.
**Headers:** `Authorization: Bearer <token>`
**Response:**
```json
{
"success": true
}
```
---
### DELETE `/api/domains/all`
Удалить все домены.
**Headers:** `Authorization: Bearer <token>`
**Response:**
```json
{
"success": true
}
```
---
### GET `/api/domains/scan/capabilities`
Получить возможности сканера доменов (наличие бинарника, timeout).
**Headers:** `Authorization: Bearer <token>`
**Response:**
```json
{
"scannerAvailable": true,
"scannerPath": "/usr/bin/RealiTLScanner-linux-64",
"timeoutAvailable": true,
"timeoutPath": "/usr/bin/timeout"
}
```
---
### GET `/api/domains/scan/status`
Получить статус текущего сканирования.
**Headers:** `Authorization: Bearer <token>`
**Response:**
```json
{
"running": false,
"runId": null,
"lastRunId": "scan-123"
}
```
---
### GET `/api/domains/scan/last-result`
Получить результат последнего сканирования.
**Headers:** `Authorization: Bearer <token>`
**Response:**
```json
{
"runId": "scan-123",
"foundCount": 10,
"domains": ["ya.ru", "vk.com", ...]
}
```
---
### POST `/api/domains/scan/start`
Запустить сканирование IP на наличие SNI.
**Headers:** `Authorization: Bearer <token>`
**Body:**
```json
{
"addr": "192.168.1.1",
"scanSeconds": 60,
"thread": 100,
"timeout": 30
}
```
**Response:**
```json
{
"success": true,
"runId": "scan-456"
}
```
---
## Туннели (перенаправление)
### GET `/api/tunnels`
Получить все туннели.
**Headers:** `Authorization: Bearer <token>`
**Response:**
```json
[
{
"id": 1,
"name": "Relay Server 1",
"ip": "192.168.1.100",
"sshPort": 22,
"username": "root",
"domain": "relay.example.com",
"isInstalled": true
}
]
```
---
### POST `/api/tunnels`
Создать новый туннель.
**Headers:** `Authorization: Bearer <token>`
**Body:**
```json
{
"name": "Relay Server 1",
"ip": "192.168.1.100",
"sshPort": 22,
"username": "root",
"password": "ssh_password",
"domain": "relay.example.com"
}
```
**Response:**
```json
{
"id": 1,
"name": "Relay Server 1",
...
}
```
---
### POST `/api/tunnels/:id/install`
Установить скрипт перенаправления на удалённый сервер.
**Headers:** `Authorization: Bearer <token>`
**Response:**
```json
{
"success": true,
"script": "#!/bin/bash ...",
"message": "Скрипт сгенерирован. Выполните его на целевом сервере."
}
```
---
### DELETE `/api/tunnels/:id`
Удалить туннель.
**Headers:** `Authorization: Bearer <token>`
**Response:**
```json
{
"success": true
}
```
---
## Настройки
### GET `/api/settings`
Получить все настройки.
**Headers:** `Authorization: Bearer <token>`
**Response:**
```json
{
"xui_url": "http://192.168.1.1:54321",
"xui_login": "admin",
"rotationIntervalMinutes": "1440",
"xui_geo_country": "Germany",
"xui_geo_flag": "🇩🇪"
}
```
---
### POST `/api/settings`
Обновить настройки.
**Headers:** `Authorization: Bearer <token>`
**Body:**
```json
{
"xui_url": "http://192.168.1.1:54321",
"xui_login": "admin",
"xui_password": "password",
"rotationIntervalMinutes": "1440"
}
```
**Response:**
```json
{
"success": true
}
```
*Автоматически извлекает host, IP, определяет страну по GeoIP.*
---
### POST `/api/settings/check`
Проверить подключение к 3x-ui панели.
**Headers:** `Authorization: Bearer <token>`
**Body:**
```json
{
"xui_url": "http://192.168.1.1:54321",
"xui_login": "admin",
"xui_password": "password"
}
```
**Response:**
```json
{
"success": true
}
```
---
## Ротация
### POST `/api/rotation/rotate-all`
Выполнить ротацию всех подписок.
**Headers:** `Authorization: Bearer <token>`
**Response:**
```json
{
"success": true,
"rotatedCount": 5
}
```
---
### POST `/api/rotation/rotate-one/:id`
Выполнить ротацию одной подписки.
**Headers:** `Authorization: Bearer <token>`
**Response:**
```json
{
"success": true,
"subscriptionId": "1"
}
```
---
## Клиентские endpoints (публичные)
Эти endpoints **не требуют аутентификации**.
### GET `/bus/:uuid`
Получить подписку в формате Base64 или HTML-страницу с QR-кодом.
**Public** — без токена.
**URL:**
```
GET /bus/abc-123-def
```
**Headers:**
- `User-Agent: Mozilla/...` → возвращает HTML с QR
- `User-Agent: v2rayN/...` → возвращает plain text Base64
**Response (Base64):**
```
vless://abc123...
vmess://xyz789...
...
```
**Response (HTML):**
```html
<!DOCTYPE html>
<html>
<body>
<h1>Subscription: My Subscription</h1>
<img src="data:image/png;base64,..." />
<a href="/bus/abc-123-def">Скачать подписку</a>
</body>
</html>
```
---
### GET `/bus/:uuid/:tunnelId`
Получить подписку с заменой хоста на релей-сервер.
**Public** — без токена.
**Query Parameters:**
- `format` (опционально): `base64` | `html`
**URL:**
```
GET /bus/abc-123-def/1
```
**Response:**
- Автоматически заменяет IP/домен в ссылках на `tunnel.domain` или `tunnel.ip`
- Возвращает Base64 или HTML с QR
---
## Сущности
### Subscription
```typescript
{
id: number;
uuid: string;
name: string;
isEnabled: boolean;
isAutoRotationEnabled: boolean;
inbounds: Inbound[];
createdAt: Date;
updatedAt: Date;
}
```
### Inbound
```typescript
{
id: number;
type: 'vless' | 'vmess' | 'shadowsocks' | 'hysteria2' | 'trojan' | 'custom';
port: number | string;
sni?: string;
link: string;
transport?: 'tcp' | 'websocket' | 'grpc' | 'xhttp';
}
```
### Domain
```typescript
{
id: number;
name: string;
}
```
### Tunnel
```typescript
{
id: number;
name: string;
ip: string;
sshPort: number;
username: string;
domain?: string;
isInstalled: boolean;
}
```
### Setting
```typescript
{
key: string;
value: string;
}
```
---
## Коды ошибок
| Код | Описание |
|-----|----------|
| 200 | Успех |
| 400 | Неверный запрос |
| 401 | Неавторизован (нет токена или неверный) |
| 403 | Доступ запрещён |
| 404 | Ресурс не найден |
| 500 | Внутренняя ошибка сервера |
---
## Примеры использования
### JavaScript (Fetch API)
```javascript
// Логин
const loginResponse = await fetch('http://localhost:3100/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ login: 'admin', password: 'admin' })
});
const { access_token } = await loginResponse.json();
// Получить все подписки
const subsResponse = await fetch('http://localhost:3100/api/subscriptions', {
headers: { 'Authorization': `Bearer ${access_token}` }
});
const subscriptions = await subsResponse.json();
// Создать подписку
const createResponse = await fetch('http://localhost:3100/api/subscriptions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${access_token}`
},
body: JSON.stringify({
name: 'My Subscription',
inboundsConfig: [
{ type: 'vless', port: 443, sni: 'example.com' }
],
isAutoRotationEnabled: true
})
});
const subscription = await createResponse.json();
```
### Python (requests)
```python
import requests
# Логин
response = requests.post('http://localhost:3100/api/auth/login', json={
'login': 'admin',
'password': 'admin'
})
token = response.json()['access_token']
# Получить все домены
headers = {'Authorization': f'Bearer {token}'}
response = requests.get('http://localhost:3100/api/domains/all', headers=headers)
domains = response.json()
# Запустить сканирование
response = requests.post('http://localhost:3100/api/domains/scan/start',
headers=headers,
json={
'addr': '192.168.1.1',
'scanSeconds': 60,
'thread': 100,
'timeout': 30
}
)
```
### cURL
```bash
# Логин
curl -X POST http://localhost:3100/api/auth/login \
-H "Content-Type: application/json" \
-d '{"login":"admin","password":"admin"}'
# Получить подписку (Base64)
curl http://localhost:3100/bus/abc-123-def
# Получить подписку (HTML с QR)
curl -H "User-Agent: Mozilla/5.0" \
http://localhost:3100/bus/abc-123-def
```
---
## Интеграция
### Ссылка на подписку в клиенте
Формат:
```
http://<host>:<port>/bus/<uuid>
```
Пример для v2rayN, Clash, Surge:
```
http://192.168.1.100:3100/bus/abc-123-def
```
### QR-код
Откройте в браузере:
```
http://<host>:<port>/bus/<uuid>
```
Сканируйте QR-код приложением.
---
## Примечания
- **JWT-токен** передаётся в заголовке `Authorization: Bearer <token>`
- **Публичные endpoints** (`/bus/*`) не требуют токена
- **Base64-кодировка** используется для подписок (стандарт v2ray)
- **Авто-ротация** выполняется по интервалу из настроек (`rotationIntervalMinutes`)
- **Scanner** (RealiTLScanner) — Go-бинарник, встраивается в backend-контейнер
---
## Контакты
- Telegram: [@denpiligrim_web](https://t.me/denpiligrim_web)
- YouTube: [DenPiligrim](https://www.youtube.com/@denpiligrim)
- GitHub: [denpiligrim/3dp-manager](https://github.com/denpiligrim/3dp-manager)