feat: add VaultSession (vault lifecycle and entry CRUD)

This commit is contained in:
2026-05-22 14:56:53 +02:00
parent 33b670510c
commit 838e078304
1284 changed files with 6589 additions and 0 deletions
@@ -0,0 +1,92 @@
import Foundation
import Combine
@MainActor
public final class VaultSession: ObservableObject {
@Published public private(set) var database: KDBXDatabase?
@Published public private(set) var isLocked: Bool = true
private var document: KDBXDocument?
private var masterPassword: String = ""
public init() {}
public func unlock(url: URL, password: String) throws {
let doc = KDBXDocument(url: url)
let db = try doc.read(password: password)
document = doc
database = db
masterPassword = password
isLocked = false
}
public func lock() {
masterPassword = ""
database = nil
document = nil
isLocked = true
}
public func save() throws {
guard let db = database, let doc = document, !masterPassword.isEmpty else { return }
try doc.write(db, password: masterPassword)
}
// MARK: Entry CRUD
public func addEntry(_ entry: Entry, toGroupId groupId: UUID) throws {
guard var db = database else { return }
mutateGroup(id: groupId, in: &db.root) { $0.entries.append(entry) }
database = db
try save()
}
public func updateEntry(_ entry: Entry) throws {
guard var db = database else { return }
updateEntryInTree(entry, in: &db.root)
database = db
try save()
}
public func deleteEntry(id entryId: UUID, fromGroupId groupId: UUID) throws {
guard var db = database else { return }
mutateGroup(id: groupId, in: &db.root) { $0.entries.removeAll { $0.id == entryId } }
database = db
try save()
}
// MARK: Search
public func allEntries() -> [Entry] {
guard let db = database else { return [] }
return flatEntries(in: db.root)
}
// MARK: Helpers
private func mutateGroup(id: UUID, in group: inout Group, _ mutation: (inout Group) -> Void) {
if group.id == id {
mutation(&group)
return
}
for i in group.subgroups.indices {
mutateGroup(id: id, in: &group.subgroups[i], mutation)
}
}
private func updateEntryInTree(_ entry: Entry, in group: inout Group) {
if let idx = group.entries.firstIndex(where: { $0.id == entry.id }) {
var updated = entry
updated.modificationDate = Date()
group.entries[idx] = updated
return
}
for i in group.subgroups.indices {
updateEntryInTree(entry, in: &group.subgroups[i])
}
}
private func flatEntries(in group: Group) -> [Entry] {
group.entries + group.subgroups.flatMap { flatEntries(in: $0) }
}
}