Add logging

This commit is contained in:
Z. Charles Dziura 2024-09-30 00:15:19 -04:00
parent 864ce0c13d
commit 8c6ffbfb5c
11 changed files with 78 additions and 69 deletions

View file

@ -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

View file

@ -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"] }

View file

@ -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:?}");

View file

@ -15,11 +15,10 @@ static REQUIRED_ENV_VARS: Lazy<HashSet<&'static str>> = 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<HashSet<&'static str>> = Lazy::new(|| {
pub struct Environment {
hostname: String,
port: u32,
domain: String,
rp_id: String,
token_key: SymmetricKey<V4>,
database_url: String,
email_sender: Sender<UserConfirmationMessage>,
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<V4> {
&self.token_key
}
@ -94,6 +83,10 @@ impl Environment {
pub fn email_sender(&self) -> &Sender<UserConfirmationMessage> {
&self.email_sender
}
pub fn rust_log(&self) -> &str {
self.rust_log.as_str()
}
}
impl From<EnvironmentObjectBuilder> for Environment {
@ -101,23 +94,21 @@ impl From<EnvironmentObjectBuilder> 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<EnvironmentObjectBuilder> for Environment {
pub struct EnvironmentObjectBuilder {
pub hostname: Option<String>,
pub port: Option<u32>,
pub domain: Option<String>,
pub rp_id: Option<String>,
pub token_key: Option<SymmetricKey<V4>>,
pub database_url: Option<String>,
pub email_sender: Option<Sender<UserConfirmationMessage>>,
pub assets_dir: Option<PathBuf>,
pub rust_log: Option<String>,
}
impl EnvironmentObjectBuilder {
@ -145,9 +135,8 @@ impl EnvironmentObjectBuilder {
pub fn uninitialized_variables(&self) -> Option<Vec<&'static str>> {
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::<V4>::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);
}
}

View file

@ -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)?;

View file

@ -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<AppState>,
Json(request): Json<UserRegistrationRequest>,
@ -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()

View file

@ -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<V4>,
@ -47,19 +48,23 @@ pub fn verify_token(
}
pub fn generate_access_token(key: &SymmetricKey<V4>, 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<V4>, user_id: i32) -> (String, SystemTime) {
generate_token(key, user_id, None, None)
}
pub fn generate_token(
pub fn generate_new_user_token(key: &SymmetricKey<V4>, 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<V4>,
user_id: i32,
duration: Option<Duration>,
@ -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)

View file

@ -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();
}

View file

@ -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(),
}
}
}

View file

@ -1,6 +1,8 @@
pub mod auth_token;
mod hasher;
mod logger;
mod mailer;
pub use hasher::*;
pub use logger::*;
pub use mailer::*;