added frontend for friendships, and fixed logic around conflicts
This commit is contained in:
@@ -10,7 +10,7 @@ CREATE TABLE IF NOT EXISTS friendship_ (
|
|||||||
user_first INT NOT NULL REFERENCES user_(id) ON DELETE CASCADE,
|
user_first INT NOT NULL REFERENCES user_(id) ON DELETE CASCADE,
|
||||||
user_second INT NOT NULL REFERENCES user_(id) ON DELETE CASCADE,
|
user_second INT NOT NULL REFERENCES user_(id) ON DELETE CASCADE,
|
||||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||||
PRIMARY KEY (user_first, user_second),
|
PRIMARY KEY (user_first, user_second)
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS friend_request_ (
|
CREATE TABLE IF NOT EXISTS friend_request_ (
|
||||||
|
|||||||
@@ -21,3 +21,14 @@ INSERT INTO message_ (sender, room, message_type, content) VALUES
|
|||||||
(2, 2, 'text', 'Anyone seen the new tech updates?'),
|
(2, 2, 'text', 'Anyone seen the new tech updates?'),
|
||||||
(1, 3, 'image', 'Heres a funny meme I found!'),
|
(1, 3, 'image', 'Heres a funny meme I found!'),
|
||||||
(3, 1, 'text', 'I love how active this room is!');
|
(3, 1, 'text', 'I love how active this room is!');
|
||||||
|
|
||||||
|
INSERT INTO friendship_ (user_first, user_second) VALUES
|
||||||
|
(1, 3), -- Alice and Carol
|
||||||
|
(2, 3); -- Bob and Carol
|
||||||
|
|
||||||
|
INSERT INTO friend_request_ (sender, receiver) VALUES
|
||||||
|
(2, 1); -- Bob sent a friend request to Alice
|
||||||
|
|
||||||
|
INSERT INTO ws_token_ (token, room_id, expires_at) VALUES
|
||||||
|
('random_token_1', 1, '2025-12-31T23:59:59Z'),
|
||||||
|
('random_token_2', 2, '2025-12-31T23:59:59Z');
|
||||||
|
|||||||
@@ -35,6 +35,14 @@ pub async fn username_from_uuid(
|
|||||||
.map_err(|_| (StatusCode::UNAUTHORIZED, String::from("Wrong token")))
|
.map_err(|_| (StatusCode::UNAUTHORIZED, String::from("Wrong token")))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn username_from_id(db: &PgPool, user_id: i32) -> Result<String, (StatusCode, String)> {
|
||||||
|
sqlx::query_scalar("SELECT username FROM user_ WHERE id = $1")
|
||||||
|
.bind(user_id)
|
||||||
|
.fetch_one(db)
|
||||||
|
.await
|
||||||
|
.map_err(|_| (StatusCode::UNAUTHORIZED, String::from("Wrong token")))
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn id_from_username(db: &PgPool, username: String) -> Result<i32, (StatusCode, String)> {
|
pub async fn id_from_username(db: &PgPool, username: String) -> Result<i32, (StatusCode, String)> {
|
||||||
sqlx::query_scalar("SELECT id FROM user_ WHERE username = $1")
|
sqlx::query_scalar("SELECT id FROM user_ WHERE username = $1")
|
||||||
.bind(username)
|
.bind(username)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use axum::{
|
|||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::db::user_id_from_uuid;
|
use crate::db::{user_id_from_uuid, username_from_id, username_from_uuid};
|
||||||
use crate::{auth::verify_jwt, db::id_from_username};
|
use crate::{auth::verify_jwt, db::id_from_username};
|
||||||
|
|
||||||
#[derive(sqlx::FromRow, serde::Serialize)]
|
#[derive(sqlx::FromRow, serde::Serialize)]
|
||||||
@@ -100,7 +100,7 @@ async fn send_request(
|
|||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
Extension(db): Extension<PgPool>,
|
Extension(db): Extension<PgPool>,
|
||||||
Json(payload): Json<SendFriendRequestPayload>,
|
Json(payload): Json<SendFriendRequestPayload>,
|
||||||
) -> Result<StatusCode, (StatusCode, String)> {
|
) -> Result<(StatusCode, Json<FriendRequest>), (StatusCode, String)> {
|
||||||
let claims = verify_jwt(headers)?;
|
let claims = verify_jwt(headers)?;
|
||||||
|
|
||||||
let sender_id = user_id_from_uuid(&db, claims.sub).await?;
|
let sender_id = user_id_from_uuid(&db, claims.sub).await?;
|
||||||
@@ -113,6 +113,28 @@ async fn send_request(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let is_already_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(sender_id)
|
||||||
|
.bind(receiver_id)
|
||||||
|
.fetch_one(&db)
|
||||||
|
.await
|
||||||
|
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Database error".into()))?;
|
||||||
|
|
||||||
|
if is_already_friend {
|
||||||
|
return Err((
|
||||||
|
StatusCode::CONFLICT,
|
||||||
|
"You are already friends with this user".into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
sqlx::query("INSERT INTO friend_request_ (sender, receiver) VALUES ($1, $2)")
|
sqlx::query("INSERT INTO friend_request_ (sender, receiver) VALUES ($1, $2)")
|
||||||
.bind(sender_id)
|
.bind(sender_id)
|
||||||
.bind(receiver_id)
|
.bind(receiver_id)
|
||||||
@@ -120,14 +142,20 @@ async fn send_request(
|
|||||||
.await
|
.await
|
||||||
.map_err(|_| (StatusCode::CONFLICT, "Request already exists".into()))?;
|
.map_err(|_| (StatusCode::CONFLICT, "Request already exists".into()))?;
|
||||||
|
|
||||||
Ok(StatusCode::CREATED)
|
Ok((
|
||||||
|
StatusCode::CREATED,
|
||||||
|
Json(FriendRequest {
|
||||||
|
sender_uuid: claims.sub,
|
||||||
|
sender_username: username_from_id(&db, receiver_id).await?,
|
||||||
|
}),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn accept_request(
|
async fn accept_request(
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
Extension(db): Extension<PgPool>,
|
Extension(db): Extension<PgPool>,
|
||||||
Json(payload): Json<AcceptFriendRequestPayload>,
|
Json(payload): Json<AcceptFriendRequestPayload>,
|
||||||
) -> Result<StatusCode, (StatusCode, String)> {
|
) -> Result<(StatusCode, Json<Friend>), (StatusCode, String)> {
|
||||||
let claims = verify_jwt(headers)?;
|
let claims = verify_jwt(headers)?;
|
||||||
|
|
||||||
let receiver_id = user_id_from_uuid(&db, claims.sub).await?;
|
let receiver_id = user_id_from_uuid(&db, claims.sub).await?;
|
||||||
@@ -144,7 +172,13 @@ async fn accept_request(
|
|||||||
.await
|
.await
|
||||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "DB error".into()))?;
|
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "DB error".into()))?;
|
||||||
|
|
||||||
let rows = sqlx::query("DELETE FROM friend_request_ WHERE sender = $1 AND receiver = $2")
|
let rows = sqlx::query(
|
||||||
|
r#"
|
||||||
|
DELETE FROM friend_request_
|
||||||
|
WHERE sender = $1 AND receiver = $2
|
||||||
|
OR sender = $2 AND receiver = $1
|
||||||
|
"#,
|
||||||
|
)
|
||||||
.bind(sender_id)
|
.bind(sender_id)
|
||||||
.bind(receiver_id)
|
.bind(receiver_id)
|
||||||
.execute(&mut *tx)
|
.execute(&mut *tx)
|
||||||
@@ -170,5 +204,11 @@ async fn accept_request(
|
|||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(StatusCode::CREATED)
|
Ok((
|
||||||
|
StatusCode::CREATED,
|
||||||
|
Json(Friend {
|
||||||
|
uuid: payload.sender_uuid,
|
||||||
|
username: username_from_uuid(&db, payload.sender_uuid).await?,
|
||||||
|
}),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user