diff --git a/Cargo.toml b/Cargo.toml index 427fd0c..9dfe812 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ edition = "2024" [dependencies] anyhow = "1.0.99" +chrono = "0.4.42" libloading = { version = "0.8.8", optional = true } once_cell = "1.21.3" serde = { version = "1.0.219", features = ["serde_derive"] } diff --git a/cli/Cargo.lock b/cli/Cargo.lock index 1a42a52..64d6923 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -2,12 +2,27 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anyhow" version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + [[package]] name = "block-buffer" version = "0.10.4" @@ -17,18 +32,47 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + [[package]] name = "bytes" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +[[package]] +name = "cc" +version = "1.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65193589c6404eb80b450d618eaf9a2cafaaafd57ecce47370519ef674a7bd44" +dependencies = [ + "find-msvc-tools", + "shlex", +] + [[package]] name = "cfg-if" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" +[[package]] +name = "chrono" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link 0.2.0", +] + [[package]] name = "cli" version = "0.1.0" @@ -36,6 +80,12 @@ dependencies = [ "voxa-server", ] +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + [[package]] name = "cpufeatures" version = "0.2.17" @@ -71,6 +121,12 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "find-msvc-tools" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fd99930f64d146689264c637b5af2f0233a933bef0d8570e2526bf9e083192d" + [[package]] name = "fnv" version = "1.0.7" @@ -116,12 +172,46 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" +[[package]] +name = "iana-time-zone" +version = "0.1.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "itoa" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "js-sys" +version = "0.3.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0b063578492ceec17683ef2f8c5e89121fbd0b172cbc280635ab7567db2738" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + [[package]] name = "libc" version = "0.2.175" @@ -150,6 +240,15 @@ version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "once_cell" version = "1.21.3" @@ -218,6 +317,12 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + [[package]] name = "ryu" version = "1.0.20" @@ -267,6 +372,12 @@ dependencies = [ "digest", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "syn" version = "2.0.106" @@ -344,6 +455,7 @@ name = "voxa-server" version = "0.1.0" dependencies = [ "anyhow", + "chrono", "libloading", "once_cell", "serde", @@ -360,19 +472,137 @@ dependencies = [ "wit-bindgen", ] +[[package]] +name = "wasm-bindgen" +version = "0.2.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e14915cadd45b529bb8d1f343c4ed0ac1de926144b746e2710f9cd05df6603b" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28d1ba982ca7923fd01448d5c30c6864d0a14109560296a162f80f305fb93bb" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c3d463ae3eff775b0c45df9da45d68837702ac35af998361e2c84e7c5ec1b0d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bb4ce89b08211f923caf51d527662b75bdc9c9c7aab40f86dcb9fb85ac552aa" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f143854a3b13752c6950862c906306adb27c7e839f7414cec8fea35beab624c1" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "windows-core" +version = "0.62.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57fe7168f7de578d2d8a05b07fd61870d2e73b4020e9f49aa00da8471723497c" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.2.0", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "windows-link" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" +[[package]] +name = "windows-link" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" + +[[package]] +name = "windows-result" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7084dcc306f89883455a206237404d3eaf961e5bd7e0f312f7c91f57eb44167f" +dependencies = [ + "windows-link 0.2.0", +] + +[[package]] +name = "windows-strings" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7218c655a553b0bed4426cf54b20d7ba363ef543b52d515b3e48d7fd55318dda" +dependencies = [ + "windows-link 0.2.0", +] + [[package]] name = "windows-targets" version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" dependencies = [ - "windows-link", + "windows-link 0.1.3", "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", diff --git a/client.js b/client.js index 571abf9..44c6df7 100644 --- a/client.js +++ b/client.js @@ -2,7 +2,10 @@ setInterval(() => { const ws = new WebSocket('ws://localhost:7080'); function sendMessage(message) { - ws.send(JSON.stringify({ type: 'send_message', params: message })); + ws.send(JSON.stringify({ type: 'send_message', params: { + channel_id: 'Hello', + contents: message + }})); } ws.onopen = () => { diff --git a/src/client.rs b/src/client.rs index fe8cf3f..9a00b8c 100644 --- a/src/client.rs +++ b/src/client.rs @@ -7,7 +7,7 @@ use std::{ use anyhow::Error; use tungstenite::{Message, Utf8Bytes, WebSocket, accept}; -use crate::types::{FromClient, ToClient, WsMessage}; +use crate::types::{ClientMessage, ServerMessage, WsMessage}; #[derive(Clone)] pub struct Client(Arc>>); @@ -37,7 +37,7 @@ impl Hash for Client { } impl Client { - pub fn read(&self) -> crate::Result>> { + pub fn read(&self) -> crate::Result>> { match self.0.lock().unwrap().read()? { Message::Text(t) => { let v = t.to_string(); @@ -55,7 +55,7 @@ impl Client { } } - pub fn send(&self, m: ToClient) -> crate::Result<()> { + pub fn send(&self, m: ServerMessage) -> crate::Result<()> { self.0 .lock() .unwrap() diff --git a/src/lib.rs b/src/lib.rs index a75419c..8063e98 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,7 +20,7 @@ pub use tungstenite; use crate::{ client::Client, plugin::DynPlugin, - types::{ToClient, data}, + types::{ClientMessage, WsMessage}, }; pub use once_cell; @@ -122,23 +122,38 @@ impl Server { // The main req/res loop loop { match client.read()? { - Some(req) => { - println!("Request: {:?}", req); - for plugin in self.plugins.lock().unwrap().iter_mut() { - plugin.on_request(&req, self); + Some(WsMessage::FromClient(req)) => match req { + ClientMessage::SendMessage { + channel_id, + contents, + } => { + Self::LOGGER.info(format!("SendMessage to {channel_id}: {contents}")); } - for c in self.clients.lock().unwrap().iter() { - if c == &client { - self.wrap_err( - &client, - c.send(ToClient::Message(data::Message { - from: format!("Server"), - contents: format!("Hello"), - })), - )?; - } + ClientMessage::EditMessage { + channel_id, + message_id, + new_contents, + } => { + Self::LOGGER.info(format!( + "EditMessage {message_id} in {channel_id}: {new_contents}" + )); } + + ClientMessage::DeleteMessage { + channel_id, + message_id, + } => { + Self::LOGGER.info(format!("DeleteMessage {message_id} in {channel_id}")); + } + }, + + Some(WsMessage::Binary(b)) => { + Self::LOGGER.info(format!("Binary message: {b:?}")); + } + + Some(WsMessage::String(s)) => { + Self::LOGGER.info(format!("String message: {s}")); } None => { diff --git a/src/plugin.rs b/src/plugin.rs index ba72677..55495df 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use crate::{ Server, - types::{FromClient, WsMessage}, + types::{ClientMessage, WsMessage}, }; pub type DynPlugin = Box; @@ -10,7 +10,7 @@ pub type DynPlugin = Box; pub trait Plugin { fn init(&mut self, server: &Arc); #[allow(unused_variables)] - fn on_request(&mut self, msg: &WsMessage, server: &Arc) -> bool { + fn on_request(&mut self, msg: &WsMessage, server: &Arc) -> bool { false } } diff --git a/src/types.rs b/src/types.rs index ee0e128..616dc8a 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,27 +1,60 @@ use serde::{Deserialize, Serialize}; use tungstenite::Bytes; +/// Messages sent *from the client* (user’s app) to the server #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(tag = "type", content = "params", rename_all = "snake_case")] -pub enum FromClient { - SendMessage(String), +pub enum ClientMessage { + /// Send a message to a channel + SendMessage { + channel_id: String, + contents: String, + }, + + /// Edit a message (if allowed) + EditMessage { + channel_id: String, + message_id: String, + new_contents: String, + }, + + /// Delete a message (if allowed) + DeleteMessage { + channel_id: String, + message_id: String, + }, } +/// Messages sent *from the server* to the client #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(tag = "type", content = "params", rename_all = "snake_case")] -pub enum FromServer {} +pub enum ServerMessage { + /// Successful authentication + Authenticated { user_id: String }, -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(tag = "type", content = "params", rename_all = "snake_case")] -pub enum ToClient { - Message(data::Message), + /// Error responses + Error { message: String }, + + /// A new message in a channel + MessageCreate(data::Message), + + /// A message was edited + MessageUpdate(data::Message), + + /// A message was deleted + MessageDelete { + channel_id: String, + message_id: String, + }, + + /// Presence updates + PresenceUpdate { user_id: String, status: String }, + + /// Typing indicator + Typing { user_id: String, channel_id: String }, } -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(tag = "type", content = "params", rename_all = "snake_case")] -pub enum ToServer {} - -/// WebSocket for client messages +/// WebSocket wrapper #[derive(Debug, Clone)] pub enum WsMessage Deserialize<'de>> { FromClient(T), @@ -29,12 +62,30 @@ pub enum WsMessage Deserialize<'de>> { String(String), } +/// Shared data structures pub mod data { use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Message { + pub id: String, + pub channel_id: u8, pub from: String, pub contents: String, + pub timestamp: i64, + } + + #[derive(Debug, Clone, Serialize, Deserialize)] + pub struct Channel { + pub id: usize, + pub name: String, + pub kind: ChannelKind, + } + + #[derive(Debug, Clone, Serialize, Deserialize)] + #[serde(rename_all = "snake_case")] + pub enum ChannelKind { + Text, + Voice, } }