Commit Graph

3 Commits

Author SHA1 Message Date
187609480f 跨端: 鸿蒙修收信/自动已读/发件箱点开 + 页签条通透(用户报的四个问题)
用户报了四个问题,逐个实测复现 → 定位根因 → 修 → 设备复验:

① 「邮件点进去自动已读的能力不正常」
   根因:鸿蒙只有「标记已读」按钮(doMarkRead,对照 WebUI MailView.tsx:522
   那个手动按钮),缺 WebUI 的**自动**路径(MailView.tsx:55-65 的 useEffect:
   可见且 unread 就 markRead)。⇒ 点开邮件不变已读,必须再点按钮。
   修:MailDetailPage.loadMail 成功尾端按 `status==='unread'` 触发 doMarkRead
   (复用按钮那条路,因而天然带上「就地改 status」+「失败弹 toast」)。
   ★ 加 autoReadMailId 守卫:WebUI 靠 useEffect 依赖数组天然只跑一次,
     鸿蒙 loadMail 是显式调用的(下拉刷新会重跑),不守会重复打接口。

② 「接收邮件的能力也有点不正常」
   三个独立缺口,每个都会单独造成"收不到":
   a) **SSE 监听寿命**:原挂在 InboxTab.aboutToAppear,而三个 tab 是
      if/else 条件挂载的 ⇒ 切到发件箱/授权时 InboxTab 被销毁、监听跟着
      移除 ⇒ 在那两栏时收不到任何新邮件。搬到 MainPage(@Entry,全程在)。
   b) **只处理 new_mail**:WebUI 监听 5 种事件(sse.ts:7-13 的 EVENTS);
      服务端权限决策后发的是 session_update(permission.go:387)⇒
      "授权栏里处理过的申请,收件箱还是旧的样子"。补齐四种。
   c) **UTF-8 解码错**:arrayBufferToString 逐字节 String.fromCharCode
      (Latin-1 语义)⇒ 中文解成乱码。改用 util.TextDecoder + stream:true,
      且**每连接一个实例**(stream 会把半截汉字存在解码器内部,
      共享实例会把两个账号的半个字拼在一起)。

③ 「发件箱内容点不开」(第二轮;5483140 补了 onClick 仍点不开)
   根因:发件箱对单封组**既画组头又画行**——
       this.SentGroupHeader(g); if (isFlatGroup(g)) { this.SentRow(...) }
   组头那半张卡没有任何点击处理 ⇒ 点卡片上半部完全无反应。
   而 isFlatGroup 自己的注释写着「单封不成组:套一个可折叠的组头只是
   多一次点击」—— 实现与注释**直接相反**。收件箱一直是正确形状
   (if (isFlatGroup) { MailRow } else { 组头 + 子行 })。
   修:与收件箱取同形,单封组只画 SentRow。

④ 「通信页面我觉得没有 webui 那么通透」
   这是我自己上一轮改错的:把 WebUI 的「页签条无背景」实现成了
   `backgroundColor(Theme.surface)` 实心白。WebUI 的真实层叠是
   .comm-pane 是玻璃卡、CommTabs 在它内部且**自己无背景**(透出卡的白)。
   铺实心白 = 把玻璃卡换成横条白 ⇒ 壁纸再也透不过来。
   ⇒ 改回玻璃族(Theme.navMaterial,与底部导航条同档)+ 通栏 +
      只左上圆角(右上 0,与窗格那道弧重合)+ 底边线。
   判据 harmony-nav.test.mjs:732 在我改错时当场判红,是它先抓到的。

判据(变异验证:还原 bug → 必须判红)
- 新增「单封组不许既画组头又画行」:两栏的 (header, row) 对必须在
  else/三目里二选一。
  ★ 第一版写弱了(用 isFlatGroup(g) 作锚点往后切片,组头在切片之前
    ⇒ 变异测不红)。改成以**行调用**作锚点往两边开窗后,删掉修复
    即判红(实测已验)。
- 新增「列表行必须把 onClick 挂在自己身上」(MailRow/SentRow)。
- harmony-logic 34→37、harmony-nav 22(新增后仍绿)、
  harmony-appearance 27→28、animation-audit 12→15 的登记数同步。

设备复验(全部有实测凭据,不是推断)
- 自动已读:点开前 unread → 点开 4s 后服务端 read(连验两封)。
  列表组头 4→2、侧栏徽标 4→2,三处数字一致。
- 实时收信:App 保持前台不重启,从 gateway 发信 ⇒ 8s 内自动出现
  (新卡片 + 侧栏 2→3 + 页签 2→3 + 3 组 8 封)。
- 发件箱点开:点原先点不动的卡片上半区 ⇒ 右栏出正文 + 蓝色选中态。
- 页签条:截图确认为玻璃通透(不再是实心白横条)。
2026-09-24 16:34:44 +08:00
9c6e9c66ad 跨端: 修复: 鸿蒙客户端"连不上服务器"—— 地址补 /api/v1 + 失败分类成人话(含网络白名单固化)
症状
  鸿蒙客户端(client/harmony)连不上服务端,界面只显示"无法连接",用户无法自助;
  而服务端 HTTPS 完全正常:https://mail.jianfgit.xyz/health → 200、
  /api/v1/auth/me → 401、/api/v1/events/stream → 401(Let's Encrypt *.jianfgit.xyz,
  TLS 校验通过;代理与直连 http://127.0.0.1:8180 行为一致)。

根因(两条,逐条核实过,其中一条**推翻了原判断**)
  ① apiBase 是"API 前缀本身"(ApiClient 里拼的是 '/auth/login' 这类相对路径),
     而 Ui 只做 trim+去尾斜杠:用户若只填 `https://mail.jianfgit.xyz/`,请求就打到
     `https://mail.jianfgit.xyz/auth/login` ⇒ 404,客户端再把它压成"无法连接"。
     **这是本次故障最可能的直接原因。**
  ② 默认值 `http://192.168.2.60:8180/api/v1` 是明文 + 写死内网 IP:手机不在同网段
     就永远不通(mail.jianfgit.xyz 解析到的就是这台内网机)。
     ★ 但"鸿蒙默认禁止明文 HTTP"这条**不成立**,已按本机离线官方文档核实:
     `devecocli docs read .../使用HTTP访问网络/http-request` 的《明文HTTP访问权限配置说明》
     写明 cleartextTrafficPermitted "默认为 true",Network Kit 默认允许明文;
     另有 FAQ《Stage模型如何配置支持http明文传输》:"无需配置,支持HTTP明文传输数据"。
     ⇒ network_config.json 按"显式固化意图"处理(照文档形状写对,但**不冒充**它是修复)。
     本机 SDK @ohos.net.http.d.ts 里确实有 2300997 Cleartext traffic not permitted(since 18),
     所以那条错误码在客户端被建成一条可读提示,而不是被忽略。

改法
  · 新增 model/ApiBase.ts(纯逻辑,无 @ohos,判据能用 node 直接跑):
    normalizeApiBase(去尾斜杠**但不咬协议 //**、末尾没有 /api/v1 就补、已有的一字不动)、
    validateApiBase(自带修法的中文提示 + "公网明文才告警、内网明文不误报")、
    describeFailure(404 自己写文案并点名 /api/v1;其它状态码让服务端文案说话;
    网络层按 2300006/2300007/2300028/2300997/2300998/2300058-60-77 分类成人话)。
  · Config.ets:DEFAULT_API_BASE → https://mail.jianfgit.xyz/api/v1。
  · ApiClient.ets:setBase/init **都**过 normalizeApiBase(唯一闸口 ⇒ 老装机里已经存下的
    坏地址在读回时就治好,光改默认值救不了它);ApiError 带 nativeCode;错误路径改走
    describeFailure;404 的提示指向"地址少了 /api/v1"。
  · LoginPage.ets:地址先校验后持久化(不合法**不落库**、给可执行提示),明文警告常驻渲染;
    SettingsPage.ets:添加账号同样校验(多账号库直接喂 SseService,坏地址会让该账号的实时
    通道永久连不上);AccountManager/SseService 落库与建连时各自再归一化一次。
  · 新增 resources/base/profile/network_config.json:按官方文档形状把内网明文
    (192.168.2.60 / 10.0.2.2 / localhost)显式列进 domain-config 白名单。
    文档给的就是这个**固定路径与文件名**,不需要在 module.json5 里写引用
    (仓库里既有的 HomeAgent 工程同样是这么放的)。
  · 新增判据 test/harmony-apibase.test.mjs(13 条)并接进 run-all.mjs 的 SUITE:
    值判据**直接跑** model/ApiBase.ts;.ets 那几条是**静态**接线判据(本机无设备)。

验证(都真跑过)
  · cd client/harmony && devecocli build clean && devecocli build
    → BUILD SUCCESSFUL in 7 s 186 ms;entry/build/default/outputs/default/entry-default-signed.hap
    存在(1214592 B,15:14)。
  · 解包 HAP:resources/base/profile/network_config.json 在包里、JSON 可解析、
    cleartextTrafficPermitted=true 且白名单含 192.168.2.60/10.0.2.2/localhost;
    bundleName 仍是 com.jianf.agentmail,module.json 没有多余的 metadata/securityConfiguration。
  · devecocli check lint → 0 error;我改过的文件**零发现**(总工性从 8 降到 7,
    顺带修掉 LoginPage 一条既有的 await-thenable)。
  · node --experimental-strip-types --no-warnings --test test/harmony-apibase.test.mjs
    → ℹ tests 13 / pass 13 / fail 0。
  · 变异验证 11/11 全红且**红在对应那条**(在 /tmp 的独立 worktree 里做的,不碰共享树):
    归一化不补后缀、去斜杠咬掉协议、404 用通用文案、401 拿通用文案顶掉服务端原因、
    网络码不再分类、默认地址退回明文内网、setBase 直接赋值、登录页去掉守卫、
    内网白名单关掉明文、出现第二处自己拼 /api/v1、守卫变成空壳。
    (M8 第一次是**假绿**——只判了方法声明、没判调用点;改成切出 doLogin 方法体后再判才红。)

未验到(别把"编译过了"读成"连通了")
  · **没有**在真机/模拟器上点过一次登录:本机当前无设备在线,所以"真的连上了服务端"
    这件事本轮**未被验证**;已验证的只是"地址会被补成带 /api/v1 的形态""构建产物正确"。
  · network_config.json 的**实际效果**未验:按官方文档明文默认就允许,这份文件是显式固化,
    没有做"关掉它再对比"的实验(也无法在无设备时做)。
  · DNS/超时/证书这几条分类的文案是按 SDK 错误码写的,**没有构造真人故障去实测**
    (即没有真的把 DNS 打坏、把证书换成自签来看提示)。
  · 登录页那条"未 /api/v1 会 404"的因果链是从服务端路由 + 客户端拼接方式推出的,
    没有用 curl 对 `https://mail.jianfgit.xyz/auth/login` 实打一次取证。
  · test/run-all.mjs 在本机(node v24.14.1)**本来就是红的**:它只认 `# pass N`,
    而 node 24 打的是 `ℹ pass N` ⇒ 25 个文件里 21 个被记成"没自报条数"。
    这是既有环境漂移(已在 HEAD 的 worktree 里复现同样的红),**本次没有动它**,
    所以新判据虽然已登记进 SUITE,要等 runner 的 marker 解析修好才会被套件真正计数。
2026-09-15 15:15:17 +08:00
f9d757b5e5 chore: directory migration - gateway→server, web→client/electron 2026-09-08 19:16:35 +08:00