Formatting
This commit is contained in:
@@ -2,10 +2,10 @@ use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
|
||||
use super::{SESSION_COOKIE_NAME, Session};
|
||||
use crate::account::user::User;
|
||||
use crate::error::Error;
|
||||
use actix_web::{Error as ActixError, FromRequest, HttpRequest, dev::Payload, http};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use crate::account::user::User;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Auth {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use argon2::{
|
||||
password_hash::{rand_core::OsRng, SaltString}, Argon2, PasswordHash, PasswordHasher,
|
||||
PasswordVerifier,
|
||||
Argon2, PasswordHash, PasswordHasher, PasswordVerifier,
|
||||
password_hash::{SaltString, rand_core::OsRng},
|
||||
};
|
||||
use rand::distr::Alphanumeric;
|
||||
use rand::prelude::*;
|
||||
|
||||
@@ -2,16 +2,16 @@ use crate::{
|
||||
account::{SESSION_COOKIE_NAME, Session, verify_hash},
|
||||
error::Error,
|
||||
};
|
||||
use actix_web::{HttpRequest, HttpResponse, ResponseError, get, post, put, web, delete};
|
||||
use actix_web::{HttpRequest, HttpResponse, ResponseError, delete, get, post, put, web};
|
||||
use serde::Deserialize;
|
||||
use utoipa::ToSchema;
|
||||
use utoipa_actix_web::scope;
|
||||
use utoipa_actix_web::service_config::ServiceConfig;
|
||||
|
||||
use crate::account::email_token::{EmailToken, send_confirm_email, send_password_reset_email};
|
||||
use crate::account::{Auth, csprng};
|
||||
use crate::account::user::{LoginRequest, RegisterRequest, UpdateUser, User, UserResponse};
|
||||
use crate::account::user_favorites::UserFavorite;
|
||||
use crate::account::{Auth, csprng};
|
||||
|
||||
#[utoipa::path(
|
||||
tag = "account",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
use crate::db;
|
||||
use crate::error::ApiResult;
|
||||
use serde::Deserialize;
|
||||
|
||||
const TABLE_NAME: &str = "user_airport_favorites";
|
||||
|
||||
@@ -19,14 +19,11 @@ impl UserFavorite {
|
||||
"#,
|
||||
TABLE_NAME
|
||||
))
|
||||
.bind(username)
|
||||
.fetch_all(pool)
|
||||
.await?;
|
||||
.bind(username)
|
||||
.fetch_all(pool)
|
||||
.await?;
|
||||
|
||||
let favorites = user_favorites
|
||||
.iter()
|
||||
.map(|uf| uf.icao.clone())
|
||||
.collect();
|
||||
let favorites = user_favorites.iter().map(|uf| uf.icao.clone()).collect();
|
||||
|
||||
Ok(favorites)
|
||||
}
|
||||
@@ -57,10 +54,10 @@ impl UserFavorite {
|
||||
"#,
|
||||
TABLE_NAME
|
||||
))
|
||||
.bind(username)
|
||||
.bind(icao)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
.bind(username)
|
||||
.bind(icao)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -561,11 +561,12 @@ impl Airport {
|
||||
.collect();
|
||||
|
||||
for chunk in airport_rows.chunks(chunk_size) {
|
||||
let mut query_builder: QueryBuilder<Postgres> = QueryBuilder::new(
|
||||
format!("INSERT INTO {} (icao, iata, local, name, category, \
|
||||
let mut query_builder: QueryBuilder<Postgres> = QueryBuilder::new(format!(
|
||||
"INSERT INTO {} (icao, iata, local, name, category, \
|
||||
iso_country, iso_region, municipality, elevation_ft, \
|
||||
longitude, latitude, geometry, has_tower, has_beacon, public) ", TABLE_NAME),
|
||||
);
|
||||
longitude, latitude, geometry, has_tower, has_beacon, public) ",
|
||||
TABLE_NAME
|
||||
));
|
||||
query_builder.push_values(chunk, |mut b, row| {
|
||||
b.push_bind(&row.icao)
|
||||
.push_bind(&row.iata)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use futures_util::stream::StreamExt as _;
|
||||
|
||||
use crate::account::ADMIN_ROLE;
|
||||
use crate::airports::{AirportQuery, UpdateAirport};
|
||||
use crate::{
|
||||
account::{Auth, verify_role},
|
||||
@@ -11,7 +12,6 @@ use actix_web::{HttpRequest, HttpResponse, ResponseError, delete, get, post, put
|
||||
use utoipa::ToSchema;
|
||||
use utoipa_actix_web::scope;
|
||||
use utoipa_actix_web::service_config::ServiceConfig;
|
||||
use crate::account::ADMIN_ROLE;
|
||||
|
||||
#[derive(ToSchema)]
|
||||
#[allow(unused)]
|
||||
|
||||
@@ -139,7 +139,9 @@ impl From<s3::error::S3Error> for Error {
|
||||
s3::error::S3Error::FromUtf8(err) => {
|
||||
Self::new(500, format!("Unknown s3 from utf8 error: {:?}", err))
|
||||
}
|
||||
s3::error::S3Error::FmtError(err) => Self::new(500, format!("Unknown s3 fmt error: {:?}", err)),
|
||||
s3::error::S3Error::FmtError(err) => {
|
||||
Self::new(500, format!("Unknown s3 fmt error: {:?}", err))
|
||||
}
|
||||
s3::error::S3Error::HeaderToStr(err) => {
|
||||
Self::new(500, format!("Unknown s3 header to str error: {:?}", err))
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::account::{hash, ADMIN_ROLE};
|
||||
use crate::account::User;
|
||||
use crate::account::{ADMIN_ROLE, hash};
|
||||
use crate::http_client::HttpClient;
|
||||
use actix_cors::Cors;
|
||||
use actix_web::{App, HttpServer, middleware::Logger, web};
|
||||
@@ -9,7 +10,6 @@ use utoipa::openapi::SecurityRequirement;
|
||||
use utoipa::openapi::security::{ApiKey, ApiKeyValue, SecurityScheme};
|
||||
use utoipa_actix_web::{AppExt, scope};
|
||||
use utoipa_swagger_ui::{Config, SwaggerUi};
|
||||
use crate::account::User;
|
||||
|
||||
mod account;
|
||||
mod airports;
|
||||
|
||||
@@ -8,13 +8,13 @@ use chrono::{DateTime, Utc};
|
||||
use flate2::read::GzDecoder;
|
||||
use reqwest::header::ETAG;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::{Postgres, QueryBuilder};
|
||||
use std::collections::HashSet;
|
||||
use std::env;
|
||||
use std::fmt::Display;
|
||||
use std::io::{Cursor, Read};
|
||||
use std::str::FromStr;
|
||||
use std::sync::OnceLock;
|
||||
use sqlx::{Postgres, QueryBuilder};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
static TIME_OFFSET: OnceLock<i64> = OnceLock::new();
|
||||
@@ -310,9 +310,11 @@ impl MetarRow {
|
||||
let chunk_size = 1000;
|
||||
|
||||
for chunk in metars.chunks(chunk_size) {
|
||||
let mut query_builder: QueryBuilder<Postgres> = QueryBuilder::new(
|
||||
format!("INSERT INTO {} (icao, observation_time, raw_text, data) ", TABLE_NAME));
|
||||
query_builder.push_values(chunk, |mut b, metar | {
|
||||
let mut query_builder: QueryBuilder<Postgres> = QueryBuilder::new(format!(
|
||||
"INSERT INTO {} (icao, observation_time, raw_text, data) ",
|
||||
TABLE_NAME
|
||||
));
|
||||
query_builder.push_values(chunk, |mut b, metar| {
|
||||
let row: Self = match metar.to_row() {
|
||||
Ok(row) => row,
|
||||
Err(e) => {
|
||||
@@ -394,12 +396,16 @@ impl Metar {
|
||||
metar.observation_time = match chrono::DateTime::parse_from_rfc3339(&observation_time) {
|
||||
Ok(datetime) => datetime.with_timezone(&Utc),
|
||||
Err(err) => return Err(err.into()),
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
Err(err) => {
|
||||
return Err(Error::new(
|
||||
err.status,
|
||||
format!("Unexpected observation time field '{}': {}; {}", observation_time, metar_string, err)));
|
||||
err.status,
|
||||
format!(
|
||||
"Unexpected observation time field '{}': {}; {}",
|
||||
observation_time, metar_string, err
|
||||
),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -531,10 +537,10 @@ impl Metar {
|
||||
visibility_parts[1]
|
||||
} else {
|
||||
log::warn!(
|
||||
"Skipping unexpected visibility field '{:?}' ({})",
|
||||
visibility_parts,
|
||||
metar_string
|
||||
);
|
||||
"Skipping unexpected visibility field '{:?}' ({})",
|
||||
visibility_parts,
|
||||
metar_string
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -570,7 +576,11 @@ impl Metar {
|
||||
metar.visibility_statute_mi = Some(format!("{:.2}", visibility));
|
||||
}
|
||||
} else {
|
||||
log::warn!("Skipping unexpected visibility field '{}' ({})", metar_parts[0], metar_string);
|
||||
log::warn!(
|
||||
"Skipping unexpected visibility field '{}' ({})",
|
||||
metar_parts[0],
|
||||
metar_string
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::env;
|
||||
use crate::http_client::HttpClient;
|
||||
use crate::metars::Metar;
|
||||
use chrono::{DateTime, Utc};
|
||||
use std::env;
|
||||
use std::time::{Duration, Instant};
|
||||
use tokio::time::interval;
|
||||
|
||||
|
||||
@@ -26,24 +26,24 @@ export function AirportSearch({ limit = 5 }: AirportSearchProps) {
|
||||
});
|
||||
const nameResponse = await getAirports({
|
||||
name: debounced,
|
||||
limit: limit - 1,
|
||||
limit: limit - 1
|
||||
});
|
||||
let combined = [...icaoResponse.data, ...nameResponse.data];
|
||||
combined = combined.slice(0, limit);
|
||||
return combined.map((airport) => ({
|
||||
key: airport.icao,
|
||||
value: airport.icao,
|
||||
label: `${airport.icao} - ${airport.name}`,
|
||||
label: `${airport.icao} - ${airport.name}`
|
||||
}));
|
||||
} catch (err) {
|
||||
console.error('airport search failed', err);
|
||||
return []
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
fetch().then(d => {
|
||||
fetch().then((d) => {
|
||||
setData(d);
|
||||
console.log(d)
|
||||
console.log(d);
|
||||
});
|
||||
}, [debounced, limit]);
|
||||
|
||||
@@ -57,7 +57,6 @@ export function AirportSearch({ limit = 5 }: AirportSearchProps) {
|
||||
onOptionSubmit={() => {}}
|
||||
radius={'xl'}
|
||||
onBlur={() => setSearch('')}
|
||||
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,7 @@ import { AirportSearch } from '@components/AirportSearch.tsx';
|
||||
// { link: '/metars', label: 'Metars' }
|
||||
// ];
|
||||
|
||||
const protectedPages = [
|
||||
'/administration',
|
||||
'/profile'
|
||||
]
|
||||
const protectedPages = ['/administration', '/profile'];
|
||||
|
||||
export function Header() {
|
||||
const { user, setUser } = useUserContext();
|
||||
@@ -73,11 +70,9 @@ export function Header() {
|
||||
setUser(undefined);
|
||||
|
||||
// See if the current page is a protected page
|
||||
const isProtected = protectedPages.some(pattern =>
|
||||
matchPath(pattern, pathname)
|
||||
)
|
||||
const isProtected = protectedPages.some((pattern) => matchPath(pattern, pathname));
|
||||
if (isProtected) {
|
||||
navigate('/', { replace: true })
|
||||
navigate('/', { replace: true });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,20 +14,15 @@ export function UserProvider({ children }: { children: ReactNode }) {
|
||||
|
||||
async function toggleFavorite(icao: string) {
|
||||
setFavorites((prev) => {
|
||||
const isFav = prev.includes(icao)
|
||||
const next = isFav
|
||||
? prev.filter((i) => i !== icao)
|
||||
: [...prev, icao]
|
||||
const isFav = prev.includes(icao);
|
||||
const next = isFav ? prev.filter((i) => i !== icao) : [...prev, icao];
|
||||
|
||||
;(isFav
|
||||
? removeFavorite(icao)
|
||||
: addFavorite(icao)
|
||||
).catch((err) => {
|
||||
console.error('Sync failed, rolling back', err)
|
||||
setFavorites(prev)
|
||||
})
|
||||
(isFav ? removeFavorite(icao) : addFavorite(icao)).catch((err) => {
|
||||
console.error('Sync failed, rolling back', err);
|
||||
setFavorites(prev);
|
||||
});
|
||||
|
||||
return next
|
||||
return next;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -57,9 +52,9 @@ export function UserProvider({ children }: { children: ReactNode }) {
|
||||
|
||||
useEffect(() => {
|
||||
if (user != undefined) {
|
||||
getFavorites().then(f => {
|
||||
setFavorites(f)
|
||||
})
|
||||
getFavorites().then((f) => {
|
||||
setFavorites(f);
|
||||
});
|
||||
}
|
||||
}, [user]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user