+14
-6
crates/knot/src/model/knot_state.rs
+14
-6
crates/knot/src/model/knot_state.rs
···
1
1
use identity::Did;
2
2
use identity::Resolver;
3
+
use lexicon::com::atproto::repo::list_records;
3
4
use oauth::public_key::PublicKey;
4
5
use std::collections::HashMap;
5
6
use std::path::PathBuf;
···
72
71
.ok_or(anyhow::anyhow!("DID document does not declare a pds"))?
73
72
.service_endpoint;
74
73
75
-
let response: crate::atproto::Response<LexiconPublicKey> = self
74
+
let response = self
76
75
.public_http
77
76
.get(list_records_url(pds.clone(), "sh.tangled.publicKey", did))
78
77
.send()
79
78
.await?
80
-
.json()
79
+
.error_for_status()?
80
+
.bytes()
81
81
.await?;
82
82
83
-
Ok(response
84
-
.records
85
-
.into_iter()
86
-
.filter_map(|rec| PublicKey::from_openssh(&rec.value.key).ok())
83
+
let result: list_records::Output<LexiconPublicKey> = serde_json::from_slice(&response)?;
84
+
Ok(result
85
+
.records()
86
+
.filter_map(|rec| {
87
+
PublicKey::from_openssh(&rec.value.key)
88
+
.inspect_err(|e| {
89
+
tracing::error!(?e, ?did, "failed to parse public key from collection")
90
+
})
91
+
.ok()
92
+
})
87
93
.collect())
88
94
}
89
95
}
+1
crates/lexicon/src/com.rs
+1
crates/lexicon/src/com.rs
···
1
+
pub mod atproto;
+1
crates/lexicon/src/com/atproto.rs
+1
crates/lexicon/src/com/atproto.rs
···
1
+
pub mod repo;
+60
crates/lexicon/src/com/atproto/repo.rs
+60
crates/lexicon/src/com/atproto/repo.rs
···
1
+
pub mod list_records {
2
+
//!
3
+
//! List a range of records in a repository, matching a specific
4
+
//! collection. Does not require auth.
5
+
//!
6
+
//! <https://docs.bsky.app/docs/api/com-atproto-repo-list-records>
7
+
//!
8
+
use serde::{Deserialize, Serialize};
9
+
use std::borrow::Cow;
10
+
11
+
#[derive(Debug, Deserialize, Serialize)]
12
+
pub struct Input<'a> {
13
+
/// The handle or DID of the repo.
14
+
#[serde(borrow)]
15
+
pub repo: Cow<'a, str>,
16
+
17
+
/// The NSID of the record type.
18
+
#[serde(borrow)]
19
+
pub collection: Cow<'a, str>,
20
+
21
+
/// The number of records to return.
22
+
///
23
+
/// Possible values: 0..=100.
24
+
#[serde(skip_serializing_if = "Option::is_none")]
25
+
pub limit: Option<usize>,
26
+
27
+
#[serde(borrow, skip_serializing_if = "Option::is_none")]
28
+
pub cursor: Option<Cow<'a, str>>,
29
+
30
+
/// Flag to reverse the order of the returned records.
31
+
#[serde(default)]
32
+
pub reverse: bool,
33
+
}
34
+
35
+
#[derive(Debug, Deserialize, Serialize)]
36
+
pub struct Output<'a, T> {
37
+
#[serde(skip_serializing_if = "Option::is_none")]
38
+
pub cursor: Option<Cow<'a, str>>,
39
+
40
+
#[serde(borrow)]
41
+
pub records: Vec<Record<'a, T>>,
42
+
}
43
+
44
+
#[derive(Debug, Deserialize, Serialize)]
45
+
pub struct Record<'a, T> {
46
+
#[serde(borrow)]
47
+
pub uri: Cow<'a, str>,
48
+
49
+
#[serde(borrow)]
50
+
pub cid: Cow<'a, str>,
51
+
52
+
pub value: T,
53
+
}
54
+
55
+
impl<'a, T> Output<'a, T> {
56
+
pub fn records(&self) -> impl Iterator<Item = &Record<'a, T>> {
57
+
self.records.iter()
58
+
}
59
+
}
60
+
}