mirror of
https://github.com/LemmyNet/lemmy.git
synced 2025-01-09 03:31:53 +00:00
got something that almost works
This commit is contained in:
parent
388b80d64e
commit
a619a0d52a
6 changed files with 148 additions and 116 deletions
67
crates/apub_receive/src/activities_new/follow.rs
Normal file
67
crates/apub_receive/src/activities_new/follow.rs
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
use url::Url;
|
||||||
|
use crate::inbox::new_inbox_routing::{ReceiveActivity, Activity, verify_domains_match};
|
||||||
|
use activitystreams::activity::kind::FollowType;
|
||||||
|
use activitystreams::activity::kind::AcceptType;
|
||||||
|
use crate::activities::receive::verify_activity_domains_valid;
|
||||||
|
use activitystreams::base::ExtendsExt;
|
||||||
|
use anyhow::Context;
|
||||||
|
use lemmy_apub::fetcher::community::get_or_fetch_and_upsert_community;
|
||||||
|
use lemmy_api_common::blocking;
|
||||||
|
use lemmy_db_schema::source::community::CommunityFollower;
|
||||||
|
use lemmy_websocket::LemmyContext;
|
||||||
|
use lemmy_utils::LemmyError;
|
||||||
|
use lemmy_utils::location_info;
|
||||||
|
use lemmy_db_queries::Followable;
|
||||||
|
use lemmy_apub::fetcher::person::get_or_fetch_and_upsert_person;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct Follow {
|
||||||
|
// todo: implement newtypes PersonUrl, GroupUrl etc (with deref function)
|
||||||
|
actor: Url,
|
||||||
|
to: Url,
|
||||||
|
object: Url,
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
kind: FollowType,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait(?Send)]
|
||||||
|
impl ReceiveActivity for Follow {
|
||||||
|
type Kind = FollowType;
|
||||||
|
async fn receive(&self,activity: Activity<Self::Kind>, context: &LemmyContext, request_counter: &mut i32) -> Result<(), LemmyError> {
|
||||||
|
println!("receive follow");
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct Accept {
|
||||||
|
// todo: implement newtypes PersonUrl, GroupUrl etc (with deref function)
|
||||||
|
actor: Url,
|
||||||
|
to: Url,
|
||||||
|
object: Activity<Follow>,
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
kind: AcceptType,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handle accepted follows
|
||||||
|
#[async_trait::async_trait(?Send)]
|
||||||
|
impl ReceiveActivity for Accept {
|
||||||
|
type Kind = AcceptType;
|
||||||
|
async fn receive(&self, activity: Activity<Self::Kind>, context: &LemmyContext, request_counter: &mut i32) -> Result<(), LemmyError> {
|
||||||
|
verify_domains_match(&self.actor, &activity.id_unchecked())?;
|
||||||
|
verify_domains_match(&self.object.inner.actor, &self.object.id_unchecked())?;
|
||||||
|
|
||||||
|
let community =
|
||||||
|
get_or_fetch_and_upsert_community(&self.actor, context, request_counter).await?;
|
||||||
|
let person = get_or_fetch_and_upsert_person(&self.to, context, request_counter).await?;
|
||||||
|
// This will throw an error if no follow was requested
|
||||||
|
blocking(&context.pool(), move |conn| {
|
||||||
|
CommunityFollower::follow_accepted(conn, community.id, person.id)
|
||||||
|
})
|
||||||
|
.await??;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
1
crates/apub_receive/src/activities_new/mod.rs
Normal file
1
crates/apub_receive/src/activities_new/mod.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pub mod follow;
|
|
@ -5,7 +5,6 @@ use crate::{
|
||||||
get_activity_id,
|
get_activity_id,
|
||||||
inbox_verify_http_signature,
|
inbox_verify_http_signature,
|
||||||
is_activity_already_known,
|
is_activity_already_known,
|
||||||
new_inbox_routing::receive_activity,
|
|
||||||
receive_for_community::{
|
receive_for_community::{
|
||||||
receive_add_for_community,
|
receive_add_for_community,
|
||||||
receive_block_user_for_community,
|
receive_block_user_for_community,
|
||||||
|
@ -137,7 +136,6 @@ pub(crate) async fn community_receive_message(
|
||||||
let any_base = activity.clone().into_any_base()?;
|
let any_base = activity.clone().into_any_base()?;
|
||||||
let actor_url = actor.actor_id();
|
let actor_url = actor.actor_id();
|
||||||
let activity_kind = activity.kind().context(location_info!())?;
|
let activity_kind = activity.kind().context(location_info!())?;
|
||||||
receive_activity(any_base.clone(), context)?;
|
|
||||||
let do_announce = match activity_kind {
|
let do_announce = match activity_kind {
|
||||||
CommunityValidTypes::Follow => {
|
CommunityValidTypes::Follow => {
|
||||||
Box::pin(handle_follow(
|
Box::pin(handle_follow(
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
use activitystreams::base::AnyBase;
|
use activitystreams::base::{AnyBase};
|
||||||
use anyhow::Context;
|
use lemmy_utils::{LemmyError};
|
||||||
use lemmy_utils::{location_info, LemmyError};
|
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use std::{collections::HashMap, str::FromStr};
|
use activitystreams::unparsed::Unparsed;
|
||||||
use strum_macros::EnumString;
|
use activitystreams::primitives::{OneOrMany};
|
||||||
|
use url::Url;
|
||||||
|
use crate::activities_new::follow::Accept;
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
use activitystreams::error::DomainError;
|
||||||
|
|
||||||
// for now, limit it to activity routing only, no http sigs, parsing or any of that
|
// for now, limit it to activity routing only, no http sigs, parsing or any of that
|
||||||
// need to route in this order:
|
// need to route in this order:
|
||||||
|
@ -12,35 +15,15 @@ use strum_macros::EnumString;
|
||||||
// 3. inner object (recursively until object is empty or an url)
|
// 3. inner object (recursively until object is empty or an url)
|
||||||
|
|
||||||
// library part
|
// library part
|
||||||
|
// todo: move this to separate crate
|
||||||
|
|
||||||
/// macro shorthand to create hashmap
|
// TODO: turn this into a trait in which app has to implement the following functions:
|
||||||
/// usage: `let counts = hashmap!['A' => 0, 'C' => 0, 'G' => 0, 'T' => 0];`
|
// .checkIdValid() - for unique, instance block etc
|
||||||
/// from https://stackoverflow.com/questions/28392008/more-concise-hashmap-initialization
|
// .checkHttpSig::<RequestType>()
|
||||||
macro_rules! hashmap {
|
// .fetchObject() - for custom http client
|
||||||
($( $key: expr => $val: expr ),*) => {{
|
// .checkActivity() - for common validity checks
|
||||||
let mut map = ::std::collections::HashMap::new();
|
|
||||||
$( map.insert($key, $val); )*
|
|
||||||
map
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Hash, Eq, PartialEq, EnumString)]
|
|
||||||
enum ActivityTypes {
|
|
||||||
Follow,
|
|
||||||
Announce,
|
|
||||||
Create,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Eq, PartialEq)]
|
|
||||||
enum ObjectTypes {
|
|
||||||
Page,
|
|
||||||
Note,
|
|
||||||
Url, // we dont dereference urls in object field, so we dont know what exactly it refers to
|
|
||||||
None, // object field doesnt exist
|
|
||||||
}
|
|
||||||
|
|
||||||
struct InboxConfig {
|
struct InboxConfig {
|
||||||
actors: Vec<ActorConfig>,
|
//actors: Vec<ActorConfig>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InboxConfig {
|
impl InboxConfig {
|
||||||
|
@ -49,52 +32,71 @@ impl InboxConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type AcceptedTypes = HashMap<ActivityTypes, InnerType>;
|
pub fn verify_domains_match(a: &Url, b: &Url) -> Result<(), LemmyError> {
|
||||||
|
if a.domain() != b.domain() {
|
||||||
// TODO: need to provide a handler function for each value
|
return Err(DomainError.into());
|
||||||
enum InnerType {
|
}
|
||||||
Simple(ObjectTypes),
|
Ok(())
|
||||||
Nested(AcceptedTypes),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ActorConfig {
|
// todo: later add a similar trait SendActivity
|
||||||
accepted_types: AcceptedTypes,
|
// todo: maybe add a separate method verify()
|
||||||
|
#[async_trait::async_trait(?Send)]
|
||||||
|
pub trait ReceiveActivity {
|
||||||
|
type Kind;
|
||||||
|
// todo: would be nice if we didnt have to pass Activity and Self separately
|
||||||
|
// todo: later handle request_counter completely inside library
|
||||||
|
async fn receive(&self, activity: Activity<Self::Kind>, context: &LemmyContext, request_counter: &mut i32) -> Result<(), LemmyError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ActorConfig {
|
// todo: instead of phantomdata, might use option<kind> to cache the fetched object (or just fetch on construction)
|
||||||
pub(crate) fn actor_inbox_handler(
|
pub struct ObjectId<'a, Kind>(Url, &'a PhantomData<Kind>);
|
||||||
self,
|
|
||||||
activity: AnyBase,
|
impl<Kind> ObjectId<'_, Kind> {
|
||||||
_context: &LemmyContext,
|
pub fn url(self) -> Url {self.0}
|
||||||
) -> Result<(), LemmyError> {
|
pub fn dereference(self) -> Result<Kind, LemmyError> {
|
||||||
// TODO: probably better to define our own struct with the fields we need + unparsed, and later
|
// todo: fetch object from http or database
|
||||||
// convert to activity (needs id_unchecked(), kind, object)
|
|
||||||
let kind = ActivityTypes::from_str(activity.kind_str().context(location_info!())?)?;
|
|
||||||
use InnerType::*;
|
|
||||||
match self.accepted_types.get(&kind).context(location_info!())? {
|
|
||||||
Simple(o) => {}
|
|
||||||
Nested(a) => {}
|
|
||||||
}
|
|
||||||
// TODO: correctly route the activity to handle_follow, receive_create_comment or receive_create_post
|
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct Activity<Kind> {
|
||||||
|
#[serde(rename = "@context")]
|
||||||
|
context: OneOrMany<AnyBase>,
|
||||||
|
id: Url,
|
||||||
|
|
||||||
|
/// type-specific fields
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub inner: Kind,
|
||||||
|
|
||||||
|
// unparsed fields
|
||||||
|
// todo: can probably remove this field
|
||||||
|
#[serde(flatten)]
|
||||||
|
unparsed: Unparsed,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Kind> Activity<Kind> {
|
||||||
|
pub fn id_unchecked(&self) -> &Url {
|
||||||
|
&self.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// application part
|
// application part
|
||||||
|
|
||||||
pub(crate) fn receive_activity(
|
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||||
activity: AnyBase,
|
pub enum PersonAcceptedActivitiesNew {
|
||||||
context: &LemmyContext,
|
Accept(Accept),
|
||||||
) -> Result<(), LemmyError> {
|
}
|
||||||
use ActivityTypes::*;
|
|
||||||
use InnerType::*;
|
// todo: there should be a better way to do this (maybe needs a derive macro)
|
||||||
use ObjectTypes::*;
|
#[async_trait::async_trait(?Send)]
|
||||||
|
impl ReceiveActivity<Kind> for PersonAcceptedActivitiesNew {
|
||||||
let accepted_types = hashmap![Follow => Simple(Url),
|
async fn receive(&self, activity: Activity<Kind>, context: &LemmyContext, request_counter: &mut i32) -> Result<(), LemmyError> {
|
||||||
Announce =>
|
use PersonAcceptedActivitiesNew::*;
|
||||||
Nested(hashmap![Create => Simple(Note), Create => Simple(Page)])];
|
match self {
|
||||||
let community_inbox_config = ActorConfig { accepted_types };
|
Accept(a) => a.receive(activity, context, request_counter)
|
||||||
let inbox_config = InboxConfig { actors: vec![] };
|
}.await
|
||||||
community_inbox_config.actor_inbox_handler(activity, context)?;
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
|
@ -66,6 +66,7 @@ use serde::{Deserialize, Serialize};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use strum_macros::EnumString;
|
use strum_macros::EnumString;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
use crate::inbox::new_inbox_routing::{PersonAcceptedActivitiesNew, ReceiveActivity, Activity};
|
||||||
|
|
||||||
/// Allowed activities for person inbox.
|
/// Allowed activities for person inbox.
|
||||||
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
|
||||||
|
@ -85,11 +86,15 @@ pub type PersonAcceptedActivities = ActorAndObject<PersonValidTypes>;
|
||||||
/// Handler for all incoming activities to person inboxes.
|
/// Handler for all incoming activities to person inboxes.
|
||||||
pub async fn person_inbox(
|
pub async fn person_inbox(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
input: web::Json<PersonAcceptedActivities>,
|
input: web::Json<Activity<PersonAcceptedActivitiesNew>>,
|
||||||
path: web::Path<String>,
|
path: web::Path<String>,
|
||||||
context: web::Data<LemmyContext>,
|
context: web::Data<LemmyContext>,
|
||||||
) -> Result<HttpResponse, LemmyError> {
|
) -> Result<HttpResponse, LemmyError> {
|
||||||
let activity = input.into_inner();
|
let activity = input.into_inner();
|
||||||
|
let request_counter = &mut 0;
|
||||||
|
activity.inner.receive(&context, request_counter);
|
||||||
|
todo!()
|
||||||
|
/*
|
||||||
// First of all check the http signature
|
// First of all check the http signature
|
||||||
let request_counter = &mut 0;
|
let request_counter = &mut 0;
|
||||||
let actor = inbox_verify_http_signature(&activity, &context, request, request_counter).await?;
|
let actor = inbox_verify_http_signature(&activity, &context, request, request_counter).await?;
|
||||||
|
@ -123,6 +128,7 @@ pub async fn person_inbox(
|
||||||
request_counter,
|
request_counter,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Receives Accept/Follow, Announce, private messages and community (undo) remove, (undo) delete
|
/// Receives Accept/Follow, Announce, private messages and community (undo) remove, (undo) delete
|
||||||
|
@ -149,14 +155,6 @@ pub(crate) async fn person_receive_message(
|
||||||
let actor_url = actor.actor_id();
|
let actor_url = actor.actor_id();
|
||||||
match kind {
|
match kind {
|
||||||
PersonValidTypes::Accept => {
|
PersonValidTypes::Accept => {
|
||||||
receive_accept(
|
|
||||||
&context,
|
|
||||||
any_base,
|
|
||||||
actor,
|
|
||||||
to_person.expect("person provided"),
|
|
||||||
request_counter,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
}
|
}
|
||||||
PersonValidTypes::Announce => {
|
PersonValidTypes::Announce => {
|
||||||
Box::pin(receive_announce(&context, any_base, actor, request_counter)).await?
|
Box::pin(receive_announce(&context, any_base, actor, request_counter)).await?
|
||||||
|
@ -234,41 +232,6 @@ async fn is_for_person_inbox(
|
||||||
Err(anyhow!("Not addressed for any local person").into())
|
Err(anyhow!("Not addressed for any local person").into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle accepted follows.
|
|
||||||
async fn receive_accept(
|
|
||||||
context: &LemmyContext,
|
|
||||||
activity: AnyBase,
|
|
||||||
actor: &dyn ActorType,
|
|
||||||
person: Person,
|
|
||||||
request_counter: &mut i32,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
let accept = Accept::from_any_base(activity)?.context(location_info!())?;
|
|
||||||
verify_activity_domains_valid(&accept, &actor.actor_id(), false)?;
|
|
||||||
|
|
||||||
let object = accept.object().to_owned().one().context(location_info!())?;
|
|
||||||
let follow = Follow::from_any_base(object)?.context(location_info!())?;
|
|
||||||
verify_activity_domains_valid(&follow, &person.actor_id(), false)?;
|
|
||||||
|
|
||||||
let community_uri = accept
|
|
||||||
.actor()?
|
|
||||||
.to_owned()
|
|
||||||
.single_xsd_any_uri()
|
|
||||||
.context(location_info!())?;
|
|
||||||
|
|
||||||
let community =
|
|
||||||
get_or_fetch_and_upsert_community(&community_uri, context, request_counter).await?;
|
|
||||||
|
|
||||||
let community_id = community.id;
|
|
||||||
let person_id = person.id;
|
|
||||||
// This will throw an error if no follow was requested
|
|
||||||
blocking(&context.pool(), move |conn| {
|
|
||||||
CommunityFollower::follow_accepted(conn, community_id, person_id)
|
|
||||||
})
|
|
||||||
.await??;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(EnumString)]
|
#[derive(EnumString)]
|
||||||
enum AnnouncableActivities {
|
enum AnnouncableActivities {
|
||||||
Create,
|
Create,
|
||||||
|
|
|
@ -2,3 +2,4 @@ mod activities;
|
||||||
mod http;
|
mod http;
|
||||||
mod inbox;
|
mod inbox;
|
||||||
pub mod routes;
|
pub mod routes;
|
||||||
|
pub mod activities_new;
|
||||||
|
|
Loading…
Reference in a new issue