feat: local users can request to fetch remote stuff
This commit is contained in:
parent
b2e6703b0e
commit
3a79ca05a2
5 changed files with 52 additions and 31 deletions
|
@ -1,9 +1,9 @@
|
||||||
use axum::{extract::{Path, State}, http::StatusCode};
|
use axum::extract::{Path, Query, State};
|
||||||
use sea_orm::{ColumnTrait, QueryFilter};
|
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 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?
|
// TODO this is used outside /routes, maybe move in model?
|
||||||
pub fn ap_activity(activity: model::activity::Model) -> serde_json::Value {
|
pub fn ap_activity(activity: model::activity::Model) -> serde_json::Value {
|
||||||
|
@ -24,24 +24,25 @@ pub async fn view(
|
||||||
State(ctx): State<Context>,
|
State(ctx): State<Context>,
|
||||||
Path(id): Path<String>,
|
Path(id): Path<String>,
|
||||||
AuthIdentity(auth): AuthIdentity,
|
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('+') {
|
let aid = if id.starts_with('+') {
|
||||||
format!("https://{}", id.replacen('+', "", 1).replace('@', "/"))
|
format!("https://{}", id.replacen('+', "", 1).replace('@', "/"))
|
||||||
} else {
|
} else {
|
||||||
ctx.aid(id.clone())
|
ctx.aid(id.clone())
|
||||||
};
|
};
|
||||||
match model::addressing::Entity::find_activities()
|
match model::addressing::Entity::find_activities()
|
||||||
.filter(model::activity::Column::Id.eq(aid))
|
.filter(model::activity::Column::Id.eq(&aid))
|
||||||
.filter(auth.filter_condition())
|
.filter(auth.filter_condition())
|
||||||
.into_model::<EmbeddedActivity>()
|
.into_model::<EmbeddedActivity>()
|
||||||
.one(ctx.db())
|
.one(ctx.db())
|
||||||
.await
|
.await?
|
||||||
{
|
{
|
||||||
Ok(Some(activity)) => Ok(JsonLD(serde_json::Value::from(activity).ld_context())),
|
Some(activity) => Ok(JsonLD(serde_json::Value::from(activity).ld_context())),
|
||||||
Ok(None) => Err(StatusCode::NOT_FOUND),
|
None => if auth.is_local() && query.fetch {
|
||||||
Err(e) => {
|
Ok(JsonLD(ap_activity(ctx.fetch().activity(&aid).await?).ld_context()))
|
||||||
tracing::error!("error querying for activity: {e}");
|
} else {
|
||||||
Err(StatusCode::INTERNAL_SERVER_ERROR)
|
Err(UpubError::not_found())
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)]
|
#[derive(Debug, serde::Deserialize)]
|
||||||
// TODO i don't really like how pleroma/mastodon do it actually, maybe change this?
|
// TODO i don't really like how pleroma/mastodon do it actually, maybe change this?
|
||||||
pub struct Pagination {
|
pub struct Pagination {
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
use axum::{extract::{Path, State}, http::StatusCode};
|
use axum::extract::{Path, Query, State};
|
||||||
use sea_orm::{ColumnTrait, QueryFilter};
|
use sea_orm::{ColumnTrait, QueryFilter};
|
||||||
|
|
||||||
use apb::{ObjectMut, BaseMut, Node};
|
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?
|
// TODO this is used outside /routes, maybe move in model?
|
||||||
pub fn ap_object(object: model::object::Model) -> serde_json::Value {
|
pub fn ap_object(object: model::object::Model) -> serde_json::Value {
|
||||||
|
@ -28,25 +28,26 @@ pub async fn view(
|
||||||
State(ctx): State<Context>,
|
State(ctx): State<Context>,
|
||||||
Path(id): Path<String>,
|
Path(id): Path<String>,
|
||||||
AuthIdentity(auth): AuthIdentity,
|
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('+') {
|
let oid = if id.starts_with('+') {
|
||||||
format!("https://{}", id.replacen('+', "", 1).replace('@', "/"))
|
format!("https://{}", id.replacen('+', "", 1).replace('@', "/"))
|
||||||
} else {
|
} else {
|
||||||
ctx.oid(id.clone())
|
ctx.oid(id.clone())
|
||||||
};
|
};
|
||||||
match model::addressing::Entity::find_activities()
|
match model::addressing::Entity::find_activities()
|
||||||
.filter(model::object::Column::Id.eq(oid))
|
.filter(model::object::Column::Id.eq(&oid))
|
||||||
.filter(auth.filter_condition())
|
.filter(auth.filter_condition())
|
||||||
.into_model::<EmbeddedActivity>()
|
.into_model::<EmbeddedActivity>()
|
||||||
.one(ctx.db())
|
.one(ctx.db())
|
||||||
.await
|
.await?
|
||||||
{
|
{
|
||||||
Ok(Some(EmbeddedActivity { activity: _, object: Some(object) })) => Ok(JsonLD(ap_object(object).ld_context())),
|
Some(EmbeddedActivity { activity: _, object: Some(object) }) => Ok(JsonLD(ap_object(object).ld_context())),
|
||||||
Ok(Some(EmbeddedActivity { activity: _, object: None })) => Err(StatusCode::NOT_FOUND),
|
Some(EmbeddedActivity { activity: _, object: None }) => Err(UpubError::not_found()),
|
||||||
Ok(None) => Err(StatusCode::NOT_FOUND),
|
None => if auth.is_local() && query.fetch {
|
||||||
Err(e) => {
|
Ok(JsonLD(ap_object(ctx.fetch().object(&oid).await?).ld_context()))
|
||||||
tracing::error!("error querying for object: {e}");
|
} else {
|
||||||
Err(StatusCode::INTERNAL_SERVER_ERROR)
|
Err(UpubError::not_found())
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,13 +4,13 @@ pub mod outbox;
|
||||||
|
|
||||||
pub mod following;
|
pub mod following;
|
||||||
|
|
||||||
use axum::extract::{Path, State};
|
use axum::extract::{Path, Query, State};
|
||||||
use sea_orm::EntityTrait;
|
use sea_orm::EntityTrait;
|
||||||
|
|
||||||
use apb::{ActorMut, BaseMut, CollectionMut, DocumentMut, DocumentType, Node, ObjectMut, PublicKeyMut};
|
use apb::{ActorMut, BaseMut, CollectionMut, DocumentMut, DocumentType, Node, ObjectMut, PublicKeyMut};
|
||||||
use crate::{errors::UpubError, model::{self, user}, server::{auth::AuthIdentity, Context}, url};
|
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 {
|
pub fn ap_user(user: model::user::Model) -> serde_json::Value {
|
||||||
serde_json::Value::new_object()
|
serde_json::Value::new_object()
|
||||||
|
@ -48,13 +48,14 @@ pub async fn view(
|
||||||
State(ctx) : State<Context>,
|
State(ctx) : State<Context>,
|
||||||
AuthIdentity(auth): AuthIdentity,
|
AuthIdentity(auth): AuthIdentity,
|
||||||
Path(id): Path<String>,
|
Path(id): Path<String>,
|
||||||
|
Query(query): Query<TryFetch>,
|
||||||
) -> crate::Result<JsonLD<serde_json::Value>> {
|
) -> crate::Result<JsonLD<serde_json::Value>> {
|
||||||
let uid = if id.starts_with('+') {
|
let uid = if id.starts_with('+') {
|
||||||
format!("https://{}", id.replacen('+', "", 1).replace('@', "/"))
|
format!("https://{}", id.replacen('+', "", 1).replace('@', "/"))
|
||||||
} else {
|
} else {
|
||||||
ctx.uid(id.clone())
|
ctx.uid(id.clone())
|
||||||
};
|
};
|
||||||
match user::Entity::find_by_id(uid)
|
match user::Entity::find_by_id(&uid)
|
||||||
.find_also_related(model::config::Entity)
|
.find_also_related(model::config::Entity)
|
||||||
.one(ctx.db()).await?
|
.one(ctx.db()).await?
|
||||||
{
|
{
|
||||||
|
@ -75,7 +76,7 @@ pub async fn view(
|
||||||
.set_collection_type(Some(apb::CollectionType::OrderedCollection))
|
.set_collection_type(Some(apb::CollectionType::OrderedCollection))
|
||||||
.set_first(Node::link(url!(ctx, "/users/{id}/following/page")))
|
.set_first(Node::link(url!(ctx, "/users/{id}/following/page")))
|
||||||
.set_total_items(
|
.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)
|
Some(user.following_count as u64)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -88,7 +89,7 @@ pub async fn view(
|
||||||
.set_collection_type(Some(apb::CollectionType::OrderedCollection))
|
.set_collection_type(Some(apb::CollectionType::OrderedCollection))
|
||||||
.set_first(Node::link(url!(ctx, "/users/{id}/followers/page")))
|
.set_first(Node::link(url!(ctx, "/users/{id}/followers/page")))
|
||||||
.set_total_items(
|
.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)
|
Some(user.followers_count as u64)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -101,7 +102,11 @@ pub async fn view(
|
||||||
},
|
},
|
||||||
// remote user TODDO doesn't work?
|
// remote user TODDO doesn't work?
|
||||||
Some((user, None)) => Ok(JsonLD(ap_user(user).ld_context())),
|
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())
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,14 +30,22 @@ impl Identity {
|
||||||
matches!(self, Self::Anonymous)
|
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 {
|
match self {
|
||||||
Self::Local(x) => x == uid,
|
Self::Local(x) => x == uid,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_server(&self, uid: &str) -> bool {
|
pub fn is_remote_server(&self, uid: &str) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::Remote(x) => x == uid,
|
Self::Remote(x) => x == uid,
|
||||||
_ => false,
|
_ => false,
|
||||||
|
|
Loading…
Reference in a new issue