···
fn xrpc_url(&self, method: &str) -> String {
-
format!("{}/xrpc/{}", self.base_url.trim_end_matches('/'), method)
async fn post_json<TReq: Serialize, TRes: DeserializeOwned>(
···
return Err(anyhow!("{}: {}", status, body));
Ok(res.json::<TRes>().await?)
pub async fn get_json<TRes: DeserializeOwned>(
···
-
("exp", (chrono::Utc::now().timestamp() + 600).to_string()),
let sa: GetSARes = pds_client
···
description: item.value.description,
···
-
("exp", (chrono::Utc::now().timestamp() + 600).to_string()),
let sa: GetSARes = pds_client
···
-
let _: serde_json::Value = self
-
.post_json("sh.tangled.repo.addSecret", &body, Some(&sa))
pub async fn remove_repo_secret(
···
let body = Req { repo: repo_at, key };
-
let _: serde_json::Value = self
-
.post_json("sh.tangled.repo.removeSecret", &body, Some(&sa))
async fn service_auth_token(&self, pds_base: &str, access_jwt: &str) -> Result<String> {
.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);
let pds = TangledClient::new(pds_base);
-
("exp", (chrono::Utc::now().timestamp() + 600).to_string()),
···
pub description: Option<String>,
#[derive(Debug, Clone, Serialize, Deserialize)]
···
fn xrpc_url(&self, method: &str) -> String {
+
let base = self.base_url.trim_end_matches('/');
+
// Add https:// if no protocol is present
+
let base_with_protocol = if base.starts_with("http://") || base.starts_with("https://") {
+
format!("https://{}", base)
+
format!("{}/xrpc/{}", base_with_protocol, method)
async fn post_json<TReq: Serialize, TRes: DeserializeOwned>(
···
return Err(anyhow!("{}: {}", status, body));
Ok(res.json::<TRes>().await?)
+
async fn post<TReq: Serialize>(
+
let url = self.xrpc_url(method);
+
let client = reqwest::Client::new();
+
.header(reqwest::header::CONTENT_TYPE, "application/json");
+
if let Some(token) = bearer {
+
reqb = reqb.header(reqwest::header::AUTHORIZATION, format!("Bearer {}", token));
+
let res = reqb.json(req).send().await?;
+
let status = res.status();
+
if !status.is_success() {
+
let body = res.text().await.unwrap_or_default();
+
return Err(anyhow!("{}: {}", status, body));
pub async fn get_json<TRes: DeserializeOwned>(
···
+
// Method-less ServiceAuth tokens must expire within 60 seconds per AT Protocol spec
+
("exp", (chrono::Utc::now().timestamp() + 60).to_string()),
let sa: GetSARes = pds_client
···
description: item.value.description,
+
spindle: item.value.spindle,
···
+
// Method-less ServiceAuth tokens must expire within 60 seconds per AT Protocol spec
+
("exp", (chrono::Utc::now().timestamp() + 60).to_string()),
let sa: GetSARes = pds_client
···
+
self.post("sh.tangled.repo.addSecret", &body, Some(&sa))
pub async fn remove_repo_secret(
···
let body = Req { repo: repo_at, key };
+
self.post("sh.tangled.repo.removeSecret", &body, Some(&sa))
async fn service_auth_token(&self, pds_base: &str, access_jwt: &str) -> Result<String> {
+
let base_trimmed = self.base_url.trim_end_matches('/');
+
let host = base_trimmed
.strip_prefix("https://")
+
.or_else(|| base_trimmed.strip_prefix("http://"))
+
.unwrap_or(base_trimmed); // If no protocol, use the URL as-is
let audience = format!("did:web:{}", host);
let pds = TangledClient::new(pds_base);
+
// Method-less ServiceAuth tokens must expire within 60 seconds per AT Protocol spec
+
("exp", (chrono::Utc::now().timestamp() + 60).to_string()),
···
pub description: Option<String>,
+
pub spindle: Option<String>,
#[derive(Debug, Clone, Serialize, Deserialize)]