romance_core/addon/
email.rs1use crate::addon::Addon;
2use anyhow::Result;
3use std::path::Path;
4
5pub struct EmailAddon;
6
7impl Addon for EmailAddon {
8 fn name(&self) -> &str {
9 "email"
10 }
11
12 fn check_prerequisites(&self, project_root: &Path) -> Result<()> {
13 super::check_romance_project(project_root)
14 }
15
16 fn is_already_installed(&self, project_root: &Path) -> bool {
17 project_root.join("backend/src/email.rs").exists()
18 }
19
20 fn install(&self, project_root: &Path) -> Result<()> {
21 install_email(project_root)
22 }
23
24 fn uninstall(&self, project_root: &Path) -> Result<()> {
25 use colored::Colorize;
26
27 println!("{}", "Uninstalling email system...".bold());
28
29 if super::remove_file_if_exists(&project_root.join("backend/src/email.rs"))? {
31 println!(" {} backend/src/email.rs", "delete".red());
32 }
33 if super::remove_file_if_exists(
34 &project_root.join("backend/src/handlers/password_reset.rs"),
35 )? {
36 println!(
37 " {} backend/src/handlers/password_reset.rs",
38 "delete".red()
39 );
40 }
41
42 super::remove_mod_from_main(project_root, "email")?;
44
45 super::remove_line_from_file(
47 &project_root.join("backend/src/handlers/mod.rs"),
48 "pub mod password_reset;",
49 )?;
50
51 super::remove_feature_flag(project_root, "email")?;
53
54 crate::ai_context::regenerate(project_root).ok();
56
57 println!();
58 println!(
59 "{}",
60 "Email system uninstalled successfully.".green().bold()
61 );
62
63 Ok(())
64 }
65}
66
67fn install_email(project_root: &Path) -> Result<()> {
68 use crate::template::TemplateEngine;
69 use crate::utils;
70 use colored::Colorize;
71 use tera::Context;
72
73 println!("{}", "Installing email system...".bold());
74
75 let engine = TemplateEngine::new()?;
76 let ctx = Context::new();
77
78 let content = engine.render("addon/email/email.rs.tera", &ctx)?;
80 utils::write_file(&project_root.join("backend/src/email.rs"), &content)?;
81 println!(" {} backend/src/email.rs", "create".green());
82
83 let content = engine.render("addon/email/password_reset.rs.tera", &ctx)?;
85 utils::write_file(
86 &project_root.join("backend/src/handlers/password_reset.rs"),
87 &content,
88 )?;
89 println!(
90 " {} backend/src/handlers/password_reset.rs",
91 "create".green()
92 );
93
94 super::add_mod_to_main(project_root, "email")?;
96
97 let mods_marker = "// === ROMANCE:MODS ===";
99 utils::insert_at_marker(
100 &project_root.join("backend/src/handlers/mod.rs"),
101 mods_marker,
102 "pub mod password_reset;",
103 )?;
104
105 crate::generator::auth::insert_cargo_dependency(
107 &project_root.join("backend/Cargo.toml"),
108 &[(
109 "lettre",
110 r#"{ version = "0.11", features = ["tokio1-native-tls"] }"#,
111 )],
112 )?;
113
114 super::append_env_var(
116 &project_root.join("backend/.env"),
117 "SMTP_HOST=smtp.example.com",
118 )?;
119 super::append_env_var(
120 &project_root.join("backend/.env"),
121 "SMTP_PORT=587",
122 )?;
123 super::append_env_var(
124 &project_root.join("backend/.env"),
125 "SMTP_USER=your_smtp_user",
126 )?;
127 super::append_env_var(
128 &project_root.join("backend/.env"),
129 "SMTP_PASS=your_smtp_password",
130 )?;
131 super::append_env_var(
132 &project_root.join("backend/.env"),
133 "FROM_EMAIL=noreply@example.com",
134 )?;
135
136 super::append_env_var(
137 &project_root.join("backend/.env.example"),
138 "SMTP_HOST=smtp.example.com",
139 )?;
140 super::append_env_var(
141 &project_root.join("backend/.env.example"),
142 "SMTP_PORT=587",
143 )?;
144 super::append_env_var(
145 &project_root.join("backend/.env.example"),
146 "SMTP_USER=",
147 )?;
148 super::append_env_var(
149 &project_root.join("backend/.env.example"),
150 "SMTP_PASS=",
151 )?;
152 super::append_env_var(
153 &project_root.join("backend/.env.example"),
154 "FROM_EMAIL=noreply@example.com",
155 )?;
156
157 super::update_feature_flag(project_root, "email", true)?;
159
160 println!();
161 println!(
162 "{}",
163 "Email system installed successfully!".green().bold()
164 );
165 println!(" Configure SMTP settings in backend/.env");
166 println!(" Use EmailService::new() to create an instance.");
167 println!(" Password reset handler available at /api/auth/password-reset");
168
169 Ok(())
170}