From 4fa511ac5f2aa422456c49610a2cf29dfee46dfd Mon Sep 17 00:00:00 2001 From: srdusr <99972264+srdusr@users.noreply.github.com> Date: Mon, 18 May 2026 23:30:00 +0200 Subject: History deletion: delete one entry (x) or clear everything (X) Store had full CRUD for match-and-replace rules but no way to delete or prune history at all - it only ever grew, with no way to remove an accidental capture or start a new engagement clean short of manually deleting the DB file outside the tool entirely. internal/store: DeleteEntry(id) removes one history row and its history_fts search index row in a transaction. ClearHistory() empties both tables entirely; rules are untouched. internal/ipc: new "delete_entry" and "clear_history" request types, Client.DeleteEntry/ ClearHistory methods. TUI: 'x' deletes the selected history entry, 'X' clears the whole database. Both gated behind a y/n confirmation - a small reusable confirmPrompt/confirmYes model state, checked first in the history list's key handling, so any key other than y/Y safely cancels rather than falling through to whatever that key normally does elsewhere (this also means ctrl+c during a pending confirmation cancels the prompt rather than quitting - a deliberate fail-safe, not an oversight: quick to dismiss, and a second ctrl+c then quits normally). 'X' is explicitly NOT scoped to an active search filter - it always clears the true total (read from daemon status, not len(m.entries), which would understate the count under a filter and make the confirmation prompt itself misleading about what's about to happen). Verified live in tmux against a running daemon with real captured entries: 'x' shows "delete #N? y/n", 'n' cancels with the entry untouched, 'y' deletes it and the list/count both refresh correctly; 'X' shows "clear all N history entries (not just this view)? y/n" with the true count, 'y' empties the database (confirmed via direct SQLite query: both history and history_fts at 0 rows afterward) and the TUI correctly shows "history (0)" / "0 requests"; 'x'/'X' on an empty list correctly no-op without crashing. go build/vet/gofmt/test/mod tidy all clean. --- internal/store/store.go | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'internal/store') diff --git a/internal/store/store.go b/internal/store/store.go index 19b71b1..c0cdea5 100644 --- a/internal/store/store.go +++ b/internal/store/store.go @@ -560,6 +560,39 @@ func (s *Store) SetRuleEnabled(id int64, enabled bool) error { return nil } +// DeleteEntry removes a single history entry and its search index row. +func (s *Store) DeleteEntry(id int64) error { + tx, err := s.db.Begin() + if err != nil { + return fmt.Errorf("begin delete entry: %w", err) + } + defer tx.Rollback() + if _, err := tx.Exec(`DELETE FROM history WHERE id = ?`, id); err != nil { + return fmt.Errorf("delete history entry %d: %w", id, err) + } + if _, err := tx.Exec(`DELETE FROM history_fts WHERE rowid = ?`, id); err != nil { + return fmt.Errorf("delete search index for entry %d: %w", id, err) + } + return tx.Commit() +} + +// ClearHistory removes every history entry and resets the search index. +// Rules are untouched - this only clears captured traffic. +func (s *Store) ClearHistory() error { + tx, err := s.db.Begin() + if err != nil { + return fmt.Errorf("begin clear history: %w", err) + } + defer tx.Rollback() + if _, err := tx.Exec(`DELETE FROM history`); err != nil { + return fmt.Errorf("clear history: %w", err) + } + if _, err := tx.Exec(`DELETE FROM history_fts`); err != nil { + return fmt.Errorf("clear search index: %w", err) + } + return tx.Commit() +} + // DeleteRule removes a rule. func (s *Store) DeleteRule(id int64) error { if _, err := s.db.Exec(`DELETE FROM rules WHERE id = ?`, id); err != nil { -- cgit v1.2.3