mail2couch/go/couch/couch.go

130 lines
3.3 KiB
Go
Raw Normal View History

package couch
import (
"context"
"fmt"
"net/url"
"time"
"github.com/go-kivik/kivik/v4"
_ "github.com/go-kivik/kivik/v4/couchdb" // The CouchDB driver
"mail2couch/config"
"mail2couch/mail"
)
// Client wraps the Kivik client
type Client struct {
*kivik.Client
}
// MailDocument represents an email message stored in CouchDB
type MailDocument struct {
ID string `json:"_id,omitempty"`
Rev string `json:"_rev,omitempty"`
SourceUID string `json:"sourceUid"` // Unique ID from the mail source (e.g., IMAP UID)
Mailbox string `json:"mailbox"` // Source mailbox name
From []string `json:"from"`
To []string `json:"to"`
Subject string `json:"subject"`
Date time.Time `json:"date"`
Body string `json:"body"`
Headers map[string][]string `json:"headers"`
StoredAt time.Time `json:"storedAt"` // When the document was stored
DocType string `json:"docType"` // Always "mail"
}
// NewClient creates a new CouchDB client from the configuration
func NewClient(cfg *config.CouchDbConfig) (*Client, error) {
parsedURL, err := url.Parse(cfg.URL)
if err != nil {
return nil, fmt.Errorf("invalid couchdb url: %w", err)
}
parsedURL.User = url.UserPassword(cfg.User, cfg.Password)
dsn := parsedURL.String()
client, err := kivik.New("couch", dsn)
if err != nil {
return nil, err
}
return &Client{client}, nil
}
// EnsureDB ensures that the configured database exists.
func (c *Client) EnsureDB(ctx context.Context, dbName string) error {
exists, err := c.DBExists(ctx, dbName)
if err != nil {
return err
}
if !exists {
return c.CreateDB(ctx, dbName)
}
return nil
}
// ConvertMessage converts an IMAP message to a MailDocument
func ConvertMessage(msg *mail.Message, mailbox string) *MailDocument {
docID := fmt.Sprintf("%s_%d", mailbox, msg.UID)
return &MailDocument{
ID: docID,
SourceUID: fmt.Sprintf("%d", msg.UID),
Mailbox: mailbox,
From: msg.From,
To: msg.To,
Subject: msg.Subject,
Date: msg.Date,
Body: msg.Body,
Headers: msg.Headers,
StoredAt: time.Now(),
DocType: "mail",
}
}
// StoreMessage stores a mail message in CouchDB
func (c *Client) StoreMessage(ctx context.Context, dbName string, doc *MailDocument) error {
db := c.DB(dbName)
if db.Err() != nil {
return db.Err()
}
// Check if document already exists
exists, err := c.DocumentExists(ctx, dbName, doc.ID)
if err != nil {
return fmt.Errorf("failed to check if document exists: %w", err)
}
if exists {
return nil // Document already exists, skip
}
// Store the document
_, err = db.Put(ctx, doc.ID, doc)
if err != nil {
return fmt.Errorf("failed to store document: %w", err)
}
return nil
}
// StoreMessages stores multiple mail messages in CouchDB
func (c *Client) StoreMessages(ctx context.Context, dbName string, docs []*MailDocument) error {
for _, doc := range docs {
if err := c.StoreMessage(ctx, dbName, doc); err != nil {
return err
}
}
return nil
}
// DocumentExists checks if a document with the given ID already exists.
func (c *Client) DocumentExists(ctx context.Context, dbName, docID string) (bool, error) {
db := c.DB(dbName)
if db.Err() != nil {
return false, db.Err()
}
row := db.Get(ctx, docID)
return row.Err() == nil, nil
}