Support KeePassGO state dir via flag and env

This commit is contained in:
Joe Julian
2026-03-29 15:41:44 -07:00
parent 37b83e654a
commit fe3fa854bb
2 changed files with 73 additions and 23 deletions
+45 -23
View File
@@ -71,6 +71,11 @@ type attachmentItem struct {
Size int
}
type statePaths struct {
DefaultSaveAsPath string
RecentVaultsPath string
}
type ui struct {
mode string
theme *material.Theme
@@ -184,19 +189,23 @@ const (
errSaveAsPathRequired = "save-as path is required"
)
func newUI(mode string) *ui {
return newUIWithSession(mode, &session.Manager{})
func newUI(mode string, paths statePaths) *ui {
return newUIWithSession(mode, &session.Manager{}, paths)
}
func newUIWithModel(mode string, model vault.Model) *ui {
return newUIWithState(mode, &uiSession{model: model})
return newUIWithState(mode, &uiSession{model: model}, defaultStatePaths(""))
}
func newUIWithSession(mode string, sess appstate.CurrentSession) *ui {
return newUIWithState(mode, sess)
func newUIWithSession(mode string, sess appstate.CurrentSession, paths ...statePaths) *ui {
selected := defaultStatePaths("")
if len(paths) > 0 {
selected = paths[0]
}
return newUIWithState(mode, sess, selected)
}
func newUIWithState(mode string, sess appstate.CurrentSession) *ui {
func newUIWithState(mode string, sess appstate.CurrentSession, paths statePaths) *ui {
th := material.NewTheme()
th.Palette.Bg = bgColor
th.Palette.Fg = color.NRGBA{R: 31, G: 29, B: 27, A: 255}
@@ -242,8 +251,8 @@ func newUIWithState(mode string, sess appstate.CurrentSession) *ui {
state: appstate.State{},
selectedHistoryIndex: -1,
lifecycleMode: "local",
defaultSaveAsPath: defaultSaveAsPath(),
recentVaultsPath: defaultRecentVaultsPath(),
defaultSaveAsPath: paths.DefaultSaveAsPath,
recentVaultsPath: paths.RecentVaultsPath,
}
u.state.Session = sess
u.phoneSplit.Value = 0.46
@@ -270,20 +279,29 @@ func (u *ui) filter() {
}
}
func defaultSaveAsPath() string {
cacheDir, err := os.UserCacheDir()
if err != nil || strings.TrimSpace(cacheDir) == "" {
cacheDir = os.TempDir()
func defaultStatePaths(stateDir string) statePaths {
baseDir := strings.TrimSpace(stateDir)
if baseDir == "" {
configDir, err := os.UserConfigDir()
if err != nil || strings.TrimSpace(configDir) == "" {
configDir = os.TempDir()
}
baseDir = filepath.Join(configDir, "keepassgo")
}
return statePaths{
DefaultSaveAsPath: filepath.Join(baseDir, "vault.kdbx"),
RecentVaultsPath: filepath.Join(baseDir, "recent-vaults.json"),
}
return filepath.Join(cacheDir, "keepassgo", "vault.kdbx")
}
func defaultRecentVaultsPath() string {
configDir, err := os.UserConfigDir()
if err != nil || strings.TrimSpace(configDir) == "" {
configDir = os.TempDir()
func resolveFlagOrEnv(flagValue, envName, fallback string) string {
if value := strings.TrimSpace(flagValue); value != "" {
return value
}
return filepath.Join(configDir, "keepassgo", "recent-vaults.json")
if value := strings.TrimSpace(os.Getenv(envName)); value != "" {
return value
}
return fallback
}
func (u *ui) selectedAttachmentItems() []attachmentItem {
@@ -1858,12 +1876,16 @@ func fill(c color.NRGBA) layout.Widget {
}
func main() {
mode := flag.String("mode", "desktop", "window mode: desktop or phone")
mode := flag.String("mode", "", "window mode: desktop or phone")
stateDir := flag.String("state-dir", "", "directory for KeePassGO state such as recent-vault history and default save targets")
flag.Parse()
resolvedMode := resolveFlagOrEnv(*mode, "KEEPASSGO_MODE", "desktop")
resolvedStateDir := resolveFlagOrEnv(*stateDir, "KEEPASSGO_STATE_DIR", "")
width := unit.Dp(1180)
height := unit.Dp(760)
if strings.EqualFold(*mode, "phone") {
if strings.EqualFold(resolvedMode, "phone") {
// Pixel 10 uses a 20:9 display; use a 412x915 dp viewport as a desktop-friendly preview.
width = unit.Dp(412)
height = unit.Dp(915)
@@ -1875,7 +1897,7 @@ func main() {
app.Title(productName),
app.Size(width, height),
)
if err := run(w, strings.ToLower(*mode)); err != nil {
if err := run(w, strings.ToLower(resolvedMode), defaultStatePaths(resolvedStateDir)); err != nil {
panic(err)
}
os.Exit(0)
@@ -1883,9 +1905,9 @@ func main() {
app.Main()
}
func run(w *app.Window, mode string) error {
func run(w *app.Window, mode string, paths statePaths) error {
var ops op.Ops
ui := newUI(mode)
ui := newUI(mode, paths)
for {
e := w.Event()
switch e := e.(type) {
+28
View File
@@ -1798,6 +1798,34 @@ func TestUILoadsRecentVaultsFromPersistedConfig(t *testing.T) {
}
}
func TestDefaultStatePathsUsesProvidedStateDir(t *testing.T) {
t.Parallel()
base := filepath.Join(t.TempDir(), "keepassgo-state")
paths := defaultStatePaths(base)
if got := paths.DefaultSaveAsPath; got != filepath.Join(base, "vault.kdbx") {
t.Fatalf("DefaultSaveAsPath = %q, want %q", got, filepath.Join(base, "vault.kdbx"))
}
if got := paths.RecentVaultsPath; got != filepath.Join(base, "recent-vaults.json") {
t.Fatalf("RecentVaultsPath = %q, want %q", got, filepath.Join(base, "recent-vaults.json"))
}
}
func TestResolveFlagOrEnvPrefersFlagThenEnvThenFallback(t *testing.T) {
t.Setenv("KEEPASSGO_TEST_VALUE", "from-env")
if got := resolveFlagOrEnv("from-flag", "KEEPASSGO_TEST_VALUE", "fallback"); got != "from-flag" {
t.Fatalf("resolveFlagOrEnv(flag) = %q, want %q", got, "from-flag")
}
if got := resolveFlagOrEnv("", "KEEPASSGO_TEST_VALUE", "fallback"); got != "from-env" {
t.Fatalf("resolveFlagOrEnv(env) = %q, want %q", got, "from-env")
}
if got := resolveFlagOrEnv("", "KEEPASSGO_TEST_MISSING", "fallback"); got != "fallback" {
t.Fatalf("resolveFlagOrEnv(fallback) = %q, want %q", got, "fallback")
}
}
func TestEnterOnLocalLifecycleScreenDefaultsToOpenVault(t *testing.T) {
t.Parallel()