This commit is contained in:
2024-09-03 11:40:09 -04:00
parent 32a0ecc1e6
commit cc74be72be
17 changed files with 47 additions and 62 deletions

View File

@@ -1,6 +1,6 @@
RUST_LOG=warn,service=debug RUST_LOG=warn,service=debug
DATABASE_CONTAINER=aviation-service DATABASE_CONTAINER=aviation-db
DATABASE_USER=aviation DATABASE_USER=aviation
DATABASE_PASSWORD= DATABASE_PASSWORD=
@@ -21,7 +21,7 @@ SERVICE_HOST=localhost
SERVICE_PORT=5000 SERVICE_PORT=5000
KEYS_DIR_PATH= KEYS_DIR_PATH=
ACCESS_TOKEN_MAXAGE=5 ACCESS_TOKEN_MAXAGE=240
REFRESH_TOKEN_MAXAGE=1440 REFRESH_TOKEN_MAXAGE=1440
GOV_API_URL=https://aviationweather.gov/cgi-bin/data GOV_API_URL=https://aviationweather.gov/cgi-bin/data

View File

@@ -59,4 +59,6 @@ generate-keys: ## Generate RSA keys
@openssl rsa -in ../keys/access_private_key.pem -pubout -outform PEM -out ../keys/access_public_key.pem @openssl rsa -in ../keys/access_private_key.pem -pubout -outform PEM -out ../keys/access_public_key.pem
@openssl genrsa -out ../keys/refresh_private_key.pem 4096 @openssl genrsa -out ../keys/refresh_private_key.pem 4096
@openssl rsa -in ../keys/refresh_private_key.pem -pubout -outform PEM -out ../keys/refresh_public_key.pem @openssl rsa -in ../keys/refresh_private_key.pem -pubout -outform PEM -out ../keys/refresh_public_key.pem
psql: ## Connect to the PSQL DB
@docker exec -it ${DATABASE_CONTAINER} psql -U ${DATABASE_USER}

View File

@@ -0,0 +1 @@
DROP TABLE airport_metar_cache;

View File

@@ -0,0 +1,5 @@
CREATE TABLE IF NOT EXISTS airport_metar_cache (
icao TEXT PRIMARY KEY NOT NULL,
has_metar BOOLEAN NOT NULL DEFAULT FALSE,
last_checked TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);

View File

@@ -45,10 +45,16 @@ pub struct Airport {
pub has_beacon: Option<bool>, pub has_beacon: Option<bool>,
pub runways: Vec<Runway>, pub runways: Vec<Runway>,
pub frequencies: Vec<Frequency>, pub frequencies: Vec<Frequency>,
pub has_metar: bool,
pub public: bool, pub public: bool,
} }
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct AirportMetarCache {
pub icao: String,
pub has_metar: bool,
pub last_checked: chrono::NaiveDateTime,
}
impl Into<QueryAirport> for Airport { impl Into<QueryAirport> for Airport {
fn into(self) -> QueryAirport { fn into(self) -> QueryAirport {
return QueryAirport { return QueryAirport {

View File

@@ -13,7 +13,7 @@ use postgis_diesel::types::{Polygon, Point};
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
struct GetAllParameters { struct AirportsQuery {
icaos: Option<String>, icaos: Option<String>,
name: Option<String>, name: Option<String>,
bounds: Option<String>, bounds: Option<String>,
@@ -26,7 +26,7 @@ struct GetAllParameters {
} }
#[post("/import")] #[post("/import")]
async fn import(mut payload: Multipart, auth: JwtAuth) -> HttpResponse { async fn import_airports(mut payload: Multipart, auth: JwtAuth) -> HttpResponse {
if let Err(err) = verify_role(&auth, "admin") { if let Err(err) = verify_role(&auth, "admin") {
return ResponseError::error_response(&err); return ResponseError::error_response(&err);
}; };
@@ -70,8 +70,8 @@ async fn import(mut payload: Multipart, auth: JwtAuth) -> HttpResponse {
} }
#[get("")] #[get("")]
async fn get_all(req: HttpRequest) -> HttpResponse { async fn get_airports(req: HttpRequest) -> HttpResponse {
let params = web::Query::<GetAllParameters>::from_query(req.query_string()).unwrap(); let params = web::Query::<AirportsQuery>::from_query(req.query_string()).unwrap();
let mut filters = QueryFilters::default(); let mut filters = QueryFilters::default();
filters.icaos = match &params.icaos { filters.icaos = match &params.icaos {
Some(i) => Some(i.split(",").map(|s| s.to_string()).collect()), Some(i) => Some(i.split(",").map(|s| s.to_string()).collect()),
@@ -180,7 +180,6 @@ async fn get_all(req: HttpRequest) -> HttpResponse {
Ok(t) => t, Ok(t) => t,
Err(_) => 0, Err(_) => 0,
}; };
let pages = ((total as f64) / (if limit <= 0 { 1 } else { limit } as f64)).ceil() as i64;
match web::block(move || QueryAirport::get_all(&filters, limit, page)) match web::block(move || QueryAirport::get_all(&filters, limit, page))
.await .await
@@ -197,7 +196,6 @@ async fn get_all(req: HttpRequest) -> HttpResponse {
meta: Some(Metadata { meta: Some(Metadata {
page, page,
limit, limit,
pages,
total, total,
}), }),
}) })
@@ -210,19 +208,11 @@ async fn get_all(req: HttpRequest) -> HttpResponse {
} }
#[get("/{icao}")] #[get("/{icao}")]
async fn get(icao: web::Path<String>) -> HttpResponse { async fn get_airport(icao: web::Path<String>) -> HttpResponse {
match QueryAirport::get(&icao.into_inner()) { match QueryAirport::get(&icao.into_inner()) {
Ok(a) => { Ok(a) => {
let airport: Airport = a.into(); let airport: Airport = a.into();
HttpResponse::Ok().json(Response { HttpResponse::Ok().json(airport)
data: airport,
meta: Some(Metadata {
page: 1,
limit: 1,
pages: 1,
total: 1,
}),
})
} }
Err(err) => { Err(err) => {
error!("{}", err); error!("{}", err);
@@ -232,7 +222,7 @@ async fn get(icao: web::Path<String>) -> HttpResponse {
} }
#[post("")] #[post("")]
async fn create(airport: web::Json<Airport>, auth: JwtAuth) -> HttpResponse { async fn create_airport(airport: web::Json<Airport>, auth: JwtAuth) -> HttpResponse {
let _ = match verify_role(&auth, "admin") { let _ = match verify_role(&auth, "admin") {
Ok(_) => {} Ok(_) => {}
Err(err) => return ResponseError::error_response(&err), Err(err) => return ResponseError::error_response(&err),
@@ -251,7 +241,7 @@ async fn create(airport: web::Json<Airport>, auth: JwtAuth) -> HttpResponse {
} }
#[put("/{icao}")] #[put("/{icao}")]
async fn update( async fn update_airport(
_icao: web::Path<String>, _icao: web::Path<String>,
airport: web::Json<Airport>, airport: web::Json<Airport>,
auth: JwtAuth, auth: JwtAuth,
@@ -274,7 +264,7 @@ async fn update(
} }
#[delete("")] #[delete("")]
async fn delete_all(auth: JwtAuth) -> HttpResponse { async fn delete_airports(auth: JwtAuth) -> HttpResponse {
let _ = match verify_role(&auth, "admin") { let _ = match verify_role(&auth, "admin") {
Ok(_) => {} Ok(_) => {}
Err(err) => return ResponseError::error_response(&err), Err(err) => return ResponseError::error_response(&err),
@@ -289,7 +279,7 @@ async fn delete_all(auth: JwtAuth) -> HttpResponse {
} }
#[delete("/{icao}")] #[delete("/{icao}")]
async fn delete(icao: web::Path<String>, auth: JwtAuth) -> HttpResponse { async fn delete_airport(icao: web::Path<String>, auth: JwtAuth) -> HttpResponse {
let _ = match verify_role(&auth, "admin") { let _ = match verify_role(&auth, "admin") {
Ok(_) => {} Ok(_) => {}
Err(err) => return ResponseError::error_response(&err), Err(err) => return ResponseError::error_response(&err),
@@ -306,12 +296,12 @@ async fn delete(icao: web::Path<String>, auth: JwtAuth) -> HttpResponse {
pub fn init_routes(config: &mut web::ServiceConfig) { pub fn init_routes(config: &mut web::ServiceConfig) {
config.service( config.service(
web::scope("airports") web::scope("airports")
.service(get_all) .service(import_airports)
.service(get) .service(get_airports)
.service(create) .service(get_airport)
.service(update) .service(create_airport)
.service(delete) .service(update_airport)
.service(delete_all) .service(delete_airports)
.service(import), .service(delete_airport),
); );
} }

View File

@@ -158,7 +158,6 @@ pub struct Response<T> {
pub struct Metadata { pub struct Metadata {
pub page: i32, pub page: i32,
pub limit: i32, pub limit: i32,
pub pages: i64,
pub total: i64, pub total: i64,
} }

View File

@@ -22,7 +22,7 @@ async fn main() -> std::io::Result<()> {
dotenv().ok(); dotenv().ok();
env_logger::init_from_env(env_logger::Env::default().filter_or("RUST_LOG", "warn,service=info")); env_logger::init_from_env(env_logger::Env::default().filter_or("RUST_LOG", "warn,service=info"));
db::init().await; db::init().await;
scheduler::update_airports(); // scheduler::update_airports();
let host = env::var("SERVICE_HOST").unwrap_or("localhost".to_string()); let host = env::var("SERVICE_HOST").unwrap_or("localhost".to_string());
let port = env::var("SERVICE_PORT").unwrap_or("5000".to_string()); let port = env::var("SERVICE_PORT").unwrap_or("5000".to_string());

View File

@@ -24,7 +24,7 @@ async fn get_all(req: HttpRequest) -> HttpResponse {
None => return HttpResponse::UnprocessableEntity().body("Missing icaos parameter"), None => return HttpResponse::UnprocessableEntity().body("Missing icaos parameter"),
}; };
let airports = let metars =
match web::block(|| Ok::<_, ServiceError>(async { Metar::get_all(icao_string).await })) match web::block(|| Ok::<_, ServiceError>(async { Metar::get_all(icao_string).await }))
.await .await
.unwrap() .unwrap()
@@ -37,15 +37,7 @@ async fn get_all(req: HttpRequest) -> HttpResponse {
return err.to_http_response(); return err.to_http_response();
} }
}; };
HttpResponse::Ok().json(MetarsResponse { HttpResponse::Ok().json(metars)
data: airports,
meta: Metadata {
page: 0,
limit: 0,
pages: 0,
total: 0,
},
})
} }
pub fn init_routes(config: &mut web::ServiceConfig) { pub fn init_routes(config: &mut web::ServiceConfig) {

View File

@@ -1,13 +1,13 @@
import { Airport, AirportOrderField, Bounds, GetAirportResponse, GetAirportsResponse } from './airport.types'; import { Airport, AirportOrderField, Bounds, GetAirportsResponse } from './airport.types';
import { getRequest, deleteRequest, postRequest, putRequest } from '.'; import { getRequest, deleteRequest, postRequest, putRequest } from '.';
interface GetAirportProps { interface GetAirportProps {
icao: string; icao: string;
} }
export async function getAirport({ icao }: GetAirportProps): Promise<GetAirportResponse> { export async function getAirport({ icao }: GetAirportProps): Promise<Airport> {
const response = await getRequest(`airports/${icao}`); const response = await getRequest(`airports/${icao}`);
return response?.json() || { data: undefined }; return response?.json() || {};
} }
interface GetAirportsProps { interface GetAirportsProps {

View File

@@ -86,11 +86,6 @@ export interface Frequency {
frequency_mhz: number; frequency_mhz: number;
} }
export interface GetAirportResponse {
data: Airport;
meta: Metadata;
}
export interface GetAirportsResponse { export interface GetAirportsResponse {
data: Airport[]; data: Airport[];
meta: Metadata; meta: Metadata;

View File

@@ -75,6 +75,5 @@ export async function deleteRequest(endpoint: string): Promise<Response> {
export interface Metadata { export interface Metadata {
limit: number; limit: number;
page: number; page: number;
pages: number;
total: number; total: number;
} }

View File

@@ -1,15 +1,11 @@
import { Metar } from './metar.types'; import { Metar } from './metar.types';
import { getRequest } from '.'; import { getRequest } from '.';
interface GetMetarsResponse { export async function getMetars(icaos: string[]): Promise<Metar[]> {
data: Metar[];
}
export async function getMetars(icaos: string[]): Promise<GetMetarsResponse> {
if (icaos.length == 0) { if (icaos.length == 0) {
return { data: [] }; return [];
} }
const stationICAOs: string = icaos.map((icao) => icao).join(','); const stationICAOs: string = icaos.map((icao) => icao).join(',');
const response = await getRequest(`metars`, { icaos: stationICAOs }); const response = await getRequest(`metars`, { icaos: stationICAOs });
return response?.json() || { data: [] }; return response?.json() || [];
} }

View File

@@ -13,9 +13,9 @@ export default function Page({ params }: { params: { icao: string } }) {
useEffect(() => { useEffect(() => {
async function loadAirport() { async function loadAirport() {
const { data: airportData } = await getAirport({ icao: params.icao }); const airportData = await getAirport({ icao: params.icao });
setAirport(airportData); setAirport(airportData);
const { data: metarData } = await getMetars([airportData.icao]); const metarData = await getMetars([airportData.icao]);
if (metarData.length > 0) { if (metarData.length > 0) {
setMetar(metarData[0]); setMetar(metarData[0]);
} }

View File

@@ -57,7 +57,7 @@ export default function MapTiles() {
page: 1 page: 1
}); });
const airports = airportData.filter((airport) => airport.has_metar); const airports = airportData.filter((airport) => airport.has_metar);
const { data: metars } = await getMetars(airports.map((a) => a.icao)); const metars = await getMetars(airports.map((a) => a.icao));
metars.forEach((metar) => { metars.forEach((metar) => {
airportData.forEach((airport) => { airportData.forEach((airport) => {
if (metar.station_id == airport.icao) { if (metar.station_id == airport.icao) {