diff --git a/plume-models/src/blocklisted_emails.rs b/plume-models/src/blocklisted_emails.rs index b6585523..4f396b1a 100644 --- a/plume-models/src/blocklisted_emails.rs +++ b/plume-models/src/blocklisted_emails.rs @@ -87,7 +87,7 @@ impl BlocklistedEmail { #[cfg(test)] pub(crate) mod tests { use super::*; - use crate::{instance::tests as instance_tests, tests::rockets, Connection as Conn}; + use crate::{instance::tests as instance_tests, tests::db, Connection as Conn}; use diesel::Connection; pub(crate) fn fill_database(conn: &Conn) -> Vec { @@ -106,29 +106,28 @@ pub(crate) mod tests { } #[test] fn test_match() { - let r = rockets(); - let conn = &*r.conn; + let conn = db(); conn.test_transaction::<_, (), _>(|| { - let various = fill_database(conn); + let various = fill_database(&conn); let match1 = "user1@bad-actor.com"; let match2 = "spammer@lax-administration.com"; let no_match = "happy-user@lax-administration.com"; assert_eq!( - BlocklistedEmail::matches_blocklist(conn, match1) + BlocklistedEmail::matches_blocklist(&conn, match1) .unwrap() .unwrap() .id, various[0].id ); assert_eq!( - BlocklistedEmail::matches_blocklist(conn, match2) + BlocklistedEmail::matches_blocklist(&conn, match2) .unwrap() .unwrap() .id, various[1].id ); assert_eq!( - BlocklistedEmail::matches_blocklist(conn, no_match) + BlocklistedEmail::matches_blocklist(&conn, no_match) .unwrap() .is_none(), true diff --git a/plume-models/src/blogs.rs b/plume-models/src/blogs.rs index cfc0624b..bf2a8ad5 100644 --- a/plume-models/src/blogs.rs +++ b/plume-models/src/blogs.rs @@ -1,6 +1,6 @@ use crate::{ - ap_url, instance::*, medias::Media, posts::Post, safe_string::SafeString, schema::blogs, - users::User, Connection, Error, PlumeRocket, Result, CONFIG, ITEMS_PER_PAGE, + ap_url, db_conn::DbConn, instance::*, medias::Media, posts::Post, safe_string::SafeString, + schema::blogs, users::User, Connection, Error, PlumeRocket, Result, CONFIG, ITEMS_PER_PAGE, }; use activitypub::{ actor::Group, @@ -131,25 +131,25 @@ impl Blog { .map_err(Error::from) } - pub fn find_by_fqn(c: &PlumeRocket, fqn: &str) -> Result { + pub fn find_by_fqn(conn: &DbConn, fqn: &str) -> Result { let from_db = blogs::table .filter(blogs::fqn.eq(fqn)) - .first(&*c.conn) + .first(&**conn) .optional()?; if let Some(from_db) = from_db { Ok(from_db) } else { - Blog::fetch_from_webfinger(c, fqn) + Blog::fetch_from_webfinger(conn, fqn) } } - fn fetch_from_webfinger(c: &PlumeRocket, acct: &str) -> Result { + fn fetch_from_webfinger(conn: &DbConn, acct: &str) -> Result { resolve_with_prefix(Prefix::Group, acct.to_owned(), true)? .links .into_iter() .find(|l| l.mime_type == Some(String::from("application/activity+json"))) .ok_or(Error::Webfinger) - .and_then(|l| Blog::from_id(c, &l.href?, None, CONFIG.proxy()).map_err(|(_, e)| e)) + .and_then(|l| Blog::from_id(conn, &l.href?, None, CONFIG.proxy()).map_err(|(_, e)| e)) } pub fn to_activity(&self, conn: &Connection) -> Result { @@ -334,20 +334,20 @@ impl IntoId for Blog { } } -impl FromId for Blog { +impl FromId for Blog { type Error = Error; type Object = CustomGroup; - fn from_db(c: &PlumeRocket, id: &str) -> Result { - Self::find_by_ap_url(&c.conn, id) + fn from_db(conn: &DbConn, id: &str) -> Result { + Self::find_by_ap_url(&conn, id) } - fn from_activity(c: &PlumeRocket, acct: CustomGroup) -> Result { + fn from_activity(conn: &DbConn, acct: CustomGroup) -> Result { let url = Url::parse(&acct.object.object_props.id_string()?)?; let inst = url.host_str()?; - let instance = Instance::find_by_domain(&c.conn, inst).or_else(|_| { + let instance = Instance::find_by_domain(conn, inst).or_else(|_| { Instance::insert( - &c.conn, + conn, NewInstance { public_domain: inst.to_owned(), name: inst.to_owned(), @@ -370,9 +370,9 @@ impl FromId for Blog { .and_then(|icon| { let owner = icon.object_props.attributed_to_link::().ok()?; Media::save_remote( - &c.conn, + conn, icon.object_props.url_string().ok()?, - &User::from_id(c, &owner, None, CONFIG.proxy()).ok()?, + &User::from_id(conn, &owner, None, CONFIG.proxy()).ok()?, ) .ok() }) @@ -386,9 +386,9 @@ impl FromId for Blog { .and_then(|banner| { let owner = banner.object_props.attributed_to_link::().ok()?; Media::save_remote( - &c.conn, + conn, banner.object_props.url_string().ok()?, - &User::from_id(c, &owner, None, CONFIG.proxy()).ok()?, + &User::from_id(conn, &owner, None, CONFIG.proxy()).ok()?, ) .ok() }) @@ -400,7 +400,7 @@ impl FromId for Blog { } Blog::insert( - &c.conn, + conn, NewBlog { actor_id: name.clone(), title: acct.object.object_props.name_string().unwrap_or(name), @@ -496,12 +496,8 @@ impl NewBlog { pub(crate) mod tests { use super::*; use crate::{ - blog_authors::*, - instance::tests as instance_tests, - medias::NewMedia, - tests::{db, rockets}, - users::tests as usersTests, - Connection as Conn, + blog_authors::*, instance::tests as instance_tests, medias::NewMedia, tests::db, + users::tests as usersTests, Connection as Conn, }; use diesel::Connection; @@ -588,10 +584,10 @@ pub(crate) mod tests { fn get_instance() { let conn = &db(); conn.test_transaction::<_, (), _>(|| { - fill_database(conn); + fill_database(&conn); let blog = Blog::insert( - conn, + &conn, NewBlog::new_local( "SomeName".to_owned(), "Some name".to_owned(), @@ -603,7 +599,7 @@ pub(crate) mod tests { .unwrap(); assert_eq!( - blog.get_instance(conn).unwrap().id, + blog.get_instance(&conn).unwrap().id, Instance::get_local().unwrap().id ); // TODO add tests for remote instance @@ -615,10 +611,10 @@ pub(crate) mod tests { fn authors() { let conn = &db(); conn.test_transaction::<_, (), _>(|| { - let (user, _) = fill_database(conn); + let (user, _) = fill_database(&conn); let b1 = Blog::insert( - conn, + &conn, NewBlog::new_local( "SomeName".to_owned(), "Some name".to_owned(), @@ -629,7 +625,7 @@ pub(crate) mod tests { ) .unwrap(); let b2 = Blog::insert( - conn, + &conn, NewBlog::new_local( "Blog".to_owned(), "Blog".to_owned(), @@ -642,7 +638,7 @@ pub(crate) mod tests { let blog = vec![b1, b2]; BlogAuthor::insert( - conn, + &conn, NewBlogAuthor { blog_id: blog[0].id, author_id: user[0].id, @@ -652,7 +648,7 @@ pub(crate) mod tests { .unwrap(); BlogAuthor::insert( - conn, + &conn, NewBlogAuthor { blog_id: blog[0].id, author_id: user[1].id, @@ -662,7 +658,7 @@ pub(crate) mod tests { .unwrap(); BlogAuthor::insert( - conn, + &conn, NewBlogAuthor { blog_id: blog[1].id, author_id: user[0].id, @@ -672,39 +668,39 @@ pub(crate) mod tests { .unwrap(); assert!(blog[0] - .list_authors(conn) + .list_authors(&conn) .unwrap() .iter() .any(|a| a.id == user[0].id)); assert!(blog[0] - .list_authors(conn) + .list_authors(&conn) .unwrap() .iter() .any(|a| a.id == user[1].id)); assert!(blog[1] - .list_authors(conn) + .list_authors(&conn) .unwrap() .iter() .any(|a| a.id == user[0].id)); assert!(!blog[1] - .list_authors(conn) + .list_authors(&conn) .unwrap() .iter() .any(|a| a.id == user[1].id)); - assert!(Blog::find_for_author(conn, &user[0]) + assert!(Blog::find_for_author(&conn, &user[0]) .unwrap() .iter() .any(|b| b.id == blog[0].id)); - assert!(Blog::find_for_author(conn, &user[1]) + assert!(Blog::find_for_author(&conn, &user[1]) .unwrap() .iter() .any(|b| b.id == blog[0].id)); - assert!(Blog::find_for_author(conn, &user[0]) + assert!(Blog::find_for_author(&conn, &user[0]) .unwrap() .iter() .any(|b| b.id == blog[1].id)); - assert!(!Blog::find_for_author(conn, &user[1]) + assert!(!Blog::find_for_author(&conn, &user[1]) .unwrap() .iter() .any(|b| b.id == blog[1].id)); @@ -714,13 +710,12 @@ pub(crate) mod tests { #[test] fn find_local() { - let r = rockets(); - let conn = &*r.conn; + let conn = &db(); conn.test_transaction::<_, (), _>(|| { - fill_database(conn); + fill_database(&conn); let blog = Blog::insert( - conn, + &conn, NewBlog::new_local( "SomeName".to_owned(), "Some name".to_owned(), @@ -731,7 +726,7 @@ pub(crate) mod tests { ) .unwrap(); - assert_eq!(Blog::find_by_fqn(&r, "SomeName").unwrap().id, blog.id); + assert_eq!(Blog::find_by_fqn(&conn, "SomeName").unwrap().id, blog.id); Ok(()) }) } @@ -740,10 +735,10 @@ pub(crate) mod tests { fn get_fqn() { let conn = &db(); conn.test_transaction::<_, (), _>(|| { - fill_database(conn); + fill_database(&conn); let blog = Blog::insert( - conn, + &conn, NewBlog::new_local( "SomeName".to_owned(), "Some name".to_owned(), @@ -763,10 +758,10 @@ pub(crate) mod tests { fn delete() { let conn = &db(); conn.test_transaction::<_, (), _>(|| { - let (_, blogs) = fill_database(conn); + let (_, blogs) = fill_database(&conn); - blogs[0].delete(conn).unwrap(); - assert!(Blog::get(conn, blogs[0].id).is_err()); + blogs[0].delete(&conn).unwrap(); + assert!(Blog::get(&conn, blogs[0].id).is_err()); Ok(()) }) } @@ -775,10 +770,10 @@ pub(crate) mod tests { fn delete_via_user() { let conn = &db(); conn.test_transaction::<_, (), _>(|| { - let (user, _) = fill_database(conn); + let (user, _) = fill_database(&conn); let b1 = Blog::insert( - conn, + &conn, NewBlog::new_local( "SomeName".to_owned(), "Some name".to_owned(), @@ -789,7 +784,7 @@ pub(crate) mod tests { ) .unwrap(); let b2 = Blog::insert( - conn, + &conn, NewBlog::new_local( "Blog".to_owned(), "Blog".to_owned(), @@ -802,7 +797,7 @@ pub(crate) mod tests { let blog = vec![b1, b2]; BlogAuthor::insert( - conn, + &conn, NewBlogAuthor { blog_id: blog[0].id, author_id: user[0].id, @@ -812,7 +807,7 @@ pub(crate) mod tests { .unwrap(); BlogAuthor::insert( - conn, + &conn, NewBlogAuthor { blog_id: blog[0].id, author_id: user[1].id, @@ -822,7 +817,7 @@ pub(crate) mod tests { .unwrap(); BlogAuthor::insert( - conn, + &conn, NewBlogAuthor { blog_id: blog[1].id, author_id: user[0].id, @@ -831,24 +826,23 @@ pub(crate) mod tests { ) .unwrap(); - user[0].delete(conn).unwrap(); - assert!(Blog::get(conn, blog[0].id).is_ok()); - assert!(Blog::get(conn, blog[1].id).is_err()); - user[1].delete(conn).unwrap(); - assert!(Blog::get(conn, blog[0].id).is_err()); + user[0].delete(&conn).unwrap(); + assert!(Blog::get(&conn, blog[0].id).is_ok()); + assert!(Blog::get(&conn, blog[1].id).is_err()); + user[1].delete(&conn).unwrap(); + assert!(Blog::get(&conn, blog[0].id).is_err()); Ok(()) }) } #[test] fn self_federation() { - let r = rockets(); - let conn = &*r.conn; + let conn = &db(); conn.test_transaction::<_, (), _>(|| { - let (users, mut blogs) = fill_database(conn); + let (users, mut blogs) = fill_database(&conn); blogs[0].icon_id = Some( Media::insert( - conn, + &conn, NewMedia { file_path: "aaa.png".into(), alt_text: String::new(), @@ -864,7 +858,7 @@ pub(crate) mod tests { ); blogs[0].banner_id = Some( Media::insert( - conn, + &conn, NewMedia { file_path: "bbb.png".into(), alt_text: String::new(), @@ -878,11 +872,11 @@ pub(crate) mod tests { .unwrap() .id, ); - let _: Blog = blogs[0].save_changes(conn).unwrap(); + let _: Blog = blogs[0].save_changes(&**conn).unwrap(); - let ap_repr = blogs[0].to_activity(conn).unwrap(); - blogs[0].delete(conn).unwrap(); - let blog = Blog::from_activity(&r, ap_repr).unwrap(); + let ap_repr = blogs[0].to_activity(&conn).unwrap(); + blogs[0].delete(&conn).unwrap(); + let blog = Blog::from_activity(&conn, ap_repr).unwrap(); assert_eq!(blog.actor_id, blogs[0].actor_id); assert_eq!(blog.title, blogs[0].title); @@ -894,8 +888,8 @@ pub(crate) mod tests { assert_eq!(blog.public_key, blogs[0].public_key); assert_eq!(blog.fqn, blogs[0].fqn); assert_eq!(blog.summary_html, blogs[0].summary_html); - assert_eq!(blog.icon_url(conn), blogs[0].icon_url(conn)); - assert_eq!(blog.banner_url(conn), blogs[0].banner_url(conn)); + assert_eq!(blog.icon_url(&conn), blogs[0].icon_url(&conn)); + assert_eq!(blog.banner_url(&conn), blogs[0].banner_url(&conn)); Ok(()) }) diff --git a/plume-models/src/comments.rs b/plume-models/src/comments.rs index d2bcb9b8..377e2dda 100644 --- a/plume-models/src/comments.rs +++ b/plume-models/src/comments.rs @@ -1,5 +1,6 @@ use crate::{ comment_seers::{CommentSeers, NewCommentSeers}, + db_conn::DbConn, instance::Instance, medias::Media, mentions::Mention, @@ -8,7 +9,7 @@ use crate::{ safe_string::SafeString, schema::comments, users::User, - Connection, Error, PlumeRocket, Result, CONFIG, + Connection, Error, Result, CONFIG, }; use activitypub::{ activity::{Create, Delete}, @@ -104,13 +105,13 @@ impl Comment { .unwrap_or(false) } - pub fn to_activity(&self, c: &PlumeRocket) -> Result { - let author = User::get(&c.conn, self.author_id)?; + pub fn to_activity(&self, conn: &DbConn) -> Result { + let author = User::get(conn, self.author_id)?; let (html, mentions, _hashtags) = utils::md_to_html( self.content.get().as_ref(), Some(&Instance::get_local()?.public_domain), true, - Some(Media::get_media_processor(&c.conn, vec![&author])), + Some(Media::get_media_processor(conn, vec![&author])), ); let mut note = Note::default(); @@ -123,8 +124,8 @@ impl Comment { note.object_props.set_content_string(html)?; note.object_props .set_in_reply_to_link(Id::new(self.in_response_to_id.map_or_else( - || Ok(Post::get(&c.conn, self.post_id)?.ap_url), - |id| Ok(Comment::get(&c.conn, id)?.ap_url.unwrap_or_default()) as Result, + || Ok(Post::get(conn, self.post_id)?.ap_url), + |id| Ok(Comment::get(conn, id)?.ap_url.unwrap_or_default()) as Result, )?))?; note.object_props .set_published_string(chrono::Utc::now().to_rfc3339())?; @@ -133,16 +134,16 @@ impl Comment { note.object_props.set_tag_link_vec( mentions .into_iter() - .filter_map(|m| Mention::build_activity(c, &m).ok()) + .filter_map(|m| Mention::build_activity(conn, &m).ok()) .collect::>(), )?; Ok(note) } - pub fn create_activity(&self, c: &PlumeRocket) -> Result { - let author = User::get(&c.conn, self.author_id)?; + pub fn create_activity(&self, conn: &DbConn) -> Result { + let author = User::get(&conn, self.author_id)?; - let note = self.to_activity(c)?; + let note = self.to_activity(conn)?; let mut act = Create::default(); act.create_props.set_actor_link(author.into_id())?; act.create_props.set_object_object(note.clone())?; @@ -151,7 +152,7 @@ impl Comment { act.object_props .set_to_link_vec(note.object_props.to_link_vec::()?)?; act.object_props - .set_cc_link_vec(vec![Id::new(self.get_author(&c.conn)?.followers_endpoint)])?; + .set_cc_link_vec(vec![Id::new(self.get_author(&conn)?.followers_endpoint)])?; Ok(act) } @@ -193,16 +194,15 @@ impl Comment { } } -impl FromId for Comment { +impl FromId for Comment { type Error = Error; type Object = Note; - fn from_db(c: &PlumeRocket, id: &str) -> Result { - Self::find_by_ap_url(&c.conn, id) + fn from_db(conn: &DbConn, id: &str) -> Result { + Self::find_by_ap_url(conn, id) } - fn from_activity(c: &PlumeRocket, note: Note) -> Result { - let conn = &*c.conn; + fn from_activity(conn: &DbConn, note: Note) -> Result { let comm = { let previous_url = note.object_props.in_reply_to.as_ref()?.as_str()?; let previous_comment = Comment::find_by_ap_url(conn, previous_url); @@ -235,7 +235,7 @@ impl FromId for Comment { Ok(Post::find_by_ap_url(conn, previous_url)?.id) as Result })?, author_id: User::from_id( - c, + conn, ¬e.object_props.attributed_to_link::()?, None, CONFIG.proxy(), @@ -296,7 +296,7 @@ impl FromId for Comment { .collect::>() // remove duplicates (don't do a query more than once) .into_iter() .map(|v| { - if let Ok(user) = User::from_id(c, &v, None, CONFIG.proxy()) { + if let Ok(user) = User::from_id(conn, &v, None, CONFIG.proxy()) { vec![user] } else { vec![] // TODO try to fetch collection @@ -322,41 +322,41 @@ impl FromId for Comment { } } -impl AsObject for Comment { +impl AsObject for Comment { type Error = Error; type Output = Self; - fn activity(self, _c: &PlumeRocket, _actor: User, _id: &str) -> Result { + fn activity(self, _conn: &DbConn, _actor: User, _id: &str) -> Result { // The actual creation takes place in the FromId impl Ok(self) } } -impl AsObject for Comment { +impl AsObject for Comment { type Error = Error; type Output = (); - fn activity(self, c: &PlumeRocket, actor: User, _id: &str) -> Result<()> { + fn activity(self, conn: &DbConn, actor: User, _id: &str) -> Result<()> { if self.author_id != actor.id { return Err(Error::Unauthorized); } - for m in Mention::list_for_comment(&c.conn, self.id)? { - for n in Notification::find_for_mention(&c.conn, &m)? { - n.delete(&c.conn)?; + for m in Mention::list_for_comment(conn, self.id)? { + for n in Notification::find_for_mention(conn, &m)? { + n.delete(conn)?; } - m.delete(&c.conn)?; + m.delete(conn)?; } - for n in Notification::find_for_comment(&c.conn, &self)? { - n.delete(&c.conn)?; + for n in Notification::find_for_comment(&conn, &self)? { + n.delete(&**conn)?; } diesel::update(comments::table) .filter(comments::in_response_to_id.eq(self.id)) .set(comments::in_response_to_id.eq(self.in_response_to_id)) - .execute(&*c.conn)?; - diesel::delete(&self).execute(&*c.conn)?; + .execute(&**conn)?; + diesel::delete(&self).execute(&**conn)?; Ok(()) } } @@ -392,17 +392,16 @@ mod tests { use super::*; use crate::inbox::{inbox, tests::fill_database, InboxResult}; use crate::safe_string::SafeString; - use crate::tests::rockets; + use crate::tests::db; use diesel::Connection; // creates a post, get it's Create activity, delete the post, // "send" the Create to the inbox, and check it works #[test] fn self_federation() { - let r = rockets(); - let conn = &*r.conn; + let conn = &db(); conn.test_transaction::<_, (), _>(|| { - let (posts, users, _) = fill_database(&r); + let (posts, users, _) = fill_database(&conn); let original_comm = Comment::insert( conn, @@ -418,14 +417,14 @@ mod tests { }, ) .unwrap(); - let act = original_comm.create_activity(&r).unwrap(); + let act = original_comm.create_activity(&conn).unwrap(); inbox( - &r, - serde_json::to_value(original_comm.build_delete(conn).unwrap()).unwrap(), + &conn, + serde_json::to_value(original_comm.build_delete(&conn).unwrap()).unwrap(), ) .unwrap(); - match inbox(&r, serde_json::to_value(act).unwrap()).unwrap() { + match inbox(&conn, serde_json::to_value(act).unwrap()).unwrap() { InboxResult::Commented(c) => { // TODO: one is HTML, the other markdown: assert_eq!(c.content, original_comm.content); assert_eq!(c.in_response_to_id, original_comm.in_response_to_id); diff --git a/plume-models/src/follows.rs b/plume-models/src/follows.rs index 676753db..da465d44 100644 --- a/plume-models/src/follows.rs +++ b/plume-models/src/follows.rs @@ -1,6 +1,6 @@ use crate::{ - ap_url, notifications::*, schema::follows, users::User, Connection, Error, PlumeRocket, Result, - CONFIG, + ap_url, db_conn::DbConn, notifications::*, schema::follows, users::User, Connection, Error, + Result, CONFIG, }; use activitypub::activity::{Accept, Follow as FollowAct, Undo}; use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl, SaveChangesDsl}; @@ -141,11 +141,11 @@ impl Follow { } } -impl AsObject for User { +impl AsObject for User { type Error = Error; type Output = Follow; - fn activity(self, c: &PlumeRocket, actor: User, id: &str) -> Result { + fn activity(self, conn: &DbConn, actor: User, id: &str) -> Result { // Mastodon (at least) requires the full Follow object when accepting it, // so we rebuilt it here let mut follow = FollowAct::default(); @@ -153,21 +153,21 @@ impl AsObject for User { follow .follow_props .set_actor_link::(actor.clone().into_id())?; - Follow::accept_follow(&c.conn, &actor, &self, follow, actor.id, self.id) + Follow::accept_follow(conn, &actor, &self, follow, actor.id, self.id) } } -impl FromId for Follow { +impl FromId for Follow { type Error = Error; type Object = FollowAct; - fn from_db(c: &PlumeRocket, id: &str) -> Result { - Follow::find_by_ap_url(&c.conn, id) + fn from_db(conn: &DbConn, id: &str) -> Result { + Follow::find_by_ap_url(conn, id) } - fn from_activity(c: &PlumeRocket, follow: FollowAct) -> Result { + fn from_activity(conn: &DbConn, follow: FollowAct) -> Result { let actor = User::from_id( - c, + conn, &follow.follow_props.actor_link::()?, None, CONFIG.proxy(), @@ -175,28 +175,28 @@ impl FromId for Follow { .map_err(|(_, e)| e)?; let target = User::from_id( - c, + conn, &follow.follow_props.object_link::()?, None, CONFIG.proxy(), ) .map_err(|(_, e)| e)?; - Follow::accept_follow(&c.conn, &actor, &target, follow, actor.id, target.id) + Follow::accept_follow(conn, &actor, &target, follow, actor.id, target.id) } } -impl AsObject for Follow { +impl AsObject for Follow { type Error = Error; type Output = (); - fn activity(self, c: &PlumeRocket, actor: User, _id: &str) -> Result<()> { - let conn = &*c.conn; + fn activity(self, conn: &DbConn, actor: User, _id: &str) -> Result<()> { + let conn = conn; if self.follower_id == actor.id { - diesel::delete(&self).execute(conn)?; + diesel::delete(&self).execute(&**conn)?; // delete associated notification if any - if let Ok(notif) = Notification::find(conn, notification_kind::FOLLOW, self.id) { - diesel::delete(¬if).execute(conn)?; + if let Ok(notif) = Notification::find(&conn, notification_kind::FOLLOW, self.id) { + diesel::delete(¬if).execute(&**conn)?; } Ok(()) diff --git a/plume-models/src/inbox.rs b/plume-models/src/inbox.rs index 751eb550..21af9d88 100644 --- a/plume-models/src/inbox.rs +++ b/plume-models/src/inbox.rs @@ -2,11 +2,12 @@ use activitypub::activity::*; use crate::{ comments::Comment, + db_conn::DbConn, follows, likes, posts::{Post, PostUpdate}, reshares::Reshare, users::User, - Error, PlumeRocket, CONFIG, + Error, CONFIG, }; use plume_common::activity_pub::inbox::Inbox; @@ -45,8 +46,8 @@ impl_into_inbox_result! { Reshare => Reshared } -pub fn inbox(ctx: &PlumeRocket, act: serde_json::Value) -> Result { - Inbox::handle(ctx, act) +pub fn inbox(conn: &DbConn, act: serde_json::Value) -> Result { + Inbox::handle(conn, act) .with::(CONFIG.proxy()) .with::(CONFIG.proxy()) .with::(CONFIG.proxy()) @@ -66,13 +67,13 @@ pub fn inbox(ctx: &PlumeRocket, act: serde_json::Value) -> Result ( Vec, Vec, @@ -81,9 +82,9 @@ pub(crate) mod tests { use crate::post_authors::*; use crate::posts::*; - let (users, blogs) = blog_fill_db(&rockets.conn); + let (users, blogs) = blog_fill_db(&conn); let post = Post::insert( - &rockets.conn, + &conn, NewPost { blog_id: blogs[0].id, slug: "testing".to_owned(), @@ -101,7 +102,7 @@ pub(crate) mod tests { .unwrap(); PostAuthor::insert( - &rockets.conn, + &conn, NewPostAuthor { post_id: post.id, author_id: users[0].id, @@ -114,10 +115,9 @@ pub(crate) mod tests { #[test] fn announce_post() { - let r = rockets(); - let conn = &*r.conn; + let conn = db(); conn.test_transaction::<_, (), _>(|| { - let (posts, users, _) = fill_database(&r); + let (posts, users, _) = fill_database(&conn); let act = json!({ "id": "https://plu.me/announce/1", "actor": users[0].ap_url, @@ -125,7 +125,7 @@ pub(crate) mod tests { "type": "Announce", }); - match super::inbox(&r, act).unwrap() { + match super::inbox(&conn, act).unwrap() { super::InboxResult::Reshared(r) => { assert_eq!(r.post_id, posts[0].id); assert_eq!(r.user_id, users[0].id); @@ -139,10 +139,9 @@ pub(crate) mod tests { #[test] fn create_comment() { - let r = rockets(); - let conn = &*r.conn; + let conn = db(); conn.test_transaction::<_, (), _>(|| { - let (posts, users, _) = fill_database(&r); + let (posts, users, _) = fill_database(&conn); let act = json!({ "id": "https://plu.me/comment/1/activity", "actor": users[0].ap_url, @@ -157,7 +156,7 @@ pub(crate) mod tests { "type": "Create", }); - match super::inbox(&r, act).unwrap() { + match super::inbox(&conn, act).unwrap() { super::InboxResult::Commented(c) => { assert_eq!(c.author_id, users[0].id); assert_eq!(c.post_id, posts[0].id); @@ -173,10 +172,9 @@ pub(crate) mod tests { #[test] fn spoof_comment() { - let r = rockets(); - let conn = &*r.conn; + let conn = db(); conn.test_transaction::<_, (), _>(|| { - let (posts, users, _) = fill_database(&r); + let (posts, users, _) = fill_database(&conn); let act = json!({ "id": "https://plu.me/comment/1/activity", "actor": users[0].ap_url, @@ -192,7 +190,7 @@ pub(crate) mod tests { }); assert!(matches!( - super::inbox(&r, act.clone()), + super::inbox(&conn, act.clone()), Err(super::Error::Inbox( box plume_common::activity_pub::inbox::InboxError::InvalidObject(_), )) @@ -203,10 +201,9 @@ pub(crate) mod tests { #[test] fn spoof_comment_by_object_with_id() { - let r = rockets(); - let conn = &*r.conn; + let conn = db(); conn.test_transaction::<_, (), _>(|| { - let (posts, users, _) = fill_database(&r); + let (posts, users, _) = fill_database(&conn); let act = json!({ "id": "https://plu.me/comment/1/activity", "actor": users[0].ap_url, @@ -224,7 +221,7 @@ pub(crate) mod tests { }); assert!(matches!( - super::inbox(&r, act.clone()), + super::inbox(&conn, act.clone()), Err(super::Error::Inbox( box plume_common::activity_pub::inbox::InboxError::InvalidObject(_), )) @@ -234,10 +231,9 @@ pub(crate) mod tests { } #[test] fn spoof_comment_by_object_without_id() { - let r = rockets(); - let conn = &*r.conn; + let conn = db(); conn.test_transaction::<_, (), _>(|| { - let (posts, users, _) = fill_database(&r); + let (posts, users, _) = fill_database(&conn); let act = json!({ "id": "https://plu.me/comment/1/activity", "actor": users[0].ap_url, @@ -253,7 +249,7 @@ pub(crate) mod tests { }); assert!(matches!( - super::inbox(&r, act.clone()), + super::inbox(&conn, act.clone()), Err(super::Error::Inbox( box plume_common::activity_pub::inbox::InboxError::InvalidObject(_), )) @@ -264,10 +260,9 @@ pub(crate) mod tests { #[test] fn create_post() { - let r = rockets(); - let conn = &*r.conn; + let conn = db(); conn.test_transaction::<_, (), _>(|| { - let (_, users, blogs) = fill_database(&r); + let (_, users, blogs) = fill_database(&conn); let act = json!({ "id": "https://plu.me/comment/1/activity", "actor": users[0].ap_url, @@ -288,9 +283,9 @@ pub(crate) mod tests { "type": "Create", }); - match super::inbox(&r, act).unwrap() { + match super::inbox(&conn, act).unwrap() { super::InboxResult::Post(p) => { - assert!(p.is_author(conn, users[0].id).unwrap()); + assert!(p.is_author(&conn, users[0].id).unwrap()); assert_eq!(p.source, "Hello.".to_owned()); assert_eq!(p.blog_id, blogs[0].id); assert_eq!(p.content, SafeString::new("Hello.")); @@ -305,10 +300,9 @@ pub(crate) mod tests { #[test] fn spoof_post() { - let r = rockets(); - let conn = &*r.conn; + let conn = db(); conn.test_transaction::<_, (), _>(|| { - let (_, users, blogs) = fill_database(&r); + let (_, users, blogs) = fill_database(&conn); let act = json!({ "id": "https://plu.me/comment/1/activity", "actor": users[0].ap_url, @@ -330,7 +324,7 @@ pub(crate) mod tests { }); assert!(matches!( - super::inbox(&r, act.clone()), + super::inbox(&conn, act.clone()), Err(super::Error::Inbox( box plume_common::activity_pub::inbox::InboxError::InvalidObject(_), )) @@ -341,10 +335,9 @@ pub(crate) mod tests { #[test] fn spoof_post_by_object_with_id() { - let r = rockets(); - let conn = &*r.conn; + let conn = db(); conn.test_transaction::<_, (), _>(|| { - let (_, users, blogs) = fill_database(&r); + let (_, users, blogs) = fill_database(&conn); let act = json!({ "id": "https://plu.me/comment/1/activity", "actor": users[0].ap_url, @@ -369,7 +362,7 @@ pub(crate) mod tests { }); assert!(matches!( - super::inbox(&r, act.clone()), + super::inbox(&conn, act.clone()), Err(super::Error::Inbox( box plume_common::activity_pub::inbox::InboxError::InvalidObject(_), )) @@ -380,10 +373,9 @@ pub(crate) mod tests { #[test] fn spoof_post_by_object_without_id() { - let r = rockets(); - let conn = &*r.conn; + let conn = db(); conn.test_transaction::<_, (), _>(|| { - let (_, users, blogs) = fill_database(&r); + let (_, users, blogs) = fill_database(&conn); let act = json!({ "id": "https://plu.me/comment/1/activity", "actor": users[0].ap_url, @@ -405,7 +397,7 @@ pub(crate) mod tests { }); assert!(matches!( - super::inbox(&r, act.clone()), + super::inbox(&conn, act.clone()), Err(super::Error::Inbox( box plume_common::activity_pub::inbox::InboxError::InvalidObject(_), )) @@ -418,12 +410,11 @@ pub(crate) mod tests { fn delete_comment() { use crate::comments::*; - let r = rockets(); - let conn = &*r.conn; + let conn = db(); conn.test_transaction::<_, (), _>(|| { - let (posts, users, _) = fill_database(&r); + let (posts, users, _) = fill_database(&conn); Comment::insert( - conn, + &conn, NewComment { content: SafeString::new("My comment"), in_response_to_id: None, @@ -443,7 +434,7 @@ pub(crate) mod tests { "object": "https://plu.me/comment/1", "type": "Delete", }); - assert!(super::inbox(&r, fail_act).is_err()); + assert!(super::inbox(&conn, fail_act).is_err()); let ok_act = json!({ "id": "https://plu.me/comment/1/delete", @@ -451,17 +442,16 @@ pub(crate) mod tests { "object": "https://plu.me/comment/1", "type": "Delete", }); - assert!(super::inbox(&r, ok_act).is_ok()); + assert!(super::inbox(&conn, ok_act).is_ok()); Ok(()) }) } #[test] fn delete_post() { - let r = rockets(); - let conn = &*r.conn; + let conn = db(); conn.test_transaction::<_, (), _>(|| { - let (posts, users, _) = fill_database(&r); + let (posts, users, _) = fill_database(&conn); let fail_act = json!({ "id": "https://plu.me/comment/1/delete", @@ -469,7 +459,7 @@ pub(crate) mod tests { "object": posts[0].ap_url, "type": "Delete", }); - assert!(super::inbox(&r, fail_act).is_err()); + assert!(super::inbox(&conn, fail_act).is_err()); let ok_act = json!({ "id": "https://plu.me/comment/1/delete", @@ -477,17 +467,16 @@ pub(crate) mod tests { "object": posts[0].ap_url, "type": "Delete", }); - assert!(super::inbox(&r, ok_act).is_ok()); + assert!(super::inbox(&conn, ok_act).is_ok()); Ok(()) }); } #[test] fn delete_user() { - let r = rockets(); - let conn = &*r.conn; + let conn = db(); conn.test_transaction::<_, (), _>(|| { - let (_, users, _) = fill_database(&r); + let (_, users, _) = fill_database(&conn); let fail_act = json!({ "id": "https://plu.me/@/Admin#delete", @@ -495,7 +484,7 @@ pub(crate) mod tests { "object": users[0].ap_url, "type": "Delete", }); - assert!(super::inbox(&r, fail_act).is_err()); + assert!(super::inbox(&conn, fail_act).is_err()); let ok_act = json!({ "id": "https://plu.me/@/Admin#delete", @@ -503,8 +492,8 @@ pub(crate) mod tests { "object": users[0].ap_url, "type": "Delete", }); - assert!(super::inbox(&r, ok_act).is_ok()); - assert!(crate::users::User::get(conn, users[0].id).is_err()); + assert!(super::inbox(&conn, ok_act).is_ok()); + assert!(crate::users::User::get(&conn, users[0].id).is_err()); Ok(()) }); @@ -512,10 +501,9 @@ pub(crate) mod tests { #[test] fn follow() { - let r = rockets(); - let conn = &*r.conn; + let conn = db(); conn.test_transaction::<_, (), _>(|| { - let (_, users, _) = fill_database(&r); + let (_, users, _) = fill_database(&conn); let act = json!({ "id": "https://plu.me/follow/1", @@ -523,7 +511,7 @@ pub(crate) mod tests { "object": users[1].ap_url, "type": "Follow", }); - match super::inbox(&r, act).unwrap() { + match super::inbox(&conn, act).unwrap() { InboxResult::Followed(f) => { assert_eq!(f.follower_id, users[0].id); assert_eq!(f.following_id, users[1].id); @@ -537,10 +525,9 @@ pub(crate) mod tests { #[test] fn like() { - let r = rockets(); - let conn = &*r.conn; + let conn = db(); conn.test_transaction::<_, (), _>(|| { - let (posts, users, _) = fill_database(&r); + let (posts, users, _) = fill_database(&conn); let act = json!({ "id": "https://plu.me/like/1", @@ -548,7 +535,7 @@ pub(crate) mod tests { "object": posts[0].ap_url, "type": "Like", }); - match super::inbox(&r, act).unwrap() { + match super::inbox(&conn, act).unwrap() { InboxResult::Liked(l) => { assert_eq!(l.user_id, users[1].id); assert_eq!(l.post_id, posts[0].id); @@ -564,13 +551,12 @@ pub(crate) mod tests { fn undo_reshare() { use crate::reshares::*; - let r = rockets(); - let conn = &*r.conn; + let conn = db(); conn.test_transaction::<_, (), _>(|| { - let (posts, users, _) = fill_database(&r); + let (posts, users, _) = fill_database(&conn); let announce = Reshare::insert( - conn, + &conn, NewReshare { post_id: posts[0].id, user_id: users[1].id, @@ -585,7 +571,7 @@ pub(crate) mod tests { "object": announce.ap_url, "type": "Undo", }); - assert!(super::inbox(&r, fail_act).is_err()); + assert!(super::inbox(&conn, fail_act).is_err()); let ok_act = json!({ "id": "https://plu.me/undo/1", @@ -593,7 +579,7 @@ pub(crate) mod tests { "object": announce.ap_url, "type": "Undo", }); - assert!(super::inbox(&r, ok_act).is_ok()); + assert!(super::inbox(&conn, ok_act).is_ok()); Ok(()) }); } @@ -602,13 +588,12 @@ pub(crate) mod tests { fn undo_follow() { use crate::follows::*; - let r = rockets(); - let conn = &*r.conn; + let conn = db(); conn.test_transaction::<_, (), _>(|| { - let (_, users, _) = fill_database(&r); + let (_, users, _) = fill_database(&conn); let follow = Follow::insert( - conn, + &conn, NewFollow { follower_id: users[0].id, following_id: users[1].id, @@ -623,7 +608,7 @@ pub(crate) mod tests { "object": follow.ap_url, "type": "Undo", }); - assert!(super::inbox(&r, fail_act).is_err()); + assert!(super::inbox(&conn, fail_act).is_err()); let ok_act = json!({ "id": "https://plu.me/undo/1", @@ -631,7 +616,7 @@ pub(crate) mod tests { "object": follow.ap_url, "type": "Undo", }); - assert!(super::inbox(&r, ok_act).is_ok()); + assert!(super::inbox(&conn, ok_act).is_ok()); Ok(()) }); } @@ -640,13 +625,12 @@ pub(crate) mod tests { fn undo_like() { use crate::likes::*; - let r = rockets(); - let conn = &*r.conn; + let conn = db(); conn.test_transaction::<_, (), _>(|| { - let (posts, users, _) = fill_database(&r); + let (posts, users, _) = fill_database(&conn); let like = Like::insert( - conn, + &conn, NewLike { post_id: posts[0].id, user_id: users[1].id, @@ -661,7 +645,7 @@ pub(crate) mod tests { "object": like.ap_url, "type": "Undo", }); - assert!(super::inbox(&r, fail_act).is_err()); + assert!(super::inbox(&conn, fail_act).is_err()); let ok_act = json!({ "id": "https://plu.me/undo/1", @@ -669,17 +653,16 @@ pub(crate) mod tests { "object": like.ap_url, "type": "Undo", }); - assert!(super::inbox(&r, ok_act).is_ok()); + assert!(super::inbox(&conn, ok_act).is_ok()); Ok(()) }); } #[test] fn update_post() { - let r = rockets(); - let conn = &*r.conn; + let conn = db(); conn.test_transaction::<_, (), _>(|| { - let (posts, users, _) = fill_database(&r); + let (posts, users, _) = fill_database(&conn); let act = json!({ "id": "https://plu.me/update/1", @@ -698,7 +681,7 @@ pub(crate) mod tests { "type": "Update", }); - super::inbox(&r, act).unwrap(); + super::inbox(&conn, act).unwrap(); Ok(()) }); } diff --git a/plume-models/src/lib.rs b/plume-models/src/lib.rs index 5a751877..a3d10755 100755 --- a/plume-models/src/lib.rs +++ b/plume-models/src/lib.rs @@ -21,6 +21,7 @@ use once_cell::sync::Lazy; use plume_common::activity_pub::inbox::InboxError; use posts::PostEvent; use riker::actors::{channel, ActorSystem, ChannelRef, SystemBuilder}; +use users::UserEvent; #[cfg(not(any(feature = "sqlite", feature = "postgres")))] compile_error!("Either feature \"sqlite\" or \"postgres\" must be enabled for this crate."); @@ -40,6 +41,9 @@ pub(crate) static ACTOR_SYS: Lazy = Lazy::new(|| { .expect("Failed to create actor system") }); +pub(crate) static USER_CHAN: Lazy> = + Lazy::new(|| channel("user_events", &*ACTOR_SYS).expect("Failed to create user channel")); + pub(crate) static POST_CHAN: Lazy> = Lazy::new(|| channel("post_events", &*ACTOR_SYS).expect("Failed to create post channel")); @@ -294,12 +298,10 @@ pub fn ap_url(url: &str) -> String { #[cfg(test)] #[macro_use] mod tests { - use crate::{db_conn, migrations::IMPORTED_MIGRATIONS, search, Connection as Conn, CONFIG}; + use crate::{db_conn, migrations::IMPORTED_MIGRATIONS, Connection as Conn, CONFIG}; use diesel::r2d2::ConnectionManager; use plume_common::utils::random_hex; - use scheduled_thread_pool::ScheduledThreadPool; use std::env::temp_dir; - use std::sync::Arc; #[macro_export] macro_rules! part_eq { @@ -329,15 +331,6 @@ mod tests { pool }; } - - pub fn rockets() -> super::PlumeRocket { - super::PlumeRocket { - conn: db_conn::DbConn((*DB_POOL).get().unwrap()), - searcher: Arc::new(search::tests::get_searcher(&CONFIG.search_tokenizers)), - worker: Arc::new(ScheduledThreadPool::new(2)), - user: None, - } - } } pub mod admin; @@ -363,6 +356,7 @@ pub mod password_reset_requests; pub mod plume_rocket; pub mod post_authors; pub mod posts; +pub mod remote_fetch_actor; pub mod reshares; pub mod safe_string; #[allow(unused_imports)] diff --git a/plume-models/src/likes.rs b/plume-models/src/likes.rs index 2a70bb47..78d81b54 100644 --- a/plume-models/src/likes.rs +++ b/plume-models/src/likes.rs @@ -1,6 +1,6 @@ use crate::{ - notifications::*, posts::Post, schema::likes, timeline::*, users::User, Connection, Error, - PlumeRocket, Result, CONFIG, + db_conn::DbConn, notifications::*, posts::Post, schema::likes, timeline::*, users::User, + Connection, Error, Result, CONFIG, }; use activitypub::activity; use chrono::NaiveDateTime; @@ -83,40 +83,40 @@ impl Like { } } -impl AsObject for Post { +impl AsObject for Post { type Error = Error; type Output = Like; - fn activity(self, c: &PlumeRocket, actor: User, id: &str) -> Result { + fn activity(self, conn: &DbConn, actor: User, id: &str) -> Result { let res = Like::insert( - &c.conn, + conn, NewLike { post_id: self.id, user_id: actor.id, ap_url: id.to_string(), }, )?; - res.notify(&c.conn)?; + res.notify(conn)?; - Timeline::add_to_all_timelines(c, &self, Kind::Like(&actor))?; + Timeline::add_to_all_timelines(conn, &self, Kind::Like(&actor))?; Ok(res) } } -impl FromId for Like { +impl FromId for Like { type Error = Error; type Object = activity::Like; - fn from_db(c: &PlumeRocket, id: &str) -> Result { - Like::find_by_ap_url(&c.conn, id) + fn from_db(conn: &DbConn, id: &str) -> Result { + Like::find_by_ap_url(conn, id) } - fn from_activity(c: &PlumeRocket, act: activity::Like) -> Result { + fn from_activity(conn: &DbConn, act: activity::Like) -> Result { let res = Like::insert( - &c.conn, + conn, NewLike { post_id: Post::from_id( - c, + conn, &act.like_props.object_link::()?, None, CONFIG.proxy(), @@ -124,7 +124,7 @@ impl FromId for Like { .map_err(|(_, e)| e)? .id, user_id: User::from_id( - c, + conn, &act.like_props.actor_link::()?, None, CONFIG.proxy(), @@ -134,23 +134,22 @@ impl FromId for Like { ap_url: act.object_props.id_string()?, }, )?; - res.notify(&c.conn)?; + res.notify(conn)?; Ok(res) } } -impl AsObject for Like { +impl AsObject for Like { type Error = Error; type Output = (); - fn activity(self, c: &PlumeRocket, actor: User, _id: &str) -> Result<()> { - let conn = &*c.conn; + fn activity(self, conn: &DbConn, actor: User, _id: &str) -> Result<()> { if actor.id == self.user_id { - diesel::delete(&self).execute(conn)?; + diesel::delete(&self).execute(&**conn)?; // delete associated notification if any - if let Ok(notif) = Notification::find(conn, notification_kind::LIKE, self.id) { - diesel::delete(¬if).execute(conn)?; + if let Ok(notif) = Notification::find(&conn, notification_kind::LIKE, self.id) { + diesel::delete(¬if).execute(&**conn)?; } Ok(()) } else { diff --git a/plume-models/src/medias.rs b/plume-models/src/medias.rs index e8d989f6..f6fa9b85 100644 --- a/plume-models/src/medias.rs +++ b/plume-models/src/medias.rs @@ -1,6 +1,6 @@ use crate::{ - ap_url, instance::Instance, safe_string::SafeString, schema::medias, users::User, Connection, - Error, PlumeRocket, Result, CONFIG, + ap_url, db_conn::DbConn, instance::Instance, safe_string::SafeString, schema::medias, + users::User, Connection, Error, Result, CONFIG, }; use activitypub::object::Image; use askama_escape::escape; @@ -196,8 +196,7 @@ impl Media { } // TODO: merge with save_remote? - pub fn from_activity(c: &PlumeRocket, image: &Image) -> Result { - let conn = &*c.conn; + pub fn from_activity(conn: &DbConn, image: &Image) -> Result { let remote_url = image.object_props.url_string().ok()?; let ext = remote_url .rsplit('.') @@ -232,7 +231,7 @@ impl Media { sensitive: image.object_props.summary_string().is_ok(), content_warning: image.object_props.summary_string().ok(), owner_id: User::from_id( - c, + conn, image .object_props .attributed_to_link_vec::() diff --git a/plume-models/src/mentions.rs b/plume-models/src/mentions.rs index 6eff48a0..28e8e5c9 100644 --- a/plume-models/src/mentions.rs +++ b/plume-models/src/mentions.rs @@ -1,6 +1,6 @@ use crate::{ - comments::Comment, notifications::*, posts::Post, schema::mentions, users::User, Connection, - Error, PlumeRocket, Result, + comments::Comment, db_conn::DbConn, notifications::*, posts::Post, schema::mentions, + users::User, Connection, Error, Result, }; use activitypub::link; use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl}; @@ -52,8 +52,8 @@ impl Mention { } } - pub fn build_activity(c: &PlumeRocket, ment: &str) -> Result { - let user = User::find_by_fqn(c, ment)?; + pub fn build_activity(conn: &DbConn, ment: &str) -> Result { + let user = User::find_by_fqn(conn, ment)?; let mut mention = link::Mention::default(); mention.link_props.set_href_string(user.ap_url)?; mention.link_props.set_name_string(format!("@{}", ment))?; diff --git a/plume-models/src/plume_rocket.rs b/plume-models/src/plume_rocket.rs index 0a2b3cec..e7d40089 100644 --- a/plume-models/src/plume_rocket.rs +++ b/plume-models/src/plume_rocket.rs @@ -2,7 +2,7 @@ pub use self::module::PlumeRocket; #[cfg(not(test))] mod module { - use crate::{db_conn::DbConn, search, users}; + use crate::{search, users}; use rocket::{ request::{self, FlashMessage, FromRequest, Request}, Outcome, State, @@ -12,7 +12,6 @@ mod module { /// Common context needed by most routes and operations on models pub struct PlumeRocket { - pub conn: DbConn, pub intl: rocket_i18n::I18n, pub user: Option, pub searcher: Arc, @@ -24,14 +23,12 @@ mod module { type Error = (); fn from_request(request: &'a Request<'r>) -> request::Outcome { - let conn = request.guard::()?; let intl = request.guard::()?; let user = request.guard::().succeeded(); let worker = request.guard::<'_, State<'_, Arc>>()?; let searcher = request.guard::<'_, State<'_, Arc>>()?; let flash_msg = request.guard::>().succeeded(); Outcome::Success(PlumeRocket { - conn, intl, user, flash_msg: flash_msg.map(|f| (f.name().into(), f.msg().into())), @@ -44,7 +41,7 @@ mod module { #[cfg(test)] mod module { - use crate::{db_conn::DbConn, search, users}; + use crate::{search, users}; use rocket::{ request::{self, FromRequest, Request}, Outcome, State, @@ -54,7 +51,6 @@ mod module { /// Common context needed by most routes and operations on models pub struct PlumeRocket { - pub conn: DbConn, pub user: Option, pub searcher: Arc, pub worker: Arc, @@ -64,12 +60,10 @@ mod module { type Error = (); fn from_request(request: &'a Request<'r>) -> request::Outcome { - let conn = request.guard::()?; let user = request.guard::().succeeded(); let worker = request.guard::<'_, State<'_, Arc>>()?; let searcher = request.guard::<'_, State<'_, Arc>>()?; Outcome::Success(PlumeRocket { - conn, user, worker: worker.clone(), searcher: searcher.clone(), diff --git a/plume-models/src/posts.rs b/plume-models/src/posts.rs index 98483491..8f1df2fe 100644 --- a/plume-models/src/posts.rs +++ b/plume-models/src/posts.rs @@ -1,7 +1,7 @@ use crate::{ - ap_url, blogs::Blog, instance::Instance, medias::Media, mentions::Mention, post_authors::*, - safe_string::SafeString, schema::posts, tags::*, timeline::*, users::User, Connection, Error, - PlumeRocket, PostEvent::*, Result, CONFIG, POST_CHAN, + ap_url, blogs::Blog, db_conn::DbConn, instance::Instance, medias::Media, mentions::Mention, + post_authors::*, safe_string::SafeString, schema::posts, tags::*, timeline::*, users::User, + Connection, Error, PostEvent::*, Result, CONFIG, POST_CHAN, }; use activitypub::{ activity::{Create, Delete, Update}, @@ -606,16 +606,16 @@ impl Post { } } -impl FromId for Post { +impl FromId for Post { type Error = Error; type Object = LicensedArticle; - fn from_db(c: &PlumeRocket, id: &str) -> Result { - Self::find_by_ap_url(&c.conn, id) + fn from_db(conn: &DbConn, id: &str) -> Result { + Self::find_by_ap_url(conn, id) } - fn from_activity(c: &PlumeRocket, article: LicensedArticle) -> Result { - let conn = &*c.conn; + fn from_activity(conn: &DbConn, article: LicensedArticle) -> Result { + let conn = conn; let license = article.custom_props.license_string().unwrap_or_default(); let article = article.object; @@ -625,13 +625,13 @@ impl FromId for Post { .into_iter() .fold((None, vec![]), |(blog, mut authors), link| { let url = link; - match User::from_id(&c, &url, None, CONFIG.proxy()) { + match User::from_id(conn, &url, None, CONFIG.proxy()) { Ok(u) => { authors.push(u); (blog, authors) } Err(_) => ( - blog.or_else(|| Blog::from_id(&c, &url, None, CONFIG.proxy()).ok()), + blog.or_else(|| Blog::from_id(conn, &url, None, CONFIG.proxy()).ok()), authors, ), } @@ -641,7 +641,7 @@ impl FromId for Post { .object_props .icon_object::() .ok() - .and_then(|img| Media::from_activity(&c, &img).ok().map(|m| m.id)); + .and_then(|img| Media::from_activity(conn, &img).ok().map(|m| m.id)); let title = article.object_props.name_string()?; let post = Post::insert( @@ -701,33 +701,33 @@ impl FromId for Post { } } - Timeline::add_to_all_timelines(c, &post, Kind::Original)?; + Timeline::add_to_all_timelines(conn, &post, Kind::Original)?; Ok(post) } } -impl AsObject for Post { +impl AsObject for Post { type Error = Error; type Output = Post; - fn activity(self, _c: &PlumeRocket, _actor: User, _id: &str) -> Result { + fn activity(self, _conn: &DbConn, _actor: User, _id: &str) -> Result { // TODO: check that _actor is actually one of the author? Ok(self) } } -impl AsObject for Post { +impl AsObject for Post { type Error = Error; type Output = (); - fn activity(self, c: &PlumeRocket, actor: User, _id: &str) -> Result<()> { + fn activity(self, conn: &DbConn, actor: User, _id: &str) -> Result<()> { let can_delete = self - .get_authors(&c.conn)? + .get_authors(conn)? .into_iter() .any(|a| actor.id == a.id); if can_delete { - self.delete(&c.conn).map(|_| ()) + self.delete(conn).map(|_| ()) } else { Err(Error::Unauthorized) } @@ -745,16 +745,16 @@ pub struct PostUpdate { pub tags: Option, } -impl FromId for PostUpdate { +impl FromId for PostUpdate { type Error = Error; type Object = LicensedArticle; - fn from_db(_: &PlumeRocket, _: &str) -> Result { + fn from_db(_: &DbConn, _: &str) -> Result { // Always fail because we always want to deserialize the AP object Err(Error::NotFound) } - fn from_activity(c: &PlumeRocket, updated: LicensedArticle) -> Result { + fn from_activity(conn: &DbConn, updated: LicensedArticle) -> Result { Ok(PostUpdate { ap_url: updated.object.object_props.id_string()?, title: updated.object.object_props.name_string().ok(), @@ -765,7 +765,7 @@ impl FromId for PostUpdate { .object_props .icon_object::() .ok() - .and_then(|img| Media::from_activity(&c, &img).ok().map(|m| m.id)), + .and_then(|img| Media::from_activity(conn, &img).ok().map(|m| m.id)), source: updated .object .ap_object_props @@ -778,13 +778,13 @@ impl FromId for PostUpdate { } } -impl AsObject for PostUpdate { +impl AsObject for PostUpdate { type Error = Error; type Output = (); - fn activity(self, c: &PlumeRocket, actor: User, _id: &str) -> Result<()> { - let conn = &*c.conn; - let mut post = Post::from_id(c, &self.ap_url, None, CONFIG.proxy()).map_err(|(_, e)| e)?; + fn activity(self, conn: &DbConn, actor: User, _id: &str) -> Result<()> { + let mut post = + Post::from_id(conn, &self.ap_url, None, CONFIG.proxy()).map_err(|(_, e)| e)?; if !post.is_author(conn, actor.id)? { // TODO: maybe the author was added in the meantime @@ -880,19 +880,18 @@ mod tests { use super::*; use crate::inbox::{inbox, tests::fill_database, InboxResult}; use crate::safe_string::SafeString; - use crate::tests::rockets; + use crate::tests::db; use diesel::Connection; // creates a post, get it's Create activity, delete the post, // "send" the Create to the inbox, and check it works #[test] fn self_federation() { - let r = rockets(); - let conn = &*r.conn; + let conn = &db(); conn.test_transaction::<_, (), _>(|| { - let (_, users, blogs) = fill_database(&r); + let (_, users, blogs) = fill_database(&conn); let post = Post::insert( - conn, + &conn, NewPost { blog_id: blogs[0].id, slug: "yo".into(), @@ -909,19 +908,19 @@ mod tests { ) .unwrap(); PostAuthor::insert( - conn, + &conn, NewPostAuthor { post_id: post.id, author_id: users[0].id, }, ) .unwrap(); - let create = post.create_activity(conn).unwrap(); - post.delete(conn).unwrap(); + let create = post.create_activity(&conn).unwrap(); + post.delete(&conn).unwrap(); - match inbox(&r, serde_json::to_value(create).unwrap()).unwrap() { + match inbox(&conn, serde_json::to_value(create).unwrap()).unwrap() { InboxResult::Post(p) => { - assert!(p.is_author(conn, users[0].id).unwrap()); + assert!(p.is_author(&conn, users[0].id).unwrap()); assert_eq!(p.source, "Hello".to_owned()); assert_eq!(p.blog_id, blogs[0].id); assert_eq!(p.content, SafeString::new("Hello")); diff --git a/plume-models/src/remote_fetch_actor.rs b/plume-models/src/remote_fetch_actor.rs new file mode 100644 index 00000000..da73b077 --- /dev/null +++ b/plume-models/src/remote_fetch_actor.rs @@ -0,0 +1,125 @@ +use crate::{ + db_conn::{DbConn, DbPool}, + follows, + posts::{LicensedArticle, Post}, + users::{User, UserEvent}, + ACTOR_SYS, CONFIG, USER_CHAN, +}; +use activitypub::activity::Create; +use plume_common::activity_pub::inbox::FromId; +use riker::actors::{Actor, ActorFactoryArgs, ActorRefFactory, Context, Sender, Subscribe, Tell}; +use std::sync::Arc; +use tracing::{error, info, warn}; + +pub struct RemoteFetchActor { + conn: DbPool, +} + +impl RemoteFetchActor { + pub fn init(conn: DbPool) { + ACTOR_SYS + .actor_of_args::("remote-fetch", conn) + .expect("Failed to initialize remote fetch actor"); + } +} + +impl Actor for RemoteFetchActor { + type Msg = UserEvent; + + fn pre_start(&mut self, ctx: &Context) { + USER_CHAN.tell( + Subscribe { + actor: Box::new(ctx.myself()), + topic: "*".into(), + }, + None, + ) + } + + fn recv(&mut self, _ctx: &Context, msg: Self::Msg, _sender: Sender) { + use UserEvent::*; + + match msg { + RemoteUserFound(user) => match self.conn.get() { + Ok(conn) => { + let conn = DbConn(conn); + // Don't call these functions in parallel + // for the case database connections limit is too small + fetch_and_cache_articles(&user, &conn); + fetch_and_cache_followers(&user, &conn); + if user.needs_update() { + fetch_and_cache_user(&user, &conn); + } + } + _ => { + error!("Failed to get database connection"); + } + }, + } + } +} + +impl ActorFactoryArgs for RemoteFetchActor { + fn create_args(conn: DbPool) -> Self { + Self { conn } + } +} + +fn fetch_and_cache_articles(user: &Arc, conn: &DbConn) { + let create_acts = user.fetch_outbox::(); + match create_acts { + Ok(create_acts) => { + for create_act in create_acts { + match create_act.create_props.object_object::() { + Ok(article) => { + Post::from_activity(conn, article) + .expect("Article from remote user couldn't be saved"); + info!("Fetched article from remote user"); + } + Err(e) => warn!("Error while fetching articles in background: {:?}", e), + } + } + } + Err(err) => { + error!("Failed to fetch outboxes: {:?}", err); + } + } +} + +fn fetch_and_cache_followers(user: &Arc, conn: &DbConn) { + let follower_ids = user.fetch_followers_ids(); + match follower_ids { + Ok(user_ids) => { + for user_id in user_ids { + let follower = User::from_id(conn, &user_id, None, CONFIG.proxy()); + match follower { + Ok(follower) => { + let inserted = follows::Follow::insert( + conn, + follows::NewFollow { + follower_id: follower.id, + following_id: user.id, + ap_url: String::new(), + }, + ); + if inserted.is_err() { + error!("Couldn't save follower for remote user: {:?}", user_id); + } + } + Err(err) => { + error!("Couldn't fetch follower: {:?}", err); + } + } + } + } + Err(err) => { + error!("Failed to fetch follower: {:?}", err); + } + } +} + +fn fetch_and_cache_user(user: &Arc, conn: &DbConn) { + if user.refetch(conn).is_err() { + error!("Couldn't update user info: {:?}", user); + } +} diff --git a/plume-models/src/reshares.rs b/plume-models/src/reshares.rs index 9d6a053e..19bceddf 100644 --- a/plume-models/src/reshares.rs +++ b/plume-models/src/reshares.rs @@ -1,6 +1,6 @@ use crate::{ - notifications::*, posts::Post, schema::reshares, timeline::*, users::User, Connection, Error, - PlumeRocket, Result, CONFIG, + db_conn::DbConn, notifications::*, posts::Post, schema::reshares, timeline::*, users::User, + Connection, Error, Result, CONFIG, }; use activitypub::activity::{Announce, Undo}; use chrono::NaiveDateTime; @@ -107,12 +107,12 @@ impl Reshare { } } -impl AsObject for Post { +impl AsObject for Post { type Error = Error; type Output = Reshare; - fn activity(self, c: &PlumeRocket, actor: User, id: &str) -> Result { - let conn = &*c.conn; + fn activity(self, conn: &DbConn, actor: User, id: &str) -> Result { + let conn = conn; let reshare = Reshare::insert( conn, NewReshare { @@ -123,25 +123,25 @@ impl AsObject for Post { )?; reshare.notify(conn)?; - Timeline::add_to_all_timelines(c, &self, Kind::Reshare(&actor))?; + Timeline::add_to_all_timelines(conn, &self, Kind::Reshare(&actor))?; Ok(reshare) } } -impl FromId for Reshare { +impl FromId for Reshare { type Error = Error; type Object = Announce; - fn from_db(c: &PlumeRocket, id: &str) -> Result { - Reshare::find_by_ap_url(&c.conn, id) + fn from_db(conn: &DbConn, id: &str) -> Result { + Reshare::find_by_ap_url(conn, id) } - fn from_activity(c: &PlumeRocket, act: Announce) -> Result { + fn from_activity(conn: &DbConn, act: Announce) -> Result { let res = Reshare::insert( - &c.conn, + conn, NewReshare { post_id: Post::from_id( - c, + conn, &act.announce_props.object_link::()?, None, CONFIG.proxy(), @@ -149,7 +149,7 @@ impl FromId for Reshare { .map_err(|(_, e)| e)? .id, user_id: User::from_id( - c, + conn, &act.announce_props.actor_link::()?, None, CONFIG.proxy(), @@ -159,23 +159,22 @@ impl FromId for Reshare { ap_url: act.object_props.id_string()?, }, )?; - res.notify(&c.conn)?; + res.notify(conn)?; Ok(res) } } -impl AsObject for Reshare { +impl AsObject for Reshare { type Error = Error; type Output = (); - fn activity(self, c: &PlumeRocket, actor: User, _id: &str) -> Result<()> { - let conn = &*c.conn; + fn activity(self, conn: &DbConn, actor: User, _id: &str) -> Result<()> { if actor.id == self.user_id { - diesel::delete(&self).execute(conn)?; + diesel::delete(&self).execute(&**conn)?; // delete associated notification if any if let Ok(notif) = Notification::find(&conn, notification_kind::RESHARE, self.id) { - diesel::delete(¬if).execute(conn)?; + diesel::delete(¬if).execute(&**conn)?; } Ok(()) diff --git a/plume-models/src/timeline/mod.rs b/plume-models/src/timeline/mod.rs index ecfb2b52..077568a5 100644 --- a/plume-models/src/timeline/mod.rs +++ b/plume-models/src/timeline/mod.rs @@ -1,8 +1,9 @@ use crate::{ + db_conn::DbConn, lists::List, posts::Post, schema::{posts, timeline, timeline_definition}, - Connection, Error, PlumeRocket, Result, + Connection, Error, Result, }; use diesel::{self, BoolExpressionMethods, ExpressionMethods, QueryDsl, RunQueryDsl}; use std::ops::Deref; @@ -208,14 +209,14 @@ impl Timeline { .map_err(Error::from) } - pub fn add_to_all_timelines(rocket: &PlumeRocket, post: &Post, kind: Kind<'_>) -> Result<()> { + pub fn add_to_all_timelines(conn: &DbConn, post: &Post, kind: Kind<'_>) -> Result<()> { let timelines = timeline_definition::table - .load::(rocket.conn.deref()) + .load::(conn.deref()) .map_err(Error::from)?; for t in timelines { - if t.matches(rocket, post, kind)? { - t.add_post(&rocket.conn, post)?; + if t.matches(conn, post, kind)? { + t.add_post(conn, post)?; } } Ok(()) @@ -231,9 +232,9 @@ impl Timeline { Ok(()) } - pub fn matches(&self, rocket: &PlumeRocket, post: &Post, kind: Kind<'_>) -> Result { + pub fn matches(&self, conn: &DbConn, post: &Post, kind: Kind<'_>) -> Result { let query = TimelineQuery::parse(&self.query)?; - query.matches(rocket, self, post, kind) + query.matches(conn, self, post, kind) } } @@ -248,7 +249,7 @@ mod tests { posts::NewPost, safe_string::SafeString, tags::Tag, - tests::{db, rockets}, + tests::db, users::tests as userTests, }; use diesel::Connection; @@ -257,63 +258,69 @@ mod tests { fn test_timeline() { let conn = &db(); conn.test_transaction::<_, (), _>(|| { - let users = userTests::fill_database(conn); + let users = userTests::fill_database(&conn); let mut tl1_u1 = Timeline::new_for_user( - conn, + &conn, users[0].id, "my timeline".to_owned(), "all".to_owned(), ) .unwrap(); - List::new(conn, "languages I speak", Some(&users[1]), ListType::Prefix).unwrap(); + List::new( + &conn, + "languages I speak", + Some(&users[1]), + ListType::Prefix, + ) + .unwrap(); let tl2_u1 = Timeline::new_for_user( - conn, + &conn, users[0].id, "another timeline".to_owned(), "followed".to_owned(), ) .unwrap(); let tl1_u2 = Timeline::new_for_user( - conn, + &conn, users[1].id, "english posts".to_owned(), "lang in \"languages I speak\"".to_owned(), ) .unwrap(); let tl1_instance = Timeline::new_for_instance( - conn, + &conn, "english posts".to_owned(), "license in [cc]".to_owned(), ) .unwrap(); - assert_eq!(tl1_u1, Timeline::get(conn, tl1_u1.id).unwrap()); + assert_eq!(tl1_u1, Timeline::get(&conn, tl1_u1.id).unwrap()); assert_eq!( tl2_u1, - Timeline::find_for_user_by_name(conn, Some(users[0].id), "another timeline") + Timeline::find_for_user_by_name(&conn, Some(users[0].id), "another timeline") .unwrap() ); assert_eq!( tl1_instance, - Timeline::find_for_user_by_name(conn, None, "english posts").unwrap() + Timeline::find_for_user_by_name(&conn, None, "english posts").unwrap() ); - let tl_u1 = Timeline::list_for_user(conn, Some(users[0].id)).unwrap(); + let tl_u1 = Timeline::list_for_user(&conn, Some(users[0].id)).unwrap(); assert_eq!(3, tl_u1.len()); // it is not 2 because there is a "Your feed" tl created for each user automatically assert!(tl_u1.iter().fold(false, |res, tl| { res || *tl == tl1_u1 })); assert!(tl_u1.iter().fold(false, |res, tl| { res || *tl == tl2_u1 })); - let tl_instance = Timeline::list_for_user(conn, None).unwrap(); + let tl_instance = Timeline::list_for_user(&conn, None).unwrap(); assert_eq!(3, tl_instance.len()); // there are also the local and federated feed by default assert!(tl_instance .iter() .fold(false, |res, tl| { res || *tl == tl1_instance })); tl1_u1.name = "My Super TL".to_owned(); - let new_tl1_u2 = tl1_u2.update(conn).unwrap(); + let new_tl1_u2 = tl1_u2.update(&conn).unwrap(); - let tl_u2 = Timeline::list_for_user(conn, Some(users[1].id)).unwrap(); + let tl_u2 = Timeline::list_for_user(&conn, Some(users[1].id)).unwrap(); assert_eq!(2, tl_u2.len()); // same here assert!(tl_u2 .iter() @@ -327,48 +334,48 @@ mod tests { fn test_timeline_creation_error() { let conn = &db(); conn.test_transaction::<_, (), _>(|| { - let users = userTests::fill_database(conn); + let users = userTests::fill_database(&conn); assert!(Timeline::new_for_user( - conn, + &conn, users[0].id, "my timeline".to_owned(), "invalid keyword".to_owned(), ) .is_err()); assert!(Timeline::new_for_instance( - conn, + &conn, "my timeline".to_owned(), "invalid keyword".to_owned(), ) .is_err()); assert!(Timeline::new_for_user( - conn, + &conn, users[0].id, "my timeline".to_owned(), "author in non_existant_list".to_owned(), ) .is_err()); assert!(Timeline::new_for_instance( - conn, + &conn, "my timeline".to_owned(), "lang in dont-exist".to_owned(), ) .is_err()); - List::new(conn, "friends", Some(&users[0]), ListType::User).unwrap(); - List::new(conn, "idk", None, ListType::Blog).unwrap(); + List::new(&conn, "friends", Some(&users[0]), ListType::User).unwrap(); + List::new(&conn, "idk", None, ListType::Blog).unwrap(); assert!(Timeline::new_for_user( - conn, + &conn, users[0].id, "my timeline".to_owned(), "blog in friends".to_owned(), ) .is_err()); assert!(Timeline::new_for_instance( - conn, + &conn, "my timeline".to_owned(), "not author in idk".to_owned(), ) @@ -380,13 +387,12 @@ mod tests { #[test] fn test_simple_match() { - let r = &rockets(); - let conn = &r.conn; + let conn = &db(); conn.test_transaction::<_, (), _>(|| { - let (users, blogs) = blogTests::fill_database(conn); + let (users, blogs) = blogTests::fill_database(&conn); let gnu_tl = Timeline::new_for_user( - conn, + &conn, users[0].id, "GNU timeline".to_owned(), "license in [AGPL, LGPL, GPL]".to_owned(), @@ -394,7 +400,7 @@ mod tests { .unwrap(); let gnu_post = Post::insert( - conn, + &conn, NewPost { blog_id: blogs[0].id, slug: "slug".to_string(), @@ -410,10 +416,10 @@ mod tests { }, ) .unwrap(); - assert!(gnu_tl.matches(r, &gnu_post, Kind::Original).unwrap()); + assert!(gnu_tl.matches(&conn, &gnu_post, Kind::Original).unwrap()); let non_free_post = Post::insert( - conn, + &conn, NewPost { blog_id: blogs[0].id, slug: "slug2".to_string(), @@ -429,7 +435,9 @@ mod tests { }, ) .unwrap(); - assert!(!gnu_tl.matches(r, &non_free_post, Kind::Original).unwrap()); + assert!(!gnu_tl + .matches(&conn, &non_free_post, Kind::Original) + .unwrap()); Ok(()) }); @@ -437,12 +445,11 @@ mod tests { #[test] fn test_complex_match() { - let r = &rockets(); - let conn = &r.conn; + let conn = &db(); conn.test_transaction::<_, (), _>(|| { - let (users, blogs) = blogTests::fill_database(conn); + let (users, blogs) = blogTests::fill_database(&conn); Follow::insert( - conn, + &conn, NewFollow { follower_id: users[0].id, following_id: users[1].id, @@ -452,11 +459,11 @@ mod tests { .unwrap(); let fav_blogs_list = - List::new(conn, "fav_blogs", Some(&users[0]), ListType::Blog).unwrap(); - fav_blogs_list.add_blogs(conn, &[blogs[0].id]).unwrap(); + List::new(&conn, "fav_blogs", Some(&users[0]), ListType::Blog).unwrap(); + fav_blogs_list.add_blogs(&conn, &[blogs[0].id]).unwrap(); let my_tl = Timeline::new_for_user( - conn, + &conn, users[0].id, "My timeline".to_owned(), "blog in fav_blogs and not has_cover or local and followed exclude likes" @@ -465,7 +472,7 @@ mod tests { .unwrap(); let post = Post::insert( - conn, + &conn, NewPost { blog_id: blogs[0].id, slug: "about-linux".to_string(), @@ -481,10 +488,10 @@ mod tests { }, ) .unwrap(); - assert!(my_tl.matches(r, &post, Kind::Original).unwrap()); // matches because of "blog in fav_blogs" (and there is no cover) + assert!(my_tl.matches(&conn, &post, Kind::Original).unwrap()); // matches because of "blog in fav_blogs" (and there is no cover) let post = Post::insert( - conn, + &conn, NewPost { blog_id: blogs[1].id, slug: "about-linux-2".to_string(), @@ -502,7 +509,7 @@ mod tests { }, ) .unwrap(); - assert!(!my_tl.matches(r, &post, Kind::Like(&users[1])).unwrap()); + assert!(!my_tl.matches(&conn, &post, Kind::Like(&users[1])).unwrap()); Ok(()) }); @@ -510,20 +517,19 @@ mod tests { #[test] fn test_add_to_all_timelines() { - let r = &rockets(); - let conn = &r.conn; + let conn = &db(); conn.test_transaction::<_, (), _>(|| { - let (users, blogs) = blogTests::fill_database(conn); + let (users, blogs) = blogTests::fill_database(&conn); let gnu_tl = Timeline::new_for_user( - conn, + &conn, users[0].id, "GNU timeline".to_owned(), "license in [AGPL, LGPL, GPL]".to_owned(), ) .unwrap(); let non_gnu_tl = Timeline::new_for_user( - conn, + &conn, users[0].id, "Stallman disapproved timeline".to_owned(), "not license in [AGPL, LGPL, GPL]".to_owned(), @@ -531,7 +537,7 @@ mod tests { .unwrap(); let gnu_post = Post::insert( - conn, + &conn, NewPost { blog_id: blogs[0].id, slug: "slug".to_string(), @@ -549,7 +555,7 @@ mod tests { .unwrap(); let non_free_post = Post::insert( - conn, + &conn, NewPost { blog_id: blogs[0].id, slug: "slug2".to_string(), @@ -566,13 +572,13 @@ mod tests { ) .unwrap(); - Timeline::add_to_all_timelines(r, &gnu_post, Kind::Original).unwrap(); - Timeline::add_to_all_timelines(r, &non_free_post, Kind::Original).unwrap(); + Timeline::add_to_all_timelines(&conn, &gnu_post, Kind::Original).unwrap(); + Timeline::add_to_all_timelines(&conn, &non_free_post, Kind::Original).unwrap(); - let res = gnu_tl.get_latest(conn, 2).unwrap(); + let res = gnu_tl.get_latest(&conn, 2).unwrap(); assert_eq!(res.len(), 1); assert_eq!(res[0].id, gnu_post.id); - let res = non_gnu_tl.get_latest(conn, 2).unwrap(); + let res = non_gnu_tl.get_latest(&conn, 2).unwrap(); assert_eq!(res.len(), 1); assert_eq!(res[0].id, non_free_post.id); @@ -582,13 +588,12 @@ mod tests { #[test] fn test_matches_lists_direct() { - let r = &rockets(); - let conn = &r.conn; + let conn = &db(); conn.test_transaction::<_, (), _>(|| { - let (users, blogs) = blogTests::fill_database(conn); + let (users, blogs) = blogTests::fill_database(&conn); let gnu_post = Post::insert( - conn, + &conn, NewPost { blog_id: blogs[0].id, slug: "slug".to_string(), @@ -605,61 +610,63 @@ mod tests { ) .unwrap(); gnu_post - .update_tags(conn, vec![Tag::build_activity("free".to_owned()).unwrap()]) + .update_tags(&conn, vec![Tag::build_activity("free".to_owned()).unwrap()]) .unwrap(); PostAuthor::insert( - conn, + &conn, NewPostAuthor { post_id: gnu_post.id, - author_id: blogs[0].list_authors(conn).unwrap()[0].id, + author_id: blogs[0].list_authors(&conn).unwrap()[0].id, }, ) .unwrap(); let tl = Timeline::new_for_user( - conn, + &conn, users[0].id, "blog timeline".to_owned(), format!("blog in [{}]", blogs[0].fqn), ) .unwrap(); - assert!(tl.matches(r, &gnu_post, Kind::Original).unwrap()); - tl.delete(conn).unwrap(); + assert!(tl.matches(&conn, &gnu_post, Kind::Original).unwrap()); + tl.delete(&conn).unwrap(); let tl = Timeline::new_for_user( - conn, + &conn, users[0].id, "blog timeline".to_owned(), "blog in [no_one@nowhere]".to_owned(), ) .unwrap(); - assert!(!tl.matches(r, &gnu_post, Kind::Original).unwrap()); - tl.delete(conn).unwrap(); + assert!(!tl.matches(&conn, &gnu_post, Kind::Original).unwrap()); + tl.delete(&conn).unwrap(); let tl = Timeline::new_for_user( - conn, + &conn, users[0].id, "author timeline".to_owned(), format!( "author in [{}]", - blogs[0].list_authors(conn).unwrap()[0].fqn + blogs[0].list_authors(&conn).unwrap()[0].fqn ), ) .unwrap(); - assert!(tl.matches(r, &gnu_post, Kind::Original).unwrap()); - tl.delete(conn).unwrap(); + assert!(tl.matches(&conn, &gnu_post, Kind::Original).unwrap()); + tl.delete(&conn).unwrap(); let tl = Timeline::new_for_user( - conn, + &conn, users[0].id, "author timeline".to_owned(), format!("author in [{}]", users[2].fqn), ) .unwrap(); - assert!(!tl.matches(r, &gnu_post, Kind::Original).unwrap()); - assert!(tl.matches(r, &gnu_post, Kind::Reshare(&users[2])).unwrap()); - assert!(!tl.matches(r, &gnu_post, Kind::Like(&users[2])).unwrap()); - tl.delete(conn).unwrap(); + assert!(!tl.matches(&conn, &gnu_post, Kind::Original).unwrap()); + assert!(tl + .matches(&conn, &gnu_post, Kind::Reshare(&users[2])) + .unwrap()); + assert!(!tl.matches(&conn, &gnu_post, Kind::Like(&users[2])).unwrap()); + tl.delete(&conn).unwrap(); let tl = Timeline::new_for_user( - conn, + &conn, users[0].id, "author timeline".to_owned(), format!( @@ -668,48 +675,50 @@ mod tests { ), ) .unwrap(); - assert!(!tl.matches(r, &gnu_post, Kind::Original).unwrap()); - assert!(!tl.matches(r, &gnu_post, Kind::Reshare(&users[2])).unwrap()); - assert!(tl.matches(r, &gnu_post, Kind::Like(&users[2])).unwrap()); - tl.delete(conn).unwrap(); + assert!(!tl.matches(&conn, &gnu_post, Kind::Original).unwrap()); + assert!(!tl + .matches(&conn, &gnu_post, Kind::Reshare(&users[2])) + .unwrap()); + assert!(tl.matches(&conn, &gnu_post, Kind::Like(&users[2])).unwrap()); + tl.delete(&conn).unwrap(); let tl = Timeline::new_for_user( - conn, + &conn, users[0].id, "tag timeline".to_owned(), "tags in [free]".to_owned(), ) .unwrap(); - assert!(tl.matches(r, &gnu_post, Kind::Original).unwrap()); - tl.delete(conn).unwrap(); + assert!(tl.matches(&conn, &gnu_post, Kind::Original).unwrap()); + tl.delete(&conn).unwrap(); let tl = Timeline::new_for_user( - conn, + &conn, users[0].id, "tag timeline".to_owned(), "tags in [private]".to_owned(), ) .unwrap(); - assert!(!tl.matches(r, &gnu_post, Kind::Original).unwrap()); - tl.delete(conn).unwrap(); + assert!(!tl.matches(&conn, &gnu_post, Kind::Original).unwrap()); + tl.delete(&conn).unwrap(); let tl = Timeline::new_for_user( - conn, + &conn, users[0].id, "english timeline".to_owned(), "lang in [en]".to_owned(), ) .unwrap(); - assert!(tl.matches(r, &gnu_post, Kind::Original).unwrap()); - tl.delete(conn).unwrap(); + assert!(tl.matches(&conn, &gnu_post, Kind::Original).unwrap()); + tl.delete(&conn).unwrap(); let tl = Timeline::new_for_user( - conn, + &conn, users[0].id, "franco-italian timeline".to_owned(), "lang in [fr, it]".to_owned(), ) .unwrap(); - assert!(!tl.matches(r, &gnu_post, Kind::Original).unwrap()); - tl.delete(conn).unwrap(); + assert!(!tl.matches(&conn, &gnu_post, Kind::Original).unwrap()); + tl.delete(&conn).unwrap(); Ok(()) }); @@ -719,12 +728,12 @@ mod tests { #[test] fn test_matches_lists_saved() { let r = &rockets(); - let conn = &r.conn; + let conn = &db(); conn.test_transaction::<_, (), _>(|| { - let (users, blogs) = blogTests::fill_database(conn); + let (users, blogs) = blogTests::fill_database(&conn); let gnu_post = Post::insert( - conn, + &conn, NewPost { blog_id: blogs[0].id, slug: "slug".to_string(), @@ -740,8 +749,8 @@ mod tests { }, ) .unwrap(); - gnu_post.update_tags(conn, vec![Tag::build_activity("free".to_owned()).unwrap()]).unwrap(); - PostAuthor::insert(conn, NewPostAuthor {post_id: gnu_post.id, author_id: blogs[0].list_authors(conn).unwrap()[0].id}).unwrap(); + gnu_post.update_tags(&conn, vec![Tag::build_activity("free".to_owned()).unwrap()]).unwrap(); + PostAuthor::insert(&conn, NewPostAuthor {post_id: gnu_post.id, author_id: blogs[0].list_authors(&conn).unwrap()[0].id}).unwrap(); unimplemented!(); @@ -751,13 +760,12 @@ mod tests { #[test] fn test_matches_keyword() { - let r = &rockets(); - let conn = &r.conn; + let conn = &db(); conn.test_transaction::<_, (), _>(|| { - let (users, blogs) = blogTests::fill_database(conn); + let (users, blogs) = blogTests::fill_database(&conn); let gnu_post = Post::insert( - conn, + &conn, NewPost { blog_id: blogs[0].id, slug: "slug".to_string(), @@ -775,61 +783,61 @@ mod tests { .unwrap(); let tl = Timeline::new_for_user( - conn, + &conn, users[0].id, "Linux title".to_owned(), "title contains Linux".to_owned(), ) .unwrap(); - assert!(tl.matches(r, &gnu_post, Kind::Original).unwrap()); - tl.delete(conn).unwrap(); + assert!(tl.matches(&conn, &gnu_post, Kind::Original).unwrap()); + tl.delete(&conn).unwrap(); let tl = Timeline::new_for_user( - conn, + &conn, users[0].id, "Microsoft title".to_owned(), "title contains Microsoft".to_owned(), ) .unwrap(); - assert!(!tl.matches(r, &gnu_post, Kind::Original).unwrap()); - tl.delete(conn).unwrap(); + assert!(!tl.matches(&conn, &gnu_post, Kind::Original).unwrap()); + tl.delete(&conn).unwrap(); let tl = Timeline::new_for_user( - conn, + &conn, users[0].id, "Linux subtitle".to_owned(), "subtitle contains Stallman".to_owned(), ) .unwrap(); - assert!(tl.matches(r, &gnu_post, Kind::Original).unwrap()); - tl.delete(conn).unwrap(); + assert!(tl.matches(&conn, &gnu_post, Kind::Original).unwrap()); + tl.delete(&conn).unwrap(); let tl = Timeline::new_for_user( - conn, + &conn, users[0].id, "Microsoft subtitle".to_owned(), "subtitle contains Nadella".to_owned(), ) .unwrap(); - assert!(!tl.matches(r, &gnu_post, Kind::Original).unwrap()); - tl.delete(conn).unwrap(); + assert!(!tl.matches(&conn, &gnu_post, Kind::Original).unwrap()); + tl.delete(&conn).unwrap(); let tl = Timeline::new_for_user( - conn, + &conn, users[0].id, "Linux content".to_owned(), "content contains Linux".to_owned(), ) .unwrap(); - assert!(tl.matches(r, &gnu_post, Kind::Original).unwrap()); - tl.delete(conn).unwrap(); + assert!(tl.matches(&conn, &gnu_post, Kind::Original).unwrap()); + tl.delete(&conn).unwrap(); let tl = Timeline::new_for_user( - conn, + &conn, users[0].id, "Microsoft content".to_owned(), "subtitle contains Windows".to_owned(), ) .unwrap(); - assert!(!tl.matches(r, &gnu_post, Kind::Original).unwrap()); - tl.delete(conn).unwrap(); + assert!(!tl.matches(&conn, &gnu_post, Kind::Original).unwrap()); + tl.delete(&conn).unwrap(); Ok(()) }); diff --git a/plume-models/src/timeline/query.rs b/plume-models/src/timeline/query.rs index 3bbed7e0..7c54dc4f 100644 --- a/plume-models/src/timeline/query.rs +++ b/plume-models/src/timeline/query.rs @@ -1,11 +1,12 @@ use crate::{ blogs::Blog, + db_conn::DbConn, lists::{self, ListType}, posts::Post, tags::Tag, timeline::Timeline, users::User, - PlumeRocket, Result, + Result, }; use plume_common::activity_pub::inbox::AsActor; use whatlang::{self, Lang}; @@ -160,19 +161,19 @@ enum TQ<'a> { impl<'a> TQ<'a> { fn matches( &self, - rocket: &PlumeRocket, + conn: &DbConn, timeline: &Timeline, post: &Post, kind: Kind<'_>, ) -> Result { match self { TQ::Or(inner) => inner.iter().try_fold(false, |s, e| { - e.matches(rocket, timeline, post, kind).map(|r| s || r) + e.matches(conn, timeline, post, kind).map(|r| s || r) }), TQ::And(inner) => inner.iter().try_fold(true, |s, e| { - e.matches(rocket, timeline, post, kind).map(|r| s && r) + e.matches(conn, timeline, post, kind).map(|r| s && r) }), - TQ::Arg(inner, invert) => Ok(inner.matches(rocket, timeline, post, kind)? ^ invert), + TQ::Arg(inner, invert) => Ok(inner.matches(conn, timeline, post, kind)? ^ invert), } } @@ -205,15 +206,15 @@ enum Arg<'a> { impl<'a> Arg<'a> { pub fn matches( &self, - rocket: &PlumeRocket, + conn: &DbConn, timeline: &Timeline, post: &Post, kind: Kind<'_>, ) -> Result { match self { - Arg::In(t, l) => t.matches(rocket, timeline, post, l, kind), + Arg::In(t, l) => t.matches(conn, timeline, post, l, kind), Arg::Contains(t, v) => t.matches(post, v), - Arg::Boolean(t) => t.matches(rocket, timeline, post, kind), + Arg::Boolean(t) => t.matches(conn, timeline, post, kind), } } } @@ -230,7 +231,7 @@ enum WithList { impl WithList { pub fn matches( &self, - rocket: &PlumeRocket, + conn: &DbConn, timeline: &Timeline, post: &Post, list: &List<'_>, @@ -238,39 +239,34 @@ impl WithList { ) -> Result { match list { List::List(name) => { - let list = - lists::List::find_for_user_by_name(&rocket.conn, timeline.user_id, &name)?; + let list = lists::List::find_for_user_by_name(conn, timeline.user_id, &name)?; match (self, list.kind()) { - (WithList::Blog, ListType::Blog) => { - list.contains_blog(&rocket.conn, post.blog_id) - } + (WithList::Blog, ListType::Blog) => list.contains_blog(conn, post.blog_id), (WithList::Author { boosts, likes }, ListType::User) => match kind { Kind::Original => Ok(list - .list_users(&rocket.conn)? + .list_users(conn)? .iter() - .any(|a| post.is_author(&rocket.conn, a.id).unwrap_or(false))), + .any(|a| post.is_author(conn, a.id).unwrap_or(false))), Kind::Reshare(u) => { if *boosts { - list.contains_user(&rocket.conn, u.id) + list.contains_user(conn, u.id) } else { Ok(false) } } Kind::Like(u) => { if *likes { - list.contains_user(&rocket.conn, u.id) + list.contains_user(conn, u.id) } else { Ok(false) } } }, - (WithList::License, ListType::Word) => { - list.contains_word(&rocket.conn, &post.license) - } + (WithList::License, ListType::Word) => list.contains_word(conn, &post.license), (WithList::Tags, ListType::Word) => { - let tags = Tag::for_post(&rocket.conn, post.id)?; + let tags = Tag::for_post(conn, post.id)?; Ok(list - .list_words(&rocket.conn)? + .list_words(conn)? .iter() .any(|s| tags.iter().any(|t| s == &t.tag))) } @@ -285,7 +281,7 @@ impl WithList { }) .unwrap_or(Lang::Eng) .name(); - list.contains_prefix(&rocket.conn, lang) + list.contains_prefix(conn, lang) } (_, _) => Err(QueryError::RuntimeError(format!( "The list '{}' is of the wrong type for this usage", @@ -297,13 +293,13 @@ impl WithList { List::Array(list) => match self { WithList::Blog => Ok(list .iter() - .filter_map(|b| Blog::find_by_fqn(rocket, b).ok()) + .filter_map(|b| Blog::find_by_fqn(conn, b).ok()) .any(|b| b.id == post.blog_id)), WithList::Author { boosts, likes } => match kind { Kind::Original => Ok(list .iter() - .filter_map(|a| User::find_by_fqn(rocket, a).ok()) - .any(|a| post.is_author(&rocket.conn, a.id).unwrap_or(false))), + .filter_map(|a| User::find_by_fqn(&*conn, a).ok()) + .any(|a| post.is_author(conn, a.id).unwrap_or(false))), Kind::Reshare(u) => { if *boosts { Ok(list.iter().any(|user| &u.fqn == user)) @@ -321,7 +317,7 @@ impl WithList { }, WithList::License => Ok(list.iter().any(|s| s == &post.license)), WithList::Tags => { - let tags = Tag::for_post(&rocket.conn, post.id)?; + let tags = Tag::for_post(conn, post.id)?; Ok(list.iter().any(|s| tags.iter().any(|t| s == &t.tag))) } WithList::Lang => { @@ -371,7 +367,7 @@ enum Bool { impl Bool { pub fn matches( &self, - rocket: &PlumeRocket, + conn: &DbConn, timeline: &Timeline, post: &Post, kind: Kind<'_>, @@ -384,21 +380,19 @@ impl Bool { let user = timeline.user_id.unwrap(); match kind { Kind::Original => post - .get_authors(&rocket.conn)? + .get_authors(conn)? .iter() - .try_fold(false, |s, a| { - a.is_followed_by(&rocket.conn, user).map(|r| s || r) - }), + .try_fold(false, |s, a| a.is_followed_by(conn, user).map(|r| s || r)), Kind::Reshare(u) => { if *boosts { - u.is_followed_by(&rocket.conn, user) + u.is_followed_by(conn, user) } else { Ok(false) } } Kind::Like(u) => { if *likes { - u.is_followed_by(&rocket.conn, user) + u.is_followed_by(conn, user) } else { Ok(false) } @@ -406,7 +400,7 @@ impl Bool { } } Bool::HasCover => Ok(post.cover_id.is_some()), - Bool::Local => Ok(post.get_blog(&rocket.conn)?.is_local() && kind == Kind::Original), + Bool::Local => Ok(post.get_blog(conn)?.is_local() && kind == Kind::Original), Bool::All => Ok(kind == Kind::Original), } } @@ -642,12 +636,12 @@ impl<'a> TimelineQuery<'a> { pub fn matches( &self, - rocket: &PlumeRocket, + conn: &DbConn, timeline: &Timeline, post: &Post, kind: Kind<'_>, ) -> Result { - self.0.matches(rocket, timeline, post, kind) + self.0.matches(conn, timeline, post, kind) } pub fn list_used_lists(&self) -> Vec<(String, ListType)> { diff --git a/plume-models/src/users.rs b/plume-models/src/users.rs index c4729acc..0b7cf3af 100644 --- a/plume-models/src/users.rs +++ b/plume-models/src/users.rs @@ -1,8 +1,8 @@ use crate::{ ap_url, blocklisted_emails::BlocklistedEmail, blogs::Blog, db_conn::DbConn, follows::Follow, instance::*, medias::Media, notifications::Notification, post_authors::PostAuthor, posts::Post, - safe_string::SafeString, schema::users, timeline::Timeline, Connection, Error, PlumeRocket, - Result, CONFIG, ITEMS_PER_PAGE, + safe_string::SafeString, schema::users, timeline::Timeline, Connection, Error, Result, + UserEvent::*, CONFIG, ITEMS_PER_PAGE, USER_CHAN, }; use activitypub::{ activity::Delete, @@ -33,6 +33,7 @@ use reqwest::{ header::{HeaderValue, ACCEPT}, ClientBuilder, }; +use riker::actors::{Publish, Tell}; use rocket::{ outcome::IntoOutcome, request::{self, FromRequest, Request}, @@ -40,6 +41,7 @@ use rocket::{ use std::{ cmp::PartialEq, hash::{Hash, Hasher}, + sync::Arc, }; use url::Url; use webfinger::*; @@ -186,29 +188,29 @@ impl User { users::table .filter(users::instance_id.eq(Instance::get_local()?.id)) .count() - .get_result(conn) + .get_result(&*conn) .map_err(Error::from) } - pub fn find_by_fqn(c: &PlumeRocket, fqn: &str) -> Result { + pub fn find_by_fqn(conn: &DbConn, fqn: &str) -> Result { let from_db = users::table .filter(users::fqn.eq(fqn)) - .first(&*c.conn) + .first(&**conn) .optional()?; if let Some(from_db) = from_db { Ok(from_db) } else { - User::fetch_from_webfinger(c, fqn) + User::fetch_from_webfinger(conn, fqn) } } - fn fetch_from_webfinger(c: &PlumeRocket, acct: &str) -> Result { + fn fetch_from_webfinger(conn: &DbConn, acct: &str) -> Result { let link = resolve(acct.to_owned(), true)? .links .into_iter() .find(|l| l.mime_type == Some(String::from("application/activity+json"))) .ok_or(Error::Webfinger)?; - User::from_id(c, link.href.as_ref()?, None, CONFIG.proxy()).map_err(|(_, e)| e) + User::from_id(conn, link.href.as_ref()?, None, CONFIG.proxy()).map_err(|(_, e)| e) } pub fn fetch_remote_interact_uri(acct: &str) -> Result { @@ -243,8 +245,8 @@ impl User { Ok(json) } - pub fn fetch_from_url(c: &PlumeRocket, url: &str) -> Result { - User::fetch(url).and_then(|json| User::from_activity(c, json)) + pub fn fetch_from_url(conn: &DbConn, url: &str) -> Result { + User::fetch(url).and_then(|json| User::from_activity(conn, json)) } pub fn refetch(&self, conn: &Connection) -> Result<()> { @@ -891,6 +893,21 @@ impl User { self.fqn.clone() } } + + pub fn remote_user_found(&self) { + tracing::trace!("{:?}", self); + self.publish_remote_user_found(); + } + + fn publish_remote_user_found(&self) { + USER_CHAN.tell( + Publish { + msg: RemoteUserFound(Arc::new(self.clone())), + topic: "user.remote_user_found".into(), + }, + None, + ) + } } impl<'a, 'r> FromRequest<'a, 'r> for User { @@ -915,20 +932,20 @@ impl IntoId for User { impl Eq for User {} -impl FromId for User { +impl FromId for User { type Error = Error; type Object = CustomPerson; - fn from_db(c: &PlumeRocket, id: &str) -> Result { - Self::find_by_ap_url(&c.conn, id) + fn from_db(conn: &DbConn, id: &str) -> Result { + Self::find_by_ap_url(conn, id) } - fn from_activity(c: &PlumeRocket, acct: CustomPerson) -> Result { + fn from_activity(conn: &DbConn, acct: CustomPerson) -> Result { let url = Url::parse(&acct.object.object_props.id_string()?)?; let inst = url.host_str()?; - let instance = Instance::find_by_domain(&c.conn, inst).or_else(|_| { + let instance = Instance::find_by_domain(conn, inst).or_else(|_| { Instance::insert( - &c.conn, + conn, NewInstance { name: inst.to_owned(), public_domain: inst.to_owned(), @@ -957,7 +974,7 @@ impl FromId for User { }; let user = User::insert( - &c.conn, + conn, NewUser { display_name: acct .object @@ -1003,10 +1020,10 @@ impl FromId for User { if let Ok(icon) = acct.object.object_props.icon_image() { if let Ok(url) = icon.object_props.url_string() { - let avatar = Media::save_remote(&c.conn, url, &user); + let avatar = Media::save_remote(conn, url, &user); if let Ok(avatar) = avatar { - user.set_avatar(&c.conn, avatar.id)?; + user.set_avatar(conn, avatar.id)?; } } } @@ -1015,7 +1032,7 @@ impl FromId for User { } } -impl AsActor<&PlumeRocket> for User { +impl AsActor<&DbConn> for User { fn get_inbox_url(&self) -> String { self.inbox_url.clone() } @@ -1031,13 +1048,13 @@ impl AsActor<&PlumeRocket> for User { } } -impl AsObject for User { +impl AsObject for User { type Error = Error; type Output = (); - fn activity(self, c: &PlumeRocket, actor: User, _id: &str) -> Result<()> { + fn activity(self, conn: &DbConn, actor: User, _id: &str) -> Result<()> { if self.id == actor.id { - self.delete(&c.conn).map(|_| ()) + self.delete(conn).map(|_| ()) } else { Err(Error::Unauthorized) } @@ -1126,12 +1143,27 @@ impl NewUser { } } +#[derive(Clone, Debug)] +pub enum UserEvent { + RemoteUserFound(Arc), +} + +impl From for Arc { + fn from(event: UserEvent) -> Self { + use UserEvent::*; + + match event { + RemoteUserFound(user) => user, + } + } +} + #[cfg(test)] pub(crate) mod tests { use super::*; use crate::{ instance::{tests as instance_tests, Instance}, - tests::{db, rockets}, + tests::db, Connection as Conn, }; use diesel::Connection; @@ -1173,12 +1205,11 @@ pub(crate) mod tests { #[test] fn find_by() { - let r = rockets(); - let conn = &*r.conn; + let conn = db(); conn.test_transaction::<_, (), _>(|| { - fill_database(conn); + fill_database(&conn); let test_user = NewUser::new_local( - conn, + &conn, "test".to_owned(), "test user".to_owned(), Role::Normal, @@ -1189,22 +1220,22 @@ pub(crate) mod tests { .unwrap(); assert_eq!( test_user.id, - User::find_by_name(conn, "test", Instance::get_local().unwrap().id) + User::find_by_name(&conn, "test", Instance::get_local().unwrap().id) .unwrap() .id ); assert_eq!( test_user.id, - User::find_by_fqn(&r, &test_user.fqn).unwrap().id + User::find_by_fqn(&conn, &test_user.fqn).unwrap().id ); assert_eq!( test_user.id, - User::find_by_email(conn, "test@example.com").unwrap().id + User::find_by_email(&conn, "test@example.com").unwrap().id ); assert_eq!( test_user.id, User::find_by_ap_url( - conn, + &conn, &format!( "https://{}/@/{}/", Instance::get_local().unwrap().public_domain, @@ -1222,11 +1253,11 @@ pub(crate) mod tests { fn delete() { let conn = &db(); conn.test_transaction::<_, (), _>(|| { - let inserted = fill_database(conn); + let inserted = fill_database(&conn); - assert!(User::get(conn, inserted[0].id).is_ok()); - inserted[0].delete(conn).unwrap(); - assert!(User::get(conn, inserted[0].id).is_err()); + assert!(User::get(&conn, inserted[0].id).is_ok()); + inserted[0].delete(&conn).unwrap(); + assert!(User::get(&conn, inserted[0].id).is_err()); Ok(()) }); } @@ -1235,20 +1266,20 @@ pub(crate) mod tests { fn admin() { let conn = &db(); conn.test_transaction::<_, (), _>(|| { - let inserted = fill_database(conn); + let inserted = fill_database(&conn); let local_inst = Instance::get_local().unwrap(); let mut i = 0; - while local_inst.has_admin(conn).unwrap() { + while local_inst.has_admin(&conn).unwrap() { assert!(i < 100); //prevent from looping indefinitelly local_inst - .main_admin(conn) + .main_admin(&conn) .unwrap() - .set_role(conn, Role::Normal) + .set_role(&conn, Role::Normal) .unwrap(); i += 1; } - inserted[0].set_role(conn, Role::Admin).unwrap(); - assert_eq!(inserted[0].id, local_inst.main_admin(conn).unwrap().id); + inserted[0].set_role(&conn, Role::Admin).unwrap(); + assert_eq!(inserted[0].id, local_inst.main_admin(&conn).unwrap().id); Ok(()) }); } @@ -1257,9 +1288,9 @@ pub(crate) mod tests { fn auth() { let conn = &db(); conn.test_transaction::<_, (), _>(|| { - fill_database(conn); + fill_database(&conn); let test_user = NewUser::new_local( - conn, + &conn, "test".to_owned(), "test user".to_owned(), Role::Normal, @@ -1270,10 +1301,10 @@ pub(crate) mod tests { .unwrap(); assert_eq!( - User::login(conn, "test", "test_password").unwrap().id, + User::login(&conn, "test", "test_password").unwrap().id, test_user.id ); - assert!(User::login(conn, "test", "other_password").is_err()); + assert!(User::login(&conn, "test", "other_password").is_err()); Ok(()) }); } @@ -1282,26 +1313,26 @@ pub(crate) mod tests { fn get_local_page() { let conn = &db(); conn.test_transaction::<_, (), _>(|| { - fill_database(conn); + fill_database(&conn); - let page = User::get_local_page(conn, (0, 2)).unwrap(); + let page = User::get_local_page(&conn, (0, 2)).unwrap(); assert_eq!(page.len(), 2); assert!(page[0].username <= page[1].username); - let mut last_username = User::get_local_page(conn, (0, 1)).unwrap()[0] + let mut last_username = User::get_local_page(&conn, (0, 1)).unwrap()[0] .username .clone(); - for i in 1..User::count_local(conn).unwrap() as i32 { - let page = User::get_local_page(conn, (i, i + 1)).unwrap(); + for i in 1..User::count_local(&conn).unwrap() as i32 { + let page = User::get_local_page(&conn, (i, i + 1)).unwrap(); assert_eq!(page.len(), 1); assert!(last_username <= page[0].username); last_username = page[0].username.clone(); } assert_eq!( - User::get_local_page(conn, (0, User::count_local(conn).unwrap() as i32 + 10)) + User::get_local_page(&conn, (0, User::count_local(&conn).unwrap() as i32 + 10)) .unwrap() .len() as i64, - User::count_local(conn).unwrap() + User::count_local(&conn).unwrap() ); Ok(()) }); @@ -1309,14 +1340,13 @@ pub(crate) mod tests { #[test] fn self_federation() { - let r = rockets(); - let conn = &*r.conn; + let conn = db(); conn.test_transaction::<_, (), _>(|| { - let users = fill_database(conn); + let users = fill_database(&conn); - let ap_repr = users[0].to_activity(conn).unwrap(); - users[0].delete(conn).unwrap(); - let user = User::from_activity(&r, ap_repr).unwrap(); + let ap_repr = users[0].to_activity(&conn).unwrap(); + users[0].delete(&conn).unwrap(); + let user = User::from_activity(&conn, ap_repr).unwrap(); assert_eq!(user.username, users[0].username); assert_eq!(user.display_name, users[0].display_name); @@ -1327,7 +1357,7 @@ pub(crate) mod tests { assert_eq!(user.public_key, users[0].public_key); assert_eq!(user.shared_inbox_url, users[0].shared_inbox_url); assert_eq!(user.followers_endpoint, users[0].followers_endpoint); - assert_eq!(user.avatar_url(conn), users[0].avatar_url(conn)); + assert_eq!(user.avatar_url(&conn), users[0].avatar_url(&conn)); assert_eq!(user.fqn, users[0].fqn); assert_eq!(user.summary_html, users[0].summary_html); Ok(()) diff --git a/src/api/mod.rs b/src/api/mod.rs index 439b394d..5bc3bbc7 100755 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -6,7 +6,7 @@ use rocket::{ use rocket_contrib::json::Json; use plume_common::utils::random_hex; -use plume_models::{api_tokens::*, apps::App, users::User, Error, PlumeRocket}; +use plume_models::{api_tokens::*, apps::App, db_conn::DbConn, users::User, Error}; type Api = Result, ApiError>; @@ -54,16 +54,12 @@ pub struct OAuthRequest { } #[get("/oauth2?")] -pub fn oauth( - query: Form, - rockets: PlumeRocket, -) -> Result, ApiError> { - let conn = &*rockets.conn; - let app = App::find_by_client_id(conn, &query.client_id)?; +pub fn oauth(query: Form, conn: DbConn) -> Result, ApiError> { + let app = App::find_by_client_id(&conn, &query.client_id)?; if app.client_secret == query.client_secret { - if let Ok(user) = User::login(conn, &query.username, &query.password) { + if let Ok(user) = User::login(&conn, &query.username, &query.password) { let token = ApiToken::insert( - conn, + &conn, NewApiToken { app_id: app.id, user_id: user.id, diff --git a/src/api/posts.rs b/src/api/posts.rs index fca56108..f29d5640 100644 --- a/src/api/posts.rs +++ b/src/api/posts.rs @@ -102,12 +102,12 @@ pub fn list( pub fn create( auth: Authorization, payload: Json, + conn: DbConn, rockets: PlumeRocket, ) -> Api { - let conn = &*rockets.conn; let worker = &rockets.worker; - let author = User::get(conn, auth.0.user_id)?; + let author = User::get(&conn, auth.0.user_id)?; let slug = &payload.title.clone().to_kebab_case(); let date = payload.creation_date.clone().and_then(|d| { @@ -119,11 +119,11 @@ pub fn create( &payload.source, Some(domain), false, - Some(Media::get_media_processor(conn, vec![&author])), + Some(Media::get_media_processor(&conn, vec![&author])), ); let blog = payload.blog_id.or_else(|| { - let blogs = Blog::find_for_author(conn, &author).ok()?; + let blogs = Blog::find_for_author(&conn, &author).ok()?; if blogs.len() == 1 { Some(blogs[0].id) } else { @@ -131,12 +131,12 @@ pub fn create( } })?; - if Post::find_by_slug(conn, slug, blog).is_ok() { + if Post::find_by_slug(&conn, slug, blog).is_ok() { return Err(Error::InvalidValue.into()); } let post = Post::insert( - conn, + &conn, NewPost { blog_id: blog, slug: slug.to_string(), @@ -157,7 +157,7 @@ pub fn create( )?; PostAuthor::insert( - conn, + &conn, NewPostAuthor { author_id: author.id, post_id: post.id, @@ -167,7 +167,7 @@ pub fn create( if let Some(ref tags) = payload.tags { for tag in tags { Tag::insert( - conn, + &conn, NewTag { tag: tag.to_string(), is_hashtag: false, @@ -178,7 +178,7 @@ pub fn create( } for hashtag in hashtags { Tag::insert( - conn, + &conn, NewTag { tag: hashtag, is_hashtag: true, @@ -190,25 +190,29 @@ pub fn create( if post.published { for m in mentions.into_iter() { Mention::from_activity( - &*conn, - &Mention::build_activity(&rockets, &m)?, + &conn, + &Mention::build_activity(&conn, &m)?, post.id, true, true, )?; } - let act = post.create_activity(&*conn)?; - let dest = User::one_by_instance(&*conn)?; + let act = post.create_activity(&conn)?; + let dest = User::one_by_instance(&conn)?; worker.execute(move || broadcast(&author, act, dest, CONFIG.proxy().cloned())); } - Timeline::add_to_all_timelines(&rockets, &post, Kind::Original)?; + Timeline::add_to_all_timelines(&conn, &post, Kind::Original)?; Ok(Json(PostData { - authors: post.get_authors(conn)?.into_iter().map(|a| a.fqn).collect(), + authors: post + .get_authors(&conn)? + .into_iter() + .map(|a| a.fqn) + .collect(), creation_date: post.creation_date.format("%Y-%m-%d").to_string(), - tags: Tag::for_post(conn, post.id)? + tags: Tag::for_post(&conn, post.id)? .into_iter() .map(|t| t.tag) .collect(), @@ -226,11 +230,11 @@ pub fn create( } #[delete("/posts/")] -pub fn delete(auth: Authorization, rockets: PlumeRocket, id: i32) -> Api<()> { - let author = User::get(&*rockets.conn, auth.0.user_id)?; - if let Ok(post) = Post::get(&*rockets.conn, id) { - if post.is_author(&*rockets.conn, author.id).unwrap_or(false) { - post.delete(&*rockets.conn)?; +pub fn delete(auth: Authorization, conn: DbConn, id: i32) -> Api<()> { + let author = User::get(&conn, auth.0.user_id)?; + if let Ok(post) = Post::get(&conn, id) { + if post.is_author(&conn, author.id).unwrap_or(false) { + post.delete(&conn)?; } } Ok(Json(())) diff --git a/src/inbox.rs b/src/inbox.rs index 77bb98ee..a7a67276 100644 --- a/src/inbox.rs +++ b/src/inbox.rs @@ -4,7 +4,7 @@ use plume_common::activity_pub::{ sign::{verify_http_headers, Signable}, }; use plume_models::{ - headers::Headers, inbox::inbox, instance::Instance, users::User, Error, PlumeRocket, CONFIG, + db_conn::DbConn, headers::Headers, inbox::inbox, instance::Instance, users::User, Error, CONFIG, }; use rocket::{data::*, http::Status, response::status, Outcome::*, Request}; use rocket_contrib::json::*; @@ -13,11 +13,10 @@ use std::io::Read; use tracing::warn; pub fn handle_incoming( - rockets: PlumeRocket, + conn: DbConn, data: SignedJson, headers: Headers<'_>, ) -> Result> { - let conn = &*rockets.conn; let act = data.1.into_inner(); let sig = data.0; @@ -27,13 +26,13 @@ pub fn handle_incoming( .or_else(|| activity["actor"]["id"].as_str()) .ok_or(status::BadRequest(Some("Missing actor id for activity")))?; - let actor = User::from_id(&rockets, actor_id, None, CONFIG.proxy()) + let actor = User::from_id(&conn, actor_id, None, CONFIG.proxy()) .expect("instance::shared_inbox: user error"); if !verify_http_headers(&actor, &headers.0, &sig).is_secure() && !act.clone().verify(&actor) { // maybe we just know an old key? actor - .refetch(conn) - .and_then(|_| User::get(conn, actor.id)) + .refetch(&conn) + .and_then(|_| User::get(&conn, actor.id)) .and_then(|u| { if verify_http_headers(&u, &headers.0, &sig).is_secure() || act.clone().verify(&u) { Ok(()) @@ -50,13 +49,13 @@ pub fn handle_incoming( })?; } - if Instance::is_blocked(conn, actor_id) + if Instance::is_blocked(&conn, actor_id) .map_err(|_| status::BadRequest(Some("Can't tell if instance is blocked")))? { return Ok(String::new()); } - Ok(match inbox(&rockets, act) { + Ok(match inbox(&conn, act) { Ok(_) => String::new(), Err(e) => { warn!("Shared inbox error: {:?}", e); diff --git a/src/main.rs b/src/main.rs index 86e45590..8ca1aa4b 100755 --- a/src/main.rs +++ b/src/main.rs @@ -16,6 +16,7 @@ use plume_models::{ db_conn::{DbPool, PragmaForeignKey}, instance::Instance, migrations::IMPORTED_MIGRATIONS, + remote_fetch_actor::RemoteFetchActor, search::{actor::SearchActor, Searcher as UnmanagedSearcher}, Connection, CONFIG, }; @@ -104,6 +105,7 @@ Then try to restart Plume. &CONFIG.search_index, &CONFIG.search_tokenizers, )); + RemoteFetchActor::init(dbpool.clone()); SearchActor::init(searcher.clone(), dbpool.clone()); let commiter = searcher.clone(); workpool.execute_with_fixed_delay( diff --git a/src/routes/blogs.rs b/src/routes/blogs.rs index e87ce457..294bf79a 100644 --- a/src/routes/blogs.rs +++ b/src/routes/blogs.rs @@ -14,21 +14,25 @@ use crate::template_utils::{IntoContext, Ructe}; use plume_common::activity_pub::{ActivityStream, ApRequest}; use plume_common::utils; use plume_models::{ - blog_authors::*, blogs::*, instance::Instance, medias::*, posts::Post, safe_string::SafeString, - users::User, Connection, PlumeRocket, + blog_authors::*, blogs::*, db_conn::DbConn, instance::Instance, medias::*, posts::Post, + safe_string::SafeString, users::User, Connection, PlumeRocket, }; #[get("/~/?", rank = 2)] -pub fn details(name: String, page: Option, rockets: PlumeRocket) -> Result { +pub fn details( + name: String, + page: Option, + conn: DbConn, + rockets: PlumeRocket, +) -> Result { let page = page.unwrap_or_default(); - let conn = &*rockets.conn; - let blog = Blog::find_by_fqn(&rockets, &name)?; - let posts = Post::blog_page(conn, &blog, page.limits())?; - let articles_count = Post::count_for_blog(conn, &blog)?; - let authors = &blog.list_authors(conn)?; + let blog = Blog::find_by_fqn(&conn, &name)?; + let posts = Post::blog_page(&conn, &blog, page.limits())?; + let articles_count = Post::count_for_blog(&conn, &blog)?; + let authors = &blog.list_authors(&conn)?; Ok(render!(blogs::details( - &rockets.to_context(), + &(&conn, &rockets).to_context(), blog, authors, page.0, @@ -40,17 +44,17 @@ pub fn details(name: String, page: Option, rockets: PlumeRocket) -> Result #[get("/~/", rank = 1)] pub fn activity_details( name: String, - rockets: PlumeRocket, + conn: DbConn, _ap: ApRequest, ) -> Option> { - let blog = Blog::find_by_fqn(&rockets, &name).ok()?; - Some(ActivityStream::new(blog.to_activity(&*rockets.conn).ok()?)) + let blog = Blog::find_by_fqn(&conn, &name).ok()?; + Some(ActivityStream::new(blog.to_activity(&conn).ok()?)) } #[get("/blogs/new")] -pub fn new(rockets: PlumeRocket, _user: User) -> Ructe { +pub fn new(conn: DbConn, rockets: PlumeRocket, _user: User) -> Ructe { render!(blogs::new( - &rockets.to_context(), + &(&conn, &rockets).to_context(), &NewBlogForm::default(), ValidationErrors::default() )) @@ -83,9 +87,12 @@ fn valid_slug(title: &str) -> Result<(), ValidationError> { } #[post("/blogs/new", data = "
")] -pub fn create(form: LenientForm, rockets: PlumeRocket) -> RespondOrRedirect { +pub fn create( + form: LenientForm, + conn: DbConn, + rockets: PlumeRocket, +) -> RespondOrRedirect { let slug = utils::make_actor_id(&form.title); - let conn = &*rockets.conn; let intl = &rockets.intl.catalog; let user = rockets.user.clone().unwrap(); @@ -93,7 +100,7 @@ pub fn create(form: LenientForm, rockets: PlumeRocket) -> RespondOr Ok(_) => ValidationErrors::new(), Err(e) => e, }; - if Blog::find_by_fqn(&rockets, &slug).is_ok() { + if Blog::find_by_fqn(&conn, &slug).is_ok() { errors.add( "title", ValidationError { @@ -108,11 +115,11 @@ pub fn create(form: LenientForm, rockets: PlumeRocket) -> RespondOr } if !errors.is_empty() { - return render!(blogs::new(&rockets.to_context(), &*form, errors)).into(); + return render!(blogs::new(&(&conn, &rockets).to_context(), &*form, errors)).into(); } let blog = Blog::insert( - &*conn, + &conn, NewBlog::new_local( slug.clone(), form.title.to_string(), @@ -126,7 +133,7 @@ pub fn create(form: LenientForm, rockets: PlumeRocket) -> RespondOr .expect("blog::create: error"); BlogAuthor::insert( - &*conn, + &conn, NewBlogAuthor { blog_id: blog.id, author_id: user.id, @@ -143,17 +150,16 @@ pub fn create(form: LenientForm, rockets: PlumeRocket) -> RespondOr } #[post("/~//delete")] -pub fn delete(name: String, rockets: PlumeRocket) -> RespondOrRedirect { - let conn = &*rockets.conn; - let blog = Blog::find_by_fqn(&rockets, &name).expect("blog::delete: blog not found"); +pub fn delete(name: String, conn: DbConn, rockets: PlumeRocket) -> RespondOrRedirect { + let blog = Blog::find_by_fqn(&conn, &name).expect("blog::delete: blog not found"); if rockets .user .clone() - .and_then(|u| u.is_author_in(&*conn, &blog).ok()) + .and_then(|u| u.is_author_in(&conn, &blog).ok()) .unwrap_or(false) { - blog.delete(&conn).expect("blog::expect: deletion error"); + blog.delete(&*conn).expect("blog::expect: deletion error"); Flash::success( Redirect::to(uri!(super::instance::index)), i18n!(rockets.intl.catalog, "Your blog was deleted."), @@ -162,7 +168,7 @@ pub fn delete(name: String, rockets: PlumeRocket) -> RespondOrRedirect { } else { // TODO actually return 403 error code render!(errors::not_authorized( - &rockets.to_context(), + &(&conn, &rockets).to_context(), i18n!( rockets.intl.catalog, "You are not allowed to delete this blog." @@ -183,22 +189,21 @@ pub struct EditForm { } #[get("/~//edit")] -pub fn edit(name: String, rockets: PlumeRocket) -> Result { - let conn = &*rockets.conn; - let blog = Blog::find_by_fqn(&rockets, &name)?; +pub fn edit(name: String, conn: DbConn, rockets: PlumeRocket) -> Result { + let blog = Blog::find_by_fqn(&conn, &name)?; if rockets .user .clone() - .and_then(|u| u.is_author_in(conn, &blog).ok()) + .and_then(|u| u.is_author_in(&conn, &blog).ok()) .unwrap_or(false) { let user = rockets .user .clone() .expect("blogs::edit: User was None while it shouldn't"); - let medias = Media::for_user(conn, user.id).expect("Couldn't list media"); + let medias = Media::for_user(&conn, user.id).expect("Couldn't list media"); Ok(render!(blogs::edit( - &rockets.to_context(), + &(&conn, &rockets).to_context(), &blog, medias, &EditForm { @@ -213,7 +218,7 @@ pub fn edit(name: String, rockets: PlumeRocket) -> Result { } else { // TODO actually return 403 error code Ok(render!(errors::not_authorized( - &rockets.to_context(), + &(&conn, &rockets).to_context(), i18n!( rockets.intl.catalog, "You are not allowed to edit this blog." @@ -235,20 +240,20 @@ fn check_media(conn: &Connection, id: i32, user: &User) -> bool { pub fn update( name: String, form: LenientForm, + conn: DbConn, rockets: PlumeRocket, ) -> RespondOrRedirect { - let conn = &*rockets.conn; let intl = &rockets.intl.catalog; - let mut blog = Blog::find_by_fqn(&rockets, &name).expect("blog::update: blog not found"); + let mut blog = Blog::find_by_fqn(&conn, &name).expect("blog::update: blog not found"); if !rockets .user .clone() - .and_then(|u| u.is_author_in(&*conn, &blog).ok()) + .and_then(|u| u.is_author_in(&conn, &blog).ok()) .unwrap_or(false) { // TODO actually return 403 error code return render!(errors::not_authorized( - &rockets.to_context(), + &(&conn, &rockets).to_context(), i18n!( rockets.intl.catalog, "You are not allowed to edit this blog." @@ -264,7 +269,7 @@ pub fn update( form.validate() .and_then(|_| { if let Some(icon) = form.icon { - if !check_media(&*conn, icon, &user) { + if !check_media(&conn, icon, &user) { let mut errors = ValidationErrors::new(); errors.add( "", @@ -282,7 +287,7 @@ pub fn update( } if let Some(banner) = form.banner { - if !check_media(&*conn, banner, &user) { + if !check_media(&conn, banner, &user) { let mut errors = ValidationErrors::new(); errors.add( "", @@ -327,9 +332,9 @@ pub fn update( )) }) .map_err(|err| { - let medias = Media::for_user(&*conn, user.id).expect("Couldn't list media"); + let medias = Media::for_user(&conn, user.id).expect("Couldn't list media"); render!(blogs::edit( - &rockets.to_context(), + &(&conn, &rockets).to_context(), &blog, medias, &*form, @@ -341,31 +346,30 @@ pub fn update( } #[get("/~//outbox")] -pub fn outbox(name: String, rockets: PlumeRocket) -> Option> { - let blog = Blog::find_by_fqn(&rockets, &name).ok()?; - Some(blog.outbox(&*rockets.conn).ok()?) +pub fn outbox(name: String, conn: DbConn) -> Option> { + let blog = Blog::find_by_fqn(&conn, &name).ok()?; + Some(blog.outbox(&conn).ok()?) } #[allow(unused_variables)] #[get("/~//outbox?")] pub fn outbox_page( name: String, page: Page, - rockets: PlumeRocket, + conn: DbConn, ) -> Option> { - let blog = Blog::find_by_fqn(&rockets, &name).ok()?; - Some(blog.outbox_page(&*rockets.conn, page.limits()).ok()?) + let blog = Blog::find_by_fqn(&conn, &name).ok()?; + Some(blog.outbox_page(&conn, page.limits()).ok()?) } #[get("/~//atom.xml")] -pub fn atom_feed(name: String, rockets: PlumeRocket) -> Option> { - let blog = Blog::find_by_fqn(&rockets, &name).ok()?; - let conn = &*rockets.conn; +pub fn atom_feed(name: String, conn: DbConn) -> Option> { + let blog = Blog::find_by_fqn(&conn, &name).ok()?; let entries = Post::get_recents_for_blog(&*conn, &blog, 15).ok()?; let uri = Instance::get_local() .ok()? .compute_box("~", &name, "atom.xml"); let title = &blog.title; let default_updated = &blog.creation_date; - let feed = super::build_atom_feed(entries, &uri, title, default_updated, conn); + let feed = super::build_atom_feed(entries, &uri, title, default_updated, &conn); Some(Content( ContentType::new("application", "atom+xml"), feed.to_string(), diff --git a/src/routes/comments.rs b/src/routes/comments.rs index 6110bf02..c8618f99 100644 --- a/src/routes/comments.rs +++ b/src/routes/comments.rs @@ -15,8 +15,9 @@ use plume_common::{ utils, }; use plume_models::{ - blogs::Blog, comments::*, inbox::inbox, instance::Instance, medias::Media, mentions::Mention, - posts::Post, safe_string::SafeString, tags::Tag, users::User, Error, PlumeRocket, CONFIG, + blogs::Blog, comments::*, db_conn::DbConn, inbox::inbox, instance::Instance, medias::Media, + mentions::Mention, posts::Post, safe_string::SafeString, tags::Tag, users::User, Error, + PlumeRocket, CONFIG, }; #[derive(Default, FromForm, Debug, Validate)] @@ -33,11 +34,11 @@ pub fn create( slug: String, form: LenientForm, user: User, + conn: DbConn, rockets: PlumeRocket, ) -> Result, Ructe> { - let conn = &*rockets.conn; - let blog = Blog::find_by_fqn(&rockets, &blog_name).expect("comments::create: blog error"); - let post = Post::find_by_slug(&*conn, &slug, blog.id).expect("comments::create: post error"); + let blog = Blog::find_by_fqn(&conn, &blog_name).expect("comments::create: blog error"); + let post = Post::find_by_slug(&conn, &slug, blog.id).expect("comments::create: post error"); form.validate() .map(|_| { let (html, mentions, _hashtags) = utils::md_to_html( @@ -51,7 +52,7 @@ pub fn create( Some(Media::get_media_processor(&conn, vec![&user])), ); let comm = Comment::insert( - &*conn, + &conn, NewComment { content: SafeString::new(html.as_ref()), in_response_to_id: form.responding_to, @@ -65,14 +66,14 @@ pub fn create( ) .expect("comments::create: insert error"); let new_comment = comm - .create_activity(&rockets) + .create_activity(&conn) .expect("comments::create: activity error"); // save mentions for ment in mentions { Mention::from_activity( - &*conn, - &Mention::build_activity(&rockets, &ment) + &conn, + &Mention::build_activity(&conn, &ment) .expect("comments::create: build mention error"), comm.id, false, @@ -81,10 +82,10 @@ pub fn create( .expect("comments::create: mention save error"); } - comm.notify(&*conn).expect("comments::create: notify error"); + comm.notify(&conn).expect("comments::create: notify error"); // federate - let dest = User::one_by_instance(&*conn).expect("comments::create: dest error"); + let dest = User::one_by_instance(&conn).expect("comments::create: dest error"); let user_clone = user.clone(); rockets.worker.execute(move || { broadcast(&user_clone, new_comment, dest, CONFIG.proxy().cloned()) @@ -101,38 +102,36 @@ pub fn create( }) .map_err(|errors| { // TODO: de-duplicate this code - let comments = CommentTree::from_post(&*conn, &post, Some(&user)) + let comments = CommentTree::from_post(&conn, &post, Some(&user)) .expect("comments::create: comments error"); - let previous = form - .responding_to - .and_then(|r| Comment::get(&*conn, r).ok()); + let previous = form.responding_to.and_then(|r| Comment::get(&conn, r).ok()); render!(posts::details( - &rockets.to_context(), + &(&conn, &rockets).to_context(), post.clone(), blog, &*form, errors, - Tag::for_post(&*conn, post.id).expect("comments::create: tags error"), + Tag::for_post(&conn, post.id).expect("comments::create: tags error"), comments, previous, - post.count_likes(&*conn) + post.count_likes(&conn) .expect("comments::create: count likes error"), - post.count_reshares(&*conn) + post.count_reshares(&conn) .expect("comments::create: count reshares error"), - user.has_liked(&*conn, &post) + user.has_liked(&conn, &post) .expect("comments::create: liked error"), - user.has_reshared(&*conn, &post) + user.has_reshared(&conn, &post) .expect("comments::create: reshared error"), user.is_following( &*conn, - post.get_authors(&*conn) + post.get_authors(&conn) .expect("comments::create: authors error")[0] .id ) .expect("comments::create: following error"), - post.get_authors(&*conn) + post.get_authors(&conn) .expect("comments::create: authors error")[0] .clone() )) @@ -145,14 +144,15 @@ pub fn delete( slug: String, id: i32, user: User, + conn: DbConn, rockets: PlumeRocket, ) -> Result, ErrorPage> { - if let Ok(comment) = Comment::get(&*rockets.conn, id) { + if let Ok(comment) = Comment::get(&conn, id) { if comment.author_id == user.id { - let dest = User::one_by_instance(&*rockets.conn)?; - let delete_activity = comment.build_delete(&*rockets.conn)?; + let dest = User::one_by_instance(&conn)?; + let delete_activity = comment.build_delete(&conn)?; inbox( - &rockets, + &conn, serde_json::to_value(&delete_activity).map_err(Error::from)?, )?; @@ -160,7 +160,6 @@ pub fn delete( rockets.worker.execute(move || { broadcast(&user_c, delete_activity, dest, CONFIG.proxy().cloned()) }); - let conn = rockets.conn; rockets .worker .execute_after(Duration::from_secs(10 * 60), move || { @@ -185,10 +184,10 @@ pub fn activity_pub( _slug: String, id: i32, _ap: ApRequest, - rockets: PlumeRocket, + conn: DbConn, ) -> Option> { - Comment::get(&*rockets.conn, id) - .and_then(|c| c.to_activity(&rockets)) + Comment::get(&conn, id) + .and_then(|c| c.to_activity(&conn)) .ok() .map(ActivityStream::new) } diff --git a/src/routes/errors.rs b/src/routes/errors.rs index 7632f347..eb160431 100644 --- a/src/routes/errors.rs +++ b/src/routes/errors.rs @@ -1,5 +1,5 @@ use crate::template_utils::{IntoContext, Ructe}; -use plume_models::{Error, PlumeRocket}; +use plume_models::{db_conn::DbConn, Error, PlumeRocket}; use rocket::{ response::{self, Responder}, Request, @@ -17,40 +17,48 @@ impl From for ErrorPage { impl<'r> Responder<'r> for ErrorPage { fn respond_to(self, req: &Request<'_>) -> response::Result<'r> { + let conn = req.guard::().unwrap(); let rockets = req.guard::().unwrap(); match self.0 { - Error::NotFound => render!(errors::not_found(&rockets.to_context())).respond_to(req), - Error::Unauthorized => { - render!(errors::not_found(&rockets.to_context())).respond_to(req) + Error::NotFound => { + render!(errors::not_found(&(&conn, &rockets).to_context())).respond_to(req) } - _ => render!(errors::not_found(&rockets.to_context())).respond_to(req), + Error::Unauthorized => { + render!(errors::not_found(&(&conn, &rockets).to_context())).respond_to(req) + } + _ => render!(errors::not_found(&(&conn, &rockets).to_context())).respond_to(req), } } } #[catch(404)] pub fn not_found(req: &Request<'_>) -> Ructe { + let conn = req.guard::().unwrap(); let rockets = req.guard::().unwrap(); - render!(errors::not_found(&rockets.to_context())) + render!(errors::not_found(&(&conn, &rockets).to_context())) } #[catch(422)] pub fn unprocessable_entity(req: &Request<'_>) -> Ructe { + let conn = req.guard::().unwrap(); let rockets = req.guard::().unwrap(); - render!(errors::unprocessable_entity(&rockets.to_context())) + render!(errors::unprocessable_entity( + &(&conn, &rockets).to_context() + )) } #[catch(500)] pub fn server_error(req: &Request<'_>) -> Ructe { + let conn = req.guard::().unwrap(); let rockets = req.guard::().unwrap(); - render!(errors::server_error(&rockets.to_context())) + render!(errors::server_error(&(&conn, &rockets).to_context())) } #[post("/csrf-violation?")] -pub fn csrf_violation(target: Option, rockets: PlumeRocket) -> Ructe { +pub fn csrf_violation(target: Option, conn: DbConn, rockets: PlumeRocket) -> Ructe { if let Some(uri) = target { warn!("Csrf violation while accessing \"{}\"", uri) } - render!(errors::csrf(&rockets.to_context())) + render!(errors::csrf(&(&conn, &rockets).to_context())) } diff --git a/src/routes/instance.rs b/src/routes/instance.rs index bec1b092..648c2960 100644 --- a/src/routes/instance.rs +++ b/src/routes/instance.rs @@ -27,8 +27,7 @@ use plume_models::{ }; #[get("/")] -pub fn index(rockets: PlumeRocket) -> Result { - let conn = &*rockets.conn; +pub fn index(conn: DbConn, rockets: PlumeRocket) -> Result { let inst = Instance::get_local()?; let timelines = Timeline::list_all_for_user(&conn, rockets.user.clone().map(|u| u.id))? .into_iter() @@ -42,19 +41,19 @@ pub fn index(rockets: PlumeRocket) -> Result { .collect(); Ok(render!(instance::index( - &rockets.to_context(), + &(&conn, &rockets).to_context(), inst, - User::count_local(conn)?, - Post::count_local(conn)?, + User::count_local(&conn)?, + Post::count_local(&conn)?, timelines ))) } #[get("/admin")] -pub fn admin(_admin: Admin, rockets: PlumeRocket) -> Result { +pub fn admin(_admin: Admin, conn: DbConn, rockets: PlumeRocket) -> Result { let local_inst = Instance::get_local()?; Ok(render!(instance::admin( - &rockets.to_context(), + &(&conn, &rockets).to_context(), local_inst.clone(), InstanceSettingsForm { name: local_inst.name.clone(), @@ -68,8 +67,8 @@ pub fn admin(_admin: Admin, rockets: PlumeRocket) -> Result { } #[get("/admin", rank = 2)] -pub fn admin_mod(_mod: Moderator, rockets: PlumeRocket) -> Ructe { - render!(instance::admin_mod(&rockets.to_context())) +pub fn admin_mod(_mod: Moderator, conn: DbConn, rockets: PlumeRocket) -> Ructe { + render!(instance::admin_mod(&(&conn, &rockets).to_context())) } #[derive(Clone, FromForm, Validate)] @@ -87,14 +86,14 @@ pub struct InstanceSettingsForm { pub fn update_settings( _admin: Admin, form: LenientForm, + conn: DbConn, rockets: PlumeRocket, ) -> RespondOrRedirect { - let conn = &*rockets.conn; if let Err(e) = form.validate() { let local_inst = Instance::get_local().expect("instance::update_settings: local instance error"); render!(instance::admin( - &rockets.to_context(), + &(&conn, &rockets).to_context(), local_inst, form.clone(), e @@ -105,7 +104,7 @@ pub fn update_settings( Instance::get_local().expect("instance::update_settings: local instance error"); instance .update( - conn, + &*conn, form.name.clone(), form.open_registrations, form.short_description.clone(), @@ -125,16 +124,17 @@ pub fn update_settings( pub fn admin_instances( _mod: Moderator, page: Option, + conn: DbConn, rockets: PlumeRocket, ) -> Result { let page = page.unwrap_or_default(); - let instances = Instance::page(&*rockets.conn, page.limits())?; + let instances = Instance::page(&conn, page.limits())?; Ok(render!(instance::list( - &rockets.to_context(), + &(&conn, &rockets).to_context(), Instance::get_local()?, instances, page.0, - Page::total(Instance::count(&*rockets.conn)? as i32) + Page::total(Instance::count(&conn)? as i32) ))) } @@ -145,14 +145,14 @@ pub fn toggle_block( id: i32, intl: I18n, ) -> Result, ErrorPage> { - let inst = Instance::get(&*conn, id)?; + let inst = Instance::get(&conn, id)?; let message = if inst.blocked { i18n!(intl.catalog, "{} has been unblocked."; &inst.name) } else { i18n!(intl.catalog, "{} has been blocked."; &inst.name) }; - inst.toggle_block(&*conn)?; + inst.toggle_block(&conn)?; Ok(Flash::success( Redirect::to(uri!(admin_instances: page = _)), message, @@ -163,14 +163,15 @@ pub fn toggle_block( pub fn admin_users( _mod: Moderator, page: Option, + conn: DbConn, rockets: PlumeRocket, ) -> Result { let page = page.unwrap_or_default(); Ok(render!(instance::users( - &rockets.to_context(), - User::get_local_page(&*rockets.conn, page.limits())?, + &(&conn, &rockets).to_context(), + User::get_local_page(&conn, page.limits())?, page.0, - Page::total(User::count_local(&*rockets.conn)? as i32) + Page::total(User::count_local(&conn)? as i32) ))) } pub struct BlocklistEmailDeletion { @@ -193,9 +194,10 @@ impl<'f> FromForm<'f> for BlocklistEmailDeletion { pub fn delete_email_blocklist( _mod: Moderator, form: Form, + conn: DbConn, rockets: PlumeRocket, ) -> Result, ErrorPage> { - BlocklistedEmail::delete_entries(&*rockets.conn, form.0.ids)?; + BlocklistedEmail::delete_entries(&conn, form.0.ids)?; Ok(Flash::success( Redirect::to(uri!(admin_email_blocklist: page = None)), i18n!(rockets.intl.catalog, "Blocks deleted"), @@ -206,9 +208,10 @@ pub fn delete_email_blocklist( pub fn add_email_blocklist( _mod: Moderator, form: LenientForm, + conn: DbConn, rockets: PlumeRocket, ) -> Result, ErrorPage> { - let result = BlocklistedEmail::insert(&*rockets.conn, form.0); + let result = BlocklistedEmail::insert(&conn, form.0); if let Err(Error::Db(_)) = result { Ok(Flash::error( @@ -226,14 +229,15 @@ pub fn add_email_blocklist( pub fn admin_email_blocklist( _mod: Moderator, page: Option, + conn: DbConn, rockets: PlumeRocket, ) -> Result { let page = page.unwrap_or_default(); Ok(render!(instance::emailblocklist( - &rockets.to_context(), - BlocklistedEmail::page(&*rockets.conn, page.limits())?, + &(&conn, &rockets).to_context(), + BlocklistedEmail::page(&conn, page.limits())?, page.0, - Page::total(BlocklistedEmail::count(&*rockets.conn)? as i32) + Page::total(BlocklistedEmail::count(&conn)? as i32) ))) } @@ -303,6 +307,7 @@ impl FromStr for UserActions { pub fn edit_users( moderator: Moderator, form: LenientForm>, + conn: DbConn, rockets: PlumeRocket, ) -> Result, ErrorPage> { // you can't change your own rights @@ -329,27 +334,26 @@ pub fn edit_users( } } - let conn = &rockets.conn; let worker = &*rockets.worker; match form.action { UserActions::Admin => { for u in form.ids.clone() { - User::get(conn, u)?.set_role(conn, Role::Admin)?; + User::get(&conn, u)?.set_role(&conn, Role::Admin)?; } } UserActions::Moderator => { for u in form.ids.clone() { - User::get(conn, u)?.set_role(conn, Role::Moderator)?; + User::get(&conn, u)?.set_role(&conn, Role::Moderator)?; } } UserActions::RevokeAdmin | UserActions::RevokeModerator => { for u in form.ids.clone() { - User::get(conn, u)?.set_role(conn, Role::Normal)?; + User::get(&conn, u)?.set_role(&conn, Role::Normal)?; } } UserActions::Ban => { for u in form.ids.clone() { - ban(u, conn, worker)?; + ban(u, &conn, worker)?; } } } @@ -387,40 +391,33 @@ fn ban(id: i32, conn: &Connection, worker: &ScheduledThreadPool) -> Result<(), E #[post("/inbox", data = "")] pub fn shared_inbox( - rockets: PlumeRocket, + conn: DbConn, data: inbox::SignedJson, headers: Headers<'_>, ) -> Result> { - inbox::handle_incoming(rockets, data, headers) + inbox::handle_incoming(conn, data, headers) } #[get("/remote_interact?")] -pub fn interact(rockets: PlumeRocket, user: Option, target: String) -> Option { - if User::find_by_fqn(&rockets, &target).is_ok() { +pub fn interact(conn: DbConn, user: Option, target: String) -> Option { + if User::find_by_fqn(&conn, &target).is_ok() { return Some(Redirect::to(uri!(super::user::details: name = target))); } - if let Ok(post) = Post::from_id(&rockets, &target, None, CONFIG.proxy()) { + if let Ok(post) = Post::from_id(&conn, &target, None, CONFIG.proxy()) { return Some(Redirect::to(uri!( - super::posts::details: blog = post - .get_blog(&rockets.conn) - .expect("Can't retrieve blog") - .fqn, + super::posts::details: blog = post.get_blog(&conn).expect("Can't retrieve blog").fqn, slug = &post.slug, responding_to = _ ))); } - if let Ok(comment) = Comment::from_id(&rockets, &target, None, CONFIG.proxy()) { - if comment.can_see(&rockets.conn, user.as_ref()) { - let post = comment - .get_post(&rockets.conn) - .expect("Can't retrieve post"); + if let Ok(comment) = Comment::from_id(&conn, &target, None, CONFIG.proxy()) { + if comment.can_see(&conn, user.as_ref()) { + let post = comment.get_post(&conn).expect("Can't retrieve post"); return Some(Redirect::to(uri!( - super::posts::details: blog = post - .get_blog(&rockets.conn) - .expect("Can't retrieve blog") - .fqn, + super::posts::details: blog = + post.get_blog(&conn).expect("Can't retrieve blog").fqn, slug = &post.slug, responding_to = comment.id ))); @@ -450,10 +447,10 @@ pub fn nodeinfo(conn: DbConn, version: String) -> Result "openRegistrations": local_inst.open_registrations, "usage": { "users": { - "total": User::count_local(&*conn)? + "total": User::count_local(&conn)? }, - "localPosts": Post::count_local(&*conn)?, - "localComments": Comment::count_local(&*conn)? + "localPosts": Post::count_local(&conn)?, + "localComments": Comment::count_local(&conn)? }, "metadata": { "nodeName": local_inst.name, @@ -469,21 +466,20 @@ pub fn nodeinfo(conn: DbConn, version: String) -> Result } #[get("/about")] -pub fn about(rockets: PlumeRocket) -> Result { - let conn = &*rockets.conn; +pub fn about(conn: DbConn, rockets: PlumeRocket) -> Result { Ok(render!(instance::about( - &rockets.to_context(), + &(&conn, &rockets).to_context(), Instance::get_local()?, - Instance::get_local()?.main_admin(conn)?, - User::count_local(conn)?, - Post::count_local(conn)?, - Instance::count(conn)? - 1 + Instance::get_local()?.main_admin(&conn)?, + User::count_local(&conn)?, + Post::count_local(&conn)?, + Instance::count(&conn)? - 1 ))) } #[get("/privacy")] -pub fn privacy(rockets: PlumeRocket) -> Ructe { - render!(instance::privacy(&rockets.to_context())) +pub fn privacy(conn: DbConn, rockets: PlumeRocket) -> Ructe { + render!(instance::privacy(&(&conn, &rockets).to_context())) } #[get("/manifest.json")] diff --git a/src/routes/likes.rs b/src/routes/likes.rs index f62aede4..267bbe45 100644 --- a/src/routes/likes.rs +++ b/src/routes/likes.rs @@ -5,8 +5,8 @@ use crate::routes::errors::ErrorPage; use plume_common::activity_pub::broadcast; use plume_common::utils; use plume_models::{ - blogs::Blog, inbox::inbox, likes, posts::Post, timeline::*, users::User, Error, PlumeRocket, - CONFIG, + blogs::Blog, db_conn::DbConn, inbox::inbox, likes, posts::Post, timeline::*, users::User, + Error, PlumeRocket, CONFIG, }; #[post("/~///like")] @@ -14,17 +14,17 @@ pub fn create( blog: String, slug: String, user: User, + conn: DbConn, rockets: PlumeRocket, ) -> Result { - let conn = &*rockets.conn; - let b = Blog::find_by_fqn(&rockets, &blog)?; - let post = Post::find_by_slug(&*conn, &slug, b.id)?; + let b = Blog::find_by_fqn(&conn, &blog)?; + let post = Post::find_by_slug(&conn, &slug, b.id)?; if !user.has_liked(&*conn, &post)? { let like = likes::Like::insert(&*conn, likes::NewLike::new(&post, &user))?; like.notify(&*conn)?; - Timeline::add_to_all_timelines(&rockets, &post, Kind::Like(&user))?; + Timeline::add_to_all_timelines(&conn, &post, Kind::Like(&user))?; let dest = User::one_by_instance(&*conn)?; let act = like.to_activity(&*conn)?; @@ -32,14 +32,14 @@ pub fn create( .worker .execute(move || broadcast(&user, act, dest, CONFIG.proxy().cloned())); } else { - let like = likes::Like::find_by_user_on_post(&*conn, user.id, post.id)?; - let delete_act = like.build_undo(&*conn)?; + let like = likes::Like::find_by_user_on_post(&conn, user.id, post.id)?; + let delete_act = like.build_undo(&conn)?; inbox( - &rockets, + &conn, serde_json::to_value(&delete_act).map_err(Error::from)?, )?; - let dest = User::one_by_instance(&*conn)?; + let dest = User::one_by_instance(&conn)?; rockets .worker .execute(move || broadcast(&user, delete_act, dest, CONFIG.proxy().cloned())); diff --git a/src/routes/medias.rs b/src/routes/medias.rs index 44bf2ba8..10b25e28 100644 --- a/src/routes/medias.rs +++ b/src/routes/medias.rs @@ -15,20 +15,25 @@ use rocket_i18n::I18n; use std::fs; #[get("/medias?")] -pub fn list(user: User, page: Option, rockets: PlumeRocket) -> Result { +pub fn list( + user: User, + page: Option, + conn: DbConn, + rockets: PlumeRocket, +) -> Result { let page = page.unwrap_or_default(); - let medias = Media::page_for_user(&*rockets.conn, &user, page.limits())?; + let medias = Media::page_for_user(&conn, &user, page.limits())?; Ok(render!(medias::index( - &rockets.to_context(), + &(&conn, &rockets).to_context(), medias, page.0, - Page::total(Media::count_for_user(&*rockets.conn, &user)? as i32) + Page::total(Media::count_for_user(&conn, &user)? as i32) ))) } #[get("/medias/new")] -pub fn new(_user: User, rockets: PlumeRocket) -> Ructe { - render!(medias::new(&rockets.to_context())) +pub fn new(_user: User, conn: DbConn, rockets: PlumeRocket) -> Ructe { + render!(medias::new(&(&conn, &rockets).to_context())) } #[post("/medias/new", data = "")] @@ -95,7 +100,7 @@ pub fn upload( .map(|cw| cw.is_empty()) .unwrap_or(false); let media = Media::insert( - &*conn, + &conn, NewMedia { file_path: dest, alt_text: read(&fields["alt"][0].data)?, @@ -126,10 +131,18 @@ fn read(data: &SavedData) -> Result> { } #[get("/medias/")] -pub fn details(id: i32, user: User, rockets: PlumeRocket) -> Result { - let media = Media::get(&*rockets.conn, id)?; +pub fn details( + id: i32, + user: User, + conn: DbConn, + rockets: PlumeRocket, +) -> Result { + let media = Media::get(&conn, id)?; if media.owner_id == user.id { - Ok(render!(medias::details(&rockets.to_context(), media))) + Ok(render!(medias::details( + &(&conn, &rockets).to_context(), + media + ))) } else { Err(Error::Unauthorized.into()) } diff --git a/src/routes/notifications.rs b/src/routes/notifications.rs index cec540d7..1b987336 100644 --- a/src/routes/notifications.rs +++ b/src/routes/notifications.rs @@ -4,20 +4,21 @@ use rocket_i18n::I18n; use crate::routes::{errors::ErrorPage, Page}; use crate::template_utils::{IntoContext, Ructe}; use plume_common::utils; -use plume_models::{notifications::Notification, users::User, PlumeRocket}; +use plume_models::{db_conn::DbConn, notifications::Notification, users::User, PlumeRocket}; #[get("/notifications?")] pub fn notifications( user: User, page: Option, + conn: DbConn, rockets: PlumeRocket, ) -> Result { let page = page.unwrap_or_default(); Ok(render!(notifications::index( - &rockets.to_context(), - Notification::page_for_user(&*rockets.conn, &user, page.limits())?, + &(&conn, &rockets).to_context(), + Notification::page_for_user(&conn, &user, page.limits())?, page.0, - Page::total(Notification::count_for_user(&*rockets.conn, &user)? as i32) + Page::total(Notification::count_for_user(&conn, &user)? as i32) ))) } diff --git a/src/routes/posts.rs b/src/routes/posts.rs index f76100b1..1aafe578 100644 --- a/src/routes/posts.rs +++ b/src/routes/posts.rs @@ -20,6 +20,7 @@ use plume_common::utils; use plume_models::{ blogs::*, comments::{Comment, CommentTree}, + db_conn::DbConn, inbox::inbox, instance::Instance, medias::Media, @@ -38,42 +39,42 @@ pub fn details( blog: String, slug: String, responding_to: Option, + conn: DbConn, rockets: PlumeRocket, ) -> Result { - let conn = &*rockets.conn; let user = rockets.user.clone(); - let blog = Blog::find_by_fqn(&rockets, &blog)?; - let post = Post::find_by_slug(&*conn, &slug, blog.id)?; + let blog = Blog::find_by_fqn(&conn, &blog)?; + let post = Post::find_by_slug(&conn, &slug, blog.id)?; if !(post.published || post - .get_authors(&*conn)? + .get_authors(&conn)? .into_iter() .any(|a| a.id == user.clone().map(|u| u.id).unwrap_or(0))) { return Ok(render!(errors::not_authorized( - &rockets.to_context(), + &(&conn, &rockets).to_context(), i18n!(rockets.intl.catalog, "This post isn't published yet.") ))); } - let comments = CommentTree::from_post(&*conn, &post, user.as_ref())?; + let comments = CommentTree::from_post(&conn, &post, user.as_ref())?; - let previous = responding_to.and_then(|r| Comment::get(&*conn, r).ok()); + let previous = responding_to.and_then(|r| Comment::get(&conn, r).ok()); Ok(render!(posts::details( - &rockets.to_context(), + &(&conn, &rockets).to_context(), post.clone(), blog, &NewCommentForm { warning: previous.clone().map(|p| p.spoiler_text).unwrap_or_default(), content: previous.clone().and_then(|p| Some(format!( "@{} {}", - p.get_author(&*conn).ok()?.fqn, - Mention::list_for_comment(&*conn, p.id).ok()? + p.get_author(&conn).ok()?.fqn, + Mention::list_for_comment(&conn, p.id).ok()? .into_iter() .filter_map(|m| { let user = user.clone(); - if let Ok(mentioned) = m.get_mentioned(&*conn) { + if let Ok(mentioned) = m.get_mentioned(&conn) { if user.is_none() || mentioned.id != user.expect("posts::details_response: user error while listing mentions").id { Some(format!("@{}", mentioned.fqn)) } else { @@ -87,15 +88,15 @@ pub fn details( ..NewCommentForm::default() }, ValidationErrors::default(), - Tag::for_post(&*conn, post.id)?, + Tag::for_post(&conn, post.id)?, comments, previous, - post.count_likes(&*conn)?, - post.count_reshares(&*conn)?, - user.clone().and_then(|u| u.has_liked(&*conn, &post).ok()).unwrap_or(false), - user.clone().and_then(|u| u.has_reshared(&*conn, &post).ok()).unwrap_or(false), - user.and_then(|u| u.is_following(&*conn, post.get_authors(&*conn).ok()?[0].id).ok()).unwrap_or(false), - post.get_authors(&*conn)?[0].clone() + post.count_likes(&conn)?, + post.count_reshares(&conn)?, + user.clone().and_then(|u| u.has_liked(&conn, &post).ok()).unwrap_or(false), + user.clone().and_then(|u| u.has_reshared(&conn, &post).ok()).unwrap_or(false), + user.and_then(|u| u.is_following(&conn, post.get_authors(&conn).ok()?[0].id).ok()).unwrap_or(false), + post.get_authors(&conn)?[0].clone() ))) } @@ -104,14 +105,13 @@ pub fn activity_details( blog: String, slug: String, _ap: ApRequest, - rockets: PlumeRocket, + conn: DbConn, ) -> Result, Option> { - let conn = &*rockets.conn; - let blog = Blog::find_by_fqn(&rockets, &blog).map_err(|_| None)?; - let post = Post::find_by_slug(&*conn, &slug, blog.id).map_err(|_| None)?; + let blog = Blog::find_by_fqn(&conn, &blog).map_err(|_| None)?; + let post = Post::find_by_slug(&conn, &slug, blog.id).map_err(|_| None)?; if post.published { Ok(ActivityStream::new( - post.to_activity(&*conn) + post.to_activity(&conn) .map_err(|_| String::from("Post serialization error"))?, )) } else { @@ -131,22 +131,26 @@ pub fn new_auth(blog: String, i18n: I18n) -> Flash { } #[get("/~//new", rank = 1)] -pub fn new(blog: String, cl: ContentLen, rockets: PlumeRocket) -> Result { - let conn = &*rockets.conn; - let b = Blog::find_by_fqn(&rockets, &blog)?; +pub fn new( + blog: String, + cl: ContentLen, + conn: DbConn, + rockets: PlumeRocket, +) -> Result { + let b = Blog::find_by_fqn(&conn, &blog)?; let user = rockets.user.clone().unwrap(); - if !user.is_author_in(&*conn, &b)? { + if !user.is_author_in(&conn, &b)? { // TODO actually return 403 error code return Ok(render!(errors::not_authorized( - &rockets.to_context(), + &(&conn, &rockets).to_context(), i18n!(rockets.intl.catalog, "You are not an author of this blog.") ))); } - let medias = Media::for_user(&*conn, user.id)?; + let medias = Media::for_user(&conn, user.id)?; Ok(render!(posts::new( - &rockets.to_context(), + &(&conn, &rockets).to_context(), i18n!(rockets.intl.catalog, "New post"), b, false, @@ -167,17 +171,17 @@ pub fn edit( blog: String, slug: String, cl: ContentLen, + conn: DbConn, rockets: PlumeRocket, ) -> Result { - let conn = &*rockets.conn; let intl = &rockets.intl.catalog; - let b = Blog::find_by_fqn(&rockets, &blog)?; - let post = Post::find_by_slug(&*conn, &slug, b.id)?; + let b = Blog::find_by_fqn(&conn, &blog)?; + let post = Post::find_by_slug(&conn, &slug, b.id)?; let user = rockets.user.clone().unwrap(); - if !user.is_author_in(&*conn, &b)? { + if !user.is_author_in(&conn, &b)? { return Ok(render!(errors::not_authorized( - &rockets.to_context(), + &(&conn, &rockets).to_context(), i18n!(intl, "You are not an author of this blog.") ))); } @@ -188,10 +192,10 @@ pub fn edit( post.content.get().clone() // fallback to HTML if the markdown was not stored }; - let medias = Media::for_user(&*conn, user.id)?; + let medias = Media::for_user(&conn, user.id)?; let title = post.title.clone(); Ok(render!(posts::new( - &rockets.to_context(), + &(&conn, &rockets).to_context(), i18n!(intl, "Edit {0}"; &title), b, true, @@ -199,7 +203,7 @@ pub fn edit( title: post.title.clone(), subtitle: post.subtitle.clone(), content: source, - tags: Tag::for_post(&*conn, post.id)? + tags: Tag::for_post(&conn, post.id)? .into_iter() .filter_map(|t| if !t.is_hashtag { Some(t.tag) } else { None }) .collect::>() @@ -222,12 +226,12 @@ pub fn update( slug: String, cl: ContentLen, form: LenientForm, + conn: DbConn, rockets: PlumeRocket, ) -> RespondOrRedirect { - let conn = &*rockets.conn; - let b = Blog::find_by_fqn(&rockets, &blog).expect("post::update: blog error"); + let b = Blog::find_by_fqn(&conn, &blog).expect("post::update: blog error"); let mut post = - Post::find_by_slug(&*conn, &slug, b.id).expect("post::update: find by slug error"); + Post::find_by_slug(&conn, &slug, b.id).expect("post::update: find by slug error"); let user = rockets.user.clone().unwrap(); let intl = &rockets.intl.catalog; @@ -242,7 +246,7 @@ pub fn update( Err(e) => e, }; - if new_slug != slug && Post::find_by_slug(&*conn, &new_slug, b.id).is_ok() { + if new_slug != slug && Post::find_by_slug(&conn, &new_slug, b.id).is_ok() { errors.add( "title", ValidationError { @@ -255,7 +259,7 @@ pub fn update( if errors.is_empty() { if !user - .is_author_in(&*conn, &b) + .is_author_in(&conn, &b) .expect("posts::update: is author in error") { // actually it's not "Ok"… @@ -298,14 +302,14 @@ pub fn update( post.source = form.content.clone(); post.license = form.license.clone(); post.cover_id = form.cover; - post.update(&*conn).expect("post::update: update error"); + post.update(&conn).expect("post::update: update error"); if post.published { post.update_mentions( &conn, mentions .into_iter() - .filter_map(|m| Mention::build_activity(&rockets, &m).ok()) + .filter_map(|m| Mention::build_activity(&conn, &m).ok()) .collect(), ) .expect("post::update: mentions error"); @@ -337,17 +341,17 @@ pub fn update( let act = post .create_activity(&conn) .expect("post::update: act error"); - let dest = User::one_by_instance(&*conn).expect("post::update: dest error"); + let dest = User::one_by_instance(&conn).expect("post::update: dest error"); rockets .worker .execute(move || broadcast(&user, act, dest, CONFIG.proxy().cloned())); - Timeline::add_to_all_timelines(&rockets, &post, Kind::Original).ok(); + Timeline::add_to_all_timelines(&conn, &post, Kind::Original).ok(); } else { let act = post - .update_activity(&*conn) + .update_activity(&conn) .expect("post::update: act error"); - let dest = User::one_by_instance(&*conn).expect("posts::update: dest error"); + let dest = User::one_by_instance(&conn).expect("posts::update: dest error"); rockets .worker .execute(move || broadcast(&user, act, dest, CONFIG.proxy().cloned())); @@ -365,9 +369,9 @@ pub fn update( .into() } } else { - let medias = Media::for_user(&*conn, user.id).expect("posts:update: medias error"); + let medias = Media::for_user(&conn, user.id).expect("posts:update: medias error"); render!(posts::new( - &rockets.to_context(), + &(&conn, &rockets).to_context(), i18n!(intl, "Edit {0}"; &form.title), b, true, @@ -410,10 +414,10 @@ pub fn create( blog_name: String, form: LenientForm, cl: ContentLen, + conn: DbConn, rockets: PlumeRocket, ) -> Result { - let conn = &*rockets.conn; - let blog = Blog::find_by_fqn(&rockets, &blog_name).expect("post::create: blog error"); + let blog = Blog::find_by_fqn(&conn, &blog_name).expect("post::create: blog error"); let slug = form.title.to_string().to_kebab_case(); let user = rockets.user.clone().unwrap(); @@ -421,7 +425,7 @@ pub fn create( Ok(_) => ValidationErrors::new(), Err(e) => e, }; - if Post::find_by_slug(&*conn, &slug, blog.id).is_ok() { + if Post::find_by_slug(&conn, &slug, blog.id).is_ok() { errors.add( "title", ValidationError { @@ -434,7 +438,7 @@ pub fn create( if errors.is_empty() { if !user - .is_author_in(&*conn, &blog) + .is_author_in(&conn, &blog) .expect("post::create: is author in error") { // actually it's not "Ok"… @@ -466,7 +470,7 @@ pub fn create( ); let post = Post::insert( - &*conn, + &conn, NewPost { blog_id: blog.id, slug: slug.to_string(), @@ -484,7 +488,7 @@ pub fn create( .expect("post::create: post save error"); PostAuthor::insert( - &*conn, + &conn, NewPostAuthor { post_id: post.id, author_id: user.id, @@ -500,7 +504,7 @@ pub fn create( .collect::>(); for tag in tags { Tag::insert( - &*conn, + &conn, NewTag { tag: tag.to_string(), is_hashtag: false, @@ -511,7 +515,7 @@ pub fn create( } for hashtag in hashtags { Tag::insert( - &*conn, + &conn, NewTag { tag: hashtag, is_hashtag: true, @@ -524,9 +528,8 @@ pub fn create( if post.published { for m in mentions { Mention::from_activity( - &*conn, - &Mention::build_activity(&rockets, &m) - .expect("post::create: mention build error"), + &conn, + &Mention::build_activity(&conn, &m).expect("post::create: mention build error"), post.id, true, true, @@ -535,13 +538,13 @@ pub fn create( } let act = post - .create_activity(&*conn) + .create_activity(&conn) .expect("posts::create: activity error"); - let dest = User::one_by_instance(&*conn).expect("posts::create: dest error"); + let dest = User::one_by_instance(&conn).expect("posts::create: dest error"); let worker = &rockets.worker; worker.execute(move || broadcast(&user, act, dest, CONFIG.proxy().cloned())); - Timeline::add_to_all_timelines(&rockets, &post, Kind::Original)?; + Timeline::add_to_all_timelines(&conn, &post, Kind::Original)?; } Ok(Flash::success( @@ -554,9 +557,9 @@ pub fn create( ) .into()) } else { - let medias = Media::for_user(&*conn, user.id).expect("posts::create: medias error"); + let medias = Media::for_user(&conn, user.id).expect("posts::create: medias error"); Ok(render!(posts::new( - &rockets.to_context(), + &(&conn, &rockets).to_context(), i18n!(rockets.intl.catalog, "New article"), blog, false, @@ -575,16 +578,17 @@ pub fn create( pub fn delete( blog_name: String, slug: String, + conn: DbConn, rockets: PlumeRocket, intl: I18n, ) -> Result, ErrorPage> { let user = rockets.user.clone().unwrap(); - let post = Blog::find_by_fqn(&rockets, &blog_name) - .and_then(|blog| Post::find_by_slug(&*rockets.conn, &slug, blog.id)); + let post = Blog::find_by_fqn(&conn, &blog_name) + .and_then(|blog| Post::find_by_slug(&conn, &slug, blog.id)); if let Ok(post) = post { if !post - .get_authors(&*rockets.conn)? + .get_authors(&conn)? .into_iter() .any(|a| a.id == user.id) { @@ -598,10 +602,10 @@ pub fn delete( )); } - let dest = User::one_by_instance(&*rockets.conn)?; - let delete_activity = post.build_delete(&*rockets.conn)?; + let dest = User::one_by_instance(&conn)?; + let delete_activity = post.build_delete(&conn)?; inbox( - &rockets, + &conn, serde_json::to_value(&delete_activity).map_err(Error::from)?, )?; @@ -609,11 +613,10 @@ pub fn delete( rockets .worker .execute(move || broadcast(&user_c, delete_activity, dest, CONFIG.proxy().cloned())); - let conn = rockets.conn; rockets .worker .execute_after(Duration::from_secs(10 * 60), move || { - user.rotate_keypair(&*conn) + user.rotate_keypair(&conn) .expect("Failed to rotate keypair"); }); @@ -630,14 +633,15 @@ pub fn delete( #[get("/~///remote_interact")] pub fn remote_interact( + conn: DbConn, rockets: PlumeRocket, blog_name: String, slug: String, ) -> Result { - let target = Blog::find_by_fqn(&rockets, &blog_name) - .and_then(|blog| Post::find_by_slug(&rockets.conn, &slug, blog.id))?; + let target = Blog::find_by_fqn(&conn, &blog_name) + .and_then(|blog| Post::find_by_slug(&conn, &slug, blog.id))?; Ok(render!(posts::remote_interact( - &rockets.to_context(), + &(&conn, &rockets).to_context(), target, super::session::LoginForm::default(), ValidationErrors::default(), @@ -648,13 +652,14 @@ pub fn remote_interact( #[post("/~///remote_interact", data = "")] pub fn remote_interact_post( + conn: DbConn, rockets: PlumeRocket, blog_name: String, slug: String, remote: LenientForm, ) -> Result { - let target = Blog::find_by_fqn(&rockets, &blog_name) - .and_then(|blog| Post::find_by_slug(&rockets.conn, &slug, blog.id))?; + let target = Blog::find_by_fqn(&conn, &blog_name) + .and_then(|blog| Post::find_by_slug(&conn, &slug, blog.id))?; if let Some(uri) = User::fetch_remote_interact_uri(&remote.remote) .ok() .map(|uri| uri.replace("{uri}", &Uri::percent_encode(&target.ap_url))) @@ -669,7 +674,7 @@ pub fn remote_interact_post( }); //could not get your remote url? Ok(render!(posts::remote_interact( - &rockets.to_context(), + &(&conn, &rockets).to_context(), target, super::session::LoginForm::default(), ValidationErrors::default(), diff --git a/src/routes/reshares.rs b/src/routes/reshares.rs index 0f334268..67b2b84f 100644 --- a/src/routes/reshares.rs +++ b/src/routes/reshares.rs @@ -5,8 +5,8 @@ use crate::routes::errors::ErrorPage; use plume_common::activity_pub::broadcast; use plume_common::utils; use plume_models::{ - blogs::Blog, inbox::inbox, posts::Post, reshares::*, timeline::*, users::User, Error, - PlumeRocket, CONFIG, + blogs::Blog, db_conn::DbConn, inbox::inbox, posts::Post, reshares::*, timeline::*, users::User, + Error, PlumeRocket, CONFIG, }; #[post("/~///reshare")] @@ -14,32 +14,32 @@ pub fn create( blog: String, slug: String, user: User, + conn: DbConn, rockets: PlumeRocket, ) -> Result { - let conn = &*rockets.conn; - let b = Blog::find_by_fqn(&rockets, &blog)?; - let post = Post::find_by_slug(&*conn, &slug, b.id)?; + let b = Blog::find_by_fqn(&conn, &blog)?; + let post = Post::find_by_slug(&conn, &slug, b.id)?; - if !user.has_reshared(&*conn, &post)? { - let reshare = Reshare::insert(&*conn, NewReshare::new(&post, &user))?; - reshare.notify(&*conn)?; + if !user.has_reshared(&conn, &post)? { + let reshare = Reshare::insert(&conn, NewReshare::new(&post, &user))?; + reshare.notify(&conn)?; - Timeline::add_to_all_timelines(&rockets, &post, Kind::Reshare(&user))?; + Timeline::add_to_all_timelines(&conn, &post, Kind::Reshare(&user))?; - let dest = User::one_by_instance(&*conn)?; - let act = reshare.to_activity(&*conn)?; + let dest = User::one_by_instance(&conn)?; + let act = reshare.to_activity(&conn)?; rockets .worker .execute(move || broadcast(&user, act, dest, CONFIG.proxy().cloned())); } else { - let reshare = Reshare::find_by_user_on_post(&*conn, user.id, post.id)?; - let delete_act = reshare.build_undo(&*conn)?; + let reshare = Reshare::find_by_user_on_post(&conn, user.id, post.id)?; + let delete_act = reshare.build_undo(&conn)?; inbox( - &rockets, + &conn, serde_json::to_value(&delete_act).map_err(Error::from)?, )?; - let dest = User::one_by_instance(&*conn)?; + let dest = User::one_by_instance(&conn)?; rockets .worker .execute(move || broadcast(&user, delete_act, dest, CONFIG.proxy().cloned())); diff --git a/src/routes/search.rs b/src/routes/search.rs index 5f4e2d42..3d6fa8fc 100644 --- a/src/routes/search.rs +++ b/src/routes/search.rs @@ -3,7 +3,7 @@ use rocket::request::Form; use crate::routes::Page; use crate::template_utils::{IntoContext, Ructe}; -use plume_models::{search::Query, PlumeRocket}; +use plume_models::{db_conn::DbConn, search::Query, PlumeRocket}; use std::str::FromStr; #[derive(Default, FromForm)] @@ -50,8 +50,7 @@ macro_rules! param_to_query { } #[get("/search?")] -pub fn search(query: Option>, rockets: PlumeRocket) -> Ructe { - let conn = &*rockets.conn; +pub fn search(query: Option>, conn: DbConn, rockets: PlumeRocket) -> Ructe { let query = query.map(Form::into_inner).unwrap_or_default(); let page = query.page.unwrap_or_default(); let mut parsed_query = @@ -65,7 +64,7 @@ pub fn search(query: Option>, rockets: PlumeRocket) -> Ructe { if str_query.is_empty() { render!(search::index( - &rockets.to_context(), + &(&conn, &rockets).to_context(), &format!("{}", Utc::today().format("%Y-%m-d")) )) } else { @@ -74,7 +73,7 @@ pub fn search(query: Option>, rockets: PlumeRocket) -> Ructe { .search_document(&conn, parsed_query, page.limits()); let next_page = if res.is_empty() { 0 } else { page.0 + 1 }; render!(search::result( - &rockets.to_context(), + &(&conn, &rockets).to_context(), &str_query, res, page.0, diff --git a/src/routes/session.rs b/src/routes/session.rs index ab854cb8..68ad84d3 100644 --- a/src/routes/session.rs +++ b/src/routes/session.rs @@ -19,15 +19,16 @@ use validator::{Validate, ValidationError, ValidationErrors}; use crate::mail::{build_mail, Mailer}; use crate::template_utils::{IntoContext, Ructe}; use plume_models::{ + db_conn::DbConn, password_reset_requests::*, users::{User, AUTH_COOKIE}, Error, PlumeRocket, CONFIG, }; #[get("/login?")] -pub fn new(m: Option, rockets: PlumeRocket) -> Ructe { +pub fn new(m: Option, conn: DbConn, rockets: PlumeRocket) -> Ructe { render!(session::login( - &rockets.to_context(), + &(&conn, &rockets).to_context(), m, &LoginForm::default(), ValidationErrors::default() @@ -46,21 +47,27 @@ pub struct LoginForm { pub fn create( form: LenientForm, mut cookies: Cookies<'_>, + conn: DbConn, rockets: PlumeRocket, ) -> RespondOrRedirect { - let conn = &*rockets.conn; let mut errors = match form.validate() { Ok(_) => ValidationErrors::new(), Err(e) => e, }; - let user = User::login(conn, &form.email_or_name, &form.password); + let user = User::login(&conn, &form.email_or_name, &form.password); let user_id = if let Ok(user) = user { user.id.to_string() } else { let mut err = ValidationError::new("invalid_login"); err.message = Some(Cow::from("Invalid username, or password")); errors.add("email_or_name", err); - return render!(session::login(&rockets.to_context(), None, &*form, errors)).into(); + return render!(session::login( + &(&conn, &rockets).to_context(), + None, + &*form, + errors + )) + .into(); }; cookies.add_private( @@ -90,7 +97,7 @@ pub fn create( .into() } else { render!(session::login( - &(conn, &rockets.intl.catalog, None, None), + &(&conn, &rockets.intl.catalog, None, None), None, &*form, errors @@ -124,9 +131,9 @@ impl PartialEq for ResetRequest { } #[get("/password-reset")] -pub fn password_reset_request_form(rockets: PlumeRocket) -> Ructe { +pub fn password_reset_request_form(conn: DbConn, rockets: PlumeRocket) -> Ructe { render!(session::password_reset_request( - &rockets.to_context(), + &(&conn, &rockets).to_context(), &ResetForm::default(), ValidationErrors::default() )) @@ -142,10 +149,11 @@ pub struct ResetForm { pub fn password_reset_request( mail: State<'_, Arc>>, form: LenientForm, + conn: DbConn, rockets: PlumeRocket, ) -> Ructe { - if User::find_by_email(&*rockets.conn, &form.email).is_ok() { - let token = PasswordResetRequest::insert(&*rockets.conn, &form.email) + if User::find_by_email(&conn, &form.email).is_ok() { + let token = PasswordResetRequest::insert(&conn, &form.email) .expect("password_reset_request::insert: error"); let url = format!("https://{}/password-reset/{}", CONFIG.base_url, token); @@ -161,16 +169,22 @@ pub fn password_reset_request( } } } - render!(session::password_reset_request_ok(&rockets.to_context())) + render!(session::password_reset_request_ok( + &(&conn, &rockets).to_context() + )) } #[get("/password-reset/")] -pub fn password_reset_form(token: String, rockets: PlumeRocket) -> Result { - PasswordResetRequest::find_by_token(&*rockets.conn, &token) - .map_err(|err| password_reset_error_response(err, &rockets))?; +pub fn password_reset_form( + token: String, + conn: DbConn, + rockets: PlumeRocket, +) -> Result { + PasswordResetRequest::find_by_token(&conn, &token) + .map_err(|err| password_reset_error_response(err, &conn, &rockets))?; Ok(render!(session::password_reset( - &rockets.to_context(), + &(&conn, &rockets).to_context(), &NewPasswordForm::default(), ValidationErrors::default() ))) @@ -199,15 +213,21 @@ fn passwords_match(form: &NewPasswordForm) -> Result<(), ValidationError> { pub fn password_reset( token: String, form: LenientForm, + conn: DbConn, rockets: PlumeRocket, ) -> Result, Ructe> { - form.validate() - .map_err(|err| render!(session::password_reset(&rockets.to_context(), &form, err)))?; + form.validate().map_err(|err| { + render!(session::password_reset( + &(&conn, &rockets).to_context(), + &form, + err + )) + })?; - PasswordResetRequest::find_and_delete_by_token(&*rockets.conn, &token) - .and_then(|request| User::find_by_email(&*rockets.conn, &request.email)) - .and_then(|user| user.reset_password(&*rockets.conn, &form.password)) - .map_err(|err| password_reset_error_response(err, &rockets))?; + PasswordResetRequest::find_and_delete_by_token(&conn, &token) + .and_then(|request| User::find_by_email(&conn, &request.email)) + .and_then(|user| user.reset_password(&conn, &form.password)) + .map_err(|err| password_reset_error_response(err, &conn, &rockets))?; Ok(Flash::success( Redirect::to(uri!(new: m = _)), @@ -218,11 +238,11 @@ pub fn password_reset( )) } -fn password_reset_error_response(err: Error, rockets: &PlumeRocket) -> Ructe { +fn password_reset_error_response(err: Error, conn: &DbConn, rockets: &PlumeRocket) -> Ructe { match err { Error::Expired => render!(session::password_reset_request_expired( - &rockets.to_context() + &(conn, rockets).to_context() )), - _ => render!(errors::not_found(&rockets.to_context())), + _ => render!(errors::not_found(&(conn, rockets).to_context())), } } diff --git a/src/routes/tags.rs b/src/routes/tags.rs index 56dabd1d..eaab8b1c 100644 --- a/src/routes/tags.rs +++ b/src/routes/tags.rs @@ -1,16 +1,21 @@ use crate::routes::{errors::ErrorPage, Page}; use crate::template_utils::{IntoContext, Ructe}; -use plume_models::{posts::Post, PlumeRocket}; +use plume_models::{db_conn::DbConn, posts::Post, PlumeRocket}; #[get("/tag/?")] -pub fn tag(name: String, page: Option, rockets: PlumeRocket) -> Result { +pub fn tag( + name: String, + page: Option, + conn: DbConn, + rockets: PlumeRocket, +) -> Result { let page = page.unwrap_or_default(); - let posts = Post::list_by_tag(&*rockets.conn, name.clone(), page.limits())?; + let posts = Post::list_by_tag(&conn, name.clone(), page.limits())?; Ok(render!(tags::index( - &rockets.to_context(), + &(&conn, &rockets).to_context(), name.clone(), posts, page.0, - Page::total(Post::count_for_tag(&*rockets.conn, name)? as i32) + Page::total(Post::count_for_tag(&conn, name)? as i32) ))) } diff --git a/src/routes/timelines.rs b/src/routes/timelines.rs index b435e449..a5c24ff7 100644 --- a/src/routes/timelines.rs +++ b/src/routes/timelines.rs @@ -3,18 +3,23 @@ use crate::routes::Page; use crate::template_utils::IntoContext; use crate::{routes::errors::ErrorPage, template_utils::Ructe}; -use plume_models::{timeline::*, PlumeRocket}; +use plume_models::{db_conn::DbConn, timeline::*, PlumeRocket}; use rocket::response::Redirect; #[get("/timeline/?")] -pub fn details(id: i32, rockets: PlumeRocket, page: Option) -> Result { +pub fn details( + id: i32, + conn: DbConn, + rockets: PlumeRocket, + page: Option, +) -> Result { let page = page.unwrap_or_default(); - let all_tl = Timeline::list_all_for_user(&rockets.conn, rockets.user.clone().map(|u| u.id))?; - let tl = Timeline::get(&rockets.conn, id)?; - let posts = tl.get_page(&rockets.conn, page.limits())?; - let total_posts = tl.count_posts(&rockets.conn)?; + let all_tl = Timeline::list_all_for_user(&conn, rockets.user.clone().map(|u| u.id))?; + let tl = Timeline::get(&conn, id)?; + let posts = tl.get_page(&conn, page.limits())?; + let total_posts = tl.count_posts(&conn)?; Ok(render!(timelines::details( - &rockets.to_context(), + &(&conn, &rockets).to_context(), tl, posts, all_tl, diff --git a/src/routes/user.rs b/src/routes/user.rs index 2a9f2080..4a3b8a8f 100644 --- a/src/routes/user.rs +++ b/src/routes/user.rs @@ -1,7 +1,4 @@ -use activitypub::{ - activity::Create, - collection::{OrderedCollection, OrderedCollectionPage}, -}; +use activitypub::collection::{OrderedCollection, OrderedCollectionPage}; use diesel::SaveChangesDsl; use rocket::{ http::{ContentType, Cookies}, @@ -10,27 +7,17 @@ use rocket::{ }; use rocket_i18n::I18n; use std::{borrow::Cow, collections::HashMap}; -use tracing::{info, warn}; use validator::{Validate, ValidationError, ValidationErrors}; use crate::inbox; use crate::routes::{errors::ErrorPage, Page, RemoteForm, RespondOrRedirect}; use crate::template_utils::{IntoContext, Ructe}; -use plume_common::activity_pub::{broadcast, inbox::FromId, ActivityStream, ApRequest, Id}; +use plume_common::activity_pub::{broadcast, ActivityStream, ApRequest, Id}; use plume_common::utils; use plume_models::{ - blogs::Blog, - db_conn::DbConn, - follows, - headers::Headers, - inbox::inbox as local_inbox, - instance::Instance, - medias::Media, - posts::{LicensedArticle, Post}, - reshares::Reshare, - safe_string::SafeString, - users::*, - Error, PlumeRocket, CONFIG, + blogs::Blog, db_conn::DbConn, follows, headers::Headers, inbox::inbox as local_inbox, + instance::Instance, medias::Media, posts::Post, reshares::Reshare, safe_string::SafeString, + users::*, Error, PlumeRocket, CONFIG, }; #[get("/me")] @@ -42,73 +29,18 @@ pub fn me(user: Option) -> RespondOrRedirect { } #[get("/@/", rank = 2)] -pub fn details( - name: String, - rockets: PlumeRocket, - fetch_rockets: PlumeRocket, - fetch_followers_rockets: PlumeRocket, - update_conn: DbConn, -) -> Result { - let conn = &*rockets.conn; - let user = User::find_by_fqn(&rockets, &name)?; +pub fn details(name: String, rockets: PlumeRocket, conn: DbConn) -> Result { + let user = User::find_by_fqn(&conn, &name)?; let recents = Post::get_recents_for_author(&*conn, &user, 6)?; let reshares = Reshare::get_recents_for_author(&*conn, &user, 6)?; - let worker = &rockets.worker; if !user.get_instance(&*conn)?.local { - // Fetch new articles - let user_clone = user.clone(); - worker.execute(move || { - for create_act in user_clone - .fetch_outbox::() - .expect("Remote user: outbox couldn't be fetched") - { - match create_act.create_props.object_object::() { - Ok(article) => { - Post::from_activity(&fetch_rockets, article) - .expect("Article from remote user couldn't be saved"); - info!("Fetched article from remote user"); - } - Err(e) => warn!("Error while fetching articles in background: {:?}", e), - } - } - }); - - // Fetch followers - let user_clone = user.clone(); - worker.execute(move || { - for user_id in user_clone - .fetch_followers_ids() - .expect("Remote user: fetching followers error") - { - let follower = - User::from_id(&fetch_followers_rockets, &user_id, None, CONFIG.proxy()) - .expect("user::details: Couldn't fetch follower"); - follows::Follow::insert( - &*fetch_followers_rockets.conn, - follows::NewFollow { - follower_id: follower.id, - following_id: user_clone.id, - ap_url: String::new(), - }, - ) - .expect("Couldn't save follower for remote user"); - } - }); - - // Update profile information if needed - let user_clone = user.clone(); - if user.needs_update() { - worker.execute(move || { - user_clone - .refetch(&*update_conn) - .expect("Couldn't update user info"); - }); - } + tracing::trace!("remote user found"); + user.remote_user_found(); // Doesn't block } Ok(render!(users::details( - &rockets.to_context(), + &(&conn, &rockets).to_context(), user.clone(), rockets .user @@ -126,12 +58,12 @@ pub fn details( } #[get("/dashboard")] -pub fn dashboard(user: User, rockets: PlumeRocket) -> Result { - let blogs = Blog::find_for_author(&*rockets.conn, &user)?; +pub fn dashboard(user: User, conn: DbConn, rockets: PlumeRocket) -> Result { + let blogs = Blog::find_for_author(&conn, &user)?; Ok(render!(users::dashboard( - &rockets.to_context(), + &(&conn, &rockets).to_context(), blogs, - Post::drafts_by_author(&*rockets.conn, &user)? + Post::drafts_by_author(&conn, &user)? ))) } @@ -150,14 +82,14 @@ pub fn dashboard_auth(i18n: I18n) -> Flash { pub fn follow( name: String, user: User, + conn: DbConn, rockets: PlumeRocket, ) -> Result, ErrorPage> { - let conn = &*rockets.conn; - let target = User::find_by_fqn(&rockets, &name)?; - let message = if let Ok(follow) = follows::Follow::find(&*conn, user.id, target.id) { - let delete_act = follow.build_undo(&*conn)?; + let target = User::find_by_fqn(&conn, &name)?; + let message = if let Ok(follow) = follows::Follow::find(&conn, user.id, target.id) { + let delete_act = follow.build_undo(&conn)?; local_inbox( - &rockets, + &conn, serde_json::to_value(&delete_act).map_err(Error::from)?, )?; @@ -168,16 +100,16 @@ pub fn follow( msg } else { let f = follows::Follow::insert( - &*conn, + &conn, follows::NewFollow { follower_id: user.id, following_id: target.id, ap_url: String::new(), }, )?; - f.notify(&*conn)?; + f.notify(&conn)?; - let act = f.to_activity(&*conn)?; + let act = f.to_activity(&conn)?; let msg = i18n!(rockets.intl.catalog, "You are now following {}."; target.name()); rockets .worker @@ -192,12 +124,13 @@ pub fn follow( #[post("/@//follow", data = "", rank = 2)] pub fn follow_not_connected( + conn: DbConn, rockets: PlumeRocket, name: String, remote_form: Option>, i18n: I18n, ) -> Result { - let target = User::find_by_fqn(&rockets, &name)?; + let target = User::find_by_fqn(&conn, &name)?; if let Some(remote_form) = remote_form { if let Some(uri) = User::fetch_remote_interact_uri(&remote_form) .ok() @@ -207,7 +140,7 @@ pub fn follow_not_connected( &format!( "{}@{}", target.fqn, - target.get_instance(&rockets.conn).ok()?.public_domain + target.get_instance(&conn).ok()?.public_domain ), )) }) @@ -224,7 +157,7 @@ pub fn follow_not_connected( ); Ok(Flash::new( render!(users::follow_remote( - &rockets.to_context(), + &(&conn, &rockets).to_context(), target, super::session::LoginForm::default(), ValidationErrors::default(), @@ -239,7 +172,7 @@ pub fn follow_not_connected( } else { Ok(Flash::new( render!(users::follow_remote( - &rockets.to_context(), + &(&conn, &rockets).to_context(), target, super::session::LoginForm::default(), ValidationErrors::default(), @@ -269,24 +202,24 @@ pub fn follow_auth(name: String, i18n: I18n) -> Flash { pub fn followers( name: String, page: Option, + conn: DbConn, rockets: PlumeRocket, ) -> Result { - let conn = &*rockets.conn; let page = page.unwrap_or_default(); - let user = User::find_by_fqn(&rockets, &name)?; - let followers_count = user.count_followers(&*conn)?; + let user = User::find_by_fqn(&conn, &name)?; + let followers_count = user.count_followers(&conn)?; Ok(render!(users::followers( - &rockets.to_context(), + &(&conn, &rockets).to_context(), user.clone(), rockets .user .clone() - .and_then(|x| x.is_following(&*conn, user.id).ok()) + .and_then(|x| x.is_following(&conn, user.id).ok()) .unwrap_or(false), user.instance_id != Instance::get_local()?.id, - user.get_instance(&*conn)?.public_domain, - user.get_followers_page(&*conn, page.limits())?, + user.get_instance(&conn)?.public_domain, + user.get_followers_page(&conn, page.limits())?, page.0, Page::total(followers_count as i32) ))) @@ -296,24 +229,24 @@ pub fn followers( pub fn followed( name: String, page: Option, + conn: DbConn, rockets: PlumeRocket, ) -> Result { - let conn = &*rockets.conn; let page = page.unwrap_or_default(); - let user = User::find_by_fqn(&rockets, &name)?; - let followed_count = user.count_followed(conn)?; + let user = User::find_by_fqn(&conn, &name)?; + let followed_count = user.count_followed(&conn)?; Ok(render!(users::followed( - &rockets.to_context(), + &(&conn, &rockets).to_context(), user.clone(), rockets .user .clone() - .and_then(|x| x.is_following(conn, user.id).ok()) + .and_then(|x| x.is_following(&conn, user.id).ok()) .unwrap_or(false), user.instance_id != Instance::get_local()?.id, - user.get_instance(conn)?.public_domain, - user.get_followed_page(conn, page.limits())?, + user.get_instance(&conn)?.public_domain, + user.get_followed_page(&conn, page.limits())?, page.0, Page::total(followed_count as i32) ))) @@ -322,17 +255,17 @@ pub fn followed( #[get("/@/", rank = 1)] pub fn activity_details( name: String, - rockets: PlumeRocket, + conn: DbConn, _ap: ApRequest, ) -> Option> { - let user = User::find_by_fqn(&rockets, &name).ok()?; - Some(ActivityStream::new(user.to_activity(&*rockets.conn).ok()?)) + let user = User::find_by_fqn(&conn, &name).ok()?; + Some(ActivityStream::new(user.to_activity(&conn).ok()?)) } #[get("/users/new")] -pub fn new(rockets: PlumeRocket) -> Result { +pub fn new(conn: DbConn, rockets: PlumeRocket) -> Result { Ok(render!(users::new( - &rockets.to_context(), + &(&conn, &rockets).to_context(), Instance::get_local()?.open_registrations, &NewUserForm::default(), ValidationErrors::default() @@ -340,10 +273,15 @@ pub fn new(rockets: PlumeRocket) -> Result { } #[get("/@//edit")] -pub fn edit(name: String, user: User, rockets: PlumeRocket) -> Result { +pub fn edit( + name: String, + user: User, + conn: DbConn, + rockets: PlumeRocket, +) -> Result { if user.username == name && !name.contains('@') { Ok(render!(users::edit( - &rockets.to_context(), + &(&conn, &rockets).to_context(), UpdateUserForm { display_name: user.display_name.clone(), email: user.email.clone().unwrap_or_default(), @@ -417,14 +355,15 @@ pub fn delete( name: String, user: User, mut cookies: Cookies<'_>, + conn: DbConn, rockets: PlumeRocket, ) -> Result, ErrorPage> { - let account = User::find_by_fqn(&rockets, &name)?; + let account = User::find_by_fqn(&conn, &name)?; if user.id == account.id { - account.delete(&*rockets.conn)?; + account.delete(&conn)?; - let target = User::one_by_instance(&*rockets.conn)?; - let delete_act = account.delete_activity(&*rockets.conn)?; + let target = User::one_by_instance(&conn)?; + let delete_act = account.delete_activity(&conn)?; rockets .worker .execute(move || broadcast(&account, delete_act, target, CONFIG.proxy().cloned())); @@ -515,9 +454,9 @@ fn to_validation(x: Error) -> ValidationErrors { #[post("/users/new", data = "")] pub fn create( form: LenientForm, + conn: DbConn, rockets: PlumeRocket, ) -> Result, Ructe> { - let conn = &*rockets.conn; if !Instance::get_local() .map(|i| i.open_registrations) .unwrap_or(true) @@ -537,7 +476,7 @@ pub fn create( form.validate() .and_then(|_| { NewUser::new_local( - conn, + &conn, form.username.to_string(), form.username.to_string(), Role::Normal, @@ -555,7 +494,7 @@ pub fn create( }) .map_err(|err| { render!(users::new( - &rockets.to_context(), + &(&conn, &rockets).to_context(), Instance::get_local() .map(|i| i.open_registrations) .unwrap_or(true), @@ -566,39 +505,39 @@ pub fn create( } #[get("/@//outbox")] -pub fn outbox(name: String, rockets: PlumeRocket) -> Option> { - let user = User::find_by_fqn(&rockets, &name).ok()?; - user.outbox(&*rockets.conn).ok() +pub fn outbox(name: String, conn: DbConn) -> Option> { + let user = User::find_by_fqn(&conn, &name).ok()?; + user.outbox(&conn).ok() } #[get("/@//outbox?")] pub fn outbox_page( name: String, page: Page, - rockets: PlumeRocket, + conn: DbConn, ) -> Option> { - let user = User::find_by_fqn(&rockets, &name).ok()?; - user.outbox_page(&*rockets.conn, page.limits()).ok() + let user = User::find_by_fqn(&conn, &name).ok()?; + user.outbox_page(&conn, page.limits()).ok() } #[post("/@//inbox", data = "")] pub fn inbox( name: String, data: inbox::SignedJson, headers: Headers<'_>, - rockets: PlumeRocket, + conn: DbConn, ) -> Result> { - User::find_by_fqn(&rockets, &name).map_err(|_| status::BadRequest(Some("User not found")))?; - inbox::handle_incoming(rockets, data, headers) + User::find_by_fqn(&conn, &name).map_err(|_| status::BadRequest(Some("User not found")))?; + inbox::handle_incoming(conn, data, headers) } #[get("/@//followers", rank = 1)] pub fn ap_followers( name: String, - rockets: PlumeRocket, + conn: DbConn, _ap: ApRequest, ) -> Option> { - let user = User::find_by_fqn(&rockets, &name).ok()?; + let user = User::find_by_fqn(&conn, &name).ok()?; let followers = user - .get_followers(&*rockets.conn) + .get_followers(&conn) .ok()? .into_iter() .map(|f| Id::new(f.ap_url)) @@ -616,16 +555,16 @@ pub fn ap_followers( } #[get("/@//atom.xml")] -pub fn atom_feed(name: String, rockets: PlumeRocket) -> Option> { - let conn = &*rockets.conn; - let author = User::find_by_fqn(&rockets, &name).ok()?; - let entries = Post::get_recents_for_author(conn, &author, 15).ok()?; +pub fn atom_feed(name: String, conn: DbConn) -> Option> { + let conn = &conn; + let author = User::find_by_fqn(&conn, &name).ok()?; + let entries = Post::get_recents_for_author(&conn, &author, 15).ok()?; let uri = Instance::get_local() .ok()? .compute_box("@", &name, "atom.xml"); let title = &author.display_name; let default_updated = &author.creation_date; - let feed = super::build_atom_feed(entries, &uri, title, default_updated, conn); + let feed = super::build_atom_feed(entries, &uri, title, default_updated, &conn); Some(Content( ContentType::new("application", "atom+xml"), feed.to_string(), diff --git a/src/routes/well_known.rs b/src/routes/well_known.rs index 1246dc97..759d4a06 100644 --- a/src/routes/well_known.rs +++ b/src/routes/well_known.rs @@ -2,7 +2,7 @@ use rocket::http::ContentType; use rocket::response::Content; use webfinger::*; -use plume_models::{ap_url, blogs::Blog, users::User, PlumeRocket, CONFIG}; +use plume_models::{ap_url, blogs::Blog, db_conn::DbConn, users::User, CONFIG}; #[get("/.well-known/nodeinfo")] pub fn nodeinfo() -> Content { @@ -42,18 +42,18 @@ pub fn host_meta() -> String { struct WebfingerResolver; -impl Resolver for WebfingerResolver { +impl Resolver for WebfingerResolver { fn instance_domain<'a>() -> &'a str { CONFIG.base_url.as_str() } - fn find(prefix: Prefix, acct: String, ctx: PlumeRocket) -> Result { + fn find(prefix: Prefix, acct: String, conn: DbConn) -> Result { match prefix { - Prefix::Acct => User::find_by_fqn(&ctx, &acct) - .and_then(|usr| usr.webfinger(&*ctx.conn)) + Prefix::Acct => User::find_by_fqn(&conn, &acct) + .and_then(|usr| usr.webfinger(&*conn)) .or(Err(ResolverError::NotFound)), - Prefix::Group => Blog::find_by_fqn(&ctx, &acct) - .and_then(|blog| blog.webfinger(&*ctx.conn)) + Prefix::Group => Blog::find_by_fqn(&conn, &acct) + .and_then(|blog| blog.webfinger(&*conn)) .or(Err(ResolverError::NotFound)), Prefix::Custom(_) => Err(ResolverError::NotFound), } @@ -61,8 +61,8 @@ impl Resolver for WebfingerResolver { } #[get("/.well-known/webfinger?")] -pub fn webfinger(resource: String, rockets: PlumeRocket) -> Content { - match WebfingerResolver::endpoint(resource, rockets) +pub fn webfinger(resource: String, conn: DbConn) -> Content { + match WebfingerResolver::endpoint(resource, conn) .and_then(|wf| serde_json::to_string(&wf).map_err(|_| ResolverError::NotFound)) { Ok(wf) => Content(ContentType::new("application", "jrd+json"), wf), diff --git a/src/template_utils.rs b/src/template_utils.rs index 0b126849..2e21c8fd 100644 --- a/src/template_utils.rs +++ b/src/template_utils.rs @@ -1,4 +1,4 @@ -use plume_models::{notifications::*, users::User, Connection, PlumeRocket}; +use plume_models::{db_conn::DbConn, notifications::*, users::User, Connection, PlumeRocket}; use crate::templates::Html; use rocket::http::hyper::header::{ETag, EntityTag}; @@ -31,7 +31,7 @@ pub trait IntoContext { ); } -impl IntoContext for PlumeRocket { +impl IntoContext for (&DbConn, &PlumeRocket) { fn to_context( &self, ) -> ( @@ -41,10 +41,10 @@ impl IntoContext for PlumeRocket { Option<(String, String)>, ) { ( - &*self.conn, - &self.intl.catalog, - self.user.clone(), - self.flash_msg.clone(), + &self.0, + &self.1.intl.catalog, + self.1.user.clone(), + self.1.flash_msg.clone(), ) } }