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 Unverified
2B82207D370127A0The key is unknown
1 file · +84 -22
M internal/cli/purge.go +84 -22
| @@ -2,6 +2,7 @@ package cli | |||
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "bufio" | |
| 5 | + | "context" | |
| 5 | 6 | "fmt" | |
| 6 | 7 | "os" | |
| 7 | 8 | "path/filepath" | |
| @@ -9,6 +10,7 @@ import ( | |||
| 9 | 10 | "unicode" | |
| 10 | 11 | ||
| 11 | 12 | "github.com/bnema/dumber/internal/config" | |
| 13 | + | "github.com/bnema/dumber/internal/db" | |
| 12 | 14 | "github.com/spf13/cobra" | |
| 13 | 15 | ) | |
| 14 | 16 | ||
| @@ -45,44 +47,46 @@ func NewPurgeCmd() *cobra.Command { | |||
| 45 | 47 | Long: `Purge various dumber data and cache files. By default, purges everything. | |
| 46 | 48 | ||
| 47 | 49 | 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 | |
| 55 | 57 | --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 | |
| 58 | 60 | --all, -a Purge everything (default if no specific flags are provided) | |
| 59 | 61 | ||
| 60 | 62 | Use --force to skip the confirmation prompt. | |
| 61 | 63 | ||
| 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. | |
| 66 | 68 | ||
| 67 | 69 | Examples: | |
| 68 | 70 | dumber purge # Purge everything (with confirmation) | |
| 69 | 71 | 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 | |
| 71 | 73 | 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 | 77 | RunE: func(_ *cobra.Command, _ []string) error { | |
| 74 | 78 | return executePurge(flags) | |
| 75 | 79 | }, | |
| 76 | 80 | } | |
| 77 | 81 | ||
| 78 | 82 | // 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)") | |
| 83 | 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)") | |
| 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") | |
| 86 | 90 | cmd.Flags().BoolVarP(&flags.All, "all", "a", false, "Purge everything") | |
| 87 | 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 | 173 | for _, item := range items { | |
| 170 | 174 | switch item { | |
| 171 | 175 | case "database": | |
| 176 | + | // Get main database path | |
| 172 | 177 | dbPath, err := config.GetDatabaseFile() | |
| 173 | 178 | if err != nil { | |
| 174 | 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 | 191 | case "history-cache": | |
| 179 | 192 | stateDir, err := config.GetStateDir() | |
| @@ -224,6 +237,12 @@ func confirmPurge(items []string, paths map[string][]string) bool { | |||
| 224 | 237 | ||
| 225 | 238 | for _, item := range items { | |
| 226 | 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 | 246 | for _, path := range paths[item] { | |
| 228 | 247 | // Check if path exists | |
| 229 | 248 | if _, err := os.Stat(path); err == nil { | |
| @@ -252,6 +271,17 @@ func performPurge(items []string, paths map[string][]string) error { | |||
| 252 | 271 | for _, item := range items { | |
| 253 | 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 | 285 | for _, path := range paths[item] { | |
| 256 | 286 | if err := deletePath(path); err != nil { | |
| 257 | 287 | errors = append(errors, fmt.Sprintf("%s: %v", path, err)) | |
| @@ -270,6 +300,38 @@ func performPurge(items []string, paths map[string][]string) error { | |||
| 270 | 300 | 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 | 335 | // deletePath deletes a file or directory | |
| 274 | 336 | func deletePath(path string) error { | |
| 275 | 337 | // 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 be | 52 | + | --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 --database | 56 | + | Note: Cookies are in cookies.db and deleted via --database |
| 55 | --filter-cache, -F Purge compiled ad blocking filters cache | 57 | --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 files | 59 | + | --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 caches | 72 | + | dumber purge -d -H -b -F # Purge database and all caches |
| 71 | dumber purge --filter-cache -f # Force purge filter cache only | 73 | 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 flags | 82 | // 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 exists | 247 | // 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 nil | 300 | 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 directory | 335 | // deletePath deletes a file or directory | ||
| 274 | func deletePath(path string) error { | 336 | func deletePath(path string) error { | ||
| 275 | // Check if path exists | 337 | // Check if path exists | ||