summaryrefslogtreecommitdiff
path: root/internal/cli/kdbx_store.go
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-04-18 16:22:05 +0300
committerPaul Buetow <paul@buetow.org>2026-04-18 16:22:05 +0300
commit667bf24a8ac2c2b4c4f9befd7f77b22f0a156a27 (patch)
tree620e41dba973e2916087ff7743d5a7ec6afd9d38 /internal/cli/kdbx_store.go
parent60f717b97ce6c375679080472750e60aab9dcd8f (diff)
refactor: extract migration logic into internal/migrate package (task q6)
Move all geheim→KeePass migration business logic from internal/cli/migrate_kdbx.go into a new internal/migrate package (migrator.go, kdbx_store.go). The CLI layer is now a thin orchestrator: parse flags, open KDBX, call migrate.Run, save, report. Key design decisions: - migrate.Run accepts separate logFn (stdout info) and warnFn (stderr errors) so per-entry errors correctly route to stderr via warn(), not stdout via logMsg() - migrate.Options contains only DryRun; DBPath/BinaryOutDir removed (CLI-only concerns) - StoreWalker interface is narrow, avoiding a direct dependency on backend.Backend - cli_paths.go extracts readPasswordFile/resolveHomeDir/expandHome shared within cli - kdbx_store.go deleted from cli (moved to internal/migrate) - Duplicate TestExtractPasswordFromContent removed from kdbx_store_test.go - Custom contains/containsStr helpers replaced with strings.Contains in tests - Dry-run test now asserts logged message content Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'internal/cli/kdbx_store.go')
-rw-r--r--internal/cli/kdbx_store.go95
1 files changed, 0 insertions, 95 deletions
diff --git a/internal/cli/kdbx_store.go b/internal/cli/kdbx_store.go
deleted file mode 100644
index bdaeb31..0000000
--- a/internal/cli/kdbx_store.go
+++ /dev/null
@@ -1,95 +0,0 @@
-package cli
-
-import (
- "fmt"
- "os"
-
- gokeepasslib "github.com/tobischo/gokeepasslib/v3"
-
- "codeberg.org/snonux/foostore/internal/keepass"
-)
-
-// KDBXStore is the minimal interface needed by migrate-kdbx.
-type KDBXStore interface {
- UpsertTextEntry(groupPath []string, title, password, notes string) (overwrote bool, err error)
- UpsertBinaryEntry(groupPath []string, title, filename string, content []byte) (overwrote bool, err error)
- Save() error
-}
-
-type kdbxStore struct {
- path string
- db *gokeepasslib.Database
-}
-
-// OpenKDBXStore opens an existing KDBX database using password credentials.
-func OpenKDBXStore(dbPath, password string) (KDBXStore, error) {
- f, err := os.Open(dbPath)
- if err != nil {
- return nil, fmt.Errorf("opening kdbx %q: %w", dbPath, err)
- }
- defer f.Close()
-
- db := gokeepasslib.NewDatabase()
- db.Credentials = gokeepasslib.NewPasswordCredentials(password)
- if err := gokeepasslib.NewDecoder(f).Decode(db); err != nil {
- return nil, fmt.Errorf("decoding kdbx %q: %w", dbPath, err)
- }
- if err := db.UnlockProtectedEntries(); err != nil {
- return nil, fmt.Errorf("unlocking kdbx %q: %w", dbPath, err)
- }
-
- if db.Content == nil {
- db.Content = gokeepasslib.NewContent()
- }
- if db.Content.Root == nil {
- db.Content.Root = gokeepasslib.NewRootData()
- }
- if len(db.Content.Root.Groups) == 0 {
- root := gokeepasslib.NewGroup()
- root.Name = "Root"
- db.Content.Root.Groups = append(db.Content.Root.Groups, root)
- }
-
- return &kdbxStore{
- path: dbPath,
- db: db,
- }, nil
-}
-
-// UpsertTextEntry creates or updates a text entry in groupPath with the given
-// title, password, and notes. Delegates field manipulation to keepass.SetEntryField
-// and group navigation to keepass.EnsureGroup to avoid duplication.
-func (s *kdbxStore) UpsertTextEntry(groupPath []string, title, password, notes string) (bool, error) {
- g := keepass.EnsureGroup(&s.db.Content.Root.Groups[0], groupPath)
- entry, overwrote := keepass.UpsertEntryByTitle(g, title)
- keepass.SetEntryField(entry, "Title", title)
- keepass.SetEntryField(entry, "Password", password)
- keepass.SetEntryField(entry, "Notes", notes)
- return overwrote, nil
-}
-
-// UpsertBinaryEntry creates or updates a binary attachment entry in groupPath.
-// Delegates field manipulation to keepass.SetEntryField and group navigation
-// to keepass.EnsureGroup to avoid duplication.
-func (s *kdbxStore) UpsertBinaryEntry(groupPath []string, title, filename string, content []byte) (bool, error) {
- g := keepass.EnsureGroup(&s.db.Content.Root.Groups[0], groupPath)
- entry, overwrote := keepass.UpsertEntryByTitle(g, title)
- keepass.SetEntryField(entry, "Title", title)
- keepass.SetEntryField(entry, "Password", "")
-
- b := s.db.AddBinary(content)
- entry.Binaries = []gokeepasslib.BinaryReference{b.CreateReference(filename)}
- // Keep notes concise for binary-only entries.
- keepass.SetEntryField(entry, "Notes", fmt.Sprintf("Migrated binary attachment: %s", filename))
- return overwrote, nil
-}
-
-// Save locks protected entries and atomically writes the database to disk.
-// Delegates the tmp→encode→rename sequence to keepass.AtomicSave to avoid
-// duplicating that logic here (keepass.Backend.save() uses the same helper).
-func (s *kdbxStore) Save() error {
- if err := s.db.LockProtectedEntries(); err != nil {
- return fmt.Errorf("locking kdbx entries: %w", err)
- }
- return keepass.AtomicSave(s.db, s.path)
-}