diff --git a/Cargo.lock b/Cargo.lock index fabf2b3..c576835 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -619,7 +619,7 @@ dependencies = [ [[package]] name = "frangipane" -version = "1.0.3" +version = "1.0.4" dependencies = [ "anyhow", "argon2", diff --git a/Cargo.toml b/Cargo.toml index eda061e..f44caf9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "frangipane" -version = "1.0.3" +version = "1.0.4" edition = "2024" [dependencies] diff --git a/src/routes/friends.rs b/src/routes/friends.rs index e579f72..1f2a93a 100644 --- a/src/routes/friends.rs +++ b/src/routes/friends.rs @@ -1,5 +1,6 @@ use axum::{ Extension, Json, Router, + extract::Path, http::{HeaderMap, StatusCode}, routing::{get, post}, }; @@ -36,6 +37,11 @@ pub struct DeclineFriendRequestPayload { pub sender_uuid: Uuid, } +#[derive(serde::Deserialize)] +pub struct RemoveFriendPayload { + pub friend_uuid: Uuid, +} + pub fn routes() -> Router { Router::new() .route("/friends", get(list_friends)) @@ -43,6 +49,8 @@ pub fn routes() -> Router { .route("/friends/request", post(send_request)) .route("/friends/accept", post(accept_request)) .route("/friends/decline", post(decline_request)) + .route("/friends/check/{uuid}", get(is_friend)) + .route("/friends/remove", post(remove_friend)) } async fn list_friends( @@ -259,3 +267,70 @@ async fn decline_request( Ok(StatusCode::CREATED) } + +async fn is_friend( + headers: HeaderMap, + Path(target_uuid): Path, + Extension(db): Extension, +) -> Result, (StatusCode, String)> { + let claims = verify_jwt(headers)?; + + let user_id = user_id_from_uuid(&db, claims.sub).await?; + let target_id = user_id_from_uuid(&db, target_uuid).await?; + + let is_friend = sqlx::query_scalar::<_, bool>( + r#" + SELECT EXISTS ( + SELECT 1 FROM friendship_ + WHERE (user_first = $1 AND user_second = $2) + OR (user_first = $2 AND user_second = $1) + ) + "#, + ) + .bind(user_id) + .bind(target_id) + .fetch_one(&db) + .await + .map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Database error".into()))?; + + Ok(Json(is_friend)) +} + +async fn remove_friend( + headers: HeaderMap, + Extension(db): Extension, + Json(payload): Json, +) -> Result { + let claims = verify_jwt(headers)?; + + let user_id = user_id_from_uuid(&db, claims.sub).await?; + let friend_id = user_id_from_uuid(&db, payload.friend_uuid).await?; + + let rows = sqlx::query( + r#" + DELETE FROM friendship_ + WHERE (user_first = $1 AND user_second = $2) + OR (user_first = $2 AND user_second = $1) + "#, + ) + .bind(user_id) + .bind(friend_id) + .execute(&db) + .await + .map_err(|_| { + ( + StatusCode::INTERNAL_SERVER_ERROR, + "Could not remove friend".into(), + ) + })? + .rows_affected(); + + if rows == 0 { + return Err(( + StatusCode::NOT_FOUND, + "User is not in your friends list".into(), + )); + } + + Ok(StatusCode::OK) +}