routes: pastes: Finish impl of paste creation

Signed-off-by: ATechnoHazard <amolele@gmail.com>
This commit is contained in:
Amogh Lele 2020-06-26 00:19:20 +05:30
parent 0e5abac774
commit 8e44b02235
No known key found for this signature in database
GPG Key ID: F475143EDEDEBA3C
15 changed files with 141 additions and 22 deletions

31
Cargo.lock generated
View File

@ -64,6 +64,12 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "anyhow"
version = "1.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f"
[[package]] [[package]]
name = "arrayref" name = "arrayref"
version = "0.3.6" version = "0.3.6"
@ -124,6 +130,18 @@ version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e223af0dc48c96d4f8342ec01a4974f139df863896b316681efd36742f22cc67" checksum = "e223af0dc48c96d4f8342ec01a4974f139df863896b316681efd36742f22cc67"
[[package]]
name = "bcrypt"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41b70db86f3c560199b0dada79a22b9a924622384abb2a756a9707ffcce077f2"
dependencies = [
"base64 0.12.2",
"blowfish",
"byteorder",
"getrandom",
]
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.2.1" version = "1.2.1"
@ -171,6 +189,17 @@ dependencies = [
"byte-tools", "byte-tools",
] ]
[[package]]
name = "blowfish"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6aeb80d00f2688459b8542068abd974cfb101e7a82182414a99b5026c0d85cc3"
dependencies = [
"block-cipher-trait",
"byteorder",
"opaque-debug",
]
[[package]] [[package]]
name = "bumpalo" name = "bumpalo"
version = "3.4.0" version = "3.4.0"
@ -601,6 +630,8 @@ dependencies = [
name = "katbin" name = "katbin"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow",
"bcrypt",
"chrono", "chrono",
"diesel", "diesel",
"diesel_migrations", "diesel_migrations",

View File

@ -18,6 +18,8 @@ dotenv = "0.15.0"
jsonwebtoken = "7.1.2" jsonwebtoken = "7.1.2"
slog = "2.5.2" slog = "2.5.2"
slog-term = "2.6.0" slog-term = "2.6.0"
anyhow = "1.0"
slog-async = "2.5.0" slog-async = "2.5.0"
bcrypt = "0.8"
chrono = "0.4.11" chrono = "0.4.11"
uuid = { version = "0.8", features = ["serde", "v4"] } uuid = { version = "0.8", features = ["serde", "v4"] }

View File

@ -1,5 +1,5 @@
# For documentation on how to configure this file, # For documentation on how to configure this file,
# see diesel.rs/guides/configuring-diesel-cli # see postgres/guides/configuring-diesel-cli
[print_schema] [print_schema]
file = "src/schema.rs" file = "src/schema.rs"

View File

@ -1,17 +1,19 @@
use rocket::{Rocket, response::status, http::{Status, Cookies, Cookie}}; use std::ops::DerefMut;
use rocket_contrib::json::Json;
use crate::core::paste::{entity::Paste, diesel::create_paste};
use serde_json::Value;
use crate::utils::{db, phonetic_key};
use std::ops::{DerefMut};
use rocket::response::status::Custom;
use crate::utils::phonetic_key::get_random_id;
use rocket::{http::{Cookie, Cookies, Status}, response::status, Rocket};
use rocket::response::status::Custom;
use rocket_contrib::json::Json;
use serde_json::Value;
use crate::core::paste::{entity::Paste, service::create_paste};
use crate::core::users::{service::create_or_fetch_user};
use crate::utils::{db, phonetic_key};
use crate::utils::phonetic_key::get_random_id;
#[post("/", data = "<paste>")] #[post("/", data = "<paste>")]
fn create(mut paste: Json<Paste>, conn: db::DbConn, mut ck: Cookies) -> Custom<Json<Value>> { fn create(mut paste: Json<Paste>, conn: db::DbConn, mut ck: Cookies) -> Custom<Json<Value>> {
// Check if frontend sent a session cookie // Check if frontend sent a session cookie
let session = match ck.get_private("session") { let user_id = match ck.get_private("session") {
Some(c) => c.value().to_string(), Some(c) => c.value().to_string(),
None => { None => {
let user_id = get_random_id(); let user_id = get_random_id();
@ -20,11 +22,22 @@ fn create(mut paste: Json<Paste>, conn: db::DbConn, mut ck: Cookies) -> Custom<J
} }
}; };
// Create or fetch already existing user
let user = match create_or_fetch_user(user_id, &conn) {
Ok(user) => user,
Err(e) => return status::Custom(Status::InternalServerError, Json(json!({
"err": e.to_string(),
"msg": "Failed to create or fetch user"
})))
};
let new_paste = paste.deref_mut(); let new_paste = paste.deref_mut();
if new_paste.id.is_none() { if new_paste.id.is_none() {
new_paste.id = Some(phonetic_key::get_random_id()); new_paste.id = Some(phonetic_key::get_random_id());
} }
new_paste.belongs_to = Some(user.id);
match create_paste(new_paste, &conn) { match create_paste(new_paste, &conn) {
Ok(_) => { Ok(_) => {
status::Custom(Status::Created, Json(json!({ status::Custom(Status::Created, Json(json!({

View File

@ -4,7 +4,7 @@ use crate::schema::pastes;
#[derive(AsChangeset, Serialize, Deserialize, Queryable, Insertable)] #[derive(AsChangeset, Serialize, Deserialize, Queryable, Insertable)]
pub struct Paste { pub struct Paste {
pub id: Option<String>, pub id: Option<String>,
pub belongs_to: String, pub belongs_to: Option<String>,
pub is_url: bool, pub is_url: bool,
pub content: String pub content: String,
} }

View File

@ -1,4 +1,3 @@
pub mod entity; pub mod entity;
pub mod repository; pub mod postgres;
pub mod diesel;
pub mod service; pub mod service;

View File

@ -1,13 +1,14 @@
use diesel::{RunQueryDsl}; use anyhow::Result;
use diesel::pg::PgConnection; use diesel::pg::PgConnection;
use diesel::result::Error; use diesel::RunQueryDsl;
use crate::schema::pastes; use crate::schema::pastes;
use super::entity::Paste; use super::entity::Paste;
pub fn create_paste(paste: &Paste, conn: &PgConnection) -> Result<usize, Error> { pub fn create_paste(paste: &Paste, conn: &PgConnection) -> Result<usize> {
diesel::insert_into(pastes::table) let rows = diesel::insert_into(pastes::table)
.values(paste) .values(paste)
.execute(conn) .execute(conn)?;
Ok(rows)
} }

View File

@ -0,0 +1,9 @@
use anyhow::Result;
use diesel::pg::PgConnection;
use super::entity::Paste;
use super::postgres;
pub fn create_paste(paste: &Paste, conn: &PgConnection) -> Result<usize> {
postgres::create_paste(paste, conn)
}

View File

@ -0,0 +1,10 @@
use crate::schema::users;
#[table_name = "users"]
#[derive(AsChangeset, Serialize, Deserialize, Queryable, Insertable)]
pub struct User {
pub id: String,
pub username: Option<String>,
pub password: Option<String>,
pub activated: Option<bool>,
}

View File

@ -1,4 +1,3 @@
pub mod diesel; pub mod postgres;
pub mod entity; pub mod entity;
pub mod repository;
pub mod service; pub mod service;

View File

@ -0,0 +1,19 @@
use diesel::prelude::*;
use diesel::pg::PgConnection;
use anyhow::Result;
use crate::core::users::entity::User;
use crate::schema::users;
pub fn create_user(user: &User, conn: &PgConnection) -> Result<usize> {
let records = diesel::insert_into(users::table)
.values(user)
.on_conflict_do_nothing()
.execute(conn)?;
Ok(records)
}
pub fn find_user(id: String, conn: &PgConnection) -> Result<User> {
let user = users::table.find(id).get_result::<User>(conn)?;
Ok(user)
}

View File

@ -0,0 +1,36 @@
use anyhow::Result;
use bcrypt::{DEFAULT_COST, hash};
use diesel::pg::PgConnection;
use diesel::result::Error;
use crate::core::users::entity::User;
use super::postgres;
pub fn create_user(user: &mut User, conn: &PgConnection) -> Result<usize> {
let hashed_pass = hash(user.password.as_ref().unwrap().as_bytes(), DEFAULT_COST)?;
user.password = Some(hashed_pass);
postgres::create_user(user, conn)
}
pub fn create_or_fetch_user(id: String, conn: &PgConnection) -> Result<User> {
let user = match postgres::find_user(id.clone(), conn) {
Ok(user) => user,
Err(err) => {
match err.downcast_ref::<Error>() {
Some(Error::NotFound) => {
let new_user = User {
id: id.clone(),
username: None,
password: None,
activated: Some(false),
};
postgres::create_user(&new_user, conn)?;
new_user
}
_ => return Err(err)
}
}
};
Ok(user)
}