Parakeet is a Rust-based Bluesky AppServer aiming to implement most of the functionality required to support the Bluesky client
appview atproto bluesky rust appserver
67
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat!: add uri and cid fields to app.bsky.actor.defs#statusView

Mia 447ed52b 2632a569

+19 -6
+3
crates/consumer/src/db/record.rs
··· 699 699 pub async fn status_upsert<C: GenericClient>( 700 700 conn: &mut C, 701 701 repo: &str, 702 + cid: Cid, 702 703 rec: AppBskyActorStatus, 703 704 ) -> PgExecResult { 705 + let cid = cid.to_string(); 704 706 let record = serde_json::to_value(&rec).unwrap(); 705 707 let thumb = rec.embed.as_ref().and_then(|v| v.external.thumb.clone()); 706 708 let thumb_mime = thumb.as_ref().map(|v| v.mime_type.clone()); ··· 710 712 include_str!("sql/status_upsert.sql"), 711 713 &[ 712 714 &repo, 715 + &cid, 713 716 &rec.status.to_string(), 714 717 &rec.duration_minutes, 715 718 &record,
+5 -4
crates/consumer/src/db/sql/status_upsert.sql
··· 1 - INSERT INTO statuses (did, status, duration, record, embed_uri, embed_title, embed_description, thumb_mime_type, 1 + INSERT INTO statuses (did, cid, status, duration, record, embed_uri, embed_title, embed_description, thumb_mime_type, 2 2 thumb_cid, created_at) 3 - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) 4 - ON CONFLICT (did) DO UPDATE SET status=EXCLUDED.status, 3 + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) 4 + ON CONFLICT (did) DO UPDATE SET cid=EXCLUDED.cid, 5 + status=EXCLUDED.status, 5 6 duration=EXCLUDED.duration, 6 7 record=EXCLUDED.record, 7 8 embed_uri=EXCLUDED.embed_uri, ··· 9 10 embed_description=EXCLUDED.embed_description, 10 11 thumb_mime_type=EXCLUDED.thumb_mime_type, 11 12 thumb_cid=EXCLUDED.thumb_cid, 12 - indexed_at=NOW() 13 + indexed_at=NOW()
+1 -1
crates/consumer/src/indexer/mod.rs
··· 561 561 } 562 562 RecordTypes::AppBskyActorStatus(record) => { 563 563 if rkey == "self" { 564 - db::status_upsert(conn, repo, record).await?; 564 + db::status_upsert(conn, repo, cid, record).await?; 565 565 redis::AsyncTypedCommands::del(rc, format!("profile#{repo}")).await?; 566 566 } 567 567 }
+2
crates/lexica/src/app_bsky/actor.rs
··· 270 270 #[derive(Clone, Debug, Serialize)] 271 271 #[serde(rename_all = "camelCase")] 272 272 pub struct StatusView { 273 + pub uri: String, 274 + pub cid: String, 273 275 pub status: Status, 274 276 pub record: serde_json::Value, 275 277 #[serde(skip_serializing_if = "Option::is_none")]
+2
crates/parakeet-db/src/models.rs
··· 361 361 #[diesel(check_for_backend(diesel::pg::Pg))] 362 362 pub struct Status { 363 363 pub did: String, 364 + pub cid: String, 365 + 364 366 pub status: String, 365 367 pub duration: Option<i32>, 366 368
+1
crates/parakeet-db/src/schema.rs
··· 400 400 thumb_cid -> Nullable<Text>, 401 401 created_at -> Timestamptz, 402 402 indexed_at -> Timestamp, 403 + cid -> Text, 403 404 } 404 405 } 405 406
+3 -1
crates/parakeet/src/hydration/profile.rs
··· 172 172 173 173 let expires_at = status 174 174 .duration 175 - .map(|v| TimeDelta::seconds(v as i64)) 175 + .map(|v| TimeDelta::minutes(v as i64)) 176 176 .map(|v| status.created_at + v); 177 177 let is_active = expires_at.map(|v| Utc::now() < v); 178 178 179 179 Some(StatusView { 180 + uri: format!("at://{}/app.bsky.actor.status/self", &status.did), 181 + cid: status.cid, 180 182 status: s, 181 183 record: status.record, 182 184 embed,
+1
migrations/2026-04-27-202647-0000_status-cid/down.sql
··· 1 + alter table statuses drop column cid;
+1
migrations/2026-04-27-202647-0000_status-cid/up.sql
··· 1 + alter table statuses add column cid text not null;