fix(auth+store): 协议登录成功清零失败计数/支持裸用户名 + 邮件日期统一 UTC 排序 #12

Merged
kevin merged 3 commits from dsh/mailgo:fix/auth-reset-and-date-sort into main 2026-08-19 23:48:59 +08:00
Contributor

本次包含两个独立修复:

1. 认证(f48776e)

  • IMAP/SMTP/POP3 认证成功路径调用 Bans.ResetFail(此前只有 Web 登录清零,协议层失败计数只增不减,合法用户 IP 被反复误封)
  • 新增 UserStore.AuthenticateLogin 支持裸用户名(唯一归属时自动解析域名,跨域歧义要求完整邮箱)
  • 新增单元测试

2. 日期排序(d9bb3cb)

  • mailStore.Create 统一 msg.Date = msg.Date.UTC()
  • date 列是 SQLite 文本,混合时区偏移(+08:00/-04:00/+00:00)导致 ORDER BY date DESC 变成错误字典序:Web 列表最新邮件排不到最前、IMAP 序号与客户端视图不一致
  • 生产数据已迁移为 UTC 格式,验证排序正确
本次包含两个独立修复: ## 1. 认证(f48776e) - IMAP/SMTP/POP3 认证成功路径调用 Bans.ResetFail(此前只有 Web 登录清零,协议层失败计数只增不减,合法用户 IP 被反复误封) - 新增 UserStore.AuthenticateLogin 支持裸用户名(唯一归属时自动解析域名,跨域歧义要求完整邮箱) - 新增单元测试 ## 2. 日期排序(d9bb3cb) - mailStore.Create 统一 msg.Date = msg.Date.UTC() - date 列是 SQLite 文本,混合时区偏移(+08:00/-04:00/+00:00)导致 ORDER BY date DESC 变成错误字典序:Web 列表最新邮件排不到最前、IMAP 序号与客户端视图不一致 - 生产数据已迁移为 UTC 格式,验证排序正确
dsh added 4 commits 2026-08-19 22:35:59 +08:00
根因:Status() 硬编码 UidValidity=1。数据库重建后消息 ID 空间
完全变化(uid 2-166 对应完全不同的邮件),但 UIDVALIDITY 不变,
Thunderbird 认为本地缓存(旧 uid 1-166)仍然有效,只下载"新增"
(uid 167:*)与"缺失"(2,4,8,9)的少数邮件——列表只剩 1-3 封,
每次刷新数量随机。

修复(RFC 3501 UIDVALIDITY 语义):新增 mailbox_states 表,
每(用户,文件夹)首次访问随机生成并持久化 UIDVALIDITY;数据库
重建后新值触发客户端丢弃缓存全量重同步。0 值修正,不同用户/
文件夹互相独立。

新增 TestMailboxStateUidValidity 单元测试。
问题:ResetFail 只在 Web 登录调用,协议层失败计数只增不减。
合法用户的客户端(手机 APP 用裸用户名重试、配置向导探测、输错
密码等)失败次数持续累积,每达到阈值就触发一次封禁档位,从第 4
次触发起真实封禁 30 分钟+——用户被反复误封,手机端表现为一直
卡在"正在接收邮件"。

修复:
- IMAP/SMTP/POP3 认证成功路径调用 Bans.ResetFail(与 Web 一致)
- 新增 UserStore.AuthenticateLogin:支持裸用户名(如 "kevin"),
  唯一归属时自动解析到其域名;跨域名同名歧义时要求完整邮箱
- 新增 TestAuthenticateLoginBareUsername 单元测试
根因:date 列在 SQLite 中是文本,历史数据混存 +08:00/-04:00/+00:00
等时区偏移,ORDER BY date DESC 变成字符串字典序而非时间序:
- Web 列表最新邮件排不到最前(如 22:21+08 的邮件排在 20:03+08 后面)
- IMAP 序号与客户端按日期排序的视图不一致

修复:
- mailStore.Create 统一 msg.Date = msg.Date.UTC()(所有写入路径
  共用:SMTP/IMAP APPEND/Web/外发回执),新数据字典序即时间序
- 生产数据一次性迁移为 UTC 格式(130 条,Python 全量解析转换)
- 验证:kevin INBOX 排序 172→170→166→164…(严格最新在前)
dsh added 1 commit 2026-08-19 22:45:24 +08:00
- 新增 [web].timezone 配置(IANA 时区或固定偏移,默认 Asia/Shanghai)
- 新增 localTime 模板函数;shortDate 的"今天"判断也按展示时区
- view.html 邮件时间改用 localTime 渲染
dsh added 1 commit 2026-08-19 22:56:27 +08:00
问题:go-imap 更新通道只能表达 FETCH/EXPUNGE 类更新,新邮件到达时
客户端(IDLE 中)只收到裸 FETCH。不少客户端(如 Apple Mail)只认
RFC 2177 要求的 EXISTS 才会唤醒拉取,表现为'必须手动点击同步'。

修复:PushNewMessage 先向所有已选中该邮箱的匹配连接直接写入
'* N EXISTS'(Dovecot 同款行为),再广播 FETCH 更新。
实测 IDLE 客户端按序收到 '* 21 EXISTS' + '* 1 FETCH (UID ...)'。
dsh added 1 commit 2026-08-19 23:13:43 +08:00
原实现每封匹配消息单独 UPDATE + GetByID + seqOf 全量扫描(含大
附件 raw_data),手机整批标记已读(60+ 封)时产生 60 次写 +
180 次全量读,连接被长时间占住,其他连接响应被推送洪泛阻塞。

优化:收集目标状态后合并为单条 UPDATE ... IN 批量写;推送更新
直接用已加载列表的序号与目标状态(buildFlagsUpdateAt),不再
重复查库。实测 19 封批量 STORE 服务器耗时 372µs。
kevin merged commit 8be5df57c6 into main 2026-08-19 23:48:59 +08:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: kevin/mailgo#12