init
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
mod auth;
|
||||
mod config;
|
||||
mod db;
|
||||
mod error;
|
||||
mod mail;
|
||||
mod models;
|
||||
mod routes;
|
||||
mod state;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use axum::http::{header, HeaderValue, Method};
|
||||
use axum::Router;
|
||||
use time::Duration;
|
||||
use tower_http::cors::{AllowOrigin, CorsLayer};
|
||||
use tower_http::trace::TraceLayer;
|
||||
use tower_sessions::{Expiry, SessionManagerLayer};
|
||||
use tower_sessions_sqlx_store::PostgresStore;
|
||||
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
|
||||
|
||||
use config::Config;
|
||||
use mail::Mailer;
|
||||
use state::AppState;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
tracing_subscriber::registry()
|
||||
.with(EnvFilter::try_from_default_env().unwrap_or_else(|_| "info,sqlx=warn".into()))
|
||||
.with(tracing_subscriber::fmt::layer())
|
||||
.init();
|
||||
|
||||
let config = Config::from_env()?;
|
||||
tracing::info!(port = config.port, "starting shoplist-backend");
|
||||
|
||||
let pool = db::connect(&config.database_url).await?;
|
||||
let mailer = Mailer::from_config(&config.smtp)?;
|
||||
|
||||
// Session store (separate table set, managed by the store's own migrations).
|
||||
let session_store = PostgresStore::new(pool.clone());
|
||||
session_store.migrate().await?;
|
||||
|
||||
let session_layer = SessionManagerLayer::new(session_store)
|
||||
.with_secure(false) // set true behind HTTPS in production
|
||||
.with_same_site(tower_sessions::cookie::SameSite::Lax)
|
||||
.with_expiry(Expiry::OnInactivity(Duration::days(30)));
|
||||
|
||||
let cors = build_cors(&config.cors_origins)?;
|
||||
|
||||
let state = AppState {
|
||||
pool,
|
||||
config: Arc::new(config.clone()),
|
||||
mailer,
|
||||
};
|
||||
|
||||
let api = Router::new()
|
||||
.merge(routes::router())
|
||||
.nest("/auth", auth::routes::router());
|
||||
|
||||
let app = Router::new()
|
||||
.nest("/api", api)
|
||||
.layer(cors)
|
||||
.layer(session_layer)
|
||||
.layer(TraceLayer::new_for_http())
|
||||
.with_state(state);
|
||||
|
||||
let addr = format!("{}:{}", config.host, config.port);
|
||||
let listener = tokio::net::TcpListener::bind(&addr).await?;
|
||||
tracing::info!("listening on {addr}");
|
||||
axum::serve(listener, app).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn build_cors(origins: &[String]) -> anyhow::Result<CorsLayer> {
|
||||
let parsed: Vec<HeaderValue> = origins
|
||||
.iter()
|
||||
.map(|o| o.parse::<HeaderValue>())
|
||||
.collect::<Result<_, _>>()
|
||||
.map_err(|e| anyhow::anyhow!("invalid CORS origin: {e}"))?;
|
||||
|
||||
Ok(CorsLayer::new()
|
||||
.allow_origin(AllowOrigin::list(parsed))
|
||||
.allow_credentials(true)
|
||||
.allow_methods([Method::GET, Method::POST, Method::PATCH, Method::DELETE])
|
||||
.allow_headers([header::CONTENT_TYPE]))
|
||||
}
|
||||
Reference in New Issue
Block a user