package ssh import ( "fmt" "os" "strings" "time" "github.com/mirivlad/sshkeeper/internal/model" ) // GenerateConfig creates OpenSSH config content from server profiles func GenerateConfig(servers []*model.Server) (string, error) { var sb strings.Builder sb.WriteString("# Generated by sshkeeper. Do not edit manually.\n") sb.WriteString(fmt.Sprintf("# Generated at: %s\n\n", time.Now().Format(time.RFC3339))) for _, s := range servers { sb.WriteString(fmt.Sprintf("Host %s\n", s.Alias)) sb.WriteString(fmt.Sprintf(" HostName %s\n", s.Host)) if s.Port != 22 { sb.WriteString(fmt.Sprintf(" Port %d\n", s.Port)) } if s.User != "" { sb.WriteString(fmt.Sprintf(" User %s\n", s.User)) } if s.IdentityFile != "" && s.AuthMethod != model.AuthPassword { sb.WriteString(fmt.Sprintf(" IdentityFile %s\n", s.IdentityFile)) } if s.ProxyJump != "" { sb.WriteString(fmt.Sprintf(" ProxyJump %s\n", s.ProxyJump)) } sb.WriteString("\n") } return sb.String(), nil } // WriteConfig writes the generated config to ~/.ssh/config.d/sshkeeper.conf func WriteConfig(servers []*model.Server) error { home, err := os.UserHomeDir() if err != nil { return err } configD := home + "/.ssh/config.d" if err := os.MkdirAll(configD, 0700); err != nil { return err } content, err := GenerateConfig(servers) if err != nil { return err } configFile := configD + "/sshkeeper.conf" tmpFile := configFile + ".tmp" if err := os.WriteFile(tmpFile, []byte(content), 0600); err != nil { return err } return os.Rename(tmpFile, configFile) } // InstallInclude adds "Include ~/.ssh/config.d/*.conf" to ~/.ssh/config func InstallInclude() error { home, err := os.UserHomeDir() if err != nil { return err } sshDir := home + "/.ssh" configD := sshDir + "/config.d" mainConfig := sshDir + "/config" if err := os.MkdirAll(configD, 0700); err != nil { return err } includeLine := "Include ~/.ssh/config.d/*.conf" // Check if already included if data, err := os.ReadFile(mainConfig); err == nil { if strings.Contains(string(data), "Include ~/.ssh/config.d") { return nil } } // Prepend include line f, err := os.OpenFile(mainConfig, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600) if err != nil { return err } defer f.Close() _, err = f.WriteString("\n" + includeLine + "\n") return err }