bitdrive-cli
Version:
A Bitspace service that for managing Bitdrives over FUSE.
182 lines (161 loc) • 5.43 kB
JavaScript
const p = require('path')
const isOptions = require('is-options')
const bitdrive = require('@web4/bitdrive')
const BitspaceClient = require('bitspace/client')
const { loadConfig } = require('./lib/config')
const importDirectory = require('./lib/cli/import')
const exportDrive = require('./lib/cli/export')
module.exports = class BitdriveServiceClient {
constructor (opts = {}) {
this.key = opts.key
this.mnt = opts.mnt
this.bitspaceClient = opts.client || new BitspaceClient(opts)
this._store = null
this._rootDrive = null
}
async ready () {
if (this._rootDrive) return
if (!this.key || !this.mnt) {
const config = await loadConfig()
if (config.rootDriveKey) this.key = Buffer.from(config.rootDriveKey, 'hex')
if (config.mnt) this.mnt = config.mnt
}
if (!this.key) throw new Error('BitdriveServiceClient was not given a root drive key.')
if (!this.mnt) throw new Error('BitdriveServiceClient was not given a root mountpoint.')
this._store = this.bitspaceClient.chainstore()
this._rootDrive = await this._createDrive({ key: this.key })
}
_resolvePath (path) {
const fullPath = p.resolve(path)
if (!fullPath.startsWith(this.mnt)) throw new Error('Can only create a mount within the root drive.')
return fullPath.slice(this.mnt.length)
}
async _createDrive (opts = {}) {
var drive = bitdrive(this._store, opts && opts.key, {
...opts,
extension: false
}).promises
await drive.ready()
return drive
}
async mount (path, opts = {}) {
await this.ready()
const drive = await this._createDrive({ key: opts.key })
await this._rootDrive.mount(this._resolvePath(path), drive.key, { ...opts, key: null })
return drive
}
async unmount (path) {
await this.ready()
return this._rootDrive.unmount(this._resolvePath(path))
}
async info (path) {
await this.ready()
return new Promise((resolve, reject) => {
const noopFilePath = '__does_not_exist'
const noopPath = p.join(this._resolvePath(path), noopFilePath)
this._rootDrive.drive.stat(noopPath, { trie: true }, (err, stat, trie, _, __, mountPath) => {
if (err && err.errno !== 2) return reject(err)
if (err && !trie) return resolve(null)
this.bitspaceClient.network.status(trie.feed.discoveryKey, (err, networkConfig) => {
if (err) return reject(err)
return resolve({
key: trie.key,
discoveryKey: trie.feed.discoveryKey,
writable: trie.feed.writable,
mountPath: mountPath.slice(0, mountPath.length - noopFilePath.length),
announce: !!(networkConfig && networkConfig.announce),
lookup: !!(networkConfig && networkConfig.lookup)
})
})
})
})
}
async _driveFromPath (path, opts = {}) {
var driveKey = null
if (path) {
const info = await this.info(path)
if (!info) return null
driveKey = info.key
} else {
driveKey = opts.key
}
return this._createDrive({ key: driveKey })
}
async stats (path, opts = {}) {
if (isOptions(path)) {
opts = path
path = null
}
const drive = await this._driveFromPath(path, opts)
const allMounts = await drive.getAllMounts({ memory: true })
const network = {}
for (const [mountpoint, { metadata, content }] of allMounts) {
network[mountpoint] = {
metadata: chainStats(metadata),
content: chainStats(content)
}
}
const storageObj = {}
if (opts.storage) {
const stats = await drive.stats('/', opts)
for (const [k, v] of stats) {
storageObj[k] = v
}
}
await drive.close()
return {
storage: storageObj,
network
}
function chainStats (chain) {
return {
key: chain.key.toString('hex'),
discoveryKey: chain.discoveryKey.toString('hex'),
writable: chain.writable,
length: chain.length,
byteLength: chain.byteLength,
peers: chain.peers.map(mapPeer)
}
}
function mapPeer (p) {
return {
...p,
remotePublicKey: p.remotePublicKey.toString('hex')
}
}
}
async seed (path, opts = {}) {
if (isOptions(path)) {
opts = path
path = null
}
var discoveryKey = opts.discoveryKey
if (discoveryKey && !Buffer.isBuffer(discoveryKey)) discoveryKey = Buffer.from(discoveryKey, 'hex')
if (!discoveryKey) {
const drive = await this._driveFromPath(path, opts)
discoveryKey = drive.discoveryKey
await drive.close()
}
return this.bitspaceClient.network.configure(discoveryKey, { ...opts, announce: true, lookup: true })
}
async unseed (path, opts = {}) {
if (isOptions(path)) {
opts = path
path = null
}
var discoveryKey = opts.discoveryKey
if (discoveryKey && !Buffer.isBuffer(discoveryKey)) discoveryKey = Buffer.from(discoveryKey, 'hex')
if (!discoveryKey) {
const drive = await this._driveFromPath(path, opts)
discoveryKey = drive.discoveryKey
await drive.close()
}
return this.bitspaceClient.network.configure(discoveryKey, { ...opts, announce: false, lookup: false })
}
async import (key, dir, opts) {
return importDirectory(this, key, dir, opts)
}
async export (key, dir, opts) {
return exportDrive(this, key, dir, opts)
}
}