fix: import existing profile metadata
This commit is contained in:
parent
aacae4d2f6
commit
bde35bcec4
1 changed files with 106 additions and 0 deletions
106
src/profiles.rs
106
src/profiles.rs
|
|
@ -1,3 +1,5 @@
|
|||
use std::time::Duration;
|
||||
|
||||
use nostr_sdk::prelude::*;
|
||||
use serde::Serialize;
|
||||
use zeroize::Zeroizing;
|
||||
|
|
@ -99,6 +101,74 @@ pub fn create_profile(
|
|||
})
|
||||
}
|
||||
|
||||
/// Add an existing profile from a hex or bech32 secret key.
|
||||
pub fn import_profile(
|
||||
vault: &mut Vault,
|
||||
label: String,
|
||||
secret: &str,
|
||||
key: Option<&VaultKey>,
|
||||
settings: &Settings,
|
||||
) -> Result<ProfileSummary, AppError> {
|
||||
if vault.is_encrypted() && key.is_none() {
|
||||
return Err(AppError::vault_locked());
|
||||
}
|
||||
|
||||
let secret_key = SecretKey::from_bech32(secret.trim())
|
||||
.or_else(|_| parse_secret_key(secret.trim()))
|
||||
.map_err(|e| AppError::invalid_secret(format!("Could not import that secret key: {e}")))?;
|
||||
let keys = Keys::new(secret_key);
|
||||
let public_key = keys
|
||||
.public_key()
|
||||
.to_bech32()
|
||||
.map_err(|e| AppError::internal(format!("Could not encode the public key: {e}")))?;
|
||||
if vault
|
||||
.profiles
|
||||
.iter()
|
||||
.any(|profile| profile.public_key == public_key)
|
||||
{
|
||||
return Err(AppError::config("This account is already added."));
|
||||
}
|
||||
|
||||
let secret_hex = Zeroizing::new(hex::encode(keys.secret_key().to_secret_bytes()));
|
||||
let stored_secret = match &vault.crypto {
|
||||
Some(_) => crate::crypto::encrypt_secret(key.expect("guarded above"), &secret_hex)?,
|
||||
None => secret_hex.to_string(),
|
||||
};
|
||||
let created_at = unix_timestamp()?;
|
||||
let metadata = fetch_profile_metadata(&public_key, &relays::enabled_urls(settings));
|
||||
let is_active = vault.active_profile.is_none();
|
||||
if is_active {
|
||||
vault.active_profile = Some(public_key.clone());
|
||||
}
|
||||
vault.profiles.push(StoredProfile {
|
||||
label: label.clone(),
|
||||
public_key: public_key.clone(),
|
||||
secret_key: stored_secret,
|
||||
created_at,
|
||||
picture: metadata.as_ref().and_then(|m| m.picture.clone()),
|
||||
nip05: metadata.as_ref().and_then(|m| m.nip05.clone()),
|
||||
});
|
||||
|
||||
let relay_urls = relays::enabled_urls(settings);
|
||||
if !relay_urls.is_empty() {
|
||||
publish_metadata_blocking(
|
||||
&keys,
|
||||
&label,
|
||||
metadata.as_ref().and_then(|m| m.picture.clone()),
|
||||
metadata.as_ref().and_then(|m| m.nip05.clone()),
|
||||
relay_urls,
|
||||
);
|
||||
}
|
||||
Ok(ProfileSummary {
|
||||
label,
|
||||
npub: public_key,
|
||||
created_at,
|
||||
is_active,
|
||||
picture: metadata.as_ref().and_then(|m| m.picture.clone()),
|
||||
nip05: metadata.and_then(|m| m.nip05),
|
||||
})
|
||||
}
|
||||
|
||||
/// The per-relay outcome of publishing a profile metadata event.
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct MetadataPublishReport {
|
||||
|
|
@ -414,6 +484,42 @@ fn publish_metadata_blocking(
|
|||
.expect("metadata publish thread panicked")
|
||||
}
|
||||
|
||||
/// Best-effort lookup of the account's latest kind-0 metadata. Import must
|
||||
/// still succeed when relays are unavailable, so lookup failures are ignored.
|
||||
fn fetch_profile_metadata(npub: &str, relay_urls: &[String]) -> Option<Metadata> {
|
||||
if relay_urls.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let public_key = PublicKey::from_bech32(npub).ok()?;
|
||||
let relay_urls = relay_urls.to_vec();
|
||||
std::thread::spawn(move || {
|
||||
tokio::runtime::Runtime::new().ok()?.block_on(async move {
|
||||
let client =
|
||||
relays::open_pool(Keys::generate(), &relay_urls, Some(Duration::from_secs(10)))
|
||||
.await
|
||||
.ok()?;
|
||||
let events = client
|
||||
.fetch_events(
|
||||
Filter::new()
|
||||
.kind(Kind::Metadata)
|
||||
.author(public_key)
|
||||
.limit(1),
|
||||
)
|
||||
.timeout(Duration::from_secs(10))
|
||||
.await
|
||||
.ok()?;
|
||||
client.disconnect().await;
|
||||
events
|
||||
.into_iter()
|
||||
.max_by_key(|event| event.created_at)
|
||||
.and_then(|event| serde_json::from_str::<Metadata>(&event.content).ok())
|
||||
})
|
||||
})
|
||||
.join()
|
||||
.ok()
|
||||
.flatten()
|
||||
}
|
||||
|
||||
async fn publish_metadata_async(
|
||||
keys: &Keys,
|
||||
label: &str,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue