summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.sqlx/query-225521d5c1c07dd1be7873b7589f3d9a9789b2a3b099d98753a2997bc046e99e.json28
-rw-r--r--.sqlx/query-7609165d94c8f1bea9d535b9b7ad727fd06592973d7f83017292d41acb203be6.json75
-rw-r--r--.sqlx/query-c9482dda19cb67fab64db269b2a993a7b12da41c392fbf8f66c29a1c08531327.json (renamed from .sqlx/query-e3f7e6a9b6f2413adf4467e112b7aa0170d9d3ad0aa78ddc0580e9d0f23c0f7f.json)8
-rw-r--r--src/db/mod.rs18
-rw-r--r--src/db/users.rs68
-rw-r--r--src/lib.rs5
-rw-r--r--src/main.rs5
-rw-r--r--src/routes/home.rs7
-rw-r--r--src/routes/login.rs18
-rw-r--r--src/routes/signup.rs16
-rw-r--r--src/routes/users.rs7
-rw-r--r--src/users.rs25
-rw-r--r--templates/login.rs.html2
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?)
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 591d0c6..2105040 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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" />