diff --git a/README.md b/README.md index 4b50d7f..37429a4 100644 --- a/README.md +++ b/README.md @@ -169,6 +169,7 @@ make run | Enter | SSH into selected server | | c | Copy SSH command to clipboard | | g | Ping selected server | +| K | Install SSH Key (authorized_keys) | | r | Refresh background data | | a | Add server | | e | Edit server | @@ -246,4 +247,3 @@ If you find Lazyssh useful, please consider giving the repo a **star** ⭐️ an - Built with [tview](https://github.com/rivo/tview) and [tcell](https://github.com/gdamore/tcell). - Inspired by [k9s](https://github.com/derailed/k9s) and [lazydocker](https://github.com/jesseduffield/lazydocker). - diff --git a/internal/adapters/ui/handlers.go b/internal/adapters/ui/handlers.go index 7b1becd..8668fa9 100644 --- a/internal/adapters/ui/handlers.go +++ b/internal/adapters/ui/handlers.go @@ -78,6 +78,9 @@ func (t *tui) handleGlobalKeys(event *tcell.EventKey) *tcell.EventKey { case 'k': t.handleNavigateUp() return nil + case 'K': + t.handleInstallSSHKey() + return nil } if event.Key() == tcell.KeyEnter { @@ -256,6 +259,17 @@ func (t *tui) handlePingSelected() { } } +func (t *tui) handleInstallSSHKey() { + if server, ok := t.serverList.GetSelectedServer(); ok { + alias := server.Alias + t.showStatusTemp(fmt.Sprintf("Installing key to %s…", alias)) + t.app.Suspend(func() { + _ = t.serverService.CopySSHKey(alias) + }) + t.refreshServerList() + } +} + func (t *tui) handleModalClose() { t.returnToMain() } diff --git a/internal/adapters/ui/hint_bar.go b/internal/adapters/ui/hint_bar.go index de94973..a7c445c 100644 --- a/internal/adapters/ui/hint_bar.go +++ b/internal/adapters/ui/hint_bar.go @@ -22,6 +22,6 @@ import ( func NewHintBar() *tview.TextView { hint := tview.NewTextView().SetDynamicColors(true) hint.SetBackgroundColor(tcell.Color233) - hint.SetText("[#BBBBBB]Press [::b]/[-:-:b] to search… • ↑↓ Navigate • Enter SSH • c Copy SSH • g Ping • r Refresh • a Add • e Edit • t Tags • d Delete • p Pin/Unpin • s Sort[-]") + hint.SetText("[#BBBBBB]Press [::b]/[-:-:b] to search… • ↑↓ Navigate • Enter SSH • c Copy SSH • g Ping • K Install Key • r Refresh • a Add • e Edit • t Tags • d Delete • p Pin/Unpin • s Sort[-]") return hint } diff --git a/internal/adapters/ui/server_details.go b/internal/adapters/ui/server_details.go index 8e0a634..6f82a32 100644 --- a/internal/adapters/ui/server_details.go +++ b/internal/adapters/ui/server_details.go @@ -213,7 +213,7 @@ func (sd *ServerDetails) UpdateServer(server domain.Server) { } // Commands list - text += "\n[::b]Commands:[-]\n Enter: SSH connect\n c: Copy SSH command\n g: Ping server\n r: Refresh list\n a: Add new server\n e: Edit entry\n t: Edit tags\n d: Delete entry\n p: Pin/Unpin" + text += "\n[::b]Commands:[-]\n Enter: SSH connect\n c: Copy SSH command\n g: Ping server\n K: Install SSH Key\n r: Refresh list\n a: Add new server\n e: Edit entry\n t: Edit tags\n d: Delete entry\n p: Pin/Unpin" sd.TextView.SetText(text) } diff --git a/internal/core/ports/services.go b/internal/core/ports/services.go index 9394751..e9aa1aa 100644 --- a/internal/core/ports/services.go +++ b/internal/core/ports/services.go @@ -27,5 +27,6 @@ type ServerService interface { DeleteServer(server domain.Server) error SetPinned(alias string, pinned bool) error SSH(alias string) error + CopySSHKey(alias string) error Ping(server domain.Server) (bool, time.Duration, error) } diff --git a/internal/core/services/server_service.go b/internal/core/services/server_service.go index c01b18b..b4542f0 100644 --- a/internal/core/services/server_service.go +++ b/internal/core/services/server_service.go @@ -168,6 +168,29 @@ func (s *serverService) SSH(alias string) error { return nil } +func (s *serverService) CopySSHKey(alias string) error { + s.logger.Infow("ssh-copy-id start", "alias", alias) + + // Ensure ssh-copy-id exists on PATH + if _, err := exec.LookPath("ssh-copy-id"); err != nil { + s.logger.Errorw("ssh-copy-id missing", "error", err) + return fmt.Errorf("ssh-copy-id not found; install OpenSSH (e.g., brew install openssh)") + } + + cmd := exec.Command("ssh-copy-id", alias) + + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + s.logger.Errorw("ssh-copy-id failed", "alias", alias, "error", err) + return err + } + + s.logger.Infow("ssh-copy-id end", "alias", alias) + return nil +} + // Ping checks if the server is reachable on its SSH port. func (s *serverService) Ping(server domain.Server) (bool, time.Duration, error) { start := time.Now()