package main import ( "database/sql" "encoding/json" "fmt" "os" "path/filepath" "time" _ "github.com/mattn/go-sqlite3" ) const historyDBName = "history.db" type EmailStatus string const ( StatusSent EmailStatus = "sent" StatusDraft EmailStatus = "draft" ) type EmailHistory struct { ID int64 From string To string Cc string Bcc string Subject string BodyText string BodyHTML string Attachments string Status EmailStatus DeliveryMethod string CreatedAt time.Time SentAt *time.Time } func getHistoryDBPath() (string, error) { configDir, err := getConfigDir() if err != nil { return "", err } return filepath.Join(configDir, historyDBName), nil } func initHistoryDB() (*sql.DB, error) { dbPath, err := getHistoryDBPath() if err != nil { return nil, err } db, err := sql.Open("sqlite3", dbPath) if err != nil { return nil, err } _, err = db.Exec(` CREATE TABLE IF NOT EXISTS emails ( id INTEGER PRIMARY KEY AUTOINCREMENT, from_addr TEXT NOT NULL, to_addrs TEXT NOT NULL, cc_addrs TEXT, bcc_addrs TEXT, subject TEXT NOT NULL, body_text TEXT, body_html TEXT, attachments TEXT, status TEXT NOT NULL, delivery_method TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, sent_at DATETIME ) `) if err != nil { return nil, err } return db, nil } func SaveEmail(from, to, cc, bcc, subject, bodyText, bodyHTML, attachments, deliveryMethod string, status EmailStatus) (int64, error) { db, err := initHistoryDB() if err != nil { return 0, err } defer db.Close() stmt, err := db.Prepare(` INSERT INTO emails (from_addr, to_addrs, cc_addrs, bcc_addrs, subject, body_text, body_html, attachments, status, delivery_method, created_at, sent_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) `) if err != nil { return 0, err } defer stmt.Close() now := time.Now() var sentAt *time.Time if status == StatusSent { sentAt = &now } result, err := stmt.Exec(from, to, cc, bcc, subject, bodyText, bodyHTML, attachments, status, deliveryMethod, now, sentAt) if err != nil { return 0, err } return result.LastInsertId() } func UpdateEmailStatus(id int64, status EmailStatus) error { db, err := initHistoryDB() if err != nil { return err } defer db.Close() _, err = db.Exec("UPDATE emails SET status = ?, sent_at = ? WHERE id = ?", status, time.Now(), id) return err } func GetEmailHistory(status EmailStatus) ([]EmailHistory, error) { db, err := initHistoryDB() if err != nil { return nil, err } defer db.Close() var query string var args []interface{} if status == "" { query = "SELECT id, from_addr, to_addrs, cc_addrs, bcc_addrs, subject, body_text, body_html, attachments, status, delivery_method, created_at, sent_at FROM emails ORDER BY created_at DESC" } else { query = "SELECT id, from_addr, to_addrs, cc_addrs, bcc_addrs, subject, body_text, body_html, attachments, status, delivery_method, created_at, sent_at FROM emails WHERE status = ? ORDER BY created_at DESC" args = []interface{}{status} } rows, err := db.Query(query, args...) if err != nil { return nil, err } defer rows.Close() var histories []EmailHistory for rows.Next() { var h EmailHistory var sentAt sql.NullTime err := rows.Scan(&h.ID, &h.From, &h.To, &h.Cc, &h.Bcc, &h.Subject, &h.BodyText, &h.BodyHTML, &h.Attachments, &h.Status, &h.DeliveryMethod, &h.CreatedAt, &sentAt) if err != nil { return nil, err } if sentAt.Valid { h.SentAt = &sentAt.Time } histories = append(histories, h) } return histories, nil } func GetEmailByID(id int64) (*EmailHistory, error) { db, err := initHistoryDB() if err != nil { return nil, err } defer db.Close() var h EmailHistory var sentAt sql.NullTime err = db.QueryRow("SELECT id, from_addr, to_addrs, cc_addrs, bcc_addrs, subject, body_text, body_html, attachments, status, delivery_method, created_at, sent_at FROM emails WHERE id = ?", id).Scan( &h.ID, &h.From, &h.To, &h.Cc, &h.Bcc, &h.Subject, &h.BodyText, &h.BodyHTML, &h.Attachments, &h.Status, &h.DeliveryMethod, &h.CreatedAt, &sentAt, ) if err != nil { return nil, err } if sentAt.Valid { h.SentAt = &sentAt.Time } return &h, nil } func DeleteEmailHistory(id int64) error { db, err := initHistoryDB() if err != nil { return err } defer db.Close() _, err = db.Exec("DELETE FROM emails WHERE id = ?", id) return err } func getAttachmentsJSON(paths []string) string { if len(paths) == 0 { return "[]" } data, _ := json.Marshal(paths) return string(data) } func parseAttachmentsJSON(jsonStr string) []string { if jsonStr == "" || jsonStr == "[]" { return nil } var paths []string json.Unmarshal([]byte(jsonStr), &paths) return paths } func init() { dbPath, err := getHistoryDBPath() if err != nil { return } if _, err := os.Stat(dbPath); os.IsNotExist(err) { if _, err := initHistoryDB(); err != nil { fmt.Fprintf(os.Stderr, "Failed to initialize history DB: %v\n", err) } } }