Files

115 lines
6.6 KiB
Markdown
Raw Permalink 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.
# WpywMail 客户端
给自建邮件服务器 **WpywMail**(`mail.example.com`)写的桌面客户端。
收信走 **IMAP4rev1(993 隐式 TLS)**,发信走 **SMTP 提交(587 STARTTLS + AUTH)**。
界面用 **shadcn/ui + Tailwind CSS v4 + Vite(React 19)** —— 结构与视觉以 shadcn 官方的
Mail 示例为基线,控件不自己发明。后端是**零依赖的 Node 服务**,负责讲 IMAP/SMTP 与 MIME。
## 怎么启动
双击 **`start.cmd`**。它会:
1. 若 `ui/dist` 尚未构建,自动 `npm install` + `npm run build`(首次约一两分钟);
2. 起一个只监听 `127.0.0.1:8788` 的本地服务(界面与邮件协议之间的桥);
3. 自动打开浏览器指向 `http://127.0.0.1:8788/`。
关闭那个黑窗口就等于退出客户端。需要 Node.js 20+(当前机器 v24)。
改界面时用热更新:`npm --prefix ui run dev`(已配好把 `/api` 代理到 8788)。
首次启动若 `data\account.json` 里没有账号,界面会要求登录;登录成功后凭据写回该文件(只在本机)。
## 现在能做什么
| 能力 | 说明 |
|---|---|
| 收信 | 6 个文件夹(收件箱/草稿/已发送/归档/垃圾邮件/废纸篓),带未读数 |
| 邮件列表 | 头像、发件人、主题、时间、大小、未读圆点、旗标、已回复/草稿标记 |
| 阅读区 | 中文主题与正文正确解码、附件下载、发件人与收件人信息、完整时间 |
| 撰写 / 回复 / 转发 | 弹窗式;中文头部自动 RFC 2047 编码;多附件;回复带 `In-Reply-To`/`References` |
| 快速回复 | 阅读区底部直接写,`Ctrl+Enter` 发送 |
| 草稿 | 「存草稿」写入 Drafts |
| 搜索 | 主题 / 发件人 / 收件人 / UID,**支持中文**(见「已知限制」) |
| 管理动作 | 已读未读、旗标、归档、删除(默认进废纸篓)、切换文件夹 |
| 主题 | 深色/浅色双主题,切换后记忆(默认深色) |
### 键盘
| 键 | 动作 |
|---|---|
| `J` / `K`(或 ↑↓) | 上下选邮件 |
| `Enter` | 打开 |
| `C` | 撰写 |
| `R` / `F` | 回复 / 转发 |
| `S` | 加/取消旗标 |
| `U` | 已读/未读切换 |
| `E` | 归档 |
| `#` | 删除 |
| `/` | 聚焦搜索框 |
| `Esc` | 关闭弹窗 / 取消焦点 |
## 架构
```
start.cmd
└─ node server/index.js 本地服务(仅 127.0.0.1:8788):托管界面 + JSON API
├─ server/imap.js IMAP4rev1 客户端(自己实现,含字面量感知解析器)
├─ server/smtp.js SMTP 提交客户端(STARTTLS + AUTH PLAIN,DATA 按字节写出)
├─ server/mime.js MIME 编解码(RFC 5322/2047/2231/2045)
├─ server/account.js 账户配置读写(data/account.json)
├─ ui/ 界面源码(shadcn/ui + Tailwind + Vite)→ 构建到 ui/dist
└─ web/ 旧版纯手写界面(兜底;ui/dist 不存在时才会被发出去)
```
**为什么后端零依赖**:IMAP/SMTP/MIME 是这块的硬骨头,自己实现能对症下药 —— 实测用它挖出了服务端
三个真实缺陷(见 `E:\deepseek\artifacts\wpywmail-server-findings.md`),并且不引入供应链风险。
**为什么界面用现成组件库**:控件观感不必自己发明。shadcn 是「把源码复制进项目」的组件(不是黑盒依赖),
随手可改,同时白拿 Radix 的可访问性底座与 Tailwind 的迭代速度。
## 设计系统
结构实例化 **shadcn/ui 官方 Mail 示例**(左:账号 + 文件夹导航;中:邮件列表;右:阅读与操作)。
配色、圆角、阴影、暗色全部用 **shadcn 默认(new-york / neutral)主题 token**,原样写在
`ui/src/styles.css`(`oklch()` 变量 + `.dark` 覆盖),所以 light/dark 都是"官方观感"。
换主题只改那一个文件的变量块。
字体走系统栈(Segoe UI / 微软雅黑 / PingFang)—— 邮件客户端要的是"读起来像系统原生",不是品牌表达。
## 验收记录
| 套件 | 结果 | 报告 |
|---|---|---|
| MIME 单元测试 `node tools/mime-test.js` | **25/25** | `E:\deepseek\artifacts\client-mime-test.txt` |
| 协议闭环自测 `node tools/smoke.js` | **20/20** | `E:\deepseek\artifacts\client-smoke.txt` |
| 后端 API 链路 `node tools/api-check.js` | **10/10** | `E:\deepseek\artifacts\client-api-check.txt` |
| 界面运行时(新 UI)`node tools/ui-check-v2.js` | **8/8** | `E:\deepseek\artifacts\client-ui-check-v2.txt` |
`smoke.js` 是真正的闭环:**用本项目的代码发一封中文信 → 服务器投递 → 再用本项目的 IMAP 代码
把它从收件箱取回并解析**,断言主题与正文(含全角标点)往返无损。
`ui-check-v2.js` 用 **Edge 无头 + CDP(Node 自带 WebSocket,零依赖)** 真开页面:收集 JS 异常与
控制台报错、断言文件夹/列表/阅读区/撰写弹窗都渲染、验证主题切换真的改变背景色、检查无横向溢出。
`api-check.js` 覆盖界面所用的那条链路:发送 → 到达 → 旗标 → 未读 → 移动(归档/移回)→ 删除 → 清理痕迹。
## 已知限制(都是真的,没藏)
1. **搜索是「服务端取回摘要 + 本地过滤」**,窗口上限 500 封,**不含正文全文**。
原因:服务端 `SEARCH` 没实现 `BODY`/`OR`,且命令行按 `Encoding.ASCII` 解码 → 中文检索词到服务端
会变成 `??`(详见服务端缺陷记录)。客户端绕过了它,所以中文搜索可用。
2. **不渲染 HTML 邮件正文**,只渲染解析出的纯文本(避免 XSS)。只有 HTML 的邮件会退化成去标签文本。
3. **附件只支持「添加并随信发出」与「下载」**,不做内联图片渲染。
4. **密码以明文存在 `data\account.json`**(本机、便携)。要更稳妥可以用 Windows DPAPI 包一层。
5. 未实现:IDLE 实时推送(目前 30 秒轮询文件夹计数)、会话视图、离线缓存、富文本撰写、多账号。
6. 服务端目前**没有 `SPECIAL-USE`**,文件夹靠名字映射。
7. `web/`(旧手写界面)已退役但保留作兜底;日常运行的是 `ui/dist`。
## 排障
- **界面显示"正在连接邮件服务器…"不动**:看黑窗口里的报错,多半是服务器 993 连不上或密码变了。
- **发送失败**:报错原样显示服务端返回(如 `550 Relay denied`)。家用 DNS 偶发瞬时解析失败
(`EAI_AGAIN`)已加一次自动重试。
- **改了界面没生效**:`npm --prefix ui run build` 重新构建(`ui/dist` 是静态产物,改了源码不会自动更新)。
- **端口被占**:设环境变量 `WPYWMAIL_PORT` 换端口。