Add 3x-ui v3.5.0 API panel connection and client management
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
from app.routers import admin, auth, public
|
||||
from app.routers import admin, auth, public, xui
|
||||
|
||||
__all__ = ["admin", "auth", "public"]
|
||||
__all__ = ["admin", "auth", "public", "xui"]
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
"""Admin routes for 3x-ui API panels."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
|
||||
from fastapi import APIRouter, Depends, Form, Request, status
|
||||
from fastapi.responses import HTMLResponse, RedirectResponse
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.database import get_db
|
||||
from app.models import XuiPanel
|
||||
from app.routers.admin import _is_redirect, require_admin
|
||||
from app.services import xui_panels as xui_service
|
||||
|
||||
router = APIRouter(prefix="/admin/xui", tags=["xui"])
|
||||
|
||||
|
||||
@router.get("", response_class=HTMLResponse)
|
||||
async def xui_list(request: Request, db: AsyncSession = Depends(get_db), admin=Depends(require_admin)):
|
||||
if _is_redirect(admin):
|
||||
return admin
|
||||
panels = await xui_service.list_panels(db)
|
||||
return request.app.state.templates.TemplateResponse(
|
||||
"admin/xui.html",
|
||||
{
|
||||
"request": request,
|
||||
"admin": admin,
|
||||
"panels": panels,
|
||||
"app_name": request.app.state.settings.app_name,
|
||||
"flash": request.query_params.get("flash"),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@router.post("")
|
||||
async def xui_create(
|
||||
name: str = Form(""),
|
||||
base_url: str = Form(...),
|
||||
username: str = Form(""),
|
||||
password: str = Form(""),
|
||||
api_token: str = Form(""),
|
||||
verify_ssl: str = Form(""),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
admin=Depends(require_admin),
|
||||
):
|
||||
if _is_redirect(admin):
|
||||
return admin
|
||||
try:
|
||||
await xui_service.create_panel(
|
||||
db,
|
||||
name=name,
|
||||
base_url=base_url,
|
||||
username=username,
|
||||
password=password,
|
||||
api_token=api_token,
|
||||
verify_ssl=verify_ssl == "1",
|
||||
)
|
||||
except Exception as exc: # noqa: BLE001
|
||||
return RedirectResponse(
|
||||
f"/admin/xui?flash=error:{exc}",
|
||||
status_code=status.HTTP_303_SEE_OTHER,
|
||||
)
|
||||
return RedirectResponse("/admin/xui?flash=created", status_code=status.HTTP_303_SEE_OTHER)
|
||||
|
||||
|
||||
@router.post("/{panel_id}/check")
|
||||
async def xui_check(
|
||||
panel_id: int,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
admin=Depends(require_admin),
|
||||
):
|
||||
if _is_redirect(admin):
|
||||
return admin
|
||||
try:
|
||||
await xui_service.check_panel(db, panel_id)
|
||||
except Exception as exc: # noqa: BLE001
|
||||
return RedirectResponse(
|
||||
f"/admin/xui?flash=error:{exc}",
|
||||
status_code=status.HTTP_303_SEE_OTHER,
|
||||
)
|
||||
return RedirectResponse("/admin/xui?flash=ok", status_code=status.HTTP_303_SEE_OTHER)
|
||||
|
||||
|
||||
@router.post("/{panel_id}/delete")
|
||||
async def xui_delete(
|
||||
panel_id: int,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
admin=Depends(require_admin),
|
||||
):
|
||||
if _is_redirect(admin):
|
||||
return admin
|
||||
await xui_service.delete_panel(db, panel_id)
|
||||
return RedirectResponse("/admin/xui?flash=deleted", status_code=status.HTTP_303_SEE_OTHER)
|
||||
|
||||
|
||||
@router.get("/{panel_id}", response_class=HTMLResponse)
|
||||
async def xui_detail(
|
||||
panel_id: int,
|
||||
request: Request,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
admin=Depends(require_admin),
|
||||
):
|
||||
if _is_redirect(admin):
|
||||
return admin
|
||||
panel = await db.get(XuiPanel, panel_id)
|
||||
if not panel:
|
||||
return RedirectResponse("/admin/xui", status_code=status.HTTP_303_SEE_OTHER)
|
||||
|
||||
data = None
|
||||
error = None
|
||||
try:
|
||||
data = await xui_service.fetch_panel_data(panel)
|
||||
except Exception as exc: # noqa: BLE001
|
||||
error = str(exc)
|
||||
|
||||
info = None
|
||||
if panel.panel_info:
|
||||
try:
|
||||
info = json.loads(panel.panel_info)
|
||||
except json.JSONDecodeError:
|
||||
info = None
|
||||
|
||||
return request.app.state.templates.TemplateResponse(
|
||||
"admin/xui_detail.html",
|
||||
{
|
||||
"request": request,
|
||||
"admin": admin,
|
||||
"panel": panel,
|
||||
"data": data,
|
||||
"info": info,
|
||||
"error": error,
|
||||
"app_name": request.app.state.settings.app_name,
|
||||
"flash": request.query_params.get("flash"),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@router.post("/{panel_id}/clients")
|
||||
async def xui_add_client(
|
||||
panel_id: int,
|
||||
email: str = Form(...),
|
||||
inbound_id: int = Form(...),
|
||||
total_gb: int = Form(0),
|
||||
limit_ip: int = Form(0),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
admin=Depends(require_admin),
|
||||
):
|
||||
if _is_redirect(admin):
|
||||
return admin
|
||||
panel = await db.get(XuiPanel, panel_id)
|
||||
if not panel:
|
||||
return RedirectResponse("/admin/xui", status_code=status.HTTP_303_SEE_OTHER)
|
||||
try:
|
||||
await xui_service.add_xui_client(
|
||||
panel,
|
||||
email=email.strip(),
|
||||
inbound_ids=[inbound_id],
|
||||
total_gb=total_gb,
|
||||
limit_ip=limit_ip,
|
||||
)
|
||||
except Exception as exc: # noqa: BLE001
|
||||
return RedirectResponse(
|
||||
f"/admin/xui/{panel_id}?flash=error:{exc}",
|
||||
status_code=status.HTTP_303_SEE_OTHER,
|
||||
)
|
||||
return RedirectResponse(
|
||||
f"/admin/xui/{panel_id}?flash=client_created",
|
||||
status_code=status.HTTP_303_SEE_OTHER,
|
||||
)
|
||||
Reference in New Issue
Block a user