]> Untitled Git - lemmy.git/blob - crates/db_queries/src/source/person.rs
Still continuing on....
[lemmy.git] / crates / db_queries / src / source / person.rs
1 use crate::{is_email_regex, ApubObject, Crud, ToSafeSettings};
2 use bcrypt::{hash, DEFAULT_COST};
3 use diesel::{dsl::*, result::Error, *};
4 use lemmy_db_schema::{
5   naive_now,
6   schema::person::dsl::*,
7   source::person::{PersonForm, Person},
8   Url,
9 };
10 use lemmy_utils::settings::Settings;
11
12 mod safe_type {
13   use crate::ToSafe;
14   use lemmy_db_schema::{schema::person::columns::*, source::person::Person};
15
16   type Columns = (
17   id,
18   name,
19   preferred_username,
20   avatar,
21   banned,
22   published,
23   updated,
24   actor_id,
25   bio,
26   local,
27   last_refreshed_at,
28   banner,
29   deleted,
30   inbox_url,
31   shared_inbox_url,
32   );
33
34   impl ToSafe for Person {
35     type SafeColumns = Columns;
36     fn safe_columns_tuple() -> Self::SafeColumns {
37       (
38         id,
39         name,
40         preferred_username,
41         avatar,
42         banned,
43         published,
44         updated,
45         actor_id,
46         bio,
47         local,
48         last_refreshed_at,
49         banner,
50         deleted,
51         inbox_url,
52         shared_inbox_url,
53       )
54     }
55   }
56 }
57
58 mod safe_type_alias_1 {
59   use crate::ToSafe;
60   use lemmy_db_schema::{schema::person_alias_1::columns::*, source::person::PersonAlias1};
61
62   type Columns = (
63         id,
64         name,
65         preferred_username,
66         avatar,
67         banned,
68         published,
69         updated,
70         actor_id,
71         bio,
72         local,
73         last_refreshed_at,
74         banner,
75         deleted,
76         inbox_url,
77         shared_inbox_url,
78   );
79
80   impl ToSafe for PersonAlias1 {
81     type SafeColumns = Columns;
82     fn safe_columns_tuple() -> Self::SafeColumns {
83       (
84         id,
85         name,
86         preferred_username,
87         avatar,
88         banned,
89         published,
90         updated,
91         actor_id,
92         bio,
93         local,
94         last_refreshed_at,
95         banner,
96         deleted,
97         inbox_url,
98         shared_inbox_url,
99       )
100     }
101   }
102 }
103
104 mod safe_type_alias_2 {
105   use crate::ToSafe;
106   use lemmy_db_schema::{schema::person_alias_2::columns::*, source::person::PersonAlias2};
107
108   type Columns = (
109         id,
110         name,
111         preferred_username,
112         avatar,
113         banned,
114         published,
115         updated,
116         actor_id,
117         bio,
118         local,
119         last_refreshed_at,
120         banner,
121         deleted,
122         inbox_url,
123         shared_inbox_url,
124   );
125
126   impl ToSafe for PersonAlias2 {
127     type SafeColumns = Columns;
128     fn safe_columns_tuple() -> Self::SafeColumns {
129       (
130         id,
131         name,
132         preferred_username,
133         avatar,
134         banned,
135         published,
136         updated,
137         actor_id,
138         bio,
139         local,
140         last_refreshed_at,
141         banner,
142         deleted,
143         inbox_url,
144         shared_inbox_url,
145       )
146     }
147   }
148 }
149
150 impl Crud<PersonForm> for Person {
151   fn read(conn: &PgConnection, person_id: i32) -> Result<Self, Error> {
152     person
153       .filter(deleted.eq(false))
154       .find(person_id)
155       .first::<Self>(conn)
156   }
157   fn delete(conn: &PgConnection, person_id: i32) -> Result<usize, Error> {
158     diesel::delete(person.find(person_id)).execute(conn)
159   }
160   fn create(conn: &PgConnection, form: &PersonForm) -> Result<Self, Error> {
161     insert_into(person).values(form).get_result::<Self>(conn)
162   }
163   fn update(conn: &PgConnection, person_id: i32, form: &PersonForm) -> Result<Self, Error> {
164     diesel::update(person.find(person_id))
165       .set(form)
166       .get_result::<Self>(conn)
167   }
168 }
169
170 impl ApubObject<PersonForm> for Person {
171   fn read_from_apub_id(conn: &PgConnection, object_id: &Url) -> Result<Self, Error> {
172     use lemmy_db_schema::schema::person::dsl::*;
173     person
174       .filter(deleted.eq(false))
175       .filter(actor_id.eq(object_id))
176       .first::<Self>(conn)
177   }
178
179   fn upsert(conn: &PgConnection, person_form: &PersonForm) -> Result<Person, Error> {
180     insert_into(person)
181       .values(person_form)
182       .on_conflict(actor_id)
183       .do_update()
184       .set(person_form)
185       .get_result::<Self>(conn)
186   }
187 }
188
189 pub trait Person_ {
190   fn register(conn: &PgConnection, form: &PersonForm) -> Result<Person, Error>;
191   fn update_password(conn: &PgConnection, person_id: i32, new_password: &str)
192     -> Result<Person, Error>;
193   fn read_from_name(conn: &PgConnection, from_name: &str) -> Result<Person, Error>;
194   fn add_admin(conn: &PgConnection, person_id: i32, added: bool) -> Result<Person, Error>;
195   fn ban_person(conn: &PgConnection, person_id: i32, ban: bool) -> Result<Person, Error>;
196   fn find_by_email_or_name(
197     conn: &PgConnection,
198     name_or_email: &str,
199   ) -> Result<Person, Error>;
200   fn find_by_name(conn: &PgConnection, name: &str) -> Result<Person, Error>;
201   fn find_by_email(conn: &PgConnection, from_email: &str) -> Result<Person, Error>;
202   fn get_profile_url(&self, hostname: &str) -> String;
203   fn mark_as_updated(conn: &PgConnection, person_id: i32) -> Result<Person, Error>;
204   fn delete_account(conn: &PgConnection, person_id: i32) -> Result<Person, Error>;
205 }
206
207 impl Person_ for Person {
208   fn register(conn: &PgConnection, form: &PersonForm) -> Result<Self, Error> {
209     let mut edited_person = form.clone();
210     let password_hash =
211       hash(&form.password_encrypted, DEFAULT_COST).expect("Couldn't hash password");
212     edited_person.password_encrypted = password_hash;
213
214     Self::create(&conn, &edited_person)
215   }
216
217   // TODO do more individual updates like these
218   fn update_password(conn: &PgConnection, person_id: i32, new_password: &str) -> Result<Self, Error> {
219     let password_hash = hash(new_password, DEFAULT_COST).expect("Couldn't hash password");
220
221     diesel::update(person.find(person_id))
222       .set((
223         password_encrypted.eq(password_hash),
224         updated.eq(naive_now()),
225       ))
226       .get_result::<Self>(conn)
227   }
228
229   fn read_from_name(conn: &PgConnection, from_name: &str) -> Result<Self, Error> {
230     person
231       .filter(local.eq(true))
232       .filter(deleted.eq(false))
233       .filter(name.eq(from_name))
234       .first::<Self>(conn)
235   }
236
237   fn add_admin(conn: &PgConnection, person_id: i32, added: bool) -> Result<Self, Error> {
238     diesel::update(person.find(person_id))
239       .set(admin.eq(added))
240       .get_result::<Self>(conn)
241   }
242
243   fn ban_person(conn: &PgConnection, person_id: i32, ban: bool) -> Result<Self, Error> {
244     diesel::update(person.find(person_id))
245       .set(banned.eq(ban))
246       .get_result::<Self>(conn)
247   }
248
249   fn find_by_email_or_name(
250     conn: &PgConnection,
251     name_or_email: &str,
252   ) -> Result<Self, Error> {
253     if is_email_regex(name_or_email) {
254       Self::find_by_email(conn, name_or_email)
255     } else {
256       Self::find_by_name(conn, name_or_email)
257     }
258   }
259
260   fn find_by_name(conn: &PgConnection, name: &str) -> Result<Person, Error> {
261     person
262       .filter(deleted.eq(false))
263       .filter(local.eq(true))
264       .filter(name.ilike(name))
265       .first::<Person>(conn)
266   }
267
268   fn find_by_email(conn: &PgConnection, from_email: &str) -> Result<Person, Error> {
269     person
270       .filter(deleted.eq(false))
271       .filter(local.eq(true))
272       .filter(email.eq(from_email))
273       .first::<Person>(conn)
274   }
275
276   fn get_profile_url(&self, hostname: &str) -> String {
277     format!(
278       "{}://{}/u/{}",
279       Settings::get().get_protocol_string(),
280       hostname,
281       self.name
282     )
283   }
284
285   fn mark_as_updated(conn: &PgConnection, person_id: i32) -> Result<Person, Error> {
286     diesel::update(person.find(person_id))
287       .set((last_refreshed_at.eq(naive_now()),))
288       .get_result::<Self>(conn)
289   }
290
291   fn delete_account(conn: &PgConnection, person_id: i32) -> Result<Person, Error> {
292     diesel::update(person.find(person_id))
293       .set((
294         preferred_username.eq::<Option<String>>(None),
295         email.eq::<Option<String>>(None),
296         matrix_user_id.eq::<Option<String>>(None),
297         bio.eq::<Option<String>>(None),
298         deleted.eq(true),
299         updated.eq(naive_now()),
300       ))
301       .get_result::<Self>(conn)
302   }
303 }
304
305 #[cfg(test)]
306 mod tests {
307   use crate::{establish_unpooled_connection, source::person::*, ListingType, SortType};
308
309   #[test]
310   fn test_crud() {
311     let conn = establish_unpooled_connection();
312
313     let new_person = PersonForm {
314       name: "thommy".into(),
315       preferred_username: None,
316       avatar: None,
317       banner: None,
318       banned: Some(false),
319       deleted: false,
320       published: None,
321       updated: None,
322       actor_id: None,
323       bio: None,
324       local: true,
325       private_key: None,
326       public_key: None,
327       last_refreshed_at: None,
328       inbox_url: None,
329       shared_inbox_url: None,
330     };
331
332     let inserted_person = Person::create(&conn, &new_person).unwrap();
333
334     let expected_person = Person {
335       id: inserted_person.id,
336       name: "thommy".into(),
337       preferred_username: None,
338       avatar: None,
339       banner: None,
340       banned: false,
341       deleted: false,
342       published: inserted_person.published,
343       updated: None,
344       actor_id: inserted_person.actor_id.to_owned(),
345       bio: None,
346       local: true,
347       private_key: None,
348       public_key: None,
349       last_refreshed_at: inserted_person.published,
350       deleted: false,
351       inbox_url: inserted_person.inbox_url.to_owned(),
352       shared_inbox_url: None,
353     };
354
355     let read_person = Person::read(&conn, inserted_person.id).unwrap();
356     let updated_person = Person::update(&conn, inserted_person.id, &new_person).unwrap();
357     let num_deleted = Person::delete(&conn, inserted_person.id).unwrap();
358
359     assert_eq!(expected_person, read_person);
360     assert_eq!(expected_person, inserted_person);
361     assert_eq!(expected_person, updated_person);
362     assert_eq!(1, num_deleted);
363   }
364 }