You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
143 lines
4.0 KiB
143 lines
4.0 KiB
use app::state::AppState;
|
|
use anyhow::{Context, Result};
|
|
use axum::Router;
|
|
use tokio::signal;
|
|
use tokio::task::{AbortHandle, JoinHandle};
|
|
use tower_http::{
|
|
trace::TraceLayer,
|
|
};
|
|
use tracing::info;
|
|
use tracing_subscriber::EnvFilter;
|
|
|
|
mod app_state;
|
|
mod db;
|
|
mod error;
|
|
mod session;
|
|
mod auth;
|
|
mod static_routes;
|
|
mod app;
|
|
//NOTE TO FUTURE ME: I'm leaving a bunch of notes about these things as part of the learning
|
|
// process. There is a lot of implementation details that are obscured by all these pieces, and it
|
|
// can be hard to tell how heavy a line is. Lots of comment in this file and some of the kind of
|
|
// "first breadcrumb" type files so you have a place to pick up the threads. Hopefully you remember
|
|
// this stuff, but in case you don't here's some notes
|
|
|
|
#[tokio::main]
|
|
async fn main() -> Result<()>{
|
|
|
|
// Load local environment variables from a .env file
|
|
// Use the regular std::env::var to use
|
|
let env_status = match dotenvy::from_filename(".env") {
|
|
Ok(_) => { "found local .env file" }
|
|
Err(_) => { "no local .env file" }
|
|
};
|
|
|
|
// Create a subscriber that will turn tracing events into
|
|
// console logs. Use macros (tracing::info!, tracing::error!)
|
|
// to create events in the code
|
|
// Set default environment variable to set the level
|
|
// Example "RUST_LOG=debug,tower_http=warn"
|
|
tracing_subscriber::fmt()
|
|
.with_env_filter(EnvFilter::from_default_env())
|
|
.compact()
|
|
.init();
|
|
|
|
info!("{}", env_status);
|
|
|
|
// Application database. What you would expect. Access
|
|
// through the application state
|
|
let db_file = std::env::var("DATABASE_URI")
|
|
.context("DATABASE_URI not set")?;
|
|
let db = db::init(&db_file).await?;
|
|
|
|
// OAUTH2 Client
|
|
let oauth_client = auth::init_client()?;
|
|
|
|
// Application state
|
|
let app_state = AppState { db, oauth_client };
|
|
|
|
// Session
|
|
let (session_layer, session_task) = session::init().await?;
|
|
|
|
// Long-running tasks
|
|
let mut tasks = vec![];
|
|
tasks.push(session_task);
|
|
|
|
// Assemble all the routes to the various handlers
|
|
let auth_routes = auth::routes();
|
|
let static_routes = static_routes::routes();
|
|
let error_routes = error::routes();
|
|
let app_routes = app::routes();
|
|
|
|
// Top level router
|
|
let router = Router::new()
|
|
.merge(auth_routes)
|
|
.merge(error_routes)
|
|
.merge(app_routes)
|
|
.merge(static_routes)
|
|
.layer(session_layer)
|
|
.layer(TraceLayer::new_for_http())
|
|
.fallback(error::not_found)
|
|
.with_state(app_state);
|
|
|
|
// Serve
|
|
let address = "0.0.0.0:4206";
|
|
let listener = tokio::net::TcpListener::bind(address)
|
|
.await
|
|
.context("failed to bind")?;
|
|
|
|
info!("listening on {}", address);
|
|
|
|
axum::serve(listener, router.into_make_service())
|
|
.with_graceful_shutdown(shutdown_signal(tasks))
|
|
.await.context("unable to serve")?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
|
|
/// This is needed to handle the shutdown of any long-running tasks
|
|
/// such as the one that clears expired sessions. This just
|
|
/// functions by listening for the termination signal--either
|
|
/// ctrl-c or SIGTERM--triggering the abort handle for each
|
|
/// task and then joining (awaiting) each handle
|
|
async fn shutdown_signal(tasks: Vec<JoinHandle<Result<()>>>) {
|
|
|
|
let abort_handles: Vec<AbortHandle> = tasks.iter().map(|h| h.abort_handle()).collect();
|
|
|
|
let ctrl_c = async {
|
|
signal::ctrl_c()
|
|
.await
|
|
.expect("failed to install Ctrl+C handler");
|
|
};
|
|
|
|
#[cfg(unix)]
|
|
let terminate = async {
|
|
signal::unix::signal(signal::unix::SignalKind::terminate())
|
|
.expect("failed to install signal handler")
|
|
.recv()
|
|
.await;
|
|
};
|
|
|
|
#[cfg(not(unix))]
|
|
let terminate = std::future::pending::<()>();
|
|
|
|
tokio::select! {
|
|
_ = ctrl_c => {},
|
|
_ = terminate => {},
|
|
};
|
|
|
|
info!("Shutdown signalled");
|
|
|
|
for handle in abort_handles {
|
|
handle.abort();
|
|
}
|
|
|
|
for handle in tasks {
|
|
let _ = handle.await;
|
|
}
|
|
|
|
info!("All processes finished");
|
|
}
|
|
|