freechat/json-api/src/invites.rs
2021-02-25 16:05:46 -08:00

181 lines
5.0 KiB
Rust

use serde_json::json;
use mysql_async;
use mysql_async::Pool;
use hyper::{Response, Body, StatusCode};
use chrono::Utc;
use std::collections::HashMap;
use db::BigInt;
use db::common::FromDB;
use db::member::Member;
use db::invites::Invite;
use crate::qs_param;
use crate::http;
use crate::meta;
/*
* Error handling:
* All errors raisable from this module come from mysql_async and thus
* are of the enum mysql_async::error::Error
*/
async fn valid_invite(pool: &Pool, id: BigInt) -> bool {
/*
* Fetches an invite from the database to check for validity
*/
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(invite) = query {
// if expires at all
if invite.expires {
let now = Utc::now().timestamp_millis();
// old?
let mut valid_status = now < invite.id;
// used?
if invite.uses.is_some() && valid_status == false {
valid_status = invite.uses.unwrap() <= 0; // safe unwrap since we know its Some(_)
}
return valid_status
}
// no expiry date? no problem
return true
}
// prolly not a real id
return false
}
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
};
// some random comment
if valid_invite(pool, id).await {
let raw_secret = auth::generate_secret();
let secret = auth::encrypt_secret(&raw_secret).unwrap();
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(_) => None
}
}
// The invite itself was not valid
// Returning None because we couldn't actually create a proper secret to store
return None;
}
pub async fn join(pool: &Pool, response: &mut Response<Body>, params: HashMap<String, String>) {
/*
* Main dispatcher for dealing with an attempted join via a given invide code
*
*/
let code = match params.get("code") {
Some(val) => {
// i64 because we're using unix timestamps and basically the database is dumb
if let Ok(num) = (*val).to_string().parse::<i64>() {
Some(num)
} else {
None
}
},
None => None
};
// Returns a new member if the code is used successfully
match use_invite(&pool, code).await {
Some(new_account) => {
http::set_json_body(response, json!({
"user": new_account,
"server": meta::get_config()
}))
},
None => {
*response.status_mut() = StatusCode::NOT_FOUND;
}
}
}
async fn allowed_perm_invite(pool: &Pool, uid: u64) -> bool {
use crate::perms;
return match db::member::Member::get(pool, uid).await {
db::Response::Row(user) => perms::has_perm(user.permissions, perms::CREATE_PERM_INVITES),
_ => false
};
}
pub async fn create(pool: &Pool, response: &mut Response<Body>, params: HashMap<String, String>) {
/*
* Creates a new invite
* Parameters required asked of the user to provide
* uses : Option<i64>
* expires: Option<bool>
*/
let id = qs_param!(params, "id", u64).unwrap();
let use_count = match params.get("uses") {
Some(val) => {
match (*val).to_string().parse::<i64>() {
Ok(count) => Some(count),
Err(_) => None
}
},
None => None
};
let expirey_request = match params.get("expires") {
Some(exp_val) => {
match exp_val.to_string().parse::<bool>() {
Ok(exp) => {
match exp {
true => allowed_perm_invite(pool, id).await,
false => false
}
},
_ => false
}
}
None => true
};
// TODO: prolly add some kind option to set an expire time
let invite = Invite::new(use_count, expirey_request);
match invite.add(pool).await {
Ok(_) => {
// return the id of the invite
// Link format from here is basically hostname.io:4536/join?code=<some-code>
http::set_json_body(response, serde_json::json!(invite))
},
Err(mysqle) => {
println!("\tINVITES::CREATE::ERROR: {}", mysqle);
*response.status_mut() = StatusCode::BAD_REQUEST;
}
}
}