From 20c82ddef30b1384e8adb5cf46c3a6d991da4fe3 Mon Sep 17 00:00:00 2001 From: Paul Makles <paulmakles@gmail.com> Date: Sun, 26 Jan 2020 14:16:58 +0000 Subject: [PATCH] Add all friend routes. --- src/auth.rs | 25 ++++- src/database.rs | 6 +- src/routes/account.rs | 10 +- src/routes/mod.rs | 2 +- src/routes/user.rs | 244 +++++++++++++++++++++++++++++++++++++++--- 5 files changed, 262 insertions(+), 25 deletions(-) diff --git a/src/auth.rs b/src/auth.rs index 349311b..2e5e9ef 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -1,6 +1,6 @@ use rocket::Outcome; -use rocket::http::Status; -use rocket::request::{self, Request, FromRequest}; +use rocket::http::{ Status, RawStr }; +use rocket::request::{ self, Request, FromRequest, FromParam }; use bson::{ bson, doc, ordered::OrderedDocument }; use ulid::Ulid; @@ -45,4 +45,23 @@ impl<'a, 'r> FromRequest<'a, 'r> for User { _ => Outcome::Failure((Status::BadRequest, AuthError::BadCount)), } } -} \ No newline at end of file +} + +impl<'r> FromParam<'r> for User { + type Error = &'r RawStr; + + fn from_param(param: &'r RawStr) -> Result<Self, Self::Error> { + let col = database::get_db().collection("users"); + let result = col.find_one(doc! { "_id": param.to_string() }, None).unwrap(); + + if let Some(user) = result { + Ok(User( + Ulid::from_string(user.get_str("_id").unwrap()).unwrap(), + user.get_str("username").unwrap().to_string(), + user + )) + } else { + Err(param) + } + } +} diff --git a/src/database.rs b/src/database.rs index ca71b45..551688e 100644 --- a/src/database.rs +++ b/src/database.rs @@ -1,4 +1,4 @@ -use mongodb::{ Client, Database }; +use mongodb::{ Client, Collection, Database }; use std::env; use once_cell::sync::OnceCell; @@ -19,3 +19,7 @@ pub fn get_connection() -> &'static Client { pub fn get_db() -> Database { get_connection().database("revolt") } + +pub fn get_collection(collection: &str) -> Collection { + get_db().collection(collection) +} diff --git a/src/routes/account.rs b/src/routes/account.rs index 17e556c..7e0aedd 100644 --- a/src/routes/account.rs +++ b/src/routes/account.rs @@ -33,7 +33,7 @@ pub struct Create { /// (3) add user and send email verification #[post("/create", data = "<info>")] pub fn create(info: Json<Create>) -> JsonValue { - let col = database::get_db().collection("users"); + let col = database::get_collection("users"); if info.username.len() < 2 || info.username.len() > 32 { return json!({ @@ -108,7 +108,7 @@ pub fn create(info: Json<Create>) -> JsonValue { /// (3) set account as verified #[get("/verify/<code>")] pub fn verify_email(code: String) -> JsonValue { - let col = database::get_db().collection("users"); + let col = database::get_collection("users"); if let Some(u) = col.find_one(doc! { "email_verification.code": code.clone() }, None).expect("Failed user lookup") { @@ -166,8 +166,8 @@ pub struct Resend { /// (2) check for rate limit /// (3) resend the email #[post("/resend", data = "<info>")] -pub fn resend_email(info: Json<Resend>) -> JsonValue { - let col = database::get_db().collection("users"); +pub fn resend_email(info: Json<Resend>) -> JsonValue { + let col = database::get_collection("users"); if let Some(u) = col.find_one(doc! { "email_verification.target": info.email.clone() }, None).expect("Failed user lookup") { @@ -239,7 +239,7 @@ pub struct Login { /// (3) return access token #[post("/login", data = "<info>")] pub fn login(info: Json<Login>) -> JsonValue { - let col = database::get_db().collection("users"); + let col = database::get_collection("users"); if let Some(u) = col.find_one(doc! { "email": info.email.clone() }, None).expect("Failed user lookup") { diff --git a/src/routes/mod.rs b/src/routes/mod.rs index 3640104..a32647e 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -6,5 +6,5 @@ mod user; pub fn mount(rocket: Rocket) -> Rocket { rocket .mount("/api/account", routes![ account::create, account::verify_email, account::resend_email, account::login ]) - .mount("/api/users", routes![ user::me, user::dms, user::lookup ]) + .mount("/api/users", routes![ user::me, user::user, user::lookup, user::dms, user::dm, user::get_friends, user::get_friend, user::add_friend, user::remove_friend ]) } diff --git a/src/routes/user.rs b/src/routes/user.rs index 8f74db4..74cd5bc 100644 --- a/src/routes/user.rs +++ b/src/routes/user.rs @@ -22,8 +22,8 @@ pub fn me(user: User) -> JsonValue { } /// retrieve another user's information -#[get("/<id>")] -pub fn user(user: User, id: String) -> JsonValue { +#[get("/<target>")] +pub fn user(user: User, target: User) -> JsonValue { json!([]) } @@ -36,7 +36,7 @@ pub struct Query { /// currently only supports exact username searches #[post("/lookup", data = "<query>")] pub fn lookup(_user: User, query: Json<Query>) -> JsonValue { - let col = database::get_db().collection("users"); + let col = database::get_collection("users"); let users = col.find( doc! { "username": query.username.clone() }, @@ -64,31 +64,245 @@ pub fn dms(user: User) -> JsonValue { } /// open a DM with a user -#[get("/<id>/dm")] -pub fn dm(user: User, id: String) -> JsonValue { +#[get("/<target>/dm")] +pub fn dm(user: User, target: User) -> JsonValue { json!([]) } +enum Relationship { + FRIEND = 0, + OUTGOING = 1, + INCOMING = 2, + BLOCKED = 3, + BLOCKED_OTHER = 4, + NONE = 5, + SELF = 6, +} + +fn get_relationship(a: &User, b: &User) -> Relationship { + if a.0.to_string() == b.0.to_string() { + return Relationship::SELF + } + + if let Ok(arr) = b.2.get_array("relations") { + let id = a.0.to_string(); + + for entry in arr { + let relation = entry.as_document().expect("Expected document in relations array."); + + if relation.get_str("id").expect("DB[id]") == id { + + match relation.get_i32("status").expect("DB[status]") { + 0 => { + return Relationship::FRIEND + }, + 1 => { + return Relationship::INCOMING + }, + 2 => { + return Relationship::OUTGOING + }, + 3 => { + return Relationship::BLOCKED_OTHER + } + _ => { + return Relationship::NONE + } + } + + } + } + } + + Relationship::NONE +} + /// retrieve all of your friends #[get("/@me/friend")] pub fn get_friends(user: User) -> JsonValue { - json!([]) + let mut results = Vec::new(); + if let Ok(arr) = user.2.get_array("relations") { + for item in arr { + let doc = item.as_document().expect("Expected document in relations array."); + results.push( + json!({ + "id": doc.get_str("id").expect("DB[id]"), + "status": doc.get_i32("status").expect("DB[status]") + }) + ) + } + } + + json!(results) } /// retrieve friend status with user -#[get("/<id>/friend")] -pub fn get_friend(user: User, id: String) -> JsonValue { - json!([]) +#[get("/<target>/friend")] +pub fn get_friend(user: User, target: User) -> JsonValue { + let relationship = get_relationship(&user, &target); + + json!({ + "id": target.0.to_string(), + "status": relationship as u8 + }) } /// create or accept a friend request -#[put("/<id>/friend")] -pub fn add_friend(user: User, id: String) -> JsonValue { - json!([]) +#[put("/<target>/friend")] +pub fn add_friend(user: User, target: User) -> JsonValue { + let col = database::get_collection("users"); + + let relationship = get_relationship(&user, &target); + let User ( id, _, _ ) = user; + let User ( tid, _, _ ) = target; + + match relationship { + Relationship::FRIEND => + json!({ + "success": false, + "error": "Already friends." + }), + Relationship::OUTGOING => + json!({ + "success": false, + "error": "Already sent a friend request." + }), + Relationship::INCOMING => { + col.update_one( + doc! { + "_id": id.to_string(), + "relations.id": tid.to_string() + }, + doc! { + "$set": { + "relations.$.status": Relationship::FRIEND as i32 + } + }, + None + ).expect("Failed update query."); + + col.update_one( + doc! { + "_id": tid.to_string(), + "relations.id": id.to_string() + }, + doc! { + "$set": { + "relations.$.status": Relationship::FRIEND as i32 + } + }, + None + ).expect("Failed update query."); + + json!({ + "success": true + }) + }, + Relationship::BLOCKED => + json!({ + "success": false, + "error": "You have blocked this person." + }), + Relationship::BLOCKED_OTHER => + json!({ + "success": false, + "error": "You have been blocked by this person." + }), + Relationship::NONE => { + col.update_one( + doc! { + "_id": id.to_string() + }, + doc! { + "$push": { + "relations": { + "id": tid.to_string(), + "status": Relationship::OUTGOING as i32 + } + } + }, + None + ).expect("Failed update query."); + + col.update_one( + doc! { + "_id": tid.to_string() + }, + doc! { + "$push": { + "relations": { + "id": id.to_string(), + "status": Relationship::INCOMING as i32 + } + } + }, + None + ).expect("Failed update query."); + + json!({ + "success": true + }) + }, + Relationship::SELF => + json!({ + "success": false, + "error": "Cannot add yourself as a friend." + }) + } } /// remove a friend or deny a request -#[delete("/<id>/friend")] -pub fn remove_friend(user: User, id: String) -> JsonValue { - json!([]) +#[delete("/<target>/friend")] +pub fn remove_friend(user: User, target: User) -> JsonValue { + let col = database::get_collection("users"); + + let relationship = get_relationship(&user, &target); + let User ( id, _, _ ) = user; + let User ( tid, _, _ ) = target; + + match relationship { + Relationship::FRIEND | + Relationship::OUTGOING | + Relationship::INCOMING => { + col.update_one( + doc! { + "_id": id.to_string() + }, + doc! { + "$pull": { + "relations": { + "id": tid.to_string() + } + } + }, + None + ).expect("Failed update query."); + + col.update_one( + doc! { + "_id": tid.to_string() + }, + doc! { + "$pull": { + "relations": { + "id": id.to_string() + } + } + }, + None + ).expect("Failed update query."); + + json!({ + "success": true + }) + }, + Relationship::BLOCKED | + Relationship::BLOCKED_OTHER | + Relationship::NONE | + Relationship::SELF => + json!({ + "success": false, + "error": "This has no effect." + }) + } } -- GitLab