From 56e8f61064cd2802d2e833fe11f4cbc3a7418e0b Mon Sep 17 00:00:00 2001 From: Louis Knight-Webb Date: Tue, 17 Jun 2025 19:59:10 -0400 Subject: [PATCH] Remove dotenv --- AGENT.md | 2 - README.md | 12 +---- backend/.env.example | 4 -- backend/Cargo.toml | 2 +- backend/src/auth.rs | 122 ------------------------------------------- backend/src/lib.rs | 1 - backend/src/main.rs | 44 ++++++++-------- 7 files changed, 23 insertions(+), 164 deletions(-) delete mode 100644 backend/.env.example delete mode 100644 backend/src/auth.rs diff --git a/AGENT.md b/AGENT.md index 54ad768f..64033489 100644 --- a/AGENT.md +++ b/AGENT.md @@ -73,5 +73,3 @@ Try to build the Typescript project after any frontend changes `npm run build` SQLX queries should be located in backend/src/models/\* Use getters and setters instead of raw SQL queries where possible. - -DATABASE_URL=sqlite:/Users/louisknight-webb/Documents/mission-control.sqlite diff --git a/README.md b/README.md index 83269f47..3bb5d0d0 100644 --- a/README.md +++ b/README.md @@ -39,23 +39,13 @@ bloop/ ### Installation -1. Install Postgres - -2. Configure .env (see template in backend/.env.example) - -3. Install dependencies +1. Install dependencies ```bash # Install dependencies npm install ``` -4. Run SQLX migrations - -```bash -cargo sqlx migrate run -``` - ### Development ```bash diff --git a/backend/.env.example b/backend/.env.example deleted file mode 100644 index a8230a98..00000000 --- a/backend/.env.example +++ /dev/null @@ -1,4 +0,0 @@ -SSH_ENCRYPTION_KEY="bloop_ssh_key_32_characters_long" -DATABASE_URL=sqlite::memory: -ADMIN_PASSWORD=SOME_DEFAULT_PASSWORD -JWT_SECRET="somesecret" \ No newline at end of file diff --git a/backend/Cargo.toml b/backend/Cargo.toml index dd8155ca..be9b4598 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -21,7 +21,6 @@ tracing-subscriber = { workspace = true } sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "sqlite", "chrono", "uuid"] } chrono = { version = "0.4", features = ["serde"] } uuid = { version = "1.0", features = ["v4", "serde"] } -dotenvy = "0.15" bcrypt = "0.15" jsonwebtoken = "9.2" ts-rs = { version = "9.0", features = ["uuid-impl", "chrono-impl"] } @@ -31,6 +30,7 @@ async-trait = "0.1" dissimilar = "1.0" rust-embed = "8.2" mime_guess = "2.0" +directories = "6.0.0" [build-dependencies] ts-rs = { version = "9.0", features = ["uuid-impl", "chrono-impl"] } diff --git a/backend/src/auth.rs b/backend/src/auth.rs deleted file mode 100644 index 8b4b8aa5..00000000 --- a/backend/src/auth.rs +++ /dev/null @@ -1,122 +0,0 @@ -use axum::{ - async_trait, - body::Body, - extract::FromRequestParts, - http::{request::Parts, Request, StatusCode}, - middleware::Next, - response::Response, -}; -use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation}; -use serde::{Deserialize, Serialize}; -use sqlx::SqlitePool; -use uuid::Uuid; - -#[derive(Debug, Serialize, Deserialize)] -pub struct Claims { - pub user_id: Uuid, - pub email: String, - pub is_admin: bool, - pub exp: usize, -} - -#[derive(Clone)] -pub struct AuthUser { - pub user_id: Uuid, - pub email: String, - pub is_admin: bool, -} - -#[async_trait] -impl FromRequestParts for AuthUser -where - S: Send + Sync, -{ - type Rejection = StatusCode; - - async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result { - // Get user from request extensions (set by auth middleware) - parts - .extensions - .get::() - .cloned() - .ok_or(StatusCode::UNAUTHORIZED) - } -} - -pub fn create_token( - user_id: Uuid, - email: String, - is_admin: bool, -) -> Result { - let jwt_secret = std::env::var("JWT_SECRET").unwrap_or_else(|_| "your-secret-key".to_string()); - - let expiration = chrono::Utc::now() - .checked_add_signed(chrono::Duration::hours(24)) - .expect("valid timestamp") - .timestamp() as usize; - - let claims = Claims { - user_id, - email, - is_admin, - exp: expiration, - }; - - encode( - &Header::default(), - &claims, - &EncodingKey::from_secret(jwt_secret.as_ref()), - ) -} - -pub fn hash_password(password: &str) -> Result { - bcrypt::hash(password, bcrypt::DEFAULT_COST) -} - -pub fn verify_password(password: &str, hash: &str) -> Result { - bcrypt::verify(password, hash) -} - -// Auth middleware that requires authentication for all routes -pub async fn auth_middleware( - mut request: Request, - next: Next, -) -> Result { - let headers = request.headers(); - - let auth_header = headers - .get("authorization") - .and_then(|value| value.to_str().ok()) - .ok_or(StatusCode::UNAUTHORIZED)?; - - let token = auth_header - .strip_prefix("Bearer ") - .ok_or(StatusCode::UNAUTHORIZED)?; - - let jwt_secret = std::env::var("JWT_SECRET").unwrap_or_else(|_| "your-secret-key".to_string()); - - let claims = decode::( - token, - &DecodingKey::from_secret(jwt_secret.as_ref()), - &Validation::default(), - ) - .map_err(|_| StatusCode::UNAUTHORIZED)? - .claims; - - // Get database pool from request extensions - let pool = request - .extensions() - .get::() - .ok_or(StatusCode::INTERNAL_SERVER_ERROR)?; - - // Note: User table removed, skipping database verification - - // Add user info to request extensions for handlers to access - request.extensions_mut().insert(AuthUser { - user_id: claims.user_id, - email: claims.email, - is_admin: claims.is_admin, - }); - - Ok(next.run(request).await) -} diff --git a/backend/src/lib.rs b/backend/src/lib.rs index 03ffc599..73bbfc30 100644 --- a/backend/src/lib.rs +++ b/backend/src/lib.rs @@ -1,4 +1,3 @@ -pub mod auth; pub mod execution_monitor; pub mod executor; pub mod executors; diff --git a/backend/src/main.rs b/backend/src/main.rs index db3b635b..0508463d 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -6,6 +6,7 @@ use axum::{ routing::{get, post}, Json, Router, }; +use directories::ProjectDirs; use rust_embed::RustEmbed; use sqlx::{sqlite::SqliteConnectOptions, SqlitePool}; use std::str::FromStr; @@ -13,7 +14,6 @@ use std::{collections::HashMap, env, sync::Arc}; use tokio::sync::Mutex; use tower_http::cors::CorsLayer; -mod auth; mod execution_monitor; mod executor; mod executors; @@ -83,11 +83,6 @@ async fn serve_file(path: &str) -> impl IntoResponse { #[tokio::main] async fn main() -> anyhow::Result<()> { - // Load environment variables from .env file - let manifest_dir = std::env::var("CARGO_MANIFEST_DIR")?; - dotenvy::from_path(format!("{manifest_dir}/.env")).ok(); - // dotenvy::dotenv().ok(); - tracing_subscriber::fmt() .with_env_filter( tracing_subscriber::EnvFilter::from_default_env() @@ -95,24 +90,16 @@ async fn main() -> anyhow::Result<()> { ) .init(); + // Create asset directory if it doesn't exist + if !asset_dir().exists() { + std::fs::create_dir_all(asset_dir())?; + } + // Database connection - let database_url = - env::var("DATABASE_URL").expect("DATABASE_URL must be set in environment or .env file"); - - // if !Sqlite::database_exists(database_url).await.unwrap_or(false) { - // println!("Creating database {}", DB_URL); - // match Sqlite::create_database(DB_URL).await { - // Ok(_) => println!("Create db success"), - // Err(error) => panic!("error: {}", error), - // } - // } else { - // println!("Database already exists"); - // } - - // let pool = SqlitePoolOptions::new() - // .max_connections(10) - // .connect(&database_url) - // .await?; + let database_url = format!( + "sqlite://{}", + asset_dir().join("db.sqlite").to_string_lossy() + ); let options = SqliteConnectOptions::from_str(&database_url)?.create_if_missing(true); let pool = SqlitePool::connect_with(options).await?; @@ -164,3 +151,14 @@ async fn main() -> anyhow::Result<()> { Ok(()) } + +fn asset_dir() -> std::path::PathBuf { + // (“com”, “YourOrg”, “MyApp”) → tweak to suit your bundle ID + let proj = ProjectDirs::from("ai", "bloop", env!("CARGO_PKG_NAME")) + .expect("OS didn’t give us a home directory"); + + // ✔ macOS → ~/Library/Application Support/MyApp + // ✔ Linux → ~/.local/share/myapp (respects XDG_DATA_HOME) + // ✔ Windows → %APPDATA%\Example\MyApp + proj.data_dir().to_path_buf() +}