From 157d1333175024f942bac9f473ee636eb547835a Mon Sep 17 00:00:00 2001 From: shockrah Date: Mon, 10 Aug 2020 21:48:44 -0700 Subject: [PATCH] join via invite seems to be scaffolded properly but now requires testing --- server/src/invites.rs | 143 +++++++++++++++++++++--------------------- 1 file changed, 71 insertions(+), 72 deletions(-) diff --git a/server/src/invites.rs b/server/src/invites.rs index dd7a1f5..0e4eae1 100644 --- a/server/src/invites.rs +++ b/server/src/invites.rs @@ -1,4 +1,5 @@ use serde_json::Value; +use serde::Serialize; use mysql_async; use mysql_async::{Conn, Pool}; @@ -8,10 +9,11 @@ use mysql_async::prelude::{params, Queryable}; use hyper::{Response, Body, StatusCode}; use chrono::Utc; -use rand::random; -use crate::db_types::{BigInt, Integer}; +use crate::db_types::BigInt; +use crate::members::{self, Member}; +#[derive(Serialize)] struct Invite { id: BigInt, uses: Option, // optional because some links are permanent @@ -23,84 +25,77 @@ struct Invite { * are of the enum mysql_async::error::Error */ -impl Invite { - pub fn from_tuple(tup: (BigInt, Option, bool)) -> Invite { - /* - * Let's us convert tuples from mysql into convenient invite structs */ - Invite { - id: tup.0, - uses: tup.1, - expires: tup.2 - } - } - - pub fn as_json_str(&self) -> String { - // TODO: Deprecate - let id = format!("\"id\":{}", self.id); - let expires = format!("\"expires\":{}", self.expires); - let uses = format!("\"uses\":{:?}", self.uses); - - let mut data = String::from("{"); - data.push_str(&format!("{},", id)); - data.push_str(&format!("{},", expires)); - data.push_str(&format!("{}}}", uses)); - data - } -} - -async fn get_invite_by_code(pool: &Pool, value: Option<&str>) -> Result, Error> { - if let Some(val) = value { - let conn = pool.get_conn().await?; - let db_row_result: (Conn, Option<(BigInt, Option, bool)>) = conn - .first_exec(r"SELECT * FROM", mysql_async::params!{"code"=>val}) - .await?; - if let Some(tup) = db_row_result.1 { - Ok(Some(Invite::from_tuple(tup))) - } - else { - // basically nothing was found but nothing bad happened - Ok(None) - } - } - // again db didn't throw a fit but we don't have a good input - else {Ok(None)} -} - -async fn record_invite_usage(pool: &Pool, data: &Invite) -> Result<(), Error>{ +async fn valid_invite(pool: &Pool, id: BigInt) -> Result{ /* - * By this this is called we really don't care about what happens as we've - * already been querying the db and the likely hood of this seriously failing - * is low enough to write a wall of text and not a wall of error handling code - */ + * Fetches an invite from the database to check for validity + */ let conn = pool.get_conn().await?; - let _db_result = conn - .prep_exec(r"UPDATE invites SET uses = :uses WHERE id = :id", mysql_async::params!{ - "uses" => data.uses - 1, - "id" => data.id - }).await?; + let db_fetch_result: (Conn, Option<(Option, bool)>) = + conn.first_exec("SELECT uses, expires FROM invites WHERE id = :id", + params!{"id" => id}).await?; + + if let Some(row) = db_fetch_result.1 { + // if expires at all + if row.1 { + let now = Utc::now().timestamp(); + // old? + let mut status = now > id; + // used? + if row.0.is_some() && status == false { + status = row.0.unwrap() <= 0; // safe unwrap since we know its Some(_) + } + return Ok(status) + } + // no expiry date? no problem + return Ok(true); + } + // prolly not a real id + else { + return Ok(false); + } - Ok(()) } -pub async fn route_join_invite_code(pool: &Pool, response: &mut Response, params: Value) -> Result<(), Error> { - // collect some things first - if let Some(code) = params.get("code") { - if let Some(row) = get_invite_by_code(pool, code.as_str()).await? { - // since we have a row make sure the invite is valid - let now = Utc::now().timestamp() as u64; - // usable and expires in the future - if row.uses > 0 && row.expires > now { - record_invite_usage(pool, &row).await?; - // TODO: assign some actual data to the body - *response.status_mut() = StatusCode::OK; +async fn use_invite(pool: &Pool, code: Option) -> Option{ + /* + * Attempts to change the state of the current invite being provided + */ + 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; + } } - else{ - *response.status_mut() = StatusCode::BAD_REQUEST; + else { + return None; } +} - Ok(()) +pub async fn join(pool: &Pool, response: &mut Response, params: Value) { + /* + * Main dispatcher for dealing with an attempted join via a given invide code + */ + let code = match params.get("invite-id") { + 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 => { + } + } } async fn insert_new_invite(pool: &Pool, invite: &Invite) -> Result<(), Error>{ @@ -126,8 +121,9 @@ pub async fn create(pool: &Pool, response: &mut Response, params: Value) { None => None }; + // TODO: remove the unwrap let expires = match params.get("expires") { - Some(val) => val.as_bool().unwrap(), + Some(val) => val.as_bool().unwrap_or(true), None => true }; @@ -139,6 +135,9 @@ pub async fn create(pool: &Pool, response: &mut Response, params: Value) { match insert_new_invite(&pool, &invite).await { Ok(_) => *response.body_mut() = Body::from("yes"), - Err(mysqle) => *response.status_mut() = StatusCode::BAD_REQUEST + Err(mysqle) => { + println!("\tINVITES::CREATE::ERROR: {}", mysqle); + *response.status_mut() = StatusCode::BAD_REQUEST; + } } }