use axum::{
body::Body,
http::{
Response, StatusCode,
header::{CONTENT_TYPE, HeaderValue},
},
response::IntoResponse,
};
use termstage_core::security::BasePath;
const INDEX_HTML: &str = include_str!("../web/dist/index.html");
const APP_JS: &[u8] = include_bytes!("../web/dist/assets/index.js");
const APP_CSS: &str = include_str!("../web/dist/assets/index.css");
const BASE_HREF_PLACEHOLDER: &str = "<!--TERMSTAGE_BASE_HREF-->";
#[must_use]
pub fn index_response(base_path: Option<&BasePath>) -> Response<Body> {
let body: Body = match base_path {
Some(prefix) => INDEX_HTML
.replace(
BASE_HREF_PLACEHOLDER,
&format!("<base href=\"{}\">", html_escape_attribute(prefix.as_str())),
)
.into(),
None => Body::from(INDEX_HTML),
};
response("text/html; charset=utf-8", body)
}
#[must_use]
pub fn asset_response(path: &str) -> Response<Body> {
match path {
"index.js" => response("text/javascript; charset=utf-8", Body::from(APP_JS)),
"index.css" => response("text/css; charset=utf-8", Body::from(APP_CSS)),
_ => StatusCode::NOT_FOUND.into_response(),
}
}
fn html_escape_attribute(value: &str) -> String {
let mut out = String::with_capacity(value.len());
for byte in value.bytes() {
match byte {
b'&' => out.push_str("&"),
b'<' => out.push_str("<"),
b'>' => out.push_str(">"),
b'"' => out.push_str("""),
b'\'' => out.push_str("'"),
_ => out.push(byte as char),
}
}
out
}
fn response(content_type: &'static str, body: Body) -> Response<Body> {
let mut response = Response::new(body);
response
.headers_mut()
.insert(CONTENT_TYPE, HeaderValue::from_static(content_type));
response
}