- Added internal/vault/client.go for creating a Vault client with configuration settings. - Introduced internal/vault/errors.go to classify Vault API errors for better UI handling. - Created internal/vault/kv.go to manage KV secrets, including listing, reading, writing, and deleting operations. - Implemented internal/vault/mounts.go to list and describe secret engine mounts. - Developed internal/vault/service.go to provide a unified entry point for Vault operations. - Added internal/vault/kv_test.go for comprehensive testing of KV operations. - Introduced internal/ui/toast.go for transient notifications in the UI. - Added renovate.json for dependency management and updates.
117 lines
3.0 KiB
Go
117 lines
3.0 KiB
Go
package ui
|
|
|
|
import (
|
|
"sort"
|
|
|
|
"charm.land/bubbles/v2/key"
|
|
tea "charm.land/bubbletea/v2"
|
|
"charm.land/lipgloss/v2"
|
|
|
|
"git.morlana.online/f.weber/vault-tui/internal/ui/keys"
|
|
)
|
|
|
|
// textInputActive reports whether a free-text field currently owns the
|
|
// keyboard, so global single-letter bindings (help "?", profiles "p", ...)
|
|
// don't get eaten while the user is typing a value/filter that happens to
|
|
// contain that letter.
|
|
func (m *Model) textInputActive() bool {
|
|
if m.top() == scrEditor {
|
|
return true
|
|
}
|
|
if m.top() == scrAuth && m.auth.phase == 1 {
|
|
return true
|
|
}
|
|
if (m.top() == scrMounts || m.top() == scrBrowser) && m.listReady && m.list.SettingFilter() {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (m *Model) openProfilePicker() (tea.Model, tea.Cmd) {
|
|
names := make([]string, 0, len(m.a.File.Profiles))
|
|
for n := range m.a.File.Profiles {
|
|
names = append(names, n)
|
|
}
|
|
sort.Strings(names)
|
|
cursor := 0
|
|
for i, n := range names {
|
|
if n == m.a.Settings.Profile {
|
|
cursor = i
|
|
}
|
|
}
|
|
m.profile = profileState{open: true, names: names, cursor: cursor}
|
|
return m, nil
|
|
}
|
|
|
|
func (m *Model) updateProfilePicker(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) {
|
|
switch {
|
|
case key.Matches(msg, m.keys.Up):
|
|
if m.profile.cursor > 0 {
|
|
m.profile.cursor--
|
|
}
|
|
case key.Matches(msg, m.keys.Down):
|
|
if m.profile.cursor < len(m.profile.names)-1 {
|
|
m.profile.cursor++
|
|
}
|
|
case key.Matches(msg, m.keys.Enter):
|
|
return m.switchToSelectedProfile()
|
|
case key.Matches(msg, m.keys.Back), key.Matches(msg, m.keys.Quit):
|
|
m.profile.open = false
|
|
}
|
|
return m, nil
|
|
}
|
|
|
|
func (m *Model) switchToSelectedProfile() (tea.Model, tea.Cmd) {
|
|
name := m.profile.names[m.profile.cursor]
|
|
m.profile.open = false
|
|
if name == m.a.Settings.Profile {
|
|
return m, nil
|
|
}
|
|
if err := m.a.SwitchProfile(name); err != nil {
|
|
return m, m.notify(toastErr, "switch profile: %v", err)
|
|
}
|
|
|
|
m.svc = m.a.Service()
|
|
km := keys.Default()
|
|
_ = km.Apply(m.a.File.Keys) // already validated once at startup; config hasn't changed
|
|
if m.a.Settings.ReadOnly {
|
|
km.DisableWrites()
|
|
}
|
|
m.keys = km
|
|
m.tokenInfo = nil
|
|
m.listSelect = ""
|
|
return m, m.startAuth()
|
|
}
|
|
|
|
func (m *Model) confirmLogout() (tea.Model, tea.Cmd) {
|
|
profile := m.a.Settings.Profile
|
|
action := func() tea.Cmd {
|
|
return func() tea.Msg {
|
|
return logoutDoneMsg{err: m.a.Store.Erase(m.ctx)}
|
|
}
|
|
}
|
|
m.confirm = &confirmSpec{
|
|
title: "Log out?",
|
|
body: "Forgets the saved token for profile " + profile + ". This does not revoke it in Vault.",
|
|
onConfirm: action,
|
|
}
|
|
return m, nil
|
|
}
|
|
|
|
func (m *Model) viewProfilePicker() string {
|
|
lines := []string{m.styles.ModalTitle.Render("Switch profile"), ""}
|
|
for i, n := range m.profile.names {
|
|
marker := " "
|
|
if n == m.a.Settings.Profile {
|
|
marker = m.styles.SuccessText.Render(symCurrent) + " "
|
|
}
|
|
line := marker + n
|
|
if i == m.profile.cursor {
|
|
line = m.styles.RowSelected.Render(padRight(marker+n, 28))
|
|
}
|
|
lines = append(lines, line)
|
|
}
|
|
lines = append(lines, "", m.styles.Help.Render("↑/↓ move • enter switch • esc cancel"))
|
|
return m.styles.Modal.Render(lipgloss.JoinVertical(lipgloss.Left, lines...))
|
|
}
|