fix: remote actor display names in thought cards — use last URL segment as username, resolve display_name after intern
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 9m13s
test / unit (pull_request) Successful in 15m56s
test / integration (pull_request) Failing after 17m29s
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 9m13s
test / unit (pull_request) Successful in 15m56s
test / integration (pull_request) Failing after 17m29s
This commit is contained in:
@@ -154,13 +154,18 @@ impl ActivityPubRepository for PgActivityPubRepository {
|
|||||||
return Ok(id);
|
return Ok(id);
|
||||||
}
|
}
|
||||||
let new_id = uuid::Uuid::new_v4();
|
let new_id = uuid::Uuid::new_v4();
|
||||||
let raw = actor_ap_url
|
// Use the last path segment as username (e.g. /users/alice → "alice").
|
||||||
.path()
|
// Falls back to a random short id for long segments (e.g. UUID-based actor URLs).
|
||||||
.trim_start_matches('/')
|
// username column is VARCHAR(32).
|
||||||
.replace('/', "_");
|
let last_seg = actor_ap_url
|
||||||
// username column is VARCHAR(32); truncate long paths (e.g. UUID-based actor URLs)
|
.path_segments()
|
||||||
let handle = if raw.len() <= 32 {
|
.and_then(|mut s| s.next_back())
|
||||||
raw
|
.unwrap_or("")
|
||||||
|
.to_string();
|
||||||
|
let handle = if last_seg.is_empty() {
|
||||||
|
format!("remote_{}", &new_id.to_string()[..13])
|
||||||
|
} else if last_seg.len() <= 32 {
|
||||||
|
last_seg
|
||||||
} else {
|
} else {
|
||||||
format!("remote_{}", &new_id.to_string()[..13])
|
format!("remote_{}", &new_id.to_string()[..13])
|
||||||
};
|
};
|
||||||
@@ -185,6 +190,25 @@ impl ActivityPubRepository for PgActivityPubRepository {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn update_remote_actor_display(
|
||||||
|
&self,
|
||||||
|
user_id: &UserId,
|
||||||
|
display_name: Option<&str>,
|
||||||
|
avatar_url: Option<&str>,
|
||||||
|
) -> Result<(), DomainError> {
|
||||||
|
sqlx::query(
|
||||||
|
"UPDATE users SET display_name=$1, avatar_url=$2, updated_at=NOW()
|
||||||
|
WHERE id=$3 AND local=false",
|
||||||
|
)
|
||||||
|
.bind(display_name)
|
||||||
|
.bind(avatar_url)
|
||||||
|
.bind(user_id.as_uuid())
|
||||||
|
.execute(&self.pool)
|
||||||
|
.await
|
||||||
|
.map_err(|e| DomainError::Internal(e.to_string()))
|
||||||
|
.map(|_| ())
|
||||||
|
}
|
||||||
|
|
||||||
async fn accept_note(
|
async fn accept_note(
|
||||||
&self,
|
&self,
|
||||||
ap_id: &Url,
|
ap_id: &Url,
|
||||||
|
|||||||
@@ -136,6 +136,22 @@ impl FederationEventService {
|
|||||||
|
|
||||||
let author_id = self.ap_repo.intern_remote_actor(&actor_url).await?;
|
let author_id = self.ap_repo.intern_remote_actor(&actor_url).await?;
|
||||||
|
|
||||||
|
// Resolve and cache display info so thought cards show proper names.
|
||||||
|
let profiles = self
|
||||||
|
.federation_action
|
||||||
|
.resolve_actor_profiles(vec![actor_ap_url.clone()])
|
||||||
|
.await;
|
||||||
|
if let Some(profile) = profiles.into_iter().next() {
|
||||||
|
let _ = self
|
||||||
|
.ap_repo
|
||||||
|
.update_remote_actor_display(
|
||||||
|
&author_id,
|
||||||
|
profile.display_name.as_deref(),
|
||||||
|
profile.avatar_url.as_deref(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
for note in notes {
|
for note in notes {
|
||||||
let ap_id = match url::Url::parse(¬e.ap_id) {
|
let ap_id = match url::Url::parse(¬e.ap_id) {
|
||||||
Ok(u) => u,
|
Ok(u) => u,
|
||||||
|
|||||||
@@ -342,6 +342,14 @@ pub trait ActivityPubRepository: Send + Sync {
|
|||||||
/// Idempotent — safe to call multiple times with the same URL.
|
/// Idempotent — safe to call multiple times with the same URL.
|
||||||
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>;
|
||||||
|
|
||||||
|
/// Update display_name and avatar_url for an already-interned remote actor.
|
||||||
|
async fn update_remote_actor_display(
|
||||||
|
&self,
|
||||||
|
user_id: &UserId,
|
||||||
|
display_name: Option<&str>,
|
||||||
|
avatar_url: Option<&str>,
|
||||||
|
) -> Result<(), DomainError>;
|
||||||
|
|
||||||
// ── Inbox processing (remote → local) ───────────────────────────
|
// ── Inbox processing (remote → local) ───────────────────────────
|
||||||
|
|
||||||
/// Persist an incoming remote Note. Idempotent on ap_id.
|
/// Persist an incoming remote Note. Idempotent on ap_id.
|
||||||
|
|||||||
@@ -779,6 +779,14 @@ impl ActivityPubRepository for TestStore {
|
|||||||
self.users.lock().unwrap().push(user);
|
self.users.lock().unwrap().push(user);
|
||||||
Ok(uid)
|
Ok(uid)
|
||||||
}
|
}
|
||||||
|
async fn update_remote_actor_display(
|
||||||
|
&self,
|
||||||
|
_user_id: &UserId,
|
||||||
|
_display_name: Option<&str>,
|
||||||
|
_avatar_url: Option<&str>,
|
||||||
|
) -> Result<(), DomainError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
async fn accept_note(
|
async fn accept_note(
|
||||||
&self,
|
&self,
|
||||||
_ap_id: &url::Url,
|
_ap_id: &url::Url,
|
||||||
|
|||||||
Reference in New Issue
Block a user