Added Quick commands
+Added Quick command Functionality
This commit is contained in:
@@ -13,3 +13,12 @@ servers:
|
||||
key: ""
|
||||
password_id: unraid-root
|
||||
kitty_fix: true
|
||||
quick_commands:
|
||||
- name: Docker PS
|
||||
command: docker ps
|
||||
- name: Disk Usage
|
||||
command: df -h
|
||||
- name: RAM Usage
|
||||
command: free -h
|
||||
- name: Uptime
|
||||
command: uptime
|
||||
|
||||
@@ -23,6 +23,12 @@ type Server struct {
|
||||
}
|
||||
|
||||
type AppConfig struct {
|
||||
Settings Settings `yaml:"settings"`
|
||||
Servers []Server `yaml:"servers"`
|
||||
Settings Settings `yaml:"settings"`
|
||||
Servers []Server `yaml:"servers"`
|
||||
QuickCommands []QuickCommand `yaml:"quick_commands"`
|
||||
}
|
||||
|
||||
type QuickCommand struct {
|
||||
Name string `yaml:"name"`
|
||||
Command string `yaml:"command"`
|
||||
}
|
||||
193
main.go
193
main.go
@@ -26,6 +26,8 @@ const (
|
||||
ViewAddServer viewMode = "add_server"
|
||||
ViewEditServer viewMode = "edit_server"
|
||||
ViewDeleteConfirm viewMode = "delete_confirm"
|
||||
ViewCommands viewMode = "commands"
|
||||
ViewCommandOutput viewMode = "command_output"
|
||||
)
|
||||
|
||||
type model struct {
|
||||
@@ -40,6 +42,10 @@ type model struct {
|
||||
addInputs []textinput.Model
|
||||
addFocus int
|
||||
editIndex int
|
||||
commandSelected int
|
||||
commandOutput string
|
||||
commandTitle string
|
||||
commandError string
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -190,6 +196,12 @@ func (m *model) saveNewServer() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type quickCommandResultMsg struct {
|
||||
Title string
|
||||
Output string
|
||||
Error string
|
||||
}
|
||||
|
||||
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
if m.err != nil {
|
||||
return m, nil
|
||||
@@ -200,8 +212,28 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
m.width = msg.Width
|
||||
m.height = msg.Height
|
||||
|
||||
case quickCommandResultMsg:
|
||||
m.commandTitle = msg.Title
|
||||
m.commandOutput = msg.Output
|
||||
m.commandError = msg.Error
|
||||
m.view = ViewCommandOutput
|
||||
return m, nil
|
||||
|
||||
case tea.KeyMsg:
|
||||
|
||||
// Command Output offen lassen, bis du ihn wegdrückst
|
||||
if m.view == ViewCommandOutput {
|
||||
switch msg.String() {
|
||||
case "q", "esc", "enter":
|
||||
m.view = ViewCommands
|
||||
return m, nil
|
||||
|
||||
case "ctrl+c":
|
||||
return m, tea.Quit
|
||||
}
|
||||
}
|
||||
|
||||
// Add/Edit Formular
|
||||
if m.view == ViewAddServer || m.view == ViewEditServer {
|
||||
switch msg.String() {
|
||||
case "ctrl+c":
|
||||
@@ -252,6 +284,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
return m, cmd
|
||||
}
|
||||
|
||||
// Delete Confirm
|
||||
if m.view == ViewDeleteConfirm {
|
||||
switch msg.String() {
|
||||
case "y", "Y", "j":
|
||||
@@ -267,6 +300,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
}
|
||||
}
|
||||
|
||||
// Normale Navigation
|
||||
switch msg.String() {
|
||||
case "ctrl+c":
|
||||
return m, tea.Quit
|
||||
@@ -301,27 +335,48 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
}
|
||||
return m, nil
|
||||
|
||||
case "c":
|
||||
if len(m.servers) > 0 {
|
||||
m.view = ViewCommands
|
||||
m.commandSelected = 0
|
||||
}
|
||||
return m, nil
|
||||
|
||||
case "s":
|
||||
m.view = ViewSettings
|
||||
return m, nil
|
||||
|
||||
case "h":
|
||||
m.view = ViewHelp
|
||||
return m, nil
|
||||
|
||||
case "up", "k":
|
||||
if m.view == ViewServers && m.selected > 0 {
|
||||
m.selected--
|
||||
}
|
||||
if m.view == ViewCommands && m.commandSelected > 0 {
|
||||
m.commandSelected--
|
||||
}
|
||||
|
||||
case "down", "j":
|
||||
if m.view == ViewServers && m.selected < len(m.servers)-1 {
|
||||
m.selected++
|
||||
}
|
||||
if m.view == ViewCommands && m.commandSelected < len(m.cfg.QuickCommands)-1 {
|
||||
m.commandSelected++
|
||||
}
|
||||
|
||||
case "enter":
|
||||
if m.view == ViewServers && len(m.servers) > 0 {
|
||||
server := m.servers[m.selected]
|
||||
return m, connectSSH(server, m.cfg.Settings)
|
||||
}
|
||||
|
||||
if m.view == ViewCommands && len(m.servers) > 0 && len(m.cfg.QuickCommands) > 0 {
|
||||
server := m.servers[m.selected]
|
||||
quick := m.cfg.QuickCommands[m.commandSelected]
|
||||
return m, runQuickCommand(server, m.cfg.Settings, quick)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -367,6 +422,10 @@ func (m model) View() string {
|
||||
right = m.renderEditServerContent()
|
||||
case ViewDeleteConfirm:
|
||||
right = m.renderDeleteConfirmContent()
|
||||
case ViewCommands:
|
||||
right = m.renderCommandsContent()
|
||||
case ViewCommandOutput:
|
||||
right = m.renderCommandOutputContent()
|
||||
default:
|
||||
right = m.renderServerList()
|
||||
}
|
||||
@@ -378,7 +437,7 @@ func (m model) View() string {
|
||||
right,
|
||||
)
|
||||
|
||||
footer := helpStyle.Render("↑/↓ Auswahl Enter Verbinden a Hinzufügen e Editieren d Löschen Tab Ansicht q Zurück/Beenden")
|
||||
footer := helpStyle.Render("↑/↓ Auswahl Enter Verbinden/Ausführen a Hinzufügen e Editieren d Löschen c Commands Tab Ansicht q Zurück/Beenden")
|
||||
|
||||
content := lipgloss.JoinVertical(
|
||||
lipgloss.Left,
|
||||
@@ -421,6 +480,7 @@ func (m model) renderNavigation() string {
|
||||
{"", "Add Server", ViewAddServer},
|
||||
{"", "Settings", ViewSettings},
|
||||
{"", "Help", ViewHelp},
|
||||
{"", "Commands", ViewCommands},
|
||||
}
|
||||
|
||||
var lines []string
|
||||
@@ -775,6 +835,137 @@ func (m model) renderServerFormContent(badge string, title string) string {
|
||||
Render(strings.Join(lines, "\n"))
|
||||
}
|
||||
|
||||
func (m model) renderCommandOutputContent() string {
|
||||
lines := []string{
|
||||
badgeStyle.Render(" COMMAND OUTPUT "),
|
||||
"",
|
||||
selectedStyle.Render(m.commandTitle),
|
||||
"",
|
||||
}
|
||||
|
||||
if m.commandError != "" {
|
||||
lines = append(lines, warnStyle().Render("Fehler: "+m.commandError))
|
||||
lines = append(lines, "")
|
||||
}
|
||||
|
||||
output := strings.TrimSpace(m.commandOutput)
|
||||
if output == "" {
|
||||
output = "Keine Ausgabe."
|
||||
}
|
||||
|
||||
lines = append(lines, output)
|
||||
lines = append(lines, "")
|
||||
lines = append(lines, mutedStyle.Render("q / Esc / Enter: zurück zu Quick Commands"))
|
||||
|
||||
return panelStyle.
|
||||
Width(max(m.width-36, 60)).
|
||||
Height(max(m.height-9, 14)).
|
||||
Render(strings.Join(lines, "\n"))
|
||||
}
|
||||
|
||||
func (m model) renderCommandsContent() string {
|
||||
lines := []string{
|
||||
badgeStyle.Render(" QUICK COMMANDS "),
|
||||
"",
|
||||
}
|
||||
|
||||
if len(m.servers) == 0 {
|
||||
lines = append(lines, mutedStyle.Render("Kein Server ausgewählt."))
|
||||
} else {
|
||||
server := m.servers[m.selected]
|
||||
lines = append(lines, fmt.Sprintf("Server: %s %s@%s:%d", server.Name, server.User, server.Host, server.Port))
|
||||
lines = append(lines, "")
|
||||
}
|
||||
|
||||
if len(m.cfg.QuickCommands) == 0 {
|
||||
lines = append(lines, mutedStyle.Render("Keine quick_commands in config.yaml gefunden."))
|
||||
} else {
|
||||
for i, cmd := range m.cfg.QuickCommands {
|
||||
line := fmt.Sprintf("%s → %s", cmd.Name, cmd.Command)
|
||||
|
||||
if i == m.commandSelected {
|
||||
lines = append(lines, selectedStyle.Render("> "+line))
|
||||
} else {
|
||||
lines = append(lines, normalStyle.Render(" "+line))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lines = append(lines, "")
|
||||
lines = append(lines, mutedStyle.Render("Enter: Command ausführen ↑/↓ Auswahl q: zurück"))
|
||||
|
||||
return panelStyle.
|
||||
Width(max(m.width-36, 60)).
|
||||
Height(max(m.height-9, 14)).
|
||||
Render(strings.Join(lines, "\n"))
|
||||
}
|
||||
|
||||
func runQuickCommand(server models.Server, settings models.Settings, quick models.QuickCommand) tea.Cmd {
|
||||
return func() tea.Msg {
|
||||
title := fmt.Sprintf("%s auf %s", quick.Name, server.Name)
|
||||
|
||||
args := []string{
|
||||
"-p", strconv.Itoa(server.Port),
|
||||
}
|
||||
|
||||
if settings.Terminal.EnableKittyFix && server.KittyFix {
|
||||
args = append(args, "-t")
|
||||
}
|
||||
|
||||
var cmd *exec.Cmd
|
||||
|
||||
if server.Auth == "key" {
|
||||
if server.Key != "" {
|
||||
args = append(args, "-i", expandHome(server.Key))
|
||||
}
|
||||
|
||||
args = append(args, server.User+"@"+server.Host, quick.Command)
|
||||
|
||||
cmd = exec.Command("ssh", args...)
|
||||
cmd.Env = buildSSHEnv(server, settings)
|
||||
} else if server.Auth == "password" {
|
||||
password, err := secret.GetPassword(server.PasswordID)
|
||||
if err != nil {
|
||||
return quickCommandResultMsg{
|
||||
Title: title,
|
||||
Output: "",
|
||||
Error: fmt.Sprintf("kein passwort gespeichert für %s", server.PasswordID),
|
||||
}
|
||||
}
|
||||
|
||||
sshArgs := []string{
|
||||
"-e",
|
||||
"ssh",
|
||||
"-p", strconv.Itoa(server.Port),
|
||||
server.User + "@" + server.Host,
|
||||
quick.Command,
|
||||
}
|
||||
|
||||
cmd = exec.Command("sshpass", sshArgs...)
|
||||
cmd.Env = append(buildSSHEnv(server, settings), "SSHPASS="+password)
|
||||
} else {
|
||||
return quickCommandResultMsg{
|
||||
Title: title,
|
||||
Output: "",
|
||||
Error: fmt.Sprintf("unbekannte auth methode: %s", server.Auth),
|
||||
}
|
||||
}
|
||||
|
||||
output, err := cmd.CombinedOutput()
|
||||
|
||||
errorText := ""
|
||||
if err != nil {
|
||||
errorText = err.Error()
|
||||
}
|
||||
|
||||
return quickCommandResultMsg{
|
||||
Title: title,
|
||||
Output: string(output),
|
||||
Error: errorText,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m model) renderDeleteConfirmContent() string {
|
||||
if len(m.servers) == 0 {
|
||||
return panelStyle.Render("Kein Server zum Löschen vorhanden.")
|
||||
|
||||
Reference in New Issue
Block a user