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 }