Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
e609ba3
Ignore plans
tompro Dec 15, 2025
e9fd0bf
feat: add relay sync data structures and extend NostrStoreApi
tompro Dec 15, 2025
c74c708
feat: implement database storage for relay sync status and retry queue
tompro Dec 15, 2025
791308d
feat: add relay sync configuration constants
tompro Dec 15, 2025
724d5f1
feat: add sync methods to TransportClientApi trait
tompro Dec 15, 2025
4c02584
feat: add user_relays and sync_running fields to NostrClient
tompro Dec 15, 2025
77c1d9a
feat: implement core relay sync logic with event fetching
tompro Dec 15, 2025
8a3a2a4
feat: implement sync_relays and retry_failed_syncs in NostrClient
tompro Dec 15, 2025
faa7d6f
feat: add sync methods to TransportService
tompro Dec 15, 2025
a2c8ede
feat: add relay sync and retry jobs to background runner
tompro Dec 15, 2025
4b4831a
fix: update test mocks to implement new relay sync trait methods
tompro Dec 15, 2025
4c3de7c
fix: make relay sync WASM-compatible by replacing tokio::spawn with f…
tompro Dec 15, 2025
c68a652
Wasm compatible bg job
tompro Dec 15, 2025
856d842
feat: add stream_events_from to NostrClient and use streaming in rela…
tompro Dec 16, 2025
8580d5c
Use event stream
tompro Dec 16, 2025
2452e35
fix: mark all relays as Completed when no source relays exist
tompro Dec 16, 2025
3b4f2c8
Logging
tompro Dec 16, 2025
e0a8002
refactor: remove redundant user_relays field from NostrClient
tompro Dec 16, 2025
3d0992f
Formatting
tompro Dec 16, 2025
b9e9c73
Tests
tompro Dec 16, 2025
2d2b84b
refactor: address code review feedback
tompro Dec 16, 2025
b0531ab
More efficient queries
tompro Dec 16, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
/files/*
/company/*
/pkg/*
/docs/plans

.idea/
.worktrees/
Expand Down
13 changes: 13 additions & 0 deletions crates/bcr-ebill-api/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,16 @@ pub const VALID_FILE_MIME_TYPES: [&str; 3] = ["image/jpeg", "image/png", "applic
pub const NOSTR_EVENT_TIME_SLACK: u64 = 3600 * 24 * 7; // 1 week
pub const DEFAULT_INITIAL_SUBSCRIPTION_DELAY_SECONDS: u32 = 1;
pub const NOSTR_MAX_RELAYS: usize = 200;

/// Delay between individual event publishes during relay sync (milliseconds)
pub const RELAY_SYNC_EVENT_DELAY_MS: u64 = 50;

/// Maximum retry attempts for failed event syncs
pub const RELAY_SYNC_MAX_RETRIES: usize = 10;

/// Number of events to fetch per retry batch
pub const RELAY_SYNC_RETRY_BATCH_SIZE: usize = 100;

/// Gap threshold for detecting removed and re-added relays (seconds)
/// If last_seen_in_config is older than this, consider it a gap and re-sync
pub const RELAY_SYNC_GAP_THRESHOLD_SECONDS: u64 = 86400; // 24 hours
12 changes: 6 additions & 6 deletions crates/bcr-ebill-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,21 @@ use bcr_ebill_persistence::db::surreal::SurrealWrapper;
#[cfg(not(target_arch = "wasm32"))]
use bcr_ebill_persistence::get_surreal_db;
use bcr_ebill_persistence::{
ContactStoreApi, NostrChainEventStoreApi, NostrEventOffsetStoreApi, NotificationStoreApi,
SurrealBillChainStore, SurrealBillStore, SurrealCompanyChainStore, SurrealCompanyStore,
SurrealContactStore, SurrealDbConfig, SurrealIdentityChainStore, SurrealIdentityStore,
SurrealNostrChainEventStore, SurrealNostrEventOffsetStore, SurrealNotificationStore,
ContactStoreApi, NostrChainEventStoreApi, NostrContactStoreApi, NostrEventOffsetStoreApi,
NotificationStoreApi, SurrealBillChainStore, SurrealBillStore, SurrealCompanyChainStore,
SurrealCompanyStore, SurrealContactStore, SurrealDbConfig, SurrealIdentityChainStore,
SurrealIdentityStore, SurrealNostrChainEventStore, SurrealNostrContactStore,
SurrealNostrEventOffsetStore, SurrealNotificationStore,
bill::{BillChainStoreApi, BillStoreApi},
company::{CompanyChainStoreApi, CompanyStoreApi},
db::{
email_notification::SurrealEmailNotificationStore, mint::SurrealMintStore,
nostr_contact_store::SurrealNostrContactStore,
nostr_send_queue::SurrealNostrEventQueueStore,
},
file_upload::FileUploadStoreApi,
identity::{IdentityChainStoreApi, IdentityStoreApi},
mint::MintStoreApi,
nostr::{NostrContactStoreApi, NostrQueuedMessageStoreApi},
nostr::NostrQueuedMessageStoreApi,
notification::EmailNotificationStoreApi,
};
use bitcoin::Network;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,12 @@ pub trait TransportServiceApi: ServiceTraitBounds {

/// Retry sending a queued message to the given node id
async fn send_retry_messages(&self) -> Result<()>;

/// Syncs historical events to newly added relays
async fn sync_relays(&self) -> Result<()>;

/// Retries failed relay sync attempts
async fn retry_failed_syncs(&self) -> Result<()>;
}

#[cfg(test)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,13 @@ pub trait TransportClientApi: ServiceTraitBounds {

/// Check if this client has a local signer for the given node_id
fn has_local_signer(&self, node_id: &NodeId) -> bool;

/// Syncs historical events to newly added relays.
/// This is called by the background job runner.
/// Returns early if sync is already in progress.
async fn sync_relays(&self) -> Result<()>;

/// Retries events that failed to sync.
/// This is called by the background job runner.
async fn retry_failed_syncs(&self) -> Result<()>;
}
18 changes: 15 additions & 3 deletions crates/bcr-ebill-api/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
pub mod tests {
use crate::CourtConfig;
use crate::service::transport_service::{self, chain_keys::ChainKeyServiceApi};
use crate::{CONFIG, DbContext, DevModeConfig, MintConfig, NostrConfig, PaymentConfig};
use crate::{
CONFIG, DbContext, DevModeConfig, MintConfig, NostrConfig, NostrContactStoreApi,
PaymentConfig,
};
use async_trait::async_trait;
use bcr_common::core::{BillId, NodeId};
use bcr_ebill_core::protocol::Address;
Expand Down Expand Up @@ -51,8 +54,8 @@ pub mod tests {
identity::{IdentityChainStoreApi, IdentityStoreApi},
mint::MintStoreApi,
nostr::{
NostrChainEvent, NostrChainEventStoreApi, NostrContactStoreApi, NostrQueuedMessage,
NostrQueuedMessageStoreApi,
NostrChainEvent, NostrChainEventStoreApi, NostrQueuedMessage,
NostrQueuedMessageStoreApi, RelaySyncStatus, SyncStatus,
},
notification::NotificationFilter,
};
Expand Down Expand Up @@ -157,6 +160,15 @@ pub mod tests {
async fn list_pending_shares_by_receiver_and_direction(&self, receiver_node_id: &NodeId, direction: ShareDirection) -> Result<Vec<PendingContactShare>>;
async fn delete_pending_share(&self, id: &str) -> Result<()>;
async fn pending_share_exists_for_node_and_receiver(&self, node_id: &NodeId, receiver_node_id: &NodeId) -> Result<bool>;
async fn get_pending_relays(&self) -> Result<Vec<url::Url>>;
async fn get_relay_sync_status(&self, relay: &url::Url) -> Result<Option<RelaySyncStatus>>;
async fn update_relay_sync_status(&self, relay: &url::Url, status: SyncStatus) -> Result<()>;
async fn update_relay_sync_progress(&self, relay: &url::Url, timestamp: bcr_ebill_core::protocol::Timestamp) -> Result<()>;
async fn update_relay_last_seen(&self, relay: &url::Url, timestamp: bcr_ebill_core::protocol::Timestamp) -> Result<()>;
async fn add_failed_relay_sync(&self, relay: &url::Url, event: nostr::Event) -> Result<()>;
async fn get_pending_relay_retries(&self, relay: &url::Url, limit: usize) -> Result<Vec<nostr::Event>>;
async fn mark_relay_retry_success(&self, relay: &url::Url, event_id: &str) -> Result<()>;
async fn mark_relay_retry_failed(&self, relay: &url::Url, event_id: &str, max_retries: usize) -> Result<()>;
}
}

Expand Down
Loading
Loading