feat: keep what was loaded between runs, and start where you left off (#2)
Starting lazysecrets again now shows what you had loaded at once, and puts you back where you were. - **Cache file** `~/.cache/lazysecrets/cache.json` (0600, written atomically): the Sources lists, vaults/scopes, secrets' metadata, Describe results and each list's cursor. **Never values**, and never repos (they're on disk anyway). Loaded on start; lists older than 2 minutes reload in the background as before. Saved every 30s when changed, and on quit. - **Housekeeping:** entries not loaded for 30 days are dropped, as is everything from accounts no longer signed in (every cache key starts with the account ID). The file has a format version; another version is ignored and replaced. `cache: false` turns it off; `--demo` never uses it. - **Sources are cached too:** Azure lists subscriptions over the network, so without this the Sources pane would still wait. - **Go to** searches everything cached straight away, and its background loading now also refreshes lists from an earlier run instead of skipping them. - **Where you left off:** the state file remembers the selected source, vault or secret per folder (the `--dir` or working directory, 100 most recent folders), and the next start in that folder goes back there quietly (no error if it's gone). Starting on a link skips it. Docs: `cache:` in config.md, a Cache section, the place in State, and the README Privacy section now mentions the cache file. Tests: cache round trip with a backend too slow to answer (lists and cursor come from the file), no value in the file, file mode 0600, old and signed-out entries dropped, back where it was left, not for another folder or a link. This touches `app.go` near PR #1's changes (Model fields, the Update switch), so whichever merges second needs a small conflict fix. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Karl-Einar Bergstrøm <karl-einarb@outlook.com> Reviewed-on: #2
This commit was merged in pull request #2.
This commit is contained in:
1 parent
17d2705e12
commit
10dc747f0f
11 files changed
+512
-12
No files matched your search
@@ -69,6 +69,12 @@ type Options struct {
|
||||
ClipboardClear time.Duration
|
||||
// OpenURL opens a web page; defaults to the system browser.
|
||||
OpenURL func(url string) error
|
||||
// CacheFile keeps what was loaded between runs; "" keeps it for this
|
||||
// run only.
|
||||
CacheFile string
|
||||
// Dir is the folder lazysecrets runs in: it goes back to where it was
|
||||
// left there, unless it was started on a link. "" doesn't.
|
||||
Dir string
|
||||
// FlatSources lists sources without provider groups.
|
||||
FlatSources bool
|
||||
// CollapseGroups starts with the Sources pane's groups folded, when
|
||||
@@ -160,6 +166,13 @@ type Model struct {
|
||||
getenv func(string) string
|
||||
nvimOpen func(server, path string, line int) error // edit.go
|
||||
now func() time.Time
|
||||
// The cache file, whether the cache changed since it was saved, and
|
||||
// when each provider's Sources list was loaded (persist.go).
|
||||
cachePath string
|
||||
cacheDirty bool
|
||||
sourcesAt map[string]time.Time
|
||||
placeDir string
|
||||
savedPlace string // the link last saved as the place
|
||||
|
||||
// A pending clear of a copied value, counted down in the footer.
|
||||
// clipToken identifies the latest copy so older ticks are ignored.
|
||||
@@ -415,6 +428,17 @@ func New(providers []provider.Provider, opts Options) Model {
|
||||
m.sourcesGen = 1
|
||||
m.sourcesPending = len(providers)
|
||||
m.spinning = true
|
||||
|
||||
m.cachePath, m.placeDir, m.sourcesAt = opts.CacheFile, opts.Dir, map[string]time.Time{}
|
||||
if m.cachePath != "" {
|
||||
m.loadCacheFile()
|
||||
if len(m.sourceResults) > 0 {
|
||||
m.rebuildSources()
|
||||
}
|
||||
}
|
||||
if m.placeDir != "" && opts.Open == nil {
|
||||
m.restorePlace()
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
@@ -438,6 +462,9 @@ func (m *Model) setVersions(items []provider.Object) {
|
||||
|
||||
func (m Model) Init() tea.Cmd {
|
||||
cmds := []tea.Cmd{m.sourcesCmd(), m.spin.Tick, m.checkClipboard()}
|
||||
if m.cachePath != "" || m.placeDir != "" {
|
||||
cmds = append(cmds, tea.Tick(persistEvery, func(time.Time) tea.Msg { return persistTickMsg{} }))
|
||||
}
|
||||
if m.fetchOnStart {
|
||||
cmds = append(cmds, m.fetchLatest(false))
|
||||
}
|
||||
@@ -530,6 +557,12 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
case editDoneMsg:
|
||||
cmds = append(cmds, m.handleEditDone(msg))
|
||||
|
||||
case persistTickMsg:
|
||||
cmds = append(cmds, m.persistTick())
|
||||
|
||||
case persistedMsg:
|
||||
m.handlePersisted(msg)
|
||||
|
||||
case crawlMsg:
|
||||
cmds = append(cmds, m.handleCrawl(msg))
|
||||
|
||||
@@ -560,6 +593,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
m.containers.SetError(msg.err)
|
||||
} else {
|
||||
m.cache.containers[msg.key] = entry[[]provider.Container]{msg.items, m.now()}
|
||||
m.cacheDirty = true
|
||||
m.containers.SetItems(m.starredContainersFirst(msg.items))
|
||||
}
|
||||
|
||||
@@ -580,6 +614,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
msg.key = m.rebind(*msg.bound)
|
||||
}
|
||||
m.cache.objects[msg.key] = entry[[]provider.Object]{msg.items, m.now()}
|
||||
m.cacheDirty = true
|
||||
if m.versionsOf != nil {
|
||||
m.setVersions(msg.items)
|
||||
} else {
|
||||
@@ -609,6 +644,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
// Errors and unsupported providers just leave the count out.
|
||||
if msg.err == nil {
|
||||
m.cache.objects[msg.key] = entry[[]provider.Object]{msg.items, m.now()}
|
||||
m.cacheDirty = true
|
||||
m.detail.SetVersions(msg.id, msg.items)
|
||||
}
|
||||
|
||||
@@ -622,6 +658,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
m.detail.SetDescribeError(msg.err)
|
||||
} else {
|
||||
m.cache.details[msg.key] = entry[provider.Object]{msg.obj, m.now()}
|
||||
m.cacheDirty = true
|
||||
m.detail.SetDescribed(msg.obj)
|
||||
}
|
||||
|
||||
@@ -1213,6 +1250,9 @@ func (m *Model) handleSources(msg sourcesMsg) tea.Cmd {
|
||||
}
|
||||
m.sourcesPending--
|
||||
m.sourceResults[msg.provider] = msg
|
||||
if msg.err == nil {
|
||||
m.sourcesAt[msg.provider], m.cacheDirty = m.now(), true
|
||||
}
|
||||
|
||||
var errs []string
|
||||
for _, p := range m.providers {
|
||||
|
||||
Reference in new issue
Block a user