Remove dotenv
This commit is contained in:
2
AGENT.md
2
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/\*
|
SQLX queries should be located in backend/src/models/\*
|
||||||
Use getters and setters instead of raw SQL queries where possible.
|
Use getters and setters instead of raw SQL queries where possible.
|
||||||
|
|
||||||
DATABASE_URL=sqlite:/Users/louisknight-webb/Documents/mission-control.sqlite
|
|
||||||
|
|||||||
12
README.md
12
README.md
@@ -39,23 +39,13 @@ bloop/
|
|||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
|
|
||||||
1. Install Postgres
|
1. Install dependencies
|
||||||
|
|
||||||
2. Configure .env (see template in backend/.env.example)
|
|
||||||
|
|
||||||
3. Install dependencies
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Install dependencies
|
# Install dependencies
|
||||||
npm install
|
npm install
|
||||||
```
|
```
|
||||||
|
|
||||||
4. Run SQLX migrations
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cargo sqlx migrate run
|
|
||||||
```
|
|
||||||
|
|
||||||
### Development
|
### Development
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -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"
|
|
||||||
@@ -21,7 +21,6 @@ tracing-subscriber = { workspace = true }
|
|||||||
sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "sqlite", "chrono", "uuid"] }
|
sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "sqlite", "chrono", "uuid"] }
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
uuid = { version = "1.0", features = ["v4", "serde"] }
|
uuid = { version = "1.0", features = ["v4", "serde"] }
|
||||||
dotenvy = "0.15"
|
|
||||||
bcrypt = "0.15"
|
bcrypt = "0.15"
|
||||||
jsonwebtoken = "9.2"
|
jsonwebtoken = "9.2"
|
||||||
ts-rs = { version = "9.0", features = ["uuid-impl", "chrono-impl"] }
|
ts-rs = { version = "9.0", features = ["uuid-impl", "chrono-impl"] }
|
||||||
@@ -31,6 +30,7 @@ async-trait = "0.1"
|
|||||||
dissimilar = "1.0"
|
dissimilar = "1.0"
|
||||||
rust-embed = "8.2"
|
rust-embed = "8.2"
|
||||||
mime_guess = "2.0"
|
mime_guess = "2.0"
|
||||||
|
directories = "6.0.0"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
ts-rs = { version = "9.0", features = ["uuid-impl", "chrono-impl"] }
|
ts-rs = { version = "9.0", features = ["uuid-impl", "chrono-impl"] }
|
||||||
|
|||||||
@@ -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<S> FromRequestParts<S> for AuthUser
|
|
||||||
where
|
|
||||||
S: Send + Sync,
|
|
||||||
{
|
|
||||||
type Rejection = StatusCode;
|
|
||||||
|
|
||||||
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
|
|
||||||
// Get user from request extensions (set by auth middleware)
|
|
||||||
parts
|
|
||||||
.extensions
|
|
||||||
.get::<AuthUser>()
|
|
||||||
.cloned()
|
|
||||||
.ok_or(StatusCode::UNAUTHORIZED)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_token(
|
|
||||||
user_id: Uuid,
|
|
||||||
email: String,
|
|
||||||
is_admin: bool,
|
|
||||||
) -> Result<String, jsonwebtoken::errors::Error> {
|
|
||||||
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<String, bcrypt::BcryptError> {
|
|
||||||
bcrypt::hash(password, bcrypt::DEFAULT_COST)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn verify_password(password: &str, hash: &str) -> Result<bool, bcrypt::BcryptError> {
|
|
||||||
bcrypt::verify(password, hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Auth middleware that requires authentication for all routes
|
|
||||||
pub async fn auth_middleware(
|
|
||||||
mut request: Request<Body>,
|
|
||||||
next: Next,
|
|
||||||
) -> Result<Response, StatusCode> {
|
|
||||||
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::<Claims>(
|
|
||||||
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::<SqlitePool>()
|
|
||||||
.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)
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
pub mod auth;
|
|
||||||
pub mod execution_monitor;
|
pub mod execution_monitor;
|
||||||
pub mod executor;
|
pub mod executor;
|
||||||
pub mod executors;
|
pub mod executors;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ use axum::{
|
|||||||
routing::{get, post},
|
routing::{get, post},
|
||||||
Json, Router,
|
Json, Router,
|
||||||
};
|
};
|
||||||
|
use directories::ProjectDirs;
|
||||||
use rust_embed::RustEmbed;
|
use rust_embed::RustEmbed;
|
||||||
use sqlx::{sqlite::SqliteConnectOptions, SqlitePool};
|
use sqlx::{sqlite::SqliteConnectOptions, SqlitePool};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
@@ -13,7 +14,6 @@ use std::{collections::HashMap, env, sync::Arc};
|
|||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use tower_http::cors::CorsLayer;
|
use tower_http::cors::CorsLayer;
|
||||||
|
|
||||||
mod auth;
|
|
||||||
mod execution_monitor;
|
mod execution_monitor;
|
||||||
mod executor;
|
mod executor;
|
||||||
mod executors;
|
mod executors;
|
||||||
@@ -83,11 +83,6 @@ async fn serve_file(path: &str) -> impl IntoResponse {
|
|||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
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()
|
tracing_subscriber::fmt()
|
||||||
.with_env_filter(
|
.with_env_filter(
|
||||||
tracing_subscriber::EnvFilter::from_default_env()
|
tracing_subscriber::EnvFilter::from_default_env()
|
||||||
@@ -95,24 +90,16 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
)
|
)
|
||||||
.init();
|
.init();
|
||||||
|
|
||||||
|
// Create asset directory if it doesn't exist
|
||||||
|
if !asset_dir().exists() {
|
||||||
|
std::fs::create_dir_all(asset_dir())?;
|
||||||
|
}
|
||||||
|
|
||||||
// Database connection
|
// Database connection
|
||||||
let database_url =
|
let database_url = format!(
|
||||||
env::var("DATABASE_URL").expect("DATABASE_URL must be set in environment or .env file");
|
"sqlite://{}",
|
||||||
|
asset_dir().join("db.sqlite").to_string_lossy()
|
||||||
// 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 options = SqliteConnectOptions::from_str(&database_url)?.create_if_missing(true);
|
let options = SqliteConnectOptions::from_str(&database_url)?.create_if_missing(true);
|
||||||
let pool = SqlitePool::connect_with(options).await?;
|
let pool = SqlitePool::connect_with(options).await?;
|
||||||
@@ -164,3 +151,14 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
|
|
||||||
Ok(())
|
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()
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user