Merging new db-lib code integration

! I should note that there are two things which are incomplete

1. channel deletion with linked messages
2. Invites module has not been refactored BUT behaves beautifully in all tests so far so i cba
This commit is contained in:
shockrah 2020-11-17 22:51:59 -08:00
commit e4fdf4feb7
20 changed files with 344 additions and 432 deletions

2
server-api/Cargo.lock generated
View File

@ -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]]

View File

@ -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

View File

@ -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
}
@ -96,6 +96,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

117
server-api/db/Cargo.lock generated
View File

@ -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",

View File

@ -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"

View File

@ -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"))
}

View File

@ -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)
}
}

View File

@ -20,6 +20,9 @@ impl FromDB<Invite, bool> for Invite {
type Row = Option<(BigInt, Option<BigInt>, bool)>;
async fn get(p: &Pool, id: UBigInt) -> Response<Self> {
// 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;
}

View File

@ -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<Member, Integer> 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, SqlError>
= conn.drop_exec(q, params!{
let query = 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"))
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<Self> {
@ -151,3 +143,69 @@ impl FromDB<Member, Integer> for Member {
}
}
}
impl Member {
pub async fn add(p: &Pool, name: &str, secret: &str, perms: u64) -> Result<Response<Self>, SqlError> {
//! @param {pool}
//! @param {&str}
//! @param {&str}
//! @param {u64}
//! @returns : on_succes => Ok(Response<Member>)
//! @returns : on_partial_succes => Ok(Response<Member>)
//! @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<UBigInt>) = 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<UBigInt, SqlError> {
//! @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)
}
}

View File

@ -112,3 +112,27 @@ impl FromDB<Message, (BigInt, UBigInt)> for Message {
}
}
}
impl Message {
pub async fn send(p: &Pool, content: &str, cid: UBigInt, uid: UBigInt) -> Result<Response<Self>, 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);
}
}

View File

@ -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,

View File

@ -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`)
);

View File

@ -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<Body>, 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<Body>, 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<Body>, 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
}
},
_ => {

View File

@ -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

View File

@ -57,9 +57,13 @@ pub async fn create_channel(pool: &Pool, response: &mut Response<Body>, 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<Body>, 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<Body>, 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());
}
}

View File

@ -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<BigInt>, // 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<bool, Error>{
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<BigInt>, bool)>) =
conn.first_exec("SELECT uses, expires FROM invites WHERE id = :id",
params!{"id" => id}).await?;
let query: Option<db::invites::Invite> = 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,26 +61,30 @@ async fn use_invite(pool: &Pool, code: Option<BigInt>) -> Option<Member>{
/*
* 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),
// 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 {
return None;
}
}
else {
return None;
}
}
pub async fn join(pool: &Pool, response: &mut Response<Body>, params: Value) {
@ -90,7 +95,6 @@ pub async fn join(pool: &Pool, response: &mut Response<Body>, 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 => {

View File

@ -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();
}

View File

@ -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<Member, SqlError> {
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<UBigInt>) = 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<Vec<(UBigInt, VarChar)>, 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<Body>) {
/*
* 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");
}

View File

@ -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<Body>, 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 {
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.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;
}
}
}
},
_ => {
*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;
}
}

View File

@ -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/<name>|/dm/<name>
];
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,