From ff86e9929c0fe6c3fe0dcdce6b2a39a987cdadcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Wo=C5=BAniak?= Date: Fri, 6 May 2022 16:02:38 +0200 Subject: [PATCH] Add seed and photos --- .env | 3 + .gitignore | 2 + Cargo.lock | 101 +++ Cargo.toml | 1 + actors/database_manager/Cargo.toml | 6 + actors/database_manager/src/account_orders.rs | 2 +- actors/database_manager/src/accounts.rs | 8 +- actors/database_manager/src/lib.rs | 29 +- actors/database_manager/src/order_items.rs | 5 +- actors/database_manager/src/photos.rs | 37 + actors/database_manager/src/product_photos.rs | 2 + actors/database_manager/src/products.rs | 4 +- .../src/shopping_cart_items.rs | 2 +- actors/database_manager/src/shopping_carts.rs | 2 +- actors/database_manager/src/stocks.rs | 2 +- actors/database_manager/src/tokens.rs | 2 +- actors/fs_manager/src/lib.rs | 28 +- api/Cargo.toml | 1 + api/src/main.rs | 4 + api/src/model.rs | 646 ------------------ api/src/model/api.rs | 126 ---- api/src/routes/admin/api_v1/uploads.rs | 107 ++- db-seed/Cargo.toml | 21 + db-seed/src/main.rs | 37 + .../20220506130226_rename_photos_column.sql | 2 + shared/model/Cargo.toml | 4 + shared/model/src/lib.rs | 81 +++ 27 files changed, 466 insertions(+), 799 deletions(-) create mode 100644 actors/database_manager/src/photos.rs create mode 100644 actors/database_manager/src/product_photos.rs delete mode 100644 api/src/model.rs delete mode 100644 api/src/model/api.rs create mode 100644 db-seed/Cargo.toml create mode 100644 db-seed/src/main.rs create mode 100644 migrations/20220506130226_rename_photos_column.sql diff --git a/.env b/.env index dda2ee0..86a9808 100644 --- a/.env +++ b/.env @@ -14,3 +14,6 @@ PAYU_CLIENT_SECRET="12f071174cb7eb79d4aac5bc2f07563f" PAYU_CLIENT_MERCHANT_ID=300746 WEB_HOST=https://bazzar.ita-prog.pl + +FILES_PUBLIC_PATH=/files +FILES_LOCAL_PATH=./tmp diff --git a/.gitignore b/.gitignore index 1adc981..1ddbce3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ /target bazzar.toml +/tmp +/uploads diff --git a/Cargo.lock b/Cargo.lock index 267cc9e..a487c73 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -616,6 +616,7 @@ dependencies = [ "derive_more", "dotenv", "email_manager", + "fs_manager", "futures", "futures-util", "gumdrop", @@ -1034,6 +1035,41 @@ dependencies = [ "cipher", ] +[[package]] +name = "darling" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" +dependencies = [ + "darling_core", + "quote", + "syn", +] + [[package]] name = "dashmap" version = "4.0.2" @@ -1058,15 +1094,34 @@ dependencies = [ "actix-rt", "chrono", "config", + "fake", "log", "model", "pretty_env_logger", + "rand", "sqlx", "sqlx-core", "thiserror", "uuid", ] +[[package]] +name = "db-seed" +version = "0.1.0" +dependencies = [ + "actix 0.13.0", + "actix-rt", + "actix-web", + "config", + "database_manager", + "dotenv", + "fake", + "log", + "model", + "pretty_env_logger", + "rand", +] + [[package]] name = "dbg" version = "1.0.4" @@ -1147,6 +1202,17 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" +[[package]] +name = "dummy" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf5ff6f74150b0bbb6e6057718a903b3d8f3fc7096c9190fc162ca99d3b2273" +dependencies = [ + "darling", + "quote", + "syn", +] + [[package]] name = "either" version = "1.6.1" @@ -1212,6 +1278,19 @@ version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71" +[[package]] +name = "fake" +version = "2.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21a8531dd3a64fd1cfbe92fad4160bc2060489c6195fe847e045e5788f710bae" +dependencies = [ + "chrono", + "dummy", + "http", + "rand", + "uuid", +] + [[package]] name = "fake-simd" version = "0.1.2" @@ -1737,6 +1816,12 @@ dependencies = [ "tokio-native-tls", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "0.2.3" @@ -1987,6 +2072,12 @@ dependencies = [ "digest 0.10.3", ] +[[package]] +name = "md5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" + [[package]] name = "memchr" version = "2.4.1" @@ -2095,6 +2186,8 @@ version = "0.1.0" dependencies = [ "chrono", "derive_more", + "fake", + "rand", "serde", "sqlx", "sqlx-core", @@ -3376,6 +3469,12 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "subtle" version = "2.4.1" @@ -3922,7 +4021,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" dependencies = [ "getrandom", + "md5", "serde", + "sha1", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 1fcde3d..6ac2308 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,4 +11,5 @@ members = [ "actors/search_manager", "actors/token_manager", "actors/fs_manager", + "db-seed" ] diff --git a/actors/database_manager/Cargo.toml b/actors/database_manager/Cargo.toml index 3aeaa9c..f7dfd50 100644 --- a/actors/database_manager/Cargo.toml +++ b/actors/database_manager/Cargo.toml @@ -3,6 +3,9 @@ name = "database_manager" version = "0.1.0" edition = "2021" +[features] +dummy = ["fake", "rand"] + [dependencies] model = { path = "../../shared/model" } config = { path = "../../shared/config" } @@ -20,3 +23,6 @@ chrono = { version = "0.4", features = ["serde"] } log = { version = "0.4", features = [] } pretty_env_logger = { version = "0.4", features = [] } + +fake = { version = "2.4.3", features = ["derive", "chrono", "http", "uuid"], optional = true } +rand = { version = "0.8.5", optional = true } diff --git a/actors/database_manager/src/account_orders.rs b/actors/database_manager/src/account_orders.rs index c58f4dc..1cbe99f 100644 --- a/actors/database_manager/src/account_orders.rs +++ b/actors/database_manager/src/account_orders.rs @@ -3,7 +3,7 @@ use sqlx::PgPool; use super::Result; use crate::{ - create_order_item, db_async_handler, shopping_cart_set_state, CreateOrderItem, Database, + create_order_item, db_async_handler, shopping_cart_set_state, CreateOrderItem, ShoppingCartSetState, }; diff --git a/actors/database_manager/src/accounts.rs b/actors/database_manager/src/accounts.rs index 84c322a..c3377bd 100644 --- a/actors/database_manager/src/accounts.rs +++ b/actors/database_manager/src/accounts.rs @@ -1,8 +1,10 @@ +#[cfg(feature = "dummy")] +use fake::Fake; use model::{AccountId, AccountState, Email, FullAccount, Login, PassHash, Role}; use sqlx::PgPool; use super::Result; -use crate::{db_async_handler, Database}; +use crate::db_async_handler; #[derive(Debug, thiserror::Error)] pub enum Error { @@ -37,10 +39,13 @@ FROM accounts }) } +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[derive(actix::Message)] #[rtype(result = "Result")] pub struct CreateAccount { + #[cfg_attr(feature = "dummy", dummy("fake::faker::internet::en::FreeEmail"))] pub email: Email, + #[cfg_attr(feature = "dummy", dummy("fake::faker::internet::en::Username"))] pub login: Login, pub pass_hash: PassHash, pub role: Role, @@ -68,6 +73,7 @@ RETURNING id, email, login, pass_hash, role, customer_id, state }) } +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[derive(actix::Message)] #[rtype(result = "Result")] pub struct UpdateAccount { diff --git a/actors/database_manager/src/lib.rs b/actors/database_manager/src/lib.rs index 3f8b0a0..f8a4997 100644 --- a/actors/database_manager/src/lib.rs +++ b/actors/database_manager/src/lib.rs @@ -1,18 +1,23 @@ -pub use account_orders::*; -pub use accounts::*; use actix::{Actor, Context}; use config::SharedAppConfig; -pub use order_items::*; -pub use products::*; -pub use shopping_cart_items::*; -pub use shopping_carts::*; use sqlx::PgPool; -pub use stocks::*; -pub use tokens::*; + +pub use crate::account_orders::*; +pub use crate::accounts::*; +pub use crate::order_items::*; +pub use crate::photos::*; +pub use crate::product_photos::*; +pub use crate::products::*; +pub use crate::shopping_cart_items::*; +pub use crate::shopping_carts::*; +pub use crate::stocks::*; +pub use crate::tokens::*; pub mod account_orders; pub mod accounts; pub mod order_items; +pub mod photos; +pub mod product_photos; pub mod products; pub mod shopping_cart_items; pub mod shopping_carts; @@ -22,7 +27,7 @@ pub mod tokens; #[macro_export] macro_rules! db_async_handler { ($msg: ty, $async: ident, $res: ty) => { - impl actix::Handler<$msg> for Database { + impl actix::Handler<$msg> for crate::Database { type Result = actix::ResponseActFuture>; fn handle(&mut self, msg: $msg, _ctx: &mut Self::Context) -> Self::Result { @@ -47,7 +52,7 @@ macro_rules! db_async_handler { } } - impl actix::Handler<$msg> for Database { + impl actix::Handler<$msg> for crate::Database { type Result = actix::ResponseActFuture>; fn handle(&mut self, msg: $msg, _ctx: &mut Self::Context) -> Self::Result { @@ -114,6 +119,10 @@ pub enum Error { ShoppingCartItem(#[from] shopping_cart_items::Error), #[error("{0}")] Token(#[from] tokens::Error), + #[error("{0}")] + Photo(#[from] photos::Error), + #[error("{0}")] + ProductPhoto(#[from] product_photos::Error), } pub type Result = std::result::Result; diff --git a/actors/database_manager/src/order_items.rs b/actors/database_manager/src/order_items.rs index 39d8946..3f49db0 100644 --- a/actors/database_manager/src/order_items.rs +++ b/actors/database_manager/src/order_items.rs @@ -1,8 +1,10 @@ +#[cfg(feature = "dummy")] +use fake::Fake; use model::*; use sqlx::PgPool; use super::Result; -use crate::{db_async_handler, Database}; +use crate::db_async_handler; #[derive(Debug, thiserror::Error)] pub enum Error { @@ -40,6 +42,7 @@ ORDER BY id DESC }) } +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[derive(actix::Message)] #[rtype(result = "Result")] pub struct CreateOrderItem { diff --git a/actors/database_manager/src/photos.rs b/actors/database_manager/src/photos.rs new file mode 100644 index 0000000..6d11c21 --- /dev/null +++ b/actors/database_manager/src/photos.rs @@ -0,0 +1,37 @@ +use crate::Result; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("Failed to create photo")] + Create, +} + +#[derive(actix::Message)] +#[rtype(result = "Result")] +pub struct CreatePhoto { + pub local_path: model::LocalPath, + pub file_name: model::FileName, +} + +crate::db_async_handler!(CreatePhoto, create_photo, model::Photo, inner_create_photo); + +pub(crate) async fn create_photo<'e, E>(msg: CreatePhoto, pool: E) -> Result +where + E: sqlx::Executor<'e, Database = sqlx::Postgres>, +{ + sqlx::query_as( + r#" +INSERT INTO photos (file_name, local_path) +VALUES ($1, $2) +RETURNING id, local_path, file_name +"#, + ) + .bind(msg.file_name) + .bind(msg.local_path) + .fetch_one(pool) + .await + .map_err(|e| { + log::error!("{e:?}"); + crate::Error::Photo(Error::Create) + }) +} diff --git a/actors/database_manager/src/product_photos.rs b/actors/database_manager/src/product_photos.rs new file mode 100644 index 0000000..0afaa5e --- /dev/null +++ b/actors/database_manager/src/product_photos.rs @@ -0,0 +1,2 @@ +#[derive(Debug, thiserror::Error)] +pub enum Error {} diff --git a/actors/database_manager/src/products.rs b/actors/database_manager/src/products.rs index 1b9d217..4738cb3 100644 --- a/actors/database_manager/src/products.rs +++ b/actors/database_manager/src/products.rs @@ -1,11 +1,12 @@ use actix::Message; +#[cfg(feature = "dummy")] +use fake::Fake; use model::{ Days, Price, Product, ProductCategory, ProductId, ProductLongDesc, ProductName, ProductShortDesc, }; use super::Result; -use crate::Database; #[derive(Debug, thiserror::Error)] pub enum Error { @@ -51,6 +52,7 @@ FROM products }) } +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[derive(Message)] #[rtype(result = "Result")] pub struct CreateProduct { diff --git a/actors/database_manager/src/shopping_cart_items.rs b/actors/database_manager/src/shopping_cart_items.rs index fc810ce..366fb5f 100644 --- a/actors/database_manager/src/shopping_cart_items.rs +++ b/actors/database_manager/src/shopping_cart_items.rs @@ -2,7 +2,7 @@ use model::*; use sqlx::PgPool; use super::Result; -use crate::{db_async_handler, Database}; +use crate::db_async_handler; #[derive(Debug, thiserror::Error)] pub enum Error { diff --git a/actors/database_manager/src/shopping_carts.rs b/actors/database_manager/src/shopping_carts.rs index 23a2f59..88f0c6d 100644 --- a/actors/database_manager/src/shopping_carts.rs +++ b/actors/database_manager/src/shopping_carts.rs @@ -2,7 +2,7 @@ use model::*; use sqlx::PgPool; use super::Result; -use crate::{db_async_handler, Database}; +use crate::db_async_handler; #[derive(Debug, thiserror::Error)] pub enum Error { diff --git a/actors/database_manager/src/stocks.rs b/actors/database_manager/src/stocks.rs index a3d11fc..9318f2f 100644 --- a/actors/database_manager/src/stocks.rs +++ b/actors/database_manager/src/stocks.rs @@ -2,7 +2,7 @@ use actix::Message; use model::{ProductId, Quantity, QuantityUnit, Stock, StockId}; use sqlx::PgPool; -use crate::{Database, Result}; +use crate::Result; #[derive(Debug, thiserror::Error)] pub enum Error { diff --git a/actors/database_manager/src/tokens.rs b/actors/database_manager/src/tokens.rs index fbb2dae..6d29aba 100644 --- a/actors/database_manager/src/tokens.rs +++ b/actors/database_manager/src/tokens.rs @@ -2,7 +2,7 @@ use actix::Message; use model::{AccountId, Audience, Token}; use sqlx::PgPool; -use crate::{db_async_handler, Database, Result}; +use crate::{db_async_handler, Result}; #[derive(Debug, thiserror::Error)] pub enum Error { diff --git a/actors/fs_manager/src/lib.rs b/actors/fs_manager/src/lib.rs index 83e718c..bdf3b8f 100644 --- a/actors/fs_manager/src/lib.rs +++ b/actors/fs_manager/src/lib.rs @@ -4,7 +4,7 @@ use std::ffi::OsStr; use std::io::Write; use config::SharedAppConfig; -use model::FileName; +use model::{FileName, LocalPath}; #[macro_export] macro_rules! fs_async_handler { @@ -37,6 +37,7 @@ pub enum Error { pub type Result = std::result::Result; +#[derive(Clone)] pub struct FsManager { config: SharedAppConfig, } @@ -54,6 +55,9 @@ impl FsManager { async fn validate_fs(config: SharedAppConfig) -> Result<()> { let l = config.lock(); let output_path = l.files().local_path(); + + tokio::fs::create_dir_all(&output_path).await.ok(); + let test_file = std::path::PathBuf::new() .join(output_path) .join(format!("{}", uuid::Uuid::new_v4())); @@ -93,16 +97,21 @@ pub(crate) async fn remove_file(msg: RemoveFile, config: SharedAppConfig) -> Res } } +pub struct WriteResult { + pub file_name: FileName, + pub local_path: LocalPath, +} + #[derive(actix::Message)] -#[rtype(result = "Result")] +#[rtype(result = "Result")] pub struct WriteFile { pub file_name: String, pub stream: tokio::sync::mpsc::UnboundedReceiver, } -fs_async_handler!(WriteFile, write_file, FileName); +fs_async_handler!(WriteFile, write_file, WriteResult); -pub(crate) async fn write_file(msg: WriteFile, config: SharedAppConfig) -> Result { +pub(crate) async fn write_file(msg: WriteFile, config: SharedAppConfig) -> Result { let WriteFile { file_name, mut stream, @@ -128,8 +137,10 @@ pub(crate) async fn write_file(msg: WriteFile, config: SharedAppConfig) -> Resul l.files().local_path() }; - let path = std::path::PathBuf::new().join(output_path).join(&file_name); - let mut file = match std::fs::File::create(path) { + let path = std::path::PathBuf::new() + .join(&output_path) + .join(&file_name); + let mut file = match std::fs::File::create(&path) { Ok(f) => f, Err(e) => return Err(Error::CantWrite(e)), }; @@ -141,5 +152,8 @@ pub(crate) async fn write_file(msg: WriteFile, config: SharedAppConfig) -> Resul Err(e) => return Err(Error::CantWrite(e)), } } - Ok(FileName::from(file_name)) + Ok(WriteResult { + file_name: FileName::from(file_name), + local_path: LocalPath::from(path.to_str().unwrap_or_default().to_string()), + }) } diff --git a/api/Cargo.toml b/api/Cargo.toml index 3477199..a02cd93 100644 --- a/api/Cargo.toml +++ b/api/Cargo.toml @@ -13,6 +13,7 @@ order_manager = { path = "../actors/order_manager" } payment_manager = { path = "../actors/payment_manager" } search_manager = { path = "../actors/search_manager" } token_manager = { path = "../actors/token_manager" } +fs_manager = { path = "../actors/fs_manager" } actix = { version = "0.13", features = [] } actix-rt = { version = "2.7", features = [] } diff --git a/api/src/main.rs b/api/src/main.rs index 2806b48..57625cb 100644 --- a/api/src/main.rs +++ b/api/src/main.rs @@ -57,6 +57,9 @@ async fn server(opts: ServerOpts) -> Result<()> { .expect("Failed to start payment manager") .start(); let search_manager = search_manager::SearchManager::new(app_config.clone()); + let fs_manager = fs_manager::FsManager::build(app_config.clone()) + .await + .expect("Failed to initialize file system storage"); let addr = { let l = app_config.lock(); let w = l.web(); @@ -84,6 +87,7 @@ async fn server(opts: ServerOpts) -> Result<()> { .app_data(Data::new(order_manager.clone())) .app_data(Data::new(payment_manager.clone())) .app_data(Data::new(search_manager.clone())) + .app_data(Data::new(fs_manager.clone())) .configure(routes::configure) .service({ let l = app_config.lock(); diff --git a/api/src/model.rs b/api/src/model.rs deleted file mode 100644 index d820608..0000000 --- a/api/src/model.rs +++ /dev/null @@ -1,646 +0,0 @@ -pub mod api; - -use std::fmt::Formatter; -use std::str::FromStr; - -use derive_more::{Deref, Display, From}; -use serde::de::{Error, Visitor}; -use serde::{Deserialize, Deserializer, Serialize}; - -#[derive(Debug, thiserror::Error)] -pub enum TransformError { - #[error("Given value is below minimal value")] - BelowMinimal, - #[error("Given value is not valid day flag")] - NotDay, - #[error("Given value is not valid email address")] - NotEmail, -} - -pub type RecordId = i32; - -#[derive(sqlx::Type, Copy, Clone, Debug, Display, Deserialize, Serialize)] -#[sqlx(rename_all = "snake_case")] -#[serde(rename_all = "snake_case")] -pub enum OrderStatus { - #[display(fmt = "Potwierdzone")] - Confirmed, - #[display(fmt = "Odebrane")] - Delivered, - #[display(fmt = "Opłacone")] - Payed, - #[display(fmt = "Anulowane")] - Cancelled, - #[display(fmt = "Wymaga zwrotu płatności")] - RequireRefund, - #[display(fmt = "Płatność zwrócona")] - Refunded, -} - -#[derive(sqlx::Type, Copy, Clone, Debug, Display, Deserialize, Serialize, PartialEq)] -#[sqlx(rename_all = "snake_case")] -#[serde(rename_all = "snake_case")] -pub enum Role { - #[display(fmt = "Adminitrator")] - Admin, - #[display(fmt = "Użytkownik")] - User, -} - -impl PartialEq<&str> for Role { - fn eq(&self, other: &&str) -> bool { - self.as_str() == *other - } -} - -impl Role { - pub fn as_str(&self) -> &str { - match self { - Role::Admin => "Admin", - Role::User => "User", - } - } -} - -#[derive(sqlx::Type, Copy, Clone, Debug, Display, Deserialize, Serialize)] -#[serde(rename_all = "snake_case")] -pub enum QuantityUnit { - #[sqlx(rename = "g")] - Gram, - #[sqlx(rename = "dkg")] - Decagram, - #[sqlx(rename = "kg")] - Kilogram, - #[sqlx(rename = "piece")] - Piece, -} - -#[derive(sqlx::Type, Copy, Clone, Debug, Display, Deserialize, Serialize)] -#[sqlx(rename_all = "snake_case")] -#[serde(rename_all = "snake_case")] -pub enum PaymentMethod { - PayU, - PaymentOnTheSpot, -} - -#[derive(sqlx::Type, Copy, Clone, Debug, Display, Deserialize, Serialize)] -#[sqlx(rename_all = "snake_case")] -#[serde(rename_all = "snake_case")] -pub enum ShoppingCartState { - Active, - Closed, -} - -#[derive(sqlx::Type, Copy, Clone, Debug, Display, Deserialize, Serialize, PartialEq)] -#[sqlx(rename_all = "snake_case")] -#[serde(rename_all = "snake_case")] -pub enum Audience { - Web, - Mobile, - Feed, - AdminPanel, -} - -impl PartialEq<&str> for Audience { - fn eq(&self, other: &&str) -> bool { - self.as_str() == *other - } -} - -impl Audience { - pub fn as_str(&self) -> &str { - match self { - Audience::Web => "Web", - Audience::Mobile => "Mobile", - Audience::Feed => "Feed", - Audience::AdminPanel => "AdminPanel", - } - } -} - -#[derive(sqlx::Type, Copy, Clone, Debug, Display, Deserialize, Serialize, PartialEq)] -#[sqlx(rename_all = "snake_case")] -#[serde(rename_all = "snake_case")] -pub enum AccountState { - Active, - Suspended, - Banned, -} - -impl Default for Audience { - fn default() -> Self { - Self::Web - } -} - -#[derive(sqlx::Type, Serialize, Deserialize, Default, Debug, Copy, Clone, Deref, From)] -#[sqlx(transparent)] -#[serde(transparent)] -pub struct Price(NonNegative); - -#[derive(sqlx::Type, Serialize, Deserialize, Default, Debug, Copy, Clone, Deref, From)] -#[sqlx(transparent)] -#[serde(transparent)] -pub struct Quantity(NonNegative); - -impl TryFrom for Quantity { - type Error = TransformError; - - fn try_from(value: i32) -> Result { - Ok(Self(value.try_into()?)) - } -} - -#[derive(sqlx::Type, Deserialize, Serialize, Debug, Deref, From, Display)] -#[sqlx(transparent)] -#[serde(transparent)] -pub struct Login(String); - -#[derive(sqlx::Type, Serialize, Debug, Deref, From, Display)] -#[sqlx(transparent)] -#[serde(transparent)] -pub struct Email(String); - -impl FromStr for Email { - type Err = TransformError; - - fn from_str(s: &str) -> Result { - if validator::validate_email(s) { - Ok(Self(String::from(s))) - } else { - Err(TransformError::NotEmail) - } - } -} - -impl<'de> serde::Deserialize<'de> for Email { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct EmailVisitor {} - impl<'v> Visitor<'v> for EmailVisitor { - type Value = String; - - fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result { - formatter.write_str("valid e-mail address") - } - - fn visit_str(self, s: &str) -> Result - where - E: Error, - { - if validator::validate_email(s) { - Ok(String::from(s)) - } else { - Err(E::custom("this is not email address")) - } - } - } - - Ok(Email(deserializer.deserialize_str(EmailVisitor {})?)) - } -} - -#[derive(sqlx::Type, Serialize, Default, Debug, Copy, Clone, Deref, Display)] -#[sqlx(transparent)] -#[serde(transparent)] -pub struct NonNegative(i32); - -impl TryFrom for NonNegative { - type Error = TransformError; - - fn try_from(value: i32) -> Result { - if value < 0 { - Err(TransformError::BelowMinimal) - } else { - Ok(Self(value)) - } - } -} - -impl<'de> serde::Deserialize<'de> for NonNegative { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct NonNegativeVisitor; - impl<'v> Visitor<'v> for NonNegativeVisitor { - type Value = i32; - - fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result { - formatter.write_str("value equal or greater than 0") - } - - fn visit_i32(self, v: i32) -> Result - where - E: Error, - { - if v >= 0 { - Ok(v) - } else { - Err(E::custom("Value must be equal or greater than 0")) - } - } - - fn visit_i64(self, v: i64) -> Result - where - E: Error, - { - let v = v - .try_into() - .map_err(|_| E::custom("Value must be equal or greater than 0"))?; - if v >= 0 { - Ok(v) - } else { - Err(E::custom("Value must be equal or greater than 0")) - } - } - - fn visit_u32(self, v: u32) -> Result - where - E: Error, - { - let v = v - .try_into() - .map_err(|_| E::custom("Value must be equal or greater than 0"))?; - Ok(v) - } - - fn visit_u64(self, v: u64) -> Result - where - E: Error, - { - let v = v - .try_into() - .map_err(|_| E::custom("Value must be equal or greater than 0"))?; - Ok(v) - } - } - - Ok(NonNegative( - deserializer.deserialize_i32(NonNegativeVisitor)?, - )) - } -} - -#[derive(Serialize, Deserialize, Debug, Copy, Clone, Display, From)] -#[serde(rename_all = "lowercase")] -pub enum Day { - Monday = 1 << 0, - Tuesday = 1 << 1, - Wednesday = 1 << 2, - Thursday = 1 << 3, - Friday = 1 << 4, - Saturday = 1 << 5, - Sunday = 1 << 6, -} - -impl TryFrom for Day { - type Error = TransformError; - - fn try_from(value: i32) -> Result { - if value == (Day::Monday as i32) { - Ok(Day::Monday) - } else if value == (Day::Tuesday as i32) { - Ok(Day::Tuesday) - } else if value == (Day::Wednesday as i32) { - Ok(Day::Wednesday) - } else if value == (Day::Thursday as i32) { - Ok(Day::Thursday) - } else if value == (Day::Friday as i32) { - Ok(Day::Friday) - } else if value == (Day::Saturday as i32) { - Ok(Day::Saturday) - } else if value == (Day::Sunday as i32) { - Ok(Day::Sunday) - } else { - Err(TransformError::NotDay) - } - } -} - -#[derive(Serialize, Deserialize, Deref, Debug)] -#[serde(transparent)] -pub struct Days(Vec); - -impl<'q> ::sqlx::encode::Encode<'q, sqlx::Postgres> for Days -where - i32: ::sqlx::encode::Encode<'q, sqlx::Postgres>, -{ - fn encode_by_ref( - &self, - buf: &mut >::ArgumentBuffer, - ) -> ::sqlx::encode::IsNull { - let value = self.0.iter().fold(1, |memo, v| memo | *v as i32); - - >::encode_by_ref(&value, buf) - } - - fn size_hint(&self) -> usize { - >::size_hint(&Default::default()) - } -} - -impl<'r> ::sqlx::decode::Decode<'r, sqlx::Postgres> for Days -where - i32: ::sqlx::decode::Decode<'r, sqlx::Postgres>, -{ - fn decode( - value: >::ValueRef, - ) -> ::std::result::Result< - Self, - ::std::boxed::Box< - dyn ::std::error::Error + 'static + ::std::marker::Send + ::std::marker::Sync, - >, - > { - let value = >::decode(value)?; - Ok(Days( - (0..9) - .into_iter() - .filter_map(|n| { - eprintln!( - "d {} {} {} {:?}", - n, - 1 << n, - value & 1 << n, - Day::try_from(value & 1 << n).ok() - ); - Day::try_from(value & 1 << n).ok() - }) - .collect(), - )) - } -} - -impl sqlx::Type for Days -where - i32: ::sqlx::Type, -{ - fn type_info() -> ::TypeInfo { - >::type_info() - } - - fn compatible(ty: &::TypeInfo) -> bool { - >::compatible(ty) - } -} - -#[derive(sqlx::Type, Serialize, Deserialize, Debug, Deref, From, Display)] -#[sqlx(transparent)] -#[serde(transparent)] -pub struct Password(String); - -#[derive(sqlx::Type, Serialize, Deserialize, Debug, Deref, From, Display)] -#[sqlx(transparent)] -#[serde(transparent)] -pub struct PasswordConfirmation(String); - -#[derive(sqlx::Type, Serialize, Deserialize, Debug, Deref, From, Display)] -#[sqlx(transparent)] -#[serde(transparent)] -pub struct PassHash(String); - -impl PartialEq for Password { - fn eq(&self, other: &PasswordConfirmation) -> bool { - self.0 == other.0 - } -} - -#[derive(sqlx::Type, Serialize, Deserialize, Copy, Clone, Debug, Deref, Display, From)] -#[sqlx(transparent)] -#[serde(transparent)] -pub struct AccountId(RecordId); - -#[derive(sqlx::FromRow, Serialize, Deserialize)] -pub struct FullAccount { - pub id: AccountId, - pub email: Email, - pub login: Login, - pub pass_hash: PassHash, - pub role: Role, - pub customer_id: uuid::Uuid, - pub state: AccountState, -} - -#[derive(sqlx::FromRow, Serialize, Deserialize)] -pub struct Account { - pub id: AccountId, - pub email: Email, - pub login: Login, - pub role: Role, - pub customer_id: uuid::Uuid, - pub state: AccountState, -} - -impl From for Account { - fn from( - FullAccount { - id, - email, - login, - pass_hash: _, - role, - customer_id, - state, - }: FullAccount, - ) -> Self { - Self { - id, - email, - login, - role, - customer_id, - state, - } - } -} - -#[derive( - sqlx::Type, - Serialize, - Deserialize, - Debug, - Copy, - Clone, - PartialEq, - Eq, - Hash, - Deref, - Display, - From, -)] -#[sqlx(transparent)] -#[serde(transparent)] -pub struct ProductId(RecordId); - -#[derive(sqlx::Type, Serialize, Deserialize, Debug, Clone, Deref, Display, From)] -#[sqlx(transparent)] -#[serde(transparent)] -pub struct ProductName(String); - -#[derive(sqlx::Type, Serialize, Deserialize, Debug, Clone, Deref, Display, From)] -#[sqlx(transparent)] -#[serde(transparent)] -pub struct ProductShortDesc(String); - -#[derive(sqlx::Type, Serialize, Deserialize, Debug, Clone, Deref, Display, From)] -#[sqlx(transparent)] -#[serde(transparent)] -pub struct ProductLongDesc(String); - -#[derive(sqlx::Type, Serialize, Deserialize, Debug, Clone, Deref, Display, From)] -#[sqlx(transparent)] -#[serde(transparent)] -pub struct ProductCategory(String); - -#[derive(sqlx::FromRow, Serialize, Deserialize)] -pub struct Product { - pub id: ProductId, - pub name: ProductName, - pub short_description: ProductShortDesc, - pub long_description: ProductLongDesc, - pub category: Option, - pub price: Price, - pub deliver_days_flag: Days, -} - -#[derive(sqlx::Type, Serialize, Deserialize)] -#[sqlx(transparent)] -#[serde(transparent)] -pub struct StockId(pub RecordId); - -#[derive(sqlx::FromRow, Serialize, Deserialize)] -pub struct Stock { - pub id: StockId, - pub product_id: ProductId, - pub quantity: Quantity, - pub quantity_unit: QuantityUnit, -} - -#[derive(sqlx::Type, Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Display, Deref)] -#[sqlx(transparent)] -#[serde(transparent)] -pub struct AccountOrderId(RecordId); - -#[derive(sqlx::Type, Serialize, Deserialize, Clone, Debug, PartialEq, Display, Deref)] -#[sqlx(transparent)] -#[serde(transparent)] -pub struct OrderId(String); - -#[derive(sqlx::FromRow, Serialize, Deserialize)] -pub struct AccountOrder { - pub id: AccountOrderId, - pub buyer_id: AccountId, - pub status: OrderStatus, - pub order_id: Option, - pub order_ext_id: uuid::Uuid, - pub service_order_id: Option, -} - -#[derive(sqlx::FromRow, Serialize, Deserialize)] -pub struct PublicAccountOrder { - pub id: AccountOrderId, - pub buyer_id: AccountId, - pub status: OrderStatus, - pub order_id: Option, -} - -impl From for PublicAccountOrder { - fn from( - AccountOrder { - id, - buyer_id, - status, - order_id, - order_ext_id: _, - service_order_id: _, - }: AccountOrder, - ) -> Self { - Self { - id, - buyer_id, - status, - order_id, - } - } -} - -#[derive(sqlx::Type, Serialize, Deserialize, Copy, Clone, Debug, Deref)] -#[sqlx(transparent)] -#[serde(transparent)] -pub struct OrderItemId(pub RecordId); - -#[derive(sqlx::FromRow, Serialize, Deserialize, Debug)] -pub struct OrderItem { - pub id: OrderItemId, - pub product_id: ProductId, - pub order_id: AccountOrderId, - pub quantity: Quantity, - pub quantity_unit: QuantityUnit, -} - -#[derive(sqlx::Type, Serialize, Deserialize, Copy, Clone, Debug, Deref, Display)] -#[sqlx(transparent)] -#[serde(transparent)] -pub struct ShoppingCartId(pub RecordId); - -#[derive(sqlx::FromRow, Serialize, Deserialize)] -pub struct ShoppingCart { - pub id: ShoppingCartId, - pub buyer_id: AccountId, - pub payment_method: PaymentMethod, - pub state: ShoppingCartState, -} - -#[derive(sqlx::Type, Serialize, Deserialize, Copy, Clone, Debug, Deref, Display)] -#[sqlx(transparent)] -#[serde(transparent)] -pub struct ShoppingCartItemId(RecordId); - -#[derive(sqlx::FromRow, Serialize, Deserialize)] -pub struct ShoppingCartItem { - pub id: ShoppingCartId, - pub product_id: ProductId, - pub shopping_cart_id: ShoppingCartId, - pub quantity: Quantity, - pub quantity_unit: QuantityUnit, -} - -impl ShoppingCartItem { - pub fn quantity(&self) -> Quantity { - self.quantity - } -} - -#[derive(sqlx::Type, Serialize, Deserialize, Copy, Clone, Deref, Display, Debug)] -#[sqlx(transparent)] -#[serde(transparent)] -pub struct TokenId(RecordId); - -#[derive(sqlx::FromRow, Serialize, Deserialize)] -pub struct Token { - pub id: TokenId, - pub customer_id: uuid::Uuid, - pub role: Role, - /// iss (issuer): Issuer of the JWT - pub issuer: String, - /// sub (subject): Subject of the JWT (the user) - pub subject: i32, - /// aud (audience): Recipient for which the JWT is intended - pub audience: Audience, - /// exp (expiration time): Time after which the JWT expires - pub expiration_time: chrono::NaiveDateTime, - /// nbt (not before time): Time before which the JWT must not be accepted - /// for processing - pub not_before_time: chrono::NaiveDateTime, - /// iat (issued at time): Time at which the JWT was issued; can be used to - /// determine age of the JWT, - pub issued_at_time: chrono::NaiveDateTime, - /// jti (JWT ID): Unique identifier; can be used to prevent the JWT from - /// being replayed (allows a token to be used only once) - pub jwt_id: uuid::Uuid, -} - -#[derive(sqlx::Type, Serialize, Deserialize, Debug, Deref, Display, From)] -pub struct TokenString(String); diff --git a/api/src/model/api.rs b/api/src/model/api.rs deleted file mode 100644 index 7731e32..0000000 --- a/api/src/model/api.rs +++ /dev/null @@ -1,126 +0,0 @@ -use serde::Serialize; - -use crate::model; -use crate::model::{AccountId, AccountOrderId, OrderId, OrderItem, OrderStatus}; - -#[derive(Serialize, Debug)] -#[serde(transparent)] -pub struct AccountOrders(pub Vec); - -impl From<(Vec, Vec)> for AccountOrders { - fn from((orders, mut items): (Vec, Vec)) -> Self { - Self( - orders - .into_iter() - .map( - |model::AccountOrder { - id, - buyer_id, - status, - order_id, - order_ext_id: _, - service_order_id: _, - }| { - AccountOrder { - id, - buyer_id, - status, - order_id, - items: items.drain_filter(|item| item.order_id == id).collect(), - } - }, - ) - .collect(), - ) - } -} - -impl From<(model::AccountOrder, Vec)> for AccountOrder { - fn from( - ( - model::AccountOrder { - id, - buyer_id, - status, - order_id, - order_ext_id: _, - service_order_id: _, - }, - mut items, - ): (model::AccountOrder, Vec), - ) -> Self { - AccountOrder { - id, - buyer_id, - status, - order_id, - items: items.drain_filter(|item| item.order_id == id).collect(), - } - } -} - -#[derive(Serialize, Debug)] -pub struct AccountOrder { - pub id: AccountOrderId, - pub buyer_id: AccountId, - pub status: OrderStatus, - pub order_id: Option, - pub items: Vec, -} - -#[derive(Serialize, Debug)] -pub struct ShoppingCartItem { - pub id: model::ShoppingCartId, - pub product_id: model::ProductId, - pub shopping_cart_id: model::ShoppingCartId, - pub quantity: model::Quantity, - pub quantity_unit: model::QuantityUnit, -} - -#[derive(Serialize, Debug)] -pub struct ShoppingCart { - pub id: model::ShoppingCartId, - pub buyer_id: AccountId, - pub payment_method: model::PaymentMethod, - pub state: model::ShoppingCartState, - pub items: Vec, -} - -impl From<(model::ShoppingCart, Vec)> for ShoppingCart { - fn from( - ( - model::ShoppingCart { - id, - buyer_id, - payment_method, - state, - }, - items, - ): (model::ShoppingCart, Vec), - ) -> Self { - Self { - id, - buyer_id, - payment_method, - state, - items: items - .into_iter() - .map( - |model::ShoppingCartItem { - id, - product_id, - shopping_cart_id, - quantity, - quantity_unit, - }| ShoppingCartItem { - id, - product_id, - shopping_cart_id, - quantity, - quantity_unit, - }, - ) - .collect(), - } - } -} diff --git a/api/src/routes/admin/api_v1/uploads.rs b/api/src/routes/admin/api_v1/uploads.rs index d2b82e3..769f522 100644 --- a/api/src/routes/admin/api_v1/uploads.rs +++ b/api/src/routes/admin/api_v1/uploads.rs @@ -1,8 +1,111 @@ -use actix_web::web::ServiceConfig; +use actix::Addr; +use actix_multipart::Multipart; +use actix_web::web::{Data, ServiceConfig}; use actix_web::{post, HttpResponse}; +use database_manager::{query_db, Database}; +use fs_manager::FsManager; +use futures_util::StreamExt; + +#[derive(Debug, thiserror::Error)] +pub enum UploadError { + #[error("Uploaded file name is invalid")] + InvalidName, + #[error("Write to disk failed")] + FileStreamBroken, + #[error("Failed to save in database")] + DbSave, +} #[post("/product-image")] -async fn upload_product_image() -> HttpResponse { +async fn upload_product_image( + mut payload: Multipart, + fs: Data>, + db: Data>, +) -> HttpResponse { + let mut name = None; + while let Some(Ok(mut field)) = payload.next().await { + let field_name = field.name(); + match field_name { + "name" => { + if name.is_some() { + return HttpResponse::BadRequest().finish(); + } + let mut value = String::with_capacity(300); + while let Some(Ok(bytes)) = field.next().await { + if let Ok(s) = std::str::from_utf8(&bytes) { + value.push_str(s); + } else { + return HttpResponse::BadRequest().finish(); + } + } + name = Some(value); + } + "photo" => { + let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); + let msg = fs_manager::WriteFile { + file_name: match name.take() { + Some(name) => name, + _ => return HttpResponse::BadRequest().finish(), + }, + stream: rx, + }; + let read = async { + while let Some(Ok(data)) = field.next().await { + for b in data { + if let Err(e) = tx.send(b) { + log::error!("{e:?}"); + return Err(UploadError::InvalidName); + } + } + } + Ok(()) + }; + + let write = async { + let fs_manager::WriteResult { + local_path, + file_name, + } = match fs.send(msg).await { + Ok(Ok(file_name)) => file_name, + Ok(Err(e)) => { + log::error!("{e}"); + return Err(UploadError::FileStreamBroken); + } + Err(e) => { + log::error!("{e}"); + return Err(UploadError::FileStreamBroken); + } + }; + query_db!( + db, + database_manager::CreatePhoto { + local_path, + file_name + }, + UploadError::DbSave + ); + Ok(()) + }; + match tokio::join!(read, write) { + (Ok(_), Ok(_)) => {} + (Ok(_), Err(e)) => { + log::error!("Write error. {e}"); + return HttpResponse::BadRequest().finish(); + } + (Err(e), Ok(_)) => { + log::error!("Read error. {e:?}"); + return HttpResponse::BadRequest().finish(); + } + (Err(read), Err(write)) => { + log::error!("Read error. {read:?}"); + log::error!("Write error. {write:?}"); + return HttpResponse::BadRequest().finish(); + } + } + } + _ => {} + } + } HttpResponse::NotImplemented().finish() } diff --git a/db-seed/Cargo.toml b/db-seed/Cargo.toml new file mode 100644 index 0000000..3cf27a7 --- /dev/null +++ b/db-seed/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "db-seed" +version = "0.1.0" +edition = "2021" + +[dependencies] +model = { path = "../shared/model", version = "0.1", features = ["db", "dummy"] } +config = { path = "../shared/config" } +database_manager = { path = "../actors/database_manager", features = ["dummy"] } + +actix = { version = "0.13", features = [] } +actix-rt = { version = "2.7", features = [] } +actix-web = { version = "4.0", features = [] } + +fake = { version = "2.4.3", features = ["derive", "chrono", "http"] } +rand = { version = "0.8.5" } + +dotenv = { version = "0.15", features = [] } + +log = { version = "0.4", features = [] } +pretty_env_logger = { version = "0.4", features = [] } diff --git a/db-seed/src/main.rs b/db-seed/src/main.rs new file mode 100644 index 0000000..6cf4a64 --- /dev/null +++ b/db-seed/src/main.rs @@ -0,0 +1,37 @@ +use actix::Actor; +use config::{AppConfig, UpdateConfig}; +use fake::{Fake, Faker}; + +pub struct Opts; +impl UpdateConfig for Opts { + fn update_config(&self, _config: &mut AppConfig) {} +} + +#[actix_web::main] +async fn main() { + dotenv::dotenv().ok(); + std::env::set_var("RUST_LOG", "DEBUG"); + pretty_env_logger::init(); + + let config = config::default_load(&Opts); + let db = database_manager::Database::build(config) + .await + .unwrap() + .start(); + + let mut users = Vec::with_capacity(10); + for _ in 0..10 { + match db + .send(Faker.fake::()) + .await + { + Ok(Ok(user)) => users.push(user), + Ok(Err(e)) => { + log::error!("{e}") + } + Err(e) => { + log::error!("{e}") + } + } + } +} diff --git a/migrations/20220506130226_rename_photos_column.sql b/migrations/20220506130226_rename_photos_column.sql new file mode 100644 index 0000000..69a4465 --- /dev/null +++ b/migrations/20220506130226_rename_photos_column.sql @@ -0,0 +1,2 @@ +ALTER TABLE photos +RENAME COLUMN hashed_path TO file_name; diff --git a/shared/model/Cargo.toml b/shared/model/Cargo.toml index 52406d4..41bc782 100644 --- a/shared/model/Cargo.toml +++ b/shared/model/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [features] db = ["sqlx", "sqlx-core"] +dummy = ["fake", "rand"] [dependencies] serde = { version = "1.0.137" } @@ -20,3 +21,6 @@ derive_more = { version = "0.99.17" } thiserror = { version = "1.0.31" } validator = { version = "0.15.0" } + +fake = { version = "2.4.3", features = ["derive", "chrono", "http", "uuid"], optional = true } +rand = { version = "0.8.5", optional = true } diff --git a/shared/model/src/lib.rs b/shared/model/src/lib.rs index 3124989..768b5ee 100644 --- a/shared/model/src/lib.rs +++ b/shared/model/src/lib.rs @@ -6,6 +6,8 @@ use std::fmt::Formatter; use std::str::FromStr; use derive_more::{Deref, Display, From}; +#[cfg(feature = "dummy")] +use fake::Fake; use serde::de::{Error, Visitor}; use serde::{Deserialize, Deserializer, Serialize}; @@ -21,6 +23,7 @@ pub enum TransformError { pub type RecordId = i32; +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(rename_all = "snake_case"))] #[derive(Copy, Clone, Debug, Display, Deserialize, Serialize)] @@ -40,6 +43,7 @@ pub enum OrderStatus { Refunded, } +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(rename_all = "snake_case"))] #[derive(Copy, Clone, Debug, Display, Deserialize, Serialize, PartialEq)] @@ -66,6 +70,7 @@ impl Role { } } +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[derive(Copy, Clone, Debug, Display, Deserialize, Serialize)] #[serde(rename_all = "snake_case")] @@ -80,6 +85,7 @@ pub enum QuantityUnit { Piece, } +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(rename_all = "snake_case"))] #[derive(Copy, Clone, Debug, Display, Deserialize, Serialize)] @@ -89,6 +95,7 @@ pub enum PaymentMethod { PaymentOnTheSpot, } +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(rename_all = "snake_case"))] #[derive(Copy, Clone, Debug, Display, Deserialize, Serialize)] @@ -98,6 +105,7 @@ pub enum ShoppingCartState { Closed, } +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(rename_all = "snake_case"))] #[derive(Copy, Clone, Debug, Display, Deserialize, Serialize, PartialEq)] @@ -126,6 +134,7 @@ impl Audience { } } +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(rename_all = "snake_case"))] #[derive(Copy, Clone, Debug, Display, Deserialize, Serialize, PartialEq)] @@ -142,12 +151,14 @@ impl Default for Audience { } } +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Default, Debug, Copy, Clone, Deref, From)] #[serde(transparent)] pub struct Price(NonNegative); +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Default, Debug, Copy, Clone, Deref, From)] @@ -162,12 +173,16 @@ impl TryFrom for Quantity { } } +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] +#[cfg_attr(feature = "dummy", dummy("fake::faker::internet::en::Username"))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Deserialize, Serialize, Debug, Deref, From, Display)] #[serde(transparent)] pub struct Login(String); +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] +#[cfg_attr(feature = "dummy", dummy("fake::faker::internet::en::FreeEmail"))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Debug, Deref, From, Display)] @@ -215,6 +230,7 @@ impl<'de> serde::Deserialize<'de> for Email { } } +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Default, Debug, Copy, Clone, Deref, Display)] @@ -298,6 +314,7 @@ impl<'de> serde::Deserialize<'de> for NonNegative { } } +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[derive(Serialize, Deserialize, Debug, Copy, Clone, Display, From)] #[serde(rename_all = "lowercase")] pub enum Day { @@ -334,6 +351,7 @@ impl TryFrom for Day { } } +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[derive(Serialize, Deserialize, Deref, Debug)] #[serde(transparent)] pub struct Days(Vec); @@ -403,18 +421,21 @@ where } } +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Debug, Deref, From, Display)] #[serde(transparent)] pub struct Password(String); +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Debug, Deref, From, Display)] #[serde(transparent)] pub struct PasswordConfirmation(String); +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Debug, Deref, From, Display)] @@ -427,12 +448,14 @@ impl PartialEq for Password { } } +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Copy, Clone, Debug, Deref, Display, From)] #[serde(transparent)] pub struct AccountId(RecordId); +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::FromRow))] #[derive(Serialize, Deserialize)] pub struct FullAccount { @@ -445,6 +468,7 @@ pub struct FullAccount { pub state: AccountState, } +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::FromRow))] #[derive(Serialize, Deserialize)] pub struct Account { @@ -479,36 +503,42 @@ impl From for Account { } } +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Hash, Deref, Display, From)] #[serde(transparent)] pub struct ProductId(RecordId); +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Debug, Clone, Deref, Display, From)] #[serde(transparent)] pub struct ProductName(String); +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Debug, Clone, Deref, Display, From)] #[serde(transparent)] pub struct ProductShortDesc(String); +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Debug, Clone, Deref, Display, From)] #[serde(transparent)] pub struct ProductLongDesc(String); +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Debug, Clone, Deref, Display, From)] #[serde(transparent)] pub struct ProductCategory(String); +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::FromRow))] #[derive(Serialize, Deserialize)] pub struct Product { @@ -521,12 +551,14 @@ pub struct Product { pub deliver_days_flag: Days, } +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize)] #[serde(transparent)] pub struct StockId(pub RecordId); +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::FromRow))] #[derive(Serialize, Deserialize)] pub struct Stock { @@ -536,18 +568,21 @@ pub struct Stock { pub quantity_unit: QuantityUnit, } +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Display, Deref)] #[serde(transparent)] pub struct AccountOrderId(RecordId); +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Display, Deref)] #[serde(transparent)] pub struct OrderId(String); +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::FromRow))] #[derive(Serialize, Deserialize)] pub struct AccountOrder { @@ -559,6 +594,7 @@ pub struct AccountOrder { pub service_order_id: Option, } +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::FromRow))] #[derive(Serialize, Deserialize)] pub struct PublicAccountOrder { @@ -588,11 +624,13 @@ impl From for PublicAccountOrder { } } +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Copy, Clone, Debug, Deref)] pub struct OrderItemId(pub RecordId); +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::FromRow))] #[derive(Serialize, Deserialize, Debug)] pub struct OrderItem { @@ -603,12 +641,14 @@ pub struct OrderItem { pub quantity_unit: QuantityUnit, } +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Copy, Clone, Debug, Deref, Display)] #[serde(transparent)] pub struct ShoppingCartId(pub RecordId); +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::FromRow))] #[derive(Serialize, Deserialize)] pub struct ShoppingCart { @@ -618,6 +658,7 @@ pub struct ShoppingCart { pub state: ShoppingCartState, } +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Copy, Clone, Debug, Deref, Display)] @@ -640,12 +681,14 @@ impl ShoppingCartItem { } } +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Copy, Clone, Deref, Display, Debug)] #[serde(transparent)] pub struct TokenId(RecordId); +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::FromRow))] #[derive(Serialize, Deserialize)] pub struct Token { @@ -671,12 +714,50 @@ pub struct Token { pub jwt_id: uuid::Uuid, } +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Debug, Deref, Display, From)] pub struct TokenString(String); +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] +#[cfg_attr(feature = "db", derive(sqlx::Type))] +#[cfg_attr(feature = "db", sqlx(transparent))] +#[derive(Serialize, Deserialize, Debug, Deref, Display, From)] +pub struct LocalPath(String); + +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Debug, Deref, Display, From)] pub struct FileName(String); + +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] +#[cfg_attr(feature = "db", derive(sqlx::Type))] +#[cfg_attr(feature = "db", sqlx(transparent))] +#[derive(Serialize, Deserialize, Debug, Deref, Display, From)] +pub struct PhotoId(RecordId); + +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] +#[cfg_attr(feature = "db", derive(sqlx::Type))] +#[cfg_attr(feature = "db", sqlx(transparent))] +#[derive(Serialize, Deserialize, Debug, Deref, Display, From)] +pub struct ProductPhotoId(RecordId); + +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] +#[cfg_attr(feature = "db", derive(sqlx::FromRow))] +#[derive(Serialize, Deserialize, Debug)] +pub struct Photo { + pub id: PhotoId, + pub local_path: LocalPath, + pub file_name: FileName, +} + +#[cfg_attr(feature = "dummy", derive(fake::Dummy))] +#[cfg_attr(feature = "db", derive(sqlx::FromRow))] +#[derive(Serialize, Deserialize, Debug)] +pub struct ProductPhoto { + pub id: ProductPhotoId, + pub product_id: ProductId, + pub photo_id: PhotoId, +}