#![expect(
dead_code,
reason = "pub(crate) web infrastructure used by route configuration"
)]
use axum::extract::FromRequestParts;
use axum::http::request::Parts;
use sqlx::PgPool;
use tracing::debug;
use crate::web::middleware::request_id::RequestId;
use crate::web::state::AppState;
use tasker_shared::types::{
auth::WorkerClaims,
web::{ApiError, DbOperationType},
};
#[derive(Debug)]
pub struct DatabaseConnection {
pub pool: PgPool,
pub operation_type: DbOperationType,
}
impl DatabaseConnection {
pub fn for_write(state: &AppState) -> Self {
Self {
pool: state.select_db_pool(DbOperationType::WebWrite).clone(),
operation_type: DbOperationType::WebWrite,
}
}
pub fn for_read(state: &AppState) -> Self {
Self {
pool: state.select_db_pool(DbOperationType::ReadOnly).clone(),
operation_type: DbOperationType::ReadOnly,
}
}
pub fn for_analytics(state: &AppState) -> Self {
Self {
pool: state.select_db_pool(DbOperationType::Analytics).clone(),
operation_type: DbOperationType::Analytics,
}
}
}
#[derive(Debug)]
pub struct AuthenticatedWorker {
pub claims: WorkerClaims,
}
impl<S> FromRequestParts<S> for AuthenticatedWorker
where
S: Send + Sync,
{
type Rejection = ApiError;
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
let claims = parts
.extensions
.get::<WorkerClaims>()
.ok_or(ApiError::Unauthorized)?
.clone();
debug!(worker_id = %claims.sub, "Extracted authenticated worker");
Ok(Self { claims })
}
}
#[derive(Debug)]
pub struct OptionalAuthenticatedWorker {
pub claims: Option<WorkerClaims>,
}
impl<S> FromRequestParts<S> for OptionalAuthenticatedWorker
where
S: Send + Sync,
{
type Rejection = ApiError;
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
let claims = parts.extensions.get::<WorkerClaims>().cloned();
if let Some(ref claims) = claims {
debug!(worker_id = %claims.sub, "Extracted optional authenticated worker");
}
Ok(Self { claims })
}
}
#[derive(Debug)]
pub struct RequestContext {
pub request_id: String,
}
impl<S> FromRequestParts<S> for RequestContext
where
S: Send + Sync,
{
type Rejection = ApiError;
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
let request_id = parts
.extensions
.get::<RequestId>()
.map(|rid| rid.as_str().to_string())
.unwrap_or_else(|| "unknown".to_string());
Ok(Self { request_id })
}
}