Tweaks to database for users

This commit is contained in:
2025-04-23 19:49:44 -04:00
parent 3b5514e825
commit ebc1f30f24
5 changed files with 32 additions and 14 deletions

View File

@@ -62,10 +62,12 @@ CREATE INDEX ON metars (observation_time DESC);
CREATE TABLE IF NOT EXISTS users ( CREATE TABLE IF NOT EXISTS users (
email TEXT PRIMARY KEY NOT NULL, email TEXT PRIMARY KEY NOT NULL,
email_verified BOOLEAN NOT NULL DEFAULT false,
password_hash TEXT NOT NULL, password_hash TEXT NOT NULL,
role TEXT NOT NULL, role TEXT NOT NULL,
first_name TEXT NOT NULL, first_name TEXT NOT NULL,
last_name TEXT NOT NULL, last_name TEXT NOT NULL,
avatar TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
); );

View File

@@ -188,10 +188,12 @@ async fn change_password(
let update_user = UpdateUser { let update_user = UpdateUser {
email: None, email: None,
email_verified: None,
password: Some(password.into_inner()), password: Some(password.into_inner()),
role: None, role: None,
first_name: None, first_name: None,
last_name: None, last_name: None,
avatar: None,
}; };
match update_user.update(&email).await { match update_user.update(&email).await {

View File

@@ -489,8 +489,6 @@ impl Airport {
airport.into() airport.into()
}) })
.collect(); .collect();
Runway::insert_all(&all_runway_rows).await?;
Frequency::insert_all(&all_frequency_rows).await?;
for chunk in airport_rows.chunks(chunk_size) { for chunk in airport_rows.chunks(chunk_size) {
let mut query_builder: QueryBuilder<Postgres> = QueryBuilder::new( let mut query_builder: QueryBuilder<Postgres> = QueryBuilder::new(
@@ -519,6 +517,9 @@ impl Airport {
query.execute(pool).await?; query.execute(pool).await?;
} }
Runway::insert_all(&all_runway_rows).await?;
Frequency::insert_all(&all_frequency_rows).await?;
Ok(()) Ok(())
} }
@@ -577,7 +578,7 @@ impl Airport {
column: &str, column: &str,
field: &'a Option<String>, field: &'a Option<String>,
) { ) {
if let Some(ref value_str) = field { if let Some(value_str) = field {
// Split on commas, trim whitespace, and drop empties. // Split on commas, trim whitespace, and drop empties.
let values: Vec<&str> = value_str let values: Vec<&str> = value_str
.split(',') .split(',')
@@ -606,7 +607,7 @@ impl Airport {
field: &'a Option<String>, field: &'a Option<String>,
) { ) {
// Query column like // Query column like
if let Some(ref value) = field { if let Some(value) = field {
if !*has_where { if !*has_where {
builder.push(" WHERE "); builder.push(" WHERE ");
*has_where = true; *has_where = true;
@@ -627,7 +628,7 @@ impl Airport {
field: &'a Option<String>, field: &'a Option<String>,
) -> ApiResult<()> { ) -> ApiResult<()> {
// Query bounds // Query bounds
if let Some(ref bounds_string) = field { if let Some(bounds_string) = field {
if !*has_where { if !*has_where {
builder.push(" WHERE "); builder.push(" WHERE ");
*has_where = true; *has_where = true;

View File

@@ -4,6 +4,7 @@ use actix_cors::Cors;
use actix_web::{App, HttpServer, middleware::Logger, web}; use actix_web::{App, HttpServer, middleware::Logger, web};
use dotenv::from_filename; use dotenv::from_filename;
use reqwest::Certificate; use reqwest::Certificate;
use uuid::Uuid;
use crate::account::hash; use crate::account::hash;
use crate::users::{User, ADMIN_ROLE}; use crate::users::{User, ADMIN_ROLE};
@@ -43,10 +44,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
} }
let admin_user = User { let admin_user = User {
email, email,
email_verified: true,
password_hash, password_hash,
role: ADMIN_ROLE.to_string(), role: ADMIN_ROLE.to_string(),
first_name: "Admin".to_string(), first_name: "Admin".to_string(),
last_name: "".to_string(), last_name: "".to_string(),
avatar: None,
updated_at: Default::default(), updated_at: Default::default(),
created_at: Default::default(), created_at: Default::default(),
}; };

View File

@@ -21,9 +21,8 @@ impl RegisterRequest {
pub fn to_user(self) -> ApiResult<User> { pub fn to_user(self) -> ApiResult<User> {
let password_hash = hash(&self.password)?; let password_hash = hash(&self.password)?;
Ok(User { Ok(User {
id: Uuid::new_v4(),
email: self.email.to_lowercase(), email: self.email.to_lowercase(),
emailVerified: false, email_verified: false,
password_hash, password_hash,
role: USER_ROLE.to_string(), role: USER_ROLE.to_string(),
first_name: self.first_name, first_name: self.first_name,
@@ -43,7 +42,6 @@ pub struct LoginRequest {
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
pub struct UserResponse { pub struct UserResponse {
pub id: Uuid,
pub email_verified: bool, pub email_verified: bool,
pub role: String, pub role: String,
pub first_name: String, pub first_name: String,
@@ -55,8 +53,7 @@ pub struct UserResponse {
impl From<User> for UserResponse { impl From<User> for UserResponse {
fn from(user: User) -> Self { fn from(user: User) -> Self {
UserResponse { UserResponse {
id: user.id, email_verified: user.email_verified,
email_verified: user.emailVerified,
role: user.role, role: user.role,
first_name: user.first_name, first_name: user.first_name,
last_name: user.last_name, last_name: user.last_name,
@@ -67,8 +64,8 @@ impl From<User> for UserResponse {
#[derive(Debug, Deserialize, sqlx::FromRow)] #[derive(Debug, Deserialize, sqlx::FromRow)]
pub struct UpdateUser { pub struct UpdateUser {
pub id: Uuid,
pub email: Option<String>, pub email: Option<String>,
pub email_verified: Option<bool>,
pub password: Option<String>, pub password: Option<String>,
pub role: Option<String>, pub role: Option<String>,
pub first_name: Option<String>, pub first_name: Option<String>,
@@ -98,6 +95,11 @@ impl UpdateUser {
query_builder.push("email = "); query_builder.push("email = ");
query_builder.push_bind(email); query_builder.push_bind(email);
} }
if let Some(ref email_verified) = self.email_verified {
push_comma(&mut query_builder);
query_builder.push("email_verified = ");
query_builder.push_bind(email_verified);
}
if let Some(ref password) = self.password { if let Some(ref password) = self.password {
push_comma(&mut query_builder); push_comma(&mut query_builder);
let password_hash = hash(password)?; let password_hash = hash(password)?;
@@ -119,6 +121,11 @@ impl UpdateUser {
query_builder.push("last_name = "); query_builder.push("last_name = ");
query_builder.push_bind(last_name); query_builder.push_bind(last_name);
} }
if let Some(ref avatar) = self.avatar {
push_comma(&mut query_builder);
query_builder.push("avatar = ");
query_builder.push_bind(avatar);
}
push_comma(&mut query_builder); push_comma(&mut query_builder);
query_builder.push("updated_at = "); query_builder.push("updated_at = ");
query_builder.push_bind(Utc::now()); query_builder.push_bind(Utc::now());
@@ -136,9 +143,8 @@ impl UpdateUser {
#[derive(Debug, Serialize, Deserialize, sqlx::FromRow)] #[derive(Debug, Serialize, Deserialize, sqlx::FromRow)]
pub struct User { pub struct User {
pub id: Uuid,
pub email: String, pub email: String,
pub emailVerified: bool, pub email_verified: bool,
pub password_hash: String, pub password_hash: String,
pub role: String, pub role: String,
pub first_name: String, pub first_name: String,
@@ -188,23 +194,27 @@ impl User {
r#" r#"
INSERT INTO {} ( INSERT INTO {} (
email, email,
email_verified,
password_hash, password_hash,
role, role,
first_name, first_name,
last_name, last_name,
avatar,
created_at, created_at,
updated_at updated_at
) )
VALUES ($1, $2, $3, $4, $5, $6, $7) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
RETURNING * RETURNING *
"#, "#,
TABLE_NAME, TABLE_NAME,
)) ))
.bind(&self.email) .bind(&self.email)
.bind(&self.email_verified)
.bind(&self.password_hash) .bind(&self.password_hash)
.bind(&self.role) .bind(&self.role)
.bind(&self.first_name) .bind(&self.first_name)
.bind(&self.last_name) .bind(&self.last_name)
.bind(&self.avatar)
.bind(self.created_at) .bind(self.created_at)
.bind(self.updated_at) .bind(self.updated_at)
.fetch_one(pool) .fetch_one(pool)