huskies: merge 1121 story Remove the marketing website from the huskies OSS repo (now lives in huskies-server)

This commit is contained in:
dave
2026-05-17 18:37:57 +00:00
parent 240beec7de
commit 6331dea8b0
27 changed files with 1 additions and 4058 deletions
-2
View File
@@ -13,12 +13,10 @@ chrono-tz = { workspace = true }
futures = { workspace = true }
homedir = { workspace = true }
ignore = { workspace = true }
mime_guess = { workspace = true }
notify = { workspace = true }
poem = { workspace = true, features = ["websocket"] }
portable-pty = { workspace = true }
reqwest = { workspace = true, features = ["json", "stream", "form"] }
rust-embed = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
serde_urlencoded = { workspace = true }
-7
View File
@@ -62,13 +62,6 @@ pub fn build_gateway_route(state_arc: Arc<GatewayState>) -> impl poem::Endpoint
"/gateway/agents/:id/assign",
poem::post(gateway_assign_agent_handler),
)
// Serve the embedded React frontend so the gateway has a UI.
.at(
"/assets/*path",
poem::get(crate::http::assets::embedded_asset),
)
.at("/*path", poem::get(crate::http::assets::embedded_file))
.at("/", poem::get(crate::http::assets::embedded_index))
.data(state_arc)
}
-149
View File
@@ -1,149 +0,0 @@
//! Static asset serving — serves the embedded React frontend via `rust-embed`.
use poem::{
Response, handler,
http::{StatusCode, header},
web::Path,
};
use rust_embed::RustEmbed;
#[derive(RustEmbed)]
#[folder = "../frontend/dist"]
struct EmbeddedAssets;
fn serve_embedded(path: &str) -> Response {
let normalized = if path.is_empty() {
"index.html"
} else {
path.trim_start_matches('/')
};
let is_asset_request = normalized.starts_with("assets/");
let asset = if is_asset_request {
EmbeddedAssets::get(normalized)
} else {
EmbeddedAssets::get(normalized).or_else(|| {
if normalized == "index.html" {
None
} else {
EmbeddedAssets::get("index.html")
}
})
};
match asset {
Some(content) => {
let body = content.data.into_owned();
let mime = mime_guess::from_path(normalized)
.first_or_octet_stream()
.to_string();
Response::builder()
.status(StatusCode::OK)
.header(header::CONTENT_TYPE, mime)
.body(body)
}
None => Response::builder()
.status(StatusCode::NOT_FOUND)
.body("Not Found"),
}
}
/// Serve a single embedded asset from the `assets/` folder.
#[handler]
pub fn embedded_asset(Path(path): Path<String>) -> Response {
let asset_path = format!("assets/{path}");
serve_embedded(&asset_path)
}
/// Serve an embedded file by path (falls back to `index.html` for SPA routing).
#[handler]
pub fn embedded_file(Path(path): Path<String>) -> Response {
serve_embedded(&path)
}
/// Serve the embedded SPA entrypoint.
#[handler]
pub fn embedded_index() -> Response {
serve_embedded("index.html")
}
#[cfg(test)]
mod tests {
use super::*;
use poem::http::StatusCode;
#[test]
fn non_asset_path_spa_fallback_or_not_found() {
// Non-asset paths fall back to index.html for SPA client-side routing.
// In release builds (with embedded dist/) this returns 200.
// In debug builds without a built frontend dist/ it returns 404.
let response = serve_embedded("__nonexistent_spa_route__.html");
let status = response.status();
assert!(
status == StatusCode::OK || status == StatusCode::NOT_FOUND,
"unexpected status: {status}",
);
}
#[test]
fn missing_asset_path_prefix_returns_not_found() {
// assets/ prefix: no SPA fallback returns 404 if the file does not exist
let response = serve_embedded("assets/__nonexistent__.js");
assert_eq!(response.status(), StatusCode::NOT_FOUND);
}
#[test]
fn serve_embedded_does_not_panic_on_empty_path() {
// Empty path normalises to index.html; OK in release, 404 in debug without dist/
let response = serve_embedded("");
let status = response.status();
assert!(
status == StatusCode::OK || status == StatusCode::NOT_FOUND,
"unexpected status: {status}",
);
}
#[test]
fn embedded_assets_struct_is_iterable() {
// Verifies that rust-embed compiled the EmbeddedAssets struct correctly.
// In debug builds without a built frontend dist/ directory the iterator is empty; that is
// expected. In release builds it will contain all bundled frontend files.
let _files: Vec<_> = EmbeddedAssets::iter().collect();
// No assertion needed the test passes as long as it compiles and does not panic.
}
#[tokio::test]
async fn embedded_index_handler_returns_ok_or_not_found() {
// Route the handler through TestClient; index.html is the SPA entry point.
let app = poem::Route::new().at("/", poem::get(embedded_index));
let cli = poem::test::TestClient::new(app);
let resp = cli.get("/").send().await;
let status = resp.0.status();
assert!(
status == StatusCode::OK || status == StatusCode::NOT_FOUND,
"unexpected status: {status}",
);
}
#[tokio::test]
async fn embedded_file_handler_with_path_returns_ok_or_not_found() {
// Non-asset paths fall back to index.html (SPA routing) or 404.
let app = poem::Route::new().at("/*path", poem::get(embedded_file));
let cli = poem::test::TestClient::new(app);
let resp = cli.get("/__spa_route__").send().await;
let status = resp.0.status();
assert!(
status == StatusCode::OK || status == StatusCode::NOT_FOUND,
"unexpected status: {status}",
);
}
#[tokio::test]
async fn embedded_asset_handler_missing_file_returns_not_found() {
// The assets/ prefix disables SPA fallback; missing files must return 404.
let app = poem::Route::new().at("/assets/*path", poem::get(embedded_asset));
let cli = poem::test::TestClient::new(app);
let resp = cli.get("/assets/__nonexistent__.js").send().await;
assert_eq!(resp.0.status(), StatusCode::NOT_FOUND);
}
}
+1 -6
View File
@@ -1,8 +1,6 @@
//! HTTP server — module declarations for all REST, MCP, WebSocket, and SSE endpoints.
/// Server-sent event stream for real-time agent output.
pub mod agents_sse;
/// Static asset serving (embedded frontend files).
pub mod assets;
/// Shared application context threaded through handlers.
pub mod context;
/// Server-sent event stream for pipeline/watcher events.
@@ -100,10 +98,7 @@ pub fn build_routes(
get(oauth::oauth_callback).data(oauth_state.clone()),
)
.at("/oauth/status", get(oauth::oauth_status))
.at("/debug/crdt", get(debug_crdt_handler))
.at("/assets/*path", get(assets::embedded_asset))
.at("/", get(assets::embedded_index))
.at("/*path", get(assets::embedded_file));
.at("/debug/crdt", get(debug_crdt_handler));
if let Some(buf) = event_buffer {
route = route.at("/api/events", get(events::events_handler).data(buf));