From 4ee7ed90aa69f6c8cf0f71bea652de4eab4eef12 Mon Sep 17 00:00:00 2001 From: ajianaz Date: Sat, 27 Jun 2026 16:58:12 +0700 Subject: [PATCH 1/2] feat: standardize data directory to ~/.codecora/trapfall/ - Default DB path: ~/.codecora/trapfall/trapfall.db (was: ./trapfall.db) - Override via TRAPFALL_DATA_DIR env var - Auto-create directory on first run - ENV/flag override (TRAPFALL_DATABASE_URL, --db) still works Closes #230 --- Cargo.lock | 18 +++++------ crates/trapfalld/src/config.rs | 59 +++++++++++++++++++++++++++++++++- crates/trapfalld/src/main.rs | 11 ++++++- 3 files changed, 77 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 976ef52..959d13e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2449,7 +2449,7 @@ dependencies = [ [[package]] name = "trapfall_alert" -version = "0.1.4" +version = "0.2.0" dependencies = [ "chrono", "serde", @@ -2462,7 +2462,7 @@ dependencies = [ [[package]] name = "trapfall_core" -version = "0.1.4" +version = "0.2.0" dependencies = [ "anyhow", "argon2", @@ -2482,7 +2482,7 @@ dependencies = [ [[package]] name = "trapfall_dashboard" -version = "0.1.4" +version = "0.2.0" dependencies = [ "axum", "rust-embed", @@ -2497,7 +2497,7 @@ dependencies = [ [[package]] name = "trapfall_db" -version = "0.1.4" +version = "0.2.0" dependencies = [ "anyhow", "async-trait", @@ -2515,7 +2515,7 @@ dependencies = [ [[package]] name = "trapfall_ingest" -version = "0.1.4" +version = "0.2.0" dependencies = [ "anyhow", "axum", @@ -2531,7 +2531,7 @@ dependencies = [ [[package]] name = "trapfall_mcp" -version = "0.1.4" +version = "0.2.0" dependencies = [ "anyhow", "serde", @@ -2546,7 +2546,7 @@ dependencies = [ [[package]] name = "trapfall_proto" -version = "0.1.4" +version = "0.2.0" dependencies = [ "chrono", "serde", @@ -2557,7 +2557,7 @@ dependencies = [ [[package]] name = "trapfall_search" -version = "0.1.4" +version = "0.2.0" dependencies = [ "anyhow", "sqlx", @@ -2569,7 +2569,7 @@ dependencies = [ [[package]] name = "trapfalld" -version = "0.1.4" +version = "0.2.0" dependencies = [ "anyhow", "axum", diff --git a/crates/trapfalld/src/config.rs b/crates/trapfalld/src/config.rs index 535a8c9..5ac3967 100644 --- a/crates/trapfalld/src/config.rs +++ b/crates/trapfalld/src/config.rs @@ -85,6 +85,37 @@ impl Config { public_url: parse_public_url(), } } + + /// Returns the default data directory: `~/.codecora/trapfall/`. + /// + /// Override with `TRAPFALL_DATA_DIR` env var. + pub fn default_data_dir() -> PathBuf { + if let Ok(dir) = std::env::var("TRAPFALL_DATA_DIR") { + return PathBuf::from(dir); + } + // Use $HOME on Unix, fallback to current directory + let home = std::env::var("HOME") + .or_else(|_| std::env::var("USERPROFILE")) + .unwrap_or_else(|_| ".".into()); + PathBuf::from(home).join(".codecora").join("trapfall") + } + + /// Returns the default database path: `~/.codecora/trapfall/trapfall.db`. + /// + /// Only used when no explicit `TRAPFALL_DATABASE_URL` or `--db` is provided. + pub fn default_db_path() -> String { + let dir = Self::default_data_dir(); + dir.join("trapfall.db").to_string_lossy().to_string() + } + + /// Ensure the data directory exists, creating it if necessary. + pub fn ensure_data_dir(dir: &PathBuf) -> std::io::Result<()> { + if !dir.exists() { + std::fs::create_dir_all(dir)?; + tracing::info!("Created data directory: {}", dir.display()); + } + Ok(()) + } } /// Parse `TRAPFALL_CORS_ORIGINS` (comma-separated, trimmed, empty filtered). @@ -135,7 +166,7 @@ mod tests { fn base_cfg() -> Config { Config { - db_path: PathBuf::from("trapfall.db"), + db_path: PathBuf::from("/tmp/test-trapfall.db"), listen_addr: "0.0.0.0:9090".into(), cors_origins: vec![], secure_cookie: true, @@ -170,6 +201,32 @@ mod tests { assert_eq!(cfg.dsn_host(), None); } + #[test] + fn default_data_dir_uses_home() { + // Just verify it doesn't panic and contains codecora/trapfall + let dir = Config::default_data_dir(); + let path = dir.to_string_lossy(); + assert!(path.contains("codecora"), "path should contain codecora: {path}"); + assert!(path.contains("trapfall"), "path should contain trapfall: {path}"); + } + + #[test] + fn default_db_path_contains_data_dir() { + let path = Config::default_db_path(); + assert!(path.contains("codecora"), "path should contain codecora: {path}"); + assert!(path.ends_with("trapfall.db"), "path should end with trapfall.db: {path}"); + } + + #[test] + fn ensure_data_dir_creates_directory() { + let dir = std::env::temp_dir().join("trapfall-test-ensure-dir"); + let _ = std::fs::remove_dir_all(&dir); + assert!(!dir.exists()); + Config::ensure_data_dir(&dir).unwrap(); + assert!(dir.exists()); + let _ = std::fs::remove_dir_all(&dir); + } + #[test] fn cookie_secure_flag_toggles() { let mut cfg = base_cfg(); diff --git a/crates/trapfalld/src/main.rs b/crates/trapfalld/src/main.rs index 50d8d19..46633ff 100644 --- a/crates/trapfalld/src/main.rs +++ b/crates/trapfalld/src/main.rs @@ -120,7 +120,16 @@ async fn main() -> Result<()> { } // Normal commands: open global database. - let db_url = trapfall_db::normalise_url(&cli.db); + // If user didn't explicitly set --db or TRAPFALL_DATABASE_URL, use + // the standardised data directory (~/.codecora/trapfall/). + let db_str = if cli.db == "trapfall.db" { + let dir = Config::default_data_dir(); + Config::ensure_data_dir(&dir).expect("failed to create data directory"); + Config::default_db_path() + } else { + cli.db.clone() + }; + let db_url = trapfall_db::normalise_url(&db_str); info!("Opening database: {db_url}"); let backend = trapfall_db::open_database(&db_url).await?; // Run idempotent schema migrations for whichever backend we got back. From db98b78072e0b35667f6ef7820e63f39dda0c32a Mon Sep 17 00:00:00 2001 From: ajianaz Date: Sat, 27 Jun 2026 17:06:38 +0700 Subject: [PATCH 2/2] =?UTF-8?q?style:=20fix=20fmt=20=E2=80=94=20chain=20en?= =?UTF-8?q?v=20var=20calls?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/trapfalld/src/config.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/trapfalld/src/config.rs b/crates/trapfalld/src/config.rs index 5ac3967..5bf76b6 100644 --- a/crates/trapfalld/src/config.rs +++ b/crates/trapfalld/src/config.rs @@ -94,9 +94,7 @@ impl Config { return PathBuf::from(dir); } // Use $HOME on Unix, fallback to current directory - let home = std::env::var("HOME") - .or_else(|_| std::env::var("USERPROFILE")) - .unwrap_or_else(|_| ".".into()); + let home = std::env::var("HOME").or_else(|_| std::env::var("USERPROFILE")).unwrap_or_else(|_| ".".into()); PathBuf::from(home).join(".codecora").join("trapfall") }