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.
inventory-app/src/main.rs

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");
}

Powered by TurnKey Linux.