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",
|
"actix-web-opentelemetry",
|
||||||
"argon2",
|
"argon2",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
|
"cart_manager",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
"config",
|
||||||
|
"database_manager",
|
||||||
"derive_more",
|
"derive_more",
|
||||||
"dotenv",
|
"dotenv",
|
||||||
|
"email_manager",
|
||||||
"futures",
|
"futures",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"gumdrop",
|
"gumdrop",
|
||||||
"hmac",
|
|
||||||
"jemallocator",
|
"jemallocator",
|
||||||
"jwt",
|
|
||||||
"log",
|
"log",
|
||||||
"messagebus",
|
"messagebus",
|
||||||
|
"model",
|
||||||
"oauth2",
|
"oauth2",
|
||||||
|
"order_manager",
|
||||||
"parking_lot 0.12.0",
|
"parking_lot 0.12.0",
|
||||||
"password-hash",
|
"password-hash",
|
||||||
"pay_u",
|
"payment_manager",
|
||||||
"pretty_env_logger",
|
"pretty_env_logger",
|
||||||
"rand_core",
|
"rand_core",
|
||||||
"sendgrid",
|
"search_manager",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2",
|
|
||||||
"sonic-channel",
|
|
||||||
"sqlx",
|
"sqlx",
|
||||||
"sqlx-core",
|
"sqlx-core",
|
||||||
"tera",
|
"tera",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
"token_manager",
|
||||||
"tokio",
|
"tokio",
|
||||||
"toml",
|
"toml",
|
||||||
"tracing",
|
"tracing",
|
||||||
"uuid",
|
"uuid",
|
||||||
"validator",
|
"validator 0.14.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -767,6 +770,22 @@ dependencies = [
|
|||||||
"bytes",
|
"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]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.73"
|
version = "1.0.73"
|
||||||
@ -827,6 +846,21 @@ dependencies = [
|
|||||||
"generic-array 0.14.5",
|
"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]]
|
[[package]]
|
||||||
name = "console_error_panic_hook"
|
name = "console_error_panic_hook"
|
||||||
version = "0.1.7"
|
version = "0.1.7"
|
||||||
@ -1016,6 +1050,23 @@ version = "2.3.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57"
|
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]]
|
[[package]]
|
||||||
name = "dbg"
|
name = "dbg"
|
||||||
version = "1.0.4"
|
version = "1.0.4"
|
||||||
@ -1102,6 +1153,22 @@ version = "1.6.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
|
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]]
|
[[package]]
|
||||||
name = "enclose"
|
name = "enclose"
|
||||||
version = "1.1.8"
|
version = "1.1.8"
|
||||||
@ -1215,6 +1282,22 @@ version = "1.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394"
|
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]]
|
[[package]]
|
||||||
name = "futures"
|
name = "futures"
|
||||||
version = "0.3.21"
|
version = "0.3.21"
|
||||||
@ -1866,9 +1949,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.16"
|
version = "0.4.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8"
|
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
@ -2006,6 +2089,20 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "model"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
|
"derive_more",
|
||||||
|
"serde",
|
||||||
|
"sqlx",
|
||||||
|
"sqlx-core",
|
||||||
|
"thiserror",
|
||||||
|
"uuid",
|
||||||
|
"validator 0.15.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "native-tls"
|
name = "native-tls"
|
||||||
version = "0.2.10"
|
version = "0.2.10"
|
||||||
@ -2206,6 +2303,22 @@ dependencies = [
|
|||||||
"opentelemetry",
|
"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]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
version = "0.11.2"
|
version = "0.11.2"
|
||||||
@ -2295,6 +2408,26 @@ dependencies = [
|
|||||||
"thiserror",
|
"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]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.1.0"
|
version = "2.1.0"
|
||||||
@ -2770,6 +2903,25 @@ dependencies = [
|
|||||||
"untrusted",
|
"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]]
|
[[package]]
|
||||||
name = "security-framework"
|
name = "security-framework"
|
||||||
version = "2.6.1"
|
version = "2.6.1"
|
||||||
@ -3288,18 +3440,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.30"
|
version = "1.0.31"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
|
checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.30"
|
version = "1.0.31"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
|
checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -3396,11 +3548,36 @@ version = "0.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
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]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.18.0"
|
version = "1.18.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0f48b6d60512a392e34dbf7fd456249fd2de3c83669ab642e021903f4015185b"
|
checksum = "dce653fb475565de9f6fb0614b28bca8df2c430c0cf84bcd9c843f15de5414cc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"libc",
|
"libc",
|
||||||
@ -3764,6 +3941,21 @@ dependencies = [
|
|||||||
"validator_types",
|
"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]]
|
[[package]]
|
||||||
name = "validator_types"
|
name = "validator_types"
|
||||||
version = "0.14.0"
|
version = "0.14.0"
|
||||||
|
14
Cargo.toml
14
Cargo.toml
@ -1,2 +1,14 @@
|
|||||||
[workspace]
|
[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 database_manager::{query_db, Database};
|
||||||
|
use model::{
|
||||||
use crate::database::{self, Database};
|
|
||||||
use crate::model::{
|
|
||||||
AccountId, ProductId, Quantity, QuantityUnit, ShoppingCartId, ShoppingCartItem,
|
AccountId, ProductId, Quantity, QuantityUnit, ShoppingCartId, ShoppingCartItem,
|
||||||
ShoppingCartItemId, ShoppingCartState,
|
ShoppingCartItemId, ShoppingCartState,
|
||||||
};
|
};
|
||||||
@ -30,7 +28,7 @@ pub enum Error {
|
|||||||
#[error("Failed to add item to cart")]
|
#[error("Failed to add item to cart")]
|
||||||
CantAddItem,
|
CantAddItem,
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
Db(#[from] database::Error),
|
Db(#[from] database_manager::Error),
|
||||||
#[error("Unable to update cart item")]
|
#[error("Unable to update cart item")]
|
||||||
UpdateFailed,
|
UpdateFailed,
|
||||||
#[error("Failed to change quantity")]
|
#[error("Failed to change quantity")]
|
||||||
@ -42,20 +40,20 @@ pub enum Error {
|
|||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
pub struct CartManager {
|
pub struct CartManager {
|
||||||
db: Addr<Database>,
|
db: actix::Addr<Database>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Actor for CartManager {
|
impl actix::Actor for CartManager {
|
||||||
type Context = Context<Self>;
|
type Context = actix::Context<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CartManager {
|
impl CartManager {
|
||||||
pub fn new(db: Addr<Database>) -> Self {
|
pub fn new(db: actix::Addr<Database>) -> Self {
|
||||||
Self { db }
|
Self { db }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Message)]
|
#[derive(actix::Message)]
|
||||||
#[rtype(result = "Result<ShoppingCartItem>")]
|
#[rtype(result = "Result<ShoppingCartItem>")]
|
||||||
pub struct AddItem {
|
pub struct AddItem {
|
||||||
pub buyer_id: AccountId,
|
pub buyer_id: AccountId,
|
||||||
@ -66,9 +64,9 @@ pub struct AddItem {
|
|||||||
|
|
||||||
cart_async_handler!(AddItem, add_item, ShoppingCartItem);
|
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
|
match db
|
||||||
.send(database::EnsureActiveShoppingCart {
|
.send(database_manager::EnsureActiveShoppingCart {
|
||||||
buyer_id: msg.buyer_id,
|
buyer_id: msg.buyer_id,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
@ -77,7 +75,7 @@ async fn add_item(msg: AddItem, db: Addr<Database>) -> Result<ShoppingCartItem>
|
|||||||
_ => return Err(Error::ShoppingCartFailed),
|
_ => return Err(Error::ShoppingCartFailed),
|
||||||
};
|
};
|
||||||
let cart = match db
|
let cart = match db
|
||||||
.send(database::AccountShoppingCarts {
|
.send(database_manager::AccountShoppingCarts {
|
||||||
account_id: msg.buyer_id,
|
account_id: msg.buyer_id,
|
||||||
state: Some(ShoppingCartState::Active),
|
state: Some(ShoppingCartState::Active),
|
||||||
})
|
})
|
||||||
@ -95,7 +93,7 @@ async fn add_item(msg: AddItem, db: Addr<Database>) -> Result<ShoppingCartItem>
|
|||||||
_ => return Err(Error::CartNotAvailable),
|
_ => return Err(Error::CartNotAvailable),
|
||||||
};
|
};
|
||||||
match db
|
match db
|
||||||
.send(database::CreateShoppingCartItem {
|
.send(database_manager::CreateShoppingCartItem {
|
||||||
product_id: msg.product_id,
|
product_id: msg.product_id,
|
||||||
shopping_cart_id: cart.id,
|
shopping_cart_id: cart.id,
|
||||||
quantity: msg.quantity,
|
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>>")]
|
#[rtype(result = "Result<Option<ShoppingCartItem>>")]
|
||||||
pub struct RemoveProduct {
|
pub struct RemoveProduct {
|
||||||
pub shopping_cart_id: ShoppingCartId,
|
pub shopping_cart_id: ShoppingCartId,
|
||||||
@ -119,10 +117,10 @@ cart_async_handler!(RemoveProduct, remove_product, Option<ShoppingCartItem>);
|
|||||||
|
|
||||||
pub(crate) async fn remove_product(
|
pub(crate) async fn remove_product(
|
||||||
msg: RemoveProduct,
|
msg: RemoveProduct,
|
||||||
db: Addr<Database>,
|
db: actix::Addr<Database>,
|
||||||
) -> Result<Option<ShoppingCartItem>> {
|
) -> Result<Option<ShoppingCartItem>> {
|
||||||
match db
|
match db
|
||||||
.send(database::RemoveCartItem {
|
.send(database_manager::RemoveCartItem {
|
||||||
shopping_cart_id: msg.shopping_cart_id,
|
shopping_cart_id: msg.shopping_cart_id,
|
||||||
shopping_cart_item_id: Some(msg.shopping_cart_item_id),
|
shopping_cart_item_id: Some(msg.shopping_cart_item_id),
|
||||||
product_id: None,
|
product_id: None,
|
||||||
@ -141,7 +139,7 @@ pub(crate) async fn remove_product(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Message)]
|
#[derive(actix::Message)]
|
||||||
#[rtype(result = "Result<Option<ShoppingCartItem>>")]
|
#[rtype(result = "Result<Option<ShoppingCartItem>>")]
|
||||||
pub struct ChangeQuantity {
|
pub struct ChangeQuantity {
|
||||||
pub shopping_cart_id: ShoppingCartId,
|
pub shopping_cart_id: ShoppingCartId,
|
||||||
@ -154,7 +152,7 @@ cart_async_handler!(ChangeQuantity, change_quantity, Option<ShoppingCartItem>);
|
|||||||
|
|
||||||
pub(crate) async fn change_quantity(
|
pub(crate) async fn change_quantity(
|
||||||
msg: ChangeQuantity,
|
msg: ChangeQuantity,
|
||||||
db: Addr<Database>,
|
db: actix::Addr<Database>,
|
||||||
) -> Result<Option<ShoppingCartItem>> {
|
) -> Result<Option<ShoppingCartItem>> {
|
||||||
if **msg.quantity == 0 {
|
if **msg.quantity == 0 {
|
||||||
return remove_product(
|
return remove_product(
|
||||||
@ -166,41 +164,23 @@ pub(crate) async fn change_quantity(
|
|||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
let item: ShoppingCartItem = match db
|
let item: ShoppingCartItem = query_db!(
|
||||||
.send(database::FindShoppingCartItem {
|
db,
|
||||||
|
database_manager::FindShoppingCartItem {
|
||||||
id: msg.shopping_cart_item_id,
|
id: msg.shopping_cart_item_id,
|
||||||
})
|
},
|
||||||
.await
|
Error::NotExists(msg.shopping_cart_item_id)
|
||||||
{
|
);
|
||||||
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));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
match db
|
Ok(Some(query_db!(
|
||||||
.send(database::UpdateShoppingCartItem {
|
db,
|
||||||
|
database_manager::UpdateShoppingCartItem {
|
||||||
id: msg.shopping_cart_item_id,
|
id: msg.shopping_cart_item_id,
|
||||||
product_id: item.product_id,
|
product_id: item.product_id,
|
||||||
shopping_cart_id: item.shopping_cart_id,
|
shopping_cart_id: item.shopping_cart_id,
|
||||||
quantity: msg.quantity,
|
quantity: msg.quantity,
|
||||||
quantity_unit: msg.quantity_unit,
|
quantity_unit: msg.quantity_unit,
|
||||||
})
|
},
|
||||||
.await
|
Error::ChangeQuantity
|
||||||
{
|
)))
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
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 sqlx::PgPool;
|
||||||
|
|
||||||
use super::Result;
|
use super::Result;
|
||||||
use crate::database::{
|
use crate::{
|
||||||
create_order_item, shopping_cart_set_state, CreateOrderItem, Database, ShoppingCartSetState,
|
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)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
@ -45,7 +45,7 @@ ORDER BY id DESC
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub mod create_order {
|
pub mod create_order {
|
||||||
use crate::model::{ProductId, Quantity, QuantityUnit};
|
use model::{ProductId, Quantity, QuantityUnit};
|
||||||
|
|
||||||
pub struct OrderItem {
|
pub struct OrderItem {
|
||||||
pub product_id: ProductId,
|
pub product_id: ProductId,
|
@ -1,9 +1,8 @@
|
|||||||
|
use model::{AccountId, AccountState, Email, FullAccount, Login, PassHash, Role};
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|
||||||
use super::Result;
|
use super::Result;
|
||||||
use crate::database::Database;
|
use crate::{db_async_handler, Database};
|
||||||
use crate::db_async_handler;
|
|
||||||
use crate::model::{AccountId, AccountState, Email, FullAccount, Login, PassHash, Role};
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
@ -1,6 +1,7 @@
|
|||||||
pub use account_orders::*;
|
pub use account_orders::*;
|
||||||
pub use accounts::*;
|
pub use accounts::*;
|
||||||
use actix::{Actor, Context};
|
use actix::{Actor, Context};
|
||||||
|
use config::SharedAppConfig;
|
||||||
pub use order_items::*;
|
pub use order_items::*;
|
||||||
pub use products::*;
|
pub use products::*;
|
||||||
pub use shopping_cart_items::*;
|
pub use shopping_cart_items::*;
|
||||||
@ -9,8 +10,6 @@ use sqlx::PgPool;
|
|||||||
pub use stocks::*;
|
pub use stocks::*;
|
||||||
pub use tokens::*;
|
pub use tokens::*;
|
||||||
|
|
||||||
use crate::config::SharedAppConfig;
|
|
||||||
|
|
||||||
pub mod account_orders;
|
pub mod account_orders;
|
||||||
pub mod accounts;
|
pub mod accounts;
|
||||||
pub mod order_items;
|
pub mod order_items;
|
||||||
@ -134,7 +133,7 @@ impl Clone for Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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 url = config.lock().database().url();
|
||||||
let pool = sqlx::PgPool::connect(&url).await.map_err(Error::Connect)?;
|
let pool = sqlx::PgPool::connect(&url).await.map_err(Error::Connect)?;
|
||||||
Ok(Database { pool })
|
Ok(Database { pool })
|
@ -1,9 +1,8 @@
|
|||||||
|
use model::*;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|
||||||
use super::Result;
|
use super::Result;
|
||||||
use crate::database::Database;
|
use crate::{db_async_handler, Database};
|
||||||
use crate::model::*;
|
|
||||||
use crate::{db_async_handler, model};
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
@ -1,12 +1,11 @@
|
|||||||
use actix::Message;
|
use actix::Message;
|
||||||
|
use model::{
|
||||||
use super::Result;
|
|
||||||
use crate::database::Database;
|
|
||||||
use crate::model::{
|
|
||||||
Days, Price, Product, ProductCategory, ProductId, ProductLongDesc, ProductName,
|
Days, Price, Product, ProductCategory, ProductId, ProductLongDesc, ProductName,
|
||||||
ProductShortDesc,
|
ProductShortDesc,
|
||||||
};
|
};
|
||||||
use crate::{database, model};
|
|
||||||
|
use super::Result;
|
||||||
|
use crate::Database;
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
@ -48,7 +47,7 @@ FROM products
|
|||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
log::error!("{e:?}");
|
log::error!("{e:?}");
|
||||||
database::Error::Product(Error::All)
|
crate::Error::Product(Error::All)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +91,7 @@ RETURNING id,
|
|||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
log::error!("{e:?}");
|
log::error!("{e:?}");
|
||||||
database::Error::Product(Error::Create)
|
crate::Error::Product(Error::Create)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,7 +143,7 @@ RETURNING id,
|
|||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
log::error!("{e:?}");
|
log::error!("{e:?}");
|
||||||
database::Error::Product(Error::Update)
|
crate::Error::Product(Error::Update)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,7 +182,7 @@ RETURNING id,
|
|||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
log::error!("{e:?}");
|
log::error!("{e:?}");
|
||||||
database::Error::Product(Error::Delete)
|
crate::Error::Product(Error::Delete)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,6 +225,6 @@ WHERE shopping_cart_id = $1
|
|||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
log::error!("{e:?}");
|
log::error!("{e:?}");
|
||||||
database::Error::Product(Error::ShoppingCartProducts)
|
crate::Error::Product(Error::ShoppingCartProducts)
|
||||||
})
|
})
|
||||||
}
|
}
|
@ -1,9 +1,8 @@
|
|||||||
|
use model::*;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|
||||||
use super::Result;
|
use super::Result;
|
||||||
use crate::database::Database;
|
use crate::{db_async_handler, Database};
|
||||||
use crate::model::*;
|
|
||||||
use crate::{database, db_async_handler};
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
@ -253,7 +252,7 @@ WHERE shopping_cart_id = $1
|
|||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
log::error!("{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(msg.shopping_cart_id)
|
||||||
.bind(product_id),
|
.bind(product_id),
|
||||||
_ => return Err(database::Error::ShoppingCartItem(Error::NoIdentity)),
|
_ => return Err(crate::Error::ShoppingCartItem(Error::NoIdentity)),
|
||||||
}
|
}
|
||||||
.fetch_optional(&pool)
|
.fetch_optional(&pool)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
log::error!("{e:?}");
|
log::error!("{e:?}");
|
||||||
database::Error::ShoppingCartItem(Error::Update {
|
crate::Error::ShoppingCartItem(Error::Update {
|
||||||
shopping_cart_item_id: msg.shopping_cart_item_id,
|
shopping_cart_item_id: msg.shopping_cart_item_id,
|
||||||
product_id: msg.product_id,
|
product_id: msg.product_id,
|
||||||
})
|
})
|
@ -1,9 +1,8 @@
|
|||||||
|
use model::*;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|
||||||
use super::Result;
|
use super::Result;
|
||||||
use crate::database::Database;
|
use crate::{db_async_handler, Database};
|
||||||
use crate::db_async_handler;
|
|
||||||
use crate::model::*;
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
@ -1,10 +1,8 @@
|
|||||||
use actix::Message;
|
use actix::Message;
|
||||||
|
use model::{ProductId, Quantity, QuantityUnit, Stock, StockId};
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|
||||||
use super::Result;
|
use crate::{Database, Result};
|
||||||
use crate::database::Database;
|
|
||||||
use crate::model::{ProductId, Quantity, QuantityUnit, Stock, StockId};
|
|
||||||
use crate::{database, model};
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
@ -35,7 +33,7 @@ FROM stocks
|
|||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
log::error!("{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
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
log::error!("{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
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
log::error!("{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
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
log::error!("{e:?}");
|
log::error!("{e:?}");
|
||||||
database::Error::Stock(Error::Delete)
|
crate::Error::Stock(Error::Delete)
|
||||||
})
|
})
|
||||||
}
|
}
|
@ -1,10 +1,8 @@
|
|||||||
use actix::Message;
|
use actix::Message;
|
||||||
|
use model::{AccountId, Audience, Token};
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|
||||||
use super::Result;
|
use crate::{db_async_handler, Database, Result};
|
||||||
use crate::database::Database;
|
|
||||||
use crate::model::{AccountId, Audience, Token};
|
|
||||||
use crate::{database, db_async_handler, Role};
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
@ -33,7 +31,7 @@ WHERE jwt_id = $1
|
|||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
log::error!("{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>")]
|
#[rtype(result = "Result<Token>")]
|
||||||
pub struct CreateToken {
|
pub struct CreateToken {
|
||||||
pub customer_id: uuid::Uuid,
|
pub customer_id: uuid::Uuid,
|
||||||
pub role: Role,
|
pub role: model::Role,
|
||||||
pub subject: AccountId,
|
pub subject: AccountId,
|
||||||
pub audience: Audience,
|
pub audience: Audience,
|
||||||
}
|
}
|
||||||
@ -68,6 +66,6 @@ RETURNING id, customer_id, role, issuer, subject, audience, expiration_time, not
|
|||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
log::error!("{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 std::sync::Arc;
|
||||||
|
|
||||||
use crate::config::SharedAppConfig;
|
use config::SharedAppConfig;
|
||||||
use crate::Email;
|
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! mail_async_handler {
|
macro_rules! mail_async_handler {
|
||||||
@ -50,7 +49,7 @@ impl EmailManager {
|
|||||||
#[derive(actix::Message)]
|
#[derive(actix::Message)]
|
||||||
#[rtype(result = "Result<SendState>")]
|
#[rtype(result = "Result<SendState>")]
|
||||||
pub struct TestMail {
|
pub struct TestMail {
|
||||||
pub receiver: Email,
|
pub receiver: model::Email,
|
||||||
}
|
}
|
||||||
|
|
||||||
mail_async_handler!(TestMail, test_mail, SendState);
|
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 actix::Message;
|
||||||
|
use config::SharedAppConfig;
|
||||||
use crate::config::SharedAppConfig;
|
use database_manager::{query_db, SharedDatabase};
|
||||||
use crate::database::{self, SharedDatabase};
|
use model::{AccountId, AccountOrder, OrderStatus, ShoppingCart, ShoppingCartId, ShoppingCartItem};
|
||||||
use crate::model::{
|
|
||||||
AccountId, AccountOrder, OrderStatus, ShoppingCart, ShoppingCartId, ShoppingCartItem,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! order_async_handler {
|
macro_rules! order_async_handler {
|
||||||
@ -63,64 +60,40 @@ pub(crate) async fn create_order(
|
|||||||
db: SharedDatabase,
|
db: SharedDatabase,
|
||||||
_config: SharedAppConfig,
|
_config: SharedAppConfig,
|
||||||
) -> Result<AccountOrder> {
|
) -> Result<AccountOrder> {
|
||||||
let cart: ShoppingCart = match db
|
let cart: ShoppingCart = query_db!(
|
||||||
.send(database::FindShoppingCart {
|
db,
|
||||||
|
database_manager::FindShoppingCart {
|
||||||
id: msg.shopping_cart_id,
|
id: msg.shopping_cart_id,
|
||||||
})
|
},
|
||||||
.await
|
Error::ShoppingCart,
|
||||||
{
|
Error::DatabaseInternal
|
||||||
Ok(Ok(cart)) => cart,
|
);
|
||||||
Ok(Err(e)) => {
|
let items: Vec<ShoppingCartItem> = query_db!(
|
||||||
log::error!("{e}");
|
db,
|
||||||
return Err(Error::ShoppingCart);
|
database_manager::AccountShoppingCartItems {
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
log::error!("{e:?}");
|
|
||||||
return Err(Error::DatabaseInternal);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let items: Vec<ShoppingCartItem> = match db
|
|
||||||
.send(database::AccountShoppingCartItems {
|
|
||||||
account_id: cart.buyer_id,
|
account_id: cart.buyer_id,
|
||||||
shopping_cart_id: Some(cart.id),
|
shopping_cart_id: Some(cart.id),
|
||||||
})
|
},
|
||||||
.await
|
Error::ShoppingCart,
|
||||||
{
|
Error::DatabaseInternal
|
||||||
Ok(Ok(items)) => items,
|
);
|
||||||
Ok(Err(e)) => {
|
let order = query_db!(
|
||||||
log::error!("{e}");
|
db,
|
||||||
return Err(Error::ShoppingCart);
|
database_manager::CreateAccountOrder {
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
log::error!("{e:?}");
|
|
||||||
return Err(Error::DatabaseInternal);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let order = match db
|
|
||||||
.send(database::CreateAccountOrder {
|
|
||||||
shopping_cart_id: cart.id,
|
shopping_cart_id: cart.id,
|
||||||
buyer_id: msg.account_id,
|
buyer_id: msg.account_id,
|
||||||
items: items
|
items: items
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|item| database::create_order::OrderItem {
|
.map(|item| database_manager::create_order::OrderItem {
|
||||||
product_id: item.product_id,
|
product_id: item.product_id,
|
||||||
quantity: item.quantity,
|
quantity: item.quantity,
|
||||||
quantity_unit: item.quantity_unit,
|
quantity_unit: item.quantity_unit,
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
})
|
},
|
||||||
.await
|
Error::CreateAccountOrder,
|
||||||
{
|
Error::DatabaseInternal
|
||||||
Ok(Ok(order)) => order,
|
);
|
||||||
Ok(Err(e)) => {
|
|
||||||
log::error!("{e}");
|
|
||||||
return Err(Error::CreateAccountOrder);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
log::error!("{e:?}");
|
|
||||||
return Err(Error::DatabaseInternal);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(order)
|
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 std::sync::Arc;
|
||||||
|
|
||||||
use actix::Addr;
|
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 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_export]
|
||||||
macro_rules! pay_async_handler {
|
macro_rules! pay_async_handler {
|
||||||
($msg: ty, $async: ident, $res: ty) => {
|
($msg: ty, $async: ident, $res: ty) => {
|
||||||
@ -183,14 +181,14 @@ pub(crate) async fn request_payment(
|
|||||||
|
|
||||||
let cart: model::ShoppingCart = query_db!(
|
let cart: model::ShoppingCart = query_db!(
|
||||||
db,
|
db,
|
||||||
database::EnsureActiveShoppingCart {
|
database_manager::EnsureActiveShoppingCart {
|
||||||
buyer_id: msg.buyer_id
|
buyer_id: msg.buyer_id
|
||||||
},
|
},
|
||||||
Error::UnavailableShoppingCart
|
Error::UnavailableShoppingCart
|
||||||
);
|
);
|
||||||
let cart_items: Vec<model::ShoppingCartItem> = query_db!(
|
let cart_items: Vec<model::ShoppingCartItem> = query_db!(
|
||||||
db,
|
db,
|
||||||
database::CartItems {
|
database_manager::CartItems {
|
||||||
shopping_cart_id: cart.id,
|
shopping_cart_id: cart.id,
|
||||||
},
|
},
|
||||||
Error::UnavailableShoppingCart
|
Error::UnavailableShoppingCart
|
||||||
@ -205,7 +203,7 @@ pub(crate) async fn request_payment(
|
|||||||
|
|
||||||
let cart_products: Vec<model::Product> = query_db!(
|
let cart_products: Vec<model::Product> = query_db!(
|
||||||
db,
|
db,
|
||||||
database::ShoppingCartProducts {
|
database_manager::ShoppingCartProducts {
|
||||||
shopping_cart_id: cart.id,
|
shopping_cart_id: cart.id,
|
||||||
},
|
},
|
||||||
Error::UnavailableShoppingCart
|
Error::UnavailableShoppingCart
|
||||||
@ -213,7 +211,7 @@ pub(crate) async fn request_payment(
|
|||||||
|
|
||||||
let db_order: model::AccountOrder = query_db!(
|
let db_order: model::AccountOrder = query_db!(
|
||||||
db,
|
db,
|
||||||
database::CreateAccountOrder {
|
database_manager::CreateAccountOrder {
|
||||||
buyer_id: msg.buyer_id,
|
buyer_id: msg.buyer_id,
|
||||||
items: cart_products
|
items: cart_products
|
||||||
.iter()
|
.iter()
|
||||||
@ -225,7 +223,7 @@ pub(crate) async fn request_payment(
|
|||||||
model::QuantityUnit::Gram,
|
model::QuantityUnit::Gram,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
database::create_order::OrderItem {
|
database_manager::create_order::OrderItem {
|
||||||
product_id: product.id,
|
product_id: product.id,
|
||||||
quantity,
|
quantity,
|
||||||
quantity_unit,
|
quantity_unit,
|
||||||
@ -276,7 +274,7 @@ pub(crate) async fn request_payment(
|
|||||||
|
|
||||||
query_db!(
|
query_db!(
|
||||||
db,
|
db,
|
||||||
database::SetOrderServiceId {
|
database_manager::SetOrderServiceId {
|
||||||
service_order_id: order_id.0,
|
service_order_id: order_id.0,
|
||||||
id: db_order.id,
|
id: db_order.id,
|
||||||
},
|
},
|
||||||
@ -289,7 +287,7 @@ pub(crate) async fn request_payment(
|
|||||||
|
|
||||||
let order_items = query_db!(
|
let order_items = query_db!(
|
||||||
db,
|
db,
|
||||||
database::OrderItems {
|
database_manager::OrderItems {
|
||||||
order_id: db_order.id
|
order_id: db_order.id
|
||||||
},
|
},
|
||||||
Error::CreateOrder
|
Error::CreateOrder
|
||||||
@ -332,7 +330,7 @@ pub(crate) async fn update_payment(
|
|||||||
pay_u::PaymentStatus::Canceled => OrderStatus::Cancelled,
|
pay_u::PaymentStatus::Canceled => OrderStatus::Cancelled,
|
||||||
};
|
};
|
||||||
let _ = db
|
let _ = db
|
||||||
.send(database::UpdateAccountOrderByExt {
|
.send(database_manager::UpdateAccountOrderByExt {
|
||||||
status,
|
status,
|
||||||
order_ext_id,
|
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 actix::{Addr, Message};
|
||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
|
use config::SharedAppConfig;
|
||||||
|
use database_manager::{query_db, Database};
|
||||||
use hmac::digest::KeyInit;
|
use hmac::digest::KeyInit;
|
||||||
use hmac::Hmac;
|
use hmac::Hmac;
|
||||||
|
use model::{AccountId, Audience, Role, Token, TokenString};
|
||||||
use sha2::Sha256;
|
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_export]
|
||||||
macro_rules! token_async_handler {
|
macro_rules! token_async_handler {
|
||||||
($msg: ty, $async: ident, $res: ty) => {
|
($msg: ty, $async: ident, $res: ty) => {
|
||||||
@ -105,25 +103,17 @@ pub(crate) async fn create_token(
|
|||||||
} = msg;
|
} = msg;
|
||||||
let audience = audience.unwrap_or_default();
|
let audience = audience.unwrap_or_default();
|
||||||
|
|
||||||
let token: Token = match db
|
let token: Token = query_db!(
|
||||||
.send(database::CreateToken {
|
db,
|
||||||
|
database_manager::CreateToken {
|
||||||
customer_id,
|
customer_id,
|
||||||
role,
|
role,
|
||||||
subject,
|
subject,
|
||||||
audience,
|
audience,
|
||||||
})
|
},
|
||||||
.await
|
Error::Save,
|
||||||
{
|
Error::SaveInternal
|
||||||
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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let token_string = {
|
let token_string = {
|
||||||
use jwt::SignWithKey;
|
use jwt::SignWithKey;
|
||||||
@ -212,25 +202,17 @@ pub(crate) async fn validate(
|
|||||||
_ => return Err(Error::Validate),
|
_ => return Err(Error::Validate),
|
||||||
};
|
};
|
||||||
|
|
||||||
let token: Token = match db
|
let token: Token = query_db!(
|
||||||
.send(TokenByJti {
|
db,
|
||||||
|
database_manager::TokenByJti {
|
||||||
jti: match uuid::Uuid::from_str(jti) {
|
jti: match uuid::Uuid::from_str(jti) {
|
||||||
Ok(uid) => uid,
|
Ok(uid) => uid,
|
||||||
_ => return Err(Error::Validate),
|
_ => return Err(Error::Validate),
|
||||||
},
|
},
|
||||||
})
|
},
|
||||||
.await
|
Error::Validate,
|
||||||
{
|
Error::ValidateInternal
|
||||||
Ok(Ok(token)) => token,
|
);
|
||||||
Ok(Err(e)) => {
|
|
||||||
log::error!("{e}");
|
|
||||||
return Err(Error::Validate);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
log::error!("{e:?}");
|
|
||||||
return Err(Error::ValidateInternal);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if !validate_pair(&claims, "cti", token.customer_id, validate_uuid) {
|
if !validate_pair(&claims, "cti", token.customer_id, validate_uuid) {
|
||||||
return Ok((token, false));
|
return Ok((token, false));
|
@ -4,6 +4,16 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[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 = { version = "0.13", features = [] }
|
||||||
actix-rt = { version = "2.7", features = [] }
|
actix-rt = { version = "2.7", features = [] }
|
||||||
actix-web = { version = "4.0", features = [] }
|
actix-web = { version = "4.0", features = [] }
|
||||||
@ -53,21 +63,11 @@ tokio = { version = "1.17", features = ["full"] }
|
|||||||
futures = { version = "0.3", features = [] }
|
futures = { version = "0.3", features = [] }
|
||||||
futures-util = { 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 = [] }
|
oauth2 = { version = "4.1", features = [] }
|
||||||
|
|
||||||
async-trait = { version = "0.1", features = [] }
|
async-trait = { version = "0.1", features = [] }
|
||||||
|
|
||||||
jemallocator = { version = "0.3", 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
|
# For rewrite into bus-based app
|
||||||
messagebus = { version = "0.9.13" }
|
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 argon2::{Algorithm, Argon2, Params, Version};
|
||||||
|
use model::Password;
|
||||||
use password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString};
|
use password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString};
|
||||||
|
|
||||||
use crate::model::Password;
|
|
||||||
use crate::PassHash;
|
use crate::PassHash;
|
||||||
|
|
||||||
mod order_state;
|
mod order_state;
|
||||||
|
@ -8,7 +8,10 @@ use actix_session::SessionMiddleware;
|
|||||||
use actix_web::middleware::Logger;
|
use actix_web::middleware::Logger;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Data;
|
||||||
use actix_web::{App, HttpServer};
|
use actix_web::{App, HttpServer};
|
||||||
|
use config::UpdateConfig;
|
||||||
|
use email_manager::TestMail;
|
||||||
use jemallocator::Jemalloc;
|
use jemallocator::Jemalloc;
|
||||||
|
use model::{Email, Login, PassHash, Password, Role};
|
||||||
use opts::{
|
use opts::{
|
||||||
Command, CreateAccountCmd, CreateAccountOpts, GenerateHashOpts, MigrateOpts, Opts, ServerOpts,
|
Command, CreateAccountCmd, CreateAccountOpts, GenerateHashOpts, MigrateOpts, Opts, ServerOpts,
|
||||||
TestMailerOpts,
|
TestMailerOpts,
|
||||||
@ -16,18 +19,9 @@ use opts::{
|
|||||||
use password_hash::SaltString;
|
use password_hash::SaltString;
|
||||||
use validator::{validate_email, validate_length};
|
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::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 logic;
|
||||||
pub mod model;
|
|
||||||
mod opts;
|
mod opts;
|
||||||
pub mod routes;
|
pub mod routes;
|
||||||
|
|
||||||
@ -43,7 +37,7 @@ pub enum Error {
|
|||||||
#[error("Unable to read password from STDIN. {0:?}")]
|
#[error("Unable to read password from STDIN. {0:?}")]
|
||||||
ReadPass(std::io::Error),
|
ReadPass(std::io::Error),
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
Database(#[from] database::Error),
|
Database(#[from] database_manager::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, 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 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 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 order_manager = order_manager::OrderManager::new(app_config.clone(), db.clone()).start();
|
||||||
let payment_manager = payment_manager::PaymentManager::build(app_config.clone(), db.clone())
|
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(payment_manager.clone()))
|
||||||
.app_data(Data::new(search_manager.clone()))
|
.app_data(Data::new(search_manager.clone()))
|
||||||
.configure(routes::configure)
|
.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)
|
.bind(addr)
|
||||||
.map_err(Error::Boot)?
|
.map_err(Error::Boot)?
|
||||||
@ -102,7 +102,7 @@ async fn migrate(opts: MigrateOpts) -> Result<()> {
|
|||||||
use sqlx::migrate::MigrateError;
|
use sqlx::migrate::MigrateError;
|
||||||
|
|
||||||
let config = config::default_load(&opts);
|
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> =
|
let res: std::result::Result<(), MigrateError> =
|
||||||
sqlx::migrate!("../migrations").run(db.pool()).await;
|
sqlx::migrate!("../migrations").run(db.pool()).await;
|
||||||
match res {
|
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");
|
panic!("Login must have at least 4 characters and no more than 100");
|
||||||
}
|
}
|
||||||
let config = config::default_load(&opts);
|
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 {
|
let pass = match opts.pass_file {
|
||||||
Some(path) => std::fs::read_to_string(path).map_err(Error::PassFile)?,
|
Some(path) => std::fs::read_to_string(path).map_err(Error::PassFile)?,
|
||||||
None => {
|
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();
|
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),
|
email: Email::from(opts.email),
|
||||||
login: Login::from(opts.login),
|
login: Login::from(opts.login),
|
||||||
pass_hash: PassHash::from(hash),
|
pass_hash: PassHash::from(hash),
|
||||||
@ -208,6 +210,9 @@ async fn main() -> Result<()> {
|
|||||||
Command::GenerateHash(opts) => generate_hash(opts).await,
|
Command::GenerateHash(opts) => generate_hash(opts).await,
|
||||||
Command::CreateAccount(opts) => create_account(opts).await,
|
Command::CreateAccount(opts) => create_account(opts).await,
|
||||||
Command::TestMailer(opts) => test_mailer(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 gumdrop::Options;
|
||||||
|
use model::Email;
|
||||||
use crate::config::AppConfig;
|
|
||||||
use crate::model::Email;
|
|
||||||
|
|
||||||
pub trait UpdateConfig {
|
|
||||||
fn update_config(&self, config: &mut AppConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ResolveDbUrl {
|
pub trait ResolveDbUrl {
|
||||||
fn own_db_url(&self) -> Option<String>;
|
fn own_db_url(&self) -> Option<String>;
|
||||||
|
@ -4,14 +4,14 @@ use actix::Addr;
|
|||||||
use actix_session::Session;
|
use actix_session::Session;
|
||||||
use actix_web::web::{scope, Data, Json, ServiceConfig};
|
use actix_web::web::{scope, Data, Json, ServiceConfig};
|
||||||
use actix_web::{delete, get, post, HttpResponse};
|
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 serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::config::SharedAppConfig;
|
|
||||||
use crate::database::{AccountByIdentity, Database};
|
|
||||||
use crate::logic::encrypt_password;
|
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::routes::{RequireLogin, Result};
|
||||||
use crate::{database, model, routes};
|
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! admin_send_db {
|
macro_rules! admin_send_db {
|
||||||
@ -42,7 +42,7 @@ pub enum Error {
|
|||||||
#[error("Password and password confirmation are different")]
|
#[error("Password and password confirmation are different")]
|
||||||
DifferentPasswords,
|
DifferentPasswords,
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
Database(#[from] database::Error),
|
Database(#[from] database_manager::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
@ -71,23 +71,14 @@ async fn sign_in(
|
|||||||
) -> Result<HttpResponse> {
|
) -> Result<HttpResponse> {
|
||||||
log::debug!("{:?}", payload);
|
log::debug!("{:?}", payload);
|
||||||
let db = db.into_inner();
|
let db = db.into_inner();
|
||||||
let user: model::FullAccount = match db
|
let user: model::FullAccount = query_db!(
|
||||||
.send(AccountByIdentity {
|
db,
|
||||||
|
database_manager::AccountByIdentity {
|
||||||
email: payload.email,
|
email: payload.email,
|
||||||
login: payload.login,
|
login: payload.login,
|
||||||
})
|
},
|
||||||
.await
|
routes::Error::Unauthorized
|
||||||
{
|
);
|
||||||
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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if let Err(e) = crate::logic::validate_password(&payload.password, &user.pass_hash) {
|
if let Err(e) = crate::logic::validate_password(&payload.password, &user.pass_hash) {
|
||||||
log::error!("Password validation failed. {}", e);
|
log::error!("Password validation failed. {}", e);
|
||||||
Err(routes::Error::Unauthorized)
|
Err(routes::Error::Unauthorized)
|
||||||
@ -143,27 +134,17 @@ async fn register(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match db
|
query_db!(
|
||||||
.send(database::CreateAccount {
|
db,
|
||||||
|
database_manager::CreateAccount {
|
||||||
email: input.email,
|
email: input.email,
|
||||||
login: input.login,
|
login: input.login,
|
||||||
pass_hash: PassHash::from(hash),
|
pass_hash: PassHash::from(hash),
|
||||||
role: input.role,
|
role: input.role,
|
||||||
})
|
},
|
||||||
.await
|
super::Error::Admin(Error::Register),
|
||||||
{
|
super::Error::Admin(Error::Register)
|
||||||
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));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
response.success = response.errors.is_empty();
|
response.success = response.errors.is_empty();
|
||||||
Ok(if response.success {
|
Ok(if response.success {
|
||||||
|
@ -2,10 +2,10 @@ use actix::Addr;
|
|||||||
use actix_session::Session;
|
use actix_session::Session;
|
||||||
use actix_web::web::{Data, Json, ServiceConfig};
|
use actix_web::web::{Data, Json, ServiceConfig};
|
||||||
use actix_web::{get, patch, post, HttpResponse};
|
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::admin::Error;
|
||||||
use crate::routes::RequireLogin;
|
use crate::routes::RequireLogin;
|
||||||
use crate::{admin_send_db, encrypt_password, routes, Email, Login, PassHash, Password, Role};
|
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")]
|
#[get("/accounts")]
|
||||||
pub async fn accounts(session: Session, db: Data<Addr<Database>>) -> routes::Result<HttpResponse> {
|
pub async fn accounts(session: Session, db: Data<Addr<Database>>) -> routes::Result<HttpResponse> {
|
||||||
session.require_admin()?;
|
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))
|
Ok(HttpResponse::Ok().json(accounts))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,7 +63,7 @@ pub async fn update_account(
|
|||||||
|
|
||||||
let account = admin_send_db!(
|
let account = admin_send_db!(
|
||||||
db,
|
db,
|
||||||
database::UpdateAccount {
|
database_manager::UpdateAccount {
|
||||||
id: payload.id,
|
id: payload.id,
|
||||||
email: payload.email,
|
email: payload.email,
|
||||||
login: payload.login,
|
login: payload.login,
|
||||||
@ -107,7 +107,7 @@ pub async fn create_account(
|
|||||||
|
|
||||||
let account = admin_send_db!(
|
let account = admin_send_db!(
|
||||||
db,
|
db,
|
||||||
database::CreateAccount {
|
database_manager::CreateAccount {
|
||||||
email: payload.email,
|
email: payload.email,
|
||||||
login: payload.login,
|
login: payload.login,
|
||||||
pass_hash: PassHash::from(hash),
|
pass_hash: PassHash::from(hash),
|
||||||
|
@ -2,19 +2,19 @@ use actix::Addr;
|
|||||||
use actix_session::Session;
|
use actix_session::Session;
|
||||||
use actix_web::get;
|
use actix_web::get;
|
||||||
use actix_web::web::{Data, Json, ServiceConfig};
|
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::admin::Error;
|
||||||
use crate::routes::RequireLogin;
|
use crate::routes::RequireLogin;
|
||||||
use crate::{admin_send_db, database, model, routes};
|
use crate::{admin_send_db, routes};
|
||||||
|
|
||||||
#[get("/orders")]
|
#[get("/orders")]
|
||||||
async fn orders(session: Session, db: Data<Addr<Database>>) -> routes::Result<Json<AccountOrders>> {
|
async fn orders(session: Session, db: Data<Addr<Database>>) -> routes::Result<Json<AccountOrders>> {
|
||||||
session.require_admin()?;
|
session.require_admin()?;
|
||||||
|
|
||||||
let orders: Vec<model::AccountOrder> = admin_send_db!(&db, database::AllAccountOrders);
|
let orders: Vec<model::AccountOrder> = admin_send_db!(&db, database_manager::AllAccountOrders);
|
||||||
let items: Vec<model::OrderItem> = admin_send_db!(db, database::AllOrderItems);
|
let items: Vec<model::OrderItem> = admin_send_db!(db, database_manager::AllOrderItems);
|
||||||
|
|
||||||
Ok(Json((orders, items).into()))
|
Ok(Json((orders, items).into()))
|
||||||
}
|
}
|
||||||
|
@ -2,22 +2,23 @@ use actix::Addr;
|
|||||||
use actix_session::Session;
|
use actix_session::Session;
|
||||||
use actix_web::web::{Data, Json, ServiceConfig};
|
use actix_web::web::{Data, Json, ServiceConfig};
|
||||||
use actix_web::{delete, get, patch, post, HttpResponse};
|
use actix_web::{delete, get, patch, post, HttpResponse};
|
||||||
use serde::Deserialize;
|
use database_manager::Database;
|
||||||
|
use model::{
|
||||||
use crate::database::Database;
|
|
||||||
use crate::model::{
|
|
||||||
Days, Price, ProductCategory, ProductId, ProductLongDesc, ProductName, ProductShortDesc,
|
Days, Price, ProductCategory, ProductId, ProductLongDesc, ProductName, ProductShortDesc,
|
||||||
Quantity, QuantityUnit,
|
Quantity, QuantityUnit,
|
||||||
};
|
};
|
||||||
|
use search_manager::SearchManager;
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::routes::admin::Error;
|
use crate::routes::admin::Error;
|
||||||
use crate::routes::RequireLogin;
|
use crate::routes::RequireLogin;
|
||||||
use crate::{admin_send_db, database, routes};
|
use crate::{admin_send_db, routes};
|
||||||
|
|
||||||
#[get("/products")]
|
#[get("/products")]
|
||||||
async fn products(session: Session, db: Data<Addr<Database>>) -> routes::Result<HttpResponse> {
|
async fn products(session: Session, db: Data<Addr<Database>>) -> routes::Result<HttpResponse> {
|
||||||
session.require_admin()?;
|
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))
|
Ok(HttpResponse::Ok().json(products))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,7 +43,7 @@ async fn update_product(
|
|||||||
|
|
||||||
let product = admin_send_db!(
|
let product = admin_send_db!(
|
||||||
db,
|
db,
|
||||||
database::UpdateProduct {
|
database_manager::UpdateProduct {
|
||||||
id: payload.id,
|
id: payload.id,
|
||||||
name: payload.name,
|
name: payload.name,
|
||||||
short_description: payload.short_description,
|
short_description: payload.short_description,
|
||||||
@ -63,19 +64,21 @@ pub struct CreateProduct {
|
|||||||
pub category: Option<ProductCategory>,
|
pub category: Option<ProductCategory>,
|
||||||
pub price: Price,
|
pub price: Price,
|
||||||
pub deliver_days_flag: Days,
|
pub deliver_days_flag: Days,
|
||||||
|
pub lang: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/product")]
|
#[post("/product")]
|
||||||
async fn create_product(
|
async fn create_product(
|
||||||
session: Session,
|
session: Session,
|
||||||
db: Data<Addr<Database>>,
|
db: Data<Addr<Database>>,
|
||||||
|
search: Data<Addr<SearchManager>>,
|
||||||
Json(payload): Json<CreateProduct>,
|
Json(payload): Json<CreateProduct>,
|
||||||
) -> routes::Result<HttpResponse> {
|
) -> routes::Result<HttpResponse> {
|
||||||
session.require_admin()?;
|
session.require_admin()?;
|
||||||
|
|
||||||
let product = admin_send_db!(
|
let product: model::Product = admin_send_db!(
|
||||||
db.clone(),
|
db.clone(),
|
||||||
database::CreateProduct {
|
database_manager::CreateProduct {
|
||||||
name: payload.name,
|
name: payload.name,
|
||||||
short_description: payload.short_description,
|
short_description: payload.short_description,
|
||||||
long_description: payload.long_description,
|
long_description: payload.long_description,
|
||||||
@ -84,9 +87,17 @@ async fn create_product(
|
|||||||
deliver_days_flag: payload.deliver_days_flag,
|
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!(
|
let _ = admin_send_db!(
|
||||||
db,
|
db,
|
||||||
database::CreateStock {
|
database_manager::CreateStock {
|
||||||
product_id: product.id,
|
product_id: product.id,
|
||||||
quantity: Quantity::try_from(0).unwrap_or_default(),
|
quantity: Quantity::try_from(0).unwrap_or_default(),
|
||||||
quantity_unit: QuantityUnit::Piece,
|
quantity_unit: QuantityUnit::Piece,
|
||||||
@ -110,7 +121,7 @@ async fn delete_product(
|
|||||||
|
|
||||||
let product = admin_send_db!(
|
let product = admin_send_db!(
|
||||||
db,
|
db,
|
||||||
database::DeleteProduct {
|
database_manager::DeleteProduct {
|
||||||
product_id: payload.id
|
product_id: payload.id
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -2,19 +2,19 @@ use actix::Addr;
|
|||||||
use actix_session::Session;
|
use actix_session::Session;
|
||||||
use actix_web::web::{Data, Json, ServiceConfig};
|
use actix_web::web::{Data, Json, ServiceConfig};
|
||||||
use actix_web::{delete, get, patch, post, HttpResponse};
|
use actix_web::{delete, get, patch, post, HttpResponse};
|
||||||
|
use database_manager::Database;
|
||||||
|
use model::{ProductId, Quantity, QuantityUnit, StockId};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::database::Database;
|
|
||||||
use crate::model::{ProductId, Quantity, QuantityUnit, StockId};
|
|
||||||
use crate::routes::admin::Error;
|
use crate::routes::admin::Error;
|
||||||
use crate::routes::RequireLogin;
|
use crate::routes::RequireLogin;
|
||||||
use crate::{admin_send_db, database, routes};
|
use crate::{admin_send_db, routes};
|
||||||
|
|
||||||
#[get("/stocks")]
|
#[get("/stocks")]
|
||||||
async fn stocks(session: Session, db: Data<Addr<Database>>) -> routes::Result<HttpResponse> {
|
async fn stocks(session: Session, db: Data<Addr<Database>>) -> routes::Result<HttpResponse> {
|
||||||
session.require_admin()?;
|
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))
|
Ok(HttpResponse::Created().json(stocks))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,7 +36,7 @@ async fn update_stock(
|
|||||||
|
|
||||||
let stock = admin_send_db!(
|
let stock = admin_send_db!(
|
||||||
db,
|
db,
|
||||||
database::UpdateStock {
|
database_manager::UpdateStock {
|
||||||
id: payload.id,
|
id: payload.id,
|
||||||
product_id: payload.product_id,
|
product_id: payload.product_id,
|
||||||
quantity: payload.quantity,
|
quantity: payload.quantity,
|
||||||
@ -63,7 +63,7 @@ async fn create_stock(
|
|||||||
|
|
||||||
let stock = admin_send_db!(
|
let stock = admin_send_db!(
|
||||||
db,
|
db,
|
||||||
database::CreateStock {
|
database_manager::CreateStock {
|
||||||
product_id: payload.product_id,
|
product_id: payload.product_id,
|
||||||
quantity: payload.quantity,
|
quantity: payload.quantity,
|
||||||
quantity_unit: payload.quantity_unit
|
quantity_unit: payload.quantity_unit
|
||||||
@ -87,7 +87,7 @@ async fn delete_stock(
|
|||||||
|
|
||||||
let stock = admin_send_db!(
|
let stock = admin_send_db!(
|
||||||
db,
|
db,
|
||||||
database::DeleteStock {
|
database_manager::DeleteStock {
|
||||||
stock_id: payload.id
|
stock_id: payload.id
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -9,13 +9,13 @@ use actix_session::Session;
|
|||||||
use actix_web::body::BoxBody;
|
use actix_web::body::BoxBody;
|
||||||
use actix_web::web::ServiceConfig;
|
use actix_web::web::ServiceConfig;
|
||||||
use actix_web::{HttpRequest, HttpResponse, Responder, ResponseError};
|
use actix_web::{HttpRequest, HttpResponse, Responder, ResponseError};
|
||||||
|
use model::{RecordId, Token, TokenString};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
use token_manager::TokenManager;
|
||||||
|
|
||||||
pub use self::admin::Error as AdminError;
|
pub use self::admin::Error as AdminError;
|
||||||
pub use self::public::{Error as PublicError, V1Error, V1ShoppingCartError};
|
pub use self::public::{Error as PublicError, V1Error, V1ShoppingCartError};
|
||||||
use crate::model::{RecordId, Token, TokenString};
|
use crate::routes;
|
||||||
use crate::token_manager::TokenManager;
|
|
||||||
use crate::{routes, token_manager};
|
|
||||||
|
|
||||||
pub trait RequireLogin {
|
pub trait RequireLogin {
|
||||||
fn require_admin(&self) -> Result<RecordId>;
|
fn require_admin(&self) -> Result<RecordId>;
|
||||||
|
@ -4,8 +4,6 @@ use actix_web::web::ServiceConfig;
|
|||||||
use actix_web::{get, HttpResponse};
|
use actix_web::{get, HttpResponse};
|
||||||
pub use api_v1::{Error as V1Error, ShoppingCartError as V1ShoppingCartError};
|
pub use api_v1::{Error as V1Error, ShoppingCartError as V1ShoppingCartError};
|
||||||
|
|
||||||
use crate::database;
|
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! public_send_db {
|
macro_rules! public_send_db {
|
||||||
($db: expr, $msg: expr) => {{
|
($db: expr, $msg: expr) => {{
|
||||||
@ -35,7 +33,7 @@ pub enum Error {
|
|||||||
#[error("Internal server error")]
|
#[error("Internal server error")]
|
||||||
DatabaseConnection,
|
DatabaseConnection,
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
Database(#[from] database::Error),
|
Database(#[from] database_manager::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/")]
|
#[get("/")]
|
||||||
|
@ -2,17 +2,16 @@ use actix::Addr;
|
|||||||
use actix_web::web::{scope, Data, Json, ServiceConfig};
|
use actix_web::web::{scope, Data, Json, ServiceConfig};
|
||||||
use actix_web::{delete, get, post, HttpRequest, HttpResponse};
|
use actix_web::{delete, get, post, HttpRequest, HttpResponse};
|
||||||
use actix_web_httpauth::extractors::bearer::BearerAuth;
|
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::routes;
|
||||||
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::public::api_v1::{Error as ApiV1Error, ShoppingCartError};
|
use crate::routes::public::api_v1::{Error as ApiV1Error, ShoppingCartError};
|
||||||
use crate::routes::public::Error as PublicError;
|
use crate::routes::public::Error as PublicError;
|
||||||
use crate::routes::{RequireUser, Result};
|
use crate::routes::{RequireUser, Result};
|
||||||
use crate::token_manager::TokenManager;
|
|
||||||
use crate::{database, model, payment_manager, query_db, query_pay, routes};
|
|
||||||
|
|
||||||
#[get("/shopping-cart")]
|
#[get("/shopping-cart")]
|
||||||
async fn shopping_cart(
|
async fn shopping_cart(
|
||||||
@ -23,7 +22,7 @@ async fn shopping_cart(
|
|||||||
let (token, _) = credentials.require_user(tm.into_inner()).await?;
|
let (token, _) = credentials.require_user(tm.into_inner()).await?;
|
||||||
let cart: model::ShoppingCart = query_db!(
|
let cart: model::ShoppingCart = query_db!(
|
||||||
db,
|
db,
|
||||||
database::EnsureActiveShoppingCart {
|
database_manager::EnsureActiveShoppingCart {
|
||||||
buyer_id: AccountId::from(token.subject),
|
buyer_id: AccountId::from(token.subject),
|
||||||
},
|
},
|
||||||
routes::Error::Public(PublicError::ApiV1(ApiV1Error::ShoppingCart(
|
routes::Error::Public(PublicError::ApiV1(ApiV1Error::ShoppingCart(
|
||||||
@ -32,7 +31,7 @@ async fn shopping_cart(
|
|||||||
);
|
);
|
||||||
let items: Vec<model::ShoppingCartItem> = query_db!(
|
let items: Vec<model::ShoppingCartItem> = query_db!(
|
||||||
db,
|
db,
|
||||||
database::AccountShoppingCartItems {
|
database_manager::AccountShoppingCartItems {
|
||||||
account_id: cart.buyer_id,
|
account_id: cart.buyer_id,
|
||||||
shopping_cart_id: Some(cart.id),
|
shopping_cart_id: Some(cart.id),
|
||||||
},
|
},
|
||||||
@ -110,22 +109,14 @@ async fn delete_cart_item(
|
|||||||
) -> Result<HttpResponse> {
|
) -> Result<HttpResponse> {
|
||||||
let (token, _) = credentials.require_user(tm.into_inner()).await?;
|
let (token, _) = credentials.require_user(tm.into_inner()).await?;
|
||||||
|
|
||||||
let sc: model::ShoppingCart = match db
|
let sc: model::ShoppingCart = query_db!(
|
||||||
.send(database::EnsureActiveShoppingCart {
|
db,
|
||||||
|
database_manager::EnsureActiveShoppingCart {
|
||||||
buyer_id: AccountId::from(token.subject),
|
buyer_id: AccountId::from(token.subject),
|
||||||
})
|
},
|
||||||
.await
|
routes::Error::Public(super::Error::RemoveItem.into()),
|
||||||
{
|
routes::Error::Public(PublicError::DatabaseConnection)
|
||||||
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));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
match cart
|
match cart
|
||||||
.into_inner()
|
.into_inner()
|
||||||
|
@ -1,24 +1,24 @@
|
|||||||
use actix::Addr;
|
use actix::Addr;
|
||||||
use actix_web::web::{Data, Json, ServiceConfig};
|
use actix_web::web::{Data, Json, ServiceConfig};
|
||||||
use actix_web::{get, post, HttpResponse};
|
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::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::public::Error as PublicError;
|
||||||
use crate::routes::{self, Result};
|
use crate::routes::{self, Result};
|
||||||
use crate::token_manager::TokenManager;
|
use crate::{public_send_db, Login, Password};
|
||||||
use crate::{payment_manager, public_send_db, token_manager, Login, Password};
|
|
||||||
|
|
||||||
#[get("/products")]
|
#[get("/products")]
|
||||||
async fn products(db: Data<Addr<Database>>) -> Result<HttpResponse> {
|
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")]
|
#[get("/stocks")]
|
||||||
async fn stocks(db: Data<Addr<Database>>) -> Result<HttpResponse> {
|
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)]
|
#[derive(serde::Deserialize)]
|
||||||
@ -41,23 +41,15 @@ async fn sign_in(
|
|||||||
let db = db.into_inner();
|
let db = db.into_inner();
|
||||||
let tm = tm.into_inner();
|
let tm = tm.into_inner();
|
||||||
|
|
||||||
let account: FullAccount = match db
|
let account: FullAccount = query_db!(
|
||||||
.send(database::AccountByIdentity {
|
db,
|
||||||
|
database_manager::AccountByIdentity {
|
||||||
login: Some(Login::from(payload.login)),
|
login: Some(Login::from(payload.login)),
|
||||||
email: None,
|
email: None,
|
||||||
})
|
},
|
||||||
.await
|
routes::Error::Public(PublicError::DatabaseConnection),
|
||||||
{
|
routes::Error::Public(PublicError::DatabaseConnection)
|
||||||
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));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if validate_password(&Password::from(payload.password), &account.pass_hash).is_err() {
|
if validate_password(&Password::from(payload.password), &account.pass_hash).is_err() {
|
||||||
return Err(routes::Error::Unauthorized);
|
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 password_hash::SaltString;
|
||||||
use serde::{Deserialize, Serialize};
|
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 {
|
trait Example: Sized {
|
||||||
fn example() -> Self;
|
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)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct AppConfig {
|
pub struct AppConfig {
|
||||||
payment: PaymentConfig,
|
payment: PaymentConfig,
|
||||||
@ -350,6 +399,7 @@ pub struct AppConfig {
|
|||||||
mail: MailConfig,
|
mail: MailConfig,
|
||||||
database: DatabaseConfig,
|
database: DatabaseConfig,
|
||||||
search: SearchConfig,
|
search: SearchConfig,
|
||||||
|
files: FilesConfig,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
config_path: String,
|
config_path: String,
|
||||||
}
|
}
|
||||||
@ -362,6 +412,7 @@ impl Example for AppConfig {
|
|||||||
mail: MailConfig::example(),
|
mail: MailConfig::example(),
|
||||||
database: DatabaseConfig::example(),
|
database: DatabaseConfig::example(),
|
||||||
search: SearchConfig::example(),
|
search: SearchConfig::example(),
|
||||||
|
files: FilesConfig::example(),
|
||||||
config_path: "".to_string(),
|
config_path: "".to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -403,6 +454,10 @@ impl AppConfig {
|
|||||||
pub fn search(&self) -> &SearchConfig {
|
pub fn search(&self) -> &SearchConfig {
|
||||||
&self.search
|
&self.search
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn files(&self) -> &FilesConfig {
|
||||||
|
&self.files
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for AppConfig {
|
impl Default for AppConfig {
|
||||||
@ -413,6 +468,7 @@ impl Default for AppConfig {
|
|||||||
mail: Default::default(),
|
mail: Default::default(),
|
||||||
database: DatabaseConfig::default(),
|
database: DatabaseConfig::default(),
|
||||||
search: Default::default(),
|
search: Default::default(),
|
||||||
|
files: FilesConfig::default(),
|
||||||
config_path: "".to_string(),
|
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!(
|
println!(
|
||||||
r#"Environment variables:
|
r#"Environment variables:
|
||||||
|
|
||||||
PAYU_CLIENT_ID - PayU client id, you can obtain it by creating account (account requires one-time payment)
|
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_SECRET - PayU client secret
|
||||||
PAYU_CLIENT_MERCHANT_ID - PayU client merchant id, you can obtain it by creating account (account requires one-time payment)
|
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
|
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
|
PASS_SALT - password encryption secret string, you can generate it with this CLI
|
||||||
SESSION_SECRET - 100 characters admin session encryption
|
SESSION_SECRET - 100 characters admin session encryption
|
||||||
JWT_SECRET - 100 characters user session encryption
|
JWT_SECRET - 100 characters user session encryption
|
||||||
BAZZAR_BIND - address to which server should be bind, typically 0.0.0.0
|
BAZZAR_BIND - address to which server should be bind, typically 0.0.0.0
|
||||||
BAZZAR_PORT - port which server should use, typically 80
|
BAZZAR_PORT - port which server should use, typically 80
|
||||||
|
|
||||||
SENDGRID_SECRET - e-mail sending service secret
|
SENDGRID_SECRET - e-mail sending service secret
|
||||||
SENDGRID_API_KEY - e-mail sending service api key
|
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
|
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)
|
DATABASE_URL - postgresql address (ex. postgres://postgres@localhost/bazzar)
|
||||||
|
|
||||||
SONIC_SEARCH_ADDR - search engine query address
|
SONIC_SEARCH_ADDR - search engine query address
|
||||||
SONIC_SEARCH_PASS - search engine query password
|
SONIC_SEARCH_PASS - search engine query password
|
||||||
SONIC_INGEST_ADDR - search engine push address
|
SONIC_INGEST_ADDR - search engine push address
|
||||||
SONIC_INGEST_PASS - search engine push password
|
SONIC_INGEST_PASS - search engine push password
|
||||||
SEARCH_ACTIVE - should use search engine
|
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