detached 227825f

feat(cli): enhance purge command for separate cookie database and history deletion

commit
227825f676ace03728ea137163d3fa7c97b17523
author
bnema · · 2025-11-19 18:24 UTC
parent
992a34a
signature
GPG Unverified2B82207D370127A0 The key is unknown
1 file · +84 -22
M internal/cli/purge.go +84 -22
@@ -2,6 +2,7 @@ package cli
22
33 import (
44 "bufio"
5+ "context"
56 "fmt"
67 "os"
78 "path/filepath"
@@ -9,6 +10,7 @@ import (
910 "unicode"
1011
1112 "github.com/bnema/dumber/internal/config"
13+ "github.com/bnema/dumber/internal/db"
1214 "github.com/spf13/cobra"
1315 )
1416
@@ -45,44 +47,46 @@ func NewPurgeCmd() *cobra.Command {
4547 Long: `Purge various dumber data and cache files. By default, purges everything.
4648
4749 Available purge targets:
48- --database, -d Purge the SQLite database (history, zoom levels, cookies,
49- certificate validations, and fuzzy cache metadata)
50- --history-cache, -H Purge dmenu fuzzy search cache for history (binary file, will be
51- database-backed in the future)
52- --browser-cache, -c Purge WebKit browser cache (cached images, files, etc.)
53- --browser-data, -b Purge WebKit browser data (localStorage, sessionStorage, IndexedDB)
54- Note: Cookies are now stored in the main database and deleted via --database
50+ --database, -d Purge SQLite databases (dumber.sqlite and cookies.db)
51+ Includes: history, zoom levels, certificates, cookies, favorites
52+ --history-cache, -H Purge browsing history from database and dmenu fuzzy search cache
53+ (binary file). Keeps cookies, zoom levels, and other database data.
54+ --browser-cache, -b Purge WebKit browser cache (cached images, files, etc.)
55+ --browser-data, -B Purge WebKit browser data (localStorage, sessionStorage, IndexedDB)
56+ Note: Cookies are in cookies.db and deleted via --database
5557 --filter-cache, -F Purge compiled ad blocking filters cache
56- --state, -s Purge all state data (includes database and caches)
57- --config Purge configuration files
58+ --state, -s Purge all state data (includes databases and caches)
59+ --config, -c Purge configuration files
5860 --all, -a Purge everything (default if no specific flags are provided)
5961
6062 Use --force to skip the confirmation prompt.
6163
62-Database Consolidation:
63- Since version with database consolidation, cookies are stored in dumber.sqlite.
64- Use --database to delete cookies along with history and other browser data.
65- The --browser-data flag now only affects localStorage and other WebKit-managed data.
64+Database Storage:
65+ Cookies are stored in cookies.db (separate from dumber.sqlite due to WebKit requirements).
66+ Use --database to delete both dumber.sqlite and cookies.db.
67+ The --browser-data flag only affects localStorage and other WebKit-managed data.
6668
6769 Examples:
6870 dumber purge # Purge everything (with confirmation)
6971 dumber purge --force # Purge everything (no confirmation)
70- dumber purge -d -H -c -F # Purge database and all caches
72+ dumber purge -d -H -b -F # Purge database and all caches
7173 dumber purge --filter-cache -f # Force purge filter cache only
72- dumber purge -d # Purge database (includes cookies)`,
74+ dumber purge -d # Purge database (includes cookies)
75+ dumber purge -H # Purge only browsing history (keep cookies)
76+ dumber purge -c # Purge config file only`,
7377 RunE: func(_ *cobra.Command, _ []string) error {
7478 return executePurge(flags)
7579 },
7680 }
7781
7882 // Add flags
79- cmd.Flags().BoolVarP(&flags.Database, "database", "d", false, "Purge the SQLite database (includes cookies)")
80- cmd.Flags().BoolVarP(&flags.HistoryCache, "history-cache", "H", false, "Purge dmenu fuzzy search cache (binary file)")
81- cmd.Flags().BoolVarP(&flags.BrowserCache, "browser-cache", "c", false, "Purge WebKit browser cache")
82- cmd.Flags().BoolVarP(&flags.BrowserData, "browser-data", "b", false, "Purge WebKit browser data (localStorage, sessionStorage)")
83+ cmd.Flags().BoolVarP(&flags.Database, "database", "d", false, "Purge SQLite databases (dumber.sqlite and cookies.db)")
84+ cmd.Flags().BoolVarP(&flags.HistoryCache, "history-cache", "H", false, "Purge browsing history and dmenu fuzzy cache")
85+ cmd.Flags().BoolVarP(&flags.BrowserCache, "browser-cache", "b", false, "Purge WebKit browser cache")
86+ cmd.Flags().BoolVarP(&flags.BrowserData, "browser-data", "B", false, "Purge WebKit browser data (localStorage, sessionStorage)")
8387 cmd.Flags().BoolVarP(&flags.FilterCache, "filter-cache", "F", false, "Purge compiled ad blocking filters cache")
84- cmd.Flags().BoolVarP(&flags.State, "state", "s", false, "Purge all state data (database and caches)")
85- cmd.Flags().BoolVar(&flags.Config, "config", false, "Purge configuration files")
88+ cmd.Flags().BoolVarP(&flags.State, "state", "s", false, "Purge all state data (databases and caches)")
89+ cmd.Flags().BoolVarP(&flags.Config, "config", "c", false, "Purge configuration files")
8690 cmd.Flags().BoolVarP(&flags.All, "all", "a", false, "Purge everything")
8791 cmd.Flags().BoolVarP(&flags.Force, "force", "f", false, "Skip confirmation prompt")
8892
@@ -169,11 +173,20 @@ func getPurgePaths(items []string) (map[string][]string, error) {
169173 for _, item := range items {
170174 switch item {
171175 case "database":
176+ // Get main database path
172177 dbPath, err := config.GetDatabaseFile()
173178 if err != nil {
174179 return nil, fmt.Errorf("failed to get database path: %w", err)
175180 }
176- paths[item] = []string{dbPath}
181+
182+ // Get data dir for cookies.db
183+ dataDir, err := config.GetDataDir()
184+ if err != nil {
185+ return nil, fmt.Errorf("failed to get data directory: %w", err)
186+ }
187+ cookiesPath := filepath.Join(dataDir, "cookies.db")
188+
189+ paths[item] = []string{dbPath, cookiesPath}
177190
178191 case "history-cache":
179192 stateDir, err := config.GetStateDir()
@@ -224,6 +237,12 @@ func confirmPurge(items []string, paths map[string][]string) bool {
224237
225238 for _, item := range items {
226239 fmt.Printf("• %s:\n", toTitle(item))
240+
241+ // Special message for history-cache
242+ if item == "history-cache" {
243+ fmt.Printf(" - All browsing history from database\n")
244+ }
245+
227246 for _, path := range paths[item] {
228247 // Check if path exists
229248 if _, err := os.Stat(path); err == nil {
@@ -252,6 +271,17 @@ func performPurge(items []string, paths map[string][]string) error {
252271 for _, item := range items {
253272 fmt.Printf("Purging %s...\n", item)
254273
274+ // Special handling for history-cache: delete from database first
275+ if item == "history-cache" {
276+ if err := purgeHistoryFromDB(); err != nil {
277+ errors = append(errors, fmt.Sprintf("history table: %v", err))
278+ fmt.Printf(" ✗ Failed to purge history from database: %v\n", err)
279+ } else {
280+ fmt.Printf(" ✓ Purged history from database\n")
281+ }
282+ }
283+
284+ // Then proceed with file/directory deletions
255285 for _, path := range paths[item] {
256286 if err := deletePath(path); err != nil {
257287 errors = append(errors, fmt.Sprintf("%s: %v", path, err))
@@ -270,6 +300,38 @@ func performPurge(items []string, paths map[string][]string) error {
270300 return nil
271301 }
272302
303+// purgeHistoryFromDB deletes all history entries from the database
304+func purgeHistoryFromDB() error {
305+ dbPath, err := config.GetDatabaseFile()
306+ if err != nil {
307+ return fmt.Errorf("failed to get database path: %w", err)
308+ }
309+
310+ // Check if database exists
311+ if _, err := os.Stat(dbPath); os.IsNotExist(err) {
312+ // Database doesn't exist, nothing to purge
313+ return nil
314+ }
315+
316+ // Initialize database connection with proper configuration
317+ database, err := db.InitDB(dbPath)
318+ if err != nil {
319+ return fmt.Errorf("failed to initialize database: %w", err)
320+ }
321+ defer database.Close()
322+
323+ // Create queries instance
324+ queries := db.New(database)
325+
326+ // Execute delete all history
327+ ctx := context.Background()
328+ if err := queries.DeleteAllHistory(ctx); err != nil {
329+ return fmt.Errorf("failed to delete history: %w", err)
330+ }
331+
332+ return nil
333+}
334+
273335 // deletePath deletes a file or directory
274336 func deletePath(path string) error {
275337 // Check if path exists
@@ -2,6 +2,7 @@ package cli
2 2
3 import (3 import (
4 "bufio"4 "bufio"
5+ "context"
5 "fmt"6 "fmt"
6 "os"7 "os"
7 "path/filepath"8 "path/filepath"
@@ -9,6 +10,7 @@ import (
9 "unicode"10 "unicode"
10 11
11 "github.com/bnema/dumber/internal/config"12 "github.com/bnema/dumber/internal/config"
13+ "github.com/bnema/dumber/internal/db"
12 "github.com/spf13/cobra"14 "github.com/spf13/cobra"
13 )15 )
14 16
@@ -45,44 +47,46 @@ func NewPurgeCmd() *cobra.Command {
45 Long: `Purge various dumber data and cache files. By default, purges everything.47 Long: `Purge various dumber data and cache files. By default, purges everything.
46 48
47 Available purge targets:49 Available purge targets:
48- --database, -d Purge the SQLite database (history, zoom levels, cookies,50+ --database, -d Purge SQLite databases (dumber.sqlite and cookies.db)
49- certificate validations, and fuzzy cache metadata)51+ Includes: history, zoom levels, certificates, cookies, favorites
50- --history-cache, -H Purge dmenu fuzzy search cache for history (binary file, will be52+ --history-cache, -H Purge browsing history from database and dmenu fuzzy search cache
51- database-backed in the future)53+ (binary file). Keeps cookies, zoom levels, and other database data.
52- --browser-cache, -c Purge WebKit browser cache (cached images, files, etc.)54+ --browser-cache, -b Purge WebKit browser cache (cached images, files, etc.)
53- --browser-data, -b Purge WebKit browser data (localStorage, sessionStorage, IndexedDB)55+ --browser-data, -B Purge WebKit browser data (localStorage, sessionStorage, IndexedDB)
54- Note: Cookies are now stored in the main database and deleted via --database56+ Note: Cookies are in cookies.db and deleted via --database
55 --filter-cache, -F Purge compiled ad blocking filters cache57 --filter-cache, -F Purge compiled ad blocking filters cache
56- --state, -s Purge all state data (includes database and caches)58+ --state, -s Purge all state data (includes databases and caches)
57- --config Purge configuration files59+ --config, -c Purge configuration files
58 --all, -a Purge everything (default if no specific flags are provided)60 --all, -a Purge everything (default if no specific flags are provided)
59 61
60 Use --force to skip the confirmation prompt.62 Use --force to skip the confirmation prompt.
61 63
62-Database Consolidation:64+Database Storage:
63- Since version with database consolidation, cookies are stored in dumber.sqlite.65+ Cookies are stored in cookies.db (separate from dumber.sqlite due to WebKit requirements).
64- Use --database to delete cookies along with history and other browser data.66+ Use --database to delete both dumber.sqlite and cookies.db.
65- The --browser-data flag now only affects localStorage and other WebKit-managed data.67+ The --browser-data flag only affects localStorage and other WebKit-managed data.
66 68
67 Examples:69 Examples:
68 dumber purge # Purge everything (with confirmation)70 dumber purge # Purge everything (with confirmation)
69 dumber purge --force # Purge everything (no confirmation)71 dumber purge --force # Purge everything (no confirmation)
70- dumber purge -d -H -c -F # Purge database and all caches72+ dumber purge -d -H -b -F # Purge database and all caches
71 dumber purge --filter-cache -f # Force purge filter cache only73 dumber purge --filter-cache -f # Force purge filter cache only
72- dumber purge -d # Purge database (includes cookies)`,74+ dumber purge -d # Purge database (includes cookies)
75+ dumber purge -H # Purge only browsing history (keep cookies)
76+ dumber purge -c # Purge config file only`,
73 RunE: func(_ *cobra.Command, _ []string) error {77 RunE: func(_ *cobra.Command, _ []string) error {
74 return executePurge(flags)78 return executePurge(flags)
75 },79 },
76 }80 }
77 81
78 // Add flags82 // Add flags
79- cmd.Flags().BoolVarP(&flags.Database, "database", "d", false, "Purge the SQLite database (includes cookies)")83+ cmd.Flags().BoolVarP(&flags.Database, "database", "d", false, "Purge SQLite databases (dumber.sqlite and cookies.db)")
80- cmd.Flags().BoolVarP(&flags.HistoryCache, "history-cache", "H", false, "Purge dmenu fuzzy search cache (binary file)")84+ cmd.Flags().BoolVarP(&flags.HistoryCache, "history-cache", "H", false, "Purge browsing history and dmenu fuzzy cache")
81- cmd.Flags().BoolVarP(&flags.BrowserCache, "browser-cache", "c", false, "Purge WebKit browser cache")85+ cmd.Flags().BoolVarP(&flags.BrowserCache, "browser-cache", "b", false, "Purge WebKit browser cache")
82- cmd.Flags().BoolVarP(&flags.BrowserData, "browser-data", "b", false, "Purge WebKit browser data (localStorage, sessionStorage)")86+ cmd.Flags().BoolVarP(&flags.BrowserData, "browser-data", "B", false, "Purge WebKit browser data (localStorage, sessionStorage)")
83 cmd.Flags().BoolVarP(&flags.FilterCache, "filter-cache", "F", false, "Purge compiled ad blocking filters cache")87 cmd.Flags().BoolVarP(&flags.FilterCache, "filter-cache", "F", false, "Purge compiled ad blocking filters cache")
84- cmd.Flags().BoolVarP(&flags.State, "state", "s", false, "Purge all state data (database and caches)")88+ cmd.Flags().BoolVarP(&flags.State, "state", "s", false, "Purge all state data (databases and caches)")
85- cmd.Flags().BoolVar(&flags.Config, "config", false, "Purge configuration files")89+ cmd.Flags().BoolVarP(&flags.Config, "config", "c", false, "Purge configuration files")
86 cmd.Flags().BoolVarP(&flags.All, "all", "a", false, "Purge everything")90 cmd.Flags().BoolVarP(&flags.All, "all", "a", false, "Purge everything")
87 cmd.Flags().BoolVarP(&flags.Force, "force", "f", false, "Skip confirmation prompt")91 cmd.Flags().BoolVarP(&flags.Force, "force", "f", false, "Skip confirmation prompt")
88 92
@@ -169,11 +173,20 @@ func getPurgePaths(items []string) (map[string][]string, error) {
169 for _, item := range items {173 for _, item := range items {
170 switch item {174 switch item {
171 case "database":175 case "database":
176+ // Get main database path
172 dbPath, err := config.GetDatabaseFile()177 dbPath, err := config.GetDatabaseFile()
173 if err != nil {178 if err != nil {
174 return nil, fmt.Errorf("failed to get database path: %w", err)179 return nil, fmt.Errorf("failed to get database path: %w", err)
175 }180 }
176- paths[item] = []string{dbPath}181+
182+ // Get data dir for cookies.db
183+ dataDir, err := config.GetDataDir()
184+ if err != nil {
185+ return nil, fmt.Errorf("failed to get data directory: %w", err)
186+ }
187+ cookiesPath := filepath.Join(dataDir, "cookies.db")
188+
189+ paths[item] = []string{dbPath, cookiesPath}
177 190
178 case "history-cache":191 case "history-cache":
179 stateDir, err := config.GetStateDir()192 stateDir, err := config.GetStateDir()
@@ -224,6 +237,12 @@ func confirmPurge(items []string, paths map[string][]string) bool {
224 237
225 for _, item := range items {238 for _, item := range items {
226 fmt.Printf("• %s:\n", toTitle(item))239 fmt.Printf("• %s:\n", toTitle(item))
240+
241+ // Special message for history-cache
242+ if item == "history-cache" {
243+ fmt.Printf(" - All browsing history from database\n")
244+ }
245+
227 for _, path := range paths[item] {246 for _, path := range paths[item] {
228 // Check if path exists247 // Check if path exists
229 if _, err := os.Stat(path); err == nil {248 if _, err := os.Stat(path); err == nil {
@@ -252,6 +271,17 @@ func performPurge(items []string, paths map[string][]string) error {
252 for _, item := range items {271 for _, item := range items {
253 fmt.Printf("Purging %s...\n", item)272 fmt.Printf("Purging %s...\n", item)
254 273
274+ // Special handling for history-cache: delete from database first
275+ if item == "history-cache" {
276+ if err := purgeHistoryFromDB(); err != nil {
277+ errors = append(errors, fmt.Sprintf("history table: %v", err))
278+ fmt.Printf(" ✗ Failed to purge history from database: %v\n", err)
279+ } else {
280+ fmt.Printf(" ✓ Purged history from database\n")
281+ }
282+ }
283+
284+ // Then proceed with file/directory deletions
255 for _, path := range paths[item] {285 for _, path := range paths[item] {
256 if err := deletePath(path); err != nil {286 if err := deletePath(path); err != nil {
257 errors = append(errors, fmt.Sprintf("%s: %v", path, err))287 errors = append(errors, fmt.Sprintf("%s: %v", path, err))
@@ -270,6 +300,38 @@ func performPurge(items []string, paths map[string][]string) error {
270 return nil300 return nil
271 }301 }
272 302
303+// purgeHistoryFromDB deletes all history entries from the database
304+func purgeHistoryFromDB() error {
305+ dbPath, err := config.GetDatabaseFile()
306+ if err != nil {
307+ return fmt.Errorf("failed to get database path: %w", err)
308+ }
309+
310+ // Check if database exists
311+ if _, err := os.Stat(dbPath); os.IsNotExist(err) {
312+ // Database doesn't exist, nothing to purge
313+ return nil
314+ }
315+
316+ // Initialize database connection with proper configuration
317+ database, err := db.InitDB(dbPath)
318+ if err != nil {
319+ return fmt.Errorf("failed to initialize database: %w", err)
320+ }
321+ defer database.Close()
322+
323+ // Create queries instance
324+ queries := db.New(database)
325+
326+ // Execute delete all history
327+ ctx := context.Background()
328+ if err := queries.DeleteAllHistory(ctx); err != nil {
329+ return fmt.Errorf("failed to delete history: %w", err)
330+ }
331+
332+ return nil
333+}
334+
273 // deletePath deletes a file or directory335 // deletePath deletes a file or directory
274 func deletePath(path string) error {336 func deletePath(path string) error {
275 // Check if path exists337 // Check if path exists

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close