Single build command
This commit is contained in:
@@ -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"] }
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
@@ -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'",
|
||||||
|
|||||||
Reference in New Issue
Block a user