config.go

  1package config
  2
  3import (
  4	"encoding/json"
  5	"os"
  6	"path/filepath"
  7
  8	"github.com/google/uuid"
  9)
 10
 11// Account stores the configuration for a single email account.
 12type Account struct {
 13	ID              string `json:"id"`
 14	Name            string `json:"name"`
 15	Email           string `json:"email"`
 16	Password        string `json:"password"`
 17	ServiceProvider string `json:"service_provider"` // "gmail", "icloud", or "custom"
 18
 19	// Custom server settings (used when ServiceProvider is "custom")
 20	IMAPServer string `json:"imap_server,omitempty"`
 21	IMAPPort   int    `json:"imap_port,omitempty"`
 22	SMTPServer string `json:"smtp_server,omitempty"`
 23	SMTPPort   int    `json:"smtp_port,omitempty"`
 24}
 25
 26// Config stores the user's email configuration with multiple accounts.
 27type Config struct {
 28	Accounts []Account `json:"accounts"`
 29}
 30
 31// GetIMAPServer returns the IMAP server address for the account.
 32func (a *Account) GetIMAPServer() string {
 33	switch a.ServiceProvider {
 34	case "gmail":
 35		return "imap.gmail.com"
 36	case "icloud":
 37		return "imap.mail.me.com"
 38	case "custom":
 39		return a.IMAPServer
 40	default:
 41		return ""
 42	}
 43}
 44
 45// GetIMAPPort returns the IMAP port for the account.
 46func (a *Account) GetIMAPPort() int {
 47	switch a.ServiceProvider {
 48	case "gmail", "icloud":
 49		return 993
 50	case "custom":
 51		if a.IMAPPort != 0 {
 52			return a.IMAPPort
 53		}
 54		return 993 // Default IMAP SSL port
 55	default:
 56		return 993
 57	}
 58}
 59
 60// GetSMTPServer returns the SMTP server address for the account.
 61func (a *Account) GetSMTPServer() string {
 62	switch a.ServiceProvider {
 63	case "gmail":
 64		return "smtp.gmail.com"
 65	case "icloud":
 66		return "smtp.mail.me.com"
 67	case "custom":
 68		return a.SMTPServer
 69	default:
 70		return ""
 71	}
 72}
 73
 74// GetSMTPPort returns the SMTP port for the account.
 75func (a *Account) GetSMTPPort() int {
 76	switch a.ServiceProvider {
 77	case "gmail", "icloud":
 78		return 587
 79	case "custom":
 80		if a.SMTPPort != 0 {
 81			return a.SMTPPort
 82		}
 83		return 587 // Default SMTP TLS port
 84	default:
 85		return 587
 86	}
 87}
 88
 89// configDir returns the path to the configuration directory.
 90func configDir() (string, error) {
 91	home, err := os.UserHomeDir()
 92	if err != nil {
 93		return "", err
 94	}
 95	return filepath.Join(home, ".config", "matcha"), nil
 96}
 97
 98// configFile returns the full path to the configuration file.
 99func configFile() (string, error) {
100	dir, err := configDir()
101	if err != nil {
102		return "", err
103	}
104	return filepath.Join(dir, "config.json"), nil
105}
106
107// SaveConfig saves the given configuration to the config file.
108func SaveConfig(config *Config) error {
109	path, err := configFile()
110	if err != nil {
111		return err
112	}
113	if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil {
114		return err
115	}
116	data, err := json.MarshalIndent(config, "", "  ")
117	if err != nil {
118		return err
119	}
120	return os.WriteFile(path, data, 0600)
121}
122
123// LoadConfig loads the configuration from the config file.
124func LoadConfig() (*Config, error) {
125	path, err := configFile()
126	if err != nil {
127		return nil, err
128	}
129	data, err := os.ReadFile(path)
130	if err != nil {
131		return nil, err
132	}
133	var config Config
134	if err := json.Unmarshal(data, &config); err != nil {
135		// Try to load legacy single-account config
136		var legacyConfig legacyConfigFormat
137		if legacyErr := json.Unmarshal(data, &legacyConfig); legacyErr == nil && legacyConfig.Email != "" {
138			// Convert legacy config to new format
139			config = Config{
140				Accounts: []Account{
141					{
142						ID:              uuid.New().String(),
143						Name:            legacyConfig.Name,
144						Email:           legacyConfig.Email,
145						Password:        legacyConfig.Password,
146						ServiceProvider: legacyConfig.ServiceProvider,
147					},
148				},
149			}
150			// Save the migrated config
151			if saveErr := SaveConfig(&config); saveErr != nil {
152				return nil, saveErr
153			}
154			return &config, nil
155		}
156		return nil, err
157	}
158	return &config, nil
159}
160
161// legacyConfigFormat represents the old single-account configuration format.
162type legacyConfigFormat struct {
163	ServiceProvider string `json:"service_provider"`
164	Email           string `json:"email"`
165	Password        string `json:"password"`
166	Name            string `json:"name"`
167}
168
169// AddAccount adds a new account to the configuration.
170func (c *Config) AddAccount(account Account) {
171	if account.ID == "" {
172		account.ID = uuid.New().String()
173	}
174	c.Accounts = append(c.Accounts, account)
175}
176
177// RemoveAccount removes an account by its ID.
178func (c *Config) RemoveAccount(id string) bool {
179	for i, acc := range c.Accounts {
180		if acc.ID == id {
181			c.Accounts = append(c.Accounts[:i], c.Accounts[i+1:]...)
182			return true
183		}
184	}
185	return false
186}
187
188// GetAccountByID returns an account by its ID.
189func (c *Config) GetAccountByID(id string) *Account {
190	for i := range c.Accounts {
191		if c.Accounts[i].ID == id {
192			return &c.Accounts[i]
193		}
194	}
195	return nil
196}
197
198// GetAccountByEmail returns an account by its email address.
199func (c *Config) GetAccountByEmail(email string) *Account {
200	for i := range c.Accounts {
201		if c.Accounts[i].Email == email {
202			return &c.Accounts[i]
203		}
204	}
205	return nil
206}
207
208// HasAccounts returns true if there are any configured accounts.
209func (c *Config) HasAccounts() bool {
210	return len(c.Accounts) > 0
211}
212
213// GetFirstAccount returns the first account or nil if none exist.
214func (c *Config) GetFirstAccount() *Account {
215	if len(c.Accounts) > 0 {
216		return &c.Accounts[0]
217	}
218	return nil
219}