docs: consolidate account workspace architecture
This commit is contained in:
1 parent
b65e32885f
commit
c2ea1b7f64
3 files changed
+145
-90
No files matched your search
@@ -0,0 +1,39 @@
|
||||
# 账号、设备、频道与加密的统一模型
|
||||
|
||||
本文件收束账号频道工作区的模型;密码学细节仍以 protocol.md 为准。临时 Share 是独立的对外发布功能,只复用文件传输和密码学,不作为频道成员模型。
|
||||
|
||||
## 领域边界
|
||||
|
||||
Account 使用不可修改的 username 作为当前版本稳定身份,displayName/avatar 为可修改的个人资料。AccountSettings 与资料在加密保险库中同步。AccountDevice 是账号下的一个浏览器安装身份,具有独立登录 session、名称与撤销状态;每个频道的设备 S 继续独立生成。ChannelMember 绑定账号与频道角色,不绑定某一次登录或某个设备。
|
||||
|
||||
Channel 是成员权限和内容加密的共同边界。Thread 属于 Channel,Folder 属于 Thread,文件与消息必须属于 Thread。Thread 不再创建第二套成员、密钥或网络连接;当前频道成员可读取该频道所有 thread。创建频道时自动建立“常规”thread,用户可继续创建其他 thread,分别上传文件或发消息。
|
||||
|
||||
## 模块互通
|
||||
|
||||
账号模块提供当前身份、个人资料、设备会话与解锁后的账号密钥。频道模块负责创建/邀请/接受/打开频道、thread 和文件目录。加密模块不读取 UI 或数据库,只提供账号密钥封装、频道凭据封装和帧加解密。Worker 是三者的编排入口,组件经 store/action 调用它。
|
||||
|
||||
账号登录授权访问服务端;频道内容密钥授权解密内容。这两种能力分开校验,服务端不能通过登录 token 推出频道密钥。频道主的成员记录才是 owner 权限的最终依据,不能凭复制管理密钥冒充另一个账号。
|
||||
|
||||
## 密钥与多设备
|
||||
|
||||
账号邀请私钥保存在密码加密的保险库中,多台设备登录同一账号后解锁同一账号私钥。频道创建时生成独立随机 K 和管理密钥 A;创建者将 owner 凭据封装给自己,服务端原子存储频道、owner 成员与该账号密钥包。邀请其他账号时仅封装 member 凭据,不能交付 A。密钥包绑定 recipient、channelId 和 mode;服务端保存密文,收件账号自动领取并在本机解密。
|
||||
|
||||
已加入成员的新设备只凭账号登录即可恢复频道密钥,不需要邀请者在线,不需要复制带密钥的链接。每个浏览器有独立设备登录凭据和独立频道 S;读取账号频道列表不占席位,实际打开频道才为设备加入席位。设备撤销会撤销其账号 sessions;账号频道成员身份保留,其他设备继续访问。
|
||||
|
||||
本机记住登录保存 session token、不可导出的保险库 CryptoKey 和加密保险库,不保存密码/verifier。它允许本机用户解锁此账号;退出删除该记录。登录态与本机资源数据库在同一启动屏障后恢复,避免先加载访客资源再切换账号。
|
||||
|
||||
## 文件目录与内容
|
||||
|
||||
thread/文件夹结构以及文件目录记录使用频道加密日志。上传文件先完成密文上传,再发布加密 file.published 目录记录,包含文件 ID、thread/folder、名称、类型、大小;成员同步目录无需下载文件内容,也不消耗下载次数。目录与文件内容中的元数据必须相互匹配,否则拒绝展示。
|
||||
|
||||
频道 K 经 HKDF 派生内容根与每文件独立 AES-GCM 密钥;保留现有逐帧认证、FINAL、连续性、padding、ID/AAD 校验与持久化密文重试。下载逐块解密到本机临时存储,完整性和大小校验成功才交付文件;出错清除临时内容。OPFS 不可用时使用 Blob 回退并明确限制。
|
||||
|
||||
## 安全边界
|
||||
|
||||
共享频道密钥意味着任何成员都能解密历史,不能通过移除成员收回其已持有的 K 或明文。当前不实现密钥 epoch 轮换或前向保密,不能声称移除成员可阻止恶意服务端向其提供将来密文。服务端成员/设备撤销负责正常服务下的访问控制。
|
||||
|
||||
首次邀请公钥来自服务端目录,必须如实保留公钥替换风险;AES-GCM 认证证明密文未被未持钥者篡改,不证明具体发送账号。逐账号签名、密钥透明性与密钥轮换属于单独的协议升级,不能以补齐账号功能代替这些保证。前端代码与运行终端仍是信任根。
|
||||
|
||||
账号昵称与设备名是不同概念,昵称不参与密钥派生。头像仅接受有大小上限的 PNG/JPEG/WebP,保存在加密资料中,禁止第三方头像 URL 和可执行 SVG。
|
||||
|
||||
界面术语采用「空间 → 频道分组 → 频道 → 文件夹/聊天」。内部 Channel/Thread 命名保留,分别映射空间/频道;分组沿用加密 CONTROL 日志,不引入新的成员或密钥体系。
|
||||
+36
-14
@@ -10,7 +10,7 @@
|
||||
| 语言 | TypeScript,`strict` + `noUncheckedIndexedAccess` | |
|
||||
| UI | React 19.3(稳定版,含 `ViewTransition`、`Activity`) | 不使用 canary |
|
||||
| 状态 | Zustand | 按领域拆 store |
|
||||
| 本地存储 | `idb`(IndexedDB) | 发送队列、「我的频道」、草稿预览所需的本机文件 |
|
||||
| 本地存储 | `idb`(IndexedDB) | 发送队列、「我的频道」、账号资源工作副本、草稿预览所需的本机文件 |
|
||||
| 样式 | 原生 CSS + CSS 变量 + CSS Modules | 无 Tailwind、无 CSS-in-JS |
|
||||
| 路由 | 自写极简导航(`app/navigation.ts`) | 只有两三个路由,且需统一挂 view transition |
|
||||
| 测试 | Vitest(单元)、Playwright(端到端) | |
|
||||
@@ -93,6 +93,8 @@ type ItemState =
|
||||
| 降级防御 | 链接含 `k` 就只接受 enc 且认证通过的帧 |
|
||||
| 不把密钥、令牌、明文写入 `console`、错误上报、URL、`localStorage` | 「我的频道」只存 IndexedDB |
|
||||
| 便捷分享草稿预览 | 原始文件只缓存在创建者本机 IndexedDB,不会随文件帧上传;移除条目或封存时清理,用于刷新后继续预览草稿 |
|
||||
| 账号资源保险库 | Worker 派生登录凭据与 AES-GCM 密钥;仅同步 link/ownerLink、草稿清单、邀请私钥和移除墓碑;不含终端 S、设备名、缓存或文件 Blob。按账号/访客分库,会话在 Worker 中使用,通过本机 IndexedDB 恢复 |
|
||||
| thread 与虚拟文件夹 | `@webdrop/core` 用共享结构编码 `thread_id`/`folder_id` 和 CONTROL;mode 只切换是否加密;worker 解析控制正文,store 保留最新位置以防旧条目更新回退 |
|
||||
| 条目预览在沙箱 iframe 渲染;raw 内容只当纯文本显示或下载 | 无内联渲染,不使用 `dangerouslySetInnerHTML` |
|
||||
| 严格 CSP,所有脚本样式带 SRI,不使用内联脚本 | 与 architecture §5 一致 |
|
||||
| 不引入任何第三方脚本、统计、字体 CDN | 字体自托管 |
|
||||
@@ -159,18 +161,38 @@ type ItemState =
|
||||
|
||||
在 `@webdrop/core` 未就绪前,`core-bridge/` 按 protocol.md 定义的接口先放适配层和假实现,真实实现就绪后替换,组件与 store 不改。
|
||||
|
||||
## 12. 与 `@webdrop/core` 对接备忘
|
||||
## 12. 实际边界与会话
|
||||
|
||||
`core-bridge/api.ts` 是前端与后端的唯一边界,现在由 `mock.ts` 实现。接 `@webdrop/core`([packages/core/README.md](../packages/core/README.md))时对应关系:
|
||||
core-bridge/instance.ts 使用真实 Worker 实现,mock 仅用于原型和测试。账号与设备由 worker/account.ts 编排;账号密钥包通过 accountChannels.ts 与 channelGrant.ts 领取和核对;频道成员/设置/设备席位通过 channelResources.ts 调用 API;thread/folder/file.published 共用 channelThreads.ts 的加密 CONTROL 日志。
|
||||
|
||||
| 前端需要 | core / 协议 |
|
||||
|---|---|
|
||||
| 新建频道或分享 | `ChannelSession.create({ mode, settings, endpointSecret })` → `POST /channels`;分享的 `settings` 为 `members_can_upload=false`、`delete_policy=owner_only`,并设 `seats`、`ttl_seconds`、`file_max_downloads` |
|
||||
| 草稿阶段调整设置 | `PATCH /channels/{id}/settings`(需所有者令牌) |
|
||||
| **完成分享** | 客户端丢弃所有者密钥 A:用成员链接重新 `ChannelSession.open(...)`,此后 `session.can('owner')` 为 false。丢弃是客户端的自觉行为,服务端不知道;若要强制,需后端提供「封存」让服务端作废 `owner_hash`(属于后端与协议改动,前端只提请求) |
|
||||
| 文件下载次数 | 每个条目的 `maxDownloads` 会强制 GATED,并带 `X-WD-Max-Downloads`,**必须随第 0 帧发送**。文本不要设,否则读文本也要拉取 |
|
||||
| 内联渲染不烧次数 | GATED 条目每次 `GET /items/{id}/frames?from=0` 计一次下载;自动加载只用于非 GATED(见 ui-design §6.5) |
|
||||
| 终端密钥 | `session.endpointSecret` 必须持久化到 IndexedDB,否则每次打开都占一个新席位。原型暂未做 |
|
||||
| 创建频道、便捷分享的验证码 | 读 `GET /info` 的 `captcha`;需要时按需加载打包在前端的官方 `cap-widget`,求解后只把一次性 token 放进创建请求的 `X-WD-Cap`。不从 CDN 加载脚本 |
|
||||
| 校验失败 | reader 的 `rejected` 事件不能忽略,至少在界面提示「内容校验失败」 |
|
||||
| Worker | `ChannelSession` 不能跨线程,在 Worker 里用链接和 `endpointSecret` 重建 |
|
||||
@webdrop/core 仅实现纯密码学与帧协议,不访问数据库或网络。sessions.ts 管本机已解锁会话。transfer.ts 管持久化密文发送队列,队列按资源/创建顺序索引,确保文件密文帧先于目录记录上传。downloadStorage.ts 优先使用 OPFS 逐块写入临时文件,认证结束后交付;不支持时最多 64 MiB 的 Blob 回退。
|
||||
|
||||
频道和 Share 各有独立领域 API 与生命周期;Share 封存由服务端事务清除 owner_hash,不能再写。账号保险库同步个人资料、邀请私钥、资源凭据和移除墓碑;设备 S、文件缓存与 outbox 保留本机。
|
||||
|
||||
## 13. 账号会话恢复与频道密钥领取
|
||||
|
||||
本机 IndexedDB 保存账号 session token、不可导出的保险库 CryptoKey 和最近验证的加密保险库,不保存密码或登录 verifier。启动时先恢复账号并验证服务端 session,再读取该账号的资源数据库;网络离线保留本机身份,服务端明确拒绝 session 时清除登录凭据。退出登录清除本机自动恢复记录。记住登录允许使用本浏览器的人访问账号资源。
|
||||
|
||||
登录/同步后按账号领取 /accounts/channels 密钥包,解密并核对频道 ID、模式和角色,再补齐本机频道;本机主动移除的资源墓碑不会被旧密钥包重新加入。新设备不复用设备 S。打开无 fragment 的频道地址时,已登录账号可以领取授权包,待接受邀请需用户确认加入。未授权时显示需要频道主邀请,不再只要求含密钥链接。
|
||||
|
||||
## 14. 用户概念映射与分组
|
||||
|
||||
界面 Space 对应既有顶层 ChannelInfo 与 /c/:id;界面频道对应 ThreadInfo 和 thread_id。ChannelGroupInfo 属于当前空间,ThreadInfo.groupId 可选。store 从 group-created/group-renamed/thread-grouped 事件重建分组;threadGroups 保留最新归属,避免延迟的创建事件将频道移回旧分组。分组不是文件夹,也不是成员/密钥边界。
|
||||
|
||||
## 15. 文件交互边界
|
||||
|
||||
FileExplorer 编排选择、菜单、拖放和目录导航,ExplorerFileRow 只订阅单个文件。选择范围与拖放封装在 lib/explorer.ts 中做纯逻辑校验;拖放数据仅含随机文件 ID 和当前空间/频道标识,不包含链接、密钥或文件内容。移动、删除、下载均经上层回调和 store 完成。
|
||||
|
||||
ContextMenu 是无业务 UI,负责 portal、视口限制、键盘焦点和关闭。悬停预览由独立组件挂载,受限文件只渲染元数据。文件详情的显式解密流程继续沿用原有认证和下载计数。
|
||||
|
||||
## 16. 文件聊天消息与身份快照
|
||||
|
||||
ItemBase.author 为可选发送者快照 {username,displayName,avatar?}。文件的快照放入已有 file.published 加密目录记录,文本通过 item.author CONTROL 记录关联。store 的 authors 索引支持身份记录先于条目到达,并在晚到的下载/进度更新中保留身份,避免取文件内容后丢失头像。该快照用于展示,不代表逐账号数字签名。
|
||||
|
||||
ChatPane 订阅当前频道的条目 ID,ChatMessage 单独订阅条目状态和上传进度;文件目录变成文件消息,不重复创建独立文本通知。预览/下载继续经已有 store action,移动文件不会重新上传。GATED 内容只有明确操作后才读取。
|
||||
|
||||
## 17. 本机凭据保存
|
||||
|
||||
accountPersistence 新增按 API endpoint 隔离的 credentials store。用户名和密码使用随机 IV 的 AES-GCM 密文保存;不可导出的 CryptoKey 通过 IndexedDB structured clone 保存在本机。只有登录表单读取时才在 Worker 解密并填充,不写入 localStorage、URL 或日志,不上传原始密码。加密存储不能防止已被控制的同源前端或终端。
|
||||
|
||||
AccountSession.remember 控制会话落盘,所有同步、资料更新与登录保存路径统一检查该标记。关闭记住或清除本机保存后,不能被轮询重新保存。正常退出保留已保存填充信息,明确清除或服务端拒绝 session 时删除。旧版保存的会话视为开启自动恢复,但没有原始密码,不伪造密码填充。
|
||||
+70
-76
@@ -74,54 +74,17 @@ blur 的性能约束:
|
||||
|
||||
## 6. 布局与页面
|
||||
|
||||
WebDrop 是**暂存区**,不是聊天室:内容按类型与待办动作组织,不按「谁发的」;发送设备只作为一行小字。
|
||||
WebDrop 是**暂存区**,频道工作区:聊天按 thread 展示,文件按 thread/文件夹组织;发送设备作为来源信息。
|
||||
|
||||
### 6.1 手机(< 900px):最新 + 分类 + 底部动作栏
|
||||
### 6.1 手机:频道导航与聊天/文件切换
|
||||
|
||||
```
|
||||
┌────────────────────────────┐
|
||||
│▒ abc123 🔒加密 ●●○ 2/3 🔍 ◐ ⇧│ ← 模糊顶栏
|
||||
├────────────────────────────┤
|
||||
│ 最新 │ 最近一项放大,拇指区一键取走
|
||||
│ [图标] 屏幕录制.webm │
|
||||
│ iPhone · 14:20 · 1.8 MB │
|
||||
│ [ 预览 ] [ 下载 ] │ 文本则是 [ 复制 ]
|
||||
├────────────────────────────┤
|
||||
│ ▸ 便签 待办… │ 折叠,点开编辑
|
||||
├────────────────────────────┤
|
||||
│ [全部][文件][文本][图片]… │ 类型分组常驻(可横向滑动)
|
||||
│ [缩略] project-assets.zip ⬇│
|
||||
│ [缩略] 设计稿.svg v3 有更新 ⬇│
|
||||
│ [文本] 明天 10 点开会… ⧉ │
|
||||
├────────────────────────────┤
|
||||
│▒ 粘贴 文件 相册 拍照 扫码 写 │ ← 模糊底部动作栏
|
||||
└────────────────────────────┘
|
||||
```
|
||||
顶栏下显示账号频道导航与当前频道的 threads。新建频道自动创建“常规”thread,成员可直接聊天和上传。下方用“聊天 / 文件”切换同一 thread 的内容,聊天输入位于底部;文件区与桌面共用目录路径、上传、文件夹、排序、移动和预览组件。
|
||||
|
||||
- 底部是**动作**而不是输入框:粘贴(读剪贴板,文字或图片)、文件、相册、拍照、扫码、写。点「写」才弹出输入。
|
||||
- 点列表项或「最新」卡打开底部抽屉详情(预览、操作、历史)。
|
||||
### 6.2 电脑:频道与 threads | 聊天 | 线程文件
|
||||
|
||||
### 6.2 电脑(≥ 900px):列表 + 内联预览 + 窄栏
|
||||
左侧固定频道导航与 thread 列表,可直接切换已加入频道、新建频道、新建/重命名 thread,并打开账号与设备设置。中间完整高度展示当前 thread 的聊天,底部输入支持文字和文件附件。右侧是资源管理器式文件区:路径面包屑、上传文件、新建文件夹、按名称/大小/时间排序、多选下载、移动、双击预览。
|
||||
|
||||
```
|
||||
┌───────────────────────────────────────────────────┬───┐
|
||||
│ abc123 🔒加密 ●●○ 2/3 🔍 ◐ │ ‹ │
|
||||
├────────────────────┬──────────────────────────────┤2/3│
|
||||
│ [粘贴][文件][扫码][写]│ 屏幕录制.webm │ ⇧ │
|
||||
│ ▸ 便签 待办… │ iPhone · 14:20 · 剩 8 次下载 │ │
|
||||
│ [全部 文件 文本 …] │ [下载] [删除] │ │
|
||||
│ ☐ [缩略] zip ⬇ │ ┌────────────────────────┐ │ │
|
||||
│ ☐ [缩略] 设计稿 ⬇ │ │ 内联预览(视频/图片/目录) │ │ │
|
||||
│ ☐ [文本] 开会… ⧉ │ └────────────────────────┘ │ │
|
||||
│ 已选 2 项 [下载] │ 修改历史 v3 当前 / v2 / v1 │ │
|
||||
└────────────────────┴──────────────────────────────┴───┘
|
||||
全局:Ctrl+V 粘贴即发送;拖到窗口任意位置即上传
|
||||
```
|
||||
|
||||
- 点左侧一行,右侧内联预览,不弹窗来回开关;默认选中最新一项。
|
||||
- 多选后可批量下载。
|
||||
- 设备与分享是低频操作,默认收成右侧窄栏(在线数、分享图标),需要时展开。
|
||||
- 窗口任意位置 `Ctrl+V` 都能直接发送(在输入框内粘贴时不拦截);拖到窗口任意位置即上传。
|
||||
设备与分享收进顶栏弹层,不占第四列。预览在独立弹层打开,不挤占聊天。加密目录先同步文件名、大小与 thread/folder 归属;受限文件必须显式点击“解密并预览文件”或下载后才读取内容,不能因选择列表项或刷新消耗次数。
|
||||
|
||||
### 6.3 顶栏
|
||||
|
||||
@@ -149,6 +112,10 @@ WebDrop 是**暂存区**,不是聊天室:内容按类型与待办动作组织,
|
||||
│ │ 拖入文件 / 文件名列表 │ │
|
||||
│ │ [选择文件] [粘贴] [创建临时分享]│ │
|
||||
│ └────────────────────────────┘ │
|
||||
│ ┌ 账号与同步 ─────────────────┐ │
|
||||
│ │ 用户名 / 密码 [登录并同步] │ │
|
||||
│ │ 资源清单先在本机加密后同步 │ │
|
||||
│ └────────────────────────────┘ │
|
||||
│ 我的频道(有才显示) │
|
||||
│ abc123… 🔒加密 所有者 3 台设备 │
|
||||
│ ┌ 设备频道 ───────────────────┐ │
|
||||
@@ -161,9 +128,14 @@ WebDrop 是**暂存区**,不是聊天室:内容按类型与待办动作组织,
|
||||
| 操作 | 结果 |
|
||||
|---|---|
|
||||
| 快速分享(输入文本、拖入、粘贴、选择、相册、拍照) | 先在首页编辑临时文本并收集文件;点「创建临时分享」后建立加密草稿并带入内容;点「完成分享」后生成链接与二维码 |
|
||||
| 新建频道 | 抽屉:模式(加密 / 明文)、设备数、保留时间、允许成员上传;创建后直接展示分享面板与二维码 |
|
||||
| 新建频道 | 登录后创建,自动成为该频道所有者;可选择是否端到端加密,默认启用;设备数、TTL、受限下载次数和上传权限仍用同一频道模型 |
|
||||
| 加入频道 | 扫码或粘贴链接 → 加入预览 → 加入 |
|
||||
| 我的频道 | 点一行进入;垃圾桶只是从本机移除,**不会删除频道**,需二次确认 |
|
||||
| 我的频道 | 点一行进入;垃圾桶从当前账号/访客资源清单移除,**不会删除频道**,需二次确认;账号清单移除会同步到其他设备 |
|
||||
| 账号与同步 | 用户名密码注册或登录;本地频道/分享凭据在 Worker 加密后同步,另一台设备登录后恢复;退出时撤销会话 |
|
||||
|
||||
账号密码不提供找回流程。忘记密码会导致账号保险库无法解密;界面需在注册区域明确提示。本机记住登录通过 IndexedDB 保存 session 和不可导出的保险库 CryptoKey,刷新后恢复身份;退出或设备被撤销后清除。
|
||||
|
||||
未登录访问首页时先显示注册/登录页;收到的频道或分享链接保持访客入口。频道加入页按当前规则显示密码输入或登录提示。右上角频道设置集中管理加入方式、陌生人开关、频道密码、成员角色、邀请与撤销,不另设权限入口。
|
||||
|
||||
| 路由 | 页面 |
|
||||
|---|---|
|
||||
@@ -174,37 +146,17 @@ WebDrop 是**暂存区**,不是聊天室:内容按类型与待办动作组织,
|
||||
|
||||
频道分享面板与分享页里的二维码只给**成员链接**;所有者链接含管理权限,只提供复制,不出二维码。二维码固定深色码点、浅色底,暗色主题下也不反转,否则很多扫码器识别不了。
|
||||
|
||||
### 6.4.1 分享(快速分享的结果)
|
||||
### 6.4.1 便捷分享
|
||||
|
||||
**分享和频道是两种东西,底层只维护一套**:分享是频道的一个预设——成员不可上传、删除策略仅所有者,并且创建者在点「完成分享」的那一刻**丢弃所有者密钥**,从此没有房主,任何人(包括创建者)都不能再追加、修改或删除,只能等 TTL 到期。
|
||||
Share 是独立的只读发布资源,不创建频道成员、threads 或席位。创建者在草稿中添加内容和设置,明确确认后封存;服务端清除 owner_hash,前端丢弃管理密钥,此后只有读取入口。访客打开分享链接直接读取加密清单,下载受限文件时计次数,不经过频道加入流程。详见 protocol.md §15。
|
||||
|
||||
```
|
||||
草稿(创建者) 已封存(创建者与访客)
|
||||
┌────────────────────────────────┐ ┌──────────────────────┐
|
||||
│ 新建分享 · 草稿 [粘贴][文件][写]│ │ 我的分享 · 已封存只读 │
|
||||
├───────────────┬────────────────┤ │ 分享链接 [复制][二维码]│
|
||||
│ 内容清单 │ 选中的内容 │ │ 3 项内容 [全部下载] │
|
||||
│ ▢ a.zip 46MB ✕│ [文件预览] │ │ 〃 开会地址… │
|
||||
│ 〃 文本 120B ✕ │ [替换文件] │ │ ▢ a.zip [下载] │
|
||||
│ │ │ └──────────────────────┘
|
||||
│ │ 文本可直接编辑 │
|
||||
├───────────────┴────────────────┤
|
||||
│ 有效期 · 最多访客 · 下载次数 │
|
||||
│ □ 我已了解,完成后无法再修改 │
|
||||
│ [ 完成分享 ] │
|
||||
└────────────────────────────────┘
|
||||
```
|
||||
### 6.5 频道 thread、文件管理与聊天
|
||||
|
||||
| 阶段 | 说明 |
|
||||
|---|---|
|
||||
| 草稿 | 左侧列出内容,选择后在右侧预览;创建者还握着所有者密钥,可直接修改文本、替换或移除文件,并调整有效期、最多访客、每个文件下载次数。**此时没有分享链接**,不会被别人看到 |
|
||||
| 完成分享 | 页面内勾选「完成后会生成分享链接和二维码。此后任何人(包括你)都不能再添加、修改或删除内容,只能等它过期」后,按钮才可用;点击即生成链接与二维码 |
|
||||
| 已封存(创建者) | 只读列表 + 「分享给别人」卡片(链接、二维码、说明) |
|
||||
| 已封存(访客) | 只读列表,只能预览、下载、复制;「全部下载」按钮 |
|
||||
频道内桌面布局为左侧频道/thread 导航、中央聊天、右侧线程文件管理器。新建频道自动创建“常规”thread;用户可以继续创建和命名其他 thread,在各自 thread 中发言或上传文件。聊天消息显示发送设备名称和时间。
|
||||
|
||||
- 访客打开链接后先看加入预览,确认才占席位。访客那一端如何识别「分享」:`members_can_upload=false` 即渲染为只读收件页,不需要新协议字段。
|
||||
- 创建者的所有者链接不展示、不出二维码。
|
||||
- 首页分成「我的分享」(草稿 / 已封存 / 收到的,带剩余时间)和「我的频道」。
|
||||
左栏按 thread 显示虚拟文件夹与文件。可以创建/重命名 thread、创建/重命名文件夹、多选文件并移动到当前 thread 的文件夹。文件预览仍使用中央预览区;消息不混入文件列表。手机上 thread 和文件树可横向/纵向收缩,聊天输入固定在文件列表下方、上传动作栏上方。
|
||||
|
||||
thread 名、文件夹名和移动操作使用同一套 CONTROL 日志条目同步;端到端加密开启时元数据是密文,关闭时服务端可见。两种传输选项共享相同的线程和目录模型。GATED 文件通过独立加密目录显示名称和归属,内容仅在显式操作后读取;没有目录记录的旧文件仍显示通用占位。
|
||||
|
||||
其他弹层:
|
||||
|
||||
@@ -213,7 +165,8 @@ WebDrop 是**暂存区**,不是聊天室:内容按类型与待办动作组织,
|
||||
| 条目详情 | 手机为底部抽屉,电脑为内联预览区;内容一致 |
|
||||
| 扫码加入 | 相机识别二维码(浏览器自带 `BarcodeDetector`,不支持或无权限时退回「粘贴链接」)→ 加入预览(模式、权限)→ 加入后进入该频道 |
|
||||
| 写文本 | 抽屉,`Ctrl+Enter` 发送;首页上用于快速分享 |
|
||||
| 频道设置 | 尚未实现(席位管理等) |
|
||||
| 频道设置 | 顶栏齿轮打开设置面板;所有者可修改名称、席位、成员上传、保留时间和受限文件下载次数 |
|
||||
| 设备管理 | 设备面板显示已加入席位数;设备可重命名,所有者可重命名任意设备并移除其他设备 |
|
||||
|
||||
扫到的链接含密钥,只放在内存里,不写日志、不持久化。
|
||||
|
||||
@@ -294,7 +247,14 @@ WebDrop 是**暂存区**,不是聊天室:内容按类型与待办动作组织,
|
||||
| 历史可读 | 加入说明处写明「持有链接的人可读取此频道全部历史」 |
|
||||
| 元数据 | 创建与设置页注明「不隐藏 IP 与访问时间」 |
|
||||
|
||||
### 7.5 空与错误状态
|
||||
### 7.5 频道文件预览与下载限制
|
||||
|
||||
- 新上传文件默认允许单帧内容直接随频道同步。加入频道的访客可直接看到文件名、类型、大小;PNG 等图片和 TXT 等文本可直接预览,不消耗下载次数。文件名与类型仍只存在于加密帧中。
|
||||
- 超出单帧容量的文件保持 GATED,列表不自动读取内容。访客主动打开预览时才取回文件,并消耗一次下载次数;取回后显示解密出的文件名、类型和大小。EXE 等不支持内联预览的类型仍提供下载。
|
||||
- 上传者可关闭直接预览并为文件设置下载次数上限。
|
||||
- 设备标签优先显示浏览器提供的机型和系统版本,例如「Pixel 9 (Android 16) · A1B2C3D4」「Windows 11 · 1234ABCD」。浏览器没有暴露具体机型时显示能识别的平台名称;保留端点短标识以区分同型号设备。
|
||||
|
||||
### 7.6 空与错误状态
|
||||
|
||||
| 码 | 页面 |
|
||||
|---|---|
|
||||
@@ -311,8 +271,10 @@ WebDrop 是**暂存区**,不是聊天室:内容按类型与待办动作组织,
|
||||
| 发送文本 | 点「写」弹出输入,`Ctrl+Enter` 发送 |
|
||||
| 粘贴 | 窗口任意位置 `Ctrl+V` 直接发送;手机点「粘贴」读取剪贴板(无权限时退回「写」)。文件或图片直接入队,先出现「传输中」条目 |
|
||||
| 拖放 | 桌面整页接收拖放;拖入时出现覆盖提示,不改变布局 |
|
||||
| 文件上传 | 上传前可设置是否让单帧文件直接同步预览,以及受限文件的下载次数上限 |
|
||||
| 频道设置 | 顶栏齿轮打开;设置通过现有 `PATCH /channels/{id}/settings` 更新,服务端广播变更给在线成员 |
|
||||
| 复制 | 点击复制,按钮短暂变为「已复制」,不弹 Toast |
|
||||
| 删除 | 单击删除后条目消失并给 5 秒「撤销」(撤销只在本地延迟提交删除) |
|
||||
| 删除 | 确认删除后提交删除,并同步频道墓碑 |
|
||||
| 新条目到达 | 若用户已滚到底部则自动跟随,否则底部出现「N 条新内容」 |
|
||||
| 触控 | 可点击区域 ≥ 44px;底栏避开安全区(`env(safe-area-inset-*)`) |
|
||||
|
||||
@@ -364,5 +326,37 @@ WebDrop 是**暂存区**,不是聊天室:内容按类型与待办动作组织,
|
||||
| 相机 | 「拍照」用 `<input capture="environment">`,iOS 与 Android 都会直接拉起相机;扫码在 Android Chrome 用 `BarcodeDetector`,iOS Safari 不支持,提示用系统相机扫码后打开链接,或粘贴链接。相机、剪贴板读取都要求 https(`localhost` 除外) |
|
||||
| 剪贴板 | iOS 读取时会弹出系统「粘贴」确认,Android 会要求权限;被拒绝时退回「写」 |
|
||||
| 图片格式 | iPhone 的 HEIC 照片在非 Safari 浏览器里无法显示,预览失败时提示直接下载 |
|
||||
| 下载 | iOS 上 `a[download]` 配合 blob 会弹出存储菜单;大文件要走 Service Worker 流式下载(架构 §6,阶段 4) |
|
||||
| 下载 | iOS 上 `a[download]` 配合 blob 会弹出存储菜单;大文件优先走 OPFS 临时存储;不支持时 Blob 回退最多 64 MiB |
|
||||
|
||||
|
||||
## 13. 空间、频道与频道分组
|
||||
|
||||
界面层级统一为「空间 → 频道分组(可选)→ 频道 → 聊天与文件夹」。原顶层频道对用户称为空间,原 thread 对用户称为频道。新建空间自动创建“常规”频道。空间负责成员、邀请、设备席位和加密边界;频道分组只组织导航,不改变权限或加密密钥。
|
||||
|
||||
左侧上段显示账号与空间列表,下段按分组展示频道。支持新建/重命名分组、折叠分组、在分组内新建频道、将已有频道移入其他分组或移回未分组。旧 thread 自动作为未分组频道展示。中间显示当前频道聊天,右侧显示当前频道文件夹和文件。
|
||||
|
||||
空间设置集中管理成员、邀请和空间权限;频道管理提供名称与所属分组。分组名及归属通过已有加密 CONTROL 日志同步,刷新与其他设备保持一致。协议/API 内部保留 channels/thread_id 等标识,避免因界面术语变更重建已有数据。
|
||||
|
||||
## 14. 文件管理器交互
|
||||
|
||||
文件区支持单击选中、Ctrl/Command 多选、Shift 连选、双击预览、右键菜单和键盘 Enter/Delete/Ctrl+A/Escape。文件夹单击选中、双击或菜单“打开文件夹”进入;面包屑用于快速返回。文件行提供更多菜单按钮,触屏也能访问操作。
|
||||
|
||||
右键文件提供预览、下载、复制名称、移动和删除;文件夹提供打开和重命名;空白区域提供上传、新建文件夹、全选。菜单和悬停卡通过 portal 挂载到 body,贴近指针且限制在视口内,支持 Escape、外部点击和键盘导航。
|
||||
|
||||
文件可拖入当前频道的文件夹或面包屑目录,多选文件可一起移动;跨空间/频道的内部拖放拒绝。外部文件可拖入指定文件夹上传。内部拖拽移动仅追加目录控制记录,不重新加密或拉取文件。
|
||||
|
||||
悬停卡延迟显示名称、类型、大小、时间;已同步且非 GATED 的小图片/文本可显示缩略预览。GATED、上传中或校验失败的文件不会自动取内容,悬停不消耗下载次数。右侧文件区与聊天之间可拖动分隔条调整宽度,分隔条也支持键盘左右键。
|
||||
|
||||
## 15. 频道中的文件消息与发送者
|
||||
|
||||
聊天时间线同时显示文本和已发布的文件,文件消息使用头像、发送者名称、时间和文件卡片,卡片提供预览和下载。当前账号发出的消息靠右,其他人的消息靠左;没有头像时显示名称首字。上传中显示进度和状态,受限文件不自动加载内容或媒体缩略图。
|
||||
|
||||
发送者名称优先使用发送时的个人名称,回退固定用户名;设备名保留在提示中。姓名和头像快照随空间加密日志同步,其他账号和设备刷新后仍能看到原上传者。旧记录没有发送者快照时回退设备来源,不推测为当前用户。
|
||||
|
||||
头像缩略图在 Worker 内生成,最大 64×64、编码后不超过 8 KB,仅使用 PNG/JPEG/WebP data URL;不使用第三方头像链接。圆形头像使用专门的 --radius-avatar token,消息气泡仍遵守现有圆角规范。
|
||||
|
||||
## 16. 记住账号和密码
|
||||
|
||||
登录/注册表单提供“记住账号和密码(本机)”选项,默认勾选。成功验证后才保存本机加密凭据,下次需要登录时自动填充用户名和密码;有效会话仍优先自动恢复。取消勾选时,只保留当前页面会话,后台同步不能再次写入自动登录数据。
|
||||
|
||||
正常退出登录清除会话,但保留用户明确选择保存的填充信息。“清除本机保存”同时清除保存的账号密码和自动恢复会话;如果当前已登录,可以继续使用当前页面,刷新后需重新登录。会话被服务端拒绝时清除本机恢复资料。界面提示只在自己的设备使用记住功能,并兼容浏览器自带密码管理器。
|
||||
Reference in new issue
Block a user