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 = [
|
dependencies = [
|
||||||
"num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"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)",
|
"time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ version = "0.1.0"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
base64 = "0.9.1"
|
base64 = "0.9.1"
|
||||||
bcrypt = "0.2"
|
bcrypt = "0.2"
|
||||||
chrono = "0.4"
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
dotenv = "*"
|
dotenv = "*"
|
||||||
heck = "0.3.0"
|
heck = "0.3.0"
|
||||||
hex = "0.3"
|
hex = "0.3"
|
||||||
|
|
|
@ -25,7 +25,7 @@ pub trait Webfinger {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve(acct: String) -> Result<String, String> {
|
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);
|
let url = format!("https://{}/.well-known/webfinger?resource=acct:{}", instance, acct);
|
||||||
Client::new()
|
Client::new()
|
||||||
.get(&url[..])
|
.get(&url[..])
|
||||||
|
@ -37,12 +37,12 @@ pub fn resolve(acct: String) -> Result<String, String> {
|
||||||
json["links"].as_array().unwrap()
|
json["links"].as_array().unwrap()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.find_map(|link| {
|
.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()))
|
Some(String::from(link["href"].as_str().unwrap()))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}).unwrap()
|
}).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 rocket_contrib;
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
extern crate serde_derive;
|
||||||
|
#[macro_use]
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
|
|
||||||
use diesel::pg::PgConnection;
|
use diesel::pg::PgConnection;
|
||||||
|
|
|
@ -34,6 +34,10 @@ impl Instance {
|
||||||
.into_iter().nth(0)
|
.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 {
|
pub fn insert<'a>(conn: &PgConnection, loc_dom: String, pub_dom: String, name: String, local: bool) -> Instance {
|
||||||
diesel::insert_into(instances::table)
|
diesel::insert_into(instances::table)
|
||||||
.values(NewInstance {
|
.values(NewInstance {
|
||||||
|
@ -54,6 +58,14 @@ impl Instance {
|
||||||
.into_iter().nth(0)
|
.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 block(&self) {}
|
||||||
|
|
||||||
pub fn has_admin(&self, conn: &PgConnection) -> bool {
|
pub fn has_admin(&self, conn: &PgConnection) -> bool {
|
||||||
|
|
|
@ -2,13 +2,17 @@ use bcrypt;
|
||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
use diesel::{self, QueryDsl, RunQueryDsl, ExpressionMethods, BelongingToDsl, PgConnection};
|
use diesel::{self, QueryDsl, RunQueryDsl, ExpressionMethods, BelongingToDsl, PgConnection};
|
||||||
use diesel::dsl::any;
|
use diesel::dsl::any;
|
||||||
|
use reqwest::Client;
|
||||||
|
use reqwest::header::{Accept, qitem};
|
||||||
|
use reqwest::mime::Mime;
|
||||||
use rocket::request::{self, FromRequest, Request};
|
use rocket::request::{self, FromRequest, Request};
|
||||||
use rocket::outcome::IntoOutcome;
|
use rocket::outcome::IntoOutcome;
|
||||||
|
use serde_json;
|
||||||
|
|
||||||
use activity_pub::activity::Activity;
|
use activity_pub::activity::Activity;
|
||||||
use activity_pub::actor::{ActorType, Actor};
|
use activity_pub::actor::{ActorType, Actor};
|
||||||
use activity_pub::outbox::Outbox;
|
use activity_pub::outbox::Outbox;
|
||||||
use activity_pub::webfinger::Webfinger;
|
use activity_pub::webfinger::{Webfinger, resolve};
|
||||||
use db_conn::DbConn;
|
use db_conn::DbConn;
|
||||||
use models::instance::Instance;
|
use models::instance::Instance;
|
||||||
use models::post_authors::PostAuthor;
|
use models::post_authors::PostAuthor;
|
||||||
|
@ -17,7 +21,7 @@ use schema::users;
|
||||||
|
|
||||||
pub const AUTH_COOKIE: &'static str = "user_id";
|
pub const AUTH_COOKIE: &'static str = "user_id";
|
||||||
|
|
||||||
#[derive(Queryable, Identifiable)]
|
#[derive(Queryable, Identifiable, Serialize)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
pub username: String,
|
pub username: String,
|
||||||
|
@ -72,14 +76,77 @@ impl User {
|
||||||
.into_iter().nth(0)
|
.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))
|
users::table.filter(users::username.eq(username))
|
||||||
|
.filter(users::instance_id.eq(instance_id))
|
||||||
.limit(1)
|
.limit(1)
|
||||||
.load::<User>(conn)
|
.load::<User>(conn)
|
||||||
.expect("Error loading user by email")
|
.expect("Error loading user by email")
|
||||||
.into_iter().nth(0)
|
.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 {
|
pub fn hash_pass(pass: String) -> String {
|
||||||
bcrypt::hash(pass.as_str(), bcrypt::DEFAULT_COST).unwrap()
|
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 form = data.get();
|
||||||
let user = match User::find_by_email(&*conn, form.email_or_name.to_string()) {
|
let user = match User::find_by_email(&*conn, form.email_or_name.to_string()) {
|
||||||
Some(usr) => Ok(usr),
|
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),
|
Some(usr) => Ok(usr),
|
||||||
None => Err("Invalid username or password")
|
None => Err("Invalid username or password")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use rocket::request::Form;
|
use rocket::request::Form;
|
||||||
use rocket::response::Redirect;
|
use rocket::response::Redirect;
|
||||||
use rocket_contrib::Template;
|
use rocket_contrib::Template;
|
||||||
|
use serde_json;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use activity_pub::ActivityPub;
|
use activity_pub::ActivityPub;
|
||||||
|
@ -16,13 +17,16 @@ fn me(user: User) -> String {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/@/<name>", rank = 2)]
|
#[get("/@/<name>", rank = 2)]
|
||||||
fn details(name: String) -> String {
|
fn details(name: String, conn: DbConn) -> Template {
|
||||||
format!("Hello, @{}", name)
|
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)]
|
#[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 user = User::find_by_name(&*conn, name).unwrap();
|
let user = User::find_local(&*conn, name).unwrap();
|
||||||
user.as_activity_pub(&*conn)
|
user.as_activity_pub(&*conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,6 +65,6 @@ fn create(conn: DbConn, data: Form<NewUserForm>) -> Redirect {
|
||||||
|
|
||||||
#[get("/@/<name>/outbox")]
|
#[get("/@/<name>/outbox")]
|
||||||
fn outbox(name: String, conn: DbConn) -> 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)
|
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;
|
let domain = Instance::get_local(&*conn).unwrap().public_domain;
|
||||||
|
|
||||||
if res_dom == 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)),
|
Some(usr) => Ok(usr.webfinger(&*conn)),
|
||||||
None => match Blog::find_by_actor_id(&*conn, String::from(user)) {
|
None => match Blog::find_by_actor_id(&*conn, String::from(user)) {
|
||||||
Some(blog) => Ok(blog.webfinger(&*conn)),
|
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