diff --git a/upub/core/src/selector/query.rs b/upub/core/src/selector/query.rs index 8c3f768..96571a6 100644 --- a/upub/core/src/selector/query.rs +++ b/upub/core/src/selector/query.rs @@ -90,6 +90,21 @@ impl Query { select } + // TODO this double join is probably not the best way to query for this... + pub fn hashtags() -> Select { + let mut select = + model::hashtag::Entity::find() + .join(sea_orm::JoinType::InnerJoin, model::hashtag::Relation::Objects.def()) + .join(sea_orm::JoinType::InnerJoin, model::object::Relation::Addressing.def()) + .select_only(); + + for col in model::object::Column::iter() { + select = select.select_column_as(col, format!("{}{}", model::object::Entity.table_name(), col.to_string())); + } + + select + } + pub fn notifications(user: i64, show_seen: bool) -> Select { let mut select = model::notification::Entity::find() diff --git a/upub/routes/src/activitypub/mod.rs b/upub/routes/src/activitypub/mod.rs index ad82650..f723aed 100644 --- a/upub/routes/src/activitypub/mod.rs +++ b/upub/routes/src/activitypub/mod.rs @@ -5,6 +5,7 @@ pub mod object; pub mod activity; pub mod application; pub mod auth; +pub mod tags; pub mod well_known; use axum::{http::StatusCode, response::IntoResponse, routing::{get, patch, post, put}, Router}; @@ -59,6 +60,9 @@ impl ActivityPubRouter for Router { // .route("/actors/:id/audience/page", get(ap::actor::audience::page)) // activities .route("/activities/:id", get(ap::activity::view)) + // hashtags + .route("/tags/:id", get(ap::tags::get)) + .route("/tags/:id/page", get(ap::tags::page)) // specific object routes .route("/objects/:id", get(ap::object::view)) .route("/objects/:id/replies", get(ap::object::replies::get)) diff --git a/upub/routes/src/activitypub/tags.rs b/upub/routes/src/activitypub/tags.rs new file mode 100644 index 0000000..415bf04 --- /dev/null +++ b/upub/routes/src/activitypub/tags.rs @@ -0,0 +1,52 @@ +use axum::extract::{Path, Query, State}; +use sea_orm::{QueryFilter, QuerySelect, ColumnTrait}; + +use upub::{selector::{BatchFillable, RichActivity}, Context}; + +use crate::{activitypub::Pagination, builders::JsonLD, AuthIdentity}; + +pub async fn get( + State(ctx): State, + Path(id): Path, +) -> crate::ApiResult> { + crate::builders::collection( + &upub::url!(ctx, "/tags/{id}"), + None, + ) +} + +pub async fn page( + State(ctx): State, + Path(id): Path, + AuthIdentity(auth): AuthIdentity, + Query(page): Query, +) -> crate::ApiResult> { + let limit = page.batch.unwrap_or(20).min(50); + let offset = page.offset.unwrap_or(0); + + let objects = upub::Query::hashtags() + .filter(auth.filter()) + .filter(upub::model::hashtag::Column::Name.eq(&id)) + .limit(limit) + .offset(offset) + .into_model::() + .all(ctx.db()) + .await? + .with_batched::(ctx.db()) + .await? + .with_batched::(ctx.db()) + .await? + .with_batched::(ctx.db()) + .await? + .into_iter() + .map(|x| x.ap()) + .collect(); + + crate::builders::collection_page( + &upub::url!(ctx, "/tags/{id}/page"), + offset, + limit, + objects, + ) + +}