diff options
-rw-r--r-- | .sqlx/query-225521d5c1c07dd1be7873b7589f3d9a9789b2a3b099d98753a2997bc046e99e.json | 28 | ||||
-rw-r--r-- | .sqlx/query-7609165d94c8f1bea9d535b9b7ad727fd06592973d7f83017292d41acb203be6.json | 75 | ||||
-rw-r--r-- | .sqlx/query-c9482dda19cb67fab64db269b2a993a7b12da41c392fbf8f66c29a1c08531327.json (renamed from .sqlx/query-e3f7e6a9b6f2413adf4467e112b7aa0170d9d3ad0aa78ddc0580e9d0f23c0f7f.json) | 8 | ||||
-rw-r--r-- | src/db/mod.rs | 18 | ||||
-rw-r--r-- | src/db/users.rs | 68 | ||||
-rw-r--r-- | src/lib.rs | 5 | ||||
-rw-r--r-- | src/main.rs | 5 | ||||
-rw-r--r-- | src/routes/home.rs | 7 | ||||
-rw-r--r-- | src/routes/login.rs | 18 | ||||
-rw-r--r-- | src/routes/signup.rs | 16 | ||||
-rw-r--r-- | src/routes/users.rs | 7 | ||||
-rw-r--r-- | src/users.rs | 25 | ||||
-rw-r--r-- | templates/login.rs.html | 2 |
13 files changed, 237 insertions, 45 deletions
diff --git a/.sqlx/query-225521d5c1c07dd1be7873b7589f3d9a9789b2a3b099d98753a2997bc046e99e.json b/.sqlx/query-225521d5c1c07dd1be7873b7589f3d9a9789b2a3b099d98753a2997bc046e99e.json new file mode 100644 index 0000000..5cb27e4 --- /dev/null +++ b/.sqlx/query-225521d5c1c07dd1be7873b7589f3d9a9789b2a3b099d98753a2997bc046e99e.json @@ -0,0 +1,28 @@ +{ + "db_name": "PostgreSQL", + "query": "select id, password from users where username = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int4" + }, + { + "ordinal": 1, + "name": "password", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false + ] + }, + "hash": "225521d5c1c07dd1be7873b7589f3d9a9789b2a3b099d98753a2997bc046e99e" +} diff --git a/.sqlx/query-7609165d94c8f1bea9d535b9b7ad727fd06592973d7f83017292d41acb203be6.json b/.sqlx/query-7609165d94c8f1bea9d535b9b7ad727fd06592973d7f83017292d41acb203be6.json new file mode 100644 index 0000000..85dc959 --- /dev/null +++ b/.sqlx/query-7609165d94c8f1bea9d535b9b7ad727fd06592973d7f83017292d41acb203be6.json @@ -0,0 +1,75 @@ +{ + "db_name": "PostgreSQL", + "query": "select * from users where id = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int4" + }, + { + "ordinal": 1, + "name": "username", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "password", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 4, + "name": "bio", + "type_info": "Text" + }, + { + "ordinal": 5, + "name": "site", + "type_info": "Varchar" + }, + { + "ordinal": 6, + "name": "privacy", + "type_info": { + "Custom": { + "name": "privacy", + "kind": { + "Enum": [ + "private", + "unlisted", + "public" + ] + } + } + } + }, + { + "ordinal": 7, + "name": "admin", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Int4" + ] + }, + "nullable": [ + false, + false, + false, + true, + true, + true, + false, + false + ] + }, + "hash": "7609165d94c8f1bea9d535b9b7ad727fd06592973d7f83017292d41acb203be6" +} diff --git a/.sqlx/query-e3f7e6a9b6f2413adf4467e112b7aa0170d9d3ad0aa78ddc0580e9d0f23c0f7f.json b/.sqlx/query-c9482dda19cb67fab64db269b2a993a7b12da41c392fbf8f66c29a1c08531327.json index 77c11b6..32952b5 100644 --- a/.sqlx/query-e3f7e6a9b6f2413adf4467e112b7aa0170d9d3ad0aa78ddc0580e9d0f23c0f7f.json +++ b/.sqlx/query-c9482dda19cb67fab64db269b2a993a7b12da41c392fbf8f66c29a1c08531327.json @@ -1,22 +1,22 @@ { "db_name": "PostgreSQL", - "query": "select password from users where username = $1", + "query": "select username from users where id = $1", "describe": { "columns": [ { "ordinal": 0, - "name": "password", + "name": "username", "type_info": "Varchar" } ], "parameters": { "Left": [ - "Text" + "Int4" ] }, "nullable": [ false ] }, - "hash": "e3f7e6a9b6f2413adf4467e112b7aa0170d9d3ad0aa78ddc0580e9d0f23c0f7f" + "hash": "c9482dda19cb67fab64db269b2a993a7b12da41c392fbf8f66c29a1c08531327" } diff --git a/src/db/mod.rs b/src/db/mod.rs new file mode 100644 index 0000000..f010bf8 --- /dev/null +++ b/src/db/mod.rs @@ -0,0 +1,18 @@ +mod users; + +use sqlx::{Pool, Postgres}; + +use self::users::Users; + +#[derive(Clone)] +pub struct Database(Pool<Postgres>); + +impl Database { + pub fn new(pool: Pool<Postgres>) -> Self { + Self(pool) + } + + pub fn users(&self) -> Users { + Users::new(self.0.clone()) + } +} diff --git a/src/db/users.rs b/src/db/users.rs new file mode 100644 index 0000000..0fc7c64 --- /dev/null +++ b/src/db/users.rs @@ -0,0 +1,68 @@ +use sqlx::{Pool, Postgres}; + +use crate::users::User; +use crate::Result; + +#[derive(Clone)] +pub struct Users(Pool<Postgres>); +// code code code code code code code code code code code code code code +impl Users { + pub fn new(pool: Pool<Postgres>) -> Self { + Self(pool) + } + + pub async fn create(&self, user: User) -> Result<()> { + sqlx::query!( + r#"insert into users (username, password, email, bio, site, privacy, admin) values ($1, $2, $3, $4, $5, $6, $7)"#, + user.username, + user.password, + user.email, + user.bio, + user.site, + user.privacy as _, + user.admin + ) + .execute(&self.0) + .await?; + Ok(()) + } + + pub async fn read(&self, user_id: i32) -> Result<User> { + Ok( + sqlx::query_as!( + User, + "select username, password, email, bio, site, privacy as \"privacy: _\", admin from users where id = $1", + user_id + ) + .fetch_one(&self.0) + .await? + ) + } + + pub async fn read_username(&self, username: &str) -> Result<User> { + Ok( + sqlx::query_as!( + User, + "select username, password, email, bio, site, privacy as \"privacy: _\", admin from users where username = $1", + username + ) + .fetch_one(&self.0) + .await? + ) + } + + pub async fn get_id(&self, username: &str) -> Result<i32> { + Ok( + sqlx::query!("select id from users where username = $1", username) + .fetch_one(&self.0) + .await? + .id, + ) + } + + pub async fn read_all(&self) -> Result<Vec<User>> { + Ok(sqlx::query_as("select * from users") + .fetch_all(&self.0) + .await?) + } +} @@ -1,18 +1,17 @@ #[macro_use] mod actix_ructe; +pub mod db; mod error; mod notification; pub mod routes; mod users; -use sqlx::{Pool, Postgres}; - type Result<T> = std::result::Result<T, crate::error::PinussyError>; #[derive(Clone)] pub struct Pinussy { - pub db: Pool<Postgres>, + pub db: db::Database, } #[derive(sqlx::Type)] diff --git a/src/main.rs b/src/main.rs index 6a439ec..a488835 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ use actix_web::middleware::ErrorHandlers; use actix_web::{web, App, HttpServer}; use sqlx::postgres::PgPoolOptions; +use pinussy::db::Database; use pinussy::routes; use pinussy::Pinussy; @@ -19,7 +20,9 @@ async fn main() -> std::io::Result<()> { sqlx::migrate!("./migrations").run(&pool).await.unwrap(); - let pinussy = Pinussy { db: pool }; + let pinussy = Pinussy { + db: Database::new(pool), + }; HttpServer::new(move || { App::new() diff --git a/src/routes/home.rs b/src/routes/home.rs index 11d3a72..a43eabc 100644 --- a/src/routes/home.rs +++ b/src/routes/home.rs @@ -8,12 +8,7 @@ use crate::{Pinussy, Result}; async fn get(session: Session, state: web::Data<Pinussy>) -> Result<HttpResponse> { let username: Option<String>; if let Some(user_id) = session.get::<i32>("user_id")? { - username = Some( - sqlx::query!("select username from users where id = $1", user_id) - .fetch_one(&state.db) - .await? - .username, - ) + username = Some(state.db.users().read(user_id).await?.username) } else { username = None } diff --git a/src/routes/login.rs b/src/routes/login.rs index 33f7f69..c6cf077 100644 --- a/src/routes/login.rs +++ b/src/routes/login.rs @@ -1,9 +1,9 @@ use actix_session::Session; use actix_web::http::header::LOCATION; use actix_web::{get, post, web, HttpResponse}; -use bcrypt::verify; use serde::Deserialize; +use crate::error::PinussyError; use crate::notification::{Kind, Notification}; use crate::templates; use crate::Pinussy; @@ -27,17 +27,11 @@ async fn post( session: Session, form: web::Form<LoginForm>, ) -> Result<HttpResponse> { - match sqlx::query!( - "select id, password from users where username = $1", - &form.username - ) - .fetch_one(&state.db) - .await - { + match state.db.users().read_username(&form.username).await { Ok(user) => { - let password_hash: String = user.password; - if verify(&form.password, &password_hash)? { - session.insert("user_id", user.id)?; + if user.verify_password(&form.password)? { + let user_id = state.db.users().get_id(&form.username).await?; + session.insert("user_id", user_id)?; return Ok(HttpResponse::SeeOther() .insert_header((LOCATION, "/")) .finish()); @@ -54,7 +48,7 @@ async fn post( )); } } - Err(sqlx::Error::RowNotFound) => { + Err(PinussyError::Database(sqlx::Error::RowNotFound)) => { return Ok(HttpResponse::NotFound().body( render!( templates::login_html, diff --git a/src/routes/signup.rs b/src/routes/signup.rs index ae10201..bb1f714 100644 --- a/src/routes/signup.rs +++ b/src/routes/signup.rs @@ -1,11 +1,12 @@ use actix_web::{get, post, web, HttpResponse}; use serde::Deserialize; +use crate::error::PinussyError; use crate::notification::{Kind as NotificationKind, Notification}; use crate::templates; +use crate::users::User; use crate::Pinussy; use crate::Result; -use bcrypt::{hash, DEFAULT_COST}; #[get("/signup")] async fn get() -> HttpResponse { @@ -20,15 +21,8 @@ struct SignupForm { #[post("/signup")] async fn post(state: web::Data<Pinussy>, form: web::Form<SignupForm>) -> Result<HttpResponse> { - let password_hash = hash(&form.password, DEFAULT_COST)?; - match sqlx::query!( - "insert into users(username, password) values ($1, $2)", - &form.username, - password_hash - ) - .execute(&state.db) - .await - { + let new_user = User::new(form.username.clone(), form.password.clone())?; + match state.db.users().create(new_user).await { Ok(_) => { return Ok(HttpResponse::Ok().body( render!( @@ -43,7 +37,7 @@ async fn post(state: web::Data<Pinussy>, form: web::Form<SignupForm>) -> Result< } Err(e) => { match e { - sqlx::Error::Database(e) => { + PinussyError::Database(sqlx::Error::Database(e)) => { if e.is_unique_violation() { return Ok(HttpResponse::Conflict().body( render!( diff --git a/src/routes/users.rs b/src/routes/users.rs index 2ad9ede..eb08ade 100644 --- a/src/routes/users.rs +++ b/src/routes/users.rs @@ -6,11 +6,6 @@ use crate::{Pinussy, Result}; #[get("/users")] async fn get(state: web::Data<Pinussy>) -> Result<HttpResponse> { - let users: Vec<User> = sqlx::query_as("select * from users") - .fetch_all(&state.db) - .await - // TODO: no unwrap - .unwrap(); - println!("lol"); + let users: Vec<User> = state.db.users().read_all().await?; Ok(HttpResponse::Ok().body(render!(templates::users_html, users).unwrap())) } diff --git a/src/users.rs b/src/users.rs index bf41fd5..4cf9310 100644 --- a/src/users.rs +++ b/src/users.rs @@ -1,8 +1,12 @@ +use bcrypt::hash; +use bcrypt::verify; +use bcrypt::DEFAULT_COST; + use crate::Privacy; +use crate::Result; #[derive(sqlx::FromRow)] pub struct User { - pub id: i32, pub username: String, pub password: String, pub email: Option<String>, @@ -11,3 +15,22 @@ pub struct User { pub privacy: Privacy, pub admin: bool, } + +impl User { + pub fn new(username: String, password: String) -> Result<Self> { + let password_hash = hash(password, DEFAULT_COST)?; + Ok(Self { + username, + password: password_hash, + email: None, + bio: None, + site: None, + privacy: Privacy::Public, + admin: true, + }) + } + + pub fn verify_password(&self, password: &str) -> Result<bool> { + Ok(verify(password, &self.password)?) + } +} diff --git a/templates/login.rs.html b/templates/login.rs.html index 10efaf3..6dff4f4 100644 --- a/templates/login.rs.html +++ b/templates/login.rs.html @@ -3,7 +3,7 @@ @(notification: Option<Notification>) -@:base_html(None, None, { +@:base_html(None, notification, { <form action="/login" method="post"> <label for="username">username:</label> <input type="text" id="username" name="username" required="true" /> |