This commit is contained in:
eraden 2022-04-14 08:07:59 +02:00
commit 5bef67cc1e
18 changed files with 4373 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

4024
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

2
Cargo.toml Normal file
View File

@ -0,0 +1,2 @@
[workspace]
members = ["web"]

5
README.md Normal file
View File

@ -0,0 +1,5 @@
# Bazzar
### Order life cycle
<img src="./assets/order-lifecycle.svg" alt="order lifecycle">

13
assets/order-flow.dot Normal file
View File

@ -0,0 +1,13 @@
digraph OrderLifecycle {
Confirmed -> Payed;
Confirmed -> Delivered;
Confirmed -> Cancelled;
Payed -> Delivered;
Payed -> RequireRefund;
RequireRefund -> Refunded;
Confirmed [shape=diamond];
Delivered [shape=box];
Cancelled [shape=box];
Refunded [shape = box];
}

107
assets/order-lifecycle.svg Normal file
View File

@ -0,0 +1,107 @@
<svg width="238pt" height="260pt"
viewBox="0.00 0.00 237.63 260.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 256)">
<title>OrderLifecycle</title>
<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-256 233.6288,-256 233.6288,4 -4,4"/>
<!-- Confirmed -->
<g id="node1" class="node">
<title>Confirmed</title>
<polygon fill="none" stroke="#000000"
points="98.5961,-252 24.0445,-234 98.5961,-216 173.1477,-234 98.5961,-252"/>
<text text-anchor="middle" x="98.5961" y="-229.8" font-family="Times,serif" font-size="14.00"
fill="#000000">Confirmed
</text>
</g>
<!-- Payed -->
<g id="node2" class="node">
<title>Payed</title>
<ellipse fill="none" stroke="#000000" cx="98.5961" cy="-162" rx="34.7709" ry="18"/>
<text text-anchor="middle" x="98.5961" y="-157.8" font-family="Times,serif" font-size="14.00"
fill="#000000">Payed
</text>
</g>
<!-- Confirmed&#45;&gt;Payed -->
<g id="edge1" class="edge">
<title>Confirmed&#45;&gt;Payed</title>
<path fill="none" stroke="#000000"
d="M98.5961,-215.8314C98.5961,-208.131 98.5961,-198.9743 98.5961,-190.4166"/>
<polygon fill="#000000" stroke="#000000"
points="102.0962,-190.4132 98.5961,-180.4133 95.0962,-190.4133 102.0962,-190.4132"/>
</g>
<!-- Delivered -->
<g id="node3" class="node">
<title>Delivered</title>
<polygon fill="none" stroke="#000000"
points="71.2886,-108 -.0964,-108 -.0964,-72 71.2886,-72 71.2886,-108"/>
<text text-anchor="middle" x="35.5961" y="-85.8" font-family="Times,serif" font-size="14.00" fill="#000000">
Delivered
</text>
</g>
<!-- Confirmed&#45;&gt;Delivered -->
<g id="edge2" class="edge">
<title>Confirmed&#45;&gt;Delivered</title>
<path fill="none" stroke="#000000"
d="M83.3313,-219.3093C73.6635,-209.1515 61.6888,-194.8386 54.5961,-180 45.2698,-160.4883 40.4937,-136.4348 38.0637,-118.1046"/>
<polygon fill="#000000" stroke="#000000"
points="41.5258,-117.5765 36.8873,-108.051 34.5733,-118.39 41.5258,-117.5765"/>
</g>
<!-- Cancelled -->
<g id="node4" class="node">
<title>Cancelled</title>
<polygon fill="none" stroke="#000000"
points="223.5723,-180 151.6199,-180 151.6199,-144 223.5723,-144 223.5723,-180"/>
<text text-anchor="middle" x="187.5961" y="-157.8" font-family="Times,serif" font-size="14.00"
fill="#000000">Cancelled
</text>
</g>
<!-- Confirmed&#45;&gt;Cancelled -->
<g id="edge3" class="edge">
<title>Confirmed&#45;&gt;Cancelled</title>
<path fill="none" stroke="#000000"
d="M115.7562,-220.1177C127.4948,-210.6213 143.3774,-197.7724 157.2239,-186.5707"/>
<polygon fill="#000000" stroke="#000000"
points="159.5213,-189.2141 165.0945,-180.2035 155.1187,-183.772 159.5213,-189.2141"/>
</g>
<!-- Payed&#45;&gt;Delivered -->
<g id="edge4" class="edge">
<title>Payed&#45;&gt;Delivered</title>
<path fill="none" stroke="#000000"
d="M83.9843,-145.3008C76.2975,-136.5159 66.7059,-125.5541 58.0942,-115.7121"/>
<polygon fill="#000000" stroke="#000000"
points="60.6721,-113.3432 51.453,-108.1222 55.404,-117.9527 60.6721,-113.3432"/>
</g>
<!-- RequireRefund -->
<g id="node5" class="node">
<title>RequireRefund</title>
<ellipse fill="none" stroke="#000000" cx="159.5961" cy="-90" rx="70.0654" ry="18"/>
<text text-anchor="middle" x="159.5961" y="-85.8" font-family="Times,serif" font-size="14.00"
fill="#000000">RequireRefund
</text>
</g>
<!-- Payed&#45;&gt;RequireRefund -->
<g id="edge5" class="edge">
<title>Payed&#45;&gt;RequireRefund</title>
<path fill="none" stroke="#000000"
d="M112.744,-145.3008C120.2979,-136.3848 129.7515,-125.2264 138.1849,-115.2722"/>
<polygon fill="#000000" stroke="#000000"
points="140.8801,-117.5055 144.6738,-107.6132 135.5392,-112.9806 140.8801,-117.5055"/>
</g>
<!-- Refunded -->
<g id="node6" class="node">
<title>Refunded</title>
<polygon fill="none" stroke="#000000"
points="195.0238,-36 124.1684,-36 124.1684,0 195.0238,0 195.0238,-36"/>
<text text-anchor="middle" x="159.5961" y="-13.8" font-family="Times,serif" font-size="14.00"
fill="#000000">Refunded
</text>
</g>
<!-- RequireRefund&#45;&gt;Refunded -->
<g id="edge6" class="edge">
<title>RequireRefund&#45;&gt;Refunded</title>
<path fill="none" stroke="#000000"
d="M159.5961,-71.8314C159.5961,-64.131 159.5961,-54.9743 159.5961,-46.4166"/>
<polygon fill="#000000" stroke="#000000"
points="163.0962,-46.4132 159.5961,-36.4133 156.0962,-46.4133 163.0962,-46.4132"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@ -0,0 +1,69 @@
CREATE EXTENSION "uuid-ossp";
CREATE TYPE "Role" AS ENUM (
'Admin',
'User'
);
CREATE TYPE "OrderStatus" AS ENUM (
'Confirmed',
'Cancelled',
'Delivered',
'Payed',
'RequireRefund',
'Refunded'
);
CREATE TABLE accounts
(
id serial not null primary key,
email varchar not null unique,
login varchar not null unique,
pass_hash varchar not null,
role "Role" not null default 'User'
);
CREATE TABLE products
(
id serial not null primary key,
name varchar not null unique,
short_description varchar not null,
long_description varchar not null,
category varchar,
price_major int not null,
price_minor int not null,
CONSTRAINT positive_price_minor check ( price_major >= 0 )
);
CREATE TABLE stocks
(
id serial not null primary key,
product_id int references products (id) not null unique,
quantity int not null default 0,
CONSTRAINT positive_quantity check ( quantity >= 0 )
);
CREATE TABLE account_orders
(
id serial not null primary key,
buyer_id int references accounts (id) not null,
status "OrderStatus" not null default 'Confirmed'
);
CREATE TABLE order_items
(
id serial not null primary key,
product_id int references products (id) not null,
order_id int references account_orders (id),
quantity int not null default 0,
CONSTRAINT positive_quantity check ( quantity >= 0 )
);
CREATE TABLE statistics
(
id serial not null primary key,
url varchar not null,
clicks int not null default 0,
date DATE not null default now(),
CONSTRAINT positive_clicks check ( clicks >= 0 )
);

7
web/Cargo.lock generated Normal file
View File

@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "web"
version = "0.1.0"

39
web/Cargo.toml Normal file
View File

@ -0,0 +1,39 @@
[package]
name = "bazzar"
version = "0.1.0"
edition = "2021"
[dependencies]
actix = { version = "0.13.0" }
actix-rt = { version = "2.7.0" }
actix-web = { version = "4.0.1" }
actix-auth = { version = "0.1.0" }
actix-cors = { version = "0.6.1" }
actix-files = { version = "0.6.0" }
actix-multipart = { version = "0.4.0" }
actix-broker = { version = "0.4.2" }
actix-identity = { version = "0.4.0" }
actix-web-opentelemetry = { version = "0.12.0" }
tera = { version = "1.15.0" }
tracing = { version = "0.1.33" }
uuid = { version = "0.8.2", features = ["serde"] }
chrono = { version = "*", features = ["serde"] }
serde = { version = "1.0.136", features = ["derive"] }
serde_json = { version = "1.0.79" }
toml = { version = "0.5.8" }
sqlx = { version = "0.5.11", features = ["migrate", "runtime-actix-rustls", "all-types", "postgres"] }
log = { version = "0.4.16" }
pretty_env_logger = { version = "0.4.0" }
dotenv = { version = "0.15.0" }
derive_more = { version = "0.99.17" }
parking_lot = { version = "0.12.0" }
password-hash = { version = "0.4.0" }

View File

1
web/src/actors/mod.rs Normal file
View File

@ -0,0 +1 @@
pub mod database;

1
web/src/logic/mod.rs Normal file
View File

@ -0,0 +1 @@
mod order_state;

View File

@ -0,0 +1,21 @@
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,
}
}

14
web/src/main.rs Normal file
View File

@ -0,0 +1,14 @@
mod actors;
mod logic;
mod model;
mod routes;
use actix_web::{App, HttpServer};
#[actix_web::main] // or #[tokio::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| App::new().configure(routes::configure))
.bind(("127.0.0.1", 8080))?
.run()
.await
}

25
web/src/model.rs Normal file
View File

@ -0,0 +1,25 @@
use derive_more::Display;
#[derive(sqlx::Type, Copy, Clone, Debug, Display)]
pub enum OrderStatus {
#[display(fmt = "Potwierdzone")]
Confirmed,
#[display(fmt = "Odebrane")]
Delivered,
#[display(fmt = "Opłacone")]
Payed,
#[display(fmt = "Anulowane")]
Cancelled,
#[display(fmt = "Wymaga zwrotu płatności")]
RequireRefund,
#[display(fmt = "Płatność zwrócona")]
Refunded,
}
#[derive(sqlx::Type, Copy, Clone, Debug, Display)]
pub enum Role {
#[display(fmt = "Adminitrator")]
Admin,
#[display(fmt = "Użytkownik")]
User,
}

23
web/src/routes/admin.rs Normal file
View File

@ -0,0 +1,23 @@
// use actix_auth::login_required;
use actix_identity::Identity;
use actix_web::web::ServiceConfig;
use actix_web::{delete, get, post, HttpResponse};
#[delete("/admin/logout")]
async fn logout() -> HttpResponse {
HttpResponse::NotImplemented().body("")
}
#[post("/admin/sign-in")]
async fn sign_in(_id: Identity) -> HttpResponse {
HttpResponse::NotImplemented().body("")
}
#[get("/admin")]
async fn landing() -> HttpResponse {
HttpResponse::NotImplemented().body("")
}
pub fn configure(config: &mut ServiceConfig) {
config.service(landing).service(sign_in).service(logout);
}

10
web/src/routes/mod.rs Normal file
View File

@ -0,0 +1,10 @@
mod admin;
mod public;
use actix_web::web::ServiceConfig;
pub fn configure(config: &mut ServiceConfig) {
config
.configure(public::configure)
.configure(admin::configure);
}

11
web/src/routes/public.rs Normal file
View File

@ -0,0 +1,11 @@
use actix_web::web::ServiceConfig;
use actix_web::{get, HttpResponse};
#[get("/")]
async fn landing() -> HttpResponse {
HttpResponse::NotImplemented().body("")
}
pub fn configure(config: &mut ServiceConfig) {
config.service(landing);
}