From a1b54fda6bbf3f2a3ecabe0f745aa28cef6b30d3 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Sat, 13 Sep 2025 17:59:12 +0200 Subject: [PATCH] Small stuff --- client.js | 51 ++++++++++++++++++++++++++++---------------- src/client.rs | 14 ++++++++++--- src/database.rs | 56 +++++++++++++++++++++++++++++-------------------- src/lib.rs | 9 ++++++-- src/types.rs | 17 +++++++++++---- 5 files changed, 97 insertions(+), 50 deletions(-) diff --git a/client.js b/client.js index 44c6df7..727bda0 100644 --- a/client.js +++ b/client.js @@ -1,21 +1,36 @@ -setInterval(() => { - const ws = new WebSocket('ws://localhost:7080'); +import readline from 'readline'; - function sendMessage(message) { - ws.send(JSON.stringify({ type: 'send_message', params: { - channel_id: 'Hello', - contents: message - }})); - } +const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout +}); - ws.onopen = () => { - console.log('WebSocket connection established'); - sendMessage('Hello, Server!'); - }; +const ws = new WebSocket('ws://localhost:7080'); - ws.onmessage = (event) => { - const message = JSON.parse(event.data); - console.log('Received:', message); - ws.close(); - }; -}, 100) \ No newline at end of file +ws.onopen = () => { + console.log('WebSocket connection established'); +}; + +ws.onmessage = (event) => { + const message = JSON.parse(event.data); + console.log('Received:', message); + // ws.close(); +}; + +function sendMessage(message) { + ws.send(JSON.stringify({ type: 'send_message', params: { + channel_id: 'Hello', + contents: message + }})); +} + +function ask(question) { + return new Promise((resolve) => { + rl.question(question, resolve); + }); +} + +while (true) { + const message = await ask(''); + sendMessage(message); +} \ No newline at end of file diff --git a/src/client.rs b/src/client.rs index 9a00b8c..7155d42 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::{ClientMessage, ServerMessage, WsMessage}; +use crate::types::{ClientMessage, ServerMessage, WsMessage, data::ResponseError}; #[derive(Clone)] pub struct Client(Arc>>); @@ -18,7 +18,7 @@ impl Client { } pub fn new_tcp(ws: TcpStream) -> crate::Result { - Ok(Self(Arc::new(Mutex::new(accept(ws)?)))) + Ok(Self::new_ws(accept(ws)?)) } } @@ -42,7 +42,7 @@ impl Client { Message::Text(t) => { let v = t.to_string(); match serde_json::from_str(&v) { - Ok(f) => Ok(Some(WsMessage::FromClient(f))), + Ok(f) => Ok(Some(WsMessage::Message(f))), Err(_) => Ok(Some(WsMessage::String(v))), } } @@ -62,4 +62,12 @@ impl Client { .send(Message::Text(Utf8Bytes::from(serde_json::to_string(&m)?))) .map_err(|e| e.into()) } + + pub fn send_err(&self, m: ResponseError) -> crate::Result<()> { + self.0 + .lock() + .unwrap() + .send(Message::Text(Utf8Bytes::from(serde_json::to_string(&m)?))) + .map_err(|e| e.into()) + } } diff --git a/src/database.rs b/src/database.rs index 9c956f5..b76bdac 100644 --- a/src/database.rs +++ b/src/database.rs @@ -1,4 +1,4 @@ -use crate::ServerConfig; +use crate::{ServerConfig, types::data::Message}; use rusqlite::{Connection, Result, params}; pub struct Database { @@ -42,29 +42,33 @@ impl MessagesDb { user_id: &str, contents: &str, timestamp: i64, - ) -> Result { + ) -> Result { self.0.execute( "INSERT INTO chat (channel_id, user_id, contents, timestamp) - VALUES (?1, ?2, ?3, ?4)", + VALUES (?1, ?2, ?3, ?4)", params![channel_id, user_id, contents, timestamp], - ) - } - - /// Fetch the latest N messages for a channel - pub fn fetch_recent( - &self, - channel_id: &str, - limit: usize, - ) -> Result> { - let mut stmt = self.0.prepare( - "SELECT id, channel_id, user_id, contents, timestamp - FROM chat - WHERE channel_id = ?1 - ORDER BY timestamp DESC - LIMIT ?2", )?; - let rows = stmt.query_map(params![channel_id, limit], |row| { + let id = self.0.last_insert_rowid(); + + Ok(Message { + id, + channel_id: channel_id.to_string(), + from: user_id.to_string(), + contents: contents.to_string(), + timestamp, + }) + } + + /// Get a message by its ID + pub fn get_by_id(&self, message_id: usize) -> Result> { + let mut stmt = self.0.prepare( + "SELECT id, channel_id, user_id, contents, timestamp + FROM chat + WHERE id = ?1", + )?; + + let mut rows = stmt.query_map(params![message_id], |row| { Ok(( row.get::<_, i64>(0)?, // id row.get::<_, String>(1)?, // channel_id @@ -74,10 +78,16 @@ impl MessagesDb { )) })?; - let mut results = Vec::new(); - for row in rows { - results.push(row?); + if let Some(row) = rows.next() { + let (id, channel_id, user_id, contents, timestamp) = row?; + return Ok(Some(Message { + id, + channel_id, + from: user_id, + contents, + timestamp, + })); } - Ok(results) + Ok(None) } } diff --git a/src/lib.rs b/src/lib.rs index 4e6620b..0863ed5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -124,13 +124,13 @@ impl Server { // The main req/res loop loop { match client.read()? { - Some(WsMessage::FromClient(req)) => match req { + Some(WsMessage::Message(req)) => match req { ClientMessage::SendMessage { channel_id, contents, } => { Self::LOGGER.info(format!("SendMessage to {channel_id}: {contents}")); - self.wrap_err( + let msg = self.wrap_err( &client, self.db.messages_db.insert( &channel_id, @@ -139,6 +139,11 @@ impl Server { chrono::Utc::now().timestamp(), ), )?; + + self.wrap_err( + &client, + client.send(types::ServerMessage::MessageCreate(msg)), + )?; } ClientMessage::EditMessage { diff --git a/src/types.rs b/src/types.rs index af1bb0d..9694079 100644 --- a/src/types.rs +++ b/src/types.rs @@ -33,7 +33,7 @@ pub enum ServerMessage { Authenticated { user_id: String }, /// Error responses - Error { message: String }, + Error(), /// A new message in a channel MessageCreate(data::Message), @@ -57,7 +57,7 @@ pub enum ServerMessage { /// WebSocket wrapper #[derive(Debug, Clone)] pub enum WsMessage Deserialize<'de>> { - FromClient(T), + Message(T), Binary(Bytes), String(String), } @@ -68,8 +68,8 @@ pub mod data { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Message { - pub id: String, - pub channel_id: u8, + pub id: i64, + pub channel_id: String, pub from: String, pub contents: String, pub timestamp: i64, @@ -88,4 +88,13 @@ pub mod data { Text, Voice, } + + #[derive(Debug, Clone, Serialize, Deserialize)] + #[serde(tag = "error", rename_all = "snake_case")] + pub enum ResponseError { + InvalidRequest { message: String }, + Unauthorized { message: String }, + NotFound { message: String }, + InternalError { message: String }, + } }