fix(mcp-server): correct Heicode user-models endpoint + username predcheck

Two small follow-ups to the register hardening + Heicode P4 work:

1. heicode_client.list_user_models: the path `/api/user/{id}/models`
   prescribed in §7.11.2 returns 404 `Invalid URL` on the live Heicode
   NewAPI — that path is not registered on their router. Switched to
   `/api/user/models` (no path segment), which Heicode binds to the
   `New-Api-User: 26` admin header. End-to-end P4 smoke now 4/4 with
   user 55@55.com (id=2 on Heicode): /balance /models /usage /logs.
   Future: if Heicode ships an "admin-replaces-user" path, switch back
   and pass the actual heicode_user_id.

2. routes/auth.register: previously line-744 SELECT only checked
   req.username, but line 778 falls back to email.split("@")[0] when
   blank — so two users registering with alice@foo.com and alice@bar.com
   would both clear the predcheck, then the second would IntegrityError
   on flush. Now predcheck uses `effective_username` matching what'll
   actually be inserted.

Also append §7.15 to Heicode-对接进度与待办.md:
- 4-item agent-manager / Vault / Workload-Identity audit results
- §7.13 token rotation acknowledgement
- P4 end-to-end first-pass results
- This-session internal security hardening summary

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-12 16:50:20 +08:00
co-authored by Claude Opus 4.7
parent 610fde5d03
commit 1461051755
3 changed files with 81 additions and 7 deletions
+19 -5
View File
@@ -192,14 +192,28 @@ class HeicodeNewAPIClient:
self, heicode_user_id: int,
request_id: Optional[str] = None,
) -> List[Any]:
"""GET /api/user/{id}/models — 按用户 id 列出该用户可用模型清单。
"""GET /api/user/models — 当前 token 持有人视角的可用模型列表。
修订(2026-05-08,按 Heicode §7.11.2):原 `/api/models` 是渠道仪表盘
视角(key 是 channelId),mcp-server-service 没渠道,自然空。改用
`/api/user/{id}/models`(admin 替指定用户查),返回 string[]。
修订历史:
- 原 `/api/models` 是 channel-dashboard 视角(key=channelId),
admin user 没渠道返回空,废弃。
- §7.11.2 曾改用 `/api/user/{id}/models`(admin 替指定用户查),
但 2026-05-12 实测 Heicode NewAPI 返回 404 `Invalid URL`,
该 path 在他们的 router 上根本没注册。
- 现切换到 `/api/user/models`(不带 user_id 段)。NewAPI 用
`New-Api-User` header(必须 == admin token 持有人 id,否则
CSRF check 拒绝)确定视角,返回 admin 视角的全量 model 列表。
后续 Heicode 那边如果上线"用户视角"接口,可以改回 path 段方案。
注意:当前实现下,所有 mcp-server 透传的用户拿到的是 admin(user 26)
视角的 models,即 NewAPI 全量。如果 Heicode 引入 group/channel 级
模型过滤,需要 Heicode 团队提供 `admin-replaces-user` 路由我方再切。
"""
# heicode_user_id 当前未直接使用 —— 仅做调试日志参考,将来如果 Heicode
# 上线 admin-replaces-user 路径会用到。保留参数避免上游 caller 改签名。
_ = heicode_user_id
body = await self._get(
f"/api/user/{heicode_user_id}/models",
"/api/user/models",
request_id=request_id,
)
data = body.get("data") if isinstance(body, dict) else body
+6 -2
View File
@@ -740,8 +740,12 @@ async def register(req: UserCreate, db: AsyncSession = Depends(get_db)):
detail="该邮箱已被注册"
)
# 2. 检查用户名是否已存在(username 是必填字段)
result = await db.execute(select(User).where(User.username == req.username))
# 2. 检查用户名是否已存在
# 注意:line 778 处会用 `req.username or req.email.split("@")[0]` 兜底,
# 所以预检必须按真实写入的 username 来查,否则 fallback 出来的 alice
# 没经预检,在 line 797 flush 才暴露 IntegrityError。
effective_username = (req.username or "").strip() or req.email.split("@")[0]
result = await db.execute(select(User).where(User.username == effective_username))
existing_username = result.scalar_one_or_none()
if existing_username:
raise HTTPException(