use std::str::FromStr;
use crate::PublicKey;
use crate::{
Capabilities, EventCursor, EventStreamBuilder, Pkdns, PubkyAuthFlow, PubkyHttpClient,
PubkySigner, PublicStorage, Result, actors::AuthFlowKind, deep_links::DeepLink,
errors::AuthError,
};
#[cfg(not(target_arch = "wasm32"))]
use crate::{PubkySession, errors::RequestError};
#[cfg(not(target_arch = "wasm32"))]
use std::path::Path;
#[derive(Clone, Debug)]
pub struct Pubky {
client: PubkyHttpClient,
}
impl Pubky {
pub fn new() -> Result<Self> {
Ok(Self {
client: PubkyHttpClient::new()?,
})
}
pub fn testnet() -> Result<Self> {
Ok(Self {
client: PubkyHttpClient::testnet()?,
})
}
#[must_use]
pub const fn with_client(client: PubkyHttpClient) -> Self {
Self { client }
}
pub fn start_auth_flow(
&self,
caps: &Capabilities,
auth_kind: AuthFlowKind,
) -> Result<PubkyAuthFlow> {
PubkyAuthFlow::builder(caps, auth_kind)
.client(self.client.clone())
.start()
}
pub fn resume_auth_flow(&self, authorization_url: &str) -> Result<PubkyAuthFlow> {
let (caps, relay, secret, auth_kind) = parse_auth_deep_link(authorization_url)?;
PubkyAuthFlow::builder(&caps, auth_kind)
.client_secret(secret)
.relay(relay)
.client(self.client.clone())
.start()
}
#[must_use]
pub fn signer(&self, keypair: crate::Keypair) -> PubkySigner {
PubkySigner {
client: self.client.clone(),
keypair,
}
}
#[must_use]
pub fn public_storage(&self) -> PublicStorage {
PublicStorage {
client: self.client.clone(),
}
}
#[must_use]
pub fn pkdns(&self) -> Pkdns {
Pkdns::with_client(self.client.clone())
}
pub async fn get_homeserver_of(&self, user_public_key: &PublicKey) -> Option<PublicKey> {
Pkdns::with_client(self.client.clone())
.get_homeserver_of(user_public_key)
.await
}
#[must_use]
pub fn event_stream_for_user(
&self,
user: &PublicKey,
cursor: Option<EventCursor>,
) -> EventStreamBuilder {
EventStreamBuilder::for_user(self.client.clone(), user, cursor)
}
#[must_use]
pub fn event_stream_for(&self, homeserver: &PublicKey) -> EventStreamBuilder {
EventStreamBuilder::for_homeserver(self.client.clone(), homeserver)
}
#[cfg(not(target_arch = "wasm32"))]
pub async fn session_from_file<P: AsRef<Path>>(&self, path: P) -> Result<PubkySession> {
PubkySession::from_secret_file(path.as_ref(), Some(self.client.clone())).await
}
#[cfg(not(target_arch = "wasm32"))]
pub fn signer_from_recovery_file<P: AsRef<Path>>(
&self,
path: P,
passphrase: &str,
) -> Result<PubkySigner> {
use pubky_common::recovery_file::decrypt_recovery_file;
let bytes = std::fs::read(path.as_ref()).map_err(|e| RequestError::Validation {
message: format!("failed to read recovery file: {e}"),
})?;
let kp =
decrypt_recovery_file(&bytes, passphrase).map_err(|e| RequestError::Validation {
message: format!("failed to decrypt recovery file: {e}"),
})?;
Ok(self.signer(kp))
}
#[inline]
#[must_use]
pub const fn client(&self) -> &PubkyHttpClient {
&self.client
}
}
fn parse_auth_deep_link(url: &str) -> Result<(Capabilities, url::Url, [u8; 32], AuthFlowKind)> {
let deep_link = DeepLink::from_str(url)
.map_err(|e| AuthError::Validation(format!("Failed to parse authorization URL: {e}")))?;
match &deep_link {
DeepLink::Signin(s) => Ok((
s.capabilities().clone(),
s.relay().clone(),
*s.secret(),
AuthFlowKind::signin(),
)),
DeepLink::Signup(s) => Ok((
s.capabilities().clone(),
s.relay().clone(),
*s.secret(),
AuthFlowKind::signup(s.homeserver().clone(), s.signup_token()),
)),
DeepLink::SeedExport(_) => {
Err(AuthError::Validation("Only signin and signup URLs can be resumed.".into()).into())
}
}
}