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