Files
coop/crates/global/src/lib.rs
reya d392602ed6 feat: resend messages after authentication (#137)
* resend failed message

* update settings
2025-09-02 18:19:53 +07:00

170 lines
4.7 KiB
Rust

use std::collections::{HashMap, HashSet};
use std::sync::OnceLock;
use std::time::Duration;
use nostr_connect::prelude::*;
use nostr_sdk::prelude::*;
use paths::nostr_file;
use smol::channel::{Receiver, Sender};
use smol::lock::RwLock;
use crate::paths::support_dir;
pub mod constants;
pub mod paths;
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct AuthRequest {
pub challenge: String,
pub url: RelayUrl,
}
impl AuthRequest {
pub fn new(challenge: impl Into<String>, url: RelayUrl) -> Self {
Self {
challenge: challenge.into(),
url,
}
}
}
#[derive(Debug, Clone)]
pub enum Notice {
RelayFailed(RelayUrl),
AuthFailed(RelayUrl),
Custom(String),
}
impl Notice {
pub fn as_str(&self) -> String {
match self {
Notice::AuthFailed(url) => format!("Authenticate failed for relay {url}"),
Notice::RelayFailed(url) => format!("Failed to connect the relay {url}"),
Notice::Custom(msg) => msg.into(),
}
}
}
/// Signals sent through the global event channel to notify UI
#[derive(Debug)]
pub enum IngesterSignal {
/// A signal to notify UI that the client's signer has been set
SignerSet(PublicKey),
/// A signal to notify UI that the client's signer has been unset
SignerUnset,
/// A signal to notify UI that the relay requires authentication
Auth(AuthRequest),
/// A signal to notify UI that the browser proxy service is down
ProxyDown,
/// A signal to notify UI that a new metadata event has been received
Metadata(Event),
/// A signal to notify UI that a new gift wrap event has been received
GiftWrap((EventId, Event)),
/// A signal to notify UI that all gift wrap events have been processed
Finish,
/// A signal to notify UI that partial processing of gift wrap events has been completed
PartialFinish,
/// A signal to notify UI that no DM relay for current user was found
DmRelayNotFound,
/// A signal to notify UI that there are errors or notices occurred
Notice(Notice),
}
#[derive(Debug)]
pub struct Ingester {
rx: Receiver<IngesterSignal>,
tx: Sender<IngesterSignal>,
}
impl Default for Ingester {
fn default() -> Self {
Self::new()
}
}
impl Ingester {
pub fn new() -> Self {
let (tx, rx) = smol::channel::bounded::<IngesterSignal>(2048);
Self { rx, tx }
}
pub fn signals(&self) -> &Receiver<IngesterSignal> {
&self.rx
}
pub async fn send(&self, signal: IngesterSignal) {
if let Err(e) = self.tx.send(signal).await {
log::error!("Failed to send signal: {e}");
}
}
}
/// A simple storage to store all runtime states that using across the application.
#[derive(Debug, Default)]
pub struct CoopSimpleStorage {
pub init_at: Timestamp,
pub sent_ids: RwLock<HashSet<EventId>>,
pub resent_ids: RwLock<Vec<Output<EventId>>>,
pub resend_queue: RwLock<HashMap<EventId, RelayUrl>>,
}
static NOSTR_CLIENT: OnceLock<Client> = OnceLock::new();
static INGESTER: OnceLock<Ingester> = OnceLock::new();
static COOP_SIMPLE_STORAGE: OnceLock<CoopSimpleStorage> = OnceLock::new();
static FIRST_RUN: OnceLock<bool> = OnceLock::new();
pub fn nostr_client() -> &'static Client {
NOSTR_CLIENT.get_or_init(|| {
// rustls uses the `aws_lc_rs` provider by default
// This only errors if the default provider has already
// been installed. We can ignore this `Result`.
rustls::crypto::aws_lc_rs::default_provider()
.install_default()
.ok();
let lmdb = NostrLMDB::open(nostr_file()).expect("Database is NOT initialized");
let opts = ClientOptions::new()
.gossip(true)
.automatic_authentication(false)
.verify_subscriptions(false)
.sleep_when_idle(SleepWhenIdle::Enabled {
timeout: Duration::from_secs(30),
});
ClientBuilder::default().database(lmdb).opts(opts).build()
})
}
pub fn ingester() -> &'static Ingester {
INGESTER.get_or_init(Ingester::new)
}
pub fn css() -> &'static CoopSimpleStorage {
COOP_SIMPLE_STORAGE.get_or_init(CoopSimpleStorage::default)
}
pub fn first_run() -> &'static bool {
FIRST_RUN.get_or_init(|| {
let flag = support_dir().join(format!(".{}-first_run", env!("CARGO_PKG_VERSION")));
if !flag.exists() {
if std::fs::write(&flag, "").is_err() {
return false;
}
true // First run
} else {
false // Not first run
}
})
}