Skip to content

Commit 0473753

Browse files
feat(gb-infra): Add service management for MinIO, Stalwart, Zitadel, and NGINX with environment variable handling
1 parent bbb1657 commit 0473753

File tree

11 files changed

+443
-2
lines changed

11 files changed

+443
-2
lines changed

Cargo.lock

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ members = [
1818
"gb-document",
1919
"gb-file",
2020
"gb-llm",
21-
"gb-calendar",
21+
"gb-calendar", "gb-infra",
2222
]
2323

2424
[workspace.package]
@@ -41,6 +41,7 @@ parking_lot = "0.12"
4141
bytes = "1.0"
4242
log = "0.4"
4343
env_logger = "0.10"
44+
ctrlc = "3.2"
4445

4546
# Web framework and servers
4647
axum = { version = "0.7.9", features = ["ws", "multipart"] }
@@ -136,4 +137,4 @@ docx = "1.1"
136137
zip = "0.6"
137138

138139
[workspace.metadata]
139-
msrv = "1.70.0"
140+
msrv = "1.70.0"

gb-infra/Cargo.toml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[package]
2+
name = "gb-infra"
3+
version.workspace = true
4+
edition.workspace = true
5+
authors.workspace = true
6+
license.workspace = true
7+
8+
[dependencies]
9+
dotenv = { workspace = true }
10+
ctrlc = { workspace = true }
11+
tokio = { workspace = true }
12+
serde = { workspace = true }
13+
serde_json = { workspace = true }

gb-infra/src/lib.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
pub mod manager;
2+
pub mod utils;
3+
pub mod services {
4+
pub mod minio;
5+
pub mod nginx;
6+
pub mod postgresql;
7+
pub mod stalwart;
8+
pub mod zitadel;
9+
}

gb-infra/src/manager.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
use crate::services::{zitadel, stalwart, minio, postgresql, nginx};
2+
use dotenv::dotenv;
3+
use std::sync::{Arc, Mutex};
4+
use std::thread;
5+
use std::time::Duration;
6+
7+
pub struct ServiceManager {
8+
services: Vec<Box<dyn Service>>,
9+
}
10+
11+
impl ServiceManager {
12+
pub fn new() -> Self {
13+
dotenv().ok();
14+
ServiceManager {
15+
services: vec![
16+
Box::new(zitadel::Zitadel::new()),
17+
Box::new(stalwart::Stalwart::new()),
18+
Box::new(minio::MinIO::new()),
19+
Box::new(postgresql::PostgreSQL::new()),
20+
Box::new(nginx::NGINX::new()),
21+
],
22+
}
23+
}
24+
25+
pub fn start(&mut self) {
26+
for service in &mut self.services {
27+
service.start().unwrap();
28+
}
29+
}
30+
31+
pub fn stop(&mut self) {
32+
for service in &mut self.services {
33+
service.stop().unwrap();
34+
}
35+
}
36+
37+
pub fn run(&mut self) {
38+
self.start();
39+
let running = Arc::new(Mutex::new(true));
40+
let running_clone = Arc::clone(&running);
41+
42+
ctrlc::set_handler(move || {
43+
println!("Exiting service manager...");
44+
let mut running = running_clone.lock().unwrap();
45+
*running = false;
46+
})
47+
.expect("Failed to set Ctrl+C handler.");
48+
49+
while *running.lock().unwrap() {
50+
thread::sleep(Duration::from_secs(1));
51+
}
52+
53+
self.stop();
54+
}
55+
}
56+
57+
pub trait Service {
58+
fn start(&mut self) -> Result<(), String>;
59+
fn stop(&mut self) -> Result<(), String>;
60+
}

gb-infra/src/services/minio.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
use crate::manager::Service;
2+
use std::env;
3+
use std::process::Command;
4+
use std::collections::HashMap;
5+
use dotenv::dotenv;
6+
7+
pub struct MinIO {
8+
env_vars: HashMap<String, String>,
9+
process: Option<std::process::Child>,
10+
}
11+
12+
impl MinIO {
13+
pub fn new() -> Self {
14+
dotenv().ok();
15+
let env_vars = vec![
16+
"MINIO_ROOT_USER",
17+
"MINIO_ROOT_PASSWORD",
18+
"MINIO_VOLUMES",
19+
"MINIO_ADDRESS",
20+
]
21+
.into_iter()
22+
.filter_map(|key| env::var(key).ok().map(|value| (key.to_string(), value)))
23+
.collect();
24+
25+
MinIO {
26+
env_vars,
27+
process: None,
28+
}
29+
}
30+
}
31+
32+
impl Service for MinIO {
33+
fn start(&mut self) -> Result<(), String> {
34+
if self.process.is_some() {
35+
return Err("MinIO is already running.".to_string());
36+
}
37+
38+
let mut command = Command::new("/opt/gbo/bin/minio");
39+
for (key, value) in &self.env_vars {
40+
command.env(key, value);
41+
}
42+
43+
self.process = Some(command.spawn().map_err(|e| e.to_string())?);
44+
Ok(())
45+
}
46+
47+
fn stop(&mut self) -> Result<(), String> {
48+
if let Some(mut child) = self.process.take() {
49+
child.kill().map_err(|e| e.to_string())?;
50+
child.wait().map_err(|e| e.to_string())?;
51+
}
52+
Ok(())
53+
}
54+
}

gb-infra/src/services/nginx.rs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
use crate::manager::Service;
2+
use std::env;
3+
use std::process::Command;
4+
use std::collections::HashMap;
5+
use dotenv::dotenv;
6+
7+
pub struct NGINX {
8+
env_vars: HashMap<String, String>,
9+
process: Option<std::process::Child>,
10+
}
11+
12+
impl NGINX {
13+
pub fn new() -> Self {
14+
dotenv().ok();
15+
let env_vars = vec![
16+
"NGINX_ERROR_LOG",
17+
"NGINX_ACCESS_LOG",
18+
]
19+
.into_iter()
20+
.filter_map(|key| env::var(key).ok().map(|value| (key.to_string(), value)))
21+
.collect();
22+
23+
NGINX {
24+
env_vars,
25+
process: None,
26+
}
27+
}
28+
}
29+
30+
impl Service for NGINX {
31+
fn start(&mut self) -> Result<(), String> {
32+
if self.process.is_some() {
33+
return Err("NGINX is already running.".to_string());
34+
}
35+
36+
// Configure NGINX logs
37+
let error_log = self.env_vars.get("NGINX_ERROR_LOG").unwrap();
38+
let access_log = self.env_vars.get("NGINX_ACCESS_LOG").unwrap();
39+
40+
// Update NGINX configuration
41+
let nginx_conf = format!(
42+
r#"
43+
error_log {} debug;
44+
access_log {};
45+
events {{}}
46+
http {{
47+
server {{
48+
listen 80;
49+
server_name localhost;
50+
location / {{
51+
root /var/www/html;
52+
}}
53+
}}
54+
}}
55+
"#,
56+
error_log, access_log
57+
);
58+
59+
// Write the configuration to /etc/nginx/nginx.conf
60+
std::fs::write("/etc/nginx/nginx.conf", nginx_conf).map_err(|e| e.to_string())?;
61+
62+
// Start NGINX
63+
let mut command = Command::new("nginx");
64+
self.process = Some(command.spawn().map_err(|e| e.to_string())?);
65+
Ok(())
66+
}
67+
68+
fn stop(&mut self) -> Result<(), String> {
69+
if let Some(mut child) = self.process.take() {
70+
child.kill().map_err(|e| e.to_string())?;
71+
child.wait().map_err(|e| e.to_string())?;
72+
}
73+
74+
// Stop NGINX
75+
Command::new("nginx")
76+
.arg("-s")
77+
.arg("stop")
78+
.status()
79+
.map_err(|e| e.to_string())?;
80+
81+
Ok(())
82+
}
83+
}

gb-infra/src/services/postgresql.rs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
use crate::manager::Service;
2+
use std::env;
3+
use std::process::Command;
4+
use std::collections::HashMap;
5+
use dotenv::dotenv;
6+
7+
pub struct PostgreSQL {
8+
env_vars: HashMap<String, String>,
9+
process: Option<std::process::Child>,
10+
}
11+
12+
impl PostgreSQL {
13+
pub fn new() -> Self {
14+
dotenv().ok();
15+
let env_vars = vec![
16+
"POSTGRES_DATA_DIR",
17+
"POSTGRES_PORT",
18+
"POSTGRES_USER",
19+
"POSTGRES_PASSWORD",
20+
]
21+
.into_iter()
22+
.filter_map(|key| env::var(key).ok().map(|value| (key.to_string(), value)))
23+
.collect();
24+
25+
PostgreSQL {
26+
env_vars,
27+
process: None,
28+
}
29+
}
30+
}
31+
32+
impl Service for PostgreSQL {
33+
fn start(&mut self) -> Result<(), String> {
34+
if self.process.is_some() {
35+
return Err("PostgreSQL is already running.".to_string());
36+
}
37+
38+
// Initialize PostgreSQL data directory if it doesn't exist
39+
let data_dir = self.env_vars.get("POSTGRES_DATA_DIR").unwrap();
40+
if !std::path::Path::new(data_dir).exists() {
41+
Command::new("sudo")
42+
.arg("-u")
43+
.arg("postgres")
44+
.arg("/usr/lib/postgresql/14/bin/initdb")
45+
.arg("-D")
46+
.arg(data_dir)
47+
.status()
48+
.map_err(|e| e.to_string())?;
49+
}
50+
51+
// Start PostgreSQL
52+
let mut command = Command::new("sudo");
53+
command
54+
.arg("-u")
55+
.arg("postgres")
56+
.arg("/usr/lib/postgresql/14/bin/pg_ctl")
57+
.arg("start")
58+
.arg("-D")
59+
.arg(data_dir);
60+
61+
for (key, value) in &self.env_vars {
62+
command.env(key, value);
63+
}
64+
65+
self.process = Some(command.spawn().map_err(|e| e.to_string())?);
66+
Ok(())
67+
}
68+
69+
fn stop(&mut self) -> Result<(), String> {
70+
if let Some(mut child) = self.process.take() {
71+
child.kill().map_err(|e| e.to_string())?;
72+
child.wait().map_err(|e| e.to_string())?;
73+
}
74+
75+
// Stop PostgreSQL
76+
let data_dir = self.env_vars.get("POSTGRES_DATA_DIR").unwrap();
77+
Command::new("sudo")
78+
.arg("-u")
79+
.arg("postgres")
80+
.arg("/usr/lib/postgresql/14/bin/pg_ctl")
81+
.arg("stop")
82+
.arg("-D")
83+
.arg(data_dir)
84+
.status()
85+
.map_err(|e| e.to_string())?;
86+
87+
Ok(())
88+
}
89+
}

0 commit comments

Comments
 (0)