feat(store): add account from_address field + v2 migration

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-23 20:16:15 +01:00
parent a4c49d4aca
commit cdffb15004
5 changed files with 143 additions and 23 deletions
+25 -14
View File
@@ -23,6 +23,7 @@ type Account struct {
SMTPSecurity string // tls | starttls
AuthType string // password | oauth2
Username string
FromAddress string // send-as identity; blank ⇒ fall back to Username
Password string // decrypted; empty in ListAccounts
WhitelistInEnabled bool
WhitelistOutEnabled bool
@@ -30,6 +31,15 @@ type Account struct {
ProcessBacklog bool
}
// SendFrom returns the From identity for outgoing mail, falling back to the
// login username when no explicit from-address is configured.
func (a Account) SendFrom() string {
if a.FromAddress != "" {
return a.FromAddress
}
return a.Username
}
func (s *Store) AddAccount(a Account) (int64, error) {
var encPw []byte
if a.Password != "" {
@@ -42,12 +52,12 @@ func (s *Store) AddAccount(a Account) (int64, error) {
res, err := s.db.Exec(`
INSERT INTO accounts
(name,mode,imap_host,imap_port,imap_security,smtp_host,smtp_port,smtp_security,
auth_type,username,
auth_type,username,from_address,
enc_password,whitelist_in_enabled,whitelist_out_enabled,subject_regex,process_backlog)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`,
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`,
a.Name, a.Mode, a.IMAPHost, a.IMAPPort, a.IMAPSecurity,
nullStr(a.SMTPHost), nullInt(a.SMTPPort), nullStr(a.SMTPSecurity),
a.AuthType, a.Username,
a.AuthType, a.Username, nullStr(a.FromAddress),
encPw, b2i(a.WhitelistInEnabled), b2i(a.WhitelistOutEnabled),
nullStr(a.SubjectRegex), b2i(a.ProcessBacklog))
if err != nil {
@@ -59,7 +69,7 @@ func (s *Store) AddAccount(a Account) (int64, error) {
func (s *Store) GetAccount(name string) (Account, error) {
row := s.db.QueryRow(`
SELECT id,name,mode,imap_host,imap_port,imap_security,smtp_host,smtp_port,smtp_security,
auth_type,username,
auth_type,username,from_address,
enc_password,whitelist_in_enabled,whitelist_out_enabled,subject_regex,process_backlog
FROM accounts WHERE name = ?`, name)
a, encPw, err := scanAccount(row)
@@ -82,7 +92,7 @@ func (s *Store) GetAccount(name string) (Account, error) {
func (s *Store) ListAccounts() ([]Account, error) {
rows, err := s.db.Query(`
SELECT id,name,mode,imap_host,imap_port,imap_security,smtp_host,smtp_port,smtp_security,
auth_type,username,
auth_type,username,from_address,
enc_password,whitelist_in_enabled,whitelist_out_enabled,subject_regex,process_backlog
FROM accounts ORDER BY name`)
if err != nil {
@@ -108,12 +118,12 @@ func (s *Store) UpdateAccount(a Account) error {
// Build the SET clause, conditionally including secret columns.
set := `mode=?, imap_host=?, imap_port=?, imap_security=?,
smtp_host=?, smtp_port=?, smtp_security=?,
auth_type=?, username=?,
auth_type=?, username=?, from_address=?,
whitelist_in_enabled=?, whitelist_out_enabled=?, subject_regex=?, process_backlog=?`
args := []any{
a.Mode, a.IMAPHost, a.IMAPPort, a.IMAPSecurity,
nullStr(a.SMTPHost), nullInt(a.SMTPPort), nullStr(a.SMTPSecurity),
a.AuthType, a.Username,
a.AuthType, a.Username, nullStr(a.FromAddress),
b2i(a.WhitelistInEnabled), b2i(a.WhitelistOutEnabled),
nullStr(a.SubjectRegex), b2i(a.ProcessBacklog),
}
@@ -152,16 +162,16 @@ type scanner interface{ Scan(dest ...any) error }
func scanAccount(sc scanner) (Account, []byte, error) {
var (
a Account
encPw []byte
subj, smtpHost, smtpSec sql.NullString
smtpPort sql.NullInt64
wlIn, wlOut int
backlog int
a Account
encPw []byte
subj, smtpHost, smtpSec, fromAddr sql.NullString
smtpPort sql.NullInt64
wlIn, wlOut int
backlog int
)
err := sc.Scan(&a.ID, &a.Name, &a.Mode, &a.IMAPHost, &a.IMAPPort, &a.IMAPSecurity,
&smtpHost, &smtpPort, &smtpSec,
&a.AuthType, &a.Username, &encPw, &wlIn, &wlOut, &subj, &backlog)
&a.AuthType, &a.Username, &fromAddr, &encPw, &wlIn, &wlOut, &subj, &backlog)
if err != nil {
return Account{}, nil, err
}
@@ -172,6 +182,7 @@ func scanAccount(sc scanner) (Account, []byte, error) {
a.WhitelistOutEnabled = wlOut != 0
a.ProcessBacklog = backlog != 0
a.SubjectRegex = subj.String
a.FromAddress = fromAddr.String
return a, encPw, nil
}