use axum::{
Json, Router,
extract::State,
http::{StatusCode, header},
response::{Html, IntoResponse, Response},
routing::{get, post},
};
use bookyard_core::{BookyardConfig, BuildOptions, CONFIG_FILE, build_catalog, build_shelf};
use tower_http::services::ServeDir;
use crate::{render, server::ServerState};
pub fn router(state: ServerState) -> Router {
let mut router = Router::new()
.route("/__bookyard/edit", get(editor_html))
.route("/__bookyard/editor/style.css", get(editor_css))
.route("/__bookyard/editor/app.js", get(editor_js))
.route("/__bookyard/api/config", get(get_config).put(put_config))
.route("/__bookyard/api/catalog", get(get_catalog))
.route("/__bookyard/api/build", post(post_build))
.fallback_service(ServeDir::new(state.output_dir.clone()));
if !state.edit {
router = Router::new().fallback_service(ServeDir::new(state.output_dir.clone()));
}
router.with_state(state)
}
async fn editor_html(State(state): State<ServerState>) -> Response {
if !state.edit {
return StatusCode::NOT_FOUND.into_response();
}
Html(crate::server::static_files::EDITOR_HTML).into_response()
}
async fn editor_css(State(state): State<ServerState>) -> Response {
if !state.edit {
return StatusCode::NOT_FOUND.into_response();
}
(
[(header::CONTENT_TYPE, "text/css; charset=utf-8")],
crate::server::static_files::EDITOR_CSS,
)
.into_response()
}
async fn editor_js(State(state): State<ServerState>) -> Response {
if !state.edit {
return StatusCode::NOT_FOUND.into_response();
}
(
[(header::CONTENT_TYPE, "text/javascript; charset=utf-8")],
crate::server::static_files::EDITOR_JS,
)
.into_response()
}
async fn get_config(State(state): State<ServerState>) -> Response {
if !state.edit {
return StatusCode::NOT_FOUND.into_response();
}
match BookyardConfig::load_from(state.root_dir.join(CONFIG_FILE)) {
Ok(config) => Json(config).into_response(),
Err(err) => error_response(err),
}
}
async fn put_config(
State(state): State<ServerState>,
Json(config): Json<BookyardConfig>,
) -> Response {
if !state.edit {
return StatusCode::NOT_FOUND.into_response();
}
let path = state.root_dir.join(CONFIG_FILE);
if let Err(err) = config.save_to(&path) {
return error_response(err);
}
if let Err(err) = render::shelf::write_shelf(&state.output_dir, &config) {
return error_response(err);
}
Json(config).into_response()
}
async fn get_catalog(State(state): State<ServerState>) -> Response {
if !state.edit {
return StatusCode::NOT_FOUND.into_response();
}
match BookyardConfig::load_from(state.root_dir.join(CONFIG_FILE)) {
Ok(config) => Json(build_catalog(&config)).into_response(),
Err(err) => error_response(err),
}
}
async fn post_build(State(state): State<ServerState>) -> Response {
if !state.edit {
return StatusCode::NOT_FOUND.into_response();
}
let config = match BookyardConfig::load_from(state.root_dir.join(CONFIG_FILE)) {
Ok(config) => config,
Err(err) => return error_response(err),
};
match build_shelf(&state.root_dir, &config, &BuildOptions::default()) {
Ok(_) => match render::shelf::write_shelf(&state.output_dir, &config) {
Ok(()) => Json(build_catalog(&config)).into_response(),
Err(err) => error_response(err),
},
Err(err) => error_response(err),
}
}
fn error_response(err: impl std::fmt::Display) -> Response {
(StatusCode::INTERNAL_SERVER_ERROR, err.to_string()).into_response()
}