From ba4c5edc7fbe81b06f93919708a054c2536fc615 Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Mon, 17 Jun 2024 22:16:08 -0700 Subject: [PATCH] add some janky admin commands --- src/service/admin.rs | 110 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 109 insertions(+), 1 deletion(-) diff --git a/src/service/admin.rs b/src/service/admin.rs index 76b6dc15..fc9857d8 100644 --- a/src/service/admin.rs +++ b/src/service/admin.rs @@ -1,4 +1,9 @@ -use std::{collections::BTreeMap, fmt::Write, sync::Arc, time::Instant}; +use std::{ + collections::{BTreeMap, HashSet}, + fmt::Write, + sync::Arc, + time::Instant, +}; use clap::Parser; use regex::Regex; @@ -121,6 +126,23 @@ enum AdminCommand { event_id: Box, }, + /// Dump auth chain events stored in the db in graphviz DOT format + /// + /// Does not validate anything. Events that are missing or generate an + /// error when we try to load them from the db are marked in red. + GetAuthChainGraph { + /// An event ID (the $ character followed by the base64 reference hash) + event_ids: Vec>, + }, + + /// Dump all events in a given events auth chain in jsonl format + /// + /// Does not validate anything. + GetAuthChainEvents { + /// An event ID (the $ character followed by the base64 reference hash) + event_ids: Vec>, + }, + #[command(verbatim_doc_comment)] /// Parse and print a PDU from a JSON /// @@ -536,6 +558,92 @@ impl Service { RoomMessageEventContent::text_plain("Event not found.") } } + AdminCommand::GetAuthChainGraph { + event_ids, + } => { + let mut dot = String::new(); + writeln!(&mut dot, "digraph G {{").unwrap(); + let mut todo = event_ids + .into_iter() + .map(Arc::::from) + .collect::>(); + let mut done = HashSet::new(); + let mut edges = HashSet::new(); + while let Some(event_id) = todo.pop() { + done.insert(event_id.clone()); + if let Ok(Some(pdu)) = + services().rooms.timeline.get_pdu(&event_id) + { + writeln!(&mut dot, " {event_id:?};").unwrap(); + for auth_event in &pdu.auth_events { + let edge = ( + Arc::clone(auth_event), + Arc::clone(&event_id), + ); + if !edges.contains(&edge) { + writeln!( + &mut dot, + " {auth_event:?} -> {event_id:?};" + ) + .unwrap(); + edges.insert(edge); + } + if !done.contains(&**auth_event) { + todo.push(Arc::clone(auth_event)); + } + } + } else { + writeln!(&mut dot, " {event_id:?}[color=\"red\"];") + .unwrap(); + } + } + write!(&mut dot, "}}").unwrap(); + RoomMessageEventContent::text_html( + format!("\n```dot\n{dot}\n```"), + format!( + "
{}\n
\n", + html_escape::encode_safe(&dot) + ), + ) + } + AdminCommand::GetAuthChainEvents { + event_ids, + } => { + let mut jsonl = String::new(); + let mut todo = event_ids + .into_iter() + .map(Arc::::from) + .collect::>(); + let mut done = HashSet::new(); + while let Some(event_id) = todo.pop() { + done.insert(event_id.clone()); + if let Ok(Some(pdu_json)) = + services().rooms.timeline.get_pdu_json(&event_id) + { + jsonl.push_str( + &serde_json::to_string(&pdu_json).unwrap(), + ); + jsonl.push('\n'); + } + if let Ok(Some(pdu)) = + services().rooms.timeline.get_pdu(&event_id) + { + for auth_event in &pdu.auth_events { + if !done.contains(&**auth_event) { + todo.push(Arc::clone(auth_event)); + } + } + } + } + RoomMessageEventContent::text_html( + format!("\n```json\n{jsonl}\n```"), + format!( + "
{}\n
\n", + html_escape::encode_safe(&jsonl) + ), + ) + } AdminCommand::ParsePdu => { if body.len() > 2 && body[0].trim() == "```"