Files
HostKeeper/pkg/tui/messages.go
T
swanadiva 7b06793002 feat: native SSH delegation via tea.ExecProcess + Gruvbox palette
Phase A — Tamagosh-inspired redesign:
- Replace goroutine-based SSH (crypto/ssh) with tea.ExecProcess + native ssh binary
- Zero-lag SSH: native binary pegang TTY langsung, ga ada Go buffering
- Password auth via sshpass -e (SSHPASS env)
- Key auth via ssh -i <tmpfile> + SSH_ASKPASS for passphrase
- ControlMaster sockets for fast reconnect
- Add askpass subcommand for SSH_ASKPASS support
- Gruvbox Material Dark Hard palette for comfortable viewing
- Clean up old session messages (openSessionMsg, sessionOutputMsg, sessionDoneMsg)
- Remove 354-line session.go, replace with 150-line ssh.go
2026-06-23 15:36:40 +07:00

181 lines
4.0 KiB
Go

package tui
import (
"context"
tea "github.com/charmbracelet/bubbletea"
"git.tukangketik.id/swanadiva/hostkeeper/internal/models"
"git.tukangketik.id/swanadiva/hostkeeper/pkg/storage"
)
// quitMsg signals the TUI to exit
type quitMsg struct{}
// sshConnectToMsg signals the TUI to connect to a host via native SSH
type sshConnectToMsg struct {
host *models.Host
}
// sshExitMsg signals that a native SSH session has ended
type sshExitMsg struct {
err error
}
// openHostFormMsg signals the TUI to open a host form tab
type openHostFormMsg struct {
editing *models.Host // nil for add mode
}
// closeFormMsg signals the TUI to close the active form tab
type closeFormMsg struct{}
// saveHostMsg is produced when the form needs to save a host
type saveHostMsg struct {
host *models.Host
dataDir string
}
// saveHostResultMsg is produced after a save attempt
type saveHostResultMsg struct {
host *models.Host
err error
}
// openSFTPMsg signals the TUI to open an SFTP browser tab
type openSFTPMsg struct {
host *models.Host
}
// loadedHostsMsg is produced after reloading hosts from storage
type loadedHostsMsg struct {
hosts []*models.Host
}
// Key management messages
type openKeyListMsg struct{}
type openKeyFormMsg struct {
editing *models.KeyPair
}
type saveKeyMsg struct {
key *models.KeyPair
dataDir string
}
type saveKeyResultMsg struct {
key *models.KeyPair
err error
}
type deleteKeyMsg struct {
id string
dataDir string
}
type deleteKeyResultMsg struct {
err error
}
// Snippet management messages
type openSnippetListMsg struct{}
type openSnippetFormMsg struct {
editing *models.Snippet
}
type saveSnippetMsg struct {
snippet *models.Snippet
dataDir string
}
type saveSnippetResultMsg struct {
snippet *models.Snippet
err error
}
type deleteSnippetMsg struct {
id string
dataDir string
}
type deleteSnippetResultMsg struct {
err error
}
// saveKeyCmd creates a command that saves a key pair to storage
func saveKeyCmd(key *models.KeyPair, dataDir string) tea.Cmd {
return func() tea.Msg {
store, err := storage.NewJSONStorage(dataDir)
if err != nil {
return saveKeyResultMsg{err: err}
}
ctx := context.Background()
if err := store.SaveKeyPair(ctx, key); err != nil {
return saveKeyResultMsg{err: err}
}
return saveKeyResultMsg{key: key}
}
}
// deleteKeyCmd creates a command that deletes a key pair
func deleteKeyCmd(id, dataDir string) tea.Cmd {
return func() tea.Msg {
store, err := storage.NewJSONStorage(dataDir)
if err != nil {
return deleteKeyResultMsg{err: err}
}
ctx := context.Background()
if err := store.DeleteKeyPair(ctx, id); err != nil {
return deleteKeyResultMsg{err: err}
}
return deleteKeyResultMsg{}
}
}
// saveSnippetCmd creates a command that saves a snippet to storage
func saveSnippetCmd(snippet *models.Snippet, dataDir string) tea.Cmd {
return func() tea.Msg {
store, err := storage.NewJSONStorage(dataDir)
if err != nil {
return saveSnippetResultMsg{err: err}
}
ctx := context.Background()
if err := store.SaveSnippet(ctx, snippet); err != nil {
return saveSnippetResultMsg{err: err}
}
return saveSnippetResultMsg{snippet: snippet}
}
}
// deleteSnippetCmd creates a command that deletes a snippet
func deleteSnippetCmd(id, dataDir string) tea.Cmd {
return func() tea.Msg {
store, err := storage.NewJSONStorage(dataDir)
if err != nil {
return deleteSnippetResultMsg{err: err}
}
ctx := context.Background()
if err := store.DeleteSnippet(ctx, id); err != nil {
return deleteSnippetResultMsg{err: err}
}
return deleteSnippetResultMsg{}
}
}
// saveHostCmd creates a command that saves a host to storage
func saveHostCmd(host *models.Host, dataDir string) tea.Cmd {
return func() tea.Msg {
store, err := storage.NewJSONStorage(dataDir)
if err != nil {
return saveHostResultMsg{err: err}
}
ctx := context.Background()
if err := store.SaveHost(ctx, host); err != nil {
return saveHostResultMsg{err: err}
}
return saveHostResultMsg{host: host}
}
}