Skip to main content

romance_core/addon/
api_keys.rs

1use 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        // Delete files
31        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        // Remove mod declaration from main.rs
36        super::remove_mod_from_main(project_root, "api_keys")?;
37
38        // Regenerate AI context
39        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", &timestamp);
70
71    // Generate api_keys module
72    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    // Generate migration
77    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    // Register migration in lib.rs
90    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    // Add mod api_keys to main.rs
103    super::add_mod_to_main(project_root, "api_keys")?;
104
105    // Add sha2 dependency (for hashing API keys)
106    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}