From b1feb8620427df9c26ee9a8279181de6bc76f52f Mon Sep 17 00:00:00 2001 From: Araozu Date: Sat, 10 Aug 2024 16:50:09 -0500 Subject: [PATCH] Database setup --- .env.example | 4 + .gitignore | 1 + Cargo.lock | 270 ++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 12 +- db/docker-compose.yml | 21 ++++ db/schema.sql | 6 + src/controller/mod.rs | 3 + src/main.rs | 73 +++++++++++- 8 files changed, 376 insertions(+), 14 deletions(-) create mode 100644 .env.example create mode 100644 db/docker-compose.yml diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..7eccb1f --- /dev/null +++ b/.env.example @@ -0,0 +1,4 @@ +# Postgres database URL +DATABASE_URL=postgres://postgres:password@localhost:3306/database +# Logger level +RUST_LOG=info diff --git a/.gitignore b/.gitignore index 74b2b6c..b74cff8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target node_modules /static/out.css +.env diff --git a/Cargo.lock b/Cargo.lock index 2775aca..d85a2fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -80,6 +80,21 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "async-stream" version = "0.3.5" @@ -210,6 +225,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + [[package]] name = "bytemuck" version = "1.14.1" @@ -243,6 +264,18 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "windows-targets 0.52.0", +] + [[package]] name = "cipher" version = "0.4.4" @@ -276,6 +309,12 @@ dependencies = [ "version_check", ] +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + [[package]] name = "cpufeatures" version = "0.2.12" @@ -424,6 +463,19 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "env_logger" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -491,7 +543,7 @@ checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" dependencies = [ "futures-core", "futures-sink", - "spin 0.9.8", + "spin", ] [[package]] @@ -783,6 +835,12 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + [[package]] name = "hyper" version = "0.14.28" @@ -807,6 +865,29 @@ dependencies = [ "want", ] +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "idna" version = "0.5.0" @@ -873,18 +954,31 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" name = "jerguero" version = "0.1.0" dependencies = [ + "dotenvy", + "env_logger", + "log", "maud", + "once_cell", "rocket", "sqlx", ] [[package]] -name = "lazy_static" -version = "1.4.0" +name = "js-sys" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ - "spin 0.5.2", + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", ] [[package]] @@ -1040,7 +1134,7 @@ dependencies = [ "httparse", "memchr", "mime", - "spin 0.9.8", + "spin", "tokio", "tokio-util", "version_check", @@ -1455,6 +1549,21 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "rocket" version = "0.5.1" @@ -1575,6 +1684,36 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.14" @@ -1599,6 +1738,16 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "serde" version = "1.0.196" @@ -1714,12 +1863,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - [[package]] name = "spin" version = "0.9.8" @@ -1773,6 +1916,7 @@ dependencies = [ "atoi", "byteorder", "bytes", + "chrono", "crc", "crossbeam-queue", "dotenvy", @@ -1791,14 +1935,19 @@ dependencies = [ "once_cell", "paste", "percent-encoding", + "rustls", + "rustls-pemfile", "serde", "serde_json", "sha2", "smallvec", "sqlformat", "thiserror", + "tokio", + "tokio-stream", "tracing", "url", + "webpki-roots", ] [[package]] @@ -1837,6 +1986,7 @@ dependencies = [ "sqlx-sqlite", "syn 1.0.109", "tempfile", + "tokio", "url", ] @@ -1851,6 +2001,7 @@ dependencies = [ "bitflags 2.4.2", "byteorder", "bytes", + "chrono", "crc", "digest", "dotenvy", @@ -1892,6 +2043,7 @@ dependencies = [ "base64", "bitflags 2.4.2", "byteorder", + "chrono", "crc", "dotenvy", "etcetera", @@ -1928,6 +2080,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "210976b7d948c7ba9fced8ca835b11cbb2d677c59c79de41ac0d397e14547490" dependencies = [ "atoi", + "chrono", "flume", "futures-channel", "futures-core", @@ -2014,6 +2167,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + [[package]] name = "thiserror" version = "1.0.56" @@ -2326,6 +2488,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "2.5.0" @@ -2376,6 +2544,66 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.48", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + [[package]] name = "whoami" version = "1.4.1" @@ -2398,6 +2626,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -2413,6 +2650,15 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.0", +] + [[package]] name = "windows-sys" version = "0.48.0" diff --git a/Cargo.toml b/Cargo.toml index 588ac4e..10d417e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,5 +7,15 @@ edition = "2021" [dependencies] maud = { version = "0.26.0", features = ["rocket"] } +once_cell = "1.19.0" rocket = { version = "0.5.1", features = ["secrets"] } -sqlx = { version = "0.7.3", features = ["postgres"] } +sqlx = { version = "0.7.3", features = [ + "runtime-tokio", + "tls-rustls", + "macros", + "chrono", + "postgres", +] } +log = "0.4.20" +env_logger = "0.10.0" +dotenvy = "0.15.7" diff --git a/db/docker-compose.yml b/db/docker-compose.yml new file mode 100644 index 0000000..d7818ed --- /dev/null +++ b/db/docker-compose.yml @@ -0,0 +1,21 @@ +# Use postgres/example user/password credentials +version: '3.9' + +services: + db: + container_name: my-postgres + image: postgres + restart: always + # set shared memory limit when using docker-compose + shm_size: 128mb + # or set shared memory limit when deploy via swarm stack + #volumes: + # - type: tmpfs + # target: /dev/shm + # tmpfs: + # size: 134217728 # 128*2^20 bytes = 128Mb + environment: + POSTGRES_PASSWORD: 1234567890 + POSTGRES_DB: jerguero + ports: + - 5432:5432 diff --git a/db/schema.sql b/db/schema.sql index aa0ec4a..6b39ed2 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -1 +1,7 @@ -- PostgreSQL schema for the system + +create table person( + person_id serial primary key, + person_email varchar(100), + person_password varchar(100) +); diff --git a/src/controller/mod.rs b/src/controller/mod.rs index c3d9da4..690a325 100644 --- a/src/controller/mod.rs +++ b/src/controller/mod.rs @@ -36,6 +36,9 @@ pub fn login_page() -> Markup { #[post("/login", data = "")] pub async fn login(data: Form) -> (Status, String) { println!("begin request: {}", data.login_email); + + + // Simulate trip to db sleep(Duration::new(5, 0)).await; println!("end request: {}", data.login_password); diff --git a/src/main.rs b/src/main.rs index 5703955..5335336 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,10 +4,21 @@ extern crate rocket; mod controller; mod view; +use std::{env, time::Instant}; + +use once_cell::sync::OnceCell; use rocket::fs::{relative, FileServer}; +use sqlx::{postgres::PgPoolOptions, Pool, Postgres}; + +static DB: OnceCell> = OnceCell::new(); #[launch] -fn rocket() -> _ { +async fn rocket() -> _ { + dotenvy::dotenv().expect("Failed to load .env file"); + env_logger::init(); + + init_db().await; + rocket::build() .mount( "/", @@ -21,3 +32,63 @@ fn rocket() -> _ { ) .mount("/static", FileServer::from(relative!("static"))) } + +/// Returns a handle to a database connection +async fn db() -> Result<&'static Pool, String> { + let attempts = 3; + + for _ in 0..attempts { + match DB.get() { + Some(db) => { + // log::info!("DB active connections: {}", db.size()); + // log::info!("DB num_idle connections: {}", db.num_idle()); + + return Ok(db); + } + None => { + log::info!("DB not initialized, initializing from db()"); + let _ = init_db().await; + } + } + } + + log::error!("Failed to initialize DB after {} attempts", attempts); + Err("Failed to initialize DB".to_string()) +} + +async fn init_db() { + /* + Init DB and set it as a global variable + */ + let db_url = match env::var("DATABASE_URL") { + Ok(url) => url, + Err(_) => panic!("env DATABASE_URL not found"), + }; + + let start = Instant::now(); + let pool = PgPoolOptions::new() + .max_connections(5) + // Set the maximum wait time for connections to 10 seconds + // In practice, the slowest connections take 1.5 seconds to connect + .acquire_timeout(std::time::Duration::from_secs(10)) + // Set the maximum idle time for connections to 10 minutes + .idle_timeout(std::time::Duration::from_secs(10 * 60)) + .connect(db_url.as_str()) + .await; + log::info!( + "DB Pool connection took: {:?} ms", + start.elapsed().as_millis() + ); + + match pool { + Ok(pool) => match DB.set(pool) { + Ok(_) => {} + Err(err) => { + panic!("Failed to set DB global variable:\n{:?}", err); + } + }, + Err(e) => { + panic!("Error connecting to DB:\n{}", e); + } + } +}