feat: add friends!
This commit is contained in:
parent
ac3e7e242b
commit
e8b8b49643
13 changed files with 439 additions and 39 deletions
4
migrations/2025-07-07-131320_add_friends/down.sql
Normal file
4
migrations/2025-07-07-131320_add_friends/down.sql
Normal file
|
@ -0,0 +1,4 @@
|
|||
-- This file should undo anything in `up.sql`
|
||||
DROP TABLE friend_requests;
|
||||
DROP FUNCTION check_friend_request;
|
||||
DROP TABLE friends;
|
35
migrations/2025-07-07-131320_add_friends/up.sql
Normal file
35
migrations/2025-07-07-131320_add_friends/up.sql
Normal file
|
@ -0,0 +1,35 @@
|
|||
-- Your SQL goes here
|
||||
CREATE TABLE friends (
|
||||
uuid1 UUID REFERENCES users(uuid),
|
||||
uuid2 UUID REFERENCES users(uuid),
|
||||
accepted_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (uuid1, uuid2),
|
||||
CHECK (uuid1 < uuid2)
|
||||
);
|
||||
|
||||
CREATE TABLE friend_requests (
|
||||
sender UUID REFERENCES users(uuid),
|
||||
receiver UUID REFERENCES users(uuid),
|
||||
requested_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (sender, receiver),
|
||||
CHECK (sender <> receiver)
|
||||
);
|
||||
|
||||
-- Create a function to check for existing friendships
|
||||
CREATE FUNCTION check_friend_request()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM friends
|
||||
WHERE (uuid1, uuid2) = (LEAST(NEW.sender, NEW.receiver), GREATEST(NEW.sender, NEW.receiver))
|
||||
) THEN
|
||||
RAISE EXCEPTION 'Users are already friends';
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- Create the trigger
|
||||
CREATE TRIGGER prevent_friend_request_conflict
|
||||
BEFORE INSERT OR UPDATE ON friend_requests
|
||||
FOR EACH ROW EXECUTE FUNCTION check_friend_request();
|
|
@ -1,9 +1,5 @@
|
|||
use crate::{
|
||||
Data,
|
||||
api::v1::auth::check_access_token,
|
||||
error::Error,
|
||||
objects::Member,
|
||||
utils::{get_auth_header, global_checks},
|
||||
api::v1::auth::check_access_token, error::Error, objects::{Me, Member}, utils::{get_auth_header, global_checks}, Data
|
||||
};
|
||||
use ::uuid::Uuid;
|
||||
use actix_web::{HttpRequest, HttpResponse, get, web};
|
||||
|
@ -28,7 +24,9 @@ pub async fn get(
|
|||
|
||||
Member::check_membership(&mut conn, uuid, guild_uuid).await?;
|
||||
|
||||
let members = Member::fetch_all(&data, guild_uuid).await?;
|
||||
let me = Me::get(&mut conn, uuid).await?;
|
||||
|
||||
let members = Member::fetch_all(&data, &me, guild_uuid).await?;
|
||||
|
||||
Ok(HttpResponse::Ok().json(members))
|
||||
}
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
use actix_web::{HttpRequest, HttpResponse, get, post, web};
|
||||
use serde::Deserialize;
|
||||
use ::uuid::Uuid;
|
||||
|
||||
pub mod uuid;
|
||||
|
||||
use crate::{
|
||||
Data,
|
||||
api::v1::auth::check_access_token,
|
||||
error::Error,
|
||||
objects::Me,
|
||||
utils::{get_auth_header, global_checks},
|
||||
};
|
||||
|
||||
/// Returns a list of users that are your friends
|
||||
#[get("/friends")]
|
||||
pub async fn get(req: HttpRequest, data: web::Data<Data>) -> Result<HttpResponse, Error> {
|
||||
let headers = req.headers();
|
||||
|
||||
let auth_header = get_auth_header(headers)?;
|
||||
|
||||
let mut conn = data.pool.get().await?;
|
||||
|
||||
let uuid = check_access_token(auth_header, &mut conn).await?;
|
||||
|
||||
global_checks(&data, uuid).await?;
|
||||
|
||||
let me = Me::get(&mut conn, uuid).await?;
|
||||
|
||||
let friends = me.get_friends(&data).await?;
|
||||
|
||||
Ok(HttpResponse::Ok().json(friends))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct UserReq {
|
||||
uuid: Uuid,
|
||||
}
|
||||
|
||||
/// `POST /api/v1/me/friends` Send friend request
|
||||
///
|
||||
/// requires auth? yes
|
||||
///
|
||||
/// ### Request Example:
|
||||
/// ```
|
||||
/// json!({
|
||||
/// "uuid": "155d2291-fb23-46bd-a656-ae7c5d8218e6",
|
||||
/// });
|
||||
/// ```
|
||||
/// NOTE: UUIDs in this response are made using `uuidgen`, UUIDs made by the actual backend will be UUIDv7 and have extractable timestamps
|
||||
///
|
||||
/// ### Responses
|
||||
/// 200 Success
|
||||
///
|
||||
/// 404 Not Found
|
||||
///
|
||||
/// 400 Bad Request (usually means users are already friends)
|
||||
///
|
||||
#[post("/friends")]
|
||||
pub async fn post(req: HttpRequest, json: web::Json<UserReq>, data: web::Data<Data>) -> Result<HttpResponse, Error> {
|
||||
let headers = req.headers();
|
||||
|
||||
let auth_header = get_auth_header(headers)?;
|
||||
|
||||
let mut conn = data.pool.get().await?;
|
||||
|
||||
let uuid = check_access_token(auth_header, &mut conn).await?;
|
||||
|
||||
global_checks(&data, uuid).await?;
|
||||
|
||||
let me = Me::get(&mut conn, uuid).await?;
|
||||
|
||||
me.add_friend(&mut conn, json.uuid).await?;
|
||||
|
||||
Ok(HttpResponse::Ok().finish())
|
||||
}
|
29
src/api/v1/me/friends/uuid.rs
Normal file
29
src/api/v1/me/friends/uuid.rs
Normal file
|
@ -0,0 +1,29 @@
|
|||
use actix_web::{HttpRequest, HttpResponse, delete, web};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
Data,
|
||||
api::v1::auth::check_access_token,
|
||||
error::Error,
|
||||
objects::Me,
|
||||
utils::{get_auth_header, global_checks},
|
||||
};
|
||||
|
||||
#[delete("/friends/{uuid}")]
|
||||
pub async fn delete(req: HttpRequest, path: web::Path<(Uuid,)>, data: web::Data<Data>) -> Result<HttpResponse, Error> {
|
||||
let headers = req.headers();
|
||||
|
||||
let auth_header = get_auth_header(headers)?;
|
||||
|
||||
let mut conn = data.pool.get().await?;
|
||||
|
||||
let uuid = check_access_token(auth_header, &mut conn).await?;
|
||||
|
||||
global_checks(&data, uuid).await?;
|
||||
|
||||
let me = Me::get(&mut conn, uuid).await?;
|
||||
|
||||
me.remove_friend(&mut conn, path.0).await?;
|
||||
|
||||
Ok(HttpResponse::Ok().finish())
|
||||
}
|
|
@ -11,12 +11,16 @@ use crate::{
|
|||
};
|
||||
|
||||
mod guilds;
|
||||
mod friends;
|
||||
|
||||
pub fn web() -> Scope {
|
||||
web::scope("/me")
|
||||
.service(get)
|
||||
.service(update)
|
||||
.service(guilds::get)
|
||||
.service(friends::get)
|
||||
.service(friends::post)
|
||||
.service(friends::uuid::delete)
|
||||
}
|
||||
|
||||
#[get("")]
|
||||
|
|
|
@ -4,11 +4,7 @@ use actix_web::{HttpRequest, HttpResponse, get, web};
|
|||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
Data,
|
||||
api::v1::auth::check_access_token,
|
||||
error::Error,
|
||||
objects::User,
|
||||
utils::{get_auth_header, global_checks},
|
||||
api::v1::auth::check_access_token, error::Error, objects::{Me, User}, utils::{get_auth_header, global_checks}, Data
|
||||
};
|
||||
|
||||
/// `GET /api/v1/users/{uuid}` Returns user with the given UUID
|
||||
|
@ -45,7 +41,9 @@ pub async fn get(
|
|||
|
||||
global_checks(&data, uuid).await?;
|
||||
|
||||
let user = User::fetch_one(&data, user_uuid).await?;
|
||||
let me = Me::get(&mut conn, uuid).await?;
|
||||
|
||||
let user = User::fetch_one_with_friendship(&data, &me, user_uuid).await?;
|
||||
|
||||
Ok(HttpResponse::Ok().json(user))
|
||||
}
|
||||
|
|
24
src/objects/friends.rs
Normal file
24
src/objects/friends.rs
Normal file
|
@ -0,0 +1,24 @@
|
|||
use chrono::{DateTime, Utc};
|
||||
use diesel::{Queryable, Selectable};
|
||||
use serde::Serialize;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::schema::{friend_requests, friends};
|
||||
|
||||
#[derive(Serialize, Queryable, Selectable, Clone)]
|
||||
#[diesel(table_name = friends)]
|
||||
#[diesel(check_for_backend(diesel::pg::Pg))]
|
||||
pub struct Friend {
|
||||
pub uuid1: Uuid,
|
||||
pub uuid2: Uuid,
|
||||
pub accepted_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Queryable, Selectable, Clone)]
|
||||
#[diesel(table_name = friend_requests)]
|
||||
#[diesel(check_for_backend(diesel::pg::Pg))]
|
||||
pub struct FriendRequest {
|
||||
pub sender: Uuid,
|
||||
pub receiver: Uuid,
|
||||
pub requested_at: DateTime<Utc>,
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
use actix_web::web::BytesMut;
|
||||
use diesel::{ExpressionMethods, QueryDsl, Queryable, Selectable, SelectableHelper, update};
|
||||
use diesel::{delete, insert_into, update, ExpressionMethods, QueryDsl, Queryable, Selectable, SelectableHelper};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use serde::Serialize;
|
||||
use tokio::task;
|
||||
|
@ -7,10 +7,7 @@ use url::Url;
|
|||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
Conn, Data,
|
||||
error::Error,
|
||||
schema::{guild_members, guilds, users},
|
||||
utils::{EMAIL_REGEX, USERNAME_REGEX, image_check},
|
||||
error::Error, objects::{FriendRequest, Friend, User}, schema::{friend_requests, friends, guild_members, guilds, users}, utils::{image_check, EMAIL_REGEX, USERNAME_REGEX}, Conn, Data
|
||||
};
|
||||
|
||||
use super::{Guild, guild::GuildBuilder, load_or_empty, member::MemberBuilder};
|
||||
|
@ -153,13 +150,11 @@ impl Me {
|
|||
) -> Result<(), Error> {
|
||||
let mut conn = data.pool.get().await?;
|
||||
|
||||
let new_display_name_option;
|
||||
|
||||
if new_display_name.is_empty() {
|
||||
new_display_name_option = None;
|
||||
let new_display_name_option = if new_display_name.is_empty() {
|
||||
None
|
||||
} else {
|
||||
new_display_name_option = Some(new_display_name)
|
||||
}
|
||||
Some(new_display_name)
|
||||
};
|
||||
|
||||
use users::dsl;
|
||||
update(users::table)
|
||||
|
@ -236,4 +231,175 @@ impl Me {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn friends_with(&self, conn: &mut Conn, user_uuid: Uuid) -> Result<Option<Friend>, Error> {
|
||||
use friends::dsl;
|
||||
|
||||
let friends: Vec<Friend> = if self.uuid < user_uuid {
|
||||
load_or_empty(
|
||||
dsl::friends
|
||||
.filter(dsl::uuid1.eq(self.uuid))
|
||||
.filter(dsl::uuid2.eq(user_uuid))
|
||||
.load(conn)
|
||||
.await
|
||||
)?
|
||||
} else {
|
||||
load_or_empty(
|
||||
dsl::friends
|
||||
.filter(dsl::uuid1.eq(user_uuid))
|
||||
.filter(dsl::uuid2.eq(self.uuid))
|
||||
.load(conn)
|
||||
.await
|
||||
)?
|
||||
};
|
||||
|
||||
if friends.is_empty() {
|
||||
return Ok(None)
|
||||
}
|
||||
|
||||
Ok(Some(friends[0].clone()))
|
||||
}
|
||||
|
||||
pub async fn add_friend(&self, conn: &mut Conn, user_uuid: Uuid) -> Result<(), Error> {
|
||||
if self.friends_with(conn, user_uuid).await?.is_some() {
|
||||
// TODO: Check if another error should be used
|
||||
return Err(Error::BadRequest("Already friends with user".to_string()))
|
||||
}
|
||||
|
||||
use friend_requests::dsl;
|
||||
|
||||
let friend_request: Vec<FriendRequest> = load_or_empty(
|
||||
dsl::friend_requests
|
||||
.filter(dsl::sender.eq(user_uuid))
|
||||
.filter(dsl::receiver.eq(self.uuid))
|
||||
.load(conn)
|
||||
.await
|
||||
)?;
|
||||
|
||||
#[allow(clippy::get_first)]
|
||||
if let Some(friend_request) = friend_request.get(0) {
|
||||
use friends::dsl;
|
||||
|
||||
if self.uuid < user_uuid {
|
||||
insert_into(friends::table)
|
||||
.values((dsl::uuid1.eq(self.uuid), dsl::uuid2.eq(user_uuid)))
|
||||
.execute(conn)
|
||||
.await?;
|
||||
} else {
|
||||
insert_into(friends::table)
|
||||
.values((dsl::uuid1.eq(user_uuid), dsl::uuid2.eq(self.uuid)))
|
||||
.execute(conn)
|
||||
.await?;
|
||||
}
|
||||
|
||||
use friend_requests::dsl as frdsl;
|
||||
|
||||
delete(friend_requests::table)
|
||||
.filter(frdsl::sender.eq(friend_request.sender))
|
||||
.filter(frdsl::receiver.eq(friend_request.receiver))
|
||||
.execute(conn)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
use friend_requests::dsl;
|
||||
|
||||
insert_into(friend_requests::table)
|
||||
.values((dsl::sender.eq(self.uuid), dsl::receiver.eq(user_uuid)))
|
||||
.execute(conn)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn remove_friend(&self, conn: &mut Conn, user_uuid: Uuid) -> Result<(), Error> {
|
||||
if self.friends_with(conn, user_uuid).await?.is_none() {
|
||||
// TODO: Check if another error should be used
|
||||
return Err(Error::BadRequest("Not friends with user".to_string()))
|
||||
}
|
||||
|
||||
use friends::dsl;
|
||||
|
||||
if self.uuid < user_uuid {
|
||||
delete(friends::table)
|
||||
.filter(dsl::uuid1.eq(self.uuid))
|
||||
.filter(dsl::uuid2.eq(user_uuid))
|
||||
.execute(conn)
|
||||
.await?;
|
||||
} else {
|
||||
delete(friends::table)
|
||||
.filter(dsl::uuid1.eq(user_uuid))
|
||||
.filter(dsl::uuid2.eq(self.uuid))
|
||||
.execute(conn)
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_friends(&self, data: &Data) -> Result<Vec<User>, Error> {
|
||||
use friends::dsl;
|
||||
|
||||
let mut conn = data.pool.get().await?;
|
||||
|
||||
let friends1 = load_or_empty(
|
||||
dsl::friends
|
||||
.filter(dsl::uuid1.eq(self.uuid))
|
||||
.select(Friend::as_select())
|
||||
.load(&mut conn)
|
||||
.await
|
||||
)?;
|
||||
|
||||
let friends2 = load_or_empty(
|
||||
dsl::friends
|
||||
.filter(dsl::uuid2.eq(self.uuid))
|
||||
.select(Friend::as_select())
|
||||
.load(&mut conn)
|
||||
.await
|
||||
)?;
|
||||
|
||||
let friend_futures = friends1.iter().map(async move |friend| {
|
||||
User::fetch_one_with_friendship(data, self, friend.uuid2).await
|
||||
});
|
||||
|
||||
let mut friends = futures::future::try_join_all(friend_futures).await?;
|
||||
|
||||
let friend_futures = friends2.iter().map(async move |friend| {
|
||||
User::fetch_one_with_friendship(data, self, friend.uuid1).await
|
||||
});
|
||||
|
||||
friends.append(&mut futures::future::try_join_all(friend_futures).await?);
|
||||
|
||||
Ok(friends)
|
||||
}
|
||||
|
||||
/* TODO
|
||||
pub async fn get_friend_requests(&self, conn: &mut Conn) -> Result<Vec<FriendRequest>, Error> {
|
||||
use friend_requests::dsl;
|
||||
|
||||
let friend_request: Vec<FriendRequest> = load_or_empty(
|
||||
dsl::friend_requests
|
||||
.filter(dsl::receiver.eq(self.uuid))
|
||||
.load(conn)
|
||||
.await
|
||||
)?;
|
||||
|
||||
Ok()
|
||||
}
|
||||
|
||||
pub async fn delete_friend_request(&self, conn: &mut Conn, user_uuid: Uuid) -> Result<Vec<FriendRequest>, Error> {
|
||||
use friend_requests::dsl;
|
||||
|
||||
let friend_request: Vec<FriendRequest> = load_or_empty(
|
||||
dsl::friend_requests
|
||||
.filter(dsl::sender.eq(user_uuid))
|
||||
.filter(dsl::receiver.eq(self.uuid))
|
||||
.load(conn)
|
||||
.await
|
||||
)?;
|
||||
|
||||
Ok()
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
|
|
@ -6,10 +6,7 @@ use serde::{Deserialize, Serialize};
|
|||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
Conn, Data,
|
||||
error::Error,
|
||||
objects::{Permissions, Role},
|
||||
schema::guild_members,
|
||||
error::Error, objects::{Me, Permissions, Role}, schema::guild_members, Conn, Data
|
||||
};
|
||||
|
||||
use super::{User, load_or_empty};
|
||||
|
@ -26,8 +23,14 @@ pub struct MemberBuilder {
|
|||
}
|
||||
|
||||
impl MemberBuilder {
|
||||
pub async fn build(&self, data: &Data) -> Result<Member, Error> {
|
||||
let user = User::fetch_one(data, self.user_uuid).await?;
|
||||
pub async fn build(&self, data: &Data, me: Option<&Me>) -> Result<Member, Error> {
|
||||
let user;
|
||||
|
||||
if let Some(me) = me {
|
||||
user = User::fetch_one_with_friendship(data, me, self.user_uuid).await?;
|
||||
} else {
|
||||
user = User::fetch_one(data, self.user_uuid).await?;
|
||||
}
|
||||
|
||||
Ok(Member {
|
||||
uuid: self.uuid,
|
||||
|
@ -94,7 +97,7 @@ impl Member {
|
|||
Ok(member_builder)
|
||||
}
|
||||
|
||||
pub async fn fetch_one(data: &Data, user_uuid: Uuid, guild_uuid: Uuid) -> Result<Self, Error> {
|
||||
pub async fn fetch_one(data: &Data, me: &Me, user_uuid: Uuid, guild_uuid: Uuid) -> Result<Self, Error> {
|
||||
let mut conn = data.pool.get().await?;
|
||||
|
||||
use guild_members::dsl;
|
||||
|
@ -105,10 +108,10 @@ impl Member {
|
|||
.get_result(&mut conn)
|
||||
.await?;
|
||||
|
||||
member.build(data).await
|
||||
member.build(data, Some(me)).await
|
||||
}
|
||||
|
||||
pub async fn fetch_all(data: &Data, guild_uuid: Uuid) -> Result<Vec<Self>, Error> {
|
||||
pub async fn fetch_all(data: &Data, me: &Me, guild_uuid: Uuid) -> Result<Vec<Self>, Error> {
|
||||
let mut conn = data.pool.get().await?;
|
||||
|
||||
use guild_members::dsl;
|
||||
|
@ -122,7 +125,7 @@ impl Member {
|
|||
|
||||
let member_futures = member_builders
|
||||
.iter()
|
||||
.map(async move |m| m.build(data).await);
|
||||
.map(async move |m| m.build(data, Some(me)).await);
|
||||
|
||||
futures::future::try_join_all(member_futures).await
|
||||
}
|
||||
|
@ -145,6 +148,6 @@ impl Member {
|
|||
.execute(&mut conn)
|
||||
.await?;
|
||||
|
||||
member.build(data).await
|
||||
member.build(data, None).await
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ mod message;
|
|||
mod password_reset_token;
|
||||
mod role;
|
||||
mod user;
|
||||
mod friends;
|
||||
|
||||
pub use channel::Channel;
|
||||
pub use email_token::EmailToken;
|
||||
|
@ -29,6 +30,8 @@ pub use password_reset_token::PasswordResetToken;
|
|||
pub use role::Permissions;
|
||||
pub use role::Role;
|
||||
pub use user::User;
|
||||
pub use friends::Friend;
|
||||
pub use friends::FriendRequest;
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
|
|
|
@ -1,15 +1,40 @@
|
|||
use chrono::{DateTime, Utc};
|
||||
use diesel::{ExpressionMethods, QueryDsl, Queryable, Selectable, SelectableHelper};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{Conn, Data, error::Error, schema::users};
|
||||
use crate::{error::Error, objects::Me, schema::users, Conn, Data};
|
||||
|
||||
use super::load_or_empty;
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone, Queryable, Selectable)]
|
||||
#[diesel(table_name = users)]
|
||||
#[diesel(check_for_backend(diesel::pg::Pg))]
|
||||
pub struct UserBuilder {
|
||||
uuid: Uuid,
|
||||
username: String,
|
||||
display_name: Option<String>,
|
||||
avatar: Option<String>,
|
||||
pronouns: Option<String>,
|
||||
about: Option<String>,
|
||||
}
|
||||
|
||||
impl UserBuilder {
|
||||
fn build(self) -> User {
|
||||
User {
|
||||
uuid: self.uuid,
|
||||
username: self.username,
|
||||
display_name: self.display_name,
|
||||
avatar: self.avatar,
|
||||
pronouns: self.pronouns,
|
||||
about: self.about,
|
||||
friends_since: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone)]
|
||||
pub struct User {
|
||||
uuid: Uuid,
|
||||
username: String,
|
||||
|
@ -17,6 +42,7 @@ pub struct User {
|
|||
avatar: Option<String>,
|
||||
pronouns: Option<String>,
|
||||
about: Option<String>,
|
||||
friends_since: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
impl User {
|
||||
|
@ -28,33 +54,49 @@ impl User {
|
|||
}
|
||||
|
||||
use users::dsl;
|
||||
let user: User = dsl::users
|
||||
let user_builder: UserBuilder = dsl::users
|
||||
.filter(dsl::uuid.eq(user_uuid))
|
||||
.select(User::as_select())
|
||||
.select(UserBuilder::as_select())
|
||||
.get_result(&mut conn)
|
||||
.await?;
|
||||
|
||||
let user = user_builder.build();
|
||||
|
||||
data.set_cache_key(user_uuid.to_string(), user.clone(), 1800)
|
||||
.await?;
|
||||
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
pub async fn fetch_one_with_friendship(data: &Data, me: &Me, user_uuid: Uuid) -> Result<Self, Error> {
|
||||
let mut conn = data.pool.get().await?;
|
||||
|
||||
let mut user = Self::fetch_one(data, user_uuid).await?;
|
||||
|
||||
if let Some(friend) = me.friends_with(&mut conn, user_uuid).await? {
|
||||
user.friends_since = Some(friend.accepted_at);
|
||||
}
|
||||
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
pub async fn fetch_amount(
|
||||
conn: &mut Conn,
|
||||
offset: i64,
|
||||
amount: i64,
|
||||
) -> Result<Vec<Self>, Error> {
|
||||
use users::dsl;
|
||||
let users: Vec<User> = load_or_empty(
|
||||
let user_builders: Vec<UserBuilder> = load_or_empty(
|
||||
dsl::users
|
||||
.limit(amount)
|
||||
.offset(offset)
|
||||
.select(User::as_select())
|
||||
.select(UserBuilder::as_select())
|
||||
.load(conn)
|
||||
.await,
|
||||
)?;
|
||||
|
||||
let users: Vec<User> = user_builders.iter().map(|u| u.clone().build()).collect();
|
||||
|
||||
Ok(users)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,22 @@ diesel::table! {
|
|||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
friend_requests (sender, receiver) {
|
||||
sender -> Uuid,
|
||||
receiver -> Uuid,
|
||||
requested_at -> Timestamptz,
|
||||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
friends (uuid1, uuid2) {
|
||||
uuid1 -> Uuid,
|
||||
uuid2 -> Uuid,
|
||||
accepted_at -> Timestamptz,
|
||||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
guild_members (uuid) {
|
||||
uuid -> Uuid,
|
||||
|
@ -153,6 +169,8 @@ diesel::allow_tables_to_appear_in_same_query!(
|
|||
access_tokens,
|
||||
channel_permissions,
|
||||
channels,
|
||||
friend_requests,
|
||||
friends,
|
||||
guild_members,
|
||||
guilds,
|
||||
instance_permissions,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue