From 135353806b0c388e2d64638594d881bf59abfd46 Mon Sep 17 00:00:00 2001 From: Claas Date: Mon, 8 Dec 2025 23:16:25 +0100 Subject: [PATCH] Fix bugs --- app/src/broadcast.ts | 16 +++++++++---- app/src/routes/chat.tsx | 31 +++++++++++++++++++++++-- app/src/service-worker/serviceWorker.ts | 15 +++++++++--- core/src/lib.rs | 1 + core/src/v2/serializable.rs | 8 ++++--- delivery-service/src/main.rs | 3 +++ 6 files changed, 61 insertions(+), 13 deletions(-) diff --git a/app/src/broadcast.ts b/app/src/broadcast.ts index 7eca8b4..cc2929d 100644 --- a/app/src/broadcast.ts +++ b/app/src/broadcast.ts @@ -1,13 +1,19 @@ import { onCleanup } from "solid-js"; -import { Group } from "./database/schema"; +import { Group, IncomingMessage } from "./database/schema"; export const BROADCAST_NAME = "meal"; const broadcast = new BroadcastChannel(BROADCAST_NAME); -export type BroadcaseMessage = { - type: "Group created"; - group: Group; -}; +export type BroadcaseMessage = + | { + type: "Group created"; + group: Group; + } + | { + type: "Message received"; + groupId: string; + message: IncomingMessage; + }; export function broadcastMessage(message: BroadcaseMessage) { broadcast.postMessage(message); diff --git a/app/src/routes/chat.tsx b/app/src/routes/chat.tsx index 8314ecd..b53ae79 100644 --- a/app/src/routes/chat.tsx +++ b/app/src/routes/chat.tsx @@ -1,7 +1,15 @@ import { Navigate, useParams } from "@solidjs/router"; -import { createResource, For, JSX, Show, Suspense } from "solid-js"; +import { + createEffect, + createResource, + For, + JSX, + Show, + Suspense, +} from "solid-js"; import { getGroup } from "../database"; import { setupCrackle } from "../useCrackle"; +import { useBroadcast } from "../broadcast"; // TODO take this inspiration https://firebasestorage.googleapis.com/v0/b/design-spec/o/projects%2Fgoogle-material-3%2Fimages%2Fly7219l1-1.png?alt=media&token=67ff316b-7515-4e9f-9971-4e580290b1f2 // from https://m3.material.io/foundations/layout/applying-layout/compact#283b4432-e3ee-46df-aa66-9ec87965c6ef @@ -12,7 +20,26 @@ export default function Chat() { return ; } - const [group] = createResource(parameters.groupId, getGroup); + const [group, { mutate }] = createResource(parameters.groupId, getGroup); + createEffect(() => { + const currentGroup = group(); + if (currentGroup === undefined) return; + + // Can be used in effects because the internal onCleanup unsubscribes from the event when this effect is cleaned up + useBroadcast((event) => { + if ( + event.data.type !== "Message received" || + event.data.groupId !== parameters.groupId + ) + return; + + const message = event.data.message; + mutate((group) => { + currentGroup.messages.push(message); + return group; + }); + }); + }); async function handleSend( event: Parameters>[0] diff --git a/app/src/service-worker/serviceWorker.ts b/app/src/service-worker/serviceWorker.ts index 97b64e0..87624ad 100644 --- a/app/src/service-worker/serviceWorker.ts +++ b/app/src/service-worker/serviceWorker.ts @@ -128,7 +128,7 @@ async function postMessage(friendId: string, body: Uint8Array) { //TODO error handling //TODO retry - await fetch(request); + return await fetch(request); } function assertNotShared( @@ -223,7 +223,9 @@ const handler = { void updateClient(client); assertNotShared(welcomePackage); - await postMessage(keyPackage.friend.id, welcomePackage); + const response = await postMessage(keyPackage.friend.id, welcomePackage); + if (response.status !== 201) + throw new Error(`Unexpected status code response ${response.status}`); }, async sendMessage(message: OutgoingMessage) { @@ -232,7 +234,7 @@ const handler = { const client = await getClient; const body = client.send_message(group.id, { - sent_at: message.sentAt, + sent_at: message.sentAt.toISOString(), text: message.text, }); @@ -281,6 +283,7 @@ self.addEventListener("activate", () => { async function receiveMessage(event: MessageEvent) { const client = await getClient; const message = client.process_message(new Uint8Array(event.data)); + console.debug("Processed message", message.type); void updateClient(client); switch (message.type) { case "Welcome": { @@ -314,6 +317,11 @@ async function receiveMessage(event: MessageEvent) { // Assume it is sorted by time group.messages.push(messageEntry); await updateGroup(group); + broadcastMessage({ + type: "Message received", + groupId: group.id, + message: messageEntry, + }); } return; } @@ -324,6 +332,7 @@ async function setupWebsocket() { // Assume client id does not change const client = await getClient; + console.debug("[Service worker] Connecting to socket", client.id); // https:// is automatically replaced with wss:// const socketUrl = new URL(client.id, messagesUrl); const socket = new WebSocket(socketUrl); diff --git a/core/src/lib.rs b/core/src/lib.rs index e21f78f..e649e94 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -59,6 +59,7 @@ pub struct Friend { pub struct MessageContent { /// The time the sender said they supposedly sent the message #[serde(with = "time::serde::iso8601")] + // #[tsify(type = "string")] sent_at: OffsetDateTime, text: String, } diff --git a/core/src/v2/serializable.rs b/core/src/v2/serializable.rs index e5fc9f0..8549f4c 100644 --- a/core/src/v2/serializable.rs +++ b/core/src/v2/serializable.rs @@ -253,7 +253,8 @@ impl Client { let mut vector = Vec::new(); vector.push(welcome); vector.push(message); - Ok(TlsSliceU8(&vector).tls_serialize_detached()?) + // The u16 describes the length of the message e.g. u8 would be max length 255 + Ok(TlsSliceU16(&vector).tls_serialize_detached()?) } fn process_private_message( @@ -335,7 +336,8 @@ impl Client { } pub fn process_message(&mut self, data: &[u8]) -> Result { - let mut messages = TlsVecU8::::tls_deserialize_exact_bytes(data)? + // Have to use TlsVecU16 because the bit length (16) stands for the space reserved to encode the length of the message not the integer stored like in a Vec + let mut messages = TlsVecU16::::tls_deserialize_exact_bytes(data)? .into_vec() .into_iter(); @@ -369,7 +371,7 @@ impl Client { // We can batch send messages so we need to wrap it in a collection let messages = &[message]; - let message = TlsSliceU8(messages); + let message = TlsSliceU16(messages); let serialized = message.tls_serialize_detached()?; diff --git a/delivery-service/src/main.rs b/delivery-service/src/main.rs index 3a0aa7c..3f3a2ca 100644 --- a/delivery-service/src/main.rs +++ b/delivery-service/src/main.rs @@ -20,6 +20,7 @@ use tower_http::{ set_header::SetResponseHeaderLayer, set_status::SetStatus, }; +use tracing::debug; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; mod extractor; @@ -124,6 +125,7 @@ async fn create_message( let channels = state.channels.lock().await; //TODO think about not leaking if they exist or not //TODO think about leaking data through timings + debug!("New message for client {}", to); let Some(sender) = channels.get(to.as_ref()) else { return StatusCode::NOT_FOUND; }; @@ -136,6 +138,7 @@ async fn create_message( } async fn handle_socket(mut socket: WebSocket, State(state): State, client_id: Arc) { + debug!("New socket connection for client {}", client_id); //TODO keep alive //TODO add client authentication to avoid session hijacking // Hijackers can deny messages to the client and analyze meta data but not read messages if they don't have the clients credentials -- 2.51.2