mirror of
https://github.com/logos-messaging/examples.waku.org.git
synced 2026-01-04 05:43:07 +00:00
add relay chat example
This commit is contained in:
parent
f0b204de3d
commit
bb06af386b
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
70
examples/relay-chat/index.html
Normal file
70
examples/relay-chat/index.html
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
|
||||||
|
<title>JS-Waku light chat</title>
|
||||||
|
<link rel="stylesheet" href="./style.css" />
|
||||||
|
<link rel="apple-touch-icon" href="./favicon.png" />
|
||||||
|
<link rel="manifest" href="./manifest.json" />
|
||||||
|
<link rel="icon" href="./favicon.ico" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="content">
|
||||||
|
<div class="header">
|
||||||
|
<h3>Status: <span id="status"></span></h3>
|
||||||
|
|
||||||
|
<h4><label for="remoteNode">Remote node multiaddr</label></h4>
|
||||||
|
<div>
|
||||||
|
<input id="remoteNode" />
|
||||||
|
<button id="connectRemoteNode">Dial</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h4><label for="webrtcPeer">WebRTC Peer</label></h4>
|
||||||
|
<div>
|
||||||
|
<input id="webrtcPeer" />
|
||||||
|
<button id="connectWebrtcPeer">Dial</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<button id="relayWebRTC">Ensure WebRTC Relay connection</button>
|
||||||
|
<button id="dropNonWebRTC">Drop non WebRTC connections</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<details open>
|
||||||
|
<summary>Peer's information</summary>
|
||||||
|
|
||||||
|
<h4>Content topic</h4>
|
||||||
|
<p id="contentTopic"></p>
|
||||||
|
|
||||||
|
<h4>Local Peer Id</h4>
|
||||||
|
<p id="localPeerId"></p>
|
||||||
|
|
||||||
|
<h4>Remote Peer Id</h4>
|
||||||
|
<p id="remotePeerId"></p>
|
||||||
|
|
||||||
|
<h4>Relay mesh's protocols</h4>
|
||||||
|
<p id="relayMeshInfo"></p>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="messages"></div>
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
<div class="inputArea">
|
||||||
|
<input type="text" id="nickText" placeholder="Nickname" />
|
||||||
|
<textarea id="messageText" placeholder="Message"></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="controls">
|
||||||
|
<button id="send">Send</button>
|
||||||
|
<button id="exit">Exit chat</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="//cdn.jsdelivr.net/npm/protobufjs@7.X.X/dist/protobuf.min.js"></script>
|
||||||
|
<script type="module" src="./index.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
340
examples/relay-chat/index.js
Normal file
340
examples/relay-chat/index.js
Normal file
@ -0,0 +1,340 @@
|
|||||||
|
import {
|
||||||
|
createRelayNode,
|
||||||
|
bytesToUtf8,
|
||||||
|
utf8ToBytes,
|
||||||
|
createDecoder,
|
||||||
|
createEncoder,
|
||||||
|
} from "@waku/sdk";
|
||||||
|
|
||||||
|
import { webSockets } from "@libp2p/websockets";
|
||||||
|
import { all as filterAll } from "@libp2p/websockets/filters";
|
||||||
|
|
||||||
|
import { webRTC } from "@libp2p/webrtc";
|
||||||
|
import { circuitRelayTransport } from "libp2p/circuit-relay";
|
||||||
|
|
||||||
|
const CONTENT_TOPIC = "/toy-chat/2/huilong/proto";
|
||||||
|
|
||||||
|
const ui = initUI();
|
||||||
|
runApp(ui).catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
ui.setStatus(`error: ${err.message}`, "error");
|
||||||
|
});
|
||||||
|
|
||||||
|
async function runApp(ui) {
|
||||||
|
const {
|
||||||
|
info,
|
||||||
|
sendMessage,
|
||||||
|
unsubscribeFromMessages,
|
||||||
|
dial,
|
||||||
|
dialWebRTCpeer,
|
||||||
|
dropNetworkConnections,
|
||||||
|
ensureWebRTCconnectionInRelayMesh,
|
||||||
|
} = await initWakuContext({
|
||||||
|
ui,
|
||||||
|
contentTopic: CONTENT_TOPIC,
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.setLocalPeer(info.localPeerId);
|
||||||
|
ui.setContentTopic(info.contentTopic);
|
||||||
|
|
||||||
|
ui.onSendMessage(sendMessage);
|
||||||
|
ui.onRemoteNodeConnect(dial);
|
||||||
|
ui.onWebrtcConnect(dialWebRTCpeer);
|
||||||
|
ui.onRelayWebRTC(ensureWebRTCconnectionInRelayMesh);
|
||||||
|
ui.onDropNonWebRTC(dropNetworkConnections);
|
||||||
|
|
||||||
|
ui.onExit(async () => {
|
||||||
|
ui.setStatus("disconnecting...", "progress");
|
||||||
|
await unsubscribeFromMessages();
|
||||||
|
ui.setStatus("disconnected", "terminated");
|
||||||
|
ui.resetMessages();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function initWakuContext({ ui, contentTopic }) {
|
||||||
|
const Decoder = createDecoder(contentTopic);
|
||||||
|
const Encoder = createEncoder({ contentTopic });
|
||||||
|
|
||||||
|
const ChatMessage = new protobuf.Type("ChatMessage")
|
||||||
|
.add(new protobuf.Field("timestamp", 1, "uint64"))
|
||||||
|
.add(new protobuf.Field("nick", 2, "string"))
|
||||||
|
.add(new protobuf.Field("text", 3, "bytes"));
|
||||||
|
|
||||||
|
ui.setStatus("starting...", "progress");
|
||||||
|
|
||||||
|
const node = await createRelayNode({
|
||||||
|
libp2p: {
|
||||||
|
addresses: {
|
||||||
|
listen: ["/webrtc"],
|
||||||
|
},
|
||||||
|
connectionGater: {
|
||||||
|
denyDialMultiaddr: () => {
|
||||||
|
// refuse to deny localhost addresses
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
transports: [
|
||||||
|
webRTC({}),
|
||||||
|
circuitRelayTransport({
|
||||||
|
discoverRelays: 1,
|
||||||
|
}),
|
||||||
|
webSockets({ filter: filterAll }),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await node.start();
|
||||||
|
|
||||||
|
// Set a filter by using Decoder for a given ContentTopic
|
||||||
|
const unsubscribeFromMessages = await node.relay.subscribe(
|
||||||
|
[Decoder],
|
||||||
|
(wakuMessage) => {
|
||||||
|
const messageObj = ChatMessage.decode(wakuMessage.payload);
|
||||||
|
ui.renderMessage({
|
||||||
|
...messageObj,
|
||||||
|
text: bytesToUtf8(messageObj.text),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
ui.setStatus("started", "success");
|
||||||
|
|
||||||
|
const localPeerId = node.libp2p.peerId.toString();
|
||||||
|
|
||||||
|
const remotePeers = await node.libp2p.peerStore.all();
|
||||||
|
const remotePeerIds = new Set(remotePeers.map((peer) => peer.id.toString()));
|
||||||
|
|
||||||
|
ui.setRemotePeer(Array.from(remotePeerIds.keys()));
|
||||||
|
|
||||||
|
node.libp2p.addEventListener("peer:connect", async (event) => {
|
||||||
|
remotePeerIds.add(event.detail.toString());
|
||||||
|
ui.setRemotePeer(Array.from(remotePeerIds.keys()));
|
||||||
|
ui.setRelayMeshInfo(node.relay.gossipSub);
|
||||||
|
});
|
||||||
|
|
||||||
|
node.libp2p.addEventListener("peer:disconnect", (event) => {
|
||||||
|
remotePeerIds.delete(event.detail.toString());
|
||||||
|
ui.setRemotePeer(Array.from(remotePeerIds.keys()));
|
||||||
|
ui.setRelayMeshInfo(node.relay.gossipSub);
|
||||||
|
});
|
||||||
|
|
||||||
|
node.libp2p.addEventListener("peer:identify", (event) => {
|
||||||
|
const peer = event.detail;
|
||||||
|
|
||||||
|
if (!peer.protocols.includes("/webrtc-signaling/0.0.1")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.setWebrtcPeer(peer.peerId.toString());
|
||||||
|
ui.setRelayMeshInfo(node.relay.gossipSub);
|
||||||
|
});
|
||||||
|
|
||||||
|
window.node = node;
|
||||||
|
|
||||||
|
return {
|
||||||
|
unsubscribeFromMessages,
|
||||||
|
info: {
|
||||||
|
contentTopic,
|
||||||
|
localPeerId,
|
||||||
|
},
|
||||||
|
sendMessage: async ({ text, nick }) => {
|
||||||
|
if (!text || !nick) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const protoMessage = ChatMessage.create({
|
||||||
|
nick,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
text: utf8ToBytes(text),
|
||||||
|
});
|
||||||
|
|
||||||
|
await node.relay.send(Encoder, {
|
||||||
|
payload: ChatMessage.encode(protoMessage).finish(),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
dial: async (multiaddr) => {
|
||||||
|
ui.setStatus("connecting...", "progress");
|
||||||
|
await node.dial(multiaddr);
|
||||||
|
ui.setStatus("connected", "success");
|
||||||
|
},
|
||||||
|
dialWebRTCpeer: async (peerId) => {
|
||||||
|
const peers = await node.libp2p.peerStore.all();
|
||||||
|
const circuitPeer = peers.filter(
|
||||||
|
(p) =>
|
||||||
|
p.protocols.includes("/libp2p/circuit/relay/0.2.0/hop") &&
|
||||||
|
p.protocols.includes("/libp2p/circuit/relay/0.2.0/stop")
|
||||||
|
)[0];
|
||||||
|
|
||||||
|
if (!circuitPeer) {
|
||||||
|
throw Error("No Circuit peer is found");
|
||||||
|
}
|
||||||
|
|
||||||
|
let multiaddr = circuitPeer.addresses.pop().multiaddr;
|
||||||
|
multiaddr = `${multiaddr}/p2p/${circuitPeer.id.toString()}/p2p-circuit/webrtc/p2p/${peerId}`;
|
||||||
|
|
||||||
|
await node.dial(multiaddr);
|
||||||
|
ui.setRelayMeshInfo(node.relay.gossipSub);
|
||||||
|
},
|
||||||
|
ensureWebRTCconnectionInRelayMesh: async () => {
|
||||||
|
const promises = node.libp2p
|
||||||
|
.getConnections()
|
||||||
|
.filter((c) => c.stat.multiplexer === "/webrtc")
|
||||||
|
.map(async (c) => {
|
||||||
|
const outboundStream = node.relay.gossipSub.streamsOutbound.get(
|
||||||
|
c.remotePeer.toString()
|
||||||
|
);
|
||||||
|
const isWebRTCOutbound =
|
||||||
|
outboundStream.rawStream.constructor.name === "WebRTCStream";
|
||||||
|
|
||||||
|
if (isWebRTCOutbound) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
node.relay.gossipSub.streamsOutbound.delete(c.remotePeer.toString());
|
||||||
|
await node.relay.gossipSub.createOutboundStream(
|
||||||
|
c.remotePeer.toString(),
|
||||||
|
c
|
||||||
|
);
|
||||||
|
});
|
||||||
|
await Promise.all(promises);
|
||||||
|
ui.setRelayMeshInfo(node.relay.gossipSub);
|
||||||
|
},
|
||||||
|
dropNetworkConnections: async () => {
|
||||||
|
const promises = node.libp2p
|
||||||
|
.getConnections()
|
||||||
|
.filter((c) => c.stat.multiplexer !== "/webrtc")
|
||||||
|
.map(async (c) => {
|
||||||
|
const peerId = c.remotePeer.toString();
|
||||||
|
|
||||||
|
node.relay.gossipSub.peers.delete(peerId);
|
||||||
|
node.relay.gossipSub.streamsInbound.delete(peerId);
|
||||||
|
node.relay.gossipSub.streamsOutbound.delete(peerId);
|
||||||
|
|
||||||
|
await node.libp2p.peerStore.delete(c.remotePeer);
|
||||||
|
await c.close();
|
||||||
|
});
|
||||||
|
await Promise.all(promises);
|
||||||
|
ui.setRelayMeshInfo(node.relay.gossipSub);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// UI adapter
|
||||||
|
function initUI() {
|
||||||
|
const exitButton = document.getElementById("exit");
|
||||||
|
const sendButton = document.getElementById("send");
|
||||||
|
|
||||||
|
const statusBlock = document.getElementById("status");
|
||||||
|
const localPeerBlock = document.getElementById("localPeerId");
|
||||||
|
const remotePeerId = document.getElementById("remotePeerId");
|
||||||
|
const contentTopicBlock = document.getElementById("contentTopic");
|
||||||
|
|
||||||
|
const messagesBlock = document.getElementById("messages");
|
||||||
|
|
||||||
|
const nickText = document.getElementById("nickText");
|
||||||
|
const messageText = document.getElementById("messageText");
|
||||||
|
|
||||||
|
const remoteNode = document.getElementById("remoteNode");
|
||||||
|
const connectRemoteNode = document.getElementById("connectRemoteNode");
|
||||||
|
|
||||||
|
const webrtcPeer = document.getElementById("webrtcPeer");
|
||||||
|
const connectWebrtcPeer = document.getElementById("connectWebrtcPeer");
|
||||||
|
|
||||||
|
const relayWebRTCbutton = document.getElementById("relayWebRTC");
|
||||||
|
const dropNonWebRTCbutton = document.getElementById("dropNonWebRTC");
|
||||||
|
|
||||||
|
const relayMeshInfo = document.getElementById("relayMeshInfo");
|
||||||
|
|
||||||
|
return {
|
||||||
|
// UI events
|
||||||
|
onExit: (cb) => {
|
||||||
|
exitButton.addEventListener("click", cb);
|
||||||
|
},
|
||||||
|
onSendMessage: (cb) => {
|
||||||
|
sendButton.addEventListener("click", async () => {
|
||||||
|
await cb({
|
||||||
|
nick: nickText.value,
|
||||||
|
text: messageText.value,
|
||||||
|
});
|
||||||
|
messageText.value = "";
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// UI renderers
|
||||||
|
setStatus: (value, className) => {
|
||||||
|
statusBlock.innerHTML = `<span class=${className || ""}>${value}</span>`;
|
||||||
|
},
|
||||||
|
setLocalPeer: (id) => {
|
||||||
|
localPeerBlock.innerText = id.toString();
|
||||||
|
},
|
||||||
|
setRemotePeer: (ids) => {
|
||||||
|
remotePeerId.innerText = ids.join("\n");
|
||||||
|
},
|
||||||
|
setContentTopic: (topic) => {
|
||||||
|
contentTopicBlock.innerText = topic.toString();
|
||||||
|
},
|
||||||
|
renderMessage: (messageObj) => {
|
||||||
|
const { nick, text, timestamp } = messageObj;
|
||||||
|
const date = new Date(timestamp);
|
||||||
|
|
||||||
|
// WARNING: XSS vulnerable
|
||||||
|
messagesBlock.innerHTML += `
|
||||||
|
<div class="message">
|
||||||
|
<p>${nick} <span>(${date.toDateString()})</span>:</p>
|
||||||
|
<p>${text}</p>
|
||||||
|
<div>
|
||||||
|
`;
|
||||||
|
},
|
||||||
|
resetMessages: () => {
|
||||||
|
messagesBlock.innerHTML = "";
|
||||||
|
},
|
||||||
|
setWebrtcPeer: (peerId) => {
|
||||||
|
webrtcPeer.value = peerId;
|
||||||
|
},
|
||||||
|
onRemoteNodeConnect: (cb) => {
|
||||||
|
connectRemoteNode.addEventListener("click", () => {
|
||||||
|
const multiaddr = remoteNode.value;
|
||||||
|
|
||||||
|
if (!multiaddr) {
|
||||||
|
throw Error("No multiaddr set to dial");
|
||||||
|
}
|
||||||
|
|
||||||
|
cb(multiaddr);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onWebrtcConnect: (cb) => {
|
||||||
|
connectWebrtcPeer.addEventListener("click", () => {
|
||||||
|
const multiaddr = webrtcPeer.value;
|
||||||
|
|
||||||
|
if (!multiaddr) {
|
||||||
|
throw Error("No multiaddr to dial webrtc");
|
||||||
|
}
|
||||||
|
|
||||||
|
cb(multiaddr);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onRelayWebRTC: (cb) => {
|
||||||
|
relayWebRTCbutton.addEventListener("click", cb);
|
||||||
|
},
|
||||||
|
onDropNonWebRTC: (cb) => {
|
||||||
|
dropNonWebRTCbutton.addEventListener("click", cb);
|
||||||
|
},
|
||||||
|
setRelayMeshInfo: (gossipSub) => {
|
||||||
|
relayMeshInfo.innerHTML = "";
|
||||||
|
|
||||||
|
Array.from(gossipSub.peers)
|
||||||
|
.map((peerId) => {
|
||||||
|
let inbound = gossipSub.streamsInbound.get(peerId);
|
||||||
|
inbound = inbound ? inbound.rawStream.constructor.name : "none";
|
||||||
|
|
||||||
|
let outbound = gossipSub.streamsOutbound.get(peerId);
|
||||||
|
outbound = outbound ? outbound.rawStream.constructor.name : "none";
|
||||||
|
|
||||||
|
return [peerId, inbound, outbound];
|
||||||
|
})
|
||||||
|
.map(([peerId, inbound, outbound]) => {
|
||||||
|
relayMeshInfo.innerHTML += `${peerId}<br /><b>inbound</b>: ${inbound}\t<b>outbound</b>: ${outbound}`;
|
||||||
|
relayMeshInfo.innerHTML += "<br /><br />";
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
185
examples/relay-chat/style.css
Normal file
185
examples/relay-chat/style.css
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
word-wrap: break-word;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-size: 16px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 + h4,
|
||||||
|
div + h4,
|
||||||
|
div + details,
|
||||||
|
div + div {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header div {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header div input {
|
||||||
|
min-width: 300px;
|
||||||
|
min-height: 30px;
|
||||||
|
width: 80%;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header div button {
|
||||||
|
min-width: 50px;
|
||||||
|
min-height: 30px;
|
||||||
|
width: 10%;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header div button + button {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
details {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
details p {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
summary {
|
||||||
|
cursor: pointer;
|
||||||
|
max-width: 100%;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
input,
|
||||||
|
textarea {
|
||||||
|
line-height: 1rem;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
min-height: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
width: 800px;
|
||||||
|
min-width: 300px;
|
||||||
|
max-width: 800px;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
#messages {
|
||||||
|
overflow-y: scroll;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message + .message {
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message :first-child {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message p + p {
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message span {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputArea {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
margin-top: 10px;
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls button {
|
||||||
|
flex-grow: 1;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#send {
|
||||||
|
background-color: #32d1a0;
|
||||||
|
border: none;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
#send:hover {
|
||||||
|
background-color: #3abd96;
|
||||||
|
}
|
||||||
|
#send:active {
|
||||||
|
background-color: #3ba183;
|
||||||
|
}
|
||||||
|
|
||||||
|
#exit {
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
background-color: #ff3a31;
|
||||||
|
}
|
||||||
|
#exit:hover {
|
||||||
|
background-color: #e4423a;
|
||||||
|
}
|
||||||
|
#exit:active {
|
||||||
|
background-color: #c84740;
|
||||||
|
}
|
||||||
|
|
||||||
|
.success {
|
||||||
|
color: #3ba183;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress {
|
||||||
|
color: #9ea13b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminated {
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
color: #c84740;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
flex-direction: column;
|
||||||
|
align-self: flex-end;
|
||||||
|
}
|
||||||
@ -13,7 +13,13 @@ module.exports = {
|
|||||||
mode: "development",
|
mode: "development",
|
||||||
plugins: [
|
plugins: [
|
||||||
new CopyWebpackPlugin({
|
new CopyWebpackPlugin({
|
||||||
patterns: ["index.html", "favicon.ico", "favicon.png", "manifest.json"],
|
patterns: [
|
||||||
|
"index.html",
|
||||||
|
"favicon.ico",
|
||||||
|
"favicon.png",
|
||||||
|
"manifest.json",
|
||||||
|
"style.css",
|
||||||
|
],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
@ -1,42 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
|
|
||||||
<title>JS-Waku light node example</title>
|
|
||||||
<link rel="apple-touch-icon" href="./favicon.png" />
|
|
||||||
<link rel="manifest" href="./manifest.json" />
|
|
||||||
<link rel="icon" href="./favicon.ico" />
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div><h2>Status</h2></div>
|
|
||||||
<div id="status"></div>
|
|
||||||
|
|
||||||
<div><h2>Local Peer Id</h2></div>
|
|
||||||
<div id="peer-id"></div>
|
|
||||||
|
|
||||||
<div><h2>Remote Peer Id</h2></div>
|
|
||||||
<div id="remote-peer-id"></div>
|
|
||||||
|
|
||||||
<label for="remote-multiaddr">Remote peer's multiaddr</label>
|
|
||||||
<input id="remote-multiaddr" type="text" value="" />
|
|
||||||
<button disabled id="dial" type="button">Dial</button>
|
|
||||||
<br />
|
|
||||||
<button disabled id="subscribe" type="button">Subscribe with Filter</button>
|
|
||||||
<button disabled id="unsubscribe" type="button">
|
|
||||||
Unsubscribe with Filter
|
|
||||||
</button>
|
|
||||||
<br />
|
|
||||||
<label for="textInput">Message text</label>
|
|
||||||
<input id="textInput" placeholder="Type your message here" type="text" />
|
|
||||||
<button disabled id="sendButton" type="button">
|
|
||||||
Send message using Light Push
|
|
||||||
</button>
|
|
||||||
<br />
|
|
||||||
<div id="messages"></div>
|
|
||||||
|
|
||||||
<script src="https://unpkg.com/@multiformats/multiaddr@12.1.1/dist/index.min.js"></script>
|
|
||||||
<script src="./index.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@ -1,132 +0,0 @@
|
|||||||
import {
|
|
||||||
createRelayNode,
|
|
||||||
waitForRemotePeer,
|
|
||||||
createEncoder,
|
|
||||||
createDecoder,
|
|
||||||
utf8ToBytes,
|
|
||||||
bytesToUtf8,
|
|
||||||
} from "@waku/sdk";
|
|
||||||
import { webSockets } from "@libp2p/websockets";
|
|
||||||
import { all as filterAll } from "@libp2p/websockets/filters";
|
|
||||||
|
|
||||||
import { webRTC } from "@libp2p/webrtc";
|
|
||||||
import { circuitRelayTransport } from "libp2p/circuit-relay";
|
|
||||||
|
|
||||||
const peerIdDiv = document.getElementById("peer-id");
|
|
||||||
const remotePeerIdDiv = document.getElementById("remote-peer-id");
|
|
||||||
const statusDiv = document.getElementById("status");
|
|
||||||
const remoteMultiAddrDiv = document.getElementById("remote-multiaddr");
|
|
||||||
const dialButton = document.getElementById("dial");
|
|
||||||
const subscribeButton = document.getElementById("subscribe");
|
|
||||||
const unsubscribeButton = document.getElementById("unsubscribe");
|
|
||||||
const messagesDiv = document.getElementById("messages");
|
|
||||||
const textInput = document.getElementById("textInput");
|
|
||||||
const sendButton = document.getElementById("sendButton");
|
|
||||||
|
|
||||||
const ContentTopic = "/js-waku-examples/1/chat/utf8";
|
|
||||||
const decoder = createDecoder(ContentTopic);
|
|
||||||
const encoder = createEncoder({ contentTopic: ContentTopic });
|
|
||||||
let messages = [];
|
|
||||||
let unsubscribe;
|
|
||||||
|
|
||||||
const updateMessages = (msgs, div) => {
|
|
||||||
div.innerHTML = "<ul>";
|
|
||||||
messages.forEach((msg) => (div.innerHTML += "<li>" + msg + "</li>"));
|
|
||||||
div.innerHTML += "</ul>";
|
|
||||||
};
|
|
||||||
|
|
||||||
statusDiv.innerHTML = "<p>Creating Waku node.</p>";
|
|
||||||
const node = await createRelayNode({
|
|
||||||
libp2p: {
|
|
||||||
addresses: {
|
|
||||||
listen: ["/webrtc"],
|
|
||||||
},
|
|
||||||
connectionGater: {
|
|
||||||
denyDialMultiaddr: () => {
|
|
||||||
// by default we refuse to dial local addresses from the browser since they
|
|
||||||
// are usually sent by remote peers broadcasting undialable multiaddrs but
|
|
||||||
// here we are explicitly connecting to a local node so do not deny dialing
|
|
||||||
// any discovered address
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
transports: [
|
|
||||||
webRTC({}),
|
|
||||||
circuitRelayTransport({
|
|
||||||
discoverRelays: 1,
|
|
||||||
}),
|
|
||||||
webSockets({ filter: filterAll }),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
window.node = node;
|
|
||||||
|
|
||||||
statusDiv.innerHTML = "<p>Starting Waku node.</p>";
|
|
||||||
await node.start();
|
|
||||||
statusDiv.innerHTML = "<p>Waku node started.</p>";
|
|
||||||
peerIdDiv.innerHTML = "<p>" + node.libp2p.peerId.toString() + "</p>";
|
|
||||||
dialButton.disabled = false;
|
|
||||||
|
|
||||||
dialButton.onclick = async () => {
|
|
||||||
const ma = remoteMultiAddrDiv.value;
|
|
||||||
if (!ma) {
|
|
||||||
statusDiv.innerHTML = "<p>Error: No multiaddr provided.</p>";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
statusDiv.innerHTML = "<p>Dialing peer.</p>";
|
|
||||||
const multiaddr = MultiformatsMultiaddr.multiaddr(ma);
|
|
||||||
await node.dial(multiaddr, ["relay"]);
|
|
||||||
await waitForRemotePeer(node, ["relay"]);
|
|
||||||
const peers = await node.libp2p.peerStore.all();
|
|
||||||
statusDiv.innerHTML = "<p>Peer dialed.</p>";
|
|
||||||
remotePeerIdDiv.innerHTML = "<p>" + peers[0].id.toString() + "</p>";
|
|
||||||
textInput.disabled = false;
|
|
||||||
sendButton.disabled = false;
|
|
||||||
subscribeButton.disabled = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const callback = (wakuMessage) => {
|
|
||||||
const text = bytesToUtf8(wakuMessage.payload);
|
|
||||||
const timestamp = wakuMessage.timestamp.toString();
|
|
||||||
messages.push(text + " - " + timestamp);
|
|
||||||
updateMessages(messages, messagesDiv);
|
|
||||||
};
|
|
||||||
|
|
||||||
subscribeButton.onclick = async () => {
|
|
||||||
unsubscribe = await node.relay.subscribe([decoder], callback);
|
|
||||||
unsubscribeButton.disabled = false;
|
|
||||||
subscribeButton.disabled = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
unsubscribeButton.onclick = async () => {
|
|
||||||
await unsubscribe();
|
|
||||||
unsubscribe = undefined;
|
|
||||||
unsubscribeButton.disabled = true;
|
|
||||||
subscribeButton.disabled = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
sendButton.onclick = async () => {
|
|
||||||
const text = textInput.value;
|
|
||||||
|
|
||||||
await node.relay.send(encoder, {
|
|
||||||
payload: utf8ToBytes(text),
|
|
||||||
});
|
|
||||||
console.log("Message sent!");
|
|
||||||
textInput.value = null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const GONODE = "16Uiu2HAmUdyH4P2UhgX3hTCbeeJHpxxfsn8EdyHkMnPyb54a92jZ";
|
|
||||||
const root = `/ip4/192.168.0.101/tcp/60001/ws/p2p/${GONODE}`;
|
|
||||||
|
|
||||||
remoteMultiAddrDiv.value = root;
|
|
||||||
|
|
||||||
window.dial = (id) => node.dial(`${root}/p2p-circuit/webrtc/p2p/${id}`);
|
|
||||||
|
|
||||||
window.drop = () =>
|
|
||||||
Array.from(node.libp2p.components.connectionManager.connections.entries())
|
|
||||||
.filter((c) => c[0].toString() === GONODE)
|
|
||||||
.map((c) => console.log(c[1][0].close()));
|
|
||||||
/*
|
|
||||||
/ip4/127.0.0.1/tcp/60001/ws/p2p/16Uiu2HAm3s9fFHbcVrKQz2h5fMAsUzm3AeCUxg52e3SBFjt7q4Gg
|
|
||||||
/ip4/127.0.0.1/tcp/60001/ws/p2p/16Uiu2HAm3s9fFHbcVrKQz2h5fMAsUzm3AeCUxg52e3SBFjt7q4Gg/p2p-circuit/p2p/12D3KooWMghpY8592CnQQTyg4fiSJcUuA7Pu8n8YzUPYaLjzH7Yo/p2p-circuit/webrtc/p2p/12D3KooWMghpY8592CnQQTyg4fiSJcUuA7Pu8n8YzUPYaLjzH7Yo
|
|
||||||
*/
|
|
||||||
Loading…
x
Reference in New Issue
Block a user