feat: share env files as encrypted links #54
No files matched your search
@@ -679,12 +679,14 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
if msg.bound != nil {
|
||||
msg.key = m.rebind(*msg.bound)
|
||||
}
|
||||
before := m.cache.objects[msg.key].value
|
||||
m.cache.objects[msg.key] = entry[[]provider.Object]{msg.items, m.now()}
|
||||
m.cacheDirty = true
|
||||
if m.versionsOf != nil {
|
||||
m.setVersions(msg.items)
|
||||
} else {
|
||||
m.secrets.SetItems(m.starredFirst(msg.items))
|
||||
cmds = append(cmds, m.refreshShown(before))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"maps"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
tea "charm.land/bubbletea/v2"
|
||||
|
||||
"git.kebcloud.no/keb/lazysecrets/internal/provider"
|
||||
)
|
||||
|
||||
@@ -48,3 +52,31 @@ func (c cache) rememberCursor(parent, child string) {
|
||||
c.cursor[parent] = child
|
||||
}
|
||||
}
|
||||
|
||||
// refreshShown shows the selected secret's newer metadata when its list
|
||||
// reloaded with it changed from before (a cached list refreshed in the
|
||||
// background), and describes it afresh: its cached details came from
|
||||
// the old listing.
|
||||
func (m *Model) refreshShown(before []provider.Object) tea.Cmd {
|
||||
s, ok := m.secrets.Selected()
|
||||
if !ok || m.secrets.SelectedID() != m.selSecret {
|
||||
return nil // a new selection is shown as usual
|
||||
}
|
||||
i := slices.IndexFunc(before, func(o provider.Object) bool { return o.ID == s.ID })
|
||||
if i < 0 || sameListing(before[i], s) {
|
||||
return nil
|
||||
}
|
||||
m.detail.Refresh(s)
|
||||
delete(m.cache.details, detailKey(s))
|
||||
return m.describe(s)
|
||||
}
|
||||
|
||||
// sameListing reports whether a and b list the same: what a list
|
||||
// says of an object, not what describing it adds.
|
||||
func sameListing(a, b provider.Object) bool {
|
||||
enabled := func(e *bool) bool { return e == nil || *e }
|
||||
return a.Name == b.Name && a.Version == b.Version && enabled(a.Enabled) == enabled(b.Enabled) &&
|
||||
a.Updated.Equal(b.Updated) && a.Expires.Equal(b.Expires) && a.ContentType == b.ContentType &&
|
||||
maps.Equal(a.Tags, b.Tags) && a.Reference == b.Reference && a.URL == b.URL &&
|
||||
slices.Equal(a.Attrs, b.Attrs)
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"slices"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -159,3 +160,33 @@ func TestMovingOnDropsUnsettledLoads(t *testing.T) {
|
||||
t.Errorf("stopping on a subscription: %d calls, want 1", c.containers-before)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReloadedListRefreshesShownSecret(t *testing.T) {
|
||||
t.Parallel()
|
||||
d, _, clock := newCachingDriver(t)
|
||||
d.keys("3") // kv-app-prod's secrets, db-password selected
|
||||
shown, _ := d.m.detail.Secret()
|
||||
// What an older lazysecrets listed and cached for it.
|
||||
const old = "https://portal.example/old"
|
||||
key := d.m.selContainer
|
||||
e := d.m.cache.objects[key]
|
||||
items := slices.Clone(e.value)
|
||||
for i := range items {
|
||||
if items[i].ID == shown.ID {
|
||||
items[i].URL = old
|
||||
}
|
||||
}
|
||||
d.m.cache.objects[key] = entry[[]provider.Object]{items, e.at}
|
||||
stale := shown
|
||||
stale.URL = old
|
||||
d.m.cache.details[detailKey(shown)] = entry[provider.Object]{stale, *clock}
|
||||
|
||||
// Away and back once the list is stale: it's shown from the cache,
|
||||
// then reloaded, and the details pane follows.
|
||||
d.keys("1", "j")
|
||||
*clock = clock.Add(cacheTTL + time.Second)
|
||||
d.keys("k", "3")
|
||||
if got, _ := d.m.detail.Secret(); got.ID != shown.ID || got.URL == old {
|
||||
t.Errorf("shown %s with %q after the reload", got.Name, got.URL)
|
||||
}
|
||||
}
|
||||
@@ -60,6 +60,16 @@ func (d *Detail) ShowSecret(s provider.Object) {
|
||||
d.container = &s.Container
|
||||
}
|
||||
|
||||
// Refresh shows s's newer metadata if s is the secret shown, keeping
|
||||
// its account and any value shown or loading.
|
||||
func (d *Detail) Refresh(s provider.Object) {
|
||||
if d.secret == nil || d.secret.ID != s.ID {
|
||||
return
|
||||
}
|
||||
s.Container = d.secret.Container
|
||||
d.secret = &s
|
||||
}
|
||||
|
||||
// ShowContainer shows a container summary when no secret is selected.
|
||||
func (d *Detail) ShowContainer(c provider.Container) {
|
||||
d.Clear()
|
||||
|
||||
Reference in new issue
Block a user