1package cmd
2
3import (
4 "fmt"
5
6 tea "github.com/charmbracelet/bubbletea"
7 "github.com/spf13/cobra"
8
9 "github.com/bnema/dumber/internal/application/usecase"
10 "github.com/bnema/dumber/internal/cli"
11 "github.com/bnema/dumber/internal/cli/model"
12 "github.com/bnema/dumber/internal/cli/styles"
13 "github.com/bnema/dumber/internal/infrastructure/desktop"
14 "github.com/bnema/dumber/internal/infrastructure/filesystem"
15 xdgadapter "github.com/bnema/dumber/internal/infrastructure/xdg"
16)
17
18var purgeForce bool
19
20var purgeCmd = &cobra.Command{
21 Use: "purge",
22 Short: "Remove dumber data and configuration",
23 Long: `Interactively select and remove dumber data directories and files.
24
25This can remove:
26 - Config directory
27 - Data directory (includes database)
28 - State directory
29 - Cache directory
30 - Content filter caches
31 - Desktop integration files
32 - Inactive browser sessions
33
34Use --force to remove everything without prompting.`,
35 RunE: runPurge,
36}
37
38func init() {
39 rootCmd.AddCommand(purgeCmd)
40 purgeCmd.Flags().BoolVarP(&purgeForce, "force", "f", false, "remove all items without prompting")
41}
42
43func runPurge(_ *cobra.Command, _ []string) error {
44 app := GetApp()
45 if app == nil {
46 return fmt.Errorf("app not initialized")
47 }
48
49 fsAdapter := filesystem.New()
50 xdgAdapter := xdgadapter.New()
51 desktopAdapter := desktop.New()
52 purgeUC := usecase.NewPurgeDataUseCase(fsAdapter, xdgAdapter, desktopAdapter)
53
54 if purgeForce {
55 return runPurgeForce(app, purgeUC)
56 }
57
58 // Build session config from app dependencies
59 cfg := model.PurgeModelConfig{
60 ListSessionsUC: app.ListSessionsUC,
61 DeleteSessionUC: app.DeleteSessionUC,
62 }
63
64 m := model.NewPurgeModel(app.Ctx(), app.Theme, purgeUC, cfg)
65 p := tea.NewProgram(m, tea.WithAltScreen())
66 _, err := p.Run()
67 return err
68}
69
70func runPurgeForce(app *cli.App, purgeUC *usecase.PurgeDataUseCase) error {
71 ctx := app.Ctx()
72 theme := app.Theme
73
74 // First, delete all inactive sessions (before we delete the database)
75 if app.ListSessionsUC != nil && app.DeleteSessionUC != nil {
76 output, err := app.ListSessionsUC.GetPurgeableSessions(ctx)
77 if err == nil && output != nil {
78 for _, s := range output.Sessions {
79 sessionID := s.Info.Session.ID
80 if delErr := app.DeleteSessionUC.Execute(ctx, usecase.DeleteSessionInput{
81 SessionID: sessionID,
82 CurrentSessionID: "", // CLI has no current session
83 }); delErr != nil {
84 fmt.Printf("%s Session %s: %v\n", theme.ErrorStyle.Render(styles.IconX), sessionID, delErr)
85 } else {
86 fmt.Printf("%s Session %s\n", theme.SuccessStyle.Render(styles.IconCheck), sessionID)
87 }
88 }
89 }
90 }
91
92 // Then purge all directories
93 out, err := purgeUC.PurgeAll(ctx)
94 if out != nil {
95 for _, r := range out.Results {
96 if r.Success {
97 fmt.Printf("%s %s\n", theme.SuccessStyle.Render(styles.IconCheck), r.Target.Path)
98 } else {
99 fmt.Printf("%s %s: %v\n", theme.ErrorStyle.Render(styles.IconX), r.Target.Path, r.Error)
100 }
101 }
102 }
103 return err
104}