tangled
alpha
login
or
join now
parakeet.at
/
parakeet
62
fork
atom
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
62
fork
atom
overview
issues
12
pulls
pipelines
feat: via for likes and reposts
mia.omg.lol
9 months ago
b15d187d
50807a71
+43
-12
8 changed files
expand all
collapse all
unified
split
consumer
src
backfill
mod.rs
repo.rs
db
copy.rs
record.rs
indexer
records.rs
migrations
2025-06-18-200415_like-repost-via
down.sql
up.sql
parakeet-db
src
schema.rs
+2
-2
consumer/src/backfill/mod.rs
···
336
337
#[derive(Debug, Default)]
338
struct CopyStore {
339
-
likes: Vec<(String, records::StrongRef, DateTime<Utc>)>,
340
posts: Vec<(String, Cid, records::AppBskyFeedPost)>,
341
-
reposts: Vec<(String, records::StrongRef, DateTime<Utc>)>,
342
blocks: Vec<(String, String, DateTime<Utc>)>,
343
follows: Vec<(String, String, DateTime<Utc>)>,
344
list_items: Vec<(String, records::AppBskyGraphListItem)>,
···
336
337
#[derive(Debug, Default)]
338
struct CopyStore {
339
+
likes: Vec<(String, records::StrongRef, Option<records::StrongRef>, DateTime<Utc>)>,
340
posts: Vec<(String, Cid, records::AppBskyFeedPost)>,
341
+
reposts: Vec<(String, records::StrongRef, Option<records::StrongRef>, DateTime<Utc>)>,
342
blocks: Vec<(String, String, DateTime<Utc>)>,
343
follows: Vec<(String, String, DateTime<Utc>)>,
344
list_items: Vec<(String, records::AppBskyGraphListItem)>,
+2
-2
consumer/src/backfill/repo.rs
···
131
deltas.incr(&rec.subject.uri, AggregateType::Like).await;
132
133
copies.push_record(&at_uri, cid);
134
-
copies.likes.push((at_uri, rec.subject, rec.created_at));
135
}
136
RecordTypes::AppBskyFeedPost(rec) => {
137
let maybe_reply = rec.reply.as_ref().map(|v| v.parent.uri.clone());
···
167
deltas.incr(&rec.subject.uri, AggregateType::Repost).await;
168
169
copies.push_record(&at_uri, cid);
170
-
copies.reposts.push((at_uri, rec.subject, rec.created_at));
171
}
172
RecordTypes::AppBskyGraphBlock(rec) => {
173
copies.push_record(&at_uri, cid);
···
131
deltas.incr(&rec.subject.uri, AggregateType::Like).await;
132
133
copies.push_record(&at_uri, cid);
134
+
copies.likes.push((at_uri, rec.subject, rec.via, rec.created_at));
135
}
136
RecordTypes::AppBskyFeedPost(rec) => {
137
let maybe_reply = rec.reply.as_ref().map(|v| v.parent.uri.clone());
···
167
deltas.incr(&rec.subject.uri, AggregateType::Repost).await;
168
169
copies.push_record(&at_uri, cid);
170
+
copies.reposts.push((at_uri, rec.subject, rec.via, rec.created_at));
171
}
172
RecordTypes::AppBskyGraphBlock(rec) => {
173
copies.push_record(&at_uri, cid);
+15
-5
consumer/src/db/copy.rs
···
14
Type::TEXT,
15
Type::TEXT,
16
Type::TEXT,
0
0
17
Type::TIMESTAMP,
18
];
19
-
type StrongRefRow = (String, records::StrongRef, DateTime<Utc>);
20
21
// SubjectRefs are used in both blocks and follows
22
const SUBJECT_TYPES: &[Type] = &[Type::TEXT, Type::TEXT, Type::TEXT, Type::TIMESTAMP];
···
39
40
let writer = conn
41
.copy_in(
42
-
"COPY likes_tmp (at_uri, did, subject, subject_cid, created_at) FROM STDIN (FORMAT binary)",
43
)
44
.await?;
45
let writer = BinaryCopyInWriter::new(writer, STRONGREF_TYPES);
···
47
pin_mut!(writer);
48
49
for row in data {
0
0
50
let writer = writer.as_mut();
51
writer
52
.write(&[
···
54
&did,
55
&row.1.uri,
56
&row.1.cid.to_string(),
57
-
&row.2.naive_utc(),
0
0
58
])
59
.await?;
60
}
···
82
83
let writer = conn
84
.copy_in(
85
-
"COPY reposts_tmp (at_uri, did, post, post_cid, created_at) FROM STDIN (FORMAT binary)",
86
)
87
.await?;
88
let writer = BinaryCopyInWriter::new(writer, STRONGREF_TYPES);
···
90
pin_mut!(writer);
91
92
for row in data {
0
0
93
let writer = writer.as_mut();
94
writer
95
.write(&[
···
97
&did,
98
&row.1.uri,
99
&row.1.cid.to_string(),
100
-
&row.2.naive_utc(),
0
0
101
])
102
.await?;
103
}
···
14
Type::TEXT,
15
Type::TEXT,
16
Type::TEXT,
17
+
Type::TEXT,
18
+
Type::TEXT,
19
Type::TIMESTAMP,
20
];
21
+
type StrongRefRow = (String, records::StrongRef, Option<records::StrongRef>, DateTime<Utc>);
22
23
// SubjectRefs are used in both blocks and follows
24
const SUBJECT_TYPES: &[Type] = &[Type::TEXT, Type::TEXT, Type::TEXT, Type::TIMESTAMP];
···
41
42
let writer = conn
43
.copy_in(
44
+
"COPY likes_tmp (at_uri, did, subject, subject_cid, via_uri, via_cid, created_at) FROM STDIN (FORMAT binary)",
45
)
46
.await?;
47
let writer = BinaryCopyInWriter::new(writer, STRONGREF_TYPES);
···
49
pin_mut!(writer);
50
51
for row in data {
52
+
let (via_uri, via_cid) = strongref_to_parts(row.2.as_ref());
53
+
54
let writer = writer.as_mut();
55
writer
56
.write(&[
···
58
&did,
59
&row.1.uri,
60
&row.1.cid.to_string(),
61
+
&via_uri,
62
+
&via_cid,
63
+
&row.3.naive_utc(),
64
])
65
.await?;
66
}
···
88
89
let writer = conn
90
.copy_in(
91
+
"COPY reposts_tmp (at_uri, did, post, post_cid, via_uri, via_cid, created_at) FROM STDIN (FORMAT binary)",
92
)
93
.await?;
94
let writer = BinaryCopyInWriter::new(writer, STRONGREF_TYPES);
···
96
pin_mut!(writer);
97
98
for row in data {
99
+
let (via_uri, via_cid) = strongref_to_parts(row.2.as_ref());
100
+
101
let writer = writer.as_mut();
102
writer
103
.write(&[
···
105
&did,
106
&row.1.uri,
107
&row.1.cid.to_string(),
108
+
&via_uri,
109
+
&via_cid,
110
+
&row.3.naive_utc(),
111
])
112
.await?;
113
}
+9
-3
consumer/src/db/record.rs
···
156
repo: &str,
157
rec: AppBskyFeedLike,
158
) -> PgExecResult {
0
0
159
conn.execute(
160
-
"INSERT INTO likes (at_uri, did, subject, subject_cid, created_at) VALUES ($1, $2, $3, $4, $5)",
161
-
&[&at_uri, &repo, &rec.subject.uri, &rec.subject.cid.to_string(), &rec.created_at]
162
).await
163
}
164
···
523
repo: &str,
524
rec: AppBskyFeedRepost,
525
) -> PgExecResult {
0
0
526
conn.execute(
527
-
"INSERT INTO reposts (at_uri, did, post, post_cid, created_at) VALUES ($1, $2, $3, $4, $5)",
528
&[
529
&at_uri,
530
&repo,
531
&rec.subject.uri,
532
&rec.subject.cid.to_string(),
0
0
533
&rec.created_at,
534
],
535
)
···
156
repo: &str,
157
rec: AppBskyFeedLike,
158
) -> PgExecResult {
159
+
let (via_uri, via_cid) = strongref_to_parts(rec.via.as_ref());
160
+
161
conn.execute(
162
+
"INSERT INTO likes (at_uri, did, subject, subject_cid, via_uri, via_cid, created_at) VALUES ($1, $2, $3, $4, $5, $6, $7)",
163
+
&[&at_uri, &repo, &rec.subject.uri, &rec.subject.cid.to_string(), &via_uri, &via_cid, &rec.created_at]
164
).await
165
}
166
···
525
repo: &str,
526
rec: AppBskyFeedRepost,
527
) -> PgExecResult {
528
+
let (via_uri, via_cid) = strongref_to_parts(rec.via.as_ref());
529
+
530
conn.execute(
531
+
"INSERT INTO reposts (at_uri, did, post, post_cid, via_uri, via_cid, created_at) VALUES ($1, $2, $3, $4, $5, $6, $7)",
532
&[
533
&at_uri,
534
&repo,
535
&rec.subject.uri,
536
&rec.subject.cid.to_string(),
537
+
&via_uri,
538
+
&via_cid,
539
&rec.created_at,
540
],
541
)
+2
consumer/src/indexer/records.rs
···
206
pub struct AppBskyFeedLike {
207
pub subject: StrongRef,
208
pub created_at: DateTime<Utc>,
0
209
}
210
211
#[derive(Debug, Deserialize, Serialize)]
···
260
pub struct AppBskyFeedRepost {
261
pub subject: StrongRef,
262
pub created_at: DateTime<Utc>,
0
263
}
264
265
#[derive(Debug, Deserialize, Serialize)]
···
206
pub struct AppBskyFeedLike {
207
pub subject: StrongRef,
208
pub created_at: DateTime<Utc>,
209
+
pub via: Option<StrongRef>,
210
}
211
212
#[derive(Debug, Deserialize, Serialize)]
···
261
pub struct AppBskyFeedRepost {
262
pub subject: StrongRef,
263
pub created_at: DateTime<Utc>,
264
+
pub via: Option<StrongRef>,
265
}
266
267
#[derive(Debug, Deserialize, Serialize)]
+2
migrations/2025-06-18-200415_like-repost-via/down.sql
···
0
0
···
1
+
alter table likes drop column via_uri, drop column via_cid;
2
+
alter table reposts drop column via_uri, drop column via_cid;
+7
migrations/2025-06-18-200415_like-repost-via/up.sql
···
0
0
0
0
0
0
0
···
1
+
alter table likes
2
+
add column via_uri text,
3
+
add column via_cid text;
4
+
5
+
alter table reposts
6
+
add column via_uri text,
7
+
add column via_cid text;
+4
parakeet-db/src/schema.rs
···
125
subject_cid -> Text,
126
created_at -> Timestamptz,
127
indexed_at -> Timestamp,
0
0
128
}
129
}
130
···
283
post_cid -> Text,
284
created_at -> Timestamptz,
285
indexed_at -> Timestamp,
0
0
286
}
287
}
288
···
125
subject_cid -> Text,
126
created_at -> Timestamptz,
127
indexed_at -> Timestamp,
128
+
via_uri -> Nullable<Text>,
129
+
via_cid -> Nullable<Text>,
130
}
131
}
132
···
285
post_cid -> Text,
286
created_at -> Timestamptz,
287
indexed_at -> Timestamp,
288
+
via_uri -> Nullable<Text>,
289
+
via_cid -> Nullable<Text>,
290
}
291
}
292