Skip to content

Commit

Permalink
Merge pull request #100 from buchhalter-ai/99-feature-implement-vault…
Browse files Browse the repository at this point in the history
…-chooser-when-buchhalter-runs-for-the-first-time

FEATURE: Allow user to select vault
  • Loading branch information
andygrunwald authored Sep 8, 2024
2 parents 09770c8 + dcd1c00 commit 06e84a6
Showing 1 changed file with 111 additions and 1 deletion.
112 changes: 111 additions & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package cmd

import (
"encoding/json"
"fmt"
"io"
"log/slog"
"os"
"os/exec"
"path/filepath"

tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/spf13/cobra"
"github.com/spf13/viper"
Expand All @@ -26,6 +29,22 @@ const (
`
)

type Vault struct {
ID string `json:"id"`
Name string `json:"name"`
}

type VaultSelectionModel struct {
vaults []Vault
cursor int
choice int
resetView bool
}

func (m VaultSelectionModel) Init() tea.Cmd {
return nil
}

var (
// cliVersion is the version of the software.
// Typically a branch or tag name.
Expand Down Expand Up @@ -97,16 +116,107 @@ func init() {
}
}

func (m *VaultSelectionModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
switch msg.String() {
case "up":
if m.cursor > 0 {
m.cursor--
}
case "down":
if m.cursor < len(m.vaults)-1 {
m.cursor++
}
case "enter":
m.choice = m.cursor
m.resetView = true
return m, tea.Quit
}
}
return m, nil
}

func (m VaultSelectionModel) View() string {
if m.resetView {
return "\033[H\033[2J"
}

s := "Select the 1password vault that should be used with buchhalter cli:\n\n"
for i, vault := range m.vaults {
if m.cursor == i {
s += "(•) "
} else {
s += "( ) "
}
s += vault.Name + "\n"
}
return s
}

// TODO Refactor and move to vault.1password.go
func getVaults() ([]Vault, error) {
cmd := exec.Command("op", "vault", "list", "--format", "json")
output, err := cmd.Output()
if err != nil {
return nil, err
}

var vaults []Vault
err = json.Unmarshal(output, &vaults)
if err != nil {
return nil, err
}

return vaults, nil
}

func selectVault(vaults []Vault) (Vault, error) {
model := VaultSelectionModel{vaults: vaults}
p := tea.NewProgram(&model)
if _, err := p.Run(); err != nil {
return Vault{}, err
}
return model.vaults[model.choice], nil
}

func initConfig() {
homeDir, _ := os.UserHomeDir()
buchhalterConfigDir := filepath.Join(homeDir, ".buchhalter")
configFile := filepath.Join(buchhalterConfigDir, ".buchhalter.yaml")
buchhalterDir := filepath.Join(homeDir, "buchhalter")

// TODO We check the existing part of the configuration file here and below -> refctor to only once
if _, err := os.Stat(configFile); os.IsNotExist(err) {
err := utils.CreateDirectoryIfNotExists(buchhalterConfigDir)
if err != nil {
fmt.Println("Error creating config directory:", err)
os.Exit(1)
}

vaults, err := getVaults()
if err != nil {
fmt.Println("Error listing vaults. Make sure 1password cli is installed and you are logged in with eval $(op signin)", err)
os.Exit(1)
}

selectedVault, err := selectVault(vaults)
if err != nil {
fmt.Println("Error selecting vault:", err)
os.Exit(1)
}

viper.Set("credential_provider_vault", selectedVault.Name)
err = viper.WriteConfigAs(configFile)
if err != nil {
fmt.Println("Error creating config file:", err)
os.Exit(1)
}
}

// Set default values for viper config
// Documented settings
viper.SetDefault("credential_provider_cli_command", "")
viper.SetDefault("credential_provider_vault", "Base")
viper.SetDefault("credential_provider_item_tag", "buchhalter-ai")
viper.SetDefault("buchhalter_directory", buchhalterDir)
viper.SetDefault("buchhalter_config_directory", buchhalterConfigDir)
Expand Down

0 comments on commit 06e84a6

Please sign in to comment.