diff --git a/api/.env b/api/.env index b86a0c4..cc9cbc9 100644 --- a/api/.env +++ b/api/.env @@ -1,8 +1,7 @@ HOSTNAME=localhost PORT=42069 -DOMAIN=http://api.debtpirate.app -RP_ID=debtpirate.app TOKEN_KEY=k4.local.hWoS2ZulK9xPEATtXH1Dvj_iynzqfUv5ER5_IFTg5-Q DATABASE_URL=postgres://debt_pirate:HRURqlUmtjIy@192.168.122.251/debt_pirate ASSETS_DIR=/home/zcdziura/Documents/Projects/debt-pirate/api/assets MAINTENANCE_USER_ACCOUNT=debt_pirate:HRURqlUmtjIy +RUST_LOG=debt_pirate=trace diff --git a/api/Cargo.toml b/api/Cargo.toml index 8165497..064021b 100644 --- a/api/Cargo.toml +++ b/api/Cargo.toml @@ -1,16 +1,15 @@ [package] -name = "auth-test" +name = "debt-pirate" version = "0.1.0" edition = "2021" [dependencies] argon2 = "0.5" axum = { version = "0.7", features = [ - "default", "macros", "multipart", "ws", -], default-features = false } +] } base64 = "0.22" dotenvy = "0.15" futures = "0.3" @@ -39,7 +38,10 @@ sqlx = { version = "0.8", features = [ "postgres", "runtime-tokio", ] } +syslog-tracing = "0.3.1" tokio = { version = "1.35", features = ["full"] } tower = "0.5" tower-http = { version = "0.6", features = ["full"] } +tracing = "0.1.40" +tracing-subscriber = { version = "0.3.18", features = ["env-filter", "time"] } uuid = { version = "1.10", features = ["serde", "v7"] } diff --git a/api/src/main.rs b/api/src/main.rs index 9663879..8e483bd 100644 --- a/api/src/main.rs +++ b/api/src/main.rs @@ -9,8 +9,9 @@ mod services; use db::{create_connection_pool, run_migrations}; use requests::start_app; -use services::{start_emailer_service, UserConfirmationMessage}; +use services::{initialize_logger, start_emailer_service, UserConfirmationMessage}; use tokio::runtime::Handle; +use tracing::info; #[tokio::main] async fn main() { @@ -24,13 +25,22 @@ async fn main() { } }; + initialize_logger(&env); + + info!("Initializing database connection pool..."); let pool = create_connection_pool(env.db_connection_uri()).await; + info!("Database connection pool created successfully."); + + info!("Running database schema migrations..."); if let Err(err) = run_migrations(&pool).await { eprintln!("{err:?}"); process::exit(2); } + info!("Database schema migrations completed successfully."); + info!("Starting email sender service..."); start_emailer_service(Handle::current(), env.assets_dir(), rx); + info!("Email service started successfully."); if let Err(err) = start_app(pool, env).await { eprintln!("{err:?}"); diff --git a/api/src/models/environment.rs b/api/src/models/environment.rs index cc4c7fa..8a60ba8 100644 --- a/api/src/models/environment.rs +++ b/api/src/models/environment.rs @@ -15,11 +15,10 @@ static REQUIRED_ENV_VARS: Lazy> = Lazy::new(|| { [ "HOSTNAME", "PORT", - "DOMAIN", - "RP_ID", "TOKEN_KEY", "DATABASE_URL", "ASSETS_DIR", + "RUST_LOG", ] .into_iter() .collect() @@ -29,12 +28,11 @@ static REQUIRED_ENV_VARS: Lazy> = Lazy::new(|| { pub struct Environment { hostname: String, port: u32, - domain: String, - rp_id: String, token_key: SymmetricKey, database_url: String, email_sender: Sender, assets_dir: PathBuf, + rust_log: String, } impl Environment { @@ -47,11 +45,10 @@ impl Environment { .for_each(|(key, value)| match key.as_str() { "HOSTNAME" => builder.with_hostname(value), "PORT" => builder.with_port(value), - "DOMAIN" => builder.with_domain(value), - "RP_ID" => builder.with_rp_id(value), "TOKEN_KEY" => builder.with_token_key(value), "DATABASE_URL" => builder.with_database_url(value), "ASSETS_DIR" => builder.with_assets_dir(value), + "RUST_LOG" => builder.with_rust_log(value), _ => {} }); @@ -71,14 +68,6 @@ impl Environment { self.port } - pub fn domain(&self) -> &str { - self.domain.as_str() - } - - pub fn rp_id(&self) -> &str { - self.rp_id.as_str() - } - pub fn token_key(&self) -> &SymmetricKey { &self.token_key } @@ -94,6 +83,10 @@ impl Environment { pub fn email_sender(&self) -> &Sender { &self.email_sender } + + pub fn rust_log(&self) -> &str { + self.rust_log.as_str() + } } impl From for Environment { @@ -101,23 +94,21 @@ impl From for Environment { let EnvironmentObjectBuilder { hostname, port, - domain, - rp_id, token_key, database_url, email_sender, assets_dir, + rust_log, } = builder; Self { hostname: hostname.unwrap(), port: port.unwrap(), - domain: domain.unwrap(), - rp_id: rp_id.unwrap(), token_key: token_key.unwrap(), database_url: database_url.unwrap(), email_sender: email_sender.unwrap(), assets_dir: assets_dir.unwrap(), + rust_log: rust_log.unwrap(), } } } @@ -126,12 +117,11 @@ impl From for Environment { pub struct EnvironmentObjectBuilder { pub hostname: Option, pub port: Option, - pub domain: Option, - pub rp_id: Option, pub token_key: Option>, pub database_url: Option, pub email_sender: Option>, pub assets_dir: Option, + pub rust_log: Option, } impl EnvironmentObjectBuilder { @@ -145,9 +135,8 @@ impl EnvironmentObjectBuilder { pub fn uninitialized_variables(&self) -> Option> { let mut missing_vars = [ ("HOSTNAME", self.hostname.as_deref()), - ("DOMAIN", self.domain.as_deref()), - ("RP_ID", self.rp_id.as_deref()), ("DATABASE_URL", self.database_url.as_deref()), + ("RUST_LOG", self.rust_log.as_deref()), ] .into_iter() .filter_map(|(key, value)| value.map(|_| key).xor(Some(key))) @@ -181,14 +170,6 @@ impl EnvironmentObjectBuilder { self.port = port; } - pub fn with_domain(&mut self, domain: String) { - self.domain = Some(domain); - } - - pub fn with_rp_id(&mut self, rp_id: String) { - self.rp_id = Some(rp_id); - } - pub fn with_token_key(&mut self, key: String) { match SymmetricKey::::try_from(key.as_str()).map_err(|_| AppError::token_key()) { Ok(key) => self.token_key = Some(key), @@ -214,4 +195,8 @@ impl EnvironmentObjectBuilder { self.assets_dir = Some(assets_dir); } + + pub fn with_rust_log(&mut self, rust_log: String) { + self.rust_log = Some(rust_log); + } } diff --git a/api/src/requests/mod.rs b/api/src/requests/mod.rs index 3c8b9db..49df903 100644 --- a/api/src/requests/mod.rs +++ b/api/src/requests/mod.rs @@ -2,6 +2,7 @@ mod user; use axum::Router; use tokio::net::TcpListener; +use tracing::info; use crate::{ db::DbPool, @@ -31,6 +32,8 @@ impl AppState { pub async fn start_app(pool: DbPool, env: Environment) -> Result<(), AppError> { let address = env.hostname(); let port = env.port(); + + info!("Listening on {address}:{port}..."); let listener = TcpListener::bind(format!("{address}:{port}")) .await .unwrap(); @@ -38,6 +41,7 @@ pub async fn start_app(pool: DbPool, env: Environment) -> Result<(), AppError> { let app_state = AppState::new(pool, env); let app = Router::new().merge(user::requests(app_state.clone())); + info!("API started successfully."); axum::serve(listener, app) .await .map_err(AppError::app_startup)?; diff --git a/api/src/requests/user/create/handler.rs b/api/src/requests/user/create/handler.rs index 562155b..22c051f 100644 --- a/api/src/requests/user/create/handler.rs +++ b/api/src/requests/user/create/handler.rs @@ -1,10 +1,8 @@ -use std::time::Duration; - use crate::{ db::{insert_new_user, NewUserEntity, UserEntity}, models::ApiResponse, requests::AppState, - services::{auth_token::generate_token, hash_string, UserConfirmationMessage}, + services::{auth_token::generate_new_user_token, hash_string, UserConfirmationMessage}, }; use axum::{ extract::State, @@ -15,8 +13,6 @@ use http::StatusCode; use super::models::{UserRegistrationRequest, UserRegistrationResponse}; -static FIFTEEN_MINUTES: u64 = 60 * 15; - pub async fn user_registration_post_handler( State(app_state): State, Json(request): Json, @@ -47,15 +43,13 @@ pub async fn user_registration_post_handler( })?; let signing_key = app_state.env().token_key(); - let (auth_token, expiration) = generate_token( - signing_key, - user_id, - Some(Duration::from_secs(FIFTEEN_MINUTES)), - Some("user-verify.debtpirate.bikeshedengineering.internal"), - ); + let (auth_token, expiration) = generate_new_user_token(signing_key, user_id); - let new_user_confirmation_message = - UserConfirmationMessage::new(email.as_str(), name.as_str(), auth_token.as_str()); + let new_user_confirmation_message = UserConfirmationMessage { + email: email.clone(), + name: name.clone(), + auth_token: auth_token.clone(), + }; let _ = app_state .env() diff --git a/api/src/requests/user/verify/request.rs b/api/src/requests/user/verify/handler.rs similarity index 100% rename from api/src/requests/user/verify/request.rs rename to api/src/requests/user/verify/handler.rs diff --git a/api/src/services/auth_token.rs b/api/src/services/auth_token.rs index 8ed35f7..376479d 100644 --- a/api/src/services/auth_token.rs +++ b/api/src/services/auth_token.rs @@ -12,8 +12,9 @@ use uuid::Uuid; use crate::models::AppError; -static FOURTY_FIVE_DAYS: u64 = 3_888_000; // 60 * 60 * 24 * 45 -static ONE_HOUR: u64 = 3_600; +static FOURTY_FIVE_DAYS: Duration = Duration::from_secs(3_888_000); +static ONE_HOUR: Duration = Duration::from_secs(3_600); +static FIFTEEN_MINUTES: Duration = Duration::from_secs(900); pub fn verify_token( key: &SymmetricKey, @@ -47,19 +48,23 @@ pub fn verify_token( } pub fn generate_access_token(key: &SymmetricKey, user_id: i32) -> (String, SystemTime) { - generate_token( - key, - user_id, - Some(Duration::from_secs(FOURTY_FIVE_DAYS)), - None, - ) + generate_token(key, user_id, Some(FOURTY_FIVE_DAYS), None) } pub fn generate_auth_token(key: &SymmetricKey, user_id: i32) -> (String, SystemTime) { generate_token(key, user_id, None, None) } -pub fn generate_token( +pub fn generate_new_user_token(key: &SymmetricKey, user_id: i32) -> (String, SystemTime) { + generate_token( + key, + user_id, + Some(FIFTEEN_MINUTES), + Some(format!("api.debtpirate.bikeshedengineering.internal/user/{user_id}/verify").as_str()), + ) +} + +fn generate_token( key: &SymmetricKey, user_id: i32, duration: Option, @@ -69,7 +74,7 @@ pub fn generate_token( let expiration = if let Some(duration) = duration { duration } else { - Duration::from_secs(ONE_HOUR) + ONE_HOUR }; let token = Claims::new_expires_in(&expiration) diff --git a/api/src/services/logger.rs b/api/src/services/logger.rs new file mode 100644 index 0000000..6a6cb7e --- /dev/null +++ b/api/src/services/logger.rs @@ -0,0 +1,18 @@ +use tracing::level_filters::LevelFilter; +use tracing_subscriber::{fmt::time::UtcTime, EnvFilter}; + +use crate::models::Environment; + +pub fn initialize_logger(env: &Environment) { + let log_level = env.rust_log(); + + tracing_subscriber::fmt() + .with_env_filter( + EnvFilter::builder() + .with_default_directive(LevelFilter::INFO.into()) + .parse_lossy(log_level), + ) + .with_timer(UtcTime::rfc_3339()) + .with_target(false) + .init(); +} diff --git a/api/src/services/mailer/user_confirmation_message.rs b/api/src/services/mailer/user_confirmation_message.rs index 91283de..d99a567 100644 --- a/api/src/services/mailer/user_confirmation_message.rs +++ b/api/src/services/mailer/user_confirmation_message.rs @@ -3,13 +3,3 @@ pub struct UserConfirmationMessage { pub name: String, pub auth_token: String, } - -impl UserConfirmationMessage { - pub fn new(email: &str, name: &str, auth_token: &str) -> Self { - Self { - email: email.to_owned(), - name: name.to_owned(), - auth_token: auth_token.to_owned(), - } - } -} diff --git a/api/src/services/mod.rs b/api/src/services/mod.rs index 43f5d3b..90666d3 100644 --- a/api/src/services/mod.rs +++ b/api/src/services/mod.rs @@ -1,6 +1,8 @@ pub mod auth_token; mod hasher; +mod logger; mod mailer; pub use hasher::*; +pub use logger::*; pub use mailer::*;