From 5e7d513a7e5ef20945521eaf8a818ff06c55bd19 Mon Sep 17 00:00:00 2001 From: Bat Date: Sun, 13 May 2018 18:00:47 +0100 Subject: [PATCH] Make it possible to display remote blogs --- src/models/blogs.rs | 76 ++++++++++++++++++++++++++++++++++++++-- src/routes/blogs.rs | 6 ++-- src/routes/posts.rs | 4 +-- src/routes/well_known.rs | 2 +- 4 files changed, 79 insertions(+), 9 deletions(-) diff --git a/src/models/blogs.rs b/src/models/blogs.rs index 1dc3240..b258022 100644 --- a/src/models/blogs.rs +++ b/src/models/blogs.rs @@ -1,3 +1,8 @@ +use reqwest::Client; +use reqwest::header::{Accept, qitem}; +use reqwest::mime::Mime; +use serde_json; +use url::Url; use chrono::NaiveDateTime; use diesel::{self, QueryDsl, RunQueryDsl, ExpressionMethods, PgConnection}; use openssl::hash::MessageDigest; @@ -60,14 +65,79 @@ impl Blog { .into_iter().nth(0) } - pub fn find_by_actor_id(conn: &PgConnection, username: String) -> Option { - blogs::table.filter(blogs::actor_id.eq(username)) + 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 actor_id") + .expect("Error loading blog by name") .into_iter().nth(0) } + pub fn find_local(conn: &PgConnection, name: String) -> Option { + Blog::find_by_name(conn, name, Instance::local_id(conn)) + } + + pub fn find_by_fqn(conn: &PgConnection, fqn: String) -> Option { + if fqn.contains("@") { // remote blog + match Instance::find_by_domain(conn, String::from(fqn.split("@").last().unwrap())) { + Some(instance) => { + match Blog::find_by_name(conn, String::from(fqn.split("@").nth(0).unwrap()), instance.id) { + Some(u) => Some(u), + None => Blog::fetch_from_webfinger(conn, fqn) + } + }, + None => Blog::fetch_from_webfinger(conn, fqn) + } + } else { // local blog + Blog::find_local(conn, fqn) + } + } + + fn fetch_from_webfinger(conn: &PgConnection, acct: String) -> Option { + match resolve(acct.clone()) { + Ok(url) => Blog::fetch_from_url(conn, url), + Err(details) => { + println!("{}", details); + None + } + } + } + + fn fetch_from_url(conn: &PgConnection, url: String) -> Option { + let req = Client::new() + .get(&url[..]) + .header(Accept(vec![qitem("application/activity+json".parse::().unwrap())])) + .send(); + match req { + Ok(mut res) => { + let json: serde_json::Value = serde_json::from_str(&res.text().unwrap()).unwrap(); + Some(Blog::from_activity(conn, json, Url::parse(url.as_ref()).unwrap().host_str().unwrap().to_string())) + }, + Err(_) => None + } + } + + fn from_activity(conn: &PgConnection, acct: serde_json::Value, inst: String) -> Blog { + let instance = match Instance::find_by_domain(conn, inst.clone()) { + Some(instance) => instance, + None => { + Instance::insert(conn, inst.clone(), inst.clone(), false) + } + }; + Blog::insert(conn, NewBlog { + actor_id: acct["preferredUsername"].as_str().unwrap().to_string(), + title: acct["name"].as_str().unwrap().to_string(), + outbox_url: acct["outbox"].as_str().unwrap().to_string(), + inbox_url: acct["inbox"].as_str().unwrap().to_string(), + summary: acct["summary"].as_str().unwrap().to_string(), + instance_id: instance.id, + ap_url: acct["id"].as_str().unwrap().to_string(), + public_key: acct["publicKey"]["publicKeyPem"].as_str().unwrap_or("").to_string(), + private_key: None + }) + } + pub fn update_boxes(&self, conn: &PgConnection) { if self.outbox_url.len() == 0 { diesel::update(self) diff --git a/src/routes/blogs.rs b/src/routes/blogs.rs index 3b6ad88..f551304 100644 --- a/src/routes/blogs.rs +++ b/src/routes/blogs.rs @@ -16,7 +16,7 @@ use utils; #[get("/~/", rank = 2)] fn details(name: String, conn: DbConn, user: Option) -> Template { - let blog = Blog::find_by_actor_id(&*conn, name).unwrap(); + 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, @@ -34,7 +34,7 @@ fn details(name: String, conn: DbConn, user: Option) -> Template { #[get("/~/", format = "application/activity+json", rank = 1)] fn activity_details(name: String, conn: DbConn) -> ActivityPub { - let blog = Blog::find_by_actor_id(&*conn, name).unwrap(); + let blog = Blog::find_local(&*conn, name).unwrap(); blog.as_activity_pub(&*conn) } @@ -74,6 +74,6 @@ fn create(conn: DbConn, data: Form, user: User) -> Redirect { #[get("/~//outbox")] fn outbox(name: String, conn: DbConn) -> Outbox { - let blog = Blog::find_by_actor_id(&*conn, name).unwrap(); + let blog = Blog::find_local(&*conn, name).unwrap(); blog.outbox(&*conn) } diff --git a/src/routes/posts.rs b/src/routes/posts.rs index d187f6c..ce63d68 100644 --- a/src/routes/posts.rs +++ b/src/routes/posts.rs @@ -18,7 +18,7 @@ use utils; #[get("/~//", rank = 4)] fn details(blog: String, slug: String, conn: DbConn, user: Option) -> Template { - let blog = Blog::find_by_actor_id(&*conn, blog).unwrap(); + 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); Template::render("posts/details", json!({ @@ -68,7 +68,7 @@ struct NewPostForm { #[post("/~//new", data = "")] fn create(blog_name: String, data: Form, user: User, conn: DbConn) -> Redirect { - let blog = Blog::find_by_actor_id(&*conn, blog_name.to_string()).unwrap(); + let blog = Blog::find_by_fqn(&*conn, blog_name.to_string()).unwrap(); let form = data.get(); let slug = form.title.to_string().to_kebab_case(); let post = Post::insert(&*conn, NewPost { diff --git a/src/routes/well_known.rs b/src/routes/well_known.rs index 4436356..6f07a46 100644 --- a/src/routes/well_known.rs +++ b/src/routes/well_known.rs @@ -36,7 +36,7 @@ fn webfinger(query: WebfingerQuery, conn: DbConn) -> Content Ok(usr.webfinger(&*conn)), - None => match Blog::find_by_actor_id(&*conn, String::from(user)) { + None => match Blog::find_local(&*conn, String::from(user)) { Some(blog) => Ok(blog.webfinger(&*conn)), None => Err("Requested actor not found") }