mirror of
https://github.com/Traceworks2023/nxtgauge-backend-rust.git
synced 2026-06-11 05:32:10 +00:00
fix payments runtime and jwt backend
This commit is contained in:
parent
d48983ee21
commit
52e30a1b4b
5 changed files with 355 additions and 188 deletions
196
Cargo.lock
generated
196
Cargo.lock
generated
|
|
@ -599,6 +599,12 @@ dependencies = [
|
||||||
"fastrand",
|
"fastrand",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base16ct"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
version = "0.22.1"
|
version = "0.22.1"
|
||||||
|
|
@ -958,6 +964,18 @@ version = "0.8.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypto-bigint"
|
||||||
|
version = "0.5.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
"rand_core 0.6.4",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crypto-common"
|
name = "crypto-common"
|
||||||
version = "0.1.7"
|
version = "0.1.7"
|
||||||
|
|
@ -986,6 +1004,33 @@ dependencies = [
|
||||||
"cmov",
|
"cmov",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "curve25519-dalek"
|
||||||
|
version = "4.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures 0.2.17",
|
||||||
|
"curve25519-dalek-derive",
|
||||||
|
"digest 0.10.7",
|
||||||
|
"fiat-crypto",
|
||||||
|
"rustc_version",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "curve25519-dalek-derive"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "customers"
|
name = "customers"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
@ -1116,6 +1161,44 @@ version = "1.0.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
|
checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ecdsa"
|
||||||
|
version = "0.16.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca"
|
||||||
|
dependencies = [
|
||||||
|
"der",
|
||||||
|
"digest 0.10.7",
|
||||||
|
"elliptic-curve",
|
||||||
|
"rfc6979",
|
||||||
|
"signature",
|
||||||
|
"spki",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ed25519"
|
||||||
|
version = "2.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
|
||||||
|
dependencies = [
|
||||||
|
"pkcs8",
|
||||||
|
"signature",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ed25519-dalek"
|
||||||
|
version = "2.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9"
|
||||||
|
dependencies = [
|
||||||
|
"curve25519-dalek",
|
||||||
|
"ed25519",
|
||||||
|
"serde",
|
||||||
|
"sha2 0.10.9",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.16.0"
|
version = "1.16.0"
|
||||||
|
|
@ -1125,6 +1208,27 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "elliptic-curve"
|
||||||
|
version = "0.13.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
|
||||||
|
dependencies = [
|
||||||
|
"base16ct",
|
||||||
|
"crypto-bigint",
|
||||||
|
"digest 0.10.7",
|
||||||
|
"ff",
|
||||||
|
"generic-array",
|
||||||
|
"group",
|
||||||
|
"hkdf",
|
||||||
|
"pem-rfc7468",
|
||||||
|
"pkcs8",
|
||||||
|
"rand_core 0.6.4",
|
||||||
|
"sec1",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "email"
|
name = "email"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
@ -1228,6 +1332,22 @@ version = "2.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"
|
checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ff"
|
||||||
|
version = "0.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.6.4",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fiat-crypto"
|
||||||
|
version = "0.2.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "find-msvc-tools"
|
name = "find-msvc-tools"
|
||||||
version = "0.1.9"
|
version = "0.1.9"
|
||||||
|
|
@ -1434,6 +1554,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"typenum",
|
"typenum",
|
||||||
"version_check",
|
"version_check",
|
||||||
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1495,6 +1616,17 @@ dependencies = [
|
||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "group"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
|
||||||
|
dependencies = [
|
||||||
|
"ff",
|
||||||
|
"rand_core 0.6.4",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "h2"
|
name = "h2"
|
||||||
version = "0.3.27"
|
version = "0.3.27"
|
||||||
|
|
@ -2048,11 +2180,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eba32bfb4ffdeaca3e34431072faf01745c9b26d25504aa7a6cf5684334fc4fc"
|
checksum = "eba32bfb4ffdeaca3e34431072faf01745c9b26d25504aa7a6cf5684334fc4fc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64",
|
||||||
|
"ed25519-dalek",
|
||||||
"getrandom 0.2.17",
|
"getrandom 0.2.17",
|
||||||
|
"hmac 0.12.1",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
|
"p256",
|
||||||
|
"p384",
|
||||||
"pem",
|
"pem",
|
||||||
|
"rand 0.8.6",
|
||||||
|
"rsa",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"sha2 0.10.9",
|
||||||
"signature",
|
"signature",
|
||||||
"simple_asn1",
|
"simple_asn1",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
|
|
@ -2451,6 +2590,30 @@ version = "0.5.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e"
|
checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "p256"
|
||||||
|
version = "0.13.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b"
|
||||||
|
dependencies = [
|
||||||
|
"ecdsa",
|
||||||
|
"elliptic-curve",
|
||||||
|
"primeorder",
|
||||||
|
"sha2 0.10.9",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "p384"
|
||||||
|
version = "0.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6"
|
||||||
|
dependencies = [
|
||||||
|
"ecdsa",
|
||||||
|
"elliptic-curve",
|
||||||
|
"primeorder",
|
||||||
|
"sha2 0.10.9",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking"
|
name = "parking"
|
||||||
version = "2.2.1"
|
version = "2.2.1"
|
||||||
|
|
@ -2632,6 +2795,15 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "primeorder"
|
||||||
|
version = "0.13.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6"
|
||||||
|
dependencies = [
|
||||||
|
"elliptic-curve",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.106"
|
version = "1.0.106"
|
||||||
|
|
@ -2909,6 +3081,16 @@ dependencies = [
|
||||||
"webpki-roots 1.0.7",
|
"webpki-roots 1.0.7",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rfc6979"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2"
|
||||||
|
dependencies = [
|
||||||
|
"hmac 0.12.1",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ring"
|
name = "ring"
|
||||||
version = "0.17.14"
|
version = "0.17.14"
|
||||||
|
|
@ -3080,6 +3262,20 @@ dependencies = [
|
||||||
"untrusted",
|
"untrusted",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sec1"
|
||||||
|
version = "0.7.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc"
|
||||||
|
dependencies = [
|
||||||
|
"base16ct",
|
||||||
|
"der",
|
||||||
|
"generic-array",
|
||||||
|
"pkcs8",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "security-framework"
|
name = "security-framework"
|
||||||
version = "3.7.0"
|
version = "3.7.0"
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,7 @@ async fn create_order(
|
||||||
|
|
||||||
sqlx::query(
|
sqlx::query(
|
||||||
r#"
|
r#"
|
||||||
INSERT INTO payments (user_id, package_id, razorpay_order_id, amount, tracecoins_credited, status)
|
INSERT INTO payments (user_id, package_id, razorpay_order_id, amount_inr, tracecoins_credited, status)
|
||||||
VALUES ($1, $2, $3, $4, $5, 'PENDING')
|
VALUES ($1, $2, $3, $4, $5, 'PENDING')
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
|
|
@ -248,8 +248,8 @@ async fn verify_payment(
|
||||||
{
|
{
|
||||||
sqlx::query(
|
sqlx::query(
|
||||||
r#"
|
r#"
|
||||||
INSERT INTO tracecoin_ledger (wallet_id, transaction_type, amount, balance_after, reference_type, reference_id, description)
|
INSERT INTO tracecoin_ledger (wallet_id, transaction_type, amount, reference_type, reference_id)
|
||||||
VALUES ($1, 'CREDIT', $2, $2, 'PAYMENT', $3, 'Package purchase')
|
VALUES ($1, 'CREDIT', $2, 'PAYMENT', $3)
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.bind(wallet_id)
|
.bind(wallet_id)
|
||||||
|
|
@ -262,7 +262,7 @@ async fn verify_payment(
|
||||||
|
|
||||||
let _ = sqlx::query(
|
let _ = sqlx::query(
|
||||||
r#"
|
r#"
|
||||||
INSERT INTO notifications (user_id, title, body, notification_type, reference_id)
|
INSERT INTO notifications (user_id, title, body, type, reference_id)
|
||||||
VALUES ($1, $2, $3, $4, $5)
|
VALUES ($1, $2, $3, $4, $5)
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ use uuid::Uuid;
|
||||||
pub struct PackageTypeQuery {
|
pub struct PackageTypeQuery {
|
||||||
pub package_type: Option<String>,
|
pub package_type: Option<String>,
|
||||||
pub applicable_role: Option<String>,
|
pub applicable_role: Option<String>,
|
||||||
|
pub role: Option<String>,
|
||||||
pub active_only: Option<bool>,
|
pub active_only: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -29,48 +30,37 @@ pub struct CreatePackageRequest {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
pub package_type: String,
|
pub package_type: String,
|
||||||
pub applicable_roles: Vec<String>,
|
pub role_key: Option<String>,
|
||||||
|
pub applicable_roles: Option<Vec<String>>,
|
||||||
pub tracecoins_amount: i32,
|
pub tracecoins_amount: i32,
|
||||||
pub price: i32,
|
pub price: Option<i32>,
|
||||||
pub duration_days: Option<i32>,
|
pub price_inr: Option<i32>,
|
||||||
pub valid_from: Option<chrono::DateTime<chrono::Utc>>,
|
|
||||||
pub valid_until: Option<chrono::DateTime<chrono::Utc>>,
|
|
||||||
pub is_promotional: Option<bool>,
|
|
||||||
pub is_active: Option<bool>,
|
pub is_active: Option<bool>,
|
||||||
pub features: Option<serde_json::Value>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct UpdatePackageRequest {
|
pub struct UpdatePackageRequest {
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
|
pub role_key: Option<String>,
|
||||||
|
pub applicable_roles: Option<Vec<String>>,
|
||||||
pub tracecoins_amount: Option<i32>,
|
pub tracecoins_amount: Option<i32>,
|
||||||
pub price: Option<i32>,
|
pub price: Option<i32>,
|
||||||
pub duration_days: Option<i32>,
|
pub price_inr: Option<i32>,
|
||||||
pub valid_from: Option<chrono::DateTime<chrono::Utc>>,
|
|
||||||
pub valid_until: Option<chrono::DateTime<chrono::Utc>>,
|
|
||||||
pub is_promotional: Option<bool>,
|
|
||||||
pub is_active: Option<bool>,
|
pub is_active: Option<bool>,
|
||||||
pub features: Option<serde_json::Value>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, FromRow)]
|
#[derive(Debug, FromRow)]
|
||||||
pub struct PricingPackageRow {
|
pub struct PricingPackageRow {
|
||||||
pub id: Uuid,
|
pub id: Uuid,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub description: Option<String>,
|
pub role_key: String,
|
||||||
pub package_type: String,
|
pub package_type: String,
|
||||||
pub applicable_roles: Vec<String>,
|
|
||||||
pub tracecoins_amount: i32,
|
pub tracecoins_amount: i32,
|
||||||
pub price: i32,
|
pub price_inr: i32,
|
||||||
pub duration_days: Option<i32>,
|
pub description: Option<String>,
|
||||||
pub valid_from: Option<chrono::DateTime<chrono::Utc>>,
|
|
||||||
pub valid_until: Option<chrono::DateTime<chrono::Utc>>,
|
|
||||||
pub is_promotional: bool,
|
|
||||||
pub is_active: bool,
|
pub is_active: bool,
|
||||||
pub features: Option<serde_json::Value>,
|
|
||||||
pub created_at: chrono::DateTime<chrono::Utc>,
|
pub created_at: chrono::DateTime<chrono::Utc>,
|
||||||
pub updated_at: chrono::DateTime<chrono::Utc>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
|
|
@ -78,10 +68,12 @@ pub struct PricingPackageResponse {
|
||||||
pub id: Uuid,
|
pub id: Uuid,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
pub package_type: String,
|
pub role_key: String,
|
||||||
pub applicable_roles: Vec<String>,
|
pub applicable_roles: Vec<String>,
|
||||||
|
pub package_type: String,
|
||||||
pub tracecoins_amount: i32,
|
pub tracecoins_amount: i32,
|
||||||
pub price: i32,
|
pub price: i32,
|
||||||
|
pub price_inr: i32,
|
||||||
pub duration_days: Option<i32>,
|
pub duration_days: Option<i32>,
|
||||||
pub valid_from: Option<chrono::DateTime<chrono::Utc>>,
|
pub valid_from: Option<chrono::DateTime<chrono::Utc>>,
|
||||||
pub valid_until: Option<chrono::DateTime<chrono::Utc>>,
|
pub valid_until: Option<chrono::DateTime<chrono::Utc>>,
|
||||||
|
|
@ -96,33 +88,58 @@ pub struct PricingPackageResponse {
|
||||||
|
|
||||||
impl From<PricingPackageRow> for PricingPackageResponse {
|
impl From<PricingPackageRow> for PricingPackageResponse {
|
||||||
fn from(row: PricingPackageRow) -> Self {
|
fn from(row: PricingPackageRow) -> Self {
|
||||||
let now = chrono::Utc::now();
|
Self {
|
||||||
let is_expired = row.valid_until.map(|v| v < now).unwrap_or(false);
|
|
||||||
let is_not_started = row.valid_from.map(|v| v > now).unwrap_or(false);
|
|
||||||
let is_available = row.is_active && !is_expired && !is_not_started;
|
|
||||||
|
|
||||||
PricingPackageResponse {
|
|
||||||
id: row.id,
|
id: row.id,
|
||||||
name: row.name,
|
name: row.name,
|
||||||
description: row.description,
|
description: row.description,
|
||||||
|
role_key: row.role_key.clone(),
|
||||||
|
applicable_roles: vec![row.role_key],
|
||||||
package_type: row.package_type,
|
package_type: row.package_type,
|
||||||
applicable_roles: row.applicable_roles,
|
|
||||||
tracecoins_amount: row.tracecoins_amount,
|
tracecoins_amount: row.tracecoins_amount,
|
||||||
price: row.price,
|
price: row.price_inr,
|
||||||
duration_days: row.duration_days,
|
price_inr: row.price_inr,
|
||||||
valid_from: row.valid_from,
|
duration_days: None,
|
||||||
valid_until: row.valid_until,
|
valid_from: None,
|
||||||
is_promotional: row.is_promotional,
|
valid_until: None,
|
||||||
|
is_promotional: false,
|
||||||
is_active: row.is_active,
|
is_active: row.is_active,
|
||||||
features: row.features,
|
features: None,
|
||||||
created_at: row.created_at,
|
created_at: row.created_at,
|
||||||
updated_at: row.updated_at,
|
updated_at: row.created_at,
|
||||||
is_available,
|
is_available: row.is_active,
|
||||||
is_expired,
|
is_expired: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn normalize_role_key(role_key: Option<String>, applicable_roles: Option<Vec<String>>) -> Result<String, String> {
|
||||||
|
if let Some(role) = role_key {
|
||||||
|
let cleaned = role.trim().to_uppercase();
|
||||||
|
if !cleaned.is_empty() {
|
||||||
|
return Ok(cleaned);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(roles) = applicable_roles {
|
||||||
|
if let Some(role) = roles.into_iter().map(|role| role.trim().to_uppercase()).find(|role| !role.is_empty()) {
|
||||||
|
return Ok(role);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err("role_key is required".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn package_query(base_where: &str, order_by: &str) -> String {
|
||||||
|
format!(
|
||||||
|
r#"
|
||||||
|
SELECT id, name, role_key, package_type, tracecoins_amount, price_inr, description, is_active, created_at
|
||||||
|
FROM pricing_packages
|
||||||
|
{base_where}
|
||||||
|
{order_by}
|
||||||
|
"#
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn router() -> Router<AppState> {
|
pub fn router() -> Router<AppState> {
|
||||||
Router::new()
|
Router::new()
|
||||||
.route("/", get(list_packages))
|
.route("/", get(list_packages))
|
||||||
|
|
@ -138,83 +155,67 @@ async fn list_packages(
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Query(q): Query<PaginationQuery>,
|
Query(q): Query<PaginationQuery>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
let page = q.page.unwrap_or(1);
|
let page = q.page.unwrap_or(1).max(1);
|
||||||
let limit = q.limit.unwrap_or(20).min(100);
|
let limit = q.limit.unwrap_or(20).clamp(1, 100);
|
||||||
let offset = (page - 1) * limit;
|
let offset = (page - 1) * limit;
|
||||||
|
let search = q.search.unwrap_or_default().trim().to_string();
|
||||||
|
|
||||||
let search_filter = q.search
|
let rows = sqlx::query_as::<_, PricingPackageRow>(
|
||||||
.as_ref()
|
|
||||||
.map(|s| format!("AND (name ILIKE '%{}%' OR description ILIKE '%{}%')", s.replace('\'', "''"), s.replace('\'', "''")))
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
let packages = sqlx::query_as::<_, PricingPackageRow>(
|
|
||||||
&format!(
|
&format!(
|
||||||
r#"
|
"{} LIMIT $2 OFFSET $3",
|
||||||
SELECT id, name, description, package_type, applicable_roles,
|
package_query(
|
||||||
tracecoins_amount, price, duration_days, valid_from, valid_until,
|
"WHERE ($1 = '' OR name ILIKE '%' || $1 || '%' OR COALESCE(description, '') ILIKE '%' || $1 || '%')",
|
||||||
is_promotional, is_active, features, created_at, updated_at
|
"ORDER BY created_at DESC"
|
||||||
FROM pricing_packages
|
)
|
||||||
WHERE 1=1 {}
|
),
|
||||||
ORDER BY created_at DESC
|
|
||||||
LIMIT {} OFFSET {}
|
|
||||||
"#,
|
|
||||||
search_filter, limit, offset
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
.bind(&search)
|
||||||
|
.bind(limit)
|
||||||
|
.bind(offset)
|
||||||
.fetch_all(&state.pool)
|
.fetch_all(&state.pool)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let packages = match packages {
|
let rows = match rows {
|
||||||
Ok(p) => p,
|
Ok(rows) => rows,
|
||||||
Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let total: (i64,) = match sqlx::query_as(
|
let total: i64 = match sqlx::query_scalar(
|
||||||
&format!(
|
"SELECT COUNT(*) FROM pricing_packages WHERE ($1 = '' OR name ILIKE '%' || $1 || '%' OR COALESCE(description, '') ILIKE '%' || $1 || '%')",
|
||||||
"SELECT COUNT(*) FROM pricing_packages WHERE 1=1 {}",
|
|
||||||
search_filter
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
.bind(&search)
|
||||||
.fetch_one(&state.pool)
|
.fetch_one(&state.pool)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(t) => t,
|
Ok(total) => total,
|
||||||
Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let packages: Vec<PricingPackageResponse> = packages.into_iter().map(|p| p.into()).collect();
|
let packages: Vec<PricingPackageResponse> = rows.into_iter().map(Into::into).collect();
|
||||||
|
|
||||||
(StatusCode::OK, Json(serde_json::json!({
|
(StatusCode::OK, Json(serde_json::json!({
|
||||||
"data": packages,
|
"data": packages,
|
||||||
|
"packages": packages,
|
||||||
"pagination": {
|
"pagination": {
|
||||||
"page": page,
|
"page": page,
|
||||||
"limit": limit,
|
"limit": limit,
|
||||||
"total": total.0,
|
"total": total,
|
||||||
"pages": (total.0 as f64 / limit as f64).ceil() as i64
|
"pages": (total as f64 / limit as f64).ceil() as i64
|
||||||
}
|
}
|
||||||
}))).into_response()
|
})))
|
||||||
|
.into_response()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_package(
|
async fn get_package(
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Path(id): Path<Uuid>,
|
Path(id): Path<Uuid>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
match sqlx::query_as::<_, PricingPackageRow>(
|
match sqlx::query_as::<_, PricingPackageRow>(&package_query("WHERE id = $1", ""))
|
||||||
r#"
|
.bind(id)
|
||||||
SELECT id, name, description, package_type, applicable_roles,
|
.fetch_optional(&state.pool)
|
||||||
tracecoins_amount, price, duration_days, valid_from, valid_until,
|
.await
|
||||||
is_promotional, is_active, features, created_at, updated_at
|
|
||||||
FROM pricing_packages WHERE id = $1
|
|
||||||
"#
|
|
||||||
)
|
|
||||||
.bind(id)
|
|
||||||
.fetch_optional(&state.pool)
|
|
||||||
.await
|
|
||||||
{
|
{
|
||||||
Ok(Some(pkg)) => {
|
Ok(Some(pkg)) => (StatusCode::OK, Json(PricingPackageResponse::from(pkg))).into_response(),
|
||||||
let response: PricingPackageResponse = pkg.into();
|
|
||||||
(StatusCode::OK, Json(response)).into_response()
|
|
||||||
}
|
|
||||||
Ok(None) => (StatusCode::NOT_FOUND, "Package not found").into_response(),
|
Ok(None) => (StatusCode::NOT_FOUND, "Package not found").into_response(),
|
||||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||||||
}
|
}
|
||||||
|
|
@ -224,37 +225,31 @@ async fn create_package(
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Json(payload): Json<CreatePackageRequest>,
|
Json(payload): Json<CreatePackageRequest>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
|
let role_key = match normalize_role_key(payload.role_key, payload.applicable_roles) {
|
||||||
|
Ok(role_key) => role_key,
|
||||||
|
Err(message) => return (StatusCode::BAD_REQUEST, message).into_response(),
|
||||||
|
};
|
||||||
|
let price_inr = payload.price_inr.or(payload.price).unwrap_or(0);
|
||||||
|
|
||||||
let result = sqlx::query_as::<_, PricingPackageRow>(
|
let result = sqlx::query_as::<_, PricingPackageRow>(
|
||||||
r#"
|
r#"
|
||||||
INSERT INTO pricing_packages (name, description, package_type, applicable_roles,
|
INSERT INTO pricing_packages (name, role_key, package_type, tracecoins_amount, price_inr, description, is_active)
|
||||||
tracecoins_amount, price, duration_days, valid_from, valid_until,
|
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||||
is_promotional, is_active, features)
|
RETURNING id, name, role_key, package_type, tracecoins_amount, price_inr, description, is_active, created_at
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
|
"#,
|
||||||
RETURNING id, name, description, package_type, applicable_roles,
|
|
||||||
tracecoins_amount, price, duration_days, valid_from, valid_until,
|
|
||||||
is_promotional, is_active, features, created_at, updated_at
|
|
||||||
"#
|
|
||||||
)
|
)
|
||||||
.bind(&payload.name)
|
.bind(&payload.name)
|
||||||
.bind(&payload.description)
|
.bind(&role_key)
|
||||||
.bind(&payload.package_type)
|
.bind(&payload.package_type)
|
||||||
.bind(&payload.applicable_roles)
|
|
||||||
.bind(payload.tracecoins_amount)
|
.bind(payload.tracecoins_amount)
|
||||||
.bind(payload.price)
|
.bind(price_inr)
|
||||||
.bind(payload.duration_days)
|
.bind(&payload.description)
|
||||||
.bind(payload.valid_from)
|
|
||||||
.bind(payload.valid_until)
|
|
||||||
.bind(payload.is_promotional.unwrap_or(false))
|
|
||||||
.bind(payload.is_active.unwrap_or(true))
|
.bind(payload.is_active.unwrap_or(true))
|
||||||
.bind(payload.features)
|
|
||||||
.fetch_one(&state.pool)
|
.fetch_one(&state.pool)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(pkg) => {
|
Ok(pkg) => (StatusCode::CREATED, Json(PricingPackageResponse::from(pkg))).into_response(),
|
||||||
let response: PricingPackageResponse = pkg.into();
|
|
||||||
(StatusCode::CREATED, Json(response)).into_response()
|
|
||||||
}
|
|
||||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -264,58 +259,47 @@ async fn update_package(
|
||||||
Path(id): Path<Uuid>,
|
Path(id): Path<Uuid>,
|
||||||
Json(payload): Json<UpdatePackageRequest>,
|
Json(payload): Json<UpdatePackageRequest>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
let existing = sqlx::query_as::<_, PricingPackageRow>(
|
let current = match sqlx::query_as::<_, PricingPackageRow>(&package_query("WHERE id = $1", ""))
|
||||||
"SELECT * FROM pricing_packages WHERE id = $1"
|
.bind(id)
|
||||||
)
|
.fetch_optional(&state.pool)
|
||||||
.bind(id)
|
.await
|
||||||
.fetch_optional(&state.pool)
|
{
|
||||||
.await;
|
Ok(Some(pkg)) => pkg,
|
||||||
|
|
||||||
let _existing = match existing {
|
|
||||||
Ok(Some(e)) => e,
|
|
||||||
Ok(None) => return (StatusCode::NOT_FOUND, "Package not found").into_response(),
|
Ok(None) => return (StatusCode::NOT_FOUND, "Package not found").into_response(),
|
||||||
Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let role_key = match normalize_role_key(payload.role_key, payload.applicable_roles) {
|
||||||
|
Ok(role_key) => role_key,
|
||||||
|
Err(_) => current.role_key.clone(),
|
||||||
|
};
|
||||||
|
let price_inr = payload.price_inr.or(payload.price).unwrap_or(current.price_inr);
|
||||||
|
|
||||||
let updated = sqlx::query_as::<_, PricingPackageRow>(
|
let updated = sqlx::query_as::<_, PricingPackageRow>(
|
||||||
r#"
|
r#"
|
||||||
UPDATE pricing_packages SET
|
UPDATE pricing_packages SET
|
||||||
name = COALESCE($2, name),
|
name = COALESCE($2, name),
|
||||||
description = COALESCE($3, description),
|
role_key = $3,
|
||||||
tracecoins_amount = COALESCE($4, tracecoins_amount),
|
description = COALESCE($4, description),
|
||||||
price = COALESCE($5, price),
|
tracecoins_amount = COALESCE($5, tracecoins_amount),
|
||||||
duration_days = COALESCE($6, duration_days),
|
price_inr = $6,
|
||||||
valid_from = COALESCE($7, valid_from),
|
is_active = COALESCE($7, is_active)
|
||||||
valid_until = COALESCE($8, valid_until),
|
|
||||||
is_promotional = COALESCE($9, is_promotional),
|
|
||||||
is_active = COALESCE($10, is_active),
|
|
||||||
features = COALESCE($11, features),
|
|
||||||
updated_at = NOW()
|
|
||||||
WHERE id = $1
|
WHERE id = $1
|
||||||
RETURNING id, name, description, package_type, applicable_roles,
|
RETURNING id, name, role_key, package_type, tracecoins_amount, price_inr, description, is_active, created_at
|
||||||
tracecoins_amount, price, duration_days, valid_from, valid_until,
|
"#,
|
||||||
is_promotional, is_active, features, created_at, updated_at
|
|
||||||
"#
|
|
||||||
)
|
)
|
||||||
.bind(id)
|
.bind(id)
|
||||||
.bind(&payload.name)
|
.bind(&payload.name)
|
||||||
|
.bind(&role_key)
|
||||||
.bind(&payload.description)
|
.bind(&payload.description)
|
||||||
.bind(payload.tracecoins_amount)
|
.bind(payload.tracecoins_amount)
|
||||||
.bind(payload.price)
|
.bind(price_inr)
|
||||||
.bind(payload.duration_days)
|
|
||||||
.bind(payload.valid_from)
|
|
||||||
.bind(payload.valid_until)
|
|
||||||
.bind(payload.is_promotional)
|
|
||||||
.bind(payload.is_active)
|
.bind(payload.is_active)
|
||||||
.bind(payload.features)
|
|
||||||
.fetch_one(&state.pool)
|
.fetch_one(&state.pool)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
match updated {
|
match updated {
|
||||||
Ok(pkg) => {
|
Ok(pkg) => (StatusCode::OK, Json(PricingPackageResponse::from(pkg))).into_response(),
|
||||||
let response: PricingPackageResponse = pkg.into();
|
|
||||||
(StatusCode::OK, Json(response)).into_response()
|
|
||||||
}
|
|
||||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -329,7 +313,7 @@ async fn delete_package(
|
||||||
.execute(&state.pool)
|
.execute(&state.pool)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(r) if r.rows_affected() > 0 => {
|
Ok(result) if result.rows_affected() > 0 => {
|
||||||
(StatusCode::OK, Json(serde_json::json!({"message": "Package deleted"}))).into_response()
|
(StatusCode::OK, Json(serde_json::json!({"message": "Package deleted"}))).into_response()
|
||||||
}
|
}
|
||||||
Ok(_) => (StatusCode::NOT_FOUND, "Package not found").into_response(),
|
Ok(_) => (StatusCode::NOT_FOUND, "Package not found").into_response(),
|
||||||
|
|
@ -341,78 +325,65 @@ async fn get_packages_by_type(
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Query(q): Query<PackageTypeQuery>,
|
Query(q): Query<PackageTypeQuery>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
let package_type = q.package_type.as_deref().unwrap_or("TRACECOIN_BUNDLE");
|
let package_type = q.package_type.unwrap_or_else(|| "TRACECOIN_BUNDLE".to_string());
|
||||||
let now = chrono::Utc::now();
|
|
||||||
|
|
||||||
let packages = sqlx::query_as::<_, PricingPackageRow>(
|
let rows = sqlx::query_as::<_, PricingPackageRow>(
|
||||||
r#"
|
&package_query(
|
||||||
SELECT id, name, description, package_type, applicable_roles,
|
"WHERE package_type = $1 AND is_active = true",
|
||||||
tracecoins_amount, price, duration_days, valid_from, valid_until,
|
"ORDER BY price_inr ASC, created_at DESC",
|
||||||
is_promotional, is_active, features, created_at, updated_at
|
),
|
||||||
FROM pricing_packages
|
|
||||||
WHERE package_type = $1
|
|
||||||
AND is_active = true
|
|
||||||
AND (valid_from IS NULL OR valid_from <= $2)
|
|
||||||
AND (valid_until IS NULL OR valid_until > $2)
|
|
||||||
ORDER BY is_promotional DESC, price ASC
|
|
||||||
"#
|
|
||||||
)
|
)
|
||||||
.bind(package_type)
|
.bind(&package_type)
|
||||||
.bind(now)
|
|
||||||
.fetch_all(&state.pool)
|
.fetch_all(&state.pool)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let packages = match packages {
|
let rows = match rows {
|
||||||
Ok(p) => p,
|
Ok(rows) => rows,
|
||||||
Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let packages: Vec<PricingPackageResponse> = packages.into_iter().map(|p| p.into()).collect();
|
let packages: Vec<PricingPackageResponse> = rows.into_iter().map(Into::into).collect();
|
||||||
|
|
||||||
(StatusCode::OK, Json(serde_json::json!({
|
(StatusCode::OK, Json(serde_json::json!({
|
||||||
"data": packages,
|
"data": packages,
|
||||||
"package_type": package_type
|
"package_type": package_type
|
||||||
}))).into_response()
|
})))
|
||||||
|
.into_response()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_packages_for_role(
|
async fn get_packages_for_role(
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Query(q): Query<PackageTypeQuery>,
|
Query(q): Query<PackageTypeQuery>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
let applicable_role = q.applicable_role.as_deref().unwrap_or("");
|
let role = q
|
||||||
|
.applicable_role
|
||||||
|
.or(q.role)
|
||||||
|
.unwrap_or_default()
|
||||||
|
.trim()
|
||||||
|
.to_uppercase();
|
||||||
let active_only = q.active_only.unwrap_or(true);
|
let active_only = q.active_only.unwrap_or(true);
|
||||||
let now = chrono::Utc::now();
|
|
||||||
|
|
||||||
let packages = sqlx::query_as::<_, PricingPackageRow>(
|
let rows = sqlx::query_as::<_, PricingPackageRow>(
|
||||||
&format!(
|
&package_query(
|
||||||
r#"
|
"WHERE ($1 = '' OR role_key = $1) AND ($2 = false OR is_active = true)",
|
||||||
SELECT id, name, description, package_type, applicable_roles,
|
"ORDER BY price_inr ASC, created_at DESC",
|
||||||
tracecoins_amount, price, duration_days, valid_from, valid_until,
|
),
|
||||||
is_promotional, is_active, features, created_at, updated_at
|
|
||||||
FROM pricing_packages
|
|
||||||
WHERE ($1 = '' OR $1 = ANY(applicable_roles))
|
|
||||||
AND (is_active = true OR {} = false)
|
|
||||||
AND (valid_from IS NULL OR valid_from <= $2)
|
|
||||||
AND (valid_until IS NULL OR valid_until > $2)
|
|
||||||
ORDER BY is_promotional DESC, price ASC
|
|
||||||
"#,
|
|
||||||
if active_only { "true" } else { "false" }
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
.bind(applicable_role)
|
.bind(&role)
|
||||||
.bind(now)
|
.bind(active_only)
|
||||||
.fetch_all(&state.pool)
|
.fetch_all(&state.pool)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let packages = match packages {
|
let rows = match rows {
|
||||||
Ok(p) => p,
|
Ok(rows) => rows,
|
||||||
Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let packages: Vec<PricingPackageResponse> = packages.into_iter().map(|p| p.into()).collect();
|
let packages: Vec<PricingPackageResponse> = rows.into_iter().map(Into::into).collect();
|
||||||
|
|
||||||
(StatusCode::OK, Json(serde_json::json!({
|
(StatusCode::OK, Json(serde_json::json!({
|
||||||
"data": packages,
|
"data": packages,
|
||||||
"applicable_role": applicable_role
|
"applicable_role": role
|
||||||
}))).into_response()
|
})))
|
||||||
|
.into_response()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
jsonwebtoken = "10.3"
|
jsonwebtoken = { version = "10.3", features = ["rust_crypto"] }
|
||||||
argon2 = "0.5"
|
argon2 = "0.5"
|
||||||
rand_core = { version = "0.6", features = ["std"] }
|
rand_core = { version = "0.6", features = ["std"] }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ chrono = { workspace = true }
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
sqlx = { workspace = true }
|
sqlx = { workspace = true }
|
||||||
async-trait = { workspace = true }
|
async-trait = { workspace = true }
|
||||||
jsonwebtoken = "10.3"
|
jsonwebtoken = { version = "10.3", features = ["rust_crypto"] }
|
||||||
db = { path = "../db" }
|
db = { path = "../db" }
|
||||||
cache = { path = "../cache" }
|
cache = { path = "../cache" }
|
||||||
storage = { path = "../storage" }
|
storage = { path = "../storage" }
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue