222 lines
5.0 KiB
Go
222 lines
5.0 KiB
Go
|
|
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)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|