feat: implement Go-based mail2couch with working IMAP and CouchDB integration
- Add configuration system with automatic file discovery (current dir, config subdir, user home, XDG config) - Implement IMAP client with TLS connection, authentication, and mailbox listing - Add CouchDB integration with database creation and document storage - Support folder filtering (include/exclude) and date filtering (since parameter) - Include duplicate detection to prevent re-storing existing messages - Add comprehensive error handling and logging throughout - Structure code in clean packages: config, mail, couch - Application currently uses placeholder messages to test the storage pipeline - Ready for real IMAP message parsing implementation 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
d0caff800a
commit
1e4a67d4cb
9 changed files with 746 additions and 0 deletions
111
go/config/config.go
Normal file
111
go/config/config.go
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
CouchDb CouchDbConfig `json:"couchDb"`
|
||||
MailSources []MailSource `json:"mailSources"`
|
||||
}
|
||||
|
||||
type CouchDbConfig struct {
|
||||
URL string `json:"url"`
|
||||
User string `json:"user"`
|
||||
Password string `json:"password"`
|
||||
Database string `json:"database"`
|
||||
}
|
||||
|
||||
type MailSource struct {
|
||||
Name string `json:"name"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Protocol string `json:"protocol"`
|
||||
Host string `json:"host"`
|
||||
Port int `json:"port"`
|
||||
User string `json:"user"`
|
||||
Password string `json:"password"`
|
||||
Sync bool `json:"sync"`
|
||||
FolderFilter FolderFilter `json:"folderFilter"`
|
||||
MessageFilter MessageFilter `json:"messageFilter"`
|
||||
}
|
||||
|
||||
type FolderFilter struct {
|
||||
Include []string `json:"include"`
|
||||
Exclude []string `json:"exclude"`
|
||||
}
|
||||
|
||||
type MessageFilter struct {
|
||||
Since string `json:"since,omitempty"`
|
||||
}
|
||||
|
||||
func LoadConfig(path string) (*Config, error) {
|
||||
configFile, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer configFile.Close()
|
||||
|
||||
var config Config
|
||||
jsonParser := json.NewDecoder(configFile)
|
||||
if err = jsonParser.Decode(&config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
// FindConfigFile searches for config.json in the following order:
|
||||
// 1. Path specified by -config flag
|
||||
// 2. ./config.json (current directory)
|
||||
// 3. ~/.config/mail2couch/config.json (user config directory)
|
||||
// 4. ~/.mail2couch.json (user home directory)
|
||||
func FindConfigFile() (string, error) {
|
||||
// Check for command line flag
|
||||
configFlag := flag.String("config", "", "Path to configuration file")
|
||||
flag.Parse()
|
||||
|
||||
if *configFlag != "" {
|
||||
if _, err := os.Stat(*configFlag); err == nil {
|
||||
return *configFlag, nil
|
||||
}
|
||||
return "", fmt.Errorf("specified config file not found: %s", *configFlag)
|
||||
}
|
||||
|
||||
// List of possible config file locations in order of preference
|
||||
candidates := []string{
|
||||
"config.json", // Current directory
|
||||
"config/config.json", // Config subdirectory
|
||||
}
|
||||
|
||||
// Add user directory paths
|
||||
if homeDir, err := os.UserHomeDir(); err == nil {
|
||||
candidates = append(candidates,
|
||||
filepath.Join(homeDir, ".config", "mail2couch", "config.json"),
|
||||
filepath.Join(homeDir, ".mail2couch.json"),
|
||||
)
|
||||
}
|
||||
|
||||
// Try each candidate location
|
||||
for _, candidate := range candidates {
|
||||
if _, err := os.Stat(candidate); err == nil {
|
||||
return candidate, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("no configuration file found. Searched locations: %v", candidates)
|
||||
}
|
||||
|
||||
// LoadConfigWithDiscovery loads configuration using automatic file discovery
|
||||
func LoadConfigWithDiscovery() (*Config, error) {
|
||||
configPath, err := FindConfigFile()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fmt.Printf("Using configuration file: %s\n", configPath)
|
||||
return LoadConfig(configPath)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue