Improve error handling

This commit is contained in:
Sofía Aritz 2024-10-16 21:15:14 +02:00
parent e667b4452b
commit 29e0907b58
Signed by: sofia
GPG key ID: 90B5116E3542B28F
4 changed files with 207 additions and 199 deletions

View file

@ -95,10 +95,7 @@ macro_rules! retrieve_sub_entry {
}}; }};
} }
pub fn entry_recursive( pub fn entry_recursive(entry_id: &str, conn: Connection) -> QueryResult<FullDatabaseEntry> {
entry_id: &str,
conn: Connection,
) -> QueryResult<FullDatabaseEntry> {
use crate::database::schema::entries::dsl::entries; use crate::database::schema::entries::dsl::entries;
let entry: Entry = entries let entry: Entry = entries

View file

@ -2,13 +2,13 @@ use crate::AppState;
use axum::{ use axum::{
async_trait, async_trait,
extract::FromRequestParts, extract::FromRequestParts,
http::{header::AUTHORIZATION, request::Parts, StatusCode}, http::{request::Parts, StatusCode},
}; };
use diesel::{ use diesel::{
r2d2::{ConnectionManager, PooledConnection}, r2d2::{ConnectionManager, PooledConnection},
SqliteConnection, SqliteConnection,
}; };
use tracing::{error, warn}; use tracing::error;
pub struct Database(pub PooledConnection<ConnectionManager<SqliteConnection>>); pub struct Database(pub PooledConnection<ConnectionManager<SqliteConnection>>);

View file

@ -80,20 +80,18 @@ async fn genkey(
use crate::database::schema::session_keys::dsl::*; use crate::database::schema::session_keys::dsl::*;
let session_key = Uuid::new_v4().to_string(); 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))) .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 }))
Ok(Json(GenkeyResponse { session_key }))
} else {
error!(
"failed to insert into session_keys {}, error: {:?}",
user.uid,
result.err()
);
Err(StatusCode::INTERNAL_SERVER_ERROR)
}
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
@ -113,27 +111,26 @@ async fn login(
) -> Result<Json<LoginResponse>, StatusCode> { ) -> Result<Json<LoginResponse>, StatusCode> {
if let Ok(Some(user)) = actions::user_by_email(&req.email, &mut conn) { 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"); 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) .verify_password(req.password.as_bytes(), &parsed_hash)
.is_err() .map_err(|_err| {
{ info!("failed login attempt, invalid password: {}", &req.email);
info!("failed login attempt, invalid password: {}", &req.email); StatusCode::UNAUTHORIZED
Err(StatusCode::UNAUTHORIZED) })?;
} else {
info!("valid login attempt: {}", req.email); info!("valid login attempt: {}", req.email);
match encode_jwt(&JwtUser { let token = encode_jwt(&JwtUser {
uid: user.id, uid: user.id,
email: user.email, email: user.email,
name: user.name, name: user.name,
exp: expiration_time(), exp: expiration_time(),
}) { })
Ok(token) => Ok(Json(LoginResponse { token })), .map_err(|err| {
Err(err) => { error!("token couldn't be encoded: {:?}", err);
error!("token couldn't be encoded: {:?}", err); StatusCode::INTERNAL_SERVER_ERROR
Err(StatusCode::INTERNAL_SERVER_ERROR) })?;
}
} Ok(Json(LoginResponse { token }))
}
} else { } else {
info!("failed login attempt, email does not exist: {}", &req.email); info!("failed login attempt, email does not exist: {}", &req.email);
Err(StatusCode::UNAUTHORIZED) Err(StatusCode::UNAUTHORIZED)
@ -159,85 +156,75 @@ async fn register(
use crate::database::schema::limits::dsl as limits; use crate::database::schema::limits::dsl as limits;
use crate::database::schema::users::dsl as users; use crate::database::schema::users::dsl as users;
let user = actions::user_by_email(&req.email, &mut conn); let user = actions::user_by_email(&req.email, &mut conn).map_err(|err| {
if user.is_err() {
error!( error!(
"failed to retrieve potential existing user from database: {}, error: {:?}", "failed to retrieve potential existing user from database: {}, error: {err:?}",
&req.email, &req.email
user.err()
); );
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); info!("tried to register existing user: {}", &req.email);
return Err(StatusCode::BAD_REQUEST); return Err(StatusCode::BAD_REQUEST);
} }
let limit_id = Uuid::new_v4().to_string(); let limit_id = Uuid::new_v4().to_string();
let result = diesel::insert_into(limits::limits) diesel::insert_into(limits::limits)
.values(( .values((
limits::id.eq(&limit_id), limits::id.eq(&limit_id),
limits::current_asset_count.eq(0), limits::current_asset_count.eq(0),
limits::max_asset_count.eq(10), limits::max_asset_count.eq(10),
)) ))
.execute(&mut conn); .execute(&mut conn)
.map_err(|err| {
if result.is_err() { error!(
error!( "failed to insert into limits: {}, error: {err:?}",
"failed to insert into limits: {}, error: {:?}", &req.email
&req.email, );
result.err() StatusCode::INTERNAL_SERVER_ERROR
); })?;
return Err(StatusCode::INTERNAL_SERVER_ERROR);
}
let salt = SaltString::generate(&mut OsRng); let salt = SaltString::generate(&mut OsRng);
let argon2 = Argon2::default(); 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 user_id = Uuid::new_v4().to_string(); diesel::insert_into(users::users)
let result = diesel::insert_into(users::users) .values((
.values(( users::id.eq(&user_id),
users::id.eq(&user_id), users::created_at.eq(Utc::now().naive_utc()),
users::created_at.eq(Utc::now().naive_utc()), users::last_connected_at.eq(Utc::now().naive_utc()),
users::last_connected_at.eq(Utc::now().naive_utc()), users::email.eq(&req.email),
users::email.eq(&req.email), users::password.eq(password_hash.to_string()),
users::password.eq(password_hash.to_string()), users::name.eq(&req.name),
users::name.eq(&req.name), users::limits.eq(&limit_id),
users::limits.eq(&limit_id), // FIXME(sofia): Implement diesel::Expression for List
// FIXME(sofia): Implement diesel::Expression for List users::assets.eq("[]"),
users::assets.eq("[]"), ))
)) .execute(&mut conn)
.execute(&mut conn); .map_err(|err| {
error!("failed to insert into users: {}, error: {err:?}", req.email);
StatusCode::INTERNAL_SERVER_ERROR
})?;
if result.is_err() { let token = crate::auth::encode_jwt(&JwtUser {
error!( uid: user_id,
"failed to insert into users: {}, error: {:?}", email: req.email,
req.email, name: req.name,
result.err() exp: expiration_time(),
); })
return Err(StatusCode::INTERNAL_SERVER_ERROR); .map_err(|err| {
} error!("token couldn't be encoded: {:?}", err);
StatusCode::INTERNAL_SERVER_ERROR
})?;
match crate::auth::encode_jwt(&JwtUser { Ok(Json(RegisterResponse { token }))
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)
}
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
@ -265,17 +252,16 @@ async fn list_heirs(
Database(mut conn): Database, Database(mut conn): Database,
ExtractJwtUser(user): ExtractJwtUser, ExtractJwtUser(user): ExtractJwtUser,
) -> Result<Json<Vec<HttpHeir>>, StatusCode> { ) -> Result<Json<Vec<HttpHeir>>, StatusCode> {
let result = actions::list_heirs(&user.uid, &mut conn); let heirs = actions::list_heirs(&user.uid, &mut conn)
if let Ok(heirs) = result { .map_err(|err| {
Ok(Json(heirs.into_iter().map(HttpHeir::from).collect())) error!(
} else { "failed to obtain heirs: {}, error: {err:?}",
error!( user.uid
"failed to obtain heirs: {}, error: {:?}", );
user.uid, StatusCode::INTERNAL_SERVER_ERROR
result.err() })?;
);
Err(StatusCode::INTERNAL_SERVER_ERROR) Ok(Json(heirs.into_iter().map(HttpHeir::from).collect()))
}
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
@ -294,7 +280,7 @@ async fn insert_heir(
use crate::database::schema::heirs::dsl::*; use crate::database::schema::heirs::dsl::*;
let heir_id = Uuid::new_v4().to_string(); let heir_id = Uuid::new_v4().to_string();
let result = diesel::insert_into(heirs) diesel::insert_into(heirs)
.values(( .values((
id.eq(heir_id), id.eq(heir_id),
created_at.eq(Utc::now().naive_utc()), created_at.eq(Utc::now().naive_utc()),
@ -303,28 +289,25 @@ async fn insert_heir(
// Only e-mail is implemented right now // Only e-mail is implemented right now
email.eq(req.value), 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() { let heirs_list = actions::list_heirs(&user.uid, &mut conn)
error!( .map_err(|err| {
"failed to insert into heirs: {}, error: {:?}", error!(
user.uid, "failed to obtain heirs: {}, error: {err:?}",
result.err() user.uid
); );
return Err(StatusCode::INTERNAL_SERVER_ERROR); StatusCode::INTERNAL_SERVER_ERROR
} })?;
let result = actions::list_heirs(&user.uid, &mut conn); Ok(Json(heirs_list.into_iter().map(HttpHeir::from).collect()))
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)
}
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
@ -339,26 +322,24 @@ async fn delete_heir(
) -> Result<Json<Vec<HttpHeir>>, StatusCode> { ) -> Result<Json<Vec<HttpHeir>>, StatusCode> {
use crate::database::schema::heirs::dsl::*; use crate::database::schema::heirs::dsl::*;
let result = diesel::delete(heirs.filter(id.eq(&req.id))).execute(&mut conn); diesel::delete(heirs.filter(id.eq(&req.id))).execute(&mut conn)
if result.is_err() { .map_err(|err| {
error!( error!(
"failed to delete from heirs: {}, heir_id: {}, error: {:?}", "failed to delete from heirs: {}, heir_id: {}, error: {err:?}",
user.uid, user.uid,
req.id, req.id,
result.err() );
); StatusCode::INTERNAL_SERVER_ERROR
return Err(StatusCode::INTERNAL_SERVER_ERROR); })?;
}
let result = actions::list_heirs(&user.uid, &mut conn); let heirs_list = actions::list_heirs(&user.uid, &mut conn)
if let Ok(heirs_list) = result { .map_err(|err| {
Ok(Json(heirs_list.into_iter().map(HttpHeir::from).collect())) error!(
} else { "failed to obtain heirs: {}, error: {err:?}",
error!( user.uid,
"failed to obtain heirs: {}, error: {:?}", );
user.uid, StatusCode::INTERNAL_SERVER_ERROR
result.err() })?;
);
Err(StatusCode::INTERNAL_SERVER_ERROR) Ok(Json(heirs_list.into_iter().map(HttpHeir::from).collect()))
}
} }

View file

@ -30,7 +30,7 @@ use crate::{
AppState, AppState,
}; };
use axum::{ use axum::{
extract::{Query, State}, extract::Query,
http::StatusCode, http::StatusCode,
routing::{delete, get, put}, routing::{delete, get, put},
Json, Router, Json, Router,
@ -85,18 +85,18 @@ async fn list_entries(
Query(query): Query<ListEntriesQuery>, Query(query): Query<ListEntriesQuery>,
ExtractUser(user): ExtractUser, ExtractUser(user): ExtractUser,
) -> Result<Json<Vec<HttpEntry>>, StatusCode> { ) -> Result<Json<Vec<HttpEntry>>, StatusCode> {
let result = actions::list_entries_recursive(&user.id, query.offset, query.limit, &mut conn); let entries = actions::list_entries_recursive(&user.id, query.offset, query.limit, &mut conn)
if let Ok(entries) = result { .map_err(|err| {
Ok(Json( error!("failed to obtain entries {}: {err:?}", user.id);
entries StatusCode::INTERNAL_SERVER_ERROR
.into_iter() })?;
.filter_map(|v| HttpEntry::try_from(v).ok())
.collect(), Ok(Json(
)) entries
} else { .into_iter()
error!("failed to obtain entries {}: {:?}", user.id, result.err()); .filter_map(|v| HttpEntry::try_from(v).ok())
Err(StatusCode::INTERNAL_SERVER_ERROR) .collect(),
} ))
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
@ -115,8 +115,18 @@ async fn insert_entry(
let entry = entry.entry; let entry = entry.entry;
match entry.base { match entry.base {
HttpEntryBase::Album { ref artist, ref title, ref links, ref id } HttpEntryBase::Album {
| HttpEntryBase::Song { ref artist, ref title, ref links, ref id } => { ref artist,
ref title,
ref links,
ref id,
}
| HttpEntryBase::Song {
ref artist,
ref title,
ref links,
ref id,
} => {
music_entry = Some(MusicEntry { music_entry = Some(MusicEntry {
id: Uuid::new_v4().to_string(), id: Uuid::new_v4().to_string(),
// FIXME(sofia): These clones seems unnecesary // FIXME(sofia): These clones seems unnecesary
@ -125,7 +135,7 @@ async fn insert_entry(
artist: artist.clone(), artist: artist.clone(),
universal_ids: id.clone().into(), universal_ids: id.clone().into(),
}) })
}, }
HttpEntryBase::Environment { ref location } => { HttpEntryBase::Environment { ref location } => {
if entry.title.as_ref().is_none_or(|v| v.is_empty()) { if entry.title.as_ref().is_none_or(|v| v.is_empty()) {
warn!( warn!(
@ -167,19 +177,19 @@ async fn insert_entry(
HttpEntryBase::Date { HttpEntryBase::Date {
ref referenced_date, ref referenced_date,
} => { } => {
let naive_date = NaiveDate::parse_from_str(referenced_date, "%Y-%m-%d"); let naive_date = NaiveDate::parse_from_str(referenced_date, "%Y-%m-%d")
if let Err(err) = naive_date { .map_err(|err| {
warn!( warn!(
"invalid date in request for inserting entry: {}, err: {err:?}", "invalid date in request for inserting entry: {}, err: {err:?}",
user.id user.id
); );
return Err(StatusCode::BAD_REQUEST); StatusCode::BAD_REQUEST
} })?;
date_entry = Some(DateEntry { date_entry = Some(DateEntry {
id: Uuid::new_v4().to_string(), id: Uuid::new_v4().to_string(),
referenced_date: NaiveDateTime::new( referenced_date: NaiveDateTime::new(
naive_date.unwrap(), naive_date,
NaiveTime::from_hms_milli_opt(0, 0, 0, 0).unwrap(), 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()); 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| { music_entry
error!("failed to insert into music_entries: {}, error: {err:?}",user.id); .map(|music_entry| {
StatusCode::INTERNAL_SERVER_ERROR actions::insert_music_entry(&music_entry, &mut conn).map_err(|err| {
})).transpose()?; 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()); 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| { location_entry
error!("failed to insert into location_entries: {}, error: {err:?}",user.id); .map(|location_entry| {
StatusCode::INTERNAL_SERVER_ERROR actions::insert_location_entry(&location_entry, &mut conn).map_err(|err| {
})).transpose()?; 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()); 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| { date_entry
error!("failed to insert into date_entries: {}, error: {err:?}",user.id); .map(|date_entry| {
StatusCode::INTERNAL_SERVER_ERROR actions::insert_date_entry(&date_entry, &mut conn).map_err(|err| {
})).transpose()?; error!(
"failed to insert into date_entries: {}, error: {err:?}",
user.id
);
StatusCode::INTERNAL_SERVER_ERROR
})
})
.transpose()?;
{ {
use crate::database::schema::entries::dsl as entries; use crate::database::schema::entries::dsl as entries;
let result = diesel::insert_into(entries::entries) diesel::insert_into(entries::entries)
.values(( .values((
entries::id.eq(Uuid::new_v4().to_string()), entries::id.eq(Uuid::new_v4().to_string()),
entries::user_id.eq(&user.id), entries::user_id.eq(&user.id),
@ -250,15 +281,14 @@ async fn insert_entry(
entries::music_entry.eq(music_entry_id), entries::music_entry.eq(music_entry_id),
entries::location_entry.eq(location_entry_id), entries::location_entry.eq(location_entry_id),
)) ))
.execute(&mut conn); .execute(&mut conn)
.map_err(|err| {
if let Err(err) = result { error!(
error!( "failed to insert into entries: {}, error: {err:?}",
"failed to insert into entries: {}, error: {:?}", user.id
user.id, err );
); StatusCode::INTERNAL_SERVER_ERROR
return Err(StatusCode::INTERNAL_SERVER_ERROR); })?;
}
} }
Ok(()) Ok(())