Improve error handling
This commit is contained in:
parent
e667b4452b
commit
29e0907b58
4 changed files with 207 additions and 199 deletions
|
@ -95,10 +95,7 @@ macro_rules! retrieve_sub_entry {
|
|||
}};
|
||||
}
|
||||
|
||||
pub fn entry_recursive(
|
||||
entry_id: &str,
|
||||
conn: Connection,
|
||||
) -> QueryResult<FullDatabaseEntry> {
|
||||
pub fn entry_recursive(entry_id: &str, conn: Connection) -> QueryResult<FullDatabaseEntry> {
|
||||
use crate::database::schema::entries::dsl::entries;
|
||||
|
||||
let entry: Entry = entries
|
||||
|
|
|
@ -2,13 +2,13 @@ use crate::AppState;
|
|||
use axum::{
|
||||
async_trait,
|
||||
extract::FromRequestParts,
|
||||
http::{header::AUTHORIZATION, request::Parts, StatusCode},
|
||||
http::{request::Parts, StatusCode},
|
||||
};
|
||||
use diesel::{
|
||||
r2d2::{ConnectionManager, PooledConnection},
|
||||
SqliteConnection,
|
||||
};
|
||||
use tracing::{error, warn};
|
||||
use tracing::error;
|
||||
|
||||
pub struct Database(pub PooledConnection<ConnectionManager<SqliteConnection>>);
|
||||
|
||||
|
|
|
@ -80,20 +80,18 @@ async fn genkey(
|
|||
use crate::database::schema::session_keys::dsl::*;
|
||||
|
||||
let session_key = Uuid::new_v4().to_string();
|
||||
let result = diesel::insert_into(session_keys)
|
||||
diesel::insert_into(session_keys)
|
||||
.values((user_id.eq(&user.uid), key.eq(&session_key)))
|
||||
.execute(&mut conn);
|
||||
.execute(&mut conn)
|
||||
.map_err(|err| {
|
||||
error!(
|
||||
"failed to insert into session_keys {}, error: {err:?}",
|
||||
user.uid
|
||||
);
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
|
||||
if result.is_ok() {
|
||||
Ok(Json(GenkeyResponse { session_key }))
|
||||
} else {
|
||||
error!(
|
||||
"failed to insert into session_keys {}, error: {:?}",
|
||||
user.uid,
|
||||
result.err()
|
||||
);
|
||||
Err(StatusCode::INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
Ok(Json(GenkeyResponse { session_key }))
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
|
@ -113,27 +111,26 @@ async fn login(
|
|||
) -> Result<Json<LoginResponse>, StatusCode> {
|
||||
if let Ok(Some(user)) = actions::user_by_email(&req.email, &mut conn) {
|
||||
let parsed_hash = PasswordHash::new(&user.password).expect("invalid argon2 password hash");
|
||||
if Argon2::default()
|
||||
Argon2::default()
|
||||
.verify_password(req.password.as_bytes(), &parsed_hash)
|
||||
.is_err()
|
||||
{
|
||||
info!("failed login attempt, invalid password: {}", &req.email);
|
||||
Err(StatusCode::UNAUTHORIZED)
|
||||
} else {
|
||||
info!("valid login attempt: {}", req.email);
|
||||
match encode_jwt(&JwtUser {
|
||||
uid: user.id,
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
exp: expiration_time(),
|
||||
}) {
|
||||
Ok(token) => Ok(Json(LoginResponse { token })),
|
||||
Err(err) => {
|
||||
error!("token couldn't be encoded: {:?}", err);
|
||||
Err(StatusCode::INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
}
|
||||
}
|
||||
.map_err(|_err| {
|
||||
info!("failed login attempt, invalid password: {}", &req.email);
|
||||
StatusCode::UNAUTHORIZED
|
||||
})?;
|
||||
|
||||
info!("valid login attempt: {}", req.email);
|
||||
let token = encode_jwt(&JwtUser {
|
||||
uid: user.id,
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
exp: expiration_time(),
|
||||
})
|
||||
.map_err(|err| {
|
||||
error!("token couldn't be encoded: {:?}", err);
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
|
||||
Ok(Json(LoginResponse { token }))
|
||||
} else {
|
||||
info!("failed login attempt, email does not exist: {}", &req.email);
|
||||
Err(StatusCode::UNAUTHORIZED)
|
||||
|
@ -159,85 +156,75 @@ async fn register(
|
|||
use crate::database::schema::limits::dsl as limits;
|
||||
use crate::database::schema::users::dsl as users;
|
||||
|
||||
let user = actions::user_by_email(&req.email, &mut conn);
|
||||
|
||||
if user.is_err() {
|
||||
let user = actions::user_by_email(&req.email, &mut conn).map_err(|err| {
|
||||
error!(
|
||||
"failed to retrieve potential existing user from database: {}, error: {:?}",
|
||||
&req.email,
|
||||
user.err()
|
||||
"failed to retrieve potential existing user from database: {}, error: {err:?}",
|
||||
&req.email
|
||||
);
|
||||
return Err(StatusCode::INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
|
||||
if user.is_ok_and(|v| v.is_some()) {
|
||||
if user.is_some() {
|
||||
info!("tried to register existing user: {}", &req.email);
|
||||
return Err(StatusCode::BAD_REQUEST);
|
||||
}
|
||||
|
||||
let limit_id = Uuid::new_v4().to_string();
|
||||
let result = diesel::insert_into(limits::limits)
|
||||
diesel::insert_into(limits::limits)
|
||||
.values((
|
||||
limits::id.eq(&limit_id),
|
||||
limits::current_asset_count.eq(0),
|
||||
limits::max_asset_count.eq(10),
|
||||
))
|
||||
.execute(&mut conn);
|
||||
|
||||
if result.is_err() {
|
||||
error!(
|
||||
"failed to insert into limits: {}, error: {:?}",
|
||||
&req.email,
|
||||
result.err()
|
||||
);
|
||||
return Err(StatusCode::INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
.execute(&mut conn)
|
||||
.map_err(|err| {
|
||||
error!(
|
||||
"failed to insert into limits: {}, error: {err:?}",
|
||||
&req.email
|
||||
);
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
|
||||
let salt = SaltString::generate(&mut OsRng);
|
||||
let argon2 = Argon2::default();
|
||||
let password_hash = argon2.hash_password(req.password.as_bytes(), &salt);
|
||||
let password_hash = argon2
|
||||
.hash_password(req.password.as_bytes(), &salt)
|
||||
.map_err(|err| {
|
||||
error!("failed to hash password: {err:?}");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
|
||||
if let Ok(password_hash) = password_hash {
|
||||
let user_id = Uuid::new_v4().to_string();
|
||||
let result = diesel::insert_into(users::users)
|
||||
.values((
|
||||
users::id.eq(&user_id),
|
||||
users::created_at.eq(Utc::now().naive_utc()),
|
||||
users::last_connected_at.eq(Utc::now().naive_utc()),
|
||||
users::email.eq(&req.email),
|
||||
users::password.eq(password_hash.to_string()),
|
||||
users::name.eq(&req.name),
|
||||
users::limits.eq(&limit_id),
|
||||
// FIXME(sofia): Implement diesel::Expression for List
|
||||
users::assets.eq("[]"),
|
||||
))
|
||||
.execute(&mut conn);
|
||||
let user_id = Uuid::new_v4().to_string();
|
||||
diesel::insert_into(users::users)
|
||||
.values((
|
||||
users::id.eq(&user_id),
|
||||
users::created_at.eq(Utc::now().naive_utc()),
|
||||
users::last_connected_at.eq(Utc::now().naive_utc()),
|
||||
users::email.eq(&req.email),
|
||||
users::password.eq(password_hash.to_string()),
|
||||
users::name.eq(&req.name),
|
||||
users::limits.eq(&limit_id),
|
||||
// FIXME(sofia): Implement diesel::Expression for List
|
||||
users::assets.eq("[]"),
|
||||
))
|
||||
.execute(&mut conn)
|
||||
.map_err(|err| {
|
||||
error!("failed to insert into users: {}, error: {err:?}", req.email);
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
|
||||
if result.is_err() {
|
||||
error!(
|
||||
"failed to insert into users: {}, error: {:?}",
|
||||
req.email,
|
||||
result.err()
|
||||
);
|
||||
return Err(StatusCode::INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
let token = crate::auth::encode_jwt(&JwtUser {
|
||||
uid: user_id,
|
||||
email: req.email,
|
||||
name: req.name,
|
||||
exp: expiration_time(),
|
||||
})
|
||||
.map_err(|err| {
|
||||
error!("token couldn't be encoded: {:?}", err);
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
|
||||
match crate::auth::encode_jwt(&JwtUser {
|
||||
uid: user_id,
|
||||
email: req.email,
|
||||
name: req.name,
|
||||
exp: expiration_time(),
|
||||
}) {
|
||||
Ok(token) => Ok(Json(RegisterResponse { token })),
|
||||
Err(err) => {
|
||||
error!("token couldn't be encoded: {:?}", err);
|
||||
Err(StatusCode::INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error!("failed to hash password: {:?}", password_hash.err());
|
||||
Err(StatusCode::INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
Ok(Json(RegisterResponse { token }))
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
|
@ -265,17 +252,16 @@ async fn list_heirs(
|
|||
Database(mut conn): Database,
|
||||
ExtractJwtUser(user): ExtractJwtUser,
|
||||
) -> Result<Json<Vec<HttpHeir>>, StatusCode> {
|
||||
let result = actions::list_heirs(&user.uid, &mut conn);
|
||||
if let Ok(heirs) = result {
|
||||
Ok(Json(heirs.into_iter().map(HttpHeir::from).collect()))
|
||||
} else {
|
||||
error!(
|
||||
"failed to obtain heirs: {}, error: {:?}",
|
||||
user.uid,
|
||||
result.err()
|
||||
);
|
||||
Err(StatusCode::INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
let heirs = actions::list_heirs(&user.uid, &mut conn)
|
||||
.map_err(|err| {
|
||||
error!(
|
||||
"failed to obtain heirs: {}, error: {err:?}",
|
||||
user.uid
|
||||
);
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
|
||||
Ok(Json(heirs.into_iter().map(HttpHeir::from).collect()))
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
|
@ -294,7 +280,7 @@ async fn insert_heir(
|
|||
use crate::database::schema::heirs::dsl::*;
|
||||
|
||||
let heir_id = Uuid::new_v4().to_string();
|
||||
let result = diesel::insert_into(heirs)
|
||||
diesel::insert_into(heirs)
|
||||
.values((
|
||||
id.eq(heir_id),
|
||||
created_at.eq(Utc::now().naive_utc()),
|
||||
|
@ -303,28 +289,25 @@ async fn insert_heir(
|
|||
// Only e-mail is implemented right now
|
||||
email.eq(req.value),
|
||||
))
|
||||
.execute(&mut conn);
|
||||
.execute(&mut conn)
|
||||
.map_err(|err| {
|
||||
error!(
|
||||
"failed to insert into heirs: {}, error: {err:?}",
|
||||
user.uid
|
||||
);
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
|
||||
if result.is_err() {
|
||||
error!(
|
||||
"failed to insert into heirs: {}, error: {:?}",
|
||||
user.uid,
|
||||
result.err()
|
||||
);
|
||||
return Err(StatusCode::INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
let heirs_list = actions::list_heirs(&user.uid, &mut conn)
|
||||
.map_err(|err| {
|
||||
error!(
|
||||
"failed to obtain heirs: {}, error: {err:?}",
|
||||
user.uid
|
||||
);
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
|
||||
let result = actions::list_heirs(&user.uid, &mut conn);
|
||||
if let Ok(heirs_list) = result {
|
||||
Ok(Json(heirs_list.into_iter().map(HttpHeir::from).collect()))
|
||||
} else {
|
||||
error!(
|
||||
"failed to obtain heirs: {}, error: {:?}",
|
||||
user.uid,
|
||||
result.err()
|
||||
);
|
||||
Err(StatusCode::INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
Ok(Json(heirs_list.into_iter().map(HttpHeir::from).collect()))
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
|
@ -339,26 +322,24 @@ async fn delete_heir(
|
|||
) -> Result<Json<Vec<HttpHeir>>, StatusCode> {
|
||||
use crate::database::schema::heirs::dsl::*;
|
||||
|
||||
let result = diesel::delete(heirs.filter(id.eq(&req.id))).execute(&mut conn);
|
||||
if result.is_err() {
|
||||
error!(
|
||||
"failed to delete from heirs: {}, heir_id: {}, error: {:?}",
|
||||
user.uid,
|
||||
req.id,
|
||||
result.err()
|
||||
);
|
||||
return Err(StatusCode::INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
diesel::delete(heirs.filter(id.eq(&req.id))).execute(&mut conn)
|
||||
.map_err(|err| {
|
||||
error!(
|
||||
"failed to delete from heirs: {}, heir_id: {}, error: {err:?}",
|
||||
user.uid,
|
||||
req.id,
|
||||
);
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
|
||||
let result = actions::list_heirs(&user.uid, &mut conn);
|
||||
if let Ok(heirs_list) = result {
|
||||
Ok(Json(heirs_list.into_iter().map(HttpHeir::from).collect()))
|
||||
} else {
|
||||
error!(
|
||||
"failed to obtain heirs: {}, error: {:?}",
|
||||
user.uid,
|
||||
result.err()
|
||||
);
|
||||
Err(StatusCode::INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
let heirs_list = actions::list_heirs(&user.uid, &mut conn)
|
||||
.map_err(|err| {
|
||||
error!(
|
||||
"failed to obtain heirs: {}, error: {err:?}",
|
||||
user.uid,
|
||||
);
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
|
||||
Ok(Json(heirs_list.into_iter().map(HttpHeir::from).collect()))
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ use crate::{
|
|||
AppState,
|
||||
};
|
||||
use axum::{
|
||||
extract::{Query, State},
|
||||
extract::Query,
|
||||
http::StatusCode,
|
||||
routing::{delete, get, put},
|
||||
Json, Router,
|
||||
|
@ -85,18 +85,18 @@ async fn list_entries(
|
|||
Query(query): Query<ListEntriesQuery>,
|
||||
ExtractUser(user): ExtractUser,
|
||||
) -> Result<Json<Vec<HttpEntry>>, StatusCode> {
|
||||
let result = actions::list_entries_recursive(&user.id, query.offset, query.limit, &mut conn);
|
||||
if let Ok(entries) = result {
|
||||
Ok(Json(
|
||||
entries
|
||||
.into_iter()
|
||||
.filter_map(|v| HttpEntry::try_from(v).ok())
|
||||
.collect(),
|
||||
))
|
||||
} else {
|
||||
error!("failed to obtain entries {}: {:?}", user.id, result.err());
|
||||
Err(StatusCode::INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
let entries = actions::list_entries_recursive(&user.id, query.offset, query.limit, &mut conn)
|
||||
.map_err(|err| {
|
||||
error!("failed to obtain entries {}: {err:?}", user.id);
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
|
||||
Ok(Json(
|
||||
entries
|
||||
.into_iter()
|
||||
.filter_map(|v| HttpEntry::try_from(v).ok())
|
||||
.collect(),
|
||||
))
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
|
@ -115,8 +115,18 @@ async fn insert_entry(
|
|||
|
||||
let entry = entry.entry;
|
||||
match entry.base {
|
||||
HttpEntryBase::Album { ref artist, ref title, ref links, ref id }
|
||||
| HttpEntryBase::Song { ref artist, ref title, ref links, ref id } => {
|
||||
HttpEntryBase::Album {
|
||||
ref artist,
|
||||
ref title,
|
||||
ref links,
|
||||
ref id,
|
||||
}
|
||||
| HttpEntryBase::Song {
|
||||
ref artist,
|
||||
ref title,
|
||||
ref links,
|
||||
ref id,
|
||||
} => {
|
||||
music_entry = Some(MusicEntry {
|
||||
id: Uuid::new_v4().to_string(),
|
||||
// FIXME(sofia): These clones seems unnecesary
|
||||
|
@ -125,7 +135,7 @@ async fn insert_entry(
|
|||
artist: artist.clone(),
|
||||
universal_ids: id.clone().into(),
|
||||
})
|
||||
},
|
||||
}
|
||||
HttpEntryBase::Environment { ref location } => {
|
||||
if entry.title.as_ref().is_none_or(|v| v.is_empty()) {
|
||||
warn!(
|
||||
|
@ -167,19 +177,19 @@ async fn insert_entry(
|
|||
HttpEntryBase::Date {
|
||||
ref referenced_date,
|
||||
} => {
|
||||
let naive_date = NaiveDate::parse_from_str(referenced_date, "%Y-%m-%d");
|
||||
if let Err(err) = naive_date {
|
||||
warn!(
|
||||
"invalid date in request for inserting entry: {}, err: {err:?}",
|
||||
user.id
|
||||
);
|
||||
return Err(StatusCode::BAD_REQUEST);
|
||||
}
|
||||
let naive_date = NaiveDate::parse_from_str(referenced_date, "%Y-%m-%d")
|
||||
.map_err(|err| {
|
||||
warn!(
|
||||
"invalid date in request for inserting entry: {}, err: {err:?}",
|
||||
user.id
|
||||
);
|
||||
StatusCode::BAD_REQUEST
|
||||
})?;
|
||||
|
||||
date_entry = Some(DateEntry {
|
||||
id: Uuid::new_v4().to_string(),
|
||||
referenced_date: NaiveDateTime::new(
|
||||
naive_date.unwrap(),
|
||||
naive_date,
|
||||
NaiveTime::from_hms_milli_opt(0, 0, 0, 0).unwrap(),
|
||||
),
|
||||
});
|
||||
|
@ -216,26 +226,47 @@ async fn insert_entry(
|
|||
}
|
||||
|
||||
let music_entry_id = music_entry.as_ref().map(|v| v.id.clone());
|
||||
music_entry.map(|music_entry| actions::insert_music_entry(&music_entry, &mut conn).map_err(|err| {
|
||||
error!("failed to insert into music_entries: {}, error: {err:?}",user.id);
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})).transpose()?;
|
||||
music_entry
|
||||
.map(|music_entry| {
|
||||
actions::insert_music_entry(&music_entry, &mut conn).map_err(|err| {
|
||||
error!(
|
||||
"failed to insert into music_entries: {}, error: {err:?}",
|
||||
user.id
|
||||
);
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
let location_entry_id = location_entry.as_ref().map(|v| v.id.clone());
|
||||
location_entry.map(|location_entry| actions::insert_location_entry(&location_entry, &mut conn).map_err(|err| {
|
||||
error!("failed to insert into location_entries: {}, error: {err:?}",user.id);
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})).transpose()?;
|
||||
location_entry
|
||||
.map(|location_entry| {
|
||||
actions::insert_location_entry(&location_entry, &mut conn).map_err(|err| {
|
||||
error!(
|
||||
"failed to insert into location_entries: {}, error: {err:?}",
|
||||
user.id
|
||||
);
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
let date_entry_id = date_entry.as_ref().map(|v| v.id.clone());
|
||||
date_entry.map(|date_entry| actions::insert_date_entry(&date_entry, &mut conn).map_err(|err| {
|
||||
error!("failed to insert into date_entries: {}, error: {err:?}",user.id);
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})).transpose()?;
|
||||
date_entry
|
||||
.map(|date_entry| {
|
||||
actions::insert_date_entry(&date_entry, &mut conn).map_err(|err| {
|
||||
error!(
|
||||
"failed to insert into date_entries: {}, error: {err:?}",
|
||||
user.id
|
||||
);
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
{
|
||||
use crate::database::schema::entries::dsl as entries;
|
||||
let result = diesel::insert_into(entries::entries)
|
||||
diesel::insert_into(entries::entries)
|
||||
.values((
|
||||
entries::id.eq(Uuid::new_v4().to_string()),
|
||||
entries::user_id.eq(&user.id),
|
||||
|
@ -250,15 +281,14 @@ async fn insert_entry(
|
|||
entries::music_entry.eq(music_entry_id),
|
||||
entries::location_entry.eq(location_entry_id),
|
||||
))
|
||||
.execute(&mut conn);
|
||||
|
||||
if let Err(err) = result {
|
||||
error!(
|
||||
"failed to insert into entries: {}, error: {:?}",
|
||||
user.id, err
|
||||
);
|
||||
return Err(StatusCode::INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
.execute(&mut conn)
|
||||
.map_err(|err| {
|
||||
error!(
|
||||
"failed to insert into entries: {}, error: {err:?}",
|
||||
user.id
|
||||
);
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
Loading…
Reference in a new issue