fix(install): auto-resolve 1Panel Redis auth (:pass@ / default)
Stop prefilling root; probe multiple Redis URL formats on credential check and persist the URL that works for init-db. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+50
-11
@@ -243,8 +243,9 @@ def _mysql_connect_error_detail(host: str, port: int) -> str:
|
||||
def _redis_auth_error_detail() -> str:
|
||||
if os.environ.get("NEXUS_DOCKER_WRITE_ENV") == "1":
|
||||
return (
|
||||
"Redis 认证失败。1Panel Redis:用户名填 root,密码填应用参数中的随机密码;"
|
||||
"主机为 1Panel-redis-xxxx(不是 127.0.0.1)。无 ACL 的旧版 Redis 用户名可留空。"
|
||||
"Redis 认证失败。1Panel:主机 1Panel-redis-xxxx;密码为应用参数;"
|
||||
"用户名通常留空(自动尝试 :pass@)或填 default。勿填 root(多数实例不支持)。"
|
||||
"诊断: bash deploy/test-1panel-redis.sh"
|
||||
)
|
||||
return "Redis 密码错误或需要 AUTH;请核对用户名/密码(无密码则留空)。"
|
||||
|
||||
@@ -337,6 +338,42 @@ def _test_redis_url(redis_url: str) -> tuple[bool, str]:
|
||||
return False, f"✗ Redis 连接失败: {e}"
|
||||
|
||||
|
||||
def _redis_url_candidates(req: InitDbRequest) -> list[tuple[str, str]]:
|
||||
"""Try order for 1Panel Redis (default user vs :pass@ vs explicit user)."""
|
||||
password = (req.redis_pass or "").strip()
|
||||
if not password:
|
||||
return [("无密码", _build_redis_url(req))]
|
||||
|
||||
user = (req.redis_user or "").strip()
|
||||
seen: set[str] = set()
|
||||
out: list[tuple[str, str]] = []
|
||||
|
||||
def add(label: str, candidate: InitDbRequest) -> None:
|
||||
url = _build_redis_url(candidate)
|
||||
if url in seen:
|
||||
return
|
||||
seen.add(url)
|
||||
out.append((label, url))
|
||||
|
||||
if user:
|
||||
add(f"用户 {user}", req)
|
||||
add("仅密码 (:pass@)", req.model_copy(update={"redis_user": ""}))
|
||||
if user.lower() != "default":
|
||||
add("用户 default", req.model_copy(update={"redis_user": "default"}))
|
||||
return out
|
||||
|
||||
|
||||
def _resolve_redis_url(req: InitDbRequest) -> tuple[bool, str, str | None]:
|
||||
"""Pick first working Redis URL among 1Panel-compatible auth variants."""
|
||||
last_msg = "✗ 未配置 Redis"
|
||||
for label, url in _redis_url_candidates(req):
|
||||
ok, msg = _test_redis_url(url)
|
||||
last_msg = msg
|
||||
if ok:
|
||||
return True, f"✓ Redis 连接正常({label})", url
|
||||
return False, last_msg, None
|
||||
|
||||
|
||||
def _docker_wizard_defaults() -> dict[str, str] | None:
|
||||
"""Prefill install step 3 when running inside docker-compose.prod.yml."""
|
||||
if os.environ.get("NEXUS_DOCKER_WRITE_ENV") != "1":
|
||||
@@ -351,7 +388,6 @@ def _docker_wizard_defaults() -> dict[str, str] | None:
|
||||
"redis_host": redis_host,
|
||||
"redis_port": "6379",
|
||||
"redis_db": "0",
|
||||
"redis_user": "root",
|
||||
}
|
||||
|
||||
|
||||
@@ -715,17 +751,16 @@ async def env_check():
|
||||
return payload
|
||||
|
||||
|
||||
async def _credential_checks(req: InitDbRequest) -> tuple[list[dict], bool]:
|
||||
async def _credential_checks(req: InitDbRequest) -> tuple[list[dict], bool, str | None]:
|
||||
"""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)
|
||||
redis_ok, redis_msg, redis_url = _resolve_redis_url(req)
|
||||
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
|
||||
return checks, mysql_ok and redis_ok, redis_url if redis_ok else None
|
||||
|
||||
|
||||
@router.post("/test-credentials")
|
||||
@@ -737,8 +772,11 @@ async def test_credentials(req: InitDbRequest):
|
||||
400,
|
||||
"系统已初始化,无法重复检测。请继续步骤 4 创建管理员,或修正 .env 后使用连接检测。",
|
||||
)
|
||||
checks, all_pass = await _credential_checks(req)
|
||||
return {"checks": checks, "all_pass": all_pass}
|
||||
checks, all_pass, redis_url = await _credential_checks(req)
|
||||
payload: dict = {"checks": checks, "all_pass": all_pass}
|
||||
if redis_url:
|
||||
payload["resolved_redis_url"] = redis_url
|
||||
return payload
|
||||
|
||||
|
||||
@router.get("/connection-check")
|
||||
@@ -768,16 +806,17 @@ async def init_db(req: InitDbRequest):
|
||||
_reject_if_env_exists()
|
||||
|
||||
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}"
|
||||
|
||||
checks, cred_ok = await _credential_checks(req)
|
||||
checks, cred_ok, redis_url = await _credential_checks(req)
|
||||
if not cred_ok:
|
||||
failed = next((c for c in checks if not c["pass"]), checks[0])
|
||||
raise HTTPException(
|
||||
400,
|
||||
failed.get("current") or "MySQL 或 Redis 连接未通过,请先检测账号密码",
|
||||
)
|
||||
if not redis_url:
|
||||
redis_url = _build_redis_url(req)
|
||||
|
||||
try:
|
||||
patch_aiomysql_do_ping()
|
||||
|
||||
Reference in New Issue
Block a user