upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src/nostr/builder.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/nostr/builder.rs')
-rw-r--r--src/nostr/builder.rs116
1 files changed, 102 insertions, 14 deletions
diff --git a/src/nostr/builder.rs b/src/nostr/builder.rs
index 8dd6291..2b4d524 100644
--- a/src/nostr/builder.rs
+++ b/src/nostr/builder.rs
@@ -13,7 +13,7 @@ use nostr_relay_builder::prelude::*;
13 13
14use crate::config::{Config, DatabaseBackend}; 14use crate::config::{Config, DatabaseBackend};
15use crate::nostr::events::{ 15use crate::nostr::events::{
16 RepositoryAnnouncement, RepositoryState, KIND_PR, KIND_PR_UPDATE, KIND_REPOSITORY_ANNOUNCEMENT, 16 RepositoryAnnouncement, KIND_PR, KIND_PR_UPDATE, KIND_REPOSITORY_ANNOUNCEMENT,
17 KIND_REPOSITORY_STATE, KIND_USER_GRASP_LIST, 17 KIND_REPOSITORY_STATE, KIND_USER_GRASP_LIST,
18}; 18};
19use crate::nostr::policy::{ 19use crate::nostr::policy::{
@@ -57,8 +57,9 @@ impl Nip34WritePolicy {
57 domain: impl Into<String>, 57 domain: impl Into<String>,
58 database: SharedDatabase, 58 database: SharedDatabase,
59 git_data_path: impl Into<std::path::PathBuf>, 59 git_data_path: impl Into<std::path::PathBuf>,
60 purgatory: std::sync::Arc<crate::purgatory::Purgatory>,
60 ) -> Self { 61 ) -> Self {
61 let ctx = PolicyContext::new(domain, database, git_data_path); 62 let ctx = PolicyContext::new(domain, database, git_data_path, purgatory);
62 Self { 63 Self {
63 announcement_policy: AnnouncementPolicy::new(ctx.clone()), 64 announcement_policy: AnnouncementPolicy::new(ctx.clone()),
64 state_policy: StatePolicy::new(ctx.clone()), 65 state_policy: StatePolicy::new(ctx.clone()),
@@ -143,21 +144,50 @@ impl Nip34WritePolicy {
143 144
144 match self.state_policy.validate(event) { 145 match self.state_policy.validate(event) {
145 StateResult::Accept => { 146 StateResult::Accept => {
146 // Parse state to get HEAD and branch info 147 // Parse state to get identifier for purgatory message
147 match RepositoryState::from_event(event.clone()) { 148 let identifier = event
148 Ok(_state) => { 149 .tags
149 // Process state alignment asynchronously 150 .iter()
150 if let Err(e) = self.state_policy.process_state_event(event).await { 151 .find_map(|tag| {
151 tracing::warn!("Failed to process state event {}: {}", event_id_str, e); 152 let tag_vec = tag.clone().to_vec();
153 if tag_vec.len() >= 2 && tag_vec[0] == "d" {
154 Some(tag_vec[1].clone())
155 } else {
156 None
152 } 157 }
158 })
159 .unwrap_or_else(|| "unknown".to_string());
153 160
154 tracing::debug!("Accepted repository state: {}", event_id_str); 161 // Process state alignment asynchronously
162 match self.state_policy.process_state_event(event).await {
163 Ok(0) => {
164 // No repos aligned - event was added to purgatory
165 tracing::info!(
166 "State event {} added to purgatory: waiting for git data for identifier {}",
167 event_id_str,
168 identifier
169 );
170 WritePolicyResult::Reject {
171 status: true, // Client sees OK
172 message: format!(
173 "purgatory: state event stored, waiting for git push for {}",
174 identifier
175 )
176 .into(),
177 }
178 }
179 Ok(count) => {
180 // Successfully aligned repos
181 tracing::debug!(
182 "Accepted repository state {}: aligned {} repo(s)",
183 event_id_str,
184 count
185 );
155 WritePolicyResult::Accept 186 WritePolicyResult::Accept
156 } 187 }
157 Err(e) => { 188 Err(e) => {
158 tracing::warn!("Failed to parse repository state {}: {}", event_id_str, e); 189 tracing::warn!("Failed to process state event {}: {}", event_id_str, e);
159 // Still accept the event even if we can't parse it 190 // Still accept the event even if processing failed
160 // The validation passed, so it's structurally valid
161 WritePolicyResult::Accept 191 WritePolicyResult::Accept
162 } 192 }
163 } 193 }
@@ -173,6 +203,58 @@ impl Nip34WritePolicy {
173 async fn handle_pr_event(&self, event: &Event) -> WritePolicyResult { 203 async fn handle_pr_event(&self, event: &Event) -> WritePolicyResult {
174 let event_id_str = event.id.to_bech32().unwrap_or_else(|_| event.id.to_hex()); 204 let event_id_str = event.id.to_bech32().unwrap_or_else(|_| event.id.to_hex());
175 205
206 // Check if git data exists (checks placeholders and commit existence)
207 match self.pr_event_policy.check_git_data_exists(event).await {
208 Ok(false) => {
209 // No git data exists - add to purgatory
210 let commit = event
211 .tags
212 .iter()
213 .find_map(|tag| {
214 let tag_vec = tag.clone().to_vec();
215 if tag_vec.len() >= 2 && tag_vec[0] == "c" {
216 Some(tag_vec[1].clone())
217 } else {
218 None
219 }
220 })
221 .unwrap_or_else(|| "unknown".to_string());
222
223 tracing::info!(
224 "PR event {} added to purgatory: waiting for git push with commit {}",
225 event_id_str,
226 commit
227 );
228
229 // Add to purgatory
230 self.ctx
231 .purgatory
232 .add_pr(event.clone(), event.id.to_hex(), commit.clone());
233
234 return WritePolicyResult::Reject {
235 status: true, // Client sees OK
236 message: format!(
237 "purgatory: PR event stored, waiting for git push with commit {}",
238 commit
239 )
240 .into(),
241 };
242 }
243 Ok(true) => {
244 // Git data exists - proceed with normal validation
245 tracing::debug!("Git data exists for PR event {}", event_id_str);
246 }
247 Err(e) => {
248 // Error checking git data - reject event
249 tracing::warn!(
250 "Failed to check git data for PR event {}: {}",
251 event_id_str,
252 e
253 );
254 return WritePolicyResult::reject(format!("Failed to check git data: {}", e));
255 }
256 }
257
176 // Validate refs/nostr refs for this PR event 258 // Validate refs/nostr refs for this PR event
177 // This deletes any refs/nostr/<event-id> that points to wrong commit 259 // This deletes any refs/nostr/<event-id> that points to wrong commit
178 if let Err(e) = self.pr_event_policy.validate_nostr_ref(event).await { 260 if let Err(e) = self.pr_event_policy.validate_nostr_ref(event).await {
@@ -289,7 +371,10 @@ pub struct RelayWithDatabase {
289/// Returns a `RelayWithDatabase` struct containing: 371/// Returns a `RelayWithDatabase` struct containing:
290/// - The `LocalRelay` for handling WebSocket connections 372/// - The `LocalRelay` for handling WebSocket connections
291/// - The `SharedDatabase` for direct database queries (e.g., push authorization) 373/// - The `SharedDatabase` for direct database queries (e.g., push authorization)
292pub async fn create_relay(config: &Config) -> Result<RelayWithDatabase> { 374pub async fn create_relay(
375 config: &Config,
376 purgatory: Arc<crate::purgatory::Purgatory>,
377) -> Result<RelayWithDatabase> {
293 tracing::info!("Configuring nostr relay with GRASP-01 validation..."); 378 tracing::info!("Configuring nostr relay with GRASP-01 validation...");
294 379
295 // Determine database path 380 // Determine database path
@@ -337,7 +422,10 @@ pub async fn create_relay(config: &Config) -> Result<RelayWithDatabase> {
337 // Build relay with GRASP-01 validation 422 // Build relay with GRASP-01 validation
338 // Clone Arc for the write policy so both relay and policy can access the database 423 // Clone Arc for the write policy so both relay and policy can access the database
339 let git_data_path = config.effective_git_data_path(); 424 let git_data_path = config.effective_git_data_path();
340 let write_policy = Nip34WritePolicy::new(&config.domain, database.clone(), &git_data_path); 425
426 // Create write policy with purgatory integration
427 let write_policy =
428 Nip34WritePolicy::new(&config.domain, database.clone(), &git_data_path, purgatory);
341 429
342 let relay = LocalRelayBuilder::default() 430 let relay = LocalRelayBuilder::default()
343 .database(database.clone()) 431 .database(database.clone())