package main import ( "fmt" "io" "log" "net" "os" "os/exec" "path/filepath" "strings" "time" "github.com/charmbracelet/huh" "github.com/spf13/cobra" "github.com/spf13/viper" "golang.org/x/crypto/ssh" ) type Config struct { SSHUser string `mapstructure:"SSH_USER"` SSHHost string `mapstructure:"SSH_HOST"` JumpUser string `mapstructure:"JUMP_USER"` JumpHost string `mapstructure:"JUMP_HOST"` WorkstationHost string `mapstructure:"WORKSTATION_HOST"` WorkstationUser string `mapstructure:"WORKSTATION_USER"` WorkstationMac string `mapstructure:"WORKSTATION_MAC"` RDPUser string `mapstructure:"RDP_USER"` RDPPassword string `mapstructure:"RDP_PASSWORD"` WorkstationIP string `mapstructure:"WORKSTATION_IP"` SSHPort int `mapstructure:"SSH_PORT"` } type Flags struct { ShowWeek bool ShowMonth bool ShowExport bool } var ( cfg Config flags Flags ) func init() { showCmd.Flags().BoolVarP(&flags.ShowWeek, "week", "w", false, "show timewarrior week summary") showCmd.Flags().BoolVarP(&flags.ShowMonth, "month", "m", false, "show timewarrior month summary") showCmd.Flags().BoolVarP(&flags.ShowExport, "export", "e", false, "export timewarrior timetable") rootCmd.AddCommand(startCmd) rootCmd.AddCommand(stopCmd) rootCmd.AddCommand(showCmd) log.Println("Loading Config File") configPath, err := os.UserConfigDir() if err != nil { return } workConfigPath := filepath.Join(configPath, "work") configFile := filepath.Join(workConfigPath, "config.toml") viper.SetConfigFile(configFile) viper.SetConfigType("toml") viper.AddConfigPath(".") viper.AutomaticEnv() err = viper.ReadInConfig() if err != nil { log.Printf("error loading config file: %v", err) } // allSettings := viper.AllSettings() // log.Printf("All loaded settings: %+v", allSettings) err = viper.UnmarshalKey("default", &cfg) if err != nil { log.Printf("unable to decode into struct: %v", err) } } func connect() { runCommand("timew", "start", "work") wakeWorkstation() sshClient, err := ssh.Dial("tcp", cfg.SSHHost+":"+fmt.Sprintf("%v", cfg.SSHPort), makeSSHClient()) if err != nil { log.Fatal("Failed to dial: ", err) } defer sshClient.Close() session, err := sshClient.NewSession() if err != nil { log.Fatal("Failed to create session: ", err) } defer session.Close() go forwardPort(sshClient, "2048", cfg.WorkstationIP, "22") go forwardPort(sshClient, "6000", cfg.WorkstationIP, "3389") connectToWorkstation() } func forwardPort(sshConn *ssh.Client, localPort, remoteHost, remotePort string) { listener, err := net.Listen("tcp", "127.0.0.1:"+localPort) if err != nil { log.Printf("Fehler beim Öffnen des lokalen Ports %s: %v", localPort, err) return } defer listener.Close() for { localConn, err := listener.Accept() if err != nil { log.Printf("Fehler beim Akzeptieren der Verbindung: %v", err) continue } remoteConn, err := sshConn.Dial("tcp", remoteHost+":"+remotePort) if err != nil { log.Printf("Fehler beim Verbinden zum Remote-Host %s:%s: %v", remoteHost, remotePort, err) localConn.Close() continue } go copyConn(localConn, remoteConn) go copyConn(remoteConn, localConn) } } func copyConn(dst, src net.Conn) { defer dst.Close() defer src.Close() io.Copy(dst, src) } func 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(cfg.RDPPassword)) if err != nil { log.Fatalf("Failed to parse private key: %s", err) } return &ssh.ClientConfig{ User: cfg.SSHUser, Auth: []ssh.AuthMethod{ ssh.PublicKeys(key), }, HostKeyCallback: ssh.InsecureIgnoreHostKey(), Timeout: 5 * time.Second, } } func 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 startSequence() { runCommand("timew", "start", "work") wakeWorkstation() connectToJump() } func wakeWorkstation() { sshCommand := fmt.Sprintf("ssh -tt -p %s %s@%s ssh -tt %s@%s \"wakeonlan %s && exit\"", fmt.Sprintf("%v", cfg.SSHPort), cfg.SSHUser, cfg.SSHHost, cfg.JumpUser, cfg.JumpHost, cfg.WorkstationMac) args := strings.Split(sshCommand, " ") log.Println(args) runCommand("ssh", args[1:]...) } func connectToJump() { sshCommand := fmt.Sprintf("ssh -tt -L 2048:%s:22 %s@%s", cfg.WorkstationHost, cfg.SSHUser, cfg.SSHHost) args := strings.Split(sshCommand, " ") runCommand("ssh", args[1:]...) } func connectToWorkstation() { sshCommand := fmt.Sprintf("ssh -tt -L 6000:%s:3389 -p 2048 %s@127.0.0.1", cfg.WorkstationHost, cfg.SSHUser) args := strings.Split(sshCommand, " ") runCommand("ssh", args[1:]...) } func startRDPConnection() { rdpCommand := fmt.Sprintf("xfreerdp /u:%s /p:%s /v:127.0.0.1:6000 /size:3000x1350", cfg.RDPUser, cfg.RDPPassword) runCommand("bash", "-c", rdpCommand) } func makeChoice() { var choice string form := huh.NewForm( huh.NewGroup( huh.NewSelect[string](). Title("What would you like to do?"). Options( huh.NewOption("Start", "Start"), 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("Wake Lou", "wake lou"), huh.NewOption("Connect to Varda", "connect to varda"), huh.NewOption("Connect to Lou", "connect to lou"), 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": connect() // startSequence() case "stop Work": runCommand("timew", "stop", "work") case "start break": runCommand("timew", "stop", "work") runCommand("timew", "start", "break") case "stop break": runCommand("timew", "stop", "break") runCommand("timew", "start", "work") case "show week summary": runCommand("timew", "summary", ":week", "work") case "show month summary": runCommand("timew", "summary", ":month", "work") case "wake lou": wakeWorkstation() case "connect to varda": connectToJump() case "connect to lou": connectToWorkstation() case "start rdp connection": startRDPConnection() case "export": fmt.Println("Exporting Work times") } } var rootCmd = &cobra.Command{ Use: "work", Short: "Fast work interactions", Long: `A CLI tool to perform basic work cli tasks.`, } var startCmd = &cobra.Command{ Use: "start", Short: "start work", Long: "command to start the work day", Run: func(cmd *cobra.Command, args []string) { connect() }, } var stopCmd = &cobra.Command{ Use: "stop", Short: "stop work", Long: "command to stop the work day", Run: func(cmd *cobra.Command, args []string) { runCommand("timew", "stop", "work") os.Exit(0) }, } var showCmd = &cobra.Command{ Use: "show", Short: "show timetracking", Long: "show different timetracking", Run: func(cmd *cobra.Command, args []string) { if flags.ShowExport { fmt.Println("Exporting timetable") } if flags.ShowWeek { runCommand("timew", "summary", ":week", "work") } if flags.ShowMonth { runCommand("timew", "summary", ":month", "work") } os.Exit(0) }, } func main() { if len(os.Args) > 1 { if err := rootCmd.Execute(); err != nil { log.Fatalf("error executing command: %v", err) } // return } else { makeChoice() } }