#![feature(slice_group_by)] use alerts::get_alerts; use axum::{ extract::{Path, State}, response::Html, routing::get, Json, Router, }; use std::{env, net::SocketAddr}; use tokio::task::JoinSet; use tracing::Level; use types::{World, Zone}; use zones::get_zone_states; mod alert_types; mod alerts; mod misc; mod types; mod zones; #[tokio::main] async fn main() { tracing_subscriber::fmt() // .with_max_level(Level::DEBUG) .init(); let app = Router::new() .layer(tower_http::trace::TraceLayer::new_for_http()) .route("/:world", get(get_one_world)) .route("/all", get(get_all_worlds)) .route("/", get(root)) .with_state(sled::open("/tmp/metagame").expect("open")); let addr = SocketAddr::from(( [0, 0, 0, 0], env::var("PORT") .unwrap_or("8076".to_string()) .parse() .unwrap(), )); tracing::debug!("listening on http://{}", addr); axum::Server::bind(&addr) .serve(app.into_make_service()) .await .unwrap(); } async fn root() -> Html<&'static str> { Html(include_str!("./html/index.html")) } pub async fn get_one_world(State(db): State, Path(world): Path) -> Json { Json(get_world(db, world).await) } pub async fn get_all_worlds(State(db): State) -> Json> { let mut set = JoinSet::new(); let mut worlds = vec![World::default(); 8]; for world in vec![1, 10, 13, 17, 19, 40, 1000, 2000] { set.spawn(get_world(db.clone(), world)); } let mut i = 0; while let Some(response) = set.join_next().await { worlds[i] = response.unwrap_or_default(); i += 1; } Json(worlds) } pub async fn get_world(db: sled::Db, world: i32) -> World { match world_from_cache(db.clone(), world) { Ok(response) => return response, _ => {} } let alerts = get_alerts(world).await.unwrap(); let zones = get_zone_states(world).await.unwrap(); let converged_zones: Vec = zones .into_iter() .map(|zone| { let mut zone = zone; let alert = alerts.iter().find(|alert| alert.zone == zone.id); zone.alert = alert.cloned(); zone }) .collect(); let response = World { id: world, zones: converged_zones, cached_at: chrono::Utc::now(), }; world_to_cache(db, world, &response); response } fn world_from_cache(db: sled::Db, world: i32) -> Result { let key = format!("world:{}", world); let value = match db.get(key) { Ok(Some(value)) => value, _ => return Err(()), }; match bincode::deserialize::(&value) { Ok(response) => { if response.cached_at + chrono::Duration::minutes(3) < chrono::Utc::now() { return Err(()); } Ok(response) } _ => Err(()), } } fn world_to_cache(db: sled::Db, world: i32, response: &World) { let key = format!("world:{}", world); let value = bincode::serialize(response).unwrap(); db.insert(key, value).unwrap(); }