romance_core/addon/
api_keys.rs1use crate::addon::Addon;
2use anyhow::Result;
3use std::path::Path;
4
5pub struct ApiKeysAddon;
6
7impl Addon for ApiKeysAddon {
8 fn name(&self) -> &str {
9 "api-keys"
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/api_keys.rs").exists()
19 }
20
21 fn install(&self, project_root: &Path) -> Result<()> {
22 install_api_keys(project_root)
23 }
24
25 fn uninstall(&self, project_root: &Path) -> Result<()> {
26 use colored::Colorize;
27
28 println!("{}", "Uninstalling API key authentication...".bold());
29
30 if super::remove_file_if_exists(&project_root.join("backend/src/api_keys.rs"))? {
32 println!(" {} backend/src/api_keys.rs", "delete".red());
33 }
34
35 super::remove_mod_from_main(project_root, "api_keys")?;
37
38 crate::ai_context::regenerate(project_root).ok();
40
41 println!();
42 println!(
43 "{}",
44 "API key authentication uninstalled successfully."
45 .green()
46 .bold()
47 );
48
49 Ok(())
50 }
51
52 fn dependencies(&self) -> Vec<&str> {
53 vec!["auth"]
54 }
55}
56
57fn install_api_keys(project_root: &Path) -> Result<()> {
58 use crate::template::TemplateEngine;
59 use crate::utils;
60 use colored::Colorize;
61 use tera::Context;
62
63 println!("{}", "Installing API key authentication...".bold());
64
65 let engine = TemplateEngine::new()?;
66 let timestamp = crate::generator::migration::next_timestamp();
67
68 let mut ctx = Context::new();
69 ctx.insert("timestamp", ×tamp);
70
71 let content = engine.render("addon/api_keys/api_keys.rs.tera", &ctx)?;
73 utils::write_file(&project_root.join("backend/src/api_keys.rs"), &content)?;
74 println!(" {} backend/src/api_keys.rs", "create".green());
75
76 let content = engine.render("addon/api_keys/migration.rs.tera", &ctx)?;
78 let migration_module = format!("m{}_create_api_keys_table", timestamp);
79 utils::write_file(
80 &project_root.join(format!("backend/migration/src/{}.rs", migration_module)),
81 &content,
82 )?;
83 println!(
84 " {} backend/migration/src/{}.rs",
85 "create".green(),
86 migration_module
87 );
88
89 let lib_path = project_root.join("backend/migration/src/lib.rs");
91 utils::insert_at_marker(
92 &lib_path,
93 "// === ROMANCE:MIGRATION_MODS ===",
94 &format!("mod {};", migration_module),
95 )?;
96 utils::insert_at_marker(
97 &lib_path,
98 "// === ROMANCE:MIGRATIONS ===",
99 &format!(" Box::new({}::Migration),", migration_module),
100 )?;
101
102 super::add_mod_to_main(project_root, "api_keys")?;
104
105 crate::generator::auth::insert_cargo_dependency(
107 &project_root.join("backend/Cargo.toml"),
108 &[("sha2", r#""0.10""#)],
109 )?;
110
111 println!();
112 println!(
113 "{}",
114 "API key authentication installed successfully!".green().bold()
115 );
116 println!(" API keys are hashed with SHA-256 before storage.");
117 println!(" Use X-API-Key header for machine-to-machine auth.");
118 println!();
119 println!("Next steps:");
120 println!(" romance db migrate");
121
122 Ok(())
123}