- Add EmailTokenService for handling email verification and password reset tokens. - Create EmailService for sending verification and reset emails via SMTP. - Update AuthService to handle email verification status during login. - Modify user registration to redirect to a check email screen instead of issuing a token. - Implement resend verification email functionality. - Add deep link handling for email verification and password reset in the mobile app. - Update mobile app routes and components to support new email verification flow. - Enhance error handling for unverified emails during login attempts. - Update configuration to include SMTP settings for email service.
87 lines
2.3 KiB
Rust
87 lines
2.3 KiB
Rust
use axum::{
|
|
http::StatusCode,
|
|
response::{IntoResponse, Response},
|
|
Json,
|
|
};
|
|
use serde_json::json;
|
|
|
|
/// Erro centralizado da aplicação.
|
|
/// Toda camada retorna `Result<T, AppError>`.
|
|
#[derive(Debug, thiserror::Error)]
|
|
pub enum AppError {
|
|
#[error("not found")]
|
|
NotFound,
|
|
|
|
#[error("unauthorized")]
|
|
Unauthorized,
|
|
|
|
#[error("forbidden")]
|
|
Forbidden,
|
|
|
|
#[error("email not verified")]
|
|
EmailNotVerified,
|
|
|
|
#[error("validation error: {0}")]
|
|
Validation(String),
|
|
|
|
#[error("bad request: {0}")]
|
|
BadRequest(String),
|
|
|
|
#[error("conflict: {0}")]
|
|
Conflict(String),
|
|
|
|
#[error("unprocessable entity: {0}")]
|
|
UnprocessableEntity(String),
|
|
|
|
#[error("internal error")]
|
|
Internal(#[from] anyhow::Error),
|
|
}
|
|
|
|
impl AppError {
|
|
fn status_and_code(&self) -> (StatusCode, &'static str) {
|
|
match self {
|
|
Self::NotFound => (StatusCode::NOT_FOUND, "NOT_FOUND"),
|
|
Self::Unauthorized => (StatusCode::UNAUTHORIZED, "UNAUTHORIZED"),
|
|
Self::Forbidden => (StatusCode::FORBIDDEN, "FORBIDDEN"),
|
|
Self::EmailNotVerified => (StatusCode::FORBIDDEN, "EMAIL_NOT_VERIFIED"),
|
|
Self::Validation(_) => (StatusCode::BAD_REQUEST, "VALIDATION_ERROR"),
|
|
Self::BadRequest(_) => (StatusCode::BAD_REQUEST, "BAD_REQUEST"),
|
|
Self::Conflict(_) => (StatusCode::CONFLICT, "CONFLICT"),
|
|
Self::UnprocessableEntity(_) => (StatusCode::UNPROCESSABLE_ENTITY, "UNPROCESSABLE_ENTITY"),
|
|
Self::Internal(_) => (StatusCode::INTERNAL_SERVER_ERROR, "INTERNAL_ERROR"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl IntoResponse for AppError {
|
|
fn into_response(self) -> Response {
|
|
let (status, code) = self.status_and_code();
|
|
|
|
if status == StatusCode::INTERNAL_SERVER_ERROR {
|
|
tracing::error!(error = %self, "Internal server error");
|
|
}
|
|
|
|
let body = Json(json!({
|
|
"error": self.to_string(),
|
|
"code": code,
|
|
}));
|
|
|
|
(status, body).into_response()
|
|
}
|
|
}
|
|
|
|
impl From<sqlx::Error> for AppError {
|
|
fn from(err: sqlx::Error) -> Self {
|
|
match err {
|
|
sqlx::Error::RowNotFound => Self::NotFound,
|
|
_ => Self::Internal(err.into()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<validator::ValidationErrors> for AppError {
|
|
fn from(err: validator::ValidationErrors) -> Self {
|
|
Self::Validation(err.to_string())
|
|
}
|
|
}
|