pub mod host { pub const PORT: i64 = 9081; pub fn get_full() -> String { let address: &str = "0.0.0.0"; format!("{}:{}", address, PORT) } } pub mod init { use std::time::Duration; use axum::routing::{delete, get, patch, post}; use tower_http::timeout::TimeoutLayer; use utoipa::OpenApi; use crate::caller::contact as contact_caller; use crate::caller::message as message_caller; use crate::caller::message::scheduling as scheduling_caller; use contact_caller::endpoint as contact_endpoints; use contact_caller::response as contact_responses; use message_caller::endpoint as message_endpoints; use message_caller::response as message_responses; use scheduling_caller::endpoint as scheduling_endpoints; use scheduling_caller::response as scheduling_responses; mod cors { pub async fn configure_cors() -> tower_http::cors::CorsLayer { let cors = tower_http::cors::CorsLayer::new() .allow_methods([ axum::http::Method::GET, axum::http::Method::POST, axum::http::Method::PUT, axum::http::Method::DELETE, ]) // Specify allowed methods:cite[2] .allow_headers([ axum::http::header::CONTENT_TYPE, axum::http::header::AUTHORIZATION, ]) // Specify allowed headers:cite[2] .allow_credentials(true) // If you need to send cookies or authentication headers:cite[2] .max_age(std::time::Duration::from_secs(3600)); // Cache the preflight response for 1 hour:cite[2] // Dynamically set the allowed origin based on the environment match std::env::var(textsender_models::envy::keys::APP_ENV).as_deref() { Ok("production") => { // In production, allow only your specific, trusted origins let allowed_origins_env = textsender_models::envy::environment::get_allowed_origins().await; match textsender_models::envy::utility::delimitize(&allowed_origins_env) { Ok(alwd) => { let allowed_origins: Vec = alwd .into_iter() .map(|a| a.parse::().unwrap()) .collect(); cors.allow_origin(allowed_origins) } Err(err) => { eprintln!("Error getting allowed origins: Error: {err:?}"); std::process::exit(-1); } } } _ => { // Development (default): Allow localhost origins cors.allow_origin(vec![ format!("http://localhost:{}", super::super::host::PORT) .parse() .unwrap(), format!("http://127.0.0.1:{}", super::super::host::PORT) .parse() .unwrap(), "http://localhost:4200".parse().unwrap(), "http://127.0.0.1:4200".parse().unwrap(), ]) } } } } #[derive(utoipa::OpenApi)] #[openapi( paths(contact_endpoints::create_contact, message_endpoints::create_message, scheduling_endpoints::schedule_message, ), components(schemas(contact_responses::AddContactResponse, message_responses::AddMessageResponse, scheduling_responses::ScheduleMessageResponse)), tags( (name = "textsender API", description = "Web API to manage texting") ) )] struct ApiDoc; pub async fn routes() -> axum::Router { axum::Router::new() .route( crate::caller::endpoints::ADD_CONTACT, post(contact_endpoints::create_contact).route_layer(axum::middleware::from_fn( crate::auth::auth::, )), ) .route( crate::caller::endpoints::GET_CONTACT, get(contact_endpoints::get_contacts).route_layer(axum::middleware::from_fn( crate::auth::auth::, )), ) .route( crate::caller::endpoints::UPDATE_CONTACT_NAME, patch(contact_endpoints::update_contact_names).route_layer( axum::middleware::from_fn(crate::auth::auth::), ), ) .route( crate::caller::endpoints::ADD_MESSAGE, post(message_endpoints::create_message).route_layer(axum::middleware::from_fn( crate::auth::auth::, )), ) .route( crate::caller::endpoints::GET_MESSAGE, get(message_endpoints::get_messages).route_layer(axum::middleware::from_fn( crate::auth::auth::, )), ) .route( crate::caller::endpoints::SCHEDULE_MESSAGE, post(scheduling_endpoints::schedule_message).route_layer( axum::middleware::from_fn(crate::auth::auth::), ), ) .route( crate::caller::endpoints::GET_SCHEDULE_MESSAGE, get(scheduling_endpoints::get_scheduled_messages).route_layer( axum::middleware::from_fn(crate::auth::auth::), ), ) .route( crate::caller::endpoints::CREATE_SCHEDULED_MESSAGE_EVENT, post(scheduling_endpoints::create_scheduled_message_event).route_layer( axum::middleware::from_fn(crate::auth::auth::), ), ) .route( crate::caller::endpoints::GET_SCHEDULED_MESSAGE_EVENT, get(scheduling_endpoints::get_scheduled_message_events).route_layer( axum::middleware::from_fn(crate::auth::auth::), ), ) .route( crate::caller::endpoints::DELETE_SCHEDULED_MESSAGE_EVENT, delete(scheduling_endpoints::delete_scheduled_message_event).route_layer( axum::middleware::from_fn(crate::auth::auth::), ), ) .route( crate::caller::endpoints::UPDATE_SCHEDULED_MESSAGE_STATUS, patch(scheduling_endpoints::update_scheduled_message_status).route_layer( axum::middleware::from_fn(crate::auth::auth::), ), ) .route( crate::caller::endpoints::FETCH_SCHEDULED_MESSAGE, get(scheduling_endpoints::fetch_scheduled_message).route_layer( axum::middleware::from_fn(crate::auth::auth::), ), ) .layer(cors::configure_cors().await) } pub async fn app() -> axum::Router { let pool = crate::db::create_pool() .await .expect("Failed to create pool"); crate::db::migrations(&pool).await; let cors = cors::configure_cors().await; routes() .await .merge( utoipa_swagger_ui::SwaggerUi::new("/swagger-ui") .url("/api-docs/openapi.json", ApiDoc::openapi()), ) .layer(axum::Extension(pool)) .layer(axum::extract::DefaultBodyLimit::max(1024 * 1024 * 1024)) .layer(TimeoutLayer::with_status_code( axum::http::StatusCode::OK, Duration::from_secs(300), )) .layer(cors) } }