keeweb / kdbxweb

Web Kdbx library
https://app.keeweb.info
MIT License
409 stars 57 forks source link

Stream to buffer #44

Open mmajcica opened 2 years ago

mmajcica commented 2 years ago

Hi,

we are having some small issues with using the library, maybe it is not directly connected to the library itself but may be an improvement point.

Loading the library expects to receive an ArrayBuffer. However, we are dealing with streams because we are downloading our kbdx file.

Initially, we were doing the following, downloading the file to the disk, then reading it to the buffer. It works well. Following the code used:

    async getDatabase(url: string): Promise<string> {
        const sampleFilePath = Path.join(
            tmpdir(),
            `keepass.${Crypto.randomBytes(6).readUIntLE(0, 6).toString(36)}.kdbx`,
        );

        const file: NodeJS.WritableStream = fs.createWriteStream(sampleFilePath);
        const response = await this.httpClient.get(url);
        const stream = response.message.pipe(file);

        const promise = new Promise((resolve, reject) => {
            stream.on('finish', () => {
                resolve(true);
            });
            stream.on('error', reject);
        });

        await promise;

        return sampleFilePath;
    }

const dbPath = await this.getDatabase(argv.url);
const keepassDb = fs.readFileSync(dbPath).buffer;
const db = await kdbxweb.Kdbx.load(keepassDb, credentials);

Now we are not allowed anymore to persist things on a disk and we need to manage all of the things in memory. That is not a big issue as our DB file is only around 200kb and it will not grow.

We changed the code to the following:

    async getDatabase(url: string): Promise<Buffer> {
        const response: IHttpClientResponse = await this.httpClient.get(url);

        return new Promise<Buffer>(async (resolve, reject) => {
            const chunks = [];
            response.message
                .on('data', function (data: Buffer) {
                    chunks.push(data);
                })
                .on('end', async function () {
                    const buffer: Buffer = Buffer.concat(chunks);

                    resolve(buffer);
                })
                .on('error', function (err) {
                    reject(err);
                });
        });
    }

const dbBuffer = await this.getDatabase(argv.url);
const db = await kdbxweb.Kdbx.load(dbBuffer, credentials);

However, this does not work, we do get an exception saying KdbxError: Error InvalidArg: data.

Do you have any idea why this may not work? Is there an example of how to get a stream into the buffer that will be accepted by kdbxweb?

Would it be possible to have a possibility to load a DB direclty from a stream?

Thank you in advance.