diff --git a/cli/Cargo.lock b/cli/Cargo.lock index 30944fb..4d97123 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + [[package]] name = "android_system_properties" version = "0.1.5" @@ -50,6 +56,12 @@ 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" @@ -101,6 +113,15 @@ dependencies = [ "libc", ] +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -139,6 +160,22 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fd99930f64d146689264c637b5af2f0233a933bef0d8570e2526bf9e083192d" +[[package]] +name = "flate2" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "foldhash" version = "0.1.5" @@ -155,6 +192,17 @@ dependencies = [ "version_check", ] +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "hashbrown" version = "0.15.5" @@ -173,6 +221,23 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "http" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "httparse" +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" @@ -226,7 +291,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ "cfg-if", - "windows-targets", + "windows-targets 0.53.3", ] [[package]] @@ -251,6 +316,15 @@ version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -266,6 +340,12 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + [[package]] name = "pkg-config" version = "0.3.32" @@ -290,6 +370,20 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "untrusted", + "windows-sys", +] + [[package]] name = "rusqlite" version = "0.37.0" @@ -304,6 +398,50 @@ dependencies = [ "smallvec", ] +[[package]] +name = "rustls" +version = "0.23.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd3c25631629d034ce7cd9940adc9d45762d46de2b0f57193c4443b92c6d4d40" +dependencies = [ + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8572f3c2cb9934231157b45499fc41e1f58c589fdfb81a844ba873265e80f8eb" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.22" @@ -371,6 +509,12 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "2.0.106" @@ -394,6 +538,48 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "ureq" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99ba1025f18a4a3fc3e9b48c868e9beb4f24f4b4b1a325bada26bd4119f46537" +dependencies = [ + "base64", + "flate2", + "log", + "percent-encoding", + "rustls", + "rustls-pemfile", + "rustls-pki-types", + "ureq-proto", + "utf-8", + "webpki-roots", +] + +[[package]] +name = "ureq-proto" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b4531c118335662134346048ddb0e54cc86bd7e81866757873055f0e38f5d2" +dependencies = [ + "base64", + "http", + "httparse", + "log", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "vcpkg" version = "0.2.15" @@ -419,8 +605,15 @@ dependencies = [ "serde", "serde_json", "sha1", + "ureq", ] +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + [[package]] name = "wasm-bindgen" version = "0.2.101" @@ -480,6 +673,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "webpki-roots" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "windows-core" version = "0.62.0" @@ -545,6 +747,31 @@ dependencies = [ "windows-link 0.2.0", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + [[package]] name = "windows-targets" version = "0.53.3" @@ -552,60 +779,114 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" dependencies = [ "windows-link 0.1.3", - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + [[package]] name = "windows_aarch64_gnullvm" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + [[package]] name = "windows_aarch64_msvc" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + [[package]] name = "windows_i686_gnu" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + [[package]] name = "windows_i686_gnullvm" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + [[package]] name = "windows_i686_msvc" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + [[package]] name = "windows_x86_64_gnu" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + [[package]] name = "windows_x86_64_gnullvm" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + [[package]] name = "windows_x86_64_msvc" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/client.js b/client.js index f423cb9..9afc910 100644 --- a/client.js +++ b/client.js @@ -1,5 +1,17 @@ import readline from 'readline'; +async function createServerToken() { + return (await (await fetch('http://localhost:3000/api/auth', { + method: 'POST', + body: JSON.stringify({ + intents: 'server', + server_ip: '127.0.0.1' + }) + })).json()).token; +} + +const token = await createServerToken(); + const rl = readline.createInterface({ input: process.stdin, output: process.stdout @@ -10,11 +22,15 @@ const ws = new WebSocket('ws://localhost:7080'); ws.onopen = () => { console.log('WebSocket connection established'); console.log('Initializing handshake'); - ws.send(JSON.stringify({ version: '0.0.1', auth_token: '' })) + ws.send(JSON.stringify({ version: '0.0.1', auth_token: token })) }; ws.onmessage = (event) => { const message = JSON.parse(event.data); + if (message.type === 'authenticated') { + console.log('Logged in with uuid: ' + message.params.messages); + console.log(message.params.messages); + } console.log('Received:', message); // ws.close(); }; @@ -34,5 +50,9 @@ function ask(question) { while (true) { const message = await ask(''); + if (message === 'q') { + ws.close(); + process.exit(); + } sendMessage(message); } \ No newline at end of file diff --git a/src/auth.rs b/src/auth.rs index 96d719b..7fafebc 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -6,15 +6,15 @@ use crate::{Server, utils::client::Client}; #[derive(Debug, Deserialize)] struct AuthApiRes { - uuid: u32, + user_id: u32, } pub fn auth(_server: &Arc, client: &mut Client, token: &str) -> crate::Result { - // let mut res = ureq::get(format!("https://api.voxa.org/server-auth?token={token}")).call()?; - - // let api_res: AuthApiRes = serde_json::from_str(&res.body_mut().read_to_string()?)?; - - client.set_uuid(232); - - Ok(232) + let mut res = ureq::get(format!( + "http://localhost:3000/api/auth?intents=server&token={token}" + )) + .call()?; + let api_res: AuthApiRes = serde_json::from_str(&res.body_mut().read_to_string()?)?; + client.set_uuid(api_res.user_id); + Ok(api_res.user_id) } diff --git a/src/lib.rs b/src/lib.rs index 76dab36..f234a68 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -89,7 +89,8 @@ impl Server { Ok(stream) => { std::thread::spawn({ let srv = self.clone(); - move || match srv.handle_client(stream) { + let client = srv.init_client(stream)?; + move || match srv.wrap_err(&client, srv.handle_client(&client)) { Ok(_) => {} Err(e) => Self::LOGGER.error(format!("Client handler failed: {e}")), } @@ -108,7 +109,7 @@ impl Server { self.plugins.lock().unwrap().push(plugin); } - fn handle_client(self: &Arc, stream: TcpStream) -> anyhow::Result<()> { + fn init_client(self: &Arc, stream: TcpStream) -> anyhow::Result { Self::LOGGER.info(format!("New connection: {}", stream.peer_addr()?)); // Initialize client let mut client = Client::new(stream)?; @@ -125,20 +126,29 @@ impl Server { match self.wrap_err(&client, client.read_t::())? { Some(types::WsMessage::Message(types::handshake::ClientDetails { - auth_token, .. + auth_token, + last_message, + .. })) => { let auth_res = auth::auth(self, &mut client, &auth_token); let uuid = self.wrap_err(&client, auth_res)?; self.wrap_err( &client, - client.send(types::ServerMessage::Authenticated { uuid }), + client.send(types::ServerMessage::Authenticated { + uuid, + messages: if let Some(i) = last_message { + self.wrap_err(&client, self.db.get_messages_after_id(i))? + } else { + self.wrap_err(&client, self.db.get_messages_after_id(0))? + }, + }), )?; } - Some(_) => { + Some(v) => { self.wrap_err( &client, client.send(types::ResponseError::InvalidHandshake(format!( - "Invalid handshake" + "Invalid handshake: {v:?}" ))), )?; } @@ -148,29 +158,37 @@ impl Server { // Insert to the set of all connected clients self.clients.lock().unwrap().insert(client.clone()); + Ok(client) + } + + fn handle_client(self: &Arc, client: &Client) -> anyhow::Result<()> { // The main req/res loop 'outer: loop { let req = client.read()?; if let Some(r) = &req { for p in self.plugins.lock().unwrap().iter_mut() { - if p.on_request(r, &client, self) { + if p.on_request(r, client, self) { continue 'outer; } } - self.call_request(r, &client)?; + self.wrap_err(&client, self.call_request(r, &client))?; } } } /// When there is a error it removes the client - pub fn wrap_err( + pub fn wrap_err( self: &Arc, client: &Client, res: std::result::Result, ) -> std::result::Result { - if res.is_err() { + if let Err(e) = &res { self.clients.lock().unwrap().remove(&client); + if client + .send(types::ResponseError::InternalError(e.to_string())) + .is_err() + {} } res diff --git a/src/requests/message.rs b/src/requests/message.rs index b6e2f5c..f041eb7 100644 --- a/src/requests/message.rs +++ b/src/requests/message.rs @@ -13,27 +13,31 @@ pub fn send( LOGGER.info(format!("SendMessage to {channel_id}: {contents}")); if contents.is_empty() { - server.wrap_err( - &client, - client.send(types::ResponseError::InvalidRequest(format!( - "Invalid message: empty message" - ))), - )?; + client.send(types::ResponseError::InvalidRequest(format!( + "Invalid message: empty message" + )))?; + return Ok(()); } - let msg = server.wrap_err( - &client, - server.db.insert_message( - &channel_id, - client.get_uuid()?, - &contents, - chrono::Utc::now().timestamp(), - ), + let msg = server.db.insert_message( + &channel_id, + client.get_uuid()?, + &contents, + chrono::Utc::now().timestamp(), )?; + let server = server.clone(); + for c in server.clients.lock().unwrap().iter() { - server.wrap_err(&c, c.send(types::ServerMessage::MessageCreate(msg.clone())))?; + let c = c.clone(); + let server = server.clone(); + let msg = msg.clone(); + std::thread::spawn(move || { + server + .wrap_err(&c, c.send(types::ServerMessage::MessageCreate(msg))) + .expect("Failed to broadcast"); + }); } Ok(()) diff --git a/src/types.rs b/src/types.rs index 73b38b7..90e054a 100644 --- a/src/types.rs +++ b/src/types.rs @@ -27,6 +27,7 @@ pub enum ServerMessage { /// Successful authentication Authenticated { uuid: u32, + messages: Vec, }, TempMessage { @@ -118,5 +119,6 @@ pub mod handshake { pub struct ClientDetails { pub version: String, pub auth_token: String, + pub last_message: Option, } } diff --git a/src/utils/client.rs b/src/utils/client.rs index 7d84029..72bbab6 100644 --- a/src/utils/client.rs +++ b/src/utils/client.rs @@ -341,7 +341,7 @@ impl Clone for Client { impl PartialEq for Client { fn eq(&self, other: &Self) -> bool { - self.0.peer_addr().unwrap() == other.0.peer_addr().unwrap() + self.0.peer_addr().unwrap_or(self.0.local_addr().unwrap()) == other.0.peer_addr().unwrap_or(other.0.local_addr().unwrap()) } } @@ -349,6 +349,9 @@ impl Eq for Client {} impl Hash for Client { fn hash(&self, state: &mut H) { - self.0.peer_addr().unwrap().hash(state); + self.0 + .peer_addr() + .unwrap_or(self.0.local_addr().unwrap()) + .hash(state); } } diff --git a/src/utils/database.rs b/src/utils/database.rs index 4b62b2d..84cf4ab 100644 --- a/src/utils/database.rs +++ b/src/utils/database.rs @@ -12,7 +12,7 @@ impl Database { "CREATE TABLE IF NOT EXISTS chat ( id INTEGER PRIMARY KEY AUTOINCREMENT, channel_id TEXT NOT NULL, - user_id TEXT NOT NULL, + user_id INTEGER NOT NULL, contents TEXT NOT NULL, timestamp INTEGER NOT NULL )", @@ -102,6 +102,33 @@ impl Database { } Ok(None) } + + /// Get all messages with an ID greater than the given one + pub fn get_messages_after_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 + ORDER BY id ASC", + )?; + + let rows = stmt.query_map(params![message_id], |row| { + Ok(Message { + id: row.get::<_, i64>(0)?, + channel_id: row.get::<_, String>(1)?, + from: row.get::<_, u32>(2)?, + contents: row.get::<_, String>(3)?, + timestamp: row.get::<_, i64>(4)?, + }) + })?; + + let mut messages = Vec::new(); + for row in rows { + messages.push(row?); + } + + Ok(messages) + } } unsafe impl Send for Database {}