Make it possible to display remote blogs

This commit is contained in:
Bat 2018-05-13 18:00:47 +01:00
parent fdc481e384
commit 5e7d513a7e
4 changed files with 79 additions and 9 deletions

View file

@ -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 chrono::NaiveDateTime;
use diesel::{self, QueryDsl, RunQueryDsl, ExpressionMethods, PgConnection}; use diesel::{self, QueryDsl, RunQueryDsl, ExpressionMethods, PgConnection};
use openssl::hash::MessageDigest; use openssl::hash::MessageDigest;
@ -60,14 +65,79 @@ impl Blog {
.into_iter().nth(0) .into_iter().nth(0)
} }
pub fn find_by_actor_id(conn: &PgConnection, username: String) -> Option<Blog> { pub fn find_by_name(conn: &PgConnection, name: String, instance_id: i32) -> Option<Blog> {
blogs::table.filter(blogs::actor_id.eq(username)) blogs::table.filter(blogs::actor_id.eq(name))
.filter(blogs::instance_id.eq(instance_id))
.limit(1) .limit(1)
.load::<Blog>(conn) .load::<Blog>(conn)
.expect("Error loading blog by actor_id") .expect("Error loading blog by name")
.into_iter().nth(0) .into_iter().nth(0)
} }
pub fn find_local(conn: &PgConnection, name: String) -> Option<Blog> {
Blog::find_by_name(conn, name, Instance::local_id(conn))
}
pub fn find_by_fqn(conn: &PgConnection, fqn: String) -> Option<Blog> {
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<Blog> {
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<Blog> {
let req = Client::new()
.get(&url[..])
.header(Accept(vec![qitem("application/activity+json".parse::<Mime>().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) { pub fn update_boxes(&self, conn: &PgConnection) {
if self.outbox_url.len() == 0 { if self.outbox_url.len() == 0 {
diesel::update(self) diesel::update(self)

View file

@ -16,7 +16,7 @@ use utils;
#[get("/~/<name>", rank = 2)] #[get("/~/<name>", rank = 2)]
fn details(name: String, conn: DbConn, user: Option<User>) -> Template { fn details(name: String, conn: DbConn, user: Option<User>) -> 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); let recents = Post::get_recents_for_blog(&*conn, &blog, 5);
Template::render("blogs/details", json!({ Template::render("blogs/details", json!({
"blog": blog, "blog": blog,
@ -34,7 +34,7 @@ fn details(name: String, conn: DbConn, user: Option<User>) -> Template {
#[get("/~/<name>", format = "application/activity+json", rank = 1)] #[get("/~/<name>", format = "application/activity+json", rank = 1)]
fn activity_details(name: String, conn: DbConn) -> ActivityPub { 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) blog.as_activity_pub(&*conn)
} }
@ -74,6 +74,6 @@ fn create(conn: DbConn, data: Form<NewBlogForm>, user: User) -> Redirect {
#[get("/~/<name>/outbox")] #[get("/~/<name>/outbox")]
fn outbox(name: String, conn: DbConn) -> 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) blog.outbox(&*conn)
} }

View file

@ -18,7 +18,7 @@ use utils;
#[get("/~/<blog>/<slug>", rank = 4)] #[get("/~/<blog>/<slug>", rank = 4)]
fn details(blog: String, slug: String, conn: DbConn, user: Option<User>) -> Template { fn details(blog: String, slug: String, conn: DbConn, user: Option<User>) -> 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 post = Post::find_by_slug(&*conn, slug).unwrap();
let comments = Comment::find_by_post(&*conn, post.id); let comments = Comment::find_by_post(&*conn, post.id);
Template::render("posts/details", json!({ Template::render("posts/details", json!({
@ -68,7 +68,7 @@ struct NewPostForm {
#[post("/~/<blog_name>/new", data = "<data>")] #[post("/~/<blog_name>/new", data = "<data>")]
fn create(blog_name: String, data: Form<NewPostForm>, user: User, conn: DbConn) -> Redirect { fn create(blog_name: String, data: Form<NewPostForm>, 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 form = data.get();
let slug = form.title.to_string().to_kebab_case(); let slug = form.title.to_string().to_kebab_case();
let post = Post::insert(&*conn, NewPost { let post = Post::insert(&*conn, NewPost {

View file

@ -36,7 +36,7 @@ fn webfinger(query: WebfingerQuery, conn: DbConn) -> Content<Result<String, &'st
if res_dom == BASE_URL.as_str() { if res_dom == BASE_URL.as_str() {
let res = match User::find_local(&*conn, String::from(user)) { let res = match User::find_local(&*conn, String::from(user)) {
Some(usr) => Ok(usr.webfinger(&*conn)), Some(usr) => 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)), Some(blog) => Ok(blog.webfinger(&*conn)),
None => Err("Requested actor not found") None => Err("Requested actor not found")
} }