feat: add VaultSession (vault lifecycle and entry CRUD)
This commit is contained in:
@@ -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) }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user