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

@ -86,6 +86,7 @@ func (c *ImapClient) ListMailboxes() ([]string, error) {
// GetMessages retrieves messages from a specific mailbox with filtering support
// Returns messages and a map of all current UIDs in the mailbox
// maxMessages: 0 means no limit, > 0 limits the number of messages to fetch
// since: if provided, only fetch messages newer than this date (for incremental sync)
func (c *ImapClient) GetMessages(mailbox string, since *time.Time, maxMessages int, messageFilter *config.MessageFilter) ([]*Message, map[uint32]bool, error) {
// Select the mailbox
mbox, err := c.Select(mailbox, nil).Wait()
@ -97,27 +98,80 @@ func (c *ImapClient) GetMessages(mailbox string, since *time.Time, maxMessages i
return []*Message{}, make(map[uint32]bool), nil
}
// For now, use a simpler approach to get all sequence numbers
var messages []*Message
currentUIDs := make(map[uint32]bool)
// First, get all current UIDs in the mailbox for sync purposes
allUIDsSet := imap.SeqSet{}
allUIDsSet.AddRange(1, mbox.NumMessages)
// Determine how many messages to fetch
numToFetch := mbox.NumMessages
if maxMessages > 0 && int(numToFetch) > maxMessages {
numToFetch = uint32(maxMessages)
// Fetch UIDs for all messages to track current state
uidCmd := c.Fetch(allUIDsSet, &imap.FetchOptions{UID: true})
for {
msg := uidCmd.Next()
if msg == nil {
break
}
data, err := msg.Collect()
if err != nil {
continue
}
if data.UID != 0 {
currentUIDs[uint32(data.UID)] = true
}
}
uidCmd.Close()
if numToFetch == 0 {
return []*Message{}, currentUIDs, nil
}
// Create sequence set for fetching (1:numToFetch)
seqSet := imap.SeqSet{}
seqSet.AddRange(1, numToFetch)
// Track all sequence numbers (for sync we'll need to get UIDs later)
for i := uint32(1); i <= mbox.NumMessages; i++ {
currentUIDs[i] = true // Using sequence numbers for now
// Determine which messages to fetch based on since date
var seqSet imap.SeqSet
if since != nil {
// Use IMAP SEARCH to find messages since the specified date
searchCriteria := &imap.SearchCriteria{
Since: *since,
}
searchCmd := c.Search(searchCriteria, nil)
searchResults, err := searchCmd.Wait()
if err != nil {
log.Printf("IMAP SEARCH failed, falling back to fetch all: %v", err)
// Fall back to fetching all messages
numToFetch := mbox.NumMessages
if maxMessages > 0 && int(numToFetch) > maxMessages {
numToFetch = uint32(maxMessages)
}
seqSet.AddRange(mbox.NumMessages-numToFetch+1, mbox.NumMessages)
} else {
// Convert search results to sequence set
searchSeqNums := searchResults.AllSeqNums()
if len(searchSeqNums) == 0 {
return []*Message{}, currentUIDs, nil
}
// Limit results if maxMessages is specified
if maxMessages > 0 && len(searchSeqNums) > maxMessages {
searchSeqNums = searchSeqNums[len(searchSeqNums)-maxMessages:]
}
for _, seqNum := range searchSeqNums {
seqSet.AddNum(seqNum)
}
}
} else {
// No since date specified, fetch recent messages up to maxMessages
numToFetch := mbox.NumMessages
if maxMessages > 0 && int(numToFetch) > maxMessages {
numToFetch = uint32(maxMessages)
}
if numToFetch == 0 {
return []*Message{}, currentUIDs, nil
}
// Fetch the most recent messages
seqSet.AddRange(mbox.NumMessages-numToFetch+1, mbox.NumMessages)
}
// Fetch message data - get envelope and full message body