tombstone gate all user paths; cleanup deletes late follower rows; spa self-delete to /; adr 0016 + purge comment

This commit is contained in:
2026-10-26 03:22:13 +01:00
parent caab63596a
commit b9cdb54340
7 changed files with 63 additions and 10 deletions

View File

@@ -27,10 +27,12 @@ pub async fn tombstone_gate(
next.run(request).await
}
/// The user id in `/users/{id}/outbox|featured|inbox`.
/// The user id in any `/users/{id}/...` path (outbox, inbox, collections,
/// content objects such as `/users/{id}/reviews/{rid}`). The bare actor path
/// has its own tombstone response.
pub fn gated_user(path: &str) -> Option<uuid::Uuid> {
let rest = path.strip_prefix("/users/")?;
let (id, endpoint) = rest.split_once('/')?;
matches!(endpoint, "outbox" | "featured" | "inbox").then_some(())?;
(!endpoint.is_empty()).then_some(())?;
uuid::Uuid::parse_str(id).ok()
}

View File

@@ -46,14 +46,22 @@ async fn an_unknown_user_is_none() {
}
#[test]
fn only_the_per_user_endpoints_are_gated() {
fn every_per_user_path_is_gated() {
use activitypub::tombstone_gate::gated_user;
let id = uuid::Uuid::new_v4();
for endpoint in ["outbox", "featured", "inbox"] {
let rid = uuid::Uuid::new_v4();
for endpoint in [
"outbox",
"featured",
"inbox",
"followers",
"following",
&format!("reviews/{rid}"),
] {
assert_eq!(gated_user(&format!("/users/{id}/{endpoint}")), Some(id));
}
assert_eq!(gated_user(&format!("/users/{id}")), None);
assert_eq!(gated_user(&format!("/users/{id}/followers")), None);
assert_eq!(gated_user(&format!("/users/{id}/")), None);
assert_eq!(gated_user("/inbox"), None);
assert_eq!(gated_user("/users/not-a-uuid/outbox"), None);
}