-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Feat/expose ipfs utils #784
Changes from 3 commits
75f140a
144f59b
489db45
788ed81
02dce62
6af5061
bb5fae1
aa21ed1
fea0d52
ebb0717
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -85,6 +85,9 @@ You can check the development status at: | |
- [HTTP-API](#http-api) | ||
- [IPFS Core (use IPFS as a module in Node.js or in the Browser)](#ipfs-core-examples-use-ipfs-as-a-module) | ||
- [Create a IPFS node instance](#create-a-ipfs-node-instance) | ||
- [Add a file](#add-a-file) | ||
- [Retrieve a file](#retrieve-a-file) | ||
- [More to come](#more-to-come) | ||
- [Tutorials and Examples](#tutorials-and-examples) | ||
- [API](#api) | ||
- [Generic API](#generic-api) | ||
|
@@ -94,6 +97,7 @@ You can check the development status at: | |
- [Files API](#files-api) | ||
- [Swarm API](#swarm-api) | ||
- [libp2p API](#libp2p-api) | ||
- [Domain data types](#domain-data-types) | ||
- [Packages](#packages) | ||
- [Development](#development) | ||
- [Clone](#clone) | ||
|
@@ -193,6 +197,8 @@ The HTTP-API exposed by the js-ipfs daemon follows the [`http-api-spec`](https:/ | |
|
||
#### Create a IPFS node instance | ||
|
||
The basic startup flow involves (optionally) creating a Repo, creating an IPFS node, `init`-ing it so it can generate its keys, `load`-ing its configuration, and putting it online with `goOnline`. Here is a structural example: | ||
|
||
```JavaScript | ||
// IPFS will need a repo, it can create one for you or you can pass | ||
// it a repo instance of the type IPFS Repo | ||
|
@@ -229,6 +235,42 @@ node.init({ emptyRepo: true, bits: 2048 }, (err) => { | |
|
||
> We are working on making this init process better, see https://github.com/ipfs/js-ipfs/issues/556 for the discussion. | ||
|
||
Below are some more examples of JavaScript IPFS in action. | ||
|
||
#### Add a file | ||
|
||
Once you have an IPFS node up and running, you can add files to it from `Buffer`s, `Readable` streams, or [arrays of objects of a certain form](https://github.com/ipfs/interface-ipfs-core/tree/master/API/files#add). If you don't have `Buffer` conveniently available (say, because you're in a browser without the Node API handy), it's available as a property of the IPFS node. | ||
|
||
```javascript | ||
// Add a single file | ||
node.files.add(node.Buffer.from('Hello world'), (err, returned) => { | ||
if (err) { | ||
throw err | ||
} | ||
console.log('IPFS hash: ', returned[0].hash) | ||
}) | ||
``` | ||
|
||
#### Retrieve a file | ||
|
||
To retrieve the contents of a file, you can use the [cat method](https://github.com/ipfs/interface-ipfs-core/tree/master/API/files#cat), which will call your callback with a Node.js-style `Readable` stream. | ||
|
||
```javascript | ||
node.files.cat('QmNRCQWfgze6AbBCaT1rkrkV5tJ2aP4oTNPb5JZcXYywve', | ||
(err, content_stream) => { | ||
if (err) { | ||
throw err | ||
} | ||
content_stream.on('data', (buffer) => { | ||
console.log('File contents:', buffer.toString('ascii')) | ||
}) | ||
}) | ||
``` | ||
|
||
#### More to come | ||
|
||
> If you have built an example, please share it with the community by submitting a Pull Request to this repo!. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's remove this line, we don't necessarily want to have thousands of examples, instead we want to have a selected few that cover it all |
||
|
||
### [Tutorials and Examples](/examples) | ||
|
||
You can find some examples and tutorials in the [examples](/examples) folder, these exist to help you get started using `js-ipfs`. | ||
|
@@ -252,6 +294,16 @@ Every IPFS instance also exposes the libp2p API at `ipfs.libp2p`. The formal int | |
- [libp2p-ipfs-nodejs](https://github.com/ipfs/js-libp2p-ipfs-nodejs) | ||
- [libp2p-ipfs-browser](https://github.com/ipfs/js-libp2p-ipfs-browser) | ||
|
||
#### Domain data types | ||
|
||
A set of data types are exposed directly from the IPFS instance under `ipfs.types`. That way you're not required to import/require the following. | ||
|
||
* `ipfs.types.Buffer` | ||
* [`ipfs.types.PeerId`](https://github.com/libp2p/js-peer-id) | ||
* [`ipfs.types.PeerInfo`](https://github.com/libp2p/js-peer-info) | ||
* [`ipfs.types.multiaddr`](https://github.com/multiformats/js-multiaddr) | ||
* [`ipfs.types.multihash`](https://github.com/multiformats/js-multihash) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👌🏽 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also add CID https://github.com/ipld/js-cid |
||
|
||
## Packages | ||
|
||
| Package | Version | Deps | DevDeps | | ||
|
@@ -287,8 +339,6 @@ Every IPFS instance also exposes the libp2p API at `ipfs.libp2p`. The formal int | |
| [`multihashing`](//github.com/multiformats/js-multihashing) | [](//github.com/multiformats/js-multihashing/releases) | [](https://david-dm.org/multiformats/js-multihashing) | [](https://david-dm.org/multiformats/js-multihashing?type=dev) | | ||
| [`mafmt`](//github.com/whyrusleeping/js-mafmt) | [](//github.com/whyrusleeping/js-mafmt/releases) | [](https://david-dm.org/whyrusleeping/js-mafmt) | [](https://david-dm.org/whyrusleeping/js-mafmt?type=dev) | | ||
|
||
|
||
|
||
## Development | ||
|
||
### Clone and install dependencies | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# Use IPFS in the browser with `<script>` tags | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. call the folder 'browser-script-tag' |
||
|
||
You can use IPFS in your in-browser JavaScript code with just a `<script>` tag. | ||
|
||
``` | ||
<script src="https://unpkg.com/ipfs/dist/index.min.js"></script> | ||
``` | ||
|
||
This exposes a global `Ipfs`; you can get a node by making a `new Ipfs()`. | ||
|
||
See `index.html` for a working example. | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
<!doctype html> | ||
<html> | ||
<head> | ||
<title>IPFS in the Browser</title> | ||
<script src="https://unpkg.com/ipfs/dist/index.min.js"></script> | ||
<script type="text/javascript"> | ||
// Set this if you have a libp2p-webrtc-star server | ||
// Something like | ||
// const SIGNALING_SERVER = '/libp2p-webrtc-star/ip4/127.0.0.1/tcp/9090/ws/ipfs/' | ||
const SIGNALING_SERVER = null | ||
|
||
// Make an IPFS node | ||
var ipfs = new Ipfs() | ||
|
||
// Init a repo in the given IPFS node it if hasn't got one already. Calls the | ||
// setup callback, passing the normal callback, after first initialization. | ||
// Calls the normal callback directly after subsequent initializations. Calls | ||
// the normal callback with an error parameter if there is an error. | ||
function initIfNeeded (ipfs, setup, callback) { | ||
ipfs.init((err) => { | ||
if (!err) { | ||
// This is the first time we have started a node | ||
setup(callback) | ||
} else if (err.message == 'repo already exists') { | ||
// The node already exists | ||
callback() | ||
} else { | ||
callback(err) | ||
} | ||
}) | ||
} | ||
|
||
// Init the node | ||
initIfNeeded(ipfs, (callback) => { | ||
// On first initialization, do some setup | ||
// Get the node config we just init-ed | ||
ipfs.config.get((err, config) => { | ||
if (err) { | ||
throw err | ||
} | ||
if (SIGNALING_SERVER) { | ||
// Add at least one libp2p-webrtc-star address. Without an address like this | ||
// the libp2p-webrtc-star transport won't be installed, and the resulting | ||
// node won't be able to dial out to libp2p-webrtc-star addresses. | ||
var star_addr = (SIGNALING_SERVER + config.Identity.PeerID) | ||
ipfs.config.set('Addresses.Swarm[1]', star_addr, (err) => { | ||
if (err) { | ||
throw err | ||
} | ||
// Continue down the already-initialized code path | ||
callback() | ||
}) | ||
} else { | ||
// No signaling server is known. Just cointinue without it. | ||
// We don't want to spam the console in our demo | ||
callback() | ||
} | ||
}) | ||
}, (err) => { | ||
// If the repo was already initialized, or after the first-time initialization | ||
// code is run, we'll do this. | ||
if (err) { | ||
throw err | ||
} | ||
// Have the node set itself up | ||
ipfs.load(() => { | ||
// Go online and connect to things | ||
ipfs.goOnline(() => { | ||
console.log('Online status: ', ipfs.isOnline() ? 'online' : 'offline') | ||
document.getElementById("status").innerHTML= 'Node status: ' + (ipfs.isOnline() ? 'online' : 'offline') | ||
// TODO: Write your code here! | ||
// Use methods like ipfs.files.add, ipfs.files.get, and so on in here | ||
// Methods requiring buffers can use ipfs.Buffer | ||
}) | ||
}) | ||
}) | ||
</script> | ||
</head> | ||
<body> | ||
<h1>IPFS in the Browser</h1> | ||
<p>This page creates an IPFS node in your browser and drops it into the global Javascript namespace as <em>ipfs</em>. Open the console to play around with it.</p> | ||
<p>Note that opening two tabs of this page in the same browser won't work well, because they will share node configuration. You'll end up trying to run two instances of the same node, with the same private key and identity, which is a Bad Idea.</p> | ||
<div id="status">Node status: offline</div> | ||
</body> | ||
</html> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# Robust Initialization and libp2p-webrtc-star Signaling | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's call this example 'how-to-use-webrtc-star' |
||
|
||
There's still a bit of work required to start up an in-browser node in a robust way, so that it will work whether or not there is an existing initialized IPFS repo in the user's browser. If there isn't one, you need to call `init` as above, but if there is one, calling `init` will fail. Moreover, there's currently no good way to check if you need to call `init` or not. | ||
|
||
Also, an in-browser node isn't able to call up normal IPFS nodes over raw TCP; it can only communicate over Websockets and WebRTC. Currently, there are no Websockets or WebRTC bootstrap nodes run by the IPFS maintainers. You will probably want to set up a [libp2p-webrtc-star signaling server](https://github.com/libp2p/js-libp2p-webrtc-star) so nodes used in your application can find each other: | ||
|
||
```bash | ||
npm i libp2p-webrtc-star -g | ||
star-sig | ||
``` | ||
|
||
You will then want to point IPFS nodes used in your application at your signaling server, so they can connect to each other. This is accomplished by adding an address to the node's configuration referencing the signaling server, of the form `/libp2p-webrtc-star/ip4/<server-ip>/tcp/<server-port>/ws/ipfs/<peer-id>`, where `<peer-id>` is the peer ID of the node that the address is being added to. This causes the node to think of itself as being contactable through the signaling server. It will then initializes its libp2p-webrtc-star implementation and automatically peer with other nodes using the same server. | ||
|
||
The `index.html` page in this directory is an example which initializes an IPFS node in a browser safely, whether a node has already been initialized by the current domain or not. It also configures `libp2p-webrtc-star` communication, using a signaling server running on the local host. (Note that since IPFS node configuration information is stored in IndexedDB in browsers, opening two tabs of this code from a local file in the same browser won't work, because they'll share the same node keys and identity. Either run the code from multiple domains, or run it in two different browsers, like Chrome and Firefox.) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
<!doctype html> | ||
<html> | ||
<head> | ||
<title>IPFS in the Browser with WebRTC</title> | ||
<script src="https://unpkg.com/ipfs/dist/index.min.js"></script> | ||
<script type="text/javascript"> | ||
// Set this if you have a libp2p-webrtc-star server | ||
// Something like | ||
const SIGNALING_SERVER = '/libp2p-webrtc-star/ip4/127.0.0.1/tcp/9090/ws/ipfs/' | ||
|
||
// Make an IPFS node | ||
var ipfs = new Ipfs("webrtc-demo") | ||
|
||
// Init a repo in the given IPFS node it if hasn't got one already. Calls the | ||
// setup callback, passing the normal callback, after first initialization. | ||
// Calls the normal callback directly after subsequent initializations. Calls | ||
// the normal callback with an error parameter if there is an error. | ||
function initIfNeeded (ipfs, setup, callback) { | ||
ipfs.init((err) => { | ||
if (!err) { | ||
// This is the first time we have started a node | ||
setup(callback) | ||
} else if (err.message == 'repo already exists') { | ||
// The node already exists | ||
callback() | ||
} else { | ||
callback(err) | ||
} | ||
}) | ||
} | ||
|
||
function showPeers () { | ||
ipfs.swarm.peers(function (err, peerInfos) { | ||
document.getElementById("peers").innerHTML= '' | ||
for (var i = 0; i < peerInfos.length; i++) { | ||
document.getElementById("peers").innerHTML += peerInfos[i].addr + '<br/>' | ||
} | ||
|
||
setTimeout(showPeers, 1000) | ||
}) | ||
} | ||
|
||
// Init the node | ||
initIfNeeded(ipfs, (callback) => { | ||
// On first initialization, do some setup | ||
// Get the node config we just init-ed | ||
ipfs.config.get((err, config) => { | ||
if (err) { | ||
throw err | ||
} | ||
if (SIGNALING_SERVER) { | ||
// Add at least one libp2p-webrtc-star address. Without an address like this | ||
// the libp2p-webrtc-star transport won't be installed, and the resulting | ||
// node won't be able to dial out to libp2p-webrtc-star addresses. | ||
var star_addr = (SIGNALING_SERVER + config.Identity.PeerID) | ||
ipfs.config.set('Addresses.Swarm[1]', star_addr, (err) => { | ||
if (err) { | ||
throw err | ||
} | ||
// Continue down the already-initialized code path | ||
callback() | ||
}) | ||
} else { | ||
// No signaling server is known. Just cointinue without it. | ||
// We don't want to spam the console in our demo | ||
callback() | ||
} | ||
}) | ||
}, (err) => { | ||
// If the repo was already initialized, or after the first-time initialization | ||
// code is run, we'll do this. | ||
if (err) { | ||
throw err | ||
} | ||
// Have the node set itself up | ||
ipfs.load(() => { | ||
// Go online and connect to things | ||
ipfs.goOnline(() => { | ||
console.log('Online status: ', ipfs.isOnline() ? 'online' : 'offline') | ||
document.getElementById("status").innerHTML= 'Node status: ' + (ipfs.isOnline() ? 'online' : 'offline') | ||
setTimeout(showPeers, 1000) | ||
// TODO: Write your code here! | ||
// Use methods like ipfs.files.add, ipfs.files.get, and so on in here | ||
// Methods requiring buffers can use ipfs.Buffer | ||
}) | ||
}) | ||
}) | ||
</script> | ||
</head> | ||
<body> | ||
<h1>IPFS in the Browser with WebRTC</h1> | ||
<p>This page creates an IPFS node in your browser and drops it into the global Javascript namespace as <em>ipfs</em>. Open the console to play around with it.</p> | ||
<p>Note that opening two tabs of this page in the same browser won't work well, because they will share node configuration. You'll end up trying to run two instances of the same node, with the same private key and identity, which is a Bad Idea.</p> | ||
<div id="status">Node status: offline</div> | ||
<p>Run a libp2p-webrtc-star signaling server! Peers detected through the server will be displayed below:</p> | ||
<p>Peers:</p> | ||
<div id="peers"></div> | ||
</body> | ||
</html> |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,10 @@ | |
|
||
const BlockService = require('ipfs-block-service') | ||
const IPLDResolver = require('ipld-resolver') | ||
const PeerId = require('peer-id') | ||
const PeerInfo = require('peer-info') | ||
const multiaddr = require('multiaddr') | ||
const multihash = require('multihashes') | ||
const PeerBook = require('peer-book') | ||
const debug = require('debug') | ||
|
||
|
@@ -24,7 +28,11 @@ class IPFS { | |
|
||
// IPFS utils | ||
this.types = { | ||
Buffer: Buffer | ||
Buffer, | ||
PeerId, | ||
PeerInfo, | ||
multiaddr, | ||
multihash | ||
} | ||
this.log = debug('jsipfs') | ||
this.log.err = debug('jsipfs:err') | ||
|
@@ -63,6 +71,9 @@ class IPFS { | |
this.ping = components.ping(this) | ||
this.pubsub = components.pubsub(this) | ||
|
||
// expose Buffer for browser applications | ||
this.Buffer = Buffer | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we still need this @diasdavid? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we can take it it, since we know have it under types. |
||
|
||
if (configOpts.EXPERIMENTAL.pubsub) { | ||
this.log('EXPERIMENTAL pubsub is enabled') | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this is well demonstrated in the examples folder, we can trim the README by just pointing to those examples.