use actix_web::{
HttpRequest, HttpResponse, Responder, get,
http::header::{CACHE_CONTROL, HeaderValue},
web,
};
use crate::config;
const HTMX: &str = include_str!("../assets/h.js");
const DAISYUI: &str = include_str!("../assets/daisyui.css");
const APP_CSS: &str = include_str!("../assets/app.css");
#[get("/assets/{filename:.*}")]
pub async fn assets(req: HttpRequest, config: web::Data<config::Config>) -> impl Responder {
let path = req.match_info().query("filename");
let cache_control = config
.cache_control
.clone()
.unwrap_or_else(|| HeaderValue::from_static(config::DEFAULT_CACHE_CONTROL));
match path {
"h.js" => HttpResponse::Ok()
.content_type("application/javascript; charset=utf-8")
.insert_header((CACHE_CONTROL, cache_control))
.body(HTMX),
"daisyui.css" | "daisyui" => HttpResponse::Ok()
.content_type("text/css; charset=utf-8")
.insert_header((CACHE_CONTROL, cache_control))
.body(DAISYUI),
"app.css" => HttpResponse::Ok()
.content_type("text/css; charset=utf-8")
.insert_header((CACHE_CONTROL, cache_control))
.body(APP_CSS),
_ => HttpResponse::NotFound().body("Not found"),
}
}
#[cfg(test)]
mod tests {
use super::*;
use actix_web::{http::header::HeaderValue, test as aw_test, web};
#[actix_web::test]
async fn test_assets_handler_known_files() {
let config = config::Config::new(
8080,
HeaderValue::from_static(config::DEFAULT_CACHE_CONTROL),
"notify.db".to_string(),
false,
"123456".to_string(),
);
let app = aw_test::init_service(
actix_web::App::new()
.app_data(web::Data::new(config))
.service(assets),
)
.await;
for (path, expected_type) in [
("/assets/h.js", "application/javascript"),
("/assets/daisyui.css", "text/css"),
("/assets/daisyui", "text/css"),
("/assets/app.css", "text/css"),
] {
let req = aw_test::TestRequest::get().uri(path).to_request();
let resp = aw_test::call_service(&app, req).await;
assert!(resp.status().is_success(), "{path} should succeed");
assert!(
resp.headers()
.get("content-type")
.unwrap()
.to_str()
.unwrap()
.starts_with(expected_type),
"{path} should have content type {expected_type}"
);
assert_eq!(
resp.headers()
.get("cache-control")
.unwrap()
.to_str()
.unwrap(),
config::DEFAULT_CACHE_CONTROL
);
}
}
#[actix_web::test]
async fn test_assets_handler_when_cache_control_is_none() {
let config = config::Config::new(
8080,
None,
"notify.db".to_string(),
false,
"123456".to_string(),
);
let app = aw_test::init_service(
actix_web::App::new()
.app_data(web::Data::new(config))
.service(assets),
)
.await;
let req = aw_test::TestRequest::get().uri("/assets/h.js").to_request();
let resp = aw_test::call_service(&app, req).await;
assert!(resp.status().is_success());
assert_eq!(
resp.headers()
.get("cache-control")
.unwrap()
.to_str()
.unwrap(),
config::DEFAULT_CACHE_CONTROL
);
}
#[actix_web::test]
async fn test_assets_handler_when_cache_control_is_no_cache() {
let config = config::Config::new(
8080,
HeaderValue::from_static("no-cache"),
"notify.db".to_string(),
false,
"123456".to_string(),
);
let app = aw_test::init_service(
actix_web::App::new()
.app_data(web::Data::new(config))
.service(assets),
)
.await;
let req = aw_test::TestRequest::get()
.uri("/assets/app.css")
.to_request();
let resp = aw_test::call_service(&app, req).await;
assert!(resp.status().is_success());
assert_eq!(
resp.headers()
.get("cache-control")
.unwrap()
.to_str()
.unwrap(),
"no-cache"
);
}
#[actix_web::test]
async fn test_assets_handler_unknown_file_returns_404() {
let config = config::Config::new(
8080,
HeaderValue::from_static(config::DEFAULT_CACHE_CONTROL),
"notify.db".to_string(),
false,
"123456".to_string(),
);
let app = aw_test::init_service(
actix_web::App::new()
.app_data(web::Data::new(config))
.service(assets),
)
.await;
let req = aw_test::TestRequest::get()
.uri("/assets/unknown.txt")
.to_request();
let resp = aw_test::call_service(&app, req).await;
assert_eq!(resp.status(), actix_web::http::StatusCode::NOT_FOUND);
}
#[test]
fn test_app_css_contains_necessary_utility_classes() {
assert!(APP_CSS.contains(".send-feedback-visible"));
assert!(APP_CSS.contains(".size-4"));
assert!(APP_CSS.contains(".size-12"));
assert!(APP_CSS.contains(".shrink-0"));
assert!(APP_CSS.contains(".grid"));
assert!(APP_CSS.contains(".md\\:grid-cols-2"));
assert!(APP_CSS.contains(".max-w-5xl"));
assert!(APP_CSS.contains(".bg-primary\\/10"));
assert!(APP_CSS.contains(".text-base-content\\/70"));
}
}