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