diff --git a/packages/app/src/Element/Event/Create/util.ts b/packages/app/src/Element/Event/Create/util.ts index 91b880c3d..c104acbf3 100644 --- a/packages/app/src/Element/Event/Create/util.ts +++ b/packages/app/src/Element/Event/Create/util.ts @@ -1,6 +1,6 @@ import { NostrEvent, OkResponse, SystemInterface } from "@snort/system"; import { removeUndefined } from "@snort/shared"; -import {getWebRtcPool} from "@/webrtc"; +import { getWebRtcPool } from "@/webrtc"; export async function sendEventToRelays( system: SystemInterface, diff --git a/packages/app/src/webrtc/WebRTCConnection.ts b/packages/app/src/webrtc/WebRTCConnection.ts index 74658f405..db6aaa318 100644 --- a/packages/app/src/webrtc/WebRTCConnection.ts +++ b/packages/app/src/webrtc/WebRTCConnection.ts @@ -5,7 +5,11 @@ export class WebRTCConnection extends EventEmitter { private peerConnection: RTCPeerConnection; private dataChannel: RTCDataChannel; - constructor(private socket: Socket, configuration: RTCConfiguration, public peerId: string) { + constructor( + private socket: Socket, + configuration: RTCConfiguration, + public peerId: string, + ) { super(); this.peerConnection = new RTCPeerConnection(configuration); this.dataChannel = this.peerConnection.createDataChannel("data"); @@ -18,24 +22,24 @@ export class WebRTCConnection extends EventEmitter { } public async handleOffer(offer: RTCSessionDescriptionInit): Promise { - this.log('Received offer', offer); + this.log("Received offer", offer); await this.peerConnection.setRemoteDescription(new RTCSessionDescription(offer)); - await this.sendLocalDescription('answer'); + await this.sendLocalDescription("answer"); } public async handleAnswer(answer: RTCSessionDescriptionInit): Promise { - this.log('Received answer', answer); + this.log("Received answer", answer); await this.peerConnection.setRemoteDescription(new RTCSessionDescription(answer)); } public handleCandidate(candidate: RTCIceCandidateInit): void { - this.log('Received ICE candidate', candidate); + this.log("Received ICE candidate", candidate); this.peerConnection.addIceCandidate(new RTCIceCandidate(candidate)); } - private async sendLocalDescription(type: 'offer' | 'answer'): Promise { + private async sendLocalDescription(type: "offer" | "answer"): Promise { let description; - if (type === 'offer') { + if (type === "offer") { description = await this.peerConnection.createOffer(); } else { description = await this.peerConnection.createAnswer(); @@ -46,19 +50,19 @@ export class WebRTCConnection extends EventEmitter { } private setupDataChannel(): void { - this.dataChannel.onopen = () => this.log('Data channel opened'); - this.dataChannel.onclose = () => this.log('Data channel closed'); + this.dataChannel.onopen = () => this.log("Data channel opened"); + this.dataChannel.onclose = () => this.log("Data channel closed"); this.dataChannel.onmessage = event => this.handleDataChannelMessage(event); } private handleDataChannelMessage(event: MessageEvent): void { this.log(`-> "${event.data}"`); - if (event.data === 'ping') { - this.send('pong'); + if (event.data === "ping") { + this.send("pong"); } else { try { const data = JSON.parse(event.data); - this.emit('event', data); + this.emit("event", data); } catch (e) { // Ignore } @@ -66,32 +70,32 @@ export class WebRTCConnection extends EventEmitter { } public send(data: any): void { - if (this.dataChannel.readyState === 'open') { + if (this.dataChannel.readyState === "open") { this.log(`<- "${data}"`); this.dataChannel.send(data); } } public async handleHello(): Promise { - if (this.peerConnection.connectionState === 'new') { - await this.sendLocalDescription('offer'); + if (this.peerConnection.connectionState === "new") { + await this.sendLocalDescription("offer"); } } private registerPeerConnectionEvents(): void { this.peerConnection.onicecandidate = event => { if (event.candidate) { - this.log('Local ICE candidate:', event.candidate); - this.socket.emit('candidate', { candidate: event.candidate.toJSON(), recipient: this.peerId }); + this.log("Local ICE candidate:", event.candidate); + this.socket.emit("candidate", { candidate: event.candidate.toJSON(), recipient: this.peerId }); } }; this.peerConnection.oniceconnectionstatechange = () => { - this.log('ICE Connection State Change:', this.peerConnection.iceConnectionState); + this.log("ICE Connection State Change:", this.peerConnection.iceConnectionState); }; this.peerConnection.onconnectionstatechange = () => { - this.log('WebRTC Connection State Change:', this.peerConnection.connectionState); + this.log("WebRTC Connection State Change:", this.peerConnection.connectionState); }; this.peerConnection.ondatachannel = event => { diff --git a/packages/app/src/webrtc/WebRTCPool.ts b/packages/app/src/webrtc/WebRTCPool.ts index fc89b3c0d..f51563bd6 100644 --- a/packages/app/src/webrtc/WebRTCPool.ts +++ b/packages/app/src/webrtc/WebRTCPool.ts @@ -1,5 +1,5 @@ -import {io, Socket} from 'socket.io-client'; -import {WebRTCConnection} from '@/webrtc/WebRTCConnection'; +import { io, Socket } from "socket.io-client"; +import { WebRTCConnection } from "@/webrtc/WebRTCConnection"; import EventEmitter from "eventemitter3"; const MAX_CONNECTIONS = 5; @@ -19,14 +19,14 @@ class WebRTCPool extends EventEmitter { } private sayHello(): void { - this.signalingServer.emit('hello', this.peerId); + this.signalingServer.emit("hello", this.peerId); } public send(data: any, recipients?: string[]): void { this.peers.forEach(conn => { if (!recipients || recipients.includes(conn.peerId)) { try { - conn.send(typeof data === 'string' ? data : JSON.stringify(data)); + conn.send(typeof data === "string" ? data : JSON.stringify(data)); } catch (e) { console.error(e); } @@ -36,10 +36,10 @@ class WebRTCPool extends EventEmitter { public createConnection(peerId: string): WebRTCConnection { if (this.peers.size >= MAX_CONNECTIONS) { - throw new Error('Maximum connections reached'); + throw new Error("Maximum connections reached"); } const connection = new WebRTCConnection(this.signalingServer, this.configuration, peerId); - connection.on('event', (event: any) => this.emit('event', event)); + connection.on("event", (event: any) => this.emit("event", event)); this.peers.set(peerId, connection); return connection; } @@ -51,31 +51,34 @@ class WebRTCPool extends EventEmitter { } private registerSocketEvents(): void { - this.signalingServer.on('connect', () => { - console.log('Connected to signaling server'); + this.signalingServer.on("connect", () => { + console.log("Connected to signaling server"); this.sayHello(); }); - this.signalingServer.on('offer', ({offer, sender}: { offer: RTCSessionDescriptionInit; sender: string }) => { + this.signalingServer.on("offer", ({ offer, sender }: { offer: RTCSessionDescriptionInit; sender: string }) => { this.handleConnectionEvent(sender, async conn => await conn.handleOffer(offer)); }); - this.signalingServer.on('answer', ({answer, sender}: { answer: RTCSessionDescriptionInit; sender: string }) => { + this.signalingServer.on("answer", ({ answer, sender }: { answer: RTCSessionDescriptionInit; sender: string }) => { this.handleConnectionEvent(sender, async conn => await conn.handleAnswer(answer)); }); - this.signalingServer.on('candidate', ({candidate, sender}: { candidate: RTCIceCandidateInit; sender: string }) => { - this.handleConnectionEvent(sender, conn => conn.handleCandidate(candidate)); - }); + this.signalingServer.on( + "candidate", + ({ candidate, sender }: { candidate: RTCIceCandidateInit; sender: string }) => { + this.handleConnectionEvent(sender, conn => conn.handleCandidate(candidate)); + }, + ); - this.signalingServer.on('hello', (sender: string) => { - console.log('Received hello from', sender); + this.signalingServer.on("hello", (sender: string) => { + console.log("Received hello from", sender); this.handleConnectionEvent(sender, conn => conn.handleHello()); }); } - + public close(): void { - console.log('closing pool'); + console.log("closing pool"); this.signalingServer.close(); for (const conn of this.peers.values()) { conn.close(); @@ -83,4 +86,4 @@ class WebRTCPool extends EventEmitter { } } -export default WebRTCPool; \ No newline at end of file +export default WebRTCPool; diff --git a/packages/app/src/webrtc/index.ts b/packages/app/src/webrtc/index.ts index a34f23938..bfa5ce2b1 100644 --- a/packages/app/src/webrtc/index.ts +++ b/packages/app/src/webrtc/index.ts @@ -1,4 +1,4 @@ -import {LoginStore} from "@/Login"; +import { LoginStore } from "@/Login"; import WebRTCPool from "@/webrtc/WebRTCPool"; let publicKey: string | undefined; @@ -9,13 +9,17 @@ LoginStore.hook(() => { const login = LoginStore.takeSnapshot(); if (login.publicKey && !login.readonly && login.publicKey !== publicKey) { publicKey = login.publicKey; - if (location.hostname === 'localhost') { + if (location.hostname === "localhost") { pool?.close(); interval && clearInterval(interval); - pool = new WebRTCPool('http://localhost:3000', { - iceServers: [{ urls: 'stun:localhost:3478' }], - }, login.publicKey); - interval = setInterval(() => pool?.send('ping'), 10000); + pool = new WebRTCPool( + "http://localhost:3000", + { + iceServers: [{ urls: "stun:localhost:3478" }], + }, + login.publicKey, + ); + interval = setInterval(() => pool?.send("ping"), 10000); } } }); diff --git a/packages/webrtc-server/README.md b/packages/webrtc-server/README.md index 4c8ed4134..8ca1439f0 100644 --- a/packages/webrtc-server/README.md +++ b/packages/webrtc-server/README.md @@ -5,4 +5,4 @@ yarn yarn start ``` -Websocket (socket.io) based signaling server for WebRTC. \ No newline at end of file +Websocket (socket.io) based signaling server for WebRTC. diff --git a/packages/webrtc-server/index.js b/packages/webrtc-server/index.js index acb020864..381cee316 100644 --- a/packages/webrtc-server/index.js +++ b/packages/webrtc-server/index.js @@ -1,37 +1,37 @@ const PORT = process.env.PORT || 3000; -const io = require('socket.io')(PORT, { +const io = require("socket.io")(PORT, { cors: { origin: "*", - methods: ["GET", "POST"] - } + methods: ["GET", "POST"], + }, }); const peerSocketMap = new Map(); const socketPeerMap = new Map(); -io.on('connection', socket => { +io.on("connection", socket => { console.log(`New client connected, socket ID: ${socket.id}`); const emitToTarget = (eventType, data, recipient) => { const targetSocketId = peerSocketMap.get(recipient); const sender = socketPeerMap.get(socket.id); if (sender && targetSocketId) { - io.to(targetSocketId).emit(eventType, {...data, sender}); + io.to(targetSocketId).emit(eventType, { ...data, sender }); } }; - socket.on('offer', data => emitToTarget('offer', data, data.recipient)); - socket.on('answer', data => emitToTarget('answer', data, data.recipient)); - socket.on('candidate', data => emitToTarget('candidate', data, data.recipient)); + socket.on("offer", data => emitToTarget("offer", data, data.recipient)); + socket.on("answer", data => emitToTarget("answer", data, data.recipient)); + socket.on("candidate", data => emitToTarget("candidate", data, data.recipient)); - socket.on('hello', peerId => { + socket.on("hello", peerId => { console.log(`Received hello from ${peerId}`); peerSocketMap.set(peerId, socket.id); socketPeerMap.set(socket.id, peerId); - socket.broadcast.emit('hello', peerId); + socket.broadcast.emit("hello", peerId); }); - socket.on('disconnect', () => { + socket.on("disconnect", () => { peerSocketMap.delete(socketPeerMap.get(socket.id)); socketPeerMap.delete(socket.id); console.log(`Client disconnected, socket ID: ${socket.id}`); @@ -43,14 +43,14 @@ console.log(`Signaling server running on port ${PORT}`); const Ministun = require("ministun"); const stunConfig = { - udp4: true, - udp6: true, - port: 3478, - log: console.log, - err: console.err, - sw: true + udp4: true, + udp6: true, + port: 3478, + log: console.log, + err: console.err, + sw: true, }; const server = new Ministun(stunConfig); -console.log(`STUN server running on port ${stunConfig.port}`) \ No newline at end of file +console.log(`STUN server running on port ${stunConfig.port}`);