···
···
.get_json("com.atproto.repo.listRecords", ¶ms, bearer)
-
let mut repos: Vec<Repository> = res.records.into_iter().map(|r| r.value).collect();
// Apply optional filters client-side
repos.retain(|r| r.knot.as_deref().unwrap_or("") == k);
···
let _: serde_json::Value = self.post_json(REPO_CREATE, &req, Some(&sa.token)).await?;
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
···
pub description: Option<String>,
···
···
.get_json("com.atproto.repo.listRecords", ¶ms, bearer)
+
let mut repos: Vec<Repository> = res
+
if val.rkey.is_none() {
+
if let Some(k) = Self::uri_rkey(&r.uri) {
+
if let Some(d) = Self::uri_did(&r.uri) {
// Apply optional filters client-side
repos.retain(|r| r.knot.as_deref().unwrap_or("") == k);
···
let _: serde_json::Value = self.post_json(REPO_CREATE, &req, Some(&sa.token)).await?;
+
pub async fn get_repo_info(
+
) -> Result<RepoRecord> {
+
let did = if owner.starts_with("did:") {
+
let params = [("handle", owner.to_string())];
+
.get_json("com.atproto.identity.resolveHandle", ¶ms, bearer)
+
records: Vec<RecordItem>,
+
("collection", "sh.tangled.repo".to_string()),
+
("limit", "100".to_string()),
+
let res: ListRes = self
+
.get_json("com.atproto.repo.listRecords", ¶ms, bearer)
+
for item in res.records {
+
if item.value.name == name {
+
Self::uri_rkey(&item.uri).ok_or_else(|| anyhow!("missing rkey in uri"))?;
+
let knot = item.value.knot.unwrap_or_default();
+
name: name.to_string(),
+
description: item.value.description,
+
Err(anyhow!("repo not found for owner/name"))
+
pub async fn delete_repo(
+
let pds_client = TangledClient::new(pds_base);
+
.get_repo_info(did, name, Some(access_jwt))
+
struct DeleteRecordReq<'a> {
+
let del = DeleteRecordReq {
+
collection: "sh.tangled.repo",
+
let _: serde_json::Value = pds_client
+
.post_json("com.atproto.repo.deleteRecord", &del, Some(access_jwt))
+
.strip_prefix("https://")
+
.or_else(|| self.base_url.trim_end_matches('/').strip_prefix("http://"))
+
.ok_or_else(|| anyhow!("invalid base_url"))?;
+
let audience = format!("did:web:{}", host);
+
("exp", (chrono::Utc::now().timestamp() + 600).to_string()),
+
let sa: GetSARes = pds_client
+
"com.atproto.server.getServiceAuth",
+
let _: serde_json::Value = self
+
.post_json("sh.tangled.repo.delete", &body, Some(&sa.token))
+
pub async fn update_repo_knot(
+
let pds_client = TangledClient::new(pds_base);
+
#[derive(Deserialize, Serialize, Clone)]
+
#[serde(skip_serializing_if = "Option::is_none")]
+
description: Option<String>,
+
#[serde(rename = "createdAt")]
+
("repo", did.to_string()),
+
("collection", "sh.tangled.repo".to_string()),
+
("rkey", rkey.to_string()),
+
let got: GetRes = pds_client
+
.get_json("com.atproto.repo.getRecord", ¶ms, Some(access_jwt))
+
let mut rec = got.value;
+
rec.knot = new_knot.to_string();
+
collection: "sh.tangled.repo",
+
let _: serde_json::Value = pds_client
+
.post_json("com.atproto.repo.putRecord", &req, Some(access_jwt))
+
pub async fn get_default_branch(
+
) -> Result<DefaultBranch> {
+
#[serde(rename = "shortHash")]
+
short_hash: Option<String>,
+
message: Option<String>,
+
let knot_client = TangledClient::new(knot_host);
+
let repo_param = format!("{}/{}", did, name);
+
let params = [("repo", repo_param)];
+
let res: Res = knot_client
+
.get_json("sh.tangled.repo.getDefaultBranch", ¶ms, None)
+
short_hash: res.short_hash,
+
pub async fn get_languages(&self, knot_host: &str, did: &str, name: &str) -> Result<Languages> {
+
let knot_client = TangledClient::new(knot_host);
+
let repo_param = format!("{}/{}", did, name);
+
let params = [("repo", repo_param)];
+
let res: serde_json::Value = knot_client
+
.get_json("sh.tangled.repo.languages", ¶ms, None)
+
.unwrap_or(serde_json::json!([]));
+
let languages: Vec<Language> = serde_json::from_value(langs)?;
+
let total_size = res.get("totalSize").and_then(|v| v.as_u64());
+
let total_files = res.get("totalFiles").and_then(|v| v.as_u64());
+
pub async fn star_repo(
+
#[serde(rename = "createdAt")]
+
let now = chrono::Utc::now().to_rfc3339();
+
subject: subject_at_uri,
+
collection: "sh.tangled.feed.star",
+
let pds_client = TangledClient::new(pds_base);
+
let res: Res = pds_client
+
.post_json("com.atproto.repo.createRecord", &req, Some(access_jwt))
+
let rkey = Self::uri_rkey(&res.uri).ok_or_else(|| anyhow!("missing rkey in star uri"))?;
+
pub async fn unstar_repo(
+
let pds_client = TangledClient::new(pds_base);
+
("repo", user_did.to_string()),
+
("collection", "sh.tangled.feed.star".to_string()),
+
("limit", "100".to_string()),
+
let res: ListRes = pds_client
+
.get_json("com.atproto.repo.listRecords", ¶ms, Some(access_jwt))
+
for item in res.records {
+
if item.value.subject == subject_at_uri {
+
rkey = Self::uri_rkey(&item.uri);
+
let rkey = rkey.ok_or_else(|| anyhow!("star record not found"))?;
+
collection: "sh.tangled.feed.star",
+
let _: serde_json::Value = pds_client
+
.post_json("com.atproto.repo.deleteRecord", &del, Some(access_jwt))
+
fn uri_rkey(uri: &str) -> Option<String> {
+
uri.rsplit('/').next().map(|s| s.to_string())
+
fn uri_did(uri: &str) -> Option<String> {
+
let parts: Vec<&str> = uri.split('/').collect();
+
Some(parts[2].to_string())
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
···
pub description: Option<String>,
+
#[derive(Debug, Clone)]
+
pub struct RepoRecord {
+
pub description: Option<String>,
+
#[derive(Debug, Clone, Serialize, Deserialize)]
+
pub struct DefaultBranch {
+
#[serde(skip_serializing_if = "Option::is_none")]
+
pub short_hash: Option<String>,
+
#[serde(skip_serializing_if = "Option::is_none")]
+
pub message: Option<String>,
+
#[derive(Debug, Clone, Serialize, Deserialize)]
+
#[derive(Debug, Clone, Serialize, Deserialize)]
+
pub languages: Vec<Language>,
+
#[serde(skip_serializing_if = "Option::is_none")]
+
pub total_size: Option<u64>,
+
#[serde(skip_serializing_if = "Option::is_none")]
+
pub total_files: Option<u64>,
+
#[derive(Debug, Clone, Serialize, Deserialize)]
+
pub struct StarRecord {
+
#[serde(rename = "createdAt")]
+
pub created_at: String,