summaryrefslogtreecommitdiff
path: root/internal/store
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-03-04 11:12:44 -1000
committerPeter Stone <thepeterstone@gmail.com>2026-03-04 11:12:44 -1000
commit0fd54eddc40f517cf491310d4f8a60b0d79dc937 (patch)
treeb026328a49b9583efb7c94b3777830c31c46fa33 /internal/store
parent4853a4a917bb7942776ffd8b3e003ee03fc49160 (diff)
feat: sync log, cache clear endpoint, Todoist projects from cached tasks
- migration 016: sync_log table - store: AddSyncLogEntry, GetRecentSyncLog, InvalidateAllCaches, GetProjectsFromTasks - settings: HandleClearCache (POST /settings/clear-cache), SyncLog in page data - settings: use GetProjectsFromTasks instead of deprecated Todoist REST /projects - handlers: populate atom projects from store - agent: log warning on registration failure instead of silently swallowing - google_tasks: simplify URL literal - tests: sync log CRUD, clear cache handler, settings page includes sync log, sync sources adds log entry, incremental sync paths, task completion response/headers, calendar cache fallback Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'internal/store')
-rw-r--r--internal/store/sqlite.go70
1 files changed, 70 insertions, 0 deletions
diff --git a/internal/store/sqlite.go b/internal/store/sqlite.go
index 158febc..366b24e 100644
--- a/internal/store/sqlite.go
+++ b/internal/store/sqlite.go
@@ -1397,6 +1397,37 @@ func (s *Store) SyncSourceConfigs(source, itemType string, items []models.Source
return tx.Commit()
}
+// InvalidateAllCaches removes cache metadata for all known cache keys
+func (s *Store) InvalidateAllCaches() error {
+ _, err := s.db.Exec(`DELETE FROM cache_metadata WHERE key IN (?, ?, ?, ?)`,
+ CacheKeyTodoistTasks, CacheKeyTrelloBoards, CacheKeyPlanToEatMeals, CacheKeyGoogleCalendar)
+ return err
+}
+
+// GetProjectsFromTasks returns distinct projects from the tasks table
+func (s *Store) GetProjectsFromTasks() ([]models.Project, error) {
+ rows, err := s.db.Query(`
+ SELECT DISTINCT project_id, project_name
+ FROM tasks
+ WHERE project_id != ''
+ ORDER BY project_name
+ `)
+ if err != nil {
+ return nil, err
+ }
+ defer func() { _ = rows.Close() }()
+
+ var projects []models.Project
+ for rows.Next() {
+ var p models.Project
+ if err := rows.Scan(&p.ID, &p.Name); err != nil {
+ return nil, err
+ }
+ projects = append(projects, p)
+ }
+ return projects, rows.Err()
+}
+
// Feature toggles
// GetFeatureToggles returns all feature toggles
@@ -1457,3 +1488,42 @@ func (s *Store) DeleteFeatureToggle(name string) error {
_, err := s.db.Exec(`DELETE FROM feature_toggles WHERE name = ?`, name)
return err
}
+
+// SyncLogEntry represents a single entry in the sync activity log
+type SyncLogEntry struct {
+ ID int64
+ EventType string
+ Message string
+ CreatedAt time.Time
+}
+
+// AddSyncLogEntry records a sync or cache event in the log
+func (s *Store) AddSyncLogEntry(eventType, message string) error {
+ _, err := s.db.Exec(
+ `INSERT INTO sync_log (event_type, message) VALUES (?, ?)`,
+ eventType, message,
+ )
+ return err
+}
+
+// GetRecentSyncLog returns the most recent sync log entries, newest first
+func (s *Store) GetRecentSyncLog(limit int) ([]SyncLogEntry, error) {
+ rows, err := s.db.Query(
+ `SELECT id, event_type, message, created_at FROM sync_log ORDER BY id DESC LIMIT ?`,
+ limit,
+ )
+ if err != nil {
+ return nil, err
+ }
+ defer func() { _ = rows.Close() }()
+
+ var entries []SyncLogEntry
+ for rows.Next() {
+ var e SyncLogEntry
+ if err := rows.Scan(&e.ID, &e.EventType, &e.Message, &e.CreatedAt); err != nil {
+ return nil, err
+ }
+ entries = append(entries, e)
+ }
+ return entries, rows.Err()
+}