chore: merge branch 'dev' of alemi/upub into dev
This commit is contained in:
commit
0097a0533a
7 changed files with 78 additions and 66 deletions
|
@ -99,6 +99,7 @@ impl<T : super::Base> Node<T> {
|
|||
}
|
||||
|
||||
/// returns id of object: url for link, id for object, None if empty or array
|
||||
// TODO return Option<&str> and avoid inner clone
|
||||
pub fn id(&self) -> Option<String> {
|
||||
match self {
|
||||
Node::Empty => None,
|
||||
|
|
|
@ -87,6 +87,7 @@ impl axum::response::IntoResponse for UpubError {
|
|||
// TODO it's kind of jank to hide this print down here, i should probably learn how spans work
|
||||
// in tracing and use the library's features but ehhhh
|
||||
tracing::debug!("emitting error response: {self:?}");
|
||||
let descr = self.to_string();
|
||||
match self {
|
||||
UpubError::Redirect(to) => Redirect::to(&to).into_response(),
|
||||
UpubError::Status(status) => status.into_response(),
|
||||
|
@ -94,7 +95,7 @@ impl axum::response::IntoResponse for UpubError {
|
|||
StatusCode::SERVICE_UNAVAILABLE,
|
||||
axum::Json(serde_json::json!({
|
||||
"error": "database",
|
||||
"description": format!("{e:#?}"),
|
||||
"inner": format!("{e:#?}"),
|
||||
}))
|
||||
).into_response(),
|
||||
UpubError::Reqwest(x) | UpubError::FetchError(x, _) => (
|
||||
|
@ -103,7 +104,8 @@ impl axum::response::IntoResponse for UpubError {
|
|||
"error": "request",
|
||||
"status": x.status().map(|s| s.to_string()).unwrap_or_default(),
|
||||
"url": x.url().map(|x| x.to_string()).unwrap_or_default(),
|
||||
"description": format!("{x:#?}"),
|
||||
"description": descr,
|
||||
"inner": format!("{x:#?}"),
|
||||
}))
|
||||
).into_response(),
|
||||
UpubError::Field(x) => (
|
||||
|
@ -111,7 +113,7 @@ impl axum::response::IntoResponse for UpubError {
|
|||
axum::Json(serde_json::json!({
|
||||
"error": "field",
|
||||
"field": x.0.to_string(),
|
||||
"description": format!("missing required field from request: '{}'", x.0),
|
||||
"description": descr,
|
||||
}))
|
||||
).into_response(),
|
||||
UpubError::Mismatch(expected, found) => (
|
||||
|
@ -120,14 +122,15 @@ impl axum::response::IntoResponse for UpubError {
|
|||
"error": "type",
|
||||
"expected": expected.as_ref().to_string(),
|
||||
"found": found.as_ref().to_string(),
|
||||
"description": self.to_string(),
|
||||
"description": descr,
|
||||
}))
|
||||
).into_response(),
|
||||
_ => (
|
||||
x => (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
axum::Json(serde_json::json!({
|
||||
"error": "unknown",
|
||||
"description": self.to_string(),
|
||||
"description": descr,
|
||||
"inner": format!("{x:#?}"),
|
||||
}))
|
||||
).into_response(),
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ use super::{fetcher::Fetcher, httpsign::HttpSignature};
|
|||
pub enum Identity {
|
||||
Anonymous,
|
||||
Remote {
|
||||
user: String,
|
||||
domain: String,
|
||||
internal: i64,
|
||||
},
|
||||
|
@ -114,6 +115,7 @@ where
|
|||
|
||||
// TODO assert payload's digest is equal to signature's
|
||||
let user_id = http_signature.key_id
|
||||
.replace("/main-key", "") // gotosocial whyyyyy
|
||||
.split('#')
|
||||
.next().ok_or(UpubError::bad_request())?
|
||||
.to_string();
|
||||
|
@ -124,11 +126,11 @@ where
|
|||
.verify(&user.public_key)
|
||||
{
|
||||
Ok(true) => {
|
||||
// TODO can we avoid this extra db rountrip made on each server fetch?
|
||||
let user = user.id;
|
||||
let domain = Context::server(&user_id);
|
||||
// TODO this will fail because we never fetch and insert into instance oops
|
||||
let internal = model::instance::Entity::domain_to_internal(&domain, ctx.db()).await?;
|
||||
identity = Identity::Remote { domain, internal };
|
||||
identity = Identity::Remote { user, domain, internal };
|
||||
},
|
||||
Ok(false) => tracing::warn!("invalid signature: {http_signature:?}"),
|
||||
Err(e) => tracing::error!("error verifying signature: {e}"),
|
||||
|
|
|
@ -131,7 +131,7 @@ impl Fetcher for Context {
|
|||
|
||||
let document = Self::request(
|
||||
Method::GET, id, None,
|
||||
&format!("https://{}", self.domain()), self.pkey(), self.domain(),
|
||||
&format!("https://{}/", self.domain()), self.pkey(), self.domain(),
|
||||
)
|
||||
.await?
|
||||
.json::<serde_json::Value>()
|
||||
|
@ -207,7 +207,8 @@ impl Fetcher for Context {
|
|||
};
|
||||
|
||||
if let Ok(res) = Self::request(
|
||||
Method::GET, &format!("https://{domain}"), None, &format!("https://{}", self.domain()), self.pkey(), self.domain(),
|
||||
Method::GET, &format!("https://{domain}"), None,
|
||||
&format!("https://{}/", self.domain()), self.pkey(), self.domain(),
|
||||
).await {
|
||||
if let Ok(actor) = res.json::<serde_json::Value>().await {
|
||||
if let Some(name) = actor.name() {
|
||||
|
@ -243,7 +244,7 @@ impl Fetcher for Context {
|
|||
if let Some(followers_url) = &document.followers().id() {
|
||||
let req = Self::request(
|
||||
Method::GET, followers_url, None,
|
||||
&format!("https://{}", self.domain()), self.pkey(), self.domain(),
|
||||
&format!("https://{}/", self.domain()), self.pkey(), self.domain(),
|
||||
).await;
|
||||
if let Ok(res) = req {
|
||||
if let Ok(user_followers) = res.json::<serde_json::Value>().await {
|
||||
|
@ -255,9 +256,9 @@ impl Fetcher for Context {
|
|||
}
|
||||
|
||||
if let Some(following_url) = &document.following().id() {
|
||||
let req = Self::request(
|
||||
let req = Self::request(
|
||||
Method::GET, following_url, None,
|
||||
&format!("https://{}", self.domain()), self.pkey(), self.domain(),
|
||||
&format!("https://{}/", self.domain()), self.pkey(), self.domain(),
|
||||
).await;
|
||||
if let Ok(res) = req {
|
||||
if let Ok(user_following) = res.json::<serde_json::Value>().await {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use apb::{server::Inbox, Activity, ActivityType};
|
||||
use apb::{server::Inbox, Activity, ActivityType, Base};
|
||||
use axum::{extract::{Query, State}, http::StatusCode, Json};
|
||||
use sea_orm::{sea_query::IntoCondition, ColumnTrait};
|
||||
use upub::{server::auth::{AuthIdentity, Identity}, Context};
|
||||
|
@ -43,7 +43,7 @@ pub async fn post(
|
|||
AuthIdentity(auth): AuthIdentity,
|
||||
Json(activity): Json<serde_json::Value>
|
||||
) -> upub::Result<()> {
|
||||
let Identity::Remote { domain: server, .. } = auth else {
|
||||
let Identity::Remote { domain: server, user: uid, .. } = auth else {
|
||||
if activity.activity_type() == Some(ActivityType::Delete) {
|
||||
// this is spammy af, ignore them!
|
||||
// we basically received a delete for a user we can't fetch and verify, meaning remote
|
||||
|
@ -61,15 +61,14 @@ pub async fn post(
|
|||
}
|
||||
};
|
||||
|
||||
let Some(actor) = activity.actor().id() else {
|
||||
return Err(upub::Error::bad_request());
|
||||
};
|
||||
let aid = activity.id().ok_or_else(|| upub::Error::field("id"))?.to_string();
|
||||
let actor = activity.actor().id().ok_or_else(|| upub::Error::field("actor"))?;
|
||||
|
||||
if server != Context::server(&actor) {
|
||||
if uid != actor {
|
||||
return Err(upub::Error::unauthorized());
|
||||
}
|
||||
|
||||
tracing::debug!("processing federated activity: '{}'", serde_json::to_string(&activity).unwrap_or_default());
|
||||
tracing::debug!("processing federated activity: '{:#}'", activity);
|
||||
|
||||
// TODO we could process Links and bare Objects maybe, but probably out of AP spec?
|
||||
match activity.activity_type().ok_or_else(upub::Error::bad_request)? {
|
||||
|
|
|
@ -45,51 +45,39 @@ pub fn Item(
|
|||
let config = use_context::<Signal<crate::Config>>().expect("missing config context");
|
||||
let id = item.id().unwrap_or_default().to_string();
|
||||
let sep = if sep { Some(view! { <hr /> }) } else { None };
|
||||
match item.object_type() {
|
||||
if !replies && !config.get().filters.visible(&item) {
|
||||
return None;
|
||||
}
|
||||
match item.object_type().unwrap_or(apb::ObjectType::Object) {
|
||||
// special case for placeholder activities
|
||||
Some(apb::ObjectType::Note) | Some(apb::ObjectType::Document(_)) => (move || {
|
||||
if !config.get().filters.replies && item.in_reply_to().id().is_some() {
|
||||
None
|
||||
} else if config.get().filters.orphans {
|
||||
Some(view! { <Object object=item.clone() />{sep.clone()} })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}).into_view(),
|
||||
apb::ObjectType::Note | apb::ObjectType::Document(_) =>
|
||||
Some(view! { <Object object=item.clone() />{sep.clone()} }.into_view()),
|
||||
// everything else
|
||||
Some(apb::ObjectType::Activity(t)) => (move || {
|
||||
if config.get().filters.visible(apb::ObjectType::Activity(t)) {
|
||||
let object_id = item.object().id().unwrap_or_default();
|
||||
if !replies && !config.get().filters.replies && CACHE.get(&object_id).map(|x| x.in_reply_to().id().is_some()).unwrap_or(false) {
|
||||
None
|
||||
} else {
|
||||
let object = match t {
|
||||
apb::ActivityType::Create | apb::ActivityType::Announce =>
|
||||
CACHE.get(&object_id).map(|obj| {
|
||||
view! { <Object object=obj /> }
|
||||
}.into_view()),
|
||||
apb::ActivityType::Follow =>
|
||||
CACHE.get(&object_id).map(|obj| {
|
||||
view! {
|
||||
<div class="ml-1">
|
||||
<ActorBanner object=obj />
|
||||
<FollowRequestButtons activity_id=id.clone() actor_id=object_id />
|
||||
</div>
|
||||
}
|
||||
}.into_view()),
|
||||
_ => None,
|
||||
};
|
||||
Some(view! {
|
||||
<ActivityLine activity=item.clone() />
|
||||
{object}
|
||||
{sep.clone()}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}).into_view(),
|
||||
apb::ObjectType::Activity(t) => {
|
||||
let object_id = item.object().id().unwrap_or_default();
|
||||
let object = match t {
|
||||
apb::ActivityType::Create | apb::ActivityType::Announce =>
|
||||
CACHE.get(&object_id).map(|obj| {
|
||||
view! { <Object object=obj /> }
|
||||
}.into_view()),
|
||||
apb::ActivityType::Follow =>
|
||||
CACHE.get(&object_id).map(|obj| {
|
||||
view! {
|
||||
<div class="ml-1">
|
||||
<ActorBanner object=obj />
|
||||
<FollowRequestButtons activity_id=id.clone() actor_id=object_id />
|
||||
</div>
|
||||
}
|
||||
}.into_view()),
|
||||
_ => None,
|
||||
};
|
||||
Some(view! {
|
||||
<ActivityLine activity=item.clone() />
|
||||
{object}
|
||||
{sep.clone()}
|
||||
}.into_view())
|
||||
},
|
||||
// should never happen
|
||||
_ => view! { <p><code>type not implemented</code></p> }.into_view(),
|
||||
t => Some(view! { <p><code>type not implemented : {t.as_ref().to_string()}</code></p> }.into_view()),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,21 +36,39 @@ pub struct FiltersConfig {
|
|||
#[serde_inline_default(true)]
|
||||
pub follows: bool,
|
||||
|
||||
#[serde_inline_default(false)]
|
||||
pub updates: bool,
|
||||
|
||||
#[serde_inline_default(true)]
|
||||
pub orphans: bool,
|
||||
}
|
||||
|
||||
impl FiltersConfig {
|
||||
pub fn visible(&self, object_type: apb::ObjectType) -> bool {
|
||||
match object_type {
|
||||
pub fn visible(&self, item: &crate::Object) -> bool {
|
||||
use apb::{Object, Activity};
|
||||
|
||||
let type_filter = match item.object_type().unwrap_or(apb::ObjectType::Object) {
|
||||
apb::ObjectType::Note | apb::ObjectType::Document(_) => self.orphans,
|
||||
apb::ObjectType::Activity(apb::ActivityType::Like | apb::ActivityType::EmojiReact) => self.likes,
|
||||
apb::ObjectType::Activity(apb::ActivityType::Create) => self.creates,
|
||||
apb::ObjectType::Activity(apb::ActivityType::Announce) => self.announces,
|
||||
apb::ObjectType::Activity(apb::ActivityType::Update) => self.updates,
|
||||
apb::ObjectType::Activity(
|
||||
apb::ActivityType::Follow | apb::ActivityType::Accept(_) | apb::ActivityType::Reject(_)
|
||||
) => self.follows,
|
||||
_ => true,
|
||||
}
|
||||
};
|
||||
let mut reply_filter = true;
|
||||
|
||||
if
|
||||
item.in_reply_to().id().is_some() ||
|
||||
item.object().get().map(|x|
|
||||
x.in_reply_to().id().is_some()
|
||||
).unwrap_or(false)
|
||||
{
|
||||
reply_filter = self.replies;
|
||||
};
|
||||
|
||||
type_filter && reply_filter
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue