2 newtypes::{CommunityId, DbUrl, PersonId},
3 schema::{instance, local_user, person, person_follower},
11 traits::{ApubActor, Crud, Followable},
12 utils::{functions::lower, get_conn, naive_now, DbPool},
14 use diesel::{dsl::insert_into, result::Error, ExpressionMethods, JoinOnDsl, QueryDsl};
15 use diesel_async::RunQueryDsl;
18 impl Crud for Person {
19 type InsertForm = PersonInsertForm;
20 type UpdateForm = PersonUpdateForm;
21 type IdType = PersonId;
22 async fn read(pool: &mut DbPool<'_>, person_id: PersonId) -> Result<Self, Error> {
23 let conn = &mut get_conn(pool).await?;
25 .filter(person::deleted.eq(false))
31 async fn create(pool: &mut DbPool<'_>, form: &PersonInsertForm) -> Result<Self, Error> {
32 let conn = &mut get_conn(pool).await?;
33 insert_into(person::table)
35 .get_result::<Self>(conn)
39 pool: &mut DbPool<'_>,
41 form: &PersonUpdateForm,
42 ) -> Result<Self, Error> {
43 let conn = &mut get_conn(pool).await?;
44 diesel::update(person::table.find(person_id))
46 .get_result::<Self>(conn)
52 /// Update or insert the person.
54 /// This is necessary for federation, because Activitypub doesnt distinguish between these actions.
55 pub async fn upsert(pool: &mut DbPool<'_>, form: &PersonInsertForm) -> Result<Self, Error> {
56 let conn = &mut get_conn(pool).await?;
57 insert_into(person::table)
59 .on_conflict(person::actor_id)
62 .get_result::<Self>(conn)
65 pub async fn delete_account(pool: &mut DbPool<'_>, person_id: PersonId) -> Result<Person, Error> {
66 let conn = &mut get_conn(pool).await?;
68 // Set the local user info to none
69 diesel::update(local_user::table.filter(local_user::person_id.eq(person_id)))
71 local_user::email.eq::<Option<String>>(None),
72 local_user::validator_time.eq(naive_now()),
77 diesel::update(person::table.find(person_id))
79 person::display_name.eq::<Option<String>>(None),
80 person::avatar.eq::<Option<String>>(None),
81 person::banner.eq::<Option<String>>(None),
82 person::bio.eq::<Option<String>>(None),
83 person::matrix_user_id.eq::<Option<String>>(None),
84 person::deleted.eq(true),
85 person::updated.eq(naive_now()),
87 .get_result::<Self>(conn)
92 pub fn is_banned(banned_: bool, expires: Option<chrono::NaiveDateTime>) -> bool {
93 if let Some(expires) = expires {
94 banned_ && expires.gt(&naive_now())
101 impl ApubActor for Person {
102 async fn read_from_apub_id(
103 pool: &mut DbPool<'_>,
105 ) -> Result<Option<Self>, Error> {
106 let conn = &mut get_conn(pool).await?;
109 .filter(person::deleted.eq(false))
110 .filter(person::actor_id.eq(object_id))
111 .first::<Person>(conn)
118 async fn read_from_name(
119 pool: &mut DbPool<'_>,
121 include_deleted: bool,
122 ) -> Result<Person, Error> {
123 let conn = &mut get_conn(pool).await?;
124 let mut q = person::table
126 .filter(person::local.eq(true))
127 .filter(lower(person::name).eq(from_name.to_lowercase()));
128 if !include_deleted {
129 q = q.filter(person::deleted.eq(false))
131 q.first::<Self>(conn).await
134 async fn read_from_name_and_domain(
135 pool: &mut DbPool<'_>,
138 ) -> Result<Person, Error> {
139 let conn = &mut get_conn(pool).await?;
142 .inner_join(instance::table)
143 .filter(lower(person::name).eq(person_name.to_lowercase()))
144 .filter(instance::domain.eq(for_domain))
145 .select(person::all_columns)
152 impl Followable for PersonFollower {
153 type Form = PersonFollowerForm;
154 async fn follow(pool: &mut DbPool<'_>, form: &PersonFollowerForm) -> Result<Self, Error> {
155 use crate::schema::person_follower::dsl::{follower_id, person_follower, person_id};
156 let conn = &mut get_conn(pool).await?;
157 insert_into(person_follower)
159 .on_conflict((follower_id, person_id))
162 .get_result::<Self>(conn)
165 async fn follow_accepted(_: &mut DbPool<'_>, _: CommunityId, _: PersonId) -> Result<Self, Error> {
168 async fn unfollow(pool: &mut DbPool<'_>, form: &PersonFollowerForm) -> Result<usize, Error> {
169 use crate::schema::person_follower::dsl::{follower_id, person_follower, person_id};
170 let conn = &mut get_conn(pool).await?;
173 .filter(follower_id.eq(&form.follower_id))
174 .filter(person_id.eq(&form.person_id)),
181 impl PersonFollower {
182 pub async fn list_followers(
183 pool: &mut DbPool<'_>,
184 for_person_id: PersonId,
185 ) -> Result<Vec<Person>, Error> {
186 let conn = &mut get_conn(pool).await?;
187 person_follower::table
188 .inner_join(person::table.on(person_follower::follower_id.eq(person::id)))
189 .filter(person_follower::person_id.eq(for_person_id))
190 .select(person::all_columns)
198 #![allow(clippy::unwrap_used)]
199 #![allow(clippy::indexing_slicing)]
204 person::{Person, PersonFollower, PersonFollowerForm, PersonInsertForm, PersonUpdateForm},
206 traits::{Crud, Followable},
207 utils::build_db_pool_for_tests,
209 use serial_test::serial;
213 async fn test_crud() {
214 let pool = &build_db_pool_for_tests().await;
215 let pool = &mut pool.into();
217 let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
221 let new_person = PersonInsertForm::builder()
222 .name("holly".into())
223 .public_key("nada".to_owned())
224 .instance_id(inserted_instance.id)
227 let inserted_person = Person::create(pool, &new_person).await.unwrap();
229 let expected_person = Person {
230 id: inserted_person.id,
231 name: "holly".into(),
237 published: inserted_person.published,
239 actor_id: inserted_person.actor_id.clone(),
245 public_key: "nada".to_owned(),
246 last_refreshed_at: inserted_person.published,
247 inbox_url: inserted_person.inbox_url.clone(),
248 shared_inbox_url: None,
249 matrix_user_id: None,
251 instance_id: inserted_instance.id,
254 let read_person = Person::read(pool, inserted_person.id).await.unwrap();
256 let update_person_form = PersonUpdateForm {
257 actor_id: Some(inserted_person.actor_id.clone()),
260 let updated_person = Person::update(pool, inserted_person.id, &update_person_form)
264 let num_deleted = Person::delete(pool, inserted_person.id).await.unwrap();
265 Instance::delete(pool, inserted_instance.id).await.unwrap();
267 assert_eq!(expected_person, read_person);
268 assert_eq!(expected_person, inserted_person);
269 assert_eq!(expected_person, updated_person);
270 assert_eq!(1, num_deleted);
276 let pool = &build_db_pool_for_tests().await;
277 let pool = &mut pool.into();
278 let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
282 let person_form_1 = PersonInsertForm::builder()
283 .name("erich".into())
284 .public_key("pubkey".to_string())
285 .instance_id(inserted_instance.id)
287 let person_1 = Person::create(pool, &person_form_1).await.unwrap();
288 let person_form_2 = PersonInsertForm::builder()
289 .name("michele".into())
290 .public_key("pubkey".to_string())
291 .instance_id(inserted_instance.id)
293 let person_2 = Person::create(pool, &person_form_2).await.unwrap();
295 let follow_form = PersonFollowerForm {
296 person_id: person_1.id,
297 follower_id: person_2.id,
300 let person_follower = PersonFollower::follow(pool, &follow_form).await.unwrap();
301 assert_eq!(person_1.id, person_follower.person_id);
302 assert_eq!(person_2.id, person_follower.follower_id);
303 assert!(!person_follower.pending);
305 let followers = PersonFollower::list_followers(pool, person_1.id)
308 assert_eq!(vec![person_2], followers);
310 let unfollow = PersonFollower::unfollow(pool, &follow_form).await.unwrap();
311 assert_eq!(1, unfollow);