Files
gree-controller/src/api/assets.rs
T
2026-08-30 23:00:29 +02:00

85 lines
5.0 KiB
Rust

async fn not_found(State(state): State<AppState>, headers: HeaderMap) -> Response {
let base = request_base_path(&state, &headers);
let home = if base.is_empty() { "/".to_owned() } else { format!("{base}/") };
let body = NOT_FOUND_HTML
.replace("__GREE_BASE_PATH__", &base)
.replace("__GREE_HOME_PATH__", &home)
.replace("__GREE_THEME_INIT_ASSET__", &format!("{base}{THEME_INIT_ASSET_PATH}"))
.replace("__GREE_STYLES_ASSET__", &format!("{base}{STYLES_CSS_ASSET_PATH}"));
let mut response = Response::new(Body::from(body));
*response.status_mut() = StatusCode::NOT_FOUND;
response.headers_mut().insert(header::CONTENT_TYPE, HeaderValue::from_static("text/html; charset=utf-8"));
response.headers_mut().insert(header::CACHE_CONTROL, HeaderValue::from_static("private, no-store, no-cache, must-revalidate"));
response
}
async fn index(State(state): State<AppState>, headers: HeaderMap) -> Response {
let base = request_base_path(&state, &headers);
let body = INDEX_HTML
.replace("__GREE_BASE_PATH__", &base)
.replace("__GREE_APP_ASSET__", &format!("{base}{APP_JS_ASSET_PATH}"))
.replace("__GREE_THEME_INIT_ASSET__", &format!("{base}{THEME_INIT_ASSET_PATH}"))
.replace("__GREE_STYLES_ASSET__", &format!("{base}{STYLES_CSS_ASSET_PATH}"));
let mut response = Response::new(Body::from(body));
response.headers_mut().insert(header::CONTENT_TYPE, HeaderValue::from_static("text/html; charset=utf-8"));
response.headers_mut().insert(header::CACHE_CONTROL, HeaderValue::from_static("private, no-store, no-cache, must-revalidate"));
response
}
fn request_base_path(state: &AppState, headers: &HeaderMap) -> String {
if !state.config.base_path.is_empty() {
state.config.base_path.clone()
} else {
forwarded_prefix(headers).unwrap_or_default()
}
}
fn forwarded_prefix(headers: &HeaderMap) -> Option<String> {
let raw = headers.get("x-forwarded-prefix")?.to_str().ok()?.split(',').next()?.trim();
if raw.is_empty() || raw == "/" { return Some(String::new()); }
if raw.contains('?') || raw.contains('#') || raw.split('/').any(|part| matches!(part, "." | "..")) { return None; }
Some(format!("/{}", raw.trim_matches('/')))
}
async fn app_js() -> Response { static_response(APP_JS, "application/javascript; charset=utf-8", "public, max-age=31536000, immutable") }
async fn app_js_legacy() -> Response { static_response(APP_JS, "application/javascript; charset=utf-8", "no-cache") }
async fn theme_init_js() -> Response { static_response(THEME_INIT_JS, "application/javascript; charset=utf-8", "public, max-age=31536000, immutable") }
async fn theme_init_js_legacy() -> Response { static_response(THEME_INIT_JS, "application/javascript; charset=utf-8", "no-cache") }
async fn styles_css() -> Response { static_response(STYLES_CSS, "text/css; charset=utf-8", "public, max-age=31536000, immutable") }
async fn styles_css_legacy() -> Response { static_response(STYLES_CSS, "text/css; charset=utf-8", "no-cache") }
async fn manifest() -> Response { static_response(MANIFEST, "application/manifest+json", "public, max-age=3600") }
async fn service_worker() -> Response {
let body = SERVICE_WORKER
.replace("__GREE_ASSET_CACHE__", ASSET_BUILD_ID)
.replace("__GREE_APP_ASSET__", APP_JS_ASSET_PATH)
.replace("__GREE_THEME_INIT_ASSET__", THEME_INIT_ASSET_PATH)
.replace("__GREE_STYLES_ASSET__", STYLES_CSS_ASSET_PATH);
owned_response(body, "application/javascript; charset=utf-8", "no-cache")
}
async fn favicon() -> Response { static_response(FAVICON, "image/svg+xml", "public, max-age=86400") }
async fn language_index() -> Response {
static_response(LANGUAGE_MANIFEST_JSON, "application/json; charset=utf-8", "no-cache")
}
async fn language_file(Path(file): Path<String>) -> Response {
let code = file.strip_suffix(".json").unwrap_or(&file);
if let Some((_, body)) = LANGUAGE_ASSETS.iter().find(|(language, _)| *language == code) {
return static_response(*body, "application/json; charset=utf-8", "no-cache");
}
let mut response = Response::new(Body::from("Language not found"));
*response.status_mut() = StatusCode::NOT_FOUND;
response.headers_mut().insert(header::CONTENT_TYPE, HeaderValue::from_static("text/plain; charset=utf-8"));
response
}
fn static_response(body: &'static str, content_type: &'static str, cache: &'static str) -> Response {
let mut response = Response::new(Body::from(body));
response.headers_mut().insert(header::CONTENT_TYPE, HeaderValue::from_static(content_type));
response.headers_mut().insert(header::CACHE_CONTROL, HeaderValue::from_static(cache));
response
}
fn owned_response(body: String, content_type: &'static str, cache: &'static str) -> Response {
let mut response = Response::new(Body::from(body));
response.headers_mut().insert(header::CONTENT_TYPE, HeaderValue::from_static(content_type));
response.headers_mut().insert(header::CACHE_CONTROL, HeaderValue::from_static(cache));
response
}