Files
mailgo/internal/connhub/hub.go
T
kevin ede85e0698 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 通过
2026-08-19 19:40:05 +08:00

139 lines
2.8 KiB
Go

// Package connhub 提供邮件协议(SMTP/IMAP/POP3)当前活动连接的注册中心,
// 供管理后台实时查看连接情况(来源 IP、用户、TLS、时长等)。
package connhub
import (
"sort"
"sync"
"time"
)
// Conn 表示一个活动中的协议连接。字段由 Hub 的锁保护。
type Conn struct {
ID uint64 // 自增序号
Protocol string // smtp | imap | pop3
IP string
Port int
User string // 认证后填充
TLS bool
Connected time.Time
LastActive time.Time
hub *Hub
}
// Hub 管理所有活动连接(同一把锁保护注册表与连接字段)。
type Hub struct {
mu sync.Mutex
seq uint64
conns map[uint64]*Conn
}
// New 创建连接注册中心。
func New() *Hub {
return &Hub{conns: make(map[uint64]*Conn)}
}
// Register 注册一个新连接并返回其句柄;调用方在连接结束时调用 Close()。
func (h *Hub) Register(protocol, ip string, port int, tls bool) *Conn {
if h == nil {
return nil
}
h.mu.Lock()
defer h.mu.Unlock()
h.seq++
now := time.Now()
c := &Conn{
ID: h.seq,
Protocol: protocol,
IP: ip,
Port: port,
TLS: tls,
Connected: now,
LastActive: now,
hub: h,
}
h.conns[c.ID] = c
return c
}
// SetUser 记录认证成功的用户名(邮箱)并刷新最后活跃时间。
func (c *Conn) SetUser(u string) {
if c == nil || c.hub == nil {
return
}
c.hub.mu.Lock()
c.User = u
c.LastActive = time.Now()
c.hub.mu.Unlock()
}
// SetTLS 更新连接的 TLS 状态(如 POP3 STLS 升级之后)。
func (c *Conn) SetTLS(on bool) {
if c == nil || c.hub == nil {
return
}
c.hub.mu.Lock()
c.TLS = on
c.hub.mu.Unlock()
}
// Touch 刷新最后活跃时间。
func (c *Conn) Touch() {
if c == nil || c.hub == nil {
return
}
c.hub.mu.Lock()
c.LastActive = time.Now()
c.hub.mu.Unlock()
}
// Close 从注册中心移除该连接。
func (c *Conn) Close() {
if c == nil || c.hub == nil {
return
}
c.hub.mu.Lock()
delete(c.hub.conns, c.ID)
c.hub.mu.Unlock()
}
// List 返回当前所有活动连接(拷贝),按连接时间升序。
func (h *Hub) List() []Conn {
if h == nil {
return nil
}
h.mu.Lock()
out := make([]Conn, 0, len(h.conns))
for _, c := range h.conns {
out = append(out, Conn{
ID: c.ID,
Protocol: c.Protocol,
IP: c.IP,
Port: c.Port,
User: c.User,
TLS: c.TLS,
Connected: c.Connected,
LastActive: c.LastActive,
})
}
h.mu.Unlock()
sort.Slice(out, func(i, j int) bool { return out[i].ID < out[j].ID })
return out
}
// Counts 返回按协议分组的当前连接数。
func (h *Hub) Counts() map[string]int {
if h == nil {
return nil
}
h.mu.Lock()
defer h.mu.Unlock()
counts := make(map[string]int)
for _, c := range h.conns {
counts[c.Protocol]++
}
return counts
}