Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support category #17

Merged
merged 10 commits into from
Nov 30, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ pub mod utils {
pub mod organization;
pub mod problem;
pub mod session;
pub mod category;
}

pub mod routes {
pub mod account;
pub mod category;
pub mod index;
pub mod problem;
pub mod organization;
pub mod problem;
}

pub mod cors;
Expand Down
20 changes: 20 additions & 0 deletions src/models/category.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use crate::models::UserRecordId;
use serde::{Deserialize, Serialize};
use surrealdb::sql::Thing;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Category {
pub id: Option<Thing>,
pub owner: Thing,
pub name: String,

pub created_at: chrono::NaiveDateTime,
pub updated_at: chrono::NaiveDateTime,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(crate = "rocket::serde")]
pub struct CreateCategory {
pub name: String,
pub owner: UserRecordId,
}
1 change: 1 addition & 0 deletions src/models/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ pub mod organization;
pub mod problem;
pub mod response;
pub mod shared;
pub mod category;

pub use shared::*;
125 changes: 125 additions & 0 deletions src/routes/category.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
use crate::{
models::{
category::{Category, CreateCategory},
error::Error,
response::{Empty, Response},
UserRecordId,
},
utils::{category, session},
Result,
};
use rocket::{post, serde::json::Json, tokio::fs::remove_dir_all, State};
use serde::{Deserialize, Serialize};
use std::path::Path;
use surrealdb::{engine::remote::ws::Client, Surreal};

#[derive(Serialize, Deserialize)]
#[serde(crate = "rocket::serde")]
pub struct CategoryData<'r> {
pub id: &'r str,
pub token: &'r str,

pub cat: CreateCategory,
}

#[derive(Serialize, Deserialize)]
pub struct CreateCatResponse {
pub id: String,
}

#[post("/create", data = "<category>")]
pub async fn create(
db: &State<Surreal<Client>>,
category: Json<CategoryData<'_>>,
) -> Result<CreateCatResponse> {
if !session::verify(db, category.id, category.token).await {
return Err(Error::Unauthorized(Json(
"Failed to grant permission".into(),
)));
}

let cat = category::create(db, category.into_inner().cat)
.await
.map_err(|e| Error::ServerError(Json(e.to_string().into())))?
.ok_or(Error::ServerError(Json("Failed to create category".into())))?;

Ok(Json(Response {
success: true,
message: "Category created successfully".into(),
data: Some(CreateCatResponse {
id: cat.id.unwrap().id.to_string(),
}),
}))
}

#[post("/delete/<id>", data = "<category>")]
pub async fn delete(
db: &State<Surreal<Client>>,
id: &str,
category: Json<CategoryData<'_>>,
) -> Result<Empty> {
if !session::verify(db, category.id, category.token).await {
return Err(Error::Unauthorized(Json(
"Failed to grant permission".into(),
)));
}

category::delete(db, id)
.await
.map_err(|e| Error::ServerError(Json(e.to_string().into())))?;

remove_dir_all(Path::new("content/").join(id))
.await
.map_err(|e| Error::ServerError(Json(e.to_string().into())))?;

Ok(Response {
success: true,
message: "Category deleted successfully".into(),
data: None,
}
.into())
}

#[post("/get_by_owner", data = "<data>")]
pub async fn get_by_owner(
db: &State<Surreal<Client>>,
data: Json<UserRecordId>,
) -> Result<Vec<Category>> {
let result = category::get_by_owner(db, data.into_inner().into())
.await
.map_err(|e| Error::ServerError(Json(e.to_string().into())))?;

if result.is_empty() {
return Err(Error::NotFound(Json("Category not found".into())));
}

Ok(Json(Response {
success: true,
message: "Category found successfully".to_string(),
data: Some(result),
}))
}

#[derive(Serialize, Deserialize)]
#[serde(crate = "rocket::serde")]
pub struct GetByName {
pub name: String,
}
#[post("/get_by_name", data = "<data>")]
pub async fn get_by_name(db: &State<Surreal<Client>>, data: Json<GetByName>) -> Result<Category> {
let result = category::get_by_name(db, data.name.clone())
.await
.map_err(|e| Error::ServerError(Json(e.to_string().into())))?
.ok_or(Error::NotFound(Json("Category not found".into())))?;

Ok(Json(Response {
success: true,
message: "Category found successfully".to_string(),
data: Some(result),
}))
}

pub fn routes() -> Vec<rocket::Route> {
use rocket::routes;
routes![create, delete, get_by_name, get_by_owner]
}
2 changes: 2 additions & 0 deletions src/routes/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::{cors::CORS, routes::account};
use anyhow::Result;
use rocket::fs::NamedFile;
use surrealdb::{engine::remote::ws::Ws, opt::auth::Root, Surreal};
use super::category;
use super::problem;
use super::organization;
#[get("/")]
Expand Down Expand Up @@ -37,6 +38,7 @@ pub async fn rocket() -> rocket::Rocket<rocket::Build> {
.mount("/account", account::routes())
.mount("/problem", problem::routes())
.mount("/org", organization::routes())
.mount("/category", category::routes())
.manage(db)

}
37 changes: 37 additions & 0 deletions src/utils/category.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use anyhow::{Ok, Result};
use surrealdb::{engine::remote::ws::Client, sql::Thing, Surreal};

use crate::models::category::{Category, CreateCategory};

pub async fn create(db: &Surreal<Client>, cat: CreateCategory) -> Result<Option<Category>> {
Ok(db
.create("category")
.content(Category {
id: None,
name: cat.name,
owner: cat.owner.into(),
created_at: chrono::Local::now().naive_local(),
updated_at: chrono::Local::now().naive_local(),
})
.await?)
}

pub async fn delete(db: &Surreal<Client>, id: &str) -> Result<Option<Category>> {
Ok(db.delete(("category", id)).await?)
}

pub async fn get_by_owner(db: &Surreal<Client>, owner: Thing) -> Result<Vec<Category>> {
Ok(db
.query("SELECT * FROM category WHERE owner = $owner")
.bind(("owner", owner))
.await?
.take(0)?)
}

pub async fn get_by_name(db: &Surreal<Client>, name: String) -> Result<Option<Category>> {
Ok(db
.query("SELECT * FROM category WHERE name = $name")
.bind(("name", name))
.await?
.take(0)?)
}