Header and login

This commit is contained in:
2023-11-18 08:45:52 -05:00
parent e2bd270d7c
commit 319f64bc16
30 changed files with 1434 additions and 776 deletions

View File

@@ -19,3 +19,5 @@ ACCESS_TOKEN_MAXAGE=5
REFRESH_TOKEN_PRIVATE_KEY=
REFRESH_TOKEN_PUBLIC_KEY=
REFRESH_TOKEN_MAXAGE=30
GOV_API_URL=https://aviationweather.gov/cgi-bin/data

9
service/Cargo.lock generated
View File

@@ -1139,9 +1139,9 @@ checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
[[package]]
name = "linux-raw-sys"
version = "0.4.7"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128"
checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829"
[[package]]
name = "local-channel"
@@ -1665,9 +1665,9 @@ dependencies = [
[[package]]
name = "rustix"
version = "0.38.13"
version = "0.38.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7db8590df6dfcd144d22afd1b83b36c21a18d7cbc1dc4bb5295a8712e9eb662"
checksum = "9ad981d6c340a49cdc40a1028d9c6084ec7e9fa33fcb839cab656a267071e234"
dependencies = [
"bitflags 2.4.0",
"errno",
@@ -1819,6 +1819,7 @@ dependencies = [
"r2d2",
"redis",
"reqwest",
"rustix",
"serde",
"serde_json",
"tokio",

View File

@@ -33,3 +33,4 @@ argon2 = "0.5.2"
jsonwebtoken = "9.0.0"
redis = { version = "0.23.3", features = ["tokio-comp", "connection-manager", "r2d2"] }
base64 = "0.21.4"
rustix = "0.38.19" # https://github.com/imsnif/bandwhich/issues/284

View File

@@ -7,5 +7,6 @@ CREATE TABLE IF NOT EXISTS users (
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
profile_picture TEXT,
favorites TEXT[] NOT NULL DEFAULT '{}',
verified BOOLEAN NOT NULL DEFAULT FALSE
);

View File

@@ -1,5 +1,5 @@
use crate::{airports::{InsertAirport, QueryAirport}, db::{self, Metadata}};
use actix_web::{delete, get, post, put, web, HttpResponse, HttpRequest};
use crate::{airports::{InsertAirport, QueryAirport}, db::{self, Metadata}, auth::{JwtAuth, verify_role}};
use actix_web::{delete, get, post, put, web, HttpResponse, HttpRequest, ResponseError};
use log::{error, warn};
use postgis_diesel::types::{Polygon, Point};
use serde::{Serialize, Deserialize};
@@ -14,7 +14,11 @@ struct GetAllParameters {
}
#[get("/import")]
async fn import() -> HttpResponse {
async fn import(auth: JwtAuth) -> HttpResponse {
let _ = match verify_role(&auth, "admin") {
Ok(_) => {},
Err(err) => return ResponseError::error_response(&err)
};
db::import_data();
HttpResponse::Ok().body({})
}
@@ -129,7 +133,11 @@ async fn get(icao: web::Path<String>) -> HttpResponse {
}
#[post("/airports")]
async fn create(airport: web::Json<InsertAirport>) -> HttpResponse {
async fn create(airport: web::Json<InsertAirport>, auth: JwtAuth) -> HttpResponse {
let _ = match verify_role(&auth, "admin") {
Ok(_) => {},
Err(err) => return ResponseError::error_response(&err)
};
match QueryAirport::create(airport.into_inner()) {
Ok(a) => HttpResponse::Created().json(a),
Err(err) => {
@@ -140,7 +148,11 @@ async fn create(airport: web::Json<InsertAirport>) -> HttpResponse {
}
#[put("/airports/{icao}")]
async fn update(icao: web::Path<i32>, airport: web::Json<InsertAirport>) -> HttpResponse {
async fn update(icao: web::Path<i32>, airport: web::Json<InsertAirport>, auth: JwtAuth) -> HttpResponse {
let _ = match verify_role(&auth, "admin") {
Ok(_) => {},
Err(err) => return ResponseError::error_response(&err)
};
match QueryAirport::update(icao.into_inner(), airport.into_inner()) {
Ok(a) => HttpResponse::Ok().json(a),
Err(err) => {
@@ -151,7 +163,11 @@ async fn update(icao: web::Path<i32>, airport: web::Json<InsertAirport>) -> Http
}
#[delete("/airports/{icao}")]
async fn delete(icao: web::Path<i32>) -> HttpResponse {
async fn delete(icao: web::Path<i32>, auth: JwtAuth) -> HttpResponse {
let _ = match verify_role(&auth, "admin") {
Ok(_) => {},
Err(err) => return ResponseError::error_response(&err)
};
match QueryAirport::delete(icao.into_inner()) {
Ok(_) => HttpResponse::NoContent().finish(),
Err(err) => {

View File

@@ -30,6 +30,7 @@ impl RegisterUser {
updated_at: chrono::Utc::now().naive_utc(),
created_at: chrono::Utc::now().naive_utc(),
profile_picture: None,
favorites: vec![],
verified: false,
})
}
@@ -52,6 +53,7 @@ pub struct QueryUser {
pub updated_at: chrono::NaiveDateTime,
pub created_at: chrono::NaiveDateTime,
pub profile_picture: Option<String>,
pub favorites: Vec<String>,
pub verified: bool,
}
@@ -78,6 +80,7 @@ pub struct InsertUser {
pub updated_at: chrono::NaiveDateTime,
pub created_at: chrono::NaiveDateTime,
pub profile_picture: Option<String>,
pub favorites: Vec<String>,
pub verified: bool,
}
@@ -90,7 +93,7 @@ impl InsertUser {
Ok(user)
}
pub fn update_profile(email: &str, profile_picture: Option<&str>) -> Result<QueryUser, ServiceError> {
pub fn update_profile_picture(email: &str, profile_picture: Option<&str>) -> Result<QueryUser, ServiceError> {
let mut conn = connection()?;
let user = diesel::update(users::table)
.filter(users::email.eq(&email))
@@ -98,6 +101,15 @@ impl InsertUser {
.get_result(&mut conn)?;
Ok(user)
}
pub fn update_favorites(email: &str, favorites: Vec<String>) -> Result<QueryUser, ServiceError> {
let mut conn = connection()?;
let user = diesel::update(users::table)
.filter(users::email.eq(&email))
.set(users::favorites.eq(favorites))
.get_result(&mut conn)?;
Ok(user)
}
}
#[derive(Debug, Serialize, Deserialize)]

View File

@@ -4,7 +4,7 @@ use redis::{Client as RedisClient, aio::Connection as RedisConnection};
use serde::{Deserialize, Serialize};
use crate::diesel_migrations::MigrationHarness;
use lazy_static::lazy_static;
use log::{error, debug, info, warn};
use log::{error, debug, info};
use r2d2;
use std::env;

View File

@@ -57,6 +57,7 @@ diesel::table! {
updated_at -> Timestamp,
created_at -> Timestamp,
profile_picture -> Nullable<Text>,
favorites -> Array<Text>,
verified -> Bool,
}
}

View File

@@ -20,7 +20,7 @@ mod scheduler;
#[actix_web::main]
async fn main() -> std::io::Result<()> {
dotenv().ok();
env_logger::init_from_env(env_logger::Env::default().filter_or("RUST_LOG", "warn,siren=info"));
env_logger::init_from_env(env_logger::Env::default().filter_or("RUST_LOG", "warn,service=info"));
db::init();
scheduler::update_airports();

View File

@@ -139,7 +139,8 @@ impl Metar {
}
async fn get_remote_metars(icaos: String) -> Vec<Metar> {
let url = format!("https://beta.aviationweather.gov/cgi-bin/data/metar.php?ids={}&format=xml", icaos);
let gov_api_url = std::env::var("GOV_API_URL").expect("GOV_API_URL must be set");
let url = format!("{}/metar.php?ids={}&format=xml", gov_api_url, icaos);
match reqwest::get(url).await {
Ok(r) => match r.text().await {
Ok(r) => {
@@ -290,7 +291,7 @@ impl Metar {
return Ok(db_metars);
}
trace!("Retrieving missing METAR data for {:?}", missing_icaos);
let missing_icaos_string: Vec<String> = missing_icaos.iter().map(|icao| format!("'{}'", icao.to_string())).collect();
let missing_icaos_string: Vec<String> = missing_icaos.iter().map(|icao| format!("{}", icao.to_string())).collect();
let mut missing_metars = Self::get_remote_metars(missing_icaos_string.join(",")).await;
if missing_metars.len() > 0 {
let insert_metars = Self::to_insert(&missing_metars);

View File

@@ -1,18 +1,59 @@
use actix_web::{get, post, delete, web, HttpResponse};
use actix_web::{get, post, delete, web, HttpResponse, ResponseError};
use crate::auth::{JwtAuth, QueryUser, InsertUser};
#[get("users/favorites")]
async fn get_favorites() -> HttpResponse {
HttpResponse::NotImplemented().finish()
async fn get_favorites(auth: JwtAuth) -> HttpResponse {
println!("{:?}", auth);
match QueryUser::get_by_email(&auth.user.email) {
Ok(user) => {
return HttpResponse::Ok().json(user.favorites)
},
Err(err) => return ResponseError::error_response(&err)
}
}
#[post("users/favorites")]
async fn add_favorite() -> HttpResponse {
HttpResponse::NotImplemented().finish()
#[post("users/favorites/{icao}")]
async fn add_favorite(icao: web::Path<String>, auth: JwtAuth) -> HttpResponse {
match QueryUser::get_by_email(&auth.user.email) {
Ok(user) => {
if user.favorites.contains(&icao) {
// Check if the airport ICAO is already in the user's favorites
return HttpResponse::Conflict().finish()
} else {
// Add the airport ICAO to the user's favorites
let mut favorites = user.favorites;
favorites.push(icao.into_inner());
match InsertUser::update_favorites(&user.email, favorites) {
Ok(_) => return HttpResponse::Ok().finish(),
Err(err) => return ResponseError::error_response(&err)
}
}
},
Err(err) => return ResponseError::error_response(&err)
}
}
#[delete("users/favorites")]
async fn delete_favorite() -> HttpResponse {
HttpResponse::NotImplemented().finish()
#[delete("users/favorites/{icao}")]
async fn delete_favorite(icao: web::Path<String>, auth: JwtAuth) -> HttpResponse {
let icao: String = icao.into_inner();
match QueryUser::get_by_email(&auth.user.email) {
Ok(user) => {
if user.favorites.contains(&icao) {
// Check if the airport ICAO is already in the user's favorites
let mut favorites = user.favorites;
favorites.retain(|x| x != &icao);
match InsertUser::update_favorites(&user.email, favorites) {
Ok(_) => return HttpResponse::Ok().finish(),
Err(err) => return ResponseError::error_response(&err)
}
} else {
// Remove the airport ICAO from the user's favorites
return HttpResponse::Conflict().finish()
}
},
Err(err) => return ResponseError::error_response(&err)
}
}
pub fn init_routes(config: &mut web::ServiceConfig) {