ipfsd-ctl
Version:
Spawn IPFS Daemons, JS or Go
192 lines (167 loc) • 4.14 kB
JavaScript
import { multiaddr } from '@multiformats/multiaddr'
import mergeOptions from 'merge-options'
import { repoExists, removeRepo, checkForRunningApi, tmpDir, defaultRepo } from './utils.js'
import { logger } from '@libp2p/logger'
const merge = mergeOptions.bind({ ignoreUndefined: true })
const daemonLog = {
info: logger('ipfsd-ctl:proc:stdout'),
err: logger('ipfsd-ctl:proc:stderr')
}
/**
* @typedef {import('./types').ControllerOptions} ControllerOptions
* @typedef {import('./types').InitOptions} InitOptions
* @typedef {import('@multiformats/multiaddr').Multiaddr} Multiaddr
*/
/**
* Controller for in process nodes
*/
class InProc {
/**
* @param {Required<ControllerOptions>} opts
*/
constructor (opts) {
this.opts = opts
this.path = this.opts.ipfsOptions.repo || (opts.disposable ? tmpDir(opts.type) : defaultRepo(opts.type))
this.initOptions = toInitOptions(opts.ipfsOptions.init)
this.disposable = opts.disposable
this.initialized = false
this.started = false
this.clean = true
/** @type {Multiaddr} */
this.apiAddr // eslint-disable-line no-unused-expressions
this.api = null
/** @type {import('./types').Subprocess | null} */
this.subprocess = null
/** @type {import('./types').PeerData | null} */
this._peerId = null
}
get peer () {
if (this._peerId == null) {
throw new Error('Not started')
}
return this._peerId
}
async setExec () {
if (this.api !== null) {
return
}
const IPFS = this.opts.ipfsModule
this.api = await IPFS.create({
...this.opts.ipfsOptions,
silent: true,
repo: this.path,
init: this.initOptions
})
}
/**
* @private
* @param {string} addr
*/
_setApi (addr) {
this.apiAddr = multiaddr(addr)
this.api = this.opts.ipfsHttpModule.create(addr)
this.api.apiHost = this.apiAddr.nodeAddress().address
this.api.apiPort = this.apiAddr.nodeAddress().port
}
/**
* Initialize a repo.
*
* @param {import('./types').InitOptions} [initOptions={}]
* @returns {Promise<InProc>}
*/
async init (initOptions = {}) {
this.initialized = await repoExists(this.path)
if (this.initialized) {
this.clean = false
return this
}
// Repo not initialized
this.initOptions = merge(
{
emptyRepo: false,
profiles: this.opts.test ? ['test'] : []
},
this.initOptions,
toInitOptions(initOptions)
)
await this.setExec()
this.clean = false
this.initialized = true
return this
}
/**
* Delete the repo that was being used.
* If the node was marked as `disposable` this will be called
* automatically when the process is exited.
*
* @returns {Promise<InProc>}
*/
async cleanup () {
if (!this.clean) {
await removeRepo(this.path)
this.clean = true
}
return this
}
/**
* Start the daemon.
*
* @returns {Promise<InProc>}
*/
async start () {
// Check if a daemon is already running
const api = checkForRunningApi(this.path)
if (api) {
this._setApi(api)
} else {
await this.setExec()
await this.api.start()
}
this.started = true
// Add `peerId`
const id = await this.api.id()
this._peerId = id
daemonLog.info(id)
return this
}
/**
* Stop the daemon.
*
* @returns {Promise<InProc>}
*/
async stop () {
if (!this.started) {
return this
}
await this.api.stop()
this.started = false
if (this.disposable) {
await this.cleanup()
}
return this
}
/**
* Get the pid of the `ipfs daemon` process
*
* @returns {Promise<number>}
*/
pid () {
return Promise.reject(new Error('not implemented'))
}
/**
* Get the version of ipfs
*
* @returns {Promise<string>}
*/
async version () {
await this.setExec()
const { version } = await this.api.version()
return version
}
}
/**
* @param {boolean | InitOptions} [init]
*/
const toInitOptions = (init = {}) =>
typeof init === 'boolean' ? {} : init
export default InProc