diff options
Diffstat (limited to 'cmd')
| -rw-r--r-- | cmd/mitmux/main.go | 92 |
1 files changed, 89 insertions, 3 deletions
diff --git a/cmd/mitmux/main.go b/cmd/mitmux/main.go index 83e59cd..dbc919d 100644 --- a/cmd/mitmux/main.go +++ b/cmd/mitmux/main.go @@ -203,6 +203,14 @@ type model struct { decoderInput viTextarea decoderOutput viewport.Model + // confirmPrompt, when non-empty, takes over the status line and + // every keypress until resolved: 'y' runs confirmYes, anything else + // cancels. Used to gate destructive actions (deleting a history + // entry, clearing all of it) behind an explicit second keypress + // rather than a single accidental one. + confirmPrompt string + confirmYes func() tea.Cmd + statusMsg string width int height int @@ -352,6 +360,27 @@ func (m *model) setFlagged(id int64, flagged bool) tea.Cmd { } } +type entryDeletedMsg struct { + id int64 + err error +} + +func (m *model) deleteEntry(id int64) tea.Cmd { + return func() tea.Msg { + return entryDeletedMsg{id: id, err: m.client.DeleteEntry(id)} + } +} + +type historyClearedMsg struct { + err error +} + +func (m *model) clearHistory() tea.Cmd { + return func() tea.Msg { + return historyClearedMsg{err: m.client.ClearHistory()} + } +} + func (m *model) loadList() tea.Msg { var entries []store.Summary var err error @@ -750,6 +779,24 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } return m, nil + case entryDeletedMsg: + if msg.err != nil { + m.statusMsg = "delete error: " + msg.err.Error() + return m, nil + } + m.statusMsg = fmt.Sprintf("deleted #%d", msg.id) + return m, tea.Batch(m.loadList, m.loadStatus) + + case historyClearedMsg: + if msg.err != nil { + m.statusMsg = "clear error: " + msg.err.Error() + return m, nil + } + m.compareBaseID = 0 + m.pendingNew = 0 + m.statusMsg = "history cleared" + return m, tea.Batch(m.loadList, m.loadStatus) + case detailLoadedMsg: if msg.err != nil { m.statusMsg = "get error: " + msg.err.Error() @@ -850,6 +897,17 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case tea.KeyMsg: switch m.mode { case viewList: + if m.confirmPrompt != "" { + yes := msg.String() == "y" || msg.String() == "Y" + action := m.confirmYes + m.confirmPrompt = "" + m.confirmYes = nil + if yes && action != nil { + return m, action() + } + m.statusMsg = "cancelled" + return m, nil + } if m.searching { switch msg.String() { case "enter": @@ -902,6 +960,29 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if row := m.table.Cursor(); row >= 0 && row < len(m.entries) { return m, m.markOrCompare(m.entries[row].ID) } + case "x": + if row := m.table.Cursor(); row >= 0 && row < len(m.entries) { + id := m.entries[row].ID + m.confirmPrompt = fmt.Sprintf("delete #%d? y/n", id) + m.confirmYes = func() tea.Cmd { return m.deleteEntry(id) } + } + return m, nil + case "X": + // Always clears the whole database, never just the + // current filtered view - m.entries under an active + // search query would understate the real count and make + // the prompt itself misleading about what's about to + // happen. + count := "all" + if m.daemonStatus != nil { + count = fmt.Sprintf("all %d", m.daemonStatus.HistoryCount) + } + if m.daemonStatus != nil && m.daemonStatus.HistoryCount == 0 { + return m, nil + } + m.confirmPrompt = fmt.Sprintf("clear %s history entries (not just this view)? y/n", count) + m.confirmYes = func() tea.Cmd { return m.clearHistory() } + return m, nil case "d": m.mode = viewDecoder m.statusMsg = "" @@ -1424,6 +1505,8 @@ func (m *model) helpView() string { "i open in Intruder", "f toggle flag (★ mark this, revisit later)", "c mark for comparison, then press c on another entry to diff", + "x delete the selected entry (asks to confirm)", + "X clear ALL history, not just the current filter (asks to confirm)", "d decoder (URL/Base64/Hex/HTML encode/decode)", "/ search: plain text, host:value, AND/OR/NOT,", " status:404 / status:4xx / status:>=400,", @@ -1506,13 +1589,16 @@ func (m *model) listView() string { } b.WriteString(m.table.View()) b.WriteString("\n") - if m.statusMsg != "" { + if m.confirmPrompt != "" { + b.WriteString(statusStyle.Render(m.confirmPrompt)) + b.WriteString("\n") + } else if m.statusMsg != "" { b.WriteString(statusStyle.Render(sanitizeLine(m.statusMsg))) b.WriteString("\n") } - help := "enter view · r/i/c/d tools · f flag · / search · m rules · ? help · q quit" + help := "enter view · r/i/c/d tools · f flag · x delete · X clear all · / search · m rules · ? help · q quit" if m.query != "" { - help = "enter view · r/i/c/d tools · f flag · / search · esc clear filter · ? help · q quit" + help = "enter view · r/i/c/d tools · f flag · x delete · X clear all · / search · esc clear filter · ? help · q quit" } b.WriteString(helpStyle.Render(help)) return b.String() |