folder_cache.go

  1package config
  2
  3import (
  4	"encoding/json"
  5	"os"
  6	"path/filepath"
  7	"strings"
  8	"time"
  9)
 10
 11// CachedFolders stores folder names for a single account.
 12type CachedFolders struct {
 13	AccountID string    `json:"account_id"`
 14	Folders   []string  `json:"folders"`
 15	UpdatedAt time.Time `json:"updated_at"`
 16}
 17
 18// FolderCache stores cached folders for all accounts.
 19type FolderCache struct {
 20	Accounts  []CachedFolders `json:"accounts"`
 21	UpdatedAt time.Time       `json:"updated_at"`
 22}
 23
 24// folderCacheFile returns the full path to the folder cache file.
 25func folderCacheFile() (string, error) {
 26	dir, err := cacheDir()
 27	if err != nil {
 28		return "", err
 29	}
 30	return filepath.Join(dir, "folder_cache.json"), nil
 31}
 32
 33// SaveFolderCache saves the folder cache to disk.
 34func SaveFolderCache(cache *FolderCache) error {
 35	path, err := folderCacheFile()
 36	if err != nil {
 37		return err
 38	}
 39	if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil {
 40		return err
 41	}
 42	cache.UpdatedAt = time.Now()
 43	data, err := json.MarshalIndent(cache, "", "  ")
 44	if err != nil {
 45		return err
 46	}
 47	return SecureWriteFile(path, data, 0600)
 48}
 49
 50// LoadFolderCache loads the folder cache from disk.
 51func LoadFolderCache() (*FolderCache, error) {
 52	path, err := folderCacheFile()
 53	if err != nil {
 54		return nil, err
 55	}
 56	data, err := SecureReadFile(path)
 57	if err != nil {
 58		return nil, err
 59	}
 60	var cache FolderCache
 61	if err := json.Unmarshal(data, &cache); err != nil {
 62		return nil, err
 63	}
 64	return &cache, nil
 65}
 66
 67// GetCachedFolders returns cached folder names for a specific account.
 68func GetCachedFolders(accountID string) []string {
 69	cache, err := LoadFolderCache()
 70	if err != nil {
 71		return nil
 72	}
 73	for _, acc := range cache.Accounts {
 74		if acc.AccountID == accountID {
 75			return acc.Folders
 76		}
 77	}
 78	return nil
 79}
 80
 81// SaveAccountFolders saves folder names for a specific account, merging into the existing cache.
 82func SaveAccountFolders(accountID string, folders []string) error {
 83	cache, err := LoadFolderCache()
 84	if err != nil {
 85		cache = &FolderCache{}
 86	}
 87
 88	found := false
 89	for i, acc := range cache.Accounts {
 90		if acc.AccountID == accountID {
 91			cache.Accounts[i].Folders = folders
 92			cache.Accounts[i].UpdatedAt = time.Now()
 93			found = true
 94			break
 95		}
 96	}
 97
 98	if !found {
 99		cache.Accounts = append(cache.Accounts, CachedFolders{
100			AccountID: accountID,
101			Folders:   folders,
102			UpdatedAt: time.Now(),
103		})
104	}
105
106	return SaveFolderCache(cache)
107}
108
109// --- Per-folder email cache ---
110
111// FolderEmailCache stores cached emails for a specific folder.
112type FolderEmailCache struct {
113	FolderName string        `json:"folder_name"`
114	Emails     []CachedEmail `json:"emails"`
115	UpdatedAt  time.Time     `json:"updated_at"`
116}
117
118// folderEmailCacheDir returns the directory for folder email cache files.
119func folderEmailCacheDir() (string, error) {
120	dir, err := cacheDir()
121	if err != nil {
122		return "", err
123	}
124	return filepath.Join(dir, "folder_emails"), nil
125}
126
127// folderEmailCacheFile returns the file path for a folder's email cache.
128// Uses a sanitized folder name to avoid filesystem issues.
129func folderEmailCacheFile(folderName string) (string, error) {
130	dir, err := folderEmailCacheDir()
131	if err != nil {
132		return "", err
133	}
134	// Sanitize folder name for use as filename
135	safe := sanitizeFolderName(folderName)
136	return filepath.Join(dir, safe+".json"), nil
137}
138
139func sanitizeFolderName(name string) string {
140	// Replace path separators and other problematic chars
141	replacer := strings.NewReplacer("/", "_", "\\", "_", ":", "_", " ", "_")
142	return replacer.Replace(name)
143}
144
145// SaveFolderEmailCache saves emails for a folder to disk.
146func SaveFolderEmailCache(folderName string, emails []CachedEmail) error {
147	path, err := folderEmailCacheFile(folderName)
148	if err != nil {
149		return err
150	}
151	if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil {
152		return err
153	}
154	cache := FolderEmailCache{
155		FolderName: folderName,
156		Emails:     emails,
157		UpdatedAt:  time.Now(),
158	}
159	data, err := json.Marshal(cache)
160	if err != nil {
161		return err
162	}
163	return SecureWriteFile(path, data, 0600)
164}
165
166// LoadFolderEmailCache loads cached emails for a folder from disk.
167func LoadFolderEmailCache(folderName string) ([]CachedEmail, error) {
168	path, err := folderEmailCacheFile(folderName)
169	if err != nil {
170		return nil, err
171	}
172	data, err := SecureReadFile(path)
173	if err != nil {
174		return nil, err
175	}
176	var cache FolderEmailCache
177	if err := json.Unmarshal(data, &cache); err != nil {
178		return nil, err
179	}
180	return cache.Emails, nil
181}