#![forbid(unsafe_code)] #![warn(clippy::todo)] /// Database abstraction layer for Kittybox, allowing the CMS to work with any kind of database. pub mod database; pub mod frontend; pub mod media; pub mod micropub; pub mod indieauth; pub mod companion { use std::{collections::HashMap, sync::Arc}; use axum::{ extract::{Extension, Path}, response::{IntoResponse, Response} }; #[derive(Debug, Clone, Copy)] struct Resource { data: &'static [u8], mime: &'static str } impl IntoResponse for &Resource { fn into_response(self) -> Response { (axum::http::StatusCode::OK, [("Content-Type", self.mime)], self.data).into_response() } } // TODO replace with the "phf" crate someday type ResourceTable = Arc>; #[tracing::instrument] async fn map_to_static( Path(name): Path, Extension(resources): Extension ) -> Response { tracing::debug!("Searching for {} in the resource table...", name); match resources.get(name.as_str()) { Some(res) => res.into_response(), None => { #[cfg(debug_assertions)] tracing::error!("Not found"); (axum::http::StatusCode::NOT_FOUND, [("Content-Type", "text/plain")], "Not found. Sorry.".as_bytes()).into_response() } } } pub fn router() -> axum::Router { let resources: ResourceTable = { let mut map = HashMap::new(); macro_rules! register_resource { ($map:ident, $prefix:expr, ($filename:literal, $mime:literal)) => {{ $map.insert($filename, Resource { data: include_bytes!(concat!($prefix, $filename)), mime: $mime }) }}; ($map:ident, $prefix:expr, ($filename:literal, $mime:literal), $( ($f:literal, $m:literal) ),+) => {{ register_resource!($map, $prefix, ($filename, $mime)); register_resource!($map, $prefix, $(($f, $m)),+); }}; } register_resource! { map, concat!(env!("OUT_DIR"), "/", "companion", "/"), ("index.html", "text/html; charset=\"utf-8\""), ("main.js", "text/javascript"), ("micropub_api.js", "text/javascript"), ("style.css", "text/css") }; Arc::new(map) }; axum::Router::new() .route( "/:filename", axum::routing::get(map_to_static) .layer(Extension(resources)) ) } }