diff --git a/Cargo.toml b/Cargo.toml index 2e0809420c..766033512e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,6 @@ hypr-audio-interface = { path = "crates/audio-interface", package = "audio-inter hypr-audio-utils = { path = "crates/audio-utils", package = "audio-utils" } hypr-buffer = { path = "crates/buffer", package = "buffer" } hypr-data = { path = "crates/data", package = "data" } -hypr-db-admin = { path = "crates/db-admin", package = "db-admin" } hypr-db-core = { path = "crates/db-core", package = "db-core" } hypr-db-user = { path = "crates/db-user", package = "db-user" } hypr-detect = { path = "crates/detect", package = "detect" } diff --git a/crates/db-admin/Cargo.toml b/crates/db-admin/Cargo.toml deleted file mode 100644 index 552311ba7d..0000000000 --- a/crates/db-admin/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "db-admin" -version = "0.1.0" -edition = "2024" - -[dependencies] -hypr-db-core = { workspace = true } -hypr-nango = { path = "../nango", package = "nango" } - -async-stripe = { workspace = true, default-features = false, features = ["runtime-tokio-hyper"] } -libsql = { workspace = true } -tokio = { workspace = true, features = ["rt", "macros"] } - -schemars = { workspace = true, features = ["chrono"] } -serde = { workspace = true, features = ["derive"] } -serde_json = { workspace = true } -strum = { workspace = true, features = ["derive"] } - -chrono = { workspace = true, features = ["serde"] } -codes-iso-639 = { workspace = true } -thiserror = { workspace = true } -uuid = { workspace = true, features = ["v4", "serde"] } diff --git a/crates/db-admin/src/accounts_migration.sql b/crates/db-admin/src/accounts_migration.sql deleted file mode 100644 index 61516ca54f..0000000000 --- a/crates/db-admin/src/accounts_migration.sql +++ /dev/null @@ -1,5 +0,0 @@ -CREATE TABLE IF NOT EXISTS accounts ( - id TEXT PRIMARY KEY, - turso_db_name TEXT NOT NULL, - clerk_org_id TEXT DEFAULT NULL -); diff --git a/crates/db-admin/src/accounts_ops.rs b/crates/db-admin/src/accounts_ops.rs deleted file mode 100644 index 9c632c1b95..0000000000 --- a/crates/db-admin/src/accounts_ops.rs +++ /dev/null @@ -1,202 +0,0 @@ -use super::{Account, AdminDatabase}; - -impl AdminDatabase { - pub async fn list_accounts(&self) -> Result, crate::Error> { - let conn = self.conn()?; - - let mut rows = conn.query("SELECT * FROM accounts", ()).await?; - - let mut items = Vec::new(); - while let Some(row) = rows.next().await? { - let item: Account = libsql::de::from_row(&row).unwrap(); - items.push(item); - } - Ok(items) - } - - pub async fn upsert_account(&self, organization: Account) -> Result { - let conn = self.conn()?; - - let mut rows = conn - .query( - "INSERT INTO accounts ( - id, - turso_db_name, - clerk_org_id - ) VALUES (?, ?, ?) RETURNING *", - vec![ - libsql::Value::Text(organization.id), - libsql::Value::Text(organization.turso_db_name), - organization - .clerk_org_id - .map(libsql::Value::Text) - .unwrap_or(libsql::Value::Null), - ], - ) - .await?; - - let row = rows.next().await?.unwrap(); - let org: Account = libsql::de::from_row(&row).unwrap(); - Ok(org) - } - - pub async fn get_account_by_id( - &self, - id: impl Into, - ) -> Result, crate::Error> { - let conn = self.conn()?; - - let mut rows = conn - .query("SELECT * FROM accounts WHERE id = ?", vec![id.into()]) - .await?; - match rows.next().await? { - None => Ok(None), - Some(row) => { - let org: Account = libsql::de::from_row(&row).unwrap(); - Ok(Some(org)) - } - } - } - - pub async fn get_account_by_clerk_org_id( - &self, - clerk_org_id: impl Into, - ) -> Result, crate::Error> { - let conn = self.conn()?; - - let mut rows = conn - .query( - "SELECT * FROM accounts WHERE clerk_org_id = ?", - vec![clerk_org_id.into()], - ) - .await?; - - match rows.next().await? { - None => Ok(None), - Some(row) => { - let org: Account = libsql::de::from_row(&row).unwrap(); - Ok(Some(org)) - } - } - } - - pub async fn get_account_by_clerk_user_id( - &self, - clerk_user_id: impl Into, - ) -> Result, crate::Error> { - let conn = self.conn()?; - - let mut rows = conn - .query( - "SELECT o.* FROM accounts o - INNER JOIN users u ON u.account_id = o.id - WHERE u.clerk_user_id = ? AND o.clerk_org_id IS NULL", - vec![clerk_user_id.into()], - ) - .await?; - - match rows.next().await? { - None => Ok(None), - Some(row) => { - let org: Account = libsql::de::from_row(&row).unwrap(); - Ok(Some(org)) - } - } - } - - pub async fn list_accounts_by_clerk_user_id( - &self, - user_id: impl Into, - ) -> Result, crate::Error> { - let conn = self.conn()?; - - let mut rows = conn - .query( - "SELECT o.* FROM accounts o - INNER JOIN users u ON u.account_id = o.id - WHERE u.clerk_user_id = ?", - vec![user_id.into()], - ) - .await?; - - let mut accounts = Vec::new(); - while let Some(row) = rows.next().await? { - let org: Account = libsql::de::from_row(&row).unwrap(); - accounts.push(org); - } - - Ok(accounts) - } -} - -#[cfg(test)] -mod tests { - use crate::{Account, User, tests::setup_db}; - - #[tokio::test] - async fn test_accounts() { - let db = setup_db().await; - - let org = Account { - id: uuid::Uuid::new_v4().to_string(), - turso_db_name: "yujonglee".to_string(), - clerk_org_id: Some("org_1".to_string()), - }; - - let org = db.upsert_account(org).await.unwrap(); - assert_eq!(org.clerk_org_id, Some("org_1".to_string())); - } - - #[tokio::test] - async fn test_list_accounts_by_clerk_user_id() { - let db = setup_db().await; - - let accounts = [ - Account { - id: uuid::Uuid::new_v4().to_string(), - turso_db_name: "account1".to_string(), - clerk_org_id: Some("org_1".to_string()), - }, - Account { - id: uuid::Uuid::new_v4().to_string(), - turso_db_name: "account2".to_string(), - clerk_org_id: Some("org_2".to_string()), - }, - ]; - for account in accounts.clone() { - db.upsert_account(account).await.unwrap(); - } - - let users = [ - User { - id: uuid::Uuid::new_v4().to_string(), - account_id: accounts[0].id.clone(), - human_id: uuid::Uuid::new_v4().to_string(), - timestamp: chrono::Utc::now(), - clerk_user_id: "clerk_user_123".to_string(), - }, - User { - id: uuid::Uuid::new_v4().to_string(), - account_id: accounts[1].id.clone(), - human_id: uuid::Uuid::new_v4().to_string(), - timestamp: chrono::Utc::now(), - clerk_user_id: "clerk_user_456".to_string(), - }, - ]; - for user in users { - db.upsert_user(user).await.unwrap(); - } - - let accounts = db - .list_accounts_by_clerk_user_id("clerk_user_123") - .await - .unwrap(); - assert_eq!(accounts.len(), 1); - - let accounts = db - .list_accounts_by_clerk_user_id("clerk_user_456") - .await - .unwrap(); - assert_eq!(accounts.len(), 1); - } -} diff --git a/crates/db-admin/src/accounts_types.rs b/crates/db-admin/src/accounts_types.rs deleted file mode 100644 index f2618b8408..0000000000 --- a/crates/db-admin/src/accounts_types.rs +++ /dev/null @@ -1,9 +0,0 @@ -use crate::admin_common_derives; - -admin_common_derives! { - pub struct Account { - pub id: String, - pub turso_db_name: String, - pub clerk_org_id: Option, - } -} diff --git a/crates/db-admin/src/billings_migration.sql b/crates/db-admin/src/billings_migration.sql deleted file mode 100644 index 183285f669..0000000000 --- a/crates/db-admin/src/billings_migration.sql +++ /dev/null @@ -1,7 +0,0 @@ -CREATE TABLE IF NOT EXISTS billings ( - id TEXT PRIMARY KEY, - organization_id TEXT NOT NULL, - stripe_customer TEXT NOT NULL, - stripe_subscription TEXT NOT NULL, - FOREIGN KEY (organization_id) REFERENCES organizations(id) -); diff --git a/crates/db-admin/src/billings_ops.rs b/crates/db-admin/src/billings_ops.rs deleted file mode 100644 index 7771ac5a7a..0000000000 --- a/crates/db-admin/src/billings_ops.rs +++ /dev/null @@ -1,118 +0,0 @@ -use super::{AdminDatabase, Billing}; - -impl AdminDatabase { - pub async fn create_empty_billing( - &self, - organization_id: impl Into, - ) -> Result, crate::Error> { - let conn = self.conn()?; - - let mut rows = conn - .query( - "INSERT INTO billings ( - id, - organization_id, - stripe_subscription, - stripe_customer - ) VALUES (?, ?, ?, ?) - RETURNING *", - vec![ - libsql::Value::Text(uuid::Uuid::new_v4().to_string()), - libsql::Value::Text(organization_id.into()), - libsql::Value::Null, - libsql::Value::Null, - ], - ) - .await?; - - match rows.next().await? { - None => Ok(None), - Some(row) => { - let billing: Billing = libsql::de::from_row(&row).unwrap(); - Ok(Some(billing)) - } - } - } - - pub async fn update_stripe_customer( - &self, - customer: &stripe::Customer, - ) -> Result, crate::Error> { - let conn = self.conn()?; - - let mut rows = conn - .query( - "UPDATE billings - SET stripe_customer = ? - WHERE json_extract(stripe_customer, '$.id') = ? - RETURNING *", - vec![ - libsql::Value::Text(serde_json::to_string(customer)?), - libsql::Value::Text(customer.id.to_string()), - ], - ) - .await?; - - match rows.next().await? { - None => Ok(None), - Some(row) => { - let billing: Billing = libsql::de::from_row(&row).unwrap(); - Ok(Some(billing)) - } - } - } - - pub async fn update_stripe_subscription( - &self, - customer_id: impl Into, - subscription: Option<&stripe::Subscription>, - ) -> Result, crate::Error> { - let conn = self.conn()?; - - let mut rows = conn - .query( - "UPDATE billings - SET stripe_subscription = ? - WHERE json_extract(stripe_customer, '$.id') = ? - RETURNING *", - vec![ - subscription - .map(|s| libsql::Value::Text(serde_json::to_string(s).unwrap())) - .unwrap_or(libsql::Value::Null), - libsql::Value::Text(customer_id.into()), - ], - ) - .await?; - - match rows.next().await? { - None => Ok(None), - Some(row) => { - let billing: Billing = libsql::de::from_row(&row).unwrap(); - Ok(Some(billing)) - } - } - } -} - -#[cfg(test)] -mod tests { - use crate::tests::setup_db; - - #[tokio::test] - async fn test_update_stripe_customer() { - let db = setup_db().await; - - db.update_stripe_customer(&stripe::Customer::default()) - .await - .unwrap(); - } - - #[tokio::test] - async fn test_update_stripe_subscription() { - let db = setup_db().await; - - db.update_stripe_subscription("TODO", Some(&stripe::Subscription::default())) - .await - .unwrap(); - } -} diff --git a/crates/db-admin/src/billings_types.rs b/crates/db-admin/src/billings_types.rs deleted file mode 100644 index 86addb5028..0000000000 --- a/crates/db-admin/src/billings_types.rs +++ /dev/null @@ -1,29 +0,0 @@ -use crate::admin_common_derives; - -admin_common_derives! { - pub struct Billing { - pub id: String, - pub organization_id: String, - #[schemars(skip)] - pub stripe_subscription: Option, - #[schemars(skip)] - pub stripe_customer: Option, - } -} - -impl Billing { - pub fn from_row(row: &libsql::Row) -> Result { - Ok(Self { - id: row.get(0).expect("id"), - organization_id: row.get(1).expect("organization_id"), - stripe_subscription: row - .get_str(2) - .map(|s| serde_json::from_str(s).unwrap()) - .ok(), - stripe_customer: row - .get_str(3) - .map(|s| serde_json::from_str(s).unwrap()) - .ok(), - }) - } -} diff --git a/crates/db-admin/src/devices_migration.sql b/crates/db-admin/src/devices_migration.sql deleted file mode 100644 index e70ea93c69..0000000000 --- a/crates/db-admin/src/devices_migration.sql +++ /dev/null @@ -1,9 +0,0 @@ -CREATE TABLE IF NOT EXISTS devices ( - id TEXT PRIMARY KEY NOT NULL, - timestamp TEXT NOT NULL, - user_id TEXT NOT NULL, - fingerprint TEXT NOT NULL, - api_key TEXT NOT NULL, - UNIQUE(user_id, fingerprint), - FOREIGN KEY (user_id) REFERENCES users(id) -); diff --git a/crates/db-admin/src/devices_ops.rs b/crates/db-admin/src/devices_ops.rs deleted file mode 100644 index 580ceb3d52..0000000000 --- a/crates/db-admin/src/devices_ops.rs +++ /dev/null @@ -1,105 +0,0 @@ -use super::{AdminDatabase, Device}; - -impl AdminDatabase { - pub async fn list_devices(&self) -> Result, crate::Error> { - let conn = self.conn()?; - - let mut rows = conn.query("SELECT * FROM devices", ()).await.unwrap(); - let mut devices = Vec::new(); - - while let Some(row) = rows.next().await.unwrap() { - let device: Device = libsql::de::from_row(&row).unwrap(); - devices.push(device); - } - - Ok(devices) - } - - pub async fn upsert_device(&self, device: Device) -> Result { - let conn = self.conn()?; - - let mut rows = conn - .query( - "INSERT INTO devices ( - id, - timestamp, - user_id, - fingerprint, - api_key - ) VALUES (?, ?, ?, ?, ?) - ON CONFLICT (user_id, fingerprint) DO UPDATE SET - api_key = excluded.api_key - RETURNING *", - vec![ - device.id, - device.timestamp.to_rfc3339(), - device.user_id, - device.fingerprint, - device.api_key, - ], - ) - .await?; - - let row = rows.next().await?.unwrap(); - let device: Device = libsql::de::from_row(&row).unwrap(); - Ok(device) - } - - pub async fn delete_device_with_api_key( - &self, - api_key: impl AsRef, - ) -> Result<(), crate::Error> { - let conn = self.conn()?; - - conn.query( - "DELETE FROM devices WHERE api_key = ?", - vec![api_key.as_ref()], - ) - .await?; - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{Account, User, tests::setup_db}; - - #[tokio::test] - async fn test_devices() { - let db = setup_db().await; - - let account = db - .upsert_account(Account { - id: uuid::Uuid::new_v4().to_string(), - turso_db_name: "yujonglee".to_string(), - clerk_org_id: Some("org_1".to_string()), - }) - .await - .unwrap(); - - let user = db - .upsert_user(User { - id: uuid::Uuid::new_v4().to_string(), - account_id: account.id.clone(), - human_id: uuid::Uuid::new_v4().to_string(), - timestamp: chrono::Utc::now(), - clerk_user_id: "21".to_string(), - }) - .await - .unwrap(); - - let device = db - .upsert_device(Device { - id: uuid::Uuid::new_v4().to_string(), - timestamp: chrono::Utc::now(), - user_id: user.id.clone(), - fingerprint: "fingerprint".to_string(), - api_key: "key".to_string(), - }) - .await - .unwrap(); - - assert_eq!(device.user_id, user.id); - } -} diff --git a/crates/db-admin/src/devices_types.rs b/crates/db-admin/src/devices_types.rs deleted file mode 100644 index 1ac96f668b..0000000000 --- a/crates/db-admin/src/devices_types.rs +++ /dev/null @@ -1,11 +0,0 @@ -use crate::admin_common_derives; - -admin_common_derives! { - pub struct Device { - pub id: String, - pub user_id: String, - pub timestamp: chrono::DateTime, - pub fingerprint: String, - pub api_key: String, - } -} diff --git a/crates/db-admin/src/integrations_migration.sql b/crates/db-admin/src/integrations_migration.sql deleted file mode 100644 index 4e77e19806..0000000000 --- a/crates/db-admin/src/integrations_migration.sql +++ /dev/null @@ -1,8 +0,0 @@ -CREATE TABLE IF NOT EXISTS integrations ( - id TEXT PRIMARY KEY NOT NULL, - user_id TEXT NOT NULL, - nango_integration_id TEXT NOT NULL, - nango_connection_id TEXT NOT NULL, - UNIQUE (user_id, nango_integration_id), - FOREIGN KEY (user_id) REFERENCES users (id) -); diff --git a/crates/db-admin/src/integrations_ops.rs b/crates/db-admin/src/integrations_ops.rs deleted file mode 100644 index 602ddfa404..0000000000 --- a/crates/db-admin/src/integrations_ops.rs +++ /dev/null @@ -1,101 +0,0 @@ -use hypr_db_core::SqlTable; - -use super::{AdminDatabase, Integration}; - -impl AdminDatabase { - pub async fn list_integrations( - &self, - user_id: impl AsRef, - ) -> Result, crate::Error> { - let conn = self.conn()?; - - let sql = format!( - "SELECT * FROM {} WHERE user_id = ?", - Integration::sql_table() - ); - - let mut rows = conn.query(&sql, vec![user_id.as_ref()]).await?; - - let mut items = Vec::new(); - while let Some(row) = rows.next().await.unwrap() { - let item: Integration = libsql::de::from_row(&row).unwrap(); - items.push(item); - } - Ok(items) - } - - pub async fn upsert_integration( - &self, - integration: Integration, - ) -> Result { - let conn = self.conn()?; - - let sql = format!( - "INSERT INTO {} ( - id, - user_id, - nango_integration_id, - nango_connection_id - ) VALUES (?, ?, ?, ?) - ON CONFLICT (user_id, nango_integration_id) DO UPDATE SET - nango_connection_id = excluded.nango_connection_id - RETURNING *", - Integration::sql_table() - ); - - let params = vec![ - integration.id, - integration.user_id, - integration.nango_integration_id.into(), - integration.nango_connection_id, - ]; - - let mut rows = conn.query(&sql, params).await?; - let row = rows.next().await.unwrap().unwrap(); - let integration: Integration = libsql::de::from_row(&row).unwrap(); - Ok(integration) - } -} - -#[cfg(test)] -mod tests { - use crate::{Account, Integration, User, tests::setup_db}; - use hypr_nango::NangoIntegration; - - #[tokio::test] - async fn test_integrations() { - let db = setup_db().await; - - let account = db - .upsert_account(Account { - id: uuid::Uuid::new_v4().to_string(), - turso_db_name: "yujonglee".to_string(), - clerk_org_id: Some("org_1".to_string()), - }) - .await - .unwrap(); - - let user = db - .upsert_user(User { - id: uuid::Uuid::new_v4().to_string(), - account_id: account.id.clone(), - human_id: uuid::Uuid::new_v4().to_string(), - timestamp: chrono::Utc::now(), - clerk_user_id: "21".to_string(), - }) - .await - .unwrap(); - - let integration = db - .upsert_integration(Integration { - id: uuid::Uuid::new_v4().to_string(), - user_id: user.id.clone(), - nango_integration_id: NangoIntegration::GoogleCalendar, - nango_connection_id: uuid::Uuid::new_v4().to_string(), - }) - .await - .unwrap(); - - assert_eq!(integration.user_id, user.id); - } -} diff --git a/crates/db-admin/src/integrations_types.rs b/crates/db-admin/src/integrations_types.rs deleted file mode 100644 index 9df3c1e1c1..0000000000 --- a/crates/db-admin/src/integrations_types.rs +++ /dev/null @@ -1,11 +0,0 @@ -use crate::admin_common_derives; - -admin_common_derives! { - #[sql_table("integrations")] - pub struct Integration { - pub id: String, - pub user_id: String, - pub nango_integration_id: hypr_nango::NangoIntegration, - pub nango_connection_id: String, - } -} diff --git a/crates/db-admin/src/lib.rs b/crates/db-admin/src/lib.rs deleted file mode 100644 index 7da44c6674..0000000000 --- a/crates/db-admin/src/lib.rs +++ /dev/null @@ -1,115 +0,0 @@ -mod accounts_ops; -mod accounts_types; -mod billings_ops; -mod billings_types; -mod devices_ops; -mod devices_types; -mod integrations_ops; -mod integrations_types; -mod users_ops; -mod users_types; - -#[allow(unused)] -pub use accounts_ops::*; -#[allow(unused)] -pub use accounts_types::*; -#[allow(unused)] -pub use billings_ops::*; -#[allow(unused)] -pub use billings_types::*; -#[allow(unused)] -pub use devices_ops::*; -#[allow(unused)] -pub use devices_types::*; -#[allow(unused)] -pub use integrations_ops::*; -#[allow(unused)] -pub use integrations_types::*; -#[allow(unused)] -pub use users_ops::*; -#[allow(unused)] -pub use users_types::*; - -#[cfg(debug_assertions)] -mod seed; -#[cfg(debug_assertions)] -pub use seed::*; - -pub use hypr_db_core::Error; - -#[macro_export] -macro_rules! admin_common_derives { - (#[sql_table($table:expr)] $(#[$meta:meta])* $vis:vis $kind:ident $name:ident { - $($body:tt)* - }) => { - #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, schemars::JsonSchema)] - $(#[$meta])* $vis $kind $name { - $($body)* - } - - impl hypr_db_core::SqlTable for $name { - fn sql_table() -> &'static str { - $table - } - } - }; - - ($item:item) => { - #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, schemars::JsonSchema)] - $item - }; -} - -#[derive(Clone)] -pub struct AdminDatabase { - db: hypr_db_core::Database, -} - -impl AdminDatabase { - pub fn from(db: hypr_db_core::Database) -> Self { - Self { db } - } -} - -impl std::ops::Deref for AdminDatabase { - type Target = hypr_db_core::Database; - - fn deref(&self) -> &Self::Target { - &self.db - } -} - -// Append only. Do not reorder. -const MIGRATIONS: [&str; 5] = [ - include_str!("./billings_migration.sql"), - include_str!("./devices_migration.sql"), - include_str!("./integrations_migration.sql"), - include_str!("./accounts_migration.sql"), - include_str!("./users_migration.sql"), -]; - -pub async fn migrate(db: &AdminDatabase) -> Result<(), crate::Error> { - let conn = db.conn()?; - hypr_db_core::migrate(&conn, MIGRATIONS.to_vec()).await?; - Ok(()) -} - -#[cfg(test)] -mod tests { - use super::AdminDatabase; - use crate::{migrate, seed}; - use hypr_db_core::DatabaseBuilder; - - pub async fn setup_db() -> AdminDatabase { - let base_db = DatabaseBuilder::default().memory().build().await.unwrap(); - let admin_db = AdminDatabase::from(base_db); - migrate(&admin_db).await.unwrap(); - admin_db - } - - #[tokio::test] - async fn test_seed() { - let db = setup_db().await; - seed(&db).await.unwrap(); - } -} diff --git a/crates/db-admin/src/seed.rs b/crates/db-admin/src/seed.rs deleted file mode 100644 index 86ba36b69a..0000000000 --- a/crates/db-admin/src/seed.rs +++ /dev/null @@ -1,30 +0,0 @@ -use super::{Account, AdminDatabase, Device, User}; - -pub async fn seed(db: &AdminDatabase) -> Result<(), crate::Error> { - let account = Account { - id: uuid::Uuid::new_v4().to_string(), - turso_db_name: "yujonglee".to_string(), - clerk_org_id: Some("org_1".to_string()), - }; - - let user = User { - id: uuid::Uuid::new_v4().to_string(), - account_id: account.id.clone(), - human_id: uuid::Uuid::new_v4().to_string(), - timestamp: chrono::Utc::now(), - clerk_user_id: "user_1".to_string(), - }; - - let device = Device { - id: uuid::Uuid::new_v4().to_string(), - timestamp: chrono::Utc::now(), - user_id: user.id.clone(), - api_key: "123".to_string(), - fingerprint: "TODO".to_string(), - }; - - db.upsert_account(account).await?; - db.upsert_user(user).await?; - db.upsert_device(device).await?; - Ok(()) -} diff --git a/crates/db-admin/src/users_migration.sql b/crates/db-admin/src/users_migration.sql deleted file mode 100644 index a7688628c0..0000000000 --- a/crates/db-admin/src/users_migration.sql +++ /dev/null @@ -1,8 +0,0 @@ -CREATE TABLE IF NOT EXISTS users ( - id TEXT PRIMARY KEY NOT NULL, - account_id TEXT NOT NULL, - human_id TEXT NOT NULL, - timestamp TEXT NOT NULL, - clerk_user_id TEXT NOT NULL UNIQUE, - FOREIGN KEY (account_id) REFERENCES accounts(id) -); diff --git a/crates/db-admin/src/users_ops.rs b/crates/db-admin/src/users_ops.rs deleted file mode 100644 index ce6362111b..0000000000 --- a/crates/db-admin/src/users_ops.rs +++ /dev/null @@ -1,219 +0,0 @@ -use super::{AdminDatabase, User}; - -impl AdminDatabase { - pub async fn list_users(&self) -> Result, crate::Error> { - let conn = self.conn()?; - - let mut rows = conn.query("SELECT * FROM users", ()).await.unwrap(); - let mut users = Vec::new(); - - while let Some(row) = rows.next().await.unwrap() { - let user: User = libsql::de::from_row(&row).unwrap(); - users.push(user); - } - - Ok(users) - } - - pub async fn upsert_user(&self, user: User) -> Result { - let conn = self.conn()?; - - let mut rows = conn - .query( - "INSERT INTO users ( - id, - account_id, - human_id, - timestamp, - clerk_user_id - ) VALUES (?, ?, ?, ?, ?) - ON CONFLICT (id) DO UPDATE SET - account_id = excluded.account_id, - human_id = excluded.human_id, - timestamp = excluded.timestamp, - clerk_user_id = excluded.clerk_user_id - RETURNING *", - vec![ - user.id, - user.account_id, - user.human_id, - user.timestamp.to_rfc3339(), - user.clerk_user_id, - ], - ) - .await?; - - let row = rows.next().await.unwrap().unwrap(); - let user: User = libsql::de::from_row(&row).unwrap(); - Ok(user) - } - - pub async fn get_user_by_clerk_user_id( - &self, - clerk_user_id: impl AsRef, - ) -> Result, crate::Error> { - let conn = self.conn()?; - - let mut rows = conn - .query( - "SELECT * FROM users WHERE clerk_user_id = ?", - vec![clerk_user_id.as_ref()], - ) - .await?; - - match rows.next().await.unwrap() { - None => Ok(None), - Some(row) => { - let user: User = libsql::de::from_row(&row).unwrap(); - Ok(Some(user)) - } - } - } - - pub async fn get_user_by_device_api_key( - &self, - api_key: impl AsRef, - ) -> Result, crate::Error> { - let conn = self.conn()?; - - let mut rows = conn - .query( - "SELECT users.* FROM users - JOIN devices ON devices.user_id = users.id - WHERE devices.api_key = ?", - vec![api_key.as_ref()], - ) - .await?; - - match rows.next().await.unwrap() { - None => Ok(None), - Some(row) => { - let user: User = libsql::de::from_row(&row).unwrap(); - Ok(Some(user)) - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{Account, Device, tests::setup_db}; - - #[tokio::test] - async fn test_create_list_get_user() { - let db = setup_db().await; - - let account = db - .upsert_account(Account { - id: uuid::Uuid::new_v4().to_string(), - turso_db_name: "yujonglee".to_string(), - clerk_org_id: Some("org_1".to_string()), - }) - .await - .unwrap(); - - let user = db - .upsert_user(User { - id: uuid::Uuid::new_v4().to_string(), - account_id: account.id.clone(), - human_id: uuid::Uuid::new_v4().to_string(), - timestamp: chrono::Utc::now(), - clerk_user_id: "21".to_string(), - }) - .await - .unwrap(); - assert_eq!(user.clerk_user_id, "21".to_string()); - - let users = db.list_users().await.unwrap(); - assert_eq!(users.len(), 1); - - let _user = db - .get_user_by_clerk_user_id("21".to_string()) - .await - .unwrap() - .unwrap(); - } - - #[tokio::test] - async fn test_create_list_get_device() { - let db = setup_db().await; - - let account = db - .upsert_account(Account { - id: uuid::Uuid::new_v4().to_string(), - turso_db_name: "yujonglee".to_string(), - clerk_org_id: Some("org_1".to_string()), - }) - .await - .unwrap(); - - let user = db - .upsert_user(User { - id: uuid::Uuid::new_v4().to_string(), - account_id: account.id.clone(), - human_id: uuid::Uuid::new_v4().to_string(), - timestamp: chrono::Utc::now(), - clerk_user_id: "21".to_string(), - }) - .await - .unwrap(); - - let device = db - .upsert_device(Device { - id: uuid::Uuid::new_v4().to_string(), - timestamp: chrono::Utc::now(), - user_id: user.id.clone(), - fingerprint: "fingerprint".to_string(), - api_key: "key".to_string(), - }) - .await - .unwrap(); - - assert_eq!(device.user_id, user.id); - } - - #[tokio::test] - async fn test_get_user_by_device_api_key() { - let db = setup_db().await; - - let account = db - .upsert_account(Account { - id: uuid::Uuid::new_v4().to_string(), - turso_db_name: "yujonglee".to_string(), - clerk_org_id: Some("org_1".to_string()), - }) - .await - .unwrap(); - - let user_1 = db - .upsert_user(User { - id: uuid::Uuid::new_v4().to_string(), - account_id: account.id.clone(), - human_id: uuid::Uuid::new_v4().to_string(), - timestamp: chrono::Utc::now(), - clerk_user_id: "21".to_string(), - }) - .await - .unwrap(); - - let device = db - .upsert_device(Device { - id: uuid::Uuid::new_v4().to_string(), - timestamp: chrono::Utc::now(), - user_id: user_1.id.clone(), - fingerprint: "fingerprint".to_string(), - api_key: "key".to_string(), - }) - .await - .unwrap(); - - let user_2 = db - .get_user_by_device_api_key(device.api_key) - .await - .unwrap() - .unwrap(); - - assert_eq!(user_1.id, user_2.id); - } -} diff --git a/crates/db-admin/src/users_types.rs b/crates/db-admin/src/users_types.rs deleted file mode 100644 index cbe4cacbd7..0000000000 --- a/crates/db-admin/src/users_types.rs +++ /dev/null @@ -1,11 +0,0 @@ -use crate::admin_common_derives; - -admin_common_derives! { - pub struct User { - pub id: String, - pub account_id: String, - pub human_id: String, - pub timestamp: chrono::DateTime, - pub clerk_user_id: String, - } -}