User Authentication and Other fixes #1

Merged
vinod merged 30 commits from user-auth into main 2023-02-05 09:01:08 +00:00
9 changed files with 1760 additions and 1145 deletions
Showing only changes of commit 02963caa73 - Show all commits

View File

@@ -1,39 +1,41 @@
use axum::{
extract::{Extension, Path},
http::{HeaderValue, Method, StatusCode},
response::IntoResponse,
routing::{get,post,get_service},
routing::{get, get_service, post},
Json, Router,
extract::{Extension, Path},
};
use clap::Parser;
use image;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::net::{IpAddr, Ipv6Addr, SocketAddr};
use std::str::FromStr;
use std::io;
use tower::ServiceBuilder;
use tower_http::services::ServeDir;
use tower_http::cors::{Any,CorsLayer};
use tower_http::trace::TraceLayer;
use booksman_search::BookMeili;
use booksman_search;
use booksman_orm::{
sea_orm::{Database, DatabaseConnection},
Mutation as MutationCore, Query as QueryCore
// BookAndMeta,
Mutation as MutationCore,
Query as QueryCore, // BookAndMeta,
};
use booksman_search;
use booksman_search::BookMeili;
use clap::Parser;
use image;
use meilisearch_sdk::client::Client;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::io;
use std::net::{IpAddr, Ipv6Addr, SocketAddr};
use std::str::FromStr;
use tower::ServiceBuilder;
use tower_http::cors::{Any, CorsLayer};
use tower_http::services::ServeDir;
use tower_http::trace::TraceLayer;
//use itertools::Itertools;
use ::entity::entities::{book,book_author,book_person,book_place,book_subject,book_time,book_isbn,user};
use std::env;
use migration::{Migrator, MigratorTrait};
use chrono::Local;
use ::entity::entities::{
book, book_author, book_isbn, book_person, book_place, book_subject, book_time, user,
};
use axum_login::{
axum_sessions::{async_session::MemoryStore as SessionMemoryStore, SessionLayer},
AuthLayer, RequireAuthorizationLayer,
};
use chrono::Local;
use migration::{Migrator, MigratorTrait};
use rand::Rng;
use std::env;
#[derive(Deserialize, Serialize, Debug, Clone)]
struct BookUI {
@@ -83,7 +85,6 @@ struct Books {
docs: Vec<Docs>,
}
#[derive(Deserialize, Serialize, Debug)]
struct PaginatedBookUIList {
num_pages: u32,
@@ -142,7 +143,6 @@ pub struct Cover {
pub medium: String,
}
impl Books {
fn set_all_cover_urls(&mut self) {
for book in self.docs.iter_mut() {
@@ -157,31 +157,38 @@ impl Books {
}
}
}
}
impl BookUI {
async fn set_descriptions(&mut self) {
if self.open_library_key.is_some() {
if !(self.open_library_key.as_ref().unwrap().is_empty()) {
let query = format!("https://openlibrary.org/{}.json", self.open_library_key.as_ref().unwrap().clone());
let query = format!(
"https://openlibrary.org/{}.json",
self.open_library_key.as_ref().unwrap().clone()
);
let res = reqwest::get(query).await.expect("Unable to request");
let resjson = res.json::<BookPageOL>().await.expect("Unable to return value");
let resjson = res
.json::<BookPageOL>()
.await
.expect("Unable to return value");
let description = resjson.description;
if !description.is_none() {
if let DescriptionOL::DescriptionString(desc_string) = description.clone().unwrap() {
if let DescriptionOL::DescriptionString(desc_string) =
description.clone().unwrap()
{
self.description = Some(desc_string);
}
if let DescriptionOL::DescriptionValueString(desc_val) = description.clone().unwrap() {
if let DescriptionOL::DescriptionValueString(desc_val) =
description.clone().unwrap()
{
self.description = Some(desc_val.value);
}
}
}
}
}
}
async fn handle_error(_err: io::Error) -> impl IntoResponse {
@@ -209,7 +216,8 @@ struct Opt {
static_dir: String,
}
type AuthContext = axum_login::extractors::AuthContext<booksman_orm::AxumUser, booksman_orm::AxumUserStore>;
type AuthContext =
axum_login::extractors::AuthContext<booksman_orm::AxumUser, booksman_orm::AxumUserStore>;
#[tokio::main]
pub async fn main() {
@@ -222,7 +230,6 @@ pub async fn main() {
// enable console logging
tracing_subscriber::fmt::init();
dotenvy::dotenv().ok();
let images_dir = env::var("IMAGES_DIR").expect("IMAGES_DIR is not set in .env file");
let cors_url = env::var("CORS_URL").expect("CORS_URL is not set in .env file");
@@ -238,17 +245,15 @@ pub async fn main() {
// Apply all pending migrations
Migrator::up(&conn, None).await.unwrap();
let session_store = SessionMemoryStore::new();
let session_layer = SessionLayer::new(session_store, &secret).with_secure(false);
let user_store = booksman_orm::AxumUserStore::new(&conn);
let auth_layer: AuthLayer<booksman_orm::AxumUserStore, booksman_orm::AxumUser> = AuthLayer::new(user_store, &secret);
let auth_layer: AuthLayer<booksman_orm::AxumUserStore, booksman_orm::AxumUser> =
AuthLayer::new(user_store, &secret);
let meili_client = Client::new(meili_url, meili_key);
let app = Router::new()
.route("/api/create_by_isbn", get(create_by_isbn))
.route("/api/create", post(create_book))
@@ -263,8 +268,14 @@ pub async fn main() {
.route("/api/login", post(login_handler))
.route("/api/register", post(register_handler))
.route("/api/logout", post(logout_handler))
.nest_service("/images", get_service(ServeDir::new(images_dir)).handle_error(handle_error))
.nest_service("/assets", get_service(ServeDir::new(opt.static_dir)).handle_error(handle_error))
.nest_service(
"/images",
get_service(ServeDir::new(images_dir)).handle_error(handle_error),
)
.nest_service(
"/assets",
get_service(ServeDir::new(opt.static_dir)).handle_error(handle_error),
)
// .merge(SpaRouter::new("/assets", opt.static_dir))
.layer(auth_layer)
.layer(session_layer)
@@ -297,26 +308,26 @@ pub async fn main() {
.expect("Unable to start server");
}
//#[axum_macros::debug_handler]
async fn register_handler(Extension(ref conn): Extension<DatabaseConnection>,
Json(user_sent): Json<booksman_orm::AxumUser>) -> impl IntoResponse {
async fn register_handler(
Extension(ref conn): Extension<DatabaseConnection>,
Json(user_sent): Json<booksman_orm::AxumUser>,
) -> impl IntoResponse {
// add to db
let user: user::Model = user::Model {
id: user_sent.id,
user_name: Some(user_sent.clone().name),
password_hash: user_sent.clone().password_hash,
};
let _created_user = MutationCore::create_user(conn, user).await.expect("Failed to create user");
let _created_user = MutationCore::create_user(conn, user)
.await
.expect("Failed to create user");
return "success";
}
async fn list_users(
Extension(ref conn): Extension<DatabaseConnection>,
) -> impl IntoResponse {
let usersmodels : Vec<user::Model> = QueryCore::list_all_users(&conn).await.unwrap_or(Vec::new());
async fn list_users(Extension(ref conn): Extension<DatabaseConnection>) -> impl IntoResponse {
let usersmodels: Vec<user::Model> =
QueryCore::list_all_users(&conn).await.unwrap_or(Vec::new());
let mut users: Vec<booksman_orm::AxumUser> = Vec::new();
for usermodel in usersmodels.iter() {
let user = booksman_orm::AxumUser {
@@ -328,13 +339,20 @@ async fn list_users(
}
return Json(users);
}
async fn login_handler(mut auth: AuthContext, Extension(ref conn): Extension<DatabaseConnection>, Json(user_sent): Json<booksman_orm::AxumUser>) -> impl IntoResponse {
let userdb : user::Model = QueryCore::find_userid_by_name(conn, user_sent.name.clone()).await.unwrap().unwrap();
async fn login_handler(
mut auth: AuthContext,
Extension(ref conn): Extension<DatabaseConnection>,
Json(user_sent): Json<booksman_orm::AxumUser>,
) -> impl IntoResponse {
let userdb: user::Model = QueryCore::find_userid_by_name(conn, user_sent.name.clone())
.await
.unwrap()
.unwrap();
let userid = userdb.id;
let corrected_user = booksman_orm::AxumUser {
id: userid,
name: user_sent.name.clone(),
password_hash: user_sent.password_hash.clone()
password_hash: user_sent.password_hash.clone(),
};
auth.login(&corrected_user).await.unwrap();
return "success";
@@ -354,7 +372,6 @@ async fn authentication_check(
return Json(true);
}
//https://openlibrary.org/api/books?bibkeys=ISBN:9780980200447&jscmd=data&format=json
async fn create_by_isbn(
Extension(ref conn): Extension<DatabaseConnection>,
@@ -362,67 +379,142 @@ async fn create_by_isbn(
Extension(user): Extension<booksman_orm::AxumUser>,
axum::extract::Query(params): axum::extract::Query<HashMap<String, String>>,
) -> impl IntoResponse {
dotenvy::dotenv().ok();
let images_dir = env::var("IMAGES_DIR").expect("IMAGES_DIR is not set in .env file");
let userid = user.id;
print!("Get items with query params: {:?}\n", params);
let isbns = params.get("isbns").unwrap();
let splitisbns : Vec<String> = isbns.split(",").map(|s| format!("ISBN:{}",s.to_string())).collect();
let splitisbns: Vec<String> = isbns
.split(",")
.map(|s| format!("ISBN:{}", s.to_string()))
.collect();
let joinedisbns = splitisbns.join(",");
let query = format!("https://openlibrary.org/api/books?bibkeys={}&jscmd=data&format=json", joinedisbns);
let query = format!(
"https://openlibrary.org/api/books?bibkeys={}&jscmd=data&format=json",
joinedisbns
);
let res = reqwest::get(query).await.expect("Unable to request");
let resjson: BooksManyISBN = res.json::<BooksManyISBN>().await.expect("Unable to return value");
let resjson: BooksManyISBN = res
.json::<BooksManyISBN>()
.await
.expect("Unable to return value");
for isbnstring in splitisbns.iter() {
let bookfound = resjson.book_items.get(isbnstring);
if !bookfound.clone().is_none() {
let mut cover_id = "".to_string();
if !bookfound.unwrap().cover.is_none() {
let coverurl = bookfound.clone().unwrap().cover.clone().unwrap().medium.clone();
let img_bytes = reqwest::get(coverurl.clone()).await.unwrap().bytes().await.unwrap();
let coverurl = bookfound
.clone()
.unwrap()
.cover
.clone()
.unwrap()
.medium
.clone();
let img_bytes = reqwest::get(coverurl.clone())
.await
.unwrap()
.bytes()
.await
.unwrap();
let image = image::load_from_memory(&img_bytes).unwrap();
let temp_cover = coverurl.clone();
let img_id = temp_cover.split("/").last().unwrap();
image.save(format!("{}/{}",images_dir,img_id)).expect("Failed to save image");
image
.save(format!("{}/{}", images_dir, img_id))
.expect("Failed to save image");
cover_id = img_id.to_string();
}
let mut goodread_id = "".to_string();
if !bookfound.unwrap().identifiers.is_none() {
let goodread_identifier = bookfound.clone().unwrap().identifiers.clone().unwrap().goodreads;
let goodread_identifier = bookfound
.clone()
.unwrap()
.identifiers
.clone()
.unwrap()
.goodreads;
if !goodread_identifier.is_none() {
goodread_id = goodread_identifier.unwrap().get(0).unwrap().to_string()
}
}
//let mut authors_name = Some(vec!["".to_string()]);
//if !bookfound.unwrap().authors.is_none() {
let authors_name = Some(bookfound.clone().unwrap().authors.iter().map(|s| s.name.clone()).collect());
let authors_name = Some(
bookfound
.clone()
.unwrap()
.authors
.iter()
.map(|s| s.name.clone())
.collect(),
);
//} else {
// authors_name = None
//}
let persons: Option<Vec<String>>;
//= Some(vec!["".to_string()]);
if !bookfound.unwrap().subject_people.is_none() {
persons = Some(bookfound.clone().unwrap().subject_people.clone().unwrap().iter().map(|s| s.name.clone()).collect());
persons = Some(
bookfound
.clone()
.unwrap()
.subject_people
.clone()
.unwrap()
.iter()
.map(|s| s.name.clone())
.collect(),
);
} else {
persons = None;
}
let places: Option<Vec<String>>;
if !bookfound.unwrap().subject_places.is_none() {
places = Some(bookfound.clone().unwrap().subject_places.clone().unwrap().iter().map(|s| s.name.clone()).collect());
places = Some(
bookfound
.clone()
.unwrap()
.subject_places
.clone()
.unwrap()
.iter()
.map(|s| s.name.clone())
.collect(),
);
} else {
places = None;
}
let subjects: Option<Vec<String>>;
if !bookfound.unwrap().subjects.is_none() {
subjects = Some(bookfound.clone().unwrap().subjects.clone().unwrap().iter().map(|s| s.name.clone()).collect());
subjects = Some(
bookfound
.clone()
.unwrap()
.subjects
.clone()
.unwrap()
.iter()
.map(|s| s.name.clone())
.collect(),
);
} else {
subjects = None;
}
let times: Option<Vec<String>>;
if !bookfound.unwrap().subject_times.is_none() {
times = Some(bookfound.clone().unwrap().subject_times.clone().unwrap().iter().map(|s| s.name.clone()).collect());
times = Some(
bookfound
.clone()
.unwrap()
.subject_times
.clone()
.unwrap()
.iter()
.map(|s| s.name.clone())
.collect(),
);
} else {
times = None;
}
@@ -446,7 +538,7 @@ async fn create_by_isbn(
place: places,
subject: subjects,
time: times,
isbn: Some(vec![isbnstring.split("ISBN:").last().unwrap().to_string()])
isbn: Some(vec![isbnstring.split("ISBN:").last().unwrap().to_string()]),
};
doc_sent.set_descriptions().await;
let book: book::Model = book::Model {
@@ -488,7 +580,6 @@ async fn create_by_isbn(
MutationCore::create_book_person(conn, record)
.await
.expect("could not create book");
}
for place in doc_sent.place.as_ref().unwrap().iter() {
let record: book_place::Model = book_place::Model {
@@ -499,7 +590,6 @@ async fn create_by_isbn(
MutationCore::create_book_place(conn, record)
.await
.expect("could not create book");
}
for subject in doc_sent.subject.as_ref().unwrap().iter() {
let record: book_subject::Model = book_subject::Model {
@@ -510,7 +600,6 @@ async fn create_by_isbn(
MutationCore::create_book_subject(conn, record)
.await
.expect("could not create book");
}
for time in doc_sent.time.as_ref().unwrap().iter() {
let record: book_time::Model = book_time::Model {
@@ -521,7 +610,6 @@ async fn create_by_isbn(
MutationCore::create_book_time(conn, record)
.await
.expect("could not create book");
}
for isbn in doc_sent.isbn.as_ref().unwrap().iter() {
let record: book_isbn::Model = book_isbn::Model {
@@ -532,7 +620,6 @@ async fn create_by_isbn(
MutationCore::create_book_isbn(conn, record)
.await
.expect("could not create book");
}
let book_meili = BookMeili {
id: doc_sent.id,
@@ -563,7 +650,6 @@ async fn create_by_isbn(
return "Done";
}
async fn search_openlibrary(
axum::extract::Query(params): axum::extract::Query<HashMap<String, String>>,
) -> impl IntoResponse {
@@ -583,8 +669,7 @@ let mut vec = Vec::with_capacity(12);
if doc.id_goodreads.is_some() {
goodread = doc.id_goodreads.unwrap().get(0).unwrap().to_string();
}
vec.push(
BookUI{
vec.push(BookUI {
id: -((i + 1) as i32),
open_library_key: Some(doc.key),
title: doc.title,
@@ -603,9 +688,8 @@ let mut vec = Vec::with_capacity(12);
place: doc.place,
subject: doc.subject,
time: doc.time,
isbn: doc.isbn
}
);
isbn: doc.isbn,
});
}
return Json(vec);
}
@@ -628,8 +712,6 @@ async fn list_book(
Extension(ref conn): Extension<DatabaseConnection>,
axum::extract::Query(params): axum::extract::Query<HashMap<String, String>>,
) -> impl IntoResponse {
dotenvy::dotenv().ok();
let backend_url = env::var("BACKEND_URL").expect("BACKEND_URL is not set in .env file");
@@ -665,25 +747,66 @@ let mut resbooks: Vec<BookUI> = Vec::with_capacity(per_page);
time_added: bookandmeta.clone().book.time_added,
rating: bookandmeta.clone().book.rating,
comments: bookandmeta.clone().book.comments,
author_name: Some(bookandmeta.clone().authors.into_iter().map(|u| u.author_name).collect() ),
person: Some(bookandmeta.clone().persons.into_iter().map(|u| u.person).collect()),
place: Some(bookandmeta.clone().places.into_iter().map(|u| u.place).collect()),
subject: Some(bookandmeta.clone().subjects.into_iter().map(|u| u.subject).collect()),
time: Some(bookandmeta.clone().times.into_iter().map(|u| u.time).collect()),
isbn: Some(bookandmeta.clone().isbns.into_iter().map(|u| u.isbn).collect()),
author_name: Some(
bookandmeta
.clone()
.authors
.into_iter()
.map(|u| u.author_name)
.collect(),
),
person: Some(
bookandmeta
.clone()
.persons
.into_iter()
.map(|u| u.person)
.collect(),
),
place: Some(
bookandmeta
.clone()
.places
.into_iter()
.map(|u| u.place)
.collect(),
),
subject: Some(
bookandmeta
.clone()
.subjects
.into_iter()
.map(|u| u.subject)
.collect(),
),
time: Some(
bookandmeta
.clone()
.times
.into_iter()
.map(|u| u.time)
.collect(),
),
isbn: Some(
bookandmeta
.clone()
.isbns
.into_iter()
.map(|u| u.isbn)
.collect(),
),
};
resbooks.push(bookui);
}
let res = PaginatedBookUIList {
num_pages: books.1 as u32,
books: resbooks
books: resbooks,
};
return Json(res);
// "success"
}
async fn list_search_book(
Extension(ref meili_client): Extension<Client>,
axum::extract::Query(params): axum::extract::Query<HashMap<String, String>>,
@@ -695,7 +818,6 @@ async fn list_search_book(
dotenvy::dotenv().ok();
let backend_url = env::var("BACKEND_URL").expect("BACKEND_URL is not set in .env file");
let books = booksman_search::search_book(search, page, userid, meili_client)
.await
.expect("could not list books");
@@ -735,13 +857,12 @@ let mut resbooks: Vec<BookUI> = Vec::with_capacity(12);
let res = PaginatedBookUIList {
num_pages: (books.1 / 12 + 1) as u32,
books: resbooks
books: resbooks,
};
return Json(res);
// "success"
}
async fn create_book(
Extension(ref conn): Extension<DatabaseConnection>,
Extension(ref meili_client): Extension<Client>,
@@ -765,11 +886,22 @@ async fn create_book(
if !doc_sent.cover.is_none() {
if !doc_sent.cover.clone().unwrap().is_empty() {
let img_bytes = reqwest::get(cover.unwrap()).await.unwrap().bytes().await.unwrap();
let img_bytes = reqwest::get(cover.unwrap())
.await
.unwrap()
.bytes()
.await
.unwrap();
let image = image::load_from_memory(&img_bytes).unwrap();
let temp_cover = doc_sent.cover.clone().unwrap();
let img_id = format!("{}{}",Local::now().format("%Y-%m-%d-%H-%M-%S"),temp_cover.split("/").last().unwrap());
image.save(format!("{}/{}",images_dir,img_id)).expect("Failed to save image");
let img_id = format!(
"{}{}",
Local::now().format("%Y-%m-%d-%H-%M-%S"),
temp_cover.split("/").last().unwrap()
);
image
.save(format!("{}/{}", images_dir, img_id))
.expect("Failed to save image");
cover = Some(img_id.to_string());
}
}
@@ -794,7 +926,12 @@ async fn create_book(
.await
.expect("could not create book");
for author_name in doc_sent.author_name.as_ref().unwrap_or(&vec!["".to_string()]).iter() {
for author_name in doc_sent
.author_name
.as_ref()
.unwrap_or(&vec!["".to_string()])
.iter()
{
let record: book_author::Model = book_author::Model {
id: 1,
book_id: (created_book.last_insert_id),
@@ -804,7 +941,12 @@ async fn create_book(
.await
.expect("could not create book");
}
for person in doc_sent.person.as_ref().unwrap_or(&vec!["".to_string()]).iter() {
for person in doc_sent
.person
.as_ref()
.unwrap_or(&vec!["".to_string()])
.iter()
{
let record: book_person::Model = book_person::Model {
id: 1,
book_id: (created_book.last_insert_id),
@@ -813,9 +955,13 @@ async fn create_book(
MutationCore::create_book_person(conn, record)
.await
.expect("could not create book");
}
for place in doc_sent.place.as_ref().unwrap_or(&vec!["".to_string()]).iter() {
for place in doc_sent
.place
.as_ref()
.unwrap_or(&vec!["".to_string()])
.iter()
{
let record: book_place::Model = book_place::Model {
id: 1,
book_id: (created_book.last_insert_id),
@@ -824,9 +970,13 @@ async fn create_book(
MutationCore::create_book_place(conn, record)
.await
.expect("could not create book");
}
for subject in doc_sent.subject.as_ref().unwrap_or(&vec!["".to_string()]).iter() {
for subject in doc_sent
.subject
.as_ref()
.unwrap_or(&vec!["".to_string()])
.iter()
{
let record: book_subject::Model = book_subject::Model {
id: 1,
book_id: (created_book.last_insert_id),
@@ -835,9 +985,13 @@ async fn create_book(
MutationCore::create_book_subject(conn, record)
.await
.expect("could not create book");
}
for time in doc_sent.time.as_ref().unwrap_or(&vec!["".to_string()]).iter() {
for time in doc_sent
.time
.as_ref()
.unwrap_or(&vec!["".to_string()])
.iter()
{
let record: book_time::Model = book_time::Model {
id: 1,
book_id: (created_book.last_insert_id),
@@ -846,9 +1000,13 @@ async fn create_book(
MutationCore::create_book_time(conn, record)
.await
.expect("could not create book");
}
for isbn in doc_sent.isbn.as_ref().unwrap_or(&vec!["".to_string()]).iter() {
for isbn in doc_sent
.isbn
.as_ref()
.unwrap_or(&vec!["".to_string()])
.iter()
{
let record: book_isbn::Model = book_isbn::Model {
id: 1,
book_id: (created_book.last_insert_id),
@@ -857,7 +1015,6 @@ async fn create_book(
MutationCore::create_book_isbn(conn, record)
.await
.expect("could not create book");
}
let book_meili = BookMeili {
id: doc_sent.id,
@@ -882,12 +1039,9 @@ async fn create_book(
};
booksman_search::create_or_update_book(book_meili, userid, meili_client).await;
"success"
}
async fn update_book(
Extension(ref conn): Extension<DatabaseConnection>,
Extension(ref meili_client): Extension<Client>,
@@ -905,14 +1059,33 @@ let mut cover = doc_sent.cover.clone();
if !doc_sent.cover.is_none() {
if !doc_sent.cover.clone().unwrap().is_empty() {
if doc_sent.cover.clone().unwrap().contains(&backend_url) {
cover = Some(doc_sent.cover.clone().unwrap().split("/").last().unwrap().to_string());
}
else {
let img_bytes = reqwest::get(cover.unwrap()).await.unwrap().bytes().await.unwrap();
cover = Some(
doc_sent
.cover
.clone()
.unwrap()
.split("/")
.last()
.unwrap()
.to_string(),
);
} else {
let img_bytes = reqwest::get(cover.unwrap())
.await
.unwrap()
.bytes()
.await
.unwrap();
let image = image::load_from_memory(&img_bytes).unwrap();
let temp_cover = doc_sent.cover.clone().unwrap();
let img_id = format!("{}{}",Local::now().format("%Y-%m-%d-%H-%M-%S"),temp_cover.split("/").last().unwrap());
image.save(format!("{}/{}",images_dir,img_id)).expect("Failed to save image");
let img_id = format!(
"{}{}",
Local::now().format("%Y-%m-%d-%H-%M-%S"),
temp_cover.split("/").last().unwrap()
);
image
.save(format!("{}/{}", images_dir, img_id))
.expect("Failed to save image");
cover = Some(img_id.to_string());
}
}
@@ -960,7 +1133,12 @@ let book: book::Model = book::Model{
.await
.expect("could not delete book isbns while updating");
for author_name in doc_sent.author_name.as_ref().unwrap_or(&vec!["".to_string()]).iter() {
for author_name in doc_sent
.author_name
.as_ref()
.unwrap_or(&vec!["".to_string()])
.iter()
{
let record: book_author::Model = book_author::Model {
id: 1,
book_id: doc_sent.id,
@@ -970,7 +1148,12 @@ let book: book::Model = book::Model{
.await
.expect("could not create book");
}
for person in doc_sent.person.as_ref().unwrap_or(&vec!["".to_string()]).iter() {
for person in doc_sent
.person
.as_ref()
.unwrap_or(&vec!["".to_string()])
.iter()
{
let record: book_person::Model = book_person::Model {
id: 1,
book_id: doc_sent.id,
@@ -979,9 +1162,13 @@ let book: book::Model = book::Model{
MutationCore::create_book_person(conn, record)
.await
.expect("could not create book");
}
for place in doc_sent.place.as_ref().unwrap_or(&vec!["".to_string()]).iter() {
for place in doc_sent
.place
.as_ref()
.unwrap_or(&vec!["".to_string()])
.iter()
{
let record: book_place::Model = book_place::Model {
id: 1,
book_id: doc_sent.id,
@@ -990,9 +1177,13 @@ let book: book::Model = book::Model{
MutationCore::create_book_place(conn, record)
.await
.expect("could not create book");
}
for subject in doc_sent.subject.as_ref().unwrap_or(&vec!["".to_string()]).iter() {
for subject in doc_sent
.subject
.as_ref()
.unwrap_or(&vec!["".to_string()])
.iter()
{
let record: book_subject::Model = book_subject::Model {
id: 1,
book_id: doc_sent.id,
@@ -1001,9 +1192,13 @@ let book: book::Model = book::Model{
MutationCore::create_book_subject(conn, record)
.await
.expect("could not create book");
}
for time in doc_sent.time.as_ref().unwrap_or(&vec!["".to_string()]).iter() {
for time in doc_sent
.time
.as_ref()
.unwrap_or(&vec!["".to_string()])
.iter()
{
let record: book_time::Model = book_time::Model {
id: 1,
book_id: doc_sent.id,
@@ -1012,9 +1207,13 @@ let book: book::Model = book::Model{
MutationCore::create_book_time(conn, record)
.await
.expect("could not create book");
}
for isbn in doc_sent.isbn.as_ref().unwrap_or(&vec!["".to_string()]).iter() {
for isbn in doc_sent
.isbn
.as_ref()
.unwrap_or(&vec!["".to_string()])
.iter()
{
let record: book_isbn::Model = book_isbn::Model {
id: 1,
book_id: doc_sent.id,
@@ -1023,7 +1222,6 @@ let book: book::Model = book::Model{
MutationCore::create_book_isbn(conn, record)
.await
.expect("could not create book");
}
let book_meili = BookMeili {

View File

@@ -1,3 +1 @@
pub mod entities;

View File

@@ -10,7 +10,6 @@ impl MigrationTrait for Migration {
// Replace the sample below with your own migration scripts
//todo!();
manager
.create_table(
Table::create()
@@ -23,12 +22,17 @@ impl MigrationTrait for Migration {
.auto_increment()
.primary_key(),
)
.col(ColumnDef::new(User::UserName).string().unique_key().not_null())
.col(
ColumnDef::new(User::UserName)
.string()
.unique_key()
.not_null(),
)
.col(ColumnDef::new(User::PasswordHash).string().not_null())
.to_owned(),
)
.await.expect("Migration failed");
.await
.expect("Migration failed");
manager
.create_table(
@@ -65,7 +69,8 @@ impl MigrationTrait for Migration {
)
.to_owned(),
)
.await.expect("Migration failed");
.await
.expect("Migration failed");
manager
.create_table(
@@ -91,7 +96,8 @@ impl MigrationTrait for Migration {
)
.to_owned(),
)
.await.expect("Migration failed");
.await
.expect("Migration failed");
manager
.create_table(
@@ -117,7 +123,8 @@ impl MigrationTrait for Migration {
)
.to_owned(),
)
.await.expect("Migration failed");
.await
.expect("Migration failed");
manager
.create_table(
@@ -143,7 +150,8 @@ impl MigrationTrait for Migration {
)
.to_owned(),
)
.await.expect("Migration failed");
.await
.expect("Migration failed");
manager
.create_table(
@@ -169,7 +177,8 @@ impl MigrationTrait for Migration {
)
.to_owned(),
)
.await.expect("Migration failed");
.await
.expect("Migration failed");
manager
.create_table(
@@ -195,7 +204,8 @@ impl MigrationTrait for Migration {
)
.to_owned(),
)
.await.expect("Migration failed");
.await
.expect("Migration failed");
manager
.create_table(
@@ -230,22 +240,28 @@ impl MigrationTrait for Migration {
manager
.drop_table(Table::drop().table(Book::Table).to_owned())
.await.expect("Drop failed");
.await
.expect("Drop failed");
manager
.drop_table(Table::drop().table(BookAuthor::Table).to_owned())
.await.expect("Drop failed");
.await
.expect("Drop failed");
manager
.drop_table(Table::drop().table(BookPerson::Table).to_owned())
.await.expect("Drop failed");
.await
.expect("Drop failed");
manager
.drop_table(Table::drop().table(BookPlace::Table).to_owned())
.await.expect("Drop failed");
.await
.expect("Drop failed");
manager
.drop_table(Table::drop().table(BookSubject::Table).to_owned())
.await.expect("Drop failed");
.await
.expect("Drop failed");
manager
.drop_table(Table::drop().table(BookTime::Table).to_owned())
.await.expect("Drop failed");
.await
.expect("Drop failed");
manager
.drop_table(Table::drop().table(BookISBN::Table).to_owned())
.await
@@ -275,7 +291,7 @@ enum Book {
TimeAdded,
Rating,
Comments,
UserId
UserId,
}
#[derive(Iden)]

View File

@@ -1,9 +1,11 @@
//use ::entity::entities::prelude::Book;
//use ::entity::entities::{prelude::*, *};
use ::entity::entities::user::Entity as User;
use ::entity::entities::book::Entity as Book;
use ::entity::entities::user::Entity as User;
//, book_author::Entity as Author, book_person::Entity as Person, book_place::Entity as Place, book_subject::Entity as Subject, book_time::Entity as Time, book_isbn::Entity as ISBN};
use ::entity::entities::{book,book_author,book_person,book_place,book_subject,book_time,book_isbn,user};
use ::entity::entities::{
book, book_author, book_isbn, book_person, book_place, book_subject, book_time, user,
};
use sea_orm::*;
//use ::entity::entities::prelude::Book;
@@ -137,7 +139,6 @@ impl Mutation {
id: i32,
form_data: book::Model,
) -> Result<book::Model, DbErr> {
let book: book::ActiveModel = Book::find_by_id(id)
.one(db)
.await?
@@ -159,7 +160,9 @@ impl Mutation {
rating: Set(form_data.rating.to_owned()),
comments: Set(form_data.comments.to_owned()),
user_id: Set(form_data.user_id.to_owned()),
}.update(db).await
}
.update(db)
.await
}
pub async fn delete_book(db: &DbConn, id: i32) -> Result<DeleteResult, DbErr> {
@@ -172,53 +175,48 @@ impl Mutation {
book.delete(db).await
}
pub async fn delete_book_author(db: &DbConn, id: i32) -> Result<DeleteResult, DbErr> {
book_author::Entity::delete_many().filter(
Condition::any()
.add(book_author::Column::BookId.eq(id))
).exec(db).await
book_author::Entity::delete_many()
.filter(Condition::any().add(book_author::Column::BookId.eq(id)))
.exec(db)
.await
}
pub async fn delete_book_person(db: &DbConn, id: i32) -> Result<DeleteResult, DbErr> {
book_person::Entity::delete_many().filter(
Condition::any()
.add(book_person::Column::BookId.eq(id))
).exec(db).await
book_person::Entity::delete_many()
.filter(Condition::any().add(book_person::Column::BookId.eq(id)))
.exec(db)
.await
}
pub async fn delete_book_place(db: &DbConn, id: i32) -> Result<DeleteResult, DbErr> {
book_place::Entity::delete_many().filter(
Condition::any()
.add(book_place::Column::BookId.eq(id))
).exec(db).await
book_place::Entity::delete_many()
.filter(Condition::any().add(book_place::Column::BookId.eq(id)))
.exec(db)
.await
}
pub async fn delete_book_subject(db: &DbConn, id: i32) -> Result<DeleteResult, DbErr> {
book_subject::Entity::delete_many().filter(
Condition::any()
.add(book_subject::Column::BookId.eq(id))
).exec(db).await
book_subject::Entity::delete_many()
.filter(Condition::any().add(book_subject::Column::BookId.eq(id)))
.exec(db)
.await
}
pub async fn delete_book_time(db: &DbConn, id: i32) -> Result<DeleteResult, DbErr> {
book_time::Entity::delete_many().filter(
Condition::any()
.add(book_time::Column::BookId.eq(id))
).exec(db).await
book_time::Entity::delete_many()
.filter(Condition::any().add(book_time::Column::BookId.eq(id)))
.exec(db)
.await
}
pub async fn delete_book_isbn(db: &DbConn, id: i32) -> Result<DeleteResult, DbErr> {
book_isbn::Entity::delete_many().filter(
Condition::any()
.add(book_isbn::Column::BookId.eq(id))
).exec(db).await
book_isbn::Entity::delete_many()
.filter(Condition::any().add(book_isbn::Column::BookId.eq(id)))
.exec(db)
.await
}
pub async fn delete_all_books(db: &DbConn) -> Result<DeleteResult, DbErr> {
Book::delete_many().exec(db).await
}

View File

@@ -2,7 +2,9 @@ use ::entity::entities::book::Entity as Book;
use ::entity::entities::user::Entity as User;
//, book_author::Entity as Author, book_person::Entity as Person, book_place::Entity as Place, book_subject::Entity as Subject, book_time::Entity as Time, book_isbn::Entity as ISBN};
use ::entity::entities::{book,book_author,book_person,book_place,book_subject,book_time,book_isbn,user};
use ::entity::entities::{
book, book_author, book_isbn, book_person, book_place, book_subject, book_time, user,
};
//use ::entity::entities::{prelude::*, *};
use sea_orm::*;
//use sea_query::Expr;
@@ -26,12 +28,16 @@ impl Query {
Book::find_by_id(id).one(db).await
}
pub async fn find_userid_by_name(db: &DbConn, name: String) -> Result<Option<user::Model>, DbErr> {
User::find().filter(user::Column::UserName.eq(name)).one(db).await
pub async fn find_userid_by_name(
db: &DbConn,
name: String,
) -> Result<Option<user::Model>, DbErr> {
User::find()
.filter(user::Column::UserName.eq(name))
.one(db)
.await
}
pub async fn find_user_by_id(db: &DbConn, id: i32) -> Result<Option<user::Model>, DbErr> {
User::find_by_id(id).one(db).await
}
@@ -62,14 +68,14 @@ impl Query {
.order_by_desc(book::Column::Id)
.paginate(db, posts_per_page);
let num_pages = paginator.num_pages().await?;
return paginator.fetch_page(page - 1).await.map(|p| (p, num_pages))
return paginator.fetch_page(page - 1).await.map(|p| (p, num_pages));
} else {
let paginator = Book::find()
.filter(book::Column::UserId.eq(userid))
.order_by_asc(book::Column::Id)
.paginate(db, posts_per_page);
let num_pages = paginator.num_pages().await?;
return paginator.fetch_page(page - 1).await.map(|p| (p, num_pages))
return paginator.fetch_page(page - 1).await.map(|p| (p, num_pages));
}
// Fetch paginated posts
@@ -83,17 +89,30 @@ pub async fn find_books_plus_meta_in_page(
sort: String,
) -> Result<(Vec<BookAndMetaV2>, u64), DbErr> {
// Setup paginator
let books = Self::find_books_in_page(db,page.try_into().unwrap(),posts_per_page.try_into().unwrap(),userid, sort).await?;
let books = Self::find_books_in_page(
db,
page.try_into().unwrap(),
posts_per_page.try_into().unwrap(),
userid,
sort,
)
.await?;
let book_ids: Vec<i32> = books.0.clone().into_iter().map(|b| b.id).collect();
println!("SIZE IS {} and {:?}", book_ids.len(), book_ids);
let mut resbooks: Vec<BookAndMetaV2> = Vec::with_capacity(book_ids.len());
for book in books.0.iter() {
let bauthors: Vec<book_author::Model> = book.find_related(book_author::Entity).all(db).await?;
let bpersons: Vec<book_person::Model> = book.find_related(book_person::Entity).all(db).await?;
let bplaces: Vec<book_place::Model> = book.find_related(book_place::Entity).all(db).await?;
let bsubjects: Vec<book_subject::Model> = book.find_related(book_subject::Entity).all(db).await?;
let btimes: Vec<book_time::Model> = book.find_related(book_time::Entity).all(db).await?;
let bisbns: Vec<book_isbn::Model> = book.find_related(book_isbn::Entity).all(db).await?;
let bauthors: Vec<book_author::Model> =
book.find_related(book_author::Entity).all(db).await?;
let bpersons: Vec<book_person::Model> =
book.find_related(book_person::Entity).all(db).await?;
let bplaces: Vec<book_place::Model> =
book.find_related(book_place::Entity).all(db).await?;
let bsubjects: Vec<book_subject::Model> =
book.find_related(book_subject::Entity).all(db).await?;
let btimes: Vec<book_time::Model> =
book.find_related(book_time::Entity).all(db).await?;
let bisbns: Vec<book_isbn::Model> =
book.find_related(book_isbn::Entity).all(db).await?;
let bookandmeta = BookAndMetaV2 {
book: (book.clone()),
@@ -102,14 +121,11 @@ pub async fn find_books_plus_meta_in_page(
places: bplaces,
subjects: bsubjects,
times: btimes,
isbns: bisbns
isbns: bisbns,
};
resbooks.push(bookandmeta);
}
Ok((resbooks, books.1))
}
}

View File

@@ -1,9 +1,8 @@
use sea_orm::*;
use crate::Query;
use axum::async_trait;
use axum_login::{secrecy::SecretVec, AuthUser, UserStore};
use crate::Query;
use serde::{Serialize, Deserialize};
use sea_orm::*;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AxumUser {
@@ -59,4 +58,3 @@ Role: PartialOrd + PartialEq + Clone + Send + Sync + 'static,
}
}
}

View File

@@ -1,6 +1,6 @@
use meilisearch_sdk::client::*;
use meilisearch_sdk::search::*;
use serde::{Serialize, Deserialize};
use serde::{Deserialize, Serialize};
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct BookMeili {
@@ -29,31 +29,33 @@ pub async fn create_or_update_book(book: BookMeili, userid: i32, client: &Client
// An index is where the documents are stored.
let books = client.index(format!("books{}", userid));
// Add some movies in the index. If the index 'movies' does not exist, Meilisearch creates it when you first add the documents.
books.add_or_replace(&[
book
], Some("id")).await.unwrap();
books.add_or_replace(&[book], Some("id")).await.unwrap();
}
pub async fn delete_book(bookid: i32, userid: i32, client: &Client) {
// An index is where the documents are stored.
let books = client.index(format!("books{}", userid));
books.delete_document(bookid).await.unwrap();
}
pub async fn search_book(search: &str, page: usize, userid: i32, client: &Client) -> Result<(Vec<BookMeili>, usize), meilisearch_sdk::errors::Error> {
pub async fn search_book(
search: &str,
page: usize,
userid: i32,
client: &Client,
) -> Result<(Vec<BookMeili>, usize), meilisearch_sdk::errors::Error> {
// An index is where the documents are stored.
let books = client.index(format!("books{}", userid));
let results : SearchResults<BookMeili> = books.search().with_query(search).with_offset((page-1)*12)
.execute::<BookMeili>().await.unwrap();
let results: SearchResults<BookMeili> = books
.search()
.with_query(search)
.with_offset((page - 1) * 12)
.execute::<BookMeili>()
.await
.unwrap();
let formatted_results : Vec<BookMeili> = (results.hits).iter().map(|r| r.result.clone()).collect();
let formatted_results: Vec<BookMeili> =
(results.hits).iter().map(|r| r.result.clone()).collect();
//.iter()s.map(|r| r.formatted_result.unwrap()).collect();
return Ok((formatted_results, results.estimated_total_hits));
}

View File

@@ -1,4 +1,3 @@
fn main() {
booksman_api::main();
}

File diff suppressed because it is too large Load Diff