b414c57b6a
- 浏览器免重登: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
194 lines
19 KiB
HTML
194 lines
19 KiB
HTML
<!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_token;refresh_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><a download></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 — 接入聚珍(Juzhen)2.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 CJK;Serif = Noto Serif CJK(本地合一)+ Noto Serif <R>(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><link></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 <URL></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_token;refresh_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_token,striped per-session 锁串行化并发 refresh,防一次性 token 被花两次而全端登出
|
||
- **凭据面收紧**:store 移除 `refreshToken` 字段(浏览器侧长寿命凭据不再触及 JS);CSRF 由 SameSite=Lax + Origin 校验双保险。桌面端不受影响
|
||
- **部署要求**:prod 须设 `CDROP_SESSION_SECRET`(任意长度高熵串),缺则拒启动
|
||
- commit `e51666c`
|
||
|
||
## 2026-06-15 — PWA 安装支持 + Safari OAuth 回跳卡顿修复
|
||
|
||
- **PWA 安装支持**:web 可「添加到主屏幕」独立启动(manifest + iOS meta + 同源静态壳 SW)。SW 绝不拦截 `/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 — 接入聚珍(Juzhen)2.0 综合 CJK 排版:全角式 + 功能分级 + 本地优先字体
|
||
|
||
- submodule `cjk-autospace` 升级 `a5faec5 → a3c94dd`:v1 自动空格 shim 重写为「聚珍(Juzhen)」2.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>
|