"""CRUD + connection tests for linked 3x-ui panels.""" from __future__ import annotations import json from datetime import datetime, timezone 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 async def list_panels(session: AsyncSession) -> list[XuiPanel]: result = await session.execute(select(XuiPanel).order_by(XuiPanel.id.desc())) return list(result.scalars().all()) async def create_panel( session: AsyncSession, *, name: str, base_url: str, username: str = "", password: str = "", api_token: str = "", verify_ssl: bool = True, ) -> XuiPanel: token = api_token.strip() or None user = username.strip() or None pwd = password.strip() or None if not token and not (user and pwd): raise ValueError("Укажите API Token или логин и пароль") url = normalize_base_url(base_url) panel = XuiPanel( name=(name.strip() or url), base_url=url, username=user, password=pwd, api_token=token, verify_ssl=verify_ssl, is_enabled=True, ) session.add(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: raise ValueError(panel.last_error or "Не удалось подключиться к 3x-ui") return panel async def check_panel(session: AsyncSession, panel_id: int) -> XuiPanel: panel = await session.get(XuiPanel, panel_id) if not panel: raise ValueError("Панель не найдена") 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: info = await client.test_connection() panel.is_reachable = True panel.last_error = None panel.panel_info = json.dumps(info, ensure_ascii=False, default=str)[:8000] panel.last_checked = now except (XuiApiError, ValueError, OSError) as exc: panel.is_reachable = False panel.last_error = str(exc)[:2000] panel.last_checked = now await session.commit() await session.refresh(panel) raise ValueError(str(exc)) from exc await session.commit() await session.refresh(panel) return panel async def delete_panel(session: AsyncSession, panel_id: int) -> None: panel = await session.get(XuiPanel, panel_id) if panel: await session.delete(panel) 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: status = await client.get_status() inbounds = await client.list_inbounds() try: clients = await client.list_clients() except XuiApiError: clients = [] return {"status": status, "inbounds": inbounds, "clients": clients} async def add_xui_client( panel: XuiPanel, *, email: str, inbound_ids: list[int], total_gb: int = 0, limit_ip: int = 0, ) -> 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, )