use crate::prelude::*;
use beet_core::prelude::*;
use beet_net::prelude::*;
pub struct BucketEndpoint;
impl BucketEndpoint {
pub fn new(
bucket: Bucket,
remove_prefix: Option<RoutePath>,
) -> EndpointBuilder {
EndpointBuilder::default()
.with_trailing_path()
.with_handler_bundle((
bucket,
async move |mut path: RoutePath,
action: AsyncEntity|
-> Result<Response> {
if let Some(prefix) = &remove_prefix {
if let Ok(stripped) = path.strip_prefix(prefix) {
path = RoutePath::new(stripped);
} else {
bevybail!("prefix {prefix} not found in {path}");
}
}
let bucket = action.get_cloned::<Bucket>().await?;
bucket_to_response(&bucket, &path).await
}
.into_endpoint_handler(),
))
}
}
async fn bucket_to_response(
bucket: &Bucket,
path: &RoutePath,
) -> Result<Response> {
if let Some(_extension) = path.extension() {
if let Some(url) = bucket.public_url(&path).await? {
debug!("redirecting to bucket: {}", url);
Ok(Response::permanent_redirect(url))
} else {
bucket
.get(path)
.await
.map(|bytes| Response::ok_mime_guess(bytes, path))?
.xok()
}
} else {
debug!("loading from bucket: {}", path);
bucket
.get(&path.join("index.html"))
.await
.map(|bytes| Response::ok_body(bytes, "text/html"))?
.xok()
}
}
#[cfg(not(target_arch = "wasm32"))]
#[cfg(test)]
mod test {
use crate::prelude::*;
use beet_core::prelude::*;
use beet_flow::prelude::*;
use beet_net::prelude::*;
#[beet_core::test]
async fn serves_fs() {
let bucket = Bucket::new_test().await;
let body = "body { color: red; }";
let path = RoutePath::from("/style.css");
bucket.insert(&path, body).await.unwrap();
let response = super::bucket_to_response(&bucket, &path).await.unwrap();
response
.into_result()
.await
.unwrap()
.text()
.await
.unwrap()
.xpect_eq(body);
}
#[beet_core::test]
async fn html() {
let bucket = Bucket::new_test().await;
let body = "<h1>Hello, world!</h1>";
bucket
.insert(&RoutePath::from("/docs/index.html"), body)
.await
.unwrap();
let response =
super::bucket_to_response(&bucket, &RoutePath::from("/docs"))
.await
.unwrap();
response
.into_result()
.await
.unwrap()
.text()
.await
.unwrap()
.xpect_eq(body);
}
#[beet_core::test]
async fn as_fallback() {
let bucket = Bucket::new_test().await;
let path = RoutePath::from("/index.html");
bucket.insert(&path, "<div>fallback</div>").await.unwrap();
ServerPlugin::world()
.spawn(ExchangeSpawner::new_flow(move || {
(Sequence, children![
common_predicates::fallback(),
BucketEndpoint::new(bucket.clone(), None),
])
}))
.oneshot_str(Request::get("/"))
.await
.xpect_str("<div>fallback</div>");
}
#[beet_core::test]
async fn remove_prefix() {
let bucket = Bucket::new_test().await;
let path = RoutePath::from("bar/index.html");
bucket.insert(&path, "<div>fallback</div>").await.unwrap();
RouterPlugin::world()
.spawn(ExchangeSpawner::new_flow(move || {
(PathPartial::new("foo"), Sequence, children![
common_predicates::fallback(),
BucketEndpoint::new(
bucket.clone(),
Some(RoutePath::new("foo"))
),
])
}))
.oneshot_str("/foo/bar")
.await
.xpect_str("<div>fallback</div>");
}
}