本提交为 .git 对象库损坏后的重建提交,内容等价于原先三个本地提交 (5e2fd4c2 / 57a53c98 / 5d7e1873)的累积结果,未做任何额外改动。 一、docs 结构整改(整改 #14) 根因:重构时新结构为 untracked 文件,执行 git stash(未带 -u)未纳入, 随后 git reset 拉回 HEAD 旧 tracked 树,导致旧树复活、新旧两棵目录 树并存于 docs/,共 791 文件、双分类体系冲突。 修复动作: - b2 同名异主题文件改名迁移保全 9 个 - C 类 39 个孤立文件按主题正确归类 - A/B1 类 222 个重复文件删除(新结构已有内容副本) - 9 个旧独有空目录删除 - 270 处内部引用按 verified 映射改写 - 整改记录 #14 登记于 04-运维文档/部署运维 结果:docs 791 → 569 文件,顶层仅规范 8 类 + 治理文件,单树恢复。 残留:约 20 处指向从未存在文件的陈旧死链,归入独立文档卫生任务。 二、compose 双目录对齐(消除踩坑 A) - docker-compose.yml:nginx 前端挂载全部由根目录 frontend-*/dist 改为 src/frontend-*/dist(h5 / agent / admin / terminal) - docker-compose.dev.yml:dev 服务 build context 与卷同步改 src/ - 效果:本地 docker compose up 不再把根目录 stale dist 挂回, 与线上一致,分叉隐患消除(已 docker compose config 校验通过) 防复发铁律: - 重构须提交;仓库修复须 git stash -u 或先 commit - 新结构须 git add 并提交,避免再次 untracked 复活 - H5 改动只动 src/frontend-h5/,禁改根目录遗留 frontend-*/
6.8 KiB
Release Notes v0.7.1
📅 发布日期:2026-06-23 | 类型:🔐 安全 + ✨ 功能 | 紧急度:🟡 重要 🔄 补充:2026-06-24 hotfix #116/#118/#119/#120 已部署(详见文末"补充 hotfix"小节)
🎯 本版本重点
- 修复扫码登录 iOS NSURLErrorCannotFindHost — 企微 App 用户扫码不再报错
- 完整 MFA + RBAC 权限体系 — 5 角色细粒度权限 + 高危操作二次认证
- H5 员工端企微 OAuth 登录 — 员工不再需要输账号密码
- 🆕 扫码登录去掉手机 confirm 步骤 — 企微 App 无确认 UI,扫码成功 = 直接登录
🔐 安全修复
#109 P0:扫码登录 NSURLError 修复
问题:用户企微 App 访问 https://itsupport.servyou.com.cn/itportal/,扫描二维码后跳转报 NSURLErrorCannotFindHost(iOS WebView DNS 解析失败)
根因:backend/app/config.py 新字段 qrcode_oauth_callback 用了 pydantic 默认行为,不剥环境变量前缀
- 字段名:
qrcode_oauth_callback - pydantic-settings 期望 env:
QRCODE_OAUTH_CALLBACK - 实际 env:
WECOM_QRCODE_CALLBACK(带前缀,匹配不上) - 结果:
settings.qrcode_oauth_callback = "",qrcode_service 走兜底返回相对路径/api/auth_qrcode/scan - iOS WebView 拼接相对路径失败 → DNS 解析错误
修复:
from pydantic import Field
class Settings(BaseSettings):
qrcode_oauth_callback: str = Field(
default="",
validation_alias="WECOM_QRCODE_CALLBACK",
)
部署:docker commit + restart + 验证,详见 phase1-progress#109-hotfix-二轮修复
RBAC 5 角色 × 4 资源 × 4 操作 × 3 范围
- 5 角色:super_admin / admin / agent / user / guest
- 4 资源:user / conversation / config / audit_log
- 4 操作:read / create / update / delete
- 3 范围:all / department / self
高危路由白名单 + 中间件
5 类高危操作需 OTP 二次认证:
role_change(角色变更)config_change(配置变更)data_export(数据导出)account_disable(账户禁用)account_create_reset(账户创建/重置)
✨ 新功能
后端扫码登录(端点 4 个)
| 端点 | 方法 | 说明 |
|---|---|---|
/api/auth_qrcode/create |
POST | 生成二维码(含 PNG base64) |
/api/auth_qrcode/poll/{ticket} |
POST | 扫码端轮询 |
/api/auth_qrcode/scan |
POST | 企微回调 |
/api/auth_qrcode/confirm |
POST | 用户确认登录 |
后端 MFA + pyotp(端点 5 个)
| 端点 | 方法 | 说明 |
|---|---|---|
/api/auth/otp-status |
GET | 查询绑定状态 |
/api/auth/otp-bind |
POST | 开始绑定(返回 secret + QR) |
/api/auth/otp-verify |
POST | 确认绑定 |
/api/auth/otp-verify |
POST | 验证 OTP |
/api/auth/otp-unbind |
POST | 禁用 MFA |
前端 MFA UI
MfaBind.vue— 绑定弹窗useHighRiskOtp.tscomposable — 高危操作 OTP 弹窗Login.vue/TopBar.vue— 集成 MFA 状态显示- 路由 + store 全链路集成
H5 员工端 OAuth 登录
- 部署路径:
https://itsupport.servyou.com.cn/itdesk/ - 自动 OAuth 静默授权(
snsapi_base) - 会话列表 + 消息收发
- 已完成 E2E 验证(#104)
🛠️ 部署要点
必备环境变量
# /opt/wecom-it-desk/.env
WECOM_QRCODE_CALLBACK=https://itsupport.servyou.com.cn/api/auth_qrcode/scan
WECOM_SSO_ENABLED=true
WECOM_SSO_CALLBACK_BASE=https://itsupport.servyou.com.cn
部署清单
- Backend commit
78f60c6(v0.7.1-dev) - docker 镜像
wecom-it-desk-backend:latest已 commit 新 config - 容器已重启 + /api/ready 200
- H5 dist 已部署到
/opt/wecom-it-desk/nginx/html/itdesk/ - nginx 已 reload
- /api/auth_qrcode/create 返回绝对 URL
- Gitea push(等用户重授权 token)
- 企微 App 实际扫码验证
⚠️ 已知问题
- Gitea push 阻塞:
workbuddy-claudetoken 2026-06-15 被吊销,需去 Gitea Web 重授权 - #108 still pending:v0.7.1-dev
78f60c6未推到 origin - /api/admin/ IP 白名单仍是 0.0.0.0/0(临时方案),v1.0 前必须收窄
📚 相关文档
- H5-DEPLOY-RUNBOOK-v0.7.1 — H5 部署 runbook
- DEPLOY-LOGIN-MIGRATION-v0.7.0 — 旧版扫码登录
- E2E-CHECKLIST-v0.7.0 — 端到端验收
- NGINX-DOMAIN-ROUTING — nginx 域名分发
- USER-GUIDE-QRCODE-MFA — 用户手册
- phase1-progress — 进展跟踪
📊 改动统计
- 代码:22 文件 / +4705 行
- 测试:78 passed + 4 xfail + 33 pre-existing failures(已分类)
- 文档:5 新增 / 3 更新
- Agent:4 并行 worktree 合并
🔄 补充 hotfix(2026-06-24 部署)
#116 P0 + #118 P0:扫码端点 405 + ticket≠state
问题:
- 坐席扫码登录报
405 Method Not Allowed - 企微 OAuth 回调走 GET(
?code=xxx&state=<ticket>),旧代码只支持 POST - 即使改双方法,
Optional[str] = None没Query()装饰器,FastAPI 当 body 参数处理 - 函数参数叫
ticket,企微用state,参数名不匹配
修复:backend/app/api/auth_qrcode.py scan 端点
@router.api_route("/scan", methods=["GET", "POST"], response_model=None)
async def scan_qrcode(
body: Optional[QrcodeScanRequest] = None,
ticket: Optional[str] = Query(None, description="兼容旧参数名"),
state: Optional[str] = Query(None, description="企微 OAuth state 标准参数名"),
code: Optional[str] = Query(None, description="企微 OAuth 授权码"),
redis_client = Depends(dep_redis),
):
if body is not None:
final_ticket, final_code = body.ticket, body.code
else:
final_ticket = state or ticket # 优先 state(企微标准),回退 ticket
final_code = code
部署:deploy-staging/hotfix-116-qrcode-scan-get/ — webcli v7 一键,20/20 pytest 过
#119:webcli v7 全自动部署链路
复用 jumpserver Playwright + Luna webcli + OTP 自动,实现 18-31KB 命令脚本单次输入,2-3 分钟跑完。
#120 P0:扫码成功 = 自动登录(去掉 confirm)
问题:企微 App 端没有"确认登录" UI,旧 confirm 步骤在生产永远走不通(用户报告"扫码成功后,手机端没有出现确认登录按钮")
用户决策:方案 A — 扫码成功 = 自动登录(推荐),2026-06-24 拍板
修复:backend/app/services/qrcode_service.py process_scan 默认 auto_confirm=True
- 扫码成功直接写
qrcode:confirm:{ticket}(含 token) - 前端 poll 立即拿到 status=confirmed + token
- 跳过手机 confirm 步骤
- 默认 roles=["user"],admin/agent 走 portal 端角色选择
测试:20/20 pytest 过(test_scan_then_poll_returns_confirmed + test_scan_auto_confirm_writes_confirm_key 等)
部署:deploy-staging/hotfix-120-qrcode-auto-confirm/ — webcli v7 一键,容器内 /api/ready 200 OK
残留:外部域名 https://itsupport.servyou.com.cn/api/ready 不通(nginx /api/ upstream 配错 + 8000 端口未暴露),等 #48 修复。容器内 backend 完全健康。