Header and login
This commit is contained in:
@@ -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
9
service/Cargo.lock
generated
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
);
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ diesel::table! {
|
||||
updated_at -> Timestamp,
|
||||
created_at -> Timestamp,
|
||||
profile_picture -> Nullable<Text>,
|
||||
favorites -> Array<Text>,
|
||||
verified -> Bool,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user