1use std::fs;
2use base64::{Engine as _, engine::general_purpose};
3
4pub struct LocalDbConfig {
5 pub db_connection: String,
6 pub db_host: String,
7 pub db_port: u16,
8 pub db_database: String,
9 pub db_username: String,
10 pub db_password: String,
11}
12
13impl LocalDbConfig {
14 pub fn load() -> Self {
15 use std::env;
16 Self {
17 db_connection: env::var("DB_CONNECTION").unwrap_or_else(|_| "sqlite".to_string()),
18 db_host: env::var("DB_HOST").unwrap_or_else(|_| "127.0.0.1".to_string()),
19 db_port: env::var("DB_PORT")
20 .unwrap_or_else(|_| "3306".to_string())
21 .parse()
22 .unwrap_or(3306),
23 db_database: env::var("DB_DATABASE").unwrap_or_else(|_| "rustbasic".to_string()),
24 db_username: env::var("DB_USERNAME").unwrap_or_else(|_| "root".to_string()),
25 db_password: env::var("DB_PASSWORD").unwrap_or_default(),
26 }
27 }
28
29 pub fn db_backend(&self) -> sea_orm::DbBackend {
30 if self.db_connection == "mysql" {
31 sea_orm::DbBackend::MySql
32 } else {
33 sea_orm::DbBackend::Sqlite
34 }
35 }
36}
37
38pub async fn connect() -> sea_orm::DatabaseConnection {
39 let cfg = LocalDbConfig::load();
40 let db_url = if cfg.db_connection == "mysql" {
41 format!(
42 "mysql://{}:{}@{}:{}/{}",
43 cfg.db_username, cfg.db_password, cfg.db_host, cfg.db_port, cfg.db_database
44 )
45 } else {
46 format!("sqlite:database/{}.sqlite?mode=rwc", cfg.db_database)
47 };
48
49 if cfg.db_connection != "mysql" {
50 let _ = std::fs::create_dir_all("database");
51 }
52
53 let mut opt = sea_orm::ConnectOptions::new(db_url);
54 opt.max_connections(20)
55 .min_connections(5)
56 .connect_timeout(std::time::Duration::from_secs(8))
57 .idle_timeout(std::time::Duration::from_secs(8))
58 .max_lifetime(std::time::Duration::from_secs(8))
59 .sqlx_logging(true);
60
61 match sea_orm::Database::connect(opt.clone()).await {
62 Ok(conn) => conn,
63 Err(e) => {
64 let err_msg = e.to_string();
65 if (err_msg.contains("1049") || err_msg.contains("Unknown database")) && cfg.db_connection == "mysql" {
66 println!("{}", "โ ๏ธ Database tidak ditemukan. Mencoba membuat database baru...".yellow());
67 let root_url = format!(
68 "mysql://{}:{}@{}:{}",
69 cfg.db_username, cfg.db_password, cfg.db_host, cfg.db_port
70 );
71 if let Ok(root_db) = sea_orm::Database::connect(root_url).await {
72 let create_query = format!("CREATE DATABASE IF NOT EXISTS `{}`", cfg.db_database);
73 if root_db.execute(sea_orm::Statement::from_string(sea_orm::DbBackend::MySql, create_query)).await.is_ok() {
74 println!("โ
Database '{}' berhasil dibuat.", cfg.db_database.green());
75 return sea_orm::Database::connect(opt).await.expect("Gagal terhubung setelah membuat database");
76 }
77 }
78 }
79 panic!("Gagal terhubung ke database: {:?}", e);
80 }
81 }
82}
83use rand::Rng;
84use regex::Regex;
85use colored::*;
86use sea_orm::ConnectionTrait;
87
88pub async fn clear_cache() {
89 println!("\n{}", "๐งน Cleaning Cache & Logs...".magenta().bold());
90
91 let log_dir = "storage/logs";
93 if let Ok(entries) = fs::read_dir(log_dir) {
94 let mut count = 0;
95 for entry in entries.flatten() {
96 let path = entry.path();
97 if path.is_file() {
98 let _ = fs::OpenOptions::new()
99 .write(true)
100 .truncate(true)
101 .open(&path);
102 count += 1;
103 }
104 }
105 println!(" {} Folder storage/logs telah dikosongkan. ({} file dibersihkan)", "โ
Logs:".green(), count);
106 } else {
107 println!(" {} Folder storage/logs tidak ditemukan.", "โ ๏ธ Logs:".yellow());
108 }
109
110 let cfg = LocalDbConfig::load();
112 let db = connect().await;
113
114 let truncate_sql = if cfg.db_connection == "mysql" {
115 "TRUNCATE TABLE sessions"
116 } else {
117 "DELETE FROM sessions"
118 };
119
120 match db.execute(sea_orm::Statement::from_string(cfg.db_backend(), truncate_sql.to_string())).await {
121 Ok(_) => println!(" {} Tabel sessions telah dikosongkan.", "โ
Sessions:".green()),
122 Err(e) => println!(" {} Gagal membersihkan tabel sessions. ({})", "โ Error:".red(), e),
123 }
124
125 println!("\n{}", "โจ Cache berhasil dibersihkan!".green().bold());
126}
127
128pub fn generate_app_key() {
129 println!("\n{}", "๐ Generating Application Key...".magenta().bold());
130
131 let mut key = [0u8; 32];
132 rand::rng().fill_bytes(&mut key);
133
134 let encoded = general_purpose::STANDARD.encode(key);
135 let key_str = format!("base64:{}", encoded);
136
137 let env_path = ".env";
138 match fs::read_to_string(env_path) {
139 Ok(content) => {
140 let re = Regex::new(r"(?m)^APP_KEY=.*").unwrap();
141 let new_content = if re.is_match(&content) {
142 re.replace(&content, &format!("APP_KEY={}", key_str)).to_string()
143 } else {
144 format!("{}\nAPP_KEY={}", content.trim_end(), key_str)
145 };
146
147 if let Err(e) = fs::write(env_path, new_content) {
148 println!("{} Gagal menulis ke file .env: {}", "โ Error:".red(), e);
149 } else {
150 println!("{} {}", "โ
Application key set successfully:".green(), key_str.cyan());
151 println!("{}", "๐ก Pastikan untuk tidak membagikan APP_KEY ini ke publik!".dimmed());
152 }
153 }
154 Err(_) => {
155 println!("{} File .env tidak ditemukan.", "โ Error:".red());
156 }
157 }
158}
159
160pub async fn ensure_session() {
161 if !std::path::Path::new(".env").exists() {
162 return;
163 }
164
165 let _ = dotenvy::dotenv();
166 let cfg = LocalDbConfig::load();
167
168 let db_url = if cfg.db_connection == "mysql" {
170 format!(
171 "mysql://{}:{}@{}:{}/{}",
172 cfg.db_username, cfg.db_password, cfg.db_host, cfg.db_port, cfg.db_database
173 )
174 } else {
175 format!("sqlite:database/{}.sqlite?mode=rwc", cfg.db_database)
176 };
177
178 if cfg.db_connection != "mysql" {
180 let _ = std::fs::create_dir_all("database");
181 }
182
183 let mut opt = sea_orm::ConnectOptions::new(db_url);
184 opt.max_connections(5)
185 .connect_timeout(std::time::Duration::from_secs(3))
186 .sqlx_logging(false);
187
188 let db = match sea_orm::Database::connect(opt).await {
189 Ok(conn) => conn,
190 Err(_) => {
191 return;
193 }
194 };
195
196 let create_table_sql = if cfg.db_connection == "mysql" {
198 "CREATE TABLE IF NOT EXISTS sessions (
199 id VARCHAR(255) PRIMARY KEY,
200 payload TEXT NOT NULL,
201 last_activity BIGINT NOT NULL,
202 ip_address VARCHAR(45)
203 )"
204 } else {
205 "CREATE TABLE IF NOT EXISTS sessions (
206 id TEXT PRIMARY KEY,
207 payload TEXT NOT NULL,
208 last_activity BIGINT NOT NULL,
209 ip_address TEXT
210 )"
211 };
212
213 let stmt = sea_orm::Statement::from_string(db.get_database_backend(), create_table_sql.to_string());
214 if db.execute(stmt).await.is_err() {
215 return;
216 }
217
218 let session_file = ".rustbasic_session";
220 let mut session_id = String::new();
221 let mut need_to_write = false;
222
223 if let Ok(content) = fs::read_to_string(session_file) {
224 session_id = content.trim().to_string();
225 }
226
227 let mut session_exists = false;
228
229 if !session_id.is_empty() {
230 let check_sql = format!("SELECT id FROM sessions WHERE id = '{}'", session_id);
231 let stmt = sea_orm::Statement::from_string(db.get_database_backend(), check_sql);
232 if let Ok(Some(_)) = db.query_one(stmt).await {
233 session_exists = true;
234 }
235 }
236
237 if !session_exists {
238 let mut bytes = [0u8; 32];
239 rand::rng().fill_bytes(&mut bytes);
240 session_id = general_purpose::STANDARD.encode(bytes);
241 need_to_write = true;
242
243 let now = chrono::Utc::now().timestamp();
244 let insert_sql = format!(
245 "INSERT INTO sessions (id, payload, last_activity, ip_address) VALUES ('{}', '{}', {}, '{}')",
246 session_id, "{}", now, "127.0.0.1"
247 );
248 let stmt = sea_orm::Statement::from_string(db.get_database_backend(), insert_sql);
249 if db.execute(stmt).await.is_ok() {
250 println!("โจ {} ({})", "Session baru berhasil dibuat dan disimpan di database.".green().bold(), session_id.cyan());
251 }
252 } else {
253 let now = chrono::Utc::now().timestamp();
254 let update_sql = format!(
255 "UPDATE sessions SET last_activity = {} WHERE id = '{}'",
256 now, session_id
257 );
258 let stmt = sea_orm::Statement::from_string(db.get_database_backend(), update_sql);
259 let _ = db.execute(stmt).await;
260 }
261
262 if need_to_write {
263 let _ = fs::write(session_file, &session_id);
264 }
265}
266