25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 1 | use std::collections::{BTreeSet, HashSet}; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 2 | use std::fs::{File, OpenOptions, create_dir_all}; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 3 | use std::future::Future; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 4 | use std::io::Error as IoError; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 5 | use std::iter::once; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 6 | use std::path::PathBuf; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 7 | use std::pin::Pin; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 8 | use std::sync::Arc; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 9 | |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 10 | use anyhow::Context as _; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 11 | use fs2::FileExt as _; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 12 | use nostr::event::{Event, EventId, Kind}; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 13 | use nostr::filter::Filter; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 14 | use nostr::nips::nip65::extract_relay_list; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 15 | use nostr_database::error::Error; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 16 | use nostr_database::{DatabaseEventStatus, Features, NostrDatabase, SaveEventStatus}; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 17 | use tokio::sync::Mutex; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 18 | |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 19 | use crate::config::{Config, relay_url}; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 20 | use crate::journal::Journal; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 21 | |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 22 | type DatabaseFuture<'database, T> = |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 23 | Pin<Box<dyn Future<Output = Result<T, Error>> + Send + 'database>>; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 24 | |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 25 | #[derive(Debug)] |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 26 | pub struct Store { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 27 | pub(crate) journal: Journal, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 28 | path: PathBuf, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 29 | min_free_bytes: u64, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 30 | max_query_bytes: usize, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 31 | max_repositories: usize, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 32 | max_discovered_peers: usize, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 33 | excluded_peers: HashSet<String>, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 34 | writer: Arc<Mutex<()>>, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 35 | _lock: File, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 36 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 37 | |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 38 | impl Store { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 39 | pub async fn open(config: &Config) -> anyhow::Result<Arc<Self>> { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 40 | if let Some(parent) = config |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 41 | .database |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 42 | .parent() |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 43 | .filter(|parent| !parent.as_os_str().is_empty()) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 44 | { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 45 | create_dir_all(parent).context("creating database directory")?; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 46 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 47 | let mut lock_name = config.database.as_os_str().to_os_string(); |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 48 | lock_name.push(".lock"); |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 49 | let mut lock_options = OpenOptions::new(); |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 50 | lock_options |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 51 | .read(true) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 52 | .write(true) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 53 | .create(true) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 54 | .truncate(false); |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 55 | #[cfg(unix)] |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 56 | { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 57 | use std::os::unix::fs::OpenOptionsExt as _; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 58 | lock_options.mode(0o600) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 59 | }; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 60 | let lock = lock_options |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 61 | .open(PathBuf::from(lock_name)) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 62 | .context("opening database lock")?; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 63 | lock.try_lock_exclusive() |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 64 | .context("database is already in use")?; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 65 | |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 66 | let excluded_peers = once(&config.public_url) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 67 | .chain(config.peers.iter().map(|peer| &peer.url)) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 68 | .map(|url| relay_url(url).map(|parsed| parsed.to_string())) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 69 | .collect::<Option<HashSet<_>>>() |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 70 | .context("validated relay url became invalid")?; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 71 | let writer = Arc::new(Mutex::new(())); |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 72 | let journal = Journal::open( |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 73 | config.database.clone(), |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 74 | config.limits.max_database_bytes, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 75 | config |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 76 | .limits |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 77 | .max_discovered_peers |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 78 | .saturating_add(excluded_peers.len()), |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 79 | excluded_peers.iter().cloned().collect(), |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 80 | Arc::clone(&writer), |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 81 | ) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 82 | .await?; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 83 | journal |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 84 | .forget_discovered(excluded_peers.iter().cloned().collect()) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 85 | .await?; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 86 | tracing::info!(path = %config.database.display(), "opened event database"); |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 87 | Ok(Arc::new(Self { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 88 | journal, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 89 | path: config.database.clone(), |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 90 | min_free_bytes: config.limits.min_free_bytes, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 91 | max_query_bytes: config.limits.max_query_bytes, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 92 | max_repositories: config.limits.max_repositories, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 93 | max_discovered_peers: config.limits.max_discovered_peers, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 94 | excluded_peers, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 95 | writer, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 96 | _lock: lock, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 97 | })) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 98 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 99 | |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 100 | pub async fn healthy(&self) -> bool { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 101 | self.journal.healthy().await |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 102 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 103 | |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 104 | async fn query_events(&self, filter: Filter) -> Result<BTreeSet<Event>, Error> { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 105 | self.journal |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 106 | .query(filter, self.max_query_bytes) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 107 | .await |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 108 | .map_err(storage_error) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 109 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 110 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 111 | |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 112 | fn storage_error(error: anyhow::Error) -> Error { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 113 | Error::storage(IoError::other(error)) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 114 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 115 | |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 116 | impl NostrDatabase for Store { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 117 | fn backend(&self) -> &'static str { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 118 | "vineyard-sqlite" |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 119 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 120 | |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 121 | fn features(&self) -> Features { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 122 | Features { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 123 | persistent: true, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 124 | event_expiration: false, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 125 | full_text_search: false, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 126 | request_to_vanish: false, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 127 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 128 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 129 | |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 130 | fn save_event<'database>( |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 131 | &'database self, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 132 | event: &'database Event, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 133 | ) -> DatabaseFuture<'database, SaveEventStatus> { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 134 | Box::pin(async move { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 135 | let writer = self.writer.lock().await; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 136 | if event.kind.as_u16() == 30617 { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 137 | if let Some(identifier) = event.tags.identifier() { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 138 | let registered = self |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 139 | .query_events( |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 140 | Filter::new() |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 141 | .kind(event.kind) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 142 | .author(event.pubkey) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 143 | .identifier(identifier) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 144 | .limit(1), |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 145 | ) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 146 | .await? |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 147 | .len(); |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 148 | if registered == 0 |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 149 | && self |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 150 | .query_events( |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 151 | Filter::new() |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 152 | .kind(event.kind) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 153 | .limit(self.max_repositories + 1), |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 154 | ) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 155 | .await? |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 156 | .len() |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 157 | >= self.max_repositories |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 158 | { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 159 | return Err(Error::storage(IoError::other( |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 160 | "repository directory is full", |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 161 | ))); |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 162 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 163 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 164 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 165 | if self.journal.status(event.id).await.map_err(storage_error)? |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 166 | != DatabaseEventStatus::Saved |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 167 | { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 168 | let free = fs2::available_space(&self.path).map_err(Error::io)?; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 169 | let event_bytes = u64::try_from(event.as_json().len()) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 170 | .map_err(|error| Error::storage(IoError::other(error.to_string())))?; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 171 | if free < self.min_free_bytes.saturating_add(event_bytes) { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 172 | return Err(Error::storage(IoError::other("storage reserve reached"))); |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 173 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 174 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 175 | let status = self |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 176 | .journal |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 177 | .save_locked(event.clone(), &writer) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 178 | .await |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 179 | .map_err(storage_error)?; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 180 | if status.is_success() && event.kind == Kind::RelayList { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 181 | let urls = extract_relay_list(event) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 182 | .map(|(url, _)| url.to_string()) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 183 | .filter(|url| !self.excluded_peers.contains(url)) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 184 | .collect(); |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 185 | if let Err(error) = self |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 186 | .journal |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 187 | .replace_discovered_locked( |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 188 | event.pubkey, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 189 | urls, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 190 | self.max_discovered_peers, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 191 | &writer, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 192 | ) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 193 | .await |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 194 | { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 195 | tracing::error!(event = %event.id, %error, "saving discovered peers failed"); |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 196 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 197 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 198 | drop(writer); |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 199 | Ok(status) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 200 | }) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 201 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 202 | |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 203 | fn check_id<'database>( |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 204 | &'database self, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 205 | event_id: &'database EventId, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 206 | ) -> DatabaseFuture<'database, DatabaseEventStatus> { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 207 | let requested_id = *event_id; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 208 | Box::pin(async move { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 209 | self.journal |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 210 | .status(requested_id) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 211 | .await |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 212 | .map_err(storage_error) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 213 | }) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 214 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 215 | |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 216 | fn event_by_id<'database>( |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 217 | &'database self, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 218 | event_id: &'database EventId, |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 219 | ) -> DatabaseFuture<'database, Option<Event>> { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 220 | let requested_id = *event_id; |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 221 | Box::pin(async move { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 222 | self.journal |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 223 | .event(requested_id) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 224 | .await |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 225 | .map_err(storage_error) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 226 | }) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 227 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 228 | |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 229 | fn count(&self, filter: Filter) -> DatabaseFuture<'_, usize> { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 230 | Box::pin(async move { self.journal.count(filter).await.map_err(storage_error) }) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 231 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 232 | |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 233 | fn query(&self, filter: Filter) -> DatabaseFuture<'_, BTreeSet<Event>> { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 234 | Box::pin(self.query_events(filter)) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 235 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 236 | |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 237 | fn delete(&self, _filter: Filter) -> DatabaseFuture<'_, ()> { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 238 | Box::pin(async { Err(Error::unsupported("use signed deletion requests")) }) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 239 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 240 | |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 241 | fn wipe(&self) -> DatabaseFuture<'_, ()> { |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 242 | Box::pin(async { Err(Error::unsupported("database wipe is disabled")) }) |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 243 | } |
25d14e49ab68 | atagen | 2026-09-24 11:07 UTC | 244 | } |