3x-ui: add expiry days and start-after-first-use for clients

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-25 22:18:37 +03:00
co-authored by Cursor
parent 1626c16df0
commit 1b2fea4539
4 changed files with 68 additions and 9 deletions
+31 -4
View File
@@ -140,6 +140,24 @@ async def load_available_inbounds(
return await client.list_available_inbounds(protocols=protocols, enabled_only=True)
def compute_expiry_time(*, days: int, start_after_first_use: bool) -> int:
"""
3x-ui expiryTime encoding:
0 — unlimited
>0 — absolute deadline in unix ms
<0 — duration ms; countdown starts after first use
"""
days = int(days or 0)
if days <= 0:
return 0
duration_ms = days * 24 * 60 * 60 * 1000
if start_after_first_use:
return -duration_ms
import time
return int(time.time() * 1000) + duration_ms
async def add_xui_client(
panel: XuiPanel,
*,
@@ -148,27 +166,36 @@ async def add_xui_client(
inbound_id: int,
total_gb: int = 0,
limit_ip: int = 0,
expiry_days: int = 0,
start_after_first_use: bool = False,
flow: str = "",
comment: str = "",
) -> dict:
proto = protocol.strip().lower()
bytes_limit = total_gb * 1024 * 1024 * 1024 if total_gb > 0 else 0
expiry_time = compute_expiry_time(days=expiry_days, start_after_first_use=start_after_first_use)
async with _client_for(panel) as client:
if proto == "vless":
return await client.add_vless_client(
result = await client.add_vless_client(
email=email,
inbound_id=inbound_id,
total_gb=bytes_limit,
expiry_time=expiry_time,
limit_ip=limit_ip,
flow=flow,
comment=comment,
)
if proto == "wireguard":
return await client.add_wireguard_client(
elif proto == "wireguard":
result = await client.add_wireguard_client(
email=email,
inbound_id=inbound_id,
total_gb=bytes_limit,
expiry_time=expiry_time,
limit_ip=limit_ip,
comment=comment,
)
raise ValueError("Поддерживаются только vless и wireguard")
else:
raise ValueError("Поддерживаются только vless и wireguard")
result["expiry_days"] = expiry_days
result["start_after_first_use"] = start_after_first_use
return result