add:clean all func
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
.env
|
.env
|
||||||
|
dist
|
||||||
|
|||||||
5
internal/pages/clean_all.go
Normal file
5
internal/pages/clean_all.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package pages
|
||||||
|
|
||||||
|
type CleanAllPageMsg struct {
|
||||||
|
ServerName string
|
||||||
|
}
|
||||||
@@ -77,6 +77,14 @@ func (s *StorageService) Send(localPaths []string) error {
|
|||||||
|
|
||||||
// CleanAll removes all files from remote storage.
|
// CleanAll removes all files from remote storage.
|
||||||
func (s *StorageService) CleanAll() error {
|
func (s *StorageService) CleanAll() error {
|
||||||
// TODO: implement
|
cmd := SSHCmd(s.server,
|
||||||
return fmt.Errorf("clean all: not yet implemented")
|
"rm -f "+defaultStoragePath+"/*",
|
||||||
|
)
|
||||||
|
debugLog("CleanAll | args: %v", cmd.Args)
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
debugLog("CleanAll | exit_err: %v | output: %q", err, strings.TrimSpace(string(out)))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("clean all failed: %w\n%s", err, strings.TrimSpace(string(out)))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,6 +86,12 @@ var (
|
|||||||
Foreground(lipgloss.Color("203")).
|
Foreground(lipgloss.Color("203")).
|
||||||
MarginTop(1)
|
MarginTop(1)
|
||||||
|
|
||||||
|
CleanWarningStyle = lipgloss.NewStyle().
|
||||||
|
Foreground(lipgloss.Color("203")).
|
||||||
|
Bold(true).
|
||||||
|
MarginBottom(1).
|
||||||
|
Width(44)
|
||||||
|
|
||||||
// Footer
|
// Footer
|
||||||
FooterStyle = lipgloss.NewStyle().
|
FooterStyle = lipgloss.NewStyle().
|
||||||
Foreground(lipgloss.Color("240")).
|
Foreground(lipgloss.Color("240")).
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package tui
|
|||||||
import (
|
import (
|
||||||
"filepass/internal/pages"
|
"filepass/internal/pages"
|
||||||
"filepass/internal/services"
|
"filepass/internal/services"
|
||||||
|
|
||||||
|
"charm.land/bubbles/v2/textinput"
|
||||||
)
|
)
|
||||||
|
|
||||||
type page int
|
type page int
|
||||||
@@ -15,6 +17,7 @@ const (
|
|||||||
pageServerActions
|
pageServerActions
|
||||||
pageFileAction
|
pageFileAction
|
||||||
pageSend
|
pageSend
|
||||||
|
pageCleanAll
|
||||||
)
|
)
|
||||||
|
|
||||||
type TUIInterface struct {
|
type TUIInterface struct {
|
||||||
@@ -45,6 +48,10 @@ type TUIInterface struct {
|
|||||||
FileOpLoading bool
|
FileOpLoading bool
|
||||||
FileOpErr error
|
FileOpErr error
|
||||||
FileOpSuccess string
|
FileOpSuccess string
|
||||||
|
// clean all confirmation page
|
||||||
|
CleanInput textinput.Model
|
||||||
|
CleanOpLoading bool
|
||||||
|
CleanOpErr error
|
||||||
// send / file picker page
|
// send / file picker page
|
||||||
Picker picker
|
Picker picker
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"filepass/internal/pages"
|
"filepass/internal/pages"
|
||||||
"filepass/internal/services"
|
"filepass/internal/services"
|
||||||
|
|
||||||
|
"charm.land/bubbles/v2/textinput"
|
||||||
tea "charm.land/bubbletea/v2"
|
tea "charm.land/bubbletea/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -60,6 +61,30 @@ func deleteFileCmd(store *services.ServicesStore, serverName, filename string) t
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type cleanAllMsg struct {
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func cleanAllCmd(store *services.ServicesStore, serverName string) tea.Cmd {
|
||||||
|
return func() tea.Msg {
|
||||||
|
storage, err := store.NewStorageService(serverName)
|
||||||
|
if err != nil {
|
||||||
|
return cleanAllMsg{err: err}
|
||||||
|
}
|
||||||
|
return cleanAllMsg{err: storage.CleanAll()}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCleanInput() textinput.Model {
|
||||||
|
ti := textinput.New()
|
||||||
|
ti.Placeholder = "yes"
|
||||||
|
ti.Prompt = ""
|
||||||
|
ti.CharLimit = 3
|
||||||
|
ti.SetWidth(10)
|
||||||
|
ti.Focus()
|
||||||
|
return ti
|
||||||
|
}
|
||||||
|
|
||||||
func checkStorageCmd(store *services.ServicesStore, serverName string) tea.Cmd {
|
func checkStorageCmd(store *services.ServicesStore, serverName string) tea.Cmd {
|
||||||
return func() tea.Msg {
|
return func() tea.Msg {
|
||||||
storage, err := store.NewStorageService(serverName)
|
storage, err := store.NewStorageService(serverName)
|
||||||
@@ -152,6 +177,22 @@ func (m TUIInterface) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
m.FileOpSuccess = ""
|
m.FileOpSuccess = ""
|
||||||
return m, nil
|
return m, nil
|
||||||
|
|
||||||
|
case pages.CleanAllPageMsg:
|
||||||
|
m.Page = pageCleanAll
|
||||||
|
m.CleanInput = newCleanInput()
|
||||||
|
m.CleanOpLoading = false
|
||||||
|
m.CleanOpErr = nil
|
||||||
|
return m, nil
|
||||||
|
|
||||||
|
case cleanAllMsg:
|
||||||
|
m.CleanOpLoading = false
|
||||||
|
if msg.err != nil {
|
||||||
|
m.CleanOpErr = msg.err
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
server := m.ActiveServer
|
||||||
|
return m, func() tea.Msg { return pages.ServerActionsPageMsg{ServerName: server} }
|
||||||
|
|
||||||
case pages.SendPageMsg:
|
case pages.SendPageMsg:
|
||||||
m.Page = pageSend
|
m.Page = pageSend
|
||||||
m.Picker = newPicker(m.LocalDir)
|
m.Picker = newPicker(m.LocalDir)
|
||||||
@@ -222,6 +263,9 @@ func (m TUIInterface) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
if m.Page == pageSend {
|
if m.Page == pageSend {
|
||||||
return m.updateSend(msg)
|
return m.updateSend(msg)
|
||||||
}
|
}
|
||||||
|
if m.Page == pageCleanAll {
|
||||||
|
return m.updateCleanAll(msg)
|
||||||
|
}
|
||||||
|
|
||||||
switch msg.String() {
|
switch msg.String() {
|
||||||
case "up", "k":
|
case "up", "k":
|
||||||
@@ -313,7 +357,7 @@ func (m TUIInterface) updateServerActions(msg tea.KeyPressMsg) (tea.Model, tea.C
|
|||||||
case "send":
|
case "send":
|
||||||
return m, func() tea.Msg { return pages.SendPageMsg{ServerName: server} }
|
return m, func() tea.Msg { return pages.SendPageMsg{ServerName: server} }
|
||||||
case "clean":
|
case "clean":
|
||||||
// TODO: navigate to clean all page
|
return m, func() tea.Msg { return pages.CleanAllPageMsg{ServerName: server} }
|
||||||
}
|
}
|
||||||
|
|
||||||
case "ctrl+c":
|
case "ctrl+c":
|
||||||
@@ -429,6 +473,36 @@ func (m TUIInterface) updateSend(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) {
|
|||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m TUIInterface) updateCleanAll(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) {
|
||||||
|
if m.CleanOpLoading {
|
||||||
|
if msg.String() == "ctrl+c" {
|
||||||
|
m.Quitting = true
|
||||||
|
return m, tea.Quit
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch msg.String() {
|
||||||
|
case "enter":
|
||||||
|
if m.CleanInput.Value() == "yes" {
|
||||||
|
m.CleanOpLoading = true
|
||||||
|
m.CleanOpErr = nil
|
||||||
|
return m, cleanAllCmd(m.Services, m.ActiveServer)
|
||||||
|
}
|
||||||
|
case "ctrl+c":
|
||||||
|
m.Quitting = true
|
||||||
|
return m, tea.Quit
|
||||||
|
case "esc":
|
||||||
|
server := m.ActiveServer
|
||||||
|
return m, func() tea.Msg { return pages.ServerActionsPageMsg{ServerName: server} }
|
||||||
|
}
|
||||||
|
|
||||||
|
// route all other keys to the text input
|
||||||
|
var cmd tea.Cmd
|
||||||
|
m.CleanInput, cmd = m.CleanInput.Update(msg)
|
||||||
|
return m, cmd
|
||||||
|
}
|
||||||
|
|
||||||
func (m TUIInterface) updateSelectServer(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) {
|
func (m TUIInterface) updateSelectServer(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) {
|
||||||
last := len(m.ServerNames) - 1
|
last := len(m.ServerNames) - 1
|
||||||
switch msg.String() {
|
switch msg.String() {
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package tui
|
package tui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"filepass/internal/styles"
|
"filepass/internal/styles"
|
||||||
|
|
||||||
tea "charm.land/bubbletea/v2"
|
tea "charm.land/bubbletea/v2"
|
||||||
@@ -34,6 +36,8 @@ func (m TUIInterface) subtitle() string {
|
|||||||
return m.ActiveFile
|
return m.ActiveFile
|
||||||
case pageSend:
|
case pageSend:
|
||||||
return "Send File"
|
return "Send File"
|
||||||
|
case pageCleanAll:
|
||||||
|
return "Clean All"
|
||||||
default:
|
default:
|
||||||
return "Secure file transfer"
|
return "Secure file transfer"
|
||||||
}
|
}
|
||||||
@@ -65,6 +69,8 @@ func (m TUIInterface) View() tea.View {
|
|||||||
body = m.viewFileAction()
|
body = m.viewFileAction()
|
||||||
case pageSend:
|
case pageSend:
|
||||||
body = m.viewSend()
|
body = m.viewSend()
|
||||||
|
case pageCleanAll:
|
||||||
|
body = m.viewCleanAll()
|
||||||
default:
|
default:
|
||||||
body = m.viewMenu()
|
body = m.viewMenu()
|
||||||
}
|
}
|
||||||
@@ -108,6 +114,10 @@ func (m TUIInterface) View() tea.View {
|
|||||||
footerHint("enter", "confirm") +
|
footerHint("enter", "confirm") +
|
||||||
footerSep() +
|
footerSep() +
|
||||||
footerHint("esc", "back")
|
footerHint("esc", "back")
|
||||||
|
case pageCleanAll:
|
||||||
|
footerStr = footerHint("enter", "confirm") +
|
||||||
|
footerSep() +
|
||||||
|
footerHint("esc", "back")
|
||||||
case pageSend:
|
case pageSend:
|
||||||
footerStr = footerHint("↑↓", "navigate") +
|
footerStr = footerHint("↑↓", "navigate") +
|
||||||
footerSep() +
|
footerSep() +
|
||||||
@@ -252,6 +262,30 @@ func (m TUIInterface) viewSend() string {
|
|||||||
return lipgloss.JoinVertical(lipgloss.Left, crumb, queryLine, list)
|
return lipgloss.JoinVertical(lipgloss.Left, crumb, queryLine, list)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m TUIInterface) viewCleanAll() string {
|
||||||
|
fileCount := len(m.StorageFiles)
|
||||||
|
warning := styles.CleanWarningStyle.Render(
|
||||||
|
fmt.Sprintf("This will permanently delete all %d file(s) from remote storage.", fileCount),
|
||||||
|
)
|
||||||
|
|
||||||
|
promptLabel := styles.FieldLabelStyle(true).Render("Type \"yes\" to confirm")
|
||||||
|
input := m.CleanInput.View()
|
||||||
|
|
||||||
|
var statusLine string
|
||||||
|
switch {
|
||||||
|
case m.CleanOpLoading:
|
||||||
|
statusLine = styles.StatusWarnStyle.Render(" deleting…")
|
||||||
|
case m.CleanOpErr != nil:
|
||||||
|
statusLine = styles.StatusErrStyle.Render("✗ " + m.CleanOpErr.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := []string{warning, promptLabel, input}
|
||||||
|
if statusLine != "" {
|
||||||
|
parts = append(parts, statusLine)
|
||||||
|
}
|
||||||
|
return lipgloss.JoinVertical(lipgloss.Left, parts...)
|
||||||
|
}
|
||||||
|
|
||||||
func (m TUIInterface) viewSelectServer() string {
|
func (m TUIInterface) viewSelectServer() string {
|
||||||
if len(m.ServerNames) == 0 {
|
if len(m.ServerNames) == 0 {
|
||||||
return styles.StatusWarnStyle.Render("⚠ No servers configured.")
|
return styles.StatusWarnStyle.Render("⚠ No servers configured.")
|
||||||
|
|||||||
Reference in New Issue
Block a user