3x-ui: create VLESS/WireGuard clients and reload available inbounds
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+58
-14
@@ -3,9 +3,10 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from urllib.parse import quote
|
||||
|
||||
from fastapi import APIRouter, Depends, Form, Request, status
|
||||
from fastapi.responses import HTMLResponse, RedirectResponse
|
||||
from fastapi import APIRouter, Depends, Form, Query, Request, status
|
||||
from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.database import get_db
|
||||
@@ -79,7 +80,10 @@ async def xui_check(
|
||||
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)
|
||||
return RedirectResponse(
|
||||
f"/admin/xui/{panel_id}?flash=ok",
|
||||
status_code=status.HTTP_303_SEE_OTHER,
|
||||
)
|
||||
|
||||
|
||||
@router.post("/{panel_id}/delete")
|
||||
@@ -94,6 +98,26 @@ async def xui_delete(
|
||||
return RedirectResponse("/admin/xui?flash=deleted", status_code=status.HTTP_303_SEE_OTHER)
|
||||
|
||||
|
||||
@router.get("/{panel_id}/inbounds")
|
||||
async def xui_inbounds_api(
|
||||
panel_id: int,
|
||||
protocol: str | None = Query(None),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
admin=Depends(require_admin),
|
||||
):
|
||||
"""JSON: available inbounds for dropdown (optionally filtered by protocol)."""
|
||||
if _is_redirect(admin):
|
||||
return JSONResponse({"success": False, "error": "unauthorized"}, status_code=401)
|
||||
panel = await db.get(XuiPanel, panel_id)
|
||||
if not panel:
|
||||
return JSONResponse({"success": False, "error": "not found"}, status_code=404)
|
||||
try:
|
||||
items = await xui_service.load_available_inbounds(panel, protocol=protocol)
|
||||
return JSONResponse({"success": True, "obj": items})
|
||||
except Exception as exc: # noqa: BLE001
|
||||
return JSONResponse({"success": False, "error": str(exc)}, status_code=400)
|
||||
|
||||
|
||||
@router.get("/{panel_id}", response_class=HTMLResponse)
|
||||
async def xui_detail(
|
||||
panel_id: int,
|
||||
@@ -114,12 +138,13 @@ async def xui_detail(
|
||||
except Exception as exc: # noqa: BLE001
|
||||
error = str(exc)
|
||||
|
||||
info = None
|
||||
if panel.panel_info:
|
||||
created = None
|
||||
created_raw = request.query_params.get("created")
|
||||
if created_raw:
|
||||
try:
|
||||
info = json.loads(panel.panel_info)
|
||||
created = json.loads(created_raw)
|
||||
except json.JSONDecodeError:
|
||||
info = None
|
||||
created = None
|
||||
|
||||
return request.app.state.templates.TemplateResponse(
|
||||
"admin/xui_detail.html",
|
||||
@@ -128,7 +153,7 @@ async def xui_detail(
|
||||
"admin": admin,
|
||||
"panel": panel,
|
||||
"data": data,
|
||||
"info": info,
|
||||
"created": created,
|
||||
"error": error,
|
||||
"app_name": request.app.state.settings.app_name,
|
||||
"flash": request.query_params.get("flash"),
|
||||
@@ -139,10 +164,13 @@ async def xui_detail(
|
||||
@router.post("/{panel_id}/clients")
|
||||
async def xui_add_client(
|
||||
panel_id: int,
|
||||
protocol: str = Form(...),
|
||||
email: str = Form(...),
|
||||
inbound_id: int = Form(...),
|
||||
total_gb: int = Form(0),
|
||||
limit_ip: int = Form(0),
|
||||
flow: str = Form(""),
|
||||
comment: str = Form(""),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
admin=Depends(require_admin),
|
||||
):
|
||||
@@ -152,19 +180,35 @@ async def xui_add_client(
|
||||
if not panel:
|
||||
return RedirectResponse("/admin/xui", status_code=status.HTTP_303_SEE_OTHER)
|
||||
try:
|
||||
await xui_service.add_xui_client(
|
||||
result = await xui_service.add_xui_client(
|
||||
panel,
|
||||
protocol=protocol,
|
||||
email=email.strip(),
|
||||
inbound_ids=[inbound_id],
|
||||
inbound_id=inbound_id,
|
||||
total_gb=total_gb,
|
||||
limit_ip=limit_ip,
|
||||
flow=flow.strip(),
|
||||
comment=comment.strip(),
|
||||
)
|
||||
# Keep redirect short: store essentials only
|
||||
slim = {
|
||||
"protocol": result.get("protocol"),
|
||||
"email": result.get("email"),
|
||||
"inbound_id": result.get("inbound_id"),
|
||||
"uuid": result.get("uuid"),
|
||||
"flow": result.get("flow"),
|
||||
"privateKey": result.get("privateKey"),
|
||||
"publicKey": result.get("publicKey"),
|
||||
"links": result.get("links") or [],
|
||||
"inbound": result.get("inbound"),
|
||||
}
|
||||
payload = quote(json.dumps(slim, ensure_ascii=False))
|
||||
return RedirectResponse(
|
||||
f"/admin/xui/{panel_id}?flash=client_created&created={payload}",
|
||||
status_code=status.HTTP_303_SEE_OTHER,
|
||||
)
|
||||
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