feat: local users can request to fetch remote stuff

This commit is contained in:
əlemi 2024-04-18 04:48:49 +02:00
parent b2e6703b0e
commit 3a79ca05a2
Signed by: alemi
GPG key ID: A4895B84D311642C
5 changed files with 52 additions and 31 deletions

View file

@ -1,9 +1,9 @@
use axum::{extract::{Path, State}, http::StatusCode};
use axum::extract::{Path, Query, State};
use sea_orm::{ColumnTrait, QueryFilter};
use crate::{model::{self, addressing::EmbeddedActivity}, server::{auth::AuthIdentity, Context}};
use crate::{errors::UpubError, model::{self, addressing::EmbeddedActivity}, server::{auth::AuthIdentity, Context}};
use apb::{ActivityMut, ObjectMut, BaseMut, Node};
use super::{jsonld::LD, JsonLD};
use super::{jsonld::LD, JsonLD, TryFetch};
// TODO this is used outside /routes, maybe move in model?
pub fn ap_activity(activity: model::activity::Model) -> serde_json::Value {
@ -24,24 +24,25 @@ pub async fn view(
State(ctx): State<Context>,
Path(id): Path<String>,
AuthIdentity(auth): AuthIdentity,
) -> Result<JsonLD<serde_json::Value>, StatusCode> {
Query(query): Query<TryFetch>,
) -> crate::Result<JsonLD<serde_json::Value>> {
let aid = if id.starts_with('+') {
format!("https://{}", id.replacen('+', "", 1).replace('@', "/"))
} else {
ctx.aid(id.clone())
};
match model::addressing::Entity::find_activities()
.filter(model::activity::Column::Id.eq(aid))
.filter(model::activity::Column::Id.eq(&aid))
.filter(auth.filter_condition())
.into_model::<EmbeddedActivity>()
.one(ctx.db())
.await
.await?
{
Ok(Some(activity)) => Ok(JsonLD(serde_json::Value::from(activity).ld_context())),
Ok(None) => Err(StatusCode::NOT_FOUND),
Err(e) => {
tracing::error!("error querying for activity: {e}");
Err(StatusCode::INTERNAL_SERVER_ERROR)
Some(activity) => Ok(JsonLD(serde_json::Value::from(activity).ld_context())),
None => if auth.is_local() && query.fetch {
Ok(JsonLD(ap_activity(ctx.fetch().activity(&aid).await?).ld_context()))
} else {
Err(UpubError::not_found())
},
}
}

View file

@ -65,6 +65,12 @@ impl ActivityPubRouter for Router<crate::server::Context> {
}
}
#[derive(Debug, serde::Deserialize)]
pub struct TryFetch {
#[serde(default)]
pub fetch: bool,
}
#[derive(Debug, serde::Deserialize)]
// TODO i don't really like how pleroma/mastodon do it actually, maybe change this?
pub struct Pagination {

View file

@ -1,10 +1,10 @@
use axum::{extract::{Path, State}, http::StatusCode};
use axum::extract::{Path, Query, State};
use sea_orm::{ColumnTrait, QueryFilter};
use apb::{ObjectMut, BaseMut, Node};
use crate::{model::{self, addressing::EmbeddedActivity}, server::{auth::AuthIdentity, Context}};
use crate::{errors::UpubError, model::{self, addressing::EmbeddedActivity}, server::{auth::AuthIdentity, Context}};
use super::{jsonld::LD, JsonLD};
use super::{jsonld::LD, JsonLD, TryFetch};
// TODO this is used outside /routes, maybe move in model?
pub fn ap_object(object: model::object::Model) -> serde_json::Value {
@ -28,25 +28,26 @@ pub async fn view(
State(ctx): State<Context>,
Path(id): Path<String>,
AuthIdentity(auth): AuthIdentity,
) -> Result<JsonLD<serde_json::Value>, StatusCode> {
Query(query): Query<TryFetch>,
) -> crate::Result<JsonLD<serde_json::Value>> {
let oid = if id.starts_with('+') {
format!("https://{}", id.replacen('+', "", 1).replace('@', "/"))
} else {
ctx.oid(id.clone())
};
match model::addressing::Entity::find_activities()
.filter(model::object::Column::Id.eq(oid))
.filter(model::object::Column::Id.eq(&oid))
.filter(auth.filter_condition())
.into_model::<EmbeddedActivity>()
.one(ctx.db())
.await
.await?
{
Ok(Some(EmbeddedActivity { activity: _, object: Some(object) })) => Ok(JsonLD(ap_object(object).ld_context())),
Ok(Some(EmbeddedActivity { activity: _, object: None })) => Err(StatusCode::NOT_FOUND),
Ok(None) => Err(StatusCode::NOT_FOUND),
Err(e) => {
tracing::error!("error querying for object: {e}");
Err(StatusCode::INTERNAL_SERVER_ERROR)
Some(EmbeddedActivity { activity: _, object: Some(object) }) => Ok(JsonLD(ap_object(object).ld_context())),
Some(EmbeddedActivity { activity: _, object: None }) => Err(UpubError::not_found()),
None => if auth.is_local() && query.fetch {
Ok(JsonLD(ap_object(ctx.fetch().object(&oid).await?).ld_context()))
} else {
Err(UpubError::not_found())
},
}
}

View file

@ -4,13 +4,13 @@ pub mod outbox;
pub mod following;
use axum::extract::{Path, State};
use axum::extract::{Path, Query, State};
use sea_orm::EntityTrait;
use apb::{ActorMut, BaseMut, CollectionMut, DocumentMut, DocumentType, Node, ObjectMut, PublicKeyMut};
use crate::{errors::UpubError, model::{self, user}, server::{auth::AuthIdentity, Context}, url};
use super::{jsonld::LD, JsonLD};
use super::{jsonld::LD, JsonLD, TryFetch};
pub fn ap_user(user: model::user::Model) -> serde_json::Value {
serde_json::Value::new_object()
@ -48,13 +48,14 @@ pub async fn view(
State(ctx) : State<Context>,
AuthIdentity(auth): AuthIdentity,
Path(id): Path<String>,
Query(query): Query<TryFetch>,
) -> crate::Result<JsonLD<serde_json::Value>> {
let uid = if id.starts_with('+') {
format!("https://{}", id.replacen('+', "", 1).replace('@', "/"))
} else {
ctx.uid(id.clone())
};
match user::Entity::find_by_id(uid)
match user::Entity::find_by_id(&uid)
.find_also_related(model::config::Entity)
.one(ctx.db()).await?
{
@ -75,7 +76,7 @@ pub async fn view(
.set_collection_type(Some(apb::CollectionType::OrderedCollection))
.set_first(Node::link(url!(ctx, "/users/{id}/following/page")))
.set_total_items(
if auth.is_user(&user.id) || cfg.show_following {
if auth.is_local_user(&user.id) || cfg.show_following {
Some(user.following_count as u64)
} else {
None
@ -88,7 +89,7 @@ pub async fn view(
.set_collection_type(Some(apb::CollectionType::OrderedCollection))
.set_first(Node::link(url!(ctx, "/users/{id}/followers/page")))
.set_total_items(
if auth.is_user(&user.id) || cfg.show_followers {
if auth.is_local_user(&user.id) || cfg.show_followers {
Some(user.followers_count as u64)
} else {
None
@ -101,7 +102,11 @@ pub async fn view(
},
// remote user TODDO doesn't work?
Some((user, None)) => Ok(JsonLD(ap_user(user).ld_context())),
None => Err(UpubError::not_found()),
None => if auth.is_local() && query.fetch {
Ok(JsonLD(ap_user(ctx.fetch().user(&uid).await?).ld_context()))
} else {
Err(UpubError::not_found())
},
}
}

View file

@ -30,14 +30,22 @@ impl Identity {
matches!(self, Self::Anonymous)
}
pub fn is_user(&self, uid: &str) -> bool {
pub fn is_local(&self) -> bool {
matches!(self, Self::Local(_))
}
pub fn is_remote(&self) -> bool {
matches!(self, Self::Remote(_))
}
pub fn is_local_user(&self, uid: &str) -> bool {
match self {
Self::Local(x) => x == uid,
_ => false,
}
}
pub fn is_server(&self, uid: &str) -> bool {
pub fn is_remote_server(&self, uid: &str) -> bool {
match self {
Self::Remote(x) => x == uid,
_ => false,