diff --git a/db.kdbx b/db.kdbx new file mode 100644 index 0000000..a68839d --- /dev/null +++ b/db.kdbx @@ -0,0 +1,25 @@ +{ + "header": { + "version": { + "major": 0, + "minor": 0 + } + }, + "groups": [ + { + "name": "Racine", + "path": "" + } + ], + "entries": [ + { + "title": "Entry", + "username": "", + "password": "", + "url": "", + "notes": "", + "groupPath": "" + } + ], + "keyFiles": [] +} \ No newline at end of file diff --git a/src/kdbx/store.ts b/src/kdbx/store.ts index bf47835..96622d3 100644 --- a/src/kdbx/store.ts +++ b/src/kdbx/store.ts @@ -7,6 +7,7 @@ export function createEmptySnapshot(): KdbxDatabaseSnapshot { }, groups: [{ name: "Racine", path: "" }], entries: [], + keyFiles: [], }; } @@ -15,6 +16,7 @@ export function cloneSnapshot(snapshot: KdbxDatabaseSnapshot): KdbxDatabaseSnaps header: { ...snapshot.header, version: { ...snapshot.header.version } }, groups: snapshot.groups.map(cloneGroup), entries: snapshot.entries.map(cloneEntry), + keyFiles: snapshot.keyFiles ? [...snapshot.keyFiles] : [], }; } diff --git a/src/kdbx/types.ts b/src/kdbx/types.ts index 2ce8db6..b608726 100644 --- a/src/kdbx/types.ts +++ b/src/kdbx/types.ts @@ -12,6 +12,8 @@ export type KdbxHeader = { streamStartBytes?: Uint8Array; innerRandomStreamId?: number; kdfParameters?: Record; + cipherUuid?: Uint8Array; + publicCustomData?: Record; }; export type KdbxEntry = { @@ -33,4 +35,5 @@ export type KdbxDatabaseSnapshot = { header: KdbxHeader; groups: KdbxGroup[]; entries: KdbxEntry[]; + keyFiles?: string[]; }; diff --git a/src/keepass.ts b/src/keepass.ts index cde46f6..492a47c 100644 --- a/src/keepass.ts +++ b/src/keepass.ts @@ -1,4 +1,4 @@ -import { readFile } from "node:fs/promises"; +import { readFile, writeFile } from "node:fs/promises"; import type { KeePassEntry, KeePassEntryInput, @@ -20,16 +20,15 @@ export class KeePassDatabase { constructor( private path: string, private options: KeePassOpenOptions, - ) { - void path; - void options; - } + ) {} async listEntries(): Promise { + await this.ensureLoaded(); return cloneSnapshot(this.snapshot).entries; } async findEntries(query: KeePassFindQuery): Promise { + await this.ensureLoaded(); return this.snapshot.entries.filter((entry) => { if (query.title && !entry.title.toLowerCase().includes(query.title.toLowerCase())) return false; if (query.username && !entry.username.toLowerCase().includes(query.username.toLowerCase())) return false; @@ -40,10 +39,12 @@ export class KeePassDatabase { } async listGroups(): Promise { + await this.ensureLoaded(); return cloneSnapshot(this.snapshot).groups; } async createEntry(entry: KeePassEntryInput): Promise { + await this.ensureLoaded(); const created: KeePassEntry = { title: entry.title, username: entry.username ?? EMPTY, @@ -59,6 +60,7 @@ export class KeePassDatabase { } async createGroup(group: KeePassGroupInput): Promise { + await this.ensureLoaded(); const path = group.path ? `${group.path}/${group.name}` : group.name; const created = { name: group.name, path }; this.snapshot.groups.push(created); @@ -67,6 +69,8 @@ export class KeePassDatabase { } async save(): Promise { + await this.ensureLoaded(); + await writeFile(this.path, JSON.stringify(this.snapshot, null, 2), "utf8"); this.dirty = false; } @@ -76,7 +80,21 @@ export class KeePassDatabase { private async ensureLoaded(): Promise { if (this.header) return; - this.header = createEmptySnapshot().header; + + try { + const buffer = new Uint8Array(await readFile(this.path)); + const parsed = parseKdbxFile(buffer); + this.header = parsed.header; + this.snapshot = { + header: parsed.header, + groups: [{ name: "Racine", path: "" }], + entries: [], + }; + return; + } catch { + this.snapshot = createEmptySnapshot(); + this.header = this.snapshot.header; + } } }