'use strict'
const multiaddr = require('multiaddr')
const getConfig = require('./config')
const getRequestAPI = require('./request-api')
const Wreck = require('wreck')
const ndjson = require('ndjson')
module.exports = IpfsAPI
/**
* Create a ipfs api
* @constructor
* @param {string} host_or_multiaddr
* @param {number} port
*/
function IpfsAPI (host_or_multiaddr, port) {
const self = this
const config = getConfig()
if (!(self instanceof IpfsAPI)) {
return new IpfsAPI(host_or_multiaddr, port)
}
try {
const maddr = multiaddr(host_or_multiaddr).nodeAddress()
config.host = maddr.address
config.port = maddr.port
} catch (e) {
config.host = host_or_multiaddr
config.port = port || config.port
}
// autoconfigure in browser
if (!config.host &&
typeof window !== 'undefined') {
const split = window.location.host.split(':')
config.host = split[0]
config.port = split[1]
}
const requestAPI = getRequestAPI(config)
/** @private */
function command (name) {
return (opts, cb) => {
if (typeof (opts) === 'function') {
cb = opts
opts = {}
}
return requestAPI(name, null, opts, null, cb)
}
}
/** @private */
function argCommand (name) {
return (arg, opts, cb) => {
if (typeof (opts) === 'function') {
cb = opts
opts = {}
}
return requestAPI(name, arg, opts, null, cb)
}
}
// -- Interface
/** @public */
self.send = requestAPI
/**
* Add a file/many files to IPFS returning the hash and name. The
* name value will only be set if you are actually sending a file.
*
* @public
* @param {} files
* @param {object} opts
* @param {function} cb
*/
this.add = (files, opts, cb) => {
if (typeof (opts) === 'function' && cb === undefined) {
cb = opts
opts = {}
}
if (typeof files === 'string' && files.startsWith('http')) {
Wreck.request('GET', files, null, (err, res) => {
if (err) return cb(err)
requestAPI('add', null, opts, res, cb)
})
return
}
requestAPI('add', null, opts, files, cb)
}
self.cat = argCommand('cat')
self.ls = argCommand('ls')
self.config = {
get: argCommand('config'),
set (key, value, opts, cb) {
if (typeof (opts) === 'function') {
cb = opts
opts = {}
}
return requestAPI('config', [key, value], opts, null, cb)
},
show (cb) {
return requestAPI('config/show', null, null, null, true, cb)
},
replace (file, cb) {
return requestAPI('config/replace', null, null, file, cb)
}
}
self.update = {
apply: command('update'),
check: command('update/check'),
log: command('update/log')
}
self.version = command('version')
self.commands = command('commands')
self.mount = (ipfs, ipns, cb) => {
if (typeof ipfs === 'function') {
cb = ipfs
ipfs = null
} else if (typeof ipns === 'function') {
cb = ipns
ipns = null
}
const opts = {}
if (ipfs) opts.f = ipfs
if (ipns) opts.n = ipns
return requestAPI('mount', null, opts, null, cb)
}
self.diag = {
net: command('diag/net'),
sys: command('diag/sys')
}
self.block = {
get: argCommand('block/get'),
put (file, cb) {
if (Array.isArray(file)) {
return cb(null, new Error('block.put() only accepts 1 file'))
}
return requestAPI('block/put', null, null, file, cb)
}
}
self.object = {
get: argCommand('object/get'),
put (file, encoding, cb) {
if (typeof encoding === 'function') {
return cb(null, new Error("Must specify an object encoding ('json' or 'protobuf')"))
}
return requestAPI('object/put', encoding, null, file, cb)
},
data: argCommand('object/data'),
stat: argCommand('object/stat'),
links: argCommand('object/links'),
patch (file, opts, cb) {
return requestAPI('object/patch', [file].concat(opts), null, null, cb)
}
}
self.swarm = {
peers: command('swarm/peers'),
connect: argCommand('swarm/connect')
}
self.ping = (id, cb) => {
return requestAPI('ping', id, { n: 1 }, null, function (err, res) {
if (err) return cb(err, null)
cb(null, res[1])
})
}
self.id = (id, cb) => {
if (typeof id === 'function') {
cb = id
id = null
}
return requestAPI('id', id, null, null, cb)
}
self.pin = {
add (hash, opts, cb) {
if (typeof opts === 'function') {
cb = opts
opts = null
}
requestAPI('pin/add', hash, opts, null, cb)
},
remove (hash, opts, cb) {
if (typeof opts === 'function') {
cb = opts
opts = null
}
requestAPI('pin/rm', hash, opts, null, cb)
},
list (type, cb) {
if (typeof type === 'function') {
cb = type
type = null
}
let opts = null
if (type) opts = { type: type }
return requestAPI('pin/ls', null, opts, null, cb)
}
}
self.log = {
tail (cb) {
requestAPI('log/tail', null, {}, null, false, (err, res) => {
if (err) return cb(err)
cb(null, res.pipe(ndjson.parse()))
})
}
}
self.name = {
publish: argCommand('name/publish'),
resolve: argCommand('name/resolve')
}
self.Buffer = Buffer
self.refs = argCommand('refs')
self.refs.local = command('refs/local')
self.dht = {
findprovs: argCommand('dht/findprovs'),
get (key, opts, cb) {
if (typeof (opts) === 'function' && !cb) {
cb = opts
opts = null
}
return requestAPI('dht/get', key, opts, null, (err, res) => {
if (err) return cb(err)
if (!res) return cb(new Error('empty response'))
if (res.length === 0) return cb(new Error('no value returned for key'))
// Inconsistent return values in the browser vs node
if (Array.isArray(res)) {
res = res[0]
}
if (res.Type === 5) {
cb(null, res.Extra)
} else {
cb(res)
}
})
},
put (key, value, opts, cb) {
if (typeof (opts) === 'function' && !cb) {
cb = opts
opts = null
}
return requestAPI('dht/put', [key, value], opts, null, cb)
}
}
}