Organize code

This commit is contained in:
eraden 2022-04-20 14:30:59 +02:00
parent b5dfb64f89
commit e7446e7df2
14 changed files with 388 additions and 1232 deletions

1353
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -4,60 +4,61 @@ version = "0.1.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
actix = { version = "0.13.0" } actix = { version = "0.13.0", features = [] }
actix-rt = { version = "2.7.0" } actix-rt = { version = "2.7.0", features = [] }
actix-web = { version = "4.0.1" } actix-web = { version = "4.0.1", features = [] }
actix-web-httpauth = { version = "0.6.0" } actix-web-httpauth = { version = "0.6.0", features = [] }
actix-auth = { version = "0.1.0" } actix-cors = { version = "0.6.1", features = [] }
actix-cors = { version = "0.6.1" } actix-files = { version = "0.6.0", features = [] }
actix-files = { version = "0.6.0" } actix-multipart = { version = "0.4.0", features = [] }
actix-multipart = { version = "0.4.0" } actix-broker = { version = "0.4.2", features = [] }
actix-broker = { version = "0.4.2" } actix-identity = { version = "0.4.0", features = [] }
actix-identity = { version = "0.4.0" } actix-web-opentelemetry = { version = "0.12.0", features = [] }
actix-web-opentelemetry = { version = "0.12.0" }
actix-session = { version = "0.6.2", features = ["actix-redis", "redis-actor-session"] } actix-session = { version = "0.6.2", features = ["actix-redis", "redis-actor-session"] }
actix-redis = { version = "0.11.0" } actix-redis = { version = "0.11.0", features = [] }
gumdrop = { version = "0.8.1" } gumdrop = { version = "0.8.1", features = [] }
tera = { version = "1.15.0" } tera = { version = "1.15.0", features = [] }
tracing = { version = "0.1.33" } tracing = { version = "0.1.33", features = [] }
uuid = { version = "0.8.2", features = ["serde"] } uuid = { version = "0.8.2", features = ["serde"] }
chrono = { version = "*", features = ["serde"] } chrono = { version = "*", features = ["serde"] }
serde = { version = "1.0.136", features = ["derive"] } serde = { version = "1.0.136", features = ["derive"] }
serde_json = { version = "1.0.79" } serde_json = { version = "1.0.79", features = [] }
toml = { version = "0.5.8" } toml = { version = "0.5.8", features = [] }
sqlx = { version = "0.5.13", features = ["migrate", "runtime-actix-rustls", "all-types", "postgres"] } sqlx = { version = "0.5.13", features = ["migrate", "runtime-actix-rustls", "all-types", "postgres"] }
sqlx-core = { version = "0.5.13" } sqlx-core = { version = "0.5.13", features = [] }
thiserror = { version = "1.0.30" } thiserror = { version = "1.0.30", features = [] }
validator = { version = "0.14.0" } validator = { version = "0.14.0", features = [] }
log = { version = "0.4.16" } log = { version = "0.4.16", features = [] }
pretty_env_logger = { version = "0.4.0" } pretty_env_logger = { version = "0.4.0", features = [] }
dotenv = { version = "0.15.0" } dotenv = { version = "0.15.0", features = [] }
derive_more = { version = "0.99.17" } derive_more = { version = "0.99.17", features = [] }
parking_lot = { version = "0.12.0" } parking_lot = { version = "0.12.0", features = [] }
password-hash = { version = "0.4.0", features = ["alloc"] } password-hash = { version = "0.4.0", features = ["alloc"] }
argon2 = { version = "0.4.0", features = ["parallel", "password-hash"] } argon2 = { version = "0.4.0", features = ["parallel", "password-hash"] }
rand_core = { version = "0.6", features = ["std"] } rand_core = { version = "0.6", features = ["std"] }
tokio = { version = "1.17.0", features = ["full"] } tokio = { version = "1.17.0", features = ["full"] }
futures = { version = "0.3.21" } futures = { version = "0.3.21", features = [] }
futures-util = { version = "0.3.21" } futures-util = { version = "0.3.21", features = [] }
jwt = { version = "0.16.0", features = [] } jwt = { version = "0.16.0", features = [] }
hmac = { version = "0.12.1" } hmac = { version = "0.12.1", features = [] }
sha2 = { version = "0.10.2" } sha2 = { version = "0.10.2", features = [] }
oauth2 = { version = "4.1.0" } oauth2 = { version = "4.1.0", features = [] }
async-trait = { version = "0.1.53" } async-trait = { version = "0.1.53", features = [] }
jemallocator = { version = "0.3.2", features = [] }

View File

@ -1,11 +1,25 @@
use actix::{Actor, Addr, Context, Message}; use actix::{Actor, Addr, Context, Message};
use crate::database::Database; use crate::database::{self, Database};
use crate::model::{ use crate::model::{
AccountId, ProductId, Quantity, QuantityUnit, ShoppingCartId, ShoppingCartItem, AccountId, ProductId, Quantity, QuantityUnit, ShoppingCartId, ShoppingCartItem,
ShoppingCartItemId, ShoppingCartState, ShoppingCartItemId, ShoppingCartState,
}; };
use crate::{cart_async_handler, database};
#[macro_export]
macro_rules! cart_async_handler {
($msg: ty, $async: ident, $res: ty) => {
impl actix::Handler<$msg> for CartManager {
type Result = actix::ResponseActFuture<Self, Result<$res>>;
fn handle(&mut self, msg: $msg, _ctx: &mut Self::Context) -> Self::Result {
use actix::WrapFuture;
let db = self.db.clone();
Box::pin(async { $async(msg, db).await }.into_actor(self))
}
}
};
}
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
pub enum Error { pub enum Error {

View File

@ -18,6 +18,21 @@ pub mod shopping_carts;
pub mod stocks; pub mod stocks;
pub mod tokens; pub mod tokens;
#[macro_export]
macro_rules! db_async_handler {
($msg: ty, $async: ident, $res: ty) => {
impl actix::Handler<$msg> for Database {
type Result = actix::ResponseActFuture<Self, Result<$res>>;
fn handle(&mut self, msg: $msg, _ctx: &mut Self::Context) -> Self::Result {
use actix::WrapFuture;
let pool = self.pool.clone();
Box::pin(async { $async(msg, pool).await }.into_actor(self))
}
}
};
}
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
pub enum Error { pub enum Error {
#[error("Failed to connect to database. {0:?}")] #[error("Failed to connect to database. {0:?}")]
@ -46,6 +61,8 @@ pub struct Database {
pool: PgPool, pool: PgPool,
} }
pub type SharedDatabase = actix::Addr<Database>;
impl Clone for Database { impl Clone for Database {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {

View File

@ -1,3 +1,4 @@
pub mod cart_manager; pub mod cart_manager;
pub mod database; pub mod database;
pub mod order_manager;
pub mod token_manager; pub mod token_manager;

View File

@ -0,0 +1,70 @@
use actix::Addr;
use actix_web::Message;
use sqlx_core::postgres::PgPool;
use crate::database::{Database, SharedDatabase, self};
use crate::model::{AccountOrder, OrderStatus, ShoppingCartId};
#[macro_export]
macro_rules! order_async_handler {
($msg: ty, $async: ident, $res: ty) => {
impl actix::Handler<$msg> for OrderManager {
type Result = actix::ResponseActFuture<Self, Result<$res>>;
fn handle(&mut self, msg: $msg, _ctx: &mut Self::Context) -> Self::Result {
use actix::WrapFuture;
let db = self.db.clone();
Box::pin(async { $async(msg, db).await }.into_actor(self))
}
}
};
}
#[derive(Debug, thiserror::Error)]
pub enum Error {}
pub type Result<T> = std::result::Result<T, Error>;
pub struct OrderManager {
db: SharedDatabase,
}
impl actix::Actor for OrderManager {
type Context = actix::Context<Self>;
}
impl OrderManager {
pub fn new(db: SharedDatabase) -> Self {
Self { db }
}
}
#[derive(Message, Debug)]
#[rtype(result = "Result<AccountOrder>")]
pub struct CreateOrder {
pub shopping_cart_id: ShoppingCartId,
}
pub(crate) async fn create_order(msg: CreateOrder, db: SharedDatabase) -> Result<AccountOrder> {
let cart = match db.send(database)
}
pub fn change(current: OrderStatus, next: OrderStatus) -> Option<OrderStatus> {
match (current, next) {
// paying
(OrderStatus::Confirmed, OrderStatus::Payed) => Some(OrderStatus::Payed),
// delivering
(OrderStatus::Confirmed | OrderStatus::Payed, OrderStatus::Delivered) => {
Some(OrderStatus::Delivered)
}
// cancelling
(OrderStatus::Confirmed, OrderStatus::Cancelled) => Some(OrderStatus::Cancelled),
(OrderStatus::Payed, OrderStatus::Cancelled) => Some(OrderStatus::RequireRefund),
(OrderStatus::Payed, OrderStatus::RequireRefund) => Some(OrderStatus::RequireRefund),
(OrderStatus::RequireRefund, OrderStatus::Refunded) => Some(OrderStatus::Refunded),
_ => None,
}
}

View File

@ -10,7 +10,23 @@ use sha2::Sha256;
use crate::database::{Database, TokenByJti}; use crate::database::{Database, TokenByJti};
use crate::model::{AccountId, Audience, Token, TokenString}; use crate::model::{AccountId, Audience, Token, TokenString};
use crate::{database, token_async_handler, Role}; use crate::{database, Role};
#[macro_export]
macro_rules! token_async_handler {
($msg: ty, $async: ident, $res: ty) => {
impl actix::Handler<$msg> for TokenManager {
type Result = actix::ResponseActFuture<Self, Result<$res>>;
fn handle(&mut self, msg: $msg, _ctx: &mut Self::Context) -> Self::Result {
use actix::WrapFuture;
let db = self.db.clone();
let secret = self.secret.clone();
Box::pin(async { $async(msg, db, secret).await }.into_actor(self))
}
}
};
}
/*struct Jwt { /*struct Jwt {
/// cti (customer id): Customer uuid identifier used by payment service /// cti (customer id): Customer uuid identifier used by payment service

View File

@ -1,21 +1 @@
use crate::model::OrderStatus; use crate::model::OrderStatus;
pub fn change(current: OrderStatus, next: OrderStatus) -> Option<OrderStatus> {
match (current, next) {
// paying
(OrderStatus::Confirmed, OrderStatus::Payed) => Some(OrderStatus::Payed),
// delivering
(OrderStatus::Confirmed | OrderStatus::Payed, OrderStatus::Delivered) => {
Some(OrderStatus::Delivered)
}
// cancelling
(OrderStatus::Confirmed, OrderStatus::Cancelled) => Some(OrderStatus::Cancelled),
(OrderStatus::Payed, OrderStatus::Cancelled) => Some(OrderStatus::RequireRefund),
(OrderStatus::Payed, OrderStatus::RequireRefund) => Some(OrderStatus::RequireRefund),
(OrderStatus::RequireRefund, OrderStatus::Refunded) => Some(OrderStatus::Refunded),
_ => None,
}
}

View File

@ -9,6 +9,7 @@ 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 gumdrop::Options; use gumdrop::Options;
use jemallocator::Jemalloc;
use password_hash::SaltString; use password_hash::SaltString;
use validator::{validate_email, validate_length}; use validator::{validate_email, validate_length};
@ -20,7 +21,9 @@ pub mod actors;
pub mod logic; pub mod logic;
pub mod model; pub mod model;
pub mod routes; pub mod routes;
mod utils;
#[global_allocator]
static GLOBAL: Jemalloc = Jemalloc;
trait ResolveDbUrl { trait ResolveDbUrl {
fn own_db_url(&self) -> Option<String>; fn own_db_url(&self) -> Option<String>;

View File

@ -178,7 +178,7 @@ async fn register(
async fn landing() -> Result<HttpResponse> { async fn landing() -> Result<HttpResponse> {
Ok(HttpResponse::NotImplemented() Ok(HttpResponse::NotImplemented()
.append_header(("Content-Type", "text/html")) .append_header(("Content-Type", "text/html"))
.body(include_str!("../../../assets/index.html"))) .body(include_str!("../../assets/index.html")))
} }
pub fn configure(config: &mut ServiceConfig) { pub fn configure(config: &mut ServiceConfig) {

View File

@ -20,7 +20,7 @@ pub enum Error {
AddItem, AddItem,
} }
pub fn configure(config: &mut ServiceConfig) { pub(crate) fn configure(config: &mut ServiceConfig) {
config.service( config.service(
scope("/api/v1") scope("/api/v1")
.configure(unrestricted::configure) .configure(unrestricted::configure)

View File

@ -186,7 +186,9 @@ async fn delete_cart_item(
} }
} }
pub fn configure(config: &mut ServiceConfig) { pub(crate) async fn create_order() {}
pub(crate) fn configure(config: &mut ServiceConfig) {
config.service(scope("") config.service(scope("")
.app_data(actix_web_httpauth::extractors::bearer::Config::default() .app_data(actix_web_httpauth::extractors::bearer::Config::default()
.realm("user api") .realm("user api")

View File

@ -84,6 +84,6 @@ async fn sign_in(
Ok(HttpResponse::Created().json(SignInOutput { token: string })) Ok(HttpResponse::Created().json(SignInOutput { token: string }))
} }
pub fn configure(config: &mut ServiceConfig) { pub(crate) fn configure(config: &mut ServiceConfig) {
config.service(products).service(stocks).service(sign_in); config.service(products).service(stocks).service(sign_in);
} }

View File

@ -1,45 +0,0 @@
#[macro_export]
macro_rules! db_async_handler {
($msg: ty, $async: ident, $res: ty) => {
impl actix::Handler<$msg> for Database {
type Result = actix::ResponseActFuture<Self, Result<$res>>;
fn handle(&mut self, msg: $msg, _ctx: &mut Self::Context) -> Self::Result {
use actix::WrapFuture;
let pool = self.pool.clone();
Box::pin(async { $async(msg, pool).await }.into_actor(self))
}
}
};
}
#[macro_export]
macro_rules! cart_async_handler {
($msg: ty, $async: ident, $res: ty) => {
impl actix::Handler<$msg> for CartManager {
type Result = actix::ResponseActFuture<Self, Result<$res>>;
fn handle(&mut self, msg: $msg, _ctx: &mut Self::Context) -> Self::Result {
use actix::WrapFuture;
let db = self.db.clone();
Box::pin(async { $async(msg, db).await }.into_actor(self))
}
}
};
}
#[macro_export]
macro_rules! token_async_handler {
($msg: ty, $async: ident, $res: ty) => {
impl actix::Handler<$msg> for TokenManager {
type Result = actix::ResponseActFuture<Self, Result<$res>>;
fn handle(&mut self, msg: $msg, _ctx: &mut Self::Context) -> Self::Result {
use actix::WrapFuture;
let db = self.db.clone();
let secret = self.secret.clone();
Box::pin(async { $async(msg, db, secret).await }.into_actor(self))
}
}
};
}