140 lines
3.8 KiB
Rust
140 lines
3.8 KiB
Rust
mod admin;
|
|
mod health_check;
|
|
mod home;
|
|
mod login;
|
|
mod posts;
|
|
mod subscriptions;
|
|
mod subscriptions_confirm;
|
|
|
|
pub use admin::*;
|
|
use askama::Template;
|
|
use axum::{
|
|
http::HeaderMap,
|
|
response::{Html, IntoResponse, Response},
|
|
};
|
|
pub use health_check::*;
|
|
pub use home::*;
|
|
pub use login::*;
|
|
pub use posts::*;
|
|
use reqwest::StatusCode;
|
|
pub use subscriptions::*;
|
|
pub use subscriptions_confirm::*;
|
|
|
|
use crate::{
|
|
authentication::AuthError,
|
|
templates::{InternalErrorTemplate, MessageTemplate, NotFoundTemplate},
|
|
};
|
|
|
|
#[derive(thiserror::Error)]
|
|
pub enum AppError {
|
|
#[error("An unexpected error was encountered.")]
|
|
UnexpectedError {
|
|
#[source]
|
|
error: anyhow::Error,
|
|
full_page: bool,
|
|
},
|
|
#[error("A validation error happened.")]
|
|
FormError(#[source] anyhow::Error),
|
|
#[error("Authentication is required.")]
|
|
NotAuthenticated,
|
|
}
|
|
|
|
impl From<anyhow::Error> for AppError {
|
|
fn from(value: anyhow::Error) -> Self {
|
|
Self::UnexpectedError {
|
|
error: value,
|
|
full_page: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl AppError {
|
|
pub fn unexpected_page(error: anyhow::Error) -> Self {
|
|
Self::UnexpectedError {
|
|
error,
|
|
full_page: true,
|
|
}
|
|
}
|
|
|
|
pub fn unexpected_message(error: anyhow::Error) -> Self {
|
|
Self::UnexpectedError {
|
|
error,
|
|
full_page: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl std::fmt::Debug for AppError {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
error_chain_fmt(self, f)
|
|
}
|
|
}
|
|
|
|
impl IntoResponse for AppError {
|
|
fn into_response(self) -> Response {
|
|
tracing::error!("{:?}", self);
|
|
|
|
match &self {
|
|
AppError::UnexpectedError {
|
|
error: _,
|
|
full_page,
|
|
} => {
|
|
let html = if *full_page {
|
|
Html(InternalErrorTemplate.render().unwrap())
|
|
} else {
|
|
let template = MessageTemplate::Error {
|
|
message: "An internal server error occured.".into(),
|
|
};
|
|
Html(template.render().unwrap())
|
|
};
|
|
(StatusCode::INTERNAL_SERVER_ERROR, html).into_response()
|
|
}
|
|
AppError::FormError(error) => {
|
|
let template = MessageTemplate::Error {
|
|
message: error.to_string(),
|
|
};
|
|
Html(template.render().unwrap()).into_response()
|
|
}
|
|
AppError::NotAuthenticated => {
|
|
let mut headers = HeaderMap::new();
|
|
headers.insert("HX-Redirect", "/login".parse().unwrap());
|
|
(StatusCode::OK, headers).into_response()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<AdminError> for AppError {
|
|
fn from(value: AdminError) -> Self {
|
|
match value {
|
|
AdminError::UnexpectedError(error) => AppError::unexpected_message(error),
|
|
AdminError::NotAuthenticated => AppError::NotAuthenticated,
|
|
AdminError::ChangePassword(s) => AppError::FormError(anyhow::anyhow!(s)),
|
|
AdminError::Publish(e) => AppError::FormError(e),
|
|
AdminError::Idempotency(s) => AppError::UnexpectedError {
|
|
error: anyhow::anyhow!(s),
|
|
full_page: false,
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<AuthError> for AppError {
|
|
fn from(value: AuthError) -> Self {
|
|
match value {
|
|
AuthError::UnexpectedError(error) => AppError::unexpected_message(error),
|
|
AuthError::InvalidCredentials(error) => {
|
|
AppError::FormError(error.context("Invalid credentials."))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub async fn not_found() -> Response {
|
|
(
|
|
StatusCode::NOT_FOUND,
|
|
Html(NotFoundTemplate.render().unwrap()),
|
|
)
|
|
.into_response()
|
|
}
|