194 lines
6.9 KiB
Rust
194 lines
6.9 KiB
Rust
|
|
use leptos::prelude::{ServerFnError, ServerFnErrorErr};
|
||
|
|
use serde::{Deserialize, Serialize};
|
||
|
|
use thiserror::Error;
|
||
|
|
use werewolves_proto::error::GameError;
|
||
|
|
|
||
|
|
use crate::game::GameId;
|
||
|
|
|
||
|
|
#[derive(Debug, Clone, PartialEq, Error, Serialize, Deserialize)]
|
||
|
|
pub enum ServerError {
|
||
|
|
#[error("internal server error")]
|
||
|
|
InternalServerError,
|
||
|
|
#[error("not found")]
|
||
|
|
NotFound,
|
||
|
|
#[error("user already exists")]
|
||
|
|
UserAlreadyExists,
|
||
|
|
#[error("invalid credentials")]
|
||
|
|
InvalidCredentials,
|
||
|
|
#[error("token expired")]
|
||
|
|
ExpiredToken,
|
||
|
|
#[error("connection error")]
|
||
|
|
ConnectionError,
|
||
|
|
#[error("invalid request: {0}")]
|
||
|
|
InvalidRequest(String),
|
||
|
|
#[error("you're already in an active game: {0}")]
|
||
|
|
AlreadyInActiveGame(GameId),
|
||
|
|
#[error("{0}")]
|
||
|
|
GameError(#[from] GameError),
|
||
|
|
}
|
||
|
|
|
||
|
|
impl leptos::prelude::FromServerFnError for ServerError {
|
||
|
|
type Encoder = leptos::server_fn::codec::JsonEncoding;
|
||
|
|
|
||
|
|
fn from_server_fn_error(value: ServerFnErrorErr) -> Self {
|
||
|
|
match value {
|
||
|
|
ServerFnErrorErr::ServerError(err) => {
|
||
|
|
log::error!("server error: {err}; truncating to ServerError::InternalServerError");
|
||
|
|
ServerError::InternalServerError
|
||
|
|
}
|
||
|
|
ServerFnErrorErr::MiddlewareError(err) => {
|
||
|
|
log::error!(
|
||
|
|
"middleware error: {err}; truncating to ServerError::InternalServerError"
|
||
|
|
);
|
||
|
|
ServerError::InternalServerError
|
||
|
|
}
|
||
|
|
ServerFnErrorErr::Request(err) => {
|
||
|
|
const CONN_ERR: &str = "TypeError: NetworkError when attempting to fetch resource.";
|
||
|
|
if err == CONN_ERR {
|
||
|
|
Self::ConnectionError
|
||
|
|
} else {
|
||
|
|
Self::InvalidRequest(err)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
err => {
|
||
|
|
let t = match &err {
|
||
|
|
ServerFnErrorErr::Registration(_) => "Registration",
|
||
|
|
ServerFnErrorErr::UnsupportedRequestMethod(_) => "UnsupportedRequestMethod",
|
||
|
|
ServerFnErrorErr::Request(_) => "Request",
|
||
|
|
ServerFnErrorErr::ServerError(_) => "ServerError",
|
||
|
|
ServerFnErrorErr::MiddlewareError(_) => "MiddlewareError",
|
||
|
|
ServerFnErrorErr::Deserialization(_) => "Deserialization",
|
||
|
|
ServerFnErrorErr::Serialization(_) => "Serialization",
|
||
|
|
ServerFnErrorErr::Args(_) => "Args",
|
||
|
|
ServerFnErrorErr::MissingArg(_) => "MissingArg",
|
||
|
|
ServerFnErrorErr::Response(_) => "Response",
|
||
|
|
};
|
||
|
|
Self::InvalidRequest(format!("[{t}]: {err}"))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// impl core::str::FromStr for ServerError {
|
||
|
|
// type Err = core::convert::Infallible;
|
||
|
|
|
||
|
|
// fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||
|
|
// panic!("ServerError::FromStr({s})")
|
||
|
|
// }
|
||
|
|
// }
|
||
|
|
// impl From<ServerFnError> for ServerError {
|
||
|
|
// fn from(err: ServerFnError) -> Self {
|
||
|
|
// match err {
|
||
|
|
// ServerFnError::ServerError(err) => {
|
||
|
|
// log::error!("server error: {err}; truncating to ServerError::InternalServerError");
|
||
|
|
// ServerError::InternalServerError
|
||
|
|
// }
|
||
|
|
// ServerFnError::MiddlewareError(err) => {
|
||
|
|
// log::error!(
|
||
|
|
// "middleware error: {err}; truncating to ServerError::InternalServerError"
|
||
|
|
// );
|
||
|
|
// ServerError::InternalServerError
|
||
|
|
// }
|
||
|
|
// ServerFnError::Request(err) => {
|
||
|
|
// log::error!("[{err}]");
|
||
|
|
// Self::ConnectionError
|
||
|
|
// }
|
||
|
|
// err => {
|
||
|
|
// let t = match &err {
|
||
|
|
// ServerFnError::Registration(_) => "Registration",
|
||
|
|
// ServerFnError::Request(_) => "Request",
|
||
|
|
// ServerFnError::ServerError(_) => "ServerError",
|
||
|
|
// ServerFnError::MiddlewareError(_) => "MiddlewareError",
|
||
|
|
// ServerFnError::Deserialization(_) => "Deserialization",
|
||
|
|
// ServerFnError::Serialization(_) => "Serialization",
|
||
|
|
// ServerFnError::Args(_) => "Args",
|
||
|
|
// ServerFnError::MissingArg(_) => "MissingArg",
|
||
|
|
// ServerFnError::Response(_) => "Response",
|
||
|
|
// ServerFnError::WrappedServerError(_) => "WrappedServerError",
|
||
|
|
// };
|
||
|
|
// Self::InvalidRequest(format!("[{t}]: {err}"))
|
||
|
|
// }
|
||
|
|
// }
|
||
|
|
// }
|
||
|
|
// }
|
||
|
|
|
||
|
|
impl From<DatabaseError> for ServerError {
|
||
|
|
fn from(err: DatabaseError) -> Self {
|
||
|
|
match err {
|
||
|
|
DatabaseError::NotFound => ServerError::NotFound,
|
||
|
|
DatabaseError::UserAlreadyExists => ServerError::UserAlreadyExists,
|
||
|
|
#[allow(unreachable_patterns)]
|
||
|
|
_ => {
|
||
|
|
log::error!(
|
||
|
|
"converting database error into ServerError::InternalServerError: {err}"
|
||
|
|
);
|
||
|
|
ServerError::InternalServerError
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#[derive(Debug, Clone, PartialEq, Error)]
|
||
|
|
pub enum DatabaseError {
|
||
|
|
#[error("user already exists")]
|
||
|
|
UserAlreadyExists,
|
||
|
|
#[error("password hashing error: {0}")]
|
||
|
|
PasswordHashError(String),
|
||
|
|
#[error("sqlx error: {0}")]
|
||
|
|
SqlxError(String),
|
||
|
|
#[error("not found")]
|
||
|
|
NotFound,
|
||
|
|
#[error("(de)serialization error: {0}")]
|
||
|
|
Serialization(String),
|
||
|
|
}
|
||
|
|
|
||
|
|
#[cfg(feature = "ssr")]
|
||
|
|
impl From<serde_json::Error> for DatabaseError {
|
||
|
|
fn from(value: serde_json::Error) -> Self {
|
||
|
|
Self::Serialization(value.to_string())
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#[cfg(feature = "ssr")]
|
||
|
|
impl axum::response::IntoResponse for ServerError {
|
||
|
|
fn into_response(self) -> axum::response::Response {
|
||
|
|
use axum::{Json, http::StatusCode};
|
||
|
|
|
||
|
|
use crate::cbor::Cbor;
|
||
|
|
|
||
|
|
(
|
||
|
|
match self {
|
||
|
|
ServerError::AlreadyInActiveGame(_)
|
||
|
|
| ServerError::GameError(_)
|
||
|
|
| ServerError::InvalidCredentials
|
||
|
|
| ServerError::InvalidRequest(_)
|
||
|
|
| ServerError::UserAlreadyExists => StatusCode::BAD_REQUEST,
|
||
|
|
ServerError::NotFound => StatusCode::NOT_FOUND,
|
||
|
|
ServerError::ConnectionError | ServerError::InternalServerError => {
|
||
|
|
StatusCode::INTERNAL_SERVER_ERROR
|
||
|
|
}
|
||
|
|
ServerError::ExpiredToken => StatusCode::UNAUTHORIZED,
|
||
|
|
},
|
||
|
|
Json(self),
|
||
|
|
)
|
||
|
|
.into_response()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#[cfg(feature = "ssr")]
|
||
|
|
impl From<sqlx::Error> for DatabaseError {
|
||
|
|
fn from(err: sqlx::Error) -> Self {
|
||
|
|
match err {
|
||
|
|
sqlx::Error::RowNotFound => Self::NotFound,
|
||
|
|
_ => Self::SqlxError(err.to_string()),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#[cfg(feature = "ssr")]
|
||
|
|
impl From<argon2::password_hash::Error> for DatabaseError {
|
||
|
|
fn from(err: argon2::password_hash::Error) -> Self {
|
||
|
|
Self::PasswordHashError(err.to_string())
|
||
|
|
}
|
||
|
|
}
|