| 1 | |
|
| 2 | |
|
| 3 | | use super::{ui, Ctx}; |
| 4 | | use anyhow::{Context, Result}; |
| 5 | | use spectra_core::ledger::Ledger; |
| 6 | | use spectra_core::principals::Principals; |
| 7 | |
|
| 8 | |
|
| 9 | |
|
| 10 | 22× | pub async fn verify(ctx: &Ctx, json: bool) -> Result<i32> { |
| 11 | 22× | let spin = if json { None } else { Some(ui::spinner("verifying the ledger")) }; |
| 12 | | |
| 13 | 22× | let v: serde_json::Value = match ctx.remote()? { |
| 14 | | Some(_) => { |
| 15 | 1× | let home = super::identity::owner_home(ctx, None); |
| 16 | 1× | let c = ctx.owner_client(&home).await?.context("no server")?; |
| 17 | 1× | super::call(&c, "GET", "/api/v1/admin/verify", None).await? |
| 18 | | } |
| 19 | 21× | None => serde_json::to_value(Ledger::open_read_only(&ctx.db()?).await?.verify(&mut Principals::default()).await?)?, |
| 20 | | }; |
| 21 | 21× | if let Some(s) = spin { |
| 22 | 10× | s.clear(); |
| 23 | 11× | } |
| 24 | 21× | let findings = v["findings"].as_array().cloned().unwrap_or_default(); |
| 25 | 21× | let clean = findings.is_empty(); |
| 26 | 21× | if json { |
| 27 | 11× | println!("{v}"); |
| 28 | 11× | return Ok(if clean { 0 } else { 1 }); |
| 29 | 10× | } |
| 30 | 10× | if clean { |
| 31 | 9× | println!("{} {}", ui::done("Verified"), ui::subject("the ledger")); |
| 32 | 9× | } else { |
| 33 | 1× | println!( |
| 34 | 1× | "{} {} {}", |
| 35 | 1× | ui::err(ui::glyph("✘", "x")), |
| 36 | 1× | ui::err("The ledger does not verify:"), |
| 37 | 1× | ui::err(format!("{} finding(s)", findings.len())) |
| 38 | 1× | ); |
| 39 | 1× | } |
| 40 | 10× | println!("{}", ui::kv("events", v["events"].as_u64().unwrap_or(0))); |
| 41 | 10× | let last = v["last_checkpoint"].as_u64().map(|s| format!("last at #{s}")).unwrap_or_else(|| "none yet".into()); |
| 42 | 10× | println!("{}", ui::kv("signed", format!("{} checkpoint(s){}{last}", v["checkpoints"].as_u64().unwrap_or(0), ui::sep()))); |
| 43 | 10× | let uncovered = v["uncovered"].as_u64().unwrap_or(0); |
| 44 | 10× | if uncovered > 0 { |
| 45 | 0× | println!("{}", ui::kv("pending", ui::dim(format!("{uncovered} event(s) since, chained and awaiting the next checkpoint")))); |
| 46 | 10× | } |
| 47 | 10× | println!("{}", ui::kv("head", ui::hash(v["head"].as_str().unwrap_or("")))); |
| 48 | 10× | for f in &findings { |
| 49 | | |
| 50 | 1× | let problem = match &f["problem"] { |
| 51 | 1× | serde_json::Value::String(p) => p |
| 52 | 1× | .split('_') |
| 53 | 2× | .map(|w| { |
| 54 | 2× | let mut c = w.chars(); |
| 55 | 2× | c.next().map(|f| f.to_uppercase().chain(c).collect::<String>()).unwrap_or_default() |
| 56 | 2× | }) |
| 57 | 1× | .collect(), |
| 58 | 0× | other => other.to_string(), |
| 59 | | }; |
| 60 | 1× | println!( |
| 61 | | " {} {} {}", |
| 62 | 1× | ui::id(format!("#{}", f["seq"].as_u64().unwrap_or(0))), |
| 63 | 1× | ui::err(problem), |
| 64 | 1× | f["detail"].as_str().unwrap_or("") |
| 65 | | ); |
| 66 | | } |
| 67 | 10× | Ok(if clean { 0 } else { 1 }) |
| 68 | 22× | } |
| 69 | |
|
| 70 | |
|
| 71 | 6× | pub async fn check(ctx: &Ctx) -> Result<i32> { |
| 72 | 6× | ctx.local_only("spectra admin check")?; |
| 73 | 5× | let ledger = ctx.writer().await?; |
| 74 | 5× | let differ = ledger.check().await?; |
| 75 | 5× | if differ.is_empty() { |
| 76 | 5× | println!("{} {}", ui::done("Checked"), ui::subject("every projection matches the ledger")); |
| 77 | 5× | Ok(0) |
| 78 | | } else { |
| 79 | 0× | println!("{} projections differ from the ledger: {}", ui::caution(), ui::err(differ.join(", "))); |
| 80 | 0× | println!(" {} {}", ui::note(), ui::dim("rebuild them with spectra admin rebuild")); |
| 81 | 0× | Ok(1) |
| 82 | | } |
| 83 | 6× | } |
| 84 | |
|
| 85 | |
|
| 86 | 3× | pub async fn rebuild(ctx: &Ctx) -> Result<i32> { |
| 87 | 3× | ctx.local_only("spectra admin rebuild")?; |
| 88 | 3× | let ledger = ctx.writer().await?; |
| 89 | 3× | let spin = ui::spinner("replaying the ledger"); |
| 90 | 3× | ledger.rebuild().await?; |
| 91 | 3× | spin.done(&format!("{} {}", ui::done("Rebuilt"), ui::subject(format!("projections from {} events", ledger.head().await.0)))); |
| 92 | 3× | Ok(0) |
| 93 | 3× | } |