upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src/git
diff options
context:
space:
mode:
Diffstat (limited to 'src/git')
-rw-r--r--src/git/authorization.rs38
-rw-r--r--src/git/sync.rs110
2 files changed, 145 insertions, 3 deletions
diff --git a/src/git/authorization.rs b/src/git/authorization.rs
index e174b51..9d53c4f 100644
--- a/src/git/authorization.rs
+++ b/src/git/authorization.rs
@@ -287,6 +287,39 @@ pub async fn fetch_repository_data(
287 }) 287 })
288} 288}
289 289
290/// Fetch repository data including announcements from purgatory
291///
292/// This combines database announcements with purgatory announcements,
293/// which is needed for authorization when the announcement hasn't been
294/// promoted yet (no git data has arrived).
295pub async fn fetch_repository_data_with_purgatory(
296 database: &SharedDatabase,
297 purgatory: &crate::purgatory::Purgatory,
298 identifier: &str,
299) -> Result<RepositoryData> {
300 // First, fetch from database
301 let mut repo_data = fetch_repository_data(database, identifier).await?;
302
303 // Then, add announcements from purgatory
304 let purgatory_announcements = purgatory.get_announcements_by_identifier(identifier);
305 let purgatory_count = purgatory_announcements.len();
306
307 for entry in purgatory_announcements {
308 if let Ok(announcement) = RepositoryAnnouncement::from_event(entry.event) {
309 repo_data.announcements.push(announcement);
310 }
311 }
312
313 debug!(
314 "Fetched repository data with purgatory: {} announcements ({} from purgatory), {} states",
315 repo_data.announcements.len(),
316 purgatory_count,
317 repo_data.states.len()
318 );
319
320 Ok(repo_data)
321}
322
290pub fn pubkey_authorised_for_repo_owners( 323pub fn pubkey_authorised_for_repo_owners(
291 pubkey: &PublicKey, 324 pubkey: &PublicKey,
292 db_repo_data: &RepositoryData, 325 db_repo_data: &RepositoryData,
@@ -539,8 +572,9 @@ pub async fn get_state_authorization_for_specific_owner_repo(
539 use crate::git::list_refs; 572 use crate::git::list_refs;
540 use crate::purgatory::RefUpdate; 573 use crate::purgatory::RefUpdate;
541 574
542 // Fetch announcements only - we don't need database states 575 // Fetch announcements from database AND purgatory - needed for authorization
543 let repo_data = fetch_repository_data(database, identifier).await?; 576 // when the announcement hasn't been promoted yet (no git data has arrived)
577 let repo_data = fetch_repository_data_with_purgatory(database, purgatory, identifier).await?;
544 578
545 if repo_data.announcements.is_empty() { 579 if repo_data.announcements.is_empty() {
546 return Ok(AuthorizationResult::denied( 580 return Ok(AuthorizationResult::denied(
diff --git a/src/git/sync.rs b/src/git/sync.rs
index e8e9655..13f30b6 100644
--- a/src/git/sync.rs
+++ b/src/git/sync.rs
@@ -51,6 +51,8 @@ use crate::purgatory::{can_apply_state, Purgatory};
51/// or from purgatory sync fetching OIDs from remote servers). 51/// or from purgatory sync fetching OIDs from remote servers).
52#[derive(Debug, Default, Clone)] 52#[derive(Debug, Default, Clone)]
53pub struct ProcessResult { 53pub struct ProcessResult {
54 /// Number of announcements released from purgatory
55 pub announcements_released: usize,
54 /// Number of state events released from purgatory 56 /// Number of state events released from purgatory
55 pub states_released: usize, 57 pub states_released: usize,
56 /// Number of PR events released from purgatory 58 /// Number of PR events released from purgatory
@@ -70,11 +72,12 @@ pub struct ProcessResult {
70impl ProcessResult { 72impl ProcessResult {
71 /// Check if any events were released 73 /// Check if any events were released
72 pub fn released_any(&self) -> bool { 74 pub fn released_any(&self) -> bool {
73 self.states_released > 0 || self.prs_released > 0 75 self.announcements_released > 0 || self.states_released > 0 || self.prs_released > 0
74 } 76 }
75 77
76 /// Merge another ProcessResult into this one 78 /// Merge another ProcessResult into this one
77 pub fn merge(&mut self, other: ProcessResult) { 79 pub fn merge(&mut self, other: ProcessResult) {
80 self.announcements_released += other.announcements_released;
78 self.states_released += other.states_released; 81 self.states_released += other.states_released;
79 self.prs_released += other.prs_released; 82 self.prs_released += other.prs_released;
80 self.repos_synced += other.repos_synced; 83 self.repos_synced += other.repos_synced;
@@ -836,6 +839,18 @@ pub async fn process_newly_available_git_data(
836 "Processing newly available git data" 839 "Processing newly available git data"
837 ); 840 );
838 841
842 // Process announcements from purgatory
843 let announcement_result = process_purgatory_announcements(
844 &identifier,
845 source_repo_path,
846 database,
847 local_relay,
848 purgatory,
849 git_data_path,
850 )
851 .await;
852 result.merge(announcement_result);
853
839 // Process state events from purgatory 854 // Process state events from purgatory
840 let state_result = process_purgatory_state_events( 855 let state_result = process_purgatory_state_events(
841 &identifier, 856 &identifier,
@@ -863,6 +878,7 @@ pub async fn process_newly_available_git_data(
863 if result.released_any() { 878 if result.released_any() {
864 info!( 879 info!(
865 identifier = %identifier, 880 identifier = %identifier,
881 announcements_released = result.announcements_released,
866 states_released = result.states_released, 882 states_released = result.states_released,
867 prs_released = result.prs_released, 883 prs_released = result.prs_released,
868 repos_synced = result.repos_synced, 884 repos_synced = result.repos_synced,
@@ -1250,6 +1266,90 @@ async fn process_purgatory_pr_events(
1250 result 1266 result
1251} 1267}
1252 1268
1269/// Process announcements from purgatory that can now be promoted.
1270///
1271/// When git data arrives for a repository, any announcements in purgatory
1272/// for that repository should be promoted to the database and served to clients.
1273async fn process_purgatory_announcements(
1274 identifier: &str,
1275 source_repo_path: &Path,
1276 database: &SharedDatabase,
1277 local_relay: Option<&nostr_relay_builder::LocalRelay>,
1278 purgatory: &Purgatory,
1279 git_data_path: &Path,
1280) -> ProcessResult {
1281 let mut result = ProcessResult::default();
1282
1283 // Extract owner pubkey from the source repo path
1284 let owner_pubkey = match extract_owner_from_repo_path(source_repo_path, git_data_path) {
1285 Some(npub) => npub,
1286 None => {
1287 debug!(
1288 identifier = %identifier,
1289 "Could not extract owner from repo path"
1290 );
1291 return result;
1292 }
1293 };
1294
1295 // Parse the npub back to PublicKey
1296 let owner = match nostr_sdk::PublicKey::parse(&owner_pubkey) {
1297 Ok(pk) => pk,
1298 Err(e) => {
1299 warn!(
1300 identifier = %identifier,
1301 owner_pubkey = %owner_pubkey,
1302 error = %e,
1303 "Failed to parse owner pubkey"
1304 );
1305 result.errors.push(format!("Failed to parse owner pubkey: {}", e));
1306 return result;
1307 }
1308 };
1309
1310 // Check if there's an announcement in purgatory for this owner and identifier
1311 let announcement_event = purgatory.promote_announcement(&owner, identifier);
1312
1313 if let Some(event) = announcement_event {
1314 // Save to database
1315 match database.save_event(&event).await {
1316 Ok(_) => {
1317 info!(
1318 identifier = %identifier,
1319 event_id = %event.id,
1320 "Promoted announcement from purgatory to database"
1321 );
1322
1323 // Notify WebSocket subscribers
1324 if let Some(relay) = local_relay {
1325 if relay.notify_event(event.clone()) {
1326 debug!(
1327 identifier = %identifier,
1328 event_id = %event.id,
1329 "Broadcast announcement event to WebSocket listeners"
1330 );
1331 }
1332 }
1333
1334 result.announcements_released += 1;
1335 }
1336 Err(e) => {
1337 warn!(
1338 identifier = %identifier,
1339 event_id = %event.id,
1340 error = %e,
1341 "Failed to save announcement to database"
1342 );
1343 result
1344 .errors
1345 .push(format!("Failed to save announcement: {}", e));
1346 }
1347 }
1348 }
1349
1350 result
1351}
1352
1253/// Extract owner pubkey from a repository path. 1353/// Extract owner pubkey from a repository path.
1254/// 1354///
1255/// Given a path like `{git_data_path}/{npub}/{identifier}.git`, extracts the npub. 1355/// Given a path like `{git_data_path}/{npub}/{identifier}.git`, extracts the npub.
@@ -1271,6 +1371,7 @@ mod tests {
1271 #[test] 1371 #[test]
1272 fn test_process_result_default() { 1372 fn test_process_result_default() {
1273 let result = ProcessResult::default(); 1373 let result = ProcessResult::default();
1374 assert_eq!(result.announcements_released, 0);
1274 assert_eq!(result.states_released, 0); 1375 assert_eq!(result.states_released, 0);
1275 assert_eq!(result.prs_released, 0); 1376 assert_eq!(result.prs_released, 0);
1276 assert_eq!(result.repos_synced, 0); 1377 assert_eq!(result.repos_synced, 0);
@@ -1282,6 +1383,10 @@ mod tests {
1282 let mut result = ProcessResult::default(); 1383 let mut result = ProcessResult::default();
1283 assert!(!result.released_any()); 1384 assert!(!result.released_any());
1284 1385
1386 result.announcements_released = 1;
1387 assert!(result.released_any());
1388
1389 result.announcements_released = 0;
1285 result.states_released = 1; 1390 result.states_released = 1;
1286 assert!(result.released_any()); 1391 assert!(result.released_any());
1287 1392
@@ -1293,6 +1398,7 @@ mod tests {
1293 #[test] 1398 #[test]
1294 fn test_process_result_merge() { 1399 fn test_process_result_merge() {
1295 let mut result1 = ProcessResult { 1400 let mut result1 = ProcessResult {
1401 announcements_released: 0,
1296 states_released: 1, 1402 states_released: 1,
1297 prs_released: 2, 1403 prs_released: 2,
1298 repos_synced: 3, 1404 repos_synced: 3,
@@ -1303,6 +1409,7 @@ mod tests {
1303 }; 1409 };
1304 1410
1305 let result2 = ProcessResult { 1411 let result2 = ProcessResult {
1412 announcements_released: 5,
1306 states_released: 10, 1413 states_released: 10,
1307 prs_released: 20, 1414 prs_released: 20,
1308 repos_synced: 30, 1415 repos_synced: 30,
@@ -1314,6 +1421,7 @@ mod tests {
1314 1421
1315 result1.merge(result2); 1422 result1.merge(result2);
1316 1423
1424 assert_eq!(result1.announcements_released, 5);
1317 assert_eq!(result1.states_released, 11); 1425 assert_eq!(result1.states_released, 11);
1318 assert_eq!(result1.prs_released, 22); 1426 assert_eq!(result1.prs_released, 22);
1319 assert_eq!(result1.repos_synced, 33); 1427 assert_eq!(result1.repos_synced, 33);