From 0782e82d6e75acd49ba36f6bfae03ef2918fe58a Mon Sep 17 00:00:00 2001 From: Daniel Flanagan Date: Tue, 14 Nov 2023 11:26:03 -0600 Subject: [PATCH] Ready for a database --- Cargo.lock | 214 ------------------ Cargo.toml | 7 - diesel.toml | 6 - flake.nix | 2 - .../2023-11-11-073646_create_users/down.sql | 1 - .../2023-11-11-073646_create_users/up.sql | 7 - src/database.rs | 41 ---- src/error.rs | 15 +- src/instrumentation.rs | 2 +- src/main.rs | 1 - src/models.rs | 13 +- src/router.rs | 69 ++++-- src/schema.rs | 10 - src/state.rs | 11 +- src/views.rs | 44 +--- 15 files changed, 75 insertions(+), 368 deletions(-) delete mode 100644 diesel.toml delete mode 100644 migrations/2023-11-11-073646_create_users/down.sql delete mode 100644 migrations/2023-11-11-073646_create_users/up.sql delete mode 100644 src/schema.rs diff --git a/Cargo.lock b/Cargo.lock index 4b57ed3..ca83346 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -358,49 +358,6 @@ dependencies = [ "cipher", ] -[[package]] -name = "deadpool" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb84100978c1c7b37f09ed3ce3e5f843af02c2a2c431bae5b19230dad2c1b490" -dependencies = [ - "async-trait", - "deadpool-runtime", - "num_cpus", - "serde", - "tokio", -] - -[[package]] -name = "deadpool-diesel" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa8404d25ddc6cb0676d4a863bbd007613ee3fffb54db23e0e6341e1fe61c3e" -dependencies = [ - "deadpool", - "deadpool-sync", - "diesel", -] - -[[package]] -name = "deadpool-runtime" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63dfa964fe2a66f3fde91fc70b267fe193d822c7e603e2a675a49a7f46ad3f49" -dependencies = [ - "tokio", -] - -[[package]] -name = "deadpool-sync" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8db70494c13cae4ce67b4b4dafdaf828cf0df7237ab5b9e2fcabee4965d0a0a" -dependencies = [ - "deadpool-runtime", - "tracing", -] - [[package]] name = "deranged" version = "0.3.9" @@ -410,50 +367,6 @@ dependencies = [ "powerfmt", ] -[[package]] -name = "diesel" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2268a214a6f118fce1838edba3d1561cf0e78d8de785475957a580a7f8c69d33" -dependencies = [ - "diesel_derives", - "libsqlite3-sys", - "time", - "uuid", -] - -[[package]] -name = "diesel_derives" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef8337737574f55a468005a83499da720f20c65586241ffea339db9ecdfd2b44" -dependencies = [ - "diesel_table_macro_syntax", - "proc-macro2", - "quote", - "syn 2.0.39", -] - -[[package]] -name = "diesel_migrations" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6036b3f0120c5961381b570ee20a02432d7e2d27ea60de9578799cf9156914ac" -dependencies = [ - "diesel", - "migrations_internals", - "migrations_macros", -] - -[[package]] -name = "diesel_table_macro_syntax" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5" -dependencies = [ - "syn 2.0.39", -] - [[package]] name = "digest" version = "0.10.7" @@ -465,12 +378,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - [[package]] name = "eyre" version = "0.6.8" @@ -593,12 +500,6 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" -[[package]] -name = "hashbrown" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" - [[package]] name = "headers" version = "0.3.9" @@ -713,16 +614,6 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" -[[package]] -name = "indexmap" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" -dependencies = [ - "equivalent", - "hashbrown", -] - [[package]] name = "inotify" version = "0.9.6" @@ -790,16 +681,6 @@ version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" -[[package]] -name = "libsqlite3-sys" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc22eff61b133b115c6e8c74e818c628d6d5e7a502afea6f64dee076dd94326" -dependencies = [ - "pkg-config", - "vcpkg", -] - [[package]] name = "lock_api" version = "0.4.11" @@ -828,10 +709,6 @@ dependencies = [ "base64", "color-eyre", "cookie", - "deadpool", - "deadpool-diesel", - "diesel", - "diesel_migrations", "maud", "notify", "serde", @@ -886,27 +763,6 @@ version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" -[[package]] -name = "migrations_internals" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f23f71580015254b020e856feac3df5878c2c7a8812297edd6c0a485ac9dada" -dependencies = [ - "serde", - "toml", -] - -[[package]] -name = "migrations_macros" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cce3325ac70e67bbab5bd837a31cae01f1a6db64e0e744a33cb03a543469ef08" -dependencies = [ - "migrations_internals", - "proc-macro2", - "quote", -] - [[package]] name = "mime" version = "0.3.17" @@ -1088,12 +944,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "pkg-config" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" - [[package]] name = "polyval" version = "0.6.1" @@ -1326,15 +1176,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_spanned" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" -dependencies = [ - "serde", -] - [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -1549,40 +1390,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "toml" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", -] - -[[package]] -name = "toml_datetime" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.19.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" -dependencies = [ - "indexmap", - "serde", - "serde_spanned", - "toml_datetime", - "winnow", -] - [[package]] name = "tower" version = "0.4.13" @@ -1759,24 +1566,12 @@ dependencies = [ "subtle", ] -[[package]] -name = "uuid" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc" - [[package]] name = "valuable" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - [[package]] name = "version_check" version = "0.9.4" @@ -1904,12 +1699,3 @@ name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "winnow" -version = "0.5.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "829846f3e3db426d4cee4510841b71a8e58aa2a76b1132579487ae430ccd9c7b" -dependencies = [ - "memchr", -] diff --git a/Cargo.toml b/Cargo.toml index bdaf6bd..c1166fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,12 +20,6 @@ tower-http = { version = "0.4.4", features = ["fs"] } tracing = "0.1.40" tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } -# database -deadpool = "0.10.0" -deadpool-diesel = { version = "0.5.0", features = ["rt_tokio_1", "sqlite", "tracing", "serde"] } -diesel = { version = "2.1.3", features = ["sqlite", "uuid"] } -diesel_migrations = { version = "2.1.0", features = ["sqlite"] } - # fancy during-development stuff notify = "6.1.1" tower-livereload = "0.8.2" @@ -34,7 +28,6 @@ thiserror = "1.0.50" axum-macros = "0.3.8" color-eyre = "0.6.2" -# color-eyre # irust # bacon # sqlx (sea orm?) diff --git a/diesel.toml b/diesel.toml deleted file mode 100644 index fcaf131..0000000 --- a/diesel.toml +++ /dev/null @@ -1,6 +0,0 @@ -[print_schema] -file = "src/schema.rs" -custom_type_derives = ["diesel::query_builder::QueryId"] - -[migrations_directory] -dir = "migrations" diff --git a/flake.nix b/flake.nix index 96104b5..0912401 100644 --- a/flake.nix +++ b/flake.nix @@ -50,11 +50,9 @@ rustfmt rustPackages.clippy - rustPackages.bacon rust-analyzer nodePackages_latest.vscode-langservers-extracted - diesel-cli hurl ]; RUST_SRC_PATH = rustPlatform.rustLibSrc; diff --git a/migrations/2023-11-11-073646_create_users/down.sql b/migrations/2023-11-11-073646_create_users/down.sql deleted file mode 100644 index d69c65a..0000000 --- a/migrations/2023-11-11-073646_create_users/down.sql +++ /dev/null @@ -1 +0,0 @@ -drop table users diff --git a/migrations/2023-11-11-073646_create_users/up.sql b/migrations/2023-11-11-073646_create_users/up.sql deleted file mode 100644 index 3b79939..0000000 --- a/migrations/2023-11-11-073646_create_users/up.sql +++ /dev/null @@ -1,7 +0,0 @@ -create table users ( - id text primary key, - username text - name text, - password_digest binary -); -create unique index users_username on users(username); diff --git a/src/database.rs b/src/database.rs index 3d52c04..8b13789 100644 --- a/src/database.rs +++ b/src/database.rs @@ -1,42 +1 @@ -use deadpool_diesel::sqlite::{Manager, Pool, Runtime}; -use diesel::sqlite::Sqlite; -use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness}; -use tracing::info; -#[derive(Clone)] -pub struct Database { - pub pool: Pool, -} - -const MIGRATIONS: EmbeddedMigrations = embed_migrations!(); - -impl Database { - // TODO: database seeding? - - fn run_migrations( - connection: &mut impl MigrationHarness, - ) -> Result<(), Box> { - // This will run the necessary migrations. - // - // See the documentation for `MigrationHarness` for - // all available methods. - connection.run_pending_migrations(MIGRATIONS)?; - - Ok(()) - } - - // TODO: make an actual error type - pub async fn new>(database_url: T) -> Result { - let manager = Manager::new(database_url.as_ref(), Runtime::Tokio1); - let pool = Pool::builder(manager).max_size(8).build().unwrap(); - - let conn = pool.get().await?; - let _ = conn - .interact(|c| Self::run_migrations(c)) - .await - .expect("Failed to run migrations"); - info!("Migrations completed!"); - - return Ok(Database { pool }); - } -} diff --git a/src/error.rs b/src/error.rs index 6b7514a..fb3678e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,13 +1,9 @@ -use std::fmt::Display; - use axum::{ http::StatusCode, response::{IntoResponse, Response}, }; -use thiserror::Error; -#[derive(Error, Debug)] -pub struct AppError(#[from] anyhow::Error); +pub struct AppError(anyhow::Error); impl IntoResponse for AppError { fn into_response(self) -> Response { @@ -19,8 +15,11 @@ impl IntoResponse for AppError { } } -impl Display for AppError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_fmt(format!("app error")) +impl From for AppError +where + E: Into, +{ + fn from(err: E) -> Self { + Self(err.into()) } } diff --git a/src/instrumentation.rs b/src/instrumentation.rs index cc3f7da..c413b06 100644 --- a/src/instrumentation.rs +++ b/src/instrumentation.rs @@ -2,7 +2,7 @@ use tracing::{instrument, trace}; use tracing_subscriber::{filter::LevelFilter, EnvFilter}; pub fn init() { - color_eyre::install(); + color_eyre::install().expect("Failed to install color_eyre"); setup_trace_logger(); } diff --git a/src/main.rs b/src/main.rs index 16d4efa..f27dc5d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,7 +11,6 @@ mod instrumentation; mod models; mod partials; mod router; -mod schema; mod server; mod state; mod views; diff --git a/src/models.rs b/src/models.rs index e411e12..1d11f79 100644 --- a/src/models.rs +++ b/src/models.rs @@ -1,23 +1,12 @@ -use diesel::prelude::*; - -#[derive(Queryable, Selectable)] -#[diesel(table_name = crate::schema::users)] -#[diesel(check_for_backend(diesel::sqlite::Sqlite))] pub struct User { - // pub id: Text, - // pub username: sql_types::Text, - // pub name: sql_types::Text, - // pub password_digest: sql_types::Binary, pub id: Vec, pub username: String, pub name: Option, pub password_digest: Vec, } -#[derive(Insertable)] -#[diesel(table_name = crate::schema::users)] pub struct NewUser<'a> { pub username: &'a str, pub name: Option<&'a str>, - pub password_digest: &'a [u8], + pub password_digest: Vec, } diff --git a/src/router.rs b/src/router.rs index 6589115..acffcbb 100644 --- a/src/router.rs +++ b/src/router.rs @@ -1,4 +1,4 @@ -use std::{env, path::Path, sync::Arc}; +use std::{env, path::Path}; use argon2::{ password_hash::{rand_core::OsRng, SaltString}, @@ -29,7 +29,7 @@ pub async fn new() -> Result { .route("/hello-world-text", get(views::greet_world_text)); let assets_dir = ServeDir::new("./assets"); - let state = Arc::new(State::new().await?); + let state = State::new().await?; let live_reload_layer = LiveReloadLayer::new(); let reloader = live_reload_layer.reloader(); @@ -48,6 +48,7 @@ pub async fn new() -> Result { .nest("/app", app_router) .nest_service("/assets", assets_dir) .route("/", get(views::index)) + .route("/login", get(views::login).post(login)) .route("/register", get(views::register).post(register)) .route("/all_users", get(views::all_users)) .with_state(state) @@ -64,11 +65,23 @@ struct Register { password: String, } -impl<'a> TryInto> for Register { - type Error = (); +impl<'a> TryInto> for &'a Register { + type Error = argon2::password_hash::Error; - fn try_into(self) -> Result, Self::Error> { - todo!() + fn try_into(self: &'a Register) -> Result, Self::Error> { + let salt = SaltString::generate(&mut OsRng); + let argon2 = Argon2::default(); + let password_digest: Vec = argon2 + .hash_password(self.password.as_bytes(), &salt)? + .hash + .expect("no password hash") + .as_bytes() + .into(); + Ok(NewUser { + username: &self.username, + name: None, + password_digest, + }) } } @@ -78,6 +91,40 @@ async fn register( ) -> Result { // TODO: https://docs.rs/axum_csrf/latest/axum_csrf/#prevent-post-replay-attacks-with-csrf + let v = csrf_token.verify(®ister.authenticity_token); + if v.is_err() { + return Ok(( + StatusCode::BAD_REQUEST, + Html(html! { "invalid request" }.into_string()), + )); + } + + let new_user: NewUser = (®ister).try_into()?; + + Ok(( + StatusCode::CREATED, + Html( + html! { + h1 { (new_user.username) } + } + .into_string(), + ), + )) +} + +#[derive(Deserialize)] +struct Login { + authenticity_token: String, + username: String, + password: String, +} + +async fn login( + csrf_token: CsrfToken, + Form(register): Form, +) -> Result { + // TODO: https://docs.rs/axum_csrf/latest/axum_csrf/#prevent-post-replay-attacks-with-csrf + let v = csrf_token.verify(®ister.authenticity_token); println!("{:?} {:?}", register.authenticity_token, v); if v.is_err() { @@ -87,16 +134,6 @@ async fn register( )); } - let salt = SaltString::generate(&mut OsRng); - let argon2 = Argon2::default(); - let password_digest = argon2.hash_password(register.password.as_bytes(), &salt)?; - - let _new_user = NewUser { - username: ®ister.username, - name: None, - password_digest: password_digest.to_string().as_bytes(), - }; - Ok(( StatusCode::CREATED, Html( diff --git a/src/schema.rs b/src/schema.rs deleted file mode 100644 index 5ccddd5..0000000 --- a/src/schema.rs +++ /dev/null @@ -1,10 +0,0 @@ -// @generated automatically by Diesel CLI. - -diesel::table! { - users (id) { - id -> Binary, - username -> Text, - name -> Nullable, - password_digest -> Binary, - } -} diff --git a/src/state.rs b/src/state.rs index 5dce4fb..0b3f416 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,17 +1,12 @@ use std::env; -use crate::database; - #[derive(Clone)] -pub struct State { - pub database: database::Database, -} +pub struct State {} impl State { pub async fn new() -> Result { - let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set"); - let database = database::Database::new(database_url).await?; + let _database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set"); - Ok(State { database }) + Ok(State {}) } } diff --git a/src/views.rs b/src/views.rs index 5937540..b75d7ce 100644 --- a/src/views.rs +++ b/src/views.rs @@ -1,15 +1,9 @@ -use std::{error::Error, sync::Arc}; - use axum::{ extract::State, response::{Html, IntoResponse}, }; use axum_csrf::CsrfToken; -use axum_macros::debug_handler; -use deadpool_diesel::InteractError; -use diesel::{QueryDsl, RunQueryDsl, SelectableHelper}; use maud::html; -use thiserror::Error; use tracing::instrument; use crate::{ @@ -102,32 +96,11 @@ pub async fn login(csrf: CsrfToken) -> impl IntoResponse { .into_response() } -#[derive(Error, Debug)] -enum AllUsersError { - #[error("other application error")] - App(#[from] AppError), - #[error("failed to retrieve users")] - DB(#[from] InteractError), -} - -#[debug_handler] +#[allow(unreachable_code)] pub async fn all_users( - State(state): State, -) -> Result, AllUsersError> { - use crate::schema::users::dsl::*; - - let conn = state.database.pool.get().await?; - let cc = |c| -> Vec { - users - .select(User::as_select()) - .load(c) - .expect("error loading users") - }; - - let all_users: Vec = match conn.interact(cc).await { - Ok(u) => u, - Err(e) => return Err(("failed to retrieve users")), - }; + State(_state): State, +) -> Result, AppError> { + let all_users: Vec = vec![]; // @if let Some(name) = u.name { // name @@ -142,9 +115,12 @@ pub async fn all_users( ul { @for u in all_users { li { - (u) - "(" - ")" + (u.username) + @if let Some(name) = u.name { + " (" + (name) + ")" + } } } }