Error handling refactor and 500 page/message templates

This commit is contained in:
Alphonse Paix
2025-09-20 04:06:48 +02:00
parent 7971095227
commit d85879a004
14 changed files with 223 additions and 201 deletions

View File

@@ -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())
}