Some checks failed
CI / Check typos (push) Successful in 8s
CI / Check links (push) Failing after 10s
CI / Clippy (push) Successful in 56s
CI / Build and test (push) Successful in 1m22s
CI / Build container (push) Successful in 1m4s
CI / Deploy on waypoint (push) Successful in 46s
117 lines
2.6 KiB
Rust
117 lines
2.6 KiB
Rust
use axum::http::{HeaderMap, StatusCode};
|
|
use chrono::TimeDelta;
|
|
use pixel_transform::TransformerChain;
|
|
use std::{pin::Pin, str::FromStr};
|
|
use toolbox::mime::MimeType;
|
|
use tracing::{error, trace};
|
|
|
|
use crate::{Rendered, RenderedBody, RequestContext, Servable};
|
|
|
|
pub struct StaticAsset {
|
|
pub bytes: &'static [u8],
|
|
pub mime: MimeType,
|
|
}
|
|
|
|
impl Servable for StaticAsset {
|
|
fn render<'a>(
|
|
&'a self,
|
|
ctx: &'a RequestContext,
|
|
) -> Pin<Box<dyn Future<Output = crate::Rendered> + 'a + Send + Sync>> {
|
|
Box::pin(async {
|
|
let ttl = Some(TimeDelta::days(30));
|
|
|
|
// Automatically provide transformation if this is an image
|
|
let is_image = TransformerChain::mime_is_image(&self.mime);
|
|
|
|
let transform = match (is_image, ctx.query.get("t")) {
|
|
(false, _) | (_, None) => None,
|
|
|
|
(true, Some(x)) => match TransformerChain::from_str(x) {
|
|
Ok(x) => Some(x),
|
|
Err(err) => {
|
|
return Rendered {
|
|
code: StatusCode::BAD_REQUEST,
|
|
body: RenderedBody::String(err),
|
|
ttl,
|
|
immutable: true,
|
|
|
|
headers: HeaderMap::new(),
|
|
mime: None,
|
|
};
|
|
}
|
|
},
|
|
};
|
|
|
|
match transform {
|
|
Some(transform) => {
|
|
trace!(message = "Transforming image", ?transform);
|
|
|
|
let task = {
|
|
let mime = Some(self.mime.clone());
|
|
let bytes = self.bytes;
|
|
tokio::task::spawn_blocking(move || {
|
|
transform.transform_bytes(bytes, mime.as_ref())
|
|
})
|
|
};
|
|
|
|
let res = match task.await {
|
|
Ok(x) => x,
|
|
Err(error) => {
|
|
error!(message = "Error while transforming image", ?error);
|
|
return Rendered {
|
|
code: StatusCode::INTERNAL_SERVER_ERROR,
|
|
body: RenderedBody::String(format!(
|
|
"Error while transforming image: {error:?}"
|
|
)),
|
|
ttl: None,
|
|
immutable: true,
|
|
|
|
headers: HeaderMap::new(),
|
|
mime: None,
|
|
};
|
|
}
|
|
};
|
|
|
|
match res {
|
|
Ok((mime, bytes)) => {
|
|
return Rendered {
|
|
code: StatusCode::OK,
|
|
body: RenderedBody::Bytes(bytes),
|
|
ttl,
|
|
immutable: true,
|
|
|
|
headers: HeaderMap::new(),
|
|
mime: Some(mime),
|
|
};
|
|
}
|
|
|
|
Err(err) => {
|
|
return Rendered {
|
|
code: StatusCode::INTERNAL_SERVER_ERROR,
|
|
body: RenderedBody::String(format!("{err}")),
|
|
ttl,
|
|
immutable: true,
|
|
|
|
headers: HeaderMap::new(),
|
|
mime: None,
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
None => {
|
|
return Rendered {
|
|
code: StatusCode::OK,
|
|
body: RenderedBody::Static(self.bytes),
|
|
ttl,
|
|
immutable: true,
|
|
|
|
headers: HeaderMap::new(),
|
|
mime: Some(self.mime.clone()),
|
|
};
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|