fix(federation): fix 27 AP bugs, gaps, and inconsistencies
Round 1 — 18 bug fixes:
- remote likes/boosts now persist in engagement tables
- intern_remote_actor uses name@domain, expanded username to VARCHAR(255)
- PgRemoteActorRepository upsert/find now handles all fields
- update_following_status no longer a no-op, count_followers counts all
- accept/reject follow publishes event before DB mark (atomicity)
- fetch_outbox_page follows pagination via next links
- actor URL canonicalized to /users/{uuid}
- content_to_html escapes single quotes
- WebFinger accepts application/ld+json type
- try_from_ap accepts Article and Page object types
- feed SQL uses parameterized viewer UUID instead of format!
- content cap raised from 500 to 5000 chars
- also_known_as changed from Option<String> to Vec<String>
- connections fetch always triggers from page 1
Round 2 — 9 gap fixes:
- on_announce_removed handler deletes boost row on Undo(Announce)
- on_update handles Person/Service/Group actor profile updates
- sync_remote_actor_to_user syncs remote_actors → users on create/update
- FederationBlockPort: block_by_username sends Block activity to remote
- domain RemoteActor gains inbox_url, shared_inbox_url fields
- remote_actors attachment column (JSONB) with read/write
- .well-known/host-meta endpoint
- 256KB body limit on AP inbox routes
- outbox cleanup job (7-day retention, hourly sweep)
This commit is contained in:
@@ -9,3 +9,4 @@ pub mod notifications;
|
||||
pub mod social;
|
||||
pub mod thoughts;
|
||||
pub mod users;
|
||||
pub mod well_known;
|
||||
|
||||
@@ -119,7 +119,15 @@ pub async fn post_block(
|
||||
AuthUser(uid): AuthUser,
|
||||
Path(username): Path<String>,
|
||||
) -> Result<StatusCode, ApiError> {
|
||||
block_by_username(&*d.blocks, &*d.users, &*d.events, &uid, &username).await?;
|
||||
block_by_username(
|
||||
&*d.blocks,
|
||||
&*d.users,
|
||||
&*d.federation,
|
||||
&*d.events,
|
||||
&uid,
|
||||
&username,
|
||||
)
|
||||
.await?;
|
||||
Ok(StatusCode::NO_CONTENT)
|
||||
}
|
||||
#[utoipa::path(delete, path = "/users/{username}/block", params(("username" = String, Path, description = "Username")), responses((status = 204, description = "Unblocked")), security(("bearer_auth" = [])))]
|
||||
@@ -128,7 +136,15 @@ pub async fn delete_block(
|
||||
AuthUser(uid): AuthUser,
|
||||
Path(username): Path<String>,
|
||||
) -> Result<StatusCode, ApiError> {
|
||||
unblock_by_username(&*d.blocks, &*d.users, &*d.events, &uid, &username).await?;
|
||||
unblock_by_username(
|
||||
&*d.blocks,
|
||||
&*d.users,
|
||||
&*d.federation,
|
||||
&*d.events,
|
||||
&uid,
|
||||
&username,
|
||||
)
|
||||
.await?;
|
||||
Ok(StatusCode::NO_CONTENT)
|
||||
}
|
||||
#[utoipa::path(put, path = "/users/me/top-friends", request_body = SetTopFriendsRequest, responses((status = 204, description = "Top friends updated")), security(("bearer_auth" = [])))]
|
||||
|
||||
20
crates/presentation/src/handlers/well_known.rs
Normal file
20
crates/presentation/src/handlers/well_known.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
use axum::{extract::State, http::header, response::IntoResponse};
|
||||
|
||||
use crate::state::AppState;
|
||||
|
||||
pub async fn host_meta(State(state): State<AppState>) -> impl IntoResponse {
|
||||
let domain = url::Url::parse(&state.base_url)
|
||||
.ok()
|
||||
.and_then(|u| u.host_str().map(|s| s.to_string()))
|
||||
.unwrap_or_default();
|
||||
let body = format!(
|
||||
r#"<?xml version="1.0" encoding="UTF-8"?>
|
||||
<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
|
||||
<Link rel="lrdd" template="https://{domain}/.well-known/webfinger?resource={{uri}}"/>
|
||||
</XRD>"#
|
||||
);
|
||||
(
|
||||
[(header::CONTENT_TYPE, "application/xrd+xml; charset=utf-8")],
|
||||
body,
|
||||
)
|
||||
}
|
||||
@@ -135,5 +135,7 @@ pub fn router() -> Router<AppState> {
|
||||
)
|
||||
.route("/api-keys/{id}", delete(api_keys::delete_api_key_handler));
|
||||
|
||||
openapi::serve(api_routes).route("/media/{*path}", get(media::get_media))
|
||||
openapi::serve(api_routes)
|
||||
.route("/media/{*path}", get(media::get_media))
|
||||
.route("/.well-known/host-meta", get(well_known::host_meta))
|
||||
}
|
||||
|
||||
@@ -97,6 +97,9 @@ impl ActivityPubRepository for NoOpApRepo {
|
||||
) -> Result<Option<ActorApUrls>, DomainError> {
|
||||
Ok(None)
|
||||
}
|
||||
async fn sync_remote_actor_to_user(&self, _actor_ap_url: &str) -> Result<(), DomainError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NoOpMediaStore;
|
||||
|
||||
Reference in New Issue
Block a user