From 49dbcb061460f9e1a5b6cfc204e56d96b91a8e3b Mon Sep 17 00:00:00 2001 From: Luc Date: Sun, 1 Dec 2024 13:36:29 +0100 Subject: [PATCH] Repo cleanup --- README.md | 22 ++++++ engine/src/models/field/definition.rs | 31 +++++++++ engine/src/models/field/kind.rs | 43 ++++++++++++ engine/src/models/field/mod.rs | 7 ++ engine/src/models/field_definitions.rs | 64 ----------------- engine/src/models/item/field.rs | 31 +++++++++ engine/src/models/item/media.rs | 25 +++++++ engine/src/models/{items.rs => item/mod.rs} | 68 +++++-------------- .../src/models/{locations.rs => location.rs} | 10 +-- engine/src/models/media.rs | 11 +-- engine/src/models/mod.rs | 6 +- engine/src/models/products.rs | 11 +-- engine/src/models/sessions.rs | 12 ++-- engine/src/models/tags.rs | 11 +-- engine/src/models/users.rs | 21 +++--- engine/src/routes/items/mod.rs | 2 +- engine/src/routes/root.rs | 2 +- 17 files changed, 220 insertions(+), 157 deletions(-) create mode 100644 engine/src/models/field/definition.rs create mode 100644 engine/src/models/field/kind.rs create mode 100644 engine/src/models/field/mod.rs delete mode 100644 engine/src/models/field_definitions.rs create mode 100644 engine/src/models/item/field.rs create mode 100644 engine/src/models/item/media.rs rename engine/src/models/{items.rs => item/mod.rs} (63%) rename engine/src/models/{locations.rs => location.rs} (66%) diff --git a/README.md b/README.md index 1b7a375..1ad4776 100644 --- a/README.md +++ b/README.md @@ -54,3 +54,25 @@ An example of a tracked item could be: "modified": "2023-06-01T00:00:00Z" } ``` + +## Developing Locally + +This project consists of two parts: + +- `engine`: A Rust application that runs the core logic and API. +- `web`: A Vite React (TS) application that runs the web interface. + +To get started locally you need to have `docker` and `docker-compose` installed. + +``` +# Start Engine +cd engine +docker compose up -d # start the database +cargo sqlx migrate run # only required for development +cargo run # start the engine + +# Start Web +cd web +pnpm install +pnpm dev +``` diff --git a/engine/src/models/field/definition.rs b/engine/src/models/field/definition.rs new file mode 100644 index 0000000..2afcbc3 --- /dev/null +++ b/engine/src/models/field/definition.rs @@ -0,0 +1,31 @@ +use crate::database::Database; +use crate::models::field::kind::FieldKind; +use serde::{Deserialize, Serialize}; +use poem_openapi::Object; +use sqlx::FromRow; + +#[derive(FromRow, Object, Debug, Clone, Serialize, Deserialize)] +pub struct FieldDefinition { + pub definition_id: String, + pub kind: FieldKind, + pub name: String, + pub created_at: Option>, + pub updated_at: Option>, +} + +impl FieldDefinition { + pub async fn create( + kind: FieldKind, + name: String, + database: &Database, + ) -> Result { + sqlx::query_as!( + FieldDefinition, + "INSERT INTO field_definitions (kind, name) VALUES ($1, $2) RETURNING *", + kind.to_string(), + name + ) + .fetch_one(&database.pool) + .await + } +} diff --git a/engine/src/models/field/kind.rs b/engine/src/models/field/kind.rs new file mode 100644 index 0000000..9aeca4c --- /dev/null +++ b/engine/src/models/field/kind.rs @@ -0,0 +1,43 @@ +use serde::{Deserialize, Serialize}; +use sqlx::Type; +use poem_openapi::Enum; + +#[derive(Type, Enum, Debug, Clone, Serialize, Deserialize)] +#[sqlx(type_name = "text")] +#[sqlx(rename_all = "lowercase")] +pub enum FieldKind { + String, + Number, + Boolean, + Json, +} + +impl std::fmt::Display for FieldKind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +impl FieldKind { + pub fn from_str(s: &str) -> Self { + match s.to_lowercase().as_str() { + "string" => FieldKind::String, + "number" => FieldKind::Number, + "boolean" => FieldKind::Boolean, + "json" => FieldKind::Json, + _ => FieldKind::String, + } + } +} + +impl From for FieldKind { + fn from(s: String) -> Self { + FieldKind::from_str(&s) + } +} + +impl From for String { + fn from(kind: FieldKind) -> Self { + kind.to_string().to_lowercase() + } +} diff --git a/engine/src/models/field/mod.rs b/engine/src/models/field/mod.rs new file mode 100644 index 0000000..974c1c7 --- /dev/null +++ b/engine/src/models/field/mod.rs @@ -0,0 +1,7 @@ +use serde::{Deserialize, Serialize}; +use sqlx::prelude::*; + +use crate::database::Database; + +pub mod kind; +pub mod definition; diff --git a/engine/src/models/field_definitions.rs b/engine/src/models/field_definitions.rs deleted file mode 100644 index abee0a8..0000000 --- a/engine/src/models/field_definitions.rs +++ /dev/null @@ -1,64 +0,0 @@ -use serde::{Deserialize, Serialize}; -use sqlx::prelude::*; - -use crate::database::Database; - -#[derive(sqlx::Type, poem_openapi::Enum, Debug, Clone, Serialize, Deserialize)] -#[sqlx(type_name = "text")] -#[sqlx(rename_all = "lowercase")] -pub enum FieldKind { - String, - Number, - Boolean, - Json, -} - -impl std::fmt::Display for FieldKind { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.to_string()) - } -} - -impl From for FieldKind { - fn from(s: String) -> Self { - match s.to_lowercase().as_str() { - "string" => FieldKind::String, - "number" => FieldKind::Number, - "boolean" => FieldKind::Boolean, - "json" => FieldKind::Json, - _ => FieldKind::String, - } - } -} - -impl From for String { - fn from(kind: FieldKind) -> Self { - kind.to_string().to_lowercase() - } -} - -#[derive(sqlx::FromRow, poem_openapi::Object, Debug, Clone, Serialize, Deserialize)] -pub struct FieldDefinition { - pub definition_id: String, - pub kind: FieldKind, - pub name: String, - pub created_at: Option>, - pub updated_at: Option>, -} - -impl FieldDefinition { - pub async fn create( - kind: FieldKind, - name: String, - database: &Database, - ) -> Result { - sqlx::query_as!( - FieldDefinition, - "INSERT INTO field_definitions (kind, name) VALUES ($1, $2) RETURNING *", - kind.to_string(), - name - ) - .fetch_one(&database.pool) - .await - } -} diff --git a/engine/src/models/item/field.rs b/engine/src/models/item/field.rs new file mode 100644 index 0000000..76857eb --- /dev/null +++ b/engine/src/models/item/field.rs @@ -0,0 +1,31 @@ + +use crate::database::Database; +use serde::{Deserialize, Serialize}; + +#[derive(sqlx::FromRow, poem_openapi::Object, Debug, Clone, Serialize, Deserialize)] +pub struct ItemField { + pub item_id: String, + pub definition_id: String, + pub value: serde_json::Value, + pub created_at: Option>, + pub updated_at: Option>, +} + +impl ItemField { + pub async fn create( + item_id: String, + definition_id: String, + value: serde_json::Value, + database: &Database, + ) -> Result { + sqlx::query_as!( + ItemField, + "INSERT INTO item_fields (item_id, definition_id, value) VALUES ($1, $2, $3) RETURNING *", + item_id, + definition_id, + value + ) + .fetch_one(&database.pool) + .await + } +} diff --git a/engine/src/models/item/media.rs b/engine/src/models/item/media.rs new file mode 100644 index 0000000..433e31d --- /dev/null +++ b/engine/src/models/item/media.rs @@ -0,0 +1,25 @@ +use crate::database::Database; +use serde::{Deserialize, Serialize}; + +#[derive(sqlx::FromRow, poem_openapi::Object, Debug, Clone, Serialize, Deserialize)] +pub struct ItemMedia { + pub item_id: String, + pub media_id: i32, +} + +impl ItemMedia { + pub async fn create( + item_id: String, + media_id: i32, + database: &Database, + ) -> Result { + sqlx::query_as!( + ItemMedia, + "INSERT INTO item_media (item_id, media_id) VALUES ($1, $2) RETURNING *", + item_id, + media_id + ) + .fetch_one(&database.pool) + .await + } +} diff --git a/engine/src/models/items.rs b/engine/src/models/item/mod.rs similarity index 63% rename from engine/src/models/items.rs rename to engine/src/models/item/mod.rs index 73bcdfb..eef3c3e 100644 --- a/engine/src/models/items.rs +++ b/engine/src/models/item/mod.rs @@ -4,6 +4,9 @@ use tracing::info; use crate::database::Database; +pub mod field; +pub mod media; + #[derive(sqlx::FromRow, poem_openapi::Object, Debug, Clone, Serialize, Deserialize)] pub struct Item { pub item_id: String, @@ -15,6 +18,20 @@ pub struct Item { pub updated_at: Option>, } +impl Default for Item { + fn default() -> Self { + Item { + item_id: "".to_string(), + name: None, + product_id: None, + owner_id: None, + location_id: None, + created_at: Some(chrono::Utc::now()), + updated_at: Some(chrono::Utc::now()), + } + } +} + impl Item { pub async fn create( item_id: String, @@ -73,54 +90,3 @@ impl Item { } } } - -#[derive(sqlx::FromRow, poem_openapi::Object, Debug, Clone, Serialize, Deserialize)] -pub struct ItemField { - pub item_id: String, - pub definition_id: String, - pub value: serde_json::Value, - pub created_at: Option>, - pub updated_at: Option>, -} - -impl ItemField { - pub async fn create( - item_id: String, - definition_id: String, - value: serde_json::Value, - database: &Database, - ) -> Result { - sqlx::query_as!( - ItemField, - "INSERT INTO item_fields (item_id, definition_id, value) VALUES ($1, $2, $3) RETURNING *", - item_id, - definition_id, - value - ) - .fetch_one(&database.pool) - .await - } -} - -#[derive(sqlx::FromRow, poem_openapi::Object, Debug, Clone, Serialize, Deserialize)] -pub struct ItemMedia { - pub item_id: String, - pub media_id: i32, -} - -impl ItemMedia { - pub async fn create( - item_id: String, - media_id: i32, - database: &Database, - ) -> Result { - sqlx::query_as!( - ItemMedia, - "INSERT INTO item_media (item_id, media_id) VALUES ($1, $2) RETURNING *", - item_id, - media_id - ) - .fetch_one(&database.pool) - .await - } -} diff --git a/engine/src/models/locations.rs b/engine/src/models/location.rs similarity index 66% rename from engine/src/models/locations.rs rename to engine/src/models/location.rs index ea742b7..a12cc7d 100644 --- a/engine/src/models/locations.rs +++ b/engine/src/models/location.rs @@ -1,14 +1,16 @@ use serde::{Deserialize, Serialize}; -use sqlx::prelude::*; +use chrono::{DateTime, Utc}; +use poem_openapi::Object; +use sqlx::FromRow; use crate::database::Database; -#[derive(sqlx::FromRow, poem_openapi::Object, Debug, Clone, Serialize, Deserialize)] +#[derive(FromRow, Object, Debug, Clone, Serialize, Deserialize)] pub struct Location { pub location_id: i32, pub name: String, - pub created_at: Option>, - pub updated_at: Option>, + pub created_at: Option>, + pub updated_at: Option>, } impl Location { diff --git a/engine/src/models/media.rs b/engine/src/models/media.rs index ff552b9..0d41660 100644 --- a/engine/src/models/media.rs +++ b/engine/src/models/media.rs @@ -1,16 +1,17 @@ use serde::{Deserialize, Serialize}; -use sqlx::prelude::*; - +use sqlx::FromRow; +use poem_openapi::Object; +use chrono::{DateTime, Utc}; use crate::database::Database; -#[derive(sqlx::FromRow, poem_openapi::Object, Debug, Clone, Serialize, Deserialize)] +#[derive(FromRow, Object, Debug, Clone, Serialize, Deserialize)] pub struct Media { pub media_id: i32, pub description: Option, pub url: String, pub kind: String, - pub created_at: Option>, - pub updated_at: Option>, + pub created_at: Option>, + pub updated_at: Option>, } impl Media { diff --git a/engine/src/models/mod.rs b/engine/src/models/mod.rs index 7ef7224..1357746 100644 --- a/engine/src/models/mod.rs +++ b/engine/src/models/mod.rs @@ -1,6 +1,6 @@ -pub mod field_definitions; -pub mod items; -pub mod locations; +pub mod field; +pub mod item; +pub mod location; pub mod media; pub mod products; pub mod sessions; diff --git a/engine/src/models/products.rs b/engine/src/models/products.rs index 003ab15..25e333d 100644 --- a/engine/src/models/products.rs +++ b/engine/src/models/products.rs @@ -1,14 +1,15 @@ use serde::{Deserialize, Serialize}; -use sqlx::prelude::*; - +use sqlx::FromRow; +use poem_openapi::Object; +use chrono::{DateTime, Utc}; use crate::database::Database; -#[derive(sqlx::FromRow, poem_openapi::Object, Debug, Clone, Serialize, Deserialize)] +#[derive(FromRow, Object, Debug, Clone, Serialize, Deserialize)] pub struct Product { pub product_id: i32, pub name: String, - pub created_at: Option>, - pub updated_at: Option>, + pub created_at: Option>, + pub updated_at: Option>, } impl Product { diff --git a/engine/src/models/sessions.rs b/engine/src/models/sessions.rs index daa4984..7b2ad37 100644 --- a/engine/src/models/sessions.rs +++ b/engine/src/models/sessions.rs @@ -1,9 +1,7 @@ use poem_openapi::Object; use serde::{Deserialize, Serialize}; -use sqlx::types::{ - chrono, - ipnetwork::{IpNetwork, Ipv4Network}, -}; +use sqlx::types::ipnetwork::IpNetwork; +use chrono::{DateTime, Utc}; use crate::database::Database; @@ -14,9 +12,9 @@ pub struct Session { pub user_agent: String, pub user_ip: String, pub valid: bool, - pub last_access: chrono::DateTime, - pub created_at: Option>, - pub updated_at: Option>, + pub last_access: DateTime, + pub created_at: Option>, + pub updated_at: Option>, } impl Session { diff --git a/engine/src/models/tags.rs b/engine/src/models/tags.rs index 1269010..18551d4 100644 --- a/engine/src/models/tags.rs +++ b/engine/src/models/tags.rs @@ -1,14 +1,15 @@ use serde::{Deserialize, Serialize}; -use sqlx::prelude::*; - +use sqlx::FromRow; +use chrono::{DateTime, Utc}; +use poem_openapi::Object; use crate::database::Database; -#[derive(sqlx::FromRow, poem_openapi::Object, Debug, Clone, Serialize, Deserialize)] +#[derive(FromRow, Object, Debug, Clone, Serialize, Deserialize)] pub struct Tag { pub tag_id: i32, pub name: String, - pub created_at: Option>, - pub updated_at: Option>, + pub created_at: Option>, + pub updated_at: Option>, } impl Tag { diff --git a/engine/src/models/users.rs b/engine/src/models/users.rs index 7a8e57a..76bf593 100644 --- a/engine/src/models/users.rs +++ b/engine/src/models/users.rs @@ -1,24 +1,20 @@ use openid::Userinfo; use serde::{Deserialize, Serialize}; -use serde_json::Value; use sqlx::{types::Json, FromRow}; use url::Url; - +use chrono::{DateTime, Utc}; +use poem_openapi::Object; use crate::database::Database; -#[derive(Deserialize, Serialize, Clone, Debug)] -struct Person { - name: String, -} - -#[derive(sqlx::FromRow, Debug, Clone, Serialize, Deserialize)] +/// A user object that is stored in the database +#[derive(FromRow, Debug, Clone, Serialize, Deserialize)] pub struct UserEntry { pub user_id: i32, pub oauth_sub: String, pub oauth_data: Json, pub nickname: Option, - pub created_at: Option>, - pub updated_at: Option>, + pub created_at: Option>, + pub updated_at: Option>, } impl UserEntry { @@ -73,7 +69,10 @@ impl UserEntry { } } -#[derive(Debug, Clone, Serialize, Deserialize, poem_openapi::Object)] +/// A user object that is returned to the client +/// This is a subset of the `UserEntry` struct +/// Use `UserEntry` to query the database for a user +#[derive(Debug, Clone, Serialize, Deserialize, Object)] pub struct User { pub id: i32, pub oauth_sub: String, diff --git a/engine/src/routes/items/mod.rs b/engine/src/routes/items/mod.rs index 5b14539..237051d 100644 --- a/engine/src/routes/items/mod.rs +++ b/engine/src/routes/items/mod.rs @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize}; use tracing::info; use crate::{ - auth::middleware::AuthToken, models::items::Item, state::AppState + auth::middleware::AuthToken, models::item::Item, state::AppState }; pub struct ItemsApi; diff --git a/engine/src/routes/root.rs b/engine/src/routes/root.rs index c213fb0..d58b819 100644 --- a/engine/src/routes/root.rs +++ b/engine/src/routes/root.rs @@ -6,7 +6,7 @@ use poem::{ use poem_openapi::{param::Query, payload::{Json, PlainText}, OpenApi}; use crate::{ - models::{items::Item, media::Media, products::Product}, + models::{item::Item, media::Media, products::Product}, state::AppState, };