Add multi-server 3x-ui registry with per-panel subscription URLs.
Admins can manage several 3x-ui panels by name and select which one issues invite/user configs via that server's /sub base URL. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1713,6 +1713,7 @@ class AddConnectionRequest(BaseModel):
|
||||
telemt_ad_tag: Optional[str] = None
|
||||
telemt_max_conns: Optional[int] = None
|
||||
xui_inbound_id: Optional[int] = None
|
||||
xui_panel_id: Optional[str] = None
|
||||
|
||||
|
||||
class EditConnectionRequest(BaseModel):
|
||||
@@ -1800,6 +1801,19 @@ class SyncSettings(BaseModel):
|
||||
xui_inbound_id: int = 0
|
||||
xui_sub_url: str = ''
|
||||
|
||||
|
||||
class XuiServerRequest(BaseModel):
|
||||
name: str = ''
|
||||
url: str = ''
|
||||
sub_url: str = ''
|
||||
api_token: str = ''
|
||||
username: str = ''
|
||||
password: str = ''
|
||||
default_inbound_id: int = 0
|
||||
enabled: bool = True
|
||||
sync_users: bool = False
|
||||
|
||||
|
||||
class CaptchaSettings(BaseModel):
|
||||
enabled: bool = False
|
||||
|
||||
@@ -1828,6 +1842,7 @@ class GuestSettings(BaseModel):
|
||||
create_protocol: str = 'xui'
|
||||
create_server_id: int = 0
|
||||
create_inbound_id: int = 0
|
||||
create_xui_panel_id: str = ''
|
||||
|
||||
|
||||
class UpdateUserRequest(BaseModel):
|
||||
@@ -1866,6 +1881,7 @@ class AddUserConnectionRequest(BaseModel):
|
||||
telemt_ad_tag: Optional[str] = None
|
||||
telemt_max_conns: Optional[int] = None
|
||||
xui_inbound_id: Optional[int] = None
|
||||
xui_panel_id: Optional[str] = None
|
||||
|
||||
|
||||
class CreateApiTokenRequest(BaseModel):
|
||||
@@ -1892,6 +1908,7 @@ class InviteCreateRequest(BaseModel):
|
||||
protocol: str = 'xui'
|
||||
server_id: int = 0
|
||||
xui_inbound_id: int = 0
|
||||
xui_panel_id: str = ''
|
||||
password: Optional[str] = None
|
||||
duration_days: int = 0 # client lifetime after redeem; 0 = no expiry
|
||||
note: str = ''
|
||||
@@ -1905,6 +1922,7 @@ class InviteUpdateRequest(BaseModel):
|
||||
protocol: Optional[str] = None
|
||||
server_id: Optional[int] = None
|
||||
xui_inbound_id: Optional[int] = None
|
||||
xui_panel_id: Optional[str] = None
|
||||
password: Optional[str] = None
|
||||
clear_password: bool = False
|
||||
duration_days: Optional[int] = None
|
||||
@@ -2233,14 +2251,19 @@ async def users_page(request: Request):
|
||||
for u in users_list:
|
||||
u['connections_count'] = sum(1 for c in conns if c['user_id'] == u['id'])
|
||||
servers = data['servers']
|
||||
sync = data.get('settings', {}).get('sync', {})
|
||||
xui_configured = bool((sync.get('xui_url') or '').strip())
|
||||
from managers.xui_servers import list_xui_servers, ensure_xui_servers, get_xui_server
|
||||
ensure_xui_servers(data.setdefault('settings', {}))
|
||||
xui_servers = list_xui_servers(data.get('settings') or {})
|
||||
primary = get_xui_server(data.get('settings') or {}, None)
|
||||
xui_configured = bool(xui_servers)
|
||||
return tpl(
|
||||
request, 'users.html',
|
||||
users=users_list,
|
||||
servers=servers,
|
||||
xui_configured=xui_configured,
|
||||
xui_inbound_id=sync.get('xui_inbound_id') or 0,
|
||||
xui_servers=xui_servers,
|
||||
xui_inbound_id=int((primary or {}).get('default_inbound_id') or 0),
|
||||
xui_default_panel_id=(primary or {}).get('id') or '',
|
||||
)
|
||||
|
||||
|
||||
@@ -3869,20 +3892,31 @@ async def api_add_user_connection(request: Request, user_id: str, req: AddUserCo
|
||||
status_code=400,
|
||||
)
|
||||
|
||||
# 3x-ui VLESS — no SSH; uses panel settings credentials
|
||||
# 3x-ui VLESS — no SSH; uses selected 3x-ui panel credentials
|
||||
if protocol_base(req.protocol) == 'xui':
|
||||
from managers.xui_servers import get_xui_server, ensure_xui_servers
|
||||
ensure_xui_servers(data.setdefault('settings', {}))
|
||||
panel = get_xui_server(data.get('settings') or {}, req.xui_panel_id)
|
||||
if not panel:
|
||||
return JSONResponse({'error': 'Add a 3x-ui server in Settings first'}, status_code=400)
|
||||
panel_id = panel.get('id') or ''
|
||||
if req.client_id:
|
||||
from managers.xui_api import xui_get_config
|
||||
config = await xui_get_config(data.get('settings', {}), req.client_id)
|
||||
result = {'client_id': req.client_id, 'config': config}
|
||||
config = await xui_get_config(data.get('settings', {}), req.client_id, panel_id=panel_id)
|
||||
result = {'client_id': req.client_id, 'config': config, 'subscription_url': config if str(config).startswith('http') else ''}
|
||||
else:
|
||||
from managers.xui_api import xui_create_vless_config
|
||||
created = await xui_create_vless_config(
|
||||
data.get('settings', {}),
|
||||
name=req.name or user.get('username') or 'user',
|
||||
inbound_id=req.xui_inbound_id,
|
||||
inbound_id=req.xui_inbound_id or panel.get('default_inbound_id') or None,
|
||||
panel_id=panel_id,
|
||||
)
|
||||
result = {'client_id': created['client_id'], 'config': created.get('config') or ''}
|
||||
result = {
|
||||
'client_id': created['client_id'],
|
||||
'config': created.get('config') or '',
|
||||
'subscription_url': created.get('subscription_url') or '',
|
||||
}
|
||||
sid = req.server_id if data['servers'] and req.server_id < len(data['servers']) else 0
|
||||
if result.get('client_id'):
|
||||
conn = {
|
||||
@@ -3892,6 +3926,7 @@ async def api_add_user_connection(request: Request, user_id: str, req: AddUserCo
|
||||
'protocol': 'xui',
|
||||
'client_id': result['client_id'],
|
||||
'name': req.name,
|
||||
'xui_panel_id': panel_id,
|
||||
'created_at': datetime.now().isoformat(),
|
||||
}
|
||||
async with DATA_LOCK:
|
||||
@@ -3901,7 +3936,12 @@ async def api_add_user_connection(request: Request, user_id: str, req: AddUserCo
|
||||
resp = {'status': 'success'}
|
||||
if result.get('config'):
|
||||
resp['config'] = result['config']
|
||||
resp['vpn_link'] = generate_vpn_link(result['config'])
|
||||
sub = result.get('subscription_url') or ''
|
||||
resp['subscription_url'] = sub
|
||||
# Don't wrap subscription HTTPS URLs into vpn://
|
||||
resp['vpn_link'] = sub if sub else (
|
||||
result['config'] if str(result['config']).startswith('http') else generate_vpn_link(result['config'])
|
||||
)
|
||||
return resp
|
||||
|
||||
if req.server_id >= len(data['servers']):
|
||||
@@ -4100,9 +4140,17 @@ async def api_share_config(token: str, connection_id: str, request: Request):
|
||||
try:
|
||||
if protocol_base(conn.get('protocol', '')) == 'xui':
|
||||
from managers.xui_api import xui_get_config
|
||||
config = await xui_get_config(data.get('settings', {}), conn['client_id'])
|
||||
vpn_link = generate_vpn_link(config) if config else ''
|
||||
return {'config': config, 'vpn_link': vpn_link}
|
||||
config = await xui_get_config(
|
||||
data.get('settings', {}),
|
||||
conn['client_id'],
|
||||
panel_id=conn.get('xui_panel_id') or None,
|
||||
)
|
||||
vpn_link = config if str(config).startswith('http') else (generate_vpn_link(config) if config else '')
|
||||
return {
|
||||
'config': config,
|
||||
'vpn_link': vpn_link,
|
||||
'subscription_url': config if str(config).startswith('http') else '',
|
||||
}
|
||||
|
||||
sid = conn['server_id']
|
||||
server = data['servers'][sid]
|
||||
@@ -4136,6 +4184,7 @@ def _guest_settings(data: Optional[dict] = None) -> dict:
|
||||
'create_protocol': 'xui',
|
||||
'create_server_id': 0,
|
||||
'create_inbound_id': 0,
|
||||
'create_xui_panel_id': '',
|
||||
}
|
||||
defaults.update(guest)
|
||||
return defaults
|
||||
@@ -4245,9 +4294,17 @@ async def api_guest_config(token: str, connection_id: str, request: Request):
|
||||
try:
|
||||
if protocol_base(conn.get('protocol', '')) == 'xui':
|
||||
from managers.xui_api import xui_get_config
|
||||
config = await xui_get_config(data.get('settings', {}), conn['client_id'])
|
||||
vpn_link = generate_vpn_link(config) if config else ''
|
||||
return {'config': config, 'vpn_link': vpn_link}
|
||||
config = await xui_get_config(
|
||||
data.get('settings', {}),
|
||||
conn['client_id'],
|
||||
panel_id=conn.get('xui_panel_id') or None,
|
||||
)
|
||||
vpn_link = config if str(config).startswith('http') else (generate_vpn_link(config) if config else '')
|
||||
return {
|
||||
'config': config,
|
||||
'vpn_link': vpn_link,
|
||||
'subscription_url': config if str(config).startswith('http') else '',
|
||||
}
|
||||
|
||||
sid = conn['server_id']
|
||||
server = data['servers'][sid]
|
||||
@@ -4284,12 +4341,24 @@ async def api_guest_create(token: str, req: GuestCreateRequest, request: Request
|
||||
try:
|
||||
if protocol_base(protocol) == 'xui':
|
||||
from managers.xui_api import xui_create_vless_config
|
||||
from managers.xui_servers import get_xui_server, ensure_xui_servers
|
||||
ensure_xui_servers(data.setdefault('settings', {}))
|
||||
panel = get_xui_server(data.get('settings') or {}, guest.get('create_xui_panel_id') or None)
|
||||
if not panel:
|
||||
return JSONResponse({'error': 'Add a 3x-ui server in Settings first'}, status_code=400)
|
||||
panel_id = panel.get('id') or ''
|
||||
created = await xui_create_vless_config(
|
||||
data.get('settings', {}),
|
||||
name=name,
|
||||
inbound_id=guest.get('create_inbound_id') or None,
|
||||
inbound_id=guest.get('create_inbound_id') or panel.get('default_inbound_id') or None,
|
||||
panel_id=panel_id,
|
||||
)
|
||||
result = {'client_id': created['client_id'], 'config': created.get('config') or ''}
|
||||
result = {
|
||||
'client_id': created['client_id'],
|
||||
'config': created.get('config') or '',
|
||||
'subscription_url': created.get('subscription_url') or '',
|
||||
'xui_panel_id': panel_id,
|
||||
}
|
||||
sid = 0
|
||||
protocol = 'xui'
|
||||
else:
|
||||
@@ -4323,6 +4392,7 @@ async def api_guest_create(token: str, req: GuestCreateRequest, request: Request
|
||||
'protocol': protocol,
|
||||
'client_id': result['client_id'],
|
||||
'name': name,
|
||||
'xui_panel_id': result.get('xui_panel_id') or '',
|
||||
'created_at': datetime.now().isoformat(),
|
||||
}
|
||||
async with DATA_LOCK:
|
||||
@@ -4331,11 +4401,13 @@ async def api_guest_create(token: str, req: GuestCreateRequest, request: Request
|
||||
save_data(data)
|
||||
|
||||
config = result.get('config') or ''
|
||||
vpn_link = generate_vpn_link(config) if config else ''
|
||||
subscription_url = result.get('subscription_url') or ''
|
||||
vpn_link = subscription_url or (config if str(config).startswith('http') else (generate_vpn_link(config) if config else ''))
|
||||
return {
|
||||
'status': 'success',
|
||||
'connection': _enrich_guest_conn(conn, data),
|
||||
'config': config,
|
||||
'subscription_url': subscription_url,
|
||||
'vpn_link': vpn_link,
|
||||
}
|
||||
except Exception as e:
|
||||
@@ -4378,6 +4450,7 @@ def _invite_public_view(link: dict) -> dict:
|
||||
'protocol': link.get('protocol') or 'xui',
|
||||
'server_id': int(link.get('server_id') or 0),
|
||||
'xui_inbound_id': int(link.get('xui_inbound_id') or 0),
|
||||
'xui_panel_id': link.get('xui_panel_id') or '',
|
||||
'duration_days': duration_days,
|
||||
'user_id': link.get('user_id') or '',
|
||||
'note': link.get('note') or '',
|
||||
@@ -4411,6 +4484,7 @@ async def _create_config_for_protocol(
|
||||
name: str,
|
||||
server_id: int = 0,
|
||||
xui_inbound_id: Optional[int] = None,
|
||||
xui_panel_id: Optional[str] = None,
|
||||
duration_days: int = 0,
|
||||
) -> dict:
|
||||
"""Create VPN client; returns {client_id, config, subscription_url, protocol, server_id}."""
|
||||
@@ -4419,12 +4493,19 @@ async def _create_config_for_protocol(
|
||||
if not xui_inbound_id:
|
||||
raise RuntimeError('Select a VLESS inbound for this invite link')
|
||||
from managers.xui_api import xui_create_vless_config
|
||||
from managers.xui_servers import get_xui_server, ensure_xui_servers
|
||||
ensure_xui_servers(data.get('settings') or {})
|
||||
panel = get_xui_server(data.get('settings') or {}, xui_panel_id)
|
||||
if not panel:
|
||||
raise RuntimeError('Add a 3x-ui server in Settings first')
|
||||
panel_id = panel.get('id') or ''
|
||||
expiry_ms = _expiry_ms_from_duration_days(duration_days)
|
||||
created = await xui_create_vless_config(
|
||||
data.get('settings', {}),
|
||||
name=name,
|
||||
inbound_id=xui_inbound_id,
|
||||
expiry_time=expiry_ms,
|
||||
panel_id=panel_id,
|
||||
)
|
||||
return {
|
||||
'client_id': created['client_id'],
|
||||
@@ -4433,6 +4514,7 @@ async def _create_config_for_protocol(
|
||||
'sub_id': created.get('sub_id') or '',
|
||||
'protocol': 'xui',
|
||||
'server_id': 0,
|
||||
'xui_panel_id': panel_id,
|
||||
'expires_at': (
|
||||
datetime.fromtimestamp(expiry_ms / 1000).isoformat()
|
||||
if expiry_ms > 0 else None
|
||||
@@ -4478,15 +4560,23 @@ async def invites_page(request: Request):
|
||||
if user['role'] not in ('admin', 'support'):
|
||||
return RedirectResponse(url='/my', status_code=302)
|
||||
data = load_data()
|
||||
from managers.xui_servers import list_xui_servers, get_xui_server, ensure_xui_servers
|
||||
ensure_xui_servers(data.setdefault('settings', {}))
|
||||
xui_servers = list_xui_servers(data.get('settings') or {})
|
||||
primary = get_xui_server(data.get('settings') or {}, None)
|
||||
any_sub = any((s.get('sub_url') or '').strip() for s in xui_servers)
|
||||
return tpl(
|
||||
request,
|
||||
'invites.html',
|
||||
invites=[_invite_public_view(x) for x in data.get('invite_links', [])],
|
||||
users=data.get('users', []),
|
||||
servers=data.get('servers', []),
|
||||
xui_configured=bool(((data.get('settings') or {}).get('sync') or {}).get('xui_url')),
|
||||
xui_sub_url=(((data.get('settings') or {}).get('sync') or {}).get('xui_sub_url') or ''),
|
||||
xui_default_inbound=int((((data.get('settings') or {}).get('sync') or {}).get('xui_inbound_id') or 0)),
|
||||
xui_servers=xui_servers,
|
||||
xui_configured=bool(xui_servers),
|
||||
xui_sub_url=(primary or {}).get('sub_url') or '',
|
||||
xui_has_sub_url=any_sub,
|
||||
xui_default_inbound=int((primary or {}).get('default_inbound_id') or 0),
|
||||
xui_default_panel_id=(primary or {}).get('id') or '',
|
||||
)
|
||||
|
||||
|
||||
@@ -4511,8 +4601,18 @@ async def api_create_invite(request: Request, req: InviteCreateRequest):
|
||||
return JSONResponse({'error': 'User not found'}, status_code=400)
|
||||
protocol = req.protocol or 'xui'
|
||||
inbound_id = int(req.xui_inbound_id or 0)
|
||||
if protocol_base(protocol) == 'xui' and not inbound_id:
|
||||
return JSONResponse({'error': 'Select a VLESS inbound'}, status_code=400)
|
||||
panel_id = (req.xui_panel_id or '').strip()
|
||||
if protocol_base(protocol) == 'xui':
|
||||
from managers.xui_servers import get_xui_server, ensure_xui_servers
|
||||
ensure_xui_servers(data.setdefault('settings', {}))
|
||||
panel = get_xui_server(data.get('settings') or {}, panel_id or None)
|
||||
if not panel:
|
||||
return JSONResponse({'error': 'Add a 3x-ui server in Settings first'}, status_code=400)
|
||||
panel_id = panel.get('id') or ''
|
||||
if not inbound_id:
|
||||
inbound_id = int(panel.get('default_inbound_id') or 0)
|
||||
if not inbound_id:
|
||||
return JSONResponse({'error': 'Select a VLESS inbound'}, status_code=400)
|
||||
link = {
|
||||
'id': str(uuid.uuid4()),
|
||||
'name': (req.name or 'Invite').strip() or 'Invite',
|
||||
@@ -4524,6 +4624,7 @@ async def api_create_invite(request: Request, req: InviteCreateRequest):
|
||||
'protocol': protocol,
|
||||
'server_id': int(req.server_id or 0),
|
||||
'xui_inbound_id': inbound_id,
|
||||
'xui_panel_id': panel_id,
|
||||
'password_hash': hash_password(req.password) if req.password else None,
|
||||
'expires_at': None,
|
||||
'duration_days': int(req.duration_days or 0),
|
||||
@@ -4560,6 +4661,8 @@ async def api_update_invite(request: Request, invite_id: str, req: InviteUpdateR
|
||||
link['server_id'] = int(req.server_id)
|
||||
if req.xui_inbound_id is not None:
|
||||
link['xui_inbound_id'] = int(req.xui_inbound_id)
|
||||
if req.xui_panel_id is not None:
|
||||
link['xui_panel_id'] = (req.xui_panel_id or '').strip()
|
||||
if req.duration_days is not None:
|
||||
if req.duration_days < 0:
|
||||
return JSONResponse({'error': 'duration_days must be >= 0'}, status_code=400)
|
||||
@@ -4698,6 +4801,7 @@ async def api_invite_create_config(token: str, req: InviteRedeemRequest, request
|
||||
name=name,
|
||||
server_id=int(link.get('server_id') or 0),
|
||||
xui_inbound_id=int(link.get('xui_inbound_id') or 0) or None,
|
||||
xui_panel_id=link.get('xui_panel_id') or None,
|
||||
duration_days=int(link.get('duration_days') or 0),
|
||||
)
|
||||
conn = {
|
||||
@@ -4707,6 +4811,7 @@ async def api_invite_create_config(token: str, req: InviteRedeemRequest, request
|
||||
'protocol': created['protocol'],
|
||||
'client_id': created['client_id'],
|
||||
'name': name,
|
||||
'xui_panel_id': created.get('xui_panel_id') or link.get('xui_panel_id') or '',
|
||||
'created_at': datetime.now().isoformat(),
|
||||
}
|
||||
async with DATA_LOCK:
|
||||
@@ -4759,9 +4864,17 @@ async def api_my_connection_config(request: Request, connection_id: str):
|
||||
|
||||
if protocol_base(conn.get('protocol', '')) == 'xui':
|
||||
from managers.xui_api import xui_get_config
|
||||
config = await xui_get_config(data.get('settings', {}), conn['client_id'])
|
||||
vpn_link = generate_vpn_link(config) if config else ''
|
||||
return {'config': config, 'vpn_link': vpn_link}
|
||||
config = await xui_get_config(
|
||||
data.get('settings', {}),
|
||||
conn['client_id'],
|
||||
panel_id=conn.get('xui_panel_id') or None,
|
||||
)
|
||||
vpn_link = config if str(config).startswith('http') else (generate_vpn_link(config) if config else '')
|
||||
return {
|
||||
'config': config,
|
||||
'vpn_link': vpn_link,
|
||||
'subscription_url': config if str(config).startswith('http') else '',
|
||||
}
|
||||
|
||||
sid = conn['server_id']
|
||||
if sid >= len(data['servers']):
|
||||
@@ -4788,12 +4901,21 @@ async def settings_page(request: Request):
|
||||
if not user:
|
||||
return RedirectResponse('/login')
|
||||
data = load_data()
|
||||
from managers.xui_servers import list_xui_servers, public_server_view, ensure_xui_servers
|
||||
settings = data.setdefault('settings', {})
|
||||
before = json.dumps(settings.get('xui_servers') or [], sort_keys=True, default=str)
|
||||
ensure_xui_servers(settings)
|
||||
after = json.dumps(settings.get('xui_servers') or [], sort_keys=True, default=str)
|
||||
if before != after:
|
||||
save_data(data)
|
||||
xui_servers = [public_server_view(s) for s in list_xui_servers(settings)]
|
||||
return tpl(
|
||||
request,
|
||||
'settings.html',
|
||||
settings=data.get('settings', {}),
|
||||
servers=data.get('servers', []),
|
||||
users=data.get('users', []),
|
||||
xui_servers=xui_servers,
|
||||
current_version=CURRENT_VERSION,
|
||||
)
|
||||
|
||||
@@ -5050,20 +5172,88 @@ async def api_xui_sync_delete(request: Request):
|
||||
|
||||
|
||||
@app.get('/api/settings/xui/inbounds', tags=["Settings"])
|
||||
async def api_xui_list_inbounds(request: Request):
|
||||
"""List VLESS inbounds from the configured 3x-ui panel."""
|
||||
async def api_xui_list_inbounds(request: Request, panel_id: str = ''):
|
||||
"""List VLESS inbounds from a 3x-ui panel (by panel_id or primary)."""
|
||||
if not _check_admin(request):
|
||||
return JSONResponse({'error': 'Forbidden'}, status_code=403)
|
||||
try:
|
||||
data = load_data()
|
||||
from managers.xui_api import xui_list_vless_inbounds
|
||||
inbounds = await xui_list_vless_inbounds(data.get('settings', {}))
|
||||
return {'inbounds': inbounds}
|
||||
from managers.xui_servers import ensure_xui_servers, get_xui_server
|
||||
ensure_xui_servers(data.setdefault('settings', {}))
|
||||
panel = get_xui_server(data.get('settings') or {}, panel_id or None)
|
||||
if not panel:
|
||||
return JSONResponse({'error': 'No 3x-ui servers configured'}, status_code=400)
|
||||
inbounds = await xui_list_vless_inbounds(data.get('settings', {}), panel_id=panel.get('id'))
|
||||
return {
|
||||
'inbounds': inbounds,
|
||||
'panel_id': panel.get('id'),
|
||||
'panel_name': panel.get('name'),
|
||||
'sub_url': panel.get('sub_url') or '',
|
||||
}
|
||||
except Exception as e:
|
||||
logger.exception("Error listing 3x-ui inbounds")
|
||||
return JSONResponse({'error': str(e)}, status_code=500)
|
||||
|
||||
|
||||
@app.get('/api/settings/xui/servers', tags=["Settings"])
|
||||
async def api_xui_list_servers(request: Request):
|
||||
if not _check_admin(request):
|
||||
return JSONResponse({'error': 'Forbidden'}, status_code=403)
|
||||
data = load_data()
|
||||
from managers.xui_servers import list_xui_servers, public_server_view, ensure_xui_servers
|
||||
ensure_xui_servers(data.setdefault('settings', {}))
|
||||
servers = [public_server_view(s) for s in list_xui_servers(data.get('settings') or {})]
|
||||
return {'servers': servers}
|
||||
|
||||
|
||||
@app.post('/api/settings/xui/servers', tags=["Settings"])
|
||||
async def api_xui_create_server(request: Request, req: XuiServerRequest):
|
||||
if not _check_admin(request):
|
||||
return JSONResponse({'error': 'Forbidden'}, status_code=403)
|
||||
try:
|
||||
data = load_data()
|
||||
from managers.xui_servers import upsert_xui_server, public_server_view, ensure_xui_servers
|
||||
ensure_xui_servers(data.setdefault('settings', {}))
|
||||
server = upsert_xui_server(data['settings'], req.model_dump())
|
||||
save_data(data)
|
||||
return {'status': 'success', 'server': public_server_view(server)}
|
||||
except Exception as e:
|
||||
logger.exception('Error creating 3x-ui server')
|
||||
return JSONResponse({'error': str(e)}, status_code=400)
|
||||
|
||||
|
||||
@app.put('/api/settings/xui/servers/{panel_id}', tags=["Settings"])
|
||||
async def api_xui_update_server(request: Request, panel_id: str, req: XuiServerRequest):
|
||||
if not _check_admin(request):
|
||||
return JSONResponse({'error': 'Forbidden'}, status_code=403)
|
||||
try:
|
||||
data = load_data()
|
||||
from managers.xui_servers import upsert_xui_server, public_server_view, get_xui_server, ensure_xui_servers
|
||||
ensure_xui_servers(data.setdefault('settings', {}))
|
||||
if not get_xui_server(data.get('settings') or {}, panel_id):
|
||||
return JSONResponse({'error': 'Server not found'}, status_code=404)
|
||||
server = upsert_xui_server(data['settings'], req.model_dump(), panel_id=panel_id)
|
||||
save_data(data)
|
||||
return {'status': 'success', 'server': public_server_view(server)}
|
||||
except Exception as e:
|
||||
logger.exception('Error updating 3x-ui server')
|
||||
return JSONResponse({'error': str(e)}, status_code=400)
|
||||
|
||||
|
||||
@app.delete('/api/settings/xui/servers/{panel_id}', tags=["Settings"])
|
||||
async def api_xui_delete_server(request: Request, panel_id: str):
|
||||
if not _check_admin(request):
|
||||
return JSONResponse({'error': 'Forbidden'}, status_code=403)
|
||||
data = load_data()
|
||||
from managers.xui_servers import delete_xui_server, ensure_xui_servers
|
||||
ensure_xui_servers(data.setdefault('settings', {}))
|
||||
if not delete_xui_server(data['settings'], panel_id):
|
||||
return JSONResponse({'error': 'Server not found'}, status_code=404)
|
||||
save_data(data)
|
||||
return {'status': 'success'}
|
||||
|
||||
|
||||
@app.get('/api/servers/{server_id}/{protocol}/clients', tags=["Connections"])
|
||||
async def api_get_server_clients(request: Request, server_id: int, protocol: str):
|
||||
if not _check_admin(request):
|
||||
|
||||
Reference in New Issue
Block a user