dsh
|
4367f7fb92
|
fix(migrate): mailbox_states 改用原生 SQL 迁移,不依赖 MailboxState 模型(兼容未合并 UIDVALIDITY 特性的上游)
|
2026-08-19 11:27:00 -04:00 |
|
dsh
|
af839a1378
|
chore: 移除误提交的 migrate 编译产物,加入 .gitignore
|
2026-08-19 11:26:32 -04:00 |
|
dsh
|
07e81fc328
|
feat(db): 支持 MySQL 迁移(修复模型外键冲突 + 大文本类型 + 迁移工具)
- Attachment 模型移除 Message 关联:其 foreignKey 名 MessageID 与
Message.MessageID 字符串字段冲突,GORM AutoMigrate 会生成错误外键
(messages.message_id → attachments.id 且强转 bigint),MySQL 下建表
直接失败(SQLite 因动态类型侥幸可用)
- Message.TextBody/HtmlBody 改 mediumtext:MySQL TEXT 仅 64KB,
大 HTML 邮件会写入失败
- 新增 cmd/migrate:SQLite → MySQL 一次性迁移工具(GORM 模型读源、
批量写目标、时间统一 UTC、ban_entries 零值时间转 NULL、逐表校验)
|
2026-08-19 11:26:32 -04:00 |
|
dsh
|
3a4636c2c5
|
fix(imap): FETCH BODY/BODYSTRUCTURE 解析失败时服务器 panic 导致客户端只能取到部分邮件
根因:backendutil.FetchBodyStructure 对部分消息返回 nil(典型场景:
- message/rfc822 附件为 base64 编码时库内不解码,把编码文本当嵌套
消息头解析报错(转发邮件场景,如 .mail-monitor/forward.py 转发)
- multipart 缺少结束边界(截断)时 extended 解析报错)
buildIMAPMessage 未处理 nil,go-imap 格式化 FETCH 响应时在 send()
协程 nil 指针解引用 panic,连接中断——Thunderbird 只取到崩溃前已
发送的几封邮件,手机客户端一直卡在"正在获取邮件"。
修复:
- BodyStructure 解析失败时降级为 text/plain 单段结构,杜绝 nil
- FetchBodySection 返回 nil 时跳过该 section,不再写入 nil literal
- 新增集成测试 TestFetchBodyMalformedMIME 覆盖两类畸形 MIME
|
2026-08-19 08:52:39 -04:00 |
|
dsh
|
bf283ba5fc
|
fix: 返回列表后未读邮件状态不更新(BFCache 静默同步)
问题:打开未读邮件(阅读页已自动标记已读)后返回列表,浏览器从 BFCache
恢复旧 DOM,列表仍显示未读,需手动刷新。
修复:
- 三个列表页的交互脚本提取为公共模板 listjs(base.html),行为一致
- pageshow(persisted=true) 时静默 fetch 最新列表并局部更新行状态
(未读类/圆点/已删除行/未读角标/总数),不整页刷新、保留滚动
- visibilitychange 兜底:标签页重新可见时也同步一次(离线失败静默忽略,
仅 BFCache 场景失败才整页刷新)
- 验证:合成 pageshow 事件驱动同步,未读->已读局部更新 PASS;
桌面/移动/平板布局回归无异常
|
2026-08-17 23:08:15 -04:00 |
|
dsh
|
f281121a4d
|
feat: 前端移动端响应式适配(≤767px)
- 左侧文件夹栏在小屏下变为底部导航条:收件箱/草稿箱/已发送/管理后台 + 居中「写信」悬浮圆钮(带未读角标)
- 顶栏紧凑化:列表页搜索框自动下移为通栏第二行
- 邮件列表:隐藏正文摘要、缩小头像/时间列、行高加大便于触控,删除按钮常显
- 阅读页/写信页/设置页:内边距与字号收紧,配额条适配
- 管理后台:侧栏改为横向换行导航,表格横向滚动,统计卡两列排布
- 全部模板 viewport 增加 viewport-fit=cover(iOS 安全区),底部导航支持 env(safe-area-inset-bottom)
- settings.html 内联样式改为 .settings-main 类以便响应式覆盖
|
2026-08-17 13:21:19 -04:00 |
|
dsh
|
df5ea7b228
|
docs: README 增加界面预览截图与 QQ 邮箱风格界面说明
|
2026-08-17 04:50:53 -04:00 |
|
dsh
|
878d31b48e
|
feat: 前端整体重写为 QQ 邮箱风格布局
- base.html: 全新设计系统(顶部导航栏 + 左侧文件夹栏 + 内容区三栏布局,
蓝/橙主题色),兼容管理后台原有组件类
- inbox/drafts/sent: 邮件列表页重写,支持全选、批量删除、实时搜索过滤、
未读标记、头像圆标、QQ 式短日期、悬停行内删除、分页
- view: 邮件阅读页重写(返回/回复/删除工具条、发件人卡片、附件区)
- compose: 写信页重写(发送/附件/取消、字段行、附件 chips、配额进度条)
- settings/login/banned: 设置页(账号信息+配额条)、登录页、封禁页重写
- server.go: 新增模板函数 mailName/mailEmail/initial/truncate/shortDate/avatarStyle
- mail.go: 侧栏文件夹计数(收件箱未读红标、草稿/已发送数量)
- 新增 render_test.go 模板渲染回归测试
|
2026-08-17 04:42:03 -04:00 |
|
dsh
|
eed0c7b95f
|
fix: 修复编辑域名时私钥留空仍报“TLS 私钥和公钥证书必须同时填写”
浏览器提交 textarea 会把换行规范为 CRLF,而证书文件(尤其是一键从
Caddy 导入的)是 LF,导致“证书未修改”的比较失效,私钥留空(保留
现有私钥)时误报必须同时填写。
- handleDomainTLSUpdate 增加 normalizePEM:提交值与磁盘文件统一
归一化为 LF 后再比较/写入
- 新增 3 个回归测试:CRLF 提交证书未变+私钥留空、CRLF 新证书对
保存为 LF、证书已变但私钥留空仍应报错
|
2026-08-16 23:45:44 -04:00 |
|
dsh
|
f0b9ad3e6f
|
feat: 管理后台一键从 Caddy 获取证书 + TLS 证书热加载
- 域名编辑页新增“从 Caddy 获取证书”按钮:一键把本机 Caddy 已签发的
证书与私钥导入该域名的 TLS 目录并自动启用 TLS,支持通配符证书
(如 *.example.com 可匹配 mail.example.com),成功/失败均回显横幅
- 新增 internal/caddycert:搜索 Caddy 证书存储(同步镜像目录优先、
caddy.data_dir 与常见位置兜底),校验密钥对/有效期/SAN,并给出
可操作的中文错误提示(未找到/证书无效/权限不足)
- install.sh 新增 setup-caddy-cert:安装 root 权限的 systemd
path+timer 同步任务(mailgo-caddy-sync),把 Caddy 证书树镜像到
/srv/mail_go/tls/caddy(证书续期后自动更新、每日兜底),另授予
ACL 作为直接读取兜底;install 时自动检测并配置
- 新增 [caddy] data_dir 配置节,支持自定义 Caddy 数据目录
- 新增 internal/tlsutil:TLS 证书热加载器,每次握手按需重载证书
文件(mtime 检测),重载失败继续使用旧证书兜底并节流重试;
应用于 SMTPS 465/IMAPS 993/POP3S 995 与 STARTTLS,导入或上传
新证书后无需重启服务即生效
- 证书来源动态切换:协议显式配置优先,否则取首个启用 TLS 且有证书
的域名(10 秒缓存),新域名一键导入证书后自动切换
- 更新 README 与界面文案(去掉“重启服务生效”提示)
|
2026-08-16 23:39:43 -04:00 |
|
dsh
|
7ce8751f46
|
feat: 出站地址族可配置(ip_family=ipv4/ipv6/auto)+ 源地址绑定(source_ip)
- ip_family 默认 ipv4(保持 PTR/SPF 最可靠的路径);运营商为静态 IPv6
配置 PTR 后可切换 ipv6
- source_ip 绑定出站源地址,避免内核使用轮换的 IPv6 临时隐私地址
(临时地址无 PTR,Gmail 等会拒收)
- 无 MX 回退时按地址族偏好排序 A/AAAA
|
2026-08-16 00:09:53 -04:00 |
|
dsh
|
76c98c94f3
|
fix: relay_starttls 默认值始终为 true(未显式配置时),避免中继密码明文传输
|
2026-08-16 00:09:53 -04:00 |
|
dsh
|
8fb7aef052
|
fix: 外发优先走 IPv4(Gmail 拒收无 PTR 的 IPv6);新增 smarthost 中继支持
- 出站 SMTP 连接强制 IPv4(tcp4),无 MX 回退时 IPv4 优先:
住宅/动态 IP 的 IPv6 临时地址通常无 PTR,Gmail 会以 5.7.25 拒收,
而 IPv4 一般具备正反向一致的 PTR(实测 Gmail 250 OK)
- [outbound] 新增 relay_host/relay_port/relay_user/relay_password/
relay_starttls:配置后所有外部投递经智能主机中继(AUTH PLAIN、
465 隐式 TLS / 其他端口 STARTTLS),解决服务器 IP 被 Spamhaus PBL
收录时 Outlook/Hotmail 拒收的问题
- 新增 smarthost 中继单元测试
|
2026-08-16 00:09:53 -04:00 |
|
dsh
|
31e5f94be6
|
fix: SMTP 收件邮件的附件未保存(Web 邮箱看不到附件)
- parseSMTPMessage 之前只提取附件元数据、丢弃内容;现在保留原始字节
- saveMessage 创建邮件后落盘附件文件(storage.Save)、写 attachments 记录
并计入用户配额 used_bytes,Web 邮箱可正常列表/下载
- 新增单元测试:附件字节提取 + 附件落盘/记录/配额校验
- 已在生产环境实测:带附件邮件经 :25 入站后,附件文件、记录、配额、
Web 下载(/attachment/:id)全部正确
|
2026-08-15 23:53:50 -04:00 |
|
dsh
|
3bc33214c3
|
test: 增加外发 mailer 单元测试(线格式/点填充/8BITMIME/永久失败)及 DKIM 签名测试
|
2026-08-15 17:30:40 -04:00 |
|
dsh
|
5eb9bc2c71
|
feat: 实现对外邮件投递(外发队列 + MX 直投 + DKIM + 退信 + 管理后台)
- 新增 internal/outbound 模块:MX 查询、SMTP 出站客户端(EHLO/STARTTLS/
MAIL/RCPT/DATA/QUIT)、4xx 临时失败与 5xx 永久失败分类、8BITMIME 支持
- 新增 outbound_messages 队列表与 OutboundStore,后台 worker 指数退避重试
- 永久失败/超限退信到发件人收件箱,包含原因与目标收件人
- 外发邮件使用域名 DKIM 私钥签名(go-msgauth)
- SMTP 提交集成:认证用户可发外部收件人,MAIL FROM 必须等于登录用户邮箱,
未认证外部投递明确拒绝(防开放中继)
- Web 发信集成:外部收件人自动入队,附件以 multipart/mixed + base64 编码
加入邮件正文
- 每用户每分钟/每日发送限速(max_per_day=0 可禁用外部投递)
- 管理后台新增外发队列页面:状态统计、失败原因、手动重试/取消
- 新增 [outbound] 配置段并更新 README / todo.md
|
2026-08-15 17:18:54 -04:00 |
|