summaryrefslogtreecommitdiff
path: root/client-web
diff options
context:
space:
mode:
Diffstat (limited to 'client-web')
-rw-r--r--client-web/public/app.html32
-rw-r--r--client-web/public/assets/style/master.css4
-rw-r--r--client-web/public/start.html24
-rw-r--r--client-web/source/index.ts2
-rw-r--r--client-web/source/local_user.ts4
-rw-r--r--client-web/source/protocol/mod.ts46
-rw-r--r--client-web/source/remote_user.ts12
-rw-r--r--client-web/source/rnnoise.ts2
-rw-r--r--client-web/source/room.ts46
-rw-r--r--client-web/source/user.ts13
10 files changed, 113 insertions, 72 deletions
diff --git a/client-web/public/app.html b/client-web/public/app.html
index 493796f..693e9a2 100644
--- a/client-web/public/app.html
+++ b/client-web/public/app.html
@@ -1,20 +1,24 @@
<!DOCTYPE html>
<html lang="en">
- <head>
- <meta charset="UTF-8" />
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <head>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
- <script defer async type="module" src="/_assets/bundle.js"></script>
- <link rel="stylesheet" href="/_assets/style/master.css" />
+ <script defer async type="module" src="/assets/bundle.js"></script>
+ <link rel="stylesheet" href="/assets/style/master.css" />
- <title>keks-meet</title>
- </head>
+ <title>keks-meet</title>
+ </head>
- <body>
- <p>
- keks-meet needs evil javascript to be enabled. Don't be afraid though, all
- the code is free (AGPL-3.0-only)! Look at it on
- <a href="https://codeberg.org/metamuffin/keks-meet">codeberg</a>
- </p>
- </body>
+ <body>
+ <p>
+ keks-meet needs evil javascript to be enabled. Don't be afraid
+ though, all the code is free (AGPL-3.0-only)! Look at it on
+ <a href="https://codeberg.org/metamuffin/keks-meet">codeberg</a>
+ </p>
+ <p>
+ If you have JS enabled, check the browser console to see if
+ something else failed
+ </p>
+ </body>
</html>
diff --git a/client-web/public/assets/style/master.css b/client-web/public/assets/style/master.css
index 0b9e205..8a22d1b 100644
--- a/client-web/public/assets/style/master.css
+++ b/client-web/public/assets/style/master.css
@@ -10,8 +10,8 @@
}
:root {
- --bg: #263238;
- --bg-dark: #000a12;
+ --bg: #212121;
+ --bg-dark: #070707;
--bg-light: #354b58;
--bg-lighter: #4f5b62;
--bg-disabled: #720000;
diff --git a/client-web/public/start.html b/client-web/public/start.html
index 4a04174..db62bcf 100644
--- a/client-web/public/start.html
+++ b/client-web/public/start.html
@@ -18,8 +18,9 @@
the third version of the GNU Affero General Public Licence only.
</p>
<p>
- To get started, just enter a unique idenfier, click 'Join', then
- share the URL with your partner.
+ To get started, click 'Join' and share the URL with your
+ partner. You can also optionally customize the url by entering a
+ <b>secure/unguessable(!!!)</b> identifier below.
</p>
<noscript>
keks-meet needs evil javascript to be enabled. Don't be afraid
@@ -30,18 +31,21 @@
const room_input = document.createElement("input");
room_input.type = "text";
room_input.id = "room-id-input";
- room_input.placeholder = "Room ID (leave blank for random id)";
+ room_input.placeholder = "Room ID";
const submit = document.createElement("input");
submit.type = "button";
submit.addEventListener("click", () => {
- if (room_input.value.length == 0)
- room_input.value = Math.floor(Math.random() * 0x100000)
- .toString(16)
- .padStart(5, "0");
- window.location.pathname = `/${encodeURIComponent(
- room_input.value
- )}`;
+ if (room_input.value.length == 0) {
+ const random = window.crypto.getRandomValues(
+ new Uint8Array(32)
+ );
+ room_input.value = Array.from(random)
+ .map((b) => b.toString(16).padStart(2, "0"))
+ .join("");
+ }
+ const url = `/room#${encodeURIComponent(room_input.value)}`;
+ window.location.href = url;
});
submit.value = "Join room!";
diff --git a/client-web/source/index.ts b/client-web/source/index.ts
index 21bf4b8..5695573 100644
--- a/client-web/source/index.ts
+++ b/client-web/source/index.ts
@@ -19,7 +19,7 @@ export interface User {
window.onload = () => main()
export async function main() {
- document.body.querySelector("p")?.remove()
+ document.body.querySelectorAll("p").forEach(e => e.remove())
log("*", "starting up")
const room_name = window.location.pathname.substring("/".length)
const conn = await (new SignalingConnection().connect(room_name))
diff --git a/client-web/source/local_user.ts b/client-web/source/local_user.ts
index bb5b779..efedfc2 100644
--- a/client-web/source/local_user.ts
+++ b/client-web/source/local_user.ts
@@ -12,8 +12,8 @@ export class LocalUser extends User {
mic_gain?: GainNode
default_gain: number = PREFS.microphone_gain
- constructor(room: Room, id: number, name: string) {
- super(room, id, name)
+ constructor(room: Room, id: number) {
+ super(room, id)
this.el.classList.add("local")
this.local = true
this.create_controls()
diff --git a/client-web/source/protocol/mod.ts b/client-web/source/protocol/mod.ts
index 4fbd607..53cbb86 100644
--- a/client-web/source/protocol/mod.ts
+++ b/client-web/source/protocol/mod.ts
@@ -1,5 +1,6 @@
+import { ClientboundPacket, RelayMessage, ServerboundPacket } from "../../../common/packets.d.ts"
import { log } from "../logger.ts"
-import { crypto_seeded_key, crypt_hash } from "./crypto.ts"
+import { crypto_encrypt, crypto_seeded_key, crypt_decrypt, crypt_hash } from "./crypto.ts"
export class SignalingConnection {
room!: string
@@ -7,6 +8,9 @@ export class SignalingConnection {
signaling_id!: string
key!: CryptoKey
+ control_handler: (_packet: ClientboundPacket) => void = () => { }
+ relay_handler: (_sender: number, _message: RelayMessage) => void = () => { }
+
constructor() { }
async connect(room: string): Promise<SignalingConnection> {
this.key = await crypto_seeded_key(room)
@@ -14,10 +18,48 @@ export class SignalingConnection {
log("ws", "connecting…")
const ws_url = new URL(`${window.location.protocol.endsWith("s:") ? "wss" : "ws"}://${window.location.host}/signaling/${encodeURIComponent(this.signaling_id)}`)
this.websocket = new WebSocket(ws_url)
- await new Promise(r => this.websocket!.onopen = r)
+ this.websocket.onerror = () => this.on_error()
+ this.websocket.onmessage = e => {
+ if (typeof e.data == "string") this.on_message(e.data)
+ }
+ await new Promise<void>(r => this.websocket!.onopen = () => {
+ this.on_open()
+ r()
+ })
log("ws", "connection opened")
return this
}
+ on_close() {
+ log("ws", "websocket closed");
+ setTimeout(() => {
+ window.location.reload()
+ }, 1000)
+ }
+ on_open() {
+ log("ws", "websocket opened");
+ setInterval(() => this.send_control({ ping: null }), 30000) // stupid workaround for nginx disconnecting inactive connections
+ }
+ on_error() {
+ log("error", "websocket error occurred!")
+ }
+ async on_message(data: string) {
+ const packet: ClientboundPacket = JSON.parse(data) // TODO dont crash if invalid
+ this.control_handler(packet)
+ if (packet.message) {
+ const inner_json = await crypt_decrypt(this.key, packet.message.message)
+ const inner: RelayMessage = JSON.parse(inner_json) // TODO make sure that protocol spec is met
+ this.relay_handler(packet.message.sender, inner)
+ }
+ }
+ send_control(data: ServerboundPacket) {
+ log("ws", `-> ${data.relay?.recipient ?? "*"}`, data)
+ this.websocket.send(JSON.stringify(data))
+ }
+ async send_relay(data: RelayMessage, recipient?: number | null) {
+ recipient ??= undefined // null -> undefined
+ const message = await crypto_encrypt(this.key, JSON.stringify(data))
+ this.send_control({ relay: { recipient, message } })
+ }
}
diff --git a/client-web/source/remote_user.ts b/client-web/source/remote_user.ts
index 6cc57a5..5283a7b 100644
--- a/client-web/source/remote_user.ts
+++ b/client-web/source/remote_user.ts
@@ -10,13 +10,13 @@ export class RemoteUser extends User {
peer: RTCPeerConnection
negotiation_busy = false
- constructor(room: Room, id: number, name: string) {
- super(room, id, name)
- log("usermodel", `added remote user: ${id} ${JSON.stringify(name)}`)
+ constructor(room: Room, id: number) {
+ super(room, id)
+ log("usermodel", `added remote user: ${id}`)
this.peer = new RTCPeerConnection(servers)
this.peer.onicecandidate = ev => {
if (!ev.candidate) return
- room.websocket_send({ relay: { recipient: this.id, message: { ice_candidate: ev.candidate.toJSON() } } })
+ room.signaling.send_relay({ ice_candidate: ev.candidate.toJSON() }, this.id)
}
this.peer.ontrack = ev => {
const t = ev.track
@@ -38,7 +38,7 @@ export class RemoteUser extends User {
await this.peer.setLocalDescription(offer_description)
const offer = { type: offer_description.type, sdp: offer_description.sdp }
log("webrtc", `sent offer: ${this.name}`, { a: offer })
- this.room.websocket_send({ relay: { recipient: this.id, message: { offer } } })
+ this.room.signaling.send_relay({ offer }, this.id)
}
async on_offer(offer: RTCSessionDescriptionInit) {
this.negotiation_busy = true
@@ -52,7 +52,7 @@ export class RemoteUser extends User {
await this.peer.setLocalDescription(answer_description)
const answer = { type: answer_description.type, sdp: answer_description.sdp }
log("webrtc", `sent answer: ${this.name}`, { a: answer })
- this.room.websocket_send({ relay: { recipient: this.id, message: { answer } } })
+ this.room.signaling.send_relay({ answer }, this.id)
this.negotiation_busy = false
}
async on_answer(answer: RTCSessionDescriptionInit) {
diff --git a/client-web/source/rnnoise.ts b/client-web/source/rnnoise.ts
index d6efb3b..2e0f857 100644
--- a/client-web/source/rnnoise.ts
+++ b/client-web/source/rnnoise.ts
@@ -23,7 +23,7 @@ export async function get_rnnoise_node(context: AudioContext): Promise<RNNoiseNo
if (!RNNoiseNode) {
log("rnnoise", "loading wasm...")
script = document.createElement("script")
- script.src = "/_assets/rnnoise/rnnoise-runtime.js"
+ script.src = "/assets/rnnoise/rnnoise-runtime.js"
script.defer = true
document.head.appendChild(script)
//@ts-ignore asdfsfad
diff --git a/client-web/source/room.ts b/client-web/source/room.ts
index 6c9a7ba..9f27230 100644
--- a/client-web/source/room.ts
+++ b/client-web/source/room.ts
@@ -4,7 +4,7 @@ import { log } from "./logger.ts";
import { RemoteUser } from "./remote_user.ts";
import { User } from "./user.ts";
import { LocalUser } from "./local_user.ts";
-import { ServerboundPacket, ClientboundPacket } from "../../common/packets.d.ts";
+import { ClientboundPacket, RelayMessage } from "../../common/packets.d.ts";
import { SignalingConnection } from "./protocol/mod.ts";
export class Room {
@@ -17,14 +17,13 @@ export class Room {
constructor(public signaling: SignalingConnection) {
this.el = document.createElement("div")
this.el.classList.add("room")
+ this.signaling.control_handler = this.control_handler
+ this.signaling.relay_handler = this.relay_handler
}
- websocket_send(data: ServerboundPacket) {
- log("ws", `-> ${data.relay?.recipient ?? "*"}`, data)
- // this.websocket.send(JSON.stringify(data))
- }
- websocket_message(packet: ClientboundPacket) {
- log("ws", `<- ${packet.message?.sender ?? "control packet"}: `, packet);
+ control_handler(packet: ClientboundPacket) {
+ if (packet.message) return // let the relay handler do that
+ log("ws", `<- [control packet]: `, packet);
if (packet.init) {
this.my_id = packet.init.your_id
// no need to check compat for now because this is hosted in the same place
@@ -33,9 +32,9 @@ export class Room {
const p = packet.client_join
log("*", `${p.id} joined`);
if (p.id == this.my_id) {
- this.local_user = new LocalUser(this, p.id, p.name);
+ this.local_user = new LocalUser(this, p.id);
} else {
- const ru = new RemoteUser(this, p.id, p.name)
+ const ru = new RemoteUser(this, p.id)
this.local_user.add_initial_to_remote(ru)
ru.offer()
this.users.set(p.id, ru)
@@ -48,26 +47,17 @@ export class Room {
this.users.delete(p.id)
this.remote_users.delete(p.id)
return
- } else if (packet.message) {
- const p = packet.message;
- const sender = this.users.get(p.sender)
- if (sender instanceof RemoteUser) {
- // if (p.message.ice_candidate) sender.add_ice_candidate(p.message.ice_candidate)
- // if (p.message.offer) sender.on_offer(p.message.offer)
- // if (p.message.answer) sender.on_answer(p.message.answer)
- } else {
- console.log("!", p, sender);
- }
}
+
}
- websocket_close() {
- log("ws", "websocket closed");
- setTimeout(() => {
- window.location.reload()
- }, 1000)
- }
- websocket_open() {
- log("ws", "websocket opened");
- setInterval(() => this.websocket_send({ ping: null }), 30000) // stupid workaround for nginx disconnecting inactive connections
+ relay_handler(sender_id: number, message: RelayMessage) {
+ const sender = this.users.get(sender_id)
+ if (sender instanceof RemoteUser) {
+ if (message.ice_candidate) sender.add_ice_candidate(message.ice_candidate)
+ if (message.offer) sender.on_offer(message.offer)
+ if (message.answer) sender.on_answer(message.answer)
+ } else {
+ console.log("!", message, sender);
+ }
}
} \ No newline at end of file
diff --git a/client-web/source/user.ts b/client-web/source/user.ts
index dbf2862..057cdf4 100644
--- a/client-web/source/user.ts
+++ b/client-web/source/user.ts
@@ -6,13 +6,12 @@ import { TrackHandle } from "./track_handle.ts";
export abstract class User {
- el: HTMLElement
-
- local = false
-
+ protected el: HTMLElement
+ public local = false
+ public name?: string
protected tracks: Set<TrackHandle> = new Set()
- constructor(public room: Room, public id: number, public name: string) {
+ constructor(public room: Room, public id: number,) {
this.el = document.createElement("div")
this.el.classList.add("user")
this.room.el.append(this.el)
@@ -34,11 +33,13 @@ export abstract class User {
})
}
+ get display_name() { return this.name ?? `guest (${this.id})` }
+
setup_view() {
const info_el = document.createElement("div")
info_el.classList.add("info")
const name_el = document.createElement("span")
- name_el.textContent = this.name
+ name_el.textContent = this.display_name
name_el.classList.add("name")
info_el.append(name_el)
this.el.append(info_el)