Add working search ingest
This commit is contained in:
parent
4740aedb73
commit
1b75a6a1ac
222
Cargo.lock
generated
222
Cargo.lock
generated
@ -609,37 +609,40 @@ dependencies = [
|
||||
"actix-web-opentelemetry",
|
||||
"argon2",
|
||||
"async-trait",
|
||||
"cart_manager",
|
||||
"chrono",
|
||||
"config",
|
||||
"database_manager",
|
||||
"derive_more",
|
||||
"dotenv",
|
||||
"email_manager",
|
||||
"futures",
|
||||
"futures-util",
|
||||
"gumdrop",
|
||||
"hmac",
|
||||
"jemallocator",
|
||||
"jwt",
|
||||
"log",
|
||||
"messagebus",
|
||||
"model",
|
||||
"oauth2",
|
||||
"order_manager",
|
||||
"parking_lot 0.12.0",
|
||||
"password-hash",
|
||||
"pay_u",
|
||||
"payment_manager",
|
||||
"pretty_env_logger",
|
||||
"rand_core",
|
||||
"sendgrid",
|
||||
"search_manager",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"sonic-channel",
|
||||
"sqlx",
|
||||
"sqlx-core",
|
||||
"tera",
|
||||
"thiserror",
|
||||
"token_manager",
|
||||
"tokio",
|
||||
"toml",
|
||||
"tracing",
|
||||
"uuid",
|
||||
"validator",
|
||||
"validator 0.14.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -767,6 +770,22 @@ dependencies = [
|
||||
"bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cart_manager"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"actix 0.13.0",
|
||||
"actix-rt",
|
||||
"chrono",
|
||||
"config",
|
||||
"database_manager",
|
||||
"log",
|
||||
"model",
|
||||
"pretty_env_logger",
|
||||
"thiserror",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.73"
|
||||
@ -827,6 +846,21 @@ dependencies = [
|
||||
"generic-array 0.14.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "config"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"actix-web",
|
||||
"log",
|
||||
"parking_lot 0.12.0",
|
||||
"password-hash",
|
||||
"pay_u",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "console_error_panic_hook"
|
||||
version = "0.1.7"
|
||||
@ -1016,6 +1050,23 @@ version = "2.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57"
|
||||
|
||||
[[package]]
|
||||
name = "database_manager"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"actix 0.13.0",
|
||||
"actix-rt",
|
||||
"chrono",
|
||||
"config",
|
||||
"log",
|
||||
"model",
|
||||
"pretty_env_logger",
|
||||
"sqlx",
|
||||
"sqlx-core",
|
||||
"thiserror",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dbg"
|
||||
version = "1.0.4"
|
||||
@ -1102,6 +1153,22 @@ version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
|
||||
|
||||
[[package]]
|
||||
name = "email_manager"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"actix 0.13.0",
|
||||
"actix-rt",
|
||||
"chrono",
|
||||
"config",
|
||||
"log",
|
||||
"model",
|
||||
"pretty_env_logger",
|
||||
"sendgrid",
|
||||
"thiserror",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enclose"
|
||||
version = "1.1.8"
|
||||
@ -1215,6 +1282,22 @@ version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394"
|
||||
|
||||
[[package]]
|
||||
name = "fs_manager"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"actix 0.13.0",
|
||||
"actix-rt",
|
||||
"chrono",
|
||||
"config",
|
||||
"log",
|
||||
"model",
|
||||
"pretty_env_logger",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.21"
|
||||
@ -1866,9 +1949,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.16"
|
||||
version = "0.4.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
@ -2006,6 +2089,20 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "model"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"derive_more",
|
||||
"serde",
|
||||
"sqlx",
|
||||
"sqlx-core",
|
||||
"thiserror",
|
||||
"uuid",
|
||||
"validator 0.15.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.10"
|
||||
@ -2206,6 +2303,22 @@ dependencies = [
|
||||
"opentelemetry",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "order_manager"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"actix 0.13.0",
|
||||
"actix-rt",
|
||||
"chrono",
|
||||
"config",
|
||||
"database_manager",
|
||||
"log",
|
||||
"model",
|
||||
"pretty_env_logger",
|
||||
"thiserror",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.11.2"
|
||||
@ -2295,6 +2408,26 @@ dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "payment_manager"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"actix 0.13.0",
|
||||
"actix-rt",
|
||||
"chrono",
|
||||
"config",
|
||||
"database_manager",
|
||||
"derive_more",
|
||||
"log",
|
||||
"model",
|
||||
"parking_lot 0.12.0",
|
||||
"pay_u",
|
||||
"pretty_env_logger",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.1.0"
|
||||
@ -2770,6 +2903,25 @@ dependencies = [
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "search_manager"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"actix 0.13.0",
|
||||
"actix-rt",
|
||||
"chrono",
|
||||
"config",
|
||||
"derive_more",
|
||||
"log",
|
||||
"model",
|
||||
"parking_lot 0.12.0",
|
||||
"pretty_env_logger",
|
||||
"serde",
|
||||
"sonic-channel",
|
||||
"thiserror",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.6.1"
|
||||
@ -3288,18 +3440,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.30"
|
||||
version = "1.0.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
|
||||
checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.30"
|
||||
version = "1.0.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
|
||||
checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -3396,11 +3548,36 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
||||
|
||||
[[package]]
|
||||
name = "token_manager"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"actix 0.13.0",
|
||||
"actix-rt",
|
||||
"argon2",
|
||||
"chrono",
|
||||
"config",
|
||||
"database_manager",
|
||||
"derive_more",
|
||||
"hmac",
|
||||
"jwt",
|
||||
"log",
|
||||
"model",
|
||||
"parking_lot 0.12.0",
|
||||
"password-hash",
|
||||
"pretty_env_logger",
|
||||
"rand_core",
|
||||
"serde",
|
||||
"sha2",
|
||||
"thiserror",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.18.0"
|
||||
version = "1.18.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f48b6d60512a392e34dbf7fd456249fd2de3c83669ab642e021903f4015185b"
|
||||
checksum = "dce653fb475565de9f6fb0614b28bca8df2c430c0cf84bcd9c843f15de5414cc"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"libc",
|
||||
@ -3764,6 +3941,21 @@ dependencies = [
|
||||
"validator_types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "validator"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f07b0a1390e01c0fc35ebb26b28ced33c9a3808f7f9fbe94d3cc01e233bfeed5"
|
||||
dependencies = [
|
||||
"idna",
|
||||
"lazy_static",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "validator_types"
|
||||
version = "0.14.0"
|
||||
|
14
Cargo.toml
14
Cargo.toml
@ -1,2 +1,14 @@
|
||||
[workspace]
|
||||
members = ["api", "web"]
|
||||
members = [
|
||||
"api",
|
||||
"web",
|
||||
"shared/model",
|
||||
"actors/cart_manager",
|
||||
"actors/database_manager",
|
||||
"actors/email_manager",
|
||||
"actors/order_manager",
|
||||
"actors/payment_manager",
|
||||
"actors/search_manager",
|
||||
"actors/token_manager",
|
||||
"actors/fs_manager",
|
||||
]
|
||||
|
20
actors/cart_manager/Cargo.toml
Normal file
20
actors/cart_manager/Cargo.toml
Normal file
@ -0,0 +1,20 @@
|
||||
[package]
|
||||
name = "cart_manager"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
model = { path = "../../shared/model" }
|
||||
config = { path = "../../shared/config" }
|
||||
database_manager = { path = "../database_manager" }
|
||||
|
||||
actix = { version = "0.13", features = [] }
|
||||
actix-rt = { version = "2.7", features = [] }
|
||||
|
||||
thiserror = { version = "1.0.31" }
|
||||
|
||||
uuid = { version = "0.8", features = ["serde"] }
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
|
||||
log = { version = "0.4", features = [] }
|
||||
pretty_env_logger = { version = "0.4", features = [] }
|
@ -1,7 +1,5 @@
|
||||
use actix::{Actor, Addr, Context, Message};
|
||||
|
||||
use crate::database::{self, Database};
|
||||
use crate::model::{
|
||||
use database_manager::{query_db, Database};
|
||||
use model::{
|
||||
AccountId, ProductId, Quantity, QuantityUnit, ShoppingCartId, ShoppingCartItem,
|
||||
ShoppingCartItemId, ShoppingCartState,
|
||||
};
|
||||
@ -30,7 +28,7 @@ pub enum Error {
|
||||
#[error("Failed to add item to cart")]
|
||||
CantAddItem,
|
||||
#[error("{0}")]
|
||||
Db(#[from] database::Error),
|
||||
Db(#[from] database_manager::Error),
|
||||
#[error("Unable to update cart item")]
|
||||
UpdateFailed,
|
||||
#[error("Failed to change quantity")]
|
||||
@ -42,20 +40,20 @@ pub enum Error {
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
pub struct CartManager {
|
||||
db: Addr<Database>,
|
||||
db: actix::Addr<Database>,
|
||||
}
|
||||
|
||||
impl Actor for CartManager {
|
||||
type Context = Context<Self>;
|
||||
impl actix::Actor for CartManager {
|
||||
type Context = actix::Context<Self>;
|
||||
}
|
||||
|
||||
impl CartManager {
|
||||
pub fn new(db: Addr<Database>) -> Self {
|
||||
pub fn new(db: actix::Addr<Database>) -> Self {
|
||||
Self { db }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Message)]
|
||||
#[derive(actix::Message)]
|
||||
#[rtype(result = "Result<ShoppingCartItem>")]
|
||||
pub struct AddItem {
|
||||
pub buyer_id: AccountId,
|
||||
@ -66,9 +64,9 @@ pub struct AddItem {
|
||||
|
||||
cart_async_handler!(AddItem, add_item, ShoppingCartItem);
|
||||
|
||||
async fn add_item(msg: AddItem, db: Addr<Database>) -> Result<ShoppingCartItem> {
|
||||
async fn add_item(msg: AddItem, db: actix::Addr<Database>) -> Result<ShoppingCartItem> {
|
||||
match db
|
||||
.send(database::EnsureActiveShoppingCart {
|
||||
.send(database_manager::EnsureActiveShoppingCart {
|
||||
buyer_id: msg.buyer_id,
|
||||
})
|
||||
.await
|
||||
@ -77,7 +75,7 @@ async fn add_item(msg: AddItem, db: Addr<Database>) -> Result<ShoppingCartItem>
|
||||
_ => return Err(Error::ShoppingCartFailed),
|
||||
};
|
||||
let cart = match db
|
||||
.send(database::AccountShoppingCarts {
|
||||
.send(database_manager::AccountShoppingCarts {
|
||||
account_id: msg.buyer_id,
|
||||
state: Some(ShoppingCartState::Active),
|
||||
})
|
||||
@ -95,7 +93,7 @@ async fn add_item(msg: AddItem, db: Addr<Database>) -> Result<ShoppingCartItem>
|
||||
_ => return Err(Error::CartNotAvailable),
|
||||
};
|
||||
match db
|
||||
.send(database::CreateShoppingCartItem {
|
||||
.send(database_manager::CreateShoppingCartItem {
|
||||
product_id: msg.product_id,
|
||||
shopping_cart_id: cart.id,
|
||||
quantity: msg.quantity,
|
||||
@ -108,7 +106,7 @@ async fn add_item(msg: AddItem, db: Addr<Database>) -> Result<ShoppingCartItem>
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Message)]
|
||||
#[derive(actix::Message)]
|
||||
#[rtype(result = "Result<Option<ShoppingCartItem>>")]
|
||||
pub struct RemoveProduct {
|
||||
pub shopping_cart_id: ShoppingCartId,
|
||||
@ -119,10 +117,10 @@ cart_async_handler!(RemoveProduct, remove_product, Option<ShoppingCartItem>);
|
||||
|
||||
pub(crate) async fn remove_product(
|
||||
msg: RemoveProduct,
|
||||
db: Addr<Database>,
|
||||
db: actix::Addr<Database>,
|
||||
) -> Result<Option<ShoppingCartItem>> {
|
||||
match db
|
||||
.send(database::RemoveCartItem {
|
||||
.send(database_manager::RemoveCartItem {
|
||||
shopping_cart_id: msg.shopping_cart_id,
|
||||
shopping_cart_item_id: Some(msg.shopping_cart_item_id),
|
||||
product_id: None,
|
||||
@ -141,7 +139,7 @@ pub(crate) async fn remove_product(
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Message)]
|
||||
#[derive(actix::Message)]
|
||||
#[rtype(result = "Result<Option<ShoppingCartItem>>")]
|
||||
pub struct ChangeQuantity {
|
||||
pub shopping_cart_id: ShoppingCartId,
|
||||
@ -154,7 +152,7 @@ cart_async_handler!(ChangeQuantity, change_quantity, Option<ShoppingCartItem>);
|
||||
|
||||
pub(crate) async fn change_quantity(
|
||||
msg: ChangeQuantity,
|
||||
db: Addr<Database>,
|
||||
db: actix::Addr<Database>,
|
||||
) -> Result<Option<ShoppingCartItem>> {
|
||||
if **msg.quantity == 0 {
|
||||
return remove_product(
|
||||
@ -166,41 +164,23 @@ pub(crate) async fn change_quantity(
|
||||
)
|
||||
.await;
|
||||
}
|
||||
let item: ShoppingCartItem = match db
|
||||
.send(database::FindShoppingCartItem {
|
||||
let item: ShoppingCartItem = query_db!(
|
||||
db,
|
||||
database_manager::FindShoppingCartItem {
|
||||
id: msg.shopping_cart_item_id,
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(Ok(row)) => row,
|
||||
Ok(Err(db_err)) => {
|
||||
log::error!("{db_err}");
|
||||
return Err(Error::NotExists(msg.shopping_cart_item_id));
|
||||
}
|
||||
Err(act_err) => {
|
||||
log::error!("{act_err:?}");
|
||||
return Err(Error::NotExists(msg.shopping_cart_item_id));
|
||||
}
|
||||
};
|
||||
},
|
||||
Error::NotExists(msg.shopping_cart_item_id)
|
||||
);
|
||||
|
||||
match db
|
||||
.send(database::UpdateShoppingCartItem {
|
||||
Ok(Some(query_db!(
|
||||
db,
|
||||
database_manager::UpdateShoppingCartItem {
|
||||
id: msg.shopping_cart_item_id,
|
||||
product_id: item.product_id,
|
||||
shopping_cart_id: item.shopping_cart_id,
|
||||
quantity: msg.quantity,
|
||||
quantity_unit: msg.quantity_unit,
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(Ok(row)) => Ok(Some(row)),
|
||||
Ok(Err(db_err)) => {
|
||||
log::error!("{db_err}");
|
||||
Err(Error::ChangeQuantity)
|
||||
}
|
||||
Err(act_err) => {
|
||||
log::error!("{act_err:?}");
|
||||
Err(Error::ChangeQuantity)
|
||||
}
|
||||
}
|
||||
},
|
||||
Error::ChangeQuantity
|
||||
)))
|
||||
}
|
22
actors/database_manager/Cargo.toml
Normal file
22
actors/database_manager/Cargo.toml
Normal file
@ -0,0 +1,22 @@
|
||||
[package]
|
||||
name = "database_manager"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
model = { path = "../../shared/model" }
|
||||
config = { path = "../../shared/config" }
|
||||
|
||||
actix = { version = "0.13", features = [] }
|
||||
actix-rt = { version = "2.7", features = [] }
|
||||
|
||||
sqlx = { version = "0.5", features = ["migrate", "runtime-actix-rustls", "all-types", "postgres"] }
|
||||
sqlx-core = { version = "0.5", features = [] }
|
||||
|
||||
thiserror = { version = "1.0.31" }
|
||||
|
||||
uuid = { version = "0.8", features = ["serde"] }
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
|
||||
log = { version = "0.4", features = [] }
|
||||
pretty_env_logger = { version = "0.4", features = [] }
|
@ -1,11 +1,11 @@
|
||||
use model::*;
|
||||
use sqlx::PgPool;
|
||||
|
||||
use super::Result;
|
||||
use crate::database::{
|
||||
create_order_item, shopping_cart_set_state, CreateOrderItem, Database, ShoppingCartSetState,
|
||||
use crate::{
|
||||
create_order_item, db_async_handler, shopping_cart_set_state, CreateOrderItem, Database,
|
||||
ShoppingCartSetState,
|
||||
};
|
||||
use crate::db_async_handler;
|
||||
use crate::model::*;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
@ -45,7 +45,7 @@ ORDER BY id DESC
|
||||
}
|
||||
|
||||
pub mod create_order {
|
||||
use crate::model::{ProductId, Quantity, QuantityUnit};
|
||||
use model::{ProductId, Quantity, QuantityUnit};
|
||||
|
||||
pub struct OrderItem {
|
||||
pub product_id: ProductId,
|
@ -1,9 +1,8 @@
|
||||
use model::{AccountId, AccountState, Email, FullAccount, Login, PassHash, Role};
|
||||
use sqlx::PgPool;
|
||||
|
||||
use super::Result;
|
||||
use crate::database::Database;
|
||||
use crate::db_async_handler;
|
||||
use crate::model::{AccountId, AccountState, Email, FullAccount, Login, PassHash, Role};
|
||||
use crate::{db_async_handler, Database};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
@ -1,6 +1,7 @@
|
||||
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::*;
|
||||
@ -9,8 +10,6 @@ use sqlx::PgPool;
|
||||
pub use stocks::*;
|
||||
pub use tokens::*;
|
||||
|
||||
use crate::config::SharedAppConfig;
|
||||
|
||||
pub mod account_orders;
|
||||
pub mod accounts;
|
||||
pub mod order_items;
|
||||
@ -134,7 +133,7 @@ impl Clone for Database {
|
||||
}
|
||||
|
||||
impl Database {
|
||||
pub(crate) async fn build(config: SharedAppConfig) -> Result<Self> {
|
||||
pub async fn build(config: SharedAppConfig) -> Result<Self> {
|
||||
let url = config.lock().database().url();
|
||||
let pool = sqlx::PgPool::connect(&url).await.map_err(Error::Connect)?;
|
||||
Ok(Database { pool })
|
@ -1,9 +1,8 @@
|
||||
use model::*;
|
||||
use sqlx::PgPool;
|
||||
|
||||
use super::Result;
|
||||
use crate::database::Database;
|
||||
use crate::model::*;
|
||||
use crate::{db_async_handler, model};
|
||||
use crate::{db_async_handler, Database};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
@ -1,12 +1,11 @@
|
||||
use actix::Message;
|
||||
|
||||
use super::Result;
|
||||
use crate::database::Database;
|
||||
use crate::model::{
|
||||
use model::{
|
||||
Days, Price, Product, ProductCategory, ProductId, ProductLongDesc, ProductName,
|
||||
ProductShortDesc,
|
||||
};
|
||||
use crate::{database, model};
|
||||
|
||||
use super::Result;
|
||||
use crate::Database;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
@ -48,7 +47,7 @@ FROM products
|
||||
.await
|
||||
.map_err(|e| {
|
||||
log::error!("{e:?}");
|
||||
database::Error::Product(Error::All)
|
||||
crate::Error::Product(Error::All)
|
||||
})
|
||||
}
|
||||
|
||||
@ -92,7 +91,7 @@ RETURNING id,
|
||||
.await
|
||||
.map_err(|e| {
|
||||
log::error!("{e:?}");
|
||||
database::Error::Product(Error::Create)
|
||||
crate::Error::Product(Error::Create)
|
||||
})
|
||||
}
|
||||
|
||||
@ -144,7 +143,7 @@ RETURNING id,
|
||||
.await
|
||||
.map_err(|e| {
|
||||
log::error!("{e:?}");
|
||||
database::Error::Product(Error::Update)
|
||||
crate::Error::Product(Error::Update)
|
||||
})
|
||||
}
|
||||
|
||||
@ -183,7 +182,7 @@ RETURNING id,
|
||||
.await
|
||||
.map_err(|e| {
|
||||
log::error!("{e:?}");
|
||||
database::Error::Product(Error::Delete)
|
||||
crate::Error::Product(Error::Delete)
|
||||
})
|
||||
}
|
||||
|
||||
@ -226,6 +225,6 @@ WHERE shopping_cart_id = $1
|
||||
.await
|
||||
.map_err(|e| {
|
||||
log::error!("{e:?}");
|
||||
database::Error::Product(Error::ShoppingCartProducts)
|
||||
crate::Error::Product(Error::ShoppingCartProducts)
|
||||
})
|
||||
}
|
@ -1,9 +1,8 @@
|
||||
use model::*;
|
||||
use sqlx::PgPool;
|
||||
|
||||
use super::Result;
|
||||
use crate::database::Database;
|
||||
use crate::model::*;
|
||||
use crate::{database, db_async_handler};
|
||||
use crate::{db_async_handler, Database};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
@ -253,7 +252,7 @@ WHERE shopping_cart_id = $1
|
||||
.await
|
||||
.map_err(|e| {
|
||||
log::error!("{e:?}");
|
||||
super::Error::ShoppingCartItem(Error::CartItems(shopping_cart_id))
|
||||
crate::Error::ShoppingCartItem(Error::CartItems(shopping_cart_id))
|
||||
})
|
||||
}
|
||||
|
||||
@ -300,13 +299,13 @@ RETURNING id, product_id, shopping_cart_id, quantity, quantity_unit
|
||||
)
|
||||
.bind(msg.shopping_cart_id)
|
||||
.bind(product_id),
|
||||
_ => return Err(database::Error::ShoppingCartItem(Error::NoIdentity)),
|
||||
_ => return Err(crate::Error::ShoppingCartItem(Error::NoIdentity)),
|
||||
}
|
||||
.fetch_optional(&pool)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
log::error!("{e:?}");
|
||||
database::Error::ShoppingCartItem(Error::Update {
|
||||
crate::Error::ShoppingCartItem(Error::Update {
|
||||
shopping_cart_item_id: msg.shopping_cart_item_id,
|
||||
product_id: msg.product_id,
|
||||
})
|
@ -1,9 +1,8 @@
|
||||
use model::*;
|
||||
use sqlx::PgPool;
|
||||
|
||||
use super::Result;
|
||||
use crate::database::Database;
|
||||
use crate::db_async_handler;
|
||||
use crate::model::*;
|
||||
use crate::{db_async_handler, Database};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
@ -1,10 +1,8 @@
|
||||
use actix::Message;
|
||||
use model::{ProductId, Quantity, QuantityUnit, Stock, StockId};
|
||||
use sqlx::PgPool;
|
||||
|
||||
use super::Result;
|
||||
use crate::database::Database;
|
||||
use crate::model::{ProductId, Quantity, QuantityUnit, Stock, StockId};
|
||||
use crate::{database, model};
|
||||
use crate::{Database, Result};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
@ -35,7 +33,7 @@ FROM stocks
|
||||
.await
|
||||
.map_err(|e| {
|
||||
log::error!("{e:?}");
|
||||
database::Error::Stock(Error::All)
|
||||
crate::Error::Stock(Error::All)
|
||||
})
|
||||
}
|
||||
|
||||
@ -64,7 +62,7 @@ RETURNING id, product_id, quantity, quantity_unit
|
||||
.await
|
||||
.map_err(|e| {
|
||||
log::error!("{e:?}");
|
||||
database::Error::Stock(Error::Create)
|
||||
crate::Error::Stock(Error::Create)
|
||||
})
|
||||
}
|
||||
|
||||
@ -98,7 +96,7 @@ RETURNING id, product_id, quantity, quantity_unit
|
||||
.await
|
||||
.map_err(|e| {
|
||||
log::error!("{e:?}");
|
||||
database::Error::Stock(Error::Update)
|
||||
crate::Error::Stock(Error::Update)
|
||||
})
|
||||
}
|
||||
|
||||
@ -123,6 +121,6 @@ RETURNING id, product_id, quantity, quantity_unit
|
||||
.await
|
||||
.map_err(|e| {
|
||||
log::error!("{e:?}");
|
||||
database::Error::Stock(Error::Delete)
|
||||
crate::Error::Stock(Error::Delete)
|
||||
})
|
||||
}
|
@ -1,10 +1,8 @@
|
||||
use actix::Message;
|
||||
use model::{AccountId, Audience, Token};
|
||||
use sqlx::PgPool;
|
||||
|
||||
use super::Result;
|
||||
use crate::database::Database;
|
||||
use crate::model::{AccountId, Audience, Token};
|
||||
use crate::{database, db_async_handler, Role};
|
||||
use crate::{db_async_handler, Database, Result};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
@ -33,7 +31,7 @@ WHERE jwt_id = $1
|
||||
.await
|
||||
.map_err(|e| {
|
||||
log::error!("{e:?}");
|
||||
database::Error::Token(Error::Jti)
|
||||
crate::Error::Token(Error::Jti)
|
||||
})
|
||||
}
|
||||
|
||||
@ -41,7 +39,7 @@ WHERE jwt_id = $1
|
||||
#[rtype(result = "Result<Token>")]
|
||||
pub struct CreateToken {
|
||||
pub customer_id: uuid::Uuid,
|
||||
pub role: Role,
|
||||
pub role: model::Role,
|
||||
pub subject: AccountId,
|
||||
pub audience: Audience,
|
||||
}
|
||||
@ -68,6 +66,6 @@ RETURNING id, customer_id, role, issuer, subject, audience, expiration_time, not
|
||||
.await
|
||||
.map_err(|e| {
|
||||
log::error!("{e:?}");
|
||||
database::Error::Token(Error::Create)
|
||||
crate::Error::Token(Error::Create)
|
||||
})
|
||||
}
|
21
actors/email_manager/Cargo.toml
Normal file
21
actors/email_manager/Cargo.toml
Normal file
@ -0,0 +1,21 @@
|
||||
[package]
|
||||
name = "email_manager"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
model = { path = "../../shared/model" }
|
||||
config = { path = "../../shared/config" }
|
||||
|
||||
actix = { version = "0.13", features = [] }
|
||||
actix-rt = { version = "2.7", features = [] }
|
||||
|
||||
sendgrid = { version = "0.17", features = ["async"] }
|
||||
|
||||
thiserror = { version = "1.0.31" }
|
||||
|
||||
uuid = { version = "0.8", features = ["serde"] }
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
|
||||
log = { version = "0.4", features = [] }
|
||||
pretty_env_logger = { version = "0.4", features = [] }
|
@ -1,7 +1,6 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::config::SharedAppConfig;
|
||||
use crate::Email;
|
||||
use config::SharedAppConfig;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! mail_async_handler {
|
||||
@ -50,7 +49,7 @@ impl EmailManager {
|
||||
#[derive(actix::Message)]
|
||||
#[rtype(result = "Result<SendState>")]
|
||||
pub struct TestMail {
|
||||
pub receiver: Email,
|
||||
pub receiver: model::Email,
|
||||
}
|
||||
|
||||
mail_async_handler!(TestMail, test_mail, SendState);
|
21
actors/fs_manager/Cargo.toml
Normal file
21
actors/fs_manager/Cargo.toml
Normal file
@ -0,0 +1,21 @@
|
||||
[package]
|
||||
name = "fs_manager"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
model = { path = "../../shared/model" }
|
||||
config = { path = "../../shared/config" }
|
||||
|
||||
actix = { version = "0.13", features = [] }
|
||||
actix-rt = { version = "2.7", features = [] }
|
||||
|
||||
thiserror = { version = "1.0.31" }
|
||||
|
||||
uuid = { version = "0.8", features = ["serde"] }
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
|
||||
log = { version = "0.4", features = [] }
|
||||
pretty_env_logger = { version = "0.4", features = [] }
|
||||
|
||||
tokio = { version = "1.18.1", features = ["full"] }
|
145
actors/fs_manager/src/lib.rs
Normal file
145
actors/fs_manager/src/lib.rs
Normal file
@ -0,0 +1,145 @@
|
||||
#![feature(io_error_more)]
|
||||
|
||||
use std::ffi::OsStr;
|
||||
use std::io::Write;
|
||||
|
||||
use config::SharedAppConfig;
|
||||
use model::FileName;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! fs_async_handler {
|
||||
($msg: ty, $async: ident, $res: ty) => {
|
||||
impl actix::Handler<$msg> for FsManager {
|
||||
type Result = actix::ResponseActFuture<Self, Result<$res>>;
|
||||
|
||||
fn handle(&mut self, msg: $msg, _ctx: &mut Self::Context) -> Self::Result {
|
||||
use actix::WrapFuture;
|
||||
let config = self.config.clone();
|
||||
Box::pin(async { $async(msg, config).await }.into_actor(self))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
#[error("Can't access file system. Please check privileges")]
|
||||
StorageUnavailable,
|
||||
#[error("Can't write to file. Please check privileges. {0:?}")]
|
||||
CantWrite(std::io::Error),
|
||||
#[error("Can't write to file. There's no more space on disk")]
|
||||
NoSpace,
|
||||
#[error("Can't remove file. Please check privileges. {0:?}")]
|
||||
CantRemove(std::io::Error),
|
||||
#[error("Can't write to file. Invalid path, no filename")]
|
||||
InvalidPath,
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
pub struct FsManager {
|
||||
config: SharedAppConfig,
|
||||
}
|
||||
|
||||
impl actix::Actor for FsManager {
|
||||
type Context = actix::Context<FsManager>;
|
||||
}
|
||||
|
||||
impl FsManager {
|
||||
pub async fn build(config: SharedAppConfig) -> Result<Self> {
|
||||
Self::validate_fs(config.clone()).await?;
|
||||
Ok(Self { config })
|
||||
}
|
||||
|
||||
async fn validate_fs(config: SharedAppConfig) -> Result<()> {
|
||||
let l = config.lock();
|
||||
let output_path = l.files().local_path();
|
||||
let test_file = std::path::PathBuf::new()
|
||||
.join(output_path)
|
||||
.join(format!("{}", uuid::Uuid::new_v4()));
|
||||
tokio::fs::remove_file(test_file.clone()).await.ok();
|
||||
tokio::fs::write(test_file.clone(), b"1")
|
||||
.await
|
||||
.map_err(|_| Error::StorageUnavailable)?;
|
||||
tokio::fs::remove_file(test_file.clone())
|
||||
.await
|
||||
.map_err(|_| Error::StorageUnavailable)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(actix::Message)]
|
||||
#[rtype(result = "Result<()>")]
|
||||
pub struct RemoveFile {
|
||||
pub file_name: String,
|
||||
}
|
||||
|
||||
fs_async_handler!(RemoveFile, remove_file, ());
|
||||
|
||||
pub(crate) async fn remove_file(msg: RemoveFile, config: SharedAppConfig) -> Result<()> {
|
||||
let output_path = {
|
||||
let l = config.lock();
|
||||
l.files().local_path()
|
||||
};
|
||||
match tokio::fs::remove_file(
|
||||
std::path::PathBuf::new()
|
||||
.join(output_path)
|
||||
.join(msg.file_name),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(()),
|
||||
Err(e) => Err(Error::CantRemove(e)),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(actix::Message)]
|
||||
#[rtype(result = "Result<FileName>")]
|
||||
pub struct WriteFile {
|
||||
pub file_name: String,
|
||||
pub stream: tokio::sync::mpsc::UnboundedReceiver<u8>,
|
||||
}
|
||||
|
||||
fs_async_handler!(WriteFile, write_file, FileName);
|
||||
|
||||
pub(crate) async fn write_file(msg: WriteFile, config: SharedAppConfig) -> Result<FileName> {
|
||||
let WriteFile {
|
||||
file_name,
|
||||
mut stream,
|
||||
} = msg;
|
||||
|
||||
let p = std::path::Path::new(&file_name);
|
||||
let ext = p
|
||||
.file_name()
|
||||
.and_then(OsStr::to_str)
|
||||
.map(std::path::Path::new)
|
||||
.and_then(std::path::Path::extension)
|
||||
.and_then(OsStr::to_str)
|
||||
.map(String::from);
|
||||
|
||||
let file_name = format!(
|
||||
"{}{}",
|
||||
uuid::Uuid::new_v4(),
|
||||
ext.map(|s| format!(".{s}")).unwrap_or_default()
|
||||
);
|
||||
|
||||
let output_path = {
|
||||
let l = config.lock();
|
||||
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) {
|
||||
Ok(f) => f,
|
||||
Err(e) => return Err(Error::CantWrite(e)),
|
||||
};
|
||||
|
||||
while let Some(b) = stream.recv().await {
|
||||
match file.write(&[b]) {
|
||||
Ok(_) => {}
|
||||
Err(e) if e.kind() == std::io::ErrorKind::StorageFull => return Err(Error::NoSpace),
|
||||
Err(e) => return Err(Error::CantWrite(e)),
|
||||
}
|
||||
}
|
||||
Ok(FileName::from(file_name))
|
||||
}
|
20
actors/order_manager/Cargo.toml
Normal file
20
actors/order_manager/Cargo.toml
Normal file
@ -0,0 +1,20 @@
|
||||
[package]
|
||||
name = "order_manager"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
model = { path = "../../shared/model" }
|
||||
config = { path = "../../shared/config" }
|
||||
database_manager = { path = "../database_manager" }
|
||||
|
||||
actix = { version = "0.13", features = [] }
|
||||
actix-rt = { version = "2.7", features = [] }
|
||||
|
||||
thiserror = { version = "1.0.31" }
|
||||
|
||||
uuid = { version = "0.8", features = ["serde"] }
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
|
||||
log = { version = "0.4", features = [] }
|
||||
pretty_env_logger = { version = "0.4", features = [] }
|
@ -1,10 +1,7 @@
|
||||
use actix::Message;
|
||||
|
||||
use crate::config::SharedAppConfig;
|
||||
use crate::database::{self, SharedDatabase};
|
||||
use crate::model::{
|
||||
AccountId, AccountOrder, OrderStatus, ShoppingCart, ShoppingCartId, ShoppingCartItem,
|
||||
};
|
||||
use config::SharedAppConfig;
|
||||
use database_manager::{query_db, SharedDatabase};
|
||||
use model::{AccountId, AccountOrder, OrderStatus, ShoppingCart, ShoppingCartId, ShoppingCartItem};
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! order_async_handler {
|
||||
@ -63,64 +60,40 @@ pub(crate) async fn create_order(
|
||||
db: SharedDatabase,
|
||||
_config: SharedAppConfig,
|
||||
) -> Result<AccountOrder> {
|
||||
let cart: ShoppingCart = match db
|
||||
.send(database::FindShoppingCart {
|
||||
let cart: ShoppingCart = query_db!(
|
||||
db,
|
||||
database_manager::FindShoppingCart {
|
||||
id: msg.shopping_cart_id,
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(Ok(cart)) => cart,
|
||||
Ok(Err(e)) => {
|
||||
log::error!("{e}");
|
||||
return Err(Error::ShoppingCart);
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("{e:?}");
|
||||
return Err(Error::DatabaseInternal);
|
||||
}
|
||||
};
|
||||
let items: Vec<ShoppingCartItem> = match db
|
||||
.send(database::AccountShoppingCartItems {
|
||||
},
|
||||
Error::ShoppingCart,
|
||||
Error::DatabaseInternal
|
||||
);
|
||||
let items: Vec<ShoppingCartItem> = query_db!(
|
||||
db,
|
||||
database_manager::AccountShoppingCartItems {
|
||||
account_id: cart.buyer_id,
|
||||
shopping_cart_id: Some(cart.id),
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(Ok(items)) => items,
|
||||
Ok(Err(e)) => {
|
||||
log::error!("{e}");
|
||||
return Err(Error::ShoppingCart);
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("{e:?}");
|
||||
return Err(Error::DatabaseInternal);
|
||||
}
|
||||
};
|
||||
let order = match db
|
||||
.send(database::CreateAccountOrder {
|
||||
},
|
||||
Error::ShoppingCart,
|
||||
Error::DatabaseInternal
|
||||
);
|
||||
let order = query_db!(
|
||||
db,
|
||||
database_manager::CreateAccountOrder {
|
||||
shopping_cart_id: cart.id,
|
||||
buyer_id: msg.account_id,
|
||||
items: items
|
||||
.into_iter()
|
||||
.map(|item| database::create_order::OrderItem {
|
||||
.map(|item| database_manager::create_order::OrderItem {
|
||||
product_id: item.product_id,
|
||||
quantity: item.quantity,
|
||||
quantity_unit: item.quantity_unit,
|
||||
})
|
||||
.collect(),
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(Ok(order)) => order,
|
||||
Ok(Err(e)) => {
|
||||
log::error!("{e}");
|
||||
return Err(Error::CreateAccountOrder);
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("{e:?}");
|
||||
return Err(Error::DatabaseInternal);
|
||||
}
|
||||
};
|
||||
},
|
||||
Error::CreateAccountOrder,
|
||||
Error::DatabaseInternal
|
||||
);
|
||||
|
||||
Ok(order)
|
||||
}
|
27
actors/payment_manager/Cargo.toml
Normal file
27
actors/payment_manager/Cargo.toml
Normal file
@ -0,0 +1,27 @@
|
||||
[package]
|
||||
name = "payment_manager"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
model = { path = "../../shared/model" }
|
||||
config = { path = "../../shared/config" }
|
||||
database_manager = { path = "../database_manager" }
|
||||
|
||||
actix = { version = "0.13", features = [] }
|
||||
actix-rt = { version = "2.7", features = [] }
|
||||
|
||||
thiserror = { version = "1.0.31" }
|
||||
|
||||
uuid = { version = "0.8", features = ["serde"] }
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
|
||||
log = { version = "0.4", features = [] }
|
||||
pretty_env_logger = { version = "0.4", features = [] }
|
||||
|
||||
derive_more = { version = "0.99", features = [] }
|
||||
parking_lot = { version = "0.12", features = [] }
|
||||
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
||||
pay_u = { version = '0.1', features = ["single-client"] }
|
@ -2,13 +2,11 @@ use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use actix::Addr;
|
||||
use config::SharedAppConfig;
|
||||
use database_manager::{query_db, Database};
|
||||
use model::{AccountId, OrderStatus, Price, ProductId, Quantity, QuantityUnit};
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use crate::config::SharedAppConfig;
|
||||
use crate::database::Database;
|
||||
use crate::model::{AccountId, OrderStatus, Price, ProductId, Quantity, QuantityUnit};
|
||||
use crate::{database, model, query_db};
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! pay_async_handler {
|
||||
($msg: ty, $async: ident, $res: ty) => {
|
||||
@ -183,14 +181,14 @@ pub(crate) async fn request_payment(
|
||||
|
||||
let cart: model::ShoppingCart = query_db!(
|
||||
db,
|
||||
database::EnsureActiveShoppingCart {
|
||||
database_manager::EnsureActiveShoppingCart {
|
||||
buyer_id: msg.buyer_id
|
||||
},
|
||||
Error::UnavailableShoppingCart
|
||||
);
|
||||
let cart_items: Vec<model::ShoppingCartItem> = query_db!(
|
||||
db,
|
||||
database::CartItems {
|
||||
database_manager::CartItems {
|
||||
shopping_cart_id: cart.id,
|
||||
},
|
||||
Error::UnavailableShoppingCart
|
||||
@ -205,7 +203,7 @@ pub(crate) async fn request_payment(
|
||||
|
||||
let cart_products: Vec<model::Product> = query_db!(
|
||||
db,
|
||||
database::ShoppingCartProducts {
|
||||
database_manager::ShoppingCartProducts {
|
||||
shopping_cart_id: cart.id,
|
||||
},
|
||||
Error::UnavailableShoppingCart
|
||||
@ -213,7 +211,7 @@ pub(crate) async fn request_payment(
|
||||
|
||||
let db_order: model::AccountOrder = query_db!(
|
||||
db,
|
||||
database::CreateAccountOrder {
|
||||
database_manager::CreateAccountOrder {
|
||||
buyer_id: msg.buyer_id,
|
||||
items: cart_products
|
||||
.iter()
|
||||
@ -225,7 +223,7 @@ pub(crate) async fn request_payment(
|
||||
model::QuantityUnit::Gram,
|
||||
)
|
||||
});
|
||||
database::create_order::OrderItem {
|
||||
database_manager::create_order::OrderItem {
|
||||
product_id: product.id,
|
||||
quantity,
|
||||
quantity_unit,
|
||||
@ -276,7 +274,7 @@ pub(crate) async fn request_payment(
|
||||
|
||||
query_db!(
|
||||
db,
|
||||
database::SetOrderServiceId {
|
||||
database_manager::SetOrderServiceId {
|
||||
service_order_id: order_id.0,
|
||||
id: db_order.id,
|
||||
},
|
||||
@ -289,7 +287,7 @@ pub(crate) async fn request_payment(
|
||||
|
||||
let order_items = query_db!(
|
||||
db,
|
||||
database::OrderItems {
|
||||
database_manager::OrderItems {
|
||||
order_id: db_order.id
|
||||
},
|
||||
Error::CreateOrder
|
||||
@ -332,7 +330,7 @@ pub(crate) async fn update_payment(
|
||||
pay_u::PaymentStatus::Canceled => OrderStatus::Cancelled,
|
||||
};
|
||||
let _ = db
|
||||
.send(database::UpdateAccountOrderByExt {
|
||||
.send(database_manager::UpdateAccountOrderByExt {
|
||||
status,
|
||||
order_ext_id,
|
||||
})
|
26
actors/search_manager/Cargo.toml
Normal file
26
actors/search_manager/Cargo.toml
Normal file
@ -0,0 +1,26 @@
|
||||
[package]
|
||||
name = "search_manager"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
model = { path = "../../shared/model" }
|
||||
config = { path = "../../shared/config" }
|
||||
|
||||
actix = { version = "0.13", features = [] }
|
||||
actix-rt = { version = "2.7", features = [] }
|
||||
|
||||
thiserror = { version = "1.0.31" }
|
||||
|
||||
uuid = { version = "0.8", features = ["serde"] }
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
|
||||
log = { version = "0.4", features = [] }
|
||||
pretty_env_logger = { version = "0.4", features = [] }
|
||||
|
||||
derive_more = { version = "0.99", features = [] }
|
||||
parking_lot = { version = "0.12", features = [] }
|
||||
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
||||
sonic-channel = { version = "0.6.0", features = ["ingest"] }
|
140
actors/search_manager/src/lib.rs
Normal file
140
actors/search_manager/src/lib.rs
Normal file
@ -0,0 +1,140 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use config::SharedAppConfig;
|
||||
use sonic_channel::SonicChannel;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! search_async_handler {
|
||||
($msg: ty, $async: ident, $res: ty) => {
|
||||
impl actix::Handler<$msg> for SearchManager {
|
||||
type Result = actix::ResponseActFuture<Self, Result<Option<$res>>>;
|
||||
|
||||
fn handle(&mut self, msg: $msg, _ctx: &mut Self::Context) -> Self::Result {
|
||||
use actix::WrapFuture;
|
||||
match self.channels.clone() {
|
||||
Some(channels) => {
|
||||
let config = self.config.clone();
|
||||
Box::pin(async { $async(msg, channels, config).await }.into_actor(self))
|
||||
}
|
||||
None => Box::pin(async { Ok(None) }.into_actor(self)),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
#[error("Can't create index")]
|
||||
CantCreate,
|
||||
#[error("Failed to find records in bucket")]
|
||||
QueryFailed,
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Channels {
|
||||
search: Arc<Mutex<sonic_channel::SearchChannel>>,
|
||||
ingest: Arc<Mutex<sonic_channel::IngestChannel>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SearchManager {
|
||||
channels: Option<Channels>,
|
||||
config: SharedAppConfig,
|
||||
}
|
||||
|
||||
impl SearchManager {
|
||||
pub fn new(config: SharedAppConfig) -> Self {
|
||||
let enabled = config.lock().search().search_active();
|
||||
|
||||
let channels = if enabled {
|
||||
let search = {
|
||||
let l = config.lock();
|
||||
Arc::new(Mutex::new(
|
||||
sonic_channel::SearchChannel::start(
|
||||
l.search().sonic_search_addr(),
|
||||
l.search().sonic_search_pass(),
|
||||
)
|
||||
.expect("Failed to connect to sonic search channel"),
|
||||
))
|
||||
};
|
||||
let ingest = {
|
||||
let l = config.lock();
|
||||
Arc::new(Mutex::new(
|
||||
sonic_channel::IngestChannel::start(
|
||||
l.search().sonic_ingest_addr(),
|
||||
l.search().sonic_ingest_pass(),
|
||||
)
|
||||
.expect("Failed to connect to sonic ingest channel"),
|
||||
))
|
||||
};
|
||||
Some(Channels { search, ingest })
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Self { channels, config }
|
||||
}
|
||||
}
|
||||
|
||||
impl actix::Actor for SearchManager {
|
||||
type Context = actix::Context<Self>;
|
||||
}
|
||||
|
||||
#[derive(actix::Message)]
|
||||
#[rtype(result = "Result<Option<Vec<String>>>")]
|
||||
pub struct Search {
|
||||
pub query: String,
|
||||
pub collection: String,
|
||||
pub lang: String,
|
||||
}
|
||||
|
||||
search_async_handler!(Search, search, Vec<String>);
|
||||
|
||||
pub(crate) async fn search(
|
||||
msg: Search,
|
||||
channels: Channels,
|
||||
_config: SharedAppConfig,
|
||||
) -> Result<Option<Vec<String>>> {
|
||||
if let Ok(l) = channels.search.lock() {
|
||||
match l.query(&msg.collection, &msg.lang, &msg.query) {
|
||||
Ok(res) => Ok(Some(res)),
|
||||
Err(e) => {
|
||||
log::error!("{e:?}");
|
||||
Err(Error::QueryFailed)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Ok(Some(vec![]))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(actix::Message)]
|
||||
#[rtype(result = "Result<Option<()>>")]
|
||||
pub struct CreateIndex {
|
||||
pub key: String,
|
||||
pub value: String,
|
||||
pub collection: String,
|
||||
pub lang: String,
|
||||
}
|
||||
|
||||
search_async_handler!(CreateIndex, create_index, ());
|
||||
|
||||
pub(crate) async fn create_index(
|
||||
msg: CreateIndex,
|
||||
channels: Channels,
|
||||
_config: SharedAppConfig,
|
||||
) -> Result<Option<()>> {
|
||||
if let Ok(l) = channels.ingest.lock() {
|
||||
match l.push(&msg.collection, &msg.lang, &msg.key, &msg.value) {
|
||||
Ok(_) => Ok(Some(())),
|
||||
Err(e) => {
|
||||
log::error!("{e:?}");
|
||||
Err(Error::CantCreate)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Ok(Some(()))
|
||||
}
|
||||
}
|
33
actors/token_manager/Cargo.toml
Normal file
33
actors/token_manager/Cargo.toml
Normal file
@ -0,0 +1,33 @@
|
||||
[package]
|
||||
name = "token_manager"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
model = { path = "../../shared/model" }
|
||||
config = { path = "../../shared/config" }
|
||||
database_manager = { path = "../database_manager" }
|
||||
|
||||
actix = { version = "0.13", features = [] }
|
||||
actix-rt = { version = "2.7", features = [] }
|
||||
|
||||
thiserror = { version = "1.0.31" }
|
||||
|
||||
uuid = { version = "0.8", features = ["serde"] }
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
|
||||
log = { version = "0.4", features = [] }
|
||||
pretty_env_logger = { version = "0.4", features = [] }
|
||||
|
||||
derive_more = { version = "0.99", features = [] }
|
||||
parking_lot = { version = "0.12", features = [] }
|
||||
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
||||
password-hash = { version = "0.4", features = ["alloc"] }
|
||||
argon2 = { version = "0.4", features = ["parallel", "password-hash"] }
|
||||
rand_core = { version = "0.6", features = ["std"] }
|
||||
|
||||
jwt = { version = "0.16", features = [] }
|
||||
hmac = { version = "0.12", features = [] }
|
||||
sha2 = { version = "0.10", features = [] }
|
@ -3,15 +3,13 @@ use std::str::FromStr;
|
||||
|
||||
use actix::{Addr, Message};
|
||||
use chrono::prelude::*;
|
||||
use config::SharedAppConfig;
|
||||
use database_manager::{query_db, Database};
|
||||
use hmac::digest::KeyInit;
|
||||
use hmac::Hmac;
|
||||
use model::{AccountId, Audience, Role, Token, TokenString};
|
||||
use sha2::Sha256;
|
||||
|
||||
use crate::config::SharedAppConfig;
|
||||
use crate::database::{Database, TokenByJti};
|
||||
use crate::model::{AccountId, Audience, Token, TokenString};
|
||||
use crate::{database, Role};
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! token_async_handler {
|
||||
($msg: ty, $async: ident, $res: ty) => {
|
||||
@ -105,25 +103,17 @@ pub(crate) async fn create_token(
|
||||
} = msg;
|
||||
let audience = audience.unwrap_or_default();
|
||||
|
||||
let token: Token = match db
|
||||
.send(database::CreateToken {
|
||||
let token: Token = query_db!(
|
||||
db,
|
||||
database_manager::CreateToken {
|
||||
customer_id,
|
||||
role,
|
||||
subject,
|
||||
audience,
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(Ok(token)) => token,
|
||||
Ok(Err(db_err)) => {
|
||||
log::error!("{db_err}");
|
||||
return Err(Error::Save);
|
||||
}
|
||||
Err(act_err) => {
|
||||
log::error!("{act_err:?}");
|
||||
return Err(Error::SaveInternal);
|
||||
}
|
||||
};
|
||||
},
|
||||
Error::Save,
|
||||
Error::SaveInternal
|
||||
);
|
||||
|
||||
let token_string = {
|
||||
use jwt::SignWithKey;
|
||||
@ -212,25 +202,17 @@ pub(crate) async fn validate(
|
||||
_ => return Err(Error::Validate),
|
||||
};
|
||||
|
||||
let token: Token = match db
|
||||
.send(TokenByJti {
|
||||
let token: Token = query_db!(
|
||||
db,
|
||||
database_manager::TokenByJti {
|
||||
jti: match uuid::Uuid::from_str(jti) {
|
||||
Ok(uid) => uid,
|
||||
_ => return Err(Error::Validate),
|
||||
},
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(Ok(token)) => token,
|
||||
Ok(Err(e)) => {
|
||||
log::error!("{e}");
|
||||
return Err(Error::Validate);
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("{e:?}");
|
||||
return Err(Error::ValidateInternal);
|
||||
}
|
||||
};
|
||||
},
|
||||
Error::Validate,
|
||||
Error::ValidateInternal
|
||||
);
|
||||
|
||||
if !validate_pair(&claims, "cti", token.customer_id, validate_uuid) {
|
||||
return Ok((token, false));
|
@ -4,6 +4,16 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
model = { path = "../shared/model", version = "0.1", features = ["db"] }
|
||||
config = { path = "../shared/config" }
|
||||
database_manager = { path = "../actors/database_manager" }
|
||||
cart_manager = { path = "../actors/cart_manager" }
|
||||
email_manager = { path = "../actors/email_manager" }
|
||||
order_manager = { path = "../actors/order_manager" }
|
||||
payment_manager = { path = "../actors/payment_manager" }
|
||||
search_manager = { path = "../actors/search_manager" }
|
||||
token_manager = { path = "../actors/token_manager" }
|
||||
|
||||
actix = { version = "0.13", features = [] }
|
||||
actix-rt = { version = "2.7", features = [] }
|
||||
actix-web = { version = "4.0", features = [] }
|
||||
@ -53,21 +63,11 @@ tokio = { version = "1.17", features = ["full"] }
|
||||
futures = { version = "0.3", features = [] }
|
||||
futures-util = { version = "0.3", features = [] }
|
||||
|
||||
jwt = { version = "0.16", features = [] }
|
||||
hmac = { version = "0.12", features = [] }
|
||||
sha2 = { version = "0.10", features = [] }
|
||||
|
||||
oauth2 = { version = "4.1", features = [] }
|
||||
|
||||
async-trait = { version = "0.1", features = [] }
|
||||
|
||||
jemallocator = { version = "0.3", features = [] }
|
||||
|
||||
sendgrid = { version = "0.17", features = ["async"] }
|
||||
|
||||
pay_u = { version = '0.1', features = ["single-client"] }
|
||||
|
||||
sonic-channel = { version = "0.6.0", features = ["ingest"] }
|
||||
|
||||
# For rewrite into bus-based app
|
||||
messagebus = { version = "0.9.13" }
|
||||
|
@ -1,7 +0,0 @@
|
||||
pub mod cart_manager;
|
||||
pub mod database;
|
||||
pub mod email_manager;
|
||||
pub mod order_manager;
|
||||
pub mod payment_manager;
|
||||
pub mod search_manager;
|
||||
pub mod token_manager;
|
@ -1,49 +0,0 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use sonic_channel::SonicChannel;
|
||||
|
||||
use crate::config::SharedAppConfig;
|
||||
|
||||
pub struct Channels {
|
||||
search: sonic_channel::SearchChannel,
|
||||
ingest: sonic_channel::IngestChannel,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SearchManager {
|
||||
channels: Option<Arc<Mutex<Channels>>>,
|
||||
config: SharedAppConfig,
|
||||
}
|
||||
|
||||
impl SearchManager {
|
||||
pub fn new(config: SharedAppConfig) -> Self {
|
||||
let enabled = config.lock().search().search_active();
|
||||
|
||||
let channels = if enabled {
|
||||
let search = {
|
||||
let l = config.lock();
|
||||
sonic_channel::SearchChannel::start(
|
||||
l.search().sonic_search_addr(),
|
||||
l.search().sonic_search_pass(),
|
||||
)
|
||||
.expect("Failed to connect to sonic search channel")
|
||||
};
|
||||
let ingest = {
|
||||
let l = config.lock();
|
||||
sonic_channel::IngestChannel::start(
|
||||
l.search().sonic_ingest_addr(),
|
||||
l.search().sonic_ingest_pass(),
|
||||
)
|
||||
.expect("Failed to connect to sonic ingest channel")
|
||||
};
|
||||
Some(Arc::new(Mutex::new(Channels { search, ingest })))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Self { channels, config }
|
||||
}
|
||||
}
|
||||
|
||||
impl actix::Actor for SearchManager {
|
||||
type Context = actix::Context<Self>;
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
use argon2::{Algorithm, Argon2, Params, Version};
|
||||
use model::Password;
|
||||
use password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString};
|
||||
|
||||
use crate::model::Password;
|
||||
use crate::PassHash;
|
||||
|
||||
mod order_state;
|
||||
|
@ -8,7 +8,10 @@ use actix_session::SessionMiddleware;
|
||||
use actix_web::middleware::Logger;
|
||||
use actix_web::web::Data;
|
||||
use actix_web::{App, HttpServer};
|
||||
use config::UpdateConfig;
|
||||
use email_manager::TestMail;
|
||||
use jemallocator::Jemalloc;
|
||||
use model::{Email, Login, PassHash, Password, Role};
|
||||
use opts::{
|
||||
Command, CreateAccountCmd, CreateAccountOpts, GenerateHashOpts, MigrateOpts, Opts, ServerOpts,
|
||||
TestMailerOpts,
|
||||
@ -16,18 +19,9 @@ use opts::{
|
||||
use password_hash::SaltString;
|
||||
use validator::{validate_email, validate_length};
|
||||
|
||||
use crate::actors::{
|
||||
database, email_manager, order_manager, payment_manager, search_manager, token_manager,
|
||||
};
|
||||
use crate::email_manager::TestMail;
|
||||
use crate::logic::encrypt_password;
|
||||
use crate::model::{Email, Login, PassHash, Password, Role};
|
||||
use crate::opts::UpdateConfig;
|
||||
|
||||
pub mod actors;
|
||||
pub mod config;
|
||||
pub mod logic;
|
||||
pub mod model;
|
||||
mod opts;
|
||||
pub mod routes;
|
||||
|
||||
@ -43,7 +37,7 @@ pub enum Error {
|
||||
#[error("Unable to read password from STDIN. {0:?}")]
|
||||
ReadPass(std::io::Error),
|
||||
#[error("{0}")]
|
||||
Database(#[from] database::Error),
|
||||
Database(#[from] database_manager::Error),
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
@ -53,7 +47,9 @@ async fn server(opts: ServerOpts) -> Result<()> {
|
||||
|
||||
let app_config = config::default_load(&opts);
|
||||
|
||||
let db = database::Database::build(app_config.clone()).await?.start();
|
||||
let db = database_manager::Database::build(app_config.clone())
|
||||
.await?
|
||||
.start();
|
||||
let token_manager = token_manager::TokenManager::new(app_config.clone(), db.clone()).start();
|
||||
let order_manager = order_manager::OrderManager::new(app_config.clone(), db.clone()).start();
|
||||
let payment_manager = payment_manager::PaymentManager::build(app_config.clone(), db.clone())
|
||||
@ -89,7 +85,11 @@ async fn server(opts: ServerOpts) -> Result<()> {
|
||||
.app_data(Data::new(payment_manager.clone()))
|
||||
.app_data(Data::new(search_manager.clone()))
|
||||
.configure(routes::configure)
|
||||
// .default_service(web::to(HttpResponse::Ok))
|
||||
.service({
|
||||
let l = app_config.lock();
|
||||
actix_files::Files::new(&l.files().public_path(), l.files().local_path())
|
||||
})
|
||||
.default_service(actix_web::web::to(actix_web::HttpResponse::Ok))
|
||||
})
|
||||
.bind(addr)
|
||||
.map_err(Error::Boot)?
|
||||
@ -102,7 +102,7 @@ async fn migrate(opts: MigrateOpts) -> Result<()> {
|
||||
use sqlx::migrate::MigrateError;
|
||||
|
||||
let config = config::default_load(&opts);
|
||||
let db = database::Database::build(config).await?;
|
||||
let db = database_manager::Database::build(config).await?;
|
||||
let res: std::result::Result<(), MigrateError> =
|
||||
sqlx::migrate!("../migrations").run(db.pool()).await;
|
||||
match res {
|
||||
@ -133,7 +133,9 @@ async fn create_account(opts: CreateAccountOpts) -> Result<()> {
|
||||
panic!("Login must have at least 4 characters and no more than 100");
|
||||
}
|
||||
let config = config::default_load(&opts);
|
||||
let db = database::Database::build(config.clone()).await?.start();
|
||||
let db = database_manager::Database::build(config.clone())
|
||||
.await?
|
||||
.start();
|
||||
let pass = match opts.pass_file {
|
||||
Some(path) => std::fs::read_to_string(path).map_err(Error::PassFile)?,
|
||||
None => {
|
||||
@ -160,7 +162,7 @@ async fn create_account(opts: CreateAccountOpts) -> Result<()> {
|
||||
}
|
||||
let hash = encrypt_password(&Password::from(pass), &config.lock().web().pass_salt()).unwrap();
|
||||
|
||||
db.send(database::CreateAccount {
|
||||
db.send(database_manager::CreateAccount {
|
||||
email: Email::from(opts.email),
|
||||
login: Login::from(opts.login),
|
||||
pass_hash: PassHash::from(hash),
|
||||
@ -208,6 +210,9 @@ async fn main() -> Result<()> {
|
||||
Command::GenerateHash(opts) => generate_hash(opts).await,
|
||||
Command::CreateAccount(opts) => create_account(opts).await,
|
||||
Command::TestMailer(opts) => test_mailer(opts).await,
|
||||
Command::ConfigInfo(_) => config::config_info().await,
|
||||
Command::ConfigInfo(_) => {
|
||||
config::config_info().await.unwrap();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,6 @@
|
||||
use config::{AppConfig, UpdateConfig};
|
||||
use gumdrop::Options;
|
||||
|
||||
use crate::config::AppConfig;
|
||||
use crate::model::Email;
|
||||
|
||||
pub trait UpdateConfig {
|
||||
fn update_config(&self, config: &mut AppConfig);
|
||||
}
|
||||
use model::Email;
|
||||
|
||||
pub trait ResolveDbUrl {
|
||||
fn own_db_url(&self) -> Option<String>;
|
||||
|
@ -4,14 +4,14 @@ use actix::Addr;
|
||||
use actix_session::Session;
|
||||
use actix_web::web::{scope, Data, Json, ServiceConfig};
|
||||
use actix_web::{delete, get, post, HttpResponse};
|
||||
use config::SharedAppConfig;
|
||||
use database_manager::{query_db, Database};
|
||||
use model::{Account, Email, Login, PassHash, Password, PasswordConfirmation, Role};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::config::SharedAppConfig;
|
||||
use crate::database::{AccountByIdentity, Database};
|
||||
use crate::logic::encrypt_password;
|
||||
use crate::model::{Account, Email, Login, PassHash, Password, PasswordConfirmation, Role};
|
||||
use crate::routes;
|
||||
use crate::routes::{RequireLogin, Result};
|
||||
use crate::{database, model, routes};
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! admin_send_db {
|
||||
@ -42,7 +42,7 @@ pub enum Error {
|
||||
#[error("Password and password confirmation are different")]
|
||||
DifferentPasswords,
|
||||
#[error("{0}")]
|
||||
Database(#[from] database::Error),
|
||||
Database(#[from] database_manager::Error),
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
@ -71,23 +71,14 @@ async fn sign_in(
|
||||
) -> Result<HttpResponse> {
|
||||
log::debug!("{:?}", payload);
|
||||
let db = db.into_inner();
|
||||
let user: model::FullAccount = match db
|
||||
.send(AccountByIdentity {
|
||||
let user: model::FullAccount = query_db!(
|
||||
db,
|
||||
database_manager::AccountByIdentity {
|
||||
email: payload.email,
|
||||
login: payload.login,
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(Ok(user)) => user,
|
||||
Ok(Err(e)) => {
|
||||
log::error!("{}", e);
|
||||
return Err(routes::Error::Unauthorized);
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("{}", e);
|
||||
return Err(routes::Error::Unauthorized);
|
||||
}
|
||||
};
|
||||
},
|
||||
routes::Error::Unauthorized
|
||||
);
|
||||
if let Err(e) = crate::logic::validate_password(&payload.password, &user.pass_hash) {
|
||||
log::error!("Password validation failed. {}", e);
|
||||
Err(routes::Error::Unauthorized)
|
||||
@ -143,27 +134,17 @@ async fn register(
|
||||
}
|
||||
};
|
||||
|
||||
match db
|
||||
.send(database::CreateAccount {
|
||||
query_db!(
|
||||
db,
|
||||
database_manager::CreateAccount {
|
||||
email: input.email,
|
||||
login: input.login,
|
||||
pass_hash: PassHash::from(hash),
|
||||
role: input.role,
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(Ok(account)) => {
|
||||
response.account = Some(account.into());
|
||||
}
|
||||
Ok(Err(e)) => {
|
||||
log::error!("{}", e);
|
||||
return Err(super::Error::Admin(Error::Register));
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("{}", e);
|
||||
return Err(super::Error::Admin(Error::Register));
|
||||
}
|
||||
};
|
||||
},
|
||||
super::Error::Admin(Error::Register),
|
||||
super::Error::Admin(Error::Register)
|
||||
);
|
||||
|
||||
response.success = response.errors.is_empty();
|
||||
Ok(if response.success {
|
||||
|
@ -2,10 +2,10 @@ use actix::Addr;
|
||||
use actix_session::Session;
|
||||
use actix_web::web::{Data, Json, ServiceConfig};
|
||||
use actix_web::{get, patch, post, HttpResponse};
|
||||
use config::SharedAppConfig;
|
||||
use database_manager::Database;
|
||||
use model::{AccountId, AccountState, PasswordConfirmation};
|
||||
|
||||
use crate::config::SharedAppConfig;
|
||||
use crate::database::{self, Database};
|
||||
use crate::model::{AccountId, AccountState, PasswordConfirmation};
|
||||
use crate::routes::admin::Error;
|
||||
use crate::routes::RequireLogin;
|
||||
use crate::{admin_send_db, encrypt_password, routes, Email, Login, PassHash, Password, Role};
|
||||
@ -13,7 +13,7 @@ use crate::{admin_send_db, encrypt_password, routes, Email, Login, PassHash, Pas
|
||||
#[get("/accounts")]
|
||||
pub async fn accounts(session: Session, db: Data<Addr<Database>>) -> routes::Result<HttpResponse> {
|
||||
session.require_admin()?;
|
||||
let accounts = admin_send_db!(db, database::AllAccounts);
|
||||
let accounts = admin_send_db!(db, database_manager::AllAccounts);
|
||||
Ok(HttpResponse::Ok().json(accounts))
|
||||
}
|
||||
|
||||
@ -63,7 +63,7 @@ pub async fn update_account(
|
||||
|
||||
let account = admin_send_db!(
|
||||
db,
|
||||
database::UpdateAccount {
|
||||
database_manager::UpdateAccount {
|
||||
id: payload.id,
|
||||
email: payload.email,
|
||||
login: payload.login,
|
||||
@ -107,7 +107,7 @@ pub async fn create_account(
|
||||
|
||||
let account = admin_send_db!(
|
||||
db,
|
||||
database::CreateAccount {
|
||||
database_manager::CreateAccount {
|
||||
email: payload.email,
|
||||
login: payload.login,
|
||||
pass_hash: PassHash::from(hash),
|
||||
|
@ -2,19 +2,19 @@ use actix::Addr;
|
||||
use actix_session::Session;
|
||||
use actix_web::get;
|
||||
use actix_web::web::{Data, Json, ServiceConfig};
|
||||
use database_manager::Database;
|
||||
use model::api::AccountOrders;
|
||||
|
||||
use crate::database::Database;
|
||||
use crate::model::api::AccountOrders;
|
||||
use crate::routes::admin::Error;
|
||||
use crate::routes::RequireLogin;
|
||||
use crate::{admin_send_db, database, model, routes};
|
||||
use crate::{admin_send_db, routes};
|
||||
|
||||
#[get("/orders")]
|
||||
async fn orders(session: Session, db: Data<Addr<Database>>) -> routes::Result<Json<AccountOrders>> {
|
||||
session.require_admin()?;
|
||||
|
||||
let orders: Vec<model::AccountOrder> = admin_send_db!(&db, database::AllAccountOrders);
|
||||
let items: Vec<model::OrderItem> = admin_send_db!(db, database::AllOrderItems);
|
||||
let orders: Vec<model::AccountOrder> = admin_send_db!(&db, database_manager::AllAccountOrders);
|
||||
let items: Vec<model::OrderItem> = admin_send_db!(db, database_manager::AllOrderItems);
|
||||
|
||||
Ok(Json((orders, items).into()))
|
||||
}
|
||||
|
@ -2,22 +2,23 @@ use actix::Addr;
|
||||
use actix_session::Session;
|
||||
use actix_web::web::{Data, Json, ServiceConfig};
|
||||
use actix_web::{delete, get, patch, post, HttpResponse};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::database::Database;
|
||||
use crate::model::{
|
||||
use database_manager::Database;
|
||||
use model::{
|
||||
Days, Price, ProductCategory, ProductId, ProductLongDesc, ProductName, ProductShortDesc,
|
||||
Quantity, QuantityUnit,
|
||||
};
|
||||
use search_manager::SearchManager;
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::routes::admin::Error;
|
||||
use crate::routes::RequireLogin;
|
||||
use crate::{admin_send_db, database, routes};
|
||||
use crate::{admin_send_db, routes};
|
||||
|
||||
#[get("/products")]
|
||||
async fn products(session: Session, db: Data<Addr<Database>>) -> routes::Result<HttpResponse> {
|
||||
session.require_admin()?;
|
||||
|
||||
let products = admin_send_db!(db, database::AllProducts);
|
||||
let products = admin_send_db!(db, database_manager::AllProducts);
|
||||
Ok(HttpResponse::Ok().json(products))
|
||||
}
|
||||
|
||||
@ -42,7 +43,7 @@ async fn update_product(
|
||||
|
||||
let product = admin_send_db!(
|
||||
db,
|
||||
database::UpdateProduct {
|
||||
database_manager::UpdateProduct {
|
||||
id: payload.id,
|
||||
name: payload.name,
|
||||
short_description: payload.short_description,
|
||||
@ -63,19 +64,21 @@ pub struct CreateProduct {
|
||||
pub category: Option<ProductCategory>,
|
||||
pub price: Price,
|
||||
pub deliver_days_flag: Days,
|
||||
pub lang: String,
|
||||
}
|
||||
|
||||
#[post("/product")]
|
||||
async fn create_product(
|
||||
session: Session,
|
||||
db: Data<Addr<Database>>,
|
||||
search: Data<Addr<SearchManager>>,
|
||||
Json(payload): Json<CreateProduct>,
|
||||
) -> routes::Result<HttpResponse> {
|
||||
session.require_admin()?;
|
||||
|
||||
let product = admin_send_db!(
|
||||
let product: model::Product = admin_send_db!(
|
||||
db.clone(),
|
||||
database::CreateProduct {
|
||||
database_manager::CreateProduct {
|
||||
name: payload.name,
|
||||
short_description: payload.short_description,
|
||||
long_description: payload.long_description,
|
||||
@ -84,9 +87,17 @@ async fn create_product(
|
||||
deliver_days_flag: payload.deliver_days_flag,
|
||||
}
|
||||
);
|
||||
|
||||
search.do_send(search_manager::CreateIndex {
|
||||
key: format!("{}", product.id),
|
||||
value: product.long_description.to_string(),
|
||||
collection: "products".into(),
|
||||
lang: payload.lang,
|
||||
});
|
||||
|
||||
let _ = admin_send_db!(
|
||||
db,
|
||||
database::CreateStock {
|
||||
database_manager::CreateStock {
|
||||
product_id: product.id,
|
||||
quantity: Quantity::try_from(0).unwrap_or_default(),
|
||||
quantity_unit: QuantityUnit::Piece,
|
||||
@ -110,7 +121,7 @@ async fn delete_product(
|
||||
|
||||
let product = admin_send_db!(
|
||||
db,
|
||||
database::DeleteProduct {
|
||||
database_manager::DeleteProduct {
|
||||
product_id: payload.id
|
||||
}
|
||||
);
|
||||
|
@ -2,19 +2,19 @@ use actix::Addr;
|
||||
use actix_session::Session;
|
||||
use actix_web::web::{Data, Json, ServiceConfig};
|
||||
use actix_web::{delete, get, patch, post, HttpResponse};
|
||||
use database_manager::Database;
|
||||
use model::{ProductId, Quantity, QuantityUnit, StockId};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::database::Database;
|
||||
use crate::model::{ProductId, Quantity, QuantityUnit, StockId};
|
||||
use crate::routes::admin::Error;
|
||||
use crate::routes::RequireLogin;
|
||||
use crate::{admin_send_db, database, routes};
|
||||
use crate::{admin_send_db, routes};
|
||||
|
||||
#[get("/stocks")]
|
||||
async fn stocks(session: Session, db: Data<Addr<Database>>) -> routes::Result<HttpResponse> {
|
||||
session.require_admin()?;
|
||||
|
||||
let stocks = admin_send_db!(db, database::AllStocks);
|
||||
let stocks = admin_send_db!(db, database_manager::AllStocks);
|
||||
Ok(HttpResponse::Created().json(stocks))
|
||||
}
|
||||
|
||||
@ -36,7 +36,7 @@ async fn update_stock(
|
||||
|
||||
let stock = admin_send_db!(
|
||||
db,
|
||||
database::UpdateStock {
|
||||
database_manager::UpdateStock {
|
||||
id: payload.id,
|
||||
product_id: payload.product_id,
|
||||
quantity: payload.quantity,
|
||||
@ -63,7 +63,7 @@ async fn create_stock(
|
||||
|
||||
let stock = admin_send_db!(
|
||||
db,
|
||||
database::CreateStock {
|
||||
database_manager::CreateStock {
|
||||
product_id: payload.product_id,
|
||||
quantity: payload.quantity,
|
||||
quantity_unit: payload.quantity_unit
|
||||
@ -87,7 +87,7 @@ async fn delete_stock(
|
||||
|
||||
let stock = admin_send_db!(
|
||||
db,
|
||||
database::DeleteStock {
|
||||
database_manager::DeleteStock {
|
||||
stock_id: payload.id
|
||||
}
|
||||
);
|
||||
|
@ -9,13 +9,13 @@ use actix_session::Session;
|
||||
use actix_web::body::BoxBody;
|
||||
use actix_web::web::ServiceConfig;
|
||||
use actix_web::{HttpRequest, HttpResponse, Responder, ResponseError};
|
||||
use model::{RecordId, Token, TokenString};
|
||||
use serde::Serialize;
|
||||
use token_manager::TokenManager;
|
||||
|
||||
pub use self::admin::Error as AdminError;
|
||||
pub use self::public::{Error as PublicError, V1Error, V1ShoppingCartError};
|
||||
use crate::model::{RecordId, Token, TokenString};
|
||||
use crate::token_manager::TokenManager;
|
||||
use crate::{routes, token_manager};
|
||||
use crate::routes;
|
||||
|
||||
pub trait RequireLogin {
|
||||
fn require_admin(&self) -> Result<RecordId>;
|
||||
|
@ -4,8 +4,6 @@ use actix_web::web::ServiceConfig;
|
||||
use actix_web::{get, HttpResponse};
|
||||
pub use api_v1::{Error as V1Error, ShoppingCartError as V1ShoppingCartError};
|
||||
|
||||
use crate::database;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! public_send_db {
|
||||
($db: expr, $msg: expr) => {{
|
||||
@ -35,7 +33,7 @@ pub enum Error {
|
||||
#[error("Internal server error")]
|
||||
DatabaseConnection,
|
||||
#[error("{0}")]
|
||||
Database(#[from] database::Error),
|
||||
Database(#[from] database_manager::Error),
|
||||
}
|
||||
|
||||
#[get("/")]
|
||||
|
@ -2,17 +2,16 @@ use actix::Addr;
|
||||
use actix_web::web::{scope, Data, Json, ServiceConfig};
|
||||
use actix_web::{delete, get, post, HttpRequest, HttpResponse};
|
||||
use actix_web_httpauth::extractors::bearer::BearerAuth;
|
||||
use cart_manager::CartManager;
|
||||
use database_manager::{query_db, Database};
|
||||
use model::{api, AccountId, ProductId, Quantity, QuantityUnit, ShoppingCartItemId};
|
||||
use payment_manager::{query_pay, PaymentManager};
|
||||
use token_manager::TokenManager;
|
||||
|
||||
use crate::actors::cart_manager;
|
||||
use crate::actors::cart_manager::CartManager;
|
||||
use crate::database::Database;
|
||||
use crate::model::{api, AccountId, ProductId, Quantity, QuantityUnit, ShoppingCartItemId};
|
||||
use crate::payment_manager::PaymentManager;
|
||||
use crate::routes;
|
||||
use crate::routes::public::api_v1::{Error as ApiV1Error, ShoppingCartError};
|
||||
use crate::routes::public::Error as PublicError;
|
||||
use crate::routes::{RequireUser, Result};
|
||||
use crate::token_manager::TokenManager;
|
||||
use crate::{database, model, payment_manager, query_db, query_pay, routes};
|
||||
|
||||
#[get("/shopping-cart")]
|
||||
async fn shopping_cart(
|
||||
@ -23,7 +22,7 @@ async fn shopping_cart(
|
||||
let (token, _) = credentials.require_user(tm.into_inner()).await?;
|
||||
let cart: model::ShoppingCart = query_db!(
|
||||
db,
|
||||
database::EnsureActiveShoppingCart {
|
||||
database_manager::EnsureActiveShoppingCart {
|
||||
buyer_id: AccountId::from(token.subject),
|
||||
},
|
||||
routes::Error::Public(PublicError::ApiV1(ApiV1Error::ShoppingCart(
|
||||
@ -32,7 +31,7 @@ async fn shopping_cart(
|
||||
);
|
||||
let items: Vec<model::ShoppingCartItem> = query_db!(
|
||||
db,
|
||||
database::AccountShoppingCartItems {
|
||||
database_manager::AccountShoppingCartItems {
|
||||
account_id: cart.buyer_id,
|
||||
shopping_cart_id: Some(cart.id),
|
||||
},
|
||||
@ -110,22 +109,14 @@ async fn delete_cart_item(
|
||||
) -> Result<HttpResponse> {
|
||||
let (token, _) = credentials.require_user(tm.into_inner()).await?;
|
||||
|
||||
let sc: model::ShoppingCart = match db
|
||||
.send(database::EnsureActiveShoppingCart {
|
||||
let sc: model::ShoppingCart = query_db!(
|
||||
db,
|
||||
database_manager::EnsureActiveShoppingCart {
|
||||
buyer_id: AccountId::from(token.subject),
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(Ok(cart)) => cart,
|
||||
Ok(Err(e)) => {
|
||||
log::error!("{e:}");
|
||||
return Err(routes::Error::Public(super::Error::RemoveItem.into()));
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("{e:?}");
|
||||
return Err(routes::Error::Public(PublicError::DatabaseConnection));
|
||||
}
|
||||
};
|
||||
},
|
||||
routes::Error::Public(super::Error::RemoveItem.into()),
|
||||
routes::Error::Public(PublicError::DatabaseConnection)
|
||||
);
|
||||
|
||||
match cart
|
||||
.into_inner()
|
||||
|
@ -1,24 +1,24 @@
|
||||
use actix::Addr;
|
||||
use actix_web::web::{Data, Json, ServiceConfig};
|
||||
use actix_web::{get, post, HttpResponse};
|
||||
use database_manager::{query_db, Database};
|
||||
use model::{Audience, FullAccount, Token, TokenString};
|
||||
use payment_manager::{PaymentManager, PaymentNotification};
|
||||
use token_manager::TokenManager;
|
||||
|
||||
use crate::database::{self, Database};
|
||||
use crate::logic::validate_password;
|
||||
use crate::model::{Audience, FullAccount, Token, TokenString};
|
||||
use crate::payment_manager::{PaymentManager, PaymentNotification};
|
||||
use crate::routes::public::Error as PublicError;
|
||||
use crate::routes::{self, Result};
|
||||
use crate::token_manager::TokenManager;
|
||||
use crate::{payment_manager, public_send_db, token_manager, Login, Password};
|
||||
use crate::{public_send_db, Login, Password};
|
||||
|
||||
#[get("/products")]
|
||||
async fn products(db: Data<Addr<Database>>) -> Result<HttpResponse> {
|
||||
public_send_db!(db.into_inner(), database::AllProducts)
|
||||
public_send_db!(db.into_inner(), database_manager::AllProducts)
|
||||
}
|
||||
|
||||
#[get("/stocks")]
|
||||
async fn stocks(db: Data<Addr<Database>>) -> Result<HttpResponse> {
|
||||
public_send_db!(db.into_inner(), database::AllStocks)
|
||||
public_send_db!(db.into_inner(), database_manager::AllStocks)
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
@ -41,23 +41,15 @@ async fn sign_in(
|
||||
let db = db.into_inner();
|
||||
let tm = tm.into_inner();
|
||||
|
||||
let account: FullAccount = match db
|
||||
.send(database::AccountByIdentity {
|
||||
let account: FullAccount = query_db!(
|
||||
db,
|
||||
database_manager::AccountByIdentity {
|
||||
login: Some(Login::from(payload.login)),
|
||||
email: None,
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(Ok(account)) => account,
|
||||
Ok(Err(db_err)) => {
|
||||
log::error!("{db_err}");
|
||||
return Err(routes::Error::Public(PublicError::DatabaseConnection));
|
||||
}
|
||||
Err(db_err) => {
|
||||
log::error!("{db_err}");
|
||||
return Err(routes::Error::Public(PublicError::DatabaseConnection));
|
||||
}
|
||||
};
|
||||
},
|
||||
routes::Error::Public(PublicError::DatabaseConnection),
|
||||
routes::Error::Public(PublicError::DatabaseConnection)
|
||||
);
|
||||
if validate_password(&Password::from(payload.password), &account.pass_hash).is_err() {
|
||||
return Err(routes::Error::Unauthorized);
|
||||
}
|
||||
|
21
shared/config/Cargo.toml
Normal file
21
shared/config/Cargo.toml
Normal file
@ -0,0 +1,21 @@
|
||||
[package]
|
||||
name = "config"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = { version = "1.0", features = [] }
|
||||
toml = { version = "0.5", features = [] }
|
||||
|
||||
parking_lot = { version = "0.12", features = [] }
|
||||
|
||||
password-hash = { version = "0.4", features = ["alloc"] }
|
||||
|
||||
pay_u = { version = '0.1', features = ["single-client"] }
|
||||
|
||||
actix-web = { version = "4.0", features = [] }
|
||||
|
||||
log = { version = "0.4" }
|
||||
|
||||
thiserror = { version = "1.0" }
|
@ -4,7 +4,14 @@ use parking_lot::Mutex;
|
||||
use password_hash::SaltString;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::UpdateConfig;
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
pub trait UpdateConfig {
|
||||
fn update_config(&self, config: &mut AppConfig);
|
||||
}
|
||||
|
||||
trait Example: Sized {
|
||||
fn example() -> Self;
|
||||
@ -343,6 +350,48 @@ impl SearchConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct FilesConfig {
|
||||
public_path: Option<String>,
|
||||
local_path: Option<String>,
|
||||
}
|
||||
|
||||
impl Example for FilesConfig {
|
||||
fn example() -> Self {
|
||||
Self {
|
||||
public_path: Some(String::from("/uploads")),
|
||||
local_path: Some(String::from("/var/local/bazzar")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for FilesConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
public_path: Some(String::from("/uploads")),
|
||||
local_path: Some(String::from("/var/local/bazzar")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FilesConfig {
|
||||
pub fn public_path(&self) -> String {
|
||||
self.public_path
|
||||
.as_ref()
|
||||
.cloned()
|
||||
.or_else(|| std::env::var("FILES_PUBLIC_PATH").ok())
|
||||
.unwrap_or_else(|| String::from("/uploads"))
|
||||
}
|
||||
|
||||
pub fn local_path(&self) -> String {
|
||||
self.local_path
|
||||
.as_ref()
|
||||
.cloned()
|
||||
.or_else(|| std::env::var("FILES_LOCAL_PATH").ok())
|
||||
.unwrap_or_else(|| String::from("/var/local/bazzar"))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct AppConfig {
|
||||
payment: PaymentConfig,
|
||||
@ -350,6 +399,7 @@ pub struct AppConfig {
|
||||
mail: MailConfig,
|
||||
database: DatabaseConfig,
|
||||
search: SearchConfig,
|
||||
files: FilesConfig,
|
||||
#[serde(skip)]
|
||||
config_path: String,
|
||||
}
|
||||
@ -362,6 +412,7 @@ impl Example for AppConfig {
|
||||
mail: MailConfig::example(),
|
||||
database: DatabaseConfig::example(),
|
||||
search: SearchConfig::example(),
|
||||
files: FilesConfig::example(),
|
||||
config_path: "".to_string(),
|
||||
}
|
||||
}
|
||||
@ -403,6 +454,10 @@ impl AppConfig {
|
||||
pub fn search(&self) -> &SearchConfig {
|
||||
&self.search
|
||||
}
|
||||
|
||||
pub fn files(&self) -> &FilesConfig {
|
||||
&self.files
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AppConfig {
|
||||
@ -413,6 +468,7 @@ impl Default for AppConfig {
|
||||
mail: Default::default(),
|
||||
database: DatabaseConfig::default(),
|
||||
search: Default::default(),
|
||||
files: FilesConfig::default(),
|
||||
config_path: "".to_string(),
|
||||
}
|
||||
}
|
||||
@ -444,27 +500,35 @@ fn load(config_path: &str, opts: &impl UpdateConfig) -> SharedAppConfig {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn config_info() -> crate::Result<()> {
|
||||
pub async fn config_info() -> Result<()> {
|
||||
println!(
|
||||
r#"Environment variables:
|
||||
|
||||
PAYU_CLIENT_ID - PayU client id, you can obtain it by creating account (account requires one-time payment)
|
||||
PAYU_CLIENT_SECRET - PayU client secret
|
||||
PAYU_CLIENT_MERCHANT_ID - PayU client merchant id, you can obtain it by creating account (account requires one-time payment)
|
||||
|
||||
WEB_HOST - your domain name, it's required for PayU notifications, service emails and redirections
|
||||
PASS_SALT - password encryption secret string, you can generate it with this CLI
|
||||
SESSION_SECRET - 100 characters admin session encryption
|
||||
JWT_SECRET - 100 characters user session encryption
|
||||
BAZZAR_BIND - address to which server should be bind, typically 0.0.0.0
|
||||
BAZZAR_PORT - port which server should use, typically 80
|
||||
|
||||
SENDGRID_SECRET - e-mail sending service secret
|
||||
SENDGRID_API_KEY - e-mail sending service api key
|
||||
SMTP_FROM - e-mail sending service authorized e-mail address used as sender e-mail address
|
||||
|
||||
DATABASE_URL - postgresql address (ex. postgres://postgres@localhost/bazzar)
|
||||
|
||||
SONIC_SEARCH_ADDR - search engine query address
|
||||
SONIC_SEARCH_PASS - search engine query password
|
||||
SONIC_INGEST_ADDR - search engine push address
|
||||
SONIC_INGEST_PASS - search engine push password
|
||||
SEARCH_ACTIVE - should use search engine
|
||||
|
||||
FILES_PUBLIC_PATH - path to files in browser
|
||||
FILES_LOCAL_PATH - path where files are saved on server
|
||||
"#
|
||||
);
|
||||
|
22
shared/model/Cargo.toml
Normal file
22
shared/model/Cargo.toml
Normal file
@ -0,0 +1,22 @@
|
||||
[package]
|
||||
name = "model"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[features]
|
||||
db = ["sqlx", "sqlx-core"]
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0.137" }
|
||||
|
||||
sqlx = { version = "0.5", features = ["migrate", "runtime-actix-rustls", "all-types", "postgres"], optional = true }
|
||||
sqlx-core = { version = "0.5", features = [], optional = true }
|
||||
|
||||
uuid = { version = "0.8", features = ["serde"] }
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
|
||||
derive_more = { version = "0.99.17" }
|
||||
|
||||
thiserror = { version = "1.0.31" }
|
||||
|
||||
validator = { version = "0.15.0" }
|
123
shared/model/src/api.rs
Normal file
123
shared/model/src/api.rs
Normal file
@ -0,0 +1,123 @@
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
#[serde(transparent)]
|
||||
pub struct AccountOrders(pub Vec<AccountOrder>);
|
||||
|
||||
impl From<(Vec<crate::AccountOrder>, Vec<crate::OrderItem>)> for AccountOrders {
|
||||
fn from((orders, mut items): (Vec<crate::AccountOrder>, Vec<crate::OrderItem>)) -> Self {
|
||||
Self(
|
||||
orders
|
||||
.into_iter()
|
||||
.map(
|
||||
|crate::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<(crate::AccountOrder, Vec<crate::OrderItem>)> for AccountOrder {
|
||||
fn from(
|
||||
(
|
||||
crate::AccountOrder {
|
||||
id,
|
||||
buyer_id,
|
||||
status,
|
||||
order_id,
|
||||
order_ext_id: _,
|
||||
service_order_id: _,
|
||||
},
|
||||
mut items,
|
||||
): (crate::AccountOrder, Vec<crate::OrderItem>),
|
||||
) -> 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: crate::AccountOrderId,
|
||||
pub buyer_id: crate::AccountId,
|
||||
pub status: crate::OrderStatus,
|
||||
pub order_id: Option<crate::OrderId>,
|
||||
pub items: Vec<crate::OrderItem>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
pub struct ShoppingCartItem {
|
||||
pub id: crate::ShoppingCartId,
|
||||
pub product_id: crate::ProductId,
|
||||
pub shopping_cart_id: crate::ShoppingCartId,
|
||||
pub quantity: crate::Quantity,
|
||||
pub quantity_unit: crate::QuantityUnit,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
pub struct ShoppingCart {
|
||||
pub id: crate::ShoppingCartId,
|
||||
pub buyer_id: crate::AccountId,
|
||||
pub payment_method: crate::PaymentMethod,
|
||||
pub state: crate::ShoppingCartState,
|
||||
pub items: Vec<ShoppingCartItem>,
|
||||
}
|
||||
|
||||
impl From<(crate::ShoppingCart, Vec<crate::ShoppingCartItem>)> for ShoppingCart {
|
||||
fn from(
|
||||
(
|
||||
crate::ShoppingCart {
|
||||
id,
|
||||
buyer_id,
|
||||
payment_method,
|
||||
state,
|
||||
},
|
||||
items,
|
||||
): (crate::ShoppingCart, Vec<crate::ShoppingCartItem>),
|
||||
) -> Self {
|
||||
Self {
|
||||
id,
|
||||
buyer_id,
|
||||
payment_method,
|
||||
state,
|
||||
items: items
|
||||
.into_iter()
|
||||
.map(
|
||||
|crate::ShoppingCartItem {
|
||||
id,
|
||||
product_id,
|
||||
shopping_cart_id,
|
||||
quantity,
|
||||
quantity_unit,
|
||||
}| ShoppingCartItem {
|
||||
id,
|
||||
product_id,
|
||||
shopping_cart_id,
|
||||
quantity,
|
||||
quantity_unit,
|
||||
},
|
||||
)
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
682
shared/model/src/lib.rs
Normal file
682
shared/model/src/lib.rs
Normal file
@ -0,0 +1,682 @@
|
||||
#![feature(drain_filter)]
|
||||
|
||||
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;
|
||||
|
||||
#[cfg_attr(feature = "db", derive(sqlx::Type))]
|
||||
#[cfg_attr(feature = "db", sqlx(rename_all = "snake_case"))]
|
||||
#[derive(Copy, Clone, Debug, Display, Deserialize, Serialize)]
|
||||
#[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,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "db", derive(sqlx::Type))]
|
||||
#[cfg_attr(feature = "db", sqlx(rename_all = "snake_case"))]
|
||||
#[derive(Copy, Clone, Debug, Display, Deserialize, Serialize, PartialEq)]
|
||||
#[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",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "db", derive(sqlx::Type))]
|
||||
#[derive(Copy, Clone, Debug, Display, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum QuantityUnit {
|
||||
#[cfg_attr(feature = "db", sqlx(rename = "g"))]
|
||||
Gram,
|
||||
#[cfg_attr(feature = "db", sqlx(rename = "dkg"))]
|
||||
Decagram,
|
||||
#[cfg_attr(feature = "db", sqlx(rename = "kg"))]
|
||||
Kilogram,
|
||||
#[cfg_attr(feature = "db", sqlx(rename = "piece"))]
|
||||
Piece,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "db", derive(sqlx::Type))]
|
||||
#[cfg_attr(feature = "db", sqlx(rename_all = "snake_case"))]
|
||||
#[derive(Copy, Clone, Debug, Display, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum PaymentMethod {
|
||||
PayU,
|
||||
PaymentOnTheSpot,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "db", derive(sqlx::Type))]
|
||||
#[cfg_attr(feature = "db", sqlx(rename_all = "snake_case"))]
|
||||
#[derive(Copy, Clone, Debug, Display, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ShoppingCartState {
|
||||
Active,
|
||||
Closed,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "db", derive(sqlx::Type))]
|
||||
#[cfg_attr(feature = "db", sqlx(rename_all = "snake_case"))]
|
||||
#[derive(Copy, Clone, Debug, Display, Deserialize, Serialize, PartialEq)]
|
||||
#[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",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "db", derive(sqlx::Type))]
|
||||
#[cfg_attr(feature = "db", sqlx(rename_all = "snake_case"))]
|
||||
#[derive(Copy, Clone, Debug, Display, Deserialize, Serialize, PartialEq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum AccountState {
|
||||
Active,
|
||||
Suspended,
|
||||
Banned,
|
||||
}
|
||||
|
||||
impl Default for Audience {
|
||||
fn default() -> Self {
|
||||
Self::Web
|
||||
}
|
||||
}
|
||||
|
||||
#[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 = "db", derive(sqlx::Type))]
|
||||
#[cfg_attr(feature = "db", sqlx(transparent))]
|
||||
#[derive(Serialize, Deserialize, Default, Debug, Copy, Clone, Deref, From)]
|
||||
#[serde(transparent)]
|
||||
pub struct Quantity(NonNegative);
|
||||
|
||||
impl TryFrom<i32> for Quantity {
|
||||
type Error = TransformError;
|
||||
|
||||
fn try_from(value: i32) -> Result<Self, Self::Error> {
|
||||
Ok(Self(value.try_into()?))
|
||||
}
|
||||
}
|
||||
|
||||
#[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 = "db", derive(sqlx::Type))]
|
||||
#[cfg_attr(feature = "db", sqlx(transparent))]
|
||||
#[derive(Serialize, Debug, Deref, From, Display)]
|
||||
#[serde(transparent)]
|
||||
pub struct Email(String);
|
||||
|
||||
impl FromStr for Email {
|
||||
type Err = TransformError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
if validator::validate_email(s) {
|
||||
Ok(Self(String::from(s)))
|
||||
} else {
|
||||
Err(TransformError::NotEmail)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde::Deserialize<'de> for Email {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
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<E>(self, s: &str) -> Result<Self::Value, E>
|
||||
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 {})?))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "db", derive(sqlx::Type))]
|
||||
#[cfg_attr(feature = "db", sqlx(transparent))]
|
||||
#[derive(Serialize, Default, Debug, Copy, Clone, Deref, Display)]
|
||||
#[serde(transparent)]
|
||||
pub struct NonNegative(i32);
|
||||
|
||||
impl TryFrom<i32> for NonNegative {
|
||||
type Error = TransformError;
|
||||
|
||||
fn try_from(value: i32) -> Result<Self, Self::Error> {
|
||||
if value < 0 {
|
||||
Err(TransformError::BelowMinimal)
|
||||
} else {
|
||||
Ok(Self(value))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde::Deserialize<'de> for NonNegative {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
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<E>(self, v: i32) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
if v >= 0 {
|
||||
Ok(v)
|
||||
} else {
|
||||
Err(E::custom("Value must be equal or greater than 0"))
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
|
||||
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<E>(self, v: u32) -> Result<Self::Value, E>
|
||||
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<E>(self, v: u64) -> Result<Self::Value, E>
|
||||
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<i32> for Day {
|
||||
type Error = TransformError;
|
||||
|
||||
fn try_from(value: i32) -> Result<Self, Self::Error> {
|
||||
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<Day>);
|
||||
|
||||
#[cfg(feature = "db")]
|
||||
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 <sqlx::Postgres as ::sqlx::database::HasArguments<'q>>::ArgumentBuffer,
|
||||
) -> ::sqlx::encode::IsNull {
|
||||
let value = self.0.iter().fold(1, |memo, v| memo | *v as i32);
|
||||
|
||||
<i32 as ::sqlx::encode::Encode<sqlx::Postgres>>::encode_by_ref(&value, buf)
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> usize {
|
||||
<i32 as ::sqlx::encode::Encode<sqlx::Postgres>>::size_hint(&Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "db")]
|
||||
impl<'r> ::sqlx::decode::Decode<'r, sqlx::Postgres> for Days
|
||||
where
|
||||
i32: ::sqlx::decode::Decode<'r, sqlx::Postgres>,
|
||||
{
|
||||
fn decode(
|
||||
value: <sqlx::Postgres as ::sqlx::database::HasValueRef<'r>>::ValueRef,
|
||||
) -> ::std::result::Result<
|
||||
Self,
|
||||
::std::boxed::Box<
|
||||
dyn ::std::error::Error + 'static + ::std::marker::Send + ::std::marker::Sync,
|
||||
>,
|
||||
> {
|
||||
let value = <i32 as ::sqlx::decode::Decode<'r, sqlx::Postgres>>::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(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "db")]
|
||||
impl sqlx::Type<sqlx::Postgres> for Days
|
||||
where
|
||||
i32: ::sqlx::Type<sqlx::Postgres>,
|
||||
{
|
||||
fn type_info() -> <sqlx::Postgres as ::sqlx::Database>::TypeInfo {
|
||||
<i32 as ::sqlx::Type<sqlx::Postgres>>::type_info()
|
||||
}
|
||||
|
||||
fn compatible(ty: &<sqlx::Postgres as ::sqlx::Database>::TypeInfo) -> bool {
|
||||
<i32 as ::sqlx::Type<sqlx::Postgres>>::compatible(ty)
|
||||
}
|
||||
}
|
||||
|
||||
#[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 = "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 = "db", derive(sqlx::Type))]
|
||||
#[cfg_attr(feature = "db", sqlx(transparent))]
|
||||
#[derive(Serialize, Deserialize, Debug, Deref, From, Display)]
|
||||
#[serde(transparent)]
|
||||
pub struct PassHash(String);
|
||||
|
||||
impl PartialEq<PasswordConfirmation> for Password {
|
||||
fn eq(&self, other: &PasswordConfirmation) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
#[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 = "db", derive(sqlx::FromRow))]
|
||||
#[derive(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,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "db", derive(sqlx::FromRow))]
|
||||
#[derive(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<FullAccount> for Account {
|
||||
fn from(
|
||||
FullAccount {
|
||||
id,
|
||||
email,
|
||||
login,
|
||||
pass_hash: _,
|
||||
role,
|
||||
customer_id,
|
||||
state,
|
||||
}: FullAccount,
|
||||
) -> Self {
|
||||
Self {
|
||||
id,
|
||||
email,
|
||||
login,
|
||||
role,
|
||||
customer_id,
|
||||
state,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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 = "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 = "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 = "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 = "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 = "db", derive(sqlx::FromRow))]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Product {
|
||||
pub id: ProductId,
|
||||
pub name: ProductName,
|
||||
pub short_description: ProductShortDesc,
|
||||
pub long_description: ProductLongDesc,
|
||||
pub category: Option<ProductCategory>,
|
||||
pub price: Price,
|
||||
pub deliver_days_flag: Days,
|
||||
}
|
||||
|
||||
#[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 = "db", derive(sqlx::FromRow))]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Stock {
|
||||
pub id: StockId,
|
||||
pub product_id: ProductId,
|
||||
pub quantity: Quantity,
|
||||
pub quantity_unit: QuantityUnit,
|
||||
}
|
||||
|
||||
#[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 = "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 = "db", derive(sqlx::FromRow))]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct AccountOrder {
|
||||
pub id: AccountOrderId,
|
||||
pub buyer_id: AccountId,
|
||||
pub status: OrderStatus,
|
||||
pub order_id: Option<OrderId>,
|
||||
pub order_ext_id: uuid::Uuid,
|
||||
pub service_order_id: Option<String>,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "db", derive(sqlx::FromRow))]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct PublicAccountOrder {
|
||||
pub id: AccountOrderId,
|
||||
pub buyer_id: AccountId,
|
||||
pub status: OrderStatus,
|
||||
pub order_id: Option<OrderId>,
|
||||
}
|
||||
|
||||
impl From<AccountOrder> 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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 = "db", derive(sqlx::FromRow))]
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct OrderItem {
|
||||
pub id: OrderItemId,
|
||||
pub product_id: ProductId,
|
||||
pub order_id: AccountOrderId,
|
||||
pub quantity: Quantity,
|
||||
pub quantity_unit: QuantityUnit,
|
||||
}
|
||||
|
||||
#[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 = "db", derive(sqlx::FromRow))]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct ShoppingCart {
|
||||
pub id: ShoppingCartId,
|
||||
pub buyer_id: AccountId,
|
||||
pub payment_method: PaymentMethod,
|
||||
pub state: ShoppingCartState,
|
||||
}
|
||||
|
||||
#[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 ShoppingCartItemId(RecordId);
|
||||
|
||||
#[cfg_attr(feature = "db", derive(sqlx::FromRow))]
|
||||
#[derive(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
|
||||
}
|
||||
}
|
||||
|
||||
#[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 = "db", derive(sqlx::FromRow))]
|
||||
#[derive(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,
|
||||
}
|
||||
|
||||
#[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 = "db", derive(sqlx::Type))]
|
||||
#[cfg_attr(feature = "db", sqlx(transparent))]
|
||||
#[derive(Serialize, Deserialize, Debug, Deref, Display, From)]
|
||||
pub struct FileName(String);
|
Loading…
Reference in New Issue
Block a user