feat: implement real IMAP message parsing with native CouchDB attachments
- Replace placeholder message generation with actual IMAP message fetching using go-message library - Add per-account CouchDB databases for better organization and isolation - Implement native CouchDB attachment storage with proper revision management - Add command line argument parsing with --max-messages flag for controlling message processing limits - Support both sync and archive modes with proper document synchronization - Add comprehensive test environment with Podman containers (GreenMail IMAP server + CouchDB) - Implement full MIME multipart parsing for proper body and attachment extraction - Add TLS and plain IMAP connection support based on port configuration - Update configuration system to support sync vs archive modes - Create test scripts and sample data for development and testing Key technical improvements: - Real email envelope and header processing with go-imap v2 API - MIME Content-Type and Content-Disposition parsing for attachment detection - CouchDB document ID generation using mailbox_uid format for uniqueness - Duplicate detection and prevention to avoid re-storing existing messages - Proper error handling and connection management for IMAP operations 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
79f19a8877
commit
ea6235b674
22 changed files with 1262 additions and 66 deletions
23
go/main.go
23
go/main.go
|
|
@ -12,7 +12,9 @@ import (
|
|||
)
|
||||
|
||||
func main() {
|
||||
cfg, err := config.LoadConfigWithDiscovery()
|
||||
args := config.ParseCommandLine()
|
||||
|
||||
cfg, err := config.LoadConfigWithDiscovery(args)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to load configuration: %v", err)
|
||||
}
|
||||
|
|
@ -46,7 +48,7 @@ func main() {
|
|||
|
||||
fmt.Printf(" - Processing source: %s\n", source.Name)
|
||||
if source.Protocol == "imap" {
|
||||
err := processImapSource(&source, couchClient, dbName)
|
||||
err := processImapSource(&source, couchClient, dbName, args.MaxMessages)
|
||||
if err != nil {
|
||||
log.Printf(" ERROR: Failed to process IMAP source %s: %v", source.Name, err)
|
||||
}
|
||||
|
|
@ -54,7 +56,7 @@ func main() {
|
|||
}
|
||||
}
|
||||
|
||||
func processImapSource(source *config.MailSource, couchClient *couch.Client, dbName string) error {
|
||||
func processImapSource(source *config.MailSource, couchClient *couch.Client, dbName string, maxMessages int) error {
|
||||
fmt.Printf(" Connecting to IMAP server: %s:%d\n", source.Host, source.Port)
|
||||
imapClient, err := mail.NewImapClient(source)
|
||||
if err != nil {
|
||||
|
|
@ -93,17 +95,26 @@ func processImapSource(source *config.MailSource, couchClient *couch.Client, dbN
|
|||
continue
|
||||
}
|
||||
|
||||
fmt.Printf(" Processing mailbox: %s\n", mailbox)
|
||||
fmt.Printf(" Processing mailbox: %s (mode: %s)\n", mailbox, source.Mode)
|
||||
|
||||
// Retrieve messages from the mailbox
|
||||
messages, err := imapClient.GetMessages(mailbox, sinceDate)
|
||||
messages, currentUIDs, err := imapClient.GetMessages(mailbox, sinceDate, maxMessages)
|
||||
if err != nil {
|
||||
log.Printf(" ERROR: Failed to get messages from %s: %v", mailbox, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Perform sync/archive logic
|
||||
syncCtx, syncCancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
err = couchClient.SyncMailbox(syncCtx, dbName, mailbox, currentUIDs, source.IsSyncMode())
|
||||
syncCancel()
|
||||
if err != nil {
|
||||
log.Printf(" ERROR: Failed to sync mailbox %s: %v", mailbox, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if len(messages) == 0 {
|
||||
fmt.Printf(" No messages found in %s\n", mailbox)
|
||||
fmt.Printf(" No new messages found in %s\n", mailbox)
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue