mirror of
https://git.joinplu.me/Plume/Plume.git
synced 2024-11-26 05:21:00 +00:00
Display remote profiles!
This commit is contained in:
parent
44473aa292
commit
8047df6848
10 changed files with 112 additions and 13 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -144,6 +144,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
dependencies = [
|
||||
"num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ version = "0.1.0"
|
|||
[dependencies]
|
||||
base64 = "0.9.1"
|
||||
bcrypt = "0.2"
|
||||
chrono = "0.4"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
dotenv = "*"
|
||||
heck = "0.3.0"
|
||||
hex = "0.3"
|
||||
|
|
|
@ -25,7 +25,7 @@ pub trait Webfinger {
|
|||
}
|
||||
|
||||
pub fn resolve(acct: String) -> Result<String, String> {
|
||||
let instance = acct.split("@").next().unwrap();
|
||||
let instance = acct.split("@").last().unwrap();
|
||||
let url = format!("https://{}/.well-known/webfinger?resource=acct:{}", instance, acct);
|
||||
Client::new()
|
||||
.get(&url[..])
|
||||
|
@ -37,12 +37,12 @@ pub fn resolve(acct: String) -> Result<String, String> {
|
|||
json["links"].as_array().unwrap()
|
||||
.into_iter()
|
||||
.find_map(|link| {
|
||||
if link["rel"].as_str().unwrap() == "self" && link["href"].as_str().unwrap() == "application/activity+json" {
|
||||
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(|_| String::from("Error while fetchin WebFinger resource"))
|
||||
.map_err(|e| format!("Error while fetchin WebFinger resource ({})", e))
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@ extern crate rocket;
|
|||
extern crate rocket_contrib;
|
||||
extern crate serde;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
#[macro_use]
|
||||
extern crate serde_json;
|
||||
|
||||
use diesel::pg::PgConnection;
|
||||
|
|
|
@ -34,6 +34,10 @@ impl Instance {
|
|||
.into_iter().nth(0)
|
||||
}
|
||||
|
||||
pub fn local_id(conn: &PgConnection) -> i32 {
|
||||
Instance::get_local(conn).unwrap().id
|
||||
}
|
||||
|
||||
pub fn insert<'a>(conn: &PgConnection, loc_dom: String, pub_dom: String, name: String, local: bool) -> Instance {
|
||||
diesel::insert_into(instances::table)
|
||||
.values(NewInstance {
|
||||
|
@ -54,6 +58,14 @@ impl Instance {
|
|||
.into_iter().nth(0)
|
||||
}
|
||||
|
||||
pub fn get_by_domain(conn: &PgConnection, domain: String) -> Option<Instance> {
|
||||
instances::table.filter(instances::public_domain.eq(domain))
|
||||
.limit(1)
|
||||
.load::<Instance>(conn)
|
||||
.expect("Couldn't load instance by domain")
|
||||
.into_iter().nth(0)
|
||||
}
|
||||
|
||||
pub fn block(&self) {}
|
||||
|
||||
pub fn has_admin(&self, conn: &PgConnection) -> bool {
|
||||
|
|
|
@ -2,13 +2,17 @@ use bcrypt;
|
|||
use chrono::NaiveDateTime;
|
||||
use diesel::{self, QueryDsl, RunQueryDsl, ExpressionMethods, BelongingToDsl, PgConnection};
|
||||
use diesel::dsl::any;
|
||||
use reqwest::Client;
|
||||
use reqwest::header::{Accept, qitem};
|
||||
use reqwest::mime::Mime;
|
||||
use rocket::request::{self, FromRequest, Request};
|
||||
use rocket::outcome::IntoOutcome;
|
||||
use serde_json;
|
||||
|
||||
use activity_pub::activity::Activity;
|
||||
use activity_pub::actor::{ActorType, Actor};
|
||||
use activity_pub::outbox::Outbox;
|
||||
use activity_pub::webfinger::Webfinger;
|
||||
use activity_pub::webfinger::{Webfinger, resolve};
|
||||
use db_conn::DbConn;
|
||||
use models::instance::Instance;
|
||||
use models::post_authors::PostAuthor;
|
||||
|
@ -17,7 +21,7 @@ use schema::users;
|
|||
|
||||
pub const AUTH_COOKIE: &'static str = "user_id";
|
||||
|
||||
#[derive(Queryable, Identifiable)]
|
||||
#[derive(Queryable, Identifiable, Serialize)]
|
||||
pub struct User {
|
||||
pub id: i32,
|
||||
pub username: String,
|
||||
|
@ -72,14 +76,77 @@ impl User {
|
|||
.into_iter().nth(0)
|
||||
}
|
||||
|
||||
pub fn find_by_name(conn: &PgConnection, username: String) -> Option<User> {
|
||||
pub fn find_by_name(conn: &PgConnection, username: String, instance_id: i32) -> Option<User> {
|
||||
users::table.filter(users::username.eq(username))
|
||||
.filter(users::instance_id.eq(instance_id))
|
||||
.limit(1)
|
||||
.load::<User>(conn)
|
||||
.expect("Error loading user by email")
|
||||
.into_iter().nth(0)
|
||||
}
|
||||
|
||||
pub fn find_local(conn: &PgConnection, username: String) -> Option<User> {
|
||||
User::find_by_name(conn, username, Instance::local_id(conn))
|
||||
}
|
||||
|
||||
pub fn find_by_fqn(conn: &PgConnection, fqn: String) -> Option<User> {
|
||||
if fqn.contains("@") { // remote user
|
||||
match Instance::get_by_domain(conn, String::from(fqn.split("@").last().unwrap())) {
|
||||
Some(instance) => {
|
||||
match User::find_by_name(conn, String::from(fqn.split("@").nth(0).unwrap()), instance.id) {
|
||||
Some(u) => Some(u),
|
||||
None => User::fetch_from_webfinger(conn, fqn)
|
||||
}
|
||||
},
|
||||
None => User::fetch_from_webfinger(conn, fqn)
|
||||
}
|
||||
} else { // local user
|
||||
User::find_local(conn, fqn)
|
||||
}
|
||||
}
|
||||
|
||||
fn fetch_from_webfinger(conn: &PgConnection, acct: String) -> Option<User> {
|
||||
match resolve(acct) {
|
||||
Ok(url) => {
|
||||
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(User::from_activity(conn, json, url.split("@").last().unwrap().to_string()))
|
||||
},
|
||||
Err(_) => None
|
||||
}
|
||||
},
|
||||
Err(details) => {
|
||||
println!("{}", details);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn from_activity(conn: &PgConnection, acct: serde_json::Value, inst: String) -> User {
|
||||
let instance = match Instance::get_by_domain(conn, inst.clone()) {
|
||||
Some(instance) => instance,
|
||||
None => {
|
||||
Instance::insert(conn, String::from(""), inst.clone(), inst.clone(), false)
|
||||
}
|
||||
};
|
||||
User::insert(conn, NewUser {
|
||||
username: acct["preferredUsername"].as_str().unwrap().to_string(),
|
||||
display_name: 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(),
|
||||
is_admin: false,
|
||||
summary: acct["summary"].as_str().unwrap().to_string(),
|
||||
email: None,
|
||||
hashed_password: None,
|
||||
instance_id: instance.id
|
||||
})
|
||||
}
|
||||
|
||||
pub fn hash_pass(pass: String) -> String {
|
||||
bcrypt::hash(pass.as_str(), bcrypt::DEFAULT_COST).unwrap()
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ fn create(conn: DbConn, data: Form<LoginForm>, mut cookies: Cookies) -> Result<R
|
|||
let form = data.get();
|
||||
let user = match User::find_by_email(&*conn, form.email_or_name.to_string()) {
|
||||
Some(usr) => Ok(usr),
|
||||
None => match User::find_by_name(&*conn, form.email_or_name.to_string()) {
|
||||
None => match User::find_local(&*conn, form.email_or_name.to_string()) {
|
||||
Some(usr) => Ok(usr),
|
||||
None => Err("Invalid username or password")
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use rocket::request::Form;
|
||||
use rocket::response::Redirect;
|
||||
use rocket_contrib::Template;
|
||||
use serde_json;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use activity_pub::ActivityPub;
|
||||
|
@ -16,13 +17,16 @@ fn me(user: User) -> String {
|
|||
}
|
||||
|
||||
#[get("/@/<name>", rank = 2)]
|
||||
fn details(name: String) -> String {
|
||||
format!("Hello, @{}", name)
|
||||
fn details(name: String, conn: DbConn) -> Template {
|
||||
let user = User::find_by_fqn(&*conn, name).unwrap();
|
||||
Template::render("users/details", json!({
|
||||
"user": serde_json::to_value(user).unwrap()
|
||||
}))
|
||||
}
|
||||
|
||||
#[get("/@/<name>", format = "application/activity+json", rank = 1)]
|
||||
fn activity_details(name: String, conn: DbConn) -> ActivityPub {
|
||||
let user = User::find_by_name(&*conn, name).unwrap();
|
||||
let user = User::find_local(&*conn, name).unwrap();
|
||||
user.as_activity_pub(&*conn)
|
||||
}
|
||||
|
||||
|
@ -61,6 +65,6 @@ fn create(conn: DbConn, data: Form<NewUserForm>) -> Redirect {
|
|||
|
||||
#[get("/@/<name>/outbox")]
|
||||
fn outbox(name: String, conn: DbConn) -> Outbox {
|
||||
let user = User::find_by_name(&*conn, name).unwrap();
|
||||
let user = User::find_local(&*conn, name).unwrap();
|
||||
user.outbox(&*conn)
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ fn webfinger(query: WebfingerQuery, conn: DbConn) -> Content<Result<String, &'st
|
|||
let domain = Instance::get_local(&*conn).unwrap().public_domain;
|
||||
|
||||
if res_dom == domain {
|
||||
let res = match User::find_by_name(&*conn, String::from(user)) {
|
||||
let res = match User::find_local(&*conn, String::from(user)) {
|
||||
Some(usr) => Ok(usr.webfinger(&*conn)),
|
||||
None => match Blog::find_by_actor_id(&*conn, String::from(user)) {
|
||||
Some(blog) => Ok(blog.webfinger(&*conn)),
|
||||
|
|
13
templates/users/details.tera
Normal file
13
templates/users/details.tera
Normal file
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>{{ user.display_name }}</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>{{ user.display_name }}</h1>
|
||||
<div>
|
||||
{{ user.summary | safe }}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in a new issue