211
Cargo.lock → backend/Cargo.lock
generated
211
Cargo.lock → backend/Cargo.lock
generated
@@ -19,6 +19,21 @@ dependencies = [
|
|||||||
"tokio-util",
|
"tokio-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "actix-cors"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b340e9cfa5b08690aae90fb61beb44e9b06f44fe3d0f93781aaa58cfba86245e"
|
||||||
|
dependencies = [
|
||||||
|
"actix-utils",
|
||||||
|
"actix-web",
|
||||||
|
"derive_more",
|
||||||
|
"futures-util",
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"smallvec",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "actix-http"
|
name = "actix-http"
|
||||||
version = "3.3.1"
|
version = "3.3.1"
|
||||||
@@ -30,7 +45,7 @@ dependencies = [
|
|||||||
"actix-service",
|
"actix-service",
|
||||||
"actix-utils",
|
"actix-utils",
|
||||||
"ahash 0.8.3",
|
"ahash 0.8.3",
|
||||||
"base64",
|
"base64 0.21.0",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"brotli",
|
"brotli",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -58,6 +73,22 @@ dependencies = [
|
|||||||
"zstd",
|
"zstd",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "actix-identity"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1224c9f9593dc27c9077b233ce04adedc1d7febcfc35ee9f53ea3c24df180bec"
|
||||||
|
dependencies = [
|
||||||
|
"actix-service",
|
||||||
|
"actix-session",
|
||||||
|
"actix-utils",
|
||||||
|
"actix-web",
|
||||||
|
"anyhow",
|
||||||
|
"futures-core",
|
||||||
|
"serde",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "actix-macros"
|
name = "actix-macros"
|
||||||
version = "0.2.3"
|
version = "0.2.3"
|
||||||
@@ -120,6 +151,23 @@ dependencies = [
|
|||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "actix-session"
|
||||||
|
version = "0.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "43da8b818ae1f11049a4d218975345fe8e56ce5a5f92c11f972abcff5ff80e87"
|
||||||
|
dependencies = [
|
||||||
|
"actix-service",
|
||||||
|
"actix-utils",
|
||||||
|
"actix-web",
|
||||||
|
"anyhow",
|
||||||
|
"async-trait",
|
||||||
|
"derive_more",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "actix-utils"
|
name = "actix-utils"
|
||||||
version = "3.0.1"
|
version = "3.0.1"
|
||||||
@@ -193,6 +241,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
|||||||
name = "adv_db"
|
name = "adv_db"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"actix-cors",
|
||||||
|
"actix-identity",
|
||||||
|
"actix-session",
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"chrono",
|
"chrono",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
@@ -200,9 +251,45 @@ dependencies = [
|
|||||||
"oracle",
|
"oracle",
|
||||||
"rand",
|
"rand",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"sha2",
|
"sha2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aead"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
|
||||||
|
dependencies = [
|
||||||
|
"crypto-common",
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aes"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cipher",
|
||||||
|
"cpufeatures",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aes-gcm"
|
||||||
|
version = "0.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "82e1366e0c69c9f927b1fa5ce2c7bf9eafc8f9268c0b9800729e8b267612447c"
|
||||||
|
dependencies = [
|
||||||
|
"aead",
|
||||||
|
"aes",
|
||||||
|
"cipher",
|
||||||
|
"ctr",
|
||||||
|
"ghash",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ahash"
|
name = "ahash"
|
||||||
version = "0.7.6"
|
version = "0.7.6"
|
||||||
@@ -259,12 +346,35 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.70"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-trait"
|
||||||
|
version = "0.1.68"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.15",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.1.0"
|
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.20.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
version = "0.21.0"
|
version = "0.21.0"
|
||||||
@@ -358,6 +468,16 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cipher"
|
||||||
|
version = "0.4.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
|
||||||
|
dependencies = [
|
||||||
|
"crypto-common",
|
||||||
|
"inout",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "codespan-reporting"
|
name = "codespan-reporting"
|
||||||
version = "0.11.1"
|
version = "0.11.1"
|
||||||
@@ -380,7 +500,14 @@ version = "0.16.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb"
|
checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"aes-gcm",
|
||||||
|
"base64 0.20.0",
|
||||||
|
"hkdf",
|
||||||
|
"hmac",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
|
"rand",
|
||||||
|
"sha2",
|
||||||
|
"subtle",
|
||||||
"time 0.3.20",
|
"time 0.3.20",
|
||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
@@ -416,9 +543,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"generic-array",
|
"generic-array",
|
||||||
|
"rand_core",
|
||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ctr"
|
||||||
|
version = "0.9.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
|
||||||
|
dependencies = [
|
||||||
|
"cipher",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cxx"
|
name = "cxx"
|
||||||
version = "1.0.94"
|
version = "1.0.94"
|
||||||
@@ -519,6 +656,7 @@ checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"block-buffer",
|
"block-buffer",
|
||||||
"crypto-common",
|
"crypto-common",
|
||||||
|
"subtle",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -640,6 +778,16 @@ dependencies = [
|
|||||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ghash"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40"
|
||||||
|
dependencies = [
|
||||||
|
"opaque-debug",
|
||||||
|
"polyval",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "h2"
|
name = "h2"
|
||||||
version = "0.3.18"
|
version = "0.3.18"
|
||||||
@@ -680,6 +828,24 @@ version = "0.3.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
|
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hkdf"
|
||||||
|
version = "0.12.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437"
|
||||||
|
dependencies = [
|
||||||
|
"hmac",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hmac"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
|
||||||
|
dependencies = [
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http"
|
name = "http"
|
||||||
version = "0.2.9"
|
version = "0.2.9"
|
||||||
@@ -759,6 +925,15 @@ dependencies = [
|
|||||||
"hashbrown",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "inout"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "io-lifetimes"
|
name = "io-lifetimes"
|
||||||
version = "1.0.10"
|
version = "1.0.10"
|
||||||
@@ -944,6 +1119,12 @@ version = "1.17.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "opaque-debug"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oracle"
|
name = "oracle"
|
||||||
version = "0.5.7"
|
version = "0.5.7"
|
||||||
@@ -1021,6 +1202,18 @@ version = "0.3.26"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
|
checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "polyval"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7ef234e08c11dfcb2e56f79fd70f6f2eb7f025c0ce2333e82f4f0518ecad30c6"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"opaque-debug",
|
||||||
|
"universal-hash",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.17"
|
version = "0.2.17"
|
||||||
@@ -1253,6 +1446,12 @@ version = "0.10.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subtle"
|
||||||
|
version = "2.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.109"
|
version = "1.0.109"
|
||||||
@@ -1421,6 +1620,16 @@ version = "0.1.10"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "universal-hash"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7d3160b73c9a19f7e2939a2fdad446c57c1bbbbf4d919d3213ff1267a580d8b5"
|
||||||
|
dependencies = [
|
||||||
|
"crypto-common",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "2.3.1"
|
version = "2.3.1"
|
||||||
@@ -6,6 +6,9 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
actix-cors = "0.6.4"
|
||||||
|
actix-identity = "0.5.2"
|
||||||
|
actix-session = { version = "0.7.2", features = ["cookie-session"] }
|
||||||
actix-web = "4.3.1"
|
actix-web = "4.3.1"
|
||||||
chrono = "0.4.24"
|
chrono = "0.4.24"
|
||||||
env_logger = "0.10.0"
|
env_logger = "0.10.0"
|
||||||
@@ -13,4 +16,5 @@ log = "0.4.17"
|
|||||||
oracle = "0.5.7"
|
oracle = "0.5.7"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
serde = { version = "1.0.160", features = ["derive"] }
|
serde = { version = "1.0.160", features = ["derive"] }
|
||||||
|
serde_json = "1.0.96"
|
||||||
sha2 = "0.10.6"
|
sha2 = "0.10.6"
|
||||||
75
backend/src/api/eatery.rs
Normal file
75
backend/src/api/eatery.rs
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
use actix_web::{web::Path, web::Json};
|
||||||
|
use oracle::Connection;
|
||||||
|
use log::error;
|
||||||
|
use crate::config::{ORACLE_USER, ORACLE_PASS, ORACLE_CON_STR};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Default)]
|
||||||
|
pub struct FoodItem {
|
||||||
|
item_id: u32,
|
||||||
|
eatery_id: Option<u32>,
|
||||||
|
item_name: Option<String>,
|
||||||
|
serving_size: Option<String>,
|
||||||
|
calories: Option<f32>,
|
||||||
|
fat: Option<f32>,
|
||||||
|
sat_fat: Option<f32>,
|
||||||
|
trans_fat: Option<f32>,
|
||||||
|
carbs: Option<f32>,
|
||||||
|
fiber: Option<f32>,
|
||||||
|
sugar: Option<f32>,
|
||||||
|
protein: Option<f32>,
|
||||||
|
sodium: Option<f32>,
|
||||||
|
potassium: Option<f32>,
|
||||||
|
cholesterol: Option<f32>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn menu(eatery: Path<String>) -> Json<Vec<FoodItem>> {
|
||||||
|
let eatery = eatery.into_inner();
|
||||||
|
let response = match grab_rows(eatery) {
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to grab rows: {}", e);
|
||||||
|
return Json(vec![]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Json(response)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn grab_rows(eatery: String) -> oracle::Result<Vec<FoodItem>> {
|
||||||
|
|
||||||
|
let conn = Connection::connect(ORACLE_USER, ORACLE_PASS, ORACLE_CON_STR)?;
|
||||||
|
|
||||||
|
let mut stmt = conn.statement("select * from menu_item natural join nutrition_info where eatery_id = :1").build()?;
|
||||||
|
|
||||||
|
|
||||||
|
let rows = stmt.query(&[&eatery])?;
|
||||||
|
let mut row_vec: Vec<FoodItem> = vec![];
|
||||||
|
|
||||||
|
for row_result in rows {
|
||||||
|
let row = row_result?;
|
||||||
|
row_vec.push( FoodItem {
|
||||||
|
item_id: row.get(0)?,
|
||||||
|
eatery_id: row.get(1).unwrap_or(None),
|
||||||
|
item_name: row.get(2).unwrap_or(None),
|
||||||
|
serving_size: row.get(3).unwrap_or(None),
|
||||||
|
calories: row.get(4).unwrap_or(None),
|
||||||
|
fat: row.get(5).unwrap_or(None),
|
||||||
|
sat_fat: row.get(6).unwrap_or(None),
|
||||||
|
trans_fat: row.get(7).unwrap_or(None),
|
||||||
|
carbs: row.get(8).unwrap_or(None),
|
||||||
|
fiber: row.get(9).unwrap_or(None),
|
||||||
|
sugar: row.get(10).unwrap_or(None),
|
||||||
|
protein: row.get(11).unwrap_or(None),
|
||||||
|
sodium: row.get(12).unwrap_or(None),
|
||||||
|
potassium: row.get(13).unwrap_or(None),
|
||||||
|
cholesterol: row.get(14).unwrap_or(None)});
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.close()?;
|
||||||
|
|
||||||
|
Ok(row_vec)
|
||||||
|
}
|
||||||
64
backend/src/api/menu.rs
Normal file
64
backend/src/api/menu.rs
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
use oracle::{Result, Connection};
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
use log::error;
|
||||||
|
use actix_web::{web::Json};
|
||||||
|
|
||||||
|
use crate::config::{ORACLE_USER, ORACLE_PASS, ORACLE_CON_STR};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Default)]
|
||||||
|
pub struct SearchTerm{
|
||||||
|
search_term: String
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Default)]
|
||||||
|
pub struct SearchResults {
|
||||||
|
results: Vec<SearchResult>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Default)]
|
||||||
|
pub struct SearchResult {
|
||||||
|
item_name: Option<String>,
|
||||||
|
item_id: u32,
|
||||||
|
eatery_id: Option<u32>,
|
||||||
|
serving_size: Option<String>,
|
||||||
|
eatery_name: Option<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn menu_search(term: Json<SearchTerm>) -> Json<SearchResults> {
|
||||||
|
|
||||||
|
let term = term.into_inner();
|
||||||
|
|
||||||
|
match fuzzy_search(&term.search_term) {
|
||||||
|
Ok(result) => Json(result),
|
||||||
|
Err(e) => {
|
||||||
|
error!("failed to search for {}: {}", term.search_term, e);
|
||||||
|
Json(SearchResults::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fuzzy_search(term: &str) -> Result<SearchResults> {
|
||||||
|
let conn = Connection::connect(ORACLE_USER,ORACLE_PASS,ORACLE_CON_STR)?;
|
||||||
|
let mut stmt = conn.statement(format!("select * from menu_item inner join eatery on menu_item.eatery_id = eatery.id where item_name like '%{}%'", term).as_str()).build()?;
|
||||||
|
|
||||||
|
let rows = stmt.query(&[])?;
|
||||||
|
|
||||||
|
let mut rows_vec = SearchResults::default();
|
||||||
|
|
||||||
|
for row_result in rows{
|
||||||
|
let row = row_result?;
|
||||||
|
rows_vec.results.push( SearchResult {
|
||||||
|
item_name: row.get(0).unwrap_or(None),
|
||||||
|
item_id: row.get(1)?,
|
||||||
|
eatery_id: row.get(2).unwrap_or(None),
|
||||||
|
serving_size: row.get(3).unwrap_or(None),
|
||||||
|
eatery_name: row.get(5).unwrap_or(None)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
conn.close()?;
|
||||||
|
|
||||||
|
Ok(rows_vec)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
8
backend/src/api/mod.rs
Normal file
8
backend/src/api/mod.rs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
pub mod user;
|
||||||
|
pub mod eatery;
|
||||||
|
pub mod plan;
|
||||||
|
pub mod week;
|
||||||
|
pub mod menu;
|
||||||
|
pub mod result;
|
||||||
|
|
||||||
|
|
||||||
152
backend/src/api/plan.rs
Normal file
152
backend/src/api/plan.rs
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
use actix_web::{web::{Json, Path}, Responder, HttpResponse};
|
||||||
|
use oracle::{Connection, Result};
|
||||||
|
use log::{error, info};
|
||||||
|
use crate::config::{ORACLE_PASS, ORACLE_USER, ORACLE_CON_STR};
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Debug, Default)]
|
||||||
|
pub struct PlanData{
|
||||||
|
pub net_id: String,
|
||||||
|
pub week_date: String,
|
||||||
|
pub total_cal: Option<f32>,
|
||||||
|
pub total_fat: Option<f32>,
|
||||||
|
pub total_sat_fat: Option<f32>,
|
||||||
|
pub total_trans_fat: Option<f32>,
|
||||||
|
pub total_carbs: Option<f32>,
|
||||||
|
pub total_fiber: Option<f32>,
|
||||||
|
pub total_sugar: Option<f32>,
|
||||||
|
pub total_protein: Option<f32>,
|
||||||
|
pub total_sodium: Option<f32>,
|
||||||
|
pub total_potassium: Option<f32>,
|
||||||
|
pub total_cholesterol: Option<f32>
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn plan(body: Json<PlanData>) -> impl Responder {
|
||||||
|
let body = body.into_inner();
|
||||||
|
match create_plan(body) {
|
||||||
|
Ok(_) => HttpResponse::Ok(),
|
||||||
|
Err(e) => {
|
||||||
|
error!("failed to create plan {}", e);
|
||||||
|
HttpResponse::InternalServerError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn plan_lookup(net_id: Path<String>) -> Json<PlanData> {
|
||||||
|
|
||||||
|
let net_id = net_id.into_inner();
|
||||||
|
|
||||||
|
match plan_search(&net_id) {
|
||||||
|
Ok(plan) => Json(plan),
|
||||||
|
Err(e) => {
|
||||||
|
error!("failed to grab plan from {}: {}", net_id, e);
|
||||||
|
Json(PlanData::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn all_plan(net_id: Path<String>) -> Json<Vec<PlanData>> {
|
||||||
|
|
||||||
|
let net_id = net_id.into_inner();
|
||||||
|
|
||||||
|
match get_all_plan(&net_id) {
|
||||||
|
Ok(plans) => Json(plans),
|
||||||
|
Err(e) => {
|
||||||
|
error!("failed to grab plans from {}: {}", net_id, e);
|
||||||
|
Json(vec![])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn create_plan(plan: PlanData) -> Result<()> {
|
||||||
|
|
||||||
|
let conn = Connection::connect(ORACLE_USER, ORACLE_PASS, ORACLE_CON_STR)?;
|
||||||
|
|
||||||
|
let mut stmt = conn.statement("insert into goal values(:net_id, :week_date, :total_cal, :total_fat, :total_sat_fat, :total_trans_fat, :total_carbs, :total_fiber, :total_sugar, :total_protein, :total_sodium, :total_potassium, :total_cholesterol)").build()?;
|
||||||
|
|
||||||
|
stmt.execute_named(&[
|
||||||
|
("net_id", &plan.net_id),
|
||||||
|
("week_date", &plan.week_date),
|
||||||
|
("total_cal", &plan.total_cal),
|
||||||
|
("total_fat", &plan.total_fat),
|
||||||
|
("total_sat_fat", &plan.total_sat_fat),
|
||||||
|
("total_trans_fat", &plan.total_trans_fat),
|
||||||
|
("total_carbs", &plan.total_carbs),
|
||||||
|
("total_fiber", &plan.total_fiber),
|
||||||
|
("total_sugar", &plan.total_sugar),
|
||||||
|
("total_protein", &plan.total_protein),
|
||||||
|
("total_sodium", &plan.total_sodium),
|
||||||
|
("total_potassium", &plan.total_potassium),
|
||||||
|
("total_cholesterol", &plan.total_cholesterol)])?;
|
||||||
|
|
||||||
|
info!("Created new plan for user: {}", plan.net_id);
|
||||||
|
conn.commit()?;
|
||||||
|
conn.close()?;
|
||||||
|
Ok(())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn plan_search(net_id: &str) -> Result<PlanData> {
|
||||||
|
|
||||||
|
let conn = Connection::connect(ORACLE_USER, ORACLE_PASS, ORACLE_CON_STR)?;
|
||||||
|
|
||||||
|
let mut stmt = conn.statement("select * from goal where s_net_id = :1 and ROWNUM = 1 order by week_date").build()?;
|
||||||
|
|
||||||
|
let row = stmt.query_row(&[&net_id])?;
|
||||||
|
|
||||||
|
|
||||||
|
let plan: PlanData = PlanData {
|
||||||
|
net_id: row.get(0)?,
|
||||||
|
week_date: row.get(1)?,
|
||||||
|
total_cal: row.get(2).unwrap_or(None),
|
||||||
|
total_fat: row.get(3).unwrap_or(None),
|
||||||
|
total_sat_fat: row.get(4).unwrap_or(None),
|
||||||
|
total_trans_fat: row.get(5).unwrap_or(None),
|
||||||
|
total_carbs: row.get(6).unwrap_or(None),
|
||||||
|
total_fiber: row.get(7).unwrap_or(None),
|
||||||
|
total_sugar: row.get(8).unwrap_or(None),
|
||||||
|
total_protein: row.get(9).unwrap_or(None),
|
||||||
|
total_sodium: row.get(10).unwrap_or(None),
|
||||||
|
total_potassium: row.get(11).unwrap_or(None),
|
||||||
|
total_cholesterol: row.get(12).unwrap_or(None)
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Ok(plan)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_all_plan(net_id: &str) -> Result<Vec<PlanData>> {
|
||||||
|
|
||||||
|
|
||||||
|
let conn = Connection::connect(ORACLE_USER, ORACLE_PASS, ORACLE_CON_STR)?;
|
||||||
|
|
||||||
|
let mut stmt = conn.statement("select * from goal where s_net_id = :1 order by week_date").build()?;
|
||||||
|
|
||||||
|
|
||||||
|
let rows = stmt.query(&[&net_id])?;
|
||||||
|
let mut row_vec: Vec<PlanData> = vec![];
|
||||||
|
|
||||||
|
for row_result in rows {
|
||||||
|
let row = row_result?;
|
||||||
|
row_vec.push(PlanData {
|
||||||
|
net_id: row.get(0)?,
|
||||||
|
week_date: row.get(1)?,
|
||||||
|
total_cal: row.get(2).unwrap_or(None),
|
||||||
|
total_fat: row.get(3).unwrap_or(None),
|
||||||
|
total_sat_fat: row.get(4).unwrap_or(None),
|
||||||
|
total_trans_fat: row.get(5).unwrap_or(None),
|
||||||
|
total_carbs: row.get(6).unwrap_or(None),
|
||||||
|
total_fiber: row.get(7).unwrap_or(None),
|
||||||
|
total_sugar: row.get(8).unwrap_or(None),
|
||||||
|
total_protein: row.get(9).unwrap_or(None),
|
||||||
|
total_sodium: row.get(10).unwrap_or(None),
|
||||||
|
total_potassium: row.get(11).unwrap_or(None),
|
||||||
|
total_cholesterol: row.get(12).unwrap_or(None)});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(row_vec)
|
||||||
|
|
||||||
|
}
|
||||||
84
backend/src/api/result.rs
Normal file
84
backend/src/api/result.rs
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
use log::error;
|
||||||
|
use actix_web::{web::Json, web::Path, Responder, HttpResponse};
|
||||||
|
use oracle::{Connection, Result};
|
||||||
|
use crate::{api::plan::PlanData, config::{ORACLE_USER, ORACLE_PASS, ORACLE_CON_STR}};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn all_result(net_id: Path<String>) -> Json<Vec<PlanData>> {
|
||||||
|
let net_id = net_id.into_inner();
|
||||||
|
|
||||||
|
match get_result(&net_id){
|
||||||
|
Ok(r) => Json(r),
|
||||||
|
Err(e) => {
|
||||||
|
error!("failed to get results for user {}: {}", net_id, e);
|
||||||
|
Json(vec![])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn add_result(result: Json<PlanData>) -> impl Responder {
|
||||||
|
let result = result.into_inner();
|
||||||
|
|
||||||
|
match push_result(&result){
|
||||||
|
Ok(_) => HttpResponse::Ok(),
|
||||||
|
Err(e) => {
|
||||||
|
error!("failed to add result for user {}: {}", result.net_id, e);
|
||||||
|
HttpResponse::InternalServerError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn get_result(net_id: &str) -> Result<Vec<PlanData>> {
|
||||||
|
|
||||||
|
let conn = Connection::connect(ORACLE_USER, ORACLE_PASS, ORACLE_CON_STR)?;
|
||||||
|
let mut stmt = conn.statement("select * from result where net_id = :1").build()?;
|
||||||
|
|
||||||
|
let rows = stmt.query(&[&net_id])?;
|
||||||
|
let mut row_vec: Vec<PlanData> = vec![];
|
||||||
|
for row_result in rows {
|
||||||
|
let row = row_result?;
|
||||||
|
|
||||||
|
row_vec.push(PlanData {
|
||||||
|
net_id: row.get(0)?,
|
||||||
|
week_date: row.get(1)?,
|
||||||
|
total_cal: row.get(2).unwrap_or(None),
|
||||||
|
total_fat: row.get(3).unwrap_or(None),
|
||||||
|
total_sat_fat: row.get(4).unwrap_or(None),
|
||||||
|
total_trans_fat: row.get(5).unwrap_or(None),
|
||||||
|
total_carbs: row.get(6).unwrap_or(None),
|
||||||
|
total_fiber: row.get(7).unwrap_or(None),
|
||||||
|
total_sugar: row.get(8).unwrap_or(None),
|
||||||
|
total_protein: row.get(9).unwrap_or(None),
|
||||||
|
total_sodium: row.get(10).unwrap_or(None),
|
||||||
|
total_potassium: row.get(11).unwrap_or(None),
|
||||||
|
total_cholesterol: row.get(12).unwrap_or(None) });
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(row_vec)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn push_result(plan: &PlanData) -> Result<()> {
|
||||||
|
let conn = Connection::connect(ORACLE_USER, ORACLE_PASS, ORACLE_CON_STR)?;
|
||||||
|
let mut stmt = conn.statement("insert into result values(:net_id, :week_date, :total_cal, :total_fat, :total_sat_fat, :total_trans_fat, :total_carbs, :total_fiber, :total_sugar, :total_protein, :total_sodium, :total_potassium, :total_cholesterol)").build()?;
|
||||||
|
|
||||||
|
stmt.execute_named(&[
|
||||||
|
("net_id", &plan.net_id),
|
||||||
|
("week_date", &plan.week_date),
|
||||||
|
("total_cal", &plan.total_cal),
|
||||||
|
("total_fat", &plan.total_fat),
|
||||||
|
("total_sat_fat", &plan.total_sat_fat),
|
||||||
|
("total_trans_fat", &plan.total_trans_fat),
|
||||||
|
("total_carbs", &plan.total_carbs),
|
||||||
|
("total_fiber", &plan.total_fiber),
|
||||||
|
("total_sugar", &plan.total_sugar),
|
||||||
|
("total_protein", &plan.total_protein),
|
||||||
|
("total_sodium", &plan.total_sodium),
|
||||||
|
("total_potassium", &plan.total_potassium),
|
||||||
|
("total_cholesterol", &plan.total_cholesterol)])?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
|
||||||
|
}
|
||||||
185
backend/src/api/user.rs
Normal file
185
backend/src/api/user.rs
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
use sha2::{Sha256, Digest};
|
||||||
|
use rand::{prelude::Rng, distributions::Alphanumeric };
|
||||||
|
use oracle::{Connection, Error};
|
||||||
|
use log::{info, warn, error};
|
||||||
|
use actix_identity::Identity;
|
||||||
|
use actix_web::{web, Responder, HttpRequest, HttpMessage, HttpResponse};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use crate::config::{ORACLE_USER, ORACLE_PASS, ORACLE_CON_STR};
|
||||||
|
|
||||||
|
static SALT_LEN: usize = 16;
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Debug, Default)]
|
||||||
|
pub struct Entry {
|
||||||
|
net_id: String,
|
||||||
|
password: String
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Debug, Default)]
|
||||||
|
pub struct User {
|
||||||
|
id: String,
|
||||||
|
first_name: String,
|
||||||
|
last_name: String
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Debug, Default)]
|
||||||
|
pub struct NewUser {
|
||||||
|
net_id: String,
|
||||||
|
password: String,
|
||||||
|
first_name: String,
|
||||||
|
last_name: String
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn login(request: HttpRequest, body: web::Json<Entry>) -> impl Responder {
|
||||||
|
//TODO: finish login, for now will simply login for everyone
|
||||||
|
let net_id: &str = body.net_id.as_str();
|
||||||
|
let password: &str = body.password.as_str();
|
||||||
|
|
||||||
|
//Identity::login(&request.extensions(), "User1".into());
|
||||||
|
println!("{:?}", request);
|
||||||
|
println!("{:?}", body);
|
||||||
|
match authenticate(net_id, password) {
|
||||||
|
Some(user) => {
|
||||||
|
web::Json(user);
|
||||||
|
HttpResponse::Ok()
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
web::Json(User::default());
|
||||||
|
HttpResponse::Unauthorized()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn logout() -> impl Responder {
|
||||||
|
HttpResponse::Ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn signup(request: HttpRequest, body: web::Json<NewUser>) -> impl Responder {
|
||||||
|
let body = body.into_inner();
|
||||||
|
|
||||||
|
match create_user(body.net_id.as_str(), body.password.as_str(), body.first_name.as_str(), body.last_name.as_str()) {
|
||||||
|
Ok(_) => {
|
||||||
|
Identity::login(&request.extensions(), body.net_id.clone()).unwrap();
|
||||||
|
web::Json(User { id: body.net_id, first_name: body.first_name, last_name: body.last_name});
|
||||||
|
HttpResponse::Ok()
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("Error creating new user: {}", e);
|
||||||
|
HttpResponse::InternalServerError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fn authenticate(username: &str, password: &str) -> Option<User> {
|
||||||
|
|
||||||
|
info!("Authenticating user: {}", username);
|
||||||
|
|
||||||
|
let conn = match Connection::connect(ORACLE_USER,ORACLE_PASS, ORACLE_CON_STR){
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(e) => {
|
||||||
|
error!("unable to open connection to server: {}", e);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mut stmt = match conn.statement("select * from student where net_id = :1").build(){
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(e) => {
|
||||||
|
error!("unable to build statement: {}", e);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let row = stmt.query_row_as::<(String, String, String, String, String)>(&[&username]).unwrap_or_default();
|
||||||
|
|
||||||
|
let true_pword = row.3;
|
||||||
|
let salt = row.4;
|
||||||
|
|
||||||
|
let mut hasher = Sha256::new();
|
||||||
|
hasher.update(password);
|
||||||
|
hasher.update(salt);
|
||||||
|
let hash = hasher.finalize();
|
||||||
|
|
||||||
|
let mut tmp: String = String::new();
|
||||||
|
for value in hash{
|
||||||
|
tmp += &format!("{:x}", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.close().unwrap_or_default();
|
||||||
|
|
||||||
|
|
||||||
|
if true_pword.eq(&tmp) {
|
||||||
|
info!("User {} successfully authenticated", username);
|
||||||
|
Some( User { id: row.0, first_name: row.1,last_name: row.2} )
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
warn!("User {} failed authentication", username);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fn create_user(username: &str, password: &str, first_name: &str, last_name: &str) -> Result<(), Error> {
|
||||||
|
|
||||||
|
info!("Creating user: {}", username);
|
||||||
|
let conn = Connection::connect(ORACLE_USER, ORACLE_PASS, ORACLE_CON_STR)?;
|
||||||
|
let mut stmt = conn.statement("insert into student values(:net_id, :first_name, :last_name, :pswd, :salt)").build()?;
|
||||||
|
|
||||||
|
let salt: String = rand::thread_rng().sample_iter(&Alphanumeric).take(SALT_LEN).map(char::from).collect();
|
||||||
|
let mut hasher = Sha256::new();
|
||||||
|
hasher.update(&password);
|
||||||
|
hasher.update(&salt);
|
||||||
|
let hash = hasher.finalize();
|
||||||
|
|
||||||
|
let mut hash_string = String::new();
|
||||||
|
|
||||||
|
for value in hash{
|
||||||
|
hash_string += &format!("{:x}", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
match stmt.execute_named(&[("net_id", &username), ("first_name", &first_name), ("last_name", &last_name), ("pswd", &hash_string), ("salt", &salt)]) {
|
||||||
|
Ok(_) => {
|
||||||
|
info!("User {} successfully created", username);
|
||||||
|
conn.commit()?;
|
||||||
|
},
|
||||||
|
Err(_) => {
|
||||||
|
warn!("Failed to create user {}", username);
|
||||||
|
conn.rollback()?;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut new_table = conn.statement(format!("create table {} ( item_id number(5), amount number(5),
|
||||||
|
item_name varchar2(128),
|
||||||
|
calories number(5),
|
||||||
|
fat_g number(5),
|
||||||
|
sat_fat_g number(5),
|
||||||
|
trans_fat_g number(5),
|
||||||
|
carbs_g number(5),
|
||||||
|
fiber_g number(5),
|
||||||
|
sugar_g number(5),
|
||||||
|
protein_g number(5),
|
||||||
|
sodium_mg number(5),
|
||||||
|
potassium_mg number(5),
|
||||||
|
cholesterol_mg number(5),
|
||||||
|
menu_item number(1),
|
||||||
|
foreign key (item_id) references menu_item (item_id))", username).as_str()).build()?;
|
||||||
|
|
||||||
|
match new_table.execute(&[]) {
|
||||||
|
Ok(_) => {
|
||||||
|
info!("User {} week table created", username);
|
||||||
|
conn.commit()?;
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
warn!("Failed to create week table for {} {}", username, e);
|
||||||
|
conn.rollback()?;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
conn.close()?;
|
||||||
|
Ok(())
|
||||||
|
|
||||||
|
}
|
||||||
293
backend/src/api/week.rs
Normal file
293
backend/src/api/week.rs
Normal file
@@ -0,0 +1,293 @@
|
|||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
use log::{error, info};
|
||||||
|
use actix_web::{self, Responder, web::{Json, Path}, HttpResponse};
|
||||||
|
use oracle::{Connection, Result};
|
||||||
|
use crate::config::{ORACLE_PASS, ORACLE_USER, ORACLE_CON_STR};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Default)]
|
||||||
|
pub struct ItemData{
|
||||||
|
net_id: String,
|
||||||
|
item_name: String,
|
||||||
|
amount: Option<f32>,
|
||||||
|
calories: Option<f32>,
|
||||||
|
fat_g: Option<f32>,
|
||||||
|
sat_fat_g: Option<f32>,
|
||||||
|
trans_fat_g: Option<f32>,
|
||||||
|
carbs_g: Option<f32>,
|
||||||
|
fiber_g: Option<f32>,
|
||||||
|
sugar_g: Option<f32>,
|
||||||
|
protein_g: Option<f32>,
|
||||||
|
sodium_mg: Option<f32>,
|
||||||
|
potassium_mg: Option<f32>,
|
||||||
|
cholesterol_mg: Option<f32>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Default)]
|
||||||
|
pub struct MenuItems {
|
||||||
|
net_id: String,
|
||||||
|
item_list: Vec<u32>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Default)]
|
||||||
|
pub struct ItemResult{
|
||||||
|
item_id: Option<u32>,
|
||||||
|
amount: Option<u32>,
|
||||||
|
item_name: Option<String>,
|
||||||
|
calories: Option<f32>,
|
||||||
|
fat_g: Option<f32>,
|
||||||
|
sat_fat_g: Option<f32>,
|
||||||
|
trans_fat_g: Option<f32>,
|
||||||
|
carbs_g: Option<f32>,
|
||||||
|
fiber_g: Option<f32>,
|
||||||
|
sugar_g: Option<f32>,
|
||||||
|
protein_g: Option<f32>,
|
||||||
|
sodium_mg: Option<f32>,
|
||||||
|
potassium_mg: Option<f32>,
|
||||||
|
cholesterol_mg: Option<f32>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Default)]
|
||||||
|
pub struct Sums{
|
||||||
|
calories: f32,
|
||||||
|
fat_g: f32,
|
||||||
|
sat_fat_g: f32,
|
||||||
|
trans_fat_g: f32,
|
||||||
|
carbs_g: f32,
|
||||||
|
fiber_g: f32,
|
||||||
|
sugar_g: f32,
|
||||||
|
protein_g: f32,
|
||||||
|
sodium_mg: f32,
|
||||||
|
potassium_mg: f32,
|
||||||
|
cholesterol_mg: f32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn week(item: Json<ItemData>) -> impl Responder {
|
||||||
|
|
||||||
|
let item = item.into_inner();
|
||||||
|
match add_item(&item) {
|
||||||
|
Ok(_) => HttpResponse::Ok(),
|
||||||
|
Err(e) => {
|
||||||
|
error!("Unable to add item to table {}: {}", item.net_id, e);
|
||||||
|
HttpResponse::InternalServerError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn week_meals(items: Json<MenuItems>) -> impl Responder {
|
||||||
|
let items = items.into_inner();
|
||||||
|
let netid = items.net_id.clone();
|
||||||
|
|
||||||
|
match add_menu_items(items) {
|
||||||
|
Ok(_) => HttpResponse::Ok(),
|
||||||
|
Err(e) => {
|
||||||
|
error!("Unable to add menu items to table {}: {}", netid, e);
|
||||||
|
HttpResponse::InternalServerError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn week_lookup(net_id: Path<String>) -> Json<Vec<ItemResult>> {
|
||||||
|
|
||||||
|
let net_id = net_id.into_inner();
|
||||||
|
|
||||||
|
match get_week(&net_id) {
|
||||||
|
Ok(week) => Json(week),
|
||||||
|
Err(e) => {
|
||||||
|
error!("failed to grab week info from {}: {}", net_id, e);
|
||||||
|
Json(vec![])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn week_sums(net_id: Path<String>) -> Json<Sums> {
|
||||||
|
let net_id = net_id.into_inner();
|
||||||
|
|
||||||
|
let week = match get_week(&net_id) {
|
||||||
|
Ok(week) => week,
|
||||||
|
Err(e) => {
|
||||||
|
error!("failed to grab week info from {}: {}", net_id, e);
|
||||||
|
return Json(Sums::default());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut sums = Sums::default();
|
||||||
|
for item in week{
|
||||||
|
sums.calories += item.calories.unwrap_or_default();
|
||||||
|
sums.fat_g += item.fat_g.unwrap_or_default();
|
||||||
|
sums.sat_fat_g += item.sat_fat_g.unwrap_or_default();
|
||||||
|
sums.trans_fat_g += item.trans_fat_g.unwrap_or_default();
|
||||||
|
sums.carbs_g += item.carbs_g.unwrap_or_default();
|
||||||
|
sums.fiber_g += item.fiber_g.unwrap_or_default();
|
||||||
|
sums.sugar_g += item.sugar_g.unwrap_or_default();
|
||||||
|
sums.protein_g += item.protein_g.unwrap_or_default();
|
||||||
|
sums.sodium_mg += item.sodium_mg.unwrap_or_default();
|
||||||
|
sums.potassium_mg += item.potassium_mg.unwrap_or_default();
|
||||||
|
sums.cholesterol_mg += item.cholesterol_mg.unwrap_or_default();
|
||||||
|
}
|
||||||
|
|
||||||
|
Json(sums)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn delete_meals(items: Json<MenuItems> ) -> impl Responder {
|
||||||
|
let items = items.into_inner();
|
||||||
|
|
||||||
|
match week_meal_delete(&items) {
|
||||||
|
Ok(_) => HttpResponse::Ok(),
|
||||||
|
Err(e) => {
|
||||||
|
error!("failed to delete items from {}: {}", items.net_id, e);
|
||||||
|
HttpResponse::InternalServerError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn add_item(item: &ItemData) -> Result<()> {
|
||||||
|
let conn = Connection::connect(ORACLE_USER, ORACLE_PASS, ORACLE_CON_STR)? ;
|
||||||
|
|
||||||
|
let mut stmt = conn.statement(format!("insert into {} values (NULL, :amount, :item_name,
|
||||||
|
:calories,
|
||||||
|
:fat_g,
|
||||||
|
:sat_fat_g,
|
||||||
|
:trans_fat_g,
|
||||||
|
:carbs_g,
|
||||||
|
:fiber_g,
|
||||||
|
:sugar_g,
|
||||||
|
:protein_g,
|
||||||
|
:sodium_mg,
|
||||||
|
:potassium_mg,
|
||||||
|
:cholesterol_mg,
|
||||||
|
0)", item.net_id).as_str()).build()?;
|
||||||
|
|
||||||
|
stmt.execute_named(&[
|
||||||
|
("amount", &item.amount),
|
||||||
|
("item_name", &item.item_name),
|
||||||
|
("calories", &item.calories),
|
||||||
|
("fat_g", &item.fat_g),
|
||||||
|
("sat_fat_g", &item.sat_fat_g),
|
||||||
|
("trans_fat_g", &item.trans_fat_g),
|
||||||
|
("carbs_g", &item.carbs_g),
|
||||||
|
("fiber_g", &item.fiber_g),
|
||||||
|
("sugar_g", &item.sugar_g),
|
||||||
|
("protein_g", &item.protein_g),
|
||||||
|
("sodium_mg", &item.sodium_mg),
|
||||||
|
("potassium_mg", &item.potassium_mg),
|
||||||
|
("cholesterol_mg", &item.cholesterol_mg)])?;
|
||||||
|
|
||||||
|
conn.commit()?;
|
||||||
|
conn.close()?;
|
||||||
|
info!("Added item {} to table {}", item.item_name, item.net_id);
|
||||||
|
Ok(())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn add_menu_items(items: MenuItems) -> Result<()> {
|
||||||
|
let conn = Connection::connect(ORACLE_USER,ORACLE_PASS, ORACLE_CON_STR)?;
|
||||||
|
|
||||||
|
let mut stmt = conn.statement(format!("insert into {} values (
|
||||||
|
:item_id,
|
||||||
|
NULL,
|
||||||
|
:item_name,
|
||||||
|
:calories,
|
||||||
|
:fat_g,
|
||||||
|
:sat_fat_g,
|
||||||
|
:trans_fat_g,
|
||||||
|
:carbs_g,
|
||||||
|
:fiber_g,
|
||||||
|
:sugar_g,
|
||||||
|
:protein_g,
|
||||||
|
:sodium_mg,
|
||||||
|
:potassium_mg,
|
||||||
|
:cholesterol_mg,
|
||||||
|
1)", items.net_id).as_str()).build()?;
|
||||||
|
|
||||||
|
let mut lookup = conn.statement("select * from nutrition_info natural join menu_item where item_id = :item_id").build()?;
|
||||||
|
|
||||||
|
for item in items.item_list {
|
||||||
|
let row = lookup.query_row_named(&[("item_id", &item)])?;
|
||||||
|
let result: ItemData = ItemData {
|
||||||
|
net_id: "".to_string(),
|
||||||
|
item_name: row.get(13).unwrap_or("".to_string()),
|
||||||
|
amount: None,
|
||||||
|
calories: row.get(2).unwrap_or(None),
|
||||||
|
fat_g: row.get(3).unwrap_or(None),
|
||||||
|
sat_fat_g: row.get(4).unwrap_or(None),
|
||||||
|
trans_fat_g: row.get(5).unwrap_or(None),
|
||||||
|
carbs_g: row.get(6).unwrap_or(None),
|
||||||
|
fiber_g: row.get(7).unwrap_or(None),
|
||||||
|
sugar_g: row.get(8).unwrap_or(None),
|
||||||
|
protein_g: row.get(9).unwrap_or(None),
|
||||||
|
sodium_mg: row.get(10).unwrap_or(None),
|
||||||
|
potassium_mg: row.get(11).unwrap_or(None),
|
||||||
|
cholesterol_mg: row.get(12).unwrap_or(None) };
|
||||||
|
|
||||||
|
stmt.execute_named(&[
|
||||||
|
("item_id",&item),
|
||||||
|
("item_name", &result.item_name),
|
||||||
|
("calories", &result.calories),
|
||||||
|
("fat_g", &result.fat_g),
|
||||||
|
("sat_fat_g", &result.sat_fat_g),
|
||||||
|
("trans_fat_g", &result.trans_fat_g),
|
||||||
|
("carbs_g", &result.carbs_g),
|
||||||
|
("fiber_g", &result.fiber_g),
|
||||||
|
("sugar_g", &result.sugar_g),
|
||||||
|
("protein_g", &result.protein_g),
|
||||||
|
("sodium_mg", &result.sodium_mg),
|
||||||
|
("potassium_mg", &result.potassium_mg),
|
||||||
|
("cholesterol_mg", &result.cholesterol_mg)
|
||||||
|
])?;
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.commit()?;
|
||||||
|
conn.close()?;
|
||||||
|
info!("inserted menu items into week table {}", items.net_id);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn get_week(net_id: &str) -> Result<Vec<ItemResult>> {
|
||||||
|
|
||||||
|
let conn = Connection::connect(ORACLE_USER, ORACLE_PASS, ORACLE_CON_STR)?;
|
||||||
|
let mut stmt = conn.statement(format!("select * from {}", net_id).as_str()).build()?;
|
||||||
|
|
||||||
|
let rows = stmt.query(&[])?;
|
||||||
|
let mut week = vec![];
|
||||||
|
|
||||||
|
for row_result in rows {
|
||||||
|
let row = row_result?;
|
||||||
|
week.push( ItemResult {
|
||||||
|
item_id: row.get(0).unwrap_or(None),
|
||||||
|
amount: row.get(1).unwrap_or(None),
|
||||||
|
item_name: row.get(2).unwrap_or(None),
|
||||||
|
calories: row.get(3).unwrap_or(None),
|
||||||
|
fat_g: row.get(4).unwrap_or(None),
|
||||||
|
sat_fat_g: row.get(5).unwrap_or(None),
|
||||||
|
trans_fat_g: row.get(6).unwrap_or(None),
|
||||||
|
carbs_g: row.get(7).unwrap_or(None),
|
||||||
|
fiber_g: row.get(8).unwrap_or(None),
|
||||||
|
sugar_g: row.get(9).unwrap_or(None),
|
||||||
|
protein_g: row.get(10).unwrap_or(None),
|
||||||
|
sodium_mg: row.get(11).unwrap_or(None),
|
||||||
|
potassium_mg: row.get(12).unwrap_or(None),
|
||||||
|
cholesterol_mg: row.get(13).unwrap_or(None) });
|
||||||
|
}
|
||||||
|
Ok(week)
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn week_meal_delete(items: &MenuItems) -> Result<()> {
|
||||||
|
|
||||||
|
let conn = Connection::connect(ORACLE_USER, ORACLE_PASS, ORACLE_CON_STR)?;
|
||||||
|
|
||||||
|
let mut stmt = conn.statement(format!("delete from {} where item_id = :1", items.net_id).as_str()).build()?;
|
||||||
|
|
||||||
|
for meal in &items.item_list {
|
||||||
|
stmt.execute(&[meal])?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
4
backend/src/config.rs
Normal file
4
backend/src/config.rs
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
pub const ORACLE_USER: &str = "timmy";
|
||||||
|
pub const ORACLE_PASS: &str = "timmy";
|
||||||
|
pub const ORACLE_CON_STR: &str = "";
|
||||||
|
|
||||||
116
backend/src/main.rs
Normal file
116
backend/src/main.rs
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
use env_logger::Env;
|
||||||
|
use actix_web::{web, App, HttpResponse, HttpServer, Responder, middleware, cookie::Key};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use actix_cors::Cors;
|
||||||
|
use actix_identity::IdentityMiddleware;
|
||||||
|
use actix_session::{SessionMiddleware, storage::CookieSessionStore};
|
||||||
|
|
||||||
|
mod api;
|
||||||
|
mod config;
|
||||||
|
|
||||||
|
static PORT: u16 = 8000;
|
||||||
|
const ALLOWED_ORIGIN: &str = "http://localhost:8009";
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Serialize, Deserialize, Clone)]
|
||||||
|
struct Login {
|
||||||
|
net_id: String,
|
||||||
|
password: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[actix_web::main]
|
||||||
|
async fn main() -> std::io::Result<()> {
|
||||||
|
//init logging system
|
||||||
|
let env = Env::default().filter_or("LOG_LEVEL", "info");
|
||||||
|
env_logger::init_from_env(env);
|
||||||
|
|
||||||
|
let secret_key = Key::generate();
|
||||||
|
|
||||||
|
let _ = HttpServer::new(move || {
|
||||||
|
App::new()
|
||||||
|
.wrap(middleware::Logger::default())
|
||||||
|
.wrap(
|
||||||
|
Cors::default()
|
||||||
|
.allowed_origin(ALLOWED_ORIGIN)
|
||||||
|
.allowed_methods(vec!["GET","POST","DELETE"])
|
||||||
|
.supports_credentials()
|
||||||
|
.allow_any_header()
|
||||||
|
.expose_any_header()
|
||||||
|
)
|
||||||
|
.wrap(IdentityMiddleware::default())
|
||||||
|
.wrap(SessionMiddleware::new(CookieSessionStore::default(), secret_key.clone()))
|
||||||
|
.service(
|
||||||
|
web::scope("/api")
|
||||||
|
.service(
|
||||||
|
web::resource("/auth")
|
||||||
|
.route(web::post().to(api::user::login))
|
||||||
|
.route(web::delete().to(api::user::logout))
|
||||||
|
)
|
||||||
|
.service(
|
||||||
|
web::resource("/signup")
|
||||||
|
.route(web::post().to(api::user::signup))
|
||||||
|
)
|
||||||
|
.service(
|
||||||
|
web::resource("/eatery/{eatery_id}")
|
||||||
|
.route(web::get().to(api::eatery::menu))
|
||||||
|
)
|
||||||
|
.service(
|
||||||
|
web::resource("/goal")
|
||||||
|
.route(web::post().to(api::plan::plan))
|
||||||
|
)
|
||||||
|
.service(
|
||||||
|
web::resource("/goal/{net_id}")
|
||||||
|
.route(web::get().to(api::plan::plan_lookup))
|
||||||
|
)
|
||||||
|
.service(
|
||||||
|
web::resource("/week_plan")
|
||||||
|
.route(web::post().to(api::week::week))
|
||||||
|
)
|
||||||
|
.service(
|
||||||
|
web::resource("/week_meals")
|
||||||
|
.route(web::post().to(api::week::week_meals))
|
||||||
|
.route(web::delete().to(api::week::delete_meals))
|
||||||
|
)
|
||||||
|
.service(
|
||||||
|
web::resource("/week_progress/{net_id}")
|
||||||
|
.route(web::get().to(api::week::week_lookup))
|
||||||
|
)
|
||||||
|
.service(
|
||||||
|
web::resource("/menu_search")
|
||||||
|
.route(web::post().to(api::menu::menu_search))
|
||||||
|
)
|
||||||
|
.service(
|
||||||
|
web::resource("week_sum/{net_id}")
|
||||||
|
.route(web::get().to(api::week::week_sums))
|
||||||
|
)
|
||||||
|
.service(
|
||||||
|
web::resource("all_goal/{net_id}")
|
||||||
|
.route(web::get().to(api::plan::all_plan))
|
||||||
|
)
|
||||||
|
.service(
|
||||||
|
web::resource("result/{net_id}")
|
||||||
|
.route(web::get().to(api::result::all_result))
|
||||||
|
)
|
||||||
|
.service(
|
||||||
|
web::resource("/add_result")
|
||||||
|
.route(web::post().to(api::result::add_result))
|
||||||
|
)
|
||||||
|
.route("/", web::get().to(api_index))
|
||||||
|
)
|
||||||
|
.route("/", web::get().to(index))
|
||||||
|
})
|
||||||
|
.bind(("0.0.0.0", PORT))?
|
||||||
|
.run()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn api_index() -> impl Responder {
|
||||||
|
HttpResponse::Ok().body("api")
|
||||||
|
}
|
||||||
|
async fn index() -> impl Responder {
|
||||||
|
HttpResponse::Ok().body("/")
|
||||||
|
}
|
||||||
|
|
||||||
66
src/main.rs
66
src/main.rs
@@ -1,66 +0,0 @@
|
|||||||
use log::{info, warn, error};
|
|
||||||
use env_logger::Env;
|
|
||||||
use actix_web::{web, get, post, web::Json, App, HttpResponse, HttpServer, Responder};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
mod security;
|
|
||||||
|
|
||||||
static PORT: u16 = 8009;
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Default, Debug, Serialize, Deserialize, Clone)]
|
|
||||||
struct Login {
|
|
||||||
net_id: String,
|
|
||||||
password: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[actix_web::main]
|
|
||||||
async fn main() -> std::io::Result<()> {
|
|
||||||
|
|
||||||
//init logging system
|
|
||||||
let env = Env::default().filter_or("LOG_LEVEL", "info");
|
|
||||||
env_logger::init_from_env(env);
|
|
||||||
|
|
||||||
let _ = HttpServer::new( || {
|
|
||||||
App::new()
|
|
||||||
.service(index)
|
|
||||||
.service(login)
|
|
||||||
.service(homepage)
|
|
||||||
.service(plan_page)
|
|
||||||
})
|
|
||||||
.bind(("0.0.0.0", PORT))?
|
|
||||||
.run()
|
|
||||||
.await;
|
|
||||||
//Temporary for testing purposes, should write something to make a random salt
|
|
||||||
let username = "cmckechn";
|
|
||||||
let password = "password";
|
|
||||||
|
|
||||||
//proof of concept tests, create_user should fail in this instance because user was already
|
|
||||||
//created
|
|
||||||
security::authenticate(username, password).unwrap();
|
|
||||||
security::create_user("test", "test_create", "test_first", "test_last").unwrap();
|
|
||||||
security::authenticate("test", "test_create").unwrap();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/")]
|
|
||||||
async fn index() -> impl Responder {
|
|
||||||
HttpResponse::Ok().body("Hello world!")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/login")]
|
|
||||||
async fn login(json: Json<Login>) -> Result<String, actix_web::Error> {
|
|
||||||
Ok(format!("{} {}", json.net_id, json.password))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/{net_id}/home")]
|
|
||||||
async fn homepage(path: web::Path<String>) -> impl Responder {
|
|
||||||
let net_id = path.into_inner();
|
|
||||||
HttpResponse::Ok().body(format!("You have reached the homepage of {} user", net_id) )
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/{net_id}/plans")]
|
|
||||||
async fn plan_page(path: web::Path<String>) -> impl Responder {
|
|
||||||
let net_id = path.into_inner();
|
|
||||||
HttpResponse::Ok().body(format!("You have reached the plan page of {}", net_id))
|
|
||||||
}
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
use sha2::{Sha256, Digest};
|
|
||||||
use rand::{prelude::Rng, distributions::Alphanumeric };
|
|
||||||
use oracle::{Connection, Error};
|
|
||||||
use log::{info, warn};
|
|
||||||
|
|
||||||
static SQL_USERNAME: &str = "group09_user";
|
|
||||||
static SQL_PASSWORD: &str = "group09_user";
|
|
||||||
static SALT_LEN: usize = 16;
|
|
||||||
|
|
||||||
pub fn authenticate(username: &str, password: &str) -> Result<bool, Error> {
|
|
||||||
|
|
||||||
info!("Authenticating user: {}", username);
|
|
||||||
|
|
||||||
let conn = Connection::connect(SQL_USERNAME,SQL_PASSWORD, "")?;
|
|
||||||
let mut stmt = conn.statement("select password, salt from student where net_id = :1").build()?;
|
|
||||||
let row = stmt.query_row_as::<(String, String)>(&[&username])?;
|
|
||||||
|
|
||||||
|
|
||||||
let true_pword = row.0;
|
|
||||||
let salt = row.1;
|
|
||||||
|
|
||||||
let mut hasher = Sha256::new();
|
|
||||||
hasher.update(password);
|
|
||||||
hasher.update(salt);
|
|
||||||
let hash = hasher.finalize();
|
|
||||||
|
|
||||||
let mut tmp: String = String::new();
|
|
||||||
for value in hash{
|
|
||||||
tmp += &format!("{:x}", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
conn.close()?;
|
|
||||||
|
|
||||||
|
|
||||||
if true_pword.eq(&tmp) {
|
|
||||||
info!("User {} successfully authenticated", username);
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
warn!("User {} failed authentication", username);
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pub fn create_user(username: &str, password: &str, first_name: &str, last_name: &str) -> Result<(), Error> {
|
|
||||||
|
|
||||||
info!("Creating user: {}", username);
|
|
||||||
let conn = Connection::connect(SQL_USERNAME, SQL_PASSWORD, "")?;
|
|
||||||
let mut stmt = conn.statement("insert into student values(:net_id, :first_name, :last_name, :password, :salt)").build()?;
|
|
||||||
|
|
||||||
let salt: String = rand::thread_rng().sample_iter(&Alphanumeric).take(SALT_LEN).map(char::from).collect();
|
|
||||||
let mut hasher = Sha256::new();
|
|
||||||
hasher.update(&password);
|
|
||||||
hasher.update(&salt);
|
|
||||||
let hash = hasher.finalize();
|
|
||||||
|
|
||||||
let mut hash_string = String::new();
|
|
||||||
|
|
||||||
for value in hash{
|
|
||||||
hash_string += &format!("{:x}", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
match stmt.execute_named(&[("net_id", &username), ("first_name", &first_name), ("last_name", &last_name), ("password", &hash_string), ("salt", &salt)]) {
|
|
||||||
Ok(_) => {
|
|
||||||
info!("User {} successfully created", username);
|
|
||||||
conn.commit()?;
|
|
||||||
},
|
|
||||||
Err(_) => {
|
|
||||||
warn!("Failed to create user {}", username);
|
|
||||||
conn.rollback()?;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
conn.close()?;
|
|
||||||
Ok(())
|
|
||||||
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user