changelog:2026-06-23 P2P 传输修复 + CJK 文字累积修复 + 传输速度显示
- 新增 2026-06-23 条目:接收混合落盘 sink / 发送进度与完成语义 / DynText 文字累积 / 速度显示(对应 main 0451549) - 既有条目直角引号「」统一改为简体弯引号“”(zh-Hans 规范;繁体 runtime 注释不动)
This commit is contained in:
+40
-26
@@ -1,4 +1,4 @@
|
||||
<!-- built with theme=dracula, mode=report, at=2026-06-21T18:44:54Z -->
|
||||
<!-- built with theme=dracula, mode=report, at=2026-06-23T07:26:20Z -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-Hans">
|
||||
<head>
|
||||
@@ -2545,16 +2545,30 @@ main > *:nth-child(n+6) { animation-delay: 0.46s; }
|
||||
|
||||
<p class="lede">本 Changelog 自 2026-05-22 起以 HTML 格式维护。此前之变更可从 main 分支 <code>git log</code> 取得;下表为近期较显著之 commit。</p>
|
||||
|
||||
<h2>2026-06-22 — 账户与登录子系统:扫码快登 · 自签会话 · OIDC 统一 · 即时吊销 · 设备=会话</h2>
|
||||
<h2>2026-06-23 — P2P 传输修复:接收混合落盘 sink · 发送进度与完成语义 · 文字累积 · 速度显示</h2>
|
||||
|
||||
<p>「OAuth 为账户来源与凭证提供者、cdrop 自维护薄账户层 + 自签会话」的折中架构落地(蓝本见仓库 <code>AUTH.md</code>)。一台设备 = 一条会话,吊销即时生效、被吊销设备自动回到未登录态。</p>
|
||||
<p>修复 iOS PWA → Mac 桌面客户端大文件 P2P 传输约 15MB 处死锁,及随之暴露的两处发送端语义问题;并修一类前端 CJK 文字累积 bug、为传输卡片增设实时速度。</p>
|
||||
|
||||
<ul>
|
||||
<li><strong>扫码快速登录</strong>:陌生设备显码、已登录手机扫码批准(微信 / WhatsApp 网页版模型)。三密钥分离(<code>request_id</code> / <code>approval_code</code> / 仅新设备私有的 <code>poll_secret</code>),偷拍 QR 无法领取会话;会话只经新设备自身轮询连接下发 cookie。两档:「信任此设备」(<code>scope=full</code>、7 天滑动) / 「仅此次」(<code>scope=guest</code>、1 小时受限),默认偏安全的「仅此次」。访客会话服务端强制受限(<code>requireFullSession</code> 守门)</li>
|
||||
<li><strong>接收端混合有界内存 sink</strong>(桌面):<code>wails://</code> 自定义 scheme 无持久 WebView 存储 → OPFS 降级、<code>createWritable</code> 回压不畅,把接收 promise 链堵死、饿死 <code>dc.onmessage</code> → 接收方 SCTP <code>rwnd=0</code> → 发送方 <code>bufferedAmount</code> 卡死 16MiB(即“PWA 16.0MB / Mac 0B、约 15MB 卡住”)。改为:小文件(暂存 < 256MB)纯内存、<code>close</code> 整文件过 Go 写盘;超上限大文件流式 spill 到 Go 临时文件(每 64MB 一批),内存恒定有界。浏览器 / iOS 接收仍走 OPFS(其内存预算紧、又无 Go 落盘,OPFS 才有意义)</li>
|
||||
<li><strong>桌面流式落盘</strong>:<code>download.go</code> 新增 <code>Begin/Append/Finalize/AbortStreamingDownload</code>(复用 <code>SaveDownload</code> 的目录解析 / 文件名 sanitize / 不可写回退 / reveal,临时文件与目标同目录便于 finalize 原子 rename),<code>app.go</code> 加 4 个绑定 + <code>net/desktop.ts</code> base64 封装;<code>IncomingSink.close()</code> 返回 <code>SinkResult</code>(<code>blob</code> | 已落盘 <code>path</code>),<code>deliverIncoming</code> 统一落地;relay 接收一并改用,桌面大文件经中继也有界落盘。含单测 <code>download_stream_test.go</code></li>
|
||||
<li><strong>发送端进度按“已交付字节”计</strong>:原先用 <code>bytesSent</code>(已塞进本地 16MiB buffer 的量),buffer 被秒满后定格、长 backpressure 等待期误报“疑似卡住”。改用 <code>bytesSent − bufferedAmount</code>(已离开本地 buffer ≈ 已交付)+ 250ms 轮询在等待期持续刷新,进度随接收端实际收程平滑爬升、不误判 stalled</li>
|
||||
<li><strong>发送端等抽干再宣告完成</strong>:原先送完入 buffer 即 <code>setState("completed")</code>,此时仍可能有多达 16MiB 在途,与接收端“仍在下载”状态不一致。改为送完 + done 帧后等 <code>bufferedAmount</code> 落到 0(字节已全部交付网络)再宣告完成</li>
|
||||
<li><strong>前端动态 CJK 文字累积修复</strong>:聚珍 shim 把会就地更新的 CJK 文本节点 charify 拆段后,旧碎片成了 React 不再追踪的兄弟节点残留、每更新一次累积一个多余字(典型“发送到 X”切设备后多出的“到”)。新增 <code>DynText</code>(<code>key</code>=文本内容,内容一变即整体卸载旧 span、连同 shim 碎片,再挂新 span 交 shim 处理一次——既消累积又<strong>保留</strong>中西排版,区别于 <code>data-jz-skip</code> 那样关掉排版),应用于发送按钮 / 设备名 / 在线数 / 相对时间 / 会话与令牌活跃时间 / 消息计数 / 剪贴板来源 / 问候语等就地更新处</li>
|
||||
<li><strong>传输卡片实时速度</strong>:store <code>upsertTransfer</code> 据相邻样本的字节 / 时间差分 + EMA(α=0.3)平滑算 <code>bytesPerSec</code>;<code>TransferRow</code> 仅在字节流动且未卡死、且速率为正时以“X/秒”展示(点分隔 meta 行追加),卡死时既有“似乎卡住了”状态已表达停滞、不显误导性 0 速</li>
|
||||
<li>commit <code>0451549</code>(main)</li>
|
||||
</ul>
|
||||
|
||||
<h2>2026-06-22 — 账户与登录子系统:扫码快登 · 自签会话 · OIDC 统一 · 即时吊销 · 设备=会话</h2>
|
||||
|
||||
<p>“OAuth 为账户来源与凭证提供者、cdrop 自维护薄账户层 + 自签会话”的折中架构落地(蓝本见仓库 <code>AUTH.md</code>)。一台设备 = 一条会话,吊销即时生效、被吊销设备自动回到未登录态。</p>
|
||||
|
||||
<ul>
|
||||
<li><strong>扫码快速登录</strong>:陌生设备显码、已登录手机扫码批准(微信 / WhatsApp 网页版模型)。三密钥分离(<code>request_id</code> / <code>approval_code</code> / 仅新设备私有的 <code>poll_secret</code>),偷拍 QR 无法领取会话;会话只经新设备自身轮询连接下发 cookie。两档:“信任此设备”(<code>scope=full</code>、7 天滑动) / “仅此次”(<code>scope=guest</code>、1 小时受限),默认偏安全的“仅此次”。访客会话服务端强制受限(<code>requireFullSession</code> 守门)</li>
|
||||
<li><strong>薄账户层 + 自签会话令牌</strong>:新增 <code>accounts</code> 表(cdrop 侧账户数据、不含凭证,存稳定 <code>match_key</code> 供管理员开启迁移时跨源关联);<code>web_sessions</code> 泛化出 <code>kind</code> / <code>scope</code> / <code>granted_by</code> / <code>stepped_up_at</code>。access token 改 cdrop 自签 HS256(密钥自 <code>CDROP_SESSION_SECRET</code> 域分离派生),脱离 IdP refresh、短 TTL(默认 900s)、载 <code>sid</code></li>
|
||||
<li><strong>OIDC 统一到自签 token</strong>:浏览器登录(含 OIDC)一律持自签 token —— <code>/auth/exchange</code> 先 JWKS 验 <code>id_token</code> 签名(唯一信任锚)再签自签 token,<code>/auth/refresh</code> 仍打 IdP 轮换 refresh_token 但回自签 token。RS256 退出浏览器每请求热路径,仅保留分支供桌面端 loopback 直带 IdP token</li>
|
||||
<li><strong>吊销即时 + 设备自知</strong>:<code>verifySelfToken</code> 每请求按 <code>sid</code> 查会话行,行删即拒 → 吊销在下次请求即生效(不等 TTL)。前端仅在「服务端以 401 确证失效」时清登录态回登录页(短期连接失败 / 5xx 不误清);离线设备下次使用即知,在线设备经 <code>Hub.Kick</code> 关流、约 1s 重连撞 401 即知</li>
|
||||
<li><strong>设备列表 = 会话列表</strong>:「登录会话」面板统一 <code>web_sessions</code> 与原生客户端设备(桌面 / iOS,自 <code>devices</code> 登记呈现,<code>native=true</code>)。吊销网页会话连带删其设备登记、即时从所有列表消失;孤儿 <code>browser</code> 设备由清扫器(<code>DeleteOrphanBrowserDevices</code>)自动清除。原生设备「登出」走 <code>DELETE /api/devices/{name}</code>(同要求 step-up)</li>
|
||||
<li><strong>吊销即时 + 设备自知</strong>:<code>verifySelfToken</code> 每请求按 <code>sid</code> 查会话行,行删即拒 → 吊销在下次请求即生效(不等 TTL)。前端仅在“服务端以 401 确证失效”时清登录态回登录页(短期连接失败 / 5xx 不误清);离线设备下次使用即知,在线设备经 <code>Hub.Kick</code> 关流、约 1s 重连撞 401 即知</li>
|
||||
<li><strong>设备列表 = 会话列表</strong>:“登录会话”面板统一 <code>web_sessions</code> 与原生客户端设备(桌面 / iOS,自 <code>devices</code> 登记呈现,<code>native=true</code>)。吊销网页会话连带删其设备登记、即时从所有列表消失;孤儿 <code>browser</code> 设备由清扫器(<code>DeleteOrphanBrowserDevices</code>)自动清除。原生设备“登出”走 <code>DELETE /api/devices/{name}</code>(同要求 step-up)</li>
|
||||
<li><strong>2FA step-up 钩子</strong>(默认关,<code>CDROP_STEP_UP_ENABLED</code>):批准扫码设备 / 吊销会话 / 注销设备等敏感动作要求一次现场 <code>prompt=login</code> 再认证(provider 的 2FA 即在此过),按会话绑定、<code>StepUpMaxAgeSeconds</code>(默认 300s)窗口内不复要求。<code>auth_time</code> 可选(Casdoor 不下发)</li>
|
||||
<li><strong>应用内扫码器 + 聚珍崩溃修复</strong>:登录页内置 <code>getUserMedia</code> + jsqr 扫码(不再外跳系统应用、避免开错浏览器);扫码 / 批准等三状态机页整页跳过聚珍(<code>data-jz-skip</code>),修 MutationObserver 与 React 重渲染同子树冲突的 <code>insertBefore</code> 崩溃</li>
|
||||
<li>蓝本 <code>AUTH.md</code>;commit 待提交</li>
|
||||
@@ -2562,13 +2576,13 @@ main > *:nth-child(n+6) { animation-delay: 0.46s; }
|
||||
|
||||
<h2>2026-06-18 — 推送通知:网页 / PWA Web Push + 桌面原生通知</h2>
|
||||
|
||||
<p>页面 / PWA 关闭、或桌面窗口非前台时也能收到系统通知;判定统一为「页面打开走应用内提示,关闭则系统通知」,三端各自落地。</p>
|
||||
<p>页面 / PWA 关闭、或桌面窗口非前台时也能收到系统通知;判定统一为“页面打开走应用内提示,关闭则系统通知”,三端各自落地。</p>
|
||||
|
||||
<ul>
|
||||
<li><strong>网页 / PWA(Web Push, VAPID)</strong>:服务端在事件未经 SSE 投递到设备时(即该设备页面已关)发 Web Push —— 收到文件、消息、传输完成 / 失败均可。新增 <code>push_subscriptions</code> 表(含 <code>locale</code>,主键为 endpoint 的 SHA-256 以幂等 upsert)+ <code>/api/push/{vapid-key, subscribe}</code> 端点 + <code>internal/push</code> 发送器(404 / 410 自动清理死订阅)。文案按订阅设备语言在服务端渲染(中简 / 中繁 / 英)。覆盖浏览器 / Android / iOS 16.4+ 已安装 PWA</li>
|
||||
<li><strong>桌面客户端原生通知</strong>:仅窗口非前台时弹原生系统通知(前台仍走应用内提示),复用 WebView 事件分发 + 客户端本地化。macOS 走 <code>UNUserNotificationCenter</code>(未签名时安全空操作,待签名 / 公证后显示)、Windows 走原生 toast</li>
|
||||
<li><strong>判定复用既有信号</strong>:网页端「页面是否打开」直接取 <code>Hub.SendTo</code> 的投递结果,零额外状态;离线消息改返 202,文本随推送送达</li>
|
||||
<li><strong>开关与配置</strong>:设置页新增「推送通知」面板(仅浏览器 / PWA),登录后自动把既有订阅重新登记到当前用户;prod 可选设 <code>CDROP_VAPID_PUBLIC_KEY</code> + <code>CDROP_VAPID_PRIVATE_KEY</code>(<code>just vapid-keygen</code> 生成一对,二者须同时设置,都留空则推送惰性关闭)</li>
|
||||
<li><strong>判定复用既有信号</strong>:网页端“页面是否打开”直接取 <code>Hub.SendTo</code> 的投递结果,零额外状态;离线消息改返 202,文本随推送送达</li>
|
||||
<li><strong>开关与配置</strong>:设置页新增“推送通知”面板(仅浏览器 / PWA),登录后自动把既有订阅重新登记到当前用户;prod 可选设 <code>CDROP_VAPID_PUBLIC_KEY</code> + <code>CDROP_VAPID_PRIVATE_KEY</code>(<code>just vapid-keygen</code> 生成一对,二者须同时设置,都留空则推送惰性关闭)</li>
|
||||
<li>commit <code>92a03ae</code></li>
|
||||
</ul>
|
||||
|
||||
@@ -2589,7 +2603,7 @@ main > *:nth-child(n+6) { animation-delay: 0.46s; }
|
||||
<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>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>
|
||||
@@ -2599,7 +2613,7 @@ main > *:nth-child(n+6) { animation-delay: 0.46s; }
|
||||
<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>中继会话防耗尽重构</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>
|
||||
@@ -2610,9 +2624,9 @@ main > *:nth-child(n+6) { animation-delay: 0.46s; }
|
||||
<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><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>
|
||||
@@ -2620,18 +2634,18 @@ main > *:nth-child(n+6) { animation-delay: 0.46s; }
|
||||
<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>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>
|
||||
<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><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>
|
||||
|
||||
@@ -2689,12 +2703,12 @@ main > *:nth-child(n+6) { animation-delay: 0.46s; }
|
||||
|
||||
## 2026-06-18 — 推送通知:网页 / PWA Web Push + 桌面原生通知
|
||||
|
||||
页面 / PWA 关闭、或桌面窗口非前台时也能收到系统通知;判定统一为「页面打开走应用内提示,关闭则系统通知」。
|
||||
页面 / PWA 关闭、或桌面窗口非前台时也能收到系统通知;判定统一为“页面打开走应用内提示,关闭则系统通知”。
|
||||
|
||||
- 网页 / PWA(Web Push, VAPID):服务端在事件未经 SSE 投递到设备时发 Web Push(收到文件 / 消息 / 传输完成 / 失败)。新增 push_subscriptions 表(含 locale,主键为 endpoint 的 SHA-256)+ /api/push/{vapid-key, subscribe} + internal/push 发送器(404 / 410 自动清理死订阅)。文案按设备语言服务端渲染(中简 / 中繁 / 英)。覆盖浏览器 / Android / iOS 16.4+ 已安装 PWA
|
||||
- 桌面客户端原生通知:仅窗口非前台时弹(前台走应用内)。macOS UNUserNotificationCenter(未签名时安全空操作,待签名后显示)、Windows 原生 toast
|
||||
- 判定复用 Hub.SendTo 的投递结果,零额外状态;离线消息改返 202,文本随推送送达
|
||||
- 设置页新增「推送通知」面板(仅浏览器 / PWA);prod 可选设 CDROP_VAPID_PUBLIC_KEY + CDROP_VAPID_PRIVATE_KEY(just vapid-keygen 生成;都留空则惰性关闭)
|
||||
- 设置页新增“推送通知”面板(仅浏览器 / PWA);prod 可选设 CDROP_VAPID_PUBLIC_KEY + CDROP_VAPID_PRIVATE_KEY(just vapid-keygen 生成;都留空则惰性关闭)
|
||||
- commit 92a03ae
|
||||
|
||||
## 2026-06-16 — 浏览器免重登 + 设备名持久化
|
||||
@@ -2711,7 +2725,7 @@ main > *:nth-child(n+6) { animation-delay: 0.46s; }
|
||||
|
||||
## 2026-06-15 — PWA 安装支持 + Safari OAuth 回跳卡顿修复
|
||||
|
||||
- **PWA 安装支持**:web 可「添加到主屏幕」独立启动(manifest + iOS meta + 同源静态壳 SW)。SW 绝不拦截 `/api`、亦不拦导航;PKCE verifier 改 `localStorage`,使独立 PWA 在 OAuth 回跳重启上下文后仍能完成登录。短名统一小写 `cdrop`
|
||||
- **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 修复)
|
||||
|
||||
@@ -2719,7 +2733,7 @@ main > *:nth-child(n+6) { animation-delay: 0.46s; }
|
||||
|
||||
多 agent 安全审计后的一轮加固,按信任模型(信任账号所有者 + 服务端;不信任跨用户 / 网络 hop peer / 本机其他软件)逐项收紧。
|
||||
|
||||
- **中继会话防耗尽重构**:中继改为「预登记」模型 —— 传输进入 `RELAY_ACTIVE` 时服务端登记两个合法参与方,之后仅已登记会话可接入、且调用方须为 sender / receiver。叠加 per-user 配额(3)+ 全局上限(8)+ 空闲回收(2min)+ ring 单 reader 断言。堵住「任意 id 铸造会话耗尽实例并钉 512 MiB」「幽灵会话」「id 泄露」「双 reader 撕裂」四类
|
||||
- **中继会话防耗尽重构**:中继改为“预登记”模型 —— 传输进入 `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 逗号分隔)
|
||||
@@ -2728,22 +2742,22 @@ main > *:nth-child(n+6) { animation-delay: 0.46s; }
|
||||
|
||||
## 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 等待)
|
||||
- **桌面端接收文件落盘到可配置目录**(默认系统 `~/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 综合排版引擎(中西间隙 / 标点挤压全角式 / 禁则 / 垂悬字避免 / 长词断字 / 功能分级)
|
||||
- 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 失败
|
||||
- `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
|
||||
|
||||
+39
-25
@@ -25,16 +25,30 @@
|
||||
|
||||
<p class="lede">本 Changelog 自 2026-05-22 起以 HTML 格式维护。此前之变更可从 main 分支 <code>git log</code> 取得;下表为近期较显著之 commit。</p>
|
||||
|
||||
<h2>2026-06-22 — 账户与登录子系统:扫码快登 · 自签会话 · OIDC 统一 · 即时吊销 · 设备=会话</h2>
|
||||
<h2>2026-06-23 — P2P 传输修复:接收混合落盘 sink · 发送进度与完成语义 · 文字累积 · 速度显示</h2>
|
||||
|
||||
<p>「OAuth 为账户来源与凭证提供者、cdrop 自维护薄账户层 + 自签会话」的折中架构落地(蓝本见仓库 <code>AUTH.md</code>)。一台设备 = 一条会话,吊销即时生效、被吊销设备自动回到未登录态。</p>
|
||||
<p>修复 iOS PWA → Mac 桌面客户端大文件 P2P 传输约 15MB 处死锁,及随之暴露的两处发送端语义问题;并修一类前端 CJK 文字累积 bug、为传输卡片增设实时速度。</p>
|
||||
|
||||
<ul>
|
||||
<li><strong>扫码快速登录</strong>:陌生设备显码、已登录手机扫码批准(微信 / WhatsApp 网页版模型)。三密钥分离(<code>request_id</code> / <code>approval_code</code> / 仅新设备私有的 <code>poll_secret</code>),偷拍 QR 无法领取会话;会话只经新设备自身轮询连接下发 cookie。两档:「信任此设备」(<code>scope=full</code>、7 天滑动) / 「仅此次」(<code>scope=guest</code>、1 小时受限),默认偏安全的「仅此次」。访客会话服务端强制受限(<code>requireFullSession</code> 守门)</li>
|
||||
<li><strong>接收端混合有界内存 sink</strong>(桌面):<code>wails://</code> 自定义 scheme 无持久 WebView 存储 → OPFS 降级、<code>createWritable</code> 回压不畅,把接收 promise 链堵死、饿死 <code>dc.onmessage</code> → 接收方 SCTP <code>rwnd=0</code> → 发送方 <code>bufferedAmount</code> 卡死 16MiB(即“PWA 16.0MB / Mac 0B、约 15MB 卡住”)。改为:小文件(暂存 < 256MB)纯内存、<code>close</code> 整文件过 Go 写盘;超上限大文件流式 spill 到 Go 临时文件(每 64MB 一批),内存恒定有界。浏览器 / iOS 接收仍走 OPFS(其内存预算紧、又无 Go 落盘,OPFS 才有意义)</li>
|
||||
<li><strong>桌面流式落盘</strong>:<code>download.go</code> 新增 <code>Begin/Append/Finalize/AbortStreamingDownload</code>(复用 <code>SaveDownload</code> 的目录解析 / 文件名 sanitize / 不可写回退 / reveal,临时文件与目标同目录便于 finalize 原子 rename),<code>app.go</code> 加 4 个绑定 + <code>net/desktop.ts</code> base64 封装;<code>IncomingSink.close()</code> 返回 <code>SinkResult</code>(<code>blob</code> | 已落盘 <code>path</code>),<code>deliverIncoming</code> 统一落地;relay 接收一并改用,桌面大文件经中继也有界落盘。含单测 <code>download_stream_test.go</code></li>
|
||||
<li><strong>发送端进度按“已交付字节”计</strong>:原先用 <code>bytesSent</code>(已塞进本地 16MiB buffer 的量),buffer 被秒满后定格、长 backpressure 等待期误报“疑似卡住”。改用 <code>bytesSent − bufferedAmount</code>(已离开本地 buffer ≈ 已交付)+ 250ms 轮询在等待期持续刷新,进度随接收端实际收程平滑爬升、不误判 stalled</li>
|
||||
<li><strong>发送端等抽干再宣告完成</strong>:原先送完入 buffer 即 <code>setState("completed")</code>,此时仍可能有多达 16MiB 在途,与接收端“仍在下载”状态不一致。改为送完 + done 帧后等 <code>bufferedAmount</code> 落到 0(字节已全部交付网络)再宣告完成</li>
|
||||
<li><strong>前端动态 CJK 文字累积修复</strong>:聚珍 shim 把会就地更新的 CJK 文本节点 charify 拆段后,旧碎片成了 React 不再追踪的兄弟节点残留、每更新一次累积一个多余字(典型“发送到 X”切设备后多出的“到”)。新增 <code>DynText</code>(<code>key</code>=文本内容,内容一变即整体卸载旧 span、连同 shim 碎片,再挂新 span 交 shim 处理一次——既消累积又<strong>保留</strong>中西排版,区别于 <code>data-jz-skip</code> 那样关掉排版),应用于发送按钮 / 设备名 / 在线数 / 相对时间 / 会话与令牌活跃时间 / 消息计数 / 剪贴板来源 / 问候语等就地更新处</li>
|
||||
<li><strong>传输卡片实时速度</strong>:store <code>upsertTransfer</code> 据相邻样本的字节 / 时间差分 + EMA(α=0.3)平滑算 <code>bytesPerSec</code>;<code>TransferRow</code> 仅在字节流动且未卡死、且速率为正时以“X/秒”展示(点分隔 meta 行追加),卡死时既有“似乎卡住了”状态已表达停滞、不显误导性 0 速</li>
|
||||
<li>commit <code>0451549</code>(main)</li>
|
||||
</ul>
|
||||
|
||||
<h2>2026-06-22 — 账户与登录子系统:扫码快登 · 自签会话 · OIDC 统一 · 即时吊销 · 设备=会话</h2>
|
||||
|
||||
<p>“OAuth 为账户来源与凭证提供者、cdrop 自维护薄账户层 + 自签会话”的折中架构落地(蓝本见仓库 <code>AUTH.md</code>)。一台设备 = 一条会话,吊销即时生效、被吊销设备自动回到未登录态。</p>
|
||||
|
||||
<ul>
|
||||
<li><strong>扫码快速登录</strong>:陌生设备显码、已登录手机扫码批准(微信 / WhatsApp 网页版模型)。三密钥分离(<code>request_id</code> / <code>approval_code</code> / 仅新设备私有的 <code>poll_secret</code>),偷拍 QR 无法领取会话;会话只经新设备自身轮询连接下发 cookie。两档:“信任此设备”(<code>scope=full</code>、7 天滑动) / “仅此次”(<code>scope=guest</code>、1 小时受限),默认偏安全的“仅此次”。访客会话服务端强制受限(<code>requireFullSession</code> 守门)</li>
|
||||
<li><strong>薄账户层 + 自签会话令牌</strong>:新增 <code>accounts</code> 表(cdrop 侧账户数据、不含凭证,存稳定 <code>match_key</code> 供管理员开启迁移时跨源关联);<code>web_sessions</code> 泛化出 <code>kind</code> / <code>scope</code> / <code>granted_by</code> / <code>stepped_up_at</code>。access token 改 cdrop 自签 HS256(密钥自 <code>CDROP_SESSION_SECRET</code> 域分离派生),脱离 IdP refresh、短 TTL(默认 900s)、载 <code>sid</code></li>
|
||||
<li><strong>OIDC 统一到自签 token</strong>:浏览器登录(含 OIDC)一律持自签 token —— <code>/auth/exchange</code> 先 JWKS 验 <code>id_token</code> 签名(唯一信任锚)再签自签 token,<code>/auth/refresh</code> 仍打 IdP 轮换 refresh_token 但回自签 token。RS256 退出浏览器每请求热路径,仅保留分支供桌面端 loopback 直带 IdP token</li>
|
||||
<li><strong>吊销即时 + 设备自知</strong>:<code>verifySelfToken</code> 每请求按 <code>sid</code> 查会话行,行删即拒 → 吊销在下次请求即生效(不等 TTL)。前端仅在「服务端以 401 确证失效」时清登录态回登录页(短期连接失败 / 5xx 不误清);离线设备下次使用即知,在线设备经 <code>Hub.Kick</code> 关流、约 1s 重连撞 401 即知</li>
|
||||
<li><strong>设备列表 = 会话列表</strong>:「登录会话」面板统一 <code>web_sessions</code> 与原生客户端设备(桌面 / iOS,自 <code>devices</code> 登记呈现,<code>native=true</code>)。吊销网页会话连带删其设备登记、即时从所有列表消失;孤儿 <code>browser</code> 设备由清扫器(<code>DeleteOrphanBrowserDevices</code>)自动清除。原生设备「登出」走 <code>DELETE /api/devices/{name}</code>(同要求 step-up)</li>
|
||||
<li><strong>吊销即时 + 设备自知</strong>:<code>verifySelfToken</code> 每请求按 <code>sid</code> 查会话行,行删即拒 → 吊销在下次请求即生效(不等 TTL)。前端仅在“服务端以 401 确证失效”时清登录态回登录页(短期连接失败 / 5xx 不误清);离线设备下次使用即知,在线设备经 <code>Hub.Kick</code> 关流、约 1s 重连撞 401 即知</li>
|
||||
<li><strong>设备列表 = 会话列表</strong>:“登录会话”面板统一 <code>web_sessions</code> 与原生客户端设备(桌面 / iOS,自 <code>devices</code> 登记呈现,<code>native=true</code>)。吊销网页会话连带删其设备登记、即时从所有列表消失;孤儿 <code>browser</code> 设备由清扫器(<code>DeleteOrphanBrowserDevices</code>)自动清除。原生设备“登出”走 <code>DELETE /api/devices/{name}</code>(同要求 step-up)</li>
|
||||
<li><strong>2FA step-up 钩子</strong>(默认关,<code>CDROP_STEP_UP_ENABLED</code>):批准扫码设备 / 吊销会话 / 注销设备等敏感动作要求一次现场 <code>prompt=login</code> 再认证(provider 的 2FA 即在此过),按会话绑定、<code>StepUpMaxAgeSeconds</code>(默认 300s)窗口内不复要求。<code>auth_time</code> 可选(Casdoor 不下发)</li>
|
||||
<li><strong>应用内扫码器 + 聚珍崩溃修复</strong>:登录页内置 <code>getUserMedia</code> + jsqr 扫码(不再外跳系统应用、避免开错浏览器);扫码 / 批准等三状态机页整页跳过聚珍(<code>data-jz-skip</code>),修 MutationObserver 与 React 重渲染同子树冲突的 <code>insertBefore</code> 崩溃</li>
|
||||
<li>蓝本 <code>AUTH.md</code>;commit 待提交</li>
|
||||
@@ -42,13 +56,13 @@
|
||||
|
||||
<h2>2026-06-18 — 推送通知:网页 / PWA Web Push + 桌面原生通知</h2>
|
||||
|
||||
<p>页面 / PWA 关闭、或桌面窗口非前台时也能收到系统通知;判定统一为「页面打开走应用内提示,关闭则系统通知」,三端各自落地。</p>
|
||||
<p>页面 / PWA 关闭、或桌面窗口非前台时也能收到系统通知;判定统一为“页面打开走应用内提示,关闭则系统通知”,三端各自落地。</p>
|
||||
|
||||
<ul>
|
||||
<li><strong>网页 / PWA(Web Push, VAPID)</strong>:服务端在事件未经 SSE 投递到设备时(即该设备页面已关)发 Web Push —— 收到文件、消息、传输完成 / 失败均可。新增 <code>push_subscriptions</code> 表(含 <code>locale</code>,主键为 endpoint 的 SHA-256 以幂等 upsert)+ <code>/api/push/{vapid-key, subscribe}</code> 端点 + <code>internal/push</code> 发送器(404 / 410 自动清理死订阅)。文案按订阅设备语言在服务端渲染(中简 / 中繁 / 英)。覆盖浏览器 / Android / iOS 16.4+ 已安装 PWA</li>
|
||||
<li><strong>桌面客户端原生通知</strong>:仅窗口非前台时弹原生系统通知(前台仍走应用内提示),复用 WebView 事件分发 + 客户端本地化。macOS 走 <code>UNUserNotificationCenter</code>(未签名时安全空操作,待签名 / 公证后显示)、Windows 走原生 toast</li>
|
||||
<li><strong>判定复用既有信号</strong>:网页端「页面是否打开」直接取 <code>Hub.SendTo</code> 的投递结果,零额外状态;离线消息改返 202,文本随推送送达</li>
|
||||
<li><strong>开关与配置</strong>:设置页新增「推送通知」面板(仅浏览器 / PWA),登录后自动把既有订阅重新登记到当前用户;prod 可选设 <code>CDROP_VAPID_PUBLIC_KEY</code> + <code>CDROP_VAPID_PRIVATE_KEY</code>(<code>just vapid-keygen</code> 生成一对,二者须同时设置,都留空则推送惰性关闭)</li>
|
||||
<li><strong>判定复用既有信号</strong>:网页端“页面是否打开”直接取 <code>Hub.SendTo</code> 的投递结果,零额外状态;离线消息改返 202,文本随推送送达</li>
|
||||
<li><strong>开关与配置</strong>:设置页新增“推送通知”面板(仅浏览器 / PWA),登录后自动把既有订阅重新登记到当前用户;prod 可选设 <code>CDROP_VAPID_PUBLIC_KEY</code> + <code>CDROP_VAPID_PRIVATE_KEY</code>(<code>just vapid-keygen</code> 生成一对,二者须同时设置,都留空则推送惰性关闭)</li>
|
||||
<li>commit <code>92a03ae</code></li>
|
||||
</ul>
|
||||
|
||||
@@ -69,7 +83,7 @@
|
||||
<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>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>
|
||||
@@ -79,7 +93,7 @@
|
||||
<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>中继会话防耗尽重构</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>
|
||||
@@ -90,9 +104,9 @@
|
||||
<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><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>
|
||||
@@ -100,18 +114,18 @@
|
||||
<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>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>
|
||||
<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><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>
|
||||
|
||||
@@ -153,12 +167,12 @@
|
||||
|
||||
## 2026-06-18 — 推送通知:网页 / PWA Web Push + 桌面原生通知
|
||||
|
||||
页面 / PWA 关闭、或桌面窗口非前台时也能收到系统通知;判定统一为「页面打开走应用内提示,关闭则系统通知」。
|
||||
页面 / PWA 关闭、或桌面窗口非前台时也能收到系统通知;判定统一为“页面打开走应用内提示,关闭则系统通知”。
|
||||
|
||||
- 网页 / PWA(Web Push, VAPID):服务端在事件未经 SSE 投递到设备时发 Web Push(收到文件 / 消息 / 传输完成 / 失败)。新增 push_subscriptions 表(含 locale,主键为 endpoint 的 SHA-256)+ /api/push/{vapid-key, subscribe} + internal/push 发送器(404 / 410 自动清理死订阅)。文案按设备语言服务端渲染(中简 / 中繁 / 英)。覆盖浏览器 / Android / iOS 16.4+ 已安装 PWA
|
||||
- 桌面客户端原生通知:仅窗口非前台时弹(前台走应用内)。macOS UNUserNotificationCenter(未签名时安全空操作,待签名后显示)、Windows 原生 toast
|
||||
- 判定复用 Hub.SendTo 的投递结果,零额外状态;离线消息改返 202,文本随推送送达
|
||||
- 设置页新增「推送通知」面板(仅浏览器 / PWA);prod 可选设 CDROP_VAPID_PUBLIC_KEY + CDROP_VAPID_PRIVATE_KEY(just vapid-keygen 生成;都留空则惰性关闭)
|
||||
- 设置页新增“推送通知”面板(仅浏览器 / PWA);prod 可选设 CDROP_VAPID_PUBLIC_KEY + CDROP_VAPID_PRIVATE_KEY(just vapid-keygen 生成;都留空则惰性关闭)
|
||||
- commit 92a03ae
|
||||
|
||||
## 2026-06-16 — 浏览器免重登 + 设备名持久化
|
||||
@@ -175,7 +189,7 @@
|
||||
|
||||
## 2026-06-15 — PWA 安装支持 + Safari OAuth 回跳卡顿修复
|
||||
|
||||
- **PWA 安装支持**:web 可「添加到主屏幕」独立启动(manifest + iOS meta + 同源静态壳 SW)。SW 绝不拦截 `/api`、亦不拦导航;PKCE verifier 改 `localStorage`,使独立 PWA 在 OAuth 回跳重启上下文后仍能完成登录。短名统一小写 `cdrop`
|
||||
- **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 修复)
|
||||
|
||||
@@ -183,7 +197,7 @@
|
||||
|
||||
多 agent 安全审计后的一轮加固,按信任模型(信任账号所有者 + 服务端;不信任跨用户 / 网络 hop peer / 本机其他软件)逐项收紧。
|
||||
|
||||
- **中继会话防耗尽重构**:中继改为「预登记」模型 —— 传输进入 `RELAY_ACTIVE` 时服务端登记两个合法参与方,之后仅已登记会话可接入、且调用方须为 sender / receiver。叠加 per-user 配额(3)+ 全局上限(8)+ 空闲回收(2min)+ ring 单 reader 断言。堵住「任意 id 铸造会话耗尽实例并钉 512 MiB」「幽灵会话」「id 泄露」「双 reader 撕裂」四类
|
||||
- **中继会话防耗尽重构**:中继改为“预登记”模型 —— 传输进入 `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 逗号分隔)
|
||||
@@ -192,22 +206,22 @@
|
||||
|
||||
## 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 等待)
|
||||
- **桌面端接收文件落盘到可配置目录**(默认系统 `~/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 综合排版引擎(中西间隙 / 标点挤压全角式 / 禁则 / 垂悬字避免 / 长词断字 / 功能分级)
|
||||
- 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 失败
|
||||
- `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
|
||||
|
||||
Reference in New Issue
Block a user