Fixed docker issue temporarily

This commit is contained in:
2025-04-11 23:02:47 -04:00
parent 05b5ceafe2
commit 74fa7da751
18 changed files with 145 additions and 92 deletions

View File

@@ -1,6 +1,7 @@
use std::collections::HashMap;
use std::str::FromStr;
use futures_util::try_join;
use reqwest::Client;
use serde::{Deserialize, Serialize};
use sqlx::{Postgres, QueryBuilder};
use crate::airports::{
@@ -194,7 +195,7 @@ impl From<AirportRow> for Airport {
}
impl Airport {
pub async fn select(icao: &str, metar: bool) -> Option<Self> {
pub async fn select(client: &Client, icao: &str, metar: bool) -> Option<Self> {
let pool = db::pool();
let airport_fut = async {
@@ -206,7 +207,7 @@ impl Airport {
let metar_fut = async {
if metar {
match Metar::find_all(&vec![icao.to_string()]).await {
match Metar::find_all(client, &vec![icao.to_string()], &false).await {
Ok(m) => Some(m.into_iter().nth(0)),
Err(err) => {
log::error!("{}", err);
@@ -269,7 +270,7 @@ impl Airport {
})
}
pub async fn select_all(query: &AirportQuery) -> ApiResult<Vec<Self>> {
pub async fn select_all(client: &Client, query: &AirportQuery) -> ApiResult<Vec<Self>> {
let pool = db::pool();
let mut builder = QueryBuilder::<Postgres>::new("SELECT * FROM ");
@@ -337,7 +338,7 @@ impl Airport {
let runway_future = Runway::select_all_map(icaos.clone());
let frequency_future = Frequency::select_all_map(icaos.clone());
let metar_future = if query.metars.unwrap_or(false) {
Some(Metar::find_all(&icaos))
Some(Metar::find_all(client, &icaos, &false))
} else {
None
};

View File

@@ -4,6 +4,7 @@ use crate::{
airports::Airport,
db::Paged,
auth::{Auth, verify_role},
AppState,
};
use actix_multipart::Multipart;
use actix_web::{delete, get, post, put, web, HttpResponse, HttpRequest, ResponseError};
@@ -53,7 +54,7 @@ async fn import_airports(mut payload: Multipart, auth: Auth) -> HttpResponse {
}
#[get("")]
async fn get_airports(req: HttpRequest) -> HttpResponse {
async fn get_airports(data: web::Data<AppState>, req: HttpRequest) -> HttpResponse {
let mut query = match web::Query::<AirportQuery>::from_query(req.query_string()) {
Ok(q) => q.into_inner(),
Err(err) => {
@@ -71,7 +72,8 @@ async fn get_airports(req: HttpRequest) -> HttpResponse {
query.limit = Some(limit);
query.page = Some(page);
match Airport::select_all(&query).await {
let client = &data.client;
match Airport::select_all(client, &query).await {
Ok(airports) => HttpResponse::Ok().json(Paged {
data: airports,
page,
@@ -86,7 +88,11 @@ async fn get_airports(req: HttpRequest) -> HttpResponse {
}
#[get("/{icao}")]
async fn get_airport(icao: web::Path<String>, req: HttpRequest) -> HttpResponse {
async fn get_airport(
data: web::Data<AppState>,
icao: web::Path<String>,
req: HttpRequest,
) -> HttpResponse {
let metar = match web::Query::<AirportQuery>::from_query(req.query_string()) {
Ok(q) => q.metars.unwrap_or_else(|| false),
Err(err) => {
@@ -95,7 +101,8 @@ async fn get_airport(icao: web::Path<String>, req: HttpRequest) -> HttpResponse
}
};
match Airport::select(&icao.into_inner(), metar).await {
let client = &data.client;
match Airport::select(client, &icao.into_inner(), metar).await {
Some(airport) => HttpResponse::Ok().json(airport),
None => HttpResponse::NotFound().finish(),
}

View File

@@ -97,7 +97,18 @@ impl From<std::env::VarError> for Error {
impl From<reqwest::Error> for Error {
fn from(error: reqwest::Error) -> Self {
Self::new(500, format!("Unknown reqwest error: {}", error))
match error.status() {
Some(status_code) => {
if status_code.is_client_error() {
Self::new(500, format!("Client reqwest error: {}", error))
} else if status_code.is_server_error() {
Self::new(500, format!("Server reqwest error: {}", error))
} else {
Self::new(500, format!("Unknown reqwest error: {:?}", error))
}
}
_ => Self::new(500, format!("Unknown reqwest error: {:?}", error)),
}
}
}

View File

@@ -1,5 +1,5 @@
use std::env;
use std::time::Duration;
use actix_cors::Cors;
use actix_web::{App, HttpServer, middleware::Logger, web};
use dotenv::from_filename;
@@ -14,15 +14,17 @@ mod metars;
mod scheduler;
mod users;
#[derive(Debug, Clone)]
struct AppState {
client: reqwest::Client,
}
#[actix_web::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
initialize_environment()?;
db::initialize().await?;
// scheduler::update_airports();
let host = "0.0.0.0".to_string();
let port = env::var("API_PORT").unwrap_or("5000".to_string());
// Initialize admin user
let admin_email = env::var("ADMIN_EMAIL");
let admin_password = env::var("ADMIN_PASSWORD");
@@ -55,6 +57,17 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
}
}
let client = reqwest::Client::builder()
.timeout(Duration::from_secs(10))
.no_proxy()
.danger_accept_invalid_certs(true)
.build()
.expect("Failed to create reqwest client");
let state = AppState { client };
let host = env::var("API_HOST").unwrap_or("localhost".to_string());
let port = env::var("API_PORT").unwrap_or("5000".to_string());
let server = match HttpServer::new(move || {
let cors = Cors::default()
.allow_any_origin()
@@ -62,18 +75,22 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.allow_any_header()
.supports_credentials()
.max_age(3600);
App::new().wrap(cors).wrap(Logger::default()).service(
web::scope("api")
.configure(airports::init_routes)
.configure(metars::init_routes)
.configure(auth::init_routes)
.configure(users::init_routes),
)
App::new()
.wrap(cors)
.wrap(Logger::default())
.app_data(web::Data::new(state.clone()))
.service(
web::scope("api")
.configure(airports::init_routes)
.configure(metars::init_routes)
.configure(auth::init_routes)
.configure(users::init_routes),
)
})
.bind(format!("{}:{}", host, port))
{
Ok(b) => {
log::info!("Binding server to {}:{}", host, port);
log::info!("Server bound to {}:{}", host, port);
b
}
Err(err) => {

View File

@@ -3,6 +3,7 @@ use crate::{error::ApiResult, db};
use chrono::{DateTime, Datelike, Utc};
use std::collections::HashSet;
use redis::{AsyncCommands, RedisResult};
use reqwest::Client;
use serde::{Deserialize, Serialize};
use crate::db::redis_async_connection;
@@ -845,8 +846,8 @@ impl Metar {
missing_metar_icaos
}
async fn get_remote_metars(icaos: &[&str]) -> ApiResult<Vec<Metar>> {
let gov_api_url = std::env::var("GOV_API_URL").expect("GOV_API_URL must be set");
async fn get_remote_metars(client: &Client, icaos: &[&str]) -> ApiResult<Vec<Metar>> {
let base_url = std::env::var("AVIATION_WEATHER_URL").expect("GOV_API_URL must be set");
// Query the remote API for the missing METAR data 10 at a time
let icao_chunks = icaos
.chunks(10)
@@ -854,14 +855,14 @@ impl Metar {
.collect::<Vec<String>>();
let mut metars: Vec<Metar> = vec![];
for icao_chunk in icao_chunks {
let url = format!("{}/metar.php?ids={}", gov_api_url, icao_chunk);
let mut m = match reqwest::get(url).await {
let url = format!("{}/metar?ids={}&order=id", base_url, icao_chunk);
let mut m = match client.get(url).send().await {
Ok(r) => {
// Check if the status code is 200
if r.status() != 200 {
return Err(Error::new(
500,
format!("Unable to get METAR request: {}", r.status()),
format!("Request returned status {}", r.status()),
));
}
match r.text().await {
@@ -876,20 +877,10 @@ impl Metar {
Err(err) => return Err(err),
}
}
Err(err) => {
return Err(Error::new(
500,
format!("Unable to parse METAR request: {}", err),
))
}
Err(err) => return Err(Error::new(500, format!("METAR parse failed: {}", err))),
}
}
Err(err) => {
return Err(Error::new(
500,
format!("Unable to get METAR request: {}", err),
))
}
Err(err) => return Err(err.into()),
};
metars.append(&mut m);
}
@@ -911,7 +902,11 @@ impl Metar {
})
}
pub async fn find_all(icao_list: &Vec<String>) -> ApiResult<Vec<Self>> {
pub async fn find_all(
client: &Client,
icao_list: &Vec<String>,
force: &bool,
) -> ApiResult<Vec<Self>> {
if icao_list.is_empty() {
return Ok(Vec::new());
}
@@ -937,17 +932,21 @@ impl Metar {
if !missing_icao_list.is_empty() {
let mut updated_missing_icao_list: Vec<&str> = Vec::new();
for icao in &missing_icao_list {
let result: RedisResult<Option<bool>> = conn.get(icao).await;
match result {
Ok(Some(value)) => {
if value {
if *force {
updated_missing_icao_list.push(icao);
} else {
let result: RedisResult<Option<bool>> = conn.get(icao).await;
match result {
Ok(Some(value)) => {
if value {
updated_missing_icao_list.push(icao);
}
}
Ok(None) => {
updated_missing_icao_list.push(icao);
}
Err(err) => return Err(err.into()),
}
Ok(None) => {
updated_missing_icao_list.push(icao);
}
Err(err) => return Err(err.into()),
}
}
if !updated_missing_icao_list.is_empty() {
@@ -955,7 +954,7 @@ impl Metar {
"Retrieving missing METAR data for {:?}",
updated_missing_icao_list
);
let mut missing_icao_list = Self::get_remote_metars(&updated_missing_icao_list)
let mut missing_icao_list = Self::get_remote_metars(client, &updated_missing_icao_list)
.await
.unwrap_or_else(|err| {
log::warn!("Unable to get remote METAR data; {}", err);

View File

@@ -2,14 +2,16 @@ use crate::metars::Metar;
use actix_web::{get, web, HttpResponse, HttpRequest};
use log::error;
use serde::{Deserialize, Serialize};
use crate::AppState;
#[derive(Debug, Serialize, Deserialize)]
struct FindAllParameters {
icaos: Option<String>,
force: Option<bool>,
}
#[get("metars")]
async fn find_all(req: HttpRequest) -> HttpResponse {
async fn find_all(data: web::Data<AppState>, req: HttpRequest) -> HttpResponse {
let parameters = web::Query::<FindAllParameters>::from_query(req.query_string()).unwrap();
let icao_option = &parameters.icaos;
let icao_string = match icao_option {
@@ -17,8 +19,10 @@ async fn find_all(req: HttpRequest) -> HttpResponse {
None => return HttpResponse::UnprocessableEntity().body("Missing icaos parameter"),
};
let icaos: Vec<String> = icao_string.split(',').map(|s| s.to_string()).collect();
let force = &parameters.force.unwrap_or(false);
let metars = match Metar::find_all(&icaos).await {
let client = &data.client;
let metars = match Metar::find_all(client, &icaos, force).await {
Ok(a) => a,
Err(err) => {
error!("{}", err);