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
51 lines
1.5 KiB
Go
51 lines
1.5 KiB
Go
package app
|
|
|
|
import (
|
|
"time"
|
|
|
|
"git.kebcloud.no/keb/lazysecrets/internal/provider"
|
|
)
|
|
|
|
// cacheTTL is how long cached metadata is shown without reloading. Older
|
|
// entries are still shown immediately, then reloaded in the background.
|
|
const cacheTTL = 2 * time.Minute
|
|
|
|
// cache holds metadata: never secret values or errors. It's kept between
|
|
// runs in the cache file (persist.go).
|
|
type cache struct {
|
|
containers map[string]entry[[]provider.Container] // by source key
|
|
objects map[string]entry[[]provider.Object] // by container key (ID|type)
|
|
details map[string]entry[provider.Object] // Describe results by ID@version
|
|
// cursor remembers the selected child per parent key, so returning to
|
|
// a subscription or vault puts the cursor back where it was.
|
|
cursor map[string]string
|
|
}
|
|
|
|
type entry[T any] struct {
|
|
value T
|
|
at time.Time
|
|
}
|
|
|
|
func newCache() cache {
|
|
return cache{
|
|
containers: map[string]entry[[]provider.Container]{},
|
|
objects: map[string]entry[[]provider.Object]{},
|
|
details: map[string]entry[provider.Object]{},
|
|
cursor: map[string]string{},
|
|
}
|
|
}
|
|
|
|
func (e entry[T]) fresh(now time.Time) bool { return now.Sub(e.at) < cacheTTL }
|
|
|
|
// detailKey includes the account: the result carries its container, and
|
|
// later requests for the object are routed by it.
|
|
func detailKey(o provider.Object) string {
|
|
return containerKey(o.Container) + "|" + o.ID + "@" + o.Version
|
|
}
|
|
|
|
func (c cache) rememberCursor(parent, child string) {
|
|
if parent != "" && child != "" {
|
|
c.cursor[parent] = child
|
|
}
|
|
}
|