Files
gochat/internal/model/channel/email.go
T
2026-06-04 15:44:48 +08:00

162 lines
5.9 KiB
Go

package channel
import (
"fmt"
"github.com/gochat/gochat/internal/model"
)
// ChannelEmail represents an Email (SMTP/IMAP) channel configuration.
// Reference: Chatwoot app/models/channel/email.rb
//
// Chatwoot's ChannelEmail model attributes:
// - email: The inbound email address (e.g. support@acme.com)
// - forward_to_address: Email address for forwarding (alternative to IMAP)
// - imap_enabled: Whether IMAP polling is enabled
// - imap_address: IMAP server hostname
// - imap_port: IMAP server port (typically 993 for SSL)
// - imap_login: IMAP login/username
// - imap_password: IMAP password
// - imap_ssl: Whether to use SSL for IMAP connection
// - smtp_enabled: Whether SMTP sending is enabled
// - smtp_address: SMTP server hostname
// - smtp_port: SMTP server port (typically 587 for TLS)
// - smtp_login: SMTP login/username
// - smtp_password: SMTP password
// - smtp_ssl: Whether to use SSL/TLS for SMTP
//
// Chatwoot lifecycle hooks:
// - before_validation :ensure_forward_to_address (sets default forwarding address)
// - before_create :ensure_imap_connection (validates IMAP credentials)
// - before_create :ensure_smtp_connection (validates SMTP credentials)
//
// gochat enhancements beyond Chatwoot:
// - mailbox_name: Friendly name for the mailbox (for display)
// - imap_ssl_mode: Explicit SSL mode (none/starttls/ssl) instead of boolean
// - smtp_ssl_mode: Explicit SSL mode instead of boolean
// - domain: Email domain for routing (e.g. "acme.com")
// - use_reply_to: Whether to use Reply-To header for contact identification
// ChannelEmail stores Email channel configuration for SMTP/IMAP.
type ChannelEmail struct {
model.Base
// Account foreign key — links to the Account that owns this channel
AccountID uint `gorm:"index;not null" json:"account_id"`
// Inbox foreign key — links to the Inbox that wraps this channel
InboxID uint `gorm:"not null;uniqueIndex" json:"inbox_id"`
// Inbound email address — the address customers send emails to
// Reference: Chatwoot EDITABLE_ATTRS = [:email]
Email string `gorm:"size:255;not null" json:"email"`
// Friendly name for the mailbox (e.g. "Acme Support")
MailboxName string `gorm:"size:255" json:"mailbox_name,omitempty"`
// Email domain for routing (extracted from Email, e.g. "acme.com")
Domain string `gorm:"size:255;index" json:"domain"`
// === IMAP Configuration ===
// Whether IMAP polling is enabled
IMAPEnabled bool `gorm:"default:false" json:"imap_enabled"`
// IMAP server hostname (e.g. "imap.gmail.com")
IMAPAddress string `gorm:"size:255" json:"imap_address,omitempty"`
// IMAP server port (typically 993 for SSL, 143 for plain)
IMAPPort int `gorm:"default:993" json:"imap_port,omitempty"`
// IMAP login/username
IMAPLogin string `gorm:"size:255" json:"imap_login,omitempty"`
// IMAP password (stored encrypted at application level)
IMAPPassword string `gorm:"size:255" json:"imap_password,omitempty"`
// IMAP SSL mode: "none", "starttls", "ssl"
IMAPSSLMode string `gorm:"size:20;default:ssl" json:"imap_ssl_mode,omitempty"`
// IMAP mailbox folder to monitor (e.g. "INBOX")
IMAPFolder string `gorm:"size:255;default:INBOX" json:"imap_folder,omitempty"`
// === SMTP Configuration ===
// Whether SMTP sending is enabled
SMTPEnabled bool `gorm:"default:false" json:"smtp_enabled"`
// SMTP server hostname (e.g. "smtp.gmail.com")
SMTPAddress string `gorm:"size:255" json:"smtp_address,omitempty"`
// SMTP server port (typically 587 for STARTTLS, 465 for SSL, 25 for plain)
SMTPPort int `gorm:"default:587" json:"smtp_port,omitempty"`
// SMTP login/username
SMTPLogin string `gorm:"size:255" json:"smtp_login,omitempty"`
// SMTP password (stored encrypted at application level)
SMTPPassword string `gorm:"size:255" json:"smtp_password,omitempty"`
// SMTP SSL mode: "none", "starttls", "ssl"
SMTPSSLMode string `gorm:"size:20;default:starttls" json:"smtp_ssl_mode,omitempty"`
// === Behavioral Configuration ===
// Whether to use Reply-To header for contact identification
// When true, the Reply-To address is used instead of From address
UseReplyTo bool `gorm:"default:false" json:"use_reply_to,omitempty"`
// Whether to automatically group emails into conversations by thread
AutoThread bool `gorm:"default:true" json:"auto_thread,omitempty"`
// Maximum attachment size in bytes (0 = no limit)
MaxAttachmentSize int64 `gorm:"default:0" json:"max_attachment_size,omitempty"`
}
func (ChannelEmail) TableName() string { return "channel_emails" }
// GetChannelBase returns the ChannelBase for this channel.
func (c *ChannelEmail) GetChannelBase() ChannelBase {
return ChannelBase{
AccountID: c.AccountID,
InboxID: c.InboxID,
ChannelType: "email",
}
}
// GetChannelType returns the channel type identifier.
func (c *ChannelEmail) GetChannelType() string { return "email" }
// IsIMAPConfigured returns true if IMAP connection parameters are set.
func (c *ChannelEmail) IsIMAPConfigured() bool {
return c.IMAPEnabled && c.IMAPAddress != "" && c.IMAPLogin != "" && c.IMAPPassword != ""
}
// IsSMTPConfigured returns true if SMTP connection parameters are set.
func (c *ChannelEmail) IsSMTPConfigured() bool {
return c.SMTPEnabled && c.SMTPAddress != "" && c.SMTPLogin != "" && c.SMTPPassword != ""
}
// IMAPConnectionString returns a human-readable IMAP connection summary.
func (c *ChannelEmail) IMAPConnectionString() string {
if !c.IsIMAPConfigured() {
return "IMAP not configured"
}
return c.IMAPLogin + "@" + c.IMAPAddress + ":" + formatPort(c.IMAPPort)
}
// SMTPConnectionString returns a human-readable SMTP connection summary.
func (c *ChannelEmail) SMTPConnectionString() string {
if !c.IsSMTPConfigured() {
return "SMTP not configured"
}
return c.SMTPLogin + "@" + c.SMTPAddress + ":" + formatPort(c.SMTPPort)
}
// formatPort converts port int to string, using defaults if zero.
func formatPort(port int) string {
if port == 0 {
return "default"
}
return fmt.Sprintf("%d", port)
}