2024-10-03 15:55:38 -04:00
|
|
|
mod auth;
|
2024-08-06 11:08:15 -04:00
|
|
|
mod user;
|
|
|
|
|
2024-10-02 12:41:25 -04:00
|
|
|
use std::time::Duration;
|
|
|
|
|
|
|
|
use axum::{
|
|
|
|
extract::{MatchedPath, Request},
|
|
|
|
response::Response,
|
|
|
|
Router,
|
|
|
|
};
|
2024-10-03 10:05:30 -04:00
|
|
|
use humantime::format_duration;
|
2024-08-06 11:08:15 -04:00
|
|
|
use tokio::net::TcpListener;
|
2024-10-02 12:41:25 -04:00
|
|
|
use tower_http::trace::TraceLayer;
|
|
|
|
use tracing::{error, info, info_span, warn, Span};
|
2024-10-05 08:09:46 -04:00
|
|
|
use uuid::Uuid;
|
2024-08-06 11:08:15 -04:00
|
|
|
|
2024-08-24 13:22:51 -04:00
|
|
|
use crate::{
|
|
|
|
db::DbPool,
|
|
|
|
models::{AppError, Environment},
|
2024-10-05 08:09:46 -04:00
|
|
|
services::CachePool,
|
2024-08-24 13:22:51 -04:00
|
|
|
};
|
2024-08-06 11:08:15 -04:00
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct AppState {
|
2024-10-05 08:09:46 -04:00
|
|
|
db_pool: DbPool,
|
|
|
|
cache_pool: CachePool,
|
2024-08-06 11:08:15 -04:00
|
|
|
env: Environment,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AppState {
|
2024-10-05 08:09:46 -04:00
|
|
|
pub fn new(db_pool: DbPool, cache_pool: CachePool, env: Environment) -> Self {
|
|
|
|
Self {
|
|
|
|
db_pool,
|
|
|
|
cache_pool,
|
|
|
|
env,
|
|
|
|
}
|
2024-08-06 11:08:15 -04:00
|
|
|
}
|
|
|
|
|
2024-10-05 08:09:46 -04:00
|
|
|
pub fn db_pool(&self) -> &DbPool {
|
|
|
|
&self.db_pool
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn cache_pool(&self) -> &CachePool {
|
|
|
|
&self.cache_pool
|
2024-08-06 11:08:15 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn env(&self) -> &Environment {
|
|
|
|
&self.env
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-05 08:09:46 -04:00
|
|
|
pub async fn start_app(
|
|
|
|
db_pool: DbPool,
|
|
|
|
cache_pool: CachePool,
|
|
|
|
env: Environment,
|
|
|
|
) -> Result<(), AppError> {
|
2024-08-22 17:29:24 -04:00
|
|
|
let address = env.hostname();
|
|
|
|
let port = env.port();
|
2024-09-30 00:15:19 -04:00
|
|
|
|
|
|
|
info!("Listening on {address}:{port}...");
|
2024-08-06 11:08:15 -04:00
|
|
|
let listener = TcpListener::bind(format!("{address}:{port}"))
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
2024-10-02 12:41:25 -04:00
|
|
|
let logging_layer = TraceLayer::new_for_http()
|
|
|
|
.make_span_with(|request: &Request| {
|
2024-10-03 10:05:30 -04:00
|
|
|
let path = request
|
2024-10-02 12:41:25 -04:00
|
|
|
.extensions()
|
|
|
|
.get::<MatchedPath>()
|
2024-10-03 15:27:30 -04:00
|
|
|
.map(MatchedPath::as_str).unwrap_or(request.uri().path());
|
2024-10-02 12:41:25 -04:00
|
|
|
|
2024-10-05 08:09:46 -04:00
|
|
|
info_span!("api_request", request_id = %Uuid::now_v7(), method = %request.method(), %path, status = tracing::field::Empty)
|
2024-10-02 12:41:25 -04:00
|
|
|
})
|
2024-10-03 10:05:30 -04:00
|
|
|
.on_response(|response: &Response, duration: Duration, span: &Span| {
|
2024-10-02 12:41:25 -04:00
|
|
|
let status = response.status();
|
2024-10-03 10:05:30 -04:00
|
|
|
span.record("status", status.to_string());
|
2024-10-02 12:41:25 -04:00
|
|
|
|
2024-10-03 10:05:30 -04:00
|
|
|
match status {
|
|
|
|
w if w.is_redirection() => warn!(duration = ?format_duration(duration).to_string()),
|
|
|
|
e if e.is_client_error() || e.is_server_error() => error!(duration = ?format_duration(duration).to_string()),
|
|
|
|
_ => info!(duration = ?format_duration(duration).to_string())
|
2024-10-02 12:41:25 -04:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2024-10-05 08:09:46 -04:00
|
|
|
let state = AppState::new(db_pool, cache_pool, env);
|
2024-10-02 12:41:25 -04:00
|
|
|
let app = Router::new()
|
2024-10-03 15:55:38 -04:00
|
|
|
.merge(user::requests(state.clone()))
|
|
|
|
.merge(auth::requests(state.clone()))
|
2024-10-02 12:41:25 -04:00
|
|
|
.layer(logging_layer);
|
2024-08-06 11:08:15 -04:00
|
|
|
|
2024-09-30 00:15:19 -04:00
|
|
|
info!("API started successfully.");
|
2024-08-06 11:08:15 -04:00
|
|
|
axum::serve(listener, app)
|
|
|
|
.await
|
|
|
|
.map_err(AppError::app_startup)?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|