Initial commit squashed

This commit is contained in:
2023-05-28 15:17:13 +05:30
commit fe253b8646
76 changed files with 30659 additions and 0 deletions

21
backend/orm/Cargo.toml Normal file
View File

@@ -0,0 +1,21 @@
[package]
name = "booksman-orm"
version = "0.1.0"
edition = "2021"
publish = false
[dependencies]
serde = { version = "1", features = ["derive"] }
entity = { path = "../entity" }
chrono = "0.4"
axum-login = "0.4"
axum = "^0.6"
eyre = "0.6.8"
[dependencies.sea-orm]
version = "^0.10.6" # sea-orm version
features = [
"debug-print",
"runtime-tokio-native-tls",
"sqlx-sqlite",
]

9
backend/orm/src/lib.rs Normal file
View File

@@ -0,0 +1,9 @@
mod mutation;
mod query;
mod userstore;
pub use mutation::*;
pub use query::*;
pub use userstore::*;
pub use sea_orm;

223
backend/orm/src/mutation.rs Normal file
View File

@@ -0,0 +1,223 @@
//use ::entity::entities::prelude::Book;
//use ::entity::entities::{prelude::*, *};
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_isbn, book_person, book_place, book_subject, book_time, user,
};
use sea_orm::*;
//use ::entity::entities::prelude::Book;
pub struct Mutation;
impl Mutation {
pub async fn create_user(
db: &DbConn,
form_data: user::Model,
) -> Result<InsertResult<user::ActiveModel>, DbErr> {
let record = user::ActiveModel {
user_name: Set(form_data.user_name.to_owned()),
password_hash: Set(form_data.password_hash.to_owned()),
..Default::default()
};
User::insert(record).exec(db).await
}
pub async fn delete_user(db: &DbConn, id: i32) -> Result<DeleteResult, DbErr> {
let user: user::ActiveModel = User::find_by_id(id)
.one(db)
.await?
.ok_or(DbErr::Custom("Cannot find user.".to_owned()))
.map(Into::into)?;
user.delete(db).await
}
pub async fn create_book(
db: &DbConn,
form_data: book::Model,
) -> Result<InsertResult<book::ActiveModel>, DbErr> {
let record = book::ActiveModel {
open_library_key: Set(form_data.open_library_key.to_owned()),
title: Set(form_data.title.to_owned()),
edition_count: Set(form_data.edition_count.to_owned()),
first_publish_year: Set(form_data.first_publish_year.to_owned()),
median_page_count: Set(form_data.median_page_count.to_owned()),
goodread_id: Set(form_data.goodread_id.to_owned()),
description: Set(form_data.description.to_owned()),
cover: Set(form_data.cover.to_owned()),
location: Set(form_data.location.to_owned()),
time_added: Set(form_data.time_added.to_owned()),
rating: Set(form_data.rating.to_owned()),
comments: Set(form_data.comments.to_owned()),
user_id: Set(form_data.user_id.to_owned()),
..Default::default()
};
Book::insert(record).exec(db).await
}
pub async fn create_book_author(
db: &DbConn,
form_data: book_author::Model,
) -> Result<book_author::ActiveModel, DbErr> {
book_author::ActiveModel {
book_id: Set(form_data.book_id.to_owned()),
author_name: Set(form_data.author_name.to_owned()),
..Default::default()
}
.save(db)
.await
}
pub async fn create_book_person(
db: &DbConn,
form_data: book_person::Model,
) -> Result<book_person::ActiveModel, DbErr> {
book_person::ActiveModel {
book_id: Set(form_data.book_id.to_owned()),
person: Set(form_data.person.to_owned()),
..Default::default()
}
.save(db)
.await
}
pub async fn create_book_place(
db: &DbConn,
form_data: book_place::Model,
) -> Result<book_place::ActiveModel, DbErr> {
book_place::ActiveModel {
book_id: Set(form_data.book_id.to_owned()),
place: Set(form_data.place.to_owned()),
..Default::default()
}
.save(db)
.await
}
pub async fn create_book_subject(
db: &DbConn,
form_data: book_subject::Model,
) -> Result<book_subject::ActiveModel, DbErr> {
book_subject::ActiveModel {
book_id: Set(form_data.book_id.to_owned()),
subject: Set(form_data.subject.to_owned()),
..Default::default()
}
.save(db)
.await
}
pub async fn create_book_time(
db: &DbConn,
form_data: book_time::Model,
) -> Result<book_time::ActiveModel, DbErr> {
book_time::ActiveModel {
book_id: Set(form_data.book_id.to_owned()),
time: Set(form_data.time.to_owned()),
..Default::default()
}
.save(db)
.await
}
pub async fn create_book_isbn(
db: &DbConn,
form_data: book_isbn::Model,
) -> Result<book_isbn::ActiveModel, DbErr> {
book_isbn::ActiveModel {
book_id: Set(form_data.book_id.to_owned()),
isbn: Set(form_data.isbn.to_owned()),
..Default::default()
}
.save(db)
.await
}
pub async fn update_book_by_id(
db: &DbConn,
id: i32,
form_data: book::Model,
) -> Result<book::Model, DbErr> {
let book: book::ActiveModel = Book::find_by_id(id)
.one(db)
.await?
.ok_or(DbErr::Custom("Cannot find book.".to_owned()))
.map(Into::into)?;
book::ActiveModel {
id: book.id,
open_library_key: Set(form_data.open_library_key.to_owned()),
title: Set(form_data.title.to_owned()),
edition_count: Set(form_data.edition_count.to_owned()),
first_publish_year: Set(form_data.first_publish_year.to_owned()),
median_page_count: Set(form_data.median_page_count.to_owned()),
goodread_id: Set(form_data.goodread_id.to_owned()),
description: Set(form_data.description.to_owned()),
cover: Set(form_data.cover.to_owned()),
location: Set(form_data.location.to_owned()),
time_added: Set(form_data.time_added.to_owned()),
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
}
pub async fn delete_book(db: &DbConn, id: i32) -> Result<DeleteResult, DbErr> {
let book: book::ActiveModel = Book::find_by_id(id)
.one(db)
.await?
.ok_or(DbErr::Custom("Cannot find book.".to_owned()))
.map(Into::into)?;
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
}
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
}
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
}
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
}
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
}
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
}
pub async fn delete_all_books(db: &DbConn) -> Result<DeleteResult, DbErr> {
Book::delete_many().exec(db).await
}
}

131
backend/orm/src/query.rs Normal file
View File

@@ -0,0 +1,131 @@
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_isbn, book_person, book_place, book_subject, book_time, user,
};
//use ::entity::entities::{prelude::*, *};
use sea_orm::*;
//use sea_query::Expr;
#[derive(Clone)]
pub struct BookAndMetaV2 {
pub book: book::Model,
pub authors: Vec<book_author::Model>,
pub persons: Vec<book_person::Model>,
pub places: Vec<book_place::Model>,
pub subjects: Vec<book_subject::Model>,
pub times: Vec<book_time::Model>,
pub isbns: Vec<book_isbn::Model>,
}
//use ::entity::entities::prelude::Book;
pub struct Query;
impl Query {
pub async fn find_book_by_id(db: &DbConn, id: i32) -> Result<Option<book::Model>, DbErr> {
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_user_by_id(db: &DbConn, id: i32) -> Result<Option<user::Model>, DbErr> {
User::find_by_id(id).one(db).await
}
pub async fn list_all_users(db: &DbConn) -> Result<Vec<user::Model>, DbErr> {
User::find().all(db).await
}
/* pub async fn find_bookplusmeta_by_id(db: &DbConn, id: i32) -> Result<Option<(book::Model, Vec<book_author::Model>, Vec<book_person::Model>)>, DbErr> {
let book = Book::find_by_id(id).one(db).await?.unwrap();
let authors = book.find_related(Author).all(db).await?;
let persons = book.find_related(Person).all(db).await?;
Ok(Some((book, authors, persons)))
}
*/
/// If ok, returns (post models, num pages).
pub async fn find_books_in_page(
db: &DbConn,
page: u64,
posts_per_page: u64,
userid: i32,
sort: String,
) -> Result<(Vec<book::Model>, u64), DbErr> {
// Setup paginator
if sort == "desc".to_string() {
let paginator = Book::find()
.filter(book::Column::UserId.eq(userid))
.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));
} 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));
}
// Fetch paginated posts
}
pub async fn find_books_plus_meta_in_page(
db: &DbConn,
page: usize,
posts_per_page: usize,
userid: i32,
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 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 bookandmeta = BookAndMetaV2 {
book: (book.clone()),
authors: bauthors,
persons: bpersons,
places: bplaces,
subjects: bsubjects,
times: btimes,
isbns: bisbns,
};
resbooks.push(bookandmeta);
}
Ok((resbooks, books.1))
}
}

View File

@@ -0,0 +1,60 @@
use crate::Query;
use axum::async_trait;
use axum_login::{secrecy::SecretVec, AuthUser, UserStore};
use sea_orm::*;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AxumUser {
pub id: i32,
pub password_hash: String,
pub name: String,
}
type Result<T = ()> = std::result::Result<T, eyre::Error>;
impl<Role> AuthUser<Role> for AxumUser
where
Role: PartialOrd + PartialEq + Clone + Send + Sync + 'static,
{
fn get_id(&self) -> String {
format!("{}", self.id)
}
fn get_password_hash(&self) -> axum_login::secrecy::SecretVec<u8> {
SecretVec::new(self.password_hash.clone().into())
}
}
#[derive(Debug, Clone)]
pub struct AxumUserStore {
conn: DatabaseConnection,
}
impl AxumUserStore {
pub fn new(conn: &DatabaseConnection) -> Self {
Self { conn: conn.clone() }
}
}
#[async_trait]
impl<Role> UserStore<Role> for AxumUserStore
where
Role: PartialOrd + PartialEq + Clone + Send + Sync + 'static,
{
type User = AxumUser;
async fn load_user(&self, user_id: &str) -> Result<Option<Self::User>> {
// my user id is a Vec<u8>, so it's stored base64 encoded
let id: i32 = user_id.parse().unwrap();
let user = Query::find_user_by_id(&self.conn, id).await?;
match user {
Some(u) => Ok(Some(AxumUser {
id: u.id,
password_hash: u.password_hash,
name: u.user_name.unwrap(),
})),
None => Ok(None),
}
}
}