feat: add tracing throughout, HTTP TraceLayer, detailed error logs
This commit is contained in:
@@ -5,5 +5,6 @@ edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
uuid = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
domain = { workspace = true }
|
||||
|
||||
@@ -21,13 +21,17 @@ pub async fn execute(deps: &LoginDeps, cmd: LoginCommand) -> Result<LoginResult,
|
||||
.user_repo
|
||||
.find_by_email(&email)
|
||||
.await?
|
||||
.ok_or_else(|| DomainError::Unauthorized("invalid credentials".into()))?;
|
||||
.ok_or_else(|| {
|
||||
tracing::warn!(email = cmd.email, "login attempt with unknown email");
|
||||
DomainError::Unauthorized("invalid credentials".into())
|
||||
})?;
|
||||
|
||||
let valid = deps
|
||||
.password_hasher
|
||||
.verify(&cmd.password, user.password_hash())
|
||||
.await?;
|
||||
if !valid {
|
||||
tracing::warn!(email = cmd.email, "login attempt with wrong password");
|
||||
return Err(DomainError::Unauthorized("invalid credentials".into()));
|
||||
}
|
||||
|
||||
@@ -44,6 +48,8 @@ pub async fn execute(deps: &LoginDeps, cmd: LoginCommand) -> Result<LoginResult,
|
||||
};
|
||||
deps.refresh_repo.create(&session).await?;
|
||||
|
||||
tracing::info!(user_id = %user.id().value(), "user logged in");
|
||||
|
||||
Ok(LoginResult {
|
||||
access_token: generated.token,
|
||||
refresh_token,
|
||||
|
||||
@@ -4,5 +4,6 @@ use super::commands::LogoutCommand;
|
||||
use super::deps::LogoutDeps;
|
||||
|
||||
pub async fn execute(deps: &LogoutDeps, cmd: LogoutCommand) -> Result<(), DomainError> {
|
||||
tracing::debug!("user logged out");
|
||||
deps.refresh_repo.revoke(&cmd.refresh_token).await
|
||||
}
|
||||
|
||||
@@ -42,6 +42,8 @@ pub async fn execute(
|
||||
};
|
||||
deps.refresh_repo.create(&new_session).await?;
|
||||
|
||||
tracing::debug!(user_id = %session.user_id.value(), "token refreshed");
|
||||
|
||||
Ok(RefreshResult {
|
||||
access_token: generated.token,
|
||||
refresh_token: new_refresh_token,
|
||||
|
||||
@@ -7,7 +7,10 @@ use super::deps::RegisterDeps;
|
||||
|
||||
pub async fn execute(deps: &RegisterDeps, cmd: RegisterCommand) -> Result<(), DomainError> {
|
||||
if !deps.allow_registration {
|
||||
return Err(DomainError::Unauthorized("registration is disabled".into()));
|
||||
tracing::warn!("registration attempt while disabled");
|
||||
return Err(DomainError::Unauthorized(
|
||||
"registration is disabled".into(),
|
||||
));
|
||||
}
|
||||
|
||||
let password = Password::new(&cmd.password)?;
|
||||
@@ -28,6 +31,9 @@ pub async fn execute(deps: &RegisterDeps, cmd: RegisterCommand) -> Result<(), Do
|
||||
|
||||
let hash = deps.password_hasher.hash(password.value()).await?;
|
||||
let user = User::new(email, username, hash);
|
||||
|
||||
tracing::info!(user_id = %user.id().value(), "new user registered");
|
||||
|
||||
deps.user_repo.save(&user).await?;
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -4,5 +4,7 @@ use super::commands::DeleteSongCommand;
|
||||
use super::deps::SongCommandDeps;
|
||||
|
||||
pub async fn execute(deps: &SongCommandDeps, cmd: DeleteSongCommand) -> Result<(), DomainError> {
|
||||
deps.repo.delete(cmd.id).await
|
||||
deps.repo.delete(cmd.id).await?;
|
||||
tracing::debug!(id = %cmd.id, "song deleted");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -8,5 +8,8 @@ pub async fn execute(
|
||||
deps: &SongCommandDeps,
|
||||
cmd: SaveSongCommand,
|
||||
) -> Result<StoredSong, DomainError> {
|
||||
deps.repo.save(&cmd.song).await
|
||||
let title = cmd.song.meta.title.clone();
|
||||
let result = deps.repo.save(&cmd.song).await?;
|
||||
tracing::debug!(id = %result.id, title, "song saved");
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
@@ -8,12 +8,15 @@ pub async fn execute(
|
||||
deps: &SongCommandDeps,
|
||||
cmd: UpdateSongMetaCommand,
|
||||
) -> Result<SongSummary, DomainError> {
|
||||
deps.repo
|
||||
let result = deps
|
||||
.repo
|
||||
.update_meta(
|
||||
cmd.id,
|
||||
cmd.title.as_deref(),
|
||||
cmd.artist.as_deref(),
|
||||
cmd.original_key.as_deref(),
|
||||
)
|
||||
.await
|
||||
.await?;
|
||||
tracing::debug!(id = %cmd.id, "song meta updated");
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
@@ -9,12 +9,14 @@ use super::deps::ParseTabDeps;
|
||||
|
||||
pub async fn execute(deps: &ParseTabDeps, cmd: ParseTabCommand) -> Result<Song, DomainError> {
|
||||
let html = if let Some(raw_html) = cmd.html {
|
||||
tracing::debug!("parsing from raw HTML input");
|
||||
Ok(raw_html)
|
||||
} else if let Some(source) = cmd.source {
|
||||
let tab_source = if source.starts_with("file://") {
|
||||
let path = source.trim_start_matches("file://");
|
||||
TabSource::File(PathBuf::from(path))
|
||||
} else {
|
||||
tracing::debug!(url = source, "fetching tab from URL");
|
||||
TabSource::Url(source)
|
||||
};
|
||||
deps.fetcher
|
||||
@@ -27,7 +29,17 @@ pub async fn execute(deps: &ParseTabDeps, cmd: ParseTabCommand) -> Result<Song,
|
||||
))
|
||||
}?;
|
||||
|
||||
deps.parser
|
||||
let song = deps
|
||||
.parser
|
||||
.parse(&html)
|
||||
.map_err(|e| DomainError::InfrastructureError(e.to_string()))
|
||||
.map_err(|e| DomainError::InfrastructureError(e.to_string()))?;
|
||||
|
||||
tracing::debug!(
|
||||
title = song.meta.title,
|
||||
artist = song.meta.artist,
|
||||
sections = song.sections.len(),
|
||||
"tab parsed"
|
||||
);
|
||||
|
||||
Ok(song)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user