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 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 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 }