upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/audit_cleanup.rs149
-rw-r--r--src/lib.rs1
-rw-r--r--src/main.rs9
3 files changed, 159 insertions, 0 deletions
diff --git a/src/audit_cleanup.rs b/src/audit_cleanup.rs
new file mode 100644
index 0000000..b976b1f
--- /dev/null
+++ b/src/audit_cleanup.rs
@@ -0,0 +1,149 @@
1//! Audit Event Cleanup
2//!
3//! Background job that periodically removes grasp-audit test events and their
4//! associated git repositories from the relay.
5//!
6//! The grasp-audit tool tags every event it creates with:
7//! `["t", "grasp-audit-test-event"]`
8//!
9//! This job:
10//! 1. Queries kind 30617 (repo announcement) events tagged "grasp-audit-test-event"
11//! that are older than `AUDIT_CLEANUP_AGE_SECS` seconds.
12//! 2. Deletes the corresponding bare git repositories from disk.
13//! 3. Deletes all events tagged "grasp-audit-test-event" older than the threshold
14//! from the Nostr database.
15//!
16//! Runs every `AUDIT_CLEANUP_INTERVAL_SECS` seconds.
17
18use std::path::PathBuf;
19use std::time::Duration;
20
21use nostr_sdk::prelude::*;
22use tracing::{debug, error, info, warn};
23
24use crate::nostr::builder::SharedDatabase;
25use crate::nostr::events::RepositoryAnnouncement;
26
27/// How old an audit event must be before it is eligible for deletion (2 hours).
28const AUDIT_CLEANUP_AGE_SECS: u64 = 2 * 3600;
29
30/// How often the cleanup job runs (30 minutes).
31const AUDIT_CLEANUP_INTERVAL_SECS: u64 = 30 * 60;
32
33/// The hashtag used by grasp-audit to mark all test events.
34const AUDIT_TEST_EVENT_TAG: &str = "grasp-audit-test-event";
35
36/// Run the audit cleanup loop indefinitely.
37///
38/// Spawned as a background tokio task in `main.rs`.
39pub async fn run_audit_cleanup_loop(database: SharedDatabase, git_data_path: PathBuf) {
40 // Use an interval that fires immediately on the first tick, then every 30 minutes.
41 let mut interval = tokio::time::interval(Duration::from_secs(AUDIT_CLEANUP_INTERVAL_SECS));
42 loop {
43 interval.tick().await;
44 run_audit_cleanup_once(&database, &git_data_path).await;
45 }
46}
47
48/// Perform a single cleanup pass.
49async fn run_audit_cleanup_once(database: &SharedDatabase, git_data_path: &PathBuf) {
50 let cutoff = Timestamp::from(Timestamp::now().as_secs().saturating_sub(AUDIT_CLEANUP_AGE_SECS));
51
52 // --- Step 1: Find repo announcements to delete git repos for ---
53 let repo_filter = Filter::new()
54 .kind(Kind::GitRepoAnnouncement)
55 .hashtag(AUDIT_TEST_EVENT_TAG)
56 .until(cutoff);
57
58 let repo_events = match database.query(repo_filter).await {
59 Ok(events) => events,
60 Err(e) => {
61 error!("audit_cleanup: failed to query repo announcements: {}", e);
62 return;
63 }
64 };
65
66 let mut repos_deleted = 0usize;
67 let mut repos_failed = 0usize;
68
69 for event in repo_events.iter() {
70 match RepositoryAnnouncement::from_event(event.clone()) {
71 Ok(announcement) => {
72 let repo_path = git_data_path.join(announcement.repo_path());
73 if repo_path.exists() {
74 match std::fs::remove_dir_all(&repo_path) {
75 Ok(()) => {
76 debug!(
77 "audit_cleanup: deleted git repo {}",
78 repo_path.display()
79 );
80 repos_deleted += 1;
81
82 // Remove the parent npub directory if it is now empty
83 if let Some(npub_dir) = repo_path.parent() {
84 if npub_dir.exists() {
85 match std::fs::read_dir(npub_dir) {
86 Ok(mut entries) => {
87 if entries.next().is_none() {
88 if let Err(e) = std::fs::remove_dir(npub_dir) {
89 warn!(
90 "audit_cleanup: could not remove empty npub dir {}: {}",
91 npub_dir.display(),
92 e
93 );
94 }
95 }
96 }
97 Err(e) => {
98 warn!(
99 "audit_cleanup: could not read npub dir {}: {}",
100 npub_dir.display(),
101 e
102 );
103 }
104 }
105 }
106 }
107 }
108 Err(e) => {
109 warn!(
110 "audit_cleanup: failed to delete git repo {}: {}",
111 repo_path.display(),
112 e
113 );
114 repos_failed += 1;
115 }
116 }
117 } else {
118 debug!(
119 "audit_cleanup: git repo already absent: {}",
120 repo_path.display()
121 );
122 }
123 }
124 Err(e) => {
125 warn!(
126 "audit_cleanup: could not parse repo announcement {}: {}",
127 event.id, e
128 );
129 }
130 }
131 }
132
133 // --- Step 2: Delete all audit events from the database ---
134 let all_audit_filter = Filter::new()
135 .hashtag(AUDIT_TEST_EVENT_TAG)
136 .until(cutoff);
137
138 match database.delete(all_audit_filter).await {
139 Ok(()) => {
140 info!(
141 "audit_cleanup: deleted audit events older than {}s; git repos deleted={}, failed={}",
142 AUDIT_CLEANUP_AGE_SECS, repos_deleted, repos_failed
143 );
144 }
145 Err(e) => {
146 error!("audit_cleanup: failed to delete audit events from database: {}", e);
147 }
148 }
149}
diff --git a/src/lib.rs b/src/lib.rs
index 8befd6f..d0e2c2d 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,3 +1,4 @@
1pub mod audit_cleanup;
1pub mod config; 2pub mod config;
2pub mod git; 3pub mod git;
3pub mod http; 4pub mod http;
diff --git a/src/main.rs b/src/main.rs
index bf3aefb..12a875c 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -7,6 +7,7 @@ use tracing::{error, info, warn};
7use tracing_subscriber::{EnvFilter, FmtSubscriber}; 7use tracing_subscriber::{EnvFilter, FmtSubscriber};
8 8
9use ngit_grasp::{ 9use ngit_grasp::{
10 audit_cleanup,
10 config::{Config, DatabaseBackend}, 11 config::{Config, DatabaseBackend},
11 git, http, 12 git, http,
12 metrics::Metrics, 13 metrics::Metrics,
@@ -175,6 +176,14 @@ async fn main() -> Result<()> {
175 }); 176 });
176 info!("Expired event cleanup task started (24h interval, keeps 7 days)"); 177 info!("Expired event cleanup task started (24h interval, keeps 7 days)");
177 178
179 // Spawn audit event cleanup task (30m interval, removes events >2h old)
180 let audit_db = relay_with_db.database.clone();
181 let audit_git_path = PathBuf::from(config.effective_git_data_path());
182 tokio::spawn(async move {
183 audit_cleanup::run_audit_cleanup_loop(audit_db, audit_git_path).await;
184 });
185 info!("Audit event cleanup task started (30m interval, removes events >2h old)");
186
178 // Start purgatory sync loop for background git data fetching 187 // Start purgatory sync loop for background git data fetching
179 // Create naughty list tracker for git remote domains with persistent errors (12h expiration) 188 // Create naughty list tracker for git remote domains with persistent errors (12h expiration)
180 let git_naughty_list = Arc::new(NaughtyListTracker::with_defaults()); 189 let git_naughty_list = Arc::new(NaughtyListTracker::with_defaults());