1use crate::pkg::audit;
2use anyhow::{Result, anyhow};
3use colored::*;
4use comfy_table::{Cell, Color, ContentArrangement, Table, presets::UTF8_FULL};
5use std::path::PathBuf;
6
7pub fn run(verify: bool, export: Option<PathBuf>, ndjson: bool) -> Result<()> {
8 if verify {
9 let report = audit::verify_chain()?;
10 if report.valid {
11 println!(
12 "{} {} (entries: {}, chained: {}, legacy: {})",
13 "::".bold().blue(),
14 report.message.green(),
15 report.total_entries,
16 report.hashed_entries,
17 report.legacy_entries
18 );
19 return Ok(());
20 }
21 return Err(anyhow!(report.message));
22 }
23
24 if let Some(path) = export {
25 let total = audit::export_history(&path, ndjson)?;
26 println!(
27 "{} Exported {} audit entr{} to {} (format: {}).",
28 "::".bold().green(),
29 total,
30 if total == 1 { "y" } else { "ies" },
31 path.display().to_string().cyan(),
32 if ndjson { "ndjson" } else { "json" }
33 );
34 return Ok(());
35 }
36
37 println!("{} Zoi operation history...", "::".bold().blue());
38
39 let history = audit::get_history()?;
40
41 if history.is_empty() {
42 println!("No history recorded. Audit logging might be disabled.");
43 return Ok(());
44 }
45
46 let mut table = Table::new();
47 table
48 .load_preset(UTF8_FULL)
49 .set_content_arrangement(ContentArrangement::Dynamic)
50 .set_header(vec![
51 "Date/Time",
52 "User",
53 "Action",
54 "Package",
55 "Version",
56 "Repo",
57 "Type",
58 "Scope",
59 ]);
60
61 for entry in history {
62 let action_cell = match entry.action {
63 audit::AuditAction::Install => Cell::new("Install").fg(Color::Green),
64 audit::AuditAction::Uninstall => Cell::new("Uninstall").fg(Color::Red),
65 audit::AuditAction::Upgrade => Cell::new("Upgrade").fg(Color::Yellow),
66 };
67
68 table.add_row(vec![
69 Cell::new(
70 entry
71 .timestamp
72 .with_timezone(&chrono::Local)
73 .format("%Y-%m-%d %H:%M:%S"),
74 ),
75 Cell::new(entry.user),
76 action_cell,
77 Cell::new(entry.package_name).fg(Color::Cyan),
78 Cell::new(entry.version),
79 Cell::new(entry.repo),
80 Cell::new(format!("{:?}", entry.package_type)),
81 Cell::new(format!("{:?}", entry.scope)),
82 ]);
83 }
84
85 println!("{table}");
86
87 Ok(())
88}