Created
May 25, 2020 04:36
-
-
Save bellbind/9355f4ad87fde72f975d2dad3236232c to your computer and use it in GitHub Desktop.
[js-ipfs][browser] Chat with js-libp2p in js-ipfs node
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<html> | |
<head> | |
<title>Chat on libp2p (js-ipfs-0.40)</title> | |
<script type="module" src="./main-40.js"></script> | |
</head> | |
<body> | |
<h3 id="myid"></h3> | |
<div> | |
to: <input id="to" type="text" size="40"/> | |
message: <input id="msg" type="text" size="40"/> | |
<button id="send">send</button> | |
</div> | |
<hr/> | |
<pre id="log"></pre> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<html> | |
<head> | |
<title>Chat on libp2p</title> | |
<script type="module" src="./main.js"></script> | |
</head> | |
<body> | |
<h3 id="myid"></h3> | |
<div> | |
to: <input id="to" type="text" size="40"/> | |
message: <input id="msg" type="text" size="40"/> | |
<button id="send">send</button> | |
</div> | |
<hr/> | |
<pre id="log"></pre> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// this bundle maybe non ES-module, but it can import to load as `window.Ipfs` | |
// This example code is for regacy js-ipfs-0.40 | |
import "https://cdn.jsdelivr.net/npm/ipfs@0.40.0/dist/index.min.js"; | |
//console.log(window.Ipfs); | |
// util1: pull-stream source from ES iterator | |
const iterableSource = iterable => { | |
const itor = iterable[Symbol.iterator](); | |
return (abort, cb) => { | |
if (abort) { | |
if (typeof itor.return === "function") itor.return(abort); | |
return cb(abort); | |
} | |
const {value, done} = itor.next(); | |
return cb(done, value); | |
}; | |
}; | |
// util2: pull-stream sink that turns a pull-stream source as ES async itor | |
const asyncIterSink = source => ({ | |
[Symbol.asyncIterator]: () => ({ | |
next: () => new Promise( | |
f => source(false, (done, value) => f({done, value}))), | |
}), | |
}); | |
const promisify = func => function (...args) { | |
return new Promise((f, e) => func.call( | |
this, ...args, (err, value) => err ? e(err) : f(value))); | |
}; | |
const main = async () => { | |
const node = window.node = await Ipfs.create({ | |
repo: `ipfs-${Math.random()}`, | |
//relay: {enabled: true, hop: {enabled: true, active: true}}, | |
relay: {enabled: true, hop: {enabled: true}}, | |
}); | |
await node.ready; | |
console.log("IPFS version:", (await node.version()).version); | |
console.log(`Peer ID:`, (await node.id()).id); | |
const myid = node.libp2p.peerInfo.id.toB58String(); | |
console.log("libp2p ID:", myid); | |
for (const ma of node.libp2p.peerInfo.multiaddrs.toArray()) { | |
console.log("multiaddr:", ma.toString()); | |
} | |
const protocolId = "chat-example-proto"; | |
node.libp2p.handle(protocolId, async (funcs, conn) => { | |
const pid = await promisify(conn.getPeerInfo.bind(conn))(); | |
const id = pid.id.toB58String(); | |
const msg = []; | |
for await (const buf of asyncIterSink(conn.source)) { | |
msg.push(new TextDecoder().decode(buf.buffer)); | |
} | |
document.querySelector("#log").prepend(`${id}: ${"".concat(msg)}\n`); | |
}); | |
document.querySelector("#send").addEventListener("click", ev => { | |
(async () => { | |
const id = document.querySelector("#to").value; | |
const msg = document.querySelector("#msg").value; | |
const p2pid = `/p2p-circuit/ipfs/${id}`; | |
const conn = await node.libp2p.dialProtocol(p2pid, protocolId); | |
conn.sink(iterableSource([new TextEncoder().encode(msg)])); | |
document.querySelector("#msg").value = ""; | |
document.querySelector("#log").prepend(`${myid}: ${msg}\n`); | |
})().catch(console.error); | |
}); | |
document.querySelector("#myid").textContent = myid; | |
}; | |
main().catch(console.error); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// this bundle maybe non ES-module, but it can import to load as `window.Ipfs` | |
// Chat example on js-libp2p in js-ipfs (>= 0.41) | |
//import "https://cdn.jsdelivr.net/npm/ipfs@0.44.0/dist/index.min.js"; | |
import "https://cdn.jsdelivr.net/npm/ipfs/dist/index.min.js"; | |
//console.log(window.Ipfs); | |
// util1: duplex-stream source from ES iterator | |
const iterableSource = async function* (iter) { | |
return yield* iter; | |
}; | |
const promisify = func => function (...args) { | |
return new Promise((f, e) => func.call( | |
this, ...args, (err, value) => err ? e(err) : f(value))); | |
}; | |
const main = async () => { | |
const node = window.node = await Ipfs.create({ | |
repo: `ipfs-${Math.random()}`, | |
relay: {enabled: true, hop: {enabled: true, active: true}}, | |
}); | |
await node.ready; | |
console.log("IPFS version:", (await node.version()).version); | |
console.log(`Peer ID:`, (await node.id()).id); | |
const myid = node.libp2p.peerInfo.id.toB58String(); | |
console.log("libp2p ID:", myid); | |
// receive | |
const protocolId = "chat-example-proto"; | |
node.libp2p.handle(protocolId, async ({connection, stream, protocol}) => { | |
// Connection: https://github.com/libp2p/js-libp2p-interfaces/tree/master/src/connection | |
//console.log(connection); | |
const id = connection.remotePeer.toB58String(); // PeerId instance | |
//console.log(id); | |
console.log(connection.remoteAddr.toString()); // Multiaddr as "/p2p/Qm..." | |
// duplex-stream: https://gist.github.com/alanshaw/591dc7dd54e4f99338a347ef568d6ee9#duplex-it | |
//console.log(stream); | |
const msg = []; | |
for await (const bl of stream.source) { | |
// bl: BufferList; see https://github.com/rvagg/bl | |
//console.log(bl); | |
//console.log(bl.slice()); // as Uint8Array | |
msg.push(new TextDecoder().decode(bl.slice())); | |
} | |
document.querySelector("#log").prepend(`${id}: ${"".concat(msg)}\n`); | |
}); | |
// from js-ipfs-0.41.0, swarm.connect() to p2pid with explicit relay required | |
const connect = async (node, id) => { | |
// swarm connect with relay | |
const addrs = new Set((await node.swarm.addrs()).map(({id}) => id)); | |
//console.log(addrs); | |
if (addrs.has(id)) return; | |
let relaid = false; | |
for (const relay of addrs) { | |
const relayid = `/p2p/${relay}/p2p-circuit/p2p/${id}`; | |
console.log("relayid", relayid); | |
try { | |
await node.swarm.connect(relayid); | |
relaid = true; | |
break; | |
} catch (error) { | |
//console.log(error); | |
} | |
} | |
if (!relaid) throw Error(`could not relay to ${id}`); | |
}; | |
// send | |
document.querySelector("#send").addEventListener("click", ev => { | |
(async () => { | |
const id = document.querySelector("#to").value; | |
const msg = document.querySelector("#msg").value; | |
const p2pid = `/p2p/${id}`; | |
console.log("p2pid", p2pid); | |
await connect(node, id); | |
// p2pid can avaialbe after dialed from the id | |
const {stream, protocol} = | |
await node.libp2p.dialProtocol(p2pid, protocolId); | |
stream.sink(iterableSource([new TextEncoder().encode(msg)])); | |
console.log(`dialed to ${p2pid}`); | |
document.querySelector("#msg").value = ""; | |
document.querySelector("#log").prepend(`${myid}: ${msg}\n`); | |
})().catch(console.error); | |
}); | |
document.querySelector("#myid").textContent = myid; | |
}; | |
main().catch(console.error); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment