From cdb8aba6ec488f133eed0212b49c64bd24be7c89 Mon Sep 17 00:00:00 2001 From: Bat Date: Sun, 17 Jun 2018 23:04:46 +0100 Subject: [PATCH 01/17] Update rocket_i18n --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index b1753932..27ebfa1a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,4 +48,4 @@ rev = "df7111143e466c18d1f56377a8d9530a5a306aba" [dependencies.rocket_i18n] git = "https://github.com/BaptisteGelez/rocket_i18n" -rev = "457b88c59ec31905a9193df43df58bee55b4b83d" +rev = "5b4225d5bed5769482dc926a7e6d6b79f1217be6" From 36bf2e114c493385a823bf6346475008747b8031 Mon Sep 17 00:00:00 2001 From: Bat Date: Mon, 18 Jun 2018 12:32:03 +0100 Subject: [PATCH 02/17] Fix local notifications --- Cargo.lock | 6 +++--- src/models/instance.rs | 2 +- src/routes/comments.rs | 3 ++- src/routes/likes.rs | 9 +++++---- src/routes/reshares.rs | 3 ++- src/routes/user.rs | 4 +++- 6 files changed, 16 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c6875108..272d796f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1014,7 +1014,7 @@ dependencies = [ "rocket 0.4.0-dev (git+https://github.com/SergioBenitez/Rocket?rev=df7111143e466c18d1f56377a8d9530a5a306aba)", "rocket_codegen 0.4.0-dev (git+https://github.com/SergioBenitez/Rocket?rev=df7111143e466c18d1f56377a8d9530a5a306aba)", "rocket_contrib 0.4.0-dev (git+https://github.com/SergioBenitez/Rocket?rev=df7111143e466c18d1f56377a8d9530a5a306aba)", - "rocket_i18n 0.1.1 (git+https://github.com/BaptisteGelez/rocket_i18n?rev=457b88c59ec31905a9193df43df58bee55b4b83d)", + "rocket_i18n 0.1.1 (git+https://github.com/BaptisteGelez/rocket_i18n?rev=5b4225d5bed5769482dc926a7e6d6b79f1217be6)", "serde 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.43 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1273,7 +1273,7 @@ dependencies = [ [[package]] name = "rocket_i18n" version = "0.1.1" -source = "git+https://github.com/BaptisteGelez/rocket_i18n?rev=457b88c59ec31905a9193df43df58bee55b4b83d#457b88c59ec31905a9193df43df58bee55b4b83d" +source = "git+https://github.com/BaptisteGelez/rocket_i18n?rev=5b4225d5bed5769482dc926a7e6d6b79f1217be6#5b4225d5bed5769482dc926a7e6d6b79f1217be6" dependencies = [ "gettext-rs 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rocket 0.4.0-dev (git+https://github.com/SergioBenitez/Rocket?rev=df7111143e466c18d1f56377a8d9530a5a306aba)", @@ -2097,7 +2097,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum rocket_codegen_next 0.4.0-dev (git+https://github.com/SergioBenitez/Rocket?rev=df7111143e466c18d1f56377a8d9530a5a306aba)" = "" "checksum rocket_contrib 0.4.0-dev (git+https://github.com/SergioBenitez/Rocket?rev=df7111143e466c18d1f56377a8d9530a5a306aba)" = "" "checksum rocket_http 0.4.0-dev (git+https://github.com/SergioBenitez/Rocket?rev=df7111143e466c18d1f56377a8d9530a5a306aba)" = "" -"checksum rocket_i18n 0.1.1 (git+https://github.com/BaptisteGelez/rocket_i18n?rev=457b88c59ec31905a9193df43df58bee55b4b83d)" = "" +"checksum rocket_i18n 0.1.1 (git+https://github.com/BaptisteGelez/rocket_i18n?rev=5b4225d5bed5769482dc926a7e6d6b79f1217be6)" = "" "checksum rustc-demangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11fb43a206a04116ffd7cfcf9bcb941f8eb6cc7ff667272246b0a1c74259a3cb" "checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f" "checksum schannel 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "85fd9df495640643ad2d00443b3d78aae69802ad488debab4f1dd52fc1806ade" diff --git a/src/models/instance.rs b/src/models/instance.rs index c3967092..c4861ee1 100644 --- a/src/models/instance.rs +++ b/src/models/instance.rs @@ -86,7 +86,7 @@ impl Instance { impl Inbox for Instance { fn received(&self, conn: &PgConnection, act: serde_json::Value) { - self.save(conn, act.clone()).unwrap(); + self.save(conn, act.clone()).expect("Shared Inbox: Couldn't save activity"); // TODO: add to stream, or whatever needs to be done } diff --git a/src/routes/comments.rs b/src/routes/comments.rs index 7c039ee7..56cbb805 100644 --- a/src/routes/comments.rs +++ b/src/routes/comments.rs @@ -4,7 +4,7 @@ use rocket::{ }; use rocket_contrib::Template; -use activity_pub::broadcast; +use activity_pub::{broadcast, IntoId, inbox::Notify}; use db_conn::DbConn; use models::{ comments::*, @@ -53,6 +53,7 @@ fn create(blog: String, slug: String, query: CommentQuery, data: Form Redirect { if !user.has_liked(&*conn, &post) { let like = likes::Like::insert(&*conn, likes::NewLike { - post_id: post.id, - user_id: user.id, - ap_url: "".to_string() + post_id: post.id, + user_id: user.id, + ap_url: "".to_string() }); like.update_ap_url(&*conn); + likes::Like::notify(&*conn, like.into_activity(&*conn), user.clone().into_id()); broadcast(&*conn, &user, like.into_activity(&*conn), user.get_followers(&*conn)); } else { let like = likes::Like::find_by_user_on_post(&*conn, &user, &post).unwrap(); diff --git a/src/routes/reshares.rs b/src/routes/reshares.rs index 50698645..cabe22ee 100644 --- a/src/routes/reshares.rs +++ b/src/routes/reshares.rs @@ -1,6 +1,6 @@ use rocket::response::{Redirect, Flash}; -use activity_pub::broadcast; +use activity_pub::{broadcast, IntoId, inbox::Notify}; use db_conn::DbConn; use models::{ posts::Post, @@ -22,6 +22,7 @@ fn create(blog: String, slug: String, user: User, conn: DbConn) -> Redirect { }); reshare.update_ap_url(&*conn); + Reshare::notify(&*conn, reshare.into_activity(&*conn), user.clone().into_id()); broadcast(&*conn, &user, reshare.into_activity(&*conn), user.get_followers(&*conn)); } else { let reshare = Reshare::find_by_user_on_post(&*conn, &user, &post).unwrap(); diff --git a/src/routes/user.rs b/src/routes/user.rs index 2bc25ec6..50258360 100644 --- a/src/routes/user.rs +++ b/src/routes/user.rs @@ -10,7 +10,7 @@ use serde_json; use activity_pub::{ activity_pub, ActivityPub, ActivityStream, context, broadcast, Id, IntoId, - inbox::Inbox, + inbox::{Inbox, Notify}, actor::Actor }; use db_conn::DbConn; @@ -103,6 +103,8 @@ fn follow(name: String, conn: DbConn, user: User) -> Redirect { act.follow_props.set_actor_link::(user.clone().into_id()).unwrap(); act.follow_props.set_object_object(user.into_activity(&*conn)).unwrap(); act.object_props.set_id_string(format!("{}/follow/{}", user.ap_url, target.ap_url)).unwrap(); + + follows::Follow::notify(&*conn, act.clone(), user.clone().into_id()); broadcast(&*conn, &user, act, vec![target]); Redirect::to(format!("/@/{}/", name)) } From 3c9210a0ed88028ebef2ac36e1b650497fffc176 Mon Sep 17 00:00:00 2001 From: Bat Date: Mon, 18 Jun 2018 14:37:49 +0100 Subject: [PATCH 03/17] Introduce a find_by! macro to avoid some code duplication --- src/models/comments.rs | 15 ++------------- src/models/instance.rs | 8 +------- src/models/likes.rs | 8 +------- src/models/mod.rs | 14 ++++++++++++++ src/models/posts.rs | 17 ++--------------- src/models/reshares.rs | 8 +------- src/models/users.rs | 8 +------- 7 files changed, 22 insertions(+), 56 deletions(-) diff --git a/src/models/comments.rs b/src/models/comments.rs index 75df9df0..116779af 100644 --- a/src/models/comments.rs +++ b/src/models/comments.rs @@ -62,19 +62,8 @@ impl Comment { .into_iter().nth(0) } - pub fn find_by_post(conn: &PgConnection, post_id: i32) -> Vec { - comments::table.filter(comments::post_id.eq(post_id)) - .load::(conn) - .expect("Error loading comment by post id") - } - - pub fn find_by_ap_url(conn: &PgConnection, ap_url: String) -> Option { - comments::table.filter(comments::ap_url.eq(ap_url)) - .limit(1) - .load::(conn) - .expect("Error loading comment by AP URL") - .into_iter().nth(0) - } + find_by!(comments, find_by_post, post_id as i32); + find_by!(comments, find_by_ap_url, ap_url as String); pub fn get_author(&self, conn: &PgConnection) -> User { User::get(conn, self.author_id).unwrap() diff --git a/src/models/instance.rs b/src/models/instance.rs index c4861ee1..f058988b 100644 --- a/src/models/instance.rs +++ b/src/models/instance.rs @@ -63,13 +63,7 @@ impl Instance { .into_iter().nth(0) } - pub fn find_by_domain(conn: &PgConnection, domain: String) -> Option { - instances::table.filter(instances::public_domain.eq(domain)) - .limit(1) - .load::(conn) - .expect("Couldn't load instance by domain") - .into_iter().nth(0) - } + find_by!(instances, find_by_domain, public_domain as String); pub fn block(&self) { unimplemented!() diff --git a/src/models/likes.rs b/src/models/likes.rs index 2462118a..4dcafeeb 100644 --- a/src/models/likes.rs +++ b/src/models/likes.rs @@ -58,13 +58,7 @@ impl Like { .into_iter().nth(0) } - pub fn find_by_ap_url(conn: &PgConnection, ap_url: String) -> Option { - likes::table.filter(likes::ap_url.eq(ap_url)) - .limit(1) - .load::(conn) - .expect("Error loading like by AP URL") - .into_iter().nth(0) - } + find_by!(likes, find_by_ap_url, ap_url as String); pub fn find_by_user_on_post(conn: &PgConnection, user: &User, post: &Post) -> Option { likes::table.filter(likes::post_id.eq(post.id)) diff --git a/src/models/mod.rs b/src/models/mod.rs index a7b1b87b..455e3d3b 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -1,3 +1,17 @@ +// TODO: support multiple columns (see Like::find_by_user_on_post) +macro_rules! find_by { + ($table:ident, $fn:ident, $col:ident as $type:ident) => { + /// Try to find a $table with a given $col + pub fn $fn(conn: &PgConnection, val: $type) -> Option { + $table::table.filter($table::$col.eq(val)) + .limit(1) + .load::(conn) + .expect("Error loading $table by $col") + .into_iter().nth(0) + } + }; +} + pub mod blog_authors; pub mod blogs; pub mod comments; diff --git a/src/models/posts.rs b/src/models/posts.rs index b17a2340..bd85e235 100644 --- a/src/models/posts.rs +++ b/src/models/posts.rs @@ -76,21 +76,8 @@ impl Post { .len() } - pub fn find_by_slug(conn: &PgConnection, slug: String) -> Option { - posts::table.filter(posts::slug.eq(slug)) - .limit(1) - .load::(conn) - .expect("Error loading post by slug") - .into_iter().nth(0) - } - - pub fn find_by_ap_url(conn: &PgConnection, ap_url: String) -> Option { - posts::table.filter(posts::ap_url.eq(ap_url)) - .limit(1) - .load::(conn) - .expect("Error loading post by AP URL") - .into_iter().nth(0) - } + find_by!(posts, find_by_slug, slug as String); + find_by!(posts, find_by_ap_url, ap_url as String); pub fn get_recents(conn: &PgConnection, limit: i64) -> Vec { posts::table.order(posts::creation_date.desc()) diff --git a/src/models/reshares.rs b/src/models/reshares.rs index 75537672..2b11cc37 100644 --- a/src/models/reshares.rs +++ b/src/models/reshares.rs @@ -51,13 +51,7 @@ impl Reshare { } } - pub fn find_by_ap_url(conn: &PgConnection, ap_url: String) -> Option { - reshares::table.filter(reshares::ap_url.eq(ap_url)) - .limit(1) - .load::(conn) - .expect("Error loading reshare by AP URL") - .into_iter().nth(0) - } + find_by!(reshares, find_by_ap_url, ap_url as String); pub fn find_by_user_on_post(conn: &PgConnection, user: &User, post: &Post) -> Option { reshares::table.filter(reshares::post_id.eq(post.id)) diff --git a/src/models/users.rs b/src/models/users.rs index 78846ac8..259f03d6 100644 --- a/src/models/users.rs +++ b/src/models/users.rs @@ -125,13 +125,7 @@ impl User { .len() } - pub fn find_by_email(conn: &PgConnection, email: String) -> Option { - users::table.filter(users::email.eq(email)) - .limit(1) - .load::(conn) - .expect("Error loading user by email") - .into_iter().nth(0) - } + find_by!(users, find_by_email, email as String); pub fn find_by_name(conn: &PgConnection, username: String, instance_id: i32) -> Option { users::table.filter(users::username.eq(username)) From 94af0b9a7d8e18b8fe91c228abf5b2cf0fbb6ae0 Mon Sep 17 00:00:00 2001 From: Bat Date: Mon, 18 Jun 2018 14:44:23 +0100 Subject: [PATCH 04/17] Introduce a get! macro to avoid some code duplication --- src/models/blog_authors.rs | 8 +------- src/models/blogs.rs | 8 +------- src/models/comments.rs | 8 +------- src/models/follows.rs | 8 +------- src/models/instance.rs | 8 +------- src/models/likes.rs | 8 +------- src/models/mod.rs | 12 ++++++++++++ src/models/notifications.rs | 8 +------- src/models/post_authors.rs | 10 ++-------- src/models/posts.rs | 8 +------- src/models/reshares.rs | 8 +------- src/models/users.rs | 8 +------- 12 files changed, 24 insertions(+), 78 deletions(-) diff --git a/src/models/blog_authors.rs b/src/models/blog_authors.rs index 28fd3f46..04be6699 100644 --- a/src/models/blog_authors.rs +++ b/src/models/blog_authors.rs @@ -26,11 +26,5 @@ impl BlogAuthor { .expect("Error saving new blog author") } - pub fn get(conn: &PgConnection, id: i32) -> Option { - blog_authors::table.filter(blog_authors::id.eq(id)) - .limit(1) - .load::(conn) - .expect("Error loading blog author by id") - .into_iter().nth(0) - } + get!(blog_authors); } diff --git a/src/models/blogs.rs b/src/models/blogs.rs index cf8bd070..ce253d36 100644 --- a/src/models/blogs.rs +++ b/src/models/blogs.rs @@ -63,13 +63,7 @@ impl Blog { .expect("Error saving new blog") } - pub fn get(conn: &PgConnection, id: i32) -> Option { - blogs::table.filter(blogs::id.eq(id)) - .limit(1) - .load::(conn) - .expect("Error loading blog by id") - .into_iter().nth(0) - } + get!(blogs); pub fn find_for_author(conn: &PgConnection, author_id: i32) -> Vec { use schema::blog_authors; diff --git a/src/models/comments.rs b/src/models/comments.rs index 116779af..be75b234 100644 --- a/src/models/comments.rs +++ b/src/models/comments.rs @@ -54,13 +54,7 @@ impl Comment { .expect("Error saving new comment") } - pub fn get(conn: &PgConnection, id: i32) -> Option { - comments::table.filter(comments::id.eq(id)) - .limit(1) - .load::(conn) - .expect("Error loading comment by id") - .into_iter().nth(0) - } + get!(comments); find_by!(comments, find_by_post, post_id as i32); find_by!(comments, find_by_ap_url, ap_url as String); diff --git a/src/models/follows.rs b/src/models/follows.rs index 6ba9385f..111cc6e0 100644 --- a/src/models/follows.rs +++ b/src/models/follows.rs @@ -32,13 +32,7 @@ impl Follow { .expect("Unable to insert new follow") } - pub fn get(conn: &PgConnection, id: i32) -> Option { - follows::table.filter(follows::id.eq(id)) - .limit(1) - .load::(conn) - .expect("Unable to load follow by id") - .into_iter().nth(0) - } + get!(follows); pub fn accept_follow( conn: &PgConnection, diff --git a/src/models/instance.rs b/src/models/instance.rs index f058988b..ac7ad9dd 100644 --- a/src/models/instance.rs +++ b/src/models/instance.rs @@ -55,13 +55,7 @@ impl Instance { .expect("Error saving new instance") } - pub fn get(conn: &PgConnection, id: i32) -> Option { - instances::table.filter(instances::id.eq(id)) - .limit(1) - .load::(conn) - .expect("Error loading local instance infos") - .into_iter().nth(0) - } + get!(instances); find_by!(instances, find_by_domain, public_domain as String); diff --git a/src/models/likes.rs b/src/models/likes.rs index 4dcafeeb..e1ad3c18 100644 --- a/src/models/likes.rs +++ b/src/models/likes.rs @@ -50,13 +50,7 @@ impl Like { } } - pub fn get(conn: &PgConnection, id: i32) -> Option { - likes::table.filter(likes::id.eq(id)) - .limit(1) - .load::(conn) - .expect("Error loading like by ID") - .into_iter().nth(0) - } + get!(likes); find_by!(likes, find_by_ap_url, ap_url as String); diff --git a/src/models/mod.rs b/src/models/mod.rs index 455e3d3b..1bab1a7d 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -12,6 +12,18 @@ macro_rules! find_by { }; } +macro_rules! get { + ($table:ident) => { + pub fn get(conn: &PgConnection, id: i32) -> Option { + $table::table.filter($table::id.eq(id)) + .limit(1) + .load::(conn) + .expect("Error loading $table by id") + .into_iter().nth(0) + } + }; +} + pub mod blog_authors; pub mod blogs; pub mod comments; diff --git a/src/models/notifications.rs b/src/models/notifications.rs index e0a9acc9..6857e1f4 100644 --- a/src/models/notifications.rs +++ b/src/models/notifications.rs @@ -33,13 +33,7 @@ impl Notification { .expect("Couldn't save notification") } - pub fn get(conn: &PgConnection, id: i32) -> Option { - notifications::table.filter(notifications::id.eq(id)) - .limit(1) - .load::(conn) - .expect("Couldn't load notification by ID") - .into_iter().nth(0) - } + get!(notifications); pub fn find_for_user(conn: &PgConnection, user: &User) -> Vec { notifications::table.filter(notifications::user_id.eq(user.id)) diff --git a/src/models/post_authors.rs b/src/models/post_authors.rs index e5849c65..4806f029 100644 --- a/src/models/post_authors.rs +++ b/src/models/post_authors.rs @@ -23,18 +23,12 @@ pub struct NewPostAuthor { } impl PostAuthor { - pub fn insert (conn: &PgConnection, new: NewPostAuthor) -> PostAuthor { + pub fn insert(conn: &PgConnection, new: NewPostAuthor) -> PostAuthor { diesel::insert_into(post_authors::table) .values(new) .get_result(conn) .expect("Error saving new blog author") } - pub fn get(conn: &PgConnection, id: i32) -> Option { - post_authors::table.filter(post_authors::id.eq(id)) - .limit(1) - .load::(conn) - .expect("Error loading blog author by id") - .into_iter().nth(0) - } + get!(post_authors); } diff --git a/src/models/posts.rs b/src/models/posts.rs index bd85e235..dafbbbde 100644 --- a/src/models/posts.rs +++ b/src/models/posts.rs @@ -57,13 +57,7 @@ impl Post { .expect("Error saving new post") } - pub fn get(conn: &PgConnection, id: i32) -> Option { - posts::table.filter(posts::id.eq(id)) - .limit(1) - .load::(conn) - .expect("Error loading post by id") - .into_iter().nth(0) - } + get!(posts); pub fn count_local(conn: &PgConnection) -> usize { use schema::post_authors; diff --git a/src/models/reshares.rs b/src/models/reshares.rs index 2b11cc37..f694170d 100644 --- a/src/models/reshares.rs +++ b/src/models/reshares.rs @@ -31,13 +31,7 @@ impl Reshare { .expect("Couldn't save reshare") } - pub fn get(conn: &PgConnection, id: i32) -> Option { - reshares::table.filter(reshares::id.eq(id)) - .limit(1) - .load::(conn) - .expect("Could'nt load reshare") - .into_iter().nth(0) - } + get!(reshares); pub fn update_ap_url(&self, conn: &PgConnection) { if self.ap_url.len() == 0 { diff --git a/src/models/users.rs b/src/models/users.rs index 259f03d6..2b7708dc 100644 --- a/src/models/users.rs +++ b/src/models/users.rs @@ -110,13 +110,7 @@ impl User { .into_iter().nth(0).unwrap() } - pub fn get(conn: &PgConnection, id: i32) -> Option { - users::table.filter(users::id.eq(id)) - .limit(1) - .load::(conn) - .expect("Error loading user by id") - .into_iter().nth(0) - } + get!(users); pub fn count_local(conn: &PgConnection) -> usize { users::table.filter(users::instance_id.eq(Instance::local_id(conn))) From cd1d0d9627d9c30b1892719effef4a1a8109eb48 Mon Sep 17 00:00:00 2001 From: Bat Date: Mon, 18 Jun 2018 14:57:38 +0100 Subject: [PATCH 05/17] Introduce an insert! macro to avoid some code duplication --- src/models/blog_authors.rs | 8 +------- src/models/blogs.rs | 16 +++++++--------- src/models/comments.rs | 8 +------- src/models/follows.rs | 8 +------- src/models/instance.rs | 13 +------------ src/models/likes.rs | 13 +++---------- src/models/mod.rs | 11 +++++++++++ src/models/notifications.rs | 8 +------- src/models/post_authors.rs | 8 +------- src/models/posts.rs | 8 +------- src/models/reshares.rs | 8 +------- src/models/users.rs | 17 ++++++++--------- src/routes/instance.rs | 10 +++++----- 13 files changed, 42 insertions(+), 94 deletions(-) diff --git a/src/models/blog_authors.rs b/src/models/blog_authors.rs index 04be6699..106832a8 100644 --- a/src/models/blog_authors.rs +++ b/src/models/blog_authors.rs @@ -19,12 +19,6 @@ pub struct NewBlogAuthor { } impl BlogAuthor { - pub fn insert (conn: &PgConnection, new: NewBlogAuthor) -> BlogAuthor { - diesel::insert_into(blog_authors::table) - .values(new) - .get_result(conn) - .expect("Error saving new blog author") - } - + insert!(blog_authors, NewBlogAuthor); get!(blog_authors); } diff --git a/src/models/blogs.rs b/src/models/blogs.rs index ce253d36..71e7bee3 100644 --- a/src/models/blogs.rs +++ b/src/models/blogs.rs @@ -22,7 +22,7 @@ use activity_pub::{ sign, webfinger::* }; -use models::instance::Instance; +use models::instance::*; use schema::blogs; @@ -56,13 +56,7 @@ pub struct NewBlog { } impl Blog { - pub fn insert (conn: &PgConnection, new: NewBlog) -> Blog { - diesel::insert_into(blogs::table) - .values(new) - .get_result(conn) - .expect("Error saving new blog") - } - + insert!(blogs, NewBlog); get!(blogs); pub fn find_for_author(conn: &PgConnection, author_id: i32) -> Vec { @@ -130,7 +124,11 @@ impl Blog { let instance = match Instance::find_by_domain(conn, inst.clone()) { Some(instance) => instance, None => { - Instance::insert(conn, inst.clone(), inst.clone(), false) + Instance::insert(conn, NewInstance { + public_domain: inst.clone(), + name: inst.clone(), + local: false + }) } }; Blog::insert(conn, NewBlog { diff --git a/src/models/comments.rs b/src/models/comments.rs index be75b234..a24ac40e 100644 --- a/src/models/comments.rs +++ b/src/models/comments.rs @@ -47,13 +47,7 @@ pub struct NewComment { } impl Comment { - pub fn insert (conn: &PgConnection, new: NewComment) -> Comment { - diesel::insert_into(comments::table) - .values(new) - .get_result(conn) - .expect("Error saving new comment") - } - + insert!(comments, NewComment); get!(comments); find_by!(comments, find_by_post, post_id as i32); diff --git a/src/models/follows.rs b/src/models/follows.rs index 111cc6e0..06548625 100644 --- a/src/models/follows.rs +++ b/src/models/follows.rs @@ -25,13 +25,7 @@ pub struct NewFollow { } impl Follow { - pub fn insert(conn: &PgConnection, new: NewFollow) -> Follow { - diesel::insert_into(follows::table) - .values(new) - .get_result(conn) - .expect("Unable to insert new follow") - } - + insert!(follows, NewFollow); get!(follows); pub fn accept_follow( diff --git a/src/models/instance.rs b/src/models/instance.rs index ac7ad9dd..b7128da6 100644 --- a/src/models/instance.rs +++ b/src/models/instance.rs @@ -44,19 +44,8 @@ impl Instance { Instance::get_local(conn).unwrap().id } - pub fn insert<'a>(conn: &PgConnection, pub_dom: String, name: String, local: bool) -> Instance { - diesel::insert_into(instances::table) - .values(NewInstance { - public_domain: pub_dom, - name: name, - local: local - }) - .get_result(conn) - .expect("Error saving new instance") - } - + insert!(instances, NewInstance); get!(instances); - find_by!(instances, find_by_domain, public_domain as String); pub fn block(&self) { diff --git a/src/models/likes.rs b/src/models/likes.rs index e1ad3c18..537a4578 100644 --- a/src/models/likes.rs +++ b/src/models/likes.rs @@ -35,12 +35,9 @@ pub struct NewLike { } impl Like { - pub fn insert(conn: &PgConnection, new: NewLike) -> Like { - diesel::insert_into(likes::table) - .values(new) - .get_result(conn) - .expect("Unable to insert new like") - } + insert!(likes, NewLike); + get!(likes); + find_by!(likes, find_by_ap_url, ap_url as String); pub fn update_ap_url(&self, conn: &PgConnection) { if self.ap_url.len() == 0 { @@ -50,10 +47,6 @@ impl Like { } } - get!(likes); - - find_by!(likes, find_by_ap_url, ap_url as String); - pub fn find_by_user_on_post(conn: &PgConnection, user: &User, post: &Post) -> Option { likes::table.filter(likes::post_id.eq(post.id)) .filter(likes::user_id.eq(user.id)) diff --git a/src/models/mod.rs b/src/models/mod.rs index 1bab1a7d..e4bc0d75 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -24,6 +24,17 @@ macro_rules! get { }; } +macro_rules! insert { + ($table:ident, $from:ident) => { + pub fn insert(conn: &PgConnection, new: $from) -> Self { + diesel::insert_into($table::table) + .values(new) + .get_result(conn) + .expect("Error saving new $table") + } + }; +} + pub mod blog_authors; pub mod blogs; pub mod comments; diff --git a/src/models/notifications.rs b/src/models/notifications.rs index 6857e1f4..c5b7d599 100644 --- a/src/models/notifications.rs +++ b/src/models/notifications.rs @@ -26,13 +26,7 @@ pub struct NewNotification { } impl Notification { - pub fn insert(conn: &PgConnection, new: NewNotification) -> Notification { - diesel::insert_into(notifications::table) - .values(new) - .get_result(conn) - .expect("Couldn't save notification") - } - + insert!(notifications, NewNotification); get!(notifications); pub fn find_for_user(conn: &PgConnection, user: &User) -> Vec { diff --git a/src/models/post_authors.rs b/src/models/post_authors.rs index 4806f029..174507c7 100644 --- a/src/models/post_authors.rs +++ b/src/models/post_authors.rs @@ -23,12 +23,6 @@ pub struct NewPostAuthor { } impl PostAuthor { - pub fn insert(conn: &PgConnection, new: NewPostAuthor) -> PostAuthor { - diesel::insert_into(post_authors::table) - .values(new) - .get_result(conn) - .expect("Error saving new blog author") - } - + insert!(post_authors, NewPostAuthor); get!(post_authors); } diff --git a/src/models/posts.rs b/src/models/posts.rs index dafbbbde..67068bd0 100644 --- a/src/models/posts.rs +++ b/src/models/posts.rs @@ -50,13 +50,7 @@ pub struct NewPost { } impl Post { - pub fn insert(conn: &PgConnection, new: NewPost) -> Post { - diesel::insert_into(posts::table) - .values(new) - .get_result(conn) - .expect("Error saving new post") - } - + insert!(posts, NewPost); get!(posts); pub fn count_local(conn: &PgConnection) -> usize { diff --git a/src/models/reshares.rs b/src/models/reshares.rs index f694170d..6a4b2407 100644 --- a/src/models/reshares.rs +++ b/src/models/reshares.rs @@ -24,13 +24,7 @@ pub struct NewReshare { } impl Reshare { - pub fn insert(conn: &PgConnection, new: NewReshare) -> Reshare { - diesel::insert_into(reshares::table) - .values(new) - .get_result(conn) - .expect("Couldn't save reshare") - } - + insert!(reshares, NewReshare); get!(reshares); pub fn update_ap_url(&self, conn: &PgConnection) { diff --git a/src/models/users.rs b/src/models/users.rs index 2b7708dc..2a956fac 100644 --- a/src/models/users.rs +++ b/src/models/users.rs @@ -38,7 +38,7 @@ use models::{ blogs::Blog, blog_authors::BlogAuthor, follows::Follow, - instance::Instance, + instance::*, post_authors::PostAuthor, posts::Post }; @@ -85,6 +85,8 @@ pub struct NewUser { } impl User { + insert!(users, NewUser); + pub fn grant_admin_rights(&self, conn: &PgConnection) { diesel::update(self) .set(users::is_admin.eq(true)) @@ -92,13 +94,6 @@ impl User { .expect("Couldn't grant admin rights"); } - pub fn insert(conn: &PgConnection, new: NewUser) -> User { - diesel::insert_into(users::table) - .values(new) - .get_result(conn) - .expect("Error saving new user") - } - pub fn update(&self, conn: &PgConnection, name: String, email: String, summary: String) -> User { diesel::update(self) .set(( @@ -178,7 +173,11 @@ impl User { let instance = match Instance::find_by_domain(conn, inst.clone()) { Some(instance) => instance, None => { - Instance::insert(conn, inst.clone(), inst.clone(), false) + Instance::insert(conn, NewInstance { + name: inst.clone(), + public_domain: inst.clone(), + local: false + }) } }; User::insert(conn, NewUser { diff --git a/src/routes/instance.rs b/src/routes/instance.rs index f366a398..a5dd54df 100644 --- a/src/routes/instance.rs +++ b/src/routes/instance.rs @@ -58,11 +58,11 @@ struct NewInstanceForm { #[post("/configure", data = "")] fn post_config(conn: DbConn, data: Form) -> Redirect { let form = data.get(); - let inst = Instance::insert( - &*conn, - BASE_URL.as_str().to_string(), - form.name.to_string(), - true); + let inst = Instance::insert(&*conn, NewInstance { + public_domain: BASE_URL.as_str().to_string(), + name: form.name.to_string(), + local: true + }); if inst.has_admin(&*conn) { Redirect::to("/") } else { From fa2435e725d346f4d189fe413732e15a32173604 Mon Sep 17 00:00:00 2001 From: Bat Date: Mon, 18 Jun 2018 16:13:09 +0100 Subject: [PATCH 06/17] Improve the find_by! macro to allow multiple columns --- src/models/blogs.rs | 9 +-------- src/models/comments.rs | 1 - src/models/likes.rs | 10 +--------- src/models/mod.rs | 8 ++++---- src/models/posts.rs | 5 ++--- src/models/reshares.rs | 13 ++----------- src/models/users.rs | 10 +--------- src/routes/likes.rs | 2 +- src/routes/reshares.rs | 2 +- 9 files changed, 13 insertions(+), 47 deletions(-) diff --git a/src/models/blogs.rs b/src/models/blogs.rs index 71e7bee3..ee1964d3 100644 --- a/src/models/blogs.rs +++ b/src/models/blogs.rs @@ -67,14 +67,7 @@ impl Blog { .expect("Couldn't load blogs ") } - pub fn find_by_name(conn: &PgConnection, name: String, instance_id: i32) -> Option { - blogs::table.filter(blogs::actor_id.eq(name)) - .filter(blogs::instance_id.eq(instance_id)) - .limit(1) - .load::(conn) - .expect("Error loading blog by name") - .into_iter().nth(0) - } + find_by!(blogs, find_by_name, ap_url as String, instance_id as i32); pub fn find_local(conn: &PgConnection, name: String) -> Option { Blog::find_by_name(conn, name, Instance::local_id(conn)) diff --git a/src/models/comments.rs b/src/models/comments.rs index a24ac40e..e4788925 100644 --- a/src/models/comments.rs +++ b/src/models/comments.rs @@ -49,7 +49,6 @@ pub struct NewComment { impl Comment { insert!(comments, NewComment); get!(comments); - find_by!(comments, find_by_post, post_id as i32); find_by!(comments, find_by_ap_url, ap_url as String); diff --git a/src/models/likes.rs b/src/models/likes.rs index 537a4578..58aba083 100644 --- a/src/models/likes.rs +++ b/src/models/likes.rs @@ -38,6 +38,7 @@ impl Like { insert!(likes, NewLike); get!(likes); find_by!(likes, find_by_ap_url, ap_url as String); + find_by!(likes, find_by_user_on_post, user_id as i32, post_id as i32); pub fn update_ap_url(&self, conn: &PgConnection) { if self.ap_url.len() == 0 { @@ -47,15 +48,6 @@ impl Like { } } - pub fn find_by_user_on_post(conn: &PgConnection, user: &User, post: &Post) -> Option { - likes::table.filter(likes::post_id.eq(post.id)) - .filter(likes::user_id.eq(user.id)) - .limit(1) - .load::(conn) - .expect("Error loading like for user and post") - .into_iter().nth(0) - } - pub fn delete(&self, conn: &PgConnection) -> activity::Undo { diesel::delete(self).execute(conn).unwrap(); diff --git a/src/models/mod.rs b/src/models/mod.rs index e4bc0d75..d0f01f3c 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -1,9 +1,9 @@ -// TODO: support multiple columns (see Like::find_by_user_on_post) macro_rules! find_by { - ($table:ident, $fn:ident, $col:ident as $type:ident) => { + ($table:ident, $fn:ident, $($col:ident as $type:ident),+) => { /// Try to find a $table with a given $col - pub fn $fn(conn: &PgConnection, val: $type) -> Option { - $table::table.filter($table::$col.eq(val)) + pub fn $fn(conn: &PgConnection, $($col: $type),+) -> Option { + $table::table + $(.filter($table::$col.eq($col)))+ .limit(1) .load::(conn) .expect("Error loading $table by $col") diff --git a/src/models/posts.rs b/src/models/posts.rs index 67068bd0..6b71928b 100644 --- a/src/models/posts.rs +++ b/src/models/posts.rs @@ -52,6 +52,8 @@ pub struct NewPost { impl Post { insert!(posts, NewPost); get!(posts); + find_by!(posts, find_by_slug, slug as String); + find_by!(posts, find_by_ap_url, ap_url as String); pub fn count_local(conn: &PgConnection) -> usize { use schema::post_authors; @@ -64,9 +66,6 @@ impl Post { .len() } - find_by!(posts, find_by_slug, slug as String); - find_by!(posts, find_by_ap_url, ap_url as String); - pub fn get_recents(conn: &PgConnection, limit: i64) -> Vec { posts::table.order(posts::creation_date.desc()) .limit(limit) diff --git a/src/models/reshares.rs b/src/models/reshares.rs index 6a4b2407..86ead790 100644 --- a/src/models/reshares.rs +++ b/src/models/reshares.rs @@ -26,6 +26,8 @@ pub struct NewReshare { impl Reshare { insert!(reshares, NewReshare); get!(reshares); + find_by!(reshares, find_by_ap_url, ap_url as String); + find_by!(reshares, find_by_user_on_post, user_id as i32, post_id as i32); pub fn update_ap_url(&self, conn: &PgConnection) { if self.ap_url.len() == 0 { @@ -39,17 +41,6 @@ impl Reshare { } } - find_by!(reshares, find_by_ap_url, ap_url as String); - - pub fn find_by_user_on_post(conn: &PgConnection, user: &User, post: &Post) -> Option { - reshares::table.filter(reshares::post_id.eq(post.id)) - .filter(reshares::user_id.eq(user.id)) - .limit(1) - .load::(conn) - .expect("Error loading reshare for user and post") - .into_iter().nth(0) - } - pub fn get_recents_for_author(conn: &PgConnection, user: &User, limit: i64) -> Vec { reshares::table.filter(reshares::user_id.eq(user.id)) .order(reshares::creation_date.desc()) diff --git a/src/models/users.rs b/src/models/users.rs index 2a956fac..88bf979a 100644 --- a/src/models/users.rs +++ b/src/models/users.rs @@ -115,15 +115,7 @@ impl User { } find_by!(users, find_by_email, email as String); - - pub fn find_by_name(conn: &PgConnection, username: String, instance_id: i32) -> Option { - users::table.filter(users::username.eq(username)) - .filter(users::instance_id.eq(instance_id)) - .limit(1) - .load::(conn) - .expect("Error loading user by name") - .into_iter().nth(0) - } + find_by!(users, find_by_name, username as String, instance_id as i32); pub fn find_local(conn: &PgConnection, username: String) -> Option { User::find_by_name(conn, username, Instance::local_id(conn)) diff --git a/src/routes/likes.rs b/src/routes/likes.rs index fb12855d..57bae6f6 100644 --- a/src/routes/likes.rs +++ b/src/routes/likes.rs @@ -25,7 +25,7 @@ fn create(blog: String, slug: String, user: User, conn: DbConn) -> Redirect { likes::Like::notify(&*conn, like.into_activity(&*conn), user.clone().into_id()); broadcast(&*conn, &user, like.into_activity(&*conn), user.get_followers(&*conn)); } else { - let like = likes::Like::find_by_user_on_post(&*conn, &user, &post).unwrap(); + let like = likes::Like::find_by_user_on_post(&*conn, user.id, post.id).unwrap(); let delete_act = like.delete(&*conn); broadcast(&*conn, &user, delete_act, user.get_followers(&*conn)); } diff --git a/src/routes/reshares.rs b/src/routes/reshares.rs index cabe22ee..5365af48 100644 --- a/src/routes/reshares.rs +++ b/src/routes/reshares.rs @@ -25,7 +25,7 @@ fn create(blog: String, slug: String, user: User, conn: DbConn) -> Redirect { Reshare::notify(&*conn, reshare.into_activity(&*conn), user.clone().into_id()); broadcast(&*conn, &user, reshare.into_activity(&*conn), user.get_followers(&*conn)); } else { - let reshare = Reshare::find_by_user_on_post(&*conn, &user, &post).unwrap(); + let reshare = Reshare::find_by_user_on_post(&*conn, user.id, post.id).unwrap(); let delete_act = reshare.delete(&*conn); broadcast(&*conn, &user, delete_act, user.get_followers(&*conn)); } From bb682a1cc1769ff4556ac88ec320682162ec2bdd Mon Sep 17 00:00:00 2001 From: Bat Date: Mon, 18 Jun 2018 16:16:18 +0100 Subject: [PATCH 07/17] Don't sign activities for each target inbox, do it only once --- src/activity_pub/mod.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/activity_pub/mod.rs b/src/activity_pub/mod.rs index af544aca..51d12d3e 100644 --- a/src/activity_pub/mod.rs +++ b/src/activity_pub/mod.rs @@ -76,18 +76,18 @@ impl<'r, O: Object> Responder<'r> for ActivityStream { } } -pub fn broadcast(conn: &PgConnection, sender: &S, act: A, to: Vec) { +pub fn broadcast(conn: &PgConnection, sender: &S, act: A, to: Vec) { let boxes = to.into_iter() .map(|u| u.get_shared_inbox_url().unwrap_or(u.get_inbox_url())) .collect::>() .unique(); + + let mut act = serde_json::to_value(act).unwrap(); + act["@context"] = context(); + let signed = act.sign(sender, conn); + for inbox in boxes { // TODO: run it in Sidekiq or something like that - - let mut act = serde_json::to_value(act.clone()).unwrap(); - act["@context"] = context(); - let signed = act.sign(sender, conn); - let res = Client::new() .post(&inbox[..]) .headers(request::headers()) From 58cc35691dbee086c6a92e11d604a8805f2b5d07 Mon Sep 17 00:00:00 2001 From: Bat Date: Mon, 18 Jun 2018 16:59:49 +0100 Subject: [PATCH 08/17] Add generic error catchers --- po/plume.pot | 6 ++++++ src/main.rs | 4 ++++ src/routes/errors.rs | 15 +++++++++++++++ src/routes/mod.rs | 1 + templates/errors/404.html.tera | 6 ++++++ 5 files changed, 32 insertions(+) create mode 100644 src/routes/errors.rs create mode 100644 templates/errors/404.html.tera diff --git a/po/plume.pot b/po/plume.pot index 1d36fc36..6eaebfb9 100644 --- a/po/plume.pot +++ b/po/plume.pot @@ -263,3 +263,9 @@ msgstr "" msgid "{{ data }} commented your article" msgstr "" + +msgid "We couldn't find this page." +msgstr "" + +msgid "The link that led you here may be broken." +msgstr "" diff --git a/src/main.rs b/src/main.rs index 69df18e8..7a5fac5a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -128,6 +128,10 @@ fn main() { routes::well_known::nodeinfo, routes::well_known::webfinger ]) + .catch(catchers![ + routes::errors::not_found, + routes::errors::server_error + ]) .manage(init_pool()) .attach(Template::custom(|engines| { rocket_i18n::tera(&mut engines.tera); diff --git a/src/routes/errors.rs b/src/routes/errors.rs new file mode 100644 index 00000000..68ca682e --- /dev/null +++ b/src/routes/errors.rs @@ -0,0 +1,15 @@ +use rocket_contrib::Template; + +#[catch(404)] +fn not_found() -> Template { + Template::render("errors/404", json!({ + "error_message": "Page not found" + })) +} + +#[catch(500)] +fn server_error() -> Template { + Template::render("errors/500", json!({ + "error_message": "Server error" + })) +} diff --git a/src/routes/mod.rs b/src/routes/mod.rs index 501c774c..4787b4d2 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -3,6 +3,7 @@ use std::path::{Path, PathBuf}; pub mod blogs; pub mod comments; +pub mod errors; pub mod instance; pub mod likes; pub mod notifications; diff --git a/templates/errors/404.html.tera b/templates/errors/404.html.tera new file mode 100644 index 00000000..413374a3 --- /dev/null +++ b/templates/errors/404.html.tera @@ -0,0 +1,6 @@ +{% extends "errors/base" %} + +{% block error %} +

{{ "We couldn't find this page." | _ }}

+

{{ "The link that led you here may be broken." | _ }}

+{% endblock error %} From 7e3cdec0b6727ecd80e6743d4feaa6730bb71b4a Mon Sep 17 00:00:00 2001 From: Bat Date: Mon, 18 Jun 2018 17:34:29 +0100 Subject: [PATCH 09/17] Add some to_json functions to models for serialization in templates --- src/models/comments.rs | 6 ++++++ src/models/posts.rs | 9 +++++++++ src/models/users.rs | 6 ++++++ src/routes/blogs.rs | 14 +------------- src/routes/instance.rs | 14 +------------- src/routes/posts.rs | 20 ++------------------ src/routes/user.rs | 36 +++--------------------------------- 7 files changed, 28 insertions(+), 77 deletions(-) diff --git a/src/models/comments.rs b/src/models/comments.rs index e4788925..79848332 100644 --- a/src/models/comments.rs +++ b/src/models/comments.rs @@ -99,6 +99,12 @@ impl Comment { .expect("Couldn't load local comments") .len() } + + pub fn to_json(&self, conn: &PgConnection) -> serde_json::Value { + let mut json = serde_json::to_value(self).unwrap(); + json["author"] = self.get_author(conn).to_json(conn); + json + } } impl FromActivity for Comment { diff --git a/src/models/posts.rs b/src/models/posts.rs index 6b71928b..8d12bc8f 100644 --- a/src/models/posts.rs +++ b/src/models/posts.rs @@ -168,6 +168,15 @@ impl Post { act.create_props.set_object_object(self.into_activity(conn)).unwrap(); act } + + pub fn to_json(&self, conn: &PgConnection) -> serde_json::Value { + json!({ + "post": self, + "author": self.get_authors(conn)[0].to_json(conn), + "url": format!("/~/{}/{}/", self.get_blog(conn).actor_id, self.slug), + "date": self.creation_date.timestamp() + }) + } } impl FromActivity
for Post { diff --git a/src/models/users.rs b/src/models/users.rs index 88bf979a..dc37ff95 100644 --- a/src/models/users.rs +++ b/src/models/users.rs @@ -330,6 +330,12 @@ impl User { }; actor } + + pub fn to_json(&self, conn: &PgConnection) -> serde_json::Value { + let mut json = serde_json::to_value(self).unwrap(); + json["fqn"] = serde_json::Value::String(self.get_fqn(conn)); + json + } } impl<'a, 'r> FromRequest<'a, 'r> for User { diff --git a/src/routes/blogs.rs b/src/routes/blogs.rs index 0a64e9e6..ecd01394 100644 --- a/src/routes/blogs.rs +++ b/src/routes/blogs.rs @@ -25,19 +25,7 @@ fn details(name: String, conn: DbConn, user: Option) -> Template { "blog": blog, "account": user, "is_author": user.map(|x| x.is_author_in(&*conn, blog)), - "recents": recents.into_iter().map(|p| { - json!({ - "post": p, - "author": ({ - let author = &p.get_authors(&*conn)[0]; - let mut json = serde_json::to_value(author).unwrap(); - json["fqn"] = serde_json::Value::String(author.get_fqn(&*conn)); - json - }), - "url": format!("/~/{}/{}/", p.get_blog(&*conn).actor_id, p.slug), - "date": p.creation_date.timestamp() - }) - }).collect::>() + "recents": recents.into_iter().map(|p| p.to_json(&*conn)).collect::>() })) } diff --git a/src/routes/instance.rs b/src/routes/instance.rs index a5dd54df..be1deb42 100644 --- a/src/routes/instance.rs +++ b/src/routes/instance.rs @@ -22,19 +22,7 @@ fn index(conn: DbConn, user: Option) -> Template { Template::render("instance/index", json!({ "instance": inst, "account": user, - "recents": recents.into_iter().map(|p| { - json!({ - "post": p, - "author": ({ - let author = &p.get_authors(&*conn)[0]; - let mut json = serde_json::to_value(author).unwrap(); - json["fqn"] = serde_json::Value::String(author.get_fqn(&*conn)); - json - }), - "url": format!("/~/{}/{}/", p.get_blog(&*conn).actor_id, p.slug), - "date": p.creation_date.timestamp() - }) - }).collect::>() + "recents": recents.into_iter().map(|p| p.to_json(&*conn)).collect::>() })) } None => { diff --git a/src/routes/posts.rs b/src/routes/posts.rs index fef302ab..c0b7663b 100644 --- a/src/routes/posts.rs +++ b/src/routes/posts.rs @@ -24,26 +24,10 @@ fn details(blog: String, slug: String, conn: DbConn, user: Option) -> Temp let comments = Comment::find_by_post(&*conn, post.id); Template::render("posts/details", json!({ - "author": ({ - let author = &post.get_authors(&*conn)[0]; - let mut json = serde_json::to_value(author).unwrap(); - json["fqn"] = serde_json::Value::String(author.get_fqn(&*conn)); - json - }), + "author": post.get_authors(&*conn)[0].to_json(&*conn), "post": post, "blog": blog, - "comments": comments.into_iter().map(|c| { - json!({ - "id": c.id, - "content": c.content, - "author": ({ - let author = &c.get_author(&*conn); - let mut json = serde_json::to_value(author).unwrap(); - json["fqn"] = serde_json::Value::String(author.get_fqn(&*conn)); - json - }) - }) - }).collect::>(), + "comments": comments.into_iter().map(|c| c.to_json(&*conn)).collect::>(), "n_likes": post.get_likes(&*conn).len(), "has_liked": user.clone().map(|u| u.has_liked(&*conn, &post)).unwrap_or(false), "n_reshares": post.get_reshares(&*conn).len(), diff --git a/src/routes/user.rs b/src/routes/user.rs index 50258360..d485df1e 100644 --- a/src/routes/user.rs +++ b/src/routes/user.rs @@ -46,33 +46,8 @@ fn details(name: String, conn: DbConn, account: Option) -> Template { "is_remote": user.instance_id != Instance::local_id(&*conn), "follows": account.clone().map(|x| x.is_following(&*conn, user.id)).unwrap_or(false), "account": account, - "recents": recents.into_iter().map(|p| { - json!({ - "post": p, - "author": ({ - let author = &p.get_authors(&*conn)[0]; - let mut json = serde_json::to_value(author).unwrap(); - json["fqn"] = serde_json::Value::String(author.get_fqn(&*conn)); - json - }), - "url": format!("/~/{}/{}/", p.get_blog(&*conn).actor_id, p.slug), - "date": p.creation_date.timestamp() - }) - }).collect::>(), - "reshares": reshares.into_iter().map(|r| { - let p = r.get_post(&*conn).unwrap(); - json!({ - "post": p, - "author": ({ - let author = &p.get_authors(&*conn)[0]; - let mut json = serde_json::to_value(author).unwrap(); - json["fqn"] = serde_json::Value::String(author.get_fqn(&*conn)); - json - }), - "url": format!("/~/{}/{}/", p.get_blog(&*conn).actor_id, p.slug), - "date": p.creation_date.timestamp() - }) - }).collect::>(), + "recents": recents.into_iter().map(|p| p.to_json(&*conn)).collect::>(), + "reshares": reshares.into_iter().map(|r| r.get_post(&*conn).unwrap().to_json(&*conn)).collect::>(), "is_self": account.map(|a| a.id == user_id).unwrap_or(false), "n_followers": n_followers })) @@ -124,12 +99,7 @@ fn followers(name: String, conn: DbConn, account: Option) -> Template { "instance_url": user.get_instance(&*conn).public_domain, "is_remote": user.instance_id != Instance::local_id(&*conn), "follows": account.clone().map(|x| x.is_following(&*conn, user.id)).unwrap_or(false), - "followers": user.get_followers(&*conn).into_iter().map(|f| { - let fqn = f.get_fqn(&*conn); - let mut json = serde_json::to_value(f).unwrap(); - json["fqn"] = serde_json::Value::String(fqn); - json - }).collect::>(), + "followers": user.get_followers(&*conn).into_iter().map(|f| f.to_json(&*conn)).collect::>(), "account": account, "is_self": account.map(|a| a.id == user_id).unwrap_or(false), "n_followers": user.get_followers(&*conn).len() From 97c652cefdfacca1c68cc1c058be6f0294312c70 Mon Sep 17 00:00:00 2001 From: Trinity Pointard Date: Mon, 18 Jun 2018 19:28:26 +0200 Subject: [PATCH 10/17] Remove HTML tags from preview fixes #24 --- templates/macros.html.tera | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/macros.html.tera b/templates/macros.html.tera index 80ad8024..0926eb92 100644 --- a/templates/macros.html.tera +++ b/templates/macros.html.tera @@ -6,7 +6,7 @@ {% endif %}

{{ article.post.title }}

-

{{ article.post.content | striptags | truncate(length=200) }}

+

{{ article.post.content | safe | striptags | truncate(length=200) }}

{{ "By {{ link_1 }}{{ link_2 }}{{ link_3 }}{{ name }}{{ link_4 }}" | _( link_1='", rank = 2)] fn details(name: String, conn: DbConn, user: Option) -> Template { - let blog = Blog::find_by_fqn(&*conn, name).unwrap(); - let recents = Post::get_recents_for_blog(&*conn, &blog, 5); - Template::render("blogs/details", json!({ - "blog": blog, - "account": user, - "is_author": user.map(|x| x.is_author_in(&*conn, blog)), - "recents": recents.into_iter().map(|p| p.to_json(&*conn)).collect::>() - })) + may_fail!(Blog::find_by_fqn(&*conn, name), "Requested blog couldn't be found", |blog| { + let recents = Post::get_recents_for_blog(&*conn, &blog, 5); + + Template::render("blogs/details", json!({ + "blog": blog, + "account": user, + "is_author": user.map(|x| x.is_author_in(&*conn, blog)), + "recents": recents.into_iter().map(|p| p.to_json(&*conn)).collect::>() + })) + }) } #[get("/~/", format = "application/activity+json", rank = 1)] diff --git a/src/routes/comments.rs b/src/routes/comments.rs index 56cbb805..289dee28 100644 --- a/src/routes/comments.rs +++ b/src/routes/comments.rs @@ -17,11 +17,12 @@ use safe_string::SafeString; #[get("/~/<_blog>//comment")] fn new(_blog: String, slug: String, user: User, conn: DbConn) -> Template { - let post = Post::find_by_slug(&*conn, slug).unwrap(); - Template::render("comments/new", json!({ - "post": post, - "account": user - })) + may_fail!(Post::find_by_slug(&*conn, slug), "Couldn't find this post", |post| { + Template::render("comments/new", json!({ + "post": post, + "account": user + })) + }) } #[get("/~///comment", rank=2)] diff --git a/src/routes/mod.rs b/src/routes/mod.rs index 4787b4d2..89ab2c00 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -1,6 +1,32 @@ use rocket::response::NamedFile; use std::path::{Path, PathBuf}; +macro_rules! may_fail { + ($expr:expr, $template:expr, $msg:expr, | $res:ident | $block:block) => { + { + let res = $expr; + if res.is_some() { + let $res = res.unwrap(); + $block + } else { + Template::render(concat!("errors/", stringify!($template)), json!({ + "error_message": $msg + })) + } + } + }; + ($expr:expr, $msg:expr, | $res:ident | $block:block) => { + may_fail!($expr, "404", $msg, |$res| { + $block + }) + }; + ($expr:expr, | $res:ident | $block:block) => { + may_fail!($expr, "", |$res| { + $block + }) + }; +} + pub mod blogs; pub mod comments; pub mod errors; diff --git a/src/routes/posts.rs b/src/routes/posts.rs index c0b7663b..4fd02666 100644 --- a/src/routes/posts.rs +++ b/src/routes/posts.rs @@ -19,22 +19,24 @@ use safe_string::SafeString; #[get("/~//", rank = 4)] fn details(blog: String, slug: String, conn: DbConn, user: Option) -> Template { - let blog = Blog::find_by_fqn(&*conn, blog).unwrap(); - let post = Post::find_by_slug(&*conn, slug).unwrap(); - let comments = Comment::find_by_post(&*conn, post.id); + may_fail!(Blog::find_by_fqn(&*conn, blog), "Couldn't find this blog", |blog| { + may_fail!(Post::find_by_slug(&*conn, slug), "Couldn't find this post", |post| { + let comments = Comment::find_by_post(&*conn, post.id); - Template::render("posts/details", json!({ - "author": post.get_authors(&*conn)[0].to_json(&*conn), - "post": post, - "blog": blog, - "comments": comments.into_iter().map(|c| c.to_json(&*conn)).collect::>(), - "n_likes": post.get_likes(&*conn).len(), - "has_liked": user.clone().map(|u| u.has_liked(&*conn, &post)).unwrap_or(false), - "n_reshares": post.get_reshares(&*conn).len(), - "has_reshared": user.clone().map(|u| u.has_reshared(&*conn, &post)).unwrap_or(false), - "account": user, - "date": &post.creation_date.timestamp() - })) + Template::render("posts/details", json!({ + "author": post.get_authors(&*conn)[0].to_json(&*conn), + "post": post, + "blog": blog, + "comments": comments.into_iter().map(|c| c.to_json(&*conn)).collect::>(), + "n_likes": post.get_likes(&*conn).len(), + "has_liked": user.clone().map(|u| u.has_liked(&*conn, &post)).unwrap_or(false), + "n_reshares": post.get_reshares(&*conn).len(), + "has_reshared": user.clone().map(|u| u.has_reshared(&*conn, &post)).unwrap_or(false), + "account": user, + "date": &post.creation_date.timestamp() + })) + }) + }) } #[get("/~/<_blog>/", rank = 3, format = "application/activity+json")] diff --git a/src/routes/user.rs b/src/routes/user.rs index d485df1e..6b925c87 100644 --- a/src/routes/user.rs +++ b/src/routes/user.rs @@ -25,7 +25,7 @@ use models::{ use utils; #[get("/me")] -fn me(user: Option) -> Result> { +fn me(user: Option) -> Result> { match user { Some(user) => Ok(Redirect::to(format!("/@/{}/", user.username))), None => Err(utils::requires_login("", "/me")) @@ -34,23 +34,24 @@ fn me(user: Option) -> Result> { #[get("/@/", rank = 2)] fn details(name: String, conn: DbConn, account: Option) -> Template { - let user = User::find_by_fqn(&*conn, name).unwrap(); - let recents = Post::get_recents_for_author(&*conn, &user, 6); - let reshares = Reshare::get_recents_for_author(&*conn, &user, 6); - let user_id = user.id.clone(); - let n_followers = user.get_followers(&*conn).len(); + may_fail!(User::find_by_fqn(&*conn, name), "Couldn't find requested user", |user| { + let recents = Post::get_recents_for_author(&*conn, &user, 6); + let reshares = Reshare::get_recents_for_author(&*conn, &user, 6); + let user_id = user.id.clone(); + let n_followers = user.get_followers(&*conn).len(); - Template::render("users/details", json!({ - "user": serde_json::to_value(user.clone()).unwrap(), - "instance_url": user.get_instance(&*conn).public_domain, - "is_remote": user.instance_id != Instance::local_id(&*conn), - "follows": account.clone().map(|x| x.is_following(&*conn, user.id)).unwrap_or(false), - "account": account, - "recents": recents.into_iter().map(|p| p.to_json(&*conn)).collect::>(), - "reshares": reshares.into_iter().map(|r| r.get_post(&*conn).unwrap().to_json(&*conn)).collect::>(), - "is_self": account.map(|a| a.id == user_id).unwrap_or(false), - "n_followers": n_followers - })) + Template::render("users/details", json!({ + "user": serde_json::to_value(user.clone()).unwrap(), + "instance_url": user.get_instance(&*conn).public_domain, + "is_remote": user.instance_id != Instance::local_id(&*conn), + "follows": account.clone().map(|x| x.is_following(&*conn, user.id)).unwrap_or(false), + "account": account, + "recents": recents.into_iter().map(|p| p.to_json(&*conn)).collect::>(), + "reshares": reshares.into_iter().map(|r| r.get_post(&*conn).unwrap().to_json(&*conn)).collect::>(), + "is_self": account.map(|a| a.id == user_id).unwrap_or(false), + "n_followers": n_followers + })) + }) } #[get("/dashboard")] @@ -91,19 +92,20 @@ fn follow_auth(name: String) -> Flash { #[get("/@//followers", rank = 2)] fn followers(name: String, conn: DbConn, account: Option) -> Template { - let user = User::find_by_fqn(&*conn, name.clone()).unwrap(); - let user_id = user.id.clone(); + may_fail!(User::find_by_fqn(&*conn, name.clone()), "Couldn't find requested user", |user| { + let user_id = user.id.clone(); - Template::render("users/followers", json!({ - "user": serde_json::to_value(user.clone()).unwrap(), - "instance_url": user.get_instance(&*conn).public_domain, - "is_remote": user.instance_id != Instance::local_id(&*conn), - "follows": account.clone().map(|x| x.is_following(&*conn, user.id)).unwrap_or(false), - "followers": user.get_followers(&*conn).into_iter().map(|f| f.to_json(&*conn)).collect::>(), - "account": account, - "is_self": account.map(|a| a.id == user_id).unwrap_or(false), - "n_followers": user.get_followers(&*conn).len() - })) + Template::render("users/followers", json!({ + "user": serde_json::to_value(user.clone()).unwrap(), + "instance_url": user.get_instance(&*conn).public_domain, + "is_remote": user.instance_id != Instance::local_id(&*conn), + "follows": account.clone().map(|x| x.is_following(&*conn, user.id)).unwrap_or(false), + "followers": user.get_followers(&*conn).into_iter().map(|f| f.to_json(&*conn)).collect::>(), + "account": account, + "is_self": account.map(|a| a.id == user_id).unwrap_or(false), + "n_followers": user.get_followers(&*conn).len() + })) + }) } #[get("/@/", format = "application/activity+json", rank = 1)] From 0dfc303c836b60bf3026055067ec4d532eef8304 Mon Sep 17 00:00:00 2001 From: Bat Date: Mon, 18 Jun 2018 18:44:18 +0100 Subject: [PATCH 12/17] Fix Blog::find_by_name --- po/en.po | 21 +++++++++++++++++++++ po/fr.po | 22 ++++++++++++++++++++++ po/pl.po | 38 +++++++++++++++++++++++++++++++++----- src/models/blogs.rs | 2 +- src/routes/mod.rs | 2 +- 5 files changed, 78 insertions(+), 7 deletions(-) diff --git a/po/en.po b/po/en.po index 2634ce4e..31d93560 100644 --- a/po/en.po +++ b/po/en.po @@ -253,3 +253,24 @@ msgstr "" msgid "You need to be logged in order to edit your profile" msgstr "" + +msgid "By {{ link_1 }}{{ link_2 }}{{ link_3 }}{{ name }}{{ link_4 }}" +msgstr "" + +msgid "{{ data }} reshared your article" +msgstr "" + +msgid "{{ data }} started following you" +msgstr "" + +msgid "{{ data }} liked your article" +msgstr "" + +msgid "{{ data }} commented your article" +msgstr "" + +msgid "We couldn't find this page." +msgstr "" + +msgid "The link that led you here may be broken." +msgstr "" diff --git a/po/fr.po b/po/fr.po index 55a2a729..22d82224 100644 --- a/po/fr.po +++ b/po/fr.po @@ -252,3 +252,25 @@ msgstr "Vous devez vous connecter pour suivre quelqu'un" msgid "You need to be logged in order to edit your profile" msgstr "Vous devez vous connecter pour modifier votre profil" + +#, fuzzy +msgid "By {{ link_1 }}{{ link_2 }}{{ link_3 }}{{ name }}{{ link_4 }}" +msgstr "Écrit par {{ link_1 }}{{ url }}{{ link_2 }}{{ name }}{{ link_3 }}" + +msgid "{{ data }} reshared your article" +msgstr "" + +msgid "{{ data }} started following you" +msgstr "" + +msgid "{{ data }} liked your article" +msgstr "" + +msgid "{{ data }} commented your article" +msgstr "" + +msgid "We couldn't find this page." +msgstr "" + +msgid "The link that led you here may be broken." +msgstr "" diff --git a/po/pl.po b/po/pl.po index 9340090a..a319828f 100644 --- a/po/pl.po +++ b/po/pl.po @@ -10,7 +10,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " "|| n%100>=20) ? 1 : 2);\n" +"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " +"|| n%100>=20) ? 1 : 2);\n" msgid "Latest articles" msgstr "Najnowsze artykuły" @@ -46,7 +47,8 @@ msgid "Something broke on our side." msgstr "Coś poszło nie tak." msgid "Sorry about that. If you think this is a bug, please report it." -msgstr "Przepraszamy. Jeżeli uważasz że wystąpił błąd, prosimy o zgłoszenie go." +msgstr "" +"Przepraszamy. Jeżeli uważasz że wystąpił błąd, prosimy o zgłoszenie go." msgid "Configuration" msgstr "Konfiguracja" @@ -114,8 +116,8 @@ msgstr "Utwórz wpis" msgid "Publish" msgstr "Opublikuj" -msgid "Logowanie" -msgstr "Zaloguj się" +msgid "Login" +msgstr "" msgid "Username or email" msgstr "Nazwa użytkownika lub adres e-mail" @@ -133,7 +135,9 @@ msgid "Your Blogs" msgstr "Twoje blogi" msgid "You don't have any blog yet. Create your own, or ask to join one." -msgstr "Nie posiadasz żadnego bloga. Utwórz własny, lub poproś o dołączanie do istniejącego." +msgstr "" +"Nie posiadasz żadnego bloga. Utwórz własny, lub poproś o dołączanie do " +"istniejącego." msgid "Start a new blog" msgstr "Utwórz nowy blog" @@ -252,3 +256,27 @@ msgstr "Musisz się zalogować, aby zacząć obserwować innych" msgid "You need to be logged in order to edit your profile" msgstr "Musisz się zalogować , aby móc edytować swój profil" +#, fuzzy +msgid "By {{ link_1 }}{{ link_2 }}{{ link_3 }}{{ name }}{{ link_4 }}" +msgstr "Napisano przez {{ link_1 }}{{ url }}{{ link_2 }}{{ name }}{{ link_3 }}" + +msgid "{{ data }} reshared your article" +msgstr "" + +msgid "{{ data }} started following you" +msgstr "" + +msgid "{{ data }} liked your article" +msgstr "" + +msgid "{{ data }} commented your article" +msgstr "" + +msgid "We couldn't find this page." +msgstr "" + +msgid "The link that led you here may be broken." +msgstr "" + +#~ msgid "Logowanie" +#~ msgstr "Zaloguj się" diff --git a/src/models/blogs.rs b/src/models/blogs.rs index ee1964d3..529bd43d 100644 --- a/src/models/blogs.rs +++ b/src/models/blogs.rs @@ -67,7 +67,7 @@ impl Blog { .expect("Couldn't load blogs ") } - find_by!(blogs, find_by_name, ap_url as String, instance_id as i32); + find_by!(blogs, find_by_name, actor_id as String, instance_id as i32); pub fn find_local(conn: &PgConnection, name: String) -> Option { Blog::find_by_name(conn, name, Instance::local_id(conn)) diff --git a/src/routes/mod.rs b/src/routes/mod.rs index 89ab2c00..6c33f75b 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -9,7 +9,7 @@ macro_rules! may_fail { let $res = res.unwrap(); $block } else { - Template::render(concat!("errors/", stringify!($template)), json!({ + Template::render(concat!("errors/", $template), json!({ "error_message": $msg })) } From 5415b70854dac7030cb5d827a41f00eafef324f4 Mon Sep 17 00:00:00 2001 From: Bat Date: Mon, 18 Jun 2018 22:50:40 +0100 Subject: [PATCH 13/17] Use the webfinger crate --- Cargo.lock | 13 +++++++++ Cargo.toml | 1 + src/main.rs | 1 + src/models/blogs.rs | 59 +++++++++++++++++++--------------------- src/models/users.rs | 54 ++++++++++++++++++------------------ src/routes/well_known.rs | 51 ++++++++++++++++++---------------- 6 files changed, 97 insertions(+), 82 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 272d796f..1708ea54 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1020,6 +1020,7 @@ dependencies = [ "serde_json 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", "tera 0.11.7 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "webfinger 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1913,6 +1914,17 @@ name = "void" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "webfinger" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "reqwest 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.43 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "winapi" version = "0.2.8" @@ -2176,6 +2188,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" "checksum version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6b772017e347561807c1aa192438c5fd74242a670a6cffacc40f2defd1dc069d" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +"checksum webfinger 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "27a4e6d1de7050af8beb026c02bcef5340ec1f3af6d4a02248b7990908baa3ff" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" diff --git a/Cargo.toml b/Cargo.toml index 27ebfa1a..87c2e879 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ serde_derive = "1.0" serde_json = "1.0" tera = "0.11" url = "1.7" +webfinger = "0.1" [dependencies.chrono] features = ["serde"] diff --git a/src/main.rs b/src/main.rs index 7a5fac5a..5455d854 100644 --- a/src/main.rs +++ b/src/main.rs @@ -33,6 +33,7 @@ extern crate serde_derive; extern crate serde_json; extern crate tera; extern crate url; +extern crate webfinger; use diesel::{pg::PgConnection, r2d2::{ConnectionManager, Pool}}; use dotenv::dotenv; diff --git a/src/models/blogs.rs b/src/models/blogs.rs index 529bd43d..dc779f6e 100644 --- a/src/models/blogs.rs +++ b/src/models/blogs.rs @@ -14,13 +14,13 @@ use openssl::{ rsa::Rsa, sign::Signer }; +use webfinger::*; use activity_pub::{ ActivityStream, Id, IntoId, actor::{Actor as APActor, ActorType}, inbox::WithInbox, - sign, - webfinger::* + sign }; use models::instance::*; use schema::blogs; @@ -91,9 +91,9 @@ impl Blog { fn fetch_from_webfinger(conn: &PgConnection, acct: String) -> Option { match resolve(acct.clone()) { - Ok(url) => Blog::fetch_from_url(conn, url), + Ok(wf) => wf.links.into_iter().find(|l| l.mime_type == Some(String::from("application/activity+json"))).and_then(|l| Blog::fetch_from_url(conn, l.href)), Err(details) => { - println!("{}", details); + println!("{:?}", details); None } } @@ -171,6 +171,30 @@ impl Blog { pub fn get_keypair(&self) -> PKey { PKey::from_rsa(Rsa::private_key_from_pem(self.private_key.clone().unwrap().as_ref()).unwrap()).unwrap() } + + pub fn webfinger(&self, conn: &PgConnection) -> Webfinger { + Webfinger { + subject: format!("acct:{}@{}", self.actor_id, self.get_instance(conn).public_domain), + aliases: vec![self.compute_id(conn)], + links: vec![ + Link { + rel: String::from("http://webfinger.net/rel/profile-page"), + mime_type: None, + href: self.compute_id(conn) + }, + Link { + rel: String::from("http://schemas.google.com/g/2010#updates-from"), + mime_type: Some(String::from("application/atom+xml")), + href: self.compute_box(conn, "feed.atom") + }, + Link { + rel: String::from("self"), + mime_type: Some(String::from("application/activity+json")), + href: self.compute_id(conn) + } + ] + } + } } impl IntoId for Blog { @@ -234,33 +258,6 @@ impl APActor for Blog { } } -impl Webfinger for Blog { - fn webfinger_subject(&self, conn: &PgConnection) -> String { - format!("acct:{}@{}", self.actor_id, self.get_instance(conn).public_domain) - } - fn webfinger_aliases(&self, conn: &PgConnection) -> Vec { - vec![self.compute_id(conn)] - } - fn webfinger_links(&self, conn: &PgConnection) -> Vec> { - vec![ - vec![ - (String::from("rel"), String::from("http://webfinger.net/rel/profile-page")), - (String::from("href"), self.compute_id(conn)) - ], - vec![ - (String::from("rel"), String::from("http://schemas.google.com/g/2010#updates-from")), - (String::from("type"), String::from("application/atom+xml")), - (String::from("href"), self.compute_box(conn, "feed.atom")) - ], - vec![ - (String::from("rel"), String::from("self")), - (String::from("type"), String::from("application/activity+json")), - (String::from("href"), self.compute_id(conn)) - ] - ] - } -} - impl sign::Signer for Blog { fn get_key_id(&self, conn: &PgConnection) -> String { format!("{}#main-key", self.compute_id(conn)) diff --git a/src/models/users.rs b/src/models/users.rs index dc37ff95..e2543117 100644 --- a/src/models/users.rs +++ b/src/models/users.rs @@ -24,6 +24,7 @@ use rocket::{ }; use serde_json; use url::Url; +use webfinger::*; use BASE_URL; use activity_pub::{ @@ -31,7 +32,7 @@ use activity_pub::{ actor::{ActorType, Actor as APActor}, inbox::{Inbox, WithInbox}, sign::{Signer, gen_keypair}, - webfinger::{Webfinger, resolve} + webfinger::{resolve} }; use db_conn::DbConn; use models::{ @@ -336,6 +337,30 @@ impl User { json["fqn"] = serde_json::Value::String(self.get_fqn(conn)); json } + + pub fn webfinger(&self, conn: &PgConnection) -> Webfinger { + Webfinger { + subject: format!("acct:{}@{}", self.username, self.get_instance(conn).public_domain), + aliases: vec![self.compute_id(conn)], + links: vec![ + Link { + rel: String::from("http://webfinger.net/rel/profile-page"), + mime_type: None, + href: self.compute_id(conn) + }, + Link { + rel: String::from("http://schemas.google.com/g/2010#updates-from"), + mime_type: Some(String::from("application/atom+xml")), + href: self.compute_box(conn, "feed.atom") + }, + Link { + rel: String::from("self"), + mime_type: Some(String::from("application/activity+json")), + href: self.compute_id(conn) + } + ] + } + } } impl<'a, 'r> FromRequest<'a, 'r> for User { @@ -445,33 +470,6 @@ impl Inbox for User { } } -impl Webfinger for User { - fn webfinger_subject(&self, conn: &PgConnection) -> String { - format!("acct:{}@{}", self.username, self.get_instance(conn).public_domain) - } - fn webfinger_aliases(&self, conn: &PgConnection) -> Vec { - vec![self.compute_id(conn)] - } - fn webfinger_links(&self, conn: &PgConnection) -> Vec> { - vec![ - vec![ - (String::from("rel"), String::from("http://webfinger.net/rel/profile-page")), - (String::from("href"), self.compute_id(conn)) - ], - vec![ - (String::from("rel"), String::from("http://schemas.google.com/g/2010#updates-from")), - (String::from("type"), String::from("application/atom+xml")), - (String::from("href"), self.compute_box(conn, "feed.atom")) - ], - vec![ - (String::from("rel"), String::from("self")), - (String::from("type"), String::from("application/activity+json")), - (String::from("href"), self.compute_id(conn)) - ] - ] - } -} - impl Signer for User { fn get_key_id(&self, conn: &PgConnection) -> String { format!("{}#main-key", self.compute_id(conn)) diff --git a/src/routes/well_known.rs b/src/routes/well_known.rs index 15b3ad2a..ec9710a2 100644 --- a/src/routes/well_known.rs +++ b/src/routes/well_known.rs @@ -1,8 +1,10 @@ use rocket::http::ContentType; use rocket::response::Content; +use serde_json; +use webfinger::*; use BASE_URL; -use activity_pub::{ap_url, webfinger::Webfinger}; +use activity_pub::ap_url; use db_conn::DbConn; use models::{blogs::Blog, users::User}; @@ -33,29 +35,32 @@ struct WebfingerQuery { resource: String } -#[get("/.well-known/webfinger?")] -fn webfinger(query: WebfingerQuery, conn: DbConn) -> Content> { - let mut parsed_query = query.resource.splitn(2, ":"); - let res_type = parsed_query.next().unwrap(); - let res = parsed_query.next().unwrap(); - if res_type == "acct" { - let mut parsed_res = res.split("@"); - let user = parsed_res.next().unwrap(); - let res_dom = parsed_res.next().unwrap(); +struct WebfingerResolver; - if res_dom == BASE_URL.as_str() { - let res = match User::find_local(&*conn, String::from(user)) { - Some(usr) => Ok(usr.webfinger(&*conn)), - None => match Blog::find_local(&*conn, String::from(user)) { - Some(blog) => Ok(blog.webfinger(&*conn)), - None => Err("Requested actor not found") - } - }; - Content(ContentType::new("application", "jrd+json"), res) - } else { - Content(ContentType::new("text", "plain"), Err("Invalid instance")) +impl Resolver for WebfingerResolver { + fn instance_domain<'a>() -> &'a str { + BASE_URL.as_str() + } + + fn find(acct: String, conn: DbConn) -> Result { + match User::find_local(&*conn, acct.clone()) { + Some(usr) => Ok(usr.webfinger(&*conn)), + None => match Blog::find_local(&*conn, acct) { + Some(blog) => Ok(blog.webfinger(&*conn)), + None => Err(ResolverError::NotFound) + } } - } else { - Content(ContentType::new("text", "plain"), Err("Invalid resource type. Only acct is supported")) + } +} + +#[get("/.well-known/webfinger?")] +fn webfinger(query: WebfingerQuery, conn: DbConn) -> Content { + match WebfingerResolver::endpoint(query.resource, conn).and_then(|wf| serde_json::to_string(&wf).map_err(|_| ResolverError::NotFound)) { + Ok(wf) => Content(ContentType::new("application", "jrd+json"), wf), + Err(err) => Content(ContentType::new("text", "plain"), String::from(match err { + ResolverError::InvalidResource => "Invalid resource. Make sure to request an acct: URI", + ResolverError::NotFound => "Requested resource was not found", + ResolverError::WrongInstance => "This is not the instance of the requested resource" + })) } } From 78be09c47cff99b53011c42ef315b49f21b02af5 Mon Sep 17 00:00:00 2001 From: Bat Date: Mon, 18 Jun 2018 22:54:13 +0100 Subject: [PATCH 14/17] Use the webfinger crate for fetching resources too --- src/activity_pub/mod.rs | 1 - src/activity_pub/webfinger.rs | 52 ----------------------------------- src/models/users.rs | 7 ++--- 3 files changed, 3 insertions(+), 57 deletions(-) delete mode 100644 src/activity_pub/webfinger.rs diff --git a/src/activity_pub/mod.rs b/src/activity_pub/mod.rs index 51d12d3e..f4c4a06a 100644 --- a/src/activity_pub/mod.rs +++ b/src/activity_pub/mod.rs @@ -17,7 +17,6 @@ pub mod inbox; pub mod object; pub mod request; pub mod sign; -pub mod webfinger; pub type ActivityPub = Content>; diff --git a/src/activity_pub/webfinger.rs b/src/activity_pub/webfinger.rs deleted file mode 100644 index 7803fa2e..00000000 --- a/src/activity_pub/webfinger.rs +++ /dev/null @@ -1,52 +0,0 @@ -use diesel::PgConnection; -use reqwest::Client; -use reqwest::{ - header::{Accept, qitem}, - mime::Mime -}; -use serde_json; - -use activity_pub::ap_url; - -pub trait Webfinger { - fn webfinger_subject(&self, conn: &PgConnection) -> String; - fn webfinger_aliases(&self, conn: &PgConnection) -> Vec; - fn webfinger_links(&self, conn: &PgConnection) -> Vec>; - - fn webfinger(&self, conn: &PgConnection) -> String { - json!({ - "subject": self.webfinger_subject(conn), - "aliases": self.webfinger_aliases(conn), - "links": self.webfinger_links(conn).into_iter().map(|link| { - let mut link_obj = serde_json::Map::new(); - for (k, v) in link { - link_obj.insert(k, serde_json::Value::String(v)); - } - serde_json::Value::Object(link_obj) - }).collect::>() - }).to_string() - } -} - -pub fn resolve(acct: String) -> Result { - let instance = acct.split("@").last().unwrap(); - let url = ap_url(format!("{}/.well-known/webfinger?resource=acct:{}", instance, acct)); - Client::new() - .get(&url[..]) - .header(Accept(vec![qitem("application/jrd+json".parse::().unwrap())])) - .send() - .map(|mut r| { - let res = r.text().unwrap(); - let json: serde_json::Value = serde_json::from_str(&res[..]).unwrap(); - json["links"].as_array().unwrap() - .into_iter() - .find_map(|link| { - if link["rel"].as_str().unwrap() == "self" && link["type"].as_str().unwrap() == "application/activity+json" { - Some(String::from(link["href"].as_str().unwrap())) - } else { - None - } - }).unwrap() - }) - .map_err(|e| format!("Error while fetchin WebFinger resource ({})", e)) -} diff --git a/src/models/users.rs b/src/models/users.rs index e2543117..5b5b1c10 100644 --- a/src/models/users.rs +++ b/src/models/users.rs @@ -31,8 +31,7 @@ use activity_pub::{ ap_url, ActivityStream, Id, IntoId, actor::{ActorType, Actor as APActor}, inbox::{Inbox, WithInbox}, - sign::{Signer, gen_keypair}, - webfinger::{resolve} + sign::{Signer, gen_keypair} }; use db_conn::DbConn; use models::{ @@ -140,9 +139,9 @@ impl User { fn fetch_from_webfinger(conn: &PgConnection, acct: String) -> Option { match resolve(acct.clone()) { - Ok(url) => User::fetch_from_url(conn, url), + Ok(wf) => wf.links.into_iter().find(|l| l.mime_type == Some(String::from("application/activity+json"))).and_then(|l| User::fetch_from_url(conn, l.href)), Err(details) => { - println!("{}", details); + println!("{:?}", details); None } } From 123bbef2eaefe2482329c18f0b7a29a73f00d1da Mon Sep 17 00:00:00 2001 From: fdb-hiroshima <35889323+fdb-hiroshima@users.noreply.github.com> Date: Tue, 19 Jun 2018 09:55:52 +0200 Subject: [PATCH 15/17] Correcting wrong translation --- po/fr.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/po/fr.po b/po/fr.po index 22d82224..000b8152 100644 --- a/po/fr.po +++ b/po/fr.po @@ -19,7 +19,7 @@ msgid "No posts to see here yet." msgstr "Aucun article pour le moment" msgid "New article" -msgstr "Derniers articles" +msgstr "Nouvel articles" msgid "New blog" msgstr "Nouveau blog" From 82d3afe7b6e97d8ddf91d55ffa7a68bfc59c0fe1 Mon Sep 17 00:00:00 2001 From: Bat Date: Tue, 19 Jun 2018 10:47:11 +0100 Subject: [PATCH 16/17] Implement Deletable for Reshare --- src/models/reshares.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/models/reshares.rs b/src/models/reshares.rs index 86ead790..09e75bfa 100644 --- a/src/models/reshares.rs +++ b/src/models/reshares.rs @@ -2,7 +2,7 @@ use activitypub::activity::{Announce, Undo}; use chrono::NaiveDateTime; use diesel::{self, PgConnection, QueryDsl, RunQueryDsl, ExpressionMethods}; -use activity_pub::{Id, IntoId, actor::Actor, inbox::{FromActivity, Notify}, object::Object}; +use activity_pub::{Id, IntoId, actor::Actor, inbox::{FromActivity, Notify, Deletable}, object::Object}; use models::{notifications::*, posts::Post, users::User}; use schema::reshares; @@ -102,3 +102,14 @@ impl Notify for Reshare { } } } + +impl Deletable for Reshare { + fn delete_activity(conn: &PgConnection, id: Id) -> bool { + if let Some(reshare) = Reshare::find_by_ap_url(conn, id.into()) { + reshare.delete(conn); + true + } else { + false + } + } +} From 7fc469fa96e69244c6b5e8b864c82c67d2043de2 Mon Sep 17 00:00:00 2001 From: Bat Date: Tue, 19 Jun 2018 10:50:01 +0100 Subject: [PATCH 17/17] Delete reshares when receiving the corresponding Undo activity --- src/activity_pub/inbox.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/activity_pub/inbox.rs b/src/activity_pub/inbox.rs index 1d8fe0a6..a20af1ea 100644 --- a/src/activity_pub/inbox.rs +++ b/src/activity_pub/inbox.rs @@ -1,6 +1,6 @@ use activitypub::{ Object, - activity::{Create, Like, Undo} + activity::{Announce, Create, Like, Undo} }; use diesel::PgConnection; use failure::Error; @@ -90,6 +90,10 @@ pub trait Inbox { likes::Like::delete_activity(conn, Id::new(act.undo_props.object_object::()?.object_props.id_string()?)); Ok(()) }, + "Announce" => { + Reshare::delete_activity(conn, Id::new(act.undo_props.object_object::()?.object_props.id_string()?)); + Ok(()) + } _ => Err(InboxError::CantUndo)? } }