UNPKG

caver-js

Version:

caver-js is a JavaScript API library that allows developers to interact with a Kaia node

204 lines (186 loc) 8.39 kB
/* eslint-disable class-methods-use-this */ /* Copyright 2020 The caver-js Authors This file is part of the caver-js library. The caver-js library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The caver-js library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the caver-js. If not, see <http://www.gnu.org/licenses/>. */ const lodash = require('lodash') const fs = require('fs') const ipfsClient = require('ipfs-http-client') const { CID } = require('multiformats/cid') const _ = require('lodash') /** * Representing a class for uploading and loading files to IPFS. * @hideconstructor * @class */ class IPFS { /** * Create an IPFS instance. * @param {string} [host] The IPFS Node url to connect with. * @param {number} [port] The port number to use. * @param {boolean} [ssl] With or without SSL. If true, the https protocol is used. Otherwise, the http protocol is used. * @param {object} [options] An object contains configuration variables. */ constructor(host, port, ssl, options) { if (host !== undefined && port !== undefined && ssl !== undefined && options !== undefined) { this.setIPFSNode(host, port, ssl, options) } } /** * Initializes a connection with an IPFS Node. * When an IPFS Node information is set through this function, you can upload files to IPFS or load files from IPFS. * * @example * caver.ipfs.setIPFSNode('localhost', 5001, false) * caver.ipfs.setIPFSNode('localhost', 5001, false, { ... }) * caver.ipfs.setIPFSNode('localhost', 5001, false, caver.ipfs.createOptions({projectId, projectSecret})) * * @param {string} host The IPFS Node url to connect with. * @param {number} port The port number to use. * @param {boolean} ssl With or without SSL. If true, the https protocol is used. Otherwise, the http protocol is used. * @param {object} [options] An object contains configuration variables. * @return {void} */ setIPFSNode(host, port, ssl, options = {}) { const protocol = ssl ? 'https' : 'http' const createObject = { host, port, protocol } if (options.headers) { // Copy the headers object createObject.headers = JSON.parse(JSON.stringify(options.headers)) } this.ipfs = ipfsClient(createObject) } /** * Makes an options object to use with ipfs request * * @example * // You can create an option object with projectId and projectSecret for Infura IPFS Node * const options = caver.ipfs.createOptions({projectId, projectSecret}) * * @param {object} opts An object that contains various configuration variables. * @return {object} */ createOptions(opts) { if (!opts || !_.isObject(opts)) { throw new Error(`Invalid parameter. Please send an object `) } const options = {} if (opts.projectId && opts.projectSecret) { const auth = `Basic ${Buffer.from(`${opts.projectId}:${opts.projectSecret}`).toString('base64')}` options.headers = options.headers ? options.headers : {} options.headers.authorization = auth } return options } /** * Adds a file to IPFS. The {@link https://docs.ipfs.io/concepts/content-addressing/#content-addressing-and-cids|CID(Content Identifier)} of the uploaded file is returned. * If the path of a file is passed, the contents of the file are loaded from the path and uploaded to IPFS. If a buffer is passed, it is uploaded to IPFS directly. * * If the `data` parameter is a `Buffer` or `ArrayBuffer`, upload to IPFS directly without using `fs`. * If the `data` parameter is a string, use `fs` to read the file. * Since `fs` is a module that can only be used on the server side, if it is client-side code, * it must read the file in advance and pass the file contents in the format of `ArrayBuffer`. * * If you get a "Error: Can't resolve 'fs'" error when building your client code, add the following to your "webpack.config.json" file. * ``` * module.exports = { * ... * node: { * fs: 'empty', * }, * ... * } * ``` * * If you use Next.js web framework(https://nextjs.org/), add the following to your "next.config.json" file. * ``` * module.exports = { * ... * webpack: (config, { isServer }) => { * // Fixes npm packages that depend on `fs` module * if (!isServer) { * config.node = { * fs: 'empty' * } * } * return config * }, * ... * } * ``` * * @example * const cid = await caver.ipfs.add('./test.txt') * const cid = await caver.ipfs.add(Buffer.from('test data')) * * @param {string|Buffer|ArrayBuffer} data The file path string or file contents. * @return {Promise<string>} */ async add(data) { if (!this.ipfs) throw new Error(`Please set IPFS Node through 'caver.ipfs.setIPFSNode'.`) // Read file if (lodash.isString(data)) { if (typeof window !== 'undefined') throw new Error(`Cannot use fs module: Please pass the file contents as a parameter of type Buffer or ArrayBuffer.`) data = fs.readFileSync(data) } if (!lodash.isBuffer(data) && !lodash.isArrayBuffer(data)) throw new Error(`Invalid data: ${data}`) const ret = await this.ipfs.add(Buffer.from(data)) return ret.path } /** * Returns a file addressed by a valid IPFS path. * * @example * const fileContents = await caver.ipfs.get('Qmd9thymMS6mejhEDZfwXPowSDunzgma9ex4ezpCSRZGwC') * * @param {string} hash An {@link https://docs.ipfs.io/concepts/content-addressing/#content-addressing-and-cids|CID(Content Identifier)} of the file to download. * @return {Promise<Buffer>} */ async get(hash) { if (!this.ipfs) throw new Error(`Please set IPFS Node through 'caver.ipfs.setIPFSNode'.`) const ret = await this.ipfs.cat(hash) return (await ret.next(0)).value } /** * Converts a {@link https://docs.ipfs.io/concepts/content-addressing/#content-addressing-and-cids|CID(Content Identifier)} to a {@link https://multiformats.io/multihash/|Multihash}. * * @example * // This will return '0x1220dc1dbe0bcf1e5f6cce80bd3d7e7d873801c5a1732add889c0f25391d53470dc3' * const multihash = caver.ipfs.toHex('Qmd9thymMS6mejhEDZfwXPowSDunzgma9ex4ezpCSRZGwC') * * @param {string} hash A {@link https://docs.ipfs.io/concepts/content-addressing/#content-addressing-and-cids|CID(Content Identifier)} to convert. * @return {string} */ toHex(hash) { const cid = CID.parse(hash) return `0x${Buffer.from(cid.toJSON().hash).toString('hex')}` } /** * Converts to {@link https://docs.ipfs.io/concepts/content-addressing/#content-addressing-and-cids|CID(Content Identifier)} from a {@link https://multiformats.io/multihash/|Multihash}. * * @example * // This will return 'Qmd9thymMS6mejhEDZfwXPowSDunzgma9ex4ezpCSRZGwC' * const multihash = caver.ipfs.fromHex('0x1220dc1dbe0bcf1e5f6cce80bd3d7e7d873801c5a1732add889c0f25391d53470dc3') * * @param {string} hash A {@link https://multiformats.io/multihash/|Multihash} to convert. * @return {string} */ fromHex(contentHash) { const hex = contentHash.substring(2) // convert hex string to Uint8Array type const buf = Uint8Array.from(hex.match(/.{1,2}/g).map(byte => parseInt(byte, 16))) return CID.decode(buf).toString() } } module.exports = IPFS