- IMAP 推送:imapBackend 实现 backend.BackendUpdater,SMTP 本地投递 与 Web 写信投递成功后 NotifyNewMessage,挂起 IDLE 的客户端即时收到 新邮件 FETCH 通知(按用户名+INBOX 过滤广播,通道满非阻塞丢弃) - 当前连接:新增 internal/connhub 连接注册中心,SMTP/IMAP/POP3 三协议 注册/注销/用户名/TLS/活跃时间追踪;后台新增「当前连接」页 (/admin/connections,统计卡片+连接表格,每 5 秒自动刷新) - 新增测试:connhub 并发安全、推送内容/非阻塞/nil 安全、 后台页面渲染;全量 -race 通过
139 lines
2.8 KiB
Go
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
|
|
}
|