Added Add Host Functionality
+added function to add server from tui +added function to eddit and delete server from tui
This commit is contained in:
369
main.go
369
main.go
@@ -13,15 +13,19 @@ import (
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/charmbracelet/bubbles/textinput"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
type viewMode string
|
||||
|
||||
const (
|
||||
ViewServers viewMode = "servers"
|
||||
ViewSettings viewMode = "settings"
|
||||
ViewHelp viewMode = "help"
|
||||
ViewServers viewMode = "servers"
|
||||
ViewSettings viewMode = "settings"
|
||||
ViewHelp viewMode = "help"
|
||||
ViewAddServer viewMode = "add_server"
|
||||
ViewEditServer viewMode = "edit_server"
|
||||
ViewDeleteConfirm viewMode = "delete_confirm"
|
||||
)
|
||||
|
||||
type model struct {
|
||||
@@ -32,6 +36,10 @@ type model struct {
|
||||
width int
|
||||
height int
|
||||
err error
|
||||
|
||||
addInputs []textinput.Model
|
||||
addFocus int
|
||||
editIndex int
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -85,18 +93,103 @@ func initialModel() model {
|
||||
return model{err: err}
|
||||
}
|
||||
|
||||
return model{
|
||||
m := model{
|
||||
cfg: cfg,
|
||||
servers: cfg.Servers,
|
||||
selected: 0,
|
||||
view: ViewServers,
|
||||
}
|
||||
|
||||
m.initAddInputs()
|
||||
return m
|
||||
}
|
||||
|
||||
func (m model) Init() tea.Cmd {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *model) initAddInputs() {
|
||||
labels := []string{
|
||||
"Name",
|
||||
"Host",
|
||||
"User",
|
||||
"Port",
|
||||
"Group",
|
||||
"Auth (key/password)",
|
||||
"Key Path",
|
||||
"Password ID",
|
||||
}
|
||||
|
||||
m.addInputs = make([]textinput.Model, len(labels))
|
||||
|
||||
for i, label := range labels {
|
||||
input := textinput.New()
|
||||
input.Placeholder = label
|
||||
input.CharLimit = 120
|
||||
input.Width = 40
|
||||
|
||||
switch label {
|
||||
case "Port":
|
||||
input.SetValue("22")
|
||||
case "Auth (key/password)":
|
||||
input.SetValue("key")
|
||||
case "Group":
|
||||
input.SetValue("Homelab")
|
||||
}
|
||||
|
||||
if i == 0 {
|
||||
input.Focus()
|
||||
}
|
||||
|
||||
m.addInputs[i] = input
|
||||
}
|
||||
}
|
||||
|
||||
func (m *model) saveNewServer() error {
|
||||
port, err := strconv.Atoi(m.addInputs[3].Value())
|
||||
if err != nil {
|
||||
port = 22
|
||||
}
|
||||
|
||||
server := models.Server{
|
||||
Name: m.addInputs[0].Value(),
|
||||
Host: m.addInputs[1].Value(),
|
||||
User: m.addInputs[2].Value(),
|
||||
Port: port,
|
||||
Group: m.addInputs[4].Value(),
|
||||
Auth: m.addInputs[5].Value(),
|
||||
Key: m.addInputs[6].Value(),
|
||||
PasswordID: m.addInputs[7].Value(),
|
||||
KittyFix: true,
|
||||
}
|
||||
|
||||
if server.Name == "" || server.Host == "" || server.User == "" {
|
||||
return fmt.Errorf("name, host und user sind pflichtfelder")
|
||||
}
|
||||
|
||||
if server.Auth == "" {
|
||||
server.Auth = "key"
|
||||
}
|
||||
|
||||
if server.Auth == "password" && server.PasswordID == "" {
|
||||
server.PasswordID = strings.ToLower(server.Name) + "-" + server.User
|
||||
}
|
||||
|
||||
m.cfg.Servers = append(m.cfg.Servers, server)
|
||||
m.servers = m.cfg.Servers
|
||||
|
||||
err = config.SaveConfig("config.yaml", m.cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.selected = len(m.servers) - 1
|
||||
m.initAddInputs()
|
||||
m.view = ViewServers
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
if m.err != nil {
|
||||
return m, nil
|
||||
@@ -108,6 +201,72 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
m.height = msg.Height
|
||||
|
||||
case tea.KeyMsg:
|
||||
|
||||
if m.view == ViewAddServer || m.view == ViewEditServer {
|
||||
switch msg.String() {
|
||||
case "ctrl+c":
|
||||
return m, tea.Quit
|
||||
|
||||
case "esc", "q":
|
||||
m.view = ViewServers
|
||||
return m, nil
|
||||
|
||||
case "tab", "down":
|
||||
m.addInputs[m.addFocus].Blur()
|
||||
m.addFocus = (m.addFocus + 1) % len(m.addInputs)
|
||||
m.addInputs[m.addFocus].Focus()
|
||||
return m, nil
|
||||
|
||||
case "shift+tab", "up":
|
||||
m.addInputs[m.addFocus].Blur()
|
||||
m.addFocus--
|
||||
if m.addFocus < 0 {
|
||||
m.addFocus = len(m.addInputs) - 1
|
||||
}
|
||||
m.addInputs[m.addFocus].Focus()
|
||||
return m, nil
|
||||
|
||||
case "enter":
|
||||
if m.addFocus < len(m.addInputs)-1 {
|
||||
m.addInputs[m.addFocus].Blur()
|
||||
m.addFocus++
|
||||
m.addInputs[m.addFocus].Focus()
|
||||
return m, nil
|
||||
}
|
||||
|
||||
var err error
|
||||
if m.view == ViewAddServer {
|
||||
err = m.saveNewServer()
|
||||
} else {
|
||||
err = m.saveEditedServer()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
m.err = err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
var cmd tea.Cmd
|
||||
m.addInputs[m.addFocus], cmd = m.addInputs[m.addFocus].Update(msg)
|
||||
return m, cmd
|
||||
}
|
||||
|
||||
if m.view == ViewDeleteConfirm {
|
||||
switch msg.String() {
|
||||
case "y", "Y", "j":
|
||||
err := m.deleteSelectedServer()
|
||||
if err != nil {
|
||||
m.err = err
|
||||
}
|
||||
return m, nil
|
||||
|
||||
case "n", "N", "esc", "q":
|
||||
m.view = ViewServers
|
||||
return m, nil
|
||||
}
|
||||
}
|
||||
|
||||
switch msg.String() {
|
||||
case "ctrl+c":
|
||||
return m, tea.Quit
|
||||
@@ -122,6 +281,26 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
case "tab":
|
||||
m.view = nextView(m.view)
|
||||
|
||||
case "a":
|
||||
m.view = ViewAddServer
|
||||
m.addFocus = 0
|
||||
m.initAddInputs()
|
||||
return m, nil
|
||||
|
||||
case "e":
|
||||
if m.view == ViewServers && len(m.servers) > 0 {
|
||||
m.editIndex = m.selected
|
||||
m.view = ViewEditServer
|
||||
m.loadServerIntoForm(m.servers[m.selected])
|
||||
}
|
||||
return m, nil
|
||||
|
||||
case "d":
|
||||
if m.view == ViewServers && len(m.servers) > 0 {
|
||||
m.view = ViewDeleteConfirm
|
||||
}
|
||||
return m, nil
|
||||
|
||||
case "s":
|
||||
m.view = ViewSettings
|
||||
|
||||
@@ -182,6 +361,12 @@ func (m model) View() string {
|
||||
right = m.renderSettingsContent()
|
||||
case ViewHelp:
|
||||
right = m.renderHelpContent()
|
||||
case ViewAddServer:
|
||||
right = m.renderAddServerContent()
|
||||
case ViewEditServer:
|
||||
right = m.renderEditServerContent()
|
||||
case ViewDeleteConfirm:
|
||||
right = m.renderDeleteConfirmContent()
|
||||
default:
|
||||
right = m.renderServerList()
|
||||
}
|
||||
@@ -193,7 +378,7 @@ func (m model) View() string {
|
||||
right,
|
||||
)
|
||||
|
||||
footer := helpStyle.Render("↑/↓ Auswahl Enter Verbinden Tab Ansicht wechseln s Settings h Hilfe q Zurück/Beenden")
|
||||
footer := helpStyle.Render("↑/↓ Auswahl Enter Verbinden a Hinzufügen e Editieren d Löschen Tab Ansicht q Zurück/Beenden")
|
||||
|
||||
content := lipgloss.JoinVertical(
|
||||
lipgloss.Left,
|
||||
@@ -233,6 +418,7 @@ func (m model) renderNavigation() string {
|
||||
view viewMode
|
||||
}{
|
||||
{"", "Server", ViewServers},
|
||||
{"", "Add Server", ViewAddServer},
|
||||
{"", "Settings", ViewSettings},
|
||||
{"", "Help", ViewHelp},
|
||||
}
|
||||
@@ -448,6 +634,179 @@ func max(a, b int) int {
|
||||
return b
|
||||
}
|
||||
|
||||
|
||||
func (m *model) loadServerIntoForm(server models.Server) {
|
||||
m.initAddInputs()
|
||||
|
||||
m.addInputs[0].SetValue(server.Name)
|
||||
m.addInputs[1].SetValue(server.Host)
|
||||
m.addInputs[2].SetValue(server.User)
|
||||
m.addInputs[3].SetValue(strconv.Itoa(server.Port))
|
||||
m.addInputs[4].SetValue(server.Group)
|
||||
m.addInputs[5].SetValue(server.Auth)
|
||||
m.addInputs[6].SetValue(server.Key)
|
||||
m.addInputs[7].SetValue(server.PasswordID)
|
||||
|
||||
m.addFocus = 0
|
||||
m.addInputs[0].Focus()
|
||||
}
|
||||
|
||||
func (m *model) saveEditedServer() error {
|
||||
if m.editIndex < 0 || m.editIndex >= len(m.servers) {
|
||||
return fmt.Errorf("ungültiger server index")
|
||||
}
|
||||
|
||||
port, err := strconv.Atoi(m.addInputs[3].Value())
|
||||
if err != nil {
|
||||
port = 22
|
||||
}
|
||||
|
||||
server := models.Server{
|
||||
Name: m.addInputs[0].Value(),
|
||||
Host: m.addInputs[1].Value(),
|
||||
User: m.addInputs[2].Value(),
|
||||
Port: port,
|
||||
Group: m.addInputs[4].Value(),
|
||||
Auth: m.addInputs[5].Value(),
|
||||
Key: m.addInputs[6].Value(),
|
||||
PasswordID: m.addInputs[7].Value(),
|
||||
KittyFix: true,
|
||||
}
|
||||
|
||||
if server.Name == "" || server.Host == "" || server.User == "" {
|
||||
return fmt.Errorf("name, host und user sind pflichtfelder")
|
||||
}
|
||||
|
||||
if server.Auth == "" {
|
||||
server.Auth = "key"
|
||||
}
|
||||
|
||||
if server.Auth == "password" && server.PasswordID == "" {
|
||||
server.PasswordID = strings.ToLower(server.Name) + "-" + server.User
|
||||
}
|
||||
|
||||
m.cfg.Servers[m.editIndex] = server
|
||||
m.servers = m.cfg.Servers
|
||||
|
||||
err = config.SaveConfig("config.yaml", m.cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.selected = m.editIndex
|
||||
m.view = ViewServers
|
||||
m.initAddInputs()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *model) deleteSelectedServer() error {
|
||||
if m.selected < 0 || m.selected >= len(m.servers) {
|
||||
return fmt.Errorf("kein gültiger server ausgewählt")
|
||||
}
|
||||
|
||||
m.cfg.Servers = append(
|
||||
m.cfg.Servers[:m.selected],
|
||||
m.cfg.Servers[m.selected+1:]...,
|
||||
)
|
||||
|
||||
m.servers = m.cfg.Servers
|
||||
|
||||
if m.selected >= len(m.servers) && m.selected > 0 {
|
||||
m.selected--
|
||||
}
|
||||
|
||||
err := config.SaveConfig("config.yaml", m.cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.view = ViewServers
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
func (m model) renderEditServerContent() string {
|
||||
return m.renderServerFormContent(" EDIT SERVER ", "Server bearbeiten")
|
||||
}
|
||||
|
||||
func (m model) renderAddServerContent() string {
|
||||
return m.renderServerFormContent(" ADD SERVER ", "Neuen SSH-Server hinzufügen")
|
||||
}
|
||||
|
||||
func (m model) renderServerFormContent(badge string, title string) string {
|
||||
lines := []string{
|
||||
badgeStyle.Render(badge),
|
||||
"",
|
||||
title,
|
||||
"",
|
||||
}
|
||||
|
||||
labels := []string{
|
||||
"Name",
|
||||
"Host",
|
||||
"User",
|
||||
"Port",
|
||||
"Group",
|
||||
"Auth",
|
||||
"Key Path",
|
||||
"Password ID",
|
||||
}
|
||||
|
||||
for i, input := range m.addInputs {
|
||||
label := labels[i]
|
||||
|
||||
if i == m.addFocus {
|
||||
lines = append(lines, selectedStyle.Render("> "+label))
|
||||
} else {
|
||||
lines = append(lines, normalStyle.Render(" "+label))
|
||||
}
|
||||
|
||||
lines = append(lines, " "+input.View())
|
||||
lines = append(lines, "")
|
||||
}
|
||||
|
||||
lines = append(lines, mutedStyle.Render("Enter: nächstes Feld / speichern Tab: weiter Esc/q: abbrechen"))
|
||||
lines = append(lines, mutedStyle.Render("Auth: key oder password"))
|
||||
|
||||
return panelStyle.
|
||||
Width(max(m.width-36, 60)).
|
||||
Height(max(m.height-9, 18)).
|
||||
Render(strings.Join(lines, "\n"))
|
||||
}
|
||||
|
||||
func (m model) renderDeleteConfirmContent() string {
|
||||
if len(m.servers) == 0 {
|
||||
return panelStyle.Render("Kein Server zum Löschen vorhanden.")
|
||||
}
|
||||
|
||||
server := m.servers[m.selected]
|
||||
|
||||
lines := []string{
|
||||
badgeStyle.Render(" DELETE SERVER "),
|
||||
"",
|
||||
warnStyle().Render("Diesen Server wirklich löschen?"),
|
||||
"",
|
||||
fmt.Sprintf("Name: %s", server.Name),
|
||||
fmt.Sprintf("Host: %s@%s:%d", server.User, server.Host, server.Port),
|
||||
fmt.Sprintf("Group: %s", server.Group),
|
||||
"",
|
||||
selectedStyle.Render("y / j = löschen"),
|
||||
mutedStyle.Render("n / q / Esc = abbrechen"),
|
||||
}
|
||||
|
||||
return panelStyle.
|
||||
Width(max(m.width-36, 60)).
|
||||
Height(max(m.height-9, 14)).
|
||||
Render(strings.Join(lines, "\n"))
|
||||
}
|
||||
|
||||
func warnStyle() lipgloss.Style {
|
||||
return lipgloss.NewStyle().
|
||||
Foreground(warn).
|
||||
Bold(true)
|
||||
}
|
||||
|
||||
func main() {
|
||||
p := tea.NewProgram(
|
||||
initialModel(),
|
||||
|
||||
Reference in New Issue
Block a user