Skip to main content

romance_core/addon/
audit_log.rs

1use crate::addon::Addon;
2use anyhow::Result;
3use std::path::Path;
4
5pub struct AuditLogAddon;
6
7impl Addon for AuditLogAddon {
8    fn name(&self) -> &str {
9        "audit-log"
10    }
11
12    fn check_prerequisites(&self, project_root: &Path) -> Result<()> {
13        super::check_romance_project(project_root)?;
14        super::check_auth_exists(project_root)
15    }
16
17    fn is_already_installed(&self, project_root: &Path) -> bool {
18        project_root.join("backend/src/audit.rs").exists()
19    }
20
21    fn install(&self, project_root: &Path) -> Result<()> {
22        install_audit_log(project_root)
23    }
24
25    fn uninstall(&self, project_root: &Path) -> Result<()> {
26        use colored::Colorize;
27
28        println!("{}", "Uninstalling audit log...".bold());
29
30        // Delete files
31        if super::remove_file_if_exists(&project_root.join("backend/src/audit.rs"))? {
32            println!("  {} backend/src/audit.rs", "delete".red());
33        }
34        if super::remove_file_if_exists(
35            &project_root.join("backend/src/entities/audit_entry.rs"),
36        )? {
37            println!(
38                "  {} backend/src/entities/audit_entry.rs",
39                "delete".red()
40            );
41        }
42        if super::remove_file_if_exists(
43            &project_root.join("backend/src/handlers/audit_log.rs"),
44        )? {
45            println!("  {} backend/src/handlers/audit_log.rs", "delete".red());
46        }
47        if super::remove_file_if_exists(
48            &project_root.join("frontend/src/features/admin/AuditLog.tsx"),
49        )? {
50            println!(
51                "  {} frontend/src/features/admin/AuditLog.tsx",
52                "delete".red()
53            );
54        }
55
56        // Remove mod declaration from main.rs
57        super::remove_mod_from_main(project_root, "audit")?;
58
59        // Remove from entities/mod.rs
60        super::remove_line_from_file(
61            &project_root.join("backend/src/entities/mod.rs"),
62            "pub mod audit_entry;",
63        )?;
64
65        // Remove from handlers/mod.rs
66        super::remove_line_from_file(
67            &project_root.join("backend/src/handlers/mod.rs"),
68            "pub mod audit_log;",
69        )?;
70
71        // Remove feature flag
72        super::remove_feature_flag(project_root, "audit_log")?;
73
74        // Regenerate AI context
75        crate::ai_context::regenerate(project_root).ok();
76
77        println!();
78        println!(
79            "{}",
80            "Audit log uninstalled successfully.".green().bold()
81        );
82
83        Ok(())
84    }
85
86    fn dependencies(&self) -> Vec<&str> {
87        vec!["auth"]
88    }
89}
90
91fn install_audit_log(project_root: &Path) -> Result<()> {
92    use crate::template::TemplateEngine;
93    use crate::utils;
94    use colored::Colorize;
95    use tera::Context;
96
97    println!("{}", "Installing audit log...".bold());
98
99    let engine = TemplateEngine::new()?;
100    let timestamp = crate::generator::migration::next_timestamp();
101
102    let mut ctx = Context::new();
103    ctx.insert("timestamp", &timestamp);
104
105    // Generate audit module
106    let content = engine.render("addon/audit_log/audit.rs.tera", &ctx)?;
107    utils::write_file(&project_root.join("backend/src/audit.rs"), &content)?;
108    println!("  {} backend/src/audit.rs", "create".green());
109
110    // Generate audit_entry entity model
111    let content = engine.render("addon/audit_log/model.rs.tera", &ctx)?;
112    utils::write_file(
113        &project_root.join("backend/src/entities/audit_entry.rs"),
114        &content,
115    )?;
116    println!(
117        "  {} backend/src/entities/audit_entry.rs",
118        "create".green()
119    );
120
121    // Generate migration
122    let content = engine.render("addon/audit_log/migration.rs.tera", &ctx)?;
123    let migration_module = format!("m{}_create_audit_entries_table", timestamp);
124    utils::write_file(
125        &project_root.join(format!("backend/migration/src/{}.rs", migration_module)),
126        &content,
127    )?;
128    println!(
129        "  {} backend/migration/src/{}.rs",
130        "create".green(),
131        migration_module
132    );
133
134    // Generate audit log handler for admin
135    let content = engine.render("addon/audit_log/handlers.rs.tera", &ctx)?;
136    utils::write_file(
137        &project_root.join("backend/src/handlers/audit_log.rs"),
138        &content,
139    )?;
140    println!(
141        "  {} backend/src/handlers/audit_log.rs",
142        "create".green()
143    );
144
145    // Generate frontend audit log viewer
146    let content = engine.render("addon/audit_log/AuditLog.tsx.tera", &ctx)?;
147    utils::write_file(
148        &project_root.join("frontend/src/features/admin/AuditLog.tsx"),
149        &content,
150    )?;
151    println!(
152        "  {} frontend/src/features/admin/AuditLog.tsx",
153        "create".green()
154    );
155
156    // Register modules
157    let mods_marker = "// === ROMANCE:MODS ===";
158    utils::insert_at_marker(
159        &project_root.join("backend/src/entities/mod.rs"),
160        mods_marker,
161        "pub mod audit_entry;",
162    )?;
163    utils::insert_at_marker(
164        &project_root.join("backend/src/handlers/mod.rs"),
165        mods_marker,
166        "pub mod audit_log;",
167    )?;
168
169    // Register migration
170    let lib_path = project_root.join("backend/migration/src/lib.rs");
171    utils::insert_at_marker(
172        &lib_path,
173        "// === ROMANCE:MIGRATION_MODS ===",
174        &format!("mod {};", migration_module),
175    )?;
176    utils::insert_at_marker(
177        &lib_path,
178        "// === ROMANCE:MIGRATIONS ===",
179        &format!("            Box::new({}::Migration),", migration_module),
180    )?;
181
182    // Add mod audit to main.rs
183    super::add_mod_to_main(project_root, "audit")?;
184
185    // Update romance.toml
186    super::update_feature_flag(project_root, "audit_log", true)?;
187
188    println!();
189    println!(
190        "{}",
191        "Audit log installed successfully!".green().bold()
192    );
193    println!("  All create/update/delete operations will be logged.");
194    println!("  View at /admin/audit-log");
195
196    Ok(())
197}