fix: content negotiation for followers/following — resolve AP router conflict
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 9m28s
test / unit (pull_request) Failing after 11m39s
test / integration (pull_request) Failing after 17m5s

This commit is contained in:
2026-05-14 21:42:38 +02:00
parent fc3b4146a5
commit 908789e639
5 changed files with 185 additions and 21 deletions

View File

@@ -1415,6 +1415,102 @@ impl domain::ports::FederationActionPort for ActivityPubService {
.await
.map_err(|e| domain::errors::DomainError::ExternalService(e.to_string()))
}
async fn followers_collection_json(
&self,
user_id: &domain::value_objects::UserId,
page: Option<u32>,
) -> Result<String, domain::errors::DomainError> {
let data = self.federation_config.to_request_data();
let uuid = user_id.as_uuid();
let collection_id = format!("{}/users/{}/followers", self.base_url, uuid);
let total = data
.federation_repo
.count_followers(uuid)
.await
.map_err(|e| domain::errors::DomainError::ExternalService(e.to_string()))?;
let obj = if let Some(p) = page {
let p = p.max(1);
let offset = (p.saturating_sub(1) as usize) * 20;
let followers = data
.federation_repo
.get_followers_page(uuid, offset as u32, 20)
.await
.map_err(|e| domain::errors::DomainError::ExternalService(e.to_string()))?;
let has_next = offset + followers.len() < total;
let items: Vec<String> = followers.into_iter().map(|f| f.actor.url).collect();
let mut obj = serde_json::json!({
"@context": "https://www.w3.org/ns/activitystreams",
"type": "OrderedCollectionPage",
"id": format!("{}?page={}", collection_id, p),
"partOf": collection_id,
"totalItems": total,
"orderedItems": items,
});
if has_next {
obj["next"] = serde_json::json!(format!("{}?page={}", collection_id, p + 1));
}
obj
} else {
serde_json::json!({
"@context": "https://www.w3.org/ns/activitystreams",
"type": "OrderedCollection",
"id": collection_id,
"totalItems": total,
"first": format!("{}?page=1", collection_id),
})
};
serde_json::to_string(&obj)
.map_err(|e| domain::errors::DomainError::ExternalService(e.to_string()))
}
async fn following_collection_json(
&self,
user_id: &domain::value_objects::UserId,
page: Option<u32>,
) -> Result<String, domain::errors::DomainError> {
let data = self.federation_config.to_request_data();
let uuid = user_id.as_uuid();
let collection_id = format!("{}/users/{}/following", self.base_url, uuid);
let total = data
.federation_repo
.count_following(uuid)
.await
.map_err(|e| domain::errors::DomainError::ExternalService(e.to_string()))?;
let obj = if let Some(p) = page {
let p = p.max(1);
let offset = (p.saturating_sub(1) as usize) * 20;
let following = data
.federation_repo
.get_following_page(uuid, offset as u32, 20)
.await
.map_err(|e| domain::errors::DomainError::ExternalService(e.to_string()))?;
let has_next = offset + following.len() < total;
let items: Vec<String> = following.into_iter().map(|a| a.url).collect();
let mut obj = serde_json::json!({
"@context": "https://www.w3.org/ns/activitystreams",
"type": "OrderedCollectionPage",
"id": format!("{}?page={}", collection_id, p),
"partOf": collection_id,
"totalItems": total,
"orderedItems": items,
});
if has_next {
obj["next"] = serde_json::json!(format!("{}?page={}", collection_id, p + 1));
}
obj
} else {
serde_json::json!({
"@context": "https://www.w3.org/ns/activitystreams",
"type": "OrderedCollection",
"id": collection_id,
"totalItems": total,
"first": format!("{}?page=1", collection_id),
})
};
serde_json::to_string(&obj)
.map_err(|e| domain::errors::DomainError::ExternalService(e.to_string()))
}
}
#[cfg(test)]