Files
Nexus/server/infrastructure/btpanel/ssh_login.py
T
r 82426b1941 feat(btpanel): 独立宝塔模块与 SSH 自动获取 API
新增侧栏「宝塔面板」菜单、代理 API 与连接配置页;通过 SSH 读写子机
api.json 自动写入凭据,5 分钟后台重试,含审计修复、检测缓存与并发锁。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-21 09:13:14 +08:00

49 lines
1.7 KiB
Python

"""SSH-based Baota temporary login URL (tools.py)."""
from __future__ import annotations
import logging
import re
from urllib.parse import urlparse
from server.domain.models import Server
from server.infrastructure.ssh.asyncssh_pool import exec_ssh_command
logger = logging.getLogger("nexus.btpanel.ssh")
_BT_PANEL_TOOLS = "/www/server/panel/tools.py"
_URL_RE = re.compile(r"^https?://\S+/login\?tmp_token=\S+$", re.IGNORECASE)
SSH_TEMP_LOGIN_CMD = (
f"test -f {_BT_PANEL_TOOLS} && "
f"cd /www/server/panel && python3 tools.py get_temp_login_ipv4 2>/dev/null | tail -1"
)
DETECT_BT_CMD = "test -f /www/server/panel/class/common.py && echo BT_OK || echo BT_NO"
async def detect_bt_panel_installed(server: Server) -> bool:
result = await exec_ssh_command(server, DETECT_BT_CMD, timeout=20)
if result.get("status") != "success":
return False
return "BT_OK" in (result.get("stdout") or "")
def _rewrite_login_host(url: str, prefer_host: str) -> str:
prefer_host = prefer_host.strip()
if not prefer_host or not _URL_RE.match(url):
return url
parsed = urlparse(url)
port = parsed.port or (443 if parsed.scheme == "https" else 80)
return f"{parsed.scheme}://{prefer_host}:{port}{parsed.path}?{parsed.query}"
async def ssh_temp_login_url(server: Server) -> str:
result = await exec_ssh_command(server, SSH_TEMP_LOGIN_CMD, timeout=45)
if result.get("status") != "success":
raise RuntimeError(result.get("stderr") or result.get("stdout") or "SSH 执行失败")
line = (result.get("stdout") or "").strip().splitlines()[-1].strip()
if not _URL_RE.match(line):
raise RuntimeError(f"未获得有效临时登录链接: {line[:120]}")
return _rewrite_login_host(line, server.domain or "")