diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2026-02-26 15:42:09 +0000 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2026-02-26 15:42:09 +0000 |
| commit | 9d86cf15f0275ffeee4519bd054e3b61dc8992ac (patch) | |
| tree | 65b5d5ffb2a11b5ecd05d01e63fb5a4a0f8b6e06 /src | |
| parent | a2ecfc5a63311570f0f90c7ee40117e289639cb8 (diff) | |
chore: apply cargo fmt and fix clippy warnings
Fix pre-existing clippy lints:
- &PathBuf -> &Path in audit_cleanup.rs
- too_many_arguments on process_newly_available_git_data,
process_purgatory_announcements, and HttpService::new
- clone_on_copy for PublicKey (Copy type) in purgatory cleanup loop
Diffstat (limited to 'src')
| -rw-r--r-- | src/audit_cleanup.rs | 24 | ||||
| -rw-r--r-- | src/git/handlers.rs | 66 | ||||
| -rw-r--r-- | src/git/sync.rs | 2 | ||||
| -rw-r--r-- | src/http/mod.rs | 1 | ||||
| -rw-r--r-- | src/nostr/builder.rs | 1 | ||||
| -rw-r--r-- | src/nostr/policy/announcement.rs | 18 | ||||
| -rw-r--r-- | src/nostr/policy/deletion.rs | 45 | ||||
| -rw-r--r-- | src/nostr/policy/pr_event.rs | 10 | ||||
| -rw-r--r-- | src/nostr/policy/state.rs | 6 | ||||
| -rw-r--r-- | src/purgatory/mod.rs | 51 | ||||
| -rw-r--r-- | src/sync/mod.rs | 3 |
11 files changed, 133 insertions, 94 deletions
diff --git a/src/audit_cleanup.rs b/src/audit_cleanup.rs index b976b1f..de78b1b 100644 --- a/src/audit_cleanup.rs +++ b/src/audit_cleanup.rs | |||
| @@ -15,7 +15,7 @@ | |||
| 15 | //! | 15 | //! |
| 16 | //! Runs every `AUDIT_CLEANUP_INTERVAL_SECS` seconds. | 16 | //! Runs every `AUDIT_CLEANUP_INTERVAL_SECS` seconds. |
| 17 | 17 | ||
| 18 | use std::path::PathBuf; | 18 | use std::path::{Path, PathBuf}; |
| 19 | use std::time::Duration; | 19 | use std::time::Duration; |
| 20 | 20 | ||
| 21 | use nostr_sdk::prelude::*; | 21 | use nostr_sdk::prelude::*; |
| @@ -46,8 +46,12 @@ pub async fn run_audit_cleanup_loop(database: SharedDatabase, git_data_path: Pat | |||
| 46 | } | 46 | } |
| 47 | 47 | ||
| 48 | /// Perform a single cleanup pass. | 48 | /// Perform a single cleanup pass. |
| 49 | async fn run_audit_cleanup_once(database: &SharedDatabase, git_data_path: &PathBuf) { | 49 | async fn run_audit_cleanup_once(database: &SharedDatabase, git_data_path: &Path) { |
| 50 | let cutoff = Timestamp::from(Timestamp::now().as_secs().saturating_sub(AUDIT_CLEANUP_AGE_SECS)); | 50 | let cutoff = Timestamp::from( |
| 51 | Timestamp::now() | ||
| 52 | .as_secs() | ||
| 53 | .saturating_sub(AUDIT_CLEANUP_AGE_SECS), | ||
| 54 | ); | ||
| 51 | 55 | ||
| 52 | // --- Step 1: Find repo announcements to delete git repos for --- | 56 | // --- Step 1: Find repo announcements to delete git repos for --- |
| 53 | let repo_filter = Filter::new() | 57 | let repo_filter = Filter::new() |
| @@ -73,10 +77,7 @@ async fn run_audit_cleanup_once(database: &SharedDatabase, git_data_path: &PathB | |||
| 73 | if repo_path.exists() { | 77 | if repo_path.exists() { |
| 74 | match std::fs::remove_dir_all(&repo_path) { | 78 | match std::fs::remove_dir_all(&repo_path) { |
| 75 | Ok(()) => { | 79 | Ok(()) => { |
| 76 | debug!( | 80 | debug!("audit_cleanup: deleted git repo {}", repo_path.display()); |
| 77 | "audit_cleanup: deleted git repo {}", | ||
| 78 | repo_path.display() | ||
| 79 | ); | ||
| 80 | repos_deleted += 1; | 81 | repos_deleted += 1; |
| 81 | 82 | ||
| 82 | // Remove the parent npub directory if it is now empty | 83 | // Remove the parent npub directory if it is now empty |
| @@ -131,9 +132,7 @@ async fn run_audit_cleanup_once(database: &SharedDatabase, git_data_path: &PathB | |||
| 131 | } | 132 | } |
| 132 | 133 | ||
| 133 | // --- Step 2: Delete all audit events from the database --- | 134 | // --- Step 2: Delete all audit events from the database --- |
| 134 | let all_audit_filter = Filter::new() | 135 | let all_audit_filter = Filter::new().hashtag(AUDIT_TEST_EVENT_TAG).until(cutoff); |
| 135 | .hashtag(AUDIT_TEST_EVENT_TAG) | ||
| 136 | .until(cutoff); | ||
| 137 | 136 | ||
| 138 | match database.delete(all_audit_filter).await { | 137 | match database.delete(all_audit_filter).await { |
| 139 | Ok(()) => { | 138 | Ok(()) => { |
| @@ -143,7 +142,10 @@ async fn run_audit_cleanup_once(database: &SharedDatabase, git_data_path: &PathB | |||
| 143 | ); | 142 | ); |
| 144 | } | 143 | } |
| 145 | Err(e) => { | 144 | Err(e) => { |
| 146 | error!("audit_cleanup: failed to delete audit events from database: {}", e); | 145 | error!( |
| 146 | "audit_cleanup: failed to delete audit events from database: {}", | ||
| 147 | e | ||
| 148 | ); | ||
| 147 | } | 149 | } |
| 148 | } | 150 | } |
| 149 | } | 151 | } |
diff --git a/src/git/handlers.rs b/src/git/handlers.rs index 5ff3a7f..b615251 100644 --- a/src/git/handlers.rs +++ b/src/git/handlers.rs | |||
| @@ -154,13 +154,10 @@ pub async fn handle_upload_pack( | |||
| 154 | 154 | ||
| 155 | // Write request to git's stdin | 155 | // Write request to git's stdin |
| 156 | if let Some(mut stdin) = git.take_stdin() { | 156 | if let Some(mut stdin) = git.take_stdin() { |
| 157 | stdin | 157 | stdin.write_all(&request_body).await.map_err(|e| { |
| 158 | .write_all(&request_body) | 158 | error!("Failed to write to git upload-pack stdin: {}", e); |
| 159 | .await | 159 | GitError::IoError(e) |
| 160 | .map_err(|e| { | 160 | })?; |
| 161 | error!("Failed to write to git upload-pack stdin: {}", e); | ||
| 162 | GitError::IoError(e) | ||
| 163 | })?; | ||
| 164 | // Close stdin to signal end of input | 161 | // Close stdin to signal end of input |
| 165 | drop(stdin); | 162 | drop(stdin); |
| 166 | } | 163 | } |
| @@ -171,24 +168,18 @@ pub async fn handle_upload_pack( | |||
| 171 | 168 | ||
| 172 | if let Some(stdout) = git.take_stdout() { | 169 | if let Some(stdout) = git.take_stdout() { |
| 173 | let mut stdout = stdout; | 170 | let mut stdout = stdout; |
| 174 | stdout | 171 | stdout.read_to_end(&mut output).await.map_err(|e| { |
| 175 | .read_to_end(&mut output) | 172 | error!("Failed to read git upload-pack stdout: {}", e); |
| 176 | .await | 173 | GitError::IoError(e) |
| 177 | .map_err(|e| { | 174 | })?; |
| 178 | error!("Failed to read git upload-pack stdout: {}", e); | ||
| 179 | GitError::IoError(e) | ||
| 180 | })?; | ||
| 181 | } | 175 | } |
| 182 | 176 | ||
| 183 | if let Some(stderr) = git.take_stderr() { | 177 | if let Some(stderr) = git.take_stderr() { |
| 184 | let mut stderr = stderr; | 178 | let mut stderr = stderr; |
| 185 | stderr | 179 | stderr.read_to_end(&mut stderr_output).await.map_err(|e| { |
| 186 | .read_to_end(&mut stderr_output) | 180 | error!("Failed to read git upload-pack stderr: {}", e); |
| 187 | .await | 181 | GitError::IoError(e) |
| 188 | .map_err(|e| { | 182 | })?; |
| 189 | error!("Failed to read git upload-pack stderr: {}", e); | ||
| 190 | GitError::IoError(e) | ||
| 191 | })?; | ||
| 192 | } | 183 | } |
| 193 | 184 | ||
| 194 | // Wait for process | 185 | // Wait for process |
| @@ -317,13 +308,10 @@ pub async fn handle_receive_pack( | |||
| 317 | 308 | ||
| 318 | // Write request to git's stdin | 309 | // Write request to git's stdin |
| 319 | if let Some(mut stdin) = git.take_stdin() { | 310 | if let Some(mut stdin) = git.take_stdin() { |
| 320 | stdin | 311 | stdin.write_all(&request_body).await.map_err(|e| { |
| 321 | .write_all(&request_body) | 312 | error!("Failed to write to git receive-pack stdin: {}", e); |
| 322 | .await | 313 | GitError::IoError(e) |
| 323 | .map_err(|e| { | 314 | })?; |
| 324 | error!("Failed to write to git receive-pack stdin: {}", e); | ||
| 325 | GitError::IoError(e) | ||
| 326 | })?; | ||
| 327 | drop(stdin); | 315 | drop(stdin); |
| 328 | } | 316 | } |
| 329 | 317 | ||
| @@ -333,24 +321,18 @@ pub async fn handle_receive_pack( | |||
| 333 | 321 | ||
| 334 | if let Some(stdout) = git.take_stdout() { | 322 | if let Some(stdout) = git.take_stdout() { |
| 335 | let mut stdout = stdout; | 323 | let mut stdout = stdout; |
| 336 | stdout | 324 | stdout.read_to_end(&mut output).await.map_err(|e| { |
| 337 | .read_to_end(&mut output) | 325 | error!("Failed to read git receive-pack stdout: {}", e); |
| 338 | .await | 326 | GitError::IoError(e) |
| 339 | .map_err(|e| { | 327 | })?; |
| 340 | error!("Failed to read git receive-pack stdout: {}", e); | ||
| 341 | GitError::IoError(e) | ||
| 342 | })?; | ||
| 343 | } | 328 | } |
| 344 | 329 | ||
| 345 | if let Some(stderr) = git.take_stderr() { | 330 | if let Some(stderr) = git.take_stderr() { |
| 346 | let mut stderr = stderr; | 331 | let mut stderr = stderr; |
| 347 | stderr | 332 | stderr.read_to_end(&mut stderr_output).await.map_err(|e| { |
| 348 | .read_to_end(&mut stderr_output) | 333 | error!("Failed to read git receive-pack stderr: {}", e); |
| 349 | .await | 334 | GitError::IoError(e) |
| 350 | .map_err(|e| { | 335 | })?; |
| 351 | error!("Failed to read git receive-pack stderr: {}", e); | ||
| 352 | GitError::IoError(e) | ||
| 353 | })?; | ||
| 354 | } | 336 | } |
| 355 | 337 | ||
| 356 | // Wait for process | 338 | // Wait for process |
diff --git a/src/git/sync.rs b/src/git/sync.rs index 9a02ad4..05dcbda 100644 --- a/src/git/sync.rs +++ b/src/git/sync.rs | |||
| @@ -814,6 +814,7 @@ pub fn extract_identifier_from_pr_event(event: &Event) -> Option<String> { | |||
| 814 | /// | 814 | /// |
| 815 | /// # Returns | 815 | /// # Returns |
| 816 | /// A `ProcessResult` describing what was processed | 816 | /// A `ProcessResult` describing what was processed |
| 817 | #[allow(clippy::too_many_arguments)] | ||
| 817 | pub async fn process_newly_available_git_data( | 818 | pub async fn process_newly_available_git_data( |
| 818 | source_repo_path: &Path, | 819 | source_repo_path: &Path, |
| 819 | new_oids: &HashSet<String>, | 820 | new_oids: &HashSet<String>, |
| @@ -1339,6 +1340,7 @@ async fn process_purgatory_pr_events( | |||
| 1339 | /// When `write_policy` and `rejected_events_index` are provided (git push path), | 1340 | /// When `write_policy` and `rejected_events_index` are provided (git push path), |
| 1340 | /// any maintainer announcements sitting in the hot cache are re-processed immediately | 1341 | /// any maintainer announcements sitting in the hot cache are re-processed immediately |
| 1341 | /// after the owner announcement is promoted, so they don't wait for the next sync cycle. | 1342 | /// after the owner announcement is promoted, so they don't wait for the next sync cycle. |
| 1343 | #[allow(clippy::too_many_arguments)] | ||
| 1342 | async fn process_purgatory_announcements( | 1344 | async fn process_purgatory_announcements( |
| 1343 | identifier: &str, | 1345 | identifier: &str, |
| 1344 | source_repo_path: &Path, | 1346 | source_repo_path: &Path, |
diff --git a/src/http/mod.rs b/src/http/mod.rs index 76ffef3..c397365 100644 --- a/src/http/mod.rs +++ b/src/http/mod.rs | |||
| @@ -105,6 +105,7 @@ struct HttpService { | |||
| 105 | } | 105 | } |
| 106 | 106 | ||
| 107 | impl HttpService { | 107 | impl HttpService { |
| 108 | #[allow(clippy::too_many_arguments)] | ||
| 108 | fn new( | 109 | fn new( |
| 109 | relay: LocalRelay, | 110 | relay: LocalRelay, |
| 110 | config: Config, | 111 | config: Config, |
diff --git a/src/nostr/builder.rs b/src/nostr/builder.rs index a0088e1..03132bf 100644 --- a/src/nostr/builder.rs +++ b/src/nostr/builder.rs | |||
| @@ -18,7 +18,6 @@ use crate::nostr::policy::{ | |||
| 18 | ReferenceResult, RelatedEventPolicy, StatePolicy, StateResult, | 18 | ReferenceResult, RelatedEventPolicy, StatePolicy, StateResult, |
| 19 | }; | 19 | }; |
| 20 | 20 | ||
| 21 | |||
| 22 | /// Type alias for the shared database used by the relay | 21 | /// Type alias for the shared database used by the relay |
| 23 | pub type SharedDatabase = Arc<dyn NostrDatabase>; | 22 | pub type SharedDatabase = Arc<dyn NostrDatabase>; |
| 24 | 23 | ||
diff --git a/src/nostr/policy/announcement.rs b/src/nostr/policy/announcement.rs index b366f0b..aba5181 100644 --- a/src/nostr/policy/announcement.rs +++ b/src/nostr/policy/announcement.rs | |||
| @@ -70,7 +70,10 @@ impl AnnouncementPolicy { | |||
| 70 | .is_some_and(|entry| event.created_at > entry.event.created_at); | 70 | .is_some_and(|entry| event.created_at > entry.event.created_at); |
| 71 | 71 | ||
| 72 | if should_evict { | 72 | if should_evict { |
| 73 | self.remove_purgatory_announcement(&event.pubkey, &announcement.identifier); | 73 | self.remove_purgatory_announcement( |
| 74 | &event.pubkey, | ||
| 75 | &announcement.identifier, | ||
| 76 | ); | ||
| 74 | } | 77 | } |
| 75 | 78 | ||
| 76 | match self | 79 | match self |
| @@ -145,10 +148,9 @@ impl AnnouncementPolicy { | |||
| 145 | ); | 148 | ); |
| 146 | AnnouncementResult::AcceptPurgatory | 149 | AnnouncementResult::AcceptPurgatory |
| 147 | } | 150 | } |
| 148 | Err(e) => AnnouncementResult::Reject(format!( | 151 | Err(e) => { |
| 149 | "Failed to parse announcement: {}", | 152 | AnnouncementResult::Reject(format!("Failed to parse announcement: {}", e)) |
| 150 | e | 153 | } |
| 151 | )), | ||
| 152 | } | 154 | } |
| 153 | } | 155 | } |
| 154 | // AcceptPurgatory shouldn't come from validate_announcement, but handle it | 156 | // AcceptPurgatory shouldn't come from validate_announcement, but handle it |
| @@ -161,11 +163,7 @@ impl AnnouncementPolicy { | |||
| 161 | /// Called when a replacement announcement arrives for a (pubkey, identifier) pair | 163 | /// Called when a replacement announcement arrives for a (pubkey, identifier) pair |
| 162 | /// that is currently in purgatory. Updates the purgatory entry and extends the | 164 | /// that is currently in purgatory. Updates the purgatory entry and extends the |
| 163 | /// expiry so the new announcement has a fresh waiting window. | 165 | /// expiry so the new announcement has a fresh waiting window. |
| 164 | fn replace_purgatory_announcement( | 166 | fn replace_purgatory_announcement(&self, event: &Event, announcement: &RepositoryAnnouncement) { |
| 165 | &self, | ||
| 166 | event: &Event, | ||
| 167 | announcement: &RepositoryAnnouncement, | ||
| 168 | ) { | ||
| 169 | let repo_path = self.ctx.git_data_path.join(announcement.repo_path()); | 167 | let repo_path = self.ctx.git_data_path.join(announcement.repo_path()); |
| 170 | let relays: HashSet<String> = announcement.relays.iter().cloned().collect(); | 168 | let relays: HashSet<String> = announcement.relays.iter().cloned().collect(); |
| 171 | 169 | ||
diff --git a/src/nostr/policy/deletion.rs b/src/nostr/policy/deletion.rs index 6457c90..c5a52d4 100644 --- a/src/nostr/policy/deletion.rs +++ b/src/nostr/policy/deletion.rs | |||
| @@ -155,7 +155,9 @@ impl DeletionPolicy { | |||
| 155 | author = %author.to_hex(), | 155 | author = %author.to_hex(), |
| 156 | "Deletion request: removing purgatory state event by event ID" | 156 | "Deletion request: removing purgatory state event by event ID" |
| 157 | ); | 157 | ); |
| 158 | self.ctx.purgatory.remove_state_event(&identifier, &entry.event.id); | 158 | self.ctx |
| 159 | .purgatory | ||
| 160 | .remove_state_event(&identifier, &entry.event.id); | ||
| 159 | return; // event IDs are unique | 161 | return; // event IDs are unique |
| 160 | } | 162 | } |
| 161 | } | 163 | } |
| @@ -223,7 +225,9 @@ impl DeletionPolicy { | |||
| 223 | if entry.author == *author | 225 | if entry.author == *author |
| 224 | && entry.event.created_at.as_secs() <= deletion_created_at | 226 | && entry.event.created_at.as_secs() <= deletion_created_at |
| 225 | { | 227 | { |
| 226 | self.ctx.purgatory.remove_state_event(identifier, &entry.event.id); | 228 | self.ctx |
| 229 | .purgatory | ||
| 230 | .remove_state_event(identifier, &entry.event.id); | ||
| 227 | removed += 1; | 231 | removed += 1; |
| 228 | } | 232 | } |
| 229 | } | 233 | } |
| @@ -306,7 +310,10 @@ mod tests { | |||
| 306 | EventBuilder::new(Kind::GitRepoAnnouncement, "") | 310 | EventBuilder::new(Kind::GitRepoAnnouncement, "") |
| 307 | .tags(vec![ | 311 | .tags(vec![ |
| 308 | Tag::identifier(identifier), | 312 | Tag::identifier(identifier), |
| 309 | Tag::custom(TagKind::custom("clone"), vec!["https://example.com/repo.git"]), | 313 | Tag::custom( |
| 314 | TagKind::custom("clone"), | ||
| 315 | vec!["https://example.com/repo.git"], | ||
| 316 | ), | ||
| 310 | ]) | 317 | ]) |
| 311 | .sign_with_keys(keys) | 318 | .sign_with_keys(keys) |
| 312 | .unwrap() | 319 | .unwrap() |
| @@ -331,7 +338,9 @@ mod tests { | |||
| 331 | let announcement = make_announcement_event(&keys, identifier); | 338 | let announcement = make_announcement_event(&keys, identifier); |
| 332 | add_to_purgatory(&ctx, &announcement, identifier); | 339 | add_to_purgatory(&ctx, &announcement, identifier); |
| 333 | 340 | ||
| 334 | assert!(ctx.purgatory.has_purgatory_announcement(&keys.public_key(), identifier)); | 341 | assert!(ctx |
| 342 | .purgatory | ||
| 343 | .has_purgatory_announcement(&keys.public_key(), identifier)); | ||
| 335 | 344 | ||
| 336 | // Build kind 5 deletion event referencing the announcement by event ID | 345 | // Build kind 5 deletion event referencing the announcement by event ID |
| 337 | let deletion = EventBuilder::new(Kind::EventDeletion, "") | 346 | let deletion = EventBuilder::new(Kind::EventDeletion, "") |
| @@ -347,7 +356,8 @@ mod tests { | |||
| 347 | 356 | ||
| 348 | assert!(matches!(result, WritePolicyResult::Accept)); | 357 | assert!(matches!(result, WritePolicyResult::Accept)); |
| 349 | assert!( | 358 | assert!( |
| 350 | !ctx.purgatory.has_purgatory_announcement(&keys.public_key(), identifier), | 359 | !ctx.purgatory |
| 360 | .has_purgatory_announcement(&keys.public_key(), identifier), | ||
| 351 | "Purgatory entry should have been removed" | 361 | "Purgatory entry should have been removed" |
| 352 | ); | 362 | ); |
| 353 | } | 363 | } |
| @@ -361,7 +371,9 @@ mod tests { | |||
| 361 | let announcement = make_announcement_event(&keys, identifier); | 371 | let announcement = make_announcement_event(&keys, identifier); |
| 362 | add_to_purgatory(&ctx, &announcement, identifier); | 372 | add_to_purgatory(&ctx, &announcement, identifier); |
| 363 | 373 | ||
| 364 | assert!(ctx.purgatory.has_purgatory_announcement(&keys.public_key(), identifier)); | 374 | assert!(ctx |
| 375 | .purgatory | ||
| 376 | .has_purgatory_announcement(&keys.public_key(), identifier)); | ||
| 365 | 377 | ||
| 366 | // Build kind 5 deletion event referencing the announcement by coordinate | 378 | // Build kind 5 deletion event referencing the announcement by coordinate |
| 367 | let coord = format!("30617:{}:{}", keys.public_key().to_hex(), identifier); | 379 | let coord = format!("30617:{}:{}", keys.public_key().to_hex(), identifier); |
| @@ -378,7 +390,8 @@ mod tests { | |||
| 378 | 390 | ||
| 379 | assert!(matches!(result, WritePolicyResult::Accept)); | 391 | assert!(matches!(result, WritePolicyResult::Accept)); |
| 380 | assert!( | 392 | assert!( |
| 381 | !ctx.purgatory.has_purgatory_announcement(&keys.public_key(), identifier), | 393 | !ctx.purgatory |
| 394 | .has_purgatory_announcement(&keys.public_key(), identifier), | ||
| 382 | "Purgatory entry should have been removed" | 395 | "Purgatory entry should have been removed" |
| 383 | ); | 396 | ); |
| 384 | } | 397 | } |
| @@ -407,7 +420,8 @@ mod tests { | |||
| 407 | 420 | ||
| 408 | assert!(matches!(result, WritePolicyResult::Accept)); | 421 | assert!(matches!(result, WritePolicyResult::Accept)); |
| 409 | assert!( | 422 | assert!( |
| 410 | ctx.purgatory.has_purgatory_announcement(&owner_keys.public_key(), identifier), | 423 | ctx.purgatory |
| 424 | .has_purgatory_announcement(&owner_keys.public_key(), identifier), | ||
| 411 | "Purgatory entry should NOT have been removed by wrong author" | 425 | "Purgatory entry should NOT have been removed by wrong author" |
| 412 | ); | 426 | ); |
| 413 | } | 427 | } |
| @@ -438,7 +452,8 @@ mod tests { | |||
| 438 | 452 | ||
| 439 | assert!(matches!(result, WritePolicyResult::Accept)); | 453 | assert!(matches!(result, WritePolicyResult::Accept)); |
| 440 | assert!( | 454 | assert!( |
| 441 | ctx.purgatory.has_purgatory_announcement(&owner_keys.public_key(), identifier), | 455 | ctx.purgatory |
| 456 | .has_purgatory_announcement(&owner_keys.public_key(), identifier), | ||
| 442 | "Purgatory entry should NOT have been removed by wrong author" | 457 | "Purgatory entry should NOT have been removed by wrong author" |
| 443 | ); | 458 | ); |
| 444 | } | 459 | } |
| @@ -450,11 +465,10 @@ mod tests { | |||
| 450 | 465 | ||
| 451 | // No purgatory entry exists — deletion should still be accepted | 466 | // No purgatory entry exists — deletion should still be accepted |
| 452 | let deletion = EventBuilder::new(Kind::EventDeletion, "") | 467 | let deletion = EventBuilder::new(Kind::EventDeletion, "") |
| 453 | .tags(vec![ | 468 | .tags(vec![Tag::custom( |
| 454 | Tag::custom(TagKind::custom("a"), vec![ | 469 | TagKind::custom("a"), |
| 455 | format!("30617:{}:nonexistent", keys.public_key().to_hex()) | 470 | vec![format!("30617:{}:nonexistent", keys.public_key().to_hex())], |
| 456 | ]), | 471 | )]) |
| 457 | ]) | ||
| 458 | .sign_with_keys(&keys) | 472 | .sign_with_keys(&keys) |
| 459 | .unwrap(); | 473 | .unwrap(); |
| 460 | 474 | ||
| @@ -491,7 +505,8 @@ mod tests { | |||
| 491 | 505 | ||
| 492 | assert!(matches!(result, WritePolicyResult::Accept)); | 506 | assert!(matches!(result, WritePolicyResult::Accept)); |
| 493 | assert!( | 507 | assert!( |
| 494 | ctx.purgatory.has_purgatory_announcement(&keys.public_key(), identifier), | 508 | ctx.purgatory |
| 509 | .has_purgatory_announcement(&keys.public_key(), identifier), | ||
| 495 | "Purgatory entry should NOT be removed: entry is newer than deletion request" | 510 | "Purgatory entry should NOT be removed: entry is newer than deletion request" |
| 496 | ); | 511 | ); |
| 497 | } | 512 | } |
diff --git a/src/nostr/policy/pr_event.rs b/src/nostr/policy/pr_event.rs index 52747a4..e4a64b8 100644 --- a/src/nostr/policy/pr_event.rs +++ b/src/nostr/policy/pr_event.rs | |||
| @@ -7,7 +7,9 @@ use nostr_relay_builder::prelude::Event; | |||
| 7 | 7 | ||
| 8 | use super::PolicyContext; | 8 | use super::PolicyContext; |
| 9 | use crate::git; | 9 | use crate::git; |
| 10 | use crate::git::authorization::{collect_authorized_maintainers, fetch_repository_data_excluding_purgatory}; | 10 | use crate::git::authorization::{ |
| 11 | collect_authorized_maintainers, fetch_repository_data_excluding_purgatory, | ||
| 12 | }; | ||
| 11 | 13 | ||
| 12 | /// Policy for validating PR and PR Update events | 14 | /// Policy for validating PR and PR Update events |
| 13 | #[derive(Clone)] | 15 | #[derive(Clone)] |
| @@ -131,7 +133,8 @@ impl PrEventPolicy { | |||
| 131 | // only be accepted for announcements that have been promoted (validated). | 133 | // only be accepted for announcements that have been promoted (validated). |
| 132 | // If the announcement is still in purgatory, the PR event should also go | 134 | // If the announcement is still in purgatory, the PR event should also go |
| 133 | // to purgatory and wait for the announcement to be promoted. | 135 | // to purgatory and wait for the announcement to be promoted. |
| 134 | let db_repo_data = fetch_repository_data_excluding_purgatory(&self.ctx.database, &identifier).await?; | 136 | let db_repo_data = |
| 137 | fetch_repository_data_excluding_purgatory(&self.ctx.database, &identifier).await?; | ||
| 135 | 138 | ||
| 136 | // Extract owner pubkey from source repo path | 139 | // Extract owner pubkey from source repo path |
| 137 | let owner_pubkey = crate::git::sync::extract_owner_from_repo_path( | 140 | let owner_pubkey = crate::git::sync::extract_owner_from_repo_path( |
| @@ -211,7 +214,8 @@ impl PrEventPolicy { | |||
| 211 | // only be accepted for announcements that have been promoted (validated). | 214 | // only be accepted for announcements that have been promoted (validated). |
| 212 | // If the announcement is still in purgatory, the PR event should also go | 215 | // If the announcement is still in purgatory, the PR event should also go |
| 213 | // to purgatory and wait for the announcement to be promoted. | 216 | // to purgatory and wait for the announcement to be promoted. |
| 214 | let db_repo_data = fetch_repository_data_excluding_purgatory(&self.ctx.database, identifier).await?; | 217 | let db_repo_data = |
| 218 | fetch_repository_data_excluding_purgatory(&self.ctx.database, identifier).await?; | ||
| 215 | 219 | ||
| 216 | // 3. Extract list of maintainers from "a 30617:<maintainer>:<identifier>" tags | 220 | // 3. Extract list of maintainers from "a 30617:<maintainer>:<identifier>" tags |
| 217 | let mut maintainer_pubkeys = std::collections::HashSet::new(); | 221 | let mut maintainer_pubkeys = std::collections::HashSet::new(); |
diff --git a/src/nostr/policy/state.rs b/src/nostr/policy/state.rs index df743ae..80fe84c 100644 --- a/src/nostr/policy/state.rs +++ b/src/nostr/policy/state.rs | |||
| @@ -158,7 +158,11 @@ impl StatePolicy { | |||
| 158 | // authorized it. | 158 | // authorized it. |
| 159 | for owner_hex in &authorized_owners { | 159 | for owner_hex in &authorized_owners { |
| 160 | if let Ok(owner_pk) = nostr_sdk::PublicKey::from_hex(owner_hex) { | 160 | if let Ok(owner_pk) = nostr_sdk::PublicKey::from_hex(owner_hex) { |
| 161 | if self.ctx.purgatory.has_purgatory_announcement(&owner_pk, &state.identifier) { | 161 | if self |
| 162 | .ctx | ||
| 163 | .purgatory | ||
| 164 | .has_purgatory_announcement(&owner_pk, &state.identifier) | ||
| 165 | { | ||
| 162 | self.ctx.purgatory.extend_announcement_expiry( | 166 | self.ctx.purgatory.extend_announcement_expiry( |
| 163 | &owner_pk, | 167 | &owner_pk, |
| 164 | &state.identifier, | 168 | &state.identifier, |
diff --git a/src/purgatory/mod.rs b/src/purgatory/mod.rs index bb6ff54..9b370d2 100644 --- a/src/purgatory/mod.rs +++ b/src/purgatory/mod.rs | |||
| @@ -16,8 +16,14 @@ pub mod persistence; | |||
| 16 | pub mod sync; | 16 | pub mod sync; |
| 17 | mod types; | 17 | mod types; |
| 18 | 18 | ||
| 19 | pub use helpers::{can_apply_state, can_satisfy_state, diagnose_state_mismatch, extract_refs_from_state, get_unpushed_refs}; | 19 | pub use helpers::{ |
| 20 | pub use types::{AnnouncementPurgatoryEntry, EventSource, PrPurgatoryEntry, RefPair, RefUpdate, StatePurgatoryEntry}; | 20 | can_apply_state, can_satisfy_state, diagnose_state_mismatch, extract_refs_from_state, |
| 21 | get_unpushed_refs, | ||
| 22 | }; | ||
| 23 | pub use types::{ | ||
| 24 | AnnouncementPurgatoryEntry, EventSource, PrPurgatoryEntry, RefPair, RefUpdate, | ||
| 25 | StatePurgatoryEntry, | ||
| 26 | }; | ||
| 21 | 27 | ||
| 22 | use dashmap::DashMap; | 28 | use dashmap::DashMap; |
| 23 | use nostr_sdk::prelude::*; | 29 | use nostr_sdk::prelude::*; |
| @@ -672,9 +678,15 @@ impl Purgatory { | |||
| 672 | /// | 678 | /// |
| 673 | /// # Returns | 679 | /// # Returns |
| 674 | /// The announcement entry if found, None otherwise | 680 | /// The announcement entry if found, None otherwise |
| 675 | pub fn find_announcement(&self, owner: &PublicKey, identifier: &str) -> Option<AnnouncementPurgatoryEntry> { | 681 | pub fn find_announcement( |
| 682 | &self, | ||
| 683 | owner: &PublicKey, | ||
| 684 | identifier: &str, | ||
| 685 | ) -> Option<AnnouncementPurgatoryEntry> { | ||
| 676 | let key = (*owner, identifier.to_string()); | 686 | let key = (*owner, identifier.to_string()); |
| 677 | self.announcement_purgatory.get(&key).map(|entry| entry.clone()) | 687 | self.announcement_purgatory |
| 688 | .get(&key) | ||
| 689 | .map(|entry| entry.clone()) | ||
| 678 | } | 690 | } |
| 679 | 691 | ||
| 680 | /// Get all announcements in purgatory for a given identifier. | 692 | /// Get all announcements in purgatory for a given identifier. |
| @@ -687,7 +699,10 @@ impl Purgatory { | |||
| 687 | /// | 699 | /// |
| 688 | /// # Returns | 700 | /// # Returns |
| 689 | /// Vector of announcement entries for this identifier | 701 | /// Vector of announcement entries for this identifier |
| 690 | pub fn get_announcements_by_identifier(&self, identifier: &str) -> Vec<AnnouncementPurgatoryEntry> { | 702 | pub fn get_announcements_by_identifier( |
| 703 | &self, | ||
| 704 | identifier: &str, | ||
| 705 | ) -> Vec<AnnouncementPurgatoryEntry> { | ||
| 691 | self.announcement_purgatory | 706 | self.announcement_purgatory |
| 692 | .iter() | 707 | .iter() |
| 693 | .filter(|entry| entry.key().1 == identifier) | 708 | .filter(|entry| entry.key().1 == identifier) |
| @@ -755,7 +770,12 @@ impl Purgatory { | |||
| 755 | /// * `owner` - The owner pubkey | 770 | /// * `owner` - The owner pubkey |
| 756 | /// * `identifier` - The repository identifier | 771 | /// * `identifier` - The repository identifier |
| 757 | /// * `duration` - Minimum duration to guarantee from now | 772 | /// * `duration` - Minimum duration to guarantee from now |
| 758 | pub fn extend_announcement_expiry(&self, owner: &PublicKey, identifier: &str, duration: Duration) { | 773 | pub fn extend_announcement_expiry( |
| 774 | &self, | ||
| 775 | owner: &PublicKey, | ||
| 776 | identifier: &str, | ||
| 777 | duration: Duration, | ||
| 778 | ) { | ||
| 759 | let key = (*owner, identifier.to_string()); | 779 | let key = (*owner, identifier.to_string()); |
| 760 | 780 | ||
| 761 | // Collect revival info before taking a mutable borrow | 781 | // Collect revival info before taking a mutable borrow |
| @@ -977,16 +997,24 @@ impl Purgatory { | |||
| 977 | .map(|entry| { | 997 | .map(|entry| { |
| 978 | let key = entry.key(); | 998 | let key = entry.key(); |
| 979 | let v = entry.value(); | 999 | let v = entry.value(); |
| 980 | (key.0.clone(), key.1.clone(), v.repo_path.clone(), v.event.id, v.soft_expired) | 1000 | ( |
| 1001 | key.0, | ||
| 1002 | key.1.clone(), | ||
| 1003 | v.repo_path.clone(), | ||
| 1004 | v.event.id, | ||
| 1005 | v.soft_expired, | ||
| 1006 | ) | ||
| 981 | }) | 1007 | }) |
| 982 | .collect(); | 1008 | .collect(); |
| 983 | 1009 | ||
| 984 | let mut announcement_removed = 0; | 1010 | let mut announcement_removed = 0; |
| 985 | for (owner, identifier, repo_path, event_id, already_soft_expired) in expired_announcements { | 1011 | for (owner, identifier, repo_path, event_id, already_soft_expired) in expired_announcements |
| 1012 | { | ||
| 986 | if already_soft_expired { | 1013 | if already_soft_expired { |
| 987 | // Phase 2: fully remove | 1014 | // Phase 2: fully remove |
| 988 | self.mark_expired(event_id); | 1015 | self.mark_expired(event_id); |
| 989 | self.announcement_purgatory.remove(&(owner.clone(), identifier.clone())); | 1016 | self.announcement_purgatory |
| 1017 | .remove(&(owner, identifier.clone())); | ||
| 990 | announcement_removed += 1; | 1018 | announcement_removed += 1; |
| 991 | tracing::info!( | 1019 | tracing::info!( |
| 992 | owner = %owner, | 1020 | owner = %owner, |
| @@ -1026,7 +1054,10 @@ impl Purgatory { | |||
| 1026 | 1054 | ||
| 1027 | if repo_gone { | 1055 | if repo_gone { |
| 1028 | // Mark soft_expired and extend expiry | 1056 | // Mark soft_expired and extend expiry |
| 1029 | if let Some(mut entry) = self.announcement_purgatory.get_mut(&(owner.clone(), identifier.clone())) { | 1057 | if let Some(mut entry) = self |
| 1058 | .announcement_purgatory | ||
| 1059 | .get_mut(&(owner, identifier.clone())) | ||
| 1060 | { | ||
| 1030 | entry.soft_expired = true; | 1061 | entry.soft_expired = true; |
| 1031 | entry.expires_at = now + SOFT_EXPIRY_EXTENDED; | 1062 | entry.expires_at = now + SOFT_EXPIRY_EXTENDED; |
| 1032 | } | 1063 | } |
diff --git a/src/sync/mod.rs b/src/sync/mod.rs index cd62380..36142e3 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs | |||
| @@ -2406,7 +2406,8 @@ impl SyncManager { | |||
| 2406 | } | 2406 | } |
| 2407 | 2407 | ||
| 2408 | // Register any new entries in repo_sync_index as StateOnly | 2408 | // Register any new entries in repo_sync_index as StateOnly |
| 2409 | let mut new_relay_urls: std::collections::HashSet<String> = std::collections::HashSet::new(); | 2409 | let mut new_relay_urls: std::collections::HashSet<String> = |
| 2410 | std::collections::HashSet::new(); | ||
| 2410 | { | 2411 | { |
| 2411 | let mut index = self.repo_sync_index.write().await; | 2412 | let mut index = self.repo_sync_index.write().await; |
| 2412 | for (repo_id, relays) in &announcements { | 2413 | for (repo_id, relays) in &announcements { |