Feat(client): getHomeserver (#86)

* added method

* added wasm rust analyzer section

* getHomeserver wasm

* updated readme

* getHomeserver js test

* correct js typing

* fixed js getHomeserver call

* fmt and clippy

* Update pubky/README.md

Co-authored-by: SHAcollision <127778313+SHAcollision@users.noreply.github.com>

* moved get_homeserver from internal to auth

* reuse extract_host_from_packet

* removed unused imports

* removed version from testnet

* fixed tests

---------

Co-authored-by: SHAcollision <127778313+SHAcollision@users.noreply.github.com>
This commit is contained in:
Severin Alexander Bühler
2025-03-19 11:17:13 +02:00
committed by GitHub
parent 5393b4575a
commit 3e761720de
9 changed files with 110 additions and 4 deletions

1
Cargo.lock generated
View File

@@ -2174,6 +2174,7 @@ dependencies = [
"getrandom 0.3.1",
"js-sys",
"log",
"mainline",
"pkarr",
"pubky-common",
"pubky-testnet",

View File

@@ -15,12 +15,13 @@ anyhow = "1.0.95"
http-relay = "0.2.0"
mainline = "5.2.0"
pkarr-relay = "0.5.7"
pubky = { version = "0.4.1", path = "../pubky" }
pubky-common = { version = "0.3.1", path = "../pubky-common" }
pubky-homeserver = { version = "0.1.1", path = "../pubky-homeserver" }
tokio = { version = "1.43.0", features = ["full"] }
tracing-subscriber = "0.3.19"
url = "2.5.4"
pubky = { path = "../pubky" }
pubky-common = { path = "../pubky-common" }
pubky-homeserver = { path = "../pubky-homeserver" }
[dev-dependencies]
tracing-subscriber = "0.3.19"

View File

@@ -59,6 +59,7 @@ futures-lite = "2.6.0"
pubky-testnet = { path = "../pubky-testnet" }
tokio = "1.43.0"
tracing-subscriber = "0.3.19"
mainline = "5.3.1"
[build-dependencies]
cfg_aliases = "0.2.1"

View File

@@ -53,3 +53,27 @@ async fn main () {
## Example code
Check more [examples](https://github.com/pubky/pubky-core/tree/main/examples) for using the Pubky client.
## Wasm Rust Analyzer
In vscode with the rust-analyzer, wasm behind the `#[cfg(wasm_browser)]` guard is not type checked. To fix this, add
a `.vscode/settings.json` file in the root of this project with the following content:
```json
{
"rust-analyzer.cargo.target": "wasm32-unknown-unknown"
}
```
If not done already, you need to add the wasm target: `cargo target add wasm32-unknown-unknown`.
This is just a workaround because it enables the wasm feature in all workspace member which creates problems.
So it is best to enable this settings only temporarily for wasm development and then turn it off again before commiting the
changes. This is a [rust-analyzer issue](https://github.com/rust-lang/rust-analyzer/issues/11900#issuecomment-1166638234).
## How To Build/Test the NPM Package
1. Go to `pubky/pkg`.
2. Run `npm run build`.
3. Run a testnet mainline DHT, Pkarr relay and Homeserver `npm run testnet`
4. Run tests with `npm run test`.

View File

@@ -103,3 +103,36 @@ test("Auth: 3rd party signin", async (t) => {
let session = await client.session(authedPubky);
t.deepEqual(session.capabilities(), capabilities.split(','))
})
test('getHomeserver not found', async (t) => {
const client = Client.testnet();
const keypair = Keypair.random()
const publicKey = keypair.publicKey()
try {
let homeserver = await client.getHomeserver(publicKey);
t.fail("getHomeserver should NOT be found.");
} catch (e) {
t.pass("getHomeserver should NOT be found.");
}
})
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
test('getHomeserver success', async (t) => {
const client = Client.testnet();
const keypair = Keypair.random()
const publicKey = keypair.publicKey()
const signupToken = await createSignupToken(client)
await client.signup(keypair, HOMESERVER_PUBLICKEY, signupToken);
let homeserver = await client.getHomeserver(publicKey);
t.is(homeserver.z32(), HOMESERVER_PUBLICKEY.z32(), "homeserver is correct");
})

View File

@@ -346,6 +346,13 @@ impl Client {
)
.await
}
/// Get the homeserver for a given Pubky public key.
/// Looks up the pkarr packet for the given public key and returns the content of the first `_pubky` SVCB record.
pub async fn get_homeserver(&self, pubky: &PublicKey) -> Option<String> {
let packet = self.pkarr.resolve_most_recent(pubky).await?;
Self::extract_host_from_packet(&packet)
}
}
#[derive(Debug, Clone)]
@@ -379,6 +386,8 @@ mod tests {
use reqwest::StatusCode;
use std::time::Duration;
use crate::{native::internal::pkarr::PublishStrategy, Client};
#[tokio::test]
async fn basic_authn() {
let testnet = Testnet::run().await.unwrap();
@@ -822,4 +831,27 @@ mod tests {
"Record was not republished after threshold exceeded"
);
}
#[tokio::test]
async fn test_get_homeserver() {
let dht = mainline::Testnet::new(3).unwrap();
let client = Client::builder()
.pkarr(|builder| builder.bootstrap(&dht.bootstrap))
.build()
.unwrap();
let keypair = Keypair::random();
let pubky = keypair.public_key();
let homeserver_key = Keypair::random().public_key().to_z32();
client
.publish_homeserver(
&keypair,
Some(homeserver_key.as_str()),
PublishStrategy::Force,
)
.await
.unwrap();
let homeserver = client.get_homeserver(&pubky).await;
assert_eq!(homeserver, Some(homeserver_key));
}
}

View File

@@ -101,7 +101,7 @@ impl Client {
/// Helper to extract the current homeserver host from a signed PKarr packet.
/// Iterates over the records with name "_pubky" and returns the first SVCB target found.
fn extract_host_from_packet(packet: &SignedPacket) -> Option<String> {
pub(crate) fn extract_host_from_packet(packet: &SignedPacket) -> Option<String> {
packet
.resource_records("_pubky")
.find_map(|rr| match &rr.rdata {

View File

@@ -106,6 +106,20 @@ impl Client {
Ok(())
}
/// Get the homeserver id for a given Pubky public key.
/// Looks up the pkarr packet for the given public key and returns the content of the first `_pubky` SVCB record.
/// Throws an error if no homeserver is found.
#[wasm_bindgen(js_name = "getHomeserver")]
pub async fn get_homeserver(&self, public_key: &PublicKey) -> Result<PublicKey, JsValue> {
let val = self.0.get_homeserver(public_key.as_inner()).await;
if val.is_none() {
return Err(JsValue::from_str("No homeserver found"));
}
let val = val.unwrap();
let js_val = JsValue::from_str(val.as_str());
PublicKey::try_from(js_val)
}
/// Republish the user's PKarr record pointing to their homeserver.
///
/// This method will republish the record if no record exists or if the existing record