chore: remove unused files and imports
							parent
							
								
									c7dc68926b
								
							
						
					
					
						commit
						3568365c6f
					
				
							
								
								
									
										112
									
								
								src/db/event.rs
								
								
								
								
							
							
						
						
									
										112
									
								
								src/db/event.rs
								
								
								
								
							| 
						 | 
				
			
			@ -1,112 +0,0 @@
 | 
			
		|||
use std::{fmt::Display, str::FromStr};
 | 
			
		||||
 | 
			
		||||
use chrono::NaiveDate;
 | 
			
		||||
use r2d2_sqlite::rusqlite::{
 | 
			
		||||
    self,
 | 
			
		||||
    types::{FromSql, FromSqlError},
 | 
			
		||||
    Row, ToSql,
 | 
			
		||||
};
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
use super::{DbError, DbPool};
 | 
			
		||||
 | 
			
		||||
pub const EVENT_TYPES: [&str; 1] = ["Watering"];
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize, Deserialize)]
 | 
			
		||||
pub enum EventType {
 | 
			
		||||
    Watering,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ToString for EventType {
 | 
			
		||||
    fn to_string(&self) -> String {
 | 
			
		||||
        String::from(match self {
 | 
			
		||||
            Self::Watering => "watering",
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub struct ParseEventTypeErr;
 | 
			
		||||
 | 
			
		||||
impl Display for ParseEventTypeErr {
 | 
			
		||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
			
		||||
        write!(f, "invalid name")
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl std::error::Error for ParseEventTypeErr {}
 | 
			
		||||
 | 
			
		||||
impl FromStr for EventType {
 | 
			
		||||
    type Err = ParseEventTypeErr;
 | 
			
		||||
 | 
			
		||||
    fn from_str(s: &str) -> Result<Self, Self::Err> {
 | 
			
		||||
        match s {
 | 
			
		||||
            "watering" => Ok(Self::Watering),
 | 
			
		||||
            _ => Err(ParseEventTypeErr),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ToSql for EventType {
 | 
			
		||||
    fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
 | 
			
		||||
        Ok(self.to_string().into())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl FromSql for EventType {
 | 
			
		||||
    fn column_result(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult<Self> {
 | 
			
		||||
        value
 | 
			
		||||
            .as_str()?
 | 
			
		||||
            .parse()
 | 
			
		||||
            .map_err(|e| FromSqlError::Other(Box::new(e)))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize)]
 | 
			
		||||
pub struct Event {
 | 
			
		||||
    id: i64,
 | 
			
		||||
    plant_id: i64,
 | 
			
		||||
    event_type: EventType,
 | 
			
		||||
    date: NaiveDate,
 | 
			
		||||
    description: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Event {
 | 
			
		||||
    pub fn from_row(row: &Row<'_>) -> Result<Self, rusqlite::Error> {
 | 
			
		||||
        Ok(Self {
 | 
			
		||||
            id: row.get("id")?,
 | 
			
		||||
            plant_id: row.get("plant_id")?,
 | 
			
		||||
            event_type: row.get("event_type")?,
 | 
			
		||||
            date: row.get("date")?,
 | 
			
		||||
            description: row.get("description")?,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Deserialize)]
 | 
			
		||||
pub struct NewEvent {
 | 
			
		||||
    plant_id: i64,
 | 
			
		||||
    event_type: EventType,
 | 
			
		||||
    date: NaiveDate,
 | 
			
		||||
    description: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl NewEvent {
 | 
			
		||||
    pub fn insert(self, pool: &DbPool) -> Result<Event, DbError> {
 | 
			
		||||
        let conn = pool.get()?;
 | 
			
		||||
 | 
			
		||||
        let mut stmt = conn.prepare(
 | 
			
		||||
            "insert into events (plant_id, event_type, date, description) values ($1, $2, $3, $4) returning *",
 | 
			
		||||
        )?;
 | 
			
		||||
 | 
			
		||||
        Ok(stmt.query_row(
 | 
			
		||||
            (
 | 
			
		||||
                &self.plant_id,
 | 
			
		||||
                &self.event_type,
 | 
			
		||||
                &self.date,
 | 
			
		||||
                &self.description,
 | 
			
		||||
            ),
 | 
			
		||||
            Event::from_row,
 | 
			
		||||
        )?)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -2,21 +2,19 @@ mod models;
 | 
			
		|||
mod schema;
 | 
			
		||||
 | 
			
		||||
use diesel::{
 | 
			
		||||
    prelude::*,
 | 
			
		||||
    r2d2::{ConnectionManager, Pool, PooledConnection},
 | 
			
		||||
    r2d2::{ConnectionManager, Pool},
 | 
			
		||||
    SqliteConnection,
 | 
			
		||||
};
 | 
			
		||||
use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};
 | 
			
		||||
 | 
			
		||||
use std::{error::Error, fmt, path::Path};
 | 
			
		||||
 | 
			
		||||
pub use models::event::{Event, EventType, NewEvent, EVENT_TYPES};
 | 
			
		||||
pub use models::event::{Event, NewEvent, EVENT_TYPES};
 | 
			
		||||
pub use models::plant::{NewPlant, Plant};
 | 
			
		||||
pub use models::session::Session;
 | 
			
		||||
pub use models::user::{NewUser, User};
 | 
			
		||||
 | 
			
		||||
pub type DbPool = Pool<ConnectionManager<SqliteConnection>>;
 | 
			
		||||
pub type DbConn = PooledConnection<ConnectionManager<SqliteConnection>>;
 | 
			
		||||
pub type DbResult<T> = Result<T, DbError>;
 | 
			
		||||
 | 
			
		||||
pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,7 +5,7 @@ use argon2::{
 | 
			
		|||
use diesel::prelude::*;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
use crate::db::{schema::*, DbConn, DbPool, DbResult};
 | 
			
		||||
use crate::db::{schema::*, DbPool, DbResult};
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize, Deserialize, Clone, Queryable, Selectable)]
 | 
			
		||||
#[diesel(table_name = users)]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,73 +0,0 @@
 | 
			
		|||
use r2d2_sqlite::rusqlite::{self, Row};
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
use super::{DbError, DbPool, Event};
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize)]
 | 
			
		||||
pub struct Plant {
 | 
			
		||||
    id: i64,
 | 
			
		||||
    name: String,
 | 
			
		||||
    species: String,
 | 
			
		||||
    description: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Deserialize)]
 | 
			
		||||
pub struct NewPlant {
 | 
			
		||||
    name: String,
 | 
			
		||||
    species: String,
 | 
			
		||||
    description: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Plant {
 | 
			
		||||
    pub fn from_row(row: &Row<'_>) -> Result<Self, rusqlite::Error> {
 | 
			
		||||
        Ok(Self {
 | 
			
		||||
            id: row.get(0)?,
 | 
			
		||||
            name: row.get(1)?,
 | 
			
		||||
            species: row.get(2)?,
 | 
			
		||||
            description: row.get(3)?,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn all(pool: &DbPool) -> Result<Vec<Self>, DbError> {
 | 
			
		||||
        let conn = pool.get()?;
 | 
			
		||||
 | 
			
		||||
        let mut stmt = conn.prepare("select * from plants")?;
 | 
			
		||||
        let plants: Result<Vec<_>, _> = stmt.query_map((), Self::from_row)?.collect();
 | 
			
		||||
 | 
			
		||||
        Ok(plants?)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn by_id(pool: &DbPool, id: i64) -> Result<Option<Self>, DbError> {
 | 
			
		||||
        let conn = pool.get()?;
 | 
			
		||||
 | 
			
		||||
        let mut stmt = conn.prepare("select * from plants where id = $1")?;
 | 
			
		||||
        match stmt.query_row((id,), |row| Plant::from_row(row)) {
 | 
			
		||||
            Ok(plant) => Ok(Some(plant)),
 | 
			
		||||
            Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
 | 
			
		||||
            Err(err) => Err(DbError::Db(err)),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn events(&self, pool: &DbPool) -> Result<Vec<Event>, DbError> {
 | 
			
		||||
        let conn = pool.get()?;
 | 
			
		||||
        let mut stmt = conn.prepare("select * from events where plant_id = $1")?;
 | 
			
		||||
 | 
			
		||||
        let events: Result<Vec<_>, _> = stmt.query_map((self.id,), Event::from_row)?.collect();
 | 
			
		||||
        Ok(events?)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl NewPlant {
 | 
			
		||||
    pub fn insert(self, pool: &DbPool) -> Result<Plant, DbError> {
 | 
			
		||||
        let conn = pool.get()?;
 | 
			
		||||
 | 
			
		||||
        let mut stmt = conn.prepare(
 | 
			
		||||
            "insert into plants (name, species, description) values ($1, $2, $3) returning *",
 | 
			
		||||
        )?;
 | 
			
		||||
 | 
			
		||||
        Ok(stmt.query_row(
 | 
			
		||||
            (&self.name, &self.species, &self.description),
 | 
			
		||||
            Plant::from_row,
 | 
			
		||||
        )?)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,39 +0,0 @@
 | 
			
		|||
use rand::Rng;
 | 
			
		||||
use rusqlite::Row;
 | 
			
		||||
 | 
			
		||||
use super::{DbError, DbPool, User};
 | 
			
		||||
 | 
			
		||||
pub struct Session {
 | 
			
		||||
    pub id: i64,
 | 
			
		||||
    pub user_id: i32,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Session {
 | 
			
		||||
    pub fn from_row(row: &Row<'_>) -> Result<Self, rusqlite::Error> {
 | 
			
		||||
        Ok(Self {
 | 
			
		||||
            id: row.get("id")?,
 | 
			
		||||
            user_id: row.get("user_id")?,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn user_from_id(pool: &DbPool, id: i64) -> Result<Option<super::User>, DbError> {
 | 
			
		||||
        let conn = pool.get()?;
 | 
			
		||||
 | 
			
		||||
        let mut stmt = conn.prepare("select users.* from sessions inner join users on sessions.user_id = users.id where sessions.id = $1")?;
 | 
			
		||||
        match stmt.query_row((id,), User::from_row) {
 | 
			
		||||
            Ok(user) => Ok(Some(user)),
 | 
			
		||||
            Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
 | 
			
		||||
            Err(err) => Err(DbError::Db(err)),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn new_for_user(pool: &DbPool, user_id: i64) -> Result<Self, DbError> {
 | 
			
		||||
        let id: i64 = rand::thread_rng().gen();
 | 
			
		||||
 | 
			
		||||
        let conn = pool.get()?;
 | 
			
		||||
        let mut stmt =
 | 
			
		||||
            conn.prepare("insert into sessions (id, user_id) values ($1, $2) returning *")?;
 | 
			
		||||
 | 
			
		||||
        Ok(stmt.query_row((&id, &user_id), Self::from_row)?)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,94 +0,0 @@
 | 
			
		|||
use argon2::{
 | 
			
		||||
    password_hash::{rand_core::OsRng, SaltString},
 | 
			
		||||
    Argon2, PasswordHash, PasswordHasher, PasswordVerifier,
 | 
			
		||||
};
 | 
			
		||||
use rusqlite::Row;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
use super::{DbError, DbPool};
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize, Deserialize, Clone)]
 | 
			
		||||
pub struct User {
 | 
			
		||||
    pub id: i64,
 | 
			
		||||
    pub username: String,
 | 
			
		||||
    pub password_hash: String,
 | 
			
		||||
    pub admin: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize, Deserialize)]
 | 
			
		||||
pub struct NewUser {
 | 
			
		||||
    pub username: String,
 | 
			
		||||
    pub password: String,
 | 
			
		||||
    pub admin: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl User {
 | 
			
		||||
    pub fn from_row(row: &Row<'_>) -> Result<Self, rusqlite::Error> {
 | 
			
		||||
        Ok(Self {
 | 
			
		||||
            id: row.get("id")?,
 | 
			
		||||
            username: row.get("username")?,
 | 
			
		||||
            password_hash: row.get("password_hash")?,
 | 
			
		||||
            admin: row.get("admin")?,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn by_username(pool: &DbPool, username: impl AsRef<str>) -> Result<Option<Self>, DbError> {
 | 
			
		||||
        let conn = pool.get()?;
 | 
			
		||||
 | 
			
		||||
        let mut stmt = conn.prepare("select * from users where username = $1")?;
 | 
			
		||||
 | 
			
		||||
        match stmt.query_row((username.as_ref(),), User::from_row) {
 | 
			
		||||
            Ok(user) => Ok(Some(user)),
 | 
			
		||||
            Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
 | 
			
		||||
            Err(err) => Err(DbError::Db(err)),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn verify_password(&self, password: impl AsRef<str>) -> bool {
 | 
			
		||||
        let password_hash = PasswordHash::new(&self.password_hash).unwrap();
 | 
			
		||||
 | 
			
		||||
        Argon2::default()
 | 
			
		||||
            .verify_password(password.as_ref().as_bytes(), &password_hash)
 | 
			
		||||
            .is_ok()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn all(pool: &DbPool) -> Result<Vec<Self>, DbError> {
 | 
			
		||||
        let conn = pool.get()?;
 | 
			
		||||
 | 
			
		||||
        let mut stmt = conn.prepare("select * from users")?;
 | 
			
		||||
        let users: Result<Vec<_>, _> = stmt.query_map((), Self::from_row)?.collect();
 | 
			
		||||
 | 
			
		||||
        Ok(users?)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn remove_by_username(pool: &DbPool, username: impl AsRef<str>) -> Result<bool, DbError> {
 | 
			
		||||
        let conn = pool.get()?;
 | 
			
		||||
        let mut stmt = conn.prepare("delete from users where username = $1")?;
 | 
			
		||||
 | 
			
		||||
        Ok(stmt.execute((username.as_ref(),))? > 0)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn hash_password(password: impl AsRef<str>) -> String {
 | 
			
		||||
    let salt = SaltString::generate(&mut OsRng);
 | 
			
		||||
    let argon2 = Argon2::default();
 | 
			
		||||
 | 
			
		||||
    argon2
 | 
			
		||||
        .hash_password(password.as_ref().as_bytes(), &salt)
 | 
			
		||||
        .unwrap()
 | 
			
		||||
        .to_string()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl NewUser {
 | 
			
		||||
    pub fn insert(self, pool: &DbPool) -> Result<User, DbError> {
 | 
			
		||||
        let conn = pool.get()?;
 | 
			
		||||
 | 
			
		||||
        let password_hash = hash_password(&self.password);
 | 
			
		||||
 | 
			
		||||
        let mut stmt = conn.prepare(
 | 
			
		||||
            "insert into users (username, password_hash, admin) values ($1, $2, $3) returning *",
 | 
			
		||||
        )?;
 | 
			
		||||
 | 
			
		||||
        Ok(stmt.query_row((&self.username, password_hash, &self.admin), User::from_row)?)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -5,7 +5,6 @@ mod server;
 | 
			
		|||
use std::{fs, path::Path, sync::Arc};
 | 
			
		||||
 | 
			
		||||
use clap::Parser;
 | 
			
		||||
use diesel_migrations::{embed_migrations, EmbeddedMigrations};
 | 
			
		||||
use tera::Tera;
 | 
			
		||||
use tower_http::compression::CompressionLayer;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,7 +3,7 @@ use axum::{
 | 
			
		|||
    http::{HeaderMap, StatusCode},
 | 
			
		||||
    middleware::Next,
 | 
			
		||||
    response::{Html, IntoResponse, Response},
 | 
			
		||||
    routing::{get, post},
 | 
			
		||||
    routing::post,
 | 
			
		||||
    Form, Router,
 | 
			
		||||
};
 | 
			
		||||
use axum_extra::extract::{
 | 
			
		||||
| 
						 | 
				
			
			@ -11,11 +11,10 @@ use axum_extra::extract::{
 | 
			
		|||
    CookieJar,
 | 
			
		||||
};
 | 
			
		||||
use serde::Deserialize;
 | 
			
		||||
use tera::Context;
 | 
			
		||||
 | 
			
		||||
use crate::db::{DbError, DbPool, Session, User};
 | 
			
		||||
 | 
			
		||||
use super::{error::AppError, render_view};
 | 
			
		||||
use super::error::AppError;
 | 
			
		||||
 | 
			
		||||
pub fn logged_in_user(pool: &DbPool, headers: &HeaderMap) -> Result<Option<User>, DbError> {
 | 
			
		||||
    let jar = CookieJar::from_headers(headers);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue