From e37b10ad43c112d7fe303ce10512900106dc8158 Mon Sep 17 00:00:00 2001 From: Timothy Quilling Date: Wed, 17 Dec 2025 17:03:14 -0500 Subject: [PATCH] fix: cursors, list uris --- parakeet/src/entities/core/list.rs | 13 +++++--- parakeet/src/entities/core/post.rs | 15 ++------- parakeet/src/xrpc/app_bsky/feed/feedgen.rs | 2 +- .../src/xrpc/app_bsky/feed/get_timeline.rs | 4 +-- parakeet/src/xrpc/app_bsky/graph/lists.rs | 31 +++++++++++++++---- parakeet/src/xrpc/app_bsky/mod.rs | 2 +- 6 files changed, 39 insertions(+), 28 deletions(-) diff --git a/parakeet/src/entities/core/list.rs b/parakeet/src/entities/core/list.rs index 7a363a11..bd4f7c38 100644 --- a/parakeet/src/entities/core/list.rs +++ b/parakeet/src/entities/core/list.rs @@ -419,7 +419,7 @@ impl ListEntity { list_rkey: &str, cursor: Option<&chrono::DateTime>, limit: u8, - ) -> eyre::Result)>> { + ) -> eyre::Result)>> { let mut conn = self.db_pool.get().await?; use diesel::sql_types::{BigInt, Integer, Text}; @@ -428,17 +428,20 @@ impl ListEntity { #[derive(diesel::QueryableByName)] struct ItemRow { #[diesel(sql_type = Integer)] - subject_actor_id: i32, + actor_id: i32, #[diesel(sql_type = BigInt)] rkey: i64, + #[diesel(sql_type = Integer)] + subject_actor_id: i32, } // Simple query - just get the data we need let results: Vec = diesel::sql_query( r#" SELECT - subject_actor_id, - rkey + actor_id, + rkey, + subject_actor_id FROM list_items WHERE list_owner_actor_id = $1 AND list_rkey = $2 @@ -466,7 +469,7 @@ impl ListEntity { } } - processed.push((row.subject_actor_id, created_at)); + processed.push((row.actor_id, row.subject_actor_id, row.rkey, created_at)); if processed.len() >= limit as usize { break; diff --git a/parakeet/src/entities/core/post.rs b/parakeet/src/entities/core/post.rs index 8cb77caf..a7043f54 100644 --- a/parakeet/src/entities/core/post.rs +++ b/parakeet/src/entities/core/post.rs @@ -480,21 +480,10 @@ impl PostEntity { // Extract text from content if available let text = if let Some(ref content_bytes) = data.post.content { - // Content is zstd compressed + // Content is zstd compressed plain text // Create a codec and decompress the content let codec = parakeet_db::compression::PostContentCodec::new(); - if let Ok(decompressed_str) = codec.decompress(content_bytes.as_slice()) { - if let Ok(record) = serde_json::from_str::(&decompressed_str) { - record.get("text") - .and_then(|t| t.as_str()) - .map(|s| s.to_string()) - .unwrap_or_default() - } else { - String::new() - } - } else { - String::new() - } + codec.decompress(content_bytes.as_slice()).unwrap_or_default() } else { String::new() }; diff --git a/parakeet/src/xrpc/app_bsky/feed/feedgen.rs b/parakeet/src/xrpc/app_bsky/feed/feedgen.rs index 3808d88c..ceb2f87f 100644 --- a/parakeet/src/xrpc/app_bsky/feed/feedgen.rs +++ b/parakeet/src/xrpc/app_bsky/feed/feedgen.rs @@ -144,7 +144,7 @@ pub async fn get_feed_generators( State(state): State, AtpAcceptLabelers(_labelers): AtpAcceptLabelers, maybe_auth: Option, - Query(query): Query, + axum_extra::extract::Query(query): axum_extra::extract::Query, ) -> XrpcResult> { // Get viewer DID if authenticated let viewer_did = maybe_auth.as_ref().map(|auth| auth.0.clone()); diff --git a/parakeet/src/xrpc/app_bsky/feed/get_timeline.rs b/parakeet/src/xrpc/app_bsky/feed/get_timeline.rs index 2016320d..3bcfb2cd 100644 --- a/parakeet/src/xrpc/app_bsky/feed/get_timeline.rs +++ b/parakeet/src/xrpc/app_bsky/feed/get_timeline.rs @@ -116,7 +116,7 @@ pub async fn get_timeline( let cursor = if has_next && timeline_items.len() > limit as usize { let last_rkey = timeline_items[limit as usize - 1].0; let timestamp = parakeet_db::tid_util::tid_to_datetime(last_rkey); - Some(timestamp.timestamp_millis().to_string()) + Some(timestamp.to_rfc3339()) } else { None }; @@ -263,7 +263,7 @@ pub async fn get_author_feed( let cursor = if has_next && posts_to_return.len() == limit as usize { let last_rkey = posts_to_return[posts_to_return.len() - 1].1; let timestamp = parakeet_db::tid_util::tid_to_datetime(last_rkey); - Some(timestamp.timestamp_millis().to_string()) + Some(timestamp.to_rfc3339()) } else { None }; diff --git a/parakeet/src/xrpc/app_bsky/graph/lists.rs b/parakeet/src/xrpc/app_bsky/graph/lists.rs index 24a67e1f..faa7d2b6 100644 --- a/parakeet/src/xrpc/app_bsky/graph/lists.rs +++ b/parakeet/src/xrpc/app_bsky/graph/lists.rs @@ -128,17 +128,31 @@ pub async fn get_list( // Calculate next cursor let cursor = item_results .last() - .map(|item| item.1.to_rfc3339()); + .map(|item| item.3.to_rfc3339()); // Get profiles for all subject actor IDs let subject_ids: Vec = item_results .iter() - .map(|item| item.0) + .map(|item| item.1) // subject_actor_id + .collect(); + + // Also get DIDs for list item actors + let item_actor_ids: Vec = item_results + .iter() + .map(|item| item.0) // actor_id .collect(); // Get the actual actor profiles let profiles = state.profile_entity.get_profile_views(&subject_ids).await; + // Get DIDs for list item actors + let mut item_actor_dids = std::collections::HashMap::new(); + for actor_id in &item_actor_ids { + if let Ok(did) = state.profile_entity.get_did_by_id(*actor_id).await { + item_actor_dids.insert(*actor_id, did); + } + } + // Map actor_id to ProfileView let mut actor_views = std::collections::HashMap::new(); for (idx, profile) in profiles.into_iter().enumerate() { @@ -150,11 +164,16 @@ pub async fn get_list( // Build ListItemViews let items: Vec = item_results .into_iter() - .filter_map(|(subject_actor_id, _created_at)| { + .filter_map(|(actor_id, subject_actor_id, rkey, _created_at)| { let subject = actor_views.get(&subject_actor_id).cloned()?; - - // TODO: Construct proper item URI - let item_uri = format!("at://unknown/app.bsky.graph.listitem/unknown"); + let item_did = item_actor_dids.get(&actor_id)?; + + // Construct proper item URI + let item_uri = format!( + "at://{}/app.bsky.graph.listitem/{}", + item_did, + parakeet_db::tid_util::encode_tid(rkey) + ); Some(ListItemView { uri: item_uri, diff --git a/parakeet/src/xrpc/app_bsky/mod.rs b/parakeet/src/xrpc/app_bsky/mod.rs index 7ad97b4e..43474413 100644 --- a/parakeet/src/xrpc/app_bsky/mod.rs +++ b/parakeet/src/xrpc/app_bsky/mod.rs @@ -57,7 +57,7 @@ pub fn routes() -> Router { .route("/app.bsky.bookmark.getBookmarks", get(bookmark::get_bookmarks)) .route("/app.bsky.feed.getActorFeeds", get(feed::feedgen::get_actor_feeds)) .route("/app.bsky.feed.getActorLikes", get(feed::likes::get_actor_likes)) - .route("/app.bsky.feed.getAuthorFeed", get(feed::posts::get_author_feed)) + .route("/app.bsky.feed.getAuthorFeed", get(feed::get_timeline::get_author_feed)) .route("/app.bsky.feed.getFeed", get(feed::posts::get_feed)) .route("/app.bsky.feed.getFeedGenerator", get(feed::feedgen::get_feed_generator)) .route("/app.bsky.feed.getFeedGenerators", get(feed::feedgen::get_feed_generators)) -- 2.51.2