backend/objects/
me.rs

1use actix_web::web::BytesMut;
2use diesel::{ExpressionMethods, QueryDsl, Queryable, Selectable, SelectableHelper, update};
3use diesel_async::RunQueryDsl;
4use serde::Serialize;
5use tokio::task;
6use url::Url;
7use uuid::Uuid;
8
9use crate::{
10    Conn, Data,
11    error::Error,
12    schema::{guild_members, guilds, users},
13    utils::{EMAIL_REGEX, USERNAME_REGEX, image_check},
14};
15
16use super::{Guild, guild::GuildBuilder, load_or_empty, member::MemberBuilder};
17
18#[derive(Serialize, Queryable, Selectable)]
19#[diesel(table_name = users)]
20#[diesel(check_for_backend(diesel::pg::Pg))]
21pub struct Me {
22    pub uuid: Uuid,
23    pub username: String,
24    pub display_name: Option<String>,
25    avatar: Option<String>,
26    pronouns: Option<String>,
27    about: Option<String>,
28    pub email: String,
29    pub email_verified: bool,
30}
31
32impl Me {
33    pub async fn get(conn: &mut Conn, user_uuid: Uuid) -> Result<Self, Error> {
34        use users::dsl;
35        let me: Me = dsl::users
36            .filter(dsl::uuid.eq(user_uuid))
37            .select(Me::as_select())
38            .get_result(conn)
39            .await?;
40
41        Ok(me)
42    }
43
44    pub async fn fetch_memberships(&self, conn: &mut Conn) -> Result<Vec<Guild>, Error> {
45        use guild_members::dsl;
46        let memberships: Vec<MemberBuilder> = load_or_empty(
47            dsl::guild_members
48                .filter(dsl::user_uuid.eq(self.uuid))
49                .select(MemberBuilder::as_select())
50                .load(conn)
51                .await,
52        )?;
53
54        let mut guilds: Vec<Guild> = vec![];
55
56        for membership in memberships {
57            use guilds::dsl;
58            guilds.push(
59                dsl::guilds
60                    .filter(dsl::uuid.eq(membership.guild_uuid))
61                    .select(GuildBuilder::as_select())
62                    .get_result(conn)
63                    .await?
64                    .build(conn)
65                    .await?,
66            )
67        }
68
69        Ok(guilds)
70    }
71
72    pub async fn set_avatar(
73        &mut self,
74        data: &Data,
75        cdn_url: Url,
76        avatar: BytesMut,
77    ) -> Result<(), Error> {
78        let avatar_clone = avatar.clone();
79        let image_type = task::spawn_blocking(move || image_check(avatar_clone)).await??;
80
81        let mut conn = data.pool.get().await?;
82
83        if let Some(avatar) = &self.avatar {
84            let avatar_url: Url = avatar.parse()?;
85
86            let relative_url = avatar_url.path().trim_start_matches('/');
87
88            data.bunny_cdn.storage.delete(relative_url).await?;
89        }
90
91        let path = format!("avatar/{}/avatar.{}", self.uuid, image_type);
92
93        data.bunny_cdn
94            .storage
95            .upload(path.clone(), avatar.into())
96            .await?;
97
98        let avatar_url = 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(&mut conn)
105            .await?;
106
107        if data.get_cache_key(self.uuid.to_string()).await.is_ok() {
108            data.del_cache_key(self.uuid.to_string()).await?
109        }
110
111        self.avatar = Some(avatar_url.to_string());
112
113        Ok(())
114    }
115
116    pub async fn verify_email(&self, conn: &mut Conn) -> Result<(), Error> {
117        use users::dsl;
118        update(users::table)
119            .filter(dsl::uuid.eq(self.uuid))
120            .set(dsl::email_verified.eq(true))
121            .execute(conn)
122            .await?;
123
124        Ok(())
125    }
126
127    pub async fn set_username(&mut self, data: &Data, new_username: String) -> Result<(), Error> {
128        if !USERNAME_REGEX.is_match(&new_username) {
129            return Err(Error::BadRequest("Invalid username".to_string()));
130        }
131
132        let mut conn = data.pool.get().await?;
133
134        use users::dsl;
135        update(users::table)
136            .filter(dsl::uuid.eq(self.uuid))
137            .set(dsl::username.eq(new_username.as_str()))
138            .execute(&mut conn)
139            .await?;
140
141        if data.get_cache_key(self.uuid.to_string()).await.is_ok() {
142            data.del_cache_key(self.uuid.to_string()).await?
143        }
144
145        self.username = new_username;
146
147        Ok(())
148    }
149
150    pub async fn set_display_name(
151        &mut self,
152        data: &Data,
153        new_display_name: String,
154    ) -> Result<(), Error> {
155        let mut conn = data.pool.get().await?;
156
157        use users::dsl;
158        update(users::table)
159            .filter(dsl::uuid.eq(self.uuid))
160            .set(dsl::display_name.eq(new_display_name.as_str()))
161            .execute(&mut conn)
162            .await?;
163
164        if data.get_cache_key(self.uuid.to_string()).await.is_ok() {
165            data.del_cache_key(self.uuid.to_string()).await?
166        }
167
168        self.display_name = Some(new_display_name);
169
170        Ok(())
171    }
172
173    pub async fn set_email(&mut self, data: &Data, new_email: String) -> Result<(), Error> {
174        if !EMAIL_REGEX.is_match(&new_email) {
175            return Err(Error::BadRequest("Invalid username".to_string()));
176        }
177
178        let mut conn = data.pool.get().await?;
179
180        use users::dsl;
181        update(users::table)
182            .filter(dsl::uuid.eq(self.uuid))
183            .set((
184                dsl::email.eq(new_email.as_str()),
185                dsl::email_verified.eq(false),
186            ))
187            .execute(&mut conn)
188            .await?;
189
190        if data.get_cache_key(self.uuid.to_string()).await.is_ok() {
191            data.del_cache_key(self.uuid.to_string()).await?
192        }
193
194        self.email = new_email;
195
196        Ok(())
197    }
198
199    pub async fn set_pronouns(&mut self, data: &Data, new_pronouns: String) -> Result<(), Error> {
200        let mut conn = data.pool.get().await?;
201
202        use users::dsl;
203        update(users::table)
204            .filter(dsl::uuid.eq(self.uuid))
205            .set((dsl::pronouns.eq(new_pronouns.as_str()),))
206            .execute(&mut conn)
207            .await?;
208
209        if data.get_cache_key(self.uuid.to_string()).await.is_ok() {
210            data.del_cache_key(self.uuid.to_string()).await?
211        }
212
213        Ok(())
214    }
215
216    pub async fn set_about(&mut self, data: &Data, new_about: String) -> Result<(), Error> {
217        let mut conn = data.pool.get().await?;
218
219        use users::dsl;
220        update(users::table)
221            .filter(dsl::uuid.eq(self.uuid))
222            .set((dsl::about.eq(new_about.as_str()),))
223            .execute(&mut conn)
224            .await?;
225
226        if data.get_cache_key(self.uuid.to_string()).await.is_ok() {
227            data.del_cache_key(self.uuid.to_string()).await?
228        }
229
230        Ok(())
231    }
232}