backend/objects/
me.rs

1use axum::body::Bytes;
2use diesel::{
3    ExpressionMethods, QueryDsl, Queryable, Selectable, SelectableHelper, delete, insert_into,
4    update,
5};
6use diesel_async::RunQueryDsl;
7use serde::Serialize;
8use tokio::task;
9use url::Url;
10use uuid::Uuid;
11
12use crate::{
13    AppState, Conn,
14    error::Error,
15    objects::{Friend, FriendRequest, User},
16    schema::{friend_requests, friends, guild_members, guilds, users},
17    utils::{CacheFns, EMAIL_REGEX, USERNAME_REGEX, image_check},
18};
19
20use super::{Guild, guild::GuildBuilder, load_or_empty, member::MemberBuilder};
21
22#[derive(Serialize, Queryable, Selectable)]
23#[diesel(table_name = users)]
24#[diesel(check_for_backend(diesel::pg::Pg))]
25pub struct Me {
26    pub uuid: Uuid,
27    pub username: String,
28    pub display_name: Option<String>,
29    avatar: Option<String>,
30    pronouns: Option<String>,
31    about: Option<String>,
32    online_status: i16,
33    pub email: String,
34    pub email_verified: bool,
35}
36
37impl Me {
38    pub async fn get(conn: &mut Conn, user_uuid: Uuid) -> Result<Self, Error> {
39        use users::dsl;
40        let me: Me = dsl::users
41            .filter(dsl::uuid.eq(user_uuid))
42            .select(Me::as_select())
43            .get_result(conn)
44            .await?;
45
46        Ok(me)
47    }
48
49    pub async fn fetch_memberships(&self, conn: &mut Conn) -> Result<Vec<Guild>, Error> {
50        use guild_members::dsl;
51        let memberships: Vec<MemberBuilder> = load_or_empty(
52            dsl::guild_members
53                .filter(dsl::user_uuid.eq(self.uuid))
54                .select(MemberBuilder::as_select())
55                .load(conn)
56                .await,
57        )?;
58
59        let mut guilds: Vec<Guild> = vec![];
60
61        for membership in memberships {
62            use guilds::dsl;
63            guilds.push(
64                dsl::guilds
65                    .filter(dsl::uuid.eq(membership.guild_uuid))
66                    .select(GuildBuilder::as_select())
67                    .get_result(conn)
68                    .await?
69                    .build(conn)
70                    .await?,
71            )
72        }
73
74        Ok(guilds)
75    }
76
77    pub async fn set_avatar(
78        &mut self,
79        conn: &mut Conn,
80        app_state: &AppState,
81        avatar: Bytes,
82    ) -> Result<(), Error> {
83        let avatar_clone = avatar.clone();
84        let image_type = task::spawn_blocking(move || image_check(avatar_clone)).await??;
85
86        if let Some(avatar) = &self.avatar {
87            let avatar_url: Url = avatar.parse()?;
88
89            let relative_url = avatar_url.path().trim_start_matches('/');
90
91            app_state.bunny_storage.delete(relative_url).await?;
92        }
93
94        let path = format!("avatar/{}/{}.{}", self.uuid, Uuid::now_v7(), image_type);
95
96        app_state.bunny_storage.upload(path.clone(), avatar).await?;
97
98        let avatar_url = app_state.config.bunny.cdn_url.join(&path)?;
99
100        use users::dsl;
101        update(users::table)
102            .filter(dsl::uuid.eq(self.uuid))
103            .set(dsl::avatar.eq(avatar_url.as_str()))
104            .execute(conn)
105            .await?;
106
107        if app_state
108            .cache_pool
109            .get_cache_key::<User>(self.uuid.to_string())
110            .await
111            .is_ok()
112        {
113            app_state
114                .cache_pool
115                .del_cache_key(self.uuid.to_string())
116                .await?
117        }
118
119        self.avatar = Some(avatar_url.to_string());
120
121        Ok(())
122    }
123
124    pub async fn verify_email(&self, conn: &mut Conn) -> Result<(), Error> {
125        use users::dsl;
126        update(users::table)
127            .filter(dsl::uuid.eq(self.uuid))
128            .set(dsl::email_verified.eq(true))
129            .execute(conn)
130            .await?;
131
132        Ok(())
133    }
134
135    pub async fn set_username(
136        &mut self,
137        conn: &mut Conn,
138        cache_pool: &redis::Client,
139        new_username: String,
140    ) -> Result<(), Error> {
141        if !USERNAME_REGEX.is_match(&new_username)
142            || new_username.len() < 3
143            || new_username.len() > 32
144        {
145            return Err(Error::BadRequest("Invalid username".to_string()));
146        }
147
148        use users::dsl;
149        update(users::table)
150            .filter(dsl::uuid.eq(self.uuid))
151            .set(dsl::username.eq(new_username.as_str()))
152            .execute(conn)
153            .await?;
154
155        if cache_pool
156            .get_cache_key::<User>(self.uuid.to_string())
157            .await
158            .is_ok()
159        {
160            cache_pool.del_cache_key(self.uuid.to_string()).await?
161        }
162
163        self.username = new_username;
164
165        Ok(())
166    }
167
168    pub async fn set_display_name(
169        &mut self,
170        conn: &mut Conn,
171        cache_pool: &redis::Client,
172        new_display_name: String,
173    ) -> Result<(), Error> {
174        let new_display_name_option = if new_display_name.is_empty() {
175            None
176        } else {
177            Some(new_display_name)
178        };
179
180        use users::dsl;
181        update(users::table)
182            .filter(dsl::uuid.eq(self.uuid))
183            .set(dsl::display_name.eq(&new_display_name_option))
184            .execute(conn)
185            .await?;
186
187        if cache_pool
188            .get_cache_key::<User>(self.uuid.to_string())
189            .await
190            .is_ok()
191        {
192            cache_pool.del_cache_key(self.uuid.to_string()).await?
193        }
194
195        self.display_name = new_display_name_option;
196
197        Ok(())
198    }
199
200    pub async fn set_email(
201        &mut self,
202        conn: &mut Conn,
203        cache_pool: &redis::Client,
204        new_email: String,
205    ) -> Result<(), Error> {
206        if !EMAIL_REGEX.is_match(&new_email) {
207            return Err(Error::BadRequest("Invalid username".to_string()));
208        }
209
210        use users::dsl;
211        update(users::table)
212            .filter(dsl::uuid.eq(self.uuid))
213            .set((
214                dsl::email.eq(new_email.as_str()),
215                dsl::email_verified.eq(false),
216            ))
217            .execute(conn)
218            .await?;
219
220        if cache_pool
221            .get_cache_key::<User>(self.uuid.to_string())
222            .await
223            .is_ok()
224        {
225            cache_pool.del_cache_key(self.uuid.to_string()).await?
226        }
227
228        self.email = new_email;
229
230        Ok(())
231    }
232
233    pub async fn set_pronouns(
234        &mut self,
235        conn: &mut Conn,
236        cache_pool: &redis::Client,
237        new_pronouns: String,
238    ) -> Result<(), Error> {
239        use users::dsl;
240        update(users::table)
241            .filter(dsl::uuid.eq(self.uuid))
242            .set((dsl::pronouns.eq(new_pronouns.as_str()),))
243            .execute(conn)
244            .await?;
245
246        if cache_pool
247            .get_cache_key::<User>(self.uuid.to_string())
248            .await
249            .is_ok()
250        {
251            cache_pool.del_cache_key(self.uuid.to_string()).await?
252        }
253
254        Ok(())
255    }
256
257    pub async fn set_about(
258        &mut self,
259        conn: &mut Conn,
260        cache_pool: &redis::Client,
261        new_about: String,
262    ) -> Result<(), Error> {
263        use users::dsl;
264        update(users::table)
265            .filter(dsl::uuid.eq(self.uuid))
266            .set((dsl::about.eq(new_about.as_str()),))
267            .execute(conn)
268            .await?;
269
270        if cache_pool
271            .get_cache_key::<User>(self.uuid.to_string())
272            .await
273            .is_ok()
274        {
275            cache_pool.del_cache_key(self.uuid.to_string()).await?
276        }
277
278        Ok(())
279    }
280
281    pub async fn set_online_status(
282        &mut self,
283        conn: &mut Conn,
284        cache_pool: &redis::Client,
285        new_status: i16,
286    ) -> Result<(), Error> {
287        if !(0..=4).contains(&new_status) {
288            return Err(Error::BadRequest("Invalid status code".to_string()));
289        }
290        self.online_status = new_status;
291
292        use users::dsl;
293        update(users::table)
294            .filter(dsl::uuid.eq(self.uuid))
295            .set(dsl::online_status.eq(new_status))
296            .execute(conn)
297            .await?;
298
299        if cache_pool
300            .get_cache_key::<User>(self.uuid.to_string())
301            .await
302            .is_ok()
303        {
304            cache_pool.del_cache_key(self.uuid.to_string()).await?
305        }
306
307        Ok(())
308    }
309
310    pub async fn friends_with(
311        &self,
312        conn: &mut Conn,
313        user_uuid: Uuid,
314    ) -> Result<Option<Friend>, Error> {
315        use friends::dsl;
316
317        let friends: Vec<Friend> = if self.uuid < user_uuid {
318            load_or_empty(
319                dsl::friends
320                    .filter(dsl::uuid1.eq(self.uuid))
321                    .filter(dsl::uuid2.eq(user_uuid))
322                    .load(conn)
323                    .await,
324            )?
325        } else {
326            load_or_empty(
327                dsl::friends
328                    .filter(dsl::uuid1.eq(user_uuid))
329                    .filter(dsl::uuid2.eq(self.uuid))
330                    .load(conn)
331                    .await,
332            )?
333        };
334
335        if friends.is_empty() {
336            return Ok(None);
337        }
338
339        Ok(Some(friends[0].clone()))
340    }
341
342    pub async fn add_friend(&self, conn: &mut Conn, user_uuid: Uuid) -> Result<(), Error> {
343        if self.friends_with(conn, user_uuid).await?.is_some() {
344            // TODO: Check if another error should be used
345            return Err(Error::BadRequest("Already friends with user".to_string()));
346        }
347
348        use friend_requests::dsl;
349
350        let friend_request: Vec<FriendRequest> = load_or_empty(
351            dsl::friend_requests
352                .filter(dsl::sender.eq(user_uuid))
353                .filter(dsl::receiver.eq(self.uuid))
354                .load(conn)
355                .await,
356        )?;
357
358        #[allow(clippy::get_first)]
359        if let Some(friend_request) = friend_request.get(0) {
360            use friends::dsl;
361
362            if self.uuid < user_uuid {
363                insert_into(friends::table)
364                    .values((dsl::uuid1.eq(self.uuid), dsl::uuid2.eq(user_uuid)))
365                    .execute(conn)
366                    .await?;
367            } else {
368                insert_into(friends::table)
369                    .values((dsl::uuid1.eq(user_uuid), dsl::uuid2.eq(self.uuid)))
370                    .execute(conn)
371                    .await?;
372            }
373
374            use friend_requests::dsl as frdsl;
375
376            delete(friend_requests::table)
377                .filter(frdsl::sender.eq(friend_request.sender))
378                .filter(frdsl::receiver.eq(friend_request.receiver))
379                .execute(conn)
380                .await?;
381
382            Ok(())
383        } else {
384            use friend_requests::dsl;
385
386            insert_into(friend_requests::table)
387                .values((dsl::sender.eq(self.uuid), dsl::receiver.eq(user_uuid)))
388                .execute(conn)
389                .await?;
390
391            Ok(())
392        }
393    }
394
395    pub async fn remove_friend(&self, conn: &mut Conn, user_uuid: Uuid) -> Result<(), Error> {
396        if self.friends_with(conn, user_uuid).await?.is_none() {
397            // TODO: Check if another error should be used
398            return Err(Error::BadRequest("Not friends with user".to_string()));
399        }
400
401        use friends::dsl;
402
403        if self.uuid < user_uuid {
404            delete(friends::table)
405                .filter(dsl::uuid1.eq(self.uuid))
406                .filter(dsl::uuid2.eq(user_uuid))
407                .execute(conn)
408                .await?;
409        } else {
410            delete(friends::table)
411                .filter(dsl::uuid1.eq(user_uuid))
412                .filter(dsl::uuid2.eq(self.uuid))
413                .execute(conn)
414                .await?;
415        }
416
417        Ok(())
418    }
419
420    pub async fn get_friends(
421        &self,
422        conn: &mut Conn,
423        cache_pool: &redis::Client,
424    ) -> Result<Vec<User>, Error> {
425        use friends::dsl;
426
427        let friends1 = load_or_empty(
428            dsl::friends
429                .filter(dsl::uuid1.eq(self.uuid))
430                .select(Friend::as_select())
431                .load(conn)
432                .await,
433        )?;
434
435        let friends2 = load_or_empty(
436            dsl::friends
437                .filter(dsl::uuid2.eq(self.uuid))
438                .select(Friend::as_select())
439                .load(conn)
440                .await,
441        )?;
442
443        let mut friends = vec![];
444
445        for friend in friends1 {
446            friends
447                .push(User::fetch_one_with_friendship(conn, cache_pool, self, friend.uuid2).await?);
448        }
449
450        for friend in friends2 {
451            friends
452                .push(User::fetch_one_with_friendship(conn, cache_pool, self, friend.uuid1).await?);
453        }
454
455        Ok(friends)
456    }
457
458    /* TODO
459    pub async fn get_friend_requests(&self, conn: &mut Conn) -> Result<Vec<FriendRequest>, Error> {
460        use friend_requests::dsl;
461
462        let friend_request: Vec<FriendRequest> = load_or_empty(
463            dsl::friend_requests
464                .filter(dsl::receiver.eq(self.uuid))
465                .load(conn)
466                .await
467        )?;
468
469        Ok()
470    }
471
472    pub async fn delete_friend_request(&self, conn: &mut Conn, user_uuid: Uuid) -> Result<Vec<FriendRequest>, Error> {
473        use friend_requests::dsl;
474
475        let friend_request: Vec<FriendRequest> = load_or_empty(
476            dsl::friend_requests
477                .filter(dsl::sender.eq(user_uuid))
478                .filter(dsl::receiver.eq(self.uuid))
479                .load(conn)
480                .await
481        )?;
482
483        Ok()
484    }
485    */
486}