Added Quick commands
+Added Quick command Functionality
This commit is contained in:
@@ -13,3 +13,12 @@ servers:
|
|||||||
key: ""
|
key: ""
|
||||||
password_id: unraid-root
|
password_id: unraid-root
|
||||||
kitty_fix: true
|
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
|
||||||
|
|||||||
@@ -25,4 +25,10 @@ type Server struct {
|
|||||||
type AppConfig struct {
|
type AppConfig struct {
|
||||||
Settings Settings `yaml:"settings"`
|
Settings Settings `yaml:"settings"`
|
||||||
Servers []Server `yaml:"servers"`
|
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"
|
ViewAddServer viewMode = "add_server"
|
||||||
ViewEditServer viewMode = "edit_server"
|
ViewEditServer viewMode = "edit_server"
|
||||||
ViewDeleteConfirm viewMode = "delete_confirm"
|
ViewDeleteConfirm viewMode = "delete_confirm"
|
||||||
|
ViewCommands viewMode = "commands"
|
||||||
|
ViewCommandOutput viewMode = "command_output"
|
||||||
)
|
)
|
||||||
|
|
||||||
type model struct {
|
type model struct {
|
||||||
@@ -40,6 +42,10 @@ type model struct {
|
|||||||
addInputs []textinput.Model
|
addInputs []textinput.Model
|
||||||
addFocus int
|
addFocus int
|
||||||
editIndex int
|
editIndex int
|
||||||
|
commandSelected int
|
||||||
|
commandOutput string
|
||||||
|
commandTitle string
|
||||||
|
commandError string
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -190,6 +196,12 @@ func (m *model) saveNewServer() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type quickCommandResultMsg struct {
|
||||||
|
Title string
|
||||||
|
Output string
|
||||||
|
Error string
|
||||||
|
}
|
||||||
|
|
||||||
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
if m.err != nil {
|
if m.err != nil {
|
||||||
return m, nil
|
return m, nil
|
||||||
@@ -200,8 +212,28 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
m.width = msg.Width
|
m.width = msg.Width
|
||||||
m.height = msg.Height
|
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:
|
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 {
|
if m.view == ViewAddServer || m.view == ViewEditServer {
|
||||||
switch msg.String() {
|
switch msg.String() {
|
||||||
case "ctrl+c":
|
case "ctrl+c":
|
||||||
@@ -252,6 +284,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
return m, cmd
|
return m, cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete Confirm
|
||||||
if m.view == ViewDeleteConfirm {
|
if m.view == ViewDeleteConfirm {
|
||||||
switch msg.String() {
|
switch msg.String() {
|
||||||
case "y", "Y", "j":
|
case "y", "Y", "j":
|
||||||
@@ -267,6 +300,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Normale Navigation
|
||||||
switch msg.String() {
|
switch msg.String() {
|
||||||
case "ctrl+c":
|
case "ctrl+c":
|
||||||
return m, tea.Quit
|
return m, tea.Quit
|
||||||
@@ -301,27 +335,48 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
}
|
}
|
||||||
return m, nil
|
return m, nil
|
||||||
|
|
||||||
|
case "c":
|
||||||
|
if len(m.servers) > 0 {
|
||||||
|
m.view = ViewCommands
|
||||||
|
m.commandSelected = 0
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
|
||||||
case "s":
|
case "s":
|
||||||
m.view = ViewSettings
|
m.view = ViewSettings
|
||||||
|
return m, nil
|
||||||
|
|
||||||
case "h":
|
case "h":
|
||||||
m.view = ViewHelp
|
m.view = ViewHelp
|
||||||
|
return m, nil
|
||||||
|
|
||||||
case "up", "k":
|
case "up", "k":
|
||||||
if m.view == ViewServers && m.selected > 0 {
|
if m.view == ViewServers && m.selected > 0 {
|
||||||
m.selected--
|
m.selected--
|
||||||
}
|
}
|
||||||
|
if m.view == ViewCommands && m.commandSelected > 0 {
|
||||||
|
m.commandSelected--
|
||||||
|
}
|
||||||
|
|
||||||
case "down", "j":
|
case "down", "j":
|
||||||
if m.view == ViewServers && m.selected < len(m.servers)-1 {
|
if m.view == ViewServers && m.selected < len(m.servers)-1 {
|
||||||
m.selected++
|
m.selected++
|
||||||
}
|
}
|
||||||
|
if m.view == ViewCommands && m.commandSelected < len(m.cfg.QuickCommands)-1 {
|
||||||
|
m.commandSelected++
|
||||||
|
}
|
||||||
|
|
||||||
case "enter":
|
case "enter":
|
||||||
if m.view == ViewServers && len(m.servers) > 0 {
|
if m.view == ViewServers && len(m.servers) > 0 {
|
||||||
server := m.servers[m.selected]
|
server := m.servers[m.selected]
|
||||||
return m, connectSSH(server, m.cfg.Settings)
|
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()
|
right = m.renderEditServerContent()
|
||||||
case ViewDeleteConfirm:
|
case ViewDeleteConfirm:
|
||||||
right = m.renderDeleteConfirmContent()
|
right = m.renderDeleteConfirmContent()
|
||||||
|
case ViewCommands:
|
||||||
|
right = m.renderCommandsContent()
|
||||||
|
case ViewCommandOutput:
|
||||||
|
right = m.renderCommandOutputContent()
|
||||||
default:
|
default:
|
||||||
right = m.renderServerList()
|
right = m.renderServerList()
|
||||||
}
|
}
|
||||||
@@ -378,7 +437,7 @@ func (m model) View() string {
|
|||||||
right,
|
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(
|
content := lipgloss.JoinVertical(
|
||||||
lipgloss.Left,
|
lipgloss.Left,
|
||||||
@@ -421,6 +480,7 @@ func (m model) renderNavigation() string {
|
|||||||
{"", "Add Server", ViewAddServer},
|
{"", "Add Server", ViewAddServer},
|
||||||
{"", "Settings", ViewSettings},
|
{"", "Settings", ViewSettings},
|
||||||
{"", "Help", ViewHelp},
|
{"", "Help", ViewHelp},
|
||||||
|
{"", "Commands", ViewCommands},
|
||||||
}
|
}
|
||||||
|
|
||||||
var lines []string
|
var lines []string
|
||||||
@@ -775,6 +835,137 @@ func (m model) renderServerFormContent(badge string, title string) string {
|
|||||||
Render(strings.Join(lines, "\n"))
|
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 {
|
func (m model) renderDeleteConfirmContent() string {
|
||||||
if len(m.servers) == 0 {
|
if len(m.servers) == 0 {
|
||||||
return panelStyle.Render("Kein Server zum Löschen vorhanden.")
|
return panelStyle.Render("Kein Server zum Löschen vorhanden.")
|
||||||
|
|||||||
Reference in New Issue
Block a user