work/app.go

230 lines
5.3 KiB
Go

package main
import (
"fmt"
"log"
"os"
"os/exec"
"strings"
"time"
"github.com/charmbracelet/huh"
"golang.org/x/crypto/ssh"
)
type App struct {
cfg Config
flags Flags
}
func NewApp() (*App, error) {
cfg, err := loadConfig()
if err != nil {
return nil, fmt.Errorf("error loading config: %w", err)
}
return &App{
cfg: cfg,
}, nil
}
func (a *App) connect() {
tw := NewTimeWarrior()
tw.StartWork()
a.wakeWorkstation()
sshCon, err := a.newSSHConnection()
if err != nil {
log.Fatalf("failed to establish ssh-connection: %v", err)
}
defer sshCon.Close()
sshFowarder := NewPortForwarder(sshCon.client, "2048", "22", a.cfg.WorkstationIP)
go sshFowarder.forward()
rdpFowarder := NewPortForwarder(sshCon.client, "6000", "3389", a.cfg.WorkstationIP)
go rdpFowarder.forward()
a.connectToWorkstation()
}
func (a *App) makeSSHClient() *ssh.ClientConfig {
keypath := os.ExpandEnv("$HOME/.ssh/hegenberg")
keyBytes, err := os.ReadFile(keypath)
if err != nil {
log.Fatalf("Failed to read private key: %s", err)
}
key, err := ssh.ParsePrivateKeyWithPassphrase(keyBytes, []byte(a.cfg.RDPPassword))
if err != nil {
log.Fatalf("Failed to parse private key: %s", err)
}
return &ssh.ClientConfig{
User: a.cfg.SSHUser,
Auth: []ssh.AuthMethod{
ssh.PublicKeys(key),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
Timeout: 5 * time.Second,
}
}
func (a *App) runCommand(name string, args ...string) {
cmd := exec.Command(name, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Stdin = os.Stdin
err := cmd.Run()
if err != nil {
fmt.Println("Error:", err)
}
}
func (a *App) wakeWorkstation() {
sshCommand := fmt.Sprintf("ssh -tt -p %s %s@%s ssh -tt %s@%s \"wakeonlan %s && exit\"",
fmt.Sprintf("%v", a.cfg.SSHPort),
a.cfg.SSHUser,
a.cfg.SSHHost,
a.cfg.JumpUser,
a.cfg.JumpHost,
a.cfg.WorkstationMac)
args := strings.Split(sshCommand, " ")
log.Println(args)
a.runCommand("ssh", args[1:]...)
}
func (a *App) connectToJump() {
sshCommand := fmt.Sprintf("ssh -tt -L 2048:%s:22 %s@%s",
a.cfg.WorkstationHost,
a.cfg.SSHUser,
a.cfg.SSHHost)
args := strings.Split(sshCommand, " ")
a.runCommand("ssh", args[1:]...)
}
func (a *App) connectToWorkstation() {
sshCommand := fmt.Sprintf("ssh -tt -L 6000:%s:3389 -p 2048 %s@127.0.0.1",
a.cfg.WorkstationHost,
a.cfg.SSHUser)
args := strings.Split(sshCommand, " ")
a.runCommand("ssh", args[1:]...)
}
func (a *App) startRDPConnection() {
rdpCommand := fmt.Sprintf("xfreerdp /u:%s /p:%s /v:127.0.0.1:6000 /size:3000x1350",
a.cfg.RDPUser,
a.cfg.RDPPassword)
a.runCommand("bash", "-c", rdpCommand)
}
func (a *App) makeChoice() {
var choice string
tw := NewTimeWarrior()
form := huh.NewForm(
huh.NewGroup(
huh.NewSelect[string]().
Title("What would you like to do?").
Options(
huh.NewOption("Start Work", "start work"),
huh.NewOption("Stop Work", "stop work"),
huh.NewOption("Show Week Summary", "show week summary"),
huh.NewOption("Show Month Summary", "show month summary"),
huh.NewOption("Start Break", "start break"),
huh.NewOption("Stop Break", "stop break"),
huh.NewOption("Export Timetable", "export"),
huh.NewOption("Connect to Jump", "connect to jump"),
huh.NewOption("Connect to Workstation", "connect to workstation"),
huh.NewOption("Start RDP Connection", "start rdp connection"),
).
Value(&choice),
),
)
err := form.Run()
if err != nil {
fmt.Println("Error:", err)
return
}
switch choice {
case "start work":
a.connect()
case "stop work":
tw.StopWork()
case "start break":
tw.StartBreak()
case "stop break":
tw.StopBreak()
case "show week summary":
tw.ShowSummary(":week")
case "show month summary":
tw.ShowSummary(":month")
case "connect to jump":
a.connectToJump()
case "connect to workstation":
a.connectToWorkstation()
case "start rdp connection":
a.startRDPConnection()
case "export":
tw.ExportSummary()
}
}
func (a *App) getSSHAuth() ssh.AuthMethod {
keypath := os.ExpandEnv("$HOME/.ssh/hegenberg")
keyBytes, err := os.ReadFile(keypath)
if err != nil {
fmt.Printf("unable to read private key: %v", err)
return nil
}
key, err := ssh.ParsePrivateKeyWithPassphrase(keyBytes, []byte(a.cfg.RDPPassword))
if err != nil {
fmt.Printf("unable to parse privat key: %v", err)
return nil
}
return ssh.PublicKeys(key)
}
func (a *App) newSSHConnection() (*SSHConnection, error) {
config := &ssh.ClientConfig{
User: a.cfg.SSHUser,
Auth: []ssh.AuthMethod{a.getSSHAuth()},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
Timeout: 5 * time.Second,
}
client, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", a.cfg.SSHHost, a.cfg.SSHPort), config)
if err != nil {
return nil, fmt.Errorf("ssh dial failed: %w", err)
}
session, err := client.NewSession()
if err != nil {
client.Close()
return nil, fmt.Errorf("creating ssh session failed: %w", err)
}
return &SSHConnection{
client: client,
session: session,
}, nil
}
func (a *App) killForwardings() error {
ports := []string{"2048", "6000"}
for _, port := range ports {
cmd := exec.Command("lsof", "-ti", "tcp:"+port)
output, err := cmd.Output()
if err != nil {
continue
}
pids := strings.Split(string(output), "\n")
pid := strings.TrimSpace(pids[0])
killCmd := exec.Command("kill", pid)
killCmd.Run()
}
return nil
}