Error handling refactor and 500 page/message templates
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
use crate::{
|
||||
authentication::{AuthError, Credentials, validate_credentials},
|
||||
routes::error_chain_fmt,
|
||||
authentication::{Credentials, validate_credentials},
|
||||
routes::AppError,
|
||||
session_state::TypedSession,
|
||||
startup::AppState,
|
||||
templates::MessageTemplate,
|
||||
};
|
||||
use anyhow::Context;
|
||||
use askama::Template;
|
||||
use axum::{
|
||||
Form, Json,
|
||||
Form,
|
||||
extract::State,
|
||||
http::HeaderMap,
|
||||
response::{Html, IntoResponse, Response},
|
||||
@@ -15,47 +15,6 @@ use axum::{
|
||||
use axum::{http::StatusCode, response::Redirect};
|
||||
use secrecy::SecretString;
|
||||
|
||||
#[derive(thiserror::Error)]
|
||||
pub enum LoginError {
|
||||
#[error("Something went wrong.")]
|
||||
UnexpectedError(#[from] anyhow::Error),
|
||||
#[error("Authentication failed.")]
|
||||
AuthError(#[source] anyhow::Error),
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for LoginError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
error_chain_fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoResponse for LoginError {
|
||||
fn into_response(self) -> Response {
|
||||
#[derive(serde::Serialize)]
|
||||
struct ErrorResponse<'a> {
|
||||
message: &'a str,
|
||||
}
|
||||
|
||||
tracing::error!("{:?}", self);
|
||||
|
||||
match &self {
|
||||
LoginError::UnexpectedError(_) => (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
Json(ErrorResponse {
|
||||
message: "An internal server error occured.",
|
||||
}),
|
||||
)
|
||||
.into_response(),
|
||||
LoginError::AuthError(e) => {
|
||||
let template = MessageTemplate::Error {
|
||||
message: e.to_string(),
|
||||
};
|
||||
Html(template.render().unwrap()).into_response()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "../templates/login.html")]
|
||||
struct LoginTemplate;
|
||||
@@ -66,11 +25,11 @@ pub struct LoginFormData {
|
||||
password: SecretString,
|
||||
}
|
||||
|
||||
pub async fn get_login(session: TypedSession) -> Result<Response, LoginError> {
|
||||
pub async fn get_login(session: TypedSession) -> Result<Response, AppError> {
|
||||
if session
|
||||
.get_user_id()
|
||||
.await
|
||||
.map_err(|e| LoginError::UnexpectedError(e.into()))?
|
||||
.context("Failed to retrieve user id from data store.")?
|
||||
.is_some()
|
||||
{
|
||||
Ok(Redirect::to("/admin/dashboard").into_response())
|
||||
@@ -85,39 +44,26 @@ pub async fn post_login(
|
||||
connection_pool, ..
|
||||
}): State<AppState>,
|
||||
Form(form): Form<LoginFormData>,
|
||||
) -> Result<Response, LoginError> {
|
||||
) -> Result<Response, AppError> {
|
||||
let credentials = Credentials {
|
||||
username: form.username.clone(),
|
||||
password: form.password,
|
||||
};
|
||||
tracing::Span::current().record("username", tracing::field::display(&credentials.username));
|
||||
match validate_credentials(credentials, &connection_pool).await {
|
||||
Err(e) => {
|
||||
let e = match e {
|
||||
AuthError::UnexpectedError(_) => LoginError::UnexpectedError(e.into()),
|
||||
AuthError::InvalidCredentials(_) => LoginError::AuthError(e.into()),
|
||||
AuthError::NotAuthenticated => unreachable!(),
|
||||
};
|
||||
Err(e)
|
||||
}
|
||||
Ok(user_id) => {
|
||||
tracing::Span::current().record("user_id", tracing::field::display(&user_id));
|
||||
session
|
||||
.renew()
|
||||
.await
|
||||
.map_err(|e| LoginError::UnexpectedError(e.into()))?;
|
||||
session
|
||||
.insert_user_id(user_id)
|
||||
.await
|
||||
.map_err(|e| LoginError::UnexpectedError(e.into()))?;
|
||||
session
|
||||
.insert_username(form.username)
|
||||
.await
|
||||
.map_err(|e| LoginError::UnexpectedError(e.into()))?;
|
||||
let user_id = validate_credentials(credentials, &connection_pool).await?;
|
||||
tracing::Span::current().record("user_id", tracing::field::display(&user_id));
|
||||
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert("HX-Redirect", "/admin/dashboard".parse().unwrap());
|
||||
Ok((StatusCode::OK, headers).into_response())
|
||||
}
|
||||
}
|
||||
session.renew().await.context("Failed to renew session.")?;
|
||||
session
|
||||
.insert_user_id(user_id)
|
||||
.await
|
||||
.context("Failed to insert user id in session data store.")?;
|
||||
session
|
||||
.insert_username(form.username)
|
||||
.await
|
||||
.context("Failed to insert username in session data store.")?;
|
||||
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert("HX-Redirect", "/admin/dashboard".parse().unwrap());
|
||||
Ok((StatusCode::OK, headers).into_response())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user