3x-ui: create VLESS/WireGuard clients and reload available inbounds

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-25 22:09:59 +03:00
co-authored by Cursor
parent b67e7b80ba
commit 1626c16df0
4 changed files with 525 additions and 136 deletions
+73 -33
View File
@@ -4,12 +4,28 @@ from __future__ import annotations
import json
from datetime import datetime, timezone
from typing import Any
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.models import XuiPanel
from app.services.xui import XuiClient, XuiApiError, normalize_base_url
from app.services.xui import (
SUPPORTED_CLIENT_PROTOCOLS,
XuiApiError,
XuiClient,
normalize_base_url,
)
def _client_for(panel: XuiPanel) -> XuiClient:
return XuiClient(
panel.base_url,
username=panel.username,
password=panel.password,
api_token=panel.api_token,
verify_ssl=panel.verify_ssl,
)
async def list_panels(session: AsyncSession) -> list[XuiPanel]:
@@ -47,7 +63,6 @@ async def create_panel(
await session.commit()
await session.refresh(panel)
# Verify immediately
await check_panel(session, panel.id)
await session.refresh(panel)
if not panel.is_reachable:
@@ -62,13 +77,7 @@ async def check_panel(session: AsyncSession, panel_id: int) -> XuiPanel:
now = datetime.now(timezone.utc)
try:
async with XuiClient(
panel.base_url,
username=panel.username,
password=panel.password,
api_token=panel.api_token,
verify_ssl=panel.verify_ssl,
) as client:
async with _client_for(panel) as client:
info = await client.test_connection()
panel.is_reachable = True
panel.last_error = None
@@ -94,41 +103,72 @@ async def delete_panel(session: AsyncSession, panel_id: int) -> None:
await session.commit()
async def fetch_panel_data(panel: XuiPanel) -> dict:
async with XuiClient(
panel.base_url,
username=panel.username,
password=panel.password,
api_token=panel.api_token,
verify_ssl=panel.verify_ssl,
) as client:
async def fetch_panel_data(panel: XuiPanel) -> dict[str, Any]:
async with _client_for(panel) as client:
status = await client.get_status()
inbounds = await client.list_inbounds()
all_inbounds = await client.list_inbounds()
available = await client.list_available_inbounds(
protocols=SUPPORTED_CLIENT_PROTOCOLS, enabled_only=False
)
vless_inbounds = [i for i in available if str(i.get("protocol")).lower() == "vless"]
wg_inbounds = [i for i in available if str(i.get("protocol")).lower() == "wireguard"]
try:
clients = await client.list_clients()
except XuiApiError:
clients = []
return {"status": status, "inbounds": inbounds, "clients": clients}
return {
"status": status,
"inbounds": all_inbounds,
"available_inbounds": available,
"vless_inbounds": vless_inbounds,
"wireguard_inbounds": wg_inbounds,
"clients": clients,
}
async def load_available_inbounds(
panel: XuiPanel,
*,
protocol: str | None = None,
) -> list[dict]:
protocols: tuple[str, ...]
if protocol:
protocols = (protocol.lower(),)
else:
protocols = SUPPORTED_CLIENT_PROTOCOLS
async with _client_for(panel) as client:
return await client.list_available_inbounds(protocols=protocols, enabled_only=True)
async def add_xui_client(
panel: XuiPanel,
*,
protocol: str,
email: str,
inbound_ids: list[int],
inbound_id: int,
total_gb: int = 0,
limit_ip: int = 0,
flow: str = "",
comment: str = "",
) -> dict:
async with XuiClient(
panel.base_url,
username=panel.username,
password=panel.password,
api_token=panel.api_token,
verify_ssl=panel.verify_ssl,
) as client:
return await client.add_client(
email=email,
inbound_ids=inbound_ids,
total_gb=total_gb * 1024 * 1024 * 1024 if total_gb > 0 else 0,
limit_ip=limit_ip,
)
proto = protocol.strip().lower()
bytes_limit = total_gb * 1024 * 1024 * 1024 if total_gb > 0 else 0
async with _client_for(panel) as client:
if proto == "vless":
return await client.add_vless_client(
email=email,
inbound_id=inbound_id,
total_gb=bytes_limit,
limit_ip=limit_ip,
flow=flow,
comment=comment,
)
if proto == "wireguard":
return await client.add_wireguard_client(
email=email,
inbound_id=inbound_id,
total_gb=bytes_limit,
limit_ip=limit_ip,
comment=comment,
)
raise ValueError("Поддерживаются только vless и wireguard")