diff --git a/rust-backend/migrations/V3__create_reviews_table.sql b/rust-backend/migrations/V3__create_reviews_table.sql new file mode 100644 index 0000000..6ab31b4 --- /dev/null +++ b/rust-backend/migrations/V3__create_reviews_table.sql @@ -0,0 +1,12 @@ +CREATE TABLE reviews ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id TEXT NOT NULL, + lat REAL NOT NULL, + lng REAL NOT NULL, + place_name TEXT NOT NULL, + place_description TEXT NOT NULL, + title TEXT NOT NULL, + content TEXT NOT NULL, + rating REAL NOT NULL +); + diff --git a/rust-backend/src/main.rs b/rust-backend/src/main.rs index f059c3a..5da3fc6 100644 --- a/rust-backend/src/main.rs +++ b/rust-backend/src/main.rs @@ -4,7 +4,7 @@ mod models; use actix_web::{get, post, delete, Responder, HttpResponse, HttpServer, App, web}; use std::sync::{Mutex, MutexGuard, Arc}; use auth::AuthorizedUser; -use models::Favorite; +use models::{Favorite, Review}; use serde::Deserialize; mod embedded { @@ -110,6 +110,94 @@ async fn delete_favorite(auth: AuthorizedUser, data:web::Data, path: we } } +#[get("/reviews")] +async fn reviews(data: web::Data) -> impl Responder { + let db = data.database.lock().unwrap(); + + match get_reviews(db) { + Some(reviews) => HttpResponse::Ok().insert_header(("Content-Type", "application/json; charset=utf-8")).json(reviews), + None => HttpResponse::InternalServerError().finish(), + } +} + +fn get_reviews(db: MutexGuard<'_, rusqlite::Connection>) -> Option> { + Some( + db.prepare("SELECT * FROM reviews").ok()? + .query_map([], |row| Review::from_row(row)) + .ok()? + .map(|rev| rev.unwrap()) + .collect() + ) +} + +#[derive(Deserialize)] +struct CreateReviewRequest { + lat: f64, + lng: f64, + place_name: String, + place_description: String, + title: String, + content: String, + rating: i64, +} + +#[post("/reviews")] +async fn create_review(auth: AuthorizedUser, data: web::Data, input: web::Json) -> impl Responder { + let db = data.database.lock().unwrap(); + + match db.execute( + "INSERT INTO reviews (user_id, lat, lng, place_name, place_description, title, content, rating) VALUES (:user_id, :lat, :lng, :place_name, :place_description, :title, :content, :rating)", + &[ + (":user_id", &auth.user_id), + (":lat", &input.lat.to_string()), + (":lng", &input.lng.to_string()), + (":place_name", &input.place_name), + (":place_description", &input.place_description), + (":title", &input.title), + (":content", &input.content), + (":rating", &input.rating.to_string()), + ], + ) { + Ok(_) => HttpResponse::Created().json(Review { + id: db.last_insert_rowid(), + user_id: auth.user_id, + lat: input.lat, + lng: input.lng, + place_name: input.place_name.clone(), + place_description: input.place_description.clone(), + title: input.title.clone(), + content: input.content.clone(), + rating: input.rating.clone(), + }), + Err(_) => HttpResponse::InternalServerError().finish(), + } +} + + +#[delete("/reviews/{review}")] +async fn delete_review(auth: AuthorizedUser, data:web::Data, path: web::Path) -> impl Responder { + let db = data.database.lock().unwrap(); + let review_id = path.into_inner(); + let params = &[(":id", &review_id.to_string())]; + + let result = db.query_row("SELECT * FROM reviews WHERE id = :id LIMIT 1", params, |row| Review::from_row(row)); + + if result.is_err() { + return HttpResponse::InternalServerError().finish(); + } + + let review = result.unwrap(); + + if review.user_id != auth.user_id { + return HttpResponse::Forbidden().body("Cannot remove review that you did not create"); + } + + match db.execute("DELETE FROM reviews WHERE id = :id", params) { + Ok(_) => HttpResponse::NoContent().finish(), + Err(_) => HttpResponse::InternalServerError().finish(), + } +} + #[actix_web::main] async fn main() -> std::io::Result<()> { let _ = dotenvy::dotenv(); @@ -142,6 +230,9 @@ async fn main() -> std::io::Result<()> { .service(favorites) .service(create_favorite) .service(delete_favorite) + .service(reviews) + .service(create_review) + .service(delete_review) }) .bind(("0.0.0.0", port))? .run() diff --git a/rust-backend/src/models.rs b/rust-backend/src/models.rs index 4892d0b..c4933ac 100644 --- a/rust-backend/src/models.rs +++ b/rust-backend/src/models.rs @@ -25,3 +25,32 @@ impl Favorite { } } +#[derive(Serialize)] +pub struct Review { + pub id: i64, + pub user_id: String, + pub lat: f64, + pub lng: f64, + pub place_name: String, + pub place_description: String, + pub title: String, + pub content: String, + pub rating: i64, +} + +impl Review { + pub fn from_row(row: &Row) -> Result { + Ok(Review { + id: row.get("id")?, + user_id: row.get("user_id")?, + lat: row.get("lat")?, + lng: row.get("lng")?, + place_name: row.get("place_name")?, + place_description: row.get("place_description")?, + title: row.get("title")?, + content: row.get("content")?, + rating: row.get("rating")?, + }) + } +} +