Support KeePassGO state dir via flag and env
This commit is contained in:
@@ -71,6 +71,11 @@ type attachmentItem struct {
|
|||||||
Size int
|
Size int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type statePaths struct {
|
||||||
|
DefaultSaveAsPath string
|
||||||
|
RecentVaultsPath string
|
||||||
|
}
|
||||||
|
|
||||||
type ui struct {
|
type ui struct {
|
||||||
mode string
|
mode string
|
||||||
theme *material.Theme
|
theme *material.Theme
|
||||||
@@ -184,19 +189,23 @@ const (
|
|||||||
errSaveAsPathRequired = "save-as path is required"
|
errSaveAsPathRequired = "save-as path is required"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newUI(mode string) *ui {
|
func newUI(mode string, paths statePaths) *ui {
|
||||||
return newUIWithSession(mode, &session.Manager{})
|
return newUIWithSession(mode, &session.Manager{}, paths)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newUIWithModel(mode string, model vault.Model) *ui {
|
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 {
|
func newUIWithSession(mode string, sess appstate.CurrentSession, paths ...statePaths) *ui {
|
||||||
return newUIWithState(mode, sess)
|
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 := material.NewTheme()
|
||||||
th.Palette.Bg = bgColor
|
th.Palette.Bg = bgColor
|
||||||
th.Palette.Fg = color.NRGBA{R: 31, G: 29, B: 27, A: 255}
|
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{},
|
state: appstate.State{},
|
||||||
selectedHistoryIndex: -1,
|
selectedHistoryIndex: -1,
|
||||||
lifecycleMode: "local",
|
lifecycleMode: "local",
|
||||||
defaultSaveAsPath: defaultSaveAsPath(),
|
defaultSaveAsPath: paths.DefaultSaveAsPath,
|
||||||
recentVaultsPath: defaultRecentVaultsPath(),
|
recentVaultsPath: paths.RecentVaultsPath,
|
||||||
}
|
}
|
||||||
u.state.Session = sess
|
u.state.Session = sess
|
||||||
u.phoneSplit.Value = 0.46
|
u.phoneSplit.Value = 0.46
|
||||||
@@ -270,20 +279,29 @@ func (u *ui) filter() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func defaultSaveAsPath() string {
|
func defaultStatePaths(stateDir string) statePaths {
|
||||||
cacheDir, err := os.UserCacheDir()
|
baseDir := strings.TrimSpace(stateDir)
|
||||||
if err != nil || strings.TrimSpace(cacheDir) == "" {
|
if baseDir == "" {
|
||||||
cacheDir = os.TempDir()
|
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 {
|
func resolveFlagOrEnv(flagValue, envName, fallback string) string {
|
||||||
configDir, err := os.UserConfigDir()
|
if value := strings.TrimSpace(flagValue); value != "" {
|
||||||
if err != nil || strings.TrimSpace(configDir) == "" {
|
return value
|
||||||
configDir = os.TempDir()
|
|
||||||
}
|
}
|
||||||
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 {
|
func (u *ui) selectedAttachmentItems() []attachmentItem {
|
||||||
@@ -1858,12 +1876,16 @@ func fill(c color.NRGBA) layout.Widget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
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()
|
flag.Parse()
|
||||||
|
|
||||||
|
resolvedMode := resolveFlagOrEnv(*mode, "KEEPASSGO_MODE", "desktop")
|
||||||
|
resolvedStateDir := resolveFlagOrEnv(*stateDir, "KEEPASSGO_STATE_DIR", "")
|
||||||
|
|
||||||
width := unit.Dp(1180)
|
width := unit.Dp(1180)
|
||||||
height := unit.Dp(760)
|
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.
|
// Pixel 10 uses a 20:9 display; use a 412x915 dp viewport as a desktop-friendly preview.
|
||||||
width = unit.Dp(412)
|
width = unit.Dp(412)
|
||||||
height = unit.Dp(915)
|
height = unit.Dp(915)
|
||||||
@@ -1875,7 +1897,7 @@ func main() {
|
|||||||
app.Title(productName),
|
app.Title(productName),
|
||||||
app.Size(width, height),
|
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)
|
panic(err)
|
||||||
}
|
}
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
@@ -1883,9 +1905,9 @@ func main() {
|
|||||||
app.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
|
var ops op.Ops
|
||||||
ui := newUI(mode)
|
ui := newUI(mode, paths)
|
||||||
for {
|
for {
|
||||||
e := w.Event()
|
e := w.Event()
|
||||||
switch e := e.(type) {
|
switch e := e.(type) {
|
||||||
|
|||||||
@@ -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) {
|
func TestEnterOnLocalLifecycleScreenDefaultsToOpenVault(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user