Files
wordpress-theme-aksrt/.zcode/plans/plan-sess_6a5711b4-7c56-4453-a8c1-805094668edc.md
T

96 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 第三方人机验证优化改造计划
约束:不写双路/兼容迁移代码;顺手清理死代码;不做真实视觉效果验收(用语法检查、契约测试、构建产物校验代替)。
已确认决策:范围全做(P0+P1+P2);下载场景 pass 放行 10 分钟;凭据不齐时仅加后台警告、前台保持放行。
## 改动文件总览
| 文件 | 动作 |
|---|---|
| `inc/modules/captcha.php` | 重写校验层 + 瘦身渲染 + 后台警告 + 静态缓存 |
| `assets/js/captcha-front.js` | 新建(从内联脚本迁移,~340 行) |
| `assets/css/shared/captcha.css` | 保留不动(改为进 bundle) |
| `tools/build-public-assets.mjs` | publicScripts 加 captcha-front.js;public.css 加 captcha.css |
| `src/Admin/Schema/SchemaPermissions.php` | 删除 'mode' 死字段(L32-34) |
| `assets/admin/src/schema/sections/permissions/index.js` | 删除 'mode' 死字段(L18 defaults、L50-56) |
| `src/Admin/SchemaProvider.php` | L403 白名单删 'mode';保存后 flush 配置静态缓存 |
| `tests/widget-system/captcha-cloud-contract.test.cjs` | 新建契约测试 |
不动的文件及原因:`aksrt-header-userbox.php`(toast-contract 测试锁定 signin 接线)、`data-aksrt-download-captcha` 挂载点(`templates/parts/resource-download-box.php:467`、`inc/helpers/aksrt-pay.php:1586`)、所有业务 JS(auth/signin/comments/cms-download/user-center/front-submit 只消费 `AKSRTCaptchaState.scenes[scene]` 与 mount 内 token input,接口不变即零改动)。
## 1. inc/modules/captcha.php — 校验层
**P0-1 腾讯云改走 WP HTTP API**:`aksrt_verify_tencent()` 删除 curl 块(L374-390,含 `CURLOPT_SSL_VERIFYPEER => false`),改用 `wp_remote_post('https://captcha.tencentcloudapi.com/', ['timeout'=>5, 'headers'=>aksrt_tencent_tc3_sign(...)生成的一维数组, 'body'=>$payload])`,与另外三家一致。签名函数不动。
**P1-5 错误信息收敛**:四家服务商的传输层错误(wp_remote_post 返回 WP_Error、响应不可解析)统一 `error_log('[AKSRT Captcha] {provider}: ' . 详情)` 后返回通用文案 `__('人机验证服务暂不可用,请稍后重试', 'aksrt')`;保留对用户有行动意义的映射文案(腾讯 CaptchaCode 表、阿里 F0xx 表)。腾讯 API Error 分支(签名错误等)也走 log+通用文案。
**P1-4**:阿里云 `SignatureNonce` 由 `uniqid('', true)` 改 `wp_generate_uuid4()`。
**P1-8**:四家 `'timeout'` 统一 5 秒(现 10 秒)。
**P1-6 Turnstile 绑定场景**:`aksrt_verify_captcha($post_data, $scene = '')` 增加第二参数(唯一调用方是 `aksrt_require_captcha`,无散落调用点);Cloudflare 成功响应校验:`action !== 'aksrt-verify'` 或 `cdata !== $scene`(存在才比对,lenient)→ 失败。客户端配套见 §2。
**P0-2 下载 pass 放行(10 分钟)**:`aksrt_require_captcha()` 内,scene 命中放行名单时先查 transient、验证通过后写 transient:
- `aksrt_captcha_pass_ttl($scene)`:仅 `download` 返回 `10 * MINUTE_IN_SECONDS`,其余返回 0(单一代码路径,无散落分支)
- key = `'aksrt_captcha_pass_' . md5($scene . '|' . get_current_user_id() . '|' . aksrt_get_client_ip() . '|' . md5(UA))`,仿 `aksrt_auth_rate_` 既有模式
## 2. assets/js/captcha-front.js — 新建(P2-9 + P1-7 客户端)
把 `aksrt_render_captcha()` 内联 `<script>`(L632-969)整体迁移为静态文件,逻辑不改名不重写:`loadScriptOnce`、四家 `load*`、`AKSRTCaptchaState.scenes[scene].verify/reset` 注册、`setField/resetVerificationState/syncDownloadSiblings` 等原样搬移。仅两处行为修正:
- 极验 `onClose` 空回调改为 `cancelPending('已取消验证')`(P1-7,与腾讯/CF 取消体验对齐)
- Turnstile render 配置加 `cdata: C.scene`(P1-6 客户端配套)
激活模型:扫描 `document.querySelectorAll('[data-aksrt-captcha-config]')`,`JSON.parse(el.getAttribute(...))` 逐个 init;DOMContentLoaded 后再补扫一次并暴露 `window.AKSRTCaptchaState.rescan()` 供未来 ajax 注入使用。保持 vanilla JS、无新依赖。
对应 PHP 渲染端瘦身:`aksrt_render_captcha()` 只输出
1. 原 wrapper 结构不变(`.aksrt-captcha-wrap` + `#uid` box + `#vid` captcha_verified 隐藏域),
2. wrapper 上挂 `data-aksrt-captcha-config`(`esc_attr(wp_json_encode($js_config))`),
3. 删除内联 `<script>` 与逐次 `<link rel="stylesheet">`。
TOKEN_FIELDS 常量、i18n 字符串原样进 JS 文件(与仓库其它 bundle 内硬编码中文一致)。
## 3. 构建与后台(P2-10 + bundle 接线)
- `tools/build-public-assets.mjs`:`publicScripts` 追加 `'assets/js/captcha-front.js'`;`cssBundles['public.css']` 追加 `'assets/css/shared/captcha.css'`。走既有 bundle 版本机制,无需新增 enqueue 代码。
- `SchemaPermissions.php`:删除 'mode' 字段定义(L32-34,单选项 always 的死字段)。
- `assets/admin/src/schema/sections/permissions/index.js`:删除 `mode` 的 defaults(L18)与字段定义(L50-56)。
- `SchemaProvider.php` L403:归一化白名单去掉 `'mode'`;`write_section_settings()` 更新 `aksrt_captcha_config` 后调用新助手 `aksrt_captcha_config_reset()` 清静态缓存(function_exists 守卫,与现有 captcha 专用块并排)。
- DB 里残留的 `mode` 键不做迁移清洗(读路径白名单已忽略它,符合"不做迁移"约束)。
## 4. inc/modules/captcha.php — 后台警告与缓存
- **P0-3**:新增顶层 `add_action('admin_notices', ...)`:`manage_options` 权限 + `enabled` 为真 + `active_provider` 非空 + `aksrt_captcha_provider_is_configured()` 为假时,输出 `notice-error` 横幅,文案指向「功能&权限 → 人机验证」补全配置(复用现有 misconfig 提示文案)。前台行为不变(保持放行)。
- **P2-11**:`aksrt_get_captcha_config()` 加 `static $cache`;新增 `aksrt_captcha_config_reset()`;写入路径(SchemaProvider)调用 reset。`aksrt_get_captcha_status_summary()` 的 12 场景循环随之受益。
## 5. 新契约测试 tests/widget-system/captcha-cloud-contract.test.cjs
仿 toast-contract 的正则断言风格:
- `captcha.php`:不匹配 `curl_init|CURLOPT_SSL_VERIFYPEER|machine-verification|slidercaptcha|imagecaptcha`;匹配 `wp_remote_post`(≥4 处)、`wp_generate_uuid4`、`'timeout'\s*=>\s*5`、`aksrt_captcha_pass_`;渲染函数不匹配 `/<script|<link/`(内联抽取完成)
- `captcha-front.js` 存在且含 `AKSRTCaptchaState`、`data-aksrt-captcha-config`
- `build-public-assets.mjs` 的 publicScripts 含 captcha-front.js、public.css 含 shared/captcha.css
- `SchemaPermissions.php` 与 admin `index.js` 的 captcha 段不匹配 `mode`
- `SchemaProvider.php` 不匹配 `'mode'`
## 6. 验证(不做视觉验收)
1. `php -l` 全部改动的 PHP 文件
2. `node --check` captcha-front.js
3. `node tools/build-public-assets.mjs` 重建 bundle
4. `grep` 全仓清扫:无 `curl_init`、无 `'mode'`(captcha 相关文件)、无 legacy 标记
5. `node --test tests/widget-system/*.test.cjs` 全量跑,预期仅存量 page-display-contract 一项失败(与本次无关,已验证在 HEAD 上同样失败)
## 明确不做
- 不做视觉验收/浏览器实测(按要求)
- 不写新旧配置迁移、不清洗 DB 残留 `mode` 键
- 不动腾讯 trerror 容灾票据策略(维持拒绝)
- 不做阿里云签名 V3 升级、Turnstile appearance/retry 配置项(列为后续可选项)
- 不动 `ExtensionController` 的 extensions 状态端点(SPA 未消费属另一话题)
## 建议提交拆分(如需提交)
1. `fix(captcha): use WP HTTP API with TLS verification for tencent`
2. `feat(captcha): short-lived download pass + turnstile binding + error hardening`
3. `refactor(captcha): extract inline runtime to captcha-front.js bundle`
4. `chore(captcha): drop dead mode field, config cache, contract tests`