From 6cc8d16219021462f35b203a34126cbfa1389b05 Mon Sep 17 00:00:00 2001 From: Daniel Boline Date: Sat, 23 Nov 2024 20:37:45 -0500 Subject: [PATCH] refactor --- Cargo.toml | 6 +- Dockerfile.build.ubuntu16.04 | 1 - gdrive_lib/Cargo.toml | 6 +- .../V11__authorized_users_created_deleted.sql | 2 + sync_app_http/Cargo.toml | 10 +-- sync_app_http/src/logged_user.rs | 67 +++++++++++++------ sync_app_lib/Cargo.toml | 8 +-- sync_app_lib/src/models.rs | 26 ++++++- 8 files changed, 89 insertions(+), 37 deletions(-) create mode 100644 migrations/V11__authorized_users_created_deleted.sql diff --git a/Cargo.toml b/Cargo.toml index eb3940c..3779728 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sync_app_rust" -version = "0.11.21" +version = "0.12.0" authors = ["Daniel Boline "] edition = "2018" @@ -21,9 +21,9 @@ env_logger = "0.11" futures = "0.3" gdrive_lib = {path="gdrive_lib"} log = "0.4" -stack-string = { git = "https://github.com/ddboline/stack-string-rs.git", features=["postgres_types"], tag="0.9.3" } +stack-string = { git = "https://github.com/ddboline/stack-string-rs.git", features=["postgres_types"], tag="1.0.2" } time = {version="0.3", features=["serde-human-readable", "macros", "formatting"]} -tokio = {version="1.40", features=["rt", "macros", "rt-multi-thread"]} +tokio = {version="1.41", features=["rt", "macros", "rt-multi-thread"]} walkdir = "2.3" [workspace] diff --git a/Dockerfile.build.ubuntu16.04 b/Dockerfile.build.ubuntu16.04 index 1a1d19f..0759308 100644 --- a/Dockerfile.build.ubuntu16.04 +++ b/Dockerfile.build.ubuntu16.04 @@ -1,5 +1,4 @@ FROM rust_stable:xenial_latest -MAINTAINER Daniel Boline WORKDIR /sync_app_rust diff --git a/gdrive_lib/Cargo.toml b/gdrive_lib/Cargo.toml index c296c8b..01b7997 100644 --- a/gdrive_lib/Cargo.toml +++ b/gdrive_lib/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gdrive_lib" -version = "0.11.21" +version = "0.12.0" authors = ["Daniel Boline "] edition = "2018" @@ -27,8 +27,8 @@ rand = "0.8" serde = "1.0" serde_derive = "1.0" serde_json = "1.0" -stack-string = { git = "https://github.com/ddboline/stack-string-rs.git", features=["postgres_types"], tag="0.9.3" } +stack-string = { git = "https://github.com/ddboline/stack-string-rs.git", features=["postgres_types"], tag="1.0.2" } stdout-channel = "0.6" time = {version="0.3", features=["serde-human-readable", "macros", "formatting"]} url = "2.3" -tokio = {version="1.35", features=["rt", "macros", "rt-multi-thread"]} +tokio = {version="1.41", features=["rt", "macros", "rt-multi-thread"]} diff --git a/migrations/V11__authorized_users_created_deleted.sql b/migrations/V11__authorized_users_created_deleted.sql new file mode 100644 index 0000000..8c461f1 --- /dev/null +++ b/migrations/V11__authorized_users_created_deleted.sql @@ -0,0 +1,2 @@ +ALTER TABLE authorized_users ADD COLUMN created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(); +ALTER TABLE authorized_users ADD COLUMN deleted_at TIMESTAMP WITH TIME ZONE; diff --git a/sync_app_http/Cargo.toml b/sync_app_http/Cargo.toml index c176825..f441a77 100644 --- a/sync_app_http/Cargo.toml +++ b/sync_app_http/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sync_app_http" -version = "0.11.21" +version = "0.12.0" authors = ["Daniel Boline "] edition = "2018" @@ -9,7 +9,7 @@ edition = "2018" [dependencies] anyhow = "1.0" async-trait = "0.1" -authorized_users = { git = "https://github.com/ddboline/auth_server_rust.git", tag="0.11.15"} +authorized_users = { git = "https://github.com/ddboline/auth_server_rust.git", tag="0.12.0"} deadqueue = "0.2" dioxus = "0.5" dioxus-core = "0.5" @@ -28,11 +28,11 @@ serde = "1.0" serde_derive = "1.0" serde_json = "1.0" serde_yaml = "0.9" -stack-string = { git = "https://github.com/ddboline/stack-string-rs.git", features=["postgres_types", "rweb-openapi"], tag="0.9.3" } +stack-string = { git = "https://github.com/ddboline/stack-string-rs.git", features=["postgres_types", "rweb-openapi"], tag="1.0.2" } stdout-channel = "0.6" sync_app_lib = {path = "../sync_app_lib"} -thiserror = "1.0" +thiserror = "2.0" time = {version="0.3", features=["serde-human-readable", "macros", "formatting"]} -tokio = {version="1.40", features=["time"]} +tokio = {version="1.41", features=["time"]} url = "2.3" uuid = "1.0" diff --git a/sync_app_http/src/logged_user.rs b/sync_app_http/src/logged_user.rs index 2716dd2..2b86f8f 100644 --- a/sync_app_http/src/logged_user.rs +++ b/sync_app_http/src/logged_user.rs @@ -1,16 +1,17 @@ pub use authorized_users::{ - get_random_key, get_secrets, token::Token, AuthorizedUser, AUTHORIZED_USERS, JWT_SECRET, - KEY_LENGTH, LOGIN_HTML, SECRET_KEY, TRIGGER_DB_UPDATE, + get_random_key, get_secrets, token::Token, AuthorizedUser as ExternalUser, AUTHORIZED_USERS, + JWT_SECRET, KEY_LENGTH, LOGIN_HTML, SECRET_KEY, TRIGGER_DB_UPDATE, }; use futures::TryStreamExt; use log::debug; -use maplit::hashset; +use maplit::hashmap; use reqwest::Client; use rweb::{filters::cookie::cookie, Filter, Rejection, Schema}; use rweb_helper::UuidWrapper; use serde::{Deserialize, Serialize}; use stack_string::{format_sstr, StackString}; use std::{ + collections::HashMap, convert::{TryFrom, TryInto}, env::var, str::FromStr, @@ -69,7 +70,7 @@ impl LoggedUser { session_key: &str, ) -> Result, anyhow::Error> { let base_url: Url = format_sstr!("https://{}", config.domain).parse()?; - let session: Option = AuthorizedUser::get_session_data( + let session: Option = ExternalUser::get_session_data( &base_url, self.session.into(), &self.secret_key, @@ -94,7 +95,7 @@ impl LoggedUser { session_value: SyncSession, ) -> Result<(), anyhow::Error> { let base_url: Url = format_sstr!("https://{}", config.domain).parse()?; - AuthorizedUser::set_session_data( + ExternalUser::set_session_data( &base_url, self.session.into(), &self.secret_key, @@ -115,7 +116,7 @@ impl LoggedUser { session_key: &str, ) -> Result<(), anyhow::Error> { let base_url: Url = format_sstr!("https://{}", config.domain).parse()?; - AuthorizedUser::rm_session_data( + ExternalUser::rm_session_data( &base_url, self.session.into(), &self.secret_key, @@ -166,8 +167,8 @@ impl LoggedUser { } } -impl From for LoggedUser { - fn from(user: AuthorizedUser) -> Self { +impl From for LoggedUser { + fn from(user: ExternalUser) -> Self { Self { email: user.email, session: user.session.into(), @@ -202,21 +203,47 @@ impl FromStr for LoggedUser { /// # Errors /// Return error if db query fails pub async fn fill_from_db(pool: &PgPool) -> Result<(), Error> { - debug!("{:?}", *TRIGGER_DB_UPDATE); - let users = if TRIGGER_DB_UPDATE.check() { - AuthorizedUsers::get_authorized_users(pool) - .await? - .map_ok(|user| user.email) - .try_collect() - .await? - } else { - AUTHORIZED_USERS.get_users() - }; if let Ok("true") = var("TESTENV").as_ref().map(String::as_str) { - AUTHORIZED_USERS.update_users(hashset! {"user@test".into()}); + AUTHORIZED_USERS.update_users(hashmap! { + "user@test".into() => ExternalUser { + email: "user@test".into(), + session: Uuid::new_v4(), + secret_key: StackString::default(), + created_at: Some(OffsetDateTime::now_utc()) + } + }); + return Ok(()); + } + let (created_at, deleted_at) = AuthorizedUsers::get_most_recent(pool).await?; + let most_recent_user_db = created_at.max(deleted_at); + let existing_users = AUTHORIZED_USERS.get_users(); + let most_recent_user = existing_users.values().map(|i| i.created_at).max(); + debug!("most_recent_user_db {most_recent_user_db:?} most_recent_user {most_recent_user:?}"); + if most_recent_user_db.is_some() + && most_recent_user.is_some() + && most_recent_user_db <= most_recent_user + { + return Ok(()); } + + let result: Result, _> = AuthorizedUsers::get_authorized_users(pool) + .await? + .map_ok(|u| { + ( + u.email.clone(), + ExternalUser { + email: u.email, + session: Uuid::new_v4(), + secret_key: StackString::default(), + created_at: Some(u.created_at), + }, + ) + }) + .try_collect() + .await; + let users = result?; AUTHORIZED_USERS.update_users(users); - debug!("{:?}", *AUTHORIZED_USERS); + debug!("AUTHORIZED_USERS {:?}", *AUTHORIZED_USERS); Ok(()) } diff --git a/sync_app_lib/Cargo.toml b/sync_app_lib/Cargo.toml index 5338b9c..ca3541b 100644 --- a/sync_app_lib/Cargo.toml +++ b/sync_app_lib/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sync_app_lib" -version = "0.11.21" +version = "0.12.0" authors = ["Daniel Boline "] edition = "2018" @@ -41,11 +41,11 @@ rust_decimal = "1.26" serde = {version="1.0", features=["derive"]} serde_json = "1.0" smallvec = "1.6" -stack-string = { git = "https://github.com/ddboline/stack-string-rs.git", features=["postgres_types"], tag="0.9.3" } +stack-string = { git = "https://github.com/ddboline/stack-string-rs.git", features=["postgres_types"], tag="1.0.2" } stdout-channel = "0.6" -thiserror = "1.0" +thiserror = "2.0" time = {version="0.3", features=["serde-human-readable", "macros", "formatting"]} -tokio = {version="1.40", features=["rt", "macros", "rt-multi-thread"]} +tokio = {version="1.41", features=["rt", "macros", "rt-multi-thread"]} tokio-postgres = {version = "0.7", features = ["with-time-0_3", "with-uuid-1", "with-serde_json-1"]} url = "2.3" uuid = "1.1" diff --git a/sync_app_lib/src/models.rs b/sync_app_lib/src/models.rs index 1d94cae..8fb690d 100644 --- a/sync_app_lib/src/models.rs +++ b/sync_app_lib/src/models.rs @@ -4,6 +4,7 @@ use log::info; use postgres_query::{query, Error as PqError, FromSqlRow}; use smallvec::{smallvec, SmallVec}; use stack_string::StackString; +use time::OffsetDateTime; use url::Url; use uuid::Uuid; @@ -682,6 +683,7 @@ impl FileSyncConfig { #[derive(FromSqlRow, Clone, Debug)] pub struct AuthorizedUsers { pub email: StackString, + pub created_at: OffsetDateTime, } impl AuthorizedUsers { @@ -690,8 +692,30 @@ impl AuthorizedUsers { pub async fn get_authorized_users( pool: &PgPool, ) -> Result>, Error> { - let query = query!("SELECT * FROM authorized_users"); + let query = query!("SELECT * FROM authorized_users WHERE deleted_at IS NULL"); let conn = pool.get().await?; query.fetch_streaming(&conn).await.map_err(Into::into) } + + /// # Errors + /// Returns error if db query fails + pub async fn get_most_recent( + pool: &PgPool, + ) -> Result<(Option, Option), Error> { + #[derive(FromSqlRow)] + struct CreatedDeleted { + created_at: Option, + deleted_at: Option, + } + + let query = query!( + "SELECT max(created_at) as created_at, max(deleted_at) as deleted_at FROM users" + ); + let conn = pool.get().await?; + let result: Option = query.fetch_opt(&conn).await?; + match result { + Some(result) => Ok((result.created_at, result.deleted_at)), + None => Ok((None, None)), + } + } }