User Authentication and Other fixes #1
621
backend/Cargo.lock
generated
621
backend/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,7 @@ booksman-orm = { path = "../orm" }
|
||||
booksman-search = { path = "../search" }
|
||||
migration = { path = "../migration" }
|
||||
entity = { path = "../entity" }
|
||||
axum = "^0.5"
|
||||
axum = "^0.6"
|
||||
axum-extra = { version = "^0.3", features = ["spa"] }
|
||||
clap = { version = "^3", features = ["derive"] }
|
||||
dotenvy = "0.15.0"
|
||||
@@ -27,9 +27,15 @@ tracing = "^0.1"
|
||||
tracing-subscriber = "^0.3"
|
||||
itertools = "0.10"
|
||||
chrono = "0.4"
|
||||
axum-login = "0.4"
|
||||
#features = ["sqlite"]
|
||||
|
||||
[dependencies.rand]
|
||||
version = "0.8.5"
|
||||
features = ["min_const_gen"]
|
||||
|
||||
[dependencies.sea-orm]
|
||||
version = "^0.9.2" # sea-orm version
|
||||
version = "^0.10.6" # sea-orm version
|
||||
features = [
|
||||
"debug-print",
|
||||
"runtime-tokio-native-tls",
|
||||
|
||||
@@ -31,6 +31,31 @@ use ::entity::entities::{book,book_author,book_person,book_place,book_subject,bo
|
||||
use std::env;
|
||||
use migration::{Migrator, MigratorTrait};
|
||||
use chrono::Local;
|
||||
use axum_login::{
|
||||
axum_sessions::{async_session::MemoryStore as SessionMemoryStore, SessionLayer},
|
||||
memory_store::MemoryStore as AuthMemoryStore,
|
||||
secrecy::SecretVec,
|
||||
AuthLayer, AuthUser, RequireAuthorizationLayer,
|
||||
};
|
||||
use rand::Rng;
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
struct User {
|
||||
id: i64,
|
||||
password_hash: String,
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl AuthUser for User {
|
||||
fn get_id(&self) -> String {
|
||||
format!("{}", self.id)
|
||||
}
|
||||
|
||||
fn get_password_hash(&self) -> SecretVec<u8> {
|
||||
SecretVec::new(self.password_hash.clone().into())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
struct BookUI {
|
||||
@@ -206,6 +231,8 @@ struct Opt {
|
||||
static_dir: String,
|
||||
}
|
||||
|
||||
type AuthContext = axum_login::extractors::AuthContext<User, AuthMemoryStore<User>>;
|
||||
|
||||
#[tokio::main]
|
||||
pub async fn main() {
|
||||
let opt = Opt::parse();
|
||||
@@ -225,6 +252,10 @@ pub async fn main() {
|
||||
let meili_url = env::var("MEILI_URL").expect("MEILI_URL is not set in .env file");
|
||||
let meili_key = env::var("MEILI_KEY").expect("MEILI_KEY is not set in .env file");
|
||||
|
||||
let secret = rand::thread_rng().gen::<[u8; 64]>();
|
||||
|
||||
let session_store = MemoryStore::new();
|
||||
let session_layer = SessionLayer::new(session_store, &secret).with_secure(false);
|
||||
|
||||
let conn = Database::connect(db_url)
|
||||
.await
|
||||
@@ -232,8 +263,37 @@ pub async fn main() {
|
||||
// Apply all pending migrations
|
||||
Migrator::up(&conn, None).await.unwrap();
|
||||
|
||||
|
||||
|
||||
let store = Arc::new(RwLock::new(HashMap::default()));
|
||||
let users : Vec<User> = get_users_seaorm(conn);
|
||||
//let user = User::get_rusty_user();
|
||||
for user in users.iter() {
|
||||
store.write().await.insert(user.get_id(), user);
|
||||
}
|
||||
|
||||
let user_store = AuthMemoryStore::new(&store);
|
||||
let auth_layer = AuthLayer::new(user_store, &secret);
|
||||
|
||||
let meili_client = Client::new(meili_url, meili_key);
|
||||
|
||||
|
||||
async fn login_handler(mut auth: AuthContext, Json(user_sent): Json<User>) {
|
||||
auth.login(&user_sent).await.unwrap();
|
||||
}
|
||||
|
||||
async fn register_handler(mut auth: AuthContext, Json(user_sent): Json<User>) {
|
||||
// add to db
|
||||
store.write().await.insert(user_sent.get_id(), user_sent);
|
||||
auth.login(&user_sent).await.unwrap();
|
||||
}
|
||||
|
||||
async fn logout_handler(mut auth: AuthContext) {
|
||||
dbg!("Logging out user: {}", &auth.current_user);
|
||||
auth.logout().await;
|
||||
}
|
||||
|
||||
|
||||
let app = Router::new()
|
||||
.route("/api/search_openlibrary", get(search_openlibrary))
|
||||
.route("/api/create_by_isbn", get(create_by_isbn))
|
||||
@@ -242,8 +302,13 @@ pub async fn main() {
|
||||
.route("/api/list_search", get(list_search_book))
|
||||
.route("/api/create", post(create_book))
|
||||
.route("/api/update", post(update_book))
|
||||
.route("/api/login", post(login_handler))
|
||||
.route("/api/register", post(register_handler))
|
||||
.route("/api/logout", post(logout_handler))
|
||||
.nest("/images", get_service(ServeDir::new(images_dir)).handle_error(handle_error))
|
||||
.merge(SpaRouter::new("/assets", opt.static_dir))
|
||||
.layer(auth_layer)
|
||||
.layer(session_layer)
|
||||
.layer(ServiceBuilder::new().layer(TraceLayer::new_for_http()))
|
||||
.layer(Extension(conn))
|
||||
.layer(Extension(meili_client))
|
||||
|
||||
@@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] }
|
||||
chrono = "0.4"
|
||||
|
||||
[dependencies.sea-orm]
|
||||
version = "^0.9.2" # sea-orm version
|
||||
version = "^0.10.6" # sea-orm version
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.10.6
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||
#[sea_orm(table_name = "book")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
@@ -19,12 +19,15 @@ pub struct Model {
|
||||
pub time_added: Option<String>,
|
||||
pub rating: Option<i32>,
|
||||
pub comments: Option<String>,
|
||||
pub user_id: i32,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(has_many = "super::book_author::Entity")]
|
||||
BookAuthor,
|
||||
#[sea_orm(has_many = "super::book_isbn::Entity")]
|
||||
BookIsbn,
|
||||
#[sea_orm(has_many = "super::book_person::Entity")]
|
||||
BookPerson,
|
||||
#[sea_orm(has_many = "super::book_place::Entity")]
|
||||
@@ -33,8 +36,14 @@ pub enum Relation {
|
||||
BookSubject,
|
||||
#[sea_orm(has_many = "super::book_time::Entity")]
|
||||
BookTime,
|
||||
#[sea_orm(has_many = "super::book_isbn::Entity")]
|
||||
BookIsbn,
|
||||
#[sea_orm(
|
||||
belongs_to = "super::user::Entity",
|
||||
from = "Column::UserId",
|
||||
to = "super::user::Column::Id",
|
||||
on_update = "Cascade",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
User,
|
||||
}
|
||||
|
||||
impl Related<super::book_author::Entity> for Entity {
|
||||
@@ -43,6 +52,12 @@ impl Related<super::book_author::Entity> for Entity {
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::book_isbn::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::BookIsbn.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::book_person::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::BookPerson.def()
|
||||
@@ -67,9 +82,9 @@ impl Related<super::book_time::Entity> for Entity {
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::book_isbn::Entity> for Entity {
|
||||
impl Related<super::user::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::BookIsbn.def()
|
||||
Relation::User.def()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.10.6
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||
#[sea_orm(table_name = "book_author")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.10.6
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||
#[sea_orm(table_name = "book_isbn")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.10.6
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||
#[sea_orm(table_name = "book_person")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.10.6
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||
#[sea_orm(table_name = "book_place")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.10.6
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||
#[sea_orm(table_name = "book_subject")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.10.6
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||
#[sea_orm(table_name = "book_time")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.10.6
|
||||
|
||||
pub mod prelude;
|
||||
|
||||
@@ -9,3 +9,4 @@ pub mod book_person;
|
||||
pub mod book_place;
|
||||
pub mod book_subject;
|
||||
pub mod book_time;
|
||||
pub mod user;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.10.6
|
||||
|
||||
pub use super::book::Entity as Book;
|
||||
pub use super::book_author::Entity as BookAuthor;
|
||||
@@ -7,3 +7,4 @@ pub use super::book_person::Entity as BookPerson;
|
||||
pub use super::book_place::Entity as BookPlace;
|
||||
pub use super::book_subject::Entity as BookSubject;
|
||||
pub use super::book_time::Entity as BookTime;
|
||||
pub use super::user::Entity as User;
|
||||
|
||||
26
backend/entity/src/entities/user.rs
Normal file
26
backend/entity/src/entities/user.rs
Normal file
@@ -0,0 +1,26 @@
|
||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.10.6
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||
#[sea_orm(table_name = "user")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
pub user_name: Option<String>,
|
||||
pub password_hash: String,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(has_many = "super::book::Entity")]
|
||||
Book,
|
||||
}
|
||||
|
||||
impl Related<super::book::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Book.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
@@ -13,7 +13,7 @@ async-std = { version = "^1", features = ["attributes", "tokio1"] }
|
||||
chrono = "0.4"
|
||||
|
||||
[dependencies.sea-orm-migration]
|
||||
version = "^0.9.2"
|
||||
version = "^0.10.6"
|
||||
features = [
|
||||
# Enable at least one `ASYNC_RUNTIME` and `DATABASE_DRIVER` feature if you want to run migration via CLI.
|
||||
# View the list of supported features at https://www.sea-ql.org/SeaORM/docs/install-and-config/database-and-async-runtime.
|
||||
|
||||
@@ -10,6 +10,26 @@ impl MigrationTrait for Migration {
|
||||
// Replace the sample below with your own migration scripts
|
||||
//todo!();
|
||||
|
||||
|
||||
manager
|
||||
.create_table(
|
||||
Table::create()
|
||||
.table(User::Table)
|
||||
.if_not_exists()
|
||||
.col(
|
||||
ColumnDef::new(User::Id)
|
||||
.integer()
|
||||
.not_null()
|
||||
.auto_increment()
|
||||
.primary_key(),
|
||||
)
|
||||
.col(ColumnDef::new(User::UserName).string().unique_key().not_null())
|
||||
.col(ColumnDef::new(User::PasswordHash).string().not_null())
|
||||
.to_owned(),
|
||||
)
|
||||
.await.expect("Migration failed");
|
||||
|
||||
|
||||
manager
|
||||
.create_table(
|
||||
Table::create()
|
||||
@@ -34,6 +54,15 @@ impl MigrationTrait for Migration {
|
||||
.col(ColumnDef::new(Book::TimeAdded).time())
|
||||
.col(ColumnDef::new(Book::Rating).integer())
|
||||
.col(ColumnDef::new(Book::Comments).string())
|
||||
.col(ColumnDef::new(Book::UserId).integer().not_null())
|
||||
.foreign_key(
|
||||
ForeignKey::create()
|
||||
.name("FK_2e303c3a712662f1fc2a4d0aavc")
|
||||
.from(Book::Table, Book::UserId)
|
||||
.to(User::Table, User::Id)
|
||||
.on_delete(ForeignKeyAction::Cascade)
|
||||
.on_update(ForeignKeyAction::Cascade),
|
||||
)
|
||||
.to_owned(),
|
||||
)
|
||||
.await.expect("Migration failed");
|
||||
@@ -246,6 +275,7 @@ enum Book {
|
||||
TimeAdded,
|
||||
Rating,
|
||||
Comments,
|
||||
UserId
|
||||
}
|
||||
|
||||
#[derive(Iden)]
|
||||
@@ -295,3 +325,11 @@ enum BookISBN {
|
||||
ISBN,
|
||||
BookId,
|
||||
}
|
||||
|
||||
#[derive(Iden)]
|
||||
enum User {
|
||||
Table,
|
||||
Id,
|
||||
UserName,
|
||||
PasswordHash,
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ entity = { path = "../entity" }
|
||||
chrono = "0.4"
|
||||
|
||||
[dependencies.sea-orm]
|
||||
version = "^0.9.2" # sea-orm version
|
||||
version = "^0.10.6" # sea-orm version
|
||||
features = [
|
||||
"debug-print",
|
||||
"runtime-tokio-native-tls",
|
||||
|
||||
@@ -1,14 +1,37 @@
|
||||
//use ::entity::entities::prelude::Book;
|
||||
//use ::entity::entities::{prelude::*, *};
|
||||
use ::entity::entities::{book::Entity as Book};
|
||||
use ::entity::entities::user::Entity as User;
|
||||
use ::entity::entities::book::Entity as Book;
|
||||
//, 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};
|
||||
use ::entity::entities::{book,book_author,book_person,book_place,book_subject,book_time,book_isbn,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,
|
||||
@@ -26,6 +49,7 @@ impl Mutation {
|
||||
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
|
||||
@@ -134,6 +158,7 @@ impl Mutation {
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -59,11 +59,13 @@ impl Query {
|
||||
/// If ok, returns (post models, num pages).
|
||||
pub async fn find_books_in_page(
|
||||
db: &DbConn,
|
||||
page: usize,
|
||||
posts_per_page: usize,
|
||||
) -> Result<(Vec<book::Model>, usize), DbErr> {
|
||||
page: u64,
|
||||
posts_per_page: u64,
|
||||
userid: i32,
|
||||
) -> Result<(Vec<book::Model>, u64), DbErr> {
|
||||
// Setup paginator
|
||||
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?;
|
||||
@@ -76,9 +78,10 @@ pub async fn find_books_plus_meta_in_page(
|
||||
db: &DbConn,
|
||||
page: usize,
|
||||
posts_per_page: usize,
|
||||
) -> Result<(Vec<BookAndMetaV2>, usize), DbErr> {
|
||||
userid: i32,
|
||||
) -> Result<(Vec<BookAndMetaV2>, u64), DbErr> {
|
||||
// Setup paginator
|
||||
let books = Self::find_books_in_page(db,page,posts_per_page).await?;
|
||||
let books = Self::find_books_in_page(db,page.try_into().unwrap(),posts_per_page.try_into().unwrap(),userid).await?;
|
||||
let book_ids: Vec<i32> = books.0.clone().into_iter().map(|b| b.id).collect();
|
||||
let mut resbooks: Vec<BookAndMetaV2> = Vec::with_capacity(book_ids.len());
|
||||
for book in books.0.iter() {
|
||||
|
||||
@@ -25,9 +25,9 @@ pub struct BookMeili {
|
||||
pub isbn: Vec<String>,
|
||||
}
|
||||
|
||||
pub async fn create_or_update_book(book: BookMeili, client: &Client) {
|
||||
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("books");
|
||||
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
|
||||
@@ -35,16 +35,16 @@ pub async fn create_or_update_book(book: BookMeili, client: &Client) {
|
||||
}
|
||||
|
||||
|
||||
pub async fn delete_book(bookid: i32, client: &Client) {
|
||||
pub async fn delete_book(bookid: i32, userid: i32, client: &Client) {
|
||||
// An index is where the documents are stored.
|
||||
let books = client.index("books");
|
||||
let books = client.index(format!("books{}",userid));
|
||||
books.delete_document(bookid).await.unwrap();
|
||||
}
|
||||
|
||||
|
||||
pub async fn search_book(search: &str, page: usize, 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("books");
|
||||
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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user