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