feat: added notifications collections
This commit is contained in:
parent
727e977c4e
commit
bb79ca7728
6 changed files with 93 additions and 5 deletions
|
@ -169,6 +169,14 @@ impl Node<serde_json::Value> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn maybe_array(values: Vec<serde_json::Value>) -> Self {
|
||||||
|
if values.is_empty() {
|
||||||
|
Node::Empty
|
||||||
|
} else {
|
||||||
|
Node::array(values)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "fetch")]
|
#[cfg(feature = "fetch")]
|
||||||
pub async fn fetch(&mut self) -> reqwest::Result<&mut Self> {
|
pub async fn fetch(&mut self) -> reqwest::Result<&mut Self> {
|
||||||
if let Node::Link(link) = self {
|
if let Node::Link(link) = self {
|
||||||
|
|
|
@ -44,7 +44,7 @@ pub trait Actor : Object {
|
||||||
#[cfg(feature = "activitypub-fe")]
|
#[cfg(feature = "activitypub-fe")]
|
||||||
fn followed_by_me(&self) -> Field<bool> { Err(FieldErr("followedByMe")) }
|
fn followed_by_me(&self) -> Field<bool> { Err(FieldErr("followedByMe")) }
|
||||||
#[cfg(feature = "activitypub-fe")]
|
#[cfg(feature = "activitypub-fe")]
|
||||||
fn feed(&self) -> Node<Self::Collection> { Node::Empty }
|
fn notifications(&self) -> Node<Self::Collection> { Node::Empty }
|
||||||
|
|
||||||
#[cfg(feature = "activitypub-counters")]
|
#[cfg(feature = "activitypub-counters")]
|
||||||
fn followers_count(&self) -> Field<u64> { Err(FieldErr("followersCount")) }
|
fn followers_count(&self) -> Field<u64> { Err(FieldErr("followersCount")) }
|
||||||
|
@ -99,7 +99,7 @@ pub trait ActorMut : ObjectMut {
|
||||||
#[cfg(feature = "activitypub-fe")]
|
#[cfg(feature = "activitypub-fe")]
|
||||||
fn set_followed_by_me(self, val: Option<bool>) -> Self;
|
fn set_followed_by_me(self, val: Option<bool>) -> Self;
|
||||||
#[cfg(feature = "activitypub-fe")]
|
#[cfg(feature = "activitypub-fe")]
|
||||||
fn set_feed(self, val: Node<Self::Collection>) -> Self;
|
fn set_notifications(self, val: Node<Self::Collection>) -> Self;
|
||||||
|
|
||||||
#[cfg(feature = "activitypub-counters")]
|
#[cfg(feature = "activitypub-counters")]
|
||||||
fn set_followers_count(self, val: Option<u64>) -> Self;
|
fn set_followers_count(self, val: Option<u64>) -> Self;
|
||||||
|
@ -155,7 +155,7 @@ impl Actor for serde_json::Value {
|
||||||
#[cfg(feature = "activitypub-fe")]
|
#[cfg(feature = "activitypub-fe")]
|
||||||
crate::getter! { followedByMe -> bool }
|
crate::getter! { followedByMe -> bool }
|
||||||
#[cfg(feature = "activitypub-fe")]
|
#[cfg(feature = "activitypub-fe")]
|
||||||
crate::getter! { feed -> node Self::Collection }
|
crate::getter! { notifications -> node Self::Collection }
|
||||||
|
|
||||||
#[cfg(feature = "activitypub-counters")]
|
#[cfg(feature = "activitypub-counters")]
|
||||||
crate::getter! { followingCount -> u64 }
|
crate::getter! { followingCount -> u64 }
|
||||||
|
@ -206,7 +206,7 @@ impl ActorMut for serde_json::Value {
|
||||||
#[cfg(feature = "activitypub-fe")]
|
#[cfg(feature = "activitypub-fe")]
|
||||||
crate::setter! { followedByMe -> bool }
|
crate::setter! { followedByMe -> bool }
|
||||||
#[cfg(feature = "activitypub-fe")]
|
#[cfg(feature = "activitypub-fe")]
|
||||||
crate::setter! { feed -> node Self::Collection }
|
crate::setter! { notifications -> node Self::Collection }
|
||||||
|
|
||||||
#[cfg(feature = "activitypub-counters")]
|
#[cfg(feature = "activitypub-counters")]
|
||||||
crate::setter! { followingCount -> u64 }
|
crate::setter! { followingCount -> u64 }
|
||||||
|
|
|
@ -85,6 +85,26 @@ impl Query {
|
||||||
select
|
select
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn notifications(user: i64, show_seen: bool) -> Select<model::notification::Entity> {
|
||||||
|
let mut select =
|
||||||
|
model::notification::Entity::find()
|
||||||
|
.join(sea_orm::JoinType::InnerJoin, model::notification::Relation::Activities.def())
|
||||||
|
.order_by_desc(model::notification::Column::Published)
|
||||||
|
.filter(model::notification::Column::Actor.eq(user));
|
||||||
|
|
||||||
|
if !show_seen {
|
||||||
|
select = select.filter(model::notification::Column::Seen.eq(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
select = select.select_only();
|
||||||
|
|
||||||
|
for column in model::activity::Column::iter() {
|
||||||
|
select = select.select_column(column);
|
||||||
|
}
|
||||||
|
|
||||||
|
select
|
||||||
|
}
|
||||||
|
|
||||||
pub fn notify(activity: i64, actor: i64) -> Insert<model::notification::ActiveModel> {
|
pub fn notify(activity: i64, actor: i64) -> Insert<model::notification::ActiveModel> {
|
||||||
model::notification::Entity::insert(
|
model::notification::Entity::insert(
|
||||||
model::notification::ActiveModel {
|
model::notification::ActiveModel {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
pub mod inbox;
|
pub mod inbox;
|
||||||
pub mod outbox;
|
pub mod outbox;
|
||||||
pub mod following;
|
pub mod following;
|
||||||
|
pub mod notifications;
|
||||||
|
|
||||||
use axum::extract::{Path, Query, State};
|
use axum::extract::{Path, Query, State};
|
||||||
|
|
||||||
|
@ -68,7 +69,7 @@ pub async fn view(
|
||||||
));
|
));
|
||||||
|
|
||||||
if auth.is(&uid) {
|
if auth.is(&uid) {
|
||||||
user = user.set_feed(Node::link(upub::url!(ctx, "/actors/{id}/feed")));
|
user = user.set_notifications(Node::link(upub::url!(ctx, "/actors/{id}/notifications")));
|
||||||
}
|
}
|
||||||
|
|
||||||
if !auth.is(&uid) && !cfg.show_followers_count {
|
if !auth.is(&uid) && !cfg.show_followers_count {
|
||||||
|
|
57
upub/routes/src/activitypub/actor/notifications.rs
Normal file
57
upub/routes/src/activitypub/actor/notifications.rs
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
use axum::extract::{Path, Query, State};
|
||||||
|
use sea_orm::{PaginatorTrait, QuerySelect};
|
||||||
|
|
||||||
|
use upub::Context;
|
||||||
|
|
||||||
|
use crate::{activitypub::Pagination, builders::JsonLD, AuthIdentity, Identity};
|
||||||
|
|
||||||
|
pub async fn get(
|
||||||
|
State(ctx): State<Context>,
|
||||||
|
Path(id): Path<String>,
|
||||||
|
AuthIdentity(auth): AuthIdentity,
|
||||||
|
) -> crate::ApiResult<JsonLD<serde_json::Value>> {
|
||||||
|
let Identity::Local { id: uid, internal } = &auth else {
|
||||||
|
// notifications are only for local users
|
||||||
|
return Err(crate::ApiError::forbidden());
|
||||||
|
};
|
||||||
|
if uid != &ctx.uid(&id) {
|
||||||
|
return Err(crate::ApiError::forbidden());
|
||||||
|
}
|
||||||
|
|
||||||
|
let count = upub::Query::notifications(*internal, false)
|
||||||
|
.count(ctx.db())
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
crate::builders::collection(&upub::url!(ctx, "/actors/{id}/notifications"), Some(count))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn page(
|
||||||
|
State(ctx): State<Context>,
|
||||||
|
Path(id): Path<String>,
|
||||||
|
AuthIdentity(auth): AuthIdentity,
|
||||||
|
Query(page): Query<Pagination>,
|
||||||
|
) -> crate::ApiResult<JsonLD<serde_json::Value>> {
|
||||||
|
let Identity::Local { id: uid, internal } = &auth else {
|
||||||
|
// notifications are only for local users
|
||||||
|
return Err(crate::ApiError::forbidden());
|
||||||
|
};
|
||||||
|
if uid != &ctx.uid(&id) {
|
||||||
|
return Err(crate::ApiError::forbidden());
|
||||||
|
}
|
||||||
|
|
||||||
|
let limit = page.batch.unwrap_or(20).min(50);
|
||||||
|
let offset = page.offset.unwrap_or(0);
|
||||||
|
|
||||||
|
let activities = upub::Query::notifications(*internal, false)
|
||||||
|
.limit(limit)
|
||||||
|
.offset(offset)
|
||||||
|
.into_model::<upub::model::activity::Model>()
|
||||||
|
.all(ctx.db())
|
||||||
|
.await?
|
||||||
|
.into_iter()
|
||||||
|
.map(|x| x.ap())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
crate::builders::collection_page(&upub::url!(ctx, "/actors/{id}/notifications/page"), offset, limit, activities)
|
||||||
|
|
||||||
|
}
|
|
@ -49,6 +49,8 @@ impl ActivityPubRouter for Router<upub::Context> {
|
||||||
.route("/actors/:id/outbox", post(ap::actor::outbox::post))
|
.route("/actors/:id/outbox", post(ap::actor::outbox::post))
|
||||||
.route("/actors/:id/outbox", get(ap::actor::outbox::get))
|
.route("/actors/:id/outbox", get(ap::actor::outbox::get))
|
||||||
.route("/actors/:id/outbox/page", get(ap::actor::outbox::page))
|
.route("/actors/:id/outbox/page", get(ap::actor::outbox::page))
|
||||||
|
.route("/actors/:id/notifications", get(ap::actor::notifications::get))
|
||||||
|
.route("/actors/:id/notifications/page", get(ap::actor::notifications::page))
|
||||||
.route("/actors/:id/followers", get(ap::actor::following::get::<false>))
|
.route("/actors/:id/followers", get(ap::actor::following::get::<false>))
|
||||||
.route("/actors/:id/followers/page", get(ap::actor::following::page::<false>))
|
.route("/actors/:id/followers/page", get(ap::actor::following::page::<false>))
|
||||||
.route("/actors/:id/following", get(ap::actor::following::get::<true>))
|
.route("/actors/:id/following", get(ap::actor::following::get::<true>))
|
||||||
|
|
Loading…
Reference in a new issue