diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 0cb689d..b80ab7c 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -51,6 +51,7 @@ jobs: tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} + # Would be cool, but doesn't play nice with cloud providers. They get confused 😕 # - name: Generate artifact attestation # uses: actions/attest-build-provenance@v1 # with: diff --git a/client/src/components/TopAppBar.tsx b/client/src/components/TopAppBar.tsx index ad6f870..1bc2815 100644 --- a/client/src/components/TopAppBar.tsx +++ b/client/src/components/TopAppBar.tsx @@ -1,5 +1,6 @@ import { JSX, VoidProps } from "solid-js"; +/** A top app bar component inspired by Material You */ export default function TopAppBar({ header, leadingAction, diff --git a/client/src/context.tsx b/client/src/context.tsx index 6ac26c3..df97c74 100644 --- a/client/src/context.tsx +++ b/client/src/context.tsx @@ -9,6 +9,7 @@ import { } from "solid-js"; import { ChatMessage } from "./routes/Chat"; +// Equivalent to the server-side Rust enum type // #[derive(Serialize)] // #[serde(tag = "type")] // enum ClientMessage { @@ -60,6 +61,7 @@ function addChatMessage(message: ChatMessage, contact: string) { setMessages((messages) => [message, ...messages]); } +// Some things to ensure security in production const isSecureRequired = window.location.protocol === "https:" || import.meta.env.MODE !== "development"; @@ -123,7 +125,7 @@ async function handleMessage(event: MessageEvent) { const state = { socket, name, setName, messagesByUser, users }; const Context = createContext(state); -// Close socket if name goes to null. Meaningif the user signs out +// Close socket if name goes to null. Meaning the user signs out createEffect>((previous) => { const value = name(); if (!(value === null && previous !== null)) return value; @@ -138,7 +140,6 @@ createEffect>((previous) => { return value; }, name()); - // Use new socket if name changes createEffect((previous) => { const id = name(); @@ -154,6 +155,7 @@ createEffect((previous) => { return newSocket; }, socket()); +// Custom hooks and component to simplify usage export function ContextProvider(properties: { children: JSX.Element }) { return ( {properties.children} diff --git a/client/src/routes/Chat.tsx b/client/src/routes/Chat.tsx index 6c0c05e..45b5fe5 100644 --- a/client/src/routes/Chat.tsx +++ b/client/src/routes/Chat.tsx @@ -11,11 +11,14 @@ export type ChatMessage = { sender: string; text: string; /** - * UTC unix timestamp in seconds + * UTC unix timestamp in milliseconds as it comes out of Date.now() */ time_utc: number; }; +/** + * The...yes chat, how did you know? + */ export default function Chat() { const parameters = useParams(); const { socket, name, messagesByUser } = useAppContext(); diff --git a/client/src/routes/Index.tsx b/client/src/routes/Index.tsx index f41218f..d553507 100644 --- a/client/src/routes/Index.tsx +++ b/client/src/routes/Index.tsx @@ -3,6 +3,9 @@ import { useAppContext } from "../context"; import { Navigate, useNavigate } from "@solidjs/router"; import TopAppBar from "../components/TopAppBar"; +/** + * Main page where users land on after set-up and see a list of available chats + */ export default function Index() { const { socket, name, setName, users } = useAppContext(); const navigate = useNavigate(); diff --git a/client/src/routes/SetUp.tsx b/client/src/routes/SetUp.tsx index 3b7382f..55505c9 100644 --- a/client/src/routes/SetUp.tsx +++ b/client/src/routes/SetUp.tsx @@ -2,6 +2,9 @@ import { useNavigate } from "@solidjs/router"; import { useAppContext } from "../context"; import { createEffect } from "solid-js"; +/** + * Alert component to because we are have the decency to inform users of potential risks + */ function Alert() { return (
@@ -36,6 +39,10 @@ function Alert() { ); } +/** + * Set up page where users land on and enter their name. + * This is kind of the authentication step even though this is no real authentciation + */ export default function SetUp() { const { name, setName } = useAppContext(); function handleSubmit(event: SubmitEvent) { @@ -46,6 +53,7 @@ export default function SetUp() { } const navigate = useNavigate(); + // Go to chat list page if already set up createEffect(() => { if (name() === null) return; navigate("/"); @@ -56,7 +64,6 @@ export default function SetUp() {
- {/* TODO use melt logo */} , @@ -15,10 +18,7 @@ struct User { enum Message { AddSocket(websocket::Handle), - ProcessSocketMessage( - SocketId, - Arc, - ), + ProcessSocketMessage(SocketId, Arc), ReceiveMessage(Arc), RemoveSocket(SocketId), AddContact(Arc), @@ -38,7 +38,6 @@ async fn run_actor(mut actor: User) { continue; } - tracing::debug!("Syncing message"); let result = socket.synchronize_message(message.clone()).await; if let Err(error) = result { @@ -133,10 +132,7 @@ impl Handle { Self { sender } } - pub(crate) async fn add_socket( - &self, - socket: websocket::Handle, - ) -> Result<(), impl Error> { + pub(crate) async fn add_socket(&self, socket: websocket::Handle) -> Result<(), impl Error> { self.sender.send(Message::AddSocket(socket)).await } @@ -150,7 +146,10 @@ impl Handle { .await } - pub(super) async fn receive_message(&self, message: Arc) -> Result<(), impl Error> { + pub(super) async fn receive_message( + &self, + message: Arc, + ) -> Result<(), impl Error> { self.sender.send(Message::ReceiveMessage(message)).await } diff --git a/server/src/actor/websocket.rs b/server/src/actor/websocket.rs index 2b07b4a..4e72b1b 100644 --- a/server/src/actor/websocket.rs +++ b/server/src/actor/websocket.rs @@ -1,8 +1,8 @@ use ::axum::extract::ws::Message as WebSocketMessage; use axum::extract::ws as axum; use nanoid::nanoid; -use std::sync::Arc; use serde::Serialize; +use std::sync::Arc; use tokio::sync::mpsc; use super::{user, ChatMessage}; @@ -26,6 +26,7 @@ enum ClientMessage { #[derive(Clone, PartialEq, Eq)] pub(super) struct SocketId(Arc); +/// A websocket actor represents a single websocket connection to a users device struct WebSocket { id: SocketId, socket: axum::WebSocket, @@ -147,11 +148,19 @@ impl Handle { self.sender.send(Message::AddContact { name }).await } - pub(super) async fn remove_contact(&self, name: Arc) -> Result<(), impl std::error::Error> { + pub(super) async fn remove_contact( + &self, + name: Arc, + ) -> Result<(), impl std::error::Error> { self.sender.send(Message::RemoveContact { name }).await } - pub(super) async fn synchronize_message(&self, message: Arc) -> Result<(), impl std::error::Error> { - self.sender.send(Message::SynchronizeMessage { message }).await + pub(super) async fn synchronize_message( + &self, + message: Arc, + ) -> Result<(), impl std::error::Error> { + self.sender + .send(Message::SynchronizeMessage { message }) + .await } } diff --git a/server/src/main.rs b/server/src/main.rs index 06c0a73..f0a46a1 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -81,7 +81,6 @@ async fn websocket_handler( websocket.on_upgrade(move |socket| create_actor(socket, state, name.into())) } -// 2/3e, duck2duck encryption, melt async fn create_actor(stream: WebSocket, State(state): State, name: Arc) { let result = state.delivery_service.get_or_insert(name).await;