fix: Code Review P0/P1 batches 1-3 and single-operator risk acceptance
Apply sync/install/auth/schedule/retry/agent/settings fixes from full code review; document accepted WS and Agent legacy risks for solo ops. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -68,6 +68,7 @@ class InitDbRequest(BaseModel):
|
||||
|
||||
|
||||
class CreateAdminRequest(BaseModel):
|
||||
install_token: str
|
||||
db_host: str = "localhost"
|
||||
db_port: str = "3306"
|
||||
db_name: str = "Nexus"
|
||||
@@ -310,11 +311,29 @@ def _finalize_install_lock() -> None:
|
||||
logger.warning("Failed to remove install state file: %s", e)
|
||||
|
||||
|
||||
def _verify_install_token(provided: str) -> None:
|
||||
"""Require one-time install token issued at step 3 (init-db)."""
|
||||
if not provided or not provided.strip():
|
||||
raise HTTPException(status_code=403, detail="缺少安装令牌,请从步骤3重新开始")
|
||||
if not STATE_FILE.exists():
|
||||
raise HTTPException(status_code=403, detail="安装会话无效,请从步骤3重新开始")
|
||||
import json
|
||||
try:
|
||||
state = json.loads(read_utf8_text(STATE_FILE))
|
||||
except (json.JSONDecodeError, OSError) as exc:
|
||||
raise HTTPException(status_code=403, detail="安装会话无效,请从步骤3重新开始") from exc
|
||||
expected = state.get("install_token")
|
||||
if not expected or not secrets.compare_digest(provided.strip(), expected):
|
||||
raise HTTPException(status_code=403, detail="安装令牌无效或已过期")
|
||||
|
||||
|
||||
# ── Endpoints ──
|
||||
|
||||
@router.get("/status")
|
||||
async def install_status():
|
||||
"""Check whether Nexus is already installed."""
|
||||
if _is_locked():
|
||||
raise HTTPException(status_code=404, detail="Not found")
|
||||
return {
|
||||
"installed": _is_installed(),
|
||||
"locked": _is_locked(),
|
||||
@@ -542,6 +561,7 @@ async def init_db(req: InitDbRequest):
|
||||
|
||||
# Save install state for step 4
|
||||
import json
|
||||
install_token = secrets.token_urlsafe(32)
|
||||
state = {
|
||||
"db_host": req.db_host,
|
||||
"db_port": req.db_port,
|
||||
@@ -549,6 +569,7 @@ async def init_db(req: InitDbRequest):
|
||||
"db_user": req.db_user,
|
||||
# P2-19: Don't store db_pass in state file — only needed for _make_db_url()
|
||||
# which is called with the full CreateAdminRequest in step 4
|
||||
"install_token": install_token,
|
||||
"pool_size": pool_size,
|
||||
"max_overflow": max_overflow,
|
||||
"install_dir": install_dir,
|
||||
@@ -561,6 +582,7 @@ async def init_db(req: InitDbRequest):
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"install_token": install_token,
|
||||
"pool_size": pool_size,
|
||||
"max_overflow": max_overflow,
|
||||
"tables_created": 14,
|
||||
@@ -576,6 +598,8 @@ async def create_admin(req: CreateAdminRequest):
|
||||
if not _has_env():
|
||||
raise HTTPException(400, "请先完成步骤3:数据库初始化")
|
||||
|
||||
_verify_install_token(req.install_token)
|
||||
|
||||
if len(req.admin_password) < 6:
|
||||
raise HTTPException(400, "密码长度至少6位")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user