Files
brk/crates/brk_website/examples/website.rs
T
2026-07-01 17:23:02 +02:00

112 lines
3.4 KiB
Rust

use std::{path::PathBuf, time::Duration};
use axum::{
ServiceExt,
body::Body,
http::{Request, Response, StatusCode, Uri},
middleware::Next,
};
use brk_website::{Website, router};
use tokio::net::TcpListener;
use tower_http::{
catch_panic::CatchPanicLayer, classify::ServerErrorsFailureClass,
compression::CompressionLayer, cors::CorsLayer, normalize_path::NormalizePathLayer,
timeout::TimeoutLayer, trace::TraceLayer,
};
use tower_layer::Layer;
use tracing::{error, info};
#[tokio::main]
async fn main() -> std::io::Result<()> {
let _ = brk_logger::init(None);
// Use the embedded website (default in release mode)
// Or use Website::Filesystem(path) to serve from a custom path
// let website = Website::Default;
let website = Website::Filesystem(PathBuf::from("./website_next"));
if !website.is_enabled() {
eprintln!("Website is disabled");
return Ok(());
}
website.log();
let compression_layer = CompressionLayer::new().br(true).gzip(true).zstd(true);
let response_uri_layer = axum::middleware::from_fn(
async |request: Request<Body>, next: Next| -> Response<Body> {
let uri = request.uri().clone();
let mut response = next.run(request).await;
response.extensions_mut().insert(uri);
response
},
);
let trace_layer = TraceLayer::new_for_http()
.on_request(())
.on_response(
|response: &Response<Body>, latency: Duration, _: &tracing::Span| {
let status = response.status().as_u16();
let Some(uri) = response.extensions().get::<Uri>() else {
return;
};
match response.status() {
StatusCode::OK
| StatusCode::NOT_MODIFIED
| StatusCode::TEMPORARY_REDIRECT
| StatusCode::PERMANENT_REDIRECT => info!(status, %uri, ?latency),
_ => error!(status, %uri, ?latency),
}
},
)
.on_body_chunk(())
.on_failure(
|error: ServerErrorsFailureClass, latency: Duration, _: &tracing::Span| {
error!(?error, ?latency, "request failed");
},
)
.on_eos(());
let app = router(website)
.layer(CatchPanicLayer::new())
.layer(compression_layer)
.layer(response_uri_layer)
.layer(trace_layer)
.layer(TimeoutLayer::with_status_code(
StatusCode::GATEWAY_TIMEOUT,
Duration::from_secs(5),
))
.layer(CorsLayer::permissive());
let mut last_error = None;
let (port, listener) = {
let mut bound = None;
for port in [3110, 3111] {
match TcpListener::bind(("0.0.0.0", port)).await {
Ok(listener) => {
bound = Some((port, listener));
break;
}
Err(error) => {
info!(port, ?error, "website server port unavailable");
last_error = Some(error);
}
}
}
bound.ok_or_else(|| last_error.expect("at least one port was attempted"))?
};
info!("website server listening on port {port}");
let service = NormalizePathLayer::trim_trailing_slash().layer(app);
axum::serve(
listener,
ServiceExt::<Request<Body>>::into_make_service(service),
)
.await
}