1use crate::config::Config;
12use crate::distro::{get_package_manager, PackageManager};
13use crate::rollback::RollbackManager;
14use crate::utils::run_command;
15use log::info;
16use std::error::Error;
17
18pub fn deploy_applications(
32 config: &Config,
33 rollback: &RollbackManager,
34) -> Result<(), Box<dyn Error>> {
35 info!("Deploying applications...");
36
37 let snapshot = rollback.create_snapshot()?;
38
39 for app in &config.deployed_apps {
40 deploy_app(app, &config.server_role)?;
41 }
42
43 rollback.commit_snapshot(snapshot)?;
44
45 info!("Application deployment completed");
46 Ok(())
47}
48
49pub fn deploy_app(app: &str, server_role: &str) -> Result<(), Box<dyn Error>> {
60 match app {
61 "nginx" => deploy_nginx()?,
62 "apache" => deploy_apache()?,
63 "mysql" => deploy_mysql()?,
64 "postgresql" => deploy_postgresql()?,
65 "php" => deploy_php(server_role)?,
66 "nodejs" => deploy_nodejs()?,
67 "python" => deploy_python()?,
68 _ => return Err(format!("Unsupported application: {}", app).into()),
69 }
70 Ok(())
71}
72
73pub fn deploy_nginx() -> Result<(), Box<dyn Error>> {
82 let package_manager = get_package_manager()?;
83
84 match package_manager {
85 PackageManager::Apt => run_command("apt", &["install", "-y", "nginx"])?,
86 PackageManager::Yum => run_command("yum", &["install", "-y", "nginx"])?,
87 PackageManager::Dnf => run_command("dnf", &["install", "-y", "nginx"])?,
88 }
89
90 run_command("systemctl", &["start", "nginx"])?;
91 run_command("systemctl", &["enable", "nginx"])?;
92
93 Ok(())
94}
95
96pub fn deploy_apache() -> Result<(), Box<dyn Error>> {
105 let package_manager = get_package_manager()?;
106
107 match package_manager {
108 PackageManager::Apt => run_command("apt", &["install", "-y", "apache2"])?,
109 PackageManager::Yum => run_command("yum", &["install", "-y", "httpd"])?,
110 PackageManager::Dnf => run_command("dnf", &["install", "-y", "httpd"])?,
111 }
112
113 if let Err(_) = run_command("systemctl", &["start", "apache2"]) {
114 run_command("systemctl", &["start", "httpd"])?;
115 }
116 if let Err(_) = run_command("systemctl", &["enable", "apache2"]) {
117 run_command("systemctl", &["enable", "httpd"])?;
118 }
119
120 Ok(())
121}
122
123pub fn deploy_mysql() -> Result<(), Box<dyn Error>> {
133 let package_manager = get_package_manager()?;
134
135 match package_manager {
136 PackageManager::Apt => run_command("apt", &["install", "-y", "mysql-server"])?,
137 PackageManager::Yum => run_command("yum", &["install", "-y", "mysql-server"])?,
138 PackageManager::Dnf => run_command("dnf", &["install", "-y", "mysql-server"])?,
139 }
140
141 run_command("systemctl", &["start", "mysql"])?;
142 run_command("systemctl", &["enable", "mysql"])?;
143
144 run_command("mysql_secure_installation", &[])?;
146
147 Ok(())
148}
149
150pub fn deploy_postgresql() -> Result<(), Box<dyn Error>> {
160 let package_manager = get_package_manager()?;
161
162 match package_manager {
163 PackageManager::Apt => run_command(
164 "apt",
165 &["install", "-y", "postgresql", "postgresql-contrib"],
166 )?,
167 PackageManager::Yum => run_command(
168 "yum",
169 &["install", "-y", "postgresql-server", "postgresql-contrib"],
170 )?,
171 PackageManager::Dnf => run_command(
172 "dnf",
173 &["install", "-y", "postgresql-server", "postgresql-contrib"],
174 )?,
175 }
176
177 if package_manager != PackageManager::Apt {
179 run_command("postgresql-setup", &["--initdb"])?;
180 }
181
182 run_command("systemctl", &["start", "postgresql"])?;
183 run_command("systemctl", &["enable", "postgresql"])?;
184
185 Ok(())
186}
187
188pub fn deploy_php(server_role: &str) -> Result<(), Box<dyn Error>> {
201 let package_manager = get_package_manager()?;
202
203 match package_manager {
204 PackageManager::Apt => {
205 run_command("apt", &["install", "-y", "php", "php-fpm", "php-mysql"])?;
206 if server_role == "web" {
207 run_command("apt", &["install", "-y", "libapache2-mod-php"])?;
208 }
209 }
210 PackageManager::Yum | PackageManager::Dnf => {
211 let install_cmd = if package_manager == PackageManager::Yum {
212 "yum"
213 } else {
214 "dnf"
215 };
216 run_command(
217 install_cmd,
218 &["install", "-y", "php", "php-fpm", "php-mysqlnd"],
219 )?;
220 if server_role == "web" {
221 run_command(install_cmd, &["install", "-y", "php-apache"])?;
222 }
223 }
224 }
225
226 run_command("systemctl", &["start", "php-fpm"])?;
227 run_command("systemctl", &["enable", "php-fpm"])?;
228
229 Ok(())
230}
231
232pub fn deploy_nodejs() -> Result<(), Box<dyn Error>> {
241 run_command(
243 "curl",
244 &[
245 "-o-",
246 "https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh",
247 "|",
248 "bash",
249 ],
250 )?;
251 run_command("source", &["~/.nvm/nvm.sh"])?;
252 run_command("nvm", &["install", "node"])?;
253 run_command("nvm", &["use", "node"])?;
254
255 run_command("npm", &["install", "-g", "pm2"])?;
257
258 Ok(())
259}
260
261pub fn deploy_python() -> Result<(), Box<dyn Error>> {
270 let package_manager = get_package_manager()?;
271
272 match package_manager {
273 PackageManager::Apt => run_command(
274 "apt",
275 &["install", "-y", "python3", "python3-pip", "python3-venv"],
276 )?,
277 PackageManager::Yum => run_command("yum", &["install", "-y", "python3", "python3-pip"])?,
278 PackageManager::Dnf => run_command("dnf", &["install", "-y", "python3", "python3-pip"])?,
279 }
280
281 run_command("pip3", &["install", "virtualenv"])?;
283
284 Ok(())
285}
286
287pub fn setup_web_server_config(app: &str) -> Result<(), Box<dyn Error>> {
288 match app {
289 "nginx" => setup_nginx_config()?,
290 "apache" => setup_apache_config()?,
291 _ => return Err(format!("Unsupported web server: {}", app).into()),
292 }
293 Ok(())
294}
295
296fn setup_nginx_config() -> Result<(), Box<dyn Error>> {
297 let nginx_config = r#"
298server {
299 listen 80 default_server;
300 listen [::]:80 default_server;
301 root /var/www/html;
302 index index.html index.htm index.nginx-debian.html;
303 server_name _;
304 location / {
305 try_files $uri $uri/ =404;
306 }
307}
308"#;
309 std::fs::write("/etc/nginx/sites-available/default", nginx_config)?;
310 run_command("systemctl", &["reload", "nginx"])?;
311 Ok(())
312}
313
314fn setup_apache_config() -> Result<(), Box<dyn Error>> {
315 let apache_config = r#"
316<VirtualHost *:80>
317 ServerAdmin webmaster@localhost
318 DocumentRoot /var/www/html
319 ErrorLog ${APACHE_LOG_DIR}/error.log
320 CustomLog ${APACHE_LOG_DIR}/access.log combined
321</VirtualHost>
322"#;
323 std::fs::write(
324 "/etc/apache2/sites-available/000-default.conf",
325 apache_config,
326 )?;
327 if let Err(_) = run_command("systemctl", &["reload", "apache2"]) {
328 run_command("systemctl", &["reload", "httpd"])?;
329 }
330 Ok(())
331}
332
333pub fn setup_database(db: &str) -> Result<(), Box<dyn Error>> {
334 match db {
335 "mysql" => setup_mysql()?,
336 "postgresql" => setup_postgresql()?,
337 _ => return Err(format!("Unsupported database: {}", db).into()),
338 }
339 Ok(())
340}
341
342fn setup_mysql() -> Result<(), Box<dyn Error>> {
343 let password = generate_secure_password();
345
346 run_command(
348 "mysql",
349 &[
350 "-e",
351 &format!(
352 "ALTER USER 'root'@'localhost' IDENTIFIED BY '{}';",
353 password
354 ),
355 ],
356 )?;
357 run_command("mysql", &["-e", "DELETE FROM mysql.user WHERE User='';"])?;
358 run_command("mysql", &["-e", "FLUSH PRIVILEGES;"])?;
359
360 std::fs::write("/root/.mysql_root_password", &password)?;
363
364 Ok(())
365}
366
367fn setup_postgresql() -> Result<(), Box<dyn Error>> {
368 let password = generate_secure_password();
370
371 run_command(
373 "sudo",
374 &[
375 "-u",
376 "postgres",
377 "psql",
378 "-c",
379 &format!("ALTER USER postgres PASSWORD '{}';", password),
380 ],
381 )?;
382
383 std::fs::write("/root/.postgres_password", &password)?;
386
387 Ok(())
388}
389
390fn generate_secure_password() -> String {
399 use rand::Rng;
400 const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
401 abcdefghijklmnopqrstuvwxyz\
402 0123456789)(*&^%$#@!~";
403 const PASSWORD_LEN: usize = 20;
404 let mut rng = rand::thread_rng();
405
406 let password: String = (0..PASSWORD_LEN)
407 .map(|_| {
408 let idx = rng.gen_range(0..CHARSET.len());
409 CHARSET[idx] as char
410 })
411 .collect();
412
413 password
414}
415
416fn create_sample_web_app(app_type: &str) -> Result<(), Box<dyn Error>> {
430 match app_type {
431 "php" => {
432 let php_content = r#"
433<?php
434echo "Hello, World! This is a sample PHP application.";
435?>
436"#;
437 std::fs::write("/var/www/html/index.php", php_content)?;
438 }
439 "nodejs" => {
440 let node_content = r#"
441const http = require('http');
442const server = http.createServer((req, res) => {
443 res.statusCode = 200;
444 res.setHeader('Content-Type', 'text/plain');
445 res.end('Hello, World! This is a sample Node.js application.');
446});
447server.listen(3000, '127.0.0.1', () => {
448 console.log('Server running on http://127.0.0.1:3000/');
449});
450"#;
451 std::fs::write("/root/app.js", node_content)?;
452 run_command("pm2", &["start", "/root/app.js"])?;
453 }
454 "python" => {
455 let python_content = r#"
456from flask import Flask
457app = Flask(__name__)
458
459@app.route('/')
460def hello_world():
461 return 'Hello, World! This is a sample Python Flask application.'
462
463if __name__ == '__main__':
464 app.run(host='0.0.0.0', port=5000)
465"#;
466 std::fs::write("/root/app.py", python_content)?;
467 run_command("pip3", &["install", "flask"])?;
468 run_command("python3", &["/root/app.py", "&"])?;
469 }
470 _ => return Err(format!("Unsupported application type: {}", app_type).into()),
471 }
472 Ok(())
473}
474
475fn setup_firewall_rules(config: &Config) -> Result<(), Box<dyn Error>> {
488 let package_manager = get_package_manager()?;
489
490 match package_manager {
491 PackageManager::Apt => {
492 run_command("ufw", &["allow", "OpenSSH"])?;
493 run_command("ufw", &["allow", "80/tcp"])?;
494 run_command("ufw", &["allow", "443/tcp"])?;
495 for rule in &config.custom_firewall_rules {
496 run_command("ufw", &["allow", rule])?;
497 }
498 run_command("ufw", &["enable"])?;
499 }
500 PackageManager::Yum | PackageManager::Dnf => {
501 run_command("firewall-cmd", &["--permanent", "--add-service=ssh"])?;
502 run_command("firewall-cmd", &["--permanent", "--add-service=http"])?;
503 run_command("firewall-cmd", &["--permanent", "--add-service=https"])?;
504 for rule in &config.custom_firewall_rules {
505 run_command("firewall-cmd", &["--permanent", "--add-port=", rule])?;
506 }
507 run_command("firewall-cmd", &["--reload"])?;
508 }
509 }
510 Ok(())
511}