Big refactoring of the Inbox (#443)
* Big refactoring of the Inbox We now have a type that routes an activity through the registered handlers until one of them matches. Each Actor/Activity/Object combination is represented by an implementation of AsObject These combinations are then registered on the Inbox type, which will try to deserialize the incoming activity in the requested types. Advantages: - nicer syntax: the final API is clearer and more idiomatic - more generic: only two traits (`AsActor` and `AsObject`) instead of one for each kind of activity - it is easier to see which activities we handle and which one we don't * Small fixes - Avoid panics - Don't search for AP ID infinitely - Code style issues * Fix tests * Introduce a new trait: FromId It should be implemented for any AP object. It allows to look for an object in database using its AP ID, or to dereference it if it was not present in database Also moves the inbox code to plume-models to test it (and write a basic test for each activity type we handle) * Use if let instead of match * Don't require PlumeRocket::intl for tests * Return early and remove a forgotten dbg! * Add more tests to try to understand where the issues come from * Also add a test for comment federation * Don't check creation_date is the same for blogs * Make user and blog federation more tolerant to errors/missing fields * Make clippy happy * Use the correct Accept header when dereferencing * Fix follow approval with Mastodon * Add spaces to characters that should not be in usernames And validate blog names too * Smarter dereferencing: only do it once for each actor/object * Forgot some files * Cargo fmt * Delete plume_test * Delete plume_tests * Update get_id docs + Remove useless : Sized * Appease cargo fmt * Remove dbg! + Use as_ref instead of clone when possible + Use and_then instead of map when possible * Remove .po~ * send unfollow to local instance * read cover from update activity * Make sure "cc" and "to" are never empty and fix a typo in a constant name * Cargo fmt
This commit is contained in:
+10
-6
@@ -7,7 +7,7 @@ use rocket_contrib::json::Json;
|
||||
use serde_json;
|
||||
|
||||
use plume_common::utils::random_hex;
|
||||
use plume_models::{api_tokens::*, apps::App, db_conn::DbConn, users::User, Error};
|
||||
use plume_models::{api_tokens::*, apps::App, users::User, Error, PlumeRocket};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ApiError(Error);
|
||||
@@ -47,13 +47,17 @@ pub struct OAuthRequest {
|
||||
}
|
||||
|
||||
#[get("/oauth2?<query..>")]
|
||||
pub fn oauth(query: Form<OAuthRequest>, conn: DbConn) -> Result<Json<serde_json::Value>, ApiError> {
|
||||
let app = App::find_by_client_id(&*conn, &query.client_id)?;
|
||||
pub fn oauth(
|
||||
query: Form<OAuthRequest>,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Json<serde_json::Value>, ApiError> {
|
||||
let conn = &*rockets.conn;
|
||||
let app = App::find_by_client_id(conn, &query.client_id)?;
|
||||
if app.client_secret == query.client_secret {
|
||||
if let Ok(user) = User::find_by_fqn(&*conn, &query.username) {
|
||||
if let Ok(user) = User::find_by_fqn(&rockets, &query.username) {
|
||||
if user.auth(&query.password) {
|
||||
let token = ApiToken::insert(
|
||||
&*conn,
|
||||
conn,
|
||||
NewApiToken {
|
||||
app_id: app.id,
|
||||
user_id: user.id,
|
||||
@@ -73,7 +77,7 @@ pub fn oauth(query: Form<OAuthRequest>, conn: DbConn) -> Result<Json<serde_json:
|
||||
// Making fake password verification to avoid different
|
||||
// response times that would make it possible to know
|
||||
// if a username is registered or not.
|
||||
User::get(&*conn, 1)?.auth(&query.password);
|
||||
User::get(conn, 1)?.auth(&query.password);
|
||||
Ok(Json(json!({
|
||||
"error": "Invalid credentials"
|
||||
})))
|
||||
|
||||
+11
-40
@@ -1,74 +1,45 @@
|
||||
use canapi::{Error as ApiError, Provider};
|
||||
use rocket::http::uri::Origin;
|
||||
use rocket_contrib::json::Json;
|
||||
use scheduled_thread_pool::ScheduledThreadPool;
|
||||
use serde_json;
|
||||
use serde_qs;
|
||||
|
||||
use api::authorization::*;
|
||||
use plume_api::posts::PostEndpoint;
|
||||
use plume_models::{
|
||||
db_conn::DbConn, posts::Post, search::Searcher as UnmanagedSearcher, Connection,
|
||||
};
|
||||
use {Searcher, Worker};
|
||||
use plume_models::{posts::Post, users::User, PlumeRocket};
|
||||
|
||||
#[get("/posts/<id>")]
|
||||
pub fn get(
|
||||
id: i32,
|
||||
conn: DbConn,
|
||||
worker: Worker,
|
||||
auth: Option<Authorization<Read, Post>>,
|
||||
search: Searcher,
|
||||
mut rockets: PlumeRocket,
|
||||
) -> Json<serde_json::Value> {
|
||||
let post = <Post as Provider<(
|
||||
&Connection,
|
||||
&ScheduledThreadPool,
|
||||
&UnmanagedSearcher,
|
||||
Option<i32>,
|
||||
)>>::get(&(&*conn, &worker, &search, auth.map(|a| a.0.user_id)), id)
|
||||
.ok();
|
||||
rockets.user = auth.and_then(|a| User::get(&*rockets.conn, a.0.user_id).ok());
|
||||
let post = <Post as Provider<PlumeRocket>>::get(&rockets, id).ok();
|
||||
Json(json!(post))
|
||||
}
|
||||
|
||||
#[get("/posts")]
|
||||
pub fn list(
|
||||
conn: DbConn,
|
||||
uri: &Origin,
|
||||
worker: Worker,
|
||||
auth: Option<Authorization<Read, Post>>,
|
||||
search: Searcher,
|
||||
mut rockets: PlumeRocket,
|
||||
) -> Json<serde_json::Value> {
|
||||
rockets.user = auth.and_then(|a| User::get(&*rockets.conn, a.0.user_id).ok());
|
||||
let query: PostEndpoint =
|
||||
serde_qs::from_str(uri.query().unwrap_or("")).expect("api::list: invalid query error");
|
||||
let post = <Post as Provider<(
|
||||
&Connection,
|
||||
&ScheduledThreadPool,
|
||||
&UnmanagedSearcher,
|
||||
Option<i32>,
|
||||
)>>::list(
|
||||
&(&*conn, &worker, &search, auth.map(|a| a.0.user_id)),
|
||||
query,
|
||||
);
|
||||
let post = <Post as Provider<PlumeRocket>>::list(&rockets, query);
|
||||
Json(json!(post))
|
||||
}
|
||||
|
||||
#[post("/posts", data = "<payload>")]
|
||||
pub fn create(
|
||||
conn: DbConn,
|
||||
payload: Json<PostEndpoint>,
|
||||
worker: Worker,
|
||||
auth: Authorization<Write, Post>,
|
||||
search: Searcher,
|
||||
payload: Json<PostEndpoint>,
|
||||
mut rockets: PlumeRocket,
|
||||
) -> Json<serde_json::Value> {
|
||||
let new_post = <Post as Provider<(
|
||||
&Connection,
|
||||
&ScheduledThreadPool,
|
||||
&UnmanagedSearcher,
|
||||
Option<i32>,
|
||||
)>>::create(
|
||||
&(&*conn, &worker, &search, Some(auth.0.user_id)),
|
||||
(*payload).clone(),
|
||||
);
|
||||
rockets.user = User::get(&*rockets.conn, auth.0.user_id).ok();
|
||||
let new_post = <Post as Provider<PlumeRocket>>::create(&rockets, (*payload).clone());
|
||||
Json(new_post.map(|p| json!(p)).unwrap_or_else(|e| {
|
||||
json!({
|
||||
"error": "Invalid data, couldn't create new post",
|
||||
|
||||
+55
-158
@@ -1,171 +1,68 @@
|
||||
#![warn(clippy::too_many_arguments)]
|
||||
use activitypub::{
|
||||
activity::{Announce, Create, Delete, Follow as FollowAct, Like, Undo, Update},
|
||||
object::Tombstone,
|
||||
};
|
||||
use failure::Error;
|
||||
use rocket::{data::*, http::Status, Outcome::*, Request};
|
||||
use rocket_contrib::json::*;
|
||||
use serde::Deserialize;
|
||||
use serde_json;
|
||||
|
||||
use std::io::Read;
|
||||
|
||||
use plume_common::activity_pub::{
|
||||
inbox::{Deletable, FromActivity, InboxError, Notify},
|
||||
inbox::FromId,
|
||||
request::Digest,
|
||||
Id,
|
||||
sign::{verify_http_headers, Signable},
|
||||
};
|
||||
use plume_models::{
|
||||
comments::Comment, follows::Follow, instance::Instance, likes, posts::Post, reshares::Reshare,
|
||||
search::Searcher, users::User, Connection,
|
||||
headers::Headers, inbox::inbox, instance::Instance, users::User, Error, PlumeRocket,
|
||||
};
|
||||
use rocket::{data::*, http::Status, response::status, Outcome::*, Request};
|
||||
use rocket_contrib::json::*;
|
||||
use serde::Deserialize;
|
||||
use std::io::Read;
|
||||
|
||||
pub trait Inbox {
|
||||
fn received(
|
||||
&self,
|
||||
conn: &Connection,
|
||||
searcher: &Searcher,
|
||||
act: serde_json::Value,
|
||||
) -> Result<(), Error> {
|
||||
let actor_id = Id::new(act["actor"].as_str().unwrap_or_else(|| {
|
||||
act["actor"]["id"]
|
||||
.as_str()
|
||||
.expect("Inbox::received: actor_id missing error")
|
||||
}));
|
||||
match act["type"].as_str() {
|
||||
Some(t) => match t {
|
||||
"Announce" => {
|
||||
Reshare::from_activity(conn, serde_json::from_value(act.clone())?, actor_id)
|
||||
.expect("Inbox::received: Announce error");;
|
||||
pub fn handle_incoming(
|
||||
rockets: PlumeRocket,
|
||||
data: SignedJson<serde_json::Value>,
|
||||
headers: Headers,
|
||||
) -> Result<String, status::BadRequest<&'static str>> {
|
||||
let conn = &*rockets.conn;
|
||||
let act = data.1.into_inner();
|
||||
let sig = data.0;
|
||||
|
||||
let activity = act.clone();
|
||||
let actor_id = activity["actor"]
|
||||
.as_str()
|
||||
.or_else(|| activity["actor"]["id"].as_str())
|
||||
.ok_or(status::BadRequest(Some("Missing actor id for activity")))?;
|
||||
|
||||
let actor =
|
||||
User::from_id(&rockets, actor_id, None).expect("instance::shared_inbox: user error");
|
||||
if !verify_http_headers(&actor, &headers.0, &sig).is_secure() && !act.clone().verify(&actor) {
|
||||
// maybe we just know an old key?
|
||||
actor
|
||||
.refetch(conn)
|
||||
.and_then(|_| User::get(conn, actor.id))
|
||||
.and_then(|u| {
|
||||
if verify_http_headers(&u, &headers.0, &sig).is_secure() || act.clone().verify(&u) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::Signature)
|
||||
}
|
||||
"Create" => {
|
||||
let act: Create = serde_json::from_value(act.clone())?;
|
||||
if Post::try_from_activity(&(conn, searcher), act.clone()).is_ok()
|
||||
|| Comment::try_from_activity(conn, act).is_ok()
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
Err(InboxError::InvalidType)?
|
||||
}
|
||||
}
|
||||
"Delete" => {
|
||||
let act: Delete = serde_json::from_value(act.clone())?;
|
||||
Post::delete_id(
|
||||
&act.delete_props
|
||||
.object_object::<Tombstone>()?
|
||||
.object_props
|
||||
.id_string()?,
|
||||
actor_id.as_ref(),
|
||||
&(conn, searcher),
|
||||
)
|
||||
.ok();
|
||||
Comment::delete_id(
|
||||
&act.delete_props
|
||||
.object_object::<Tombstone>()?
|
||||
.object_props
|
||||
.id_string()?,
|
||||
actor_id.as_ref(),
|
||||
conn,
|
||||
)
|
||||
.ok();
|
||||
Ok(())
|
||||
}
|
||||
"Follow" => {
|
||||
Follow::from_activity(conn, serde_json::from_value(act.clone())?, actor_id)
|
||||
.and_then(|f| f.notify(conn))
|
||||
.expect("Inbox::received: follow from activity error");;
|
||||
Ok(())
|
||||
}
|
||||
"Like" => {
|
||||
likes::Like::from_activity(
|
||||
conn,
|
||||
serde_json::from_value(act.clone())?,
|
||||
actor_id,
|
||||
)
|
||||
.expect("Inbox::received: like from activity error");;
|
||||
Ok(())
|
||||
}
|
||||
"Undo" => {
|
||||
let act: Undo = serde_json::from_value(act.clone())?;
|
||||
if let Some(t) = act.undo_props.object["type"].as_str() {
|
||||
match t {
|
||||
"Like" => {
|
||||
likes::Like::delete_id(
|
||||
&act.undo_props
|
||||
.object_object::<Like>()?
|
||||
.object_props
|
||||
.id_string()?,
|
||||
actor_id.as_ref(),
|
||||
conn,
|
||||
)
|
||||
.expect("Inbox::received: undo like fail");;
|
||||
Ok(())
|
||||
}
|
||||
"Announce" => {
|
||||
Reshare::delete_id(
|
||||
&act.undo_props
|
||||
.object_object::<Announce>()?
|
||||
.object_props
|
||||
.id_string()?,
|
||||
actor_id.as_ref(),
|
||||
conn,
|
||||
)
|
||||
.expect("Inbox::received: undo reshare fail");;
|
||||
Ok(())
|
||||
}
|
||||
"Follow" => {
|
||||
Follow::delete_id(
|
||||
&act.undo_props
|
||||
.object_object::<FollowAct>()?
|
||||
.object_props
|
||||
.id_string()?,
|
||||
actor_id.as_ref(),
|
||||
conn,
|
||||
)
|
||||
.expect("Inbox::received: undo follow error");;
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(InboxError::CantUndo)?,
|
||||
}
|
||||
} else {
|
||||
let link =
|
||||
act.undo_props.object.as_str().expect(
|
||||
"Inbox::received: undo doesn't contain a type and isn't Link",
|
||||
);
|
||||
if let Ok(like) = likes::Like::find_by_ap_url(conn, link) {
|
||||
likes::Like::delete_id(&like.ap_url, actor_id.as_ref(), conn)
|
||||
.expect("Inbox::received: delete Like error");
|
||||
Ok(())
|
||||
} else if let Ok(reshare) = Reshare::find_by_ap_url(conn, link) {
|
||||
Reshare::delete_id(&reshare.ap_url, actor_id.as_ref(), conn)
|
||||
.expect("Inbox::received: delete Announce error");
|
||||
Ok(())
|
||||
} else if let Ok(follow) = Follow::find_by_ap_url(conn, link) {
|
||||
Follow::delete_id(&follow.ap_url, actor_id.as_ref(), conn)
|
||||
.expect("Inbox::received: delete Follow error");
|
||||
Ok(())
|
||||
} else {
|
||||
Err(InboxError::NoType)?
|
||||
}
|
||||
}
|
||||
}
|
||||
"Update" => {
|
||||
let act: Update = serde_json::from_value(act.clone())?;
|
||||
Post::handle_update(conn, &act.update_props.object_object()?, searcher)
|
||||
.expect("Inbox::received: post update error");
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(InboxError::InvalidType)?,
|
||||
},
|
||||
None => Err(InboxError::NoType)?,
|
||||
}
|
||||
})
|
||||
.map_err(|_| {
|
||||
println!(
|
||||
"Rejected invalid activity supposedly from {}, with headers {:?}",
|
||||
actor.username, headers.0
|
||||
);
|
||||
status::BadRequest(Some("Invalid signature"))
|
||||
})?;
|
||||
}
|
||||
}
|
||||
|
||||
impl Inbox for Instance {}
|
||||
impl Inbox for User {}
|
||||
if Instance::is_blocked(conn, actor_id)
|
||||
.map_err(|_| status::BadRequest(Some("Can't tell if instance is blocked")))?
|
||||
{
|
||||
return Ok(String::new());
|
||||
}
|
||||
|
||||
Ok(match inbox(&rockets, act) {
|
||||
Ok(_) => String::new(),
|
||||
Err(e) => {
|
||||
println!("Shared inbox error: {:?}", e);
|
||||
format!("Error: {:?}", e)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const JSON_LIMIT: u64 = 1 << 20;
|
||||
|
||||
|
||||
+1
-3
@@ -10,7 +10,6 @@ extern crate colored;
|
||||
extern crate ctrlc;
|
||||
extern crate diesel;
|
||||
extern crate dotenv;
|
||||
extern crate failure;
|
||||
#[macro_use]
|
||||
extern crate gettext_macros;
|
||||
extern crate gettext_utils;
|
||||
@@ -66,7 +65,6 @@ include!(concat!(env!("OUT_DIR"), "/templates.rs"));
|
||||
|
||||
compile_i18n!();
|
||||
|
||||
type Worker<'a> = State<'a, ScheduledThreadPool>;
|
||||
type Searcher<'a> = State<'a, Arc<UnmanagedSearcher>>;
|
||||
|
||||
/// Initializes a database pool.
|
||||
@@ -241,7 +239,7 @@ Then try to restart Plume
|
||||
.manage(Arc::new(Mutex::new(mail)))
|
||||
.manage::<Arc<Mutex<Vec<routes::session::ResetRequest>>>>(Arc::new(Mutex::new(vec![])))
|
||||
.manage(dbpool)
|
||||
.manage(workpool)
|
||||
.manage(Arc::new(workpool))
|
||||
.manage(searcher)
|
||||
.manage(include_i18n!())
|
||||
.attach(
|
||||
|
||||
+58
-45
@@ -13,17 +13,17 @@ use validator::{Validate, ValidationError, ValidationErrors};
|
||||
use plume_common::activity_pub::{ActivityStream, ApRequest};
|
||||
use plume_common::utils;
|
||||
use plume_models::{
|
||||
blog_authors::*, blogs::*, db_conn::DbConn, instance::Instance, medias::*, posts::Post,
|
||||
safe_string::SafeString, users::User, Connection,
|
||||
blog_authors::*, blogs::*, instance::Instance, medias::*, posts::Post, safe_string::SafeString,
|
||||
users::User, Connection, PlumeRocket,
|
||||
};
|
||||
use routes::{errors::ErrorPage, Page, PlumeRocket};
|
||||
use routes::{errors::ErrorPage, Page};
|
||||
use template_utils::Ructe;
|
||||
|
||||
#[get("/~/<name>?<page>", rank = 2)]
|
||||
pub fn details(name: String, page: Option<Page>, rockets: PlumeRocket) -> Result<Ructe, ErrorPage> {
|
||||
let page = page.unwrap_or_default();
|
||||
let conn = rockets.conn;
|
||||
let blog = Blog::find_by_fqn(&*conn, &name)?;
|
||||
let conn = &*rockets.conn;
|
||||
let blog = Blog::find_by_fqn(&rockets, &name)?;
|
||||
let posts = Post::blog_page(&*conn, &blog, page.limits())?;
|
||||
let articles_count = Post::count_for_blog(&*conn, &blog)?;
|
||||
let authors = &blog.list_authors(&*conn)?;
|
||||
@@ -43,18 +43,18 @@ pub fn details(name: String, page: Option<Page>, rockets: PlumeRocket) -> Result
|
||||
#[get("/~/<name>", rank = 1)]
|
||||
pub fn activity_details(
|
||||
name: String,
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
_ap: ApRequest,
|
||||
) -> Option<ActivityStream<CustomGroup>> {
|
||||
let blog = Blog::find_by_fqn(&*conn, &name).ok()?;
|
||||
Some(ActivityStream::new(blog.to_activity(&*conn).ok()?))
|
||||
let blog = Blog::find_by_fqn(&rockets, &name).ok()?;
|
||||
Some(ActivityStream::new(blog.to_activity(&*rockets.conn).ok()?))
|
||||
}
|
||||
|
||||
#[get("/blogs/new")]
|
||||
pub fn new(rockets: PlumeRocket) -> Ructe {
|
||||
let user = rockets.user.unwrap();
|
||||
let intl = rockets.intl;
|
||||
let conn = rockets.conn;
|
||||
let conn = &*rockets.conn;
|
||||
|
||||
render!(blogs::new(
|
||||
&(&*conn, &intl.catalog, Some(user)),
|
||||
@@ -92,20 +92,23 @@ fn valid_slug(title: &str) -> Result<(), ValidationError> {
|
||||
#[post("/blogs/new", data = "<form>")]
|
||||
pub fn create(form: LenientForm<NewBlogForm>, rockets: PlumeRocket) -> Result<Redirect, Ructe> {
|
||||
let slug = utils::make_actor_id(&form.title);
|
||||
let conn = rockets.conn;
|
||||
let intl = rockets.intl;
|
||||
let user = rockets.user.unwrap();
|
||||
let conn = &*rockets.conn;
|
||||
let intl = &rockets.intl.catalog;
|
||||
let user = rockets.user.clone().unwrap();
|
||||
|
||||
let mut errors = match form.validate() {
|
||||
Ok(_) => ValidationErrors::new(),
|
||||
Err(e) => e,
|
||||
};
|
||||
if Blog::find_by_fqn(&*conn, &slug).is_ok() {
|
||||
if Blog::find_by_fqn(&rockets, &slug).is_ok() {
|
||||
errors.add(
|
||||
"title",
|
||||
ValidationError {
|
||||
code: Cow::from("existing_slug"),
|
||||
message: Some(Cow::from("A blog with the same name already exists.")),
|
||||
message: Some(Cow::from(i18n!(
|
||||
intl,
|
||||
"A blog with the same name already exists."
|
||||
))),
|
||||
params: HashMap::new(),
|
||||
},
|
||||
);
|
||||
@@ -139,7 +142,7 @@ pub fn create(form: LenientForm<NewBlogForm>, rockets: PlumeRocket) -> Result<Re
|
||||
Ok(Redirect::to(uri!(details: name = slug.clone(), page = _)))
|
||||
} else {
|
||||
Err(render!(blogs::new(
|
||||
&(&*conn, &intl.catalog, Some(user)),
|
||||
&(&*conn, intl, Some(user)),
|
||||
&*form,
|
||||
errors
|
||||
)))
|
||||
@@ -148,8 +151,8 @@ pub fn create(form: LenientForm<NewBlogForm>, rockets: PlumeRocket) -> Result<Re
|
||||
|
||||
#[post("/~/<name>/delete")]
|
||||
pub fn delete(name: String, rockets: PlumeRocket) -> Result<Redirect, Ructe> {
|
||||
let conn = rockets.conn;
|
||||
let blog = Blog::find_by_fqn(&*conn, &name).expect("blog::delete: blog not found");
|
||||
let conn = &*rockets.conn;
|
||||
let blog = Blog::find_by_fqn(&rockets, &name).expect("blog::delete: blog not found");
|
||||
let user = rockets.user;
|
||||
let intl = rockets.intl;
|
||||
let searcher = rockets.searcher;
|
||||
@@ -181,22 +184,21 @@ pub struct EditForm {
|
||||
}
|
||||
|
||||
#[get("/~/<name>/edit")]
|
||||
pub fn edit(
|
||||
conn: DbConn,
|
||||
name: String,
|
||||
user: Option<User>,
|
||||
intl: I18n,
|
||||
) -> Result<Ructe, ErrorPage> {
|
||||
let blog = Blog::find_by_fqn(&*conn, &name)?;
|
||||
if user
|
||||
pub fn edit(name: String, rockets: PlumeRocket) -> Result<Ructe, ErrorPage> {
|
||||
let conn = &*rockets.conn;
|
||||
let blog = Blog::find_by_fqn(&rockets, &name)?;
|
||||
if rockets
|
||||
.user
|
||||
.clone()
|
||||
.and_then(|u| u.is_author_in(&*conn, &blog).ok())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
let user = user.expect("blogs::edit: User was None while it shouldn't");
|
||||
let user = rockets
|
||||
.user
|
||||
.expect("blogs::edit: User was None while it shouldn't");
|
||||
let medias = Media::for_user(&*conn, user.id).expect("Couldn't list media");
|
||||
Ok(render!(blogs::edit(
|
||||
&(&*conn, &intl.catalog, Some(user)),
|
||||
&(&*conn, &rockets.intl.catalog, Some(user)),
|
||||
&blog,
|
||||
medias,
|
||||
&EditForm {
|
||||
@@ -210,8 +212,11 @@ pub fn edit(
|
||||
} else {
|
||||
// TODO actually return 403 error code
|
||||
Ok(render!(errors::not_authorized(
|
||||
&(&*conn, &intl.catalog, user),
|
||||
i18n!(intl.catalog, "You are not allowed to edit this blog.")
|
||||
&(&*conn, &rockets.intl.catalog, rockets.user),
|
||||
i18n!(
|
||||
rockets.intl.catalog,
|
||||
"You are not allowed to edit this blog."
|
||||
)
|
||||
)))
|
||||
}
|
||||
}
|
||||
@@ -227,19 +232,23 @@ fn check_media(conn: &Connection, id: i32, user: &User) -> bool {
|
||||
|
||||
#[put("/~/<name>/edit", data = "<form>")]
|
||||
pub fn update(
|
||||
conn: DbConn,
|
||||
name: String,
|
||||
user: Option<User>,
|
||||
intl: I18n,
|
||||
form: LenientForm<EditForm>,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Redirect, Ructe> {
|
||||
let mut blog = Blog::find_by_fqn(&*conn, &name).expect("blog::update: blog not found");
|
||||
if user
|
||||
let conn = &*rockets.conn;
|
||||
let intl = &rockets.intl.catalog;
|
||||
let mut blog = Blog::find_by_fqn(&rockets, &name).expect("blog::update: blog not found");
|
||||
if rockets
|
||||
.user
|
||||
.clone()
|
||||
.and_then(|u| u.is_author_in(&*conn, &blog).ok())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
let user = user.expect("blogs::edit: User was None while it shouldn't");
|
||||
let user = rockets
|
||||
.user
|
||||
.clone()
|
||||
.expect("blogs::edit: User was None while it shouldn't");
|
||||
form.validate()
|
||||
.and_then(|_| {
|
||||
if let Some(icon) = form.icon {
|
||||
@@ -250,7 +259,7 @@ pub fn update(
|
||||
ValidationError {
|
||||
code: Cow::from("icon"),
|
||||
message: Some(Cow::from(i18n!(
|
||||
intl.catalog,
|
||||
intl,
|
||||
"You can't use this media as a blog icon."
|
||||
))),
|
||||
params: HashMap::new(),
|
||||
@@ -268,7 +277,7 @@ pub fn update(
|
||||
ValidationError {
|
||||
code: Cow::from("banner"),
|
||||
message: Some(Cow::from(i18n!(
|
||||
intl.catalog,
|
||||
intl,
|
||||
"You can't use this media as a blog banner."
|
||||
))),
|
||||
params: HashMap::new(),
|
||||
@@ -304,7 +313,7 @@ pub fn update(
|
||||
.map_err(|err| {
|
||||
let medias = Media::for_user(&*conn, user.id).expect("Couldn't list media");
|
||||
render!(blogs::edit(
|
||||
&(&*conn, &intl.catalog, Some(user)),
|
||||
&(&*conn, intl, Some(user)),
|
||||
&blog,
|
||||
medias,
|
||||
&*form,
|
||||
@@ -314,21 +323,25 @@ pub fn update(
|
||||
} else {
|
||||
// TODO actually return 403 error code
|
||||
Err(render!(errors::not_authorized(
|
||||
&(&*conn, &intl.catalog, user),
|
||||
i18n!(intl.catalog, "You are not allowed to edit this blog.")
|
||||
&(&*conn, &rockets.intl.catalog, rockets.user),
|
||||
i18n!(
|
||||
rockets.intl.catalog,
|
||||
"You are not allowed to edit this blog."
|
||||
)
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/~/<name>/outbox")]
|
||||
pub fn outbox(name: String, conn: DbConn) -> Option<ActivityStream<OrderedCollection>> {
|
||||
let blog = Blog::find_by_fqn(&*conn, &name).ok()?;
|
||||
Some(blog.outbox(&*conn).ok()?)
|
||||
pub fn outbox(name: String, rockets: PlumeRocket) -> Option<ActivityStream<OrderedCollection>> {
|
||||
let blog = Blog::find_by_fqn(&rockets, &name).ok()?;
|
||||
Some(blog.outbox(&*rockets.conn).ok()?)
|
||||
}
|
||||
|
||||
#[get("/~/<name>/atom.xml")]
|
||||
pub fn atom_feed(name: String, conn: DbConn) -> Option<Content<String>> {
|
||||
let blog = Blog::find_by_fqn(&*conn, &name).ok()?;
|
||||
pub fn atom_feed(name: String, rockets: PlumeRocket) -> Option<Content<String>> {
|
||||
let blog = Blog::find_by_fqn(&rockets, &name).ok()?;
|
||||
let conn = &*rockets.conn;
|
||||
let feed = FeedBuilder::default()
|
||||
.title(blog.title.clone())
|
||||
.id(Instance::get_local(&*conn)
|
||||
|
||||
+34
-30
@@ -1,25 +1,19 @@
|
||||
use activitypub::object::Note;
|
||||
use rocket::{request::LenientForm, response::Redirect};
|
||||
use rocket_i18n::I18n;
|
||||
use template_utils::Ructe;
|
||||
use validator::Validate;
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use plume_common::{
|
||||
activity_pub::{
|
||||
broadcast,
|
||||
inbox::{Deletable, Notify},
|
||||
ActivityStream, ApRequest,
|
||||
},
|
||||
activity_pub::{broadcast, ActivityStream, ApRequest},
|
||||
utils,
|
||||
};
|
||||
use plume_models::{
|
||||
blogs::Blog, comments::*, db_conn::DbConn, instance::Instance, medias::Media,
|
||||
mentions::Mention, posts::Post, safe_string::SafeString, tags::Tag, users::User,
|
||||
blogs::Blog, comments::*, inbox::inbox, instance::Instance, medias::Media, mentions::Mention,
|
||||
posts::Post, safe_string::SafeString, tags::Tag, users::User, Error, PlumeRocket,
|
||||
};
|
||||
use routes::errors::ErrorPage;
|
||||
use Worker;
|
||||
|
||||
#[derive(Default, FromForm, Debug, Validate)]
|
||||
pub struct NewCommentForm {
|
||||
@@ -35,11 +29,10 @@ pub fn create(
|
||||
slug: String,
|
||||
form: LenientForm<NewCommentForm>,
|
||||
user: User,
|
||||
conn: DbConn,
|
||||
worker: Worker,
|
||||
intl: I18n,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Redirect, Ructe> {
|
||||
let blog = Blog::find_by_fqn(&*conn, &blog_name).expect("comments::create: blog error");
|
||||
let conn = &*rockets.conn;
|
||||
let blog = Blog::find_by_fqn(&rockets, &blog_name).expect("comments::create: blog error");
|
||||
let post = Post::find_by_slug(&*conn, &slug, blog.id).expect("comments::create: post error");
|
||||
form.validate()
|
||||
.map(|_| {
|
||||
@@ -67,14 +60,14 @@ pub fn create(
|
||||
.expect("comments::create: insert error");
|
||||
comm.notify(&*conn).expect("comments::create: notify error");
|
||||
let new_comment = comm
|
||||
.create_activity(&*conn)
|
||||
.create_activity(&rockets)
|
||||
.expect("comments::create: activity error");
|
||||
|
||||
// save mentions
|
||||
for ment in mentions {
|
||||
Mention::from_activity(
|
||||
&*conn,
|
||||
&Mention::build_activity(&*conn, &ment)
|
||||
&Mention::build_activity(&rockets, &ment)
|
||||
.expect("comments::create: build mention error"),
|
||||
comm.id,
|
||||
false,
|
||||
@@ -86,7 +79,9 @@ pub fn create(
|
||||
// federate
|
||||
let dest = User::one_by_instance(&*conn).expect("comments::create: dest error");
|
||||
let user_clone = user.clone();
|
||||
worker.execute(move || broadcast(&user_clone, new_comment, dest));
|
||||
rockets
|
||||
.worker
|
||||
.execute(move || broadcast(&user_clone, new_comment, dest));
|
||||
|
||||
Redirect::to(
|
||||
uri!(super::posts::details: blog = blog_name, slug = slug, responding_to = _),
|
||||
@@ -102,7 +97,7 @@ pub fn create(
|
||||
.and_then(|r| Comment::get(&*conn, r).ok());
|
||||
|
||||
render!(posts::details(
|
||||
&(&*conn, &intl.catalog, Some(user.clone())),
|
||||
&(&*conn, &rockets.intl.catalog, Some(user.clone())),
|
||||
post.clone(),
|
||||
blog,
|
||||
&*form,
|
||||
@@ -138,19 +133,28 @@ pub fn delete(
|
||||
slug: String,
|
||||
id: i32,
|
||||
user: User,
|
||||
conn: DbConn,
|
||||
worker: Worker,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Redirect, ErrorPage> {
|
||||
if let Ok(comment) = Comment::get(&*conn, id) {
|
||||
if let Ok(comment) = Comment::get(&*rockets.conn, id) {
|
||||
if comment.author_id == user.id {
|
||||
let dest = User::one_by_instance(&*conn)?;
|
||||
let delete_activity = comment.delete(&*conn)?;
|
||||
let dest = User::one_by_instance(&*rockets.conn)?;
|
||||
let delete_activity = comment.build_delete(&*rockets.conn)?;
|
||||
inbox(
|
||||
&rockets,
|
||||
serde_json::to_value(&delete_activity).map_err(Error::from)?,
|
||||
)?;
|
||||
|
||||
let user_c = user.clone();
|
||||
worker.execute(move || broadcast(&user_c, delete_activity, dest));
|
||||
worker.execute_after(Duration::from_secs(10 * 60), move || {
|
||||
user.rotate_keypair(&conn)
|
||||
.expect("Failed to rotate keypair");
|
||||
});
|
||||
rockets
|
||||
.worker
|
||||
.execute(move || broadcast(&user_c, delete_activity, dest));
|
||||
let conn = rockets.conn;
|
||||
rockets
|
||||
.worker
|
||||
.execute_after(Duration::from_secs(10 * 60), move || {
|
||||
user.rotate_keypair(&conn)
|
||||
.expect("Failed to rotate keypair");
|
||||
});
|
||||
}
|
||||
}
|
||||
Ok(Redirect::to(
|
||||
@@ -164,10 +168,10 @@ pub fn activity_pub(
|
||||
_slug: String,
|
||||
id: i32,
|
||||
_ap: ApRequest,
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
) -> Option<ActivityStream<Note>> {
|
||||
Comment::get(&*conn, id)
|
||||
.and_then(|c| c.to_activity(&*conn))
|
||||
Comment::get(&*rockets.conn, id)
|
||||
.and_then(|c| c.to_activity(&rockets))
|
||||
.ok()
|
||||
.map(ActivityStream::new)
|
||||
}
|
||||
|
||||
+5
-51
@@ -7,11 +7,10 @@ use rocket_i18n::I18n;
|
||||
use serde_json;
|
||||
use validator::{Validate, ValidationErrors};
|
||||
|
||||
use inbox::{Inbox, SignedJson};
|
||||
use plume_common::activity_pub::sign::{verify_http_headers, Signable};
|
||||
use inbox;
|
||||
use plume_models::{
|
||||
admin::Admin, comments::Comment, db_conn::DbConn, headers::Headers, instance::*, posts::Post,
|
||||
safe_string::SafeString, users::User, Error, CONFIG,
|
||||
safe_string::SafeString, users::User, Error, PlumeRocket, CONFIG,
|
||||
};
|
||||
use routes::{errors::ErrorPage, rocket_uri_macro_static_files, Page};
|
||||
use template_utils::Ructe;
|
||||
@@ -211,56 +210,11 @@ pub fn ban(
|
||||
|
||||
#[post("/inbox", data = "<data>")]
|
||||
pub fn shared_inbox(
|
||||
conn: DbConn,
|
||||
data: SignedJson<serde_json::Value>,
|
||||
rockets: PlumeRocket,
|
||||
data: inbox::SignedJson<serde_json::Value>,
|
||||
headers: Headers,
|
||||
searcher: Searcher,
|
||||
) -> Result<String, status::BadRequest<&'static str>> {
|
||||
let act = data.1.into_inner();
|
||||
let sig = data.0;
|
||||
|
||||
let activity = act.clone();
|
||||
let actor_id = activity["actor"]
|
||||
.as_str()
|
||||
.or_else(|| activity["actor"]["id"].as_str())
|
||||
.ok_or(status::BadRequest(Some("Missing actor id for activity")))?;
|
||||
|
||||
let actor = User::from_url(&conn, actor_id).expect("instance::shared_inbox: user error");
|
||||
if !verify_http_headers(&actor, &headers.0, &sig).is_secure() && !act.clone().verify(&actor) {
|
||||
// maybe we just know an old key?
|
||||
actor
|
||||
.refetch(&conn)
|
||||
.and_then(|_| User::get(&conn, actor.id))
|
||||
.and_then(|u| {
|
||||
if verify_http_headers(&u, &headers.0, &sig).is_secure() || act.clone().verify(&u) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::Signature)
|
||||
}
|
||||
})
|
||||
.map_err(|_| {
|
||||
println!(
|
||||
"Rejected invalid activity supposedly from {}, with headers {:?}",
|
||||
actor.username, headers.0
|
||||
);
|
||||
status::BadRequest(Some("Invalid signature"))
|
||||
})?;
|
||||
}
|
||||
|
||||
if Instance::is_blocked(&*conn, actor_id)
|
||||
.map_err(|_| status::BadRequest(Some("Can't tell if instance is blocked")))?
|
||||
{
|
||||
return Ok(String::new());
|
||||
}
|
||||
let instance = Instance::get_local(&*conn)
|
||||
.expect("instance::shared_inbox: local instance not found error");
|
||||
Ok(match instance.received(&*conn, &searcher, act) {
|
||||
Ok(_) => String::new(),
|
||||
Err(e) => {
|
||||
println!("Shared inbox error: {}\n{}", e.as_fail(), e.backtrace());
|
||||
format!("Error: {}", e.as_fail())
|
||||
}
|
||||
})
|
||||
inbox::handle_incoming(rockets, data, headers)
|
||||
}
|
||||
|
||||
#[get("/nodeinfo/<version>")]
|
||||
|
||||
+17
-12
@@ -1,24 +1,22 @@
|
||||
use rocket::response::{Flash, Redirect};
|
||||
use rocket_i18n::I18n;
|
||||
|
||||
use plume_common::activity_pub::{
|
||||
broadcast,
|
||||
inbox::{Deletable, Notify},
|
||||
};
|
||||
use plume_common::activity_pub::broadcast;
|
||||
use plume_common::utils;
|
||||
use plume_models::{blogs::Blog, db_conn::DbConn, likes, posts::Post, users::User};
|
||||
use plume_models::{
|
||||
blogs::Blog, inbox::inbox, likes, posts::Post, users::User, Error, PlumeRocket,
|
||||
};
|
||||
use routes::errors::ErrorPage;
|
||||
use Worker;
|
||||
|
||||
#[post("/~/<blog>/<slug>/like")]
|
||||
pub fn create(
|
||||
blog: String,
|
||||
slug: String,
|
||||
user: User,
|
||||
conn: DbConn,
|
||||
worker: Worker,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Redirect, ErrorPage> {
|
||||
let b = Blog::find_by_fqn(&*conn, &blog)?;
|
||||
let conn = &*rockets.conn;
|
||||
let b = Blog::find_by_fqn(&rockets, &blog)?;
|
||||
let post = Post::find_by_slug(&*conn, &slug, b.id)?;
|
||||
|
||||
if !user.has_liked(&*conn, &post)? {
|
||||
@@ -27,12 +25,19 @@ pub fn create(
|
||||
|
||||
let dest = User::one_by_instance(&*conn)?;
|
||||
let act = like.to_activity(&*conn)?;
|
||||
worker.execute(move || broadcast(&user, act, dest));
|
||||
rockets.worker.execute(move || broadcast(&user, act, dest));
|
||||
} else {
|
||||
let like = likes::Like::find_by_user_on_post(&*conn, user.id, post.id)?;
|
||||
let delete_act = like.delete(&*conn)?;
|
||||
let delete_act = like.build_undo(&*conn)?;
|
||||
inbox(
|
||||
&rockets,
|
||||
serde_json::to_value(&delete_act).map_err(Error::from)?,
|
||||
)?;
|
||||
|
||||
let dest = User::one_by_instance(&*conn)?;
|
||||
worker.execute(move || broadcast(&user, delete_act, dest));
|
||||
rockets
|
||||
.worker
|
||||
.execute(move || broadcast(&user, delete_act, dest));
|
||||
}
|
||||
|
||||
Ok(Redirect::to(
|
||||
|
||||
+1
-32
@@ -10,40 +10,9 @@ use rocket::{
|
||||
response::NamedFile,
|
||||
Outcome,
|
||||
};
|
||||
use rocket_i18n::I18n;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use plume_models::{db_conn::DbConn, posts::Post, users::User, Connection};
|
||||
|
||||
use Searcher;
|
||||
use Worker;
|
||||
|
||||
pub struct PlumeRocket<'a> {
|
||||
conn: DbConn,
|
||||
intl: I18n,
|
||||
user: Option<User>,
|
||||
searcher: Searcher<'a>,
|
||||
worker: Worker<'a>,
|
||||
}
|
||||
|
||||
impl<'a, 'r> FromRequest<'a, 'r> for PlumeRocket<'a> {
|
||||
type Error = ();
|
||||
|
||||
fn from_request(request: &'a Request<'r>) -> request::Outcome<PlumeRocket<'a>, ()> {
|
||||
let conn = request.guard::<DbConn>()?;
|
||||
let intl = request.guard::<I18n>()?;
|
||||
let user = request.guard::<User>().succeeded();
|
||||
let worker = request.guard::<Worker>()?;
|
||||
let searcher = request.guard::<Searcher>()?;
|
||||
rocket::Outcome::Success(PlumeRocket {
|
||||
conn,
|
||||
intl,
|
||||
user,
|
||||
worker,
|
||||
searcher,
|
||||
})
|
||||
}
|
||||
}
|
||||
use plume_models::{posts::Post, Connection};
|
||||
|
||||
const ITEMS_PER_PAGE: i32 = 12;
|
||||
|
||||
|
||||
+58
-54
@@ -10,12 +10,12 @@ use std::{
|
||||
};
|
||||
use validator::{Validate, ValidationError, ValidationErrors};
|
||||
|
||||
use plume_common::activity_pub::{broadcast, inbox::Deletable, ActivityStream, ApRequest};
|
||||
use plume_common::activity_pub::{broadcast, ActivityStream, ApRequest};
|
||||
use plume_common::utils;
|
||||
use plume_models::{
|
||||
blogs::*,
|
||||
comments::{Comment, CommentTree},
|
||||
db_conn::DbConn,
|
||||
inbox::inbox,
|
||||
instance::Instance,
|
||||
medias::Media,
|
||||
mentions::Mention,
|
||||
@@ -24,20 +24,21 @@ use plume_models::{
|
||||
safe_string::SafeString,
|
||||
tags::*,
|
||||
users::User,
|
||||
Error, PlumeRocket,
|
||||
};
|
||||
use routes::{comments::NewCommentForm, errors::ErrorPage, ContentLen, PlumeRocket};
|
||||
use routes::{comments::NewCommentForm, errors::ErrorPage, ContentLen};
|
||||
use template_utils::Ructe;
|
||||
|
||||
#[get("/~/<blog>/<slug>?<responding_to>", rank = 4)]
|
||||
pub fn details(
|
||||
blog: String,
|
||||
slug: String,
|
||||
conn: DbConn,
|
||||
user: Option<User>,
|
||||
responding_to: Option<i32>,
|
||||
intl: I18n,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Ructe, ErrorPage> {
|
||||
let blog = Blog::find_by_fqn(&*conn, &blog)?;
|
||||
let conn = &*rockets.conn;
|
||||
let user = rockets.user.clone();
|
||||
let blog = Blog::find_by_fqn(&rockets, &blog)?;
|
||||
let post = Post::find_by_slug(&*conn, &slug, blog.id)?;
|
||||
if post.published
|
||||
|| post
|
||||
@@ -50,7 +51,7 @@ pub fn details(
|
||||
let previous = responding_to.and_then(|r| Comment::get(&*conn, r).ok());
|
||||
|
||||
Ok(render!(posts::details(
|
||||
&(&*conn, &intl.catalog, user.clone()),
|
||||
&(&*conn, &rockets.intl.catalog, user.clone()),
|
||||
post.clone(),
|
||||
blog,
|
||||
&NewCommentForm {
|
||||
@@ -88,8 +89,8 @@ pub fn details(
|
||||
)))
|
||||
} else {
|
||||
Ok(render!(errors::not_authorized(
|
||||
&(&*conn, &intl.catalog, user.clone()),
|
||||
i18n!(intl.catalog, "This post isn't published yet.")
|
||||
&(&*conn, &rockets.intl.catalog, user.clone()),
|
||||
i18n!(rockets.intl.catalog, "This post isn't published yet.")
|
||||
)))
|
||||
}
|
||||
}
|
||||
@@ -98,10 +99,11 @@ pub fn details(
|
||||
pub fn activity_details(
|
||||
blog: String,
|
||||
slug: String,
|
||||
conn: DbConn,
|
||||
_ap: ApRequest,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<ActivityStream<LicensedArticle>, Option<String>> {
|
||||
let blog = Blog::find_by_fqn(&*conn, &blog).map_err(|_| None)?;
|
||||
let conn = &*rockets.conn;
|
||||
let blog = Blog::find_by_fqn(&rockets, &blog).map_err(|_| None)?;
|
||||
let post = Post::find_by_slug(&*conn, &slug, blog.id).map_err(|_| None)?;
|
||||
if post.published {
|
||||
Ok(ActivityStream::new(
|
||||
@@ -126,8 +128,8 @@ pub fn new_auth(blog: String, i18n: I18n) -> Flash<Redirect> {
|
||||
|
||||
#[get("/~/<blog>/new", rank = 1)]
|
||||
pub fn new(blog: String, cl: ContentLen, rockets: PlumeRocket) -> Result<Ructe, ErrorPage> {
|
||||
let conn = rockets.conn;
|
||||
let b = Blog::find_by_fqn(&*conn, &blog)?;
|
||||
let conn = &*rockets.conn;
|
||||
let b = Blog::find_by_fqn(&rockets, &blog)?;
|
||||
let user = rockets.user.unwrap();
|
||||
let intl = rockets.intl;
|
||||
|
||||
@@ -164,16 +166,16 @@ pub fn edit(
|
||||
cl: ContentLen,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Ructe, ErrorPage> {
|
||||
let conn = rockets.conn;
|
||||
let intl = rockets.intl;
|
||||
let b = Blog::find_by_fqn(&*conn, &blog)?;
|
||||
let conn = &*rockets.conn;
|
||||
let intl = &rockets.intl.catalog;
|
||||
let b = Blog::find_by_fqn(&rockets, &blog)?;
|
||||
let post = Post::find_by_slug(&*conn, &slug, b.id)?;
|
||||
let user = rockets.user.unwrap();
|
||||
|
||||
if !user.is_author_in(&*conn, &b)? {
|
||||
return Ok(render!(errors::not_authorized(
|
||||
&(&*conn, &intl.catalog, Some(user)),
|
||||
i18n!(intl.catalog, "You are not an author of this blog.")
|
||||
&(&*conn, intl, Some(user)),
|
||||
i18n!(intl, "You are not an author of this blog.")
|
||||
)));
|
||||
}
|
||||
|
||||
@@ -186,8 +188,8 @@ pub fn edit(
|
||||
let medias = Media::for_user(&*conn, user.id)?;
|
||||
let title = post.title.clone();
|
||||
Ok(render!(posts::new(
|
||||
&(&*conn, &intl.catalog, Some(user)),
|
||||
i18n!(intl.catalog, "Edit {0}"; &title),
|
||||
&(&*conn, intl, Some(user)),
|
||||
i18n!(intl, "Edit {0}"; &title),
|
||||
b,
|
||||
true,
|
||||
&NewPostForm {
|
||||
@@ -219,12 +221,12 @@ pub fn update(
|
||||
form: LenientForm<NewPostForm>,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Redirect, Ructe> {
|
||||
let conn = rockets.conn;
|
||||
let b = Blog::find_by_fqn(&*conn, &blog).expect("post::update: blog error");
|
||||
let conn = &*rockets.conn;
|
||||
let b = Blog::find_by_fqn(&rockets, &blog).expect("post::update: blog error");
|
||||
let mut post =
|
||||
Post::find_by_slug(&*conn, &slug, b.id).expect("post::update: find by slug error");
|
||||
let user = rockets.user.unwrap();
|
||||
let intl = rockets.intl;
|
||||
let user = rockets.user.clone().unwrap();
|
||||
let intl = &rockets.intl.catalog;
|
||||
|
||||
let new_slug = if !post.published {
|
||||
form.title.to_string().to_kebab_case()
|
||||
@@ -282,8 +284,6 @@ pub fn update(
|
||||
false
|
||||
};
|
||||
|
||||
let searcher = rockets.searcher;
|
||||
let worker = rockets.worker;
|
||||
post.slug = new_slug.clone();
|
||||
post.title = form.title.clone();
|
||||
post.subtitle = form.subtitle.clone();
|
||||
@@ -291,7 +291,7 @@ pub fn update(
|
||||
post.source = form.content.clone();
|
||||
post.license = form.license.clone();
|
||||
post.cover_id = form.cover;
|
||||
post.update(&*conn, &searcher)
|
||||
post.update(&*conn, &rockets.searcher)
|
||||
.expect("post::update: update error");;
|
||||
|
||||
if post.published {
|
||||
@@ -299,7 +299,7 @@ pub fn update(
|
||||
&conn,
|
||||
mentions
|
||||
.into_iter()
|
||||
.filter_map(|m| Mention::build_activity(&conn, &m).ok())
|
||||
.filter_map(|m| Mention::build_activity(&rockets, &m).ok())
|
||||
.collect(),
|
||||
)
|
||||
.expect("post::update: mentions error");;
|
||||
@@ -333,13 +333,13 @@ pub fn update(
|
||||
.create_activity(&conn)
|
||||
.expect("post::update: act error");
|
||||
let dest = User::one_by_instance(&*conn).expect("post::update: dest error");
|
||||
worker.execute(move || broadcast(&user, act, dest));
|
||||
rockets.worker.execute(move || broadcast(&user, act, dest));
|
||||
} else {
|
||||
let act = post
|
||||
.update_activity(&*conn)
|
||||
.expect("post::update: act error");
|
||||
let dest = User::one_by_instance(&*conn).expect("posts::update: dest error");
|
||||
worker.execute(move || broadcast(&user, act, dest));
|
||||
rockets.worker.execute(move || broadcast(&user, act, dest));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -350,8 +350,8 @@ pub fn update(
|
||||
} else {
|
||||
let medias = Media::for_user(&*conn, user.id).expect("posts:update: medias error");
|
||||
Err(render!(posts::new(
|
||||
&(&*conn, &intl.catalog, Some(user)),
|
||||
i18n!(intl.catalog, "Edit {0}"; &form.title),
|
||||
&(&*conn, intl, Some(user)),
|
||||
i18n!(intl, "Edit {0}"; &form.title),
|
||||
b,
|
||||
true,
|
||||
&*form,
|
||||
@@ -394,10 +394,10 @@ pub fn create(
|
||||
cl: ContentLen,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Redirect, Result<Ructe, ErrorPage>> {
|
||||
let conn = rockets.conn;
|
||||
let blog = Blog::find_by_fqn(&*conn, &blog_name).expect("post::create: blog error");;
|
||||
let conn = &*rockets.conn;
|
||||
let blog = Blog::find_by_fqn(&rockets, &blog_name).expect("post::create: blog error");;
|
||||
let slug = form.title.to_string().to_kebab_case();
|
||||
let user = rockets.user.unwrap();
|
||||
let user = rockets.user.clone().unwrap();
|
||||
|
||||
let mut errors = match form.validate() {
|
||||
Ok(_) => ValidationErrors::new(),
|
||||
@@ -440,7 +440,6 @@ pub fn create(
|
||||
)),
|
||||
);
|
||||
|
||||
let searcher = rockets.searcher;
|
||||
let post = Post::insert(
|
||||
&*conn,
|
||||
NewPost {
|
||||
@@ -456,7 +455,7 @@ pub fn create(
|
||||
source: form.content.clone(),
|
||||
cover_id: form.cover,
|
||||
},
|
||||
&searcher,
|
||||
&rockets.searcher,
|
||||
)
|
||||
.expect("post::create: post save error");
|
||||
|
||||
@@ -502,7 +501,7 @@ pub fn create(
|
||||
for m in mentions {
|
||||
Mention::from_activity(
|
||||
&*conn,
|
||||
&Mention::build_activity(&*conn, &m)
|
||||
&Mention::build_activity(&rockets, &m)
|
||||
.expect("post::create: mention build error"),
|
||||
post.id,
|
||||
true,
|
||||
@@ -546,14 +545,13 @@ pub fn delete(
|
||||
slug: String,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Redirect, ErrorPage> {
|
||||
let conn = rockets.conn;
|
||||
let user = rockets.user.unwrap();
|
||||
let post = Blog::find_by_fqn(&*conn, &blog_name)
|
||||
.and_then(|blog| Post::find_by_slug(&*conn, &slug, blog.id));
|
||||
let user = rockets.user.clone().unwrap();
|
||||
let post = Blog::find_by_fqn(&rockets, &blog_name)
|
||||
.and_then(|blog| Post::find_by_slug(&*rockets.conn, &slug, blog.id));
|
||||
|
||||
if let Ok(post) = post {
|
||||
if !post
|
||||
.get_authors(&*conn)?
|
||||
.get_authors(&*rockets.conn)?
|
||||
.into_iter()
|
||||
.any(|a| a.id == user.id)
|
||||
{
|
||||
@@ -562,18 +560,24 @@ pub fn delete(
|
||||
));
|
||||
}
|
||||
|
||||
let searcher = rockets.searcher;
|
||||
let worker = rockets.worker;
|
||||
let dest = User::one_by_instance(&*rockets.conn)?;
|
||||
let delete_activity = post.build_delete(&*rockets.conn)?;
|
||||
inbox(
|
||||
&rockets,
|
||||
serde_json::to_value(&delete_activity).map_err(Error::from)?,
|
||||
)?;
|
||||
|
||||
let dest = User::one_by_instance(&*conn)?;
|
||||
let delete_activity = post.delete(&(&conn, &searcher))?;
|
||||
let user_c = user.clone();
|
||||
|
||||
worker.execute(move || broadcast(&user_c, delete_activity, dest));
|
||||
worker.execute_after(Duration::from_secs(10 * 60), move || {
|
||||
user.rotate_keypair(&conn)
|
||||
.expect("Failed to rotate keypair");
|
||||
});
|
||||
rockets
|
||||
.worker
|
||||
.execute(move || broadcast(&user_c, delete_activity, dest));
|
||||
let conn = rockets.conn;
|
||||
rockets
|
||||
.worker
|
||||
.execute_after(Duration::from_secs(10 * 60), move || {
|
||||
user.rotate_keypair(&*conn)
|
||||
.expect("Failed to rotate keypair");
|
||||
});
|
||||
|
||||
Ok(Redirect::to(
|
||||
uri!(super::blogs::details: name = blog_name, page = _),
|
||||
|
||||
+17
-12
@@ -1,24 +1,22 @@
|
||||
use rocket::response::{Flash, Redirect};
|
||||
use rocket_i18n::I18n;
|
||||
|
||||
use plume_common::activity_pub::{
|
||||
broadcast,
|
||||
inbox::{Deletable, Notify},
|
||||
};
|
||||
use plume_common::activity_pub::broadcast;
|
||||
use plume_common::utils;
|
||||
use plume_models::{blogs::Blog, db_conn::DbConn, posts::Post, reshares::*, users::User};
|
||||
use plume_models::{
|
||||
blogs::Blog, inbox::inbox, posts::Post, reshares::*, users::User, Error, PlumeRocket,
|
||||
};
|
||||
use routes::errors::ErrorPage;
|
||||
use Worker;
|
||||
|
||||
#[post("/~/<blog>/<slug>/reshare")]
|
||||
pub fn create(
|
||||
blog: String,
|
||||
slug: String,
|
||||
user: User,
|
||||
conn: DbConn,
|
||||
worker: Worker,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Redirect, ErrorPage> {
|
||||
let b = Blog::find_by_fqn(&*conn, &blog)?;
|
||||
let conn = &*rockets.conn;
|
||||
let b = Blog::find_by_fqn(&rockets, &blog)?;
|
||||
let post = Post::find_by_slug(&*conn, &slug, b.id)?;
|
||||
|
||||
if !user.has_reshared(&*conn, &post)? {
|
||||
@@ -27,12 +25,19 @@ pub fn create(
|
||||
|
||||
let dest = User::one_by_instance(&*conn)?;
|
||||
let act = reshare.to_activity(&*conn)?;
|
||||
worker.execute(move || broadcast(&user, act, dest));
|
||||
rockets.worker.execute(move || broadcast(&user, act, dest));
|
||||
} else {
|
||||
let reshare = Reshare::find_by_user_on_post(&*conn, user.id, post.id)?;
|
||||
let delete_act = reshare.delete(&*conn)?;
|
||||
let delete_act = reshare.build_undo(&*conn)?;
|
||||
inbox(
|
||||
&rockets,
|
||||
serde_json::to_value(&delete_act).map_err(Error::from)?,
|
||||
)?;
|
||||
|
||||
let dest = User::one_by_instance(&*conn)?;
|
||||
worker.execute(move || broadcast(&user, delete_act, dest));
|
||||
rockets
|
||||
.worker
|
||||
.execute(move || broadcast(&user, delete_act, dest));
|
||||
}
|
||||
|
||||
Ok(Redirect::to(
|
||||
|
||||
@@ -19,7 +19,7 @@ use mail::{build_mail, Mailer};
|
||||
use plume_models::{
|
||||
db_conn::DbConn,
|
||||
users::{User, AUTH_COOKIE},
|
||||
Error, CONFIG,
|
||||
Error, PlumeRocket, CONFIG,
|
||||
};
|
||||
use routes::errors::ErrorPage;
|
||||
|
||||
@@ -43,14 +43,14 @@ pub struct LoginForm {
|
||||
|
||||
#[post("/login", data = "<form>")]
|
||||
pub fn create(
|
||||
conn: DbConn,
|
||||
form: LenientForm<LoginForm>,
|
||||
flash: Option<FlashMessage>,
|
||||
mut cookies: Cookies,
|
||||
intl: I18n,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Redirect, Ructe> {
|
||||
let conn = &*rockets.conn;
|
||||
let user = User::find_by_email(&*conn, &form.email_or_name)
|
||||
.or_else(|_| User::find_by_fqn(&*conn, &form.email_or_name));
|
||||
.or_else(|_| User::find_by_fqn(&rockets, &form.email_or_name));
|
||||
let mut errors = match form.validate() {
|
||||
Ok(_) => ValidationErrors::new(),
|
||||
Err(e) => e,
|
||||
@@ -98,7 +98,7 @@ pub fn create(
|
||||
.map(IntoOwned::into_owned)
|
||||
.map_err(|_| {
|
||||
render!(session::login(
|
||||
&(&*conn, &intl.catalog, None),
|
||||
&(&*conn, &rockets.intl.catalog, None),
|
||||
None,
|
||||
&*form,
|
||||
errors
|
||||
@@ -108,7 +108,7 @@ pub fn create(
|
||||
Ok(Redirect::to(uri))
|
||||
} else {
|
||||
Err(render!(session::login(
|
||||
&(&*conn, &intl.catalog, None),
|
||||
&(&*conn, &rockets.intl.catalog, None),
|
||||
None,
|
||||
&*form,
|
||||
errors
|
||||
|
||||
+63
-115
@@ -10,29 +10,23 @@ use serde_json;
|
||||
use std::{borrow::Cow, collections::HashMap};
|
||||
use validator::{Validate, ValidationError, ValidationErrors};
|
||||
|
||||
use inbox::{Inbox, SignedJson};
|
||||
use plume_common::activity_pub::{
|
||||
broadcast,
|
||||
inbox::{Deletable, FromActivity, Notify},
|
||||
sign::{verify_http_headers, Signable},
|
||||
ActivityStream, ApRequest, Id, IntoId,
|
||||
};
|
||||
use inbox;
|
||||
use plume_common::activity_pub::{broadcast, inbox::FromId, ActivityStream, ApRequest, Id};
|
||||
use plume_common::utils;
|
||||
use plume_models::{
|
||||
blogs::Blog,
|
||||
db_conn::DbConn,
|
||||
follows,
|
||||
headers::Headers,
|
||||
inbox::inbox as local_inbox,
|
||||
instance::Instance,
|
||||
posts::{LicensedArticle, Post},
|
||||
reshares::Reshare,
|
||||
users::*,
|
||||
Error,
|
||||
Error, PlumeRocket,
|
||||
};
|
||||
use routes::{errors::ErrorPage, Page, PlumeRocket};
|
||||
use routes::{errors::ErrorPage, Page};
|
||||
use template_utils::Ructe;
|
||||
use Searcher;
|
||||
use Worker;
|
||||
|
||||
#[get("/me")]
|
||||
pub fn me(user: Option<User>) -> Result<Redirect, Flash<Redirect>> {
|
||||
@@ -46,21 +40,19 @@ pub fn me(user: Option<User>) -> Result<Redirect, Flash<Redirect>> {
|
||||
pub fn details(
|
||||
name: String,
|
||||
rockets: PlumeRocket,
|
||||
fetch_articles_conn: DbConn,
|
||||
fetch_followers_conn: DbConn,
|
||||
fetch_rockets: PlumeRocket,
|
||||
fetch_followers_rockets: PlumeRocket,
|
||||
update_conn: DbConn,
|
||||
) -> Result<Ructe, ErrorPage> {
|
||||
let conn = rockets.conn;
|
||||
let user = User::find_by_fqn(&*conn, &name)?;
|
||||
let conn = &*rockets.conn;
|
||||
let user = User::find_by_fqn(&rockets, &name)?;
|
||||
let recents = Post::get_recents_for_author(&*conn, &user, 6)?;
|
||||
let reshares = Reshare::get_recents_for_author(&*conn, &user, 6)?;
|
||||
let searcher = rockets.searcher;
|
||||
let worker = rockets.worker;
|
||||
|
||||
if !user.get_instance(&*conn)?.local {
|
||||
// Fetch new articles
|
||||
let user_clone = user.clone();
|
||||
let searcher = searcher.clone();
|
||||
worker.execute(move || {
|
||||
for create_act in user_clone
|
||||
.fetch_outbox::<Create>()
|
||||
@@ -68,12 +60,8 @@ pub fn details(
|
||||
{
|
||||
match create_act.create_props.object_object::<LicensedArticle>() {
|
||||
Ok(article) => {
|
||||
Post::from_activity(
|
||||
&(&*fetch_articles_conn, &searcher),
|
||||
article,
|
||||
user_clone.clone().into_id(),
|
||||
)
|
||||
.expect("Article from remote user couldn't be saved");
|
||||
Post::from_activity(&fetch_rockets, article)
|
||||
.expect("Article from remote user couldn't be saved");
|
||||
println!("Fetched article from remote user");
|
||||
}
|
||||
Err(e) => println!("Error while fetching articles in background: {:?}", e),
|
||||
@@ -88,10 +76,10 @@ pub fn details(
|
||||
.fetch_followers_ids()
|
||||
.expect("Remote user: fetching followers error")
|
||||
{
|
||||
let follower = User::from_url(&*fetch_followers_conn, &user_id)
|
||||
let follower = User::from_id(&fetch_followers_rockets, &user_id, None)
|
||||
.expect("user::details: Couldn't fetch follower");
|
||||
follows::Follow::insert(
|
||||
&*fetch_followers_conn,
|
||||
&*fetch_followers_rockets.conn,
|
||||
follows::NewFollow {
|
||||
follower_id: follower.id,
|
||||
following_id: user_clone.id,
|
||||
@@ -153,16 +141,19 @@ pub fn dashboard_auth(i18n: I18n) -> Flash<Redirect> {
|
||||
}
|
||||
|
||||
#[post("/@/<name>/follow")]
|
||||
pub fn follow(
|
||||
name: String,
|
||||
conn: DbConn,
|
||||
user: User,
|
||||
worker: Worker,
|
||||
) -> Result<Redirect, ErrorPage> {
|
||||
let target = User::find_by_fqn(&*conn, &name)?;
|
||||
pub fn follow(name: String, user: User, rockets: PlumeRocket) -> Result<Redirect, ErrorPage> {
|
||||
let conn = &*rockets.conn;
|
||||
let target = User::find_by_fqn(&rockets, &name)?;
|
||||
if let Ok(follow) = follows::Follow::find(&*conn, user.id, target.id) {
|
||||
let delete_act = follow.delete(&*conn)?;
|
||||
worker.execute(move || broadcast(&user, delete_act, vec![target]));
|
||||
let delete_act = follow.build_undo(&*conn)?;
|
||||
local_inbox(
|
||||
&rockets,
|
||||
serde_json::to_value(&delete_act).map_err(Error::from)?,
|
||||
)?;
|
||||
|
||||
rockets
|
||||
.worker
|
||||
.execute(move || broadcast(&user, delete_act, vec![target]));
|
||||
} else {
|
||||
let f = follows::Follow::insert(
|
||||
&*conn,
|
||||
@@ -175,7 +166,9 @@ pub fn follow(
|
||||
f.notify(&*conn)?;
|
||||
|
||||
let act = f.to_activity(&*conn)?;
|
||||
worker.execute(move || broadcast(&user, act, vec![target]));
|
||||
rockets
|
||||
.worker
|
||||
.execute(move || broadcast(&user, act, vec![target]));
|
||||
}
|
||||
Ok(Redirect::to(uri!(details: name = name)))
|
||||
}
|
||||
@@ -194,19 +187,19 @@ pub fn follow_auth(name: String, i18n: I18n) -> Flash<Redirect> {
|
||||
#[get("/@/<name>/followers?<page>", rank = 2)]
|
||||
pub fn followers(
|
||||
name: String,
|
||||
conn: DbConn,
|
||||
account: Option<User>,
|
||||
page: Option<Page>,
|
||||
intl: I18n,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Ructe, ErrorPage> {
|
||||
let conn = &*rockets.conn;
|
||||
let page = page.unwrap_or_default();
|
||||
let user = User::find_by_fqn(&*conn, &name)?;
|
||||
let user = User::find_by_fqn(&rockets, &name)?;
|
||||
let followers_count = user.count_followers(&*conn)?;
|
||||
|
||||
Ok(render!(users::followers(
|
||||
&(&*conn, &intl.catalog, account.clone()),
|
||||
&(&*conn, &rockets.intl.catalog, rockets.user.clone()),
|
||||
user.clone(),
|
||||
account
|
||||
rockets
|
||||
.user
|
||||
.and_then(|x| x.is_following(&*conn, user.id).ok())
|
||||
.unwrap_or(false),
|
||||
user.instance_id != Instance::get_local(&*conn)?.id,
|
||||
@@ -220,19 +213,19 @@ pub fn followers(
|
||||
#[get("/@/<name>/followed?<page>", rank = 2)]
|
||||
pub fn followed(
|
||||
name: String,
|
||||
conn: DbConn,
|
||||
account: Option<User>,
|
||||
page: Option<Page>,
|
||||
intl: I18n,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Ructe, ErrorPage> {
|
||||
let conn = &*rockets.conn;
|
||||
let page = page.unwrap_or_default();
|
||||
let user = User::find_by_fqn(&*conn, &name)?;
|
||||
let user = User::find_by_fqn(&rockets, &name)?;
|
||||
let followed_count = user.count_followed(&*conn)?;
|
||||
|
||||
Ok(render!(users::followed(
|
||||
&(&*conn, &intl.catalog, account.clone()),
|
||||
&(&*conn, &rockets.intl.catalog, rockets.user.clone()),
|
||||
user.clone(),
|
||||
account
|
||||
rockets
|
||||
.user
|
||||
.and_then(|x| x.is_following(&*conn, user.id).ok())
|
||||
.unwrap_or(false),
|
||||
user.instance_id != Instance::get_local(&*conn)?.id,
|
||||
@@ -246,11 +239,11 @@ pub fn followed(
|
||||
#[get("/@/<name>", rank = 1)]
|
||||
pub fn activity_details(
|
||||
name: String,
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
_ap: ApRequest,
|
||||
) -> Option<ActivityStream<CustomPerson>> {
|
||||
let user = User::find_by_fqn(&*conn, &name).ok()?;
|
||||
Some(ActivityStream::new(user.to_activity(&*conn).ok()?))
|
||||
let user = User::find_by_fqn(&rockets, &name).ok()?;
|
||||
Some(ActivityStream::new(user.to_activity(&*rockets.conn).ok()?))
|
||||
}
|
||||
|
||||
#[get("/users/new")]
|
||||
@@ -329,14 +322,13 @@ pub fn update(
|
||||
#[post("/@/<name>/delete")]
|
||||
pub fn delete(
|
||||
name: String,
|
||||
conn: DbConn,
|
||||
user: User,
|
||||
mut cookies: Cookies,
|
||||
searcher: Searcher,
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<Redirect, ErrorPage> {
|
||||
let account = User::find_by_fqn(&*conn, &name)?;
|
||||
let account = User::find_by_fqn(&rockets, &name)?;
|
||||
if user.id == account.id {
|
||||
account.delete(&*conn, &searcher)?;
|
||||
account.delete(&*rockets.conn, &rockets.searcher)?;
|
||||
|
||||
if let Some(cookie) = cookies.get_private(AUTH_COOKIE) {
|
||||
cookies.remove_private(cookie);
|
||||
@@ -439,76 +431,31 @@ pub fn create(conn: DbConn, form: LenientForm<NewUserForm>, intl: I18n) -> Resul
|
||||
}
|
||||
|
||||
#[get("/@/<name>/outbox")]
|
||||
pub fn outbox(name: String, conn: DbConn) -> Option<ActivityStream<OrderedCollection>> {
|
||||
let user = User::find_by_fqn(&*conn, &name).ok()?;
|
||||
user.outbox(&*conn).ok()
|
||||
pub fn outbox(name: String, rockets: PlumeRocket) -> Option<ActivityStream<OrderedCollection>> {
|
||||
let user = User::find_by_fqn(&rockets, &name).ok()?;
|
||||
user.outbox(&*rockets.conn).ok()
|
||||
}
|
||||
|
||||
#[post("/@/<name>/inbox", data = "<data>")]
|
||||
pub fn inbox(
|
||||
name: String,
|
||||
conn: DbConn,
|
||||
data: SignedJson<serde_json::Value>,
|
||||
data: inbox::SignedJson<serde_json::Value>,
|
||||
headers: Headers,
|
||||
searcher: Searcher,
|
||||
) -> Result<String, Option<status::BadRequest<&'static str>>> {
|
||||
let user = User::find_by_fqn(&*conn, &name).map_err(|_| None)?;
|
||||
let act = data.1.into_inner();
|
||||
let sig = data.0;
|
||||
|
||||
let activity = act.clone();
|
||||
let actor_id = activity["actor"]
|
||||
.as_str()
|
||||
.or_else(|| activity["actor"]["id"].as_str())
|
||||
.ok_or(Some(status::BadRequest(Some(
|
||||
"Missing actor id for activity",
|
||||
))))?;
|
||||
|
||||
let actor = User::from_url(&conn, actor_id).expect("user::inbox: user error");
|
||||
if !verify_http_headers(&actor, &headers.0, &sig).is_secure() && !act.clone().verify(&actor) {
|
||||
// maybe we just know an old key?
|
||||
actor
|
||||
.refetch(&conn)
|
||||
.and_then(|_| User::get(&conn, actor.id))
|
||||
.and_then(|actor| {
|
||||
if verify_http_headers(&actor, &headers.0, &sig).is_secure()
|
||||
|| act.clone().verify(&actor)
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::Signature)
|
||||
}
|
||||
})
|
||||
.map_err(|_| {
|
||||
println!(
|
||||
"Rejected invalid activity supposedly from {}, with headers {:?}",
|
||||
actor.username, headers.0
|
||||
);
|
||||
status::BadRequest(Some("Invalid signature"))
|
||||
})?;
|
||||
}
|
||||
|
||||
if Instance::is_blocked(&*conn, actor_id).map_err(|_| None)? {
|
||||
return Ok(String::new());
|
||||
}
|
||||
Ok(match user.received(&*conn, &searcher, act) {
|
||||
Ok(_) => String::new(),
|
||||
Err(e) => {
|
||||
println!("User inbox error: {}\n{}", e.as_fail(), e.backtrace());
|
||||
format!("Error: {}", e.as_fail())
|
||||
}
|
||||
})
|
||||
rockets: PlumeRocket,
|
||||
) -> Result<String, status::BadRequest<&'static str>> {
|
||||
User::find_by_fqn(&rockets, &name).map_err(|_| status::BadRequest(Some("User not found")))?;
|
||||
inbox::handle_incoming(rockets, data, headers)
|
||||
}
|
||||
|
||||
#[get("/@/<name>/followers", rank = 1)]
|
||||
pub fn ap_followers(
|
||||
name: String,
|
||||
conn: DbConn,
|
||||
rockets: PlumeRocket,
|
||||
_ap: ApRequest,
|
||||
) -> Option<ActivityStream<OrderedCollection>> {
|
||||
let user = User::find_by_fqn(&*conn, &name).ok()?;
|
||||
let user = User::find_by_fqn(&rockets, &name).ok()?;
|
||||
let followers = user
|
||||
.get_followers(&*conn)
|
||||
.get_followers(&*rockets.conn)
|
||||
.ok()?
|
||||
.into_iter()
|
||||
.map(|f| Id::new(f.ap_url))
|
||||
@@ -526,18 +473,19 @@ pub fn ap_followers(
|
||||
}
|
||||
|
||||
#[get("/@/<name>/atom.xml")]
|
||||
pub fn atom_feed(name: String, conn: DbConn) -> Option<Content<String>> {
|
||||
let author = User::find_by_fqn(&*conn, &name).ok()?;
|
||||
pub fn atom_feed(name: String, rockets: PlumeRocket) -> Option<Content<String>> {
|
||||
let conn = &*rockets.conn;
|
||||
let author = User::find_by_fqn(&rockets, &name).ok()?;
|
||||
let feed = FeedBuilder::default()
|
||||
.title(author.display_name.clone())
|
||||
.id(Instance::get_local(&*conn)
|
||||
.id(Instance::get_local(conn)
|
||||
.unwrap()
|
||||
.compute_box("@", &name, "atom.xml"))
|
||||
.entries(
|
||||
Post::get_recents_for_author(&*conn, &author, 15)
|
||||
Post::get_recents_for_author(conn, &author, 15)
|
||||
.ok()?
|
||||
.into_iter()
|
||||
.map(|p| super::post_to_atom(p, &*conn))
|
||||
.map(|p| super::post_to_atom(p, conn))
|
||||
.collect::<Vec<Entry>>(),
|
||||
)
|
||||
.build()
|
||||
|
||||
@@ -3,7 +3,7 @@ use rocket::response::Content;
|
||||
use serde_json;
|
||||
use webfinger::*;
|
||||
|
||||
use plume_models::{ap_url, blogs::Blog, db_conn::DbConn, users::User, CONFIG};
|
||||
use plume_models::{ap_url, blogs::Blog, users::User, PlumeRocket, CONFIG};
|
||||
|
||||
#[get("/.well-known/nodeinfo")]
|
||||
pub fn nodeinfo() -> Content<String> {
|
||||
@@ -43,25 +43,25 @@ pub fn host_meta() -> String {
|
||||
|
||||
struct WebfingerResolver;
|
||||
|
||||
impl Resolver<DbConn> for WebfingerResolver {
|
||||
impl Resolver<PlumeRocket> for WebfingerResolver {
|
||||
fn instance_domain<'a>() -> &'a str {
|
||||
CONFIG.base_url.as_str()
|
||||
}
|
||||
|
||||
fn find(acct: String, conn: DbConn) -> Result<Webfinger, ResolverError> {
|
||||
User::find_by_fqn(&*conn, &acct)
|
||||
.and_then(|usr| usr.webfinger(&*conn))
|
||||
fn find(acct: String, ctx: PlumeRocket) -> Result<Webfinger, ResolverError> {
|
||||
User::find_by_fqn(&ctx, &acct)
|
||||
.and_then(|usr| usr.webfinger(&*ctx.conn))
|
||||
.or_else(|_| {
|
||||
Blog::find_by_fqn(&*conn, &acct)
|
||||
.and_then(|blog| blog.webfinger(&*conn))
|
||||
Blog::find_by_fqn(&ctx, &acct)
|
||||
.and_then(|blog| blog.webfinger(&*ctx.conn))
|
||||
.or(Err(ResolverError::NotFound))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/.well-known/webfinger?<resource>")]
|
||||
pub fn webfinger(resource: String, conn: DbConn) -> Content<String> {
|
||||
match WebfingerResolver::endpoint(resource, conn)
|
||||
pub fn webfinger(resource: String, rockets: PlumeRocket) -> Content<String> {
|
||||
match WebfingerResolver::endpoint(resource, rockets)
|
||||
.and_then(|wf| serde_json::to_string(&wf).map_err(|_| ResolverError::NotFound))
|
||||
{
|
||||
Ok(wf) => Content(ContentType::new("application", "jrd+json"), wf),
|
||||
|
||||
Reference in New Issue
Block a user