refactor(domain): remove ap_id/inbox_url from User and Thought; use ActivityPubRepository lookups
This commit is contained in:
@@ -74,6 +74,7 @@ fn thought_note_json(
|
|||||||
thought: &domain::models::thought::Thought,
|
thought: &domain::models::thought::Thought,
|
||||||
local_actor: &crate::actors::DbActor,
|
local_actor: &crate::actors::DbActor,
|
||||||
base_url: &str,
|
base_url: &str,
|
||||||
|
in_reply_to_url: Option<&str>,
|
||||||
) -> anyhow::Result<(url::Url, serde_json::Value)> {
|
) -> anyhow::Result<(url::Url, serde_json::Value)> {
|
||||||
let ap_id = url::Url::parse(&format!("{}/thoughts/{}", base_url, thought.id))?;
|
let ap_id = url::Url::parse(&format!("{}/thoughts/{}", base_url, thought.id))?;
|
||||||
|
|
||||||
@@ -107,7 +108,7 @@ fn thought_note_json(
|
|||||||
if let Some(ref cw) = thought.content_warning {
|
if let Some(ref cw) = thought.content_warning {
|
||||||
note["summary"] = serde_json::json!(cw);
|
note["summary"] = serde_json::json!(cw);
|
||||||
}
|
}
|
||||||
if let Some(ref reply_url) = thought.in_reply_to_url {
|
if let Some(reply_url) = in_reply_to_url {
|
||||||
note["inReplyTo"] = serde_json::json!(reply_url);
|
note["inReplyTo"] = serde_json::json!(reply_url);
|
||||||
}
|
}
|
||||||
if let Some(updated_at) = thought.updated_at {
|
if let Some(updated_at) = thought.updated_at {
|
||||||
@@ -1415,6 +1416,7 @@ impl domain::ports::OutboundFederationPort for ActivityPubService {
|
|||||||
author_user_id: &domain::value_objects::UserId,
|
author_user_id: &domain::value_objects::UserId,
|
||||||
thought: &domain::models::thought::Thought,
|
thought: &domain::models::thought::Thought,
|
||||||
_author_username: &str,
|
_author_username: &str,
|
||||||
|
in_reply_to_url: Option<&str>,
|
||||||
) -> Result<(), domain::errors::DomainError> {
|
) -> Result<(), domain::errors::DomainError> {
|
||||||
let user_uuid = author_user_id.as_uuid();
|
let user_uuid = author_user_id.as_uuid();
|
||||||
let data = self.federation_config.to_request_data();
|
let data = self.federation_config.to_request_data();
|
||||||
@@ -1426,8 +1428,9 @@ impl domain::ports::OutboundFederationPort for ActivityPubService {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
|
||||||
let (ap_id, note) = thought_note_json(thought, &local_actor, &self.base_url)
|
let (ap_id, note) =
|
||||||
.map_err(|e| domain::errors::DomainError::Internal(e.to_string()))?;
|
thought_note_json(thought, &local_actor, &self.base_url, in_reply_to_url)
|
||||||
|
.map_err(|e| domain::errors::DomainError::Internal(e.to_string()))?;
|
||||||
|
|
||||||
let create = crate::activities::CreateActivity {
|
let create = crate::activities::CreateActivity {
|
||||||
id: ap_id,
|
id: ap_id,
|
||||||
@@ -1476,6 +1479,7 @@ impl domain::ports::OutboundFederationPort for ActivityPubService {
|
|||||||
author_user_id: &domain::value_objects::UserId,
|
author_user_id: &domain::value_objects::UserId,
|
||||||
thought: &domain::models::thought::Thought,
|
thought: &domain::models::thought::Thought,
|
||||||
_author_username: &str,
|
_author_username: &str,
|
||||||
|
in_reply_to_url: Option<&str>,
|
||||||
) -> Result<(), domain::errors::DomainError> {
|
) -> Result<(), domain::errors::DomainError> {
|
||||||
let user_uuid = author_user_id.as_uuid();
|
let user_uuid = author_user_id.as_uuid();
|
||||||
let data = self.federation_config.to_request_data();
|
let data = self.federation_config.to_request_data();
|
||||||
@@ -1487,8 +1491,9 @@ impl domain::ports::OutboundFederationPort for ActivityPubService {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
|
||||||
let (_ap_id, note) = thought_note_json(thought, &local_actor, &self.base_url)
|
let (_ap_id, note) =
|
||||||
.map_err(|e| domain::errors::DomainError::Internal(e.to_string()))?;
|
thought_note_json(thought, &local_actor, &self.base_url, in_reply_to_url)
|
||||||
|
.map_err(|e| domain::errors::DomainError::Internal(e.to_string()))?;
|
||||||
|
|
||||||
let update_id = url::Url::parse(&format!(
|
let update_id = url::Url::parse(&format!(
|
||||||
"{}/activities/update/{}",
|
"{}/activities/update/{}",
|
||||||
|
|||||||
@@ -29,8 +29,6 @@ struct FeedRow {
|
|||||||
t_user_id: uuid::Uuid,
|
t_user_id: uuid::Uuid,
|
||||||
content: String,
|
content: String,
|
||||||
in_reply_to_id: Option<uuid::Uuid>,
|
in_reply_to_id: Option<uuid::Uuid>,
|
||||||
in_reply_to_url: Option<String>,
|
|
||||||
t_ap_id: Option<String>,
|
|
||||||
visibility: String,
|
visibility: String,
|
||||||
content_warning: Option<String>,
|
content_warning: Option<String>,
|
||||||
sensitive: bool,
|
sensitive: bool,
|
||||||
@@ -47,8 +45,6 @@ struct FeedRow {
|
|||||||
header_url: Option<String>,
|
header_url: Option<String>,
|
||||||
custom_css: Option<String>,
|
custom_css: Option<String>,
|
||||||
author_local: bool,
|
author_local: bool,
|
||||||
u_ap_id: Option<String>,
|
|
||||||
inbox_url: Option<String>,
|
|
||||||
author_created_at: DateTime<Utc>,
|
author_created_at: DateTime<Utc>,
|
||||||
author_updated_at: DateTime<Utc>,
|
author_updated_at: DateTime<Utc>,
|
||||||
like_count: i64,
|
like_count: i64,
|
||||||
@@ -59,12 +55,12 @@ struct FeedRow {
|
|||||||
const FEED_SELECT: &str = "
|
const FEED_SELECT: &str = "
|
||||||
SELECT
|
SELECT
|
||||||
t.id AS thought_id, t.user_id AS t_user_id, t.content,
|
t.id AS thought_id, t.user_id AS t_user_id, t.content,
|
||||||
t.in_reply_to_id, t.in_reply_to_url, t.ap_id AS t_ap_id,
|
t.in_reply_to_id,
|
||||||
t.visibility, t.content_warning, t.sensitive, t.local AS t_local,
|
t.visibility, t.content_warning, t.sensitive, t.local AS t_local,
|
||||||
t.created_at AS thought_created_at, t.updated_at,
|
t.created_at AS thought_created_at, t.updated_at,
|
||||||
u.id AS author_id, u.username, u.email, u.password_hash,
|
u.id AS author_id, u.username, u.email, u.password_hash,
|
||||||
u.display_name, u.bio, u.avatar_url, u.header_url, u.custom_css,
|
u.display_name, u.bio, u.avatar_url, u.header_url, u.custom_css,
|
||||||
u.local AS author_local, u.ap_id AS u_ap_id, u.inbox_url,
|
u.local AS author_local,
|
||||||
u.created_at AS author_created_at, u.updated_at AS author_updated_at,
|
u.created_at AS author_created_at, u.updated_at AS author_updated_at,
|
||||||
(SELECT COUNT(*) FROM likes l WHERE l.thought_id=t.id) AS like_count,
|
(SELECT COUNT(*) FROM likes l WHERE l.thought_id=t.id) AS like_count,
|
||||||
(SELECT COUNT(*) FROM boosts b WHERE b.thought_id=t.id) AS boost_count,
|
(SELECT COUNT(*) FROM boosts b WHERE b.thought_id=t.id) AS boost_count,
|
||||||
@@ -77,8 +73,6 @@ fn row_to_entry(r: FeedRow) -> FeedEntry {
|
|||||||
user_id: UserId::from_uuid(r.t_user_id),
|
user_id: UserId::from_uuid(r.t_user_id),
|
||||||
content: Content::new_remote(r.content),
|
content: Content::new_remote(r.content),
|
||||||
in_reply_to_id: r.in_reply_to_id.map(ThoughtId::from_uuid),
|
in_reply_to_id: r.in_reply_to_id.map(ThoughtId::from_uuid),
|
||||||
in_reply_to_url: r.in_reply_to_url,
|
|
||||||
ap_id: r.t_ap_id,
|
|
||||||
visibility: Visibility::from_db_str(&r.visibility),
|
visibility: Visibility::from_db_str(&r.visibility),
|
||||||
content_warning: r.content_warning,
|
content_warning: r.content_warning,
|
||||||
sensitive: r.sensitive,
|
sensitive: r.sensitive,
|
||||||
@@ -97,8 +91,6 @@ fn row_to_entry(r: FeedRow) -> FeedEntry {
|
|||||||
header_url: r.header_url,
|
header_url: r.header_url,
|
||||||
custom_css: r.custom_css,
|
custom_css: r.custom_css,
|
||||||
local: r.author_local,
|
local: r.author_local,
|
||||||
ap_id: r.u_ap_id,
|
|
||||||
inbox_url: r.inbox_url,
|
|
||||||
created_at: r.author_created_at,
|
created_at: r.author_created_at,
|
||||||
updated_at: r.author_updated_at,
|
updated_at: r.author_updated_at,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -60,8 +60,6 @@ impl ActivityPubRepository for PgActivityPubRepository {
|
|||||||
user_id: UserId::from_uuid(r.user_id),
|
user_id: UserId::from_uuid(r.user_id),
|
||||||
content: Content::new_remote(r.content),
|
content: Content::new_remote(r.content),
|
||||||
in_reply_to_id: r.in_reply_to_id.map(ThoughtId::from_uuid),
|
in_reply_to_id: r.in_reply_to_id.map(ThoughtId::from_uuid),
|
||||||
in_reply_to_url: None,
|
|
||||||
ap_id: None,
|
|
||||||
visibility: Visibility::Public,
|
visibility: Visibility::Public,
|
||||||
content_warning: r.content_warning,
|
content_warning: r.content_warning,
|
||||||
sensitive: r.sensitive,
|
sensitive: r.sensitive,
|
||||||
@@ -127,8 +125,6 @@ impl ActivityPubRepository for PgActivityPubRepository {
|
|||||||
user_id: UserId::from_uuid(r.user_id),
|
user_id: UserId::from_uuid(r.user_id),
|
||||||
content: Content::new_remote(r.content),
|
content: Content::new_remote(r.content),
|
||||||
in_reply_to_id: r.in_reply_to_id.map(ThoughtId::from_uuid),
|
in_reply_to_id: r.in_reply_to_id.map(ThoughtId::from_uuid),
|
||||||
in_reply_to_url: None,
|
|
||||||
ap_id: None,
|
|
||||||
visibility: Visibility::Public,
|
visibility: Visibility::Public,
|
||||||
content_warning: r.content_warning,
|
content_warning: r.content_warning,
|
||||||
sensitive: r.sensitive,
|
sensitive: r.sensitive,
|
||||||
|
|||||||
@@ -29,8 +29,6 @@ struct FeedRow {
|
|||||||
t_user_id: uuid::Uuid,
|
t_user_id: uuid::Uuid,
|
||||||
content: String,
|
content: String,
|
||||||
in_reply_to_id: Option<uuid::Uuid>,
|
in_reply_to_id: Option<uuid::Uuid>,
|
||||||
in_reply_to_url: Option<String>,
|
|
||||||
t_ap_id: Option<String>,
|
|
||||||
visibility: String,
|
visibility: String,
|
||||||
content_warning: Option<String>,
|
content_warning: Option<String>,
|
||||||
sensitive: bool,
|
sensitive: bool,
|
||||||
@@ -47,8 +45,6 @@ struct FeedRow {
|
|||||||
header_url: Option<String>,
|
header_url: Option<String>,
|
||||||
custom_css: Option<String>,
|
custom_css: Option<String>,
|
||||||
author_local: bool,
|
author_local: bool,
|
||||||
u_ap_id: Option<String>,
|
|
||||||
inbox_url: Option<String>,
|
|
||||||
author_created_at: DateTime<Utc>,
|
author_created_at: DateTime<Utc>,
|
||||||
author_updated_at: DateTime<Utc>,
|
author_updated_at: DateTime<Utc>,
|
||||||
like_count: i64,
|
like_count: i64,
|
||||||
@@ -83,7 +79,7 @@ fn feed_select(viewer: Option<uuid::Uuid>) -> String {
|
|||||||
"
|
"
|
||||||
SELECT
|
SELECT
|
||||||
t.id AS thought_id, t.user_id AS t_user_id, t.content,
|
t.id AS thought_id, t.user_id AS t_user_id, t.content,
|
||||||
t.in_reply_to_id, t.in_reply_to_url, t.ap_id AS t_ap_id,
|
t.in_reply_to_id,
|
||||||
t.visibility, t.content_warning, t.sensitive, t.local AS t_local,
|
t.visibility, t.content_warning, t.sensitive, t.local AS t_local,
|
||||||
t.created_at AS thought_created_at, t.updated_at,
|
t.created_at AS thought_created_at, t.updated_at,
|
||||||
u.id AS author_id,
|
u.id AS author_id,
|
||||||
@@ -98,7 +94,7 @@ fn feed_select(viewer: Option<uuid::Uuid>) -> String {
|
|||||||
u.bio,
|
u.bio,
|
||||||
COALESCE(ra.avatar_url, u.avatar_url) AS avatar_url,
|
COALESCE(ra.avatar_url, u.avatar_url) AS avatar_url,
|
||||||
u.header_url, u.custom_css,
|
u.header_url, u.custom_css,
|
||||||
u.local AS author_local, u.ap_id AS u_ap_id, u.inbox_url,
|
u.local AS author_local,
|
||||||
u.created_at AS author_created_at, u.updated_at AS author_updated_at,
|
u.created_at AS author_created_at, u.updated_at AS author_updated_at,
|
||||||
(SELECT COUNT(*) FROM likes l WHERE l.thought_id=t.id) AS like_count,
|
(SELECT COUNT(*) FROM likes l WHERE l.thought_id=t.id) AS like_count,
|
||||||
(SELECT COUNT(*) FROM boosts b WHERE b.thought_id=t.id) AS boost_count,
|
(SELECT COUNT(*) FROM boosts b WHERE b.thought_id=t.id) AS boost_count,
|
||||||
@@ -116,8 +112,6 @@ fn row_to_entry(r: FeedRow) -> FeedEntry {
|
|||||||
user_id: UserId::from_uuid(r.t_user_id),
|
user_id: UserId::from_uuid(r.t_user_id),
|
||||||
content: Content::new_remote(r.content),
|
content: Content::new_remote(r.content),
|
||||||
in_reply_to_id: r.in_reply_to_id.map(ThoughtId::from_uuid),
|
in_reply_to_id: r.in_reply_to_id.map(ThoughtId::from_uuid),
|
||||||
in_reply_to_url: r.in_reply_to_url,
|
|
||||||
ap_id: r.t_ap_id,
|
|
||||||
visibility: Visibility::from_db_str(&r.visibility),
|
visibility: Visibility::from_db_str(&r.visibility),
|
||||||
content_warning: r.content_warning,
|
content_warning: r.content_warning,
|
||||||
sensitive: r.sensitive,
|
sensitive: r.sensitive,
|
||||||
@@ -136,8 +130,6 @@ fn row_to_entry(r: FeedRow) -> FeedEntry {
|
|||||||
header_url: r.header_url,
|
header_url: r.header_url,
|
||||||
custom_css: r.custom_css,
|
custom_css: r.custom_css,
|
||||||
local: r.author_local,
|
local: r.author_local,
|
||||||
ap_id: r.u_ap_id,
|
|
||||||
inbox_url: r.inbox_url,
|
|
||||||
created_at: r.author_created_at,
|
created_at: r.author_created_at,
|
||||||
updated_at: r.author_updated_at,
|
updated_at: r.author_updated_at,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -28,8 +28,6 @@ pub(crate) struct ThoughtRow {
|
|||||||
pub user_id: uuid::Uuid,
|
pub user_id: uuid::Uuid,
|
||||||
pub content: String,
|
pub content: String,
|
||||||
pub in_reply_to_id: Option<uuid::Uuid>,
|
pub in_reply_to_id: Option<uuid::Uuid>,
|
||||||
pub in_reply_to_url: Option<String>,
|
|
||||||
pub ap_id: Option<String>,
|
|
||||||
pub visibility: String,
|
pub visibility: String,
|
||||||
pub content_warning: Option<String>,
|
pub content_warning: Option<String>,
|
||||||
pub sensitive: bool,
|
pub sensitive: bool,
|
||||||
@@ -45,8 +43,6 @@ impl From<ThoughtRow> for Thought {
|
|||||||
user_id: UserId::from_uuid(r.user_id),
|
user_id: UserId::from_uuid(r.user_id),
|
||||||
content: Content::new_remote(r.content),
|
content: Content::new_remote(r.content),
|
||||||
in_reply_to_id: r.in_reply_to_id.map(ThoughtId::from_uuid),
|
in_reply_to_id: r.in_reply_to_id.map(ThoughtId::from_uuid),
|
||||||
in_reply_to_url: r.in_reply_to_url,
|
|
||||||
ap_id: r.ap_id,
|
|
||||||
visibility: Visibility::from_db_str(&r.visibility),
|
visibility: Visibility::from_db_str(&r.visibility),
|
||||||
content_warning: r.content_warning,
|
content_warning: r.content_warning,
|
||||||
sensitive: r.sensitive,
|
sensitive: r.sensitive,
|
||||||
@@ -58,22 +54,20 @@ impl From<ThoughtRow> for Thought {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const THOUGHT_SELECT: &str =
|
const THOUGHT_SELECT: &str =
|
||||||
"SELECT id,user_id,content,in_reply_to_id,in_reply_to_url,ap_id,visibility,content_warning,sensitive,local,created_at,updated_at FROM thoughts";
|
"SELECT id,user_id,content,in_reply_to_id,visibility,content_warning,sensitive,local,created_at,updated_at FROM thoughts";
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl ThoughtRepository for PgThoughtRepository {
|
impl ThoughtRepository for PgThoughtRepository {
|
||||||
async fn save(&self, t: &Thought) -> Result<(), DomainError> {
|
async fn save(&self, t: &Thought) -> Result<(), DomainError> {
|
||||||
sqlx::query(
|
sqlx::query(
|
||||||
"INSERT INTO thoughts(id,user_id,content,in_reply_to_id,in_reply_to_url,ap_id,visibility,content_warning,sensitive,local,created_at)
|
"INSERT INTO thoughts(id,user_id,content,in_reply_to_id,visibility,content_warning,sensitive,local,created_at)
|
||||||
VALUES($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11)
|
VALUES($1,$2,$3,$4,$5,$6,$7,$8,$9)
|
||||||
ON CONFLICT(id) DO UPDATE SET content=EXCLUDED.content,updated_at=NOW()"
|
ON CONFLICT(id) DO UPDATE SET content=EXCLUDED.content,updated_at=NOW()"
|
||||||
)
|
)
|
||||||
.bind(t.id.as_uuid())
|
.bind(t.id.as_uuid())
|
||||||
.bind(t.user_id.as_uuid())
|
.bind(t.user_id.as_uuid())
|
||||||
.bind(t.content.as_str())
|
.bind(t.content.as_str())
|
||||||
.bind(t.in_reply_to_id.as_ref().map(|x| x.as_uuid()))
|
.bind(t.in_reply_to_id.as_ref().map(|x| x.as_uuid()))
|
||||||
.bind(&t.in_reply_to_url)
|
|
||||||
.bind(&t.ap_id)
|
|
||||||
.bind(t.visibility.as_str())
|
.bind(t.visibility.as_str())
|
||||||
.bind(&t.content_warning)
|
.bind(&t.content_warning)
|
||||||
.bind(t.sensitive)
|
.bind(t.sensitive)
|
||||||
@@ -121,11 +115,11 @@ impl ThoughtRepository for PgThoughtRepository {
|
|||||||
// Recursive CTE: fetches the root thought and all nested replies at any depth.
|
// Recursive CTE: fetches the root thought and all nested replies at any depth.
|
||||||
sqlx::query_as::<_, ThoughtRow>(
|
sqlx::query_as::<_, ThoughtRow>(
|
||||||
"WITH RECURSIVE thread AS (
|
"WITH RECURSIVE thread AS (
|
||||||
SELECT id,user_id,content,in_reply_to_id,in_reply_to_url,ap_id,
|
SELECT id,user_id,content,in_reply_to_id,
|
||||||
visibility,content_warning,sensitive,local,created_at,updated_at
|
visibility,content_warning,sensitive,local,created_at,updated_at
|
||||||
FROM thoughts WHERE id = $1
|
FROM thoughts WHERE id = $1
|
||||||
UNION ALL
|
UNION ALL
|
||||||
SELECT t.id,t.user_id,t.content,t.in_reply_to_id,t.in_reply_to_url,t.ap_id,
|
SELECT t.id,t.user_id,t.content,t.in_reply_to_id,
|
||||||
t.visibility,t.content_warning,t.sensitive,t.local,t.created_at,t.updated_at
|
t.visibility,t.content_warning,t.sensitive,t.local,t.created_at,t.updated_at
|
||||||
FROM thoughts t JOIN thread ON t.in_reply_to_id = thread.id
|
FROM thoughts t JOIN thread ON t.in_reply_to_id = thread.id
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -58,15 +58,13 @@ impl TopFriendRepository for PgTopFriendRepository {
|
|||||||
header_url: Option<String>,
|
header_url: Option<String>,
|
||||||
custom_css: Option<String>,
|
custom_css: Option<String>,
|
||||||
local: bool,
|
local: bool,
|
||||||
ap_id: Option<String>,
|
|
||||||
inbox_url: Option<String>,
|
|
||||||
created_at: chrono::DateTime<chrono::Utc>,
|
created_at: chrono::DateTime<chrono::Utc>,
|
||||||
updated_at: chrono::DateTime<chrono::Utc>,
|
updated_at: chrono::DateTime<chrono::Utc>,
|
||||||
}
|
}
|
||||||
let rows = sqlx::query_as::<_, Row>(
|
let rows = sqlx::query_as::<_, Row>(
|
||||||
"SELECT tf.user_id AS tf_user_id, tf.friend_id, tf.position,
|
"SELECT tf.user_id AS tf_user_id, tf.friend_id, tf.position,
|
||||||
u.id, u.username, u.email, u.password_hash, u.display_name, u.bio,
|
u.id, u.username, u.email, u.password_hash, u.display_name, u.bio,
|
||||||
u.avatar_url, u.header_url, u.custom_css, u.local, u.ap_id, u.inbox_url,
|
u.avatar_url, u.header_url, u.custom_css, u.local,
|
||||||
u.created_at, u.updated_at
|
u.created_at, u.updated_at
|
||||||
FROM top_friends tf JOIN users u ON u.id=tf.friend_id
|
FROM top_friends tf JOIN users u ON u.id=tf.friend_id
|
||||||
WHERE tf.user_id=$1 ORDER BY tf.position",
|
WHERE tf.user_id=$1 ORDER BY tf.position",
|
||||||
@@ -96,8 +94,6 @@ impl TopFriendRepository for PgTopFriendRepository {
|
|||||||
header_url: r.header_url,
|
header_url: r.header_url,
|
||||||
custom_css: r.custom_css,
|
custom_css: r.custom_css,
|
||||||
local: r.local,
|
local: r.local,
|
||||||
ap_id: r.ap_id,
|
|
||||||
inbox_url: r.inbox_url,
|
|
||||||
created_at: r.created_at,
|
created_at: r.created_at,
|
||||||
updated_at: r.updated_at,
|
updated_at: r.updated_at,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -30,8 +30,6 @@ pub struct UserRow {
|
|||||||
pub header_url: Option<String>,
|
pub header_url: Option<String>,
|
||||||
pub custom_css: Option<String>,
|
pub custom_css: Option<String>,
|
||||||
pub local: bool,
|
pub local: bool,
|
||||||
pub ap_id: Option<String>,
|
|
||||||
pub inbox_url: Option<String>,
|
|
||||||
pub created_at: DateTime<Utc>,
|
pub created_at: DateTime<Utc>,
|
||||||
pub updated_at: DateTime<Utc>,
|
pub updated_at: DateTime<Utc>,
|
||||||
}
|
}
|
||||||
@@ -49,15 +47,15 @@ impl From<UserRow> for User {
|
|||||||
header_url: r.header_url,
|
header_url: r.header_url,
|
||||||
custom_css: r.custom_css,
|
custom_css: r.custom_css,
|
||||||
local: r.local,
|
local: r.local,
|
||||||
ap_id: r.ap_id,
|
|
||||||
inbox_url: r.inbox_url,
|
|
||||||
created_at: r.created_at,
|
created_at: r.created_at,
|
||||||
updated_at: r.updated_at,
|
updated_at: r.updated_at,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const USER_SELECT: &str = "SELECT id,username,email,password_hash,display_name,bio,avatar_url,header_url,custom_css,local,ap_id,inbox_url,created_at,updated_at FROM users";
|
pub const USER_SELECT: &str =
|
||||||
|
"SELECT id,username,email,password_hash,display_name,bio,avatar_url,header_url,\
|
||||||
|
custom_css,local,created_at,updated_at FROM users";
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl UserRepository for PgUserRepository {
|
impl UserRepository for PgUserRepository {
|
||||||
@@ -90,14 +88,14 @@ impl UserRepository for PgUserRepository {
|
|||||||
|
|
||||||
async fn save(&self, user: &User) -> Result<(), DomainError> {
|
async fn save(&self, user: &User) -> Result<(), DomainError> {
|
||||||
sqlx::query(
|
sqlx::query(
|
||||||
"INSERT INTO users (id,username,email,password_hash,display_name,bio,avatar_url,header_url,custom_css,local,ap_id,inbox_url,created_at,updated_at)
|
"INSERT INTO users (id,username,email,password_hash,display_name,bio,avatar_url,header_url,custom_css,local,created_at,updated_at)
|
||||||
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14)
|
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12)
|
||||||
ON CONFLICT(id) DO UPDATE SET
|
ON CONFLICT(id) DO UPDATE SET
|
||||||
username=EXCLUDED.username, email=EXCLUDED.email,
|
username=EXCLUDED.username, email=EXCLUDED.email,
|
||||||
password_hash=EXCLUDED.password_hash, display_name=EXCLUDED.display_name,
|
password_hash=EXCLUDED.password_hash, display_name=EXCLUDED.display_name,
|
||||||
bio=EXCLUDED.bio, avatar_url=EXCLUDED.avatar_url,
|
bio=EXCLUDED.bio, avatar_url=EXCLUDED.avatar_url,
|
||||||
header_url=EXCLUDED.header_url, custom_css=EXCLUDED.custom_css,
|
header_url=EXCLUDED.header_url, custom_css=EXCLUDED.custom_css,
|
||||||
local=EXCLUDED.local, ap_id=EXCLUDED.ap_id, inbox_url=EXCLUDED.inbox_url,
|
local=EXCLUDED.local,
|
||||||
updated_at=NOW()"
|
updated_at=NOW()"
|
||||||
)
|
)
|
||||||
.bind(user.id.as_uuid())
|
.bind(user.id.as_uuid())
|
||||||
@@ -110,8 +108,6 @@ impl UserRepository for PgUserRepository {
|
|||||||
.bind(&user.header_url)
|
.bind(&user.header_url)
|
||||||
.bind(&user.custom_css)
|
.bind(&user.custom_css)
|
||||||
.bind(user.local)
|
.bind(user.local)
|
||||||
.bind(&user.ap_id)
|
|
||||||
.bind(&user.inbox_url)
|
|
||||||
.bind(user.created_at)
|
.bind(user.created_at)
|
||||||
.bind(user.updated_at)
|
.bind(user.updated_at)
|
||||||
.execute(&self.pool)
|
.execute(&self.pool)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use domain::{
|
use domain::{
|
||||||
errors::DomainError,
|
errors::DomainError,
|
||||||
events::DomainEvent,
|
events::DomainEvent,
|
||||||
models::thought::{Thought, Visibility},
|
models::thought::Visibility,
|
||||||
ports::{ActivityPubRepository, OutboundFederationPort, ThoughtRepository, UserRepository},
|
ports::{ActivityPubRepository, OutboundFederationPort, ThoughtRepository, UserRepository},
|
||||||
value_objects::ThoughtId,
|
value_objects::ThoughtId,
|
||||||
};
|
};
|
||||||
@@ -18,11 +18,11 @@ pub struct FederationEventService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl FederationEventService {
|
impl FederationEventService {
|
||||||
fn object_ap_id(&self, thought: &Thought, thought_id: &ThoughtId) -> String {
|
async fn object_ap_id(&self, thought_id: &ThoughtId) -> Result<String, DomainError> {
|
||||||
thought
|
if let Some(ap_id) = self.ap_repo.get_thought_ap_id(thought_id).await? {
|
||||||
.ap_id
|
return Ok(ap_id);
|
||||||
.clone()
|
}
|
||||||
.unwrap_or_else(|| format!("{}/thoughts/{}", self.base_url, thought_id))
|
Ok(format!("{}/thoughts/{}", self.base_url, thought_id))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn process(&self, event: &DomainEvent) -> Result<(), DomainError> {
|
pub async fn process(&self, event: &DomainEvent) -> Result<(), DomainError> {
|
||||||
@@ -48,31 +48,24 @@ impl FederationEventService {
|
|||||||
Some(u) => u,
|
Some(u) => u,
|
||||||
None => return Ok(()),
|
None => return Ok(()),
|
||||||
};
|
};
|
||||||
// For replies to remote posts: in_reply_to_url is None but in_reply_to_id
|
// Resolve in_reply_to_url for the parent thought via AP repo.
|
||||||
// points to the locally-stored remote thought. Resolve its ap_id so the
|
let in_reply_to_url = if let Some(ref reply_id) = thought.in_reply_to_id {
|
||||||
// outbound Note includes inReplyTo and Mastodon threads it correctly.
|
let ap_id = self
|
||||||
let thought = if thought.in_reply_to_url.is_none() {
|
.ap_repo
|
||||||
if let Some(ref reply_id) = thought.in_reply_to_id {
|
.get_thought_ap_id(reply_id)
|
||||||
match self.thoughts.find_by_id(reply_id).await? {
|
.await?
|
||||||
Some(parent) => {
|
.unwrap_or_else(|| format!("{}/thoughts/{}", self.base_url, reply_id));
|
||||||
let parent_ap_url = parent.ap_id.unwrap_or_else(|| {
|
Some(ap_id)
|
||||||
format!("{}/thoughts/{}", self.base_url, reply_id)
|
|
||||||
});
|
|
||||||
domain::models::thought::Thought {
|
|
||||||
in_reply_to_url: Some(parent_ap_url),
|
|
||||||
..thought
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => thought,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
thought
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
thought
|
None
|
||||||
};
|
};
|
||||||
self.ap
|
self.ap
|
||||||
.broadcast_create(user_id, &thought, user.username.as_str())
|
.broadcast_create(
|
||||||
|
user_id,
|
||||||
|
&thought,
|
||||||
|
user.username.as_str(),
|
||||||
|
in_reply_to_url.as_deref(),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,8 +99,21 @@ impl FederationEventService {
|
|||||||
Some(u) => u,
|
Some(u) => u,
|
||||||
None => return Ok(()),
|
None => return Ok(()),
|
||||||
};
|
};
|
||||||
|
let in_reply_to_url = if let Some(ref reply_id) = thought.in_reply_to_id {
|
||||||
|
self.ap_repo
|
||||||
|
.get_thought_ap_id(reply_id)
|
||||||
|
.await?
|
||||||
|
.or_else(|| Some(format!("{}/thoughts/{}", self.base_url, reply_id)))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
self.ap
|
self.ap
|
||||||
.broadcast_update(user_id, &thought, user.username.as_str())
|
.broadcast_update(
|
||||||
|
user_id,
|
||||||
|
&thought,
|
||||||
|
user.username.as_str(),
|
||||||
|
in_reply_to_url.as_deref(),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,11 +128,10 @@ impl FederationEventService {
|
|||||||
_ => return Ok(()),
|
_ => return Ok(()),
|
||||||
};
|
};
|
||||||
let _ = booster;
|
let _ = booster;
|
||||||
let thought = match self.thoughts.find_by_id(thought_id).await? {
|
if self.thoughts.find_by_id(thought_id).await?.is_none() {
|
||||||
Some(t) => t,
|
return Ok(());
|
||||||
None => return Ok(()),
|
}
|
||||||
};
|
let object_ap_id = self.object_ap_id(thought_id).await?;
|
||||||
let object_ap_id = self.object_ap_id(&thought, thought_id);
|
|
||||||
self.ap.broadcast_announce(user_id, &object_ap_id).await
|
self.ap.broadcast_announce(user_id, &object_ap_id).await
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,11 +139,10 @@ impl FederationEventService {
|
|||||||
user_id,
|
user_id,
|
||||||
thought_id,
|
thought_id,
|
||||||
} => {
|
} => {
|
||||||
let thought = match self.thoughts.find_by_id(thought_id).await? {
|
if self.thoughts.find_by_id(thought_id).await?.is_none() {
|
||||||
Some(t) => t,
|
return Ok(());
|
||||||
None => return Ok(()),
|
}
|
||||||
};
|
let object_ap_id = self.object_ap_id(thought_id).await?;
|
||||||
let object_ap_id = self.object_ap_id(&thought, thought_id);
|
|
||||||
self.ap
|
self.ap
|
||||||
.broadcast_undo_announce(user_id, &object_ap_id)
|
.broadcast_undo_announce(user_id, &object_ap_id)
|
||||||
.await
|
.await
|
||||||
@@ -262,17 +266,19 @@ impl FederationEventService {
|
|||||||
};
|
};
|
||||||
let _ = liker;
|
let _ = liker;
|
||||||
let thought = match self.thoughts.find_by_id(thought_id).await? {
|
let thought = match self.thoughts.find_by_id(thought_id).await? {
|
||||||
Some(t) if t.ap_id.is_some() => t,
|
Some(t) => t,
|
||||||
_ => return Ok(()),
|
_ => return Ok(()),
|
||||||
};
|
};
|
||||||
let author = match self.users.find_by_id(&thought.user_id).await? {
|
let thought_ap_id = match self.ap_repo.get_thought_ap_id(thought_id).await? {
|
||||||
Some(u) if u.inbox_url.is_some() => u,
|
Some(id) => id,
|
||||||
_ => return Ok(()),
|
None => return Ok(()), // local thought — no federation needed
|
||||||
|
};
|
||||||
|
let actor_urls = match self.ap_repo.get_actor_ap_urls(&thought.user_id).await? {
|
||||||
|
Some(u) => u,
|
||||||
|
None => return Ok(()),
|
||||||
};
|
};
|
||||||
let object_ap_id = thought.ap_id.unwrap();
|
|
||||||
let inbox_url = author.inbox_url.unwrap();
|
|
||||||
self.ap
|
self.ap
|
||||||
.broadcast_like(user_id, &object_ap_id, &inbox_url)
|
.broadcast_like(user_id, &thought_ap_id, &actor_urls.inbox_url)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -286,17 +292,19 @@ impl FederationEventService {
|
|||||||
};
|
};
|
||||||
let _ = liker;
|
let _ = liker;
|
||||||
let thought = match self.thoughts.find_by_id(thought_id).await? {
|
let thought = match self.thoughts.find_by_id(thought_id).await? {
|
||||||
Some(t) if t.ap_id.is_some() => t,
|
Some(t) => t,
|
||||||
_ => return Ok(()),
|
_ => return Ok(()),
|
||||||
};
|
};
|
||||||
let author = match self.users.find_by_id(&thought.user_id).await? {
|
let thought_ap_id = match self.ap_repo.get_thought_ap_id(thought_id).await? {
|
||||||
Some(u) if u.inbox_url.is_some() => u,
|
Some(id) => id,
|
||||||
_ => return Ok(()),
|
None => return Ok(()),
|
||||||
|
};
|
||||||
|
let actor_urls = match self.ap_repo.get_actor_ap_urls(&thought.user_id).await? {
|
||||||
|
Some(u) => u,
|
||||||
|
None => return Ok(()),
|
||||||
};
|
};
|
||||||
let object_ap_id = thought.ap_id.unwrap();
|
|
||||||
let inbox_url = author.inbox_url.unwrap();
|
|
||||||
self.ap
|
self.ap
|
||||||
.broadcast_undo_like(user_id, &object_ap_id, &inbox_url)
|
.broadcast_undo_like(user_id, &thought_ap_id, &actor_urls.inbox_url)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -345,6 +353,7 @@ mod tests {
|
|||||||
_: &UserId,
|
_: &UserId,
|
||||||
thought: &Thought,
|
thought: &Thought,
|
||||||
_: &str,
|
_: &str,
|
||||||
|
_in_reply_to_url: Option<&str>,
|
||||||
) -> Result<(), DomainError> {
|
) -> Result<(), DomainError> {
|
||||||
self.created.lock().unwrap().push(thought.id.clone());
|
self.created.lock().unwrap().push(thought.id.clone());
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -358,6 +367,7 @@ mod tests {
|
|||||||
_: &UserId,
|
_: &UserId,
|
||||||
thought: &Thought,
|
thought: &Thought,
|
||||||
_: &str,
|
_: &str,
|
||||||
|
_in_reply_to_url: Option<&str>,
|
||||||
) -> Result<(), DomainError> {
|
) -> Result<(), DomainError> {
|
||||||
self.updated.lock().unwrap().push(thought.id.clone());
|
self.updated.lock().unwrap().push(thought.id.clone());
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -460,10 +470,9 @@ mod tests {
|
|||||||
async fn remote_thought_created_does_not_broadcast() {
|
async fn remote_thought_created_does_not_broadcast() {
|
||||||
let store = TestStore::default();
|
let store = TestStore::default();
|
||||||
let alice = alice();
|
let alice = alice();
|
||||||
// Remote thought: local = false, ap_id = Some(...)
|
// Remote thought: local = false
|
||||||
let mut thought = local_thought(alice.id.clone());
|
let mut thought = local_thought(alice.id.clone());
|
||||||
thought.local = false;
|
thought.local = false;
|
||||||
thought.ap_id = Some("https://remote.example/notes/1".into());
|
|
||||||
store.users.lock().unwrap().push(alice.clone());
|
store.users.lock().unwrap().push(alice.clone());
|
||||||
store.thoughts.lock().unwrap().push(thought.clone());
|
store.thoughts.lock().unwrap().push(thought.clone());
|
||||||
|
|
||||||
@@ -553,7 +562,10 @@ mod tests {
|
|||||||
let alice = alice();
|
let alice = alice();
|
||||||
let mut thought = local_thought(alice.id.clone());
|
let mut thought = local_thought(alice.id.clone());
|
||||||
thought.local = false;
|
thought.local = false;
|
||||||
thought.ap_id = Some("https://mastodon.social/users/bob/statuses/123".into());
|
store.thought_ap_ids.lock().unwrap().insert(
|
||||||
|
thought.id.clone(),
|
||||||
|
"https://mastodon.social/users/bob/statuses/123".into(),
|
||||||
|
);
|
||||||
store.users.lock().unwrap().push(alice.clone());
|
store.users.lock().unwrap().push(alice.clone());
|
||||||
store.thoughts.lock().unwrap().push(thought.clone());
|
store.thoughts.lock().unwrap().push(thought.clone());
|
||||||
|
|
||||||
@@ -702,7 +714,10 @@ mod tests {
|
|||||||
let alice = alice();
|
let alice = alice();
|
||||||
let mut thought = local_thought(alice.id.clone());
|
let mut thought = local_thought(alice.id.clone());
|
||||||
thought.local = false;
|
thought.local = false;
|
||||||
thought.ap_id = Some("https://mastodon.social/users/bob/statuses/456".into());
|
store.thought_ap_ids.lock().unwrap().insert(
|
||||||
|
thought.id.clone(),
|
||||||
|
"https://mastodon.social/users/bob/statuses/456".into(),
|
||||||
|
);
|
||||||
store.thoughts.lock().unwrap().push(thought.clone());
|
store.thoughts.lock().unwrap().push(thought.clone());
|
||||||
|
|
||||||
let spy = Arc::new(SpyPort::default());
|
let spy = Arc::new(SpyPort::default());
|
||||||
@@ -797,10 +812,19 @@ mod tests {
|
|||||||
PasswordHash("h".into()),
|
PasswordHash("h".into()),
|
||||||
);
|
);
|
||||||
author.local = false;
|
author.local = false;
|
||||||
author.inbox_url = Some("https://mastodon.social/users/author/inbox".into());
|
store.actor_ap_urls.lock().unwrap().insert(
|
||||||
|
author.id.clone(),
|
||||||
|
domain::ports::ActorApUrls {
|
||||||
|
ap_id: "https://mastodon.social/users/author".into(),
|
||||||
|
inbox_url: "https://mastodon.social/users/author/inbox".into(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
let mut thought = local_thought(author.id.clone());
|
let thought = local_thought(author.id.clone());
|
||||||
thought.ap_id = Some("https://mastodon.social/posts/123".into());
|
store.thought_ap_ids.lock().unwrap().insert(
|
||||||
|
thought.id.clone(),
|
||||||
|
"https://mastodon.social/posts/123".into(),
|
||||||
|
);
|
||||||
|
|
||||||
let liker = alice();
|
let liker = alice();
|
||||||
|
|
||||||
|
|||||||
@@ -15,8 +15,6 @@ pub struct Thought {
|
|||||||
pub user_id: UserId,
|
pub user_id: UserId,
|
||||||
pub content: Content,
|
pub content: Content,
|
||||||
pub in_reply_to_id: Option<ThoughtId>,
|
pub in_reply_to_id: Option<ThoughtId>,
|
||||||
pub in_reply_to_url: Option<String>,
|
|
||||||
pub ap_id: Option<String>,
|
|
||||||
pub visibility: Visibility,
|
pub visibility: Visibility,
|
||||||
pub content_warning: Option<String>,
|
pub content_warning: Option<String>,
|
||||||
pub sensitive: bool,
|
pub sensitive: bool,
|
||||||
@@ -60,8 +58,6 @@ impl Thought {
|
|||||||
user_id,
|
user_id,
|
||||||
content,
|
content,
|
||||||
in_reply_to_id,
|
in_reply_to_id,
|
||||||
in_reply_to_url: None,
|
|
||||||
ap_id: None,
|
|
||||||
visibility,
|
visibility,
|
||||||
content_warning,
|
content_warning,
|
||||||
sensitive,
|
sensitive,
|
||||||
|
|||||||
@@ -13,8 +13,6 @@ pub struct User {
|
|||||||
pub header_url: Option<String>,
|
pub header_url: Option<String>,
|
||||||
pub custom_css: Option<String>,
|
pub custom_css: Option<String>,
|
||||||
pub local: bool,
|
pub local: bool,
|
||||||
pub ap_id: Option<String>,
|
|
||||||
pub inbox_url: Option<String>,
|
|
||||||
pub created_at: DateTime<Utc>,
|
pub created_at: DateTime<Utc>,
|
||||||
pub updated_at: DateTime<Utc>,
|
pub updated_at: DateTime<Utc>,
|
||||||
}
|
}
|
||||||
@@ -38,8 +36,6 @@ impl User {
|
|||||||
header_url: None,
|
header_url: None,
|
||||||
custom_css: None,
|
custom_css: None,
|
||||||
local: true,
|
local: true,
|
||||||
ap_id: None,
|
|
||||||
inbox_url: None,
|
|
||||||
created_at: now,
|
created_at: now,
|
||||||
updated_at: now,
|
updated_at: now,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -442,6 +442,7 @@ pub trait OutboundFederationPort: Send + Sync {
|
|||||||
author_user_id: &UserId,
|
author_user_id: &UserId,
|
||||||
thought: &Thought,
|
thought: &Thought,
|
||||||
author_username: &str,
|
author_username: &str,
|
||||||
|
in_reply_to_url: Option<&str>,
|
||||||
) -> Result<(), DomainError>;
|
) -> Result<(), DomainError>;
|
||||||
|
|
||||||
/// Fan out a Delete tombstone for a now-deleted local Note.
|
/// Fan out a Delete tombstone for a now-deleted local Note.
|
||||||
@@ -459,6 +460,7 @@ pub trait OutboundFederationPort: Send + Sync {
|
|||||||
author_user_id: &UserId,
|
author_user_id: &UserId,
|
||||||
thought: &Thought,
|
thought: &Thought,
|
||||||
author_username: &str,
|
author_username: &str,
|
||||||
|
in_reply_to_url: Option<&str>,
|
||||||
) -> Result<(), DomainError>;
|
) -> Result<(), DomainError>;
|
||||||
|
|
||||||
/// Fan out an Announce(object_ap_id) for a boost.
|
/// Fan out an Announce(object_ap_id) for a boost.
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use url;
|
use url;
|
||||||
|
|
||||||
@@ -35,6 +36,12 @@ pub struct TestStore {
|
|||||||
pub top_friends: Arc<Mutex<Vec<TopFriend>>>,
|
pub top_friends: Arc<Mutex<Vec<TopFriend>>>,
|
||||||
pub notifications: Arc<Mutex<Vec<Notification>>>,
|
pub notifications: Arc<Mutex<Vec<Notification>>>,
|
||||||
pub events: Arc<Mutex<Vec<DomainEvent>>>,
|
pub events: Arc<Mutex<Vec<DomainEvent>>>,
|
||||||
|
/// AP URL → UserId for remote actors (used by find_remote_actor_id / intern_remote_actor)
|
||||||
|
pub actor_ap_ids: Arc<Mutex<HashMap<String, UserId>>>,
|
||||||
|
/// ThoughtId → AP object URL (used by get_thought_ap_id)
|
||||||
|
pub thought_ap_ids: Arc<Mutex<HashMap<ThoughtId, String>>>,
|
||||||
|
/// UserId → ActorApUrls (used by get_actor_ap_urls)
|
||||||
|
pub actor_ap_urls: Arc<Mutex<HashMap<UserId, ActorApUrls>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@@ -802,14 +809,12 @@ impl ActivityPubRepository for TestStore {
|
|||||||
&self,
|
&self,
|
||||||
actor_ap_url: &url::Url,
|
actor_ap_url: &url::Url,
|
||||||
) -> Result<Option<UserId>, DomainError> {
|
) -> Result<Option<UserId>, DomainError> {
|
||||||
let url = actor_ap_url.to_string();
|
|
||||||
Ok(self
|
Ok(self
|
||||||
.users
|
.actor_ap_ids
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.iter()
|
.get(actor_ap_url.as_str())
|
||||||
.find(|u| u.ap_id.as_deref() == Some(&url))
|
.cloned())
|
||||||
.map(|u| u.id.clone()))
|
|
||||||
}
|
}
|
||||||
async fn intern_remote_actor(&self, actor_ap_url: &url::Url) -> Result<UserId, DomainError> {
|
async fn intern_remote_actor(&self, actor_ap_url: &url::Url) -> Result<UserId, DomainError> {
|
||||||
if let Some(uid) = self.find_remote_actor_id(actor_ap_url).await? {
|
if let Some(uid) = self.find_remote_actor_id(actor_ap_url).await? {
|
||||||
@@ -831,12 +836,14 @@ impl ActivityPubRepository for TestStore {
|
|||||||
header_url: None,
|
header_url: None,
|
||||||
custom_css: None,
|
custom_css: None,
|
||||||
local: false,
|
local: false,
|
||||||
ap_id: Some(actor_ap_url.to_string()),
|
|
||||||
inbox_url: None,
|
|
||||||
created_at: chrono::Utc::now(),
|
created_at: chrono::Utc::now(),
|
||||||
updated_at: chrono::Utc::now(),
|
updated_at: chrono::Utc::now(),
|
||||||
};
|
};
|
||||||
self.users.lock().unwrap().push(user);
|
self.users.lock().unwrap().push(user);
|
||||||
|
self.actor_ap_ids
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.insert(actor_ap_url.to_string(), uid.clone());
|
||||||
Ok(uid)
|
Ok(uid)
|
||||||
}
|
}
|
||||||
async fn update_remote_actor_display(
|
async fn update_remote_actor_display(
|
||||||
@@ -884,15 +891,15 @@ impl ActivityPubRepository for TestStore {
|
|||||||
}
|
}
|
||||||
async fn get_thought_ap_id(
|
async fn get_thought_ap_id(
|
||||||
&self,
|
&self,
|
||||||
_thought_id: &ThoughtId,
|
thought_id: &ThoughtId,
|
||||||
) -> Result<Option<String>, DomainError> {
|
) -> Result<Option<String>, DomainError> {
|
||||||
Ok(None)
|
Ok(self.thought_ap_ids.lock().unwrap().get(thought_id).cloned())
|
||||||
}
|
}
|
||||||
async fn get_actor_ap_urls(
|
async fn get_actor_ap_urls(
|
||||||
&self,
|
&self,
|
||||||
_user_id: &UserId,
|
user_id: &UserId,
|
||||||
) -> Result<Option<ActorApUrls>, DomainError> {
|
) -> Result<Option<ActorApUrls>, DomainError> {
|
||||||
Ok(None)
|
Ok(self.actor_ap_urls.lock().unwrap().get(user_id).cloned())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ pub fn to_thought_response(e: &domain::models::feed::FeedEntry) -> ThoughtRespon
|
|||||||
content: e.thought.content.as_str().to_string(),
|
content: e.thought.content.as_str().to_string(),
|
||||||
author: to_user_response(&e.author),
|
author: to_user_response(&e.author),
|
||||||
in_reply_to_id: e.thought.in_reply_to_id.as_ref().map(|id| id.as_uuid()),
|
in_reply_to_id: e.thought.in_reply_to_id.as_ref().map(|id| id.as_uuid()),
|
||||||
in_reply_to_url: e.thought.in_reply_to_url.clone(),
|
in_reply_to_url: None,
|
||||||
visibility: e.thought.visibility.as_str().to_string(),
|
visibility: e.thought.visibility.as_str().to_string(),
|
||||||
content_warning: e.thought.content_warning.clone(),
|
content_warning: e.thought.content_warning.clone(),
|
||||||
sensitive: e.thought.sensitive,
|
sensitive: e.thought.sensitive,
|
||||||
|
|||||||
Reference in New Issue
Block a user