123 lines
4.0 KiB
Rust
123 lines
4.0 KiB
Rust
use serde_json::Value;
|
|
|
|
use mysql_async;
|
|
use mysql_async::{Conn, Pool};
|
|
use mysql_async::error::Error;
|
|
use mysql_async::prelude::{params, Queryable};
|
|
|
|
use hyper::{Response, Body, StatusCode};
|
|
|
|
use chrono::Utc;
|
|
use rand::random;
|
|
struct InviteRow {
|
|
id: u64,
|
|
expires: u64,
|
|
uses: i32,
|
|
}
|
|
/*
|
|
* Error handling:
|
|
* All errors raisable from this module come from mysql_async and thus
|
|
* are of the enum mysql_async::error::Error
|
|
*/
|
|
|
|
impl InviteRow {
|
|
pub fn new() -> InviteRow {
|
|
let dt = Utc::now() + chrono::Duration::minutes(30);
|
|
// TODO:[maybe] ensure no collisions by doing a quick database check here
|
|
let invite = InviteRow {
|
|
id: random::<u64>(), // hopefully there won't ever be collision with this size of pool
|
|
uses: 1, // default/hardcorded for now
|
|
expires: dt.timestamp() as u64
|
|
};
|
|
invite
|
|
}
|
|
pub fn from_tuple(tup: (u64, u64, i32)) -> InviteRow {
|
|
InviteRow {
|
|
id: tup.0,
|
|
expires: tup.1,
|
|
uses: tup.2,
|
|
}
|
|
}
|
|
|
|
pub fn as_json_str(&self) -> String {
|
|
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<Option<InviteRow>, Error> {
|
|
if let Some(val) = value {
|
|
let conn = pool.get_conn().await?;
|
|
let db_row_result: (Conn, Option<(u64, u64, i32)>) = conn
|
|
.first_exec(r"SELECT * FROM", mysql_async::params!{"code"=>val})
|
|
.await?;
|
|
if let Some(tup) = db_row_result.1 {
|
|
Ok(Some(InviteRow::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: &InviteRow) -> Result<(), Error>{
|
|
/*
|
|
* 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
|
|
*/
|
|
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?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn route_join_invite_code(pool: &Pool, response: &mut Response<Body>, params: Value) -> Result<(), Error> {
|
|
// First check that the code is there
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
else{
|
|
*response.status_mut() = StatusCode::BAD_REQUEST;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn create_invite(pool: &Pool, response: &mut Response<Body>) -> Result<(), Error> {
|
|
let invite = InviteRow::new();
|
|
let conn = pool.get_conn().await?;
|
|
conn.prep_exec(r"INSERT INTO invites (id, expires, uses) VALUES (:id, :expires, :uses",
|
|
mysql_async::params!{
|
|
"id" => invite.id,
|
|
"expires" => invite.expires,
|
|
"uses" => invite.uses,
|
|
}).await?;
|
|
|
|
*response.body_mut() = Body::from(invite.as_json_str());
|
|
*response.status_mut() = StatusCode::OK;
|
|
Ok(())
|
|
} |