refactor: introduce app struct

This commit is contained in:
Patryk Hegenberg 2025-01-03 11:21:28 +01:00
parent c052e6daf0
commit aa3000cce6

204
main.go
View file

@ -37,48 +37,51 @@ type Flags struct {
ShowExport bool ShowExport bool
} }
var ( type App struct {
cfg Config cfg Config
flags Flags flags Flags
) }
func init() { func NewApp() (*App, error) {
showCmd.Flags().BoolVarP(&flags.ShowWeek, "week", "w", false, "show timewarrior week summary") cfg, err := loadConfig()
showCmd.Flags().BoolVarP(&flags.ShowMonth, "month", "m", false, "show timewarrior month summary") if err != nil {
showCmd.Flags().BoolVarP(&flags.ShowExport, "export", "e", false, "export timewarrior timetable") return nil, fmt.Errorf("error loading config: %w", err)
rootCmd.AddCommand(startCmd) }
rootCmd.AddCommand(stopCmd)
rootCmd.AddCommand(showCmd) return &App{
log.Println("Loading Config File") cfg: cfg,
}, nil
}
func loadConfig() (Config, error) {
var cfg Config
configPath, err := os.UserConfigDir() configPath, err := os.UserConfigDir()
if err != nil { if err != nil {
return return cfg, err
} }
workConfigPath := filepath.Join(configPath, "work") workConfigPath := filepath.Join(configPath, "work")
configFile := filepath.Join(workConfigPath, "config.toml") configFile := filepath.Join(workConfigPath, "config.toml")
viper.SetConfigFile(configFile) viper.SetConfigFile(configFile)
viper.SetConfigType("toml") viper.SetConfigType("toml")
viper.AddConfigPath(".") viper.AddConfigPath(".")
viper.AutomaticEnv() viper.AutomaticEnv()
if err := viper.ReadInConfig(); err != nil {
err = viper.ReadInConfig() return cfg, fmt.Errorf("error reading config file: %w", err)
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() { if err := viper.UnmarshalKey("default", &cfg); err != nil {
runCommand("timew", "start", "work") return cfg, fmt.Errorf("error decoding config: %w", err)
wakeWorkstation() }
sshClient, err := ssh.Dial("tcp", cfg.SSHHost+":"+fmt.Sprintf("%v", cfg.SSHPort), makeSSHClient())
return cfg, nil
}
func (a *App) connect() {
a.runCommand("timew", "start", "work")
a.wakeWorkstation()
sshClient, err := ssh.Dial("tcp", a.cfg.SSHHost+":"+fmt.Sprintf("%v", a.cfg.SSHPort), a.makeSSHClient())
if err != nil { if err != nil {
log.Fatal("Failed to dial: ", err) log.Fatal("Failed to dial: ", err)
} }
@ -90,11 +93,11 @@ func connect() {
} }
defer session.Close() defer session.Close()
go forwardPort(sshClient, "2048", cfg.WorkstationIP, "22") go forwardPort(sshClient, "2048", a.cfg.WorkstationIP, "22")
go forwardPort(sshClient, "6000", cfg.WorkstationIP, "3389") go forwardPort(sshClient, "6000", a.cfg.WorkstationIP, "3389")
connectToWorkstation() a.connectToWorkstation()
} }
func forwardPort(sshConn *ssh.Client, localPort, remoteHost, remotePort string) { func forwardPort(sshConn *ssh.Client, localPort, remoteHost, remotePort string) {
@ -130,20 +133,20 @@ func copyConn(dst, src net.Conn) {
io.Copy(dst, src) io.Copy(dst, src)
} }
func makeSSHClient() *ssh.ClientConfig { func (a *App) makeSSHClient() *ssh.ClientConfig {
keypath := os.ExpandEnv("$HOME/.ssh/hegenberg") keypath := os.ExpandEnv("$HOME/.ssh/hegenberg")
keyBytes, err := os.ReadFile(keypath) keyBytes, err := os.ReadFile(keypath)
if err != nil { if err != nil {
log.Fatalf("Failed to read private key: %s", err) log.Fatalf("Failed to read private key: %s", err)
} }
key, err := ssh.ParsePrivateKeyWithPassphrase(keyBytes, []byte(cfg.RDPPassword)) key, err := ssh.ParsePrivateKeyWithPassphrase(keyBytes, []byte(a.cfg.RDPPassword))
if err != nil { if err != nil {
log.Fatalf("Failed to parse private key: %s", err) log.Fatalf("Failed to parse private key: %s", err)
} }
return &ssh.ClientConfig{ return &ssh.ClientConfig{
User: cfg.SSHUser, User: a.cfg.SSHUser,
Auth: []ssh.AuthMethod{ Auth: []ssh.AuthMethod{
ssh.PublicKeys(key), ssh.PublicKeys(key),
}, },
@ -152,7 +155,7 @@ func makeSSHClient() *ssh.ClientConfig {
} }
} }
func runCommand(name string, args ...string) { func (a *App) runCommand(name string, args ...string) {
cmd := exec.Command(name, args...) cmd := exec.Command(name, args...)
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
@ -163,50 +166,44 @@ func runCommand(name string, args ...string) {
} }
} }
func startSequence() { func (a *App) wakeWorkstation() {
runCommand("timew", "start", "work")
wakeWorkstation()
connectToJump()
}
func wakeWorkstation() {
sshCommand := fmt.Sprintf("ssh -tt -p %s %s@%s ssh -tt %s@%s \"wakeonlan %s && exit\"", sshCommand := fmt.Sprintf("ssh -tt -p %s %s@%s ssh -tt %s@%s \"wakeonlan %s && exit\"",
fmt.Sprintf("%v", cfg.SSHPort), fmt.Sprintf("%v", a.cfg.SSHPort),
cfg.SSHUser, a.cfg.SSHUser,
cfg.SSHHost, a.cfg.SSHHost,
cfg.JumpUser, a.cfg.JumpUser,
cfg.JumpHost, a.cfg.JumpHost,
cfg.WorkstationMac) a.cfg.WorkstationMac)
args := strings.Split(sshCommand, " ") args := strings.Split(sshCommand, " ")
log.Println(args) log.Println(args)
runCommand("ssh", args[1:]...) a.runCommand("ssh", args[1:]...)
} }
func connectToJump() { func (a *App) connectToJump() {
sshCommand := fmt.Sprintf("ssh -tt -L 2048:%s:22 %s@%s", sshCommand := fmt.Sprintf("ssh -tt -L 2048:%s:22 %s@%s",
cfg.WorkstationHost, a.cfg.WorkstationHost,
cfg.SSHUser, a.cfg.SSHUser,
cfg.SSHHost) a.cfg.SSHHost)
args := strings.Split(sshCommand, " ") args := strings.Split(sshCommand, " ")
runCommand("ssh", args[1:]...) a.runCommand("ssh", args[1:]...)
} }
func connectToWorkstation() { func (a *App) connectToWorkstation() {
sshCommand := fmt.Sprintf("ssh -tt -L 6000:%s:3389 -p 2048 %s@127.0.0.1", sshCommand := fmt.Sprintf("ssh -tt -L 6000:%s:3389 -p 2048 %s@127.0.0.1",
cfg.WorkstationHost, a.cfg.WorkstationHost,
cfg.SSHUser) a.cfg.SSHUser)
args := strings.Split(sshCommand, " ") args := strings.Split(sshCommand, " ")
runCommand("ssh", args[1:]...) a.runCommand("ssh", args[1:]...)
} }
func startRDPConnection() { func (a *App) startRDPConnection() {
rdpCommand := fmt.Sprintf("xfreerdp /u:%s /p:%s /v:127.0.0.1:6000 /size:3000x1350", rdpCommand := fmt.Sprintf("xfreerdp /u:%s /p:%s /v:127.0.0.1:6000 /size:3000x1350",
cfg.RDPUser, a.cfg.RDPUser,
cfg.RDPPassword) a.cfg.RDPPassword)
runCommand("bash", "-c", rdpCommand) a.runCommand("bash", "-c", rdpCommand)
} }
func makeChoice() { func (a *App) makeChoice() {
var choice string var choice string
form := huh.NewForm( form := huh.NewForm(
huh.NewGroup( huh.NewGroup(
@ -220,9 +217,9 @@ func makeChoice() {
huh.NewOption("Start Break", "start break"), huh.NewOption("Start Break", "start break"),
huh.NewOption("Stop Break", "stop break"), huh.NewOption("Stop Break", "stop break"),
huh.NewOption("Export Timetable", "export"), huh.NewOption("Export Timetable", "export"),
huh.NewOption("Wake Lou", "wake lou"), huh.NewOption("Wake Workstation", "wake workstation"),
huh.NewOption("Connect to Varda", "connect to varda"), huh.NewOption("Connect to Jump", "connect to jump"),
huh.NewOption("Connect to Lou", "connect to lou"), huh.NewOption("Connect to Workstation", "connect to workstation"),
huh.NewOption("Start RDP Connection", "start rdp connection"), huh.NewOption("Start RDP Connection", "start rdp connection"),
). ).
Value(&choice), Value(&choice),
@ -237,83 +234,104 @@ func makeChoice() {
switch choice { switch choice {
case "Start": case "Start":
connect() a.connect()
// startSequence() // startSequence()
case "stop Work": case "stop Work":
runCommand("timew", "stop", "work") a.runCommand("timew", "stop", "work")
case "start break": case "start break":
runCommand("timew", "stop", "work") a.runCommand("timew", "track", "break")
runCommand("timew", "start", "break")
case "stop break": case "stop break":
runCommand("timew", "stop", "break") a.runCommand("timew", "track", "work")
runCommand("timew", "start", "work")
case "show week summary": case "show week summary":
runCommand("timew", "summary", ":week", "work") a.runCommand("timew", "summary", ":week", "work")
case "show month summary": case "show month summary":
runCommand("timew", "summary", ":month", "work") a.runCommand("timew", "summary", ":month", "work")
case "wake lou": case "wake workstation":
wakeWorkstation() a.wakeWorkstation()
case "connect to varda": case "connect to jump":
connectToJump() a.connectToJump()
case "connect to lou": case "connect to workstation":
connectToWorkstation() a.connectToWorkstation()
case "start rdp connection": case "start rdp connection":
startRDPConnection() a.startRDPConnection()
case "export": case "export":
fmt.Println("Exporting Work times") fmt.Println("Exporting Work times")
} }
} }
var rootCmd = &cobra.Command{ func (a *App) setupCommands() *cobra.Command {
rootCmd := &cobra.Command{
Use: "work", Use: "work",
Short: "Fast work interactions", Short: "Fast work interactions",
Long: `A CLI tool to perform basic work cli tasks.`, Long: `A CLI tool to perform basic work cli tasks.`,
} }
var startCmd = &cobra.Command{ rootCmd.AddCommand(a.startCommand())
rootCmd.AddCommand(a.stopCommand())
rootCmd.AddCommand(a.showCommand())
return rootCmd
}
func (a *App) startCommand() *cobra.Command {
return &cobra.Command{
Use: "start", Use: "start",
Short: "start work", Short: "start work",
Long: "command to start the work day", Long: "command to start the work day",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
connect() a.connect()
}, },
} }
}
var stopCmd = &cobra.Command{ func (a *App) stopCommand() *cobra.Command {
return &cobra.Command{
Use: "stop", Use: "stop",
Short: "stop work", Short: "stop work",
Long: "command to stop the work day", Long: "command to stop the work day",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
runCommand("timew", "stop", "work") a.runCommand("timew", "stop", "work")
os.Exit(0) os.Exit(0)
}, },
} }
}
var showCmd = &cobra.Command{ func (a *App) showCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "show", Use: "show",
Short: "show timetracking", Short: "show timetracking",
Long: "show different timetracking", Long: "show different timetracking",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
if flags.ShowExport { if a.flags.ShowExport {
fmt.Println("Exporting timetable") fmt.Println("Exporting timetable")
} }
if flags.ShowWeek { if a.flags.ShowWeek {
runCommand("timew", "summary", ":week", "work") a.runCommand("timew", "summary", ":week", "work")
} }
if flags.ShowMonth { if a.flags.ShowMonth {
runCommand("timew", "summary", ":month", "work") a.runCommand("timew", "summary", ":month", "work")
} }
os.Exit(0) os.Exit(0)
}, },
} }
cmd.Flags().BoolVarP(&a.flags.ShowWeek, "week", "w", false, "show timewarrior week summary")
cmd.Flags().BoolVarP(&a.flags.ShowMonth, "month", "m", false, "show timewarrior month summary")
cmd.Flags().BoolVarP(&a.flags.ShowExport, "export", "e", false, "export timewarrior timetable")
return cmd
}
func main() { func main() {
app, err := NewApp()
if err != nil {
log.Fatalf("unable to setup application: %v", err)
}
if len(os.Args) > 1 { if len(os.Args) > 1 {
if err := rootCmd.Execute(); err != nil { if err := app.setupCommands().Execute(); err != nil {
log.Fatalf("error executing command: %v", err) log.Fatalf("error executing command: %v", err)
} }
// return // return
} else { } else {
makeChoice() app.makeChoice()
} }
} }