diff --git a/service/Cargo.toml b/service/Cargo.toml index 3e1db39..9c59dff 100644 --- a/service/Cargo.toml +++ b/service/Cargo.toml @@ -23,6 +23,6 @@ r2d2 = "0.8.10" reqwest = "0.11.20" serde = {version = "1.0.188", features = ["derive"]} serde_json = "1.0.105" -tokio = { version = "1.32.0", features = ["macros", "rt"] } +tokio = { version = "1.32.0", features = ["macros", "rt", "time"] } uuid = { version = "1.4.1", features = ["serde", "v4"] } log = "0.4.20" \ No newline at end of file diff --git a/service/src/airports/model.rs b/service/src/airports/model.rs index 2c48306..5a42216 100644 --- a/service/src/airports/model.rs +++ b/service/src/airports/model.rs @@ -44,13 +44,13 @@ pub struct QueryAirport { } impl QueryAirport { - pub fn get_all(bounds: &Option>, category: &Option, filter: &Option, limit: i32, page: i32) -> Result, ServiceError> { + pub fn get_all(bounds: &Option>, category: &Option, filter: &Option, order_by: bool, limit: i32, page: i32) -> Result, ServiceError> { let mut conn = db::connection()?; let mut query = airports::table .limit(limit as i64) .into_boxed(); - query = query.filter(airports::id.gt(std::cmp::max(1, page - 1) * limit)); + query = query.filter(airports::id.gt(std::cmp::max(0, page - 1) * limit)); if let Some(bounds) = bounds { query = query.filter(st_contains(bounds, airports::point)); @@ -64,7 +64,10 @@ impl QueryAirport { .or(airports::full_name.ilike(format!("%{}%", filter))) ) } - let airports: Vec = query.order((airports::id.asc(), airports::category.asc())).load::(&mut conn)?; + if order_by { + query = query.order(airports::category.asc()); + } + let airports: Vec = query.load::(&mut conn)?; Ok(airports) } diff --git a/service/src/airports/routes.rs b/service/src/airports/routes.rs index 616de6a..220cc3c 100644 --- a/service/src/airports/routes.rs +++ b/service/src/airports/routes.rs @@ -96,7 +96,7 @@ async fn get_all(req: HttpRequest) -> HttpResponse { }; let pages = ((total as f64) / (if limit <= 0 { 1 } else { limit} as f64)).ceil() as i64; - match web::block(move || QueryAirport::get_all(&polygon, &category, &filter, limit, page)).await.unwrap() { + match web::block(move || QueryAirport::get_all(&polygon, &category, &filter, true, limit, page)).await.unwrap() { Ok(a) => HttpResponse::Ok().json(AirportsResponse { data: a, meta: Metadata { page, limit, pages, total } diff --git a/service/src/main.rs b/service/src/main.rs index efa33e7..e85a638 100644 --- a/service/src/main.rs +++ b/service/src/main.rs @@ -17,50 +17,52 @@ mod error_handler; mod metars; mod users; mod schema; +mod scheduler; #[actix_rt::main] async fn main() -> std::io::Result<()> { - dotenv().ok(); - if std::env::var_os("RUST_LOG").is_none() { - std::env::set_var("RUST_LOG", "info,actix=info,diesel_migrations=warn,reqwest=warn,hyper=warn"); - } - env_logger::init_from_env(Env::default().default_filter_or("info")); - db::init(); + dotenv().ok(); + if std::env::var_os("RUST_LOG").is_none() { + std::env::set_var("RUST_LOG", "info,actix=info,diesel_migrations=warn,reqwest=warn,hyper=warn"); + } + env_logger::init_from_env(Env::default().default_filter_or("info")); + db::init(); + scheduler::update_airports(); - let mut listenfd = ListenFd::from_env(); - let mut server = HttpServer::new(|| { - let cors = Cors::default() - .allow_any_origin() - .allow_any_method() - .allow_any_header(); - App::new() - .configure(airports::init_routes) - .configure(metars::init_routes) - .configure(users::init_routes) - .wrap(cors) - .wrap(Logger::default()) - }); + let mut listenfd = ListenFd::from_env(); + let mut server = HttpServer::new(|| { + let cors = Cors::default() + .allow_any_origin() + .allow_any_method() + .allow_any_header(); + App::new() + .configure(airports::init_routes) + .configure(metars::init_routes) + .configure(users::init_routes) + .wrap(cors) + .wrap(Logger::default()) + }); - server = match listenfd.take_tcp_listener(0)? { - Some(listener) => server.listen(listener)?, - None => { - let host = match std::env::var("SERVICE_HOST") { - Ok(h) => h, - Err(_) => { - warn!("Defaulting to SERVICE_HOST localhost"); - "localhost".to_string() - } - }; - let port = match std::env::var("SERVICE_PORT") { - Ok(p) => p, - Err(_) => { - warn!("Defaulting to SERVICE_PORT 5000"); - "5000".to_string() - } - }; - debug!("Binding server to {}:{}", host, port); - server.bind(format!("{}:{}", host, port))? + server = match listenfd.take_tcp_listener(0)? { + Some(listener) => server.listen(listener)?, + None => { + let host = match std::env::var("SERVICE_HOST") { + Ok(h) => h, + Err(_) => { + warn!("Defaulting to SERVICE_HOST localhost"); + "localhost".to_string() } - }; - server.run().await -} \ No newline at end of file + }; + let port = match std::env::var("SERVICE_PORT") { + Ok(p) => p, + Err(_) => { + warn!("Defaulting to SERVICE_PORT 5000"); + "5000".to_string() + } + }; + debug!("Binding server to {}:{}", host, port); + server.bind(format!("{}:{}", host, port))? + } + }; + server.run().await +} diff --git a/service/src/scheduler.rs b/service/src/scheduler.rs new file mode 100644 index 0000000..14e75cc --- /dev/null +++ b/service/src/scheduler.rs @@ -0,0 +1,53 @@ +use tokio::time::{sleep, Duration}; +use log::{warn, debug, trace}; + +use crate::airports::QueryAirport; +use crate::metars::Metar; + +pub fn update_airports() { + tokio::spawn(async { + loop { + debug!("METAR update start"); + let total = match QueryAirport::get_count(&None, &None, &None) { + Ok(t) => t, + Err(err) => { + warn!("{}", err); + break + } + }; + let limit = 50; + let pages = ((total as f32) / (if limit <= 0 { 1 } else { limit} as f32)).ceil() as i32; + let mut airports: Vec = vec![]; + for page in 1..(pages + 1) { + match QueryAirport::get_all(&None, &None, &None, false, limit, page) { + Ok(mut a) => { + airports.append(&mut a) + }, + Err(err) => { + warn!("{}", err); + break + } + } + } + debug!("Updating {} airport METARS", airports.len()); + + let airport_icaos: Vec = airports.iter().map(|a| a.icao.to_string()).collect(); + let mut peekable = airport_icaos.into_iter().peekable(); + while peekable.peek().is_some() { + let chunk: Vec = peekable.by_ref().take(limit as usize).collect(); + let icao_string = chunk.join(","); + trace!("Updating METARS for: {}", icao_string); + match Metar::get_all(icao_string).await { + Ok(_) => { + sleep(Duration::from_millis(100)).await; + }, + Err(err) => { + warn!("{}", err); + } + } + } + debug!("METAR update complete"); + sleep(Duration::from_secs(60 * 60)).await; + } + }); +} \ No newline at end of file