From c7dc68926bb525f61fd45cd753ef582391ace353 Mon Sep 17 00:00:00 2001 From: Jef Roosens Date: Thu, 16 Jan 2025 15:00:26 +0100 Subject: [PATCH] feat: fully replace database operations with diesel --- Cargo.lock | 175 ++++++++++-------- Cargo.toml | 4 +- .../2025-01-13-114912_create_plants/up.sql | 2 +- .../down.sql | 1 - .../2025-01-13-115415_create_comments/up.sql | 7 - .../2025-01-13-115505_create_events/up.sql | 4 +- migrations/2025-01-13-115534_add_auth/up.sql | 4 +- src/db/mod.rs | 74 +++----- src/db/models/event.rs | 32 +++- src/db/models/mod.rs | 8 +- src/db/models/plant.rs | 31 ++-- src/db/models/session.rs | 21 +-- src/db/models/user.rs | 34 ++-- src/db/schema.rs | 21 +-- src/main.rs | 27 +-- src/server/plants.rs | 6 +- 16 files changed, 227 insertions(+), 224 deletions(-) delete mode 100644 migrations/2025-01-13-115415_create_comments/down.sql delete mode 100644 migrations/2025-01-13-115415_create_comments/up.sql diff --git a/Cargo.lock b/Cargo.lock index a582dcf..b8e8b6c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,18 +17,6 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" -[[package]] -name = "ahash" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", -] - [[package]] name = "aho-corasick" version = "1.1.3" @@ -341,10 +329,8 @@ dependencies = [ "chrono", "clap", "diesel", - "r2d2", - "r2d2_sqlite", + "diesel_migrations", "rand", - "rusqlite", "serde", "tera", "tokio", @@ -597,6 +583,17 @@ dependencies = [ "syn", ] +[[package]] +name = "diesel_migrations" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a73ce704bad4231f001bff3314d91dce4aba0770cee8b233991859abc15c1f6" +dependencies = [ + "diesel", + "migrations_internals", + "migrations_macros", +] + [[package]] name = "diesel_table_macro_syntax" version = "0.2.0" @@ -638,16 +635,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] -name = "fallible-iterator" -version = "0.3.0" +name = "equivalent" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" - -[[package]] -name = "fallible-streaming-iterator" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "flate2" @@ -766,21 +757,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", -] - -[[package]] -name = "hashlink" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" -dependencies = [ - "hashbrown", -] +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "heck" @@ -929,6 +908,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "indexmap" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -975,7 +964,6 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" dependencies = [ - "cc", "pkg-config", "vcpkg", ] @@ -1008,6 +996,27 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "migrations_internals" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd01039851e82f8799046eabbb354056283fb265c8ec0996af940f4e85a380ff" +dependencies = [ + "serde", + "toml", +] + +[[package]] +name = "migrations_macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb161cc72176cb37aa47f1fc520d3ef02263d67d661f44f05d05a079e1237fd" +dependencies = [ + "migrations_internals", + "proc-macro2", + "quote", +] + [[package]] name = "mime" version = "0.3.17" @@ -1284,17 +1293,6 @@ dependencies = [ "scheduled-thread-pool", ] -[[package]] -name = "r2d2_sqlite" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb14dba8247a6a15b7fdbc7d389e2e6f03ee9f184f87117706d509c092dfe846" -dependencies = [ - "r2d2", - "rusqlite", - "uuid", -] - [[package]] name = "rand" version = "0.8.5" @@ -1363,21 +1361,6 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" -[[package]] -name = "rusqlite" -version = "0.32.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" -dependencies = [ - "bitflags", - "chrono", - "fallible-iterator", - "fallible-streaming-iterator", - "hashlink", - "libsqlite3-sys", - "smallvec", -] - [[package]] name = "rustc-demangle" version = "0.1.24" @@ -1462,6 +1445,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -1695,6 +1687,40 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "tower" version = "0.5.2" @@ -1888,16 +1914,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" -[[package]] -name = "uuid" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" -dependencies = [ - "getrandom", - "rand", -] - [[package]] name = "valuable" version = "0.1.0" @@ -2108,6 +2124,15 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winnow" +version = "0.6.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" +dependencies = [ + "memchr", +] + [[package]] name = "zerocopy" version = "0.7.35" diff --git a/Cargo.toml b/Cargo.toml index 3308327..e9f74fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,12 +14,10 @@ axum-extra = { version = "0.10.0", features = ["cookie"] } chrono = { version = "0.4.39", features = ["serde"] } clap = { version = "4.5.26", features = ["derive", "env"] } diesel = { version = "2.2.6", features = ["sqlite", "returning_clauses_for_sqlite_3_35", "r2d2", "chrono"] } -r2d2 = "0.8.10" -r2d2_sqlite = "0.25.0" +diesel_migrations = { version = "2.2.0", features = ["sqlite"] } rand = "0.8.5" # this dependency is needed soly because the r2d2_sqlite crate doesn't export # the 'chrono' feature flag -rusqlite = { version = "0.32.1", features = ["chrono", "bundled"] } serde = { version = "1.0.217", features = ["derive"] } tera = "1.20.0" tokio = { version = "1.42.0", features = ["full"] } diff --git a/migrations/2025-01-13-114912_create_plants/up.sql b/migrations/2025-01-13-114912_create_plants/up.sql index 538066c..254f3c3 100644 --- a/migrations/2025-01-13-114912_create_plants/up.sql +++ b/migrations/2025-01-13-114912_create_plants/up.sql @@ -1,5 +1,5 @@ create table plants ( - id bigint primary key not null, + id integer primary key not null, name text not null, species text not null, description text not null diff --git a/migrations/2025-01-13-115415_create_comments/down.sql b/migrations/2025-01-13-115415_create_comments/down.sql deleted file mode 100644 index 756f4d8..0000000 --- a/migrations/2025-01-13-115415_create_comments/down.sql +++ /dev/null @@ -1 +0,0 @@ -drop table comments; diff --git a/migrations/2025-01-13-115415_create_comments/up.sql b/migrations/2025-01-13-115415_create_comments/up.sql deleted file mode 100644 index e4f8983..0000000 --- a/migrations/2025-01-13-115415_create_comments/up.sql +++ /dev/null @@ -1,7 +0,0 @@ -create table comments ( - id bigint primary key not null, - plant_id bigint not null - references plants (id) - on delete cascade, - comment text not null -); diff --git a/migrations/2025-01-13-115505_create_events/up.sql b/migrations/2025-01-13-115505_create_events/up.sql index cc9c0c5..1c112be 100644 --- a/migrations/2025-01-13-115505_create_events/up.sql +++ b/migrations/2025-01-13-115505_create_events/up.sql @@ -1,6 +1,6 @@ create table events ( - id bigint primary key not null, - plant_id bigint not null + id integer primary key not null, + plant_id integer not null references plants (id) on delete cascade, event_type text not null, diff --git a/migrations/2025-01-13-115534_add_auth/up.sql b/migrations/2025-01-13-115534_add_auth/up.sql index d0e1259..5f0cec9 100644 --- a/migrations/2025-01-13-115534_add_auth/up.sql +++ b/migrations/2025-01-13-115534_add_auth/up.sql @@ -1,5 +1,5 @@ create table users ( - id bigint primary key not null, + id integer primary key not null, username text unique not null, password_hash text not null, admin boolean not null @@ -7,7 +7,7 @@ create table users ( create table sessions ( id bigint primary key not null, - user_id bigint not null + user_id integer not null references users (id) on delete cascade, unique (id, user_id) diff --git a/src/db/mod.rs b/src/db/mod.rs index 17e00c8..c224b9b 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -1,32 +1,37 @@ -mod event; mod models; -mod plant; mod schema; -mod session; -mod user; -use r2d2_sqlite::{rusqlite, SqliteConnectionManager}; +use diesel::{ + prelude::*, + r2d2::{ConnectionManager, Pool, PooledConnection}, + SqliteConnection, +}; +use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness}; -use std::{error::Error, fmt}; +use std::{error::Error, fmt, path::Path}; -pub use event::{Event, EventType, NewEvent, EVENT_TYPES}; -pub use plant::{NewPlant, Plant}; -pub use session::Session; -pub use user::{NewUser, User}; +pub use models::event::{Event, EventType, NewEvent, EVENT_TYPES}; +pub use models::plant::{NewPlant, Plant}; +pub use models::session::Session; +pub use models::user::{NewUser, User}; -pub type DbPool = r2d2::Pool; +pub type DbPool = Pool>; +pub type DbConn = PooledConnection>; +pub type DbResult = Result; + +pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations"); #[derive(Debug)] pub enum DbError { - Pool(r2d2::Error), - Db(rusqlite::Error), + Pool(diesel::r2d2::PoolError), + Db(diesel::result::Error), } impl fmt::Display for DbError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Pool(_) => write!(f, "failed to acquire connection from pool"), - Self::Db(_) => write!(f, "error while accessing the database"), + Self::Db(_) => write!(f, "error while executing query"), } } } @@ -40,46 +45,25 @@ impl Error for DbError { } } -impl From for DbError { - fn from(value: r2d2::Error) -> Self { +impl From for DbError { + fn from(value: diesel::r2d2::PoolError) -> Self { Self::Pool(value) } } -impl From for DbError { - fn from(value: rusqlite::Error) -> Self { +impl From for DbError { + fn from(value: diesel::result::Error) -> Self { Self::Db(value) } } -pub fn run_migrations(pool: &DbPool, migrations: &[&str]) -> rusqlite::Result<()> { - let mut conn = pool.get().unwrap(); +pub fn initialize_db(path: impl AsRef, run_migrations: bool) -> Result { + let manager = ConnectionManager::::new(path.as_ref().to_string_lossy()); + let pool = Pool::new(manager)?; - // If the migration version query fails, we assume it's because the table isn't there yet so we - // try to run the first migration - let mut next_version = conn - .query_row("select max(version) from migration_version", (), |row| { - row.get::<_, usize>(0).map(|n| n + 1) - }) - .unwrap_or(0); - - while next_version < migrations.len() { - let tx = conn.transaction()?; - - tx.execute_batch(migrations[next_version])?; - - let cur_time = chrono::Local::now().timestamp(); - tx.execute( - "insert into migration_version values ($1, $2)", - (next_version, cur_time), - )?; - - tx.commit()?; - - tracing::info!("Applied migration {next_version}"); - - next_version += 1; + if run_migrations { + pool.get()?.run_pending_migrations(MIGRATIONS).unwrap(); } - Ok(()) + Ok(pool) } diff --git a/src/db/models/event.rs b/src/db/models/event.rs index b6084f9..7e5dee1 100644 --- a/src/db/models/event.rs +++ b/src/db/models/event.rs @@ -11,7 +11,9 @@ use serde::{Deserialize, Serialize}; use std::{fmt, str::FromStr}; -use crate::db::schema::*; +pub const EVENT_TYPES: [&str; 1] = ["Watering"]; + +use crate::db::{schema::*, DbPool, DbResult}; #[derive(FromSqlRow, Debug, AsExpression, Serialize, Deserialize)] #[diesel(sql_type = Text)] @@ -72,10 +74,11 @@ impl ToSql for EventType { #[derive(Serialize, Queryable, Selectable)] #[diesel(table_name = events)] +#[diesel(belongs_to(super::plant::Plant))] #[diesel(check_for_backend(diesel::sqlite::Sqlite))] pub struct Event { - id: i64, - plant_id: i64, + id: i32, + plant_id: i32, event_type: EventType, date: NaiveDate, description: String, @@ -85,17 +88,26 @@ pub struct Event { #[diesel(table_name = events)] #[diesel(check_for_backend(diesel::sqlite::Sqlite))] pub struct NewEvent { - plant_id: i64, + plant_id: i32, event_type: EventType, date: NaiveDate, description: String, } -impl NewEvent { - pub fn insert(self, conn: &mut SqliteConnection) -> QueryResult { - diesel::insert_into(events::table) - .values(self) - .returning(Event::as_returning()) - .get_result(conn) +impl Event { + pub fn for_plant(pool: &DbPool, plant_id: i32) -> DbResult> { + Ok(events::dsl::events + .select(Self::as_select()) + .filter(events::dsl::plant_id.eq(plant_id)) + .load(&mut pool.get()?)?) + } +} + +impl NewEvent { + pub fn insert(self, pool: &DbPool) -> DbResult { + Ok(diesel::insert_into(events::table) + .values(self) + .returning(Event::as_returning()) + .get_result(&mut pool.get()?)?) } } diff --git a/src/db/models/mod.rs b/src/db/models/mod.rs index 443bd6a..983ea02 100644 --- a/src/db/models/mod.rs +++ b/src/db/models/mod.rs @@ -1,4 +1,4 @@ -mod event; -mod plant; -mod session; -mod user; +pub mod event; +pub mod plant; +pub mod session; +pub mod user; diff --git a/src/db/models/plant.rs b/src/db/models/plant.rs index 6f0f119..205a847 100644 --- a/src/db/models/plant.rs +++ b/src/db/models/plant.rs @@ -1,16 +1,16 @@ use diesel::prelude::*; use serde::{Deserialize, Serialize}; -use crate::db::schema::*; +use crate::db::{schema::*, DbPool, DbResult}; #[derive(Serialize, Queryable, Selectable)] #[diesel(table_name = plants)] #[diesel(check_for_backend(diesel::sqlite::Sqlite))] pub struct Plant { - id: i64, - name: String, - species: String, - description: String, + pub id: i32, + pub name: String, + pub species: String, + pub description: String, } #[derive(Deserialize, Insertable)] @@ -23,19 +23,26 @@ pub struct NewPlant { } impl NewPlant { - pub fn insert(self, conn: &mut SqliteConnection) -> QueryResult { - self.insert_into(plants::table) + pub fn insert(self, pool: &DbPool) -> DbResult { + Ok(self + .insert_into(plants::table) .returning(Plant::as_returning()) - .get_result(conn) + .get_result(&mut pool.get()?)?) } } impl Plant { - pub fn by_id(conn: &mut SqliteConnection, id: i64) -> QueryResult> { - plants::table + pub fn all(pool: &DbPool) -> DbResult> { + Ok(plants::dsl::plants + .select(Self::as_select()) + .load(&mut pool.get()?)?) + } + + pub fn by_id(pool: &DbPool, id: i32) -> DbResult> { + Ok(plants::table .find(id) .select(Self::as_select()) - .first(conn) - .optional() + .first(&mut pool.get()?) + .optional()?) } } diff --git a/src/db/models/session.rs b/src/db/models/session.rs index da0f981..30211c6 100644 --- a/src/db/models/session.rs +++ b/src/db/models/session.rs @@ -2,7 +2,7 @@ use diesel::prelude::*; use rand::Rng; use super::user::User; -use crate::db::schema::*; +use crate::db::{schema::*, DbPool, DbResult}; #[derive(Clone, Queryable, Selectable, Insertable, Associations)] #[diesel(belongs_to(super::user::User))] @@ -10,28 +10,25 @@ use crate::db::schema::*; #[diesel(check_for_backend(diesel::sqlite::Sqlite))] pub struct Session { pub id: i64, - pub user_id: i64, + pub user_id: i32, } impl Session { - pub fn new_for_user(conn: &mut SqliteConnection, user_id: i64) -> QueryResult { + pub fn new_for_user(pool: &DbPool, user_id: i32) -> DbResult { let id: i64 = rand::thread_rng().gen(); - Self { id, user_id } + Ok(Self { id, user_id } .insert_into(sessions::table) .returning(Self::as_returning()) - .get_result(conn) + .get_result(&mut pool.get()?)?) } - pub fn user_from_id( - conn: &mut SqliteConnection, - id: i64, - ) -> QueryResult> { - sessions::dsl::sessions + pub fn user_from_id(pool: &DbPool, id: i64) -> DbResult> { + Ok(sessions::dsl::sessions .inner_join(users::table) .filter(sessions::id.eq(id)) .select(User::as_select()) - .get_result(conn) - .optional() + .get_result(&mut pool.get()?) + .optional()?) } } diff --git a/src/db/models/user.rs b/src/db/models/user.rs index 1423e4e..d581fc7 100644 --- a/src/db/models/user.rs +++ b/src/db/models/user.rs @@ -5,13 +5,13 @@ use argon2::{ use diesel::prelude::*; use serde::{Deserialize, Serialize}; -use crate::db::schema::*; +use crate::db::{schema::*, DbConn, DbPool, DbResult}; #[derive(Serialize, Deserialize, Clone, Queryable, Selectable)] #[diesel(table_name = users)] #[diesel(check_for_backend(diesel::sqlite::Sqlite))] pub struct User { - pub id: i64, + pub id: i32, pub username: String, pub password_hash: String, pub admin: bool, @@ -45,24 +45,27 @@ impl NewUser { } } - pub fn insert(self, conn: &mut SqliteConnection) -> QueryResult { - diesel::insert_into(users::table) + pub fn insert(self, pool: &DbPool) -> DbResult { + Ok(diesel::insert_into(users::table) .values(self) .returning(User::as_returning()) - .get_result(conn) + .get_result(&mut pool.get()?)?) } } impl User { - pub fn by_username( - conn: &mut SqliteConnection, - username: impl AsRef, - ) -> QueryResult> { - users::dsl::users + pub fn by_username(pool: &DbPool, username: impl AsRef) -> DbResult> { + Ok(users::dsl::users .select(User::as_select()) .filter(users::username.eq(username.as_ref())) - .first(conn) - .optional() + .first(&mut pool.get()?) + .optional()?) + } + + pub fn all(pool: &DbPool) -> DbResult> { + Ok(users::dsl::users + .select(User::as_select()) + .load(&mut pool.get()?)?) } pub fn verify_password(&self, password: impl AsRef) -> bool { @@ -72,4 +75,11 @@ impl User { .verify_password(password.as_ref().as_bytes(), &password_hash) .is_ok() } + + pub fn remove_by_username(pool: &DbPool, username: impl AsRef) -> DbResult { + Ok(diesel::delete(users::table) + .filter(users::username.eq(username.as_ref())) + .execute(&mut pool.get()?) + .map(|n| n > 0)?) + } } diff --git a/src/db/schema.rs b/src/db/schema.rs index 664720e..256f915 100644 --- a/src/db/schema.rs +++ b/src/db/schema.rs @@ -1,17 +1,9 @@ // @generated automatically by Diesel CLI. -diesel::table! { - comments (id) { - id -> BigInt, - plant_id -> BigInt, - comment -> Text, - } -} - diesel::table! { events (id) { - id -> BigInt, - plant_id -> BigInt, + id -> Integer, + plant_id -> Integer, event_type -> Text, date -> Date, description -> Text, @@ -20,7 +12,7 @@ diesel::table! { diesel::table! { plants (id) { - id -> BigInt, + id -> Integer, name -> Text, species -> Text, description -> Text, @@ -30,21 +22,20 @@ diesel::table! { diesel::table! { sessions (id) { id -> BigInt, - user_id -> BigInt, + user_id -> Integer, } } diesel::table! { users (id) { - id -> BigInt, + id -> Integer, username -> Text, password_hash -> Text, admin -> Bool, } } -diesel::joinable!(comments -> plants (plant_id)); diesel::joinable!(events -> plants (plant_id)); diesel::joinable!(sessions -> users (user_id)); -diesel::allow_tables_to_appear_in_same_query!(comments, events, plants, sessions, users,); +diesel::allow_tables_to_appear_in_same_query!(events, plants, sessions, users,); diff --git a/src/main.rs b/src/main.rs index 4ffd7e2..e126c83 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,18 +5,13 @@ mod server; use std::{fs, path::Path, sync::Arc}; use clap::Parser; -use cli::UserCmd; -use db::DbError; -use r2d2_sqlite::SqliteConnectionManager; +use diesel_migrations::{embed_migrations, EmbeddedMigrations}; use tera::Tera; use tower_http::compression::CompressionLayer; -const MIGRATIONS: [&str; 4] = [ - include_str!("migrations/000_initial.sql"), - include_str!("migrations/001_plants.sql"), - include_str!("migrations/003_events.sql"), - include_str!("migrations/004_auth.sql"), -]; +use cli::UserCmd; +use db::DbError; + const DB_FILENAME: &str = "db.sqlite3"; #[derive(Clone)] @@ -26,8 +21,7 @@ pub struct Context { } fn run_user_cli(data_dir: impl AsRef, cmd: UserCmd) -> Result<(), DbError> { - let manager = SqliteConnectionManager::file(data_dir.as_ref().join(DB_FILENAME)); - let pool = r2d2::Pool::new(manager)?; + let pool = db::initialize_db(data_dir.as_ref().join(DB_FILENAME), false)?; match cmd.cmd { cli::UserSubCmd::List => { @@ -44,12 +38,7 @@ fn run_user_cli(data_dir: impl AsRef, cmd: UserCmd) -> Result<(), DbError> password, admin, } => { - db::NewUser { - username, - password, - admin, - } - .insert(&pool)?; + db::NewUser::new(username, password, admin).insert(&pool)?; } cli::UserSubCmd::Remove { username } => { db::User::remove_by_username(&pool, &username)?; @@ -71,9 +60,7 @@ async fn main() { fs::create_dir_all(&args.data_dir).unwrap(); } - let manager = SqliteConnectionManager::file(args.data_dir.join(DB_FILENAME)); - let pool = r2d2::Pool::new(manager).unwrap(); - db::run_migrations(&pool, &MIGRATIONS).unwrap(); + let pool = db::initialize_db(args.data_dir.join(DB_FILENAME), true).unwrap(); let tera = Tera::new(&format!("{}/**/*", args.templates_dir.to_string_lossy())).unwrap(); diff --git a/src/server/plants.rs b/src/server/plants.rs index 223a9c8..d983baf 100644 --- a/src/server/plants.rs +++ b/src/server/plants.rs @@ -7,7 +7,7 @@ use axum::{ }; use tera::Context; -use crate::db::{self, DbError, Plant}; +use crate::db::{self, DbError, Event, Plant}; use super::error::AppError; @@ -20,13 +20,13 @@ pub fn app() -> axum::Router { async fn get_plant_page( State(ctx): State, headers: HeaderMap, - Path(plant_id): Path, + Path(plant_id): Path, ) -> super::Result> { let res = tokio::task::spawn_blocking(move || { let plant = Plant::by_id(&ctx.pool, plant_id)?; if let Some(plant) = plant { - let events = plant.events(&ctx.pool)?; + let events = Event::for_plant(&ctx.pool, plant.id)?; Ok::<_, DbError>(Some((plant, events))) } else {