From 89a1cf729dfbb8b14c105654be413bff0b7b9cf5 Mon Sep 17 00:00:00 2001
From: DougA
Date: Sun, 12 Jul 2020 12:34:52 -0400
Subject: [PATCH 01/22] ipns publish example
---
.../examples/browser-ipns-publish/.gitignore | 2 +
.../examples/browser-ipns-publish/README.md | 83 ++++++
.../examples/browser-ipns-publish/index.html | 198 +++++++++++++
.../examples/browser-ipns-publish/index.js | 263 ++++++++++++++++++
.../browser-ipns-publish/package.json | 34 +++
.../examples/browser-ipns-publish/util.js | 31 +++
6 files changed, 611 insertions(+)
create mode 100644 packages/ipfs-http-client/examples/browser-ipns-publish/.gitignore
create mode 100644 packages/ipfs-http-client/examples/browser-ipns-publish/README.md
create mode 100644 packages/ipfs-http-client/examples/browser-ipns-publish/index.html
create mode 100644 packages/ipfs-http-client/examples/browser-ipns-publish/index.js
create mode 100644 packages/ipfs-http-client/examples/browser-ipns-publish/package.json
create mode 100644 packages/ipfs-http-client/examples/browser-ipns-publish/util.js
diff --git a/packages/ipfs-http-client/examples/browser-ipns-publish/.gitignore b/packages/ipfs-http-client/examples/browser-ipns-publish/.gitignore
new file mode 100644
index 0000000000..4a94694d21
--- /dev/null
+++ b/packages/ipfs-http-client/examples/browser-ipns-publish/.gitignore
@@ -0,0 +1,2 @@
+bundle.js
+.cache
\ No newline at end of file
diff --git a/packages/ipfs-http-client/examples/browser-ipns-publish/README.md b/packages/ipfs-http-client/examples/browser-ipns-publish/README.md
new file mode 100644
index 0000000000..25d9b0532c
--- /dev/null
+++ b/packages/ipfs-http-client/examples/browser-ipns-publish/README.md
@@ -0,0 +1,83 @@
+# Publish to IPNS from the browser
+
+> Use ipns from the browser!
+
+This example is a demo web application that allows you to connect to a go-IPFS node, and publish your IPNS record to the go-DHT network but using your js-ipfs private key. We'll start two IPFS nodes; one in the browser and one on a go-Server. We'll use `ipfs-http-client` to connect to the go-Node to ensure our pubsub messages are getting through, and confirm the IPNS record resolves on the go-Node. We're aiming for something like this:
+
+```
+ +-----------+ websocket +-----------+
+ | +-------------------> |
+ | js-ipfs | pubsub | go-ipfs |
+ | <-------------------+ |
+ +-----^-----+ +-----^-----+
+ | |
+ | IPFS in browser | HTTP API
+ | |
++-------------------------------------------------+
+| Browser |
++-------------------------------------------------+
+| | | |
+| | | |
+| IPFS direct | | js-http-client |
+| a.k.a. ipfsNode | | a.k.a. ipfsAPI |
+| | | |
++-------------------------------------------------+
+```
+
+## 1. Get started
+
+With Node.js and git installed, clone the repo and install the project dependencies:
+
+```sh
+git clone https://github.com/ipfs/js-ipfs-http-client.git
+cd js-ipfs-http-client
+npm install # Installs ipfs-http-client dependencies
+cd examples/browser-pubsub
+npm install # Installs browser-pubsub app dependencies
+```
+
+Start the example application:
+
+```sh
+npm start
+```
+
+You should see something similar to the following in your terminal and the web app should now be available if you navigate to http://127.0.0.1:8888 using your browser:
+
+```sh
+Starting up http-server, serving ./
+Available on:
+ http://127.0.0.1:8888
+```
+
+## 2. Start two IPFS nodes
+
+The first node is the js-ipfs made in the browser and the demo does that for you.
+
+The second is a go-ipfs node on a server. To get our IPNS record to the DHT, we'll need [a server running go-IPFS](https://blog.ipfs.io/22-run-ipfs-on-a-vps/) with the API enabled on port 5001.
+
+Right now the easiest way to do this is to install and start a `go-ipfs` node.
+
+### Install and start the Go IPFS node
+
+Head over to https://dist.ipfs.io/#go-ipfs and hit the "Download go-ipfs" button. Extract the archive and read the instructions to install.
+
+After installation:
+
+```sh
+ipfs init
+# Configure CORS to allow ipfs-http-client to access this IPFS node
+ipfs config --json API.HTTPHeaders.Access-Control-Allow-Origin '["http://127.0.0.1:8888"]'
+# Start the IPFS node, enabling pubsub
+ipfs daemon --enable-pubsub-experiment &
+```
+
+## 3. Open the demo in a browser and connect to the go-node
+
+Now, open up the demo in a browser window.
+
+In the "CONNECT TO GO-IPFS VIA API MULTIADDRESS" field enter `/ip4/YourServerIP/tcp/5001` (where `YourSeverIP` is your server's IP address or use `/dns4/yourdomain.com/tcp/5001`) and click connect. Once it connects, put your go-IPFS websocket address in the next field `/dns4/yourdomain.com/tcp/4003/wss/p2p/QmPeerIDHash` and hit the second "Connect" button.
+
+This connects the API to the go-Node and connects your js-IPFS node via websocket to the go-Node so pubsub will work.
+
+Finally, enter `/ipfs/QmSomeHash` as the content you want to publish to IPNS.You should see the messages sent from the browser to the server appear in the logs below, ending with "Success, reolved" if it all worked.
diff --git a/packages/ipfs-http-client/examples/browser-ipns-publish/index.html b/packages/ipfs-http-client/examples/browser-ipns-publish/index.html
new file mode 100644
index 0000000000..ebe7ff99ef
--- /dev/null
+++ b/packages/ipfs-http-client/examples/browser-ipns-publish/index.html
@@ -0,0 +1,198 @@
+
+
+
+ Publish to IPNS from the browser
+
+
+
+
+
+
+
+
+
+ The idea is to publish from js-ipfs so you control your own
+ private keys, but publish to go-ipfs to benefit from the DHT.
+ Since the DHT in js-ipfs isn't working yet, we need to use PubSub
+ and have a go-ipfs node subscribed to that PubSub to get our IPNS
+ record onto the DHT.
+
+
+ To make this demo work, you're going to need:
+
+
+ 1. Access to a go-ipfs node and it's API, a-la
+
/dns4/domain.com/tcp/5001
+
+
+ This is how the demo talks to the server, to ensure things
+ like:
+
+
+
+ A) pubsub is enabled, { EXPERIMENTAL: { ipnsPubsub: true } }
+ and --enable-pubsub-experiment
+
+
+ B) go-ipfs is connected to the js-ipfs node in the browser,
+ via node.swarm.peers(),
+
+
+ C) the pubsub messages are getting through to the go node,
+ via node.pubsub.subscribe().
+
+ Since we need PubSub for IPNS to reach the go-IPFS node (and
+ further replicate through the go-DHT network) we need to
+ connect our pubsub enabled JS-IPFS node in the browser to our
+ go-IPFS node on the server. The way we connect is via
+ Websocket. See
+ this example
+ for reference.
+
+
+
+ Once we can talk to go-IPFS and we're connected via Websocket,
+ then we can publish in our browser node, have the pubsub push it
+ to the go-IPFS server, and then check with the server that it's
+ confirmed as published. Once it's on the go-IPFS node it should
+ replicate throughout the rest of the DHT amongst the go-Nodes.
+
+
+
+
+
+
+
+
+ 1. Connect to Go-IPFS via API MultiAddress
+
+
+
+
+
+
+ 2. Connect to Go-IPFS via Websocket MultiAddress (for PubSub to work)
+
+
+
+
+
+
+
+ 3. Choose a key:
+
+
+
+
+
+ 4. IPFS hash to publish
+
+
+
+
+
+
Browser Console
+
+
+
+
Server Console
+
+
+
+
+
diff --git a/packages/ipfs-http-client/examples/browser-ipns-publish/index.js b/packages/ipfs-http-client/examples/browser-ipns-publish/index.js
new file mode 100644
index 0000000000..311c4d73c2
--- /dev/null
+++ b/packages/ipfs-http-client/examples/browser-ipns-publish/index.js
@@ -0,0 +1,263 @@
+"use strict";
+
+const IpfsHttpClient = require("ipfs-http-client");
+const ipns = require("ipns");
+const IPFS = require("ipfs");
+const pRetry = require("p-retry");
+const base64url = require("base64url");
+const last = require("it-last");
+const cryptoKeys = require("human-crypto-keys"); // { getKeyPairFromSeed }
+
+const { sleep, Logger, onEnterPress, catchAndLog } = require("./util");
+const { Date } = require("ipfs-utils/src/globalthis");
+
+async function main() {
+ const apiUrlInput = document.getElementById("api-url");
+ const nodeConnectBtn = document.getElementById("node-connect");
+
+ const peerAddrInput = document.getElementById("peer-addr");
+ const wsConnectBtn = document.getElementById("peer-connect");
+
+ const ipnsInput = document.getElementById("topic");
+ const publishBtn = document.getElementById("publish");
+
+ const namespace = "/record/";
+ const retryOptions = {
+ retries: 5,
+ };
+
+ let log = Logger(document.getElementById("console"));
+ let sLog = Logger(document.getElementById("server-console"));
+ let keyName = "self";
+
+ let ipfsAPI; // remote server IPFS node
+ let ipfsBrowser; // local browser IPFS node
+ let peerId;
+
+ // init local browser node right away
+ log(`Browser IPFS getting ready...`);
+ ipfsBrowser = await IPFS.create({ pass: "01234567890123456789", EXPERIMENTAL: { ipnsPubsub: true } });
+ const { id } = await ipfsBrowser.id();
+ log(`Browser IPFS ready! Node id: ${id}`);
+
+ async function nodeConnect(url) {
+ log(`Connecting to ${url}`);
+ ipfsAPI = IpfsHttpClient(url);
+ const { id, agentVersion } = await ipfsAPI.id();
+ peerId = id;
+ log(`Success!`);
+ log(`Version ${agentVersion}`);
+ log(`Peer ID ${id}`);
+ }
+
+ async function wsConnect(addr) {
+ if (!addr) throw new Error("Missing peer multiaddr");
+ if (!ipfsBrowser)
+ throw new Error("Wait for the local IPFS node to start first");
+ log(`Connecting to peer ${addr}`);
+ await ipfsBrowser.swarm.connect(addr);
+ log(`Success!`);
+ log("Listing swarm peers...");
+ await sleep();
+ const peers = await ipfsBrowser.swarm.peers();
+ peers.forEach((peer) => {
+ //console.log(`peer: ${JSON.stringify(peer, null, 2)}`);
+ const fullAddr = `${peer.addr}/ipfs/${peer.peer}`;
+ log(
+ `${fullAddr}`
+ );
+ });
+ log(`(${peers.length} peers total)`);
+ }
+
+ // Wait until a peer subscribes a topic
+ const waitForPeerToSubscribe = async (daemon, topic) => {
+ await pRetry(async () => {
+ const res = await daemon.pubsub.ls();
+
+ if (!res || !res.length || !res.includes(topic)) {
+ throw new Error("Could not find subscription");
+ }
+
+ return res[0];
+ }, retryOptions);
+ };
+
+ // wait until a peer know about other peer to subscribe a topic
+ const waitForNotificationOfSubscription = (daemon, topic, peerId) =>
+ pRetry(async () => {
+ const res = await daemon.pubsub.peers(topic);
+
+ if (!res || !res.length || !res.includes(peerId)) {
+ throw new Error("Could not find peer subscribing");
+ }
+ }, retryOptions);
+
+ async function subs(node, topic, tLog) {
+ tLog(`Subscribing to ${topic}`);
+ await node.pubsub.subscribe(
+ topic,
+ (msg) => {
+ const from = msg.from;
+ const seqno = msg.seqno.toString("hex");
+
+ tLog(
+ `${new Date(
+ Date.now()
+ ).toLocaleTimeString()}\n Message ${seqno} from ${from}`
+ );
+
+ let regex = "/record/";
+ if (topic.match(regex) ? topic.match(regex).length > 0 : false) {
+ tLog(
+ "#" +
+ ipns.unmarshal(msg.data).sequence.toString() +
+ ") Topic: " +
+ msg.topicIDs[0].toString()
+ );
+ tLog("Value:\n" + ipns.unmarshal(msg.data).value.toString());
+ } else {
+ try {
+ tLog(JSON.stringify(msg.data.toString(), null, 2));
+ } catch (_) {
+ tLog(msg.data.toString("hex"));
+ }
+ }
+ },
+ {
+ onError: (err, fatal) => {
+ if (fatal) {
+ console.error(err);
+ tLog(`${err.message}`);
+ tLog(`Resubscribing in 5s to ${topic}...`);
+ setTimeout(
+ catchAndLog(() => subs(node, topic, tLog), tLog),
+ 5000
+ );
+ } else {
+ console.warn(err);
+ }
+ },
+ }
+ );
+ }
+
+ async function createKey(keyName) {
+ return new Promise(async (resolve, reject) => {
+ try {
+ // quick and dirty key gen, don't do this in real life
+ const key = await IPFS.multihashing.digest(
+ Buffer.from(keyName + Math.random().toString(36).substring(2)),
+ "sha2-256"
+ );
+ const keyPair = await cryptoKeys.getKeyPairFromSeed(key, "rsa");
+
+ // put it on the browser IPNS keychain and name it
+ await ipfsBrowser.key.import(keyName, keyPair.privateKey);
+ // now this key can be used to publish to this ipns publicKey
+ resolve(true);
+ } catch (err) {
+ console.log(`Error creating Key ${keyName}: \n ${err}`);
+ reject(false);
+ }
+ });
+ }
+
+ async function publish(content) {
+ if (!content) throw new Error("Missing ipns content to publish");
+ if (!ipfsAPI) throw new Error("Connect to a go-server node first");
+ if (!ipfsAPI.name.pubsub.state())
+ throw new Error(
+ "IPNS Pubsub must be enabled on bother peers, use --enable-namesys-pubsub"
+ );
+
+ let browserNode = await ipfsBrowser.id();
+ let serverNode = await ipfsAPI.id();
+
+ let b58 = await IPFS.multihash.fromB58String(browserNode.id);
+ const keys = ipns.getIdKeys(b58);
+ const topic = `${namespace}${base64url.encode(keys.routingKey.toBuffer())}`;
+
+ log(`Initial Resolve ${browserNode.id}`); // subscribes the server to our IPNS topics
+ last(ipfsAPI.name.resolve(browserNode.id, { stream: false })); // save the pubsub topic to the server to make them listen
+
+ await sleep(1000); // give it a moment to save it
+
+ // subscribe and log on both nodes
+ await subs(ipfsBrowser, topic, log); // browserLog
+ await subs(ipfsAPI, topic, sLog); // serverLog
+
+ // confirm they are subscribed
+ await waitForPeerToSubscribe(ipfsAPI, topic); // confirm topic is on THEIR list // API
+ await waitForNotificationOfSubscription(ipfsBrowser, topic, serverNode.id); // confirm they are on OUR list
+
+ await sleep(2500); // give it a moment to save it
+
+ let remList = await ipfsAPI.pubsub.ls(); // API
+ if (!remList.includes(topic)) sLog(`[Fail] !Pubsub.ls ${topic}`);
+ else sLog(`[Pass] Pubsub.ls`);
+
+ let remListSubs = await ipfsAPI.name.pubsub.subs(); // API
+ if (!remListSubs.includes(`/ipns/${browserNode.id}`))
+ sLog(`[Fail] !Name.Pubsub.subs ${browserNode.id}`);
+ else sLog(`[Pass] Name.Pubsub.subs`);
+
+ keyName = document.querySelector('input[name="keyName"]:checked').value;
+ if (keyName != "self") {
+ await createKey(keyName);
+ let keys = await ipfsBrowser.key.list()
+ log(JSON.stringify(keys));
+ }
+
+ // publish will send a pubsub msg to the server to update their ipns record
+ log(`Publishing ${content} to ${keyName}...`);
+ const results = await ipfsBrowser.name.publish(content, {
+ resolve: false,
+ key: keyName,
+ });
+ log(`Published`); // ${results.name} to ${results.value}
+
+ log(`Wait 5 seconds, then resolve...`);
+ await sleep(5000);
+
+ log(
+ `Try to resolve ${browserNode.id} on server through API to confirm published`
+ );
+
+ let name = await last(
+ ipfsAPI.name.resolve(browserNode.id, {
+ stream: false,
+ })
+ );
+ log(`Resolved: ${name}`);
+ if (name == content)
+ log(`IPNS Publish Success!`);
+ log(
+ `Look at that! /ipns/${browserNode.id} resolves to ${content}`
+ );
+ }
+
+ const onNodeConnectClick = catchAndLog(
+ () => nodeConnect(apiUrlInput.value),
+ log
+ );
+
+ apiUrlInput.addEventListener("keydown", onEnterPress(onNodeConnectClick));
+ nodeConnectBtn.addEventListener("click", onNodeConnectClick);
+
+ const onwsConnectClick = catchAndLog(
+ () => wsConnect(peerAddrInput.value),
+ log
+ );
+ peerAddrInput.addEventListener("keydown", onEnterPress(onwsConnectClick));
+ wsConnectBtn.addEventListener("click", onwsConnectClick);
+
+ const onPublishClick = catchAndLog(() => publish(ipnsInput.value), log);
+ ipnsInput.addEventListener("keydown", onEnterPress(onPublishClick));
+ publishBtn.addEventListener("click", onPublishClick);
+
+}
+
+main();
diff --git a/packages/ipfs-http-client/examples/browser-ipns-publish/package.json b/packages/ipfs-http-client/examples/browser-ipns-publish/package.json
new file mode 100644
index 0000000000..b3f7a5fdd1
--- /dev/null
+++ b/packages/ipfs-http-client/examples/browser-ipns-publish/package.json
@@ -0,0 +1,34 @@
+{
+ "name": "browser-ipns-publish-example",
+ "version": "1.0.0",
+ "description": "An example demonstrating publishing to IPNS in the browser",
+ "private": true,
+ "main": "index.js",
+ "scripts": {
+ "start": "parcel index.html"
+ },
+ "author": "Doug Anderson",
+ "license": "MIT",
+ "dependencies": {
+ "ipfs": "file:../../../ipfs",
+ "ipfs-http-client": "../../",
+ "p-retry": "^4.2.0",
+ "it-last": "^1.0.2",
+ "base64url": "^3.0.1",
+ "human-crypto-keys": "^0.1.4"
+ },
+ "browserslist": [
+ "last 2 versions and not dead and > 2%"
+ ],
+ "devDependencies": {
+ "parcel-bundler": "^1.12.4"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/ipfs/js-ipfs/tree/master/packages/ipfs-http-client/examples"
+ },
+ "keywords": [
+ "IPNS",
+ "Publish"
+ ]
+}
diff --git a/packages/ipfs-http-client/examples/browser-ipns-publish/util.js b/packages/ipfs-http-client/examples/browser-ipns-publish/util.js
new file mode 100644
index 0000000000..e6aada61f8
--- /dev/null
+++ b/packages/ipfs-http-client/examples/browser-ipns-publish/util.js
@@ -0,0 +1,31 @@
+exports.sleep = (ms = 1000) => new Promise(resolve => setTimeout(resolve, ms))
+
+exports.Logger = outEl => {
+ outEl.innerHTML = ''
+ return message => {
+ const container = document.createElement('div')
+ container.innerHTML = message
+ outEl.appendChild(container)
+ outEl.scrollTop = outEl.scrollHeight
+ }
+}
+
+exports.onEnterPress = fn => {
+ return e => {
+ if (event.which == 13 || event.keyCode == 13) {
+ e.preventDefault()
+ fn()
+ }
+ }
+}
+
+exports.catchAndLog = (fn, log) => {
+ return async (...args) => {
+ try {
+ await fn(...args)
+ } catch (err) {
+ console.error(err)
+ log(`${err.message}`)
+ }
+ }
+}
From 250cbd8957b55000d53285136233f21eaa74ae0f Mon Sep 17 00:00:00 2001
From: DougA
Date: Sun, 12 Jul 2020 12:56:13 -0400
Subject: [PATCH 02/22] slight adjustments to readme
---
.../examples/browser-ipns-publish/.gitignore | 3 ++-
.../examples/browser-ipns-publish/README.md | 14 ++++++++------
2 files changed, 10 insertions(+), 7 deletions(-)
diff --git a/packages/ipfs-http-client/examples/browser-ipns-publish/.gitignore b/packages/ipfs-http-client/examples/browser-ipns-publish/.gitignore
index 4a94694d21..46b007034c 100644
--- a/packages/ipfs-http-client/examples/browser-ipns-publish/.gitignore
+++ b/packages/ipfs-http-client/examples/browser-ipns-publish/.gitignore
@@ -1,2 +1,3 @@
bundle.js
-.cache
\ No newline at end of file
+.cache
+/node_modules/
\ No newline at end of file
diff --git a/packages/ipfs-http-client/examples/browser-ipns-publish/README.md b/packages/ipfs-http-client/examples/browser-ipns-publish/README.md
index 25d9b0532c..1b146bc012 100644
--- a/packages/ipfs-http-client/examples/browser-ipns-publish/README.md
+++ b/packages/ipfs-http-client/examples/browser-ipns-publish/README.md
@@ -32,7 +32,7 @@ With Node.js and git installed, clone the repo and install the project dependenc
git clone https://github.com/ipfs/js-ipfs-http-client.git
cd js-ipfs-http-client
npm install # Installs ipfs-http-client dependencies
-cd examples/browser-pubsub
+cd examples/browser-ipns-publish
npm install # Installs browser-pubsub app dependencies
```
@@ -42,12 +42,12 @@ Start the example application:
npm start
```
-You should see something similar to the following in your terminal and the web app should now be available if you navigate to http://127.0.0.1:8888 using your browser:
+You should see something similar to the following in your terminal and the web app should now be available if you navigate to http://127.0.0.1:1234 using your browser:
```sh
Starting up http-server, serving ./
Available on:
- http://127.0.0.1:8888
+ http://127.0.0.1:1234
```
## 2. Start two IPFS nodes
@@ -67,9 +67,9 @@ After installation:
```sh
ipfs init
# Configure CORS to allow ipfs-http-client to access this IPFS node
-ipfs config --json API.HTTPHeaders.Access-Control-Allow-Origin '["http://127.0.0.1:8888"]'
+ipfs config --json API.HTTPHeaders.Access-Control-Allow-Origin '["http://127.0.0.1:1234"]'
# Start the IPFS node, enabling pubsub
-ipfs daemon --enable-pubsub-experiment &
+ipfs daemon --enable-pubsub-experiment
```
## 3. Open the demo in a browser and connect to the go-node
@@ -80,4 +80,6 @@ In the "CONNECT TO GO-IPFS VIA API MULTIADDRESS" field enter `/ip4/YourServerIP/
This connects the API to the go-Node and connects your js-IPFS node via websocket to the go-Node so pubsub will work.
-Finally, enter `/ipfs/QmSomeHash` as the content you want to publish to IPNS.You should see the messages sent from the browser to the server appear in the logs below, ending with "Success, reolved" if it all worked.
+You can choose whether to publish this record under the PeerId of the node that is running in the browser ('self') or choose to add a custom key to the IPFS keychain and publish under that instead. Either should work.
+
+Finally, enter `/ipfs/QmSomeHash` as the content you want to publish to IPNS. You should see the messages sent from the browser to the server appear in the logs below, ending with "Success, reolved" if it all worked.
From a57bb528dc304d8f67e7bf112d2084dba22d6165 Mon Sep 17 00:00:00 2001
From: DougA
Date: Wed, 15 Jul 2020 13:36:19 -0400
Subject: [PATCH 03/22] move to /examples folder
---
.../examples => examples}/browser-ipns-publish/.gitignore | 0
.../examples => examples}/browser-ipns-publish/README.md | 4 ++--
.../examples => examples}/browser-ipns-publish/index.html | 0
.../examples => examples}/browser-ipns-publish/index.js | 0
.../examples => examples}/browser-ipns-publish/package.json | 0
.../examples => examples}/browser-ipns-publish/util.js | 0
6 files changed, 2 insertions(+), 2 deletions(-)
rename {packages/ipfs-http-client/examples => examples}/browser-ipns-publish/.gitignore (100%)
rename {packages/ipfs-http-client/examples => examples}/browser-ipns-publish/README.md (97%)
rename {packages/ipfs-http-client/examples => examples}/browser-ipns-publish/index.html (100%)
rename {packages/ipfs-http-client/examples => examples}/browser-ipns-publish/index.js (100%)
rename {packages/ipfs-http-client/examples => examples}/browser-ipns-publish/package.json (100%)
rename {packages/ipfs-http-client/examples => examples}/browser-ipns-publish/util.js (100%)
diff --git a/packages/ipfs-http-client/examples/browser-ipns-publish/.gitignore b/examples/browser-ipns-publish/.gitignore
similarity index 100%
rename from packages/ipfs-http-client/examples/browser-ipns-publish/.gitignore
rename to examples/browser-ipns-publish/.gitignore
diff --git a/packages/ipfs-http-client/examples/browser-ipns-publish/README.md b/examples/browser-ipns-publish/README.md
similarity index 97%
rename from packages/ipfs-http-client/examples/browser-ipns-publish/README.md
rename to examples/browser-ipns-publish/README.md
index 1b146bc012..8bdb02546d 100644
--- a/packages/ipfs-http-client/examples/browser-ipns-publish/README.md
+++ b/examples/browser-ipns-publish/README.md
@@ -29,8 +29,8 @@ This example is a demo web application that allows you to connect to a go-IPFS n
With Node.js and git installed, clone the repo and install the project dependencies:
```sh
-git clone https://github.com/ipfs/js-ipfs-http-client.git
-cd js-ipfs-http-client
+git clone https://github.com/ipfs/js-ipfs.git
+cd js-ipfs
npm install # Installs ipfs-http-client dependencies
cd examples/browser-ipns-publish
npm install # Installs browser-pubsub app dependencies
diff --git a/packages/ipfs-http-client/examples/browser-ipns-publish/index.html b/examples/browser-ipns-publish/index.html
similarity index 100%
rename from packages/ipfs-http-client/examples/browser-ipns-publish/index.html
rename to examples/browser-ipns-publish/index.html
diff --git a/packages/ipfs-http-client/examples/browser-ipns-publish/index.js b/examples/browser-ipns-publish/index.js
similarity index 100%
rename from packages/ipfs-http-client/examples/browser-ipns-publish/index.js
rename to examples/browser-ipns-publish/index.js
diff --git a/packages/ipfs-http-client/examples/browser-ipns-publish/package.json b/examples/browser-ipns-publish/package.json
similarity index 100%
rename from packages/ipfs-http-client/examples/browser-ipns-publish/package.json
rename to examples/browser-ipns-publish/package.json
diff --git a/packages/ipfs-http-client/examples/browser-ipns-publish/util.js b/examples/browser-ipns-publish/util.js
similarity index 100%
rename from packages/ipfs-http-client/examples/browser-ipns-publish/util.js
rename to examples/browser-ipns-publish/util.js
From ddbf720e5cbf4182dca9fc9ecfa1cc0140ec3122 Mon Sep 17 00:00:00 2001
From: DougA
Date: Wed, 15 Jul 2020 13:48:25 -0400
Subject: [PATCH 04/22] edit package.json for new location
---
examples/browser-ipns-publish/package.json | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/examples/browser-ipns-publish/package.json b/examples/browser-ipns-publish/package.json
index b3f7a5fdd1..ebb0d351a6 100644
--- a/examples/browser-ipns-publish/package.json
+++ b/examples/browser-ipns-publish/package.json
@@ -10,8 +10,8 @@
"author": "Doug Anderson",
"license": "MIT",
"dependencies": {
- "ipfs": "file:../../../ipfs",
- "ipfs-http-client": "../../",
+ "ipfs": "file:../packages/ipfs",
+ "ipfs-http-client": "file:../packages/ipfs-http-client",
"p-retry": "^4.2.0",
"it-last": "^1.0.2",
"base64url": "^3.0.1",
@@ -25,7 +25,7 @@
},
"repository": {
"type": "git",
- "url": "https://github.com/ipfs/js-ipfs/tree/master/packages/ipfs-http-client/examples"
+ "url": "https://github.com/ipfs/js-ipfs/examples"
},
"keywords": [
"IPNS",
From 6670b1ee96b4b1236082cdd373eb4b1937bcb2ed Mon Sep 17 00:00:00 2001
From: DougA
Date: Fri, 17 Jul 2020 09:56:46 -0400
Subject: [PATCH 05/22] bump http-server version
---
examples/test-ipfs-example/package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/examples/test-ipfs-example/package.json b/examples/test-ipfs-example/package.json
index 4f5590c846..f110bb9bdd 100644
--- a/examples/test-ipfs-example/package.json
+++ b/examples/test-ipfs-example/package.json
@@ -10,7 +10,7 @@
"chromedriver": "^83.0.0",
"execa": "^4.0.0",
"fs-extra": "^8.1.0",
- "http-server": "^0.11.1",
+ "http-server": "^0.12.3",
"nightwatch": "^1.2.4",
"which": "^2.0.1"
}
From b7bf749e943e7e32195fea51f978d5bad24b5c05 Mon Sep 17 00:00:00 2001
From: DougA
Date: Fri, 17 Jul 2020 09:57:55 -0400
Subject: [PATCH 06/22] init test commit
---
examples/browser-ipns-publish/test.js | 221 ++++++++++++++++++++++++++
1 file changed, 221 insertions(+)
create mode 100644 examples/browser-ipns-publish/test.js
diff --git a/examples/browser-ipns-publish/test.js b/examples/browser-ipns-publish/test.js
new file mode 100644
index 0000000000..2f72c037de
--- /dev/null
+++ b/examples/browser-ipns-publish/test.js
@@ -0,0 +1,221 @@
+"use strict";
+
+const fs = require("fs-extra");
+const os = require("os");
+const path = require("path");
+const execa = require("execa");
+const delay = require("delay");
+const { startServer } = require("test-ipfs-example/utils");
+const pkg = require("./package.json");
+const { createFactory } = require("ipfsd-ctl");
+const df = createFactory({
+ ipfsHttpModule: require("ipfs-http-client"),
+});
+
+const daemonsOptions = {
+ args: ["--enable-namesys-pubsub"], // enable ipns over pubsub
+};
+const apiPort = "5001";
+
+async function testUI(url, apiMultiAddr) {
+ const proc = execa(
+ require.resolve("test-ipfs-example/node_modules/.bin/nightwatch"),
+ [
+ "--config",
+ require.resolve("test-ipfs-example/nightwatch.conf.js"),
+ path.join(__dirname, "test.js"),
+ ],
+ {
+ cwd: path.resolve(__dirname, "../"),
+ env: {
+ ...process.env,
+ CI: true,
+ IPFS_EXAMPLE_TEST_URL: url,
+ IPFS_API_MULTIADDR: apiMultiAddr,
+ },
+ all: true,
+ }
+ );
+ proc.all.on("data", (data) => {
+ process.stdout.write(data);
+ });
+
+ await proc;
+}
+
+/*
+This test needs:
+✓ a server listening on an API port 5001
+✓ listening on websocket port 4003
+
+then
+
+1.
+*/
+async function runTest() {
+ let nodes = [];
+ nodes = await Promise.all([
+ df.spawn({
+ type: "go",
+ test: true,
+ ipfsOptions: {
+ config: {
+ Addresses: {
+ Swarm: ["/ip4/127.0.0.1/tcp/4003/ws", "/ip4/0.0.0.0/tcp/4001"],
+ API: `/ip4/127.0.0.1/tcp/${apiPort}`,
+ },
+ },
+ },
+ ...daemonsOptions,
+ }),
+ df.spawn({
+ type: "js",
+ test: true,
+ ...daemonsOptions,
+ }),
+ // go-ipfs needs two nodes in the DHT to be able to publish a record
+ // TODO: Remove this when js-ipfs has a DHT
+ df.spawn({
+ type: "go",
+ test: true,
+ ...daemonsOptions,
+ }),
+ ]);
+
+ const server = await startServer(__dirname);
+
+ try {
+
+ // swarm the go node to the other 2 nodes
+ await nodes[0].api.swarm.connect(nodes[1].api.peerId.addresses[0])
+ // go-ipfs needs two nodes in the DHT to be able to publish a record
+ // TODO: Remove this when js-ipfs has a DHT
+ await nodes[0].api.swarm.connect(nodes[2].api.peerId.addresses[0])
+
+ console.log('wait for republish as we can receive the republish message first') // eslint-disable-line no-console
+ await delay(60000);
+
+ const goPeer = await nodes[0].api.id();
+
+ const address = goPeer.addresses
+ .map((ma) => ma.toString())
+ .find((addr) => addr.includes("/ws/p2p/Qm"));
+
+ if (!address) {
+ throw new Error(
+ `Could not find web socket address in ${goPeer.addresses}`
+ );
+ }
+
+ let apiMultiAddr = `/ip4/${server.url}/tcp/${apiPort}`;
+
+ await testUI(server.url, apiMultiAddr);
+
+ } finally {
+ await nodes[0].stop();
+ await nodes[1].stop();
+ await nodes[2].stop();
+ await server.stop();
+ }
+}
+
+module.exports = runTest;
+
+/*
+This test needs:
+
+1. put the server API port in the text input, click connect
+assert connected
+
+2. put the websocket multiaddr int he input, click connect
+assert connected
+
+3. put the content to publish in the input, click publish
+assert published
+
+4. listen for resolve
+
+*/
+module.exports[pkg.name] = function (browser) {
+ let local = null;
+ let remote = null;
+
+ browser
+ .url(process.env.IPFS_EXAMPLE_TEST_URL)
+ .waitForElementVisible("#api-url")
+ .clearValue("#api-url")
+ .setValue("#api-url", process.env.IPFS_API_MULTIADDR)
+ .pause(1000)
+ .click("#node-connect");
+
+ /*
+ browser.expect
+ .element("#peers-addrs")
+ .text.to.contain(process.env.IPFS_RELAY_ID);
+ browser.expect.element("#peer-id").text.to.not.equal("");
+
+ // exchange peer info
+ browser
+ .getText("#addrs", (result) => {
+ local = {
+ addr: result.value.trim(),
+ };
+ console.info(`got local circuit relay address ${local.addr}`); // eslint-disable-line no-console
+ })
+ .getText("#peer-id", (result) => {
+ local.id = result.value.trim();
+ console.info(`got local peer id ${local.id}`); // eslint-disable-line no-console
+ })
+ .perform(async (browser, done) => {
+ console.info(`writing local data ${local.addr}`); // eslint-disable-line no-console
+ await fs.writeJson(process.env.IPFS_LOCAL_PEER_ID_FILE, local);
+
+ console.info("reading remote circuit relay address"); // eslint-disable-line no-console
+ for (let i = 0; i < 100; i++) {
+ try {
+ remote = await fs.readJson(process.env.IPFS_REMOTE_PEER_ID_FILE, {
+ encoding: "utf8",
+ });
+
+ if (!remote || !remote.addr || !remote.id) {
+ throw new Error("Remote circuit relay address was empty");
+ }
+
+ console.info(`got remote circuit relay address ${remote.addr}`); // eslint-disable-line no-console
+ done();
+
+ break;
+ } catch (err) {
+ // ignore
+ }
+
+ await delay(1000);
+ }
+
+ console.info(`connecting to remote peer ${remote.addr}`); // eslint-disable-line no-console
+
+ browser
+ .clearValue("#peer")
+ .setValue("#peer", remote.addr)
+ .pause(1000)
+ .click("#connect");
+
+ browser.expect.element("#peers-addrs").text.to.contain(remote.id);
+
+ browser
+ .clearValue("#message")
+ .setValue("#message", "hello")
+ .pause(1000)
+ .click("#send");
+
+ browser.expect
+ .element("#msgs")
+ .text.to.contain(`${remote.id.substr(-4)}: hello`);
+ browser.expect
+ .element("#msgs")
+ .text.to.contain(`${local.id.substr(-4)}: hello`);
+ });
+ */
+
+ browser.end();
+};
From 518ad1c25b3d527f6baf794db3775eedb0dddca3 Mon Sep 17 00:00:00 2001
From: DougA
Date: Fri, 17 Jul 2020 09:58:39 -0400
Subject: [PATCH 07/22] package.json installs
---
examples/browser-ipns-publish/package.json | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/examples/browser-ipns-publish/package.json b/examples/browser-ipns-publish/package.json
index ebb0d351a6..8f744ad6bd 100644
--- a/examples/browser-ipns-publish/package.json
+++ b/examples/browser-ipns-publish/package.json
@@ -5,17 +5,20 @@
"private": true,
"main": "index.js",
"scripts": {
- "start": "parcel index.html"
+ "start": "parcel index.html",
+ "test": "test-ipfs-example"
},
"author": "Doug Anderson",
"license": "MIT",
"dependencies": {
- "ipfs": "file:../packages/ipfs",
- "ipfs-http-client": "file:../packages/ipfs-http-client",
- "p-retry": "^4.2.0",
- "it-last": "^1.0.2",
"base64url": "^3.0.1",
- "human-crypto-keys": "^0.1.4"
+ "human-crypto-keys": "^0.1.4",
+ "ipfs": "file:../../packages/ipfs/",
+ "ipfs-http-client": "file:../../packages/ipfs-http-client/",
+ "ipfs-utils": "^2.3.1",
+ "ipns": "^0.7.3",
+ "it-last": "^1.0.2",
+ "p-retry": "^4.2.0"
},
"browserslist": [
"last 2 versions and not dead and > 2%"
From 9c4834686adaf55843e4e2ffe361273c9ce44244 Mon Sep 17 00:00:00 2001
From: DougA
Date: Fri, 17 Jul 2020 09:59:51 -0400
Subject: [PATCH 08/22] bug fixes
---
examples/browser-ipns-publish/index.html | 38 ++++++-----
examples/browser-ipns-publish/index.js | 80 ++++++++++++++----------
2 files changed, 69 insertions(+), 49 deletions(-)
diff --git a/examples/browser-ipns-publish/index.html b/examples/browser-ipns-publish/index.html
index ebe7ff99ef..fbd4a2dcbb 100644
--- a/examples/browser-ipns-publish/index.html
+++ b/examples/browser-ipns-publish/index.html
@@ -34,7 +34,7 @@
private keys, but publish to go-ipfs to benefit from the DHT.
Since the DHT in js-ipfs isn't working yet, we need to use PubSub
and have a go-ipfs node subscribed to that PubSub to get our IPNS
- record onto the DHT.
+ record onto the DHT. In order to use PubSub between these two
+ nodes, you'll need a websocket to connect them.
To make this demo work, you're going to need:
@@ -141,23 +142,26 @@
diff --git a/examples/browser-ipns-publish/index.js b/examples/browser-ipns-publish/index.js
index 311c4d73c2..33248db32f 100644
--- a/examples/browser-ipns-publish/index.js
+++ b/examples/browser-ipns-publish/index.js
@@ -9,7 +9,7 @@ const last = require("it-last");
const cryptoKeys = require("human-crypto-keys"); // { getKeyPairFromSeed }
const { sleep, Logger, onEnterPress, catchAndLog } = require("./util");
-const { Date } = require("ipfs-utils/src/globalthis");
+const { Date, Element } = require("ipfs-utils/src/globalthis");
async function main() {
const apiUrlInput = document.getElementById("api-url");
@@ -36,7 +36,10 @@ async function main() {
// init local browser node right away
log(`Browser IPFS getting ready...`);
- ipfsBrowser = await IPFS.create({ pass: "01234567890123456789", EXPERIMENTAL: { ipnsPubsub: true } });
+ ipfsBrowser = await IPFS.create({
+ pass: "01234567890123456789",
+ EXPERIMENTAL: { ipnsPubsub: true },
+ });
const { id } = await ipfsBrowser.id();
log(`Browser IPFS ready! Node id: ${id}`);
@@ -112,7 +115,7 @@ async function main() {
let regex = "/record/";
if (topic.match(regex) ? topic.match(regex).length > 0 : false) {
tLog(
- "#" +
+ "\n#" +
ipns.unmarshal(msg.data).sequence.toString() +
") Topic: " +
msg.topicIDs[0].toString()
@@ -168,23 +171,41 @@ async function main() {
async function publish(content) {
if (!content) throw new Error("Missing ipns content to publish");
if (!ipfsAPI) throw new Error("Connect to a go-server node first");
- if (!ipfsAPI.name.pubsub.state())
+ if (!ipfsAPI.name.pubsub.state() || !ipfsBrowser.name.pubsub.state())
throw new Error(
"IPNS Pubsub must be enabled on bother peers, use --enable-namesys-pubsub"
);
+ log(`Publish to IPNS`); // subscribes the server to our IPNS topic
+
let browserNode = await ipfsBrowser.id();
let serverNode = await ipfsAPI.id();
- let b58 = await IPFS.multihash.fromB58String(browserNode.id);
- const keys = ipns.getIdKeys(b58);
- const topic = `${namespace}${base64url.encode(keys.routingKey.toBuffer())}`;
+ // get which key this will be publish under, self or an imported custom key
+ keyName = document.querySelector('input[name="keyName"]:checked').value;
+ let keys = { name: "self", id: browserNode.id }; // default init
+
+ if (keyName != "self") {
+ if (!(await ipfsBrowser.key.list()).find((k) => k.name == keyName))
+ // skip if custom key exists already
+ await createKey(keyName);
+ let r = await ipfsBrowser.key.list();
+ keys = r.find((k) => k.name == keyName);
+ log(JSON.stringify(keys));
+ }
- log(`Initial Resolve ${browserNode.id}`); // subscribes the server to our IPNS topics
- last(ipfsAPI.name.resolve(browserNode.id, { stream: false })); // save the pubsub topic to the server to make them listen
+ log(`Initial Resolve ${keys.id}`); // subscribes the server to our IPNS topic
+ last(ipfsAPI.name.resolve(keys.id, { stream: false })); // save the pubsub topic to the server to make them listen
await sleep(1000); // give it a moment to save it
+ // set up the topic from ipns key
+ let b58 = await IPFS.multihash.fromB58String(keys.id);
+ const ipnsKeys = ipns.getIdKeys(b58);
+ const topic = `${namespace}${base64url.encode(
+ ipnsKeys.routingKey.toBuffer()
+ )}`;
+
// subscribe and log on both nodes
await subs(ipfsBrowser, topic, log); // browserLog
await subs(ipfsAPI, topic, sLog); // serverLog
@@ -196,47 +217,43 @@ async function main() {
await sleep(2500); // give it a moment to save it
let remList = await ipfsAPI.pubsub.ls(); // API
- if (!remList.includes(topic)) sLog(`[Fail] !Pubsub.ls ${topic}`);
+ if (!remList.includes(topic)) sLog(`[Fail] !Pubsub.ls ${topic}`);
else sLog(`[Pass] Pubsub.ls`);
let remListSubs = await ipfsAPI.name.pubsub.subs(); // API
- if (!remListSubs.includes(`/ipns/${browserNode.id}`))
- sLog(`[Fail] !Name.Pubsub.subs ${browserNode.id}`);
+ if (!remListSubs.includes(`/ipns/${keys.id}`))
+ sLog(`[Fail] !Name.Pubsub.subs ${keys.id}`);
else sLog(`[Pass] Name.Pubsub.subs`);
- keyName = document.querySelector('input[name="keyName"]:checked').value;
- if (keyName != "self") {
- await createKey(keyName);
- let keys = await ipfsBrowser.key.list()
- log(JSON.stringify(keys));
- }
-
// publish will send a pubsub msg to the server to update their ipns record
- log(`Publishing ${content} to ${keyName}...`);
+ log(`Publishing ${content} to ${keys.name} /ipns/${keys.id}`);
const results = await ipfsBrowser.name.publish(content, {
resolve: false,
key: keyName,
});
- log(`Published`); // ${results.name} to ${results.value}
+ log(`Published ${results.name} to ${results.value}`); //
- log(`Wait 5 seconds, then resolve...`);
- await sleep(5000);
+ log(`Wait 45 seconds, then resolve...`);
+ await sleep(45000);
- log(
- `Try to resolve ${browserNode.id} on server through API to confirm published`
- );
+ log(`Try resolve ${keys.id} on server through API`);
let name = await last(
- ipfsAPI.name.resolve(browserNode.id, {
+ ipfsAPI.name.resolve(keys.id, {
stream: false,
})
);
log(`Resolved: ${name}`);
- if (name == content)
+ if (name == content) {
log(`IPNS Publish Success!`);
- log(
- `Look at that! /ipns/${browserNode.id} resolves to ${content}`
- );
+ log(
+ `Look at that! /ipns/${keys.id} resolves to ${content}`
+ );
+ } else {
+ log(
+ `Error, resolve did not match ${name} !== ${content}`
+ );
+ }
}
const onNodeConnectClick = catchAndLog(
@@ -257,7 +274,6 @@ async function main() {
const onPublishClick = catchAndLog(() => publish(ipnsInput.value), log);
ipnsInput.addEventListener("keydown", onEnterPress(onPublishClick));
publishBtn.addEventListener("click", onPublishClick);
-
}
main();
From bad6942960d877256a3fcbbbd9ab47745c8ee3a8 Mon Sep 17 00:00:00 2001
From: DougA
Date: Fri, 17 Jul 2020 13:18:24 -0400
Subject: [PATCH 09/22] add dev Deps
---
examples/browser-ipns-publish/package.json | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/examples/browser-ipns-publish/package.json b/examples/browser-ipns-publish/package.json
index 8f744ad6bd..dbde4a279b 100644
--- a/examples/browser-ipns-publish/package.json
+++ b/examples/browser-ipns-publish/package.json
@@ -24,7 +24,14 @@
"last 2 versions and not dead and > 2%"
],
"devDependencies": {
- "parcel-bundler": "^1.12.4"
+ "delay": "^4.3.0",
+ "execa": "^4.0.0",
+ "fs-extra": "^9.0.1",
+ "ipfsd-ctl": "^5.0.0",
+ "os": "^0.1.1",
+ "parcel-bundler": "^1.12.4",
+ "path": "^0.12.7",
+ "test-ipfs-example": "file:../test-ipfs-example/"
},
"repository": {
"type": "git",
From dd9a235cc1a41ff7c6f29635134a3fa928d6952e Mon Sep 17 00:00:00 2001
From: DougA
Date: Sat, 18 Jul 2020 21:42:44 -0400
Subject: [PATCH 10/22] index tweaks
---
examples/browser-ipns-publish/index.html | 9 ++++++++-
examples/browser-ipns-publish/index.js | 13 ++++++++++---
2 files changed, 18 insertions(+), 4 deletions(-)
diff --git a/examples/browser-ipns-publish/index.html b/examples/browser-ipns-publish/index.html
index fbd4a2dcbb..74e52fd1e6 100644
--- a/examples/browser-ipns-publish/index.html
+++ b/examples/browser-ipns-publish/index.html
@@ -110,10 +110,12 @@