Files
Commilitia-Drop/CHANGELOG.src.html
T
admin b414c57b6a docs:CHANGELOG 补 2026-06-16 浏览器免重登 + 2026-06-15 PWA / Safari 修复
- 浏览器免重登:HttpOnly cookie + 服务端 web_sessions 会话表 + refresh_token
  AES-256-GCM 落盘 + 7 天滑动失活 + 设备名随会话持久化 + striped 锁防多 tab
  并发 refresh;前端 store 移除 refreshToken 字段;prod 强制 CDROP_SESSION_SECRET(e51666c)
- PWA 安装支持 + Safari OAuth 回跳卡顿(改整页跳转)修复(5c28ddf / 7082ccc /
  af754dc / 8f550ad)
- HTML 与 md twin 同步,build.sh 重建 CHANGELOG.html
2026-06-16 01:11:43 +08:00

194 lines
19 KiB
HTML
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.
<!DOCTYPE html>
<html lang="zh-Hans">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>cdrop — Changelog</title>
<meta name="generated-at" content="2026-06-16">
<meta name="source" content="cdrop · docs branch">
<!-- RUNTIME_CSS -->
</head>
<body>
<header class="page">
<div class="meta">
<h1>cdrop — Changelog</h1>
<p class="subtitle">时间倒序。细粒度迭代见 git 历史;本档只记录结构性 / 行为性变更与重要 fix。</p>
<p class="timestamp"><time>2026-06-16</time> · <span>docs branch</span></p>
</div>
<div class="actions">
<!-- ACTIONS -->
</div>
</header>
<main>
<p class="lede">本 Changelog 自 2026-05-22 起以 HTML 格式维护。此前之变更可从 main 分支 <code>git log</code> 取得;下表为近期较显著之 commit。</p>
<h2>2026-06-16 — 浏览器免重登 + 设备名持久化</h2>
<p>浏览器端不再每次重启都要重新登录;长寿命 refresh_token 从此完全不进 JS,改由服务端 HttpOnly cookie 会话持有。对长寿命凭据而言比原先(refresh_token 在 JS 内存、开页期 XSS 可读)严格更安全。</p>
<ul>
<li><strong>服务端会话表 + cookie</strong>:新增 <code>web_sessions</code> 表(sqlc)。<code>/auth/exchange</code> 换完 token 后建会话并下发 cookie<code>HttpOnly; Secure; SameSite=Lax; Path=/api/auth</code>),响应体不再回传 refresh_tokenrefresh_token 以 AES-256-GCM 落盘(密钥取自 <code>CDROP_SESSION_SECRET</code>,在 env、不在库内),cookie 只放不透明随机串、表 id 存其 SHA-256 —— DB 单独泄露既换不出可用 cookie、也解不出 token</li>
<li><strong>开机静默免重登</strong><code>/auth/refresh</code> 改为 cookie 驱动(7 天滑动失活)—— 这条同时就是开机静默免重登路径,前端首屏渲染前 <code>bootstrapAuth</code> 用 cookie 续期,命中直接进已登录态、避免登录页闪现;新增 <code>/auth/logout</code>(删会话 + 清 cookie)、<code>/auth/device</code>(写设备名入会话)</li>
<li><strong>设备名持久化</strong>:设备名随会话存,开机 refresh 带回前端,抗 iOS PWA 存储清除 —— localStorage 被驱逐也能恢复,不再误跳设置页</li>
<li><strong>多 tab 并发 refresh 防护</strong>:同一用户多 tab 共享一个 cookie → 一个 refresh_token,加 striped per-session 锁串行化并发 refresh,防一次性 refresh_token 被花两次而把用户从所有 tab 一起登出</li>
<li><strong>凭据面收紧</strong>:前端 store 彻底移除 <code>refreshToken</code> 字段,浏览器侧长寿命凭据不再触及 JS。CSRF 由 SameSite=Lax + Origin 校验双保险。桌面端不受影响(自有 loopback flow + 系统密钥库,不碰这两端点)</li>
<li><strong>部署要求</strong>prod 须设 <code>CDROP_SESSION_SECRET</code>(任意长度高熵串,内部 SHA-256 派生 AES-256 密钥),缺则拒启动</li>
<li>commit <code>e51666c</code></li>
</ul>
<h2>2026-06-15 — PWA 安装支持 + Safari OAuth 回跳卡顿修复</h2>
<ul>
<li><strong>PWA 安装支持</strong>:web 可「添加到主屏幕」独立启动(manifest + iOS meta + 同源静态壳 service worker)。SW 绝不拦截 <code>/api</code>(SSE / 剪贴板 / 中继需活连接)、亦不拦导航;PKCE verifier 改 <code>localStorage</code>,使独立 PWA 在 OAuth 回跳重启上下文后仍能完成登录。面向用户短名统一小写 <code>cdrop</code></li>
<li><strong>Safari OAuth 回跳卡顿修复</strong>Safari / iOS PWA 从 IdP 回跳登录后,<code>setup.js</code> 与头像约 2 分钟拿不到连接(Chrome 正常)。根因是 <code>oauth.callback</code> 登录成功后用路由软导航,复用了 OAuth 流程遗留的 HTTP 连接,这些连接在 Safari 下卡死后续资源请求;改为整页跳转(<code>window.location.replace</code>)拿全新连接绕开。曾误判 service worker / h3 为主因,均已排除</li>
<li>commit <code>5c28ddf</code>PWA 安装)/ <code>7082ccc</code>(短名)/ <code>af754dc</code> · <code>8f550ad</code>Safari 修复)</li>
</ul>
<h2>2026-06-14 — 安全加固:中继会话防耗尽 + 令牌入系统密钥库 + 鉴权 / 限流多项收紧</h2>
<p>多 agent 安全审计后的一轮加固,按信任模型(信任账号所有者 + 服务端;不信任跨用户 / 网络 hop peer / 本机其他软件)逐项收紧。</p>
<ul>
<li><strong>中继会话防耗尽重构</strong>:中继改为「预登记」模型 —— 传输进入 <code>RELAY_ACTIVE</code> 时由服务端登记其两个合法参与方,之后仅已登记会话可接入、且调用方须为 sender / receiver。叠加 per-user 配额(3)+ 全局上限(8+ 空闲回收(2min+ ring 单 reader 断言。一举堵住「任意 id 凭空铸造会话耗尽全实例并钉 512 MiB」「幽灵会话」「id 泄露后无第二道防线」「双 reader 撕裂数据」四类问题</li>
<li><strong>桌面 refresh_token 入系统密钥库</strong>:长效 refresh_token 不再明文落盘,改存系统密钥库(macOS Keychain 经 <code>security</code> / Windows Credential Manager·DPAPI / Linux Secret Service),同机软件无法直读冒充;会话文件只留短效 access_token。遗留明文文件首次启动自动迁移并抹除</li>
<li><strong>HS256 token 强制 jti</strong>HS256 路径只签 clipboard 限域的快捷指令 token(必带 jti),无 jti 的有效签名一律拒签,把密钥泄露的爆炸半径压回 clipboard-only</li>
<li><strong>prod 强制 OIDC audience</strong>prod 下 <code>CDROP_OIDC_AUDIENCE</code> 为空即拒绝启动 —— 空 audience 会跳过校验、令同一 JWKS 下兄弟应用的 token 也能验过(跨 app 冒充)。<strong>部署要求</strong>:prod 须设该变量(web + 桌面 client_id 逗号分隔)</li>
<li><strong>请求面收紧</strong>:信令 / 文本消息加请求体上限(防 OOM);内置 TURN 凭据 TTL 由 24h 降至 1h(缩泄露窗口 24×);<code>/auth/exchange</code><code>/auth/refresh</code> 加 per-IP 限流(防把服务当 OIDC 暴力跳板)</li>
<li>commit <code>bcdc2b4</code>(中继 / HS256 / 密钥库 / body 上限)·<code>c536a35</code>audience / TURN TTL / 限流)</li>
</ul>
<h2>2026-06-14 — 桌面文件落盘可配置 + 传输列表行内操作 + macOS 下载兜底修正</h2>
<ul>
<li><strong>桌面端接收文件落盘到可配置目录</strong>(默认系统 <code>~/Downloads</code>):设置页加「下载目录」+ 原生目录选择框(<code>ChooseDownloadDir</code> → Wails <code>OpenDirectoryDialog</code>)。机制为 Go 绑定写盘 —— 桌面模式下 <code>downloadBlob</code> 改走 <code>SaveDownload</code>blob → base64 过桥 → Go 写盘),对端文件名经 <code>sanitizeFileName</code> 剥目录成分防路径穿越、重名加 <code>(n)</code>、落盘后 <code>open -R</code> / <code>explorer /select</code> 揭示</li>
<li><strong>macOS 下载兜底修正</strong>WKWebView 不处理 <code>&lt;a download&gt;</code><code>blob:</code>,原「失败回退浏览器下载」在 macOS 是空操作、会静默丢文件;改为写配置目录失败时自动改写系统 <code>~/Downloads</code>,连默认目录也失败才显式报错(<code>toast.error</code>),不再丢文件</li>
<li><strong>传输列表行内操作</strong>:每行加 —— 非终态「取消」(拆本端 P2P / relay + <code>POST /transfer/{id}/cancel</code>)、终态「删除」(从 active + history 移除)、发送方仍在试 P2P 时「立即中继」(清 30s ICE watchdog + <code>POST /transfer/{id}/fallback</code>,跳过等待)</li>
<li>双端桌面客户端(macOS universal <code>.app</code> + Windows <code>.exe</code>)重构并同步;web 端 prod 部署(<code>drop.commilitia.net</code></li>
<li>commit <code>43ddf36</code>(下载路径)/ <code>10a4c99</code>(传输 UX + 兜底修正)</li>
</ul>
<h2>2026-06-12 — 接入聚珍(Juzhen2.0 综合 CJK 排版:全角式 + 功能分级 + 本地优先字体</h2>
<ul>
<li>submodule <code>cjk-autospace</code> 升级 <code>a5faec5 → a3c94dd</code>:v1 自动空格 shim 从零重写为「聚珍(Juzhen)」2.0 综合排版引擎 —— 中西间隙 / 标点挤压(全角式)/ 禁则避头尾 / 垂悬字避免 / 长英文断词 / 功能分级;JS 只注入 <code>jz-*</code> 结构、视觉尺寸全在 <code>juzhen.css</code>(必配)</li>
<li><strong>功能分级</strong>:功能分文本级(中西间隙 / 标点挤压,任意文本片段适用)与段落级(禁则 / 垂悬字 / 长词断字,需行段布局,对单行 UI 文本无意义甚至有害)。薄壳 <code>level:{text:".juzhen"}</code> 令全树默认文本级(cdrop 绝大多数是 UI chrome);消息正文 / 剪贴板内容 / Callout / Toast 正文 / Field hint / 登录说明等 8 处流动正文加 <code>data-jz-level="paragraph"</code> opt-in 段落级,失败模式良性</li>
<li><strong>字体逐 region 明确化</strong><code>fonts.css</code><code>:lang()</code> 切 SC/TC/JP/KR):Sans Orbit Gothic CJKSerif Noto Serif CJK(本地合一)+ Noto Serif &lt;R&gt;(Web 子集、跨区并集覆盖);Mono Maple Mono + Orbit Gothic CJK</li>
<li><strong>本地优先字体层</strong>(新增 <code>fontface.css</code>):CDN 的 <code>@font-face</code><code>local()</code> 且 family 名与本机安装同名 → 被捕获、永远走网络。本层在 CDN <code>&lt;link&gt;</code> 之后用 <code>src: local(), url()</code> 重声明项目用到的字重,本机命中即零网络、否则回退 CDN(swap)</li>
<li>Badge 加固(children 包单个 inline span,防聚珍注入元素在 <code>inline-flex</code> gap 中各成 flex item 致 gap 叠加);术语「Pangu」统一改「聚珍」;commit <code>907d132</code> / <code>b3c74a6</code></li>
</ul>
<h2>2026-05-22 — README 补 clone 注意事项</h2>
<ul>
<li>新 clone 之 repo 预设不自动 fetch submodule<code>web/src/lib/cjk-autospace/</code> 为空时 <code>vite build</code> 会因找不到 import 而失败;错误信息虽含路径但不直观</li>
<li><code>README.md</code> 顶部加「克隆」小节,指引 <code>git clone --recurse-submodules &lt;URL&gt;</code> 或补拉 <code>git submodule update --init --recursive</code></li>
<li>commit <code>1443543</code></li>
</ul>
<h2>2026-05-22 — cjk-autospace 抽出为共用 submodule</h2>
<ul>
<li>把原内联在 <code>web/src/utils/cjkAutospace.ts</code> 的 Pangu shim 抽出为上游 git 仓库 <a href="https://git.commilitia.net/admin/cjk-autospace">cjk-autospace</a>(双 pass 架构:跨样式标签透明 + 行内块边界,commit <code>c3defc4</code></li>
<li>以 git submodule 引入 <code>web/src/lib/cjk-autospace/</code><code>cjkAutospace.ts</code> 缩为 ~50 行薄壳,只负责 cdrop 的 <code>pillSelector="code, kbd, .cjk-pill"</code> 配置 + React 下的 MutationObserver 增量观察(自反馈防护、<code>queueMicrotask</code> 防同 batch 重排)</li>
<li>单一真实源:lib 修改一次三端(HTML 模板 / Peek.nvim shim / cdrop)同步生效;本仓库 deploy 时 Vite 把 submodule 检出之文件当一般 TS / JS 模块打包,运行时不读 Dropbox 也不依赖 git 元数据</li>
<li>架构文档(<code>PROJECT_BRIEF.md</code> + <code>FRONTEND_DESIGN.md</code>+ Changelog 移到 docs 分支(本档)</li>
</ul>
<h2>历史变更(移转前未以本档记录)</h2>
<table>
<thead>
<tr><th>commit</th><th>说明</th></tr>
</thead>
<tbody>
<tr><td><code>aa80793</code></td><td>CJK 排版子系统:Pangu.js v7.2.1 端口 + cjk.css + Button label 包裹</td></tr>
<tr><td><code>4e8c541</code></td><td>前端视觉重塑后续迭代 + i18n 本地化清理</td></tr>
<tr><td><code>81dfbe1</code></td><td>前端重设计:tokens 变量化 + features-first + 原子组件 + 首页重画 + Toast + Dracula Pro 主题</td></tr>
<tr><td><code>6db6444</code></td><td>新增繁体中文(臺灣)+ locale 检测按 Hans / Hant + 区域子标签细分</td></tr>
</tbody>
</table>
</main>
<footer class="page">
<p>cdrop · docs branch · CHANGELOG</p>
</footer>
<!-- RUNTIME_SPRITE -->
<script type="text/markdown" id="md-source">
# cdrop Changelog
时间倒序细粒度迭代见 git 历史本档只记录结构性 / 行为性变更与重要 fix
## 2026-06-16 浏览器免重登 + 设备名持久化
浏览器端不再每次重启都要重新登录长寿命 refresh_token 完全不进 JS改由服务端 HttpOnly cookie 会话持有比原先 refresh_token JS 内存严格更安全
- **服务端会话表 + cookie**新增 `web_sessions` sqlc`/auth/exchange` 建会话并下发 `HttpOnly; Secure; SameSite=Lax; Path=/api/auth` cookie响应体不再回传 refresh_tokenrefresh_token AES-256-GCM 落盘密钥 `CDROP_SESSION_SECRET` env不在库内cookie 只放不透明随机串 id 存其 SHA-256 DB 单独泄露既换不出可用 cookie也解不出 token
- **开机静默免重登**`/auth/refresh` cookie 驱动7 天滑动失活前端首屏前 `bootstrapAuth` 续期命中直接进已登录态新增 `/auth/logout``/auth/device`写设备名入会话
- **设备名持久化**设备名随会话存开机 refresh 带回前端 iOS PWA 存储清除localStorage 被驱逐也能恢复不再误跳设置页
- ** tab 并发 refresh 防护** tab 共享一个 cookie 一个 refresh_tokenstriped per-session 锁串行化并发 refresh防一次性 token 被花两次而全端登出
- **凭据面收紧**store 移除 `refreshToken` 字段浏览器侧长寿命凭据不再触及 JSCSRF SameSite=Lax + Origin 校验双保险桌面端不受影响
- **部署要求**prod 须设 `CDROP_SESSION_SECRET`任意长度高熵串缺则拒启动
- commit `e51666c`
## 2026-06-15 PWA 安装支持 + Safari OAuth 回跳卡顿修复
- **PWA 安装支持**web 添加到主屏幕独立启动manifest + iOS meta + 同源静态壳 SWSW 绝不拦截 `/api`亦不拦导航PKCE verifier `localStorage`使独立 PWA OAuth 回跳重启上下文后仍能完成登录短名统一小写 `cdrop`
- **Safari OAuth 回跳卡顿修复**Safari / iOS PWA 回跳登录后 `setup.js` 与头像约 2 分钟拿不到连接Chrome 正常根因是 `oauth.callback` 成功后路由软导航复用了 OAuth 流程遗留的 HTTP 连接 Safari 下卡死后续资源改为整页跳转`window.location.replace`拿全新连接绕开
- commit `5c28ddf`PWA/ `7082ccc`短名/ `af754dc` · `8f550ad`Safari 修复
## 2026-06-14 安全加固中继会话防耗尽 + 令牌入系统密钥库 + 鉴权 / 限流多项收紧
agent 安全审计后的一轮加固按信任模型信任账号所有者 + 服务端不信任跨用户 / 网络 hop peer / 本机其他软件逐项收紧
- **中继会话防耗尽重构**中继改为预登记模型 传输进入 `RELAY_ACTIVE` 时服务端登记两个合法参与方之后仅已登记会话可接入且调用方须为 sender / receiver叠加 per-user 配额3+ 全局上限8+ 空闲回收2min+ ring reader 断言堵住任意 id 铸造会话耗尽实例并钉 512 MiB幽灵会话id 泄露 reader 撕裂四类
- **桌面 refresh_token 入系统密钥库**长效令牌不再明文落盘改存 macOS Keychain / Windows DPAPI / Linux Secret Service同机软件无法直读会话文件只留短效 access_token遗留明文文件首次启动自动迁移并抹除
- **HS256 token 强制 jti** jti 的有效签名一律拒签把密钥泄露爆炸半径压回 clipboard-only
- **prod 强制 OIDC audience**`CDROP_OIDC_AUDIENCE` 为空即拒绝启动 audience 跳过校验 兄弟 app 跨冒充部署须设该变量web + 桌面 client_id 逗号分隔
- **请求面收紧**信令 / 消息加 body 上限TURN 凭据 TTL 24h 1h`/auth/exchange` + `/auth/refresh` per-IP 限流
- commit `bcdc2b4` · `c536a35`
## 2026-06-14 桌面文件落盘可配置 + 传输列表行内操作 + macOS 下载兜底修正
- **桌面端接收文件落盘到可配置目录**默认系统 `~/Downloads`设置页加下载目录+ 原生目录选择框机制为 Go 绑定写盘blob base64 过桥 `SaveDownload`文件名 `sanitizeFileName` 防路径穿越重名加 `(n)`落盘后揭示 Finder / Explorer
- **macOS 下载兜底修正**WKWebView 不处理 `<a download>` blob回退浏览器下载 macOS 空操作会丢文件改为配置目录失败时改写系统 `~/Downloads`仍失败才 `toast.error`
- **传输列表行内操作**非终态取消`/cancel`终态删除移除记录发送方立即中继 ICE watchdog + `/fallback`跳过 P2P 等待
- 双端桌面macOS universal `.app` + Windows `.exe`重构并同步web prod 部署
- commit `43ddf36` / `10a4c99`
## 2026-06-12 接入聚珍Juzhen2.0 综合 CJK 排版全角式 + 功能分级 + 本地优先字体
- submodule `cjk-autospace` 升级 `a5faec5 → a3c94dd`v1 自动空格 shim 重写为聚珍Juzhen2.0 综合排版引擎中西间隙 / 标点挤压全角式 / 禁则 / 垂悬字避免 / 长词断字 / 功能分级
- **功能分级**文本级间隙 / 标点挤压任意文本vs 段落级禁则 / 垂悬字 / 长词需行段布局薄壳 `level:{text:".juzhen"}` 全树默认文本级8 处流动正文加 `data-jz-level="paragraph"` opt-in 段落级
- **字体逐 region 明确化**`fonts.css`+ **本地优先字体层**新增 `fontface.css``local()` 优先覆盖 CDN url `@font-face`本机命中零网络
- Badge 加固术语 Pangu 聚珍commit `907d132` / `b3c74a6`
## 2026-05-22 README clone 注意事项
- `README.md` 顶部加克隆小节指引 `git clone --recurse-submodules <URL>` `git submodule update --init --recursive`避免 `web/src/lib/cjk-autospace/` 为空时 vite build 失败
- commit `1443543`
## 2026-05-22 cjk-autospace 抽出为共用 submodule
- 把原内联在 `cjkAutospace.ts` Pangu shim 抽出为上游 git 仓库 cjk-autospace pass 架构跨样式标签透明 + 行内块边界
- git submodule 引入 `web/src/lib/cjk-autospace/``cjkAutospace.ts` 缩为 ~50 行薄壳
- 架构文档 + Changelog 移到 docs 分支
## 历史变更
- `aa80793` CJK 排版子系统Pangu.js v7.2.1 端口
- `4e8c541` 前端视觉重塑后续迭代
- `81dfbe1` 前端重设计
- `6db6444` 新增繁体中文 locale 检测
</script>
<!-- RUNTIME_JS -->
</body>
</html>