feat: implement merge readiness plan to close gaps between v2 and v1
Some checks failed
lint / lint (push) Has been cancelled
test / unit (push) Has been cancelled
test / integration (push) Has been cancelled
lint / lint (pull_request) Failing after 5m8s
test / unit (pull_request) Successful in 16m18s
test / integration (pull_request) Failing after 16m59s
Some checks failed
lint / lint (push) Has been cancelled
test / unit (push) Has been cancelled
test / integration (push) Has been cancelled
lint / lint (pull_request) Failing after 5m8s
test / unit (pull_request) Successful in 16m18s
test / integration (pull_request) Failing after 16m59s
- Task 1: Fix feed response hydration by adding `to_thought_response` helper and updating feed handlers to return full `ThoughtResponse`. - Task 2: Wire follower/following REST routes for user feeds. - Task 3: Add user listing and count endpoints, including `GET /users` and `GET /users/count`. - Task 4: Implement popular tags feature with `GET /tags/popular`. - Task 5: Enhance configuration with HOST, CORS_ORIGINS, and optional rate limiting using tower-governor.
This commit is contained in:
@@ -1,14 +1,14 @@
|
||||
use std::sync::Arc;
|
||||
use anyhow::{anyhow, Result};
|
||||
use async_trait::async_trait;
|
||||
use chrono::{DateTime, Utc};
|
||||
use std::sync::Arc;
|
||||
use url::Url;
|
||||
|
||||
use crate::note::ThoughtNote;
|
||||
use crate::urls::ThoughtsUrls;
|
||||
use activitypub_base::ApObjectHandler;
|
||||
use domain::ports::ActivityPubRepository;
|
||||
use domain::value_objects::UserId;
|
||||
use crate::note::ThoughtNote;
|
||||
use crate::urls::ThoughtsUrls;
|
||||
|
||||
pub struct ThoughtsObjectHandler {
|
||||
repo: Arc<dyn ActivityPubRepository>,
|
||||
@@ -17,7 +17,10 @@ pub struct ThoughtsObjectHandler {
|
||||
|
||||
impl ThoughtsObjectHandler {
|
||||
pub fn new(repo: Arc<dyn ActivityPubRepository>, base_url: &str) -> Self {
|
||||
Self { repo, urls: ThoughtsUrls::new(base_url) }
|
||||
Self {
|
||||
repo,
|
||||
urls: ThoughtsUrls::new(base_url),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,21 +31,34 @@ impl ApObjectHandler for ThoughtsObjectHandler {
|
||||
user_id: uuid::Uuid,
|
||||
) -> Result<Vec<(Url, serde_json::Value)>> {
|
||||
let uid = UserId::from_uuid(user_id);
|
||||
let entries = self.repo.outbox_entries_for_actor(&uid).await
|
||||
let entries = self
|
||||
.repo
|
||||
.outbox_entries_for_actor(&uid)
|
||||
.await
|
||||
.map_err(|e| anyhow!("{e}"))?;
|
||||
entries.into_iter().map(|e| {
|
||||
let note_url = self.urls.thought_url(e.thought.id.as_uuid());
|
||||
let actor_url = self.urls.user_url(e.author_username.as_str());
|
||||
let followers = self.urls.user_followers(e.author_username.as_str());
|
||||
let in_reply_to = e.thought.in_reply_to_id.map(|id| self.urls.thought_url(id.as_uuid()));
|
||||
let note = ThoughtNote::new_public(
|
||||
note_url.clone(), actor_url,
|
||||
e.thought.content.as_str().to_owned(),
|
||||
e.thought.created_at, in_reply_to,
|
||||
e.thought.sensitive, e.thought.content_warning, followers,
|
||||
);
|
||||
Ok((note_url, serde_json::to_value(¬e)?))
|
||||
}).collect()
|
||||
entries
|
||||
.into_iter()
|
||||
.map(|e| {
|
||||
let note_url = self.urls.thought_url(e.thought.id.as_uuid());
|
||||
let actor_url = self.urls.user_url(e.author_username.as_str());
|
||||
let followers = self.urls.user_followers(e.author_username.as_str());
|
||||
let in_reply_to = e
|
||||
.thought
|
||||
.in_reply_to_id
|
||||
.map(|id| self.urls.thought_url(id.as_uuid()));
|
||||
let note = ThoughtNote::new_public(
|
||||
note_url.clone(),
|
||||
actor_url,
|
||||
e.thought.content.as_str().to_owned(),
|
||||
e.thought.created_at,
|
||||
in_reply_to,
|
||||
e.thought.sensitive,
|
||||
e.thought.content_warning,
|
||||
followers,
|
||||
);
|
||||
Ok((note_url, serde_json::to_value(¬e)?))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
async fn get_local_objects_page(
|
||||
@@ -52,22 +68,35 @@ impl ApObjectHandler for ThoughtsObjectHandler {
|
||||
limit: usize,
|
||||
) -> Result<Vec<(Url, serde_json::Value, DateTime<Utc>)>> {
|
||||
let uid = UserId::from_uuid(user_id);
|
||||
let entries = self.repo.outbox_page_for_actor(&uid, before, limit).await
|
||||
let entries = self
|
||||
.repo
|
||||
.outbox_page_for_actor(&uid, before, limit)
|
||||
.await
|
||||
.map_err(|e| anyhow!("{e}"))?;
|
||||
entries.into_iter().map(|e| {
|
||||
let created_at = e.thought.created_at;
|
||||
let note_url = self.urls.thought_url(e.thought.id.as_uuid());
|
||||
let actor_url = self.urls.user_url(e.author_username.as_str());
|
||||
let followers = self.urls.user_followers(e.author_username.as_str());
|
||||
let in_reply_to = e.thought.in_reply_to_id.map(|id| self.urls.thought_url(id.as_uuid()));
|
||||
let note = ThoughtNote::new_public(
|
||||
note_url.clone(), actor_url,
|
||||
e.thought.content.as_str().to_owned(),
|
||||
created_at, in_reply_to,
|
||||
e.thought.sensitive, e.thought.content_warning, followers,
|
||||
);
|
||||
Ok((note_url, serde_json::to_value(¬e)?, created_at))
|
||||
}).collect()
|
||||
entries
|
||||
.into_iter()
|
||||
.map(|e| {
|
||||
let created_at = e.thought.created_at;
|
||||
let note_url = self.urls.thought_url(e.thought.id.as_uuid());
|
||||
let actor_url = self.urls.user_url(e.author_username.as_str());
|
||||
let followers = self.urls.user_followers(e.author_username.as_str());
|
||||
let in_reply_to = e
|
||||
.thought
|
||||
.in_reply_to_id
|
||||
.map(|id| self.urls.thought_url(id.as_uuid()));
|
||||
let note = ThoughtNote::new_public(
|
||||
note_url.clone(),
|
||||
actor_url,
|
||||
e.thought.content.as_str().to_owned(),
|
||||
created_at,
|
||||
in_reply_to,
|
||||
e.thought.sensitive,
|
||||
e.thought.content_warning,
|
||||
followers,
|
||||
);
|
||||
Ok((note_url, serde_json::to_value(¬e)?, created_at))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
async fn on_create(
|
||||
@@ -77,15 +106,22 @@ impl ApObjectHandler for ThoughtsObjectHandler {
|
||||
object: serde_json::Value,
|
||||
) -> Result<()> {
|
||||
let note: ThoughtNote = serde_json::from_value(object)?;
|
||||
let author_id = self.repo.intern_remote_actor(actor_url).await
|
||||
let author_id = self
|
||||
.repo
|
||||
.intern_remote_actor(actor_url)
|
||||
.await
|
||||
.map_err(|e| anyhow!("{e}"))?;
|
||||
self.repo.accept_note(
|
||||
ap_id, &author_id,
|
||||
¬e.content,
|
||||
note.published,
|
||||
note.sensitive,
|
||||
note.summary,
|
||||
).await.map_err(|e| anyhow!("{e}"))
|
||||
self.repo
|
||||
.accept_note(
|
||||
ap_id,
|
||||
&author_id,
|
||||
¬e.content,
|
||||
note.published,
|
||||
note.sensitive,
|
||||
note.summary,
|
||||
)
|
||||
.await
|
||||
.map_err(|e| anyhow!("{e}"))
|
||||
}
|
||||
|
||||
async fn on_update(
|
||||
@@ -95,19 +131,30 @@ impl ApObjectHandler for ThoughtsObjectHandler {
|
||||
object: serde_json::Value,
|
||||
) -> Result<()> {
|
||||
let note: ThoughtNote = serde_json::from_value(object)?;
|
||||
self.repo.apply_note_update(ap_id, ¬e.content).await
|
||||
self.repo
|
||||
.apply_note_update(ap_id, ¬e.content)
|
||||
.await
|
||||
.map_err(|e| anyhow!("{e}"))
|
||||
}
|
||||
|
||||
async fn on_delete(&self, ap_id: &Url, _actor_url: &Url) -> Result<()> {
|
||||
self.repo.retract_note(ap_id).await.map_err(|e| anyhow!("{e}"))
|
||||
self.repo
|
||||
.retract_note(ap_id)
|
||||
.await
|
||||
.map_err(|e| anyhow!("{e}"))
|
||||
}
|
||||
|
||||
async fn on_actor_removed(&self, actor_url: &Url) -> Result<()> {
|
||||
self.repo.retract_actor_notes(actor_url).await.map_err(|e| anyhow!("{e}"))
|
||||
self.repo
|
||||
.retract_actor_notes(actor_url)
|
||||
.await
|
||||
.map_err(|e| anyhow!("{e}"))
|
||||
}
|
||||
|
||||
async fn count_local_posts(&self) -> Result<u64> {
|
||||
self.repo.count_local_notes().await.map_err(|e| anyhow!("{e}"))
|
||||
self.repo
|
||||
.count_local_notes()
|
||||
.await
|
||||
.map_err(|e| anyhow!("{e}"))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user