ce4c2e086d
Generate a SHA-256 content hash for the embedded hemx runtime, expose the hashed path from hemx-axum, serve immutable runtime headers, and update examples to load the hemx-owned path instead of fixed /hemx.js URLs. req: axum_integration/005
83 lines
2.4 KiB
Rust
83 lines
2.4 KiB
Rust
use axum::body::Body;
|
|
use axum::extract::{Query, State};
|
|
use axum::response::{IntoResponse, Response};
|
|
use axum::routing::get;
|
|
use axum::Router;
|
|
use futures_util::stream;
|
|
use hemx::IntoEffect;
|
|
use hemx_axum::{runtime_js, runtime_js_path, sse, EffectResponse, InteractionRequest};
|
|
use hemx_saas_example::{home_page, live_status, registry, settings_page, ui, AppContext};
|
|
use std::collections::BTreeMap;
|
|
use std::convert::Infallible;
|
|
|
|
#[tokio::main]
|
|
async fn main() {
|
|
let app = app(AppContext::demo());
|
|
let listener = tokio::net::TcpListener::bind("127.0.0.1:3003")
|
|
.await
|
|
.expect("bind saas tutorial example");
|
|
axum::serve(listener, app)
|
|
.await
|
|
.expect("serve saas tutorial example");
|
|
}
|
|
|
|
fn app(ctx: AppContext) -> Router {
|
|
Router::new()
|
|
.route("/", get(home).post(interact))
|
|
.route("/settings", get(settings))
|
|
.route("/events", get(events))
|
|
.route(runtime_js_path(), get(runtime))
|
|
.route("/app.css", get(css))
|
|
.route("/metrics.js", get(metrics_js))
|
|
.with_state(ctx)
|
|
}
|
|
|
|
async fn home(State(ctx): State<AppContext>) -> impl IntoResponse {
|
|
axum::response::Html(home_page(&ctx).into_string())
|
|
}
|
|
|
|
async fn settings(State(ctx): State<AppContext>) -> impl IntoResponse {
|
|
axum::response::Html(settings_page(&ctx).into_string())
|
|
}
|
|
|
|
async fn interact(
|
|
State(ctx): State<AppContext>,
|
|
request: InteractionRequest,
|
|
) -> Result<EffectResponse, impl IntoResponse> {
|
|
request.dispatch_async(registry(ctx)).await
|
|
}
|
|
|
|
async fn events(
|
|
Query(params): Query<BTreeMap<String, String>>,
|
|
State(ctx): State<AppContext>,
|
|
) -> impl IntoResponse {
|
|
let count = ctx.projects().len();
|
|
if params.contains_key("once") {
|
|
return sse(stream::iter([Ok::<_, Infallible>(
|
|
live_status(count).into_batch(ui::BUILD_FINGERPRINT),
|
|
)]));
|
|
}
|
|
|
|
sse(stream::iter([Ok::<_, Infallible>(
|
|
live_status(count).into_batch(ui::BUILD_FINGERPRINT),
|
|
)]))
|
|
}
|
|
|
|
async fn runtime() -> impl IntoResponse {
|
|
runtime_js()
|
|
}
|
|
|
|
async fn css() -> Response {
|
|
Response::builder()
|
|
.header("content-type", "text/css; charset=utf-8")
|
|
.body(Body::from(include_str!("../templates/app.css")))
|
|
.expect("css response")
|
|
}
|
|
|
|
async fn metrics_js() -> Response {
|
|
Response::builder()
|
|
.header("content-type", "text/javascript; charset=utf-8")
|
|
.body(Body::from(include_str!("../templates/metrics.js")))
|
|
.expect("metrics js response")
|
|
}
|