diff --git a/server-api/Cargo.lock b/server-api/Cargo.lock index 84f9040..12dd7a2 100644 --- a/server-api/Cargo.lock +++ b/server-api/Cargo.lock @@ -308,8 +308,10 @@ name = "db" version = "0.1.0" dependencies = [ "async-trait 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.15 (registry+https://github.com/rust-lang/crates.io-index)", "mysql_async 0.23.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.117 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.59 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/server-api/build.sh b/server-api/build.sh index a419e3c..c45e877 100755 --- a/server-api/build.sh +++ b/server-api/build.sh @@ -16,8 +16,7 @@ while getopts ":htbr" arg; do case ${arg} in h)echo help command;; t) - cargo build 1>/dev/null 2>&1 - cargo test 1>/dev/null 2>&1 + cargo build cargo run -- -s & server=$! echo Waiting on server to spin up && sleep 2 diff --git a/server-api/client-tests/client.py b/server-api/client-tests/client.py index 2e967e2..c126064 100644 --- a/server-api/client-tests/client.py +++ b/server-api/client-tests/client.py @@ -21,16 +21,16 @@ class Test: self.test_count = 0 if create_admin: - self.body = Test.__create_admin() + self.body = Test.__create_admin() #this was an afterthough for pipelines(later not now) elif admin is not None: self.body = body else: # for now we use this because my dev server has this fake ass acc on it self.body = { - 'secret': 'utO088fltYrTZg323NZfAGrAkArMkDfwjPmt0ooAYta2oJOYDWcAd1FnrpVVMqZtMeUX4_Hu57-LHCkXy8gedg==', - 'id': 23, - 'name': 'admin', - 'joindate':1602385239, + 'secret': 'JfW_Icct2P1WEo6PQlGb7l1IMd2QsRXAVarPoPZHZnj7NOMWBMdirnH9JqAKgrO5z3fb54QYsWlPPlRGowwFSA==', + 'id': 1, + 'name': 'owner sama uwu', + 'joindate': 69, 'status': 0, 'permissions': 18446744073709551615 } @@ -95,6 +95,7 @@ class Test: if __name__ == '__main__': worker = Test(create_admin=False) + # First the invites api gets some basic tests worker.get('/invite/create') @@ -112,6 +113,10 @@ if __name__ == '__main__': # Messaging - worker.post('/channels/create', name='send-channel', kind=TEXT_CHANNEL) - worker.post('/message/send', channel='send-channel', content="some random content") - worker.delete('/channels/delete', name='send-channel') + msg_chan_id = time.time() + msg_chan_raw = worker.post('/channels/create', name=f'{msg_chan_id}', kind=TEXT_CHANNEL) + msg_chan = json.loads(msg_chan_raw) + print(f'Channel id to be used: {msg_chan["id"]}') + + worker.post('/message/send', channel=msg_chan['id'], content="some random content") + worker.delete('/channels/delete', channel_id=msg_chan['id']) # finally clean up the channel we created diff --git a/server-api/db/Cargo.lock b/server-api/db/Cargo.lock index 645cd67..e4be112 100644 --- a/server-api/db/Cargo.lock +++ b/server-api/db/Cargo.lock @@ -63,9 +63,9 @@ dependencies = [ [[package]] name = "base-x" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2734baf8ed08920ccecce1b48a2dfce4ac74a973144add031163bd21a1c5dab" +checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" [[package]] name = "base64" @@ -138,9 +138,9 @@ checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" [[package]] name = "cc" -version = "1.0.61" +version = "1.0.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d" +checksum = "f1770ced377336a88a67c473594ccc14eca6f4559217c34f64aac8f83d641b40" [[package]] name = "cfg-if" @@ -176,9 +176,9 @@ checksum = "c478836e029dcef17fb47c89023448c64f781a046e0300e257ad8225ae59afab" [[package]] name = "core-foundation" -version = "0.7.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" +checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" dependencies = [ "core-foundation-sys", "libc", @@ -186,9 +186,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.7.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" +checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" [[package]] name = "crc32fast" @@ -276,8 +276,10 @@ name = "db" version = "0.1.0" dependencies = [ "async-trait", + "chrono", "mysql_async", "serde", + "serde_json", ] [[package]] @@ -357,6 +359,16 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "form_urlencoded" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00" +dependencies = [ + "matches", + "percent-encoding", +] + [[package]] name = "fuchsia-zircon" version = "0.3.3" @@ -375,15 +387,15 @@ checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" [[package]] name = "futures-core" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18eaa56102984bed2c88ea39026cff3ce3b4c7f508ca970cedf2450ea10d4e46" +checksum = "847ce131b72ffb13b6109a221da9ad97a64cbe48feb1028356b836b47b8f1748" [[package]] name = "futures-macro" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36fccf3fc58563b4a14d265027c627c3b665d7fed489427e88e7cc929559efe" +checksum = "77408a692f1f97bcc61dc001d752e00643408fbc922e4d634c655df50d595556" dependencies = [ "proc-macro-hack", "proc-macro2", @@ -393,24 +405,24 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e3ca3f17d6e8804ae5d3df7a7d35b2b3a6fe89dac84b31872720fc3060a0b11" +checksum = "f878195a49cee50e006b02b93cf7e0a95a38ac7b776b4c4d9cc1207cd20fcb3d" [[package]] name = "futures-task" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d502af37186c4fef99453df03e374683f8a1eec9dcc1e66b3b82dc8278ce3c" +checksum = "7c554eb5bf48b2426c4771ab68c6b14468b6e76cc90996f528c3338d761a4d0d" dependencies = [ "once_cell", ] [[package]] name = "futures-util" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abcb44342f62e6f3e8ac427b8aa815f724fd705dfad060b18ac7866c15bb8e34" +checksum = "d304cff4a7b99cfb7986f7d43fbe93d175e72e704a8860787cc95e9ffd85cbd2" dependencies = [ "futures-core", "futures-macro", @@ -603,7 +615,7 @@ checksum = "0840c1c50fd55e521b247f949c241c9997709f23bd7f023b9762cd561e935656" dependencies = [ "log", "mio", - "miow 0.3.5", + "miow 0.3.6", "winapi 0.3.9", ] @@ -632,9 +644,9 @@ dependencies = [ [[package]] name = "miow" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07b88fb9795d4d36d62a012dfbf49a8f5cf12751f36d31a9dbe66d528e58979e" +checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897" dependencies = [ "socket2", "winapi 0.3.9", @@ -692,16 +704,16 @@ dependencies = [ "serde_json", "sha1", "sha2", - "time 0.2.22", + "time 0.2.23", "twox-hash", "uuid", ] [[package]] name = "native-tls" -version = "0.2.4" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b0d88c06fe90d5ee94048ba40409ef1d9315d86f6f38c2efdaad4fb50c58b2d" +checksum = "6fcc7939b5edc4e4f86b1b4a04bb1498afaaf871b1a6691838ed06fcb48d3a3f" dependencies = [ "lazy_static", "libc", @@ -764,9 +776,9 @@ checksum = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397" [[package]] name = "once_cell" -version = "1.4.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad" +checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" [[package]] name = "opaque-debug" @@ -1030,9 +1042,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "security-framework" -version = "0.4.4" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64808902d7d99f78eaddd2b4e2509713babc3dc3c85ad6f4c447680f3c01e535" +checksum = "c1759c2e3c8580017a484a7ac56d3abc5a6c1feadf88db2f3633f12ae4268c69" dependencies = [ "bitflags", "core-foundation", @@ -1043,9 +1055,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "0.4.3" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17bf11d99252f512695eb468de5516e5cf75455521e69dfe343f3b74e4748405" +checksum = "f99b9d5e26d2a71633cc4f2ebae7cc9f874044e0c351a27e17892d76dce5678b" dependencies = [ "core-foundation-sys", "libc", @@ -1071,6 +1083,9 @@ name = "serde" version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a" +dependencies = [ + "serde_derive", +] [[package]] name = "serde_derive" @@ -1120,9 +1135,9 @@ checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" [[package]] name = "socket2" -version = "0.3.15" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1fa70dc5c8104ec096f4fe7ede7a221d35ae13dcd19ba1ad9a81d2cab9a1c44" +checksum = "7fd8b795c389288baa5f355489c65e71fd48a02104600d15c4cfbc561e9e429d" dependencies = [ "cfg-if 0.1.10", "libc", @@ -1132,9 +1147,9 @@ dependencies = [ [[package]] name = "standback" -version = "0.2.11" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4e0831040d2cf2bdfd51b844be71885783d489898a192f254ae25d57cce725c" +checksum = "cf906c8b8fc3f6ecd1046e01da1d8ddec83e48c8b08b84dcc02b585a6bedf5a8" dependencies = [ "version_check", ] @@ -1273,9 +1288,9 @@ dependencies = [ [[package]] name = "time" -version = "0.2.22" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55b7151c9065e80917fbf285d9a5d1432f60db41d170ccafc749a136b41a93af" +checksum = "bcdaeea317915d59b2b4cd3b5efcd156c309108664277793f5351700c02ce98b" dependencies = [ "const_fn", "libc", @@ -1311,15 +1326,24 @@ dependencies = [ [[package]] name = "tinyvec" -version = "0.3.4" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "238ce071d267c5710f9d31451efec16c5ee22de34df17cc05e56cbc92e967117" +checksum = "b78a366903f506d2ad52ca8dc552102ffdd3e937ba8a227f024dc1d1eae28575" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "0.2.22" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d34ca54d84bf2b5b4d7d31e901a8464f7b60ac145a284fba25ceb801f2ddccd" +checksum = "a6d7ad61edd59bfcc7e80dababf0f4aed2e6d5e0ba1659356ae889752dfc12ff" dependencies = [ "bytes", "fnv", @@ -1337,9 +1361,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389" +checksum = "e44da00bfc73a25f814cd8d7e57a68a5c31b74b3152a0a1d1f590c97ed06265a" dependencies = [ "proc-macro2", "quote", @@ -1398,9 +1422,9 @@ dependencies = [ [[package]] name = "unicode-normalization" -version = "0.1.13" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977" +checksum = "f1e9a0b71dba18b6fa17c7b3dcf1440bb3522552deb2f84bf47dabd9fb7e5570" dependencies = [ "tinyvec", ] @@ -1413,10 +1437,11 @@ checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" [[package]] name = "url" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" +checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e" dependencies = [ + "form_urlencoded", "idna", "matches", "percent-encoding", diff --git a/server-api/db/Cargo.toml b/server-api/db/Cargo.toml index 7517282..07308c4 100644 --- a/server-api/db/Cargo.toml +++ b/server-api/db/Cargo.toml @@ -9,4 +9,8 @@ edition = "2018" [dependencies] mysql_async = "0.23.1" async-trait = "0.1.40" -serde = "1.0.117" + +serde_json = "1.0" +serde = { version = "1.0.117", features = [ "derive" ] } + +chrono = "0.4.0" diff --git a/server-api/db/src/channels.rs b/server-api/db/src/channels.rs index 06a2a70..cdbfa04 100644 --- a/server-api/db/src/channels.rs +++ b/server-api/db/src/channels.rs @@ -10,7 +10,6 @@ use crate::{sql_err, no_conn, Response}; use serde::Serialize; -#[allow(dead_code)] #[derive(Serialize)] pub struct Channel { pub id: UBigInt, @@ -185,7 +184,8 @@ impl Channel { Err(_) => Response::Empty }; } - return Response::Other("Could fetch new channel".into()); + // TODO: change this to return Result<> which should help with ambiguity + return Response::Other("Could not fetch new channel".into()); } return Response::Other(no_conn!("db::channels::add")) } diff --git a/server-api/db/src/common.rs b/server-api/db/src/common.rs index 22669bd..5c0d891 100644 --- a/server-api/db/src/common.rs +++ b/server-api/db/src/common.rs @@ -15,6 +15,11 @@ macro_rules! no_conn { macro_rules! sql_err { ($spec:literal) => { format!("[ SQL Error ] : {}", $spec) + }; + + // Using this mostly to pull in sql err types from lib to outside world for logging + ($exp:expr) => { + format!("[ SQL Error ] : {}", $exp) } } diff --git a/server-api/db/src/invites.rs b/server-api/db/src/invites.rs index b64d58a..6cb2de6 100644 --- a/server-api/db/src/invites.rs +++ b/server-api/db/src/invites.rs @@ -20,6 +20,9 @@ impl FromDB for Invite { type Row = Option<(BigInt, Option, bool)>; async fn get(p: &Pool, id: UBigInt) -> Response { + // NOTE: cast is required for this as `id` is used as unix timestamp + let id: BigInt = id as BigInt; + if id <= 0 { return Response::Empty; } diff --git a/server-api/db/src/member.rs b/server-api/db/src/member.rs index 617163e..6596c73 100644 --- a/server-api/db/src/member.rs +++ b/server-api/db/src/member.rs @@ -4,11 +4,14 @@ use mysql_async::error::Error as SqlError; use async_trait::async_trait; +use serde::Serialize; + use crate::{Response, no_conn, sql_err}; use crate::{UBigInt, BigInt, Integer, VarChar}; use crate::common::{FromDB}; +#[derive(Serialize)] pub struct Member { pub id: UBigInt, pub secret: VarChar, @@ -61,33 +64,22 @@ impl FromDB for Member { let q = r#"UPDATE members SET status = :status, permissions = :perms, name = :name WHERE id = :id"#; - // TODO: remoev this retarded extra network hit - // technically this is really nice for the user but ultimately it either works or it - // doesn't so like the fucks the point of doing some bs like this - // the next a client queries this they'll get whatever data is in the db - // likewise clients are going likely going to re-get this data since its been updated - // or at least should + if let Ok(conn) = p.get_conn().await { - match Member::get(p, row.id).await { - Response::Row(_) => { - let db_result: Result - = conn.drop_exec(q, params!{ - "id" => row.id, - "status" => row.status, - "name" => row.name, - "perms" => row.permissions - }).await; - match db_result { - Ok(_) => return Response::Success, - Err(_) => return Response::Other(sql_err!("WARN member found | Member::FromDB::update failed - ")) - } - }, - Response::Empty => return Response::Empty, - Response::Other(msg) => return Response::Other(msg), - _ => return Response::Other(sql_err!("Member::FromD::update Bro i dont even")) + let query = conn.drop_exec(q, params!{ + "id" => row.id, + "status" => row.status, + "name" => row.name, + "perms" => row.permissions + }).await; + + return match query { + Ok(_) => Response::Success, + Err(err) => Response::Other(sql_err!(err)) } } - return Response::Other(no_conn!("Member::FromDB::update")); + + return Response::Other(no_conn!("db::Member::update")); } async fn delete(p: &Pool, id: UBigInt) -> Response { @@ -151,3 +143,69 @@ impl FromDB for Member { } } } + +impl Member { + pub async fn add(p: &Pool, name: &str, secret: &str, perms: u64) -> Result, SqlError> { + //! @param {pool} + //! @param {&str} + //! @param {&str} + //! @param {u64} + + //! @returns : on_succes => Ok(Response) + //! @returns : on_partial_succes => Ok(Response) + //! @returns : on_failure => Err(SomeBS) + use chrono::Utc; + + let conn = p.get_conn().await?; + + //name + //perms + //secret + let now: BigInt = Utc::now().timestamp(); + + + let conn = conn.drop_exec( + "INSERT INTO members(secret, name, joindate, status, permissions) + VALUES(:secret, :name, :joindate, :status, :permissions)", + mysql_async::params!{ + "secret" => secret.clone(), + "name" => name.clone(), + "joindate" => now, + "status" => STATUS_ONLINE, + "permissions" => perms + }).await?; + + let (_, opt_id): (Conn, Option) = conn.first_exec( + "SELECT id FROM members WHERE secret = :secret", + params!{ + "secret" => secret.clone() + }).await?; + + if let Some(id) = opt_id { + return Ok(Response::Row(Self { + id: id, + secret: secret.into(), + name: name.into(), + joindate: now, + status: STATUS_ONLINE, + permissions: perms + })) + } + + return Ok(Response::Empty); + } + + pub async fn update_perms(p: &Pool, uid: UBigInt, permissions: UBigInt) -> Result { + //! @return on_sucess Ok(NewPermisionsMask) + //! + let conn = p.get_conn().await?; + conn.drop_exec( + "UPDATE members SET permissions = :perms WHERE id = :id", + params!{ + "id" => uid, + "perms" => permissions + }).await?; + + Ok(permissions) + } +} diff --git a/server-api/db/src/messages.rs b/server-api/db/src/messages.rs index e11ed9b..620bbe4 100644 --- a/server-api/db/src/messages.rs +++ b/server-api/db/src/messages.rs @@ -112,3 +112,27 @@ impl FromDB for Message { } } } + +impl Message { + pub async fn send(p: &Pool, content: &str, cid: UBigInt, uid: UBigInt) -> Result, SqlError> { + //! @returns on_sucess -> empty + //! @returns on_failure Err(SqlErr) + + use chrono::Utc; + let conn = p.get_conn().await?; + let q = "INSERT INTO messages + (time, content, author_id, channel_id) + VALUES (:time, :content, :author, :channel)"; + let now = Utc::now().timestamp(); + + conn.prep_exec(q, params!{ + "time" => now, + "content" => content, + "author" => uid, + "channel" => cid + }).await?; + + return Ok(Response::Empty); + } +} + diff --git a/server-api/migrations/2020-07-05-215114_members/up.sql b/server-api/migrations/2020-07-05-215114_members/up.sql index a76d470..9715076 100644 --- a/server-api/migrations/2020-07-05-215114_members/up.sql +++ b/server-api/migrations/2020-07-05-215114_members/up.sql @@ -1,7 +1,7 @@ -- TODO: add rate limiter in some form -- PERMISSIONS start at 0 and full perms => all F's CREATE TABLE IF NOT EXISTS `members`( - `id` bigint UNSIGNED NOT NULL auto_increment, + `id` BIGINT UNSIGNED NOT NULL auto_increment, `secret` varchar(256) NOT NULL, `name` varchar(256) NOT NULL, `joindate` bigint NOT NULL, diff --git a/server-api/migrations/2020-07-06-022319_messages/up.sql b/server-api/migrations/2020-07-06-022319_messages/up.sql index 661a932..d97f62f 100644 --- a/server-api/migrations/2020-07-06-022319_messages/up.sql +++ b/server-api/migrations/2020-07-06-022319_messages/up.sql @@ -4,6 +4,8 @@ CREATE TABLE IF NOT EXISTS `messages`( `time` BIGINT NOT NULL, `content` VARCHAR(2048) NOT NULL, `author_id` BIGINT UNSIGNED NOT NULL, + `channel_id` BIGINT UNSIGNED NOT NULL, PRIMARY KEY (`id`), - FOREIGN KEY (`author_id`) REFERENCES members(`id`) ON DELETE CASCADE + FOREIGN KEY (`author_id`) REFERENCES members(`id`), + FOREIGN KEY (`channel_id`) REFERENCES channels(`id`) ); diff --git a/server-api/src/admin.rs b/server-api/src/admin.rs index ca64cd1..cf2ab9d 100644 --- a/server-api/src/admin.rs +++ b/server-api/src/admin.rs @@ -4,13 +4,16 @@ use hyper::{Response, Body}; use hyper::StatusCode; use mysql_async::Pool; -use mysql_async::error::Error as SqlError; -use mysql_async::prelude::Queryable; use serde_json::Value; use crate::perms::ADMIN_PERMS; +use db::{ + self, + member::Member +}; + macro_rules! get_target_id { ($obj:expr) => { match $obj.get("target-id") { @@ -20,24 +23,15 @@ macro_rules! get_target_id { } } -async fn modify_perms(p: &Pool, uid: u64, new_perms: u64) -> Result<(), SqlError>{ - use mysql_async::params; - let conn = p.get_conn().await?; - conn.prep_exec( - "UPDATE members SET permissions = :perms WHERE id = :id", - params!{ - "id" => uid, - "perms" => new_perms - }).await?; - - Ok(()) -} - pub async fn new_admin(p: &Pool, response: &mut Response, params: Value) { // @requires: owner level permission as regular admins can have conflict of interests + // @user-param: "target-id": Number if let Some(uid) = get_target_id!(params) { - let _ = modify_perms(p, uid, ADMIN_PERMS).await; + + if let Err(e) = Member::update_perms(p, uid, ADMIN_PERMS).await { + eprintln!("{}", e); + } } else { // this is likely the users fault providing shit ass json @@ -46,32 +40,21 @@ pub async fn new_admin(p: &Pool, response: &mut Response, params: Value) { } } -async fn update_member_permissions(p: &Pool, uid: u64, perms: u64) -> Result<(), SqlError>{ - use mysql_async::params; - let conn = p.get_conn().await?; - conn.prep_exec( - "UPDATE members permissions = :perms WHERE id = :id", - params!{ - "id" => uid, - "perms" => perms - } - ).await?; - - Ok(()) -} pub async fn set_permissions(p: &Pool, response: &mut Response, params: Value) { // @requiresL: admin level permissions, admins can't touch other admins let tuid = get_target_id!(params); - let new_perms = match params.get("permissions") { + let new_perms_opt = match params.get("permissions") { Some(val) => val.as_u64(), None => None }; - match (tuid, new_perms) { - (Some(uid), Some(perms)) => { - if let Ok(_) = update_member_permissions(p, uid, perms).await { + match (tuid, new_perms_opt) { + (Some(uid), Some(new_perms)) => { + // Returns Ok(Response::sucess) | Err(log) + if let Err(e) = Member::update_perms(p, uid, new_perms).await { + eprintln!("{}", e); // wil be some sql error } }, _ => { diff --git a/server-api/src/auth.rs b/server-api/src/auth.rs index 8a11c6a..31ca816 100644 --- a/server-api/src/auth.rs +++ b/server-api/src/auth.rs @@ -9,7 +9,6 @@ use db::{member::Member, common::FromDB}; use db::Response; // used when we create a new users for the first time -pub const BCRYPT_COST: u32 = 14; pub enum AuthReason { Good, //passed regular check OpenAuth, // route does not require auth diff --git a/server-api/src/channels.rs b/server-api/src/channels.rs index 2126f41..d27ed2a 100644 --- a/server-api/src/channels.rs +++ b/server-api/src/channels.rs @@ -57,9 +57,13 @@ pub async fn create_channel(pool: &Pool, response: &mut Response, params: *response.body_mut() = Body::from(to_string(&row).unwrap_or("{}".into())); }, - db::Response::Empty => {}, - db::Response::Other(msg) => {}, - _ => {*response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;} + db::Response::Empty => *response.status_mut() = StatusCode::NOT_FOUND, + // TODO: loggin + db::Response::Other(msg) => { + *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; + eprintln!("\t{}", msg); + } + _ => *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR } }, // basically one of the parameter gets failed so we bail on all of this @@ -74,8 +78,14 @@ pub async fn delete_channel(pool: &Pool, response: &mut Response, params: // TODO: something more intelligent with the logging im ngl match Channel::delete(pool, id).await { db::Response::Success => {}, - db::Response::Other(data) => println!("\t{}", data), - _ => {} + db::Response::Other(data) => { + eprintln!("\t{}", data); + *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; + } + _ => { + eprintln!("\tBro like restart the server"); + *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; + } } } else { @@ -87,87 +97,3 @@ pub async fn delete_channel(pool: &Pool, response: &mut Response, params: } } - -#[cfg(test)] -mod channels_tests { - use crate::testing::{get_pool, hyper_resp}; - use serde_json::Value; - use hyper::StatusCode; - const DUMMY_TRANSIENT_CHANNEL: &'static str = "sample channel"; - - #[tokio::test] - async fn list_all_channels_good() { - // Generation of data - let p = get_pool(); - let mut resp = hyper_resp(); - // @params: none - // Collection of data - super::list_channels(&p, &mut resp).await; - - // Analysis - assert_eq!(StatusCode::OK, resp.status()); - println!("list_all_channels_good : \t{:?}", resp.body()); - let _ = p.disconnect().await; - } - - #[tokio::test] - async fn delete_and_create_channel_good() { - use chrono::Utc; - let p = get_pool(); - let mut resp = hyper_resp(); - // @params: name + kind + [description] - let cname_id = Utc::now(); - let params: Value = serde_json::from_str(&format!(r#" - {{ - "name": "{}-{}", - "kind": 2, - "description": "some random bs" - }} - "#, DUMMY_TRANSIENT_CHANNEL, cname_id)).unwrap(); - - super::create_channel(&p, &mut resp, params).await; - - println!("CREATE CHANNEL: {:?}", resp.body()); - assert_eq!(StatusCode::OK, resp.status()); - - - // clean up and hopefully delete the channel properly - let mut resp_delete = hyper_resp(); - let params_delete: Value = serde_json::from_str(&format!(r#" - {{ - "name": "{}-{}" - }} - "#, DUMMY_TRANSIENT_CHANNEL, cname_id)).unwrap(); - println!("Parameters: {}", params_delete); - super::delete_channel(&p, &mut resp_delete, params_delete).await; - - println!("Body: {:?}", resp.body()); - let _ = p.disconnect().await; - } - - #[tokio::test] - async fn delete_channel_missing_name() { - let p = get_pool(); - let mut resp = hyper_resp(); - // this endpoint is super lenient for some reason btw - let param: Value = serde_json::from_str("{}").expect("JSON is not written correctly"); - - super::delete_channel(&p, &mut resp, param).await; - - assert_eq!(StatusCode::BAD_REQUEST, resp.status()); - } - - #[tokio::test] - async fn delet_channel_non_real_channel() { - let p = get_pool(); - let mut resp = hyper_resp(); - // this endpoint is super lenient for some reason btw - let param: Value = serde_json::from_str(r#"{ - "name": "this channel doesn't exist" - }"#).expect("JSON is not written correctly"); - - super::delete_channel(&p, &mut resp, param).await; - - assert_eq!(StatusCode::OK, resp.status()); - } -} diff --git a/server-api/src/invites.rs b/server-api/src/invites.rs index 846b7ad..226d3e6 100644 --- a/server-api/src/invites.rs +++ b/server-api/src/invites.rs @@ -11,11 +11,13 @@ use hyper::{Response, Body, StatusCode}; use chrono::Utc; use db::{UBigInt, BigInt}; -use crate::members::{self, Member}; +use db::common::FromDB; +use db::member::Member; + #[derive(Serialize)] struct Invite { - id: BigInt, + id: BigInt, // doubles as the timestamp for when it dies uses: Option, // optional because some links are permanent expires: bool, } @@ -25,34 +27,33 @@ struct Invite { * are of the enum mysql_async::error::Error */ -async fn valid_invite(pool: &Pool, id: BigInt) -> Result{ +async fn valid_invite(pool: &Pool, id: BigInt) -> bool { /* * Fetches an invite from the database to check for validity */ - let conn = pool.get_conn().await?; - let db_fetch_result: (Conn, Option<(Option, bool)>) = - conn.first_exec("SELECT uses, expires FROM invites WHERE id = :id", - params!{"id" => id}).await?; + let query: Option = match db::invites::Invite::get(pool, id as u64).await { + db::Response::Row(invite) => { Some(invite) }, + _ => { None } + }; - if let Some(row) = db_fetch_result.1 { + if let Some(invite) = query { // if expires at all - if row.1 { + if invite.expires { let now = Utc::now().timestamp(); // old? - let mut status = now > id; + let mut valid_status = now > invite.id; // used? - if row.0.is_some() && status == false { - status = row.0.unwrap() <= 0; // safe unwrap since we know its Some(_) + if invite.uses.is_some() && valid_status == false { + valid_status = invite.uses.unwrap() <= 0; // safe unwrap since we know its Some(_) } - return Ok(status) + return valid_status } // no expiry date? no problem - return Ok(true); + return true } + // prolly not a real id - else { - return Ok(false); - } + return false } @@ -60,21 +61,25 @@ async fn use_invite(pool: &Pool, code: Option) -> Option{ /* * Attempts to change the state of the current invite being provided */ + use crate::auth; use crate::perms::GENERAL_NEW; let id = match code { Some(id) => id, None => 0 }; - if let Ok(valid) = valid_invite(pool, id).await { - if valid { - match members::insert_new_member(pool, "Anonymous".into(), GENERAL_NEW).await { - Ok(member) => return Some(member), - Err(_) => return None - } - } - else { - return None; + // some random comment + if valid_invite(pool, id).await { + let secret = auth::generate_secret(); + return match db::member::Member::add(pool, "Anonymous".into(), &secret, GENERAL_NEW).await { + Ok(response) => { + match response { + db::Response::Row(member) => Some(member), + _ => None, + } + }, + // TODO: logggin or something idk + Err(_) => return None } } else { @@ -90,7 +95,6 @@ pub async fn join(pool: &Pool, response: &mut Response, params: Value) { Some(val) => val.as_i64(), None => None }; - match use_invite(&pool, code).await { Some(new_account) => *response.body_mut() = Body::from(serde_json::to_string(&new_account).unwrap()), None => { diff --git a/server-api/src/main.rs b/server-api/src/main.rs index 58de189..ae08c46 100644 --- a/server-api/src/main.rs +++ b/server-api/src/main.rs @@ -207,9 +207,25 @@ OPTIONS: if let Some(owner_name) = args.value_of("create-owner") { let p = Pool::new(&env::var("DATABASE_URL").unwrap()); eprintln!("Creating owner {{ {} }}...", owner_name); - if let Ok(owner) = members::insert_new_member(&p, owner_name.to_string(), std::u64::MAX).await { - println!("{}", serde_json::to_string(&owner).unwrap()); + let owner_secret = auth::generate_secret(); + // creation of owner should just dump straight to stdout since this fires + // from a commandline parameter anyway + match db::member::Member::add(&p, "owner sama uwu", &owner_secret, perms::OWNER).await { + Ok(response) => { + match response { + db::Response::Row(owner) => + println!("{}", serde_json::to_string(&owner).expect("SQL query passed but serde couldn't parse the data for some reason")), + db::Response::Empty => { + eprintln!("SQL server failed to return owner data, check configs and also the members table to make sure there's nothing there by accident"); + }, + _ => {} + }; + }, + Err(_) => { + eprintln!("Could not communicate with the SQL server, check your configs!"); + } } + //println!("{}", serde_json::to_string(&owner).unwrap()); p.disconnect(); } diff --git a/server-api/src/members.rs b/server-api/src/members.rs index 6dd2302..c875c86 100644 --- a/server-api/src/members.rs +++ b/server-api/src/members.rs @@ -1,86 +1,22 @@ -use chrono::Utc; -use hyper::{Response, Body}; -use mysql_async::{Conn, Pool, error::Error as SqlError}; -use mysql_async::prelude::{params, Queryable}; -use serde::Serialize; +use hyper::{Response, Body, StatusCode}; +use hyper::header::HeaderValue; +use mysql_async::Pool; -use db::{UBigInt, BigInt, Integer, VarChar}; use db::member::STATUS_ONLINE; -use crate::auth; - -#[derive(Serialize)] -pub struct Member { - pub id: UBigInt, - pub secret: VarChar, - pub name: VarChar, - pub joindate: BigInt, - pub status: Integer, - pub permissions: UBigInt, -} - - -pub async fn insert_new_member(p: &Pool, name: VarChar, perms: u64) -> Result { - use crate::auth::generate_secret; - - let conn: Conn = p.get_conn().await?; - let secret_raw: String = generate_secret(); - let secret = match bcrypt::hash(&secret_raw, auth::BCRYPT_COST) { - Ok(value) => value, - Err(e) => panic!("\tCould not insert member due to bcrypt failure:\n\t\t{}",e) - }; - let now: BigInt = Utc::now().timestamp(); - - let conn = conn.drop_exec( - "INSERT INTO members(secret, name, joindate, status, permissions) - VALUES(:secret, :name, :joindate, :status, :permissions)", - mysql_async::params!{ - "secret" => secret.clone(), - "name" => name.clone(), - "joindate" => now, - "status" => STATUS_ONLINE, - "permissions" => perms - }).await?; - - // now pull back the user from our db and return that row - let db_row_result: (Conn, Option) = conn.first_exec( - "SELECT id FROM members WHERE secret = :secret", - params!{ - "secret" => secret.clone() - }).await?; - - Ok(Member { - id: db_row_result.1.unwrap(), // if we made it this far this shouldn't fail (i hope) - secret: secret_raw, - name: name, - joindate: now, - status: 0, - permissions: perms - }) -} - -async fn select_online_default(p: &Pool) -> Result, SqlError> { - let conn = p.get_conn().await?; - let q = format!("SELECT id, name FROM members WHERE status = {} LIMIT 100", STATUS_ONLINE); - let data = conn.prep_exec(&q, ()).await?; - let (_, users) = data.map_and_drop(|row| { - let (id, nickname): (UBigInt, VarChar) = mysql_async::from_row(row); - (id, nickname) - }).await?; - - return Ok(users); -} +use db::common::FromDB; pub async fn get_online_members(p: &Pool, response: &mut Response) { - /* - * Json { - * "members": [...], - * "start": 0, - * "count": 100 - * } - */ - if let Ok(list) = select_online_default(p).await { - // unwrap_or`ing for serde_json since it shouldn't fail in theory(on paper) - *response.body_mut() = Body::from(serde_json::to_string(&list).unwrap_or("[]".into())); + // TODO: at some point we should provide a way of not querying literally every user in + // existance + // TODO: loggin at some point or something idklol + return match db::channels::Channel::filter(p, STATUS_ONLINE).await { + db::Response::Set(users) => { + response.headers_mut().insert("Content-Type", + HeaderValue::from_static("application/json")); + + *response.body_mut() = Body::from(serde_json::to_string(&users).unwrap_or("[]".into())); + }, + db::Response::Other(_msg) => *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR, + _ => *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR } - println!("fuck bro"); } diff --git a/server-api/src/messages.rs b/server-api/src/messages.rs index 220f6df..d66bc90 100644 --- a/server-api/src/messages.rs +++ b/server-api/src/messages.rs @@ -1,78 +1,35 @@ -use std::borrow::Cow; - -use mysql_async::{Pool, params}; -use mysql_async::prelude::{Queryable}; -use mysql_async::error::Error; +use mysql_async::Pool; use hyper::{Response, Body, StatusCode}; use serde_json::Value; -use chrono::Utc; -use db::UBigInt; - - -pub async fn insert_message(pool: &Pool, content: &Value, channel_name: &Value, author_id: UBigInt) - -> Result<(), Error>{ - match (content.as_str(), channel_name.as_str()) { - (Some(content), Some(channel)) => { - let conn = pool.get_conn().await?; - let time = Utc::now().timestamp(); - conn.prep_exec( - r"INSERT INTO messages - (time, content, author_id, channel_name) - VALUES(:time, :content, :author, :channel)", - params!{ - "time" => time, - "content" => content, - "author" => author_id, - "channel" => channel - }).await?; - Ok(()) - } - _ => { - let e = Cow::from("Required parameter missing"); - Err(Error::Other(e)) - } - } -} pub async fn send_message(pool: &Pool, response: &mut Response, params: Value) { /* * @content: expecting string type - * @id: expecting the channel id that we're posting data to + * @channel: channel id that we're going to send a message to */ - let content_r = params.get("content"); - let channel_name_r = params.get("channel"); - // auth module guarantees this will be there in the correct form + // NOTE: auth module guarantees this will be there in the correct form let author = params.get("id") .unwrap().as_u64().unwrap(); - match (content_r, channel_name_r) { - (Some(content), Some(channel_name)) => { - match insert_message(pool, content, channel_name, author).await { - Ok(_) => *response.status_mut() = StatusCode::OK, - Err(err) => { - use mysql_async::error::Error::{Server}; - println!("\tDB Error::send_message: {:?}", err); - // doing this to avoid client confusion as some input does cause sql errors - if let Server(se) = err { - if se.code == 1452 { - *response.status_mut() = StatusCode::BAD_REQUEST; - *response.body_mut() = Body::from(format!("{} does not exist", channel_name)); - } - else { - *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; - } - } - else { - *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; - } + match (params.get("content") , params.get("channel")) { + (Some(content_v), Some(channel_id_v)) => { + let (content, channel) = (content_v.as_str(), channel_id_v.as_u64()); + + if let (Some(message), Some(cid)) = (content, channel) { + // call returns empty on sucess so we don't need to do anything + // TODO: loggin + if let Err(issue) = db::messages::Message::send(pool, message, cid, author).await { + *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; + eprintln!("\t{}", issue); } + // if there's no issue then we don't need to do anything at all + } + else { + *response.status_mut() = StatusCode::BAD_REQUEST; } }, - _ => { - *response.status_mut() = StatusCode::BAD_REQUEST; - *response.body_mut() = Body::from("content/channel missing from json parameters"); - } + _ => *response.status_mut() = StatusCode::BAD_REQUEST } } @@ -103,46 +60,5 @@ mod messaging_tests { assert_ne!(StatusCode::OK, resp.status()); } - #[tokio::test]#[ignore] - async fn send_message_good() { - use crate::members::insert_new_member; - use crate::perms::GENERAL_NEW; - use mysql_async::params; - use mysql_async::prelude::Queryable; - use crate::testing::tmp_channel_params; - - let p = get_pool(); - let mut resp = hyper_resp(); - - let tmp_chan = tmp_channel_params(&p, "sample").await; - - const TMP_NAME: &'static str = "bs user"; - let temp_member = insert_new_member(&p, TMP_NAME.into(), GENERAL_NEW).await.unwrap(); - - - let params: Value = serde_json::from_str(&format!(r#" - {{ - "id": {}, - "channel": "{}", - "content": "bs message" - }} - "#, temp_member.id, tmp_chan.name)).unwrap(); - - super::send_message(&p, &mut resp, params).await; - - if resp.status() == StatusCode::BAD_REQUEST { - panic!("{:?}", resp.body()); - } - - // Destroy the the message and the user that we created - let conn = match p.get_conn().await { - Ok(c) => c, - Err(e) => panic!("Could not get connection to db during send_message_good:\nIssue:\t{}", e) - }; - - let conn = conn.drop_exec("DELETE FROM messages WHERE author_id = :id", params!{"id" => temp_member.id}).await.unwrap(); - let conn = conn.drop_exec("DELETE FROM members WHERE id = :id", params!{"id" => temp_member.id}).await.unwrap(); - let _ = conn.drop_exec("DELETE FROM channels WHERE name = :name", params!{"name" => tmp_chan.name}).await; - } } diff --git a/server-api/src/routes.rs b/server-api/src/routes.rs index 4d0129a..1a7e0b3 100644 --- a/server-api/src/routes.rs +++ b/server-api/src/routes.rs @@ -1,26 +1,31 @@ // TODO: this whole ass module bro i mean c'mon.... just just clean it -pub const INVITE_CREATE: &'static str = "/invite/create"; // requires none +type Rstr = &'static str; -pub const CHANNELS_LIST: &'static str = "/channels/list"; // requires none -pub const CHANNELS_CREATE: &'static str = "/channels/create"; // requires @name @kind -pub const CHANNELS_DELETE: &'static str = "/channels/delete"; // requires @channel_id +pub const SERVER_META: Rstr = "/meta"; // @returns server meta document TBD +pub const INVITE_CREATE: Rstr = "/invite/create"; // @ perms::CREATE_INVITE -pub const MESSAGE_SEND: &'static str = "/message/send"; // requires @content +pub const CHANNELS_LIST: Rstr = "/channels/list"; // requires none +pub const CHANNELS_CREATE: Rstr = "/channels/create"; // requires @name @kind perms::CREATE_CHANNEl +pub const CHANNELS_DELETE: Rstr = "/channels/delete"; // requires @name perms::DELETE_CHANNEL -pub const SERVER_META: &'static str = "/meta"; // open +pub const MESSAGE_SEND: Rstr = "/message/send"; // requires @content perms::MESSAGE_SEND -pub const GET_ONLINE_MEMBERS: &'static str = "/members/get_online"; -// @requires: admin permissions -// -pub const SET_PERMS_BY_ADMIN: &'static str = "/admin/setpermisions"; -pub const SET_NEW_ADMIN: &'static str = "/owner/newadmin"; // @requiers: owner perms +pub const GET_ONLINE_MEMBERS: Rstr = "/members/get_online"; + + +// ADMIN ROUTES +pub const SET_PERMS_BY_ADMIN: Rstr = "/admin/setpermisions"; // @requires perms::ADMIN +pub const SET_NEW_ADMIN: Rstr = "/owner/newadmin"; // @requiers: owner perms + + +// TODO: here be dragons... // potentially adding more bases later -pub const DYNAMIC_ROUTE_BASES: [(&'static str, bool);3] = [ +pub const DYNAMIC_ROUTE_BASES: [(Rstr, bool);3] = [ ("/join", true), // open ("/public", true), // open : valid sections := neighbors|rules|description ("/user", false), // TODO: valid sections := /meta/|/dm/ ]; -pub const DYN_JOIN: &'static str = DYNAMIC_ROUTE_BASES[0].0; +pub const DYN_JOIN: Rstr = DYNAMIC_ROUTE_BASES[0].0; pub struct DynRoute { pub base: String,