feat(install): require MySQL/Redis credential check at wizard step 3
Add test-credentials API and UI gate before init-db writes .env. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+47
-12
@@ -233,6 +233,15 @@ def _mysql_connect_error_detail(host: str, port: int) -> str:
|
||||
return ""
|
||||
|
||||
|
||||
def _redis_auth_error_detail() -> str:
|
||||
if os.environ.get("NEXUS_DOCKER_WRITE_ENV") == "1":
|
||||
return (
|
||||
"Redis 密码错误或用户被禁用(1Panel Redis 请在步骤 3 填写应用参数中的密码;"
|
||||
"无密码则留空)。主机应为 1Panel-redis-xxxx,不是 127.0.0.1。"
|
||||
)
|
||||
return "Redis 密码错误或需要 AUTH;请核对 Redis 密码(无密码则留空)。"
|
||||
|
||||
|
||||
def _redis_connect_error_detail(host: str, port: int) -> str:
|
||||
if os.environ.get("NEXUS_DOCKER_WRITE_ENV") != "1":
|
||||
return ""
|
||||
@@ -310,6 +319,14 @@ def _test_redis_url(redis_url: str) -> tuple[bool, str]:
|
||||
detail = _redis_connect_error_detail(host, port)
|
||||
if detail:
|
||||
return False, detail
|
||||
lowered = err.lower()
|
||||
if (
|
||||
"invalid username-password" in lowered
|
||||
or "wrongpass" in lowered
|
||||
or "noauth" in lowered
|
||||
or "authentication required" in lowered
|
||||
):
|
||||
return False, _redis_auth_error_detail()
|
||||
return False, f"✗ Redis 连接失败: {e}"
|
||||
|
||||
|
||||
@@ -690,6 +707,32 @@ async def env_check():
|
||||
return payload
|
||||
|
||||
|
||||
async def _credential_checks(req: InitDbRequest) -> tuple[list[dict], bool]:
|
||||
"""Verify MySQL + Redis with credentials from install step 3 (TCP + AUTH)."""
|
||||
db_url = _make_db_url(req)
|
||||
redis_url = _build_redis_url(req)
|
||||
mysql_ok, mysql_msg = await _test_mysql_url(db_url)
|
||||
redis_ok, redis_msg = _test_redis_url(redis_url)
|
||||
checks = [
|
||||
{"name": "MySQL", "required": "可连接", "current": mysql_msg, "pass": mysql_ok},
|
||||
{"name": "Redis", "required": "可连接", "current": redis_msg, "pass": redis_ok},
|
||||
]
|
||||
return checks, mysql_ok and redis_ok
|
||||
|
||||
|
||||
@router.post("/test-credentials")
|
||||
async def test_credentials(req: InitDbRequest):
|
||||
"""Step 3: Test MySQL/Redis account password before init-db writes .env."""
|
||||
_reject_if_locked()
|
||||
if _has_env():
|
||||
raise HTTPException(
|
||||
400,
|
||||
"系统已初始化,无法重复检测。请继续步骤 4 创建管理员,或修正 .env 后使用连接检测。",
|
||||
)
|
||||
checks, all_pass = await _credential_checks(req)
|
||||
return {"checks": checks, "all_pass": all_pass}
|
||||
|
||||
|
||||
@router.get("/connection-check")
|
||||
async def connection_check():
|
||||
"""Step 4: Verify MySQL + Redis using .env written in step 3."""
|
||||
@@ -719,21 +762,13 @@ async def init_db(req: InitDbRequest):
|
||||
db_url = _make_db_url(req)
|
||||
redis_url = _build_redis_url(req)
|
||||
site_url = req.site_url.rstrip("/") if req.site_url else f"http://127.0.0.1:{req.api_port}"
|
||||
db_port = int(req.db_port)
|
||||
redis_port = int(req.redis_port)
|
||||
|
||||
if not _service_tcp_reachable(req.db_host, db_port):
|
||||
detail = _mysql_connect_error_detail(req.db_host, db_port)
|
||||
checks, cred_ok = await _credential_checks(req)
|
||||
if not cred_ok:
|
||||
failed = next((c for c in checks if not c["pass"]), checks[0])
|
||||
raise HTTPException(
|
||||
400,
|
||||
detail or f"无法连接 MySQL 服务器 {req.db_host}:{db_port}(TCP 不通,请确认服务已启动)",
|
||||
)
|
||||
|
||||
if not _service_tcp_reachable(req.redis_host, redis_port):
|
||||
detail = _redis_connect_error_detail(req.redis_host, redis_port)
|
||||
raise HTTPException(
|
||||
400,
|
||||
detail or f"无法连接 Redis 服务器 {req.redis_host}:{redis_port}(TCP 不通,请确认服务已启动)",
|
||||
failed.get("current") or "MySQL 或 Redis 连接未通过,请先检测账号密码",
|
||||
)
|
||||
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user