Add logging
This commit is contained in:
parent
864ce0c13d
commit
8c6ffbfb5c
11 changed files with 78 additions and 69 deletions
3
api/.env
3
api/.env
|
@ -1,8 +1,7 @@
|
||||||
HOSTNAME=localhost
|
HOSTNAME=localhost
|
||||||
PORT=42069
|
PORT=42069
|
||||||
DOMAIN=http://api.debtpirate.app
|
|
||||||
RP_ID=debtpirate.app
|
|
||||||
TOKEN_KEY=k4.local.hWoS2ZulK9xPEATtXH1Dvj_iynzqfUv5ER5_IFTg5-Q
|
TOKEN_KEY=k4.local.hWoS2ZulK9xPEATtXH1Dvj_iynzqfUv5ER5_IFTg5-Q
|
||||||
DATABASE_URL=postgres://debt_pirate:HRURqlUmtjIy@192.168.122.251/debt_pirate
|
DATABASE_URL=postgres://debt_pirate:HRURqlUmtjIy@192.168.122.251/debt_pirate
|
||||||
ASSETS_DIR=/home/zcdziura/Documents/Projects/debt-pirate/api/assets
|
ASSETS_DIR=/home/zcdziura/Documents/Projects/debt-pirate/api/assets
|
||||||
MAINTENANCE_USER_ACCOUNT=debt_pirate:HRURqlUmtjIy
|
MAINTENANCE_USER_ACCOUNT=debt_pirate:HRURqlUmtjIy
|
||||||
|
RUST_LOG=debt_pirate=trace
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
[package]
|
[package]
|
||||||
name = "auth-test"
|
name = "debt-pirate"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
argon2 = "0.5"
|
argon2 = "0.5"
|
||||||
axum = { version = "0.7", features = [
|
axum = { version = "0.7", features = [
|
||||||
"default",
|
|
||||||
"macros",
|
"macros",
|
||||||
"multipart",
|
"multipart",
|
||||||
"ws",
|
"ws",
|
||||||
], default-features = false }
|
] }
|
||||||
base64 = "0.22"
|
base64 = "0.22"
|
||||||
dotenvy = "0.15"
|
dotenvy = "0.15"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
|
@ -39,7 +38,10 @@ sqlx = { version = "0.8", features = [
|
||||||
"postgres",
|
"postgres",
|
||||||
"runtime-tokio",
|
"runtime-tokio",
|
||||||
] }
|
] }
|
||||||
|
syslog-tracing = "0.3.1"
|
||||||
tokio = { version = "1.35", features = ["full"] }
|
tokio = { version = "1.35", features = ["full"] }
|
||||||
tower = "0.5"
|
tower = "0.5"
|
||||||
tower-http = { version = "0.6", features = ["full"] }
|
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"] }
|
uuid = { version = "1.10", features = ["serde", "v7"] }
|
||||||
|
|
|
@ -9,8 +9,9 @@ mod services;
|
||||||
|
|
||||||
use db::{create_connection_pool, run_migrations};
|
use db::{create_connection_pool, run_migrations};
|
||||||
use requests::start_app;
|
use requests::start_app;
|
||||||
use services::{start_emailer_service, UserConfirmationMessage};
|
use services::{initialize_logger, start_emailer_service, UserConfirmationMessage};
|
||||||
use tokio::runtime::Handle;
|
use tokio::runtime::Handle;
|
||||||
|
use tracing::info;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn 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;
|
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 {
|
if let Err(err) = run_migrations(&pool).await {
|
||||||
eprintln!("{err:?}");
|
eprintln!("{err:?}");
|
||||||
process::exit(2);
|
process::exit(2);
|
||||||
}
|
}
|
||||||
|
info!("Database schema migrations completed successfully.");
|
||||||
|
|
||||||
|
info!("Starting email sender service...");
|
||||||
start_emailer_service(Handle::current(), env.assets_dir(), rx);
|
start_emailer_service(Handle::current(), env.assets_dir(), rx);
|
||||||
|
info!("Email service started successfully.");
|
||||||
|
|
||||||
if let Err(err) = start_app(pool, env).await {
|
if let Err(err) = start_app(pool, env).await {
|
||||||
eprintln!("{err:?}");
|
eprintln!("{err:?}");
|
||||||
|
|
|
@ -15,11 +15,10 @@ static REQUIRED_ENV_VARS: Lazy<HashSet<&'static str>> = Lazy::new(|| {
|
||||||
[
|
[
|
||||||
"HOSTNAME",
|
"HOSTNAME",
|
||||||
"PORT",
|
"PORT",
|
||||||
"DOMAIN",
|
|
||||||
"RP_ID",
|
|
||||||
"TOKEN_KEY",
|
"TOKEN_KEY",
|
||||||
"DATABASE_URL",
|
"DATABASE_URL",
|
||||||
"ASSETS_DIR",
|
"ASSETS_DIR",
|
||||||
|
"RUST_LOG",
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect()
|
.collect()
|
||||||
|
@ -29,12 +28,11 @@ static REQUIRED_ENV_VARS: Lazy<HashSet<&'static str>> = Lazy::new(|| {
|
||||||
pub struct Environment {
|
pub struct Environment {
|
||||||
hostname: String,
|
hostname: String,
|
||||||
port: u32,
|
port: u32,
|
||||||
domain: String,
|
|
||||||
rp_id: String,
|
|
||||||
token_key: SymmetricKey<V4>,
|
token_key: SymmetricKey<V4>,
|
||||||
database_url: String,
|
database_url: String,
|
||||||
email_sender: Sender<UserConfirmationMessage>,
|
email_sender: Sender<UserConfirmationMessage>,
|
||||||
assets_dir: PathBuf,
|
assets_dir: PathBuf,
|
||||||
|
rust_log: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Environment {
|
impl Environment {
|
||||||
|
@ -47,11 +45,10 @@ impl Environment {
|
||||||
.for_each(|(key, value)| match key.as_str() {
|
.for_each(|(key, value)| match key.as_str() {
|
||||||
"HOSTNAME" => builder.with_hostname(value),
|
"HOSTNAME" => builder.with_hostname(value),
|
||||||
"PORT" => builder.with_port(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),
|
"TOKEN_KEY" => builder.with_token_key(value),
|
||||||
"DATABASE_URL" => builder.with_database_url(value),
|
"DATABASE_URL" => builder.with_database_url(value),
|
||||||
"ASSETS_DIR" => builder.with_assets_dir(value),
|
"ASSETS_DIR" => builder.with_assets_dir(value),
|
||||||
|
"RUST_LOG" => builder.with_rust_log(value),
|
||||||
_ => {}
|
_ => {}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -71,14 +68,6 @@ impl Environment {
|
||||||
self.port
|
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> {
|
pub fn token_key(&self) -> &SymmetricKey<V4> {
|
||||||
&self.token_key
|
&self.token_key
|
||||||
}
|
}
|
||||||
|
@ -94,6 +83,10 @@ impl Environment {
|
||||||
pub fn email_sender(&self) -> &Sender<UserConfirmationMessage> {
|
pub fn email_sender(&self) -> &Sender<UserConfirmationMessage> {
|
||||||
&self.email_sender
|
&self.email_sender
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn rust_log(&self) -> &str {
|
||||||
|
self.rust_log.as_str()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<EnvironmentObjectBuilder> for Environment {
|
impl From<EnvironmentObjectBuilder> for Environment {
|
||||||
|
@ -101,23 +94,21 @@ impl From<EnvironmentObjectBuilder> for Environment {
|
||||||
let EnvironmentObjectBuilder {
|
let EnvironmentObjectBuilder {
|
||||||
hostname,
|
hostname,
|
||||||
port,
|
port,
|
||||||
domain,
|
|
||||||
rp_id,
|
|
||||||
token_key,
|
token_key,
|
||||||
database_url,
|
database_url,
|
||||||
email_sender,
|
email_sender,
|
||||||
assets_dir,
|
assets_dir,
|
||||||
|
rust_log,
|
||||||
} = builder;
|
} = builder;
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
hostname: hostname.unwrap(),
|
hostname: hostname.unwrap(),
|
||||||
port: port.unwrap(),
|
port: port.unwrap(),
|
||||||
domain: domain.unwrap(),
|
|
||||||
rp_id: rp_id.unwrap(),
|
|
||||||
token_key: token_key.unwrap(),
|
token_key: token_key.unwrap(),
|
||||||
database_url: database_url.unwrap(),
|
database_url: database_url.unwrap(),
|
||||||
email_sender: email_sender.unwrap(),
|
email_sender: email_sender.unwrap(),
|
||||||
assets_dir: assets_dir.unwrap(),
|
assets_dir: assets_dir.unwrap(),
|
||||||
|
rust_log: rust_log.unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,12 +117,11 @@ impl From<EnvironmentObjectBuilder> for Environment {
|
||||||
pub struct EnvironmentObjectBuilder {
|
pub struct EnvironmentObjectBuilder {
|
||||||
pub hostname: Option<String>,
|
pub hostname: Option<String>,
|
||||||
pub port: Option<u32>,
|
pub port: Option<u32>,
|
||||||
pub domain: Option<String>,
|
|
||||||
pub rp_id: Option<String>,
|
|
||||||
pub token_key: Option<SymmetricKey<V4>>,
|
pub token_key: Option<SymmetricKey<V4>>,
|
||||||
pub database_url: Option<String>,
|
pub database_url: Option<String>,
|
||||||
pub email_sender: Option<Sender<UserConfirmationMessage>>,
|
pub email_sender: Option<Sender<UserConfirmationMessage>>,
|
||||||
pub assets_dir: Option<PathBuf>,
|
pub assets_dir: Option<PathBuf>,
|
||||||
|
pub rust_log: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EnvironmentObjectBuilder {
|
impl EnvironmentObjectBuilder {
|
||||||
|
@ -145,9 +135,8 @@ impl EnvironmentObjectBuilder {
|
||||||
pub fn uninitialized_variables(&self) -> Option<Vec<&'static str>> {
|
pub fn uninitialized_variables(&self) -> Option<Vec<&'static str>> {
|
||||||
let mut missing_vars = [
|
let mut missing_vars = [
|
||||||
("HOSTNAME", self.hostname.as_deref()),
|
("HOSTNAME", self.hostname.as_deref()),
|
||||||
("DOMAIN", self.domain.as_deref()),
|
|
||||||
("RP_ID", self.rp_id.as_deref()),
|
|
||||||
("DATABASE_URL", self.database_url.as_deref()),
|
("DATABASE_URL", self.database_url.as_deref()),
|
||||||
|
("RUST_LOG", self.rust_log.as_deref()),
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|(key, value)| value.map(|_| key).xor(Some(key)))
|
.filter_map(|(key, value)| value.map(|_| key).xor(Some(key)))
|
||||||
|
@ -181,14 +170,6 @@ impl EnvironmentObjectBuilder {
|
||||||
self.port = port;
|
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) {
|
pub fn with_token_key(&mut self, key: String) {
|
||||||
match SymmetricKey::<V4>::try_from(key.as_str()).map_err(|_| AppError::token_key()) {
|
match SymmetricKey::<V4>::try_from(key.as_str()).map_err(|_| AppError::token_key()) {
|
||||||
Ok(key) => self.token_key = Some(key),
|
Ok(key) => self.token_key = Some(key),
|
||||||
|
@ -214,4 +195,8 @@ impl EnvironmentObjectBuilder {
|
||||||
|
|
||||||
self.assets_dir = Some(assets_dir);
|
self.assets_dir = Some(assets_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn with_rust_log(&mut self, rust_log: String) {
|
||||||
|
self.rust_log = Some(rust_log);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ mod user;
|
||||||
|
|
||||||
use axum::Router;
|
use axum::Router;
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
|
use tracing::info;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::DbPool,
|
db::DbPool,
|
||||||
|
@ -31,6 +32,8 @@ impl AppState {
|
||||||
pub async fn start_app(pool: DbPool, env: Environment) -> Result<(), AppError> {
|
pub async fn start_app(pool: DbPool, env: Environment) -> Result<(), AppError> {
|
||||||
let address = env.hostname();
|
let address = env.hostname();
|
||||||
let port = env.port();
|
let port = env.port();
|
||||||
|
|
||||||
|
info!("Listening on {address}:{port}...");
|
||||||
let listener = TcpListener::bind(format!("{address}:{port}"))
|
let listener = TcpListener::bind(format!("{address}:{port}"))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.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_state = AppState::new(pool, env);
|
||||||
let app = Router::new().merge(user::requests(app_state.clone()));
|
let app = Router::new().merge(user::requests(app_state.clone()));
|
||||||
|
|
||||||
|
info!("API started successfully.");
|
||||||
axum::serve(listener, app)
|
axum::serve(listener, app)
|
||||||
.await
|
.await
|
||||||
.map_err(AppError::app_startup)?;
|
.map_err(AppError::app_startup)?;
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::{insert_new_user, NewUserEntity, UserEntity},
|
db::{insert_new_user, NewUserEntity, UserEntity},
|
||||||
models::ApiResponse,
|
models::ApiResponse,
|
||||||
requests::AppState,
|
requests::AppState,
|
||||||
services::{auth_token::generate_token, hash_string, UserConfirmationMessage},
|
services::{auth_token::generate_new_user_token, hash_string, UserConfirmationMessage},
|
||||||
};
|
};
|
||||||
use axum::{
|
use axum::{
|
||||||
extract::State,
|
extract::State,
|
||||||
|
@ -15,8 +13,6 @@ use http::StatusCode;
|
||||||
|
|
||||||
use super::models::{UserRegistrationRequest, UserRegistrationResponse};
|
use super::models::{UserRegistrationRequest, UserRegistrationResponse};
|
||||||
|
|
||||||
static FIFTEEN_MINUTES: u64 = 60 * 15;
|
|
||||||
|
|
||||||
pub async fn user_registration_post_handler(
|
pub async fn user_registration_post_handler(
|
||||||
State(app_state): State<AppState>,
|
State(app_state): State<AppState>,
|
||||||
Json(request): Json<UserRegistrationRequest>,
|
Json(request): Json<UserRegistrationRequest>,
|
||||||
|
@ -47,15 +43,13 @@ pub async fn user_registration_post_handler(
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let signing_key = app_state.env().token_key();
|
let signing_key = app_state.env().token_key();
|
||||||
let (auth_token, expiration) = generate_token(
|
let (auth_token, expiration) = generate_new_user_token(signing_key, user_id);
|
||||||
signing_key,
|
|
||||||
user_id,
|
|
||||||
Some(Duration::from_secs(FIFTEEN_MINUTES)),
|
|
||||||
Some("user-verify.debtpirate.bikeshedengineering.internal"),
|
|
||||||
);
|
|
||||||
|
|
||||||
let new_user_confirmation_message =
|
let new_user_confirmation_message = UserConfirmationMessage {
|
||||||
UserConfirmationMessage::new(email.as_str(), name.as_str(), auth_token.as_str());
|
email: email.clone(),
|
||||||
|
name: name.clone(),
|
||||||
|
auth_token: auth_token.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
let _ = app_state
|
let _ = app_state
|
||||||
.env()
|
.env()
|
||||||
|
|
|
@ -12,8 +12,9 @@ use uuid::Uuid;
|
||||||
|
|
||||||
use crate::models::AppError;
|
use crate::models::AppError;
|
||||||
|
|
||||||
static FOURTY_FIVE_DAYS: u64 = 3_888_000; // 60 * 60 * 24 * 45
|
static FOURTY_FIVE_DAYS: Duration = Duration::from_secs(3_888_000);
|
||||||
static ONE_HOUR: u64 = 3_600;
|
static ONE_HOUR: Duration = Duration::from_secs(3_600);
|
||||||
|
static FIFTEEN_MINUTES: Duration = Duration::from_secs(900);
|
||||||
|
|
||||||
pub fn verify_token(
|
pub fn verify_token(
|
||||||
key: &SymmetricKey<V4>,
|
key: &SymmetricKey<V4>,
|
||||||
|
@ -47,19 +48,23 @@ pub fn verify_token(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_access_token(key: &SymmetricKey<V4>, user_id: i32) -> (String, SystemTime) {
|
pub fn generate_access_token(key: &SymmetricKey<V4>, user_id: i32) -> (String, SystemTime) {
|
||||||
generate_token(
|
generate_token(key, user_id, Some(FOURTY_FIVE_DAYS), None)
|
||||||
key,
|
|
||||||
user_id,
|
|
||||||
Some(Duration::from_secs(FOURTY_FIVE_DAYS)),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_auth_token(key: &SymmetricKey<V4>, user_id: i32) -> (String, SystemTime) {
|
pub fn generate_auth_token(key: &SymmetricKey<V4>, user_id: i32) -> (String, SystemTime) {
|
||||||
generate_token(key, user_id, None, None)
|
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>,
|
key: &SymmetricKey<V4>,
|
||||||
user_id: i32,
|
user_id: i32,
|
||||||
duration: Option<Duration>,
|
duration: Option<Duration>,
|
||||||
|
@ -69,7 +74,7 @@ pub fn generate_token(
|
||||||
let expiration = if let Some(duration) = duration {
|
let expiration = if let Some(duration) = duration {
|
||||||
duration
|
duration
|
||||||
} else {
|
} else {
|
||||||
Duration::from_secs(ONE_HOUR)
|
ONE_HOUR
|
||||||
};
|
};
|
||||||
|
|
||||||
let token = Claims::new_expires_in(&expiration)
|
let token = Claims::new_expires_in(&expiration)
|
||||||
|
|
18
api/src/services/logger.rs
Normal file
18
api/src/services/logger.rs
Normal 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();
|
||||||
|
}
|
|
@ -3,13 +3,3 @@ pub struct UserConfirmationMessage {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub auth_token: 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(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
pub mod auth_token;
|
pub mod auth_token;
|
||||||
mod hasher;
|
mod hasher;
|
||||||
|
mod logger;
|
||||||
mod mailer;
|
mod mailer;
|
||||||
|
|
||||||
pub use hasher::*;
|
pub use hasher::*;
|
||||||
|
pub use logger::*;
|
||||||
pub use mailer::*;
|
pub use mailer::*;
|
||||||
|
|
Loading…
Add table
Reference in a new issue