Add support for remote interact (#519)
Add support for remote interaction ([this thing](https://eldritch.cafe/users/Barmaid/remote_follow) in mastodon) - [x] create the endpoint dispatching remote interactions to local pages - [x] add this endpoint to web-finger - [x] propose remote interaction when following and not connected - [x] propose remote interaction when liking/sharing and not connected - [x] propose remote interaction when commenting and not connected - [x] fetch posts/comments we don't know but remote interaction was requested for ?
This commit is contained in:
committed by
Baptiste Gelez
parent
12efe721cc
commit
0d708e1639
@@ -27,6 +27,8 @@ extern crate rocket;
|
||||
extern crate rocket_contrib;
|
||||
extern crate rocket_csrf;
|
||||
extern crate rocket_i18n;
|
||||
#[macro_use]
|
||||
extern crate runtime_fmt;
|
||||
extern crate scheduled_thread_pool;
|
||||
extern crate serde;
|
||||
#[macro_use]
|
||||
@@ -162,6 +164,7 @@ Then try to restart Plume
|
||||
routes::instance::toggle_block,
|
||||
routes::instance::update_settings,
|
||||
routes::instance::shared_inbox,
|
||||
routes::instance::interact,
|
||||
routes::instance::nodeinfo,
|
||||
routes::instance::about,
|
||||
routes::instance::web_manifest,
|
||||
@@ -183,6 +186,8 @@ Then try to restart Plume
|
||||
routes::posts::new_auth,
|
||||
routes::posts::create,
|
||||
routes::posts::delete,
|
||||
routes::posts::remote_interact,
|
||||
routes::posts::remote_interact_post,
|
||||
routes::reshares::create,
|
||||
routes::reshares::create_auth,
|
||||
routes::search::search,
|
||||
@@ -207,6 +212,7 @@ Then try to restart Plume
|
||||
routes::user::update,
|
||||
routes::user::delete,
|
||||
routes::user::follow,
|
||||
routes::user::follow_not_connected,
|
||||
routes::user::follow_auth,
|
||||
routes::user::activity_details,
|
||||
routes::user::outbox,
|
||||
|
||||
@@ -8,6 +8,7 @@ use serde_json;
|
||||
use validator::{Validate, ValidationErrors};
|
||||
|
||||
use inbox;
|
||||
use plume_common::activity_pub::inbox::FromId;
|
||||
use plume_models::{
|
||||
admin::Admin, comments::Comment, db_conn::DbConn, headers::Headers, instance::*, posts::Post,
|
||||
safe_string::SafeString, users::User, Error, PlumeRocket, CONFIG,
|
||||
@@ -217,6 +218,36 @@ pub fn shared_inbox(
|
||||
inbox::handle_incoming(rockets, data, headers)
|
||||
}
|
||||
|
||||
#[get("/remote_interact?<target>")]
|
||||
pub fn interact(rockets: PlumeRocket, user: Option<User>, target: String) -> Option<Redirect> {
|
||||
if User::find_by_fqn(&rockets, &target).is_ok() {
|
||||
return Some(Redirect::to(uri!(super::user::details: name = target)));
|
||||
}
|
||||
|
||||
if let Ok(post) = Post::from_id(&rockets, &target, None) {
|
||||
return Some(Redirect::to(
|
||||
uri!(super::posts::details: blog = post.get_blog(&rockets.conn).expect("Can't retrieve blog").fqn, slug = &post.slug, responding_to = _),
|
||||
));
|
||||
}
|
||||
|
||||
if let Ok(comment) = Comment::from_id(&rockets, &target, None) {
|
||||
if comment.can_see(&rockets.conn, user.as_ref()) {
|
||||
let post = comment
|
||||
.get_post(&rockets.conn)
|
||||
.expect("Can't retrieve post");
|
||||
return Some(Redirect::to(uri!(
|
||||
super::posts::details: blog = post
|
||||
.get_blog(&rockets.conn)
|
||||
.expect("Can't retrieve blog")
|
||||
.fqn,
|
||||
slug = &post.slug,
|
||||
responding_to = comment.id
|
||||
)));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
#[get("/nodeinfo/<version>")]
|
||||
pub fn nodeinfo(conn: DbConn, version: String) -> Result<Json<serde_json::Value>, ErrorPage> {
|
||||
if version != "2.0" && version != "2.1" {
|
||||
|
||||
@@ -71,6 +71,12 @@ impl Default for Page {
|
||||
}
|
||||
}
|
||||
|
||||
/// A form for remote interaction, used by multiple routes
|
||||
#[derive(Clone, Default, FromForm)]
|
||||
pub struct RemoteForm {
|
||||
pub remote: String,
|
||||
}
|
||||
|
||||
pub fn post_to_atom(post: Post, conn: &Connection) -> Entry {
|
||||
EntryBuilder::default()
|
||||
.title(format!("<![CDATA[{}]]>", post.title))
|
||||
|
||||
+54
-1
@@ -26,7 +26,7 @@ use plume_models::{
|
||||
users::User,
|
||||
Error, PlumeRocket,
|
||||
};
|
||||
use routes::{comments::NewCommentForm, errors::ErrorPage, ContentLen};
|
||||
use routes::{comments::NewCommentForm, errors::ErrorPage, ContentLen, RemoteForm};
|
||||
use template_utils::Ructe;
|
||||
|
||||
#[get("/~/<blog>/<slug>?<responding_to>", rank = 4)]
|
||||
@@ -588,3 +588,56 @@ pub fn delete(
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/~/<blog_name>/<slug>/remote_interact")]
|
||||
pub fn remote_interact(
|
||||
rockets: PlumeRocket,
|
||||
blog_name: String,
|
||||
slug: String,
|
||||
i18n: I18n,
|
||||
) -> Result<Ructe, ErrorPage> {
|
||||
let target = Blog::find_by_fqn(&rockets, &blog_name)
|
||||
.and_then(|blog| Post::find_by_slug(&rockets.conn, &slug, blog.id))?;
|
||||
Ok(render!(posts::remote_interact(
|
||||
&(&rockets.conn, &i18n.catalog, None),
|
||||
target,
|
||||
super::session::LoginForm::default(),
|
||||
ValidationErrors::default(),
|
||||
RemoteForm::default(),
|
||||
ValidationErrors::default()
|
||||
)))
|
||||
}
|
||||
|
||||
#[post("/~/<blog_name>/<slug>/remote_interact", data = "<remote>")]
|
||||
pub fn remote_interact_post(
|
||||
rockets: PlumeRocket,
|
||||
blog_name: String,
|
||||
slug: String,
|
||||
remote: LenientForm<RemoteForm>,
|
||||
i18n: I18n,
|
||||
) -> Result<Result<Ructe, Redirect>, ErrorPage> {
|
||||
let target = Blog::find_by_fqn(&rockets, &blog_name)
|
||||
.and_then(|blog| Post::find_by_slug(&rockets.conn, &slug, blog.id))?;
|
||||
if let Some(uri) = User::fetch_remote_interact_uri(&remote.remote)
|
||||
.ok()
|
||||
.and_then(|uri| rt_format!(uri, uri = target.ap_url).ok())
|
||||
{
|
||||
Ok(Err(Redirect::to(uri)))
|
||||
} else {
|
||||
let mut errs = ValidationErrors::new();
|
||||
errs.add("remote", ValidationError {
|
||||
code: Cow::from("invalid_remote"),
|
||||
message: Some(Cow::from(i18n!(&i18n.catalog, "Couldn't obtain enough information about your account. Please make sure your username is correct."))),
|
||||
params: HashMap::new(),
|
||||
});
|
||||
//could not get your remote url?
|
||||
Ok(Ok(render!(posts::remote_interact(
|
||||
&(&rockets.conn, &i18n.catalog, None),
|
||||
target,
|
||||
super::session::LoginForm::default(),
|
||||
ValidationErrors::default(),
|
||||
remote.clone(),
|
||||
errs
|
||||
))))
|
||||
}
|
||||
}
|
||||
|
||||
+66
-2
@@ -25,7 +25,7 @@ use plume_models::{
|
||||
users::*,
|
||||
Error, PlumeRocket,
|
||||
};
|
||||
use routes::{errors::ErrorPage, Page};
|
||||
use routes::{errors::ErrorPage, Page, RemoteForm};
|
||||
use template_utils::Ructe;
|
||||
|
||||
#[get("/me")]
|
||||
@@ -173,7 +173,71 @@ pub fn follow(name: String, user: User, rockets: PlumeRocket) -> Result<Redirect
|
||||
Ok(Redirect::to(uri!(details: name = name)))
|
||||
}
|
||||
|
||||
#[post("/@/<name>/follow", rank = 2)]
|
||||
#[post("/@/<name>/follow", data = "<remote_form>", rank = 2)]
|
||||
pub fn follow_not_connected(
|
||||
rockets: PlumeRocket,
|
||||
name: String,
|
||||
remote_form: Option<LenientForm<RemoteForm>>,
|
||||
i18n: I18n,
|
||||
) -> Result<Result<Flash<Ructe>, Redirect>, ErrorPage> {
|
||||
let target = User::find_by_fqn(&rockets, &name)?;
|
||||
if let Some(remote_form) = remote_form {
|
||||
let remote = &remote_form.remote;
|
||||
if let Some(uri) = User::fetch_remote_interact_uri(remote)
|
||||
.ok()
|
||||
.and_then(|uri| {
|
||||
rt_format!(
|
||||
uri,
|
||||
uri = format!(
|
||||
"{}@{}",
|
||||
target.fqn,
|
||||
target.get_instance(&rockets.conn).ok()?.public_domain
|
||||
)
|
||||
)
|
||||
.ok()
|
||||
})
|
||||
{
|
||||
Ok(Err(Redirect::to(uri)))
|
||||
} else {
|
||||
let mut err = ValidationErrors::default();
|
||||
err.add("remote",
|
||||
ValidationError {
|
||||
code: Cow::from("invalid_remote"),
|
||||
message: Some(Cow::from(i18n!(&i18n.catalog, "Couldn't obtain enough information about your account. Please make sure your username is correct."))),
|
||||
params: HashMap::new(),
|
||||
},
|
||||
);
|
||||
Ok(Ok(Flash::new(
|
||||
render!(users::follow_remote(
|
||||
&(&rockets.conn, &i18n.catalog, None),
|
||||
target,
|
||||
super::session::LoginForm::default(),
|
||||
ValidationErrors::default(),
|
||||
remote_form.clone(),
|
||||
err
|
||||
)),
|
||||
"callback",
|
||||
uri!(follow: name = name).to_string(),
|
||||
)))
|
||||
}
|
||||
} else {
|
||||
Ok(Ok(Flash::new(
|
||||
render!(users::follow_remote(
|
||||
&(&rockets.conn, &i18n.catalog, None),
|
||||
target,
|
||||
super::session::LoginForm::default(),
|
||||
ValidationErrors::default(),
|
||||
#[allow(clippy::map_clone)]
|
||||
remote_form.map(|x| x.clone()).unwrap_or_default(),
|
||||
ValidationErrors::default()
|
||||
)),
|
||||
"callback",
|
||||
uri!(follow: name = name).to_string(),
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/@/<name>/follow?local", rank = 2)]
|
||||
pub fn follow_auth(name: String, i18n: I18n) -> Flash<Redirect> {
|
||||
utils::requires_login(
|
||||
&i18n!(
|
||||
|
||||
Reference in New Issue
Block a user