Changed Project Name to Pulsegate

+added README.MD
This commit is contained in:
2026-05-03 02:54:48 +02:00
parent b886c3e9e8
commit 9c6e2b2ff5
7 changed files with 333 additions and 11 deletions

103
main.go
View File

@@ -6,10 +6,12 @@ import (
"os/exec"
"strconv"
"strings"
"net"
"time"
"pdev-ssh/internal/config"
"pdev-ssh/internal/models"
"pdev-ssh/internal/secret"
"pulsegate/internal/config"
"pulsegate/internal/models"
"pulsegate/internal/secret"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
@@ -46,6 +48,7 @@ type model struct {
commandOutput string
commandTitle string
commandError string
serverStatus map[int]string
}
var (
@@ -104,6 +107,7 @@ func initialModel() model {
servers: cfg.Servers,
selected: 0,
view: ViewServers,
serverStatus: make(map[int]string),
}
m.initAddInputs()
@@ -111,7 +115,7 @@ func initialModel() model {
}
func (m model) Init() tea.Cmd {
return nil
return checkAllServerStatus(m.servers)
}
func (m *model) initAddInputs() {
@@ -202,6 +206,11 @@ type quickCommandResultMsg struct {
Error string
}
type serverStatusMsg struct {
Index int
Status string
}
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if m.err != nil {
return m, nil
@@ -219,6 +228,14 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.view = ViewCommandOutput
return m, nil
case serverStatusMsg:
if m.serverStatus == nil {
m.serverStatus = make(map[int]string)
}
m.serverStatus[msg.Index] = msg.Status
return m, nil
case tea.KeyMsg:
// Command Output offen lassen, bis du ihn wegdrückst
@@ -248,6 +265,9 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.addFocus = (m.addFocus + 1) % len(m.addInputs)
m.addInputs[m.addFocus].Focus()
return m, nil
case "r":
return m, checkAllServerStatus(m.servers)
case "shift+tab", "up":
m.addInputs[m.addFocus].Blur()
@@ -437,7 +457,7 @@ func (m model) View() string {
right,
)
footer := helpStyle.Render("↑/↓ Auswahl Enter Verbinden/Ausführen a Hinzufügen e Editieren d Löschen c Commands Tab Ansicht q Zurück/Beenden")
footer := helpStyle.Render("↑/↓ Auswahl Enter Verbinden a Hinzufügen e Editieren d Löschen c Commands r Status refresh Tab Ansicht q Zurück/Beenden")
content := lipgloss.JoinVertical(
lipgloss.Left,
@@ -450,7 +470,7 @@ func (m model) View() string {
}
func (m model) renderHeader() string {
title := "󰣀 PDEV SSH Manager"
title := "󰣀 PulseGate"
subtitle := fmt.Sprintf(
"Homelab SSH Control Center • Server: %d • Theme: %s • TERM: %s",
len(m.servers),
@@ -509,6 +529,8 @@ func (m model) renderServerList() string {
for i, server := range m.servers {
authIcon := "󰌾"
status := m.serverStatus[i]
statusRendered := statusStyle(status).Render(statusIcon(status))
if server.Auth == "key" {
authIcon = "󰌆"
}
@@ -518,8 +540,17 @@ func (m model) renderServerList() string {
kitty = " 󰄛 kitty"
}
statusText := ""
if status == "online" {
statusText = " ssh"
} else if status == "offline" {
statusText = " off"
}
line := fmt.Sprintf(
"%s %s %s@%s:%d [%s]%s",
"%s%s %s %s %s@%s:%d [%s]%s",
statusRendered,
statusText,
authIcon,
server.Name,
server.User,
@@ -966,6 +997,64 @@ func runQuickCommand(server models.Server, settings models.Settings, quick model
}
}
func checkAllServerStatus(servers []models.Server) tea.Cmd {
cmds := make([]tea.Cmd, 0, len(servers))
for i, server := range servers {
cmds = append(cmds, checkServerStatus(i, server))
}
return tea.Batch(cmds...)
}
func checkServerStatus(index int, server models.Server) tea.Cmd {
return func() tea.Msg {
port := server.Port
if port == 0 {
port = 22
}
addr := fmt.Sprintf("%s:%d", server.Host, port)
conn, err := net.DialTimeout("tcp", addr, 1500*time.Millisecond)
if err != nil {
return serverStatusMsg{
Index: index,
Status: "offline",
}
}
_ = conn.Close()
return serverStatusMsg{
Index: index,
Status: "online",
}
}
}
func statusIcon(status string) string {
switch status {
case "online":
return "●"
case "offline":
return "○"
default:
return "◌"
}
}
func statusStyle(status string) lipgloss.Style {
switch status {
case "online":
return lipgloss.NewStyle().Foreground(green).Bold(true)
case "offline":
return lipgloss.NewStyle().Foreground(gray)
default:
return lipgloss.NewStyle().Foreground(dimText)
}
}
func (m model) renderDeleteConfirmContent() string {
if len(m.servers) == 0 {
return panelStyle.Render("Kein Server zum Löschen vorhanden.")