romance_core/addon/
audit_log.rs1use 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 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 super::remove_mod_from_main(project_root, "audit")?;
58
59 super::remove_line_from_file(
61 &project_root.join("backend/src/entities/mod.rs"),
62 "pub mod audit_entry;",
63 )?;
64
65 super::remove_line_from_file(
67 &project_root.join("backend/src/handlers/mod.rs"),
68 "pub mod audit_log;",
69 )?;
70
71 super::remove_feature_flag(project_root, "audit_log")?;
73
74 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", ×tamp);
104
105 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 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 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 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 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 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 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 super::add_mod_to_main(project_root, "audit")?;
184
185 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}