clubmanager/src/main.rs

156 lines
4.1 KiB
Rust

mod api;
mod axum_ructe;
use axum_ructe::render;
use cm_lib::{
models::{Drink, Shift},
report::GenerateDrinkReport,
schema::shifts,
};
use axum::{
extract::{Path, State},
response::IntoResponse,
routing::get,
Router,
};
use diesel::{
result::OptionalExtension, BelongingToDsl, ExpressionMethods, QueryDsl, SelectableHelper,
};
use diesel_async::{
pooled_connection::{deadpool::Pool, AsyncDieselConnectionManager},
AsyncMysqlConnection, RunQueryDsl,
};
use dotenvy::dotenv;
use std::net::SocketAddr;
use tower_http::services::{ServeDir, ServeFile};
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
use crate::templates::*;
include!(concat!(env!("OUT_DIR"), "/templates.rs"));
fn establish_connection() -> Pool<AsyncMysqlConnection> {
dotenv().ok();
let database_url = std::env::var("DATABASE_URL").expect("You must set DATABASE_URL");
let config = AsyncDieselConnectionManager::<AsyncMysqlConnection>::new(database_url);
Pool::builder(config)
.build()
.expect("Error making connection pool")
}
#[derive(Clone)]
enum AdaUpdate {
RefreshDancers,
}
#[derive(Clone)]
pub(crate) struct AppState {
connection: Pool<AsyncMysqlConnection>,
ada_sender: tokio::sync::broadcast::Sender<AdaUpdate>,
}
impl AppState {
fn init() -> Self {
Self {
connection: establish_connection(),
ada_sender: tokio::sync::broadcast::channel(10).0,
}
}
}
#[tokio::main]
async fn main() {
// initialize tracing
tracing_subscriber::registry()
.with(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| "clubmanager=debug,tower_http=debug".into()),
)
.with(tracing_subscriber::fmt::layer())
.init();
let state = AppState::init();
let fallback_handler = ServeDir::new("dist").not_found_service(ServeFile::new("dist/404.html"));
// build our application with a route
let app = Router::new()
.nest("/api", api::router())
.route("/", get(root))
.route("/ada", get(ada))
.route("/shift_reports", get(shift_reports))
.route("/shifts/:id/drinks", get(drinks))
.route("/shifts/:id/report", get(shift_report))
.fallback_service(fallback_handler)
.with_state(state);
// run our app with hyper
// `axum::Server` is a re-export of `hyper::Server`
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
tracing::debug!("listening on http://{}/", addr);
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
}
async fn root(State(state): State<AppState>) -> impl IntoResponse {
let mut conn = state.connection.get().await.unwrap();
let open_shift: Option<Shift> = {
use cm_lib::schema::shifts::dsl::*;
shifts
.filter(end.is_null())
.select(Shift::as_select())
.first(&mut conn)
.await
.optional()
.expect("Query failed: No open shifts found")
};
tracing::debug!("{open_shift:?}");
render!(templates::home_html, open_shift)
}
async fn drinks(Path(id): Path<u32>) -> impl IntoResponse {
render!(templates::drinks_html, id)
}
async fn shift_report(State(state): State<AppState>, Path(id): Path<u32>) -> impl IntoResponse {
let mut conn = state.connection.get().await.unwrap();
let shift: Shift = shifts::table.find(id).first(&mut conn).await.unwrap();
let drinks: Vec<Drink> = Drink::belonging_to(&shift).load(&mut conn).await.unwrap();
render!(
crate::templates::shift_report_html,
drinks.generate_report()
)
}
async fn shift_reports(State(state): State<AppState>) -> impl IntoResponse {
let mut conn = state.connection.get().await.unwrap();
let mut shifts: Vec<Shift> = shifts::table
.select(Shift::as_select())
.load(&mut conn)
.await
.unwrap();
shifts.sort_by(|a, b| b.start.cmp(&a.start));
render!(shift_reports_html, shifts)
}
async fn ada() -> impl IntoResponse {
render!(ada_html)
}