Tokens manager
This commit is contained in:
parent
dc8fe6dc25
commit
ba00afab41
1
.env
1
.env
@ -4,6 +4,7 @@ ACCOUNT_DATABASE_URL=postgres://postgres@localhost/bazzar_accounts
|
|||||||
CART_DATABASE_URL=postgres://postgres@localhost/bazzar_carts
|
CART_DATABASE_URL=postgres://postgres@localhost/bazzar_carts
|
||||||
STOCK_DATABASE_URL=postgres://postgres@localhost/bazzar_stocks
|
STOCK_DATABASE_URL=postgres://postgres@localhost/bazzar_stocks
|
||||||
ORDER_DATABASE_URL=postgres://postgres@localhost/bazzar_orders
|
ORDER_DATABASE_URL=postgres://postgres@localhost/bazzar_orders
|
||||||
|
TOKEN_DATABASE_URL=postgres://postgres@localhost/bazzar_tokens
|
||||||
|
|
||||||
PASS_SALT=18CHwV7eGFAea16z+qMKZg
|
PASS_SALT=18CHwV7eGFAea16z+qMKZg
|
||||||
RUST_LOG=debug
|
RUST_LOG=debug
|
||||||
|
689
Cargo.lock
generated
689
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -16,7 +16,7 @@ members = [
|
|||||||
"crates/search_manager",
|
"crates/search_manager",
|
||||||
"crates/stock_manager",
|
"crates/stock_manager",
|
||||||
"crates/token_manager",
|
"crates/token_manager",
|
||||||
"crates/fs_manager",
|
# "crates/fs_manager",
|
||||||
"crates/lang_provider",
|
"crates/lang_provider",
|
||||||
"crates/payment_adapter",
|
"crates/payment_adapter",
|
||||||
# "crates/payment_adapter_pay_u",
|
# "crates/payment_adapter_pay_u",
|
||||||
|
@ -8,28 +8,24 @@ name = "account-manager"
|
|||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bincode = { version = "1.3.3" }
|
bincode = { version = "1" }
|
||||||
bytes = { version = "1.2.1" }
|
bytes = { version = "1" }
|
||||||
channels = { path = "../channels" }
|
channels = { path = "../channels" }
|
||||||
config = { path = "../config" }
|
config = { path = "../config" }
|
||||||
dotenv = { version = "0.15.0" }
|
dotenv = { version = "0" }
|
||||||
futures = { version = "0.3.25" }
|
futures = { version = "0" }
|
||||||
gumdrop = { version = "0.8.1" }
|
gumdrop = { version = "0" }
|
||||||
json = { version = "0.12.4" }
|
json = { version = "0" }
|
||||||
model = { path = "../model", features = ['db'] }
|
model = { path = "../model", features = ['db'] }
|
||||||
opentelemetry = { version = "0.17.0" }
|
|
||||||
opentelemetry-jaeger = { version = "0.17.0" }
|
|
||||||
rumqttc = { version = "*" }
|
rumqttc = { version = "*" }
|
||||||
serde = { version = "1.0.137", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
sqlx = { version = "0.6.2", features = ["migrate", "runtime-actix-rustls", "all-types", "postgres"] }
|
sqlx = { version = "0", features = ["migrate", "runtime-actix-rustls", "all-types", "postgres"] }
|
||||||
sqlx-core = { version = "0.6.2", features = [] }
|
sqlx-core = { version = "0", features = [] }
|
||||||
tarpc = { version = "0.31.0", features = ["tokio1", "serde-transport-bincode", "serde-transport", "serde", "serde-transport-json", "tcp"] }
|
tarpc = { version = "0", features = ["tokio1", "serde-transport-bincode", "serde-transport", "serde", "serde-transport-json", "tcp"] }
|
||||||
thiserror = { version = "1.0.31" }
|
thiserror = { version = "1" }
|
||||||
tokio = { version = "1.21.2", features = ['full'] }
|
tokio = { version = "1", features = ['full'] }
|
||||||
tracing = { version = "0.1.6" }
|
tracing = { version = "0" }
|
||||||
tracing-opentelemetry = { version = "0.17.4" }
|
|
||||||
tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
fake = { version = "2.5.0" }
|
fake = { version = "2" }
|
||||||
testx = { path = "../testx" }
|
testx = { path = "../testx" }
|
||||||
|
@ -34,7 +34,6 @@ async fn main() -> std::io::Result<()> {
|
|||||||
|
|
||||||
// Let the background span processor finish.
|
// Let the background span processor finish.
|
||||||
sleep(Duration::from_micros(1)).await;
|
sleep(Duration::from_micros(1)).await;
|
||||||
opentelemetry::global::shutdown_tracer_provider();
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,6 @@
|
|||||||
#![feature(structural_match)]
|
#![feature(structural_match)]
|
||||||
|
|
||||||
use std::env;
|
|
||||||
|
|
||||||
use config::UpdateConfig;
|
use config::UpdateConfig;
|
||||||
use tracing_subscriber::fmt::format::FmtSpan;
|
|
||||||
use tracing_subscriber::layer::SubscriberExt;
|
|
||||||
use tracing_subscriber::util::SubscriberInitExt;
|
|
||||||
|
|
||||||
pub mod actions;
|
pub mod actions;
|
||||||
pub mod db;
|
pub mod db;
|
||||||
@ -35,7 +30,7 @@ impl UpdateConfig for Opts {}
|
|||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
dotenv::dotenv().ok();
|
dotenv::dotenv().ok();
|
||||||
init_tracing("account-manager");
|
config::init_tracing("account-manager");
|
||||||
|
|
||||||
let opts = Opts {};
|
let opts = Opts {};
|
||||||
|
|
||||||
@ -46,24 +41,3 @@ async fn main() {
|
|||||||
let mqtt_client = mqtt::start(config.clone(), db.clone()).await;
|
let mqtt_client = mqtt::start(config.clone(), db.clone()).await;
|
||||||
rpc::start(config.clone(), db.clone(), mqtt_client.clone()).await;
|
rpc::start(config.clone(), db.clone(), mqtt_client.clone()).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_tracing(_service_name: &str) {
|
|
||||||
env::set_var("OTEL_BSP_MAX_EXPORT_BATCH_SIZE", "12");
|
|
||||||
|
|
||||||
let tracer = {
|
|
||||||
use opentelemetry::sdk::export::trace::stdout::new_pipeline;
|
|
||||||
use opentelemetry::sdk::trace::Config;
|
|
||||||
|
|
||||||
new_pipeline()
|
|
||||||
.with_trace_config(Config::default())
|
|
||||||
.with_pretty_print(true)
|
|
||||||
.install_simple()
|
|
||||||
};
|
|
||||||
|
|
||||||
tracing_subscriber::registry()
|
|
||||||
.with(tracing_subscriber::EnvFilter::from_default_env())
|
|
||||||
.with(tracing_subscriber::fmt::layer().with_span_events(FmtSpan::NEW | FmtSpan::CLOSE))
|
|
||||||
.with(tracing_opentelemetry::layer().with_tracer(tracer))
|
|
||||||
.try_init()
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
@ -4,52 +4,50 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix = { version = "0.13", features = [] }
|
actix = { version = "0", features = [] }
|
||||||
actix-broker = { version = "0.4", features = [] }
|
actix-broker = { version = "0", features = [] }
|
||||||
actix-cors = { version = "0.6", features = [] }
|
actix-cors = { version = "0", features = [] }
|
||||||
actix-files = { version = "0.6", features = [] }
|
actix-files = { version = "0", features = [] }
|
||||||
actix-identity = { version = "0.4", features = [] }
|
actix-identity = { version = "0", features = [] }
|
||||||
actix-multipart = { version = "0.4", features = [] }
|
actix-multipart = { version = "0", features = [] }
|
||||||
actix-redis = { version = "0.11", features = [] }
|
actix-redis = { version = "0", features = [] }
|
||||||
actix-rt = { version = "2.7", features = [] }
|
actix-rt = { version = "2", features = [] }
|
||||||
actix-session = { version = "0.6", features = ["actix-redis", "redis-actor-session"] }
|
actix-session = { version = "0", features = ["actix-redis", "redis-actor-session"] }
|
||||||
actix-web = { version = "4.0", features = [] }
|
actix-web = { version = "4", features = [] }
|
||||||
actix-web-httpauth = { version = "0.6", features = [] }
|
actix-web-httpauth = { version = "0", features = [] }
|
||||||
actix-web-opentelemetry = { version = "0.12", features = [] }
|
actix-web-opentelemetry = { version = "0", features = [] }
|
||||||
async-trait = { version = "0.1", features = [] }
|
async-trait = { version = "0", features = [] }
|
||||||
bytes = { version = "1.1.0" }
|
bytes = { version = "1" }
|
||||||
channels = { path = "../channels", features = ['accounts', 'carts', 'emails', 'search'] }
|
channels = { path = "../channels", features = ['accounts', 'carts', 'emails', 'search'] }
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0", features = ["serde"] }
|
||||||
config = { path = "../config", features = ['full'] }
|
config = { path = "../config", features = ['full'] }
|
||||||
#database_manager = { path = "../database_manager" }
|
#database_manager = { path = "../database_manager" }
|
||||||
derive_more = { version = "0.99", features = [] }
|
derive_more = { version = "0", features = [] }
|
||||||
dotenv = { version = "0.15", features = [] }
|
dotenv = { version = "0", features = [] }
|
||||||
fs_manager = { path = "../fs_manager" }
|
fs_manager = { path = "../fs_manager" }
|
||||||
futures = { version = "0.3", features = [] }
|
futures = { version = "0", features = [] }
|
||||||
futures-util = { version = "0.3", features = [] }
|
futures-util = { version = "0", features = [] }
|
||||||
gumdrop = { version = "0.8", features = [] }
|
gumdrop = { version = "0", features = [] }
|
||||||
human-panic = { version = "1.0.3" }
|
human-panic = { version = "1" }
|
||||||
include_dir = { version = "0.7.2", features = [] }
|
include_dir = { version = "0", features = [] }
|
||||||
itertools = { version = "0.10.5" }
|
itertools = { version = "0" }
|
||||||
jemallocator = { version = "0.3", features = [] }
|
jemallocator = { version = "0", features = [] }
|
||||||
model = { path = "../model", version = "0.1", features = ["db"] }
|
model = { path = "../model", version = "0.1", features = ["db"] }
|
||||||
oauth2 = { version = "4.1", features = [] }
|
oauth2 = { version = "4", features = [] }
|
||||||
order_manager = { path = "../order_manager" }
|
order_manager = { path = "../order_manager" }
|
||||||
parking_lot = { version = "0.12", features = [] }
|
parking_lot = { version = "0", features = [] }
|
||||||
payment_manager = { path = "../payment_manager" }
|
payment_manager = { path = "../payment_manager" }
|
||||||
pretty_env_logger = { version = "0.4", features = [] }
|
pretty_env_logger = { version = "0", features = [] }
|
||||||
rumqttc = { version = "*" }
|
rumqttc = { version = "*" }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = { version = "1.0", features = [] }
|
serde_json = { version = "1", features = [] }
|
||||||
sqlx = { version = "0.6.2", features = ["migrate", "runtime-actix-rustls", "all-types", "postgres"] }
|
sqlx = { version = "0", features = ["migrate", "runtime-actix-rustls", "all-types", "postgres"] }
|
||||||
sqlx-core = { version = "0.6.2", features = [] }
|
sqlx-core = { version = "0", features = [] }
|
||||||
tarpc = { version = "0.31.0", features = ["tokio1", "serde-transport-bincode", "serde-transport", "serde", "serde-transport-json", "tcp"] }
|
tarpc = { version = "0", features = ["tokio1", "serde-transport-bincode", "serde-transport", "serde", "serde-transport-json", "tcp"] }
|
||||||
tera = { version = "1.15", features = [] }
|
tera = { version = "1", features = [] }
|
||||||
thiserror = { version = "1.0", features = [] }
|
thiserror = { version = "1", features = [] }
|
||||||
token_manager = { path = "../token_manager" }
|
token_manager = { path = "../token_manager" }
|
||||||
tokio = { version = "1.17", features = ["full"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
toml = { version = "0.5", features = [] }
|
toml = { version = "0", features = [] }
|
||||||
tracing = { version = "0.1.34" }
|
uuid = { version = "1", features = ["serde"] }
|
||||||
tracing-subscriber = { version = "0.3.11" }
|
validator = { version = "0", features = [] }
|
||||||
uuid = { version = "1.2.1", features = ["serde"] }
|
|
||||||
validator = { version = "0.14", features = [] }
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#![feature(drain_filter)]
|
+#![feature(drain_filter)]
|
||||||
|
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
@ -9,26 +9,21 @@ path = "src/main.rs"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
channels = { path = "../channels" }
|
channels = { path = "../channels" }
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0", features = ["serde"] }
|
||||||
config = { path = "../config" }
|
config = { path = "../config" }
|
||||||
dotenv = { version = "0.15.0" }
|
dotenv = { version = "0" }
|
||||||
futures = { version = "0.3.25" }
|
futures = { version = "0" }
|
||||||
model = { path = "../model", features = ["db"] }
|
model = { path = "../model", features = ["db"] }
|
||||||
opentelemetry = { version = "0.17.0" }
|
|
||||||
opentelemetry-jaeger = { version = "0.17.0" }
|
|
||||||
pretty_env_logger = { version = "0.4", features = [] }
|
|
||||||
rumqttc = { version = "*" }
|
rumqttc = { version = "*" }
|
||||||
serde = { version = "1.0.137", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
sqlx = { version = "0.6.2", features = ["migrate", "runtime-actix-rustls", "all-types", "postgres"] }
|
sqlx = { version = "0", features = ["migrate", "runtime-actix-rustls", "all-types", "postgres"] }
|
||||||
sqlx-core = { version = "0.6.2", features = [] }
|
sqlx-core = { version = "0", features = [] }
|
||||||
tarpc = { version = "0.31.0", features = ["tokio1", "serde-transport-bincode", "serde-transport", "serde", "serde-transport-json", "tcp"] }
|
tarpc = { version = "0", features = ["tokio1", "serde-transport-bincode", "serde-transport", "serde", "serde-transport-json", "tcp"] }
|
||||||
thiserror = { version = "1.0.31" }
|
thiserror = { version = "1" }
|
||||||
tokio = { version = "1.21.2", features = ['full'] }
|
tokio = { version = "1", features = ['full'] }
|
||||||
tracing = { version = "0.1.37" }
|
tracing = { version = "0" }
|
||||||
tracing-opentelemetry = { version = "0.17.4" }
|
uuid = { version = "0", features = ["serde", "v4"] }
|
||||||
tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
|
|
||||||
uuid = { version = "0.8", features = ["serde", "v4"] }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
fake = { version = "2.5.0" }
|
fake = { version = "2" }
|
||||||
testx = { path = "../testx" }
|
testx = { path = "../testx" }
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
use config::UpdateConfig;
|
use config::UpdateConfig;
|
||||||
use tracing_subscriber::fmt::format::FmtSpan;
|
|
||||||
use tracing_subscriber::layer::SubscriberExt;
|
|
||||||
use tracing_subscriber::util::SubscriberInitExt;
|
|
||||||
|
|
||||||
use crate::db::Database;
|
use crate::db::Database;
|
||||||
|
|
||||||
@ -17,7 +14,7 @@ impl UpdateConfig for Opts {}
|
|||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
dotenv::dotenv().ok();
|
dotenv::dotenv().ok();
|
||||||
init_tracing("account-manager");
|
config::init_tracing("account-manager");
|
||||||
|
|
||||||
let opts = Opts {};
|
let opts = Opts {};
|
||||||
|
|
||||||
@ -28,23 +25,3 @@ async fn main() {
|
|||||||
let mqtt_client = mqtt::start(config.clone(), db.clone()).await;
|
let mqtt_client = mqtt::start(config.clone(), db.clone()).await;
|
||||||
rpc::start(config.clone(), db.clone(), mqtt_client.clone()).await;
|
rpc::start(config.clone(), db.clone(), mqtt_client.clone()).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_tracing(_service_name: &str) {
|
|
||||||
std::env::set_var("OTEL_BSP_MAX_EXPORT_BATCH_SIZE", "12");
|
|
||||||
|
|
||||||
let tracer = {
|
|
||||||
use opentelemetry::sdk::export::trace::stdout::new_pipeline;
|
|
||||||
use opentelemetry::sdk::trace::Config;
|
|
||||||
new_pipeline()
|
|
||||||
.with_trace_config(Config::default())
|
|
||||||
.with_pretty_print(true)
|
|
||||||
.install_simple()
|
|
||||||
};
|
|
||||||
|
|
||||||
tracing_subscriber::registry()
|
|
||||||
.with(tracing_subscriber::EnvFilter::from_default_env())
|
|
||||||
.with(tracing_subscriber::fmt::layer().with_span_events(FmtSpan::NEW | FmtSpan::CLOSE))
|
|
||||||
.with(tracing_opentelemetry::layer().with_tracer(tracer))
|
|
||||||
.try_init()
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
@ -11,20 +11,21 @@ search = []
|
|||||||
stocks = []
|
stocks = []
|
||||||
orders = []
|
orders = []
|
||||||
payments = ['payment_adapter']
|
payments = ['payment_adapter']
|
||||||
default = ['accounts', 'carts', 'emails', 'search', 'stocks', 'orders', 'payments']
|
tokens = []
|
||||||
|
default = ['accounts', 'carts', 'emails', 'search', 'stocks', 'orders', 'payments', 'tokens']
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bincode = { version = "*" }
|
bincode = { version = "*" }
|
||||||
bytes = { version = "1.2.1" }
|
bytes = { version = "1" }
|
||||||
config = { path = "../config" }
|
config = { path = "../config" }
|
||||||
futures = { version = "0.3.25" }
|
futures = { version = "0" }
|
||||||
model = { path = "../model" }
|
model = { path = "../model" }
|
||||||
|
payment_adapter = { path = "../payment_adapter", optional = true }
|
||||||
rumqttc = { version = "*" }
|
rumqttc = { version = "*" }
|
||||||
serde = { version = "*", features = ['derive'] }
|
serde = { version = "*", features = ['derive'] }
|
||||||
strum = { version = "0.24.1", features = ['strum_macros', 'default', 'derive'] }
|
strum = { version = "0", features = ['strum_macros', 'default', 'derive'] }
|
||||||
tarpc = { version = "0.31.0", features = ["tokio1", "serde-transport-bincode", "serde-transport", "serde", "serde-transport-json", "tcp"] }
|
tarpc = { version = "0", features = ["tokio1", "serde-transport-bincode", "serde-transport", "serde", "serde-transport-json", "tcp"] }
|
||||||
thiserror = { version = "1.0.37" }
|
thiserror = { version = "1" }
|
||||||
tokio = { version = "1.21.2", features = ['full'] }
|
tokio = { version = "1", features = ['full'] }
|
||||||
tracing = { version = "0.1.37" }
|
tracing = { version = "0" }
|
||||||
whatlang = { version = "0.16.2" }
|
whatlang = { version = "0" }
|
||||||
payment_adapter = { path = "../payment_adapter", optional = true }
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
pub static CLIENT_NAME: &str = "cart-manager";
|
pub static CLIENT_NAME: &str = "carts";
|
||||||
|
|
||||||
pub enum Topic {}
|
pub enum Topic {}
|
||||||
|
|
||||||
@ -154,7 +154,7 @@ pub mod mqtt {
|
|||||||
use config::SharedAppConfig;
|
use config::SharedAppConfig;
|
||||||
use rumqttc::EventLoop;
|
use rumqttc::EventLoop;
|
||||||
|
|
||||||
use crate::carts::CLIENT_NAME;
|
use super::CLIENT_NAME;
|
||||||
use crate::AsyncClient;
|
use crate::AsyncClient;
|
||||||
|
|
||||||
pub fn create_client(config: SharedAppConfig) -> (AsyncClient, EventLoop) {
|
pub fn create_client(config: SharedAppConfig) -> (AsyncClient, EventLoop) {
|
||||||
|
@ -18,6 +18,8 @@ pub mod rpc;
|
|||||||
pub mod search;
|
pub mod search;
|
||||||
#[cfg(feature = "stocks")]
|
#[cfg(feature = "stocks")]
|
||||||
pub mod stocks;
|
pub mod stocks;
|
||||||
|
#[cfg(feature = "tokens")]
|
||||||
|
pub mod tokens;
|
||||||
|
|
||||||
pub trait DeserializePayload {
|
pub trait DeserializePayload {
|
||||||
fn deserialize_payload<T: serde::de::DeserializeOwned>(self, bytes: bytes::Bytes) -> Option<T>;
|
fn deserialize_payload<T: serde::de::DeserializeOwned>(self, bytes: bytes::Bytes) -> Option<T>;
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
// use rumqttc::QoS;
|
|
||||||
|
|
||||||
pub use payment_adapter::*;
|
pub use payment_adapter::*;
|
||||||
// use crate::AsyncClient;
|
|
||||||
|
|
||||||
pub static CLIENT_NAME: &str = "payments";
|
pub static CLIENT_NAME: &str = "payments";
|
||||||
|
|
||||||
@ -13,8 +10,21 @@ pub enum Error {
|
|||||||
UnknownAdapter(String),
|
UnknownAdapter(String),
|
||||||
#[error("Payment failed")]
|
#[error("Payment failed")]
|
||||||
PaymentFailed,
|
PaymentFailed,
|
||||||
#[error("Payment adapter returned invalid response data")]
|
#[error("Cancel failed")]
|
||||||
|
CancelFailed,
|
||||||
|
#[error("Payment adapter returned invalid create order response data")]
|
||||||
MalformedCreatePaymentResult,
|
MalformedCreatePaymentResult,
|
||||||
|
#[error("Payment adapter returned invalid cancel response data")]
|
||||||
|
MalformedCancelResult,
|
||||||
|
#[error("Update payment state failed")]
|
||||||
|
UpdateStateFailed,
|
||||||
|
#[error("Payment adapter returned invalid update state response data")]
|
||||||
|
MalformedUpdateStateResult,
|
||||||
|
//
|
||||||
|
#[error("Unable to send msg to adapter")]
|
||||||
|
SendToWasmFailed,
|
||||||
|
#[error("Unable to receive response from adapter")]
|
||||||
|
RecvFromWasmFailed,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
@ -91,7 +101,7 @@ pub mod cancel {
|
|||||||
|
|
||||||
use super::Error;
|
use super::Error;
|
||||||
|
|
||||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
pub enum RefundType {
|
pub enum RefundType {
|
||||||
/// Refund entire payment
|
/// Refund entire payment
|
||||||
Full,
|
Full,
|
||||||
|
97
crates/channels/src/tokens.rs
Normal file
97
crates/channels/src/tokens.rs
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
pub static CLIENT_NAME: &str = "tokens";
|
||||||
|
|
||||||
|
pub enum Topic {}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, thiserror::Error, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub enum Error {}
|
||||||
|
|
||||||
|
pub mod create_pair {
|
||||||
|
use super::Error;
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct Input {}
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct Details {}
|
||||||
|
|
||||||
|
pub type Output = Result<Details, Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod validate {
|
||||||
|
use super::Error;
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct Input {}
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct Details {}
|
||||||
|
|
||||||
|
pub type Output = Result<Details, Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod refresh {
|
||||||
|
use model::RefreshTokenString;
|
||||||
|
|
||||||
|
use super::Error;
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct Input {
|
||||||
|
pub refresh_token: RefreshTokenString,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct Details {}
|
||||||
|
|
||||||
|
pub type Output = Result<Details, Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod rpc {
|
||||||
|
use config::SharedAppConfig;
|
||||||
|
|
||||||
|
use crate::tokens::{create_pair, refresh, validate};
|
||||||
|
|
||||||
|
#[tarpc::service]
|
||||||
|
pub trait Tokens {
|
||||||
|
/// Create new access token and refresh token without any validations
|
||||||
|
async fn create_pair(input: create_pair::Input) -> create_pair::Output;
|
||||||
|
|
||||||
|
/// Check if access token is valid
|
||||||
|
async fn validate(input: validate::Input) -> validate::Output;
|
||||||
|
|
||||||
|
/// Validate with refresh token and create new access token and refresh
|
||||||
|
/// token
|
||||||
|
async fn refresh(input: refresh::Input) -> refresh::Output;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn create_client(config: SharedAppConfig) -> TokensClient {
|
||||||
|
use tarpc::client;
|
||||||
|
use tarpc::tokio_serde::formats::Bincode;
|
||||||
|
|
||||||
|
let addr = {
|
||||||
|
let l = config.lock();
|
||||||
|
(l.tokens().rpc_bind.clone(), l.tokens().rpc_port)
|
||||||
|
};
|
||||||
|
|
||||||
|
let transport = tarpc::serde_transport::tcp::connect(addr, Bincode::default);
|
||||||
|
|
||||||
|
let client = TokensClient::new(
|
||||||
|
client::Config::default(),
|
||||||
|
transport.await.expect("Failed to connect to server"),
|
||||||
|
)
|
||||||
|
.spawn();
|
||||||
|
|
||||||
|
client
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod mqtt {
|
||||||
|
use config::SharedAppConfig;
|
||||||
|
use rumqttc::EventLoop;
|
||||||
|
|
||||||
|
use super::CLIENT_NAME;
|
||||||
|
use crate::AsyncClient;
|
||||||
|
|
||||||
|
pub fn create_client(config: SharedAppConfig) -> (AsyncClient, EventLoop) {
|
||||||
|
crate::mqtt::create_client(CLIENT_NAME, config.lock().tokens().mqtt_addr())
|
||||||
|
}
|
||||||
|
}
|
@ -8,11 +8,13 @@ full = ['actix-web', 'cookie']
|
|||||||
default = []
|
default = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-web = { version = "4.0", features = [], optional = true }
|
actix-web = { version = "4", features = [], optional = true }
|
||||||
cookie = { version = "0.16.1", features = ["signed"], optional = true }
|
cookie = { version = "0", features = ["signed"], optional = true }
|
||||||
parking_lot = { version = "0.12", features = [] }
|
parking_lot = { version = "0", features = [] }
|
||||||
password-hash = { version = "0.4", features = ["alloc"] }
|
password-hash = { version = "0", features = ["alloc"] }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = { version = "1.0", features = [] }
|
serde_json = { version = "1", features = [] }
|
||||||
thiserror = { version = "1.0" }
|
thiserror = { version = "1" }
|
||||||
toml = { version = "0.5", features = [] }
|
toml = { version = "0", features = [] }
|
||||||
|
tracing-subscriber = { version = "0", features = ['env-filter'] }
|
||||||
|
tracing-timing = { version = "0", features = [] }
|
||||||
|
@ -625,32 +625,67 @@ impl PaymentConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct TokensConfig {
|
||||||
|
pub rpc_port: u16,
|
||||||
|
pub rpc_bind: String,
|
||||||
|
pub mqtt_port: u16,
|
||||||
|
pub mqtt_bind: String,
|
||||||
|
pub database_url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TokensConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
rpc_port: 19336,
|
||||||
|
rpc_bind: "0.0.0.0".into(),
|
||||||
|
mqtt_port: 1889,
|
||||||
|
mqtt_bind: "0.0.0.0".into(),
|
||||||
|
database_url: "postgres://postgres@localhost/bazzar_tokens".into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Example for TokensConfig {}
|
||||||
|
|
||||||
|
impl TokensConfig {
|
||||||
|
pub fn rpc_addr(&self) -> (&str, u16) {
|
||||||
|
(&self.rpc_bind, self.rpc_port)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mqtt_addr(&self) -> (&str, u16) {
|
||||||
|
(&self.mqtt_bind, self.mqtt_port)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct AppConfig {
|
pub struct AppConfig {
|
||||||
#[serde(default)]
|
|
||||||
payment: PaymentConfig,
|
|
||||||
#[serde(default)]
|
|
||||||
web: WebConfig,
|
|
||||||
#[serde(default)]
|
|
||||||
mail: MailConfig,
|
|
||||||
#[serde(default)]
|
|
||||||
database: DatabaseConfig,
|
|
||||||
#[serde(default)]
|
|
||||||
search: SearchConfig,
|
|
||||||
#[serde(default)]
|
|
||||||
files: FilesConfig,
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
account_manager: AccountManagerConfig,
|
account_manager: AccountManagerConfig,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
cart_manager: CartManagerConfig,
|
cart_manager: CartManagerConfig,
|
||||||
|
#[serde(skip)]
|
||||||
|
config_path: String,
|
||||||
|
#[serde(default)]
|
||||||
|
database: DatabaseConfig,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
email_sender: EmailSenderConfig,
|
email_sender: EmailSenderConfig,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
stocks: StocksConfig,
|
files: FilesConfig,
|
||||||
|
#[serde(default)]
|
||||||
|
mail: MailConfig,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
order_manager: OrderConfig,
|
order_manager: OrderConfig,
|
||||||
#[serde(skip)]
|
#[serde(default)]
|
||||||
config_path: String,
|
payment: PaymentConfig,
|
||||||
|
#[serde(default)]
|
||||||
|
search: SearchConfig,
|
||||||
|
#[serde(default)]
|
||||||
|
stocks: StocksConfig,
|
||||||
|
#[serde(default)]
|
||||||
|
tokens: TokensConfig,
|
||||||
|
#[serde(default)]
|
||||||
|
web: WebConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Example for AppConfig {
|
impl Example for AppConfig {
|
||||||
@ -668,51 +703,12 @@ impl Example for AppConfig {
|
|||||||
stocks: StocksConfig::example(),
|
stocks: StocksConfig::example(),
|
||||||
order_manager: OrderConfig::example(),
|
order_manager: OrderConfig::example(),
|
||||||
config_path: "".to_string(),
|
config_path: "".to_string(),
|
||||||
|
tokens: TokensConfig::example(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AppConfig {
|
impl AppConfig {
|
||||||
pub fn payment(&self) -> &PaymentConfig {
|
|
||||||
&self.payment
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn web(&self) -> &WebConfig {
|
|
||||||
&self.web
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn mail(&self) -> &MailConfig {
|
|
||||||
&self.mail
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn database(&self) -> &DatabaseConfig {
|
|
||||||
&self.database
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn payment_mut(&mut self) -> &mut PaymentConfig {
|
|
||||||
&mut self.payment
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn web_mut(&mut self) -> &mut WebConfig {
|
|
||||||
&mut self.web
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn mail_mut(&mut self) -> &mut MailConfig {
|
|
||||||
&mut self.mail
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn database_mut(&mut self) -> &mut DatabaseConfig {
|
|
||||||
&mut self.database
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn search(&self) -> &SearchConfig {
|
|
||||||
&self.search
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn files(&self) -> &FilesConfig {
|
|
||||||
&self.files
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn account_manager(&self) -> &AccountManagerConfig {
|
pub fn account_manager(&self) -> &AccountManagerConfig {
|
||||||
&self.account_manager
|
&self.account_manager
|
||||||
}
|
}
|
||||||
@ -721,16 +717,60 @@ impl AppConfig {
|
|||||||
&self.cart_manager
|
&self.cart_manager
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn database(&self) -> &DatabaseConfig {
|
||||||
|
&self.database
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn database_mut(&mut self) -> &mut DatabaseConfig {
|
||||||
|
&mut self.database
|
||||||
|
}
|
||||||
|
|
||||||
pub fn email_sender(&self) -> &EmailSenderConfig {
|
pub fn email_sender(&self) -> &EmailSenderConfig {
|
||||||
&self.email_sender
|
&self.email_sender
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn files(&self) -> &FilesConfig {
|
||||||
|
&self.files
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mail(&self) -> &MailConfig {
|
||||||
|
&self.mail
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mail_mut(&mut self) -> &mut MailConfig {
|
||||||
|
&mut self.mail
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn orders_manager(&self) -> &OrderConfig {
|
||||||
|
&self.order_manager
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn payment(&self) -> &PaymentConfig {
|
||||||
|
&self.payment
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn payment_mut(&mut self) -> &mut PaymentConfig {
|
||||||
|
&mut self.payment
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn search(&self) -> &SearchConfig {
|
||||||
|
&self.search
|
||||||
|
}
|
||||||
|
|
||||||
pub fn stocks_manager(&self) -> &StocksConfig {
|
pub fn stocks_manager(&self) -> &StocksConfig {
|
||||||
&self.stocks
|
&self.stocks
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn orders_manager(&self) -> &OrderConfig {
|
pub fn tokens(&self) -> &TokensConfig {
|
||||||
&self.order_manager
|
&self.tokens
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn web(&self) -> &WebConfig {
|
||||||
|
&self.web
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn web_mut(&mut self) -> &mut WebConfig {
|
||||||
|
&mut self.web
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -749,6 +789,7 @@ impl Default for AppConfig {
|
|||||||
stocks: StocksConfig::default(),
|
stocks: StocksConfig::default(),
|
||||||
order_manager: OrderConfig::default(),
|
order_manager: OrderConfig::default(),
|
||||||
config_path: "".to_string(),
|
config_path: "".to_string(),
|
||||||
|
tokens: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -822,3 +863,15 @@ pub fn save(config_path: &str, config: &mut AppConfig) {
|
|||||||
config.config_path = config_path.into();
|
config.config_path = config_path.into();
|
||||||
std::fs::write(config_path, toml::to_string_pretty(&config).unwrap()).unwrap();
|
std::fs::write(config_path, toml::to_string_pretty(&config).unwrap()).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn init_tracing(_service_name: &str) {
|
||||||
|
use tracing_subscriber::fmt::format::FmtSpan;
|
||||||
|
use tracing_subscriber::layer::SubscriberExt;
|
||||||
|
use tracing_subscriber::util::SubscriberInitExt;
|
||||||
|
|
||||||
|
tracing_subscriber::registry()
|
||||||
|
.with(tracing_subscriber::EnvFilter::from_default_env())
|
||||||
|
.with(tracing_subscriber::fmt::layer().with_span_events(FmtSpan::NEW | FmtSpan::CLOSE))
|
||||||
|
.try_init()
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
@ -7,24 +7,24 @@ edition = "2021"
|
|||||||
dummy = ["fake", "rand"]
|
dummy = ["fake", "rand"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix = { version = "0.13", features = [] }
|
actix = { version = "0", features = [] }
|
||||||
actix-rt = { version = "2.7", features = [] }
|
actix-rt = { version = "2", features = [] }
|
||||||
async-trait = { version = "0.1.56" }
|
async-trait = { version = "0" }
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0", features = ["serde"] }
|
||||||
config = { path = "../config" }
|
config = { path = "../config" }
|
||||||
fake = { version = "2.4.3", features = ["derive", "chrono", "http", "uuid"], optional = true }
|
fake = { version = "2", features = ["derive", "chrono", "http", "uuid"], optional = true }
|
||||||
itertools = { version = "0.10.3" }
|
itertools = { version = "0" }
|
||||||
model = { path = "../model", features = ['db', 'dummy', 'rand'] }
|
model = { path = "../model", features = ['db', 'dummy', 'rand'] }
|
||||||
pretty_env_logger = { version = "0.4", features = [] }
|
pretty_env_logger = { version = "0", features = [] }
|
||||||
rand = { version = "0.8.5", optional = true }
|
rand = { version = "0", optional = true }
|
||||||
rumqttc = { version = "*" }
|
rumqttc = { version = "*" }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
sqlx = { version = "0.6.2", features = ["migrate", "runtime-actix-rustls", "all-types", "postgres"] }
|
sqlx = { version = "0", features = ["migrate", "runtime-actix-rustls", "all-types", "postgres"] }
|
||||||
sqlx-core = { version = "0.6.2", features = [] }
|
sqlx-core = { version = "0", features = [] }
|
||||||
thiserror = { version = "1.0.31" }
|
thiserror = { version = "1" }
|
||||||
tracing = { version = "0.1.34" }
|
tracing = { version = "0" }
|
||||||
uuid = { version = "1.2.1", features = ["serde"] }
|
uuid = { version = "1", features = ["serde"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
fake = { version = "2.5.0" }
|
fake = { version = "2" }
|
||||||
testx = { path = "../testx" }
|
testx = { path = "../testx" }
|
||||||
|
@ -5,18 +5,18 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
account-manager = { path = '../account_manager' }
|
account-manager = { path = '../account_manager' }
|
||||||
bytes = { version = "1.1.0" }
|
bytes = { version = "1" }
|
||||||
channels = { path = '../channels' }
|
channels = { path = '../channels' }
|
||||||
config = { path = "../config" }
|
config = { path = "../config" }
|
||||||
dotenv = { version = "0.15", features = [] }
|
dotenv = { version = "0", features = [] }
|
||||||
fakeit = { version = "1.1.1", features = [] }
|
fakeit = { version = "1", features = [] }
|
||||||
fs_manager = { path = "../fs_manager", features = [] }
|
fs_manager = { path = "../fs_manager", features = [] }
|
||||||
human-panic = { version = "1.0.3" }
|
human-panic = { version = "1" }
|
||||||
model = { path = "../model", version = "0.1", features = ["db", "dummy"] }
|
model = { path = "../model", version = "0.1", features = ["db", "dummy"] }
|
||||||
password-hash = { version = "0.4", features = ["alloc"] }
|
password-hash = { version = "0", features = ["alloc"] }
|
||||||
rand = { version = "0.8.5" }
|
rand = { version = "0" }
|
||||||
stock-manager = { path = "../stock_manager" }
|
stock-manager = { path = "../stock_manager" }
|
||||||
thiserror = { version = "1.0.31" }
|
thiserror = { version = "1" }
|
||||||
tokio = { version = "1.18.1", features = ["full"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
tracing = { version = "0.1.34" }
|
tracing = { version = "0" }
|
||||||
tracing-subscriber = { version = "0.3.11" }
|
tracing-subscriber = { version = "0" }
|
||||||
|
@ -5,5 +5,5 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
model = { path = "../model", features = ["db"] }
|
model = { path = "../model", features = ["db"] }
|
||||||
sqlx = { version = "0.6.2", features = ["migrate", "runtime-actix-rustls", "all-types", "postgres"] }
|
sqlx = { version = "0", features = ["migrate", "runtime-actix-rustls", "all-types", "postgres"] }
|
||||||
sqlx-core = { version = "0.6.2", features = [] }
|
sqlx-core = { version = "0", features = [] }
|
||||||
|
@ -8,29 +8,22 @@ name = "email-sender"
|
|||||||
path = "./src/main.rs"
|
path = "./src/main.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix = { version = "0.13", features = [] }
|
|
||||||
actix-rt = { version = "2.7", features = [] }
|
|
||||||
channels = { path = "../channels" }
|
channels = { path = "../channels" }
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0", features = ["serde"] }
|
||||||
config = { path = "../config" }
|
config = { path = "../config" }
|
||||||
dotenv = { version = "0.15.0" }
|
dotenv = { version = "0" }
|
||||||
handlebars = { version = "*", features = [] }
|
handlebars = { version = "*", features = [] }
|
||||||
model = { path = "../model" }
|
model = { path = "../model" }
|
||||||
opentelemetry = { version = "0.17.0" }
|
|
||||||
opentelemetry-jaeger = { version = "0.17.0" }
|
|
||||||
pretty_env_logger = { version = "0.4", features = [] }
|
|
||||||
rumqttc = { version = "*" }
|
rumqttc = { version = "*" }
|
||||||
sendgrid = { version = "0.18.1", features = ["async"] }
|
sendgrid = { version = "0", features = ["async"] }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = { version = "1.0", features = [] }
|
serde_json = { version = "1", features = [] }
|
||||||
tarpc = { version = "0.31.0", features = ["tokio1", "serde-transport-bincode", "serde-transport", "serde", "serde-transport-json", "tcp"] }
|
tarpc = { version = "0", features = ["tokio1", "serde-transport-bincode", "serde-transport", "serde", "serde-transport-json", "tcp"] }
|
||||||
thiserror = { version = "1.0.31" }
|
thiserror = { version = "1" }
|
||||||
tokio = { version = "1.21.2", features = ['full'] }
|
tokio = { version = "1", features = ['full'] }
|
||||||
tracing = { version = "0.1.37" }
|
tracing = { version = "0" }
|
||||||
tracing-opentelemetry = { version = "0.17.4" }
|
uuid = { version = "0", features = ["serde"] }
|
||||||
tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
|
|
||||||
uuid = { version = "0.8", features = ["serde"] }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
fake = { version = "2.5.0" }
|
fake = { version = "2" }
|
||||||
testx = { path = "../testx" }
|
testx = { path = "../testx" }
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use config::{SharedAppConfig, UpdateConfig};
|
use config::{SharedAppConfig, UpdateConfig};
|
||||||
use tracing_subscriber::fmt::format::FmtSpan;
|
|
||||||
use tracing_subscriber::layer::SubscriberExt;
|
|
||||||
use tracing_subscriber::util::SubscriberInitExt;
|
|
||||||
|
|
||||||
// use crate::db::Database;
|
// use crate::db::Database;
|
||||||
|
|
||||||
@ -55,10 +52,10 @@ impl Context {
|
|||||||
|
|
||||||
pub type SharedContext = Arc<Context>;
|
pub type SharedContext = Arc<Context>;
|
||||||
|
|
||||||
#[actix::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
dotenv::dotenv().ok();
|
dotenv::dotenv().ok();
|
||||||
init_tracing("email-sender");
|
config::init_tracing("email-sender");
|
||||||
|
|
||||||
let config = config::default_load(&Opts {});
|
let config = config::default_load(&Opts {});
|
||||||
|
|
||||||
@ -67,23 +64,3 @@ async fn main() {
|
|||||||
let _mqtt_client = mqtt::start(config.clone(), context.clone()).await;
|
let _mqtt_client = mqtt::start(config.clone(), context.clone()).await;
|
||||||
// rpc::start(config.clone(), db.clone(), mqtt_client.clone()).await;
|
// rpc::start(config.clone(), db.clone(), mqtt_client.clone()).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_tracing(_service_name: &str) {
|
|
||||||
std::env::set_var("OTEL_BSP_MAX_EXPORT_BATCH_SIZE", "12");
|
|
||||||
|
|
||||||
let tracer = {
|
|
||||||
use opentelemetry::sdk::export::trace::stdout::new_pipeline;
|
|
||||||
use opentelemetry::sdk::trace::Config;
|
|
||||||
new_pipeline()
|
|
||||||
.with_trace_config(Config::default())
|
|
||||||
.with_pretty_print(true)
|
|
||||||
.install_simple()
|
|
||||||
};
|
|
||||||
|
|
||||||
tracing_subscriber::registry()
|
|
||||||
.with(tracing_subscriber::EnvFilter::from_default_env())
|
|
||||||
.with(tracing_subscriber::fmt::layer().with_span_events(FmtSpan::NEW | FmtSpan::CLOSE))
|
|
||||||
.with(tracing_opentelemetry::layer().with_tracer(tracer))
|
|
||||||
.try_init()
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
@ -4,19 +4,13 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix = { version = "0.13", features = [] }
|
bytes = { version = "1" }
|
||||||
actix-rt = { version = "2.7", features = [] }
|
chrono = { version = "0", features = ["serde"] }
|
||||||
actix-web = { version = "4.0.1" }
|
|
||||||
bytes = { version = "1.1.0" }
|
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
|
||||||
config = { path = "../config" }
|
config = { path = "../config" }
|
||||||
fibers_rpc = { version = "0.3.4", features = [] }
|
|
||||||
model = { path = "../model" }
|
model = { path = "../model" }
|
||||||
pretty_env_logger = { version = "0.4", features = [] }
|
|
||||||
rumqttc = { version = "*" }
|
rumqttc = { version = "*" }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
thiserror = { version = "1.0.31" }
|
thiserror = { version = "1" }
|
||||||
tokio = { version = "1.18.1", features = ["full"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
tracing = { version = "0.1.34" }
|
tracing = { version = "0" }
|
||||||
uuid = { version = "1.2.1", features = ["serde"] }
|
uuid = { version = "1", features = ["serde"] }
|
||||||
|
|
||||||
|
@ -4,13 +4,10 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix = { version = "0.13", features = [] }
|
|
||||||
actix-rt = { version = "2.7", features = [] }
|
|
||||||
config = { path = "../config" }
|
config = { path = "../config" }
|
||||||
fluent = { version = "0.16.0" }
|
fluent = { version = "0" }
|
||||||
model = { path = "../model" }
|
model = { path = "../model" }
|
||||||
pretty_env_logger = { version = "0.4", features = [] }
|
|
||||||
rumqttc = { version = "*" }
|
rumqttc = { version = "*" }
|
||||||
thiserror = { version = "1.0.31" }
|
thiserror = { version = "1" }
|
||||||
tracing = { version = "0.1.34" }
|
tracing = { version = "0" }
|
||||||
unic-langid = { version = "0.9.0" }
|
unic-langid = { version = "0" }
|
||||||
|
@ -17,17 +17,16 @@ payments = []
|
|||||||
default = ['accounts', 'carts', 'emails', 'search', 'stocks', 'orders', 'payments']
|
default = ['accounts', 'carts', 'emails', 'search', 'stocks', 'orders', 'payments']
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
argon2 = { version = "0.4", features = ["parallel", "password-hash"] }
|
argon2 = { version = "0", features = ["parallel", "password-hash"] }
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0", features = ["serde"] }
|
||||||
derive_more = { version = "0.99.17" }
|
derive_more = { version = "0" }
|
||||||
fake = { version = "2", features = ["derive", "chrono", "http", "uuid", "dummy"], optional = true }
|
fake = { version = "2", features = ["derive", "chrono", "http", "uuid", "dummy"], optional = true }
|
||||||
password-hash = { version = "0.4", features = ["alloc"] }
|
password-hash = { version = "0", features = ["alloc"] }
|
||||||
rand = { version = "0.8.5", optional = true }
|
rand = { version = "0", optional = true }
|
||||||
rand_core = { version = "0.6", features = ["std"] }
|
rand_core = { version = "0", features = ["std"] }
|
||||||
serde = { version = "1.0.137" }
|
serde = { version = "1" }
|
||||||
sqlx = { version = "0.6.2", features = ["migrate", "runtime-actix-rustls", "all-types", "postgres"], optional = true }
|
sqlx = { version = "0", features = ["migrate", "runtime-actix-rustls", "all-types", "postgres"], optional = true }
|
||||||
sqlx-core = { version = "0.6.2", features = [], optional = true }
|
sqlx-core = { version = "0", features = [], optional = true }
|
||||||
thiserror = { version = "1.0.31" }
|
thiserror = { version = "1" }
|
||||||
uuid = { version = "1.2.1", features = ["serde"] }
|
uuid = { version = "1", features = ["serde"] }
|
||||||
validator = { version = "0.16.0" }
|
validator = { version = "0" }
|
||||||
#tracing = { version = "0.1.34" }
|
|
||||||
|
@ -9,24 +9,20 @@ path = "./src/main.rs"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
channels = { path = "../channels" }
|
channels = { path = "../channels" }
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0", features = ["serde"] }
|
||||||
config = { path = "../config" }
|
config = { path = "../config" }
|
||||||
db-utils = { path = "../db-utils" }
|
db-utils = { path = "../db-utils" }
|
||||||
model = { path = "../model", features = ["db"] }
|
model = { path = "../model", features = ["db"] }
|
||||||
opentelemetry = { version = "0.17.0" }
|
|
||||||
opentelemetry-jaeger = { version = "0.17.0" }
|
|
||||||
rumqttc = { version = "*" }
|
rumqttc = { version = "*" }
|
||||||
serde = { version = "1.0.137", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
sqlx = { version = "0.6.2", features = ["migrate", "runtime-actix-rustls", "all-types", "postgres"] }
|
sqlx = { version = "0", features = ["migrate", "runtime-actix-rustls", "all-types", "postgres"] }
|
||||||
sqlx-core = { version = "0.6.2", features = [] }
|
sqlx-core = { version = "0", features = [] }
|
||||||
tarpc = { version = "0.31.0", features = ["tokio1", "serde-transport-bincode", "serde-transport", "serde", "serde-transport-json", "tcp"] }
|
tarpc = { version = "0", features = ["tokio1", "serde-transport-bincode", "serde-transport", "serde", "serde-transport-json", "tcp"] }
|
||||||
thiserror = { version = "1.0.31" }
|
thiserror = { version = "1" }
|
||||||
tokio = { version = "1.21.2", features = ['full'] }
|
tokio = { version = "1", features = ['full'] }
|
||||||
tracing = { version = "0.1.6" }
|
tracing = { version = "0" }
|
||||||
tracing-opentelemetry = { version = "0.17.4" }
|
uuid = { version = "1", features = ["serde"] }
|
||||||
tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
|
|
||||||
uuid = { version = "1.2.1", features = ["serde"] }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
fake = { version = "2.5.0" }
|
fake = { version = "2" }
|
||||||
testx = { path = "../testx" }
|
testx = { path = "../testx" }
|
||||||
|
@ -13,6 +13,7 @@ impl UpdateConfig for Opts {}
|
|||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let opts = Opts {};
|
let opts = Opts {};
|
||||||
|
config::init_tracing("orders");
|
||||||
|
|
||||||
let config = config::default_load(&opts);
|
let config = config::default_load(&opts);
|
||||||
|
|
||||||
|
@ -5,9 +5,9 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
config = { path = "../config", default-features = false, features = [] }
|
config = { path = "../config", default-features = false, features = [] }
|
||||||
|
futures = { version = "0" }
|
||||||
model = { path = "../model" }
|
model = { path = "../model" }
|
||||||
serde = { version = "1.0.149", features = ['derive'] }
|
serde = { version = "1", features = ['derive'] }
|
||||||
uuid = { version = "1.2.2", features = ['v4'] }
|
thiserror = { version = "1" }
|
||||||
futures = { version = "0.3.25" }
|
tracing = { version = "0" }
|
||||||
tracing = { version = "0.1.37" }
|
uuid = { version = "1", features = ['v4'] }
|
||||||
thiserror = { version = "1.0.37" }
|
|
||||||
|
@ -66,7 +66,6 @@ pub struct Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
#[repr(C)]
|
|
||||||
pub struct CreatePayment {
|
pub struct CreatePayment {
|
||||||
pub buyer: Buyer,
|
pub buyer: Buyer,
|
||||||
pub customer_ip: String,
|
pub customer_ip: String,
|
||||||
@ -80,24 +79,38 @@ pub struct CreatePayment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
#[repr(C)]
|
|
||||||
pub struct OrderCreated {
|
pub struct OrderCreated {
|
||||||
pub ext_order_id: Option<ExtOrderId>,
|
pub ext_order_id: Option<ExtOrderId>,
|
||||||
pub redirect_uri: Option<String>,
|
pub redirect_uri: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub enum RefundType {
|
||||||
|
/// Refund entire payment
|
||||||
|
Full,
|
||||||
|
/// Refund only part given in enum
|
||||||
|
Partial(Price),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct Cancel {
|
||||||
|
pub refund: RefundType,
|
||||||
|
pub provider_order_id: String,
|
||||||
|
pub description: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct Cancelled {}
|
||||||
|
|
||||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
#[repr(C)]
|
|
||||||
pub struct UpdateStatus {
|
pub struct UpdateStatus {
|
||||||
pub payload: Vec<u8>,
|
pub payload: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
#[repr(C)]
|
|
||||||
pub struct StatusUpdated {}
|
pub struct StatusUpdated {}
|
||||||
|
|
||||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
#[repr(C)]
|
|
||||||
pub enum HttpMethod {
|
pub enum HttpMethod {
|
||||||
Get,
|
Get,
|
||||||
Post,
|
Post,
|
||||||
|
276
crates/payment_adapter_pay_u/Cargo.lock
generated
276
crates/payment_adapter_pay_u/Cargo.lock
generated
@ -2,6 +2,12 @@
|
|||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "adler"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "0.7.20"
|
version = "0.7.20"
|
||||||
@ -38,6 +44,12 @@ version = "1.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64"
|
||||||
|
version = "0.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64ct"
|
name = "base64ct"
|
||||||
version = "1.5.3"
|
version = "1.5.3"
|
||||||
@ -143,6 +155,8 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"toml",
|
"toml",
|
||||||
|
"tracing-subscriber",
|
||||||
|
"tracing-timing",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -157,6 +171,29 @@ version = "0.8.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
|
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crc32fast"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2801af0d36612ae591caa9568261fddce32ce6e08a7275ea334a06a4ad021a2c"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"crossbeam-channel",
|
||||||
|
"crossbeam-deque",
|
||||||
|
"crossbeam-epoch",
|
||||||
|
"crossbeam-queue",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-channel"
|
name = "crossbeam-channel"
|
||||||
version = "0.5.6"
|
version = "0.5.6"
|
||||||
@ -191,6 +228,16 @@ dependencies = [
|
|||||||
"scopeguard",
|
"scopeguard",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-queue"
|
||||||
|
version = "0.3.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-utils"
|
name = "crossbeam-utils"
|
||||||
version = "0.8.14"
|
version = "0.8.14"
|
||||||
@ -278,12 +325,28 @@ dependencies = [
|
|||||||
"subtle",
|
"subtle",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "doc-comment"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.8.0"
|
version = "1.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
|
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "flate2"
|
||||||
|
version = "1.0.25"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841"
|
||||||
|
dependencies = [
|
||||||
|
"crc32fast",
|
||||||
|
"miniz_oxide",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "form_urlencoded"
|
name = "form_urlencoded"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
@ -382,6 +445,15 @@ dependencies = [
|
|||||||
"slab",
|
"slab",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fxhash"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "generic-array"
|
name = "generic-array"
|
||||||
version = "0.14.6"
|
version = "0.14.6"
|
||||||
@ -403,6 +475,26 @@ dependencies = [
|
|||||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.12.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hdrhistogram"
|
||||||
|
version = "7.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f19b9f54f7c7f55e31401bb647626ce0cf0f67b0004982ce815b3ee72a02aa8"
|
||||||
|
dependencies = [
|
||||||
|
"base64",
|
||||||
|
"byteorder",
|
||||||
|
"crossbeam-channel",
|
||||||
|
"flate2",
|
||||||
|
"nom",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.1.19"
|
version = "0.1.19"
|
||||||
@ -457,6 +549,16 @@ dependencies = [
|
|||||||
"unicode-normalization",
|
"unicode-normalization",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "1.9.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "instant"
|
name = "instant"
|
||||||
version = "0.1.12"
|
version = "0.1.12"
|
||||||
@ -521,6 +623,24 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mach"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "matchers"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
|
||||||
|
dependencies = [
|
||||||
|
"regex-automata",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "matches"
|
name = "matches"
|
||||||
version = "0.1.9"
|
version = "0.1.9"
|
||||||
@ -542,6 +662,21 @@ dependencies = [
|
|||||||
"autocfg",
|
"autocfg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "minimal-lexical"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "miniz_oxide"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa"
|
||||||
|
dependencies = [
|
||||||
|
"adler",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "model"
|
name = "model"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@ -557,6 +692,26 @@ dependencies = [
|
|||||||
"validator",
|
"validator",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nom"
|
||||||
|
version = "7.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"minimal-lexical",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nu-ansi-term"
|
||||||
|
version = "0.46.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
|
||||||
|
dependencies = [
|
||||||
|
"overload",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-integer"
|
name = "num-integer"
|
||||||
version = "0.1.45"
|
version = "0.1.45"
|
||||||
@ -592,6 +747,12 @@ version = "1.16.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
|
checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "overload"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
version = "0.11.2"
|
version = "0.11.2"
|
||||||
@ -712,6 +873,22 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quanta"
|
||||||
|
version = "0.9.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "20afe714292d5e879d8b12740aa223c6a88f118af41870e8b6196e39a02238a8"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
"libc",
|
||||||
|
"mach",
|
||||||
|
"once_cell",
|
||||||
|
"raw-cpuid",
|
||||||
|
"wasi 0.10.0+wasi-snapshot-preview1",
|
||||||
|
"web-sys",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.21"
|
version = "1.0.21"
|
||||||
@ -730,6 +907,15 @@ dependencies = [
|
|||||||
"getrandom",
|
"getrandom",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "raw-cpuid"
|
||||||
|
version = "10.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a6823ea29436221176fe662da99998ad3b4db2c7f31e7b6f5fe43adccd6320bb"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rayon"
|
name = "rayon"
|
||||||
version = "1.6.0"
|
version = "1.6.0"
|
||||||
@ -773,6 +959,15 @@ dependencies = [
|
|||||||
"regex-syntax",
|
"regex-syntax",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
|
||||||
|
dependencies = [
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.6.28"
|
version = "0.6.28"
|
||||||
@ -865,6 +1060,15 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sharded-slab"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.4.7"
|
version = "0.4.7"
|
||||||
@ -926,6 +1130,15 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thread_local"
|
||||||
|
version = "1.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.1.45"
|
version = "0.1.45"
|
||||||
@ -991,6 +1204,53 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a"
|
checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
"valuable",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-log"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
"log",
|
||||||
|
"tracing-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-subscriber"
|
||||||
|
version = "0.3.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70"
|
||||||
|
dependencies = [
|
||||||
|
"matchers",
|
||||||
|
"nu-ansi-term",
|
||||||
|
"once_cell",
|
||||||
|
"regex",
|
||||||
|
"sharded-slab",
|
||||||
|
"smallvec",
|
||||||
|
"thread_local",
|
||||||
|
"tracing",
|
||||||
|
"tracing-core",
|
||||||
|
"tracing-log",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-timing"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fbe4966d7b6ae25201de6ff9fa822afb0c9e933809187d5b82ad846ec108771b"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam",
|
||||||
|
"doc-comment",
|
||||||
|
"fxhash",
|
||||||
|
"hdrhistogram",
|
||||||
|
"indexmap",
|
||||||
|
"quanta",
|
||||||
|
"slab",
|
||||||
|
"tracing-core",
|
||||||
|
"tracing-subscriber",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1062,6 +1322,12 @@ dependencies = [
|
|||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "valuable"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.9.4"
|
version = "0.9.4"
|
||||||
@ -1154,6 +1420,16 @@ version = "0.2.83"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
|
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "web-sys"
|
||||||
|
version = "0.3.60"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f"
|
||||||
|
dependencies = [
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
|
@ -7,13 +7,13 @@ edition = "2021"
|
|||||||
crate-type = ['cdylib']
|
crate-type = ['cdylib']
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
bincode = { version = "1" }
|
||||||
|
#tracing = { version = "0" }
|
||||||
|
chrono = { version = "0", features = ['alloc', 'wasmbind'] }
|
||||||
|
common_macros = { version = "0" }
|
||||||
payment_adapter = { path = "../payment_adapter" }
|
payment_adapter = { path = "../payment_adapter" }
|
||||||
bincode = { version = "1.3.3" }
|
serde = { version = "1", features = ['derive'] }
|
||||||
wapc-codec = { version = "1.0.0" }
|
serde_json = { version = "1" }
|
||||||
wapc-guest = { version = "1.0.0" }
|
thiserror = { version = "1" }
|
||||||
#tracing = { version = "0.1.37" }
|
wapc-codec = { version = "1" }
|
||||||
chrono = { version = "0.4.23", features = ['alloc', 'wasmbind'] }
|
wapc-guest = { version = "1" }
|
||||||
serde = { version = "1.0.149", features = ['derive'] }
|
|
||||||
thiserror = { version = "1.0.37" }
|
|
||||||
serde_json = { version = "1.0.89" }
|
|
||||||
common_macros = { version = "0.1.1" }
|
|
||||||
|
@ -8,36 +8,31 @@ name = "payment-manager"
|
|||||||
path = "./src/main.rs"
|
path = "./src/main.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
bincode = { version = "1" }
|
||||||
channels = { path = "../channels" }
|
channels = { path = "../channels" }
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0", features = ["serde"] }
|
||||||
config = { path = "../config" }
|
config = { path = "../config" }
|
||||||
db-utils = { path = "../db-utils" }
|
db-utils = { path = "../db-utils" }
|
||||||
gumdrop = { version = "0.8.1", features = [] }
|
gumdrop = { version = "0", features = [] }
|
||||||
llvmenv = { version = "0.3.2" }
|
llvmenv = { version = "0" }
|
||||||
model = { path = "../model", features = ["db"] }
|
model = { path = "../model", features = ["db"] }
|
||||||
opentelemetry = { version = "0.17.0" }
|
|
||||||
opentelemetry-jaeger = { version = "0.17.0" }
|
|
||||||
payment_adapter = { path = "../payment_adapter" }
|
payment_adapter = { path = "../payment_adapter" }
|
||||||
|
reqwest = { version = "0", features = ["default", "json", "blocking"] }
|
||||||
rumqttc = { version = "*" }
|
rumqttc = { version = "*" }
|
||||||
serde = { version = "1.0.137", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
sqlx = { version = "0.6.2", features = ["migrate", "runtime-actix-rustls", "all-types", "postgres"] }
|
sqlx = { version = "0", features = ["migrate", "runtime-actix-rustls", "all-types", "postgres"] }
|
||||||
sqlx-core = { version = "0.6.2", features = [] }
|
sqlx-core = { version = "0", features = [] }
|
||||||
tarpc = { version = "0.31.0", features = ["tokio1", "serde-transport-bincode", "serde-transport", "serde", "serde-transport-json", "tcp"] }
|
tarpc = { version = "0", features = ["tokio1", "serde-transport-bincode", "serde-transport", "serde", "serde-transport-json", "tcp"] }
|
||||||
thiserror = { version = "1.0.31" }
|
thiserror = { version = "1" }
|
||||||
tokio = { version = "1.21.2", features = ['full'] }
|
tokio = { version = "1", features = ['full'] }
|
||||||
tracing = { version = "0.1.6" }
|
tracing = { version = "0" }
|
||||||
tracing-opentelemetry = { version = "0.17.4" }
|
uuid = { version = "1", features = ["serde", "v4"] }
|
||||||
tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
|
wapc = { version = "1", features = [] }
|
||||||
uuid = { version = "1.2.1", features = ["serde", "v4"] }
|
wapc-codec = { version = "1", features = [] }
|
||||||
bincode = { version = "1.3.3" }
|
wapc-pool = { version = "1", features = [] }
|
||||||
wapc = { version = "1.0.0", features = [] }
|
wasmtime = { version = "3", features = ['parallel-compilation', 'async'] }
|
||||||
wapc-codec = { version = "1.0.0", features = [] }
|
wasmtime-provider = { version = "1", features = ['wasmtime-wasi', 'wasi-common', 'wasi', 'cache'] }
|
||||||
wapc-pool = { version = "1.0.0", features = [] }
|
|
||||||
wasmtime = { version = "3.0.1", features = ['parallel-compilation', 'async'] }
|
|
||||||
wasmtime-provider = { version = "1.3.2", features = ['wasmtime-wasi', 'wasi-common', 'wasi', 'cache'] }
|
|
||||||
reqwest = { version = "0.11.13", features = ["default", "json", "blocking"] }
|
|
||||||
#pay_u = { path = "../../vendor/pay_u" }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
fake = { version = "2.5.0" }
|
fake = { version = "2" }
|
||||||
testx = { path = "../testx" }
|
testx = { path = "../testx" }
|
||||||
|
@ -3,14 +3,16 @@ use std::fs::read_dir;
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
use std::sync::{Arc, LockResult, MutexGuard, RwLock, RwLockReadGuard};
|
use std::sync::{Arc, LockResult, MutexGuard};
|
||||||
|
|
||||||
use config::{AppConfig, UpdateConfig};
|
use config::{AppConfig, SharedAppConfig, UpdateConfig};
|
||||||
use payment_adapter::{HttpMethod, HttpRequest, Item, Product};
|
use payment_adapter::{HttpMethod, HttpRequest};
|
||||||
use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
|
use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
|
||||||
use wapc::WasiParams;
|
use wapc::WasiParams;
|
||||||
use wapc_pool::{HostPool, HostPoolBuilder};
|
use wapc_pool::{HostPool, HostPoolBuilder};
|
||||||
|
|
||||||
|
use crate::rpc::ModuleSender;
|
||||||
|
|
||||||
// mod actions;
|
// mod actions;
|
||||||
// mod context;
|
// mod context;
|
||||||
// mod db;
|
// mod db;
|
||||||
@ -23,14 +25,23 @@ mod rpc;
|
|||||||
pub enum WasmMsg {
|
pub enum WasmMsg {
|
||||||
CreateOrder {
|
CreateOrder {
|
||||||
create_payment: payment_adapter::CreatePayment,
|
create_payment: payment_adapter::CreatePayment,
|
||||||
callback: std::sync::mpsc::Sender<ResultMsg>,
|
tx: Sender<ResultMsg>,
|
||||||
|
},
|
||||||
|
UpdateStatus {
|
||||||
|
update_status: payment_adapter::UpdateStatus,
|
||||||
|
tx: Sender<ResultMsg>,
|
||||||
|
},
|
||||||
|
Cancel {
|
||||||
|
cancel: payment_adapter::Cancel,
|
||||||
|
tx: Sender<ResultMsg>,
|
||||||
},
|
},
|
||||||
UpdateStatus(Vec<u8>),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum ResultMsg {
|
pub enum ResultMsg {
|
||||||
OrderCreated(payment_adapter::OrderCreated),
|
OrderCreated(payment_adapter::OrderCreated),
|
||||||
|
StatusUpdated(payment_adapter::StatusUpdated),
|
||||||
|
Cancel(payment_adapter::Cancelled),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct HostChannel {
|
pub struct HostChannel {
|
||||||
@ -54,41 +65,84 @@ impl HostChannel {
|
|||||||
match msg {
|
match msg {
|
||||||
WasmMsg::CreateOrder {
|
WasmMsg::CreateOrder {
|
||||||
create_payment: msg,
|
create_payment: msg,
|
||||||
callback,
|
tx,
|
||||||
} => {
|
} => {
|
||||||
let msg = match wapc_codec::messagepack::serialize(msg) {
|
Self::send_and_rec(
|
||||||
Ok(r) => r,
|
"create_payment",
|
||||||
_ => continue,
|
msg,
|
||||||
};
|
tx,
|
||||||
let call_result = match pool.call("create_payment", msg).await {
|
&pool,
|
||||||
Ok(r) => r,
|
ResultMsg::OrderCreated,
|
||||||
_ => continue,
|
)
|
||||||
};
|
.await
|
||||||
let result: payment_adapter::OrderCreated =
|
.ok();
|
||||||
match wapc_codec::messagepack::deserialize(&call_result) {
|
|
||||||
Ok(r) => r,
|
|
||||||
_ => continue,
|
|
||||||
};
|
|
||||||
if let Err(e) = callback.send(ResultMsg::OrderCreated(result)) {
|
|
||||||
tracing::error!("{e:?}");
|
|
||||||
}
|
}
|
||||||
|
WasmMsg::UpdateStatus {
|
||||||
|
update_status: msg,
|
||||||
|
tx,
|
||||||
|
} => {
|
||||||
|
Self::send_and_rec(
|
||||||
|
"update_status",
|
||||||
|
msg,
|
||||||
|
tx,
|
||||||
|
&pool,
|
||||||
|
ResultMsg::StatusUpdated,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
WasmMsg::Cancel { cancel: msg, tx } => {
|
||||||
|
Self::send_and_rec("cancel_order", msg, tx, &pool, ResultMsg::Cancel)
|
||||||
|
.await
|
||||||
|
.ok();
|
||||||
}
|
}
|
||||||
WasmMsg::UpdateStatus(_) => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn send_and_rec<R, T, F>(
|
||||||
|
name: &str,
|
||||||
|
msg: R,
|
||||||
|
tx: Sender<ResultMsg>,
|
||||||
|
pool: &HostPool,
|
||||||
|
f: F,
|
||||||
|
) -> Result<(), ()>
|
||||||
|
where
|
||||||
|
R: serde::Serialize,
|
||||||
|
T: serde::de::DeserializeOwned,
|
||||||
|
F: FnOnce(T) -> ResultMsg,
|
||||||
|
{
|
||||||
|
macro_rules! ok_or_bail {
|
||||||
|
($run: expr) => {
|
||||||
|
match $run {
|
||||||
|
Ok(r) => r,
|
||||||
|
_ => return Err(()),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let msg = ok_or_bail!(wapc_codec::messagepack::serialize(msg));
|
||||||
|
let call_result = ok_or_bail!(pool.call(name, msg).await);
|
||||||
|
let result: T = ok_or_bail!(wapc_codec::messagepack::deserialize(&call_result));
|
||||||
|
if let Err(e) = tx.send(f(result)) {
|
||||||
|
tracing::error!("{e:?}");
|
||||||
|
Err(())
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Modules(Arc<std::sync::Mutex<HashMap<String, std::sync::mpsc::Sender<WasmMsg>>>>);
|
pub struct Modules(Arc<std::sync::Mutex<HashMap<String, ModuleSender>>>);
|
||||||
|
|
||||||
impl Modules {
|
impl Modules {
|
||||||
pub fn new(h: HashMap<String, std::sync::mpsc::Sender<WasmMsg>>) -> Self {
|
pub fn new(h: HashMap<String, ModuleSender>) -> Self {
|
||||||
Self(Arc::new(std::sync::Mutex::new(h)))
|
Self(Arc::new(std::sync::Mutex::new(h)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lock(&self) -> LockResult<MutexGuard<'_, HashMap<String, Sender<WasmMsg>>>> {
|
pub fn lock(&self) -> LockResult<MutexGuard<'_, HashMap<String, ModuleSender>>> {
|
||||||
self.0.lock()
|
self.0.lock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -108,35 +162,71 @@ impl UpdateConfig for Opts {
|
|||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
std::env::set_var("RUST_LOG", "debug");
|
config::init_tracing("payments");
|
||||||
|
|
||||||
tracing_subscriber::fmt::init();
|
|
||||||
|
|
||||||
let opts: Opts = gumdrop::parse_args_default_or_exit();
|
let opts: Opts = gumdrop::parse_args_default_or_exit();
|
||||||
|
|
||||||
let config = config::default_load(&opts);
|
let config = config::default_load(&opts);
|
||||||
let mut modules;
|
let files = scan_adapters_dir(&config);
|
||||||
|
let mut modules = HashMap::with_capacity(files.len());
|
||||||
|
|
||||||
{
|
load_adapters(&config, &mut modules, files).await;
|
||||||
let adapters_path = config.lock().payment().adapters_path.clone();
|
|
||||||
let adapters = adapters_path.unwrap_or_else(|| panic!("No payment adapters path provided"));
|
|
||||||
let dir = read_dir(&adapters).unwrap_or_else(|e| {
|
|
||||||
panic!(
|
|
||||||
"Failed to load payment adapters at path {:?}. {}",
|
|
||||||
adapters, e
|
|
||||||
)
|
|
||||||
});
|
|
||||||
let files = dir
|
|
||||||
.filter_map(|r| r.map(|r| r.path()).ok())
|
|
||||||
.filter(|file| file.extension().and_then(|s| s.to_str()) == Some("wasm"))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
if files.is_empty() {
|
// for pool in modules.values() {
|
||||||
panic!("No payment adapters found in adapters directory");
|
// let msg = payment_adapter::CreatePayment {
|
||||||
|
// buyer: payment_adapter::Buyer {
|
||||||
|
// email: "hello@example.com".to_string(),
|
||||||
|
// phone: "530698478".to_string(),
|
||||||
|
// first_name: "Joe".to_string(),
|
||||||
|
// last_name: "Doe".to_string(),
|
||||||
|
// language: "pl".to_string(),
|
||||||
|
// },
|
||||||
|
// customer_ip: "12.22.34.54".to_string(),
|
||||||
|
// currency: "PLN".to_string(),
|
||||||
|
// description: "Nesciunt fugit libero quis dolorum quo.
|
||||||
|
// Tempore aut nisi voluptatem. Odio et aspernatur est. Sint vel
|
||||||
|
// molestias sunt cumque quibusdam reprehenderit est.".to_string(),
|
||||||
|
// cart_products: vec![Product {
|
||||||
|
// id: 23,
|
||||||
|
// name: "Socks".to_string(),
|
||||||
|
// unit_price: 1542,
|
||||||
|
// quantity_unit: "Unit".to_string(),
|
||||||
|
// quantity: 2,
|
||||||
|
// }],
|
||||||
|
// items: vec![Item {
|
||||||
|
// product_id: 23,
|
||||||
|
// quantity: 2,
|
||||||
|
// quantity_unit: "Unit".to_string(),
|
||||||
|
// }],
|
||||||
|
// order_ext_id: None,
|
||||||
|
// notify_uri: "https://localhost:3030/notify_uri".to_string(),
|
||||||
|
// continue_uri: "https://localhost:3030/continue_uri".to_string(),
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// tracing::info!("Start create_payment");
|
||||||
|
// let call_result = pool
|
||||||
|
// .call(
|
||||||
|
// "create_payment",
|
||||||
|
// wapc_codec::messagepack::serialize(msg).unwrap(),
|
||||||
|
// )
|
||||||
|
// .await
|
||||||
|
// .unwrap();
|
||||||
|
// let result: payment_adapter::OrderCreated =
|
||||||
|
// wapc_codec::messagepack::deserialize(&call_result).unwrap();
|
||||||
|
//
|
||||||
|
// tracing::info!("create payment res {:?}", result)
|
||||||
|
// }
|
||||||
|
|
||||||
|
let modules = Modules::new(modules);
|
||||||
|
let mqtt_client = mqtt::start(config.clone(), modules.clone()).await;
|
||||||
|
rpc::start(config, mqtt_client, modules.clone()).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
modules = HashMap::with_capacity(files.len());
|
async fn load_adapters(
|
||||||
|
config: &SharedAppConfig,
|
||||||
|
modules: &mut HashMap<String, ModuleSender>,
|
||||||
|
files: Vec<PathBuf>,
|
||||||
|
) {
|
||||||
for file in files {
|
for file in files {
|
||||||
let module = std::fs::read(&file).unwrap();
|
let module = std::fs::read(&file).unwrap();
|
||||||
let engine = wasmtime_provider::WasmtimeEngineProviderBuilder::new()
|
let engine = wasmtime_provider::WasmtimeEngineProviderBuilder::new()
|
||||||
@ -187,58 +277,28 @@ async fn main() {
|
|||||||
}
|
}
|
||||||
.start();
|
.start();
|
||||||
|
|
||||||
modules.insert(name, tx);
|
modules.insert(name, ModuleSender::new(tx));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// for pool in modules.values() {
|
fn scan_adapters_dir(config: &SharedAppConfig) -> Vec<PathBuf> {
|
||||||
// let msg = payment_adapter::CreatePayment {
|
let adapters_path = config.lock().payment().adapters_path.clone();
|
||||||
// buyer: payment_adapter::Buyer {
|
let adapters = adapters_path.unwrap_or_else(|| panic!("No payment adapters path provided"));
|
||||||
// email: "hello@example.com".to_string(),
|
let dir = read_dir(&adapters).unwrap_or_else(|e| {
|
||||||
// phone: "530698478".to_string(),
|
panic!(
|
||||||
// first_name: "Joe".to_string(),
|
"Failed to load payment adapters at path {:?}. {}",
|
||||||
// last_name: "Doe".to_string(),
|
adapters, e
|
||||||
// language: "pl".to_string(),
|
)
|
||||||
// },
|
});
|
||||||
// customer_ip: "12.22.34.54".to_string(),
|
let files = dir
|
||||||
// currency: "PLN".to_string(),
|
.filter_map(|r| r.map(|r| r.path()).ok())
|
||||||
// description: "Nesciunt fugit libero quis dolorum quo.
|
.filter(|file| file.extension().and_then(|s| s.to_str()) == Some("wasm"))
|
||||||
// Tempore aut nisi voluptatem. Odio et aspernatur est. Sint vel
|
.collect::<Vec<_>>();
|
||||||
// molestias sunt cumque quibusdam reprehenderit est.".to_string(),
|
|
||||||
// cart_products: vec![Product {
|
|
||||||
// id: 23,
|
|
||||||
// name: "Socks".to_string(),
|
|
||||||
// unit_price: 1542,
|
|
||||||
// quantity_unit: "Unit".to_string(),
|
|
||||||
// quantity: 2,
|
|
||||||
// }],
|
|
||||||
// items: vec![Item {
|
|
||||||
// product_id: 23,
|
|
||||||
// quantity: 2,
|
|
||||||
// quantity_unit: "Unit".to_string(),
|
|
||||||
// }],
|
|
||||||
// order_ext_id: None,
|
|
||||||
// notify_uri: "https://localhost:3030/notify_uri".to_string(),
|
|
||||||
// continue_uri: "https://localhost:3030/continue_uri".to_string(),
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// tracing::info!("Start create_payment");
|
|
||||||
// let call_result = pool
|
|
||||||
// .call(
|
|
||||||
// "create_payment",
|
|
||||||
// wapc_codec::messagepack::serialize(msg).unwrap(),
|
|
||||||
// )
|
|
||||||
// .await
|
|
||||||
// .unwrap();
|
|
||||||
// let result: payment_adapter::OrderCreated =
|
|
||||||
// wapc_codec::messagepack::deserialize(&call_result).unwrap();
|
|
||||||
//
|
|
||||||
// tracing::info!("create payment res {:?}", result)
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
let modules = Modules::new(modules);
|
if files.is_empty() {
|
||||||
let mqtt_client = mqtt::start(config.clone(), modules.clone()).await;
|
panic!("No payment adapters found in adapters directory");
|
||||||
rpc::start(config, mqtt_client, modules.clone()).await;
|
}
|
||||||
|
files
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[tracing::instrument]
|
// #[tracing::instrument]
|
||||||
|
@ -1,13 +1,43 @@
|
|||||||
use std::sync::mpsc::RecvError;
|
use std::sync::mpsc::Sender;
|
||||||
|
|
||||||
use channels::payments::rpc::Payments;
|
use channels::payments::rpc::Payments;
|
||||||
use channels::payments::{adapters, cancel, notification, start_payment};
|
use channels::payments::{adapters, cancel, notification, start_payment};
|
||||||
use channels::AsyncClient;
|
use channels::{payments, AsyncClient};
|
||||||
use config::SharedAppConfig;
|
use config::SharedAppConfig;
|
||||||
|
use payment_adapter::Cancel;
|
||||||
use tarpc::context;
|
use tarpc::context;
|
||||||
|
|
||||||
use crate::{Modules, ResultMsg, WasmMsg};
|
use crate::{Modules, ResultMsg, WasmMsg};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ModuleSender {
|
||||||
|
tx: Sender<WasmMsg>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModuleSender {
|
||||||
|
pub fn new(tx: Sender<WasmMsg>) -> Self {
|
||||||
|
Self { tx }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send<F>(&self, f: F) -> Result<ResultMsg, payments::Error>
|
||||||
|
where
|
||||||
|
F: FnOnce(Sender<ResultMsg>) -> WasmMsg,
|
||||||
|
{
|
||||||
|
use payments::Error;
|
||||||
|
|
||||||
|
let (tx, rx) = std::sync::mpsc::channel();
|
||||||
|
self.tx.send(f(tx)).map_err(|e| {
|
||||||
|
tracing::error!("{e}");
|
||||||
|
Error::SendToWasmFailed
|
||||||
|
})?;
|
||||||
|
let res = rx.recv().map_err(|e| {
|
||||||
|
tracing::error!("{e}");
|
||||||
|
Error::RecvFromWasmFailed
|
||||||
|
})?;
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct PaymentsServer {
|
pub struct PaymentsServer {
|
||||||
pub config: SharedAppConfig,
|
pub config: SharedAppConfig,
|
||||||
@ -15,6 +45,21 @@ pub struct PaymentsServer {
|
|||||||
pub modules: Modules,
|
pub modules: Modules,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PaymentsServer {
|
||||||
|
fn adapter_sender(&self, adapter_name: String) -> Result<ModuleSender, payments::Error> {
|
||||||
|
use channels::payments::Error;
|
||||||
|
let modules = self.modules.clone();
|
||||||
|
let lock = modules.lock().map_err(|e| {
|
||||||
|
tracing::error!("{e}");
|
||||||
|
Error::InternalServerError
|
||||||
|
})?;
|
||||||
|
Ok(lock
|
||||||
|
.get(&adapter_name)
|
||||||
|
.ok_or_else(|| Error::UnknownAdapter(adapter_name))?
|
||||||
|
.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[tarpc::server]
|
#[tarpc::server]
|
||||||
impl Payments for PaymentsServer {
|
impl Payments for PaymentsServer {
|
||||||
async fn start_payment(
|
async fn start_payment(
|
||||||
@ -28,32 +73,8 @@ impl Payments for PaymentsServer {
|
|||||||
create_payment,
|
create_payment,
|
||||||
} = input;
|
} = input;
|
||||||
|
|
||||||
// let (tx, rx) = std::sync::mpsc::channel();
|
let module = self.adapter_sender(adapter_name)?;
|
||||||
|
let res = module.send(|tx| WasmMsg::CreateOrder { create_payment, tx })?;
|
||||||
let modules = self.modules.clone();
|
|
||||||
let lock = modules.lock().map_err(|e| {
|
|
||||||
tracing::error!("{e}");
|
|
||||||
Error::InternalServerError
|
|
||||||
})?;
|
|
||||||
let module = lock
|
|
||||||
.get(&adapter_name)
|
|
||||||
.ok_or_else(|| Error::UnknownAdapter(adapter_name))?
|
|
||||||
.clone();
|
|
||||||
|
|
||||||
let (tx, rx) = std::sync::mpsc::channel();
|
|
||||||
module
|
|
||||||
.send(WasmMsg::CreateOrder {
|
|
||||||
create_payment,
|
|
||||||
callback: tx,
|
|
||||||
})
|
|
||||||
.map_err(|e| {
|
|
||||||
tracing::error!("{e}");
|
|
||||||
Error::PaymentFailed
|
|
||||||
})?;
|
|
||||||
let res = rx.recv().map_err(|e| {
|
|
||||||
tracing::error!("{e}");
|
|
||||||
Error::MalformedCreatePaymentResult
|
|
||||||
})?;
|
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
ResultMsg::OrderCreated(res) => Ok(start_payment::Details {
|
ResultMsg::OrderCreated(res) => Ok(start_payment::Details {
|
||||||
@ -64,7 +85,31 @@ impl Payments for PaymentsServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn cancel(self, _: context::Context, input: cancel::Input) -> cancel::Output {
|
async fn cancel(self, _: context::Context, input: cancel::Input) -> cancel::Output {
|
||||||
todo!()
|
use channels::payments::cancel::RefundType;
|
||||||
|
use channels::payments::Error;
|
||||||
|
|
||||||
|
let cancel::Input {
|
||||||
|
adapter_name,
|
||||||
|
refund,
|
||||||
|
provider_order_id,
|
||||||
|
description,
|
||||||
|
} = input;
|
||||||
|
let module = self.adapter_sender(adapter_name)?;
|
||||||
|
let res = module.send(|tx| WasmMsg::Cancel {
|
||||||
|
cancel: Cancel {
|
||||||
|
refund: match refund {
|
||||||
|
RefundType::Full => payment_adapter::RefundType::Full,
|
||||||
|
RefundType::Partial(v) => payment_adapter::RefundType::Partial(v),
|
||||||
|
},
|
||||||
|
provider_order_id,
|
||||||
|
description,
|
||||||
|
},
|
||||||
|
tx,
|
||||||
|
})?;
|
||||||
|
match res {
|
||||||
|
ResultMsg::Cancel(_res) => Ok(cancel::Details {}),
|
||||||
|
_ => Err(Error::MalformedCancelResult),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn notification(
|
async fn notification(
|
||||||
@ -72,11 +117,29 @@ impl Payments for PaymentsServer {
|
|||||||
_: context::Context,
|
_: context::Context,
|
||||||
input: notification::Input,
|
input: notification::Input,
|
||||||
) -> notification::Output {
|
) -> notification::Output {
|
||||||
todo!()
|
use channels::payments::{notification, Error};
|
||||||
|
|
||||||
|
let notification::Input { adapter_name, body } = input;
|
||||||
|
let module = self.adapter_sender(adapter_name)?;
|
||||||
|
let res = module.send(|tx| WasmMsg::UpdateStatus {
|
||||||
|
update_status: payment_adapter::UpdateStatus { payload: body },
|
||||||
|
tx,
|
||||||
|
})?;
|
||||||
|
match res {
|
||||||
|
ResultMsg::StatusUpdated(_res) => Ok(notification::Details {}),
|
||||||
|
_ => Err(Error::MalformedUpdateStateResult),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn adapters(self, _: context::Context, input: adapters::Input) -> adapters::Output {
|
async fn adapters(self, _: context::Context, _input: adapters::Input) -> adapters::Output {
|
||||||
todo!()
|
Ok(adapters::Details {
|
||||||
|
names: self
|
||||||
|
.modules
|
||||||
|
.0
|
||||||
|
.lock()
|
||||||
|
.map(|r| r.keys().cloned().collect::<Vec<_>>())
|
||||||
|
.unwrap_or_default(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,31 +8,26 @@ name = "search-manager"
|
|||||||
path = "./src/main.rs"
|
path = "./src/main.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix = { version = "0.13", features = [] }
|
actix = { version = "0", features = [] }
|
||||||
actix-rt = { version = "2.7", features = [] }
|
actix-rt = { version = "2", features = [] }
|
||||||
channels = { path = "../channels" }
|
channels = { path = "../channels" }
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0", features = ["serde"] }
|
||||||
config = { path = "../config" }
|
config = { path = "../config" }
|
||||||
derive_more = { version = "0.99", features = [] }
|
derive_more = { version = "0", features = [] }
|
||||||
dotenv = { version = "0.15.0" }
|
dotenv = { version = "0" }
|
||||||
futures = { version = "0.3.25" }
|
futures = { version = "0" }
|
||||||
model = { path = "../model", features = ["db"] }
|
model = { path = "../model", features = ["db"] }
|
||||||
opentelemetry = { version = "0.17.0" }
|
parking_lot = { version = "0", features = [] }
|
||||||
opentelemetry-jaeger = { version = "0.17.0" }
|
|
||||||
parking_lot = { version = "0.12", features = [] }
|
|
||||||
pretty_env_logger = { version = "0.4", features = [] }
|
|
||||||
rumqttc = { version = "*" }
|
rumqttc = { version = "*" }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
sonic-channel = { version = "1.1.0", features = ["ingest"] }
|
sonic-channel = { version = "1", features = ["ingest"] }
|
||||||
tarpc = { version = "0.31.0", features = ["tokio1", "serde-transport-bincode", "serde-transport", "serde", "serde-transport-json", "tcp"] }
|
tarpc = { version = "0", features = ["tokio1", "serde-transport-bincode", "serde-transport", "serde", "serde-transport-json", "tcp"] }
|
||||||
thiserror = { version = "1.0.31" }
|
thiserror = { version = "1" }
|
||||||
tokio = { version = "1.21.2", features = ['full'] }
|
tokio = { version = "1", features = ['full'] }
|
||||||
tracing = { version = "0.1.6" }
|
tracing = { version = "0" }
|
||||||
tracing-opentelemetry = { version = "0.17.4" }
|
uuid = { version = "1", features = ["serde"] }
|
||||||
tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
|
whatlang = { version = "0" }
|
||||||
uuid = { version = "1.2.1", features = ["serde"] }
|
|
||||||
whatlang = { version = "0.16.2" }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
fake = { version = "2.5.0" }
|
fake = { version = "2" }
|
||||||
testx = { path = "../testx" }
|
testx = { path = "../testx" }
|
||||||
|
@ -4,9 +4,6 @@ use std::env;
|
|||||||
|
|
||||||
use config::UpdateConfig;
|
use config::UpdateConfig;
|
||||||
pub use context::*;
|
pub use context::*;
|
||||||
use tracing_subscriber::fmt::format::FmtSpan;
|
|
||||||
use tracing_subscriber::layer::SubscriberExt;
|
|
||||||
use tracing_subscriber::util::SubscriberInitExt;
|
|
||||||
|
|
||||||
pub mod actions;
|
pub mod actions;
|
||||||
pub mod context;
|
pub mod context;
|
||||||
@ -35,7 +32,7 @@ impl UpdateConfig for Opts {}
|
|||||||
#[actix::main]
|
#[actix::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
dotenv::dotenv().ok();
|
dotenv::dotenv().ok();
|
||||||
init_tracing("search-manager");
|
config::init_tracing("search-manager");
|
||||||
|
|
||||||
let opts = Opts {};
|
let opts = Opts {};
|
||||||
|
|
||||||
@ -47,24 +44,3 @@ async fn main() {
|
|||||||
|
|
||||||
rpc::start(config.clone(), ctx.clone()).await;
|
rpc::start(config.clone(), ctx.clone()).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_tracing(_service_name: &str) {
|
|
||||||
env::set_var("OTEL_BSP_MAX_EXPORT_BATCH_SIZE", "12");
|
|
||||||
|
|
||||||
let tracer = {
|
|
||||||
use opentelemetry::sdk::export::trace::stdout::new_pipeline;
|
|
||||||
use opentelemetry::sdk::trace::Config;
|
|
||||||
|
|
||||||
new_pipeline()
|
|
||||||
.with_trace_config(Config::default())
|
|
||||||
.with_pretty_print(true)
|
|
||||||
.install_simple()
|
|
||||||
};
|
|
||||||
|
|
||||||
tracing_subscriber::registry()
|
|
||||||
.with(tracing_subscriber::EnvFilter::from_default_env())
|
|
||||||
.with(tracing_subscriber::fmt::layer().with_span_events(FmtSpan::NEW | FmtSpan::CLOSE))
|
|
||||||
.with(tracing_opentelemetry::layer().with_tracer(tracer))
|
|
||||||
.try_init()
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
@ -9,29 +9,24 @@ path = "./src/main.rs"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
channels = { path = "../channels" }
|
channels = { path = "../channels" }
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0", features = ["serde"] }
|
||||||
config = { path = "../config" }
|
config = { path = "../config" }
|
||||||
db-utils = { path = "../db-utils" }
|
db-utils = { path = "../db-utils" }
|
||||||
derive_more = { version = "0.99", features = [] }
|
derive_more = { version = "0", features = [] }
|
||||||
dotenv = { version = "0.15.0" }
|
dotenv = { version = "0" }
|
||||||
futures = { version = "0.3.25" }
|
futures = { version = "0" }
|
||||||
model = { path = "../model", features = ["db"] }
|
model = { path = "../model", features = ["db"] }
|
||||||
opentelemetry = { version = "0.17.0" }
|
|
||||||
opentelemetry-jaeger = { version = "0.17.0" }
|
|
||||||
pretty_env_logger = { version = "0.4", features = [] }
|
|
||||||
rumqttc = { version = "*" }
|
rumqttc = { version = "*" }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
sqlx = { version = "0.6.2", features = ["migrate", "runtime-actix-rustls", "all-types", "postgres"] }
|
sqlx = { version = "0", features = ["migrate", "runtime-actix-rustls", "all-types", "postgres"] }
|
||||||
sqlx-core = { version = "0.6.2", features = [] }
|
sqlx-core = { version = "0", features = [] }
|
||||||
tarpc = { version = "0.31.0", features = ["tokio1", "serde-transport-bincode", "serde-transport", "serde", "serde-transport-json", "tcp"] }
|
tarpc = { version = "0", features = ["tokio1", "serde-transport-bincode", "serde-transport", "serde", "serde-transport-json", "tcp"] }
|
||||||
thiserror = { version = "1.0.31" }
|
thiserror = { version = "1" }
|
||||||
tokio = { version = "1.21.2", features = ['full'] }
|
tokio = { version = "1", features = ['full'] }
|
||||||
tracing = { version = "0.1.6" }
|
tracing = { version = "0" }
|
||||||
tracing-opentelemetry = { version = "0.17.4" }
|
uuid = { version = "1", features = ['v4'] }
|
||||||
tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
|
|
||||||
uuid = { version = "1.2.1", features = ['v4'] }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
fakeit = { version = "1.1.1" }
|
fakeit = { version = "1" }
|
||||||
insta = { version = "1.21.0" }
|
insta = { version = "1" }
|
||||||
testx = { path = "../testx" }
|
testx = { path = "../testx" }
|
||||||
|
@ -14,6 +14,8 @@ impl UpdateConfig for Opts {}
|
|||||||
async fn main() {
|
async fn main() {
|
||||||
let opts = Opts {};
|
let opts = Opts {};
|
||||||
|
|
||||||
|
config::init_tracing("stocks");
|
||||||
|
|
||||||
let config = config::default_load(&opts);
|
let config = config::default_load(&opts);
|
||||||
|
|
||||||
let db = db::Database::build(config.clone()).await;
|
let db = db::Database::build(config.clone()).await;
|
||||||
|
@ -3,31 +3,36 @@ name = "token_manager"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = 'token-manager'
|
||||||
|
path = './src/main.rs'
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix = { version = "0.13", features = [] }
|
argon2 = { version = "0", features = ["parallel", "password-hash"] }
|
||||||
actix-rt = { version = "2.7", features = [] }
|
channels = { path = "../channels" }
|
||||||
argon2 = { version = "0.4", features = ["parallel", "password-hash"] }
|
chrono = { version = "0", features = ["serde"] }
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
|
||||||
config = { path = "../config" }
|
config = { path = "../config" }
|
||||||
database_manager = { path = "../database_manager" }
|
db-utils = { path = "../db-utils" }
|
||||||
derive_more = { version = "0.99", features = [] }
|
derive_more = { version = "0", features = [] }
|
||||||
futures = { version = "0.3", features = [] }
|
dotenv = { version = "0" }
|
||||||
futures-util = { version = "0.3", features = [] }
|
futures = { version = "0" }
|
||||||
hmac = { version = "0.12", features = [] }
|
futures-util = { version = "0", features = [] }
|
||||||
jwt = { version = "0.16", features = [] }
|
hmac = { version = "0", features = [] }
|
||||||
model = { path = "../model" }
|
jwt = { version = "0", features = [] }
|
||||||
parking_lot = { version = "0.12", features = [] }
|
model = { path = "../model", features = ["db"] }
|
||||||
password-hash = { version = "0.4", features = ["alloc"] }
|
password-hash = { version = "0", features = ["alloc"] }
|
||||||
pretty_env_logger = { version = "0.4", features = [] }
|
rand_core = { version = "0", features = ["std"] }
|
||||||
rand_core = { version = "0.6", features = ["std"] }
|
|
||||||
rumqttc = { version = "*" }
|
rumqttc = { version = "*" }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
sha2 = { version = "0.10", features = [] }
|
sha2 = { version = "0", features = [] }
|
||||||
thiserror = { version = "1.0.31" }
|
sqlx = { version = "0", features = ["migrate", "runtime-actix-rustls", "all-types", "postgres"] }
|
||||||
tokio = { version = "1.17", features = ["full"] }
|
sqlx-core = { version = "0", features = [] }
|
||||||
tracing = { version = "0.1.34" }
|
tarpc = { version = "0", features = ["tokio1", "serde-transport-bincode", "serde-transport", "serde", "serde-transport-json", "tcp"] }
|
||||||
uuid = { version = "1.2.1", features = ["serde"] }
|
thiserror = { version = "1" }
|
||||||
|
tokio = { version = "1", features = ['full'] }
|
||||||
|
tracing = { version = "0" }
|
||||||
|
uuid = { version = "1", features = ['v4'] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
fake = { version = "2.5.0" }
|
fake = { version = "2" }
|
||||||
testx = { path = "../testx" }
|
testx = { path = "../testx" }
|
||||||
|
27
crates/token_manager/migrations/202204131841_init.sql
Normal file
27
crates/token_manager/migrations/202204131841_init.sql
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||||
|
|
||||||
|
CREATE TYPE "Audience" AS ENUM (
|
||||||
|
'web',
|
||||||
|
'mobile',
|
||||||
|
'feed',
|
||||||
|
'admin_panel'
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TYPE "Role" AS ENUM (
|
||||||
|
'admin',
|
||||||
|
'user'
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE tokens
|
||||||
|
(
|
||||||
|
id integer NOT NULL,
|
||||||
|
customer_id uuid NOT NULL,
|
||||||
|
role "Role" NOT NULL,
|
||||||
|
issuer character varying DEFAULT 'bazzar'::character varying NOT NULL,
|
||||||
|
subject integer NOT NULL,
|
||||||
|
audience "Audience" DEFAULT 'web'::"Audience" NOT NULL,
|
||||||
|
expiration_time timestamp without time zone DEFAULT (now() + '14 days'::interval) NOT NULL,
|
||||||
|
not_before_time timestamp without time zone DEFAULT (now() - '00:01:00'::interval) NOT NULL,
|
||||||
|
issued_at_time timestamp without time zone DEFAULT now() NOT NULL,
|
||||||
|
jwt_id uuid DEFAULT gen_random_uuid() NOT NULL
|
||||||
|
);
|
0
crates/token_manager/src/context.rs
Normal file
0
crates/token_manager/src/context.rs
Normal file
28
crates/token_manager/src/db/mod.rs
Normal file
28
crates/token_manager/src/db/mod.rs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
use config::SharedAppConfig;
|
||||||
|
use sqlx_core::pool::Pool;
|
||||||
|
use sqlx_core::postgres::Postgres;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Database {
|
||||||
|
pub pool: sqlx::PgPool,
|
||||||
|
_config: SharedAppConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Database {
|
||||||
|
pub async fn build(config: SharedAppConfig) -> Self {
|
||||||
|
let url = config.lock().tokens().database_url.clone();
|
||||||
|
let pool = sqlx::PgPool::connect(&url).await.unwrap_or_else(|e| {
|
||||||
|
eprintln!("Failed to connect to database. {e:?}");
|
||||||
|
tracing::error!("Failed to connect to database. {e:?}");
|
||||||
|
std::process::exit(1);
|
||||||
|
});
|
||||||
|
Self {
|
||||||
|
pool,
|
||||||
|
_config: config,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pool(&self) -> Pool<Postgres> {
|
||||||
|
self.pool.clone()
|
||||||
|
}
|
||||||
|
}
|
@ -1,641 +0,0 @@
|
|||||||
//! Tokens management system.
|
|
||||||
//! It's responsible for creating and validating all tokens.
|
|
||||||
//!
|
|
||||||
//! Application flow goes like this:
|
|
||||||
//!
|
|
||||||
//! ```ascii
|
|
||||||
//! Client API TokenManager Database
|
|
||||||
//!
|
|
||||||
//! │ │ │ │
|
|
||||||
//! │ │ │ │
|
|
||||||
//! │ │ │ ┌───────────────►│
|
|
||||||
//! ├────────────────►├──────────────────►├──────►│ │
|
|
||||||
//! │ Sign In │ CreatePair │ └───────────────►│
|
|
||||||
//! │ │ │ Create │
|
|
||||||
//! │ │ │ * AccessToken │
|
|
||||||
//! │ │ │ * RefreshToken │
|
|
||||||
//! │ │ │ │
|
|
||||||
//! │ │ │ │
|
|
||||||
//!
|
|
||||||
//! │ │ │ │
|
|
||||||
//! ├────────────────►├──────────────────►├───────────────────────►│
|
|
||||||
//! │ Validate token │ ValidateToken │ Load token │
|
|
||||||
//! │ │ (string) │◄───────────────────────┤
|
|
||||||
//! │ │ │ │
|
|
||||||
//! │ │ Is Valid? │
|
|
||||||
//! │ │ │ │
|
|
||||||
//! │ │◄──────────────── YES │
|
|
||||||
//! │ │ AccessToken │ │
|
|
||||||
//! │ │ │ │
|
|
||||||
//! │ │ │ │
|
|
||||||
//! │ │◄──────────────── NO │
|
|
||||||
//! │ │ Error │ │
|
|
||||||
//!
|
|
||||||
//! │ │ │ │
|
|
||||||
//! │ │ │ │
|
|
||||||
//! │ │ │ ┌───────────────►│
|
|
||||||
//! ├────────────────►├──────────────────►├──────►│ │
|
|
||||||
//! │ Refresh token │ CreatePair │ └───────────────►│
|
|
||||||
//! │ │ │ Create │
|
|
||||||
//! │ │◄──────────────────┤ * AccessToken │
|
|
||||||
//! │ │ Access Token │ * RefreshToken │
|
|
||||||
//! │ │ Refresh Token │ │
|
|
||||||
//! │ │ │ │
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! If you need to operate on tokens from API or any other actor you should
|
|
||||||
//! always use this actor and never touch database directly.
|
|
||||||
//!
|
|
||||||
//! # Examples
|
|
||||||
//!
|
|
||||||
//! ```
|
|
||||||
//! use actix::{Actor, Addr};
|
|
||||||
//! use config::SharedAppConfig;
|
|
||||||
//! use database_manager::Database;
|
|
||||||
//! use token_manager::*;
|
|
||||||
//! use model::*;
|
|
||||||
//!
|
|
||||||
//! async fn tokens(db: Addr<Database>, config: SharedAppConfig) {
|
|
||||||
//! let manager = TokenManager::new(config, db);
|
|
||||||
//!
|
|
||||||
//! let manager_addr = manager.start();
|
|
||||||
//!
|
|
||||||
//! let AuthPair { access_token, access_token_string, refresh_token_string, .. } = manager_addr.send(CreatePair {
|
|
||||||
//! customer_id: uuid::Uuid::new_v4(),
|
|
||||||
//! account_id: AccountId::from(0),
|
|
||||||
//! role: Role::Admin
|
|
||||||
//! }).await.unwrap().unwrap();
|
|
||||||
//!
|
|
||||||
//! manager_addr.send(Validate { token: access_token_string }).await.unwrap().unwrap();
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use actix::{Addr, Message};
|
|
||||||
use chrono::prelude::*;
|
|
||||||
use config::SharedAppConfig;
|
|
||||||
use database_manager::{query_db, Database};
|
|
||||||
use hmac::digest::KeyInit;
|
|
||||||
use hmac::Hmac;
|
|
||||||
use model::{AccessTokenString, AccountId, Audience, Role, Token};
|
|
||||||
use sha2::Sha256;
|
|
||||||
|
|
||||||
#[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 config = self.config.clone();
|
|
||||||
Box::pin(async { $async(msg, db, config).await }.into_actor(self))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! query_tm {
|
|
||||||
($tm: expr, $msg: expr, default $fail: expr) => {
|
|
||||||
match $tm.send($msg).await {
|
|
||||||
Ok(Ok(r)) => r,
|
|
||||||
Ok(Err(e)) => {
|
|
||||||
tracing::error!("{e}");
|
|
||||||
$fail
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
tracing::error!("{e:?}");
|
|
||||||
$fail
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
(multi, $tm: expr, $fail: expr, $($msg: expr),*) => {{
|
|
||||||
use futures_util::TryFutureExt;
|
|
||||||
tokio::join!(
|
|
||||||
$(
|
|
||||||
$tm.send($msg).map_ok_or_else(
|
|
||||||
|e| {
|
|
||||||
tracing::error!("{e:?}");
|
|
||||||
Err($fail)
|
|
||||||
},
|
|
||||||
|res| match res {
|
|
||||||
Ok(rec) => Ok(rec),
|
|
||||||
Err(e) => {
|
|
||||||
tracing::error!("{e}");
|
|
||||||
Err($fail)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
),*
|
|
||||||
)
|
|
||||||
}};
|
|
||||||
|
|
||||||
($tm: expr, $msg: expr, $fail: expr) => {
|
|
||||||
$crate::query_tm!($tm, $msg, $fail, $fail)
|
|
||||||
};
|
|
||||||
|
|
||||||
($tm: expr, $msg: expr, $db_fail: expr, $act_fail: expr) => {
|
|
||||||
match $tm.send($msg).await {
|
|
||||||
Ok(Ok(r)) => r,
|
|
||||||
Ok(Err(e)) => {
|
|
||||||
tracing::error!("{e}");
|
|
||||||
return Err($db_fail);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
tracing::error!("{e:?}");
|
|
||||||
return Err($act_fail);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/*struct Jwt {
|
|
||||||
/// cti (customer id): Customer uuid identifier used by payment service
|
|
||||||
pub cti: uuid::Uuid,
|
|
||||||
/// arl (account role): account role
|
|
||||||
pub arl: Role,
|
|
||||||
/// iss (issuer): Issuer of the JWT
|
|
||||||
pub iss: String,
|
|
||||||
/// sub (subject): Subject of the JWT (the user)
|
|
||||||
pub sub: i32,
|
|
||||||
/// aud (audience): Recipient for which the JWT is intended
|
|
||||||
pub aud: Audience,
|
|
||||||
/// exp (expiration time): Time after which the JWT expires
|
|
||||||
pub exp: chrono::NaiveDateTime,
|
|
||||||
/// nbt (not before time): Time before which the JWT must not be accepted
|
|
||||||
/// for processing
|
|
||||||
pub nbt: chrono::NaiveDateTime,
|
|
||||||
/// iat (issued at time): Time at which the JWT was issued; can be used to
|
|
||||||
/// determine age of the JWT,
|
|
||||||
pub iat: 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 jti: uuid::Uuid,
|
|
||||||
}*/
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, serde::Serialize, thiserror::Error)]
|
|
||||||
#[serde(rename_all = "kebab-case", tag = "token")]
|
|
||||||
pub enum Error {
|
|
||||||
#[error("Unable to save new token")]
|
|
||||||
Save,
|
|
||||||
#[error("Unable to save new token. Can't connect to database")]
|
|
||||||
SaveInternal,
|
|
||||||
#[error("Unable to validate token")]
|
|
||||||
Validate,
|
|
||||||
#[error("Unable to validate token. Can't connect to database")]
|
|
||||||
ValidateInternal,
|
|
||||||
#[error("Token does not exists or some fields are incorrect")]
|
|
||||||
Invalid,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
|
||||||
|
|
||||||
pub struct TokenManager {
|
|
||||||
db: Addr<Database>,
|
|
||||||
config: SharedAppConfig,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl actix::Actor for TokenManager {
|
|
||||||
type Context = actix::Context<Self>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TokenManager {
|
|
||||||
pub fn new(config: SharedAppConfig, db: Addr<Database>) -> Self {
|
|
||||||
Self { db, config }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates single token, it's mostly used by [CreatePair]
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use actix::Addr;
|
|
||||||
/// use model::{AccountId, Role};
|
|
||||||
/// use token_manager::*;
|
|
||||||
/// async fn create_pair(token_manager: Addr<token_manager::TokenManager>) {
|
|
||||||
/// match token_manager.send(CreateToken { customer_id: uuid::Uuid::new_v4(), role: Role::Admin, subject: AccountId::from(1), audience: None, exp: None }).await {
|
|
||||||
/// Ok(Ok(pair)) => {}
|
|
||||||
/// Ok(Err(manager_error)) => {}
|
|
||||||
/// Err(actor_error) => {}
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
#[derive(Message)]
|
|
||||||
#[rtype(result = "Result<(Token, AccessTokenString)>")]
|
|
||||||
pub struct CreateToken {
|
|
||||||
pub customer_id: uuid::Uuid,
|
|
||||||
pub role: Role,
|
|
||||||
pub subject: AccountId,
|
|
||||||
pub audience: Option<Audience>,
|
|
||||||
pub exp: Option<NaiveDateTime>,
|
|
||||||
}
|
|
||||||
|
|
||||||
token_async_handler!(CreateToken, create_token, (Token, AccessTokenString));
|
|
||||||
|
|
||||||
pub(crate) async fn create_token(
|
|
||||||
msg: CreateToken,
|
|
||||||
db: Addr<Database>,
|
|
||||||
config: SharedAppConfig,
|
|
||||||
) -> Result<(Token, AccessTokenString)> {
|
|
||||||
let CreateToken {
|
|
||||||
customer_id,
|
|
||||||
role,
|
|
||||||
subject,
|
|
||||||
audience,
|
|
||||||
exp,
|
|
||||||
} = msg;
|
|
||||||
let audience = audience.unwrap_or_default();
|
|
||||||
|
|
||||||
let token: Token = match exp {
|
|
||||||
None => query_db!(
|
|
||||||
db,
|
|
||||||
database_manager::CreateToken {
|
|
||||||
customer_id,
|
|
||||||
role,
|
|
||||||
subject,
|
|
||||||
audience,
|
|
||||||
},
|
|
||||||
Error::Save,
|
|
||||||
Error::SaveInternal
|
|
||||||
),
|
|
||||||
Some(exp) => query_db!(
|
|
||||||
db,
|
|
||||||
database_manager::CreateExtendedToken {
|
|
||||||
customer_id,
|
|
||||||
role,
|
|
||||||
subject,
|
|
||||||
audience,
|
|
||||||
expiration_time: exp
|
|
||||||
},
|
|
||||||
Error::Save,
|
|
||||||
Error::SaveInternal
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
let token_string = {
|
|
||||||
use jwt::SignWithKey;
|
|
||||||
|
|
||||||
let secret = config.lock().web().jwt_secret();
|
|
||||||
let key: Hmac<Sha256> = build_key(secret)?;
|
|
||||||
let mut claims = BTreeMap::new();
|
|
||||||
|
|
||||||
// cti (customer id): Customer uuid identifier used by payment service
|
|
||||||
claims.insert("cti", format!("{}", token.customer_id));
|
|
||||||
// arl (account role): account role
|
|
||||||
claims.insert("arl", String::from(token.role.as_str()));
|
|
||||||
// iss (issuer): Issuer of the JWT
|
|
||||||
claims.insert("iss", token.issuer.to_string());
|
|
||||||
// sub (subject): Subject of the JWT (the user)
|
|
||||||
claims.insert("sub", format!("{}", token.subject));
|
|
||||||
// aud (audience): Recipient for which the JWT is intended
|
|
||||||
claims.insert("aud", String::from(token.audience.as_str()));
|
|
||||||
// exp (expiration time): Time after which the JWT expires
|
|
||||||
claims.insert(
|
|
||||||
"exp",
|
|
||||||
format!(
|
|
||||||
"{}",
|
|
||||||
Utc.from_utc_datetime(&token.expiration_time).format("%+")
|
|
||||||
),
|
|
||||||
);
|
|
||||||
// nbt (not before time): Time before which the JWT must not be accepted
|
|
||||||
// for processing
|
|
||||||
claims.insert(
|
|
||||||
"nbt",
|
|
||||||
format!(
|
|
||||||
"{}",
|
|
||||||
Utc.from_utc_datetime(&token.not_before_time).format("%+")
|
|
||||||
),
|
|
||||||
);
|
|
||||||
// iat (issued at time): Time at which the JWT was issued; can be used
|
|
||||||
// to determine age of the JWT,
|
|
||||||
claims.insert(
|
|
||||||
"iat",
|
|
||||||
format!(
|
|
||||||
"{}",
|
|
||||||
Utc.from_utc_datetime(&token.issued_at_time).format("%+")
|
|
||||||
),
|
|
||||||
);
|
|
||||||
// jti (JWT ID): Unique identifier; can be used to prevent the JWT from
|
|
||||||
// being replayed (allows a token to be used only once)
|
|
||||||
claims.insert("jti", format!("{}", token.jwt_id));
|
|
||||||
|
|
||||||
let s = match claims.sign_with_key(&key) {
|
|
||||||
Ok(s) => s,
|
|
||||||
Err(e) => {
|
|
||||||
tracing::error!("{e:?}");
|
|
||||||
return Err(Error::SaveInternal);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
AccessTokenString::new(s)
|
|
||||||
};
|
|
||||||
Ok((token, token_string))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct AuthPair {
|
|
||||||
pub access_token: Token,
|
|
||||||
pub access_token_string: AccessTokenString,
|
|
||||||
pub _refresh_token: Token,
|
|
||||||
pub refresh_token_string: model::RefreshTokenString,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates access token and refresh token
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use actix::Addr;
|
|
||||||
/// use model::{AccountId, Role};
|
|
||||||
/// use token_manager::CreatePair;
|
|
||||||
/// async fn create_pair(token_manager: Addr<token_manager::TokenManager>) {
|
|
||||||
/// match token_manager.send(CreatePair { customer_id: uuid::Uuid::new_v4(), account_id: AccountId::from(0), role: Role::Admin }).await {
|
|
||||||
/// Ok(Ok(pair)) => {}
|
|
||||||
/// Ok(Err(manager_error)) => {}
|
|
||||||
/// Err(actor_error) => {}
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
#[derive(Message)]
|
|
||||||
#[rtype(result = "Result<AuthPair>")]
|
|
||||||
pub struct CreatePair {
|
|
||||||
pub customer_id: uuid::Uuid,
|
|
||||||
pub role: Role,
|
|
||||||
pub account_id: AccountId,
|
|
||||||
}
|
|
||||||
|
|
||||||
token_async_handler!(CreatePair, create_pair, AuthPair);
|
|
||||||
|
|
||||||
pub(crate) async fn create_pair(
|
|
||||||
msg: CreatePair,
|
|
||||||
db: Addr<Database>,
|
|
||||||
config: SharedAppConfig,
|
|
||||||
) -> Result<AuthPair> {
|
|
||||||
let (access_token, refresh_token) = tokio::join!(
|
|
||||||
create_token(
|
|
||||||
CreateToken {
|
|
||||||
customer_id: msg.customer_id,
|
|
||||||
role: msg.role,
|
|
||||||
subject: msg.account_id,
|
|
||||||
audience: Some(model::Audience::Web),
|
|
||||||
exp: None
|
|
||||||
},
|
|
||||||
db.clone(),
|
|
||||||
config.clone()
|
|
||||||
),
|
|
||||||
create_token(
|
|
||||||
CreateToken {
|
|
||||||
customer_id: msg.customer_id,
|
|
||||||
role: msg.role,
|
|
||||||
subject: msg.account_id,
|
|
||||||
audience: Some(model::Audience::Web),
|
|
||||||
exp: Some((chrono::Utc::now() + chrono::Duration::days(31)).naive_utc())
|
|
||||||
},
|
|
||||||
db.clone(),
|
|
||||||
config.clone()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
let (access_token, access_token_string): (Token, AccessTokenString) = access_token?;
|
|
||||||
let (refresh_token, refresh_token_string): (Token, AccessTokenString) = refresh_token?;
|
|
||||||
Ok(AuthPair {
|
|
||||||
access_token,
|
|
||||||
access_token_string,
|
|
||||||
_refresh_token: refresh_token,
|
|
||||||
refresh_token_string: refresh_token_string.into(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks if token is still valid
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use actix::Addr;
|
|
||||||
/// use model::{AccessTokenString, AccountId, Role};
|
|
||||||
/// use token_manager::{CreatePair, Validate};
|
|
||||||
/// async fn create_pair(token_manager: Addr<token_manager::TokenManager>, token: AccessTokenString) {
|
|
||||||
/// match token_manager.send(Validate { token }).await {
|
|
||||||
/// Ok(Ok(pair)) => {}
|
|
||||||
/// Ok(Err(manager_error)) => {}
|
|
||||||
/// Err(actor_error) => {}
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
#[derive(Message)]
|
|
||||||
#[rtype(result = "Result<Token>")]
|
|
||||||
pub struct Validate {
|
|
||||||
pub token: AccessTokenString,
|
|
||||||
}
|
|
||||||
|
|
||||||
token_async_handler!(Validate, validate, Token);
|
|
||||||
|
|
||||||
pub(crate) async fn validate(
|
|
||||||
msg: Validate,
|
|
||||||
db: Addr<Database>,
|
|
||||||
config: SharedAppConfig,
|
|
||||||
) -> Result<Token> {
|
|
||||||
use jwt::VerifyWithKey;
|
|
||||||
|
|
||||||
tracing::info!("Validating token {:?}", msg.token);
|
|
||||||
|
|
||||||
let secret = config.lock().web().jwt_secret();
|
|
||||||
let key: Hmac<Sha256> = build_key(secret)?;
|
|
||||||
let claims: BTreeMap<String, String> = match msg.token.verify_with_key(&key) {
|
|
||||||
Ok(claims) => claims,
|
|
||||||
_ => return Err(Error::Validate),
|
|
||||||
};
|
|
||||||
let jti = match claims.get("jti") {
|
|
||||||
Some(jti) => jti,
|
|
||||||
_ => return Err(Error::Validate),
|
|
||||||
};
|
|
||||||
|
|
||||||
let token: Token = query_db!(
|
|
||||||
db,
|
|
||||||
database_manager::TokenByJti {
|
|
||||||
jti: match uuid::Uuid::from_str(jti) {
|
|
||||||
Ok(uid) => uid,
|
|
||||||
_ => return Err(Error::Validate),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Error::Validate,
|
|
||||||
Error::ValidateInternal
|
|
||||||
);
|
|
||||||
|
|
||||||
if token.expiration_time < Utc::now().naive_utc() {
|
|
||||||
return Err(Error::Validate);
|
|
||||||
}
|
|
||||||
|
|
||||||
validate_pair(&claims, "cti", token.customer_id, validate_uuid)?;
|
|
||||||
validate_pair(&claims, "arl", token.role, eq)?;
|
|
||||||
validate_pair(&claims, "iss", &token.issuer, eq)?;
|
|
||||||
validate_pair(&claims, "sub", token.subject, validate_num)?;
|
|
||||||
validate_pair(&claims, "aud", token.audience, eq)?;
|
|
||||||
validate_pair(&claims, "exp", &token.expiration_time, validate_time)?;
|
|
||||||
validate_pair(&claims, "nbt", &token.not_before_time, validate_time)?;
|
|
||||||
validate_pair(&claims, "iat", &token.issued_at_time, validate_time)?;
|
|
||||||
|
|
||||||
tracing::info!("JWT token valid");
|
|
||||||
Ok(token)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_key(secret: String) -> Result<Hmac<Sha256>> {
|
|
||||||
match Hmac::new_from_slice(secret.as_bytes()) {
|
|
||||||
Ok(key) => Ok(key),
|
|
||||||
Err(e) => {
|
|
||||||
tracing::error!("{e:?}");
|
|
||||||
dbg!(e);
|
|
||||||
Err(Error::ValidateInternal)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn validate_pair<F, V>(
|
|
||||||
claims: &BTreeMap<String, String>,
|
|
||||||
key: &str,
|
|
||||||
v: V,
|
|
||||||
cmp: F,
|
|
||||||
) -> std::result::Result<(), Error>
|
|
||||||
where
|
|
||||||
F: for<'s> FnOnce(V, &'s str) -> bool,
|
|
||||||
V: PartialEq,
|
|
||||||
{
|
|
||||||
claims
|
|
||||||
.get(key)
|
|
||||||
.map(|s| cmp(v, s.as_str()))
|
|
||||||
.unwrap_or_default()
|
|
||||||
.then_some(())
|
|
||||||
.ok_or(Error::Invalid)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn eq<V>(value: V, text: &str) -> bool
|
|
||||||
where
|
|
||||||
V: for<'s> PartialEq<&'s str>,
|
|
||||||
{
|
|
||||||
value == text
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn validate_time(left: &NaiveDateTime, right: &str) -> bool {
|
|
||||||
chrono::DateTime::parse_from_str(right, "%+")
|
|
||||||
.map(|t| t.naive_utc() == *left)
|
|
||||||
.unwrap_or_default()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn validate_num(left: i32, right: &str) -> bool {
|
|
||||||
right.parse::<i32>().map(|n| left == n).unwrap_or_default()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn validate_uuid(left: uuid::Uuid, right: &str) -> bool {
|
|
||||||
uuid::Uuid::from_str(right)
|
|
||||||
.map(|u| u == left)
|
|
||||||
.unwrap_or_default()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use actix::Actor;
|
|
||||||
use config::UpdateConfig;
|
|
||||||
use database_manager::Database;
|
|
||||||
use model::*;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
pub struct NoOpts;
|
|
||||||
|
|
||||||
impl UpdateConfig for NoOpts {}
|
|
||||||
|
|
||||||
#[actix::test]
|
|
||||||
async fn create_token() {
|
|
||||||
testx::db!(config, db);
|
|
||||||
let db = db.start();
|
|
||||||
|
|
||||||
let (token, _text) = super::create_token(
|
|
||||||
CreateToken {
|
|
||||||
customer_id: Default::default(),
|
|
||||||
role: Role::Admin,
|
|
||||||
subject: AccountId::from(1),
|
|
||||||
audience: None,
|
|
||||||
exp: None,
|
|
||||||
},
|
|
||||||
db.clone(),
|
|
||||||
config,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
db.send(database_manager::DeleteToken { token_id: token.id })
|
|
||||||
.await
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[actix::test]
|
|
||||||
async fn create_pair() {
|
|
||||||
testx::db!(config, db);
|
|
||||||
let db = db.start();
|
|
||||||
|
|
||||||
let AuthPair {
|
|
||||||
access_token,
|
|
||||||
access_token_string: _,
|
|
||||||
refresh_token_string: _,
|
|
||||||
_refresh_token,
|
|
||||||
} = super::create_pair(
|
|
||||||
CreatePair {
|
|
||||||
customer_id: Default::default(),
|
|
||||||
role: Role::Admin,
|
|
||||||
account_id: AccountId::from(0),
|
|
||||||
},
|
|
||||||
db.clone(),
|
|
||||||
config,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
db.send(database_manager::DeleteToken {
|
|
||||||
token_id: access_token.id,
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.ok();
|
|
||||||
|
|
||||||
db.send(database_manager::DeleteToken {
|
|
||||||
token_id: _refresh_token.id,
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[actix::test]
|
|
||||||
async fn validate() {
|
|
||||||
testx::db!(config, db);
|
|
||||||
let db = db.start();
|
|
||||||
|
|
||||||
let (token, text) = super::create_token(
|
|
||||||
CreateToken {
|
|
||||||
customer_id: Default::default(),
|
|
||||||
role: Role::Admin,
|
|
||||||
subject: AccountId::from(1),
|
|
||||||
audience: None,
|
|
||||||
exp: None,
|
|
||||||
},
|
|
||||||
db.clone(),
|
|
||||||
config.clone(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
super::validate(Validate { token: text }, db.clone(), config.clone())
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
db.send(database_manager::DeleteToken { token_id: token.id })
|
|
||||||
.await
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
}
|
|
570
crates/token_manager/src/main.rs
Normal file
570
crates/token_manager/src/main.rs
Normal file
@ -0,0 +1,570 @@
|
|||||||
|
//! Tokens management system.
|
||||||
|
//! It's responsible for creating and validating all tokens.
|
||||||
|
//!
|
||||||
|
//! Application flow goes like this:
|
||||||
|
//!
|
||||||
|
//! ```ascii
|
||||||
|
//! Client API TokenManager Database
|
||||||
|
//!
|
||||||
|
//! │ │ │ │
|
||||||
|
//! │ │ │ │
|
||||||
|
//! │ │ │ ┌───────────────►│
|
||||||
|
//! ├────────────────►├──────────────────►├──────►│ │
|
||||||
|
//! │ Sign In │ CreatePair │ └───────────────►│
|
||||||
|
//! │ │ │ Create │
|
||||||
|
//! │ │ │ * AccessToken │
|
||||||
|
//! │ │ │ * RefreshToken │
|
||||||
|
//! │ │ │ │
|
||||||
|
//! │ │ │ │
|
||||||
|
//!
|
||||||
|
//! │ │ │ │
|
||||||
|
//! ├────────────────►├──────────────────►├───────────────────────►│
|
||||||
|
//! │ Validate token │ ValidateToken │ Load token │
|
||||||
|
//! │ │ (string) │◄───────────────────────┤
|
||||||
|
//! │ │ │ │
|
||||||
|
//! │ │ Is Valid? │
|
||||||
|
//! │ │ │ │
|
||||||
|
//! │ │◄──────────────── YES │
|
||||||
|
//! │ │ AccessToken │ │
|
||||||
|
//! │ │ │ │
|
||||||
|
//! │ │ │ │
|
||||||
|
//! │ │◄──────────────── NO │
|
||||||
|
//! │ │ Error │ │
|
||||||
|
//!
|
||||||
|
//! │ │ │ │
|
||||||
|
//! │ │ │ │
|
||||||
|
//! │ │ │ ┌───────────────►│
|
||||||
|
//! ├────────────────►├──────────────────►├──────►│ │
|
||||||
|
//! │ Refresh token │ CreatePair │ └───────────────►│
|
||||||
|
//! │ │ │ Create │
|
||||||
|
//! │ │◄──────────────────┤ * AccessToken │
|
||||||
|
//! │ │ Access Token │ * RefreshToken │
|
||||||
|
//! │ │ Refresh Token │ │
|
||||||
|
//! │ │ │ │
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! If you need to operate on tokens from API or any other actor you should
|
||||||
|
//! always use this actor and never touch database directly.
|
||||||
|
//!
|
||||||
|
//! # Examples
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! use actix::{Actor, Addr};
|
||||||
|
//! use config::SharedAppConfig;
|
||||||
|
//! use database_manager::Database;
|
||||||
|
//! use token_manager::*;
|
||||||
|
//! use model::*;
|
||||||
|
//!
|
||||||
|
//! async fn tokens(db: Addr<Database>, config: SharedAppConfig) {
|
||||||
|
//! let manager = TokenManager::new(config, db);
|
||||||
|
//!
|
||||||
|
//! let manager_addr = manager.start();
|
||||||
|
//!
|
||||||
|
//! let AuthPair { access_token, access_token_string, refresh_token_string, .. } = manager_addr.send(CreatePair {
|
||||||
|
//! customer_id: uuid::Uuid::new_v4(),
|
||||||
|
//! account_id: AccountId::from(0),
|
||||||
|
//! role: Role::Admin
|
||||||
|
//! }).await.unwrap().unwrap();
|
||||||
|
//!
|
||||||
|
//! manager_addr.send(Validate { token: access_token_string }).await.unwrap().unwrap();
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
mod db;
|
||||||
|
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use channels::payments::CreatePayment;
|
||||||
|
use chrono::prelude::*;
|
||||||
|
use config::SharedAppConfig;
|
||||||
|
use hmac::digest::KeyInit;
|
||||||
|
use hmac::Hmac;
|
||||||
|
use model::{AccessTokenString, AccountId, Audience, Role, Token};
|
||||||
|
use sha2::Sha256;
|
||||||
|
|
||||||
|
use crate::db::Database;
|
||||||
|
|
||||||
|
/*struct Jwt {
|
||||||
|
/// cti (customer id): Customer uuid identifier used by payment service
|
||||||
|
pub cti: uuid::Uuid,
|
||||||
|
/// arl (account role): account role
|
||||||
|
pub arl: Role,
|
||||||
|
/// iss (issuer): Issuer of the JWT
|
||||||
|
pub iss: String,
|
||||||
|
/// sub (subject): Subject of the JWT (the user)
|
||||||
|
pub sub: i32,
|
||||||
|
/// aud (audience): Recipient for which the JWT is intended
|
||||||
|
pub aud: Audience,
|
||||||
|
/// exp (expiration time): Time after which the JWT expires
|
||||||
|
pub exp: chrono::NaiveDateTime,
|
||||||
|
/// nbt (not before time): Time before which the JWT must not be accepted
|
||||||
|
/// for processing
|
||||||
|
pub nbt: chrono::NaiveDateTime,
|
||||||
|
/// iat (issued at time): Time at which the JWT was issued; can be used to
|
||||||
|
/// determine age of the JWT,
|
||||||
|
pub iat: 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 jti: uuid::Uuid,
|
||||||
|
}*/
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, serde::Serialize, thiserror::Error)]
|
||||||
|
#[serde(rename_all = "kebab-case", tag = "token")]
|
||||||
|
pub enum Error {
|
||||||
|
#[error("Unable to save new token")]
|
||||||
|
Save,
|
||||||
|
#[error("Unable to save new token. Can't connect to database")]
|
||||||
|
SaveInternal,
|
||||||
|
#[error("Unable to validate token")]
|
||||||
|
Validate,
|
||||||
|
#[error("Unable to validate token. Can't connect to database")]
|
||||||
|
ValidateInternal,
|
||||||
|
#[error("Token does not exists or some fields are incorrect")]
|
||||||
|
Invalid,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
|
pub struct TokenManager {
|
||||||
|
db: Database,
|
||||||
|
config: SharedAppConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TokenManager {
|
||||||
|
pub fn new(config: SharedAppConfig, db: Database) -> Self {
|
||||||
|
Self { db, config }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates single token, it's mostly used by [CreatePair]
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use actix::Addr;
|
||||||
|
/// use model::{AccountId, Role};
|
||||||
|
/// use token_manager::*;
|
||||||
|
/// async fn create_pair(token_manager: Addr<token_manager::TokenManager>) {
|
||||||
|
/// match token_manager.send(CreateToken { customer_id: uuid::Uuid::new_v4(), role: Role::Admin, subject: AccountId::from(1), audience: None, exp: None }).await {
|
||||||
|
/// Ok(Ok(pair)) => {}
|
||||||
|
/// Ok(Err(manager_error)) => {}
|
||||||
|
/// Err(actor_error) => {}
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct CreateToken {
|
||||||
|
pub customer_id: uuid::Uuid,
|
||||||
|
pub role: Role,
|
||||||
|
pub subject: AccountId,
|
||||||
|
pub audience: Option<Audience>,
|
||||||
|
pub exp: Option<NaiveDateTime>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CreateToken {
|
||||||
|
pub async fn run(
|
||||||
|
self,
|
||||||
|
db: Addr<Database>,
|
||||||
|
config: SharedAppConfig,
|
||||||
|
) -> Result<(Token, AccessTokenString)> {
|
||||||
|
let CreateToken {
|
||||||
|
customer_id,
|
||||||
|
role,
|
||||||
|
subject,
|
||||||
|
audience,
|
||||||
|
exp,
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
let audience = audience.unwrap_or_default();
|
||||||
|
|
||||||
|
let token: Token = match exp {
|
||||||
|
None => query_db!(
|
||||||
|
db,
|
||||||
|
database_manager::CreateToken {
|
||||||
|
customer_id,
|
||||||
|
role,
|
||||||
|
subject,
|
||||||
|
audience,
|
||||||
|
},
|
||||||
|
Error::Save,
|
||||||
|
Error::SaveInternal
|
||||||
|
),
|
||||||
|
Some(exp) => query_db!(
|
||||||
|
db,
|
||||||
|
database_manager::CreateExtendedToken {
|
||||||
|
customer_id,
|
||||||
|
role,
|
||||||
|
subject,
|
||||||
|
audience,
|
||||||
|
expiration_time: exp
|
||||||
|
},
|
||||||
|
Error::Save,
|
||||||
|
Error::SaveInternal
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
let token_string = {
|
||||||
|
use jwt::SignWithKey;
|
||||||
|
|
||||||
|
let secret = config.lock().web().jwt_secret();
|
||||||
|
let key: Hmac<Sha256> = build_key(secret)?;
|
||||||
|
let mut claims = BTreeMap::new();
|
||||||
|
|
||||||
|
// cti (customer id): Customer uuid identifier used by payment service
|
||||||
|
claims.insert("cti", format!("{}", token.customer_id));
|
||||||
|
// arl (account role): account role
|
||||||
|
claims.insert("arl", String::from(token.role.as_str()));
|
||||||
|
// iss (issuer): Issuer of the JWT
|
||||||
|
claims.insert("iss", token.issuer.to_string());
|
||||||
|
// sub (subject): Subject of the JWT (the user)
|
||||||
|
claims.insert("sub", format!("{}", token.subject));
|
||||||
|
// aud (audience): Recipient for which the JWT is intended
|
||||||
|
claims.insert("aud", String::from(token.audience.as_str()));
|
||||||
|
// exp (expiration time): Time after which the JWT expires
|
||||||
|
claims.insert(
|
||||||
|
"exp",
|
||||||
|
format!(
|
||||||
|
"{}",
|
||||||
|
Utc.from_utc_datetime(&token.expiration_time).format("%+")
|
||||||
|
),
|
||||||
|
);
|
||||||
|
// nbt (not before time): Time before which the JWT must not be accepted
|
||||||
|
// for processing
|
||||||
|
claims.insert(
|
||||||
|
"nbt",
|
||||||
|
format!(
|
||||||
|
"{}",
|
||||||
|
Utc.from_utc_datetime(&token.not_before_time).format("%+")
|
||||||
|
),
|
||||||
|
);
|
||||||
|
// iat (issued at time): Time at which the JWT was issued; can be used
|
||||||
|
// to determine age of the JWT,
|
||||||
|
claims.insert(
|
||||||
|
"iat",
|
||||||
|
format!(
|
||||||
|
"{}",
|
||||||
|
Utc.from_utc_datetime(&token.issued_at_time).format("%+")
|
||||||
|
),
|
||||||
|
);
|
||||||
|
// jti (JWT ID): Unique identifier; can be used to prevent the JWT from
|
||||||
|
// being replayed (allows a token to be used only once)
|
||||||
|
claims.insert("jti", format!("{}", token.jwt_id));
|
||||||
|
|
||||||
|
let s = match claims.sign_with_key(&key) {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(e) => {
|
||||||
|
tracing::error!("{e:?}");
|
||||||
|
return Err(Error::SaveInternal);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
AccessTokenString::new(s)
|
||||||
|
};
|
||||||
|
Ok((token, token_string))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct AuthPair {
|
||||||
|
pub access_token: Token,
|
||||||
|
pub access_token_string: AccessTokenString,
|
||||||
|
pub _refresh_token: Token,
|
||||||
|
pub refresh_token_string: model::RefreshTokenString,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates access token and refresh token
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use actix::Addr;
|
||||||
|
/// use model::{AccountId, Role};
|
||||||
|
/// use token_manager::CreatePair;
|
||||||
|
/// async fn create_pair(token_manager: Addr<token_manager::TokenManager>) {
|
||||||
|
/// match token_manager.send(CreatePair { customer_id: uuid::Uuid::new_v4(), account_id: AccountId::from(0), role: Role::Admin }).await {
|
||||||
|
/// Ok(Ok(pair)) => {}
|
||||||
|
/// Ok(Err(manager_error)) => {}
|
||||||
|
/// Err(actor_error) => {}
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct CreatePair {
|
||||||
|
pub customer_id: uuid::Uuid,
|
||||||
|
pub role: Role,
|
||||||
|
pub account_id: AccountId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CreatePair {
|
||||||
|
pub async fn run(self, db: Database, config: SharedAppConfig) -> Result<AuthPair> {
|
||||||
|
let (access_token, refresh_token) = tokio::join!(
|
||||||
|
create_token(
|
||||||
|
CreateToken {
|
||||||
|
customer_id: self.customer_id,
|
||||||
|
role: self.role,
|
||||||
|
subject: self.account_id,
|
||||||
|
audience: Some(model::Audience::Web),
|
||||||
|
exp: None
|
||||||
|
},
|
||||||
|
db.clone(),
|
||||||
|
config.clone()
|
||||||
|
),
|
||||||
|
create_token(
|
||||||
|
CreateToken {
|
||||||
|
customer_id: self.customer_id,
|
||||||
|
role: self.role,
|
||||||
|
subject: self.account_id,
|
||||||
|
audience: Some(model::Audience::Web),
|
||||||
|
exp: Some((chrono::Utc::now() + chrono::Duration::days(31)).naive_utc())
|
||||||
|
},
|
||||||
|
db.clone(),
|
||||||
|
config.clone()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
let (access_token, access_token_string): (Token, AccessTokenString) = access_token?;
|
||||||
|
let (refresh_token, refresh_token_string): (Token, AccessTokenString) = refresh_token?;
|
||||||
|
Ok(AuthPair {
|
||||||
|
access_token,
|
||||||
|
access_token_string,
|
||||||
|
_refresh_token: refresh_token,
|
||||||
|
refresh_token_string: refresh_token_string.into(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if token is still valid
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use actix::Addr;
|
||||||
|
/// use model::{AccessTokenString, AccountId, Role};
|
||||||
|
/// use token_manager::{CreatePair, Validate};
|
||||||
|
/// async fn create_pair(token_manager: Addr<token_manager::TokenManager>, token: AccessTokenString) {
|
||||||
|
/// match token_manager.send(Validate { token }).await {
|
||||||
|
/// Ok(Ok(pair)) => {}
|
||||||
|
/// Ok(Err(manager_error)) => {}
|
||||||
|
/// Err(actor_error) => {}
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Validate {
|
||||||
|
pub token: AccessTokenString,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Validate {
|
||||||
|
pub async fn run(self, db: Database, config: SharedAppConfig) -> Result<Token> {
|
||||||
|
use jwt::VerifyWithKey;
|
||||||
|
|
||||||
|
tracing::info!("Validating token {:?}", self.token);
|
||||||
|
|
||||||
|
let secret = config.lock().web().jwt_secret();
|
||||||
|
let key: Hmac<Sha256> = build_key(secret)?;
|
||||||
|
let claims: BTreeMap<String, String> = match self.token.verify_with_key(&key) {
|
||||||
|
Ok(claims) => claims,
|
||||||
|
_ => return Err(Error::Validate),
|
||||||
|
};
|
||||||
|
let jti = match claims.get("jti") {
|
||||||
|
Some(jti) => jti,
|
||||||
|
_ => return Err(Error::Validate),
|
||||||
|
};
|
||||||
|
|
||||||
|
let token: Token = query_db!(
|
||||||
|
db,
|
||||||
|
database_manager::TokenByJti {
|
||||||
|
jti: match uuid::Uuid::from_str(jti) {
|
||||||
|
Ok(uid) => uid,
|
||||||
|
_ => return Err(Error::Validate),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Error::Validate,
|
||||||
|
Error::ValidateInternal
|
||||||
|
);
|
||||||
|
|
||||||
|
if token.expiration_time < Utc::now().naive_utc() {
|
||||||
|
return Err(Error::Validate);
|
||||||
|
}
|
||||||
|
|
||||||
|
validate_pair(&claims, "cti", token.customer_id, validate_uuid)?;
|
||||||
|
validate_pair(&claims, "arl", token.role, eq)?;
|
||||||
|
validate_pair(&claims, "iss", &token.issuer, eq)?;
|
||||||
|
validate_pair(&claims, "sub", token.subject, validate_num)?;
|
||||||
|
validate_pair(&claims, "aud", token.audience, eq)?;
|
||||||
|
validate_pair(&claims, "exp", &token.expiration_time, validate_time)?;
|
||||||
|
validate_pair(&claims, "nbt", &token.not_before_time, validate_time)?;
|
||||||
|
validate_pair(&claims, "iat", &token.issued_at_time, validate_time)?;
|
||||||
|
|
||||||
|
tracing::info!("JWT token valid");
|
||||||
|
Ok(token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Refresh {}
|
||||||
|
|
||||||
|
impl Refresh {
|
||||||
|
pub async fn run(self, db: Database, config: SharedAppConfig) -> Result<AuthPair> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_key(secret: String) -> Result<Hmac<Sha256>> {
|
||||||
|
match Hmac::new_from_slice(secret.as_bytes()) {
|
||||||
|
Ok(key) => Ok(key),
|
||||||
|
Err(e) => {
|
||||||
|
tracing::error!("{e:?}");
|
||||||
|
dbg!(e);
|
||||||
|
Err(Error::ValidateInternal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn validate_pair<F, V>(
|
||||||
|
claims: &BTreeMap<String, String>,
|
||||||
|
key: &str,
|
||||||
|
v: V,
|
||||||
|
cmp: F,
|
||||||
|
) -> std::result::Result<(), Error>
|
||||||
|
where
|
||||||
|
F: for<'s> FnOnce(V, &'s str) -> bool,
|
||||||
|
V: PartialEq,
|
||||||
|
{
|
||||||
|
claims
|
||||||
|
.get(key)
|
||||||
|
.map(|s| cmp(v, s.as_str()))
|
||||||
|
.unwrap_or_default()
|
||||||
|
.then_some(())
|
||||||
|
.ok_or(Error::Invalid)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn eq<V>(value: V, text: &str) -> bool
|
||||||
|
where
|
||||||
|
V: for<'s> PartialEq<&'s str>,
|
||||||
|
{
|
||||||
|
value == text
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn validate_time(left: &NaiveDateTime, right: &str) -> bool {
|
||||||
|
chrono::DateTime::parse_from_str(right, "%+")
|
||||||
|
.map(|t| t.naive_utc() == *left)
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn validate_num(left: i32, right: &str) -> bool {
|
||||||
|
right.parse::<i32>().map(|n| left == n).unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn validate_uuid(left: uuid::Uuid, right: &str) -> bool {
|
||||||
|
uuid::Uuid::from_str(right)
|
||||||
|
.map(|u| u == left)
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
// #[cfg(test)]
|
||||||
|
// mod tests {
|
||||||
|
// use actix::Actor;
|
||||||
|
// use config::UpdateConfig;
|
||||||
|
// use database_manager::Database;
|
||||||
|
// use model::*;
|
||||||
|
//
|
||||||
|
// use super::*;
|
||||||
|
//
|
||||||
|
// pub struct NoOpts;
|
||||||
|
//
|
||||||
|
// impl UpdateConfig for NoOpts {}
|
||||||
|
//
|
||||||
|
// #[actix::test]
|
||||||
|
// async fn create_token() {
|
||||||
|
// testx::db!(config, db);
|
||||||
|
// let db = db.start();
|
||||||
|
//
|
||||||
|
// let (token, _text) = super::create_token(
|
||||||
|
// CreateToken {
|
||||||
|
// customer_id: Default::default(),
|
||||||
|
// role: Role::Admin,
|
||||||
|
// subject: AccountId::from(1),
|
||||||
|
// audience: None,
|
||||||
|
// exp: None,
|
||||||
|
// },
|
||||||
|
// db.clone(),
|
||||||
|
// config,
|
||||||
|
// )
|
||||||
|
// .await
|
||||||
|
// .unwrap();
|
||||||
|
//
|
||||||
|
// db.send(database_manager::DeleteToken { token_id: token.id })
|
||||||
|
// .await
|
||||||
|
// .ok();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// #[actix::test]
|
||||||
|
// async fn create_pair() {
|
||||||
|
// testx::db!(config, db);
|
||||||
|
// let db = db.start();
|
||||||
|
//
|
||||||
|
// let AuthPair {
|
||||||
|
// access_token,
|
||||||
|
// access_token_string: _,
|
||||||
|
// refresh_token_string: _,
|
||||||
|
// _refresh_token,
|
||||||
|
// } = super::create_pair(
|
||||||
|
// CreatePair {
|
||||||
|
// customer_id: Default::default(),
|
||||||
|
// role: Role::Admin,
|
||||||
|
// account_id: AccountId::from(0),
|
||||||
|
// },
|
||||||
|
// db.clone(),
|
||||||
|
// config,
|
||||||
|
// )
|
||||||
|
// .await
|
||||||
|
// .unwrap();
|
||||||
|
//
|
||||||
|
// db.send(database_manager::DeleteToken {
|
||||||
|
// token_id: access_token.id,
|
||||||
|
// })
|
||||||
|
// .await
|
||||||
|
// .ok();
|
||||||
|
//
|
||||||
|
// db.send(database_manager::DeleteToken {
|
||||||
|
// token_id: _refresh_token.id,
|
||||||
|
// })
|
||||||
|
// .await
|
||||||
|
// .ok();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// #[actix::test]
|
||||||
|
// async fn validate() {
|
||||||
|
// testx::db!(config, db);
|
||||||
|
// let db = db.start();
|
||||||
|
//
|
||||||
|
// let (token, text) = super::create_token(
|
||||||
|
// CreateToken {
|
||||||
|
// customer_id: Default::default(),
|
||||||
|
// role: Role::Admin,
|
||||||
|
// subject: AccountId::from(1),
|
||||||
|
// audience: None,
|
||||||
|
// exp: None,
|
||||||
|
// },
|
||||||
|
// db.clone(),
|
||||||
|
// config.clone(),
|
||||||
|
// )
|
||||||
|
// .await
|
||||||
|
// .unwrap();
|
||||||
|
//
|
||||||
|
// super::validate(Validate { token: text }, db.clone(), config.clone())
|
||||||
|
// .await
|
||||||
|
// .unwrap();
|
||||||
|
//
|
||||||
|
// db.send(database_manager::DeleteToken { token_id: token.id })
|
||||||
|
// .await
|
||||||
|
// .ok();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {}
|
@ -10,14 +10,14 @@ crate-type = ["cdylib"]
|
|||||||
chrono = { version = "*", features = ["wasm-bindgen", "wasmbind"] }
|
chrono = { version = "*", features = ["wasm-bindgen", "wasmbind"] }
|
||||||
gloo-timers = { version = "*", features = ["futures"] }
|
gloo-timers = { version = "*", features = ["futures"] }
|
||||||
indexmap = { version = "1", default-features = false, features = ["serde-1", "std"] }
|
indexmap = { version = "1", default-features = false, features = ["serde-1", "std"] }
|
||||||
js-sys = { version = "0.3.57", features = [] }
|
js-sys = { version = "0", features = [] }
|
||||||
model = { path = "../model", features = ["dummy"] }
|
model = { path = "../model", features = ["dummy"] }
|
||||||
rusty-money = { version = "0.4.1", features = ["iso"] }
|
rusty-money = { version = "0", features = ["iso"] }
|
||||||
seed = { version = "0.9.1", features = [] }
|
seed = { version = "0", features = [] }
|
||||||
serde = { version = "1.0.137", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde-wasm-bindgen = { version = "0.4.2" }
|
serde-wasm-bindgen = { version = "0" }
|
||||||
serde_json = { version = "1.0.81" }
|
serde_json = { version = "1" }
|
||||||
thiserror = { version = "1.0.31" }
|
thiserror = { version = "1" }
|
||||||
uuid = { version = "1.0.0", features = ["v4"] }
|
uuid = { version = "1", features = ["v4"] }
|
||||||
wasm-bindgen = { version = "0.2.80", features = ["default"] }
|
wasm-bindgen = { version = "0", features = ["default"] }
|
||||||
web-sys = { version = "0.3.57", features = ["Navigator"] }
|
web-sys = { version = "0", features = ["Navigator"] }
|
||||||
|
@ -8,6 +8,7 @@ then
|
|||||||
psql postgres postgres -c "DROP DATABASE ${DATABASE_NAME}_carts" || echo 0
|
psql postgres postgres -c "DROP DATABASE ${DATABASE_NAME}_carts" || echo 0
|
||||||
psql postgres postgres -c "DROP DATABASE ${DATABASE_NAME}_stocks" || echo 0
|
psql postgres postgres -c "DROP DATABASE ${DATABASE_NAME}_stocks" || echo 0
|
||||||
psql postgres postgres -c "DROP DATABASE ${DATABASE_NAME}_orders" || echo 0
|
psql postgres postgres -c "DROP DATABASE ${DATABASE_NAME}_orders" || echo 0
|
||||||
|
psql postgres postgres -c "DROP DATABASE ${DATABASE_NAME}_tokens" || echo 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
psql postgres postgres -c "CREATE DATABASE ${DATABASE_NAME}_accounts" || echo 0
|
psql postgres postgres -c "CREATE DATABASE ${DATABASE_NAME}_accounts" || echo 0
|
||||||
@ -21,3 +22,6 @@ sqlx migrate run -D "${STOCK_DATABASE_URL}" --source ./crates/stock_manager/migr
|
|||||||
|
|
||||||
psql postgres postgres -c "CREATE DATABASE ${DATABASE_NAME}_orders" || echo 0
|
psql postgres postgres -c "CREATE DATABASE ${DATABASE_NAME}_orders" || echo 0
|
||||||
sqlx migrate run -D "${ORDER_DATABASE_URL}" --source ./crates/order_manager/migrations
|
sqlx migrate run -D "${ORDER_DATABASE_URL}" --source ./crates/order_manager/migrations
|
||||||
|
|
||||||
|
psql postgres postgres -c "CREATE DATABASE ${DATABASE_NAME}_tokens" || echo 0
|
||||||
|
sqlx migrate run -D "${TOKEN_DATABASE_URL}" --source ./crates/token_manager/migrations
|
||||||
|
Loading…
Reference in New Issue
Block a user