Single build command

This commit is contained in:
Louis Knight-Webb
2025-06-17 11:24:03 -04:00
parent 7c5604a81b
commit ac2f227cf0
3 changed files with 59 additions and 4 deletions

View File

@@ -29,6 +29,8 @@ dirs = "5.0"
git2 = "0.18" git2 = "0.18"
async-trait = "0.1" async-trait = "0.1"
dissimilar = "1.0" dissimilar = "1.0"
rust-embed = "8.2"
mime_guess = "2.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"] }

View File

@@ -1,10 +1,13 @@
use axum::{ use axum::{
body::Body,
extract::Extension, extract::Extension,
http::{header, HeaderValue, StatusCode},
middleware, middleware,
response::Json as ResponseJson, response::{IntoResponse, Json as ResponseJson, Response},
routing::{get, post}, routing::{get, post},
Json, Router, Json, Router,
}; };
use rust_embed::RustEmbed;
use sqlx::postgres::PgPoolOptions; use sqlx::postgres::PgPoolOptions;
use std::{collections::HashMap, env, sync::Arc}; use std::{collections::HashMap, env, sync::Arc};
use tokio::sync::Mutex; use tokio::sync::Mutex;
@@ -22,6 +25,10 @@ use execution_monitor::{execution_monitor, AppState};
use models::{user::User, ApiResponse}; use models::{user::User, ApiResponse};
use routes::{filesystem, health, projects, tasks, users}; use routes::{filesystem, health, projects, tasks, users};
#[derive(RustEmbed)]
#[folder = "../frontend/dist"]
struct Assets;
async fn echo_handler( async fn echo_handler(
Json(payload): Json<serde_json::Value>, Json(payload): Json<serde_json::Value>,
) -> ResponseJson<ApiResponse<serde_json::Value>> { ) -> ResponseJson<ApiResponse<serde_json::Value>> {
@@ -32,6 +39,49 @@ async fn echo_handler(
}) })
} }
async fn static_handler(uri: axum::extract::Path<String>) -> impl IntoResponse {
let path = uri.trim_start_matches('/');
serve_file(path).await
}
async fn index_handler() -> impl IntoResponse {
serve_file("index.html").await
}
async fn serve_file(path: &str) -> impl IntoResponse {
let file = Assets::get(path);
match file {
Some(content) => {
let mime = mime_guess::from_path(path).first_or_octet_stream();
Response::builder()
.status(StatusCode::OK)
.header(
header::CONTENT_TYPE,
HeaderValue::from_str(mime.as_ref()).unwrap(),
)
.body(Body::from(content.data.into_owned()))
.unwrap()
}
None => {
// For SPA routing, serve index.html for unknown routes
if let Some(index) = Assets::get("index.html") {
Response::builder()
.status(StatusCode::OK)
.header(header::CONTENT_TYPE, HeaderValue::from_static("text/html"))
.body(Body::from(index.data.into_owned()))
.unwrap()
} else {
Response::builder()
.status(StatusCode::NOT_FOUND)
.body(Body::from("404 Not Found"))
.unwrap()
}
}
}
}
#[tokio::main] #[tokio::main]
async fn main() -> anyhow::Result<()> { async fn main() -> anyhow::Result<()> {
// Load environment variables from .env file // Load environment variables from .env file
@@ -74,9 +124,8 @@ async fn main() -> anyhow::Result<()> {
// Public routes (no auth required) // Public routes (no auth required)
let public_routes = Router::new() let public_routes = Router::new()
.route("/", get(|| async { "Bloop API" })) .route("/api/health", get(health::health_check))
.route("/health", get(health::health_check)) .route("/api/echo", post(echo_handler))
.route("/echo", post(echo_handler))
.merge(users::public_users_router()); .merge(users::public_users_router());
// Protected routes (auth required) // Protected routes (auth required)
@@ -91,6 +140,9 @@ async fn main() -> anyhow::Result<()> {
let app = Router::new() let app = Router::new()
.merge(public_routes) .merge(public_routes)
.merge(protected_routes) .merge(protected_routes)
// Static file serving routes
.route("/", get(index_handler))
.route("/*path", get(static_handler))
.layer(Extension(pool)) .layer(Extension(pool))
.layer(Extension(app_state)) .layer(Extension(app_state))
.layer(CorsLayer::permissive()); .layer(CorsLayer::permissive());

View File

@@ -4,6 +4,7 @@
"scripts": { "scripts": {
"dev": "concurrently \"cargo watch -x 'run --manifest-path backend/Cargo.toml'\" \"npm run frontend:dev\"", "dev": "concurrently \"cargo watch -x 'run --manifest-path backend/Cargo.toml'\" \"npm run frontend:dev\"",
"build": "npm run frontend:build && cargo build --release --manifest-path backend/Cargo.toml", "build": "npm run frontend:build && cargo build --release --manifest-path backend/Cargo.toml",
"build:single": "npm run frontend:build && cargo build --release --manifest-path backend/Cargo.toml",
"frontend:dev": "cd frontend && npm run dev", "frontend:dev": "cd frontend && npm run dev",
"frontend:build": "cd frontend && npm run build", "frontend:build": "cd frontend && npm run build",
"backend:dev": "cargo watch -x 'run --manifest-path backend/Cargo.toml'", "backend:dev": "cargo watch -x 'run --manifest-path backend/Cargo.toml'",