diff --git a/crates/adapters/activitypub-base/src/service.rs b/crates/adapters/activitypub-base/src/service.rs index 73a4c5d..c88aa1f 100644 --- a/crates/adapters/activitypub-base/src/service.rs +++ b/crates/adapters/activitypub-base/src/service.rs @@ -1528,8 +1528,26 @@ impl domain::ports::FederationActionPort for ActivityPubService { ) -> Result, domain::errors::DomainError> { use chrono::DateTime; - let url = format!("{}?page={}", outbox_url, page); - let resp: serde_json::Value = reqwest::Client::new() + // Fetch the base outbox to find the real first-page URL. + // Mastodon uses ?page=true; other servers may use ?page=1 or a different param. + let client = reqwest::Client::new(); + let base: serde_json::Value = client + .get(outbox_url) + .header("Accept", "application/activity+json, application/ld+json") + .send() + .await + .map_err(|e| domain::errors::DomainError::ExternalService(e.to_string()))? + .json() + .await + .map_err(|e| domain::errors::DomainError::ExternalService(e.to_string()))?; + + // Prefer the `first` link from the OrderedCollection; fall back to ?page=1. + let url = base["first"] + .as_str() + .map(|s| s.to_string()) + .unwrap_or_else(|| format!("{}?page={}", outbox_url, page)); + + let resp: serde_json::Value = client .get(&url) .header("Accept", "application/activity+json, application/ld+json") .send() diff --git a/crates/adapters/postgres/src/activitypub.rs b/crates/adapters/postgres/src/activitypub.rs index f0b3688..664d034 100644 --- a/crates/adapters/postgres/src/activitypub.rs +++ b/crates/adapters/postgres/src/activitypub.rs @@ -154,10 +154,16 @@ impl ActivityPubRepository for PgActivityPubRepository { return Ok(id); } let new_id = uuid::Uuid::new_v4(); - let handle = actor_ap_url + let raw = actor_ap_url .path() .trim_start_matches('/') .replace('/', "_"); + // username column is VARCHAR(32); truncate long paths (e.g. UUID-based actor URLs) + let handle = if raw.len() <= 32 { + raw + } else { + format!("remote_{}", &new_id.to_string()[..13]) + }; sqlx::query( "INSERT INTO users(id,username,email,password_hash,local,ap_id,created_at,updated_at) VALUES($1,$2,$3,'',false,$4,NOW(),NOW()) ON CONFLICT(ap_id) DO NOTHING",