feat(imap): 新邮件实时推送(IDLE)+ 后台当前连接监控

- IMAP 推送:imapBackend 实现 backend.BackendUpdater,SMTP 本地投递
  与 Web 写信投递成功后 NotifyNewMessage,挂起 IDLE 的客户端即时收到
  新邮件 FETCH 通知(按用户名+INBOX 过滤广播,通道满非阻塞丢弃)
- 当前连接:新增 internal/connhub 连接注册中心,SMTP/IMAP/POP3 三协议
  注册/注销/用户名/TLS/活跃时间追踪;后台新增「当前连接」页
  (/admin/connections,统计卡片+连接表格,每 5 秒自动刷新)
- 新增测试:connhub 并发安全、推送内容/非阻塞/nil 安全、
  后台页面渲染;全量 -race 通过
This commit is contained in:
2026-08-19 19:40:05 +08:00
parent b158b8f1f5
commit ede85e0698
27 changed files with 783 additions and 53 deletions
+26 -22
View File
@@ -16,6 +16,7 @@ import (
"time"
"mail_go/config"
"mail_go/internal/connhub"
"mail_go/internal/db"
"mail_go/internal/imap_server"
"mail_go/internal/outbound"
@@ -237,8 +238,27 @@ func main() {
fmt.Println("外发邮件投递未启用(outbound.max_per_day = 0")
}
// 7. Start SMTP server
smtpSrv := smtp_server.NewSMTPServer(cfg.SMTP, stores, attStorage, outboundMgr, smtpTLS, cfg.Ban)
// 6. 连接注册中心(后台「当前连接」页 + IMAP 新邮件推送)
connHub := connhub.New()
// 7. Start IMAP server(先于 SMTP 创建,SMTP 投递成功时通知其推送)
imapSrv := imap_server.NewIMAPServer(cfg.IMAP, stores, imapTLS, cfg.Ban, connHub)
go func() {
if err := imapSrv.Start(); err != nil {
log.Printf("IMAP 服务启动失败: %v", err)
}
}()
// Start IMAPS if TLS is configured
if cfg.IMAP.TLSCert != "" && cfg.IMAP.TLSKey != "" {
go func() {
if err := imapSrv.StartTLS(); err != nil {
log.Printf("IMAPS 服务启动失败: %v", err)
}
}()
}
// 8. Start SMTP server(本地投递成功后触发 IMAP 新邮件推送)
smtpSrv := smtp_server.NewSMTPServer(cfg.SMTP, stores, attStorage, outboundMgr, smtpTLS, cfg.Ban, connHub, imapSrv.NotifyNewMessage)
go func() {
if err := smtpSrv.Start(); err != nil {
log.Printf("SMTP 服务启动失败: %v", err)
@@ -258,24 +278,8 @@ func main() {
}()
}
// 7. Start IMAP server
imapSrv := imap_server.NewIMAPServer(cfg.IMAP, stores, imapTLS, cfg.Ban)
go func() {
if err := imapSrv.Start(); err != nil {
log.Printf("IMAP 服务启动失败: %v", err)
}
}()
// Start IMAPS if TLS is configured
if cfg.IMAP.TLSCert != "" && cfg.IMAP.TLSKey != "" {
go func() {
if err := imapSrv.StartTLS(); err != nil {
log.Printf("IMAPS 服务启动失败: %v", err)
}
}()
}
// 8. Start POP3 server
pop3Srv := pop3_server.NewPOP3Server(cfg.POP3, stores, pop3TLS, cfg.Ban)
// 9. Start POP3 server
pop3Srv := pop3_server.NewPOP3Server(cfg.POP3, stores, pop3TLS, cfg.Ban, connHub)
go func() {
if err := pop3Srv.Start(); err != nil {
log.Printf("POP3 服务启动失败: %v", err)
@@ -290,8 +294,8 @@ func main() {
}()
}
// 10. Start Web server
webServer, err := web.NewWebServer(cfg.Web, stores, attStorage, cfg.Storage, cfg.Auth, cfg.Ban, cfg.Caddy, outboundMgr)
// 10. Start Web server(本地写信投递成功后同样触发 IMAP 新邮件推送)
webServer, err := web.NewWebServer(cfg.Web, stores, attStorage, cfg.Storage, cfg.Auth, cfg.Ban, cfg.Caddy, outboundMgr, connHub, imapSrv.NotifyNewMessage)
if err != nil {
log.Fatalf("Web 服务初始化失败: %v", err)
}