]> Untitled Git - lemmy.git/blob - crates/db_schema/src/impls/person.rs
Adding temporary bans. Fixes #1423 (#1999)
[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::Crud,
8 };
9 use diesel::{dsl::*, result::Error, ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl};
10 use url::Url;
11
12 mod safe_type {
13   use crate::{schema::person::columns::*, source::person::Person, traits::ToSafe};
14
15   type Columns = (
16     id,
17     name,
18     display_name,
19     avatar,
20     banned,
21     published,
22     updated,
23     actor_id,
24     bio,
25     local,
26     banner,
27     deleted,
28     inbox_url,
29     shared_inbox_url,
30     matrix_user_id,
31     admin,
32     bot_account,
33     ban_expires,
34   );
35
36   impl ToSafe for Person {
37     type SafeColumns = Columns;
38     fn safe_columns_tuple() -> Self::SafeColumns {
39       (
40         id,
41         name,
42         display_name,
43         avatar,
44         banned,
45         published,
46         updated,
47         actor_id,
48         bio,
49         local,
50         banner,
51         deleted,
52         inbox_url,
53         shared_inbox_url,
54         matrix_user_id,
55         admin,
56         bot_account,
57         ban_expires,
58       )
59     }
60   }
61 }
62
63 mod safe_type_alias_1 {
64   use crate::{schema::person_alias_1::columns::*, source::person::PersonAlias1, traits::ToSafe};
65
66   type Columns = (
67     id,
68     name,
69     display_name,
70     avatar,
71     banned,
72     published,
73     updated,
74     actor_id,
75     bio,
76     local,
77     banner,
78     deleted,
79     inbox_url,
80     shared_inbox_url,
81     matrix_user_id,
82     admin,
83     bot_account,
84     ban_expires,
85   );
86
87   impl ToSafe for PersonAlias1 {
88     type SafeColumns = Columns;
89     fn safe_columns_tuple() -> Self::SafeColumns {
90       (
91         id,
92         name,
93         display_name,
94         avatar,
95         banned,
96         published,
97         updated,
98         actor_id,
99         bio,
100         local,
101         banner,
102         deleted,
103         inbox_url,
104         shared_inbox_url,
105         matrix_user_id,
106         admin,
107         bot_account,
108         ban_expires,
109       )
110     }
111   }
112 }
113
114 mod safe_type_alias_2 {
115   use crate::{schema::person_alias_2::columns::*, source::person::PersonAlias2, traits::ToSafe};
116
117   type Columns = (
118     id,
119     name,
120     display_name,
121     avatar,
122     banned,
123     published,
124     updated,
125     actor_id,
126     bio,
127     local,
128     banner,
129     deleted,
130     inbox_url,
131     shared_inbox_url,
132     matrix_user_id,
133     admin,
134     bot_account,
135     ban_expires,
136   );
137
138   impl ToSafe for PersonAlias2 {
139     type SafeColumns = Columns;
140     fn safe_columns_tuple() -> Self::SafeColumns {
141       (
142         id,
143         name,
144         display_name,
145         avatar,
146         banned,
147         published,
148         updated,
149         actor_id,
150         bio,
151         local,
152         banner,
153         deleted,
154         inbox_url,
155         shared_inbox_url,
156         matrix_user_id,
157         admin,
158         bot_account,
159         ban_expires,
160       )
161     }
162   }
163 }
164
165 impl Crud for Person {
166   type Form = PersonForm;
167   type IdType = PersonId;
168   fn read(conn: &PgConnection, person_id: PersonId) -> Result<Self, Error> {
169     person
170       .filter(deleted.eq(false))
171       .find(person_id)
172       .first::<Self>(conn)
173   }
174   fn delete(conn: &PgConnection, person_id: PersonId) -> Result<usize, Error> {
175     diesel::delete(person.find(person_id)).execute(conn)
176   }
177   fn create(conn: &PgConnection, form: &PersonForm) -> Result<Self, Error> {
178     insert_into(person).values(form).get_result::<Self>(conn)
179   }
180   fn update(conn: &PgConnection, person_id: PersonId, form: &PersonForm) -> Result<Self, Error> {
181     diesel::update(person.find(person_id))
182       .set(form)
183       .get_result::<Self>(conn)
184   }
185 }
186
187 impl Person {
188   pub fn ban_person(
189     conn: &PgConnection,
190     person_id: PersonId,
191     ban: bool,
192     expires: Option<chrono::NaiveDateTime>,
193   ) -> Result<Self, Error> {
194     diesel::update(person.find(person_id))
195       .set((banned.eq(ban), ban_expires.eq(expires)))
196       .get_result::<Self>(conn)
197   }
198
199   pub fn add_admin(conn: &PgConnection, person_id: PersonId, added: bool) -> Result<Self, Error> {
200     diesel::update(person.find(person_id))
201       .set(admin.eq(added))
202       .get_result::<Self>(conn)
203   }
204
205   pub fn find_by_name(conn: &PgConnection, from_name: &str) -> Result<Person, Error> {
206     person
207       .filter(deleted.eq(false))
208       .filter(local.eq(true))
209       .filter(lower(name).eq(lower(from_name)))
210       .first::<Person>(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 read_from_apub_id(conn: &PgConnection, object_id: Url) -> Result<Option<Self>, Error> {
251     use crate::schema::person::dsl::*;
252     let object_id: DbUrl = object_id.into();
253     Ok(
254       person
255         .filter(deleted.eq(false))
256         .filter(actor_id.eq(object_id))
257         .first::<Person>(conn)
258         .ok()
259         .map(Into::into),
260     )
261   }
262
263   pub fn update_deleted(
264     conn: &PgConnection,
265     person_id: PersonId,
266     new_deleted: bool,
267   ) -> Result<Person, Error> {
268     use crate::schema::person::dsl::*;
269     diesel::update(person.find(person_id))
270       .set(deleted.eq(new_deleted))
271       .get_result::<Self>(conn)
272   }
273
274   pub fn is_banned(&self) -> bool {
275     is_banned(self.banned, self.ban_expires)
276   }
277 }
278
279 impl PersonSafe {
280   pub fn is_banned(&self) -> bool {
281     is_banned(self.banned, self.ban_expires)
282   }
283 }
284
285 fn is_banned(banned_: bool, expires: Option<chrono::NaiveDateTime>) -> bool {
286   if let Some(expires) = expires {
287     banned_ && expires.gt(&naive_now())
288   } else {
289     banned_
290   }
291 }
292
293 #[cfg(test)]
294 mod tests {
295   use crate::{establish_unpooled_connection, source::person::*, traits::Crud};
296
297   #[test]
298   fn test_crud() {
299     let conn = establish_unpooled_connection();
300
301     let new_person = PersonForm {
302       name: "holly".into(),
303       public_key: "nada".to_owned(),
304       ..PersonForm::default()
305     };
306
307     let inserted_person = Person::create(&conn, &new_person).unwrap();
308
309     let expected_person = Person {
310       id: inserted_person.id,
311       name: "holly".into(),
312       display_name: None,
313       avatar: None,
314       banner: None,
315       banned: false,
316       deleted: false,
317       published: inserted_person.published,
318       updated: None,
319       actor_id: inserted_person.actor_id.to_owned(),
320       bio: None,
321       local: true,
322       bot_account: false,
323       admin: false,
324       private_key: None,
325       public_key: "nada".to_owned(),
326       last_refreshed_at: inserted_person.published,
327       inbox_url: inserted_person.inbox_url.to_owned(),
328       shared_inbox_url: None,
329       matrix_user_id: None,
330       ban_expires: None,
331     };
332
333     let read_person = Person::read(&conn, inserted_person.id).unwrap();
334     let updated_person = Person::update(&conn, inserted_person.id, &new_person).unwrap();
335     let num_deleted = Person::delete(&conn, inserted_person.id).unwrap();
336
337     assert_eq!(expected_person, read_person);
338     assert_eq!(expected_person, inserted_person);
339     assert_eq!(expected_person, updated_person);
340     assert_eq!(1, num_deleted);
341   }
342 }