diff --git a/src/http/error.rs b/src/http/error.rs index 41a20be..6267324 100644 --- a/src/http/error.rs +++ b/src/http/error.rs @@ -1,12 +1,8 @@ -use askama::Template; use axum::{ extract::rejection::JsonRejection, - http::{header::ACCEPT, Request, StatusCode}, - middleware::Next, + http::StatusCode, response::{IntoResponse, Response}, - Json, }; -use serde_json::json; use thiserror::Error; #[derive(Error, Debug)] @@ -23,18 +19,14 @@ pub enum AppError { #[error("incoming JSON format error: {0}")] JSONFormat(#[from] JsonRejection), + + #[error("template error: {0}")] + Template(#[from] askama::Error), } -struct ErrorInfo { - code: String, - message: String, -} - -#[derive(Template)] -#[template(path = "error.html")] -struct ErrorTemplate { - code: String, - message: String, +pub(super) struct ErrorInfo { + pub(super) code: String, + pub(super) message: String, } impl IntoResponse for AppError { @@ -73,6 +65,13 @@ impl IntoResponse for AppError { }, ) } + AppError::Template(err) => ( + StatusCode::INTERNAL_SERVER_ERROR, + ErrorInfo { + code: "template-error".to_string(), + message: err.to_string(), + }, + ), }; let mut response = status.into_response(); @@ -80,23 +79,3 @@ impl IntoResponse for AppError { response } } - -pub async fn error_interceptor(request: Request, next: Next) -> Response { - let accept_header = request - .headers() - .get(&ACCEPT) - .map(|value| value.as_ref().to_owned()); - - let mut response = next.run(request).await; - - if let Some(ErrorInfo { code, message }) = response.extensions_mut().remove::() { - match accept_header.as_deref() { - Some(b"application/json") => { - return Json(json!({"code": code, "message": message})).into_response() - } - _ => return ErrorTemplate { code, message }.into_response(), - } - } - - response -} diff --git a/src/http/mod.rs b/src/http/mod.rs index a91e735..52897d3 100644 --- a/src/http/mod.rs +++ b/src/http/mod.rs @@ -1 +1,2 @@ pub mod error; +pub mod response; diff --git a/src/http/response.rs b/src/http/response.rs new file mode 100644 index 0000000..7cf4777 --- /dev/null +++ b/src/http/response.rs @@ -0,0 +1,65 @@ +use askama::Template; +use askama_axum::IntoResponse; +use axum::{ + http::{header::ACCEPT, Request, StatusCode}, + middleware::Next, + response::Html, + Json, +}; +use serde_json::json; + +use super::error::{AppError, ErrorInfo}; + +struct Response { + html: String, + json: serde_json::Value, +} + +#[derive(Template)] +#[template(path = "error.html")] +struct ErrorTemplate { + code: String, + message: String, +} + +pub fn reply( + html: T, + json: serde_json::Value, +) -> Result { + let mut response = StatusCode::OK.into_response(); + response.extensions_mut().insert(Response { + html: html.render()?, + json, + }); + Ok(response) +} + +pub async fn response_interceptor( + request: Request, + next: Next, +) -> axum::response::Response { + let accept_header = request + .headers() + .get(&ACCEPT) + .map(|value| value.as_ref().to_owned()); + + let mut response = next.run(request).await; + + if let Some(ErrorInfo { code, message }) = response.extensions_mut().remove::() { + match accept_header.as_deref() { + Some(b"application/json") => { + return Json(json!({"code": code, "message": message})).into_response() + } + _ => return ErrorTemplate { code, message }.into_response(), + } + } + + if let Some(Response { html, json }) = response.extensions_mut().remove::() { + match accept_header.as_deref() { + Some(b"application/json") => return Json(json).into_response(), + _ => return Html(html).into_response(), + } + } + + response +} diff --git a/src/main.rs b/src/main.rs index b9bca3b..bafb411 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,7 +4,7 @@ use bollard::Docker; use clap::Parser; use std::net::SocketAddr; -use crate::http::error::error_interceptor; +use crate::http::response::response_interceptor; mod http; mod route; @@ -52,7 +52,7 @@ async fn main() -> Result<()> { let app = route::router() .with_state(state) - .layer(from_fn(error_interceptor)); + .layer(from_fn(response_interceptor)); axum::Server::bind(&args.bind) .serve(app.into_make_service()) diff --git a/src/route/mod.rs b/src/route/mod.rs index 29cd367..f169d35 100644 --- a/src/route/mod.rs +++ b/src/route/mod.rs @@ -1,7 +1,12 @@ use askama::Template; +use askama_axum::IntoResponse; use axum::{extract::State, routing::get, Router}; +use serde_json::json; -use crate::{http::error::AppError, stack, AppState}; +use crate::{ + http::{error::AppError, response::reply}, + stack, AppState, +}; #[derive(Template)] #[template(path = "home.html")] @@ -9,11 +14,16 @@ struct HomeTemplate { stacks: Vec, } -async fn home(State(state): State) -> Result { +async fn home(State(state): State) -> Result { let list = stack::list(&state.stack_dir) .await .map_err(AppError::from)?; - Ok(HomeTemplate { stacks: list }) + reply( + HomeTemplate { + stacks: list.clone(), + }, + json!({ "stacks": list}), + ) } pub(super) fn router() -> Router { diff --git a/templates/home.html b/templates/home.html index 8f26caa..c5d7cb7 100644 --- a/templates/home.html +++ b/templates/home.html @@ -5,7 +5,7 @@ {% block content %}
{% for stack in stacks %} -
  • {{stack}}
  • +
  • {{stack}}
  • {% endfor %}
    {% endblock %} \ No newline at end of file