fix(install): probe redis via docker service host in env-check

Install wizard no longer hardcodes 127.0.0.1:6379; Docker installs get
mysql/redis prefills and a passing Redis check inside the nexus container.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Nexus Deploy
2026-06-06 02:31:11 +08:00
parent 9b7dba99ff
commit 5e7c5beb43
3 changed files with 76 additions and 5 deletions
@@ -0,0 +1,27 @@
# 2026-06-05 — 安装向导 Docker Redis 检测修复
## 摘要
修复 Docker 部署下安装向导步骤 2 误报 `127.0.0.1:6379 Connection refused`
Compose 栈中 Redis 仅在容器内网 `redis:6379`,未映射宿主机端口。
## 变更
- `server/api/install.py``env-check` 读取 `NEXUS_REDIS_URL` / `REDIS_HOST`
- 返回 `docker_defaults`db_host=mysql, redis_host=redis 等)供步骤 3 预填
- `web/app/install.html`:环境检测后自动应用 `docker_defaults`
## 验证
- Docker 容器内 `env-check` Redis 应对 `redis:6379` 显示 ✓
- 步骤 3 数据库主机应为 `mysql`Redis 为 `redis`
## 已部署容器热修
```bash
cd /opt/nexus && git pull
bash deploy/sync-install-wizard-to-container.sh
docker compose -f docker/docker-compose.prod.yml --env-file docker/.env.prod restart nexus
```
`nexus-1panel.sh upgrade` 重建镜像。
+46 -5
View File
@@ -12,7 +12,7 @@ import logging
import subprocess import subprocess
from pathlib import Path from pathlib import Path
from datetime import datetime, timezone from datetime import datetime, timezone
from urllib.parse import quote_plus from urllib.parse import quote_plus, urlparse
from fastapi import APIRouter, HTTPException from fastapi import APIRouter, HTTPException
from pydantic import BaseModel from pydantic import BaseModel
@@ -98,6 +98,36 @@ def _build_redis_url(req: InitDbRequest) -> str:
return url return url
def _resolve_redis_probe() -> tuple[str, int, str | None]:
"""Redis target for install wizard health check (Docker uses redis:6379, not host loopback)."""
url = os.environ.get("NEXUS_REDIS_URL", "").strip()
if url:
parsed = urlparse(url)
host = parsed.hostname or "127.0.0.1"
port = parsed.port or 6379
return host, port, parsed.password
host = os.environ.get("REDIS_HOST", "127.0.0.1")
port = int(os.environ.get("REDIS_PORT", "6379"))
return host, port, 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" and not os.environ.get("REDIS_HOST"):
return None
rh, rp, _ = _resolve_redis_probe()
return {
"db_host": os.environ.get("MYSQL_HOST", "mysql"),
"db_port": os.environ.get("MYSQL_PORT", "3306"),
"db_name": "nexus",
"db_user": "nexus",
"redis_host": rh,
"redis_port": str(rp),
"redis_db": "0",
}
def _install_keys_from_environment() -> tuple[str, str, str] | None: def _install_keys_from_environment() -> tuple[str, str, str] | None:
"""Reuse keys pre-generated by Docker install (docker/.env.prod → compose env).""" """Reuse keys pre-generated by Docker install (docker/.env.prod → compose env)."""
import os import os
@@ -413,16 +443,23 @@ async def env_check():
"pass": False, "pass": False,
}) })
# Redis # Redis — Docker Compose: redis:6379 (not published on host 127.0.0.1)
redis_ok = False redis_ok = False
redis_msg = "未安装" redis_msg = "未安装"
rh, rp, rpass = _resolve_redis_probe()
redis_label = f"{rh}:{rp}"
try: try:
import redis as rlib import redis as rlib
try: try:
r = rlib.Redis(host="127.0.0.1", port=6379, socket_timeout=0.5) r = rlib.Redis(
host=rh,
port=rp,
password=rpass or None,
socket_timeout=0.5,
)
r.ping() r.ping()
redis_ok = True redis_ok = True
redis_msg = "✓ 已连接 127.0.0.1:6379" redis_msg = f"✓ 已连接 {redis_label}"
except Exception as e: except Exception as e:
redis_msg = f"连接失败: {e}" redis_msg = f"连接失败: {e}"
except ImportError: except ImportError:
@@ -460,7 +497,11 @@ async def env_check():
}) })
all_pass = all(c["pass"] for c in checks) all_pass = all(c["pass"] for c in checks)
return {"checks": checks, "all_pass": all_pass} payload: dict = {"checks": checks, "all_pass": all_pass}
defaults = _docker_wizard_defaults()
if defaults:
payload["docker_defaults"] = defaults
return payload
@router.post("/init-db") @router.post("/init-db")
+3
View File
@@ -639,6 +639,9 @@ function installWizard() {
const d = await r.json(); const d = await r.json();
this.envChecks = d.checks; this.envChecks = d.checks;
this.envAllPass = d.all_pass; this.envAllPass = d.all_pass;
if (d.docker_defaults) {
Object.assign(this.form, d.docker_defaults);
}
} }
} catch(e) { } catch(e) {
this.error = '环境检测请求失败: ' + e.message; this.error = '环境检测请求失败: ' + e.message;