diff --git a/docs/changelog/2026-06-05-install-wizard-docker-redis.md b/docs/changelog/2026-06-05-install-wizard-docker-redis.md new file mode 100644 index 00000000..65a69e9d --- /dev/null +++ b/docs/changelog/2026-06-05-install-wizard-docker-redis.md @@ -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` 重建镜像。 diff --git a/server/api/install.py b/server/api/install.py index 6227bb26..31f0fc8c 100644 --- a/server/api/install.py +++ b/server/api/install.py @@ -12,7 +12,7 @@ import logging import subprocess from pathlib import Path from datetime import datetime, timezone -from urllib.parse import quote_plus +from urllib.parse import quote_plus, urlparse from fastapi import APIRouter, HTTPException from pydantic import BaseModel @@ -98,6 +98,36 @@ def _build_redis_url(req: InitDbRequest) -> str: 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: """Reuse keys pre-generated by Docker install (docker/.env.prod → compose env).""" import os @@ -413,16 +443,23 @@ async def env_check(): "pass": False, }) - # Redis + # Redis — Docker Compose: redis:6379 (not published on host 127.0.0.1) redis_ok = False redis_msg = "未安装" + rh, rp, rpass = _resolve_redis_probe() + redis_label = f"{rh}:{rp}" try: import redis as rlib 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() redis_ok = True - redis_msg = "✓ 已连接 127.0.0.1:6379" + redis_msg = f"✓ 已连接 {redis_label}" except Exception as e: redis_msg = f"连接失败: {e}" except ImportError: @@ -460,7 +497,11 @@ async def env_check(): }) 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") diff --git a/web/app/install.html b/web/app/install.html index 56fa0947..6d388933 100644 --- a/web/app/install.html +++ b/web/app/install.html @@ -639,6 +639,9 @@ function installWizard() { const d = await r.json(); this.envChecks = d.checks; this.envAllPass = d.all_pass; + if (d.docker_defaults) { + Object.assign(this.form, d.docker_defaults); + } } } catch(e) { this.error = '环境检测请求失败: ' + e.message;