OverviewFilesCommitsIssuesDocsReleasesPeers

hypercore-kb / hyper-social / bridge-vault / lib / vault.js

97 lines · javascript ref:
import Corestore from 'corestore'
import Hyperbee from 'hyperbee'
import b4a from 'b4a'
import { encrypt, decrypt, generateKey } from './crypto.js'

class Vault {
  constructor (storage, opts = {}) {
    this._storage = storage
    this._key = opts.key || generateKey()
    this._store = null
    this._db = null
    this._ready = false
  }

  async ready () {
    if (this._ready) return

    this._store = new Corestore(this._storage)
    const core = this._store.get({ name: 'bridge-vault' })
    this._db = new Hyperbee(core, {
      keyEncoding: 'utf-8',
      valueEncoding: 'binary'
    })
    await this._db.ready()
    this._ready = true
  }

  _key4 (platform, name) {
    return `cred/${platform}/${name}`
  }

  async put (platform, name, value) {
    await this.ready()
    const plaintext = b4a.from(JSON.stringify(value))
    const encrypted = encrypt(plaintext, this._key)
    await this._db.put(this._key4(platform, name), encrypted)
  }

  async get (platform, name) {
    await this.ready()
    const node = await this._db.get(this._key4(platform, name))
    if (!node) return null

    const plaintext = decrypt(node.value, this._key)
    return JSON.parse(b4a.toString(plaintext))
  }

  async del (platform, name) {
    await this.ready()
    await this._db.del(this._key4(platform, name))
  }

  async list (platform) {
    await this.ready()
    const prefix = `cred/${platform}/`
    const results = []

    for await (const node of this._db.createReadStream({
      gte: prefix,
      lt: prefix + '\xff'
    })) {
      const name = node.key.slice(prefix.length)
      try {
        const plaintext = decrypt(node.value, this._key)
        results.push({ name, value: JSON.parse(b4a.toString(plaintext)) })
      } catch {
        results.push({ name, value: null, error: 'decrypt failed' })
      }
    }

    return results
  }

  async listPlatforms () {
    await this.ready()
    const platforms = new Set()
    const prefix = 'cred/'

    for await (const node of this._db.createReadStream({
      gte: prefix,
      lt: prefix + '\xff'
    })) {
      const parts = node.key.slice(prefix.length).split('/')
      if (parts.length >= 1) platforms.add(parts[0])
    }

    return [...platforms]
  }

  async close () {
    if (this._store) await this._store.close()
    this._ready = false
  }
}

export { Vault }