hehe
This commit is contained in:
parent
b90e6ae744
commit
c961c2ae23
6 changed files with 96 additions and 41 deletions
|
@ -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<B>(request: Request<B>, next: Next<B>) -> 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::<ErrorInfo>() {
|
||||
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
|
||||
}
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
pub mod error;
|
||||
pub mod response;
|
||||
|
|
65
src/http/response.rs
Normal file
65
src/http/response.rs
Normal file
|
@ -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<T: Template>(
|
||||
html: T,
|
||||
json: serde_json::Value,
|
||||
) -> Result<axum::response::Response, AppError> {
|
||||
let mut response = StatusCode::OK.into_response();
|
||||
response.extensions_mut().insert(Response {
|
||||
html: html.render()?,
|
||||
json,
|
||||
});
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
pub async fn response_interceptor<B>(
|
||||
request: Request<B>,
|
||||
next: Next<B>,
|
||||
) -> 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::<ErrorInfo>() {
|
||||
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::<Response>() {
|
||||
match accept_header.as_deref() {
|
||||
Some(b"application/json") => return Json(json).into_response(),
|
||||
_ => return Html(html).into_response(),
|
||||
}
|
||||
}
|
||||
|
||||
response
|
||||
}
|
|
@ -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())
|
||||
|
|
|
@ -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<String>,
|
||||
}
|
||||
|
||||
async fn home(State(state): State<AppState>) -> Result<HomeTemplate, AppError> {
|
||||
async fn home(State(state): State<AppState>) -> Result<impl IntoResponse, AppError> {
|
||||
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<AppState> {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
{% block content %}
|
||||
<main>
|
||||
{% for stack in stacks %}
|
||||
<li>{{stack}}</li>
|
||||
<li><a href="/stack/{{stack}}">{{stack}}</a></li>
|
||||
{% endfor %}
|
||||
</main>
|
||||
{% endblock %}
|
Reference in a new issue