feat: add comprehensive README documentation and clean up configuration

## Documentation Enhancements
- Create comprehensive README with installation, configuration, and usage examples
- Add simple, advanced, and provider-specific configuration examples
- Document all features: incremental sync, wildcard patterns, keyword filtering, attachment support
- Include production deployment guidance and troubleshooting section
- Add architecture documentation with database structure and document format examples

## Configuration Cleanup
- Remove unnecessary `database` field from CouchDB configuration
- Add `m2c_` prefix to all CouchDB database names for better namespace isolation
- Update GenerateAccountDBName() to consistently prefix databases with `m2c_`
- Clean up all configuration examples to remove deprecated database field

## Test Environment Simplification
- Simplify test script structure to eliminate confusion and redundancy
- Remove redundant populate-test-messages.sh wrapper script
- Update run-tests.sh to be comprehensive automated test with cleanup
- Maintain clear separation: automated tests vs manual testing environment
- Update all test scripts to expect m2c-prefixed database names

## Configuration Examples Added
- config-simple.json: Basic single Gmail account setup
- config-advanced.json: Multi-account with complex filtering and different providers
- config-providers.json: Real-world configurations for Gmail, Outlook, Yahoo, iCloud

## Benefits
- Clear documentation for users from beginner to advanced
- Namespace isolation prevents database conflicts in shared CouchDB instances
- Simplified test workflow eliminates user confusion about which scripts to use
- Comprehensive examples cover common email provider configurations

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Ole-Morten Duesund 2025-08-01 21:26:53 +02:00
commit c2ad55eaaf
17 changed files with 1139 additions and 111 deletions

View file

@ -73,14 +73,14 @@ func processImapSource(source *config.MailSource, couchClient *couch.Client, dbN
fmt.Printf(" Found %d mailboxes.\n", len(mailboxes))
// Parse the since date if provided
var sinceDate *time.Time
// Parse the since date from config if provided (fallback for first sync)
var configSinceDate *time.Time
if source.MessageFilter.Since != "" {
parsed, err := time.Parse("2006-01-02", source.MessageFilter.Since)
if err != nil {
log.Printf(" WARNING: Invalid since date format '%s', ignoring filter", source.MessageFilter.Since)
} else {
sinceDate = &parsed
configSinceDate = &parsed
}
}
@ -97,6 +97,32 @@ func processImapSource(source *config.MailSource, couchClient *couch.Client, dbN
fmt.Printf(" Processing mailbox: %s (mode: %s)\n", mailbox, source.Mode)
// Get sync metadata to determine incremental sync date
syncCtx, syncCancel := context.WithTimeout(context.Background(), 10*time.Second)
syncMetadata, err := couchClient.GetSyncMetadata(syncCtx, dbName, mailbox)
syncCancel()
if err != nil {
log.Printf(" ERROR: Failed to get sync metadata for %s: %v", mailbox, err)
continue
}
// Determine the since date for incremental sync
var sinceDate *time.Time
if syncMetadata != nil {
// Use last sync time for incremental sync
sinceDate = &syncMetadata.LastSyncTime
fmt.Printf(" Incremental sync since: %s (last synced %d messages)\n",
sinceDate.Format("2006-01-02 15:04:05"), syncMetadata.MessageCount)
} else {
// First sync - use config since date if available
sinceDate = configSinceDate
if sinceDate != nil {
fmt.Printf(" First sync since: %s (from config)\n", sinceDate.Format("2006-01-02"))
} else {
fmt.Printf(" First full sync (no date filter)\n")
}
}
// Retrieve messages from the mailbox
messages, currentUIDs, err := imapClient.GetMessages(mailbox, sinceDate, maxMessages, &source.MessageFilter)
if err != nil {
@ -105,9 +131,9 @@ func processImapSource(source *config.MailSource, couchClient *couch.Client, dbN
}
// Perform sync/archive logic
syncCtx, syncCancel := context.WithTimeout(context.Background(), 30*time.Second)
err = couchClient.SyncMailbox(syncCtx, dbName, mailbox, currentUIDs, source.IsSyncMode())
syncCancel()
mailboxSyncCtx, mailboxSyncCancel := context.WithTimeout(context.Background(), 30*time.Second)
err = couchClient.SyncMailbox(mailboxSyncCtx, dbName, mailbox, currentUIDs, source.IsSyncMode())
mailboxSyncCancel()
if err != nil {
log.Printf(" ERROR: Failed to sync mailbox %s: %v", mailbox, err)
continue
@ -143,6 +169,35 @@ func processImapSource(source *config.MailSource, couchClient *couch.Client, dbN
fmt.Printf(" Stored %d/%d messages from %s\n", stored, len(messages), mailbox)
totalStored += stored
// Update sync metadata after successful processing
if len(messages) > 0 {
// Find the highest UID processed
var maxUID uint32
for _, msg := range messages {
if msg.UID > maxUID {
maxUID = msg.UID
}
}
// Create/update sync metadata
newMetadata := &couch.SyncMetadata{
Mailbox: mailbox,
LastSyncTime: time.Now(),
LastMessageUID: maxUID,
MessageCount: stored,
}
// Store sync metadata
metadataCtx, metadataCancel := context.WithTimeout(context.Background(), 10*time.Second)
err = couchClient.StoreSyncMetadata(metadataCtx, dbName, newMetadata)
metadataCancel()
if err != nil {
log.Printf(" WARNING: Failed to store sync metadata for %s: %v", mailbox, err)
} else {
fmt.Printf(" Updated sync metadata (last UID: %d)\n", maxUID)
}
}
}
fmt.Printf(" Summary: Processed %d messages, stored %d new messages\n", totalMessages, totalStored)