From 6ce6b9289a003bd9285bf03e377e1d8206e01dd1 Mon Sep 17 00:00:00 2001 From: Alexander Ng Date: Tue, 10 Dec 2024 03:04:55 -0800 Subject: [PATCH] got nested routing working --- Cargo.lock | 237 ++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 10 +- docker-compose.yml | 2 +- src/db.rs | 11 ++ src/main.rs | 45 +++++--- src/state.rs | 8 ++ src/structs/mod.rs | 1 + src/structs/users.rs | 9 ++ src/v1/auth/mod.rs | 10 ++ src/v1/auth/signup.rs | 14 ++- 10 files changed, 328 insertions(+), 19 deletions(-) create mode 100644 src/db.rs create mode 100644 src/state.rs create mode 100644 src/structs/mod.rs create mode 100644 src/structs/users.rs diff --git a/Cargo.lock b/Cargo.lock index eba9d89..36e7f3c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -44,6 +44,21 @@ version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +[[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 = "anyhow" version = "1.0.92" @@ -56,6 +71,8 @@ version = "0.1.0" dependencies = [ "anyhow", "axum", + "chrono", + "dotenv", "serde", "serde_json", "sqlx", @@ -64,6 +81,7 @@ dependencies = [ "utoipa", "utoipa-axum", "utoipa-swagger-ui", + "uuid", ] [[package]] @@ -234,6 +252,20 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets 0.52.6", +] + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -249,6 +281,12 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + [[package]] name = "cpufeatures" version = "0.2.14" @@ -352,6 +390,12 @@ dependencies = [ "syn", ] +[[package]] +name = "dotenv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" + [[package]] name = "dotenvy" version = "0.15.7" @@ -697,6 +741,29 @@ dependencies = [ "tower-service", ] +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +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" @@ -724,6 +791,16 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "js-sys" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -1105,6 +1182,21 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[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 = "rsa" version = "0.9.6" @@ -1178,6 +1270,46 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustls" +version = "0.23.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9cc1d47e243d655ace55ed38201c19ae02c148ae56412ab8750e8f0166ab7f" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" + +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.18" @@ -1391,6 +1523,7 @@ dependencies = [ "atoi", "byteorder", "bytes", + "chrono", "crc", "crossbeam-queue", "either", @@ -1409,14 +1542,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]] @@ -1454,6 +1592,7 @@ dependencies = [ "sqlx-sqlite", "syn", "tempfile", + "tokio", "url", ] @@ -1468,6 +1607,7 @@ dependencies = [ "bitflags", "byteorder", "bytes", + "chrono", "crc", "digest", "dotenvy", @@ -1509,6 +1649,7 @@ dependencies = [ "base64", "bitflags", "byteorder", + "chrono", "crc", "dotenvy", "etcetera", @@ -1544,6 +1685,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5b2cf34a45953bfd3daaf3db0f7a7878ab9b7a6b91b422d24a7a9e4c857b680" dependencies = [ "atoi", + "chrono", "flume", "futures-channel", "futures-core", @@ -1677,6 +1819,17 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-stream" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + [[package]] name = "tower" version = "0.5.1" @@ -1782,6 +1935,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" +[[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.2" @@ -1847,6 +2006,12 @@ dependencies = [ "zip", ] +[[package]] +name = "uuid" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" + [[package]] name = "vcpkg" version = "0.2.15" @@ -1881,6 +2046,69 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" +[[package]] +name = "wasm-bindgen" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" + +[[package]] +name = "webpki-roots" +version = "0.26.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "whoami" version = "1.5.2" @@ -1900,6 +2128,15 @@ dependencies = [ "windows-sys 0.59.0", ] +[[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.6", +] + [[package]] name = "windows-sys" version = "0.48.0" diff --git a/Cargo.toml b/Cargo.toml index b955903..fe8390b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,4 +13,12 @@ utoipa-axum = "0.1.2" serde = "1" serde_json = "1" anyhow = "1.0.92" -sqlx = "0.8.2" +sqlx = { version = "0.8.2", features = [ + "postgres", + "chrono", + "runtime-tokio-rustls", + "macros", +] } +uuid = "1.11.0" +chrono = "0.4.39" +dotenv = "0.15.0" diff --git a/docker-compose.yml b/docker-compose.yml index cc36512..56139bf 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,7 +9,7 @@ services: POSTGRES_PASSWORD: postgres POSTGRES_DB: postgres volumes: - - ./postgres-data:/var/lib/postgresql/data + - ~/databases/uplifting-api-postgres-data:/var/lib/postgresql/data volumes: postgres-data: diff --git a/src/db.rs b/src/db.rs new file mode 100644 index 0000000..5db6739 --- /dev/null +++ b/src/db.rs @@ -0,0 +1,11 @@ +use anyhow::{Context, Result}; +use sqlx::postgres::PgPoolOptions; + +pub async fn db() -> Result> { + let db_url = std::env::var("DATABASE_URL").context("DATABASE_URL must be set")?; + + Ok(PgPoolOptions::new() + .max_connections(5) + .connect(&db_url) + .await?) +} diff --git a/src/main.rs b/src/main.rs index 48ca7cc..fc1fbec 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,23 +1,30 @@ -use std::net::Ipv4Addr; +use std::{net::Ipv4Addr, sync::Arc}; use tokio::net::TcpListener; use utoipa::OpenApi; use utoipa_axum::{router::OpenApiRouter, routes}; use utoipa_swagger_ui::SwaggerUi; +// tags +use v1::auth::AUTH_TAG; + mod v1; +mod structs; +mod state; +mod db; #[derive(OpenApi)] #[openapi( tags( + (name = AUTH_TAG, description = "Authentication API endpoints"), // (name = CUSTOMER_TAG, description = "Customer API endpoints"), // (name = ORDER_TAG, description = "Order API endpoints") - ) + ), )] struct ApiDoc; /// Get health of the API. #[utoipa::path( - method(get, head), + get, path = "/.well-known/health-check", responses( (status = OK, description = "Success", body = str, content_type = "text/plain") @@ -27,22 +34,36 @@ async fn health_check() -> &'static str { "ok" } +#[utoipa::path( + get, + path = "/", + responses( + (status = OK, description = "Success", body = str, content_type = "text/plain") + ) +)] +async fn index() -> &'static str { + "ok 200" +} + #[tokio::main] async fn main() -> anyhow::Result<()> { + dotenv::dotenv().ok(); + let db = db::db().await?; + let state = state::AppState { db: Arc::new(db) }; + + let port: u16 = std::env::var("PORT").unwrap_or_else(|_| "8080".to_string()).parse()?; + let (router, api) = OpenApiRouter::with_openapi(ApiDoc::openapi()) .routes(routes!(health_check)) - // .nest("/api/customer", customer::router()) - // .nest("/api/order", order::router()) - // .routes(routes!( - // inner::secret_handlers::get_secret, - // inner::secret_handlers::post_secret - // )) + .routes(routes!(index)) + .with_state(state.clone()) + .nest("/api/v1/auth", v1::auth::router(state.clone())) .split_for_parts(); - let router = router.merge(SwaggerUi::new("/swagger-ui").url("/apidoc/openapi.json", api)); + let router = router.merge(SwaggerUi::new("/docs").url("/docs/openapi.json", api)); - println!("Listening on http://localhost:8080"); - let listener = TcpListener::bind((Ipv4Addr::LOCALHOST, 8080)).await?; + println!("Listening on http://localhost:{port}"); + let listener = TcpListener::bind((Ipv4Addr::LOCALHOST, port)).await?; axum::serve(listener, router).await?; Ok(()) diff --git a/src/state.rs b/src/state.rs new file mode 100644 index 0000000..db36638 --- /dev/null +++ b/src/state.rs @@ -0,0 +1,8 @@ +use std::sync::Arc; + +#[derive(Clone)] +pub struct AppState { + pub db: DB, +} + +pub type DB = Arc>; diff --git a/src/structs/mod.rs b/src/structs/mod.rs new file mode 100644 index 0000000..913bd46 --- /dev/null +++ b/src/structs/mod.rs @@ -0,0 +1 @@ +pub mod users; diff --git a/src/structs/users.rs b/src/structs/users.rs new file mode 100644 index 0000000..c307e46 --- /dev/null +++ b/src/structs/users.rs @@ -0,0 +1,9 @@ +pub struct User { + pub id: uuid::Uuid, + pub real_name: String, + pub username: String, + pub email: String, + pub password_hash: String, + pub is_admin: bool, + pub created_at: chrono::NaiveDateTime, +} diff --git a/src/v1/auth/mod.rs b/src/v1/auth/mod.rs index 6e41b41..9d00c2c 100644 --- a/src/v1/auth/mod.rs +++ b/src/v1/auth/mod.rs @@ -1,3 +1,13 @@ +use crate::state::AppState; + pub(super) use super::*; pub mod signup; + +pub const AUTH_TAG: &str = "auth"; + +pub fn router(state: AppState) -> OpenApiRouter { + OpenApiRouter::new() + .routes(routes!(signup::signup)) + .with_state(state) +} diff --git a/src/v1/auth/signup.rs b/src/v1/auth/signup.rs index ee4d9af..da64cb8 100644 --- a/src/v1/auth/signup.rs +++ b/src/v1/auth/signup.rs @@ -1,7 +1,11 @@ +use axum::{extract::State, response::Response}; + +use crate::state::AppState; + use super::*; -// /// Get customer -// /// -// /// Just return a static Customer object -// #[utoipa::path(get, path = "", responses((status = OK, body = Customer)), tag = super::CUSTOMER_TAG)] -// pub async fn signup() -> Json {} +/// Sign up +#[utoipa::path(get, path = "/signup", responses((status = OK, body = String)), tag = super::AUTH_TAG)] +pub async fn signup(State(state): State) -> Response { + Response::new("Hello, World!".to_string()) +}