diff --git a/Cargo.toml b/Cargo.toml index b43e251..0a45caf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,3 +24,5 @@ bincode = "1.3.3" serde = { version = "1.0", features = ["derive"] } serde_derive = "1.0.132" rss = "2.0.0" +rocket = { version = "0.5.0-rc.1", features = ["json", "secrets"] } +captcha = "0.0.8" diff --git a/src/captcha.rs b/src/captcha.rs new file mode 100644 index 0000000..b5a1f63 --- /dev/null +++ b/src/captcha.rs @@ -0,0 +1,62 @@ +// Generate & verify CAPTCHA's +extern crate captcha; + +use std::fs::File; + +use captcha::filters::{Grid, Noise, Wave}; +use captcha::Captcha; +use std::path::Path; + +use crate::structures::{CaptchaAnswer}; +use rocket::{form::Form, http::Cookie, http::CookieJar}; + +// Create a new captcha image +fn create_captcha() -> Captcha { + let mut captcha = Captcha::new(); // create empty captcha + + captcha + .add_chars(7) + .apply_filter(Noise::new(0.2)) + .apply_filter(Grid::new(41, 67)) + .add_text_area() + .apply_filter(Wave::new(1.6373, 5.1363)) + .view(220, 120); // create image + + let captcha_text = captcha.chars_as_string(); + + captcha + .save(Path::new( + &("/tmp/captcha".to_owned() + &captcha_text + ".png"), + )) + .expect("save failed"); // save captcha + + return captcha; +} + +// API to get a new captcha +#[get("/captcha")] +pub fn return_captcha(cookies: &CookieJar<'_>) -> File { + let captcha = create_captcha(); // Create a new captcha + let captcha_text = captcha.chars_as_string(); // Store the captcha answer text + + let file = File::open(&("/tmp/captcha".to_owned() + &captcha_text + ".png")); // Open the captcha image file + + info!("created new captcha {}", captcha_text); // Print the captcha text + cookies.add_private(Cookie::new( // Set the token as a private cookie + "token", // Add a cookie to store the captcha answer + captcha_text + )); + + return file.unwrap(); // Return the captcha image +} + +// Check if the provided captcha answer is correct +#[post("/captcha", data = "")] +pub fn check_captcha(answer: Form, cookies: &CookieJar<'_>) -> String { + let user_token = cookies.get_private("token").unwrap(); + if user_token.value() == answer.text { + return "correct".to_string(); + } else { + return "incorrect".to_string(); + } +} diff --git a/src/main.rs b/src/main.rs index fabac6c..096978d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,17 @@ use uuid::Uuid; mod structures; mod io; mod tests; +mod networking; +mod captcha; + +#[macro_use] +extern crate rocket; +use rocket::fairing::{Fairing, Info, Kind}; +use rocket::{http::Header, Request, Response}; +use rocket::fs::FileServer; + +use crate::networking::*; +use crate::captcha::*; fn construct_main_page() { // Create the footer @@ -67,16 +78,24 @@ fn rebuild() { io::write_feed("en"); } -fn main() { - /* - let full_recipe = Recipe::new( - "Full Recipe".to_string(), - (3, Minutes), - (4, Hours), - 7, - vec![(3, Cups, "flour".to_string()), (4, Tablespoons, "sugar".to_string()), (1, Cups, "water".to_string())], - vec!["Mix flour and sugar in a bowl".to_string(), "Slowly add water".to_string(), "Bake in the oven for 4 hours".to_string()], - "Erin Nova".to_string(), - Some(vec![Tag('#', "meow".to_string()), Tag('#', "gay".to_string())]));*/ +#[launch] +fn rocket() -> _ { rebuild(); + + // Launch rocket + let _config = sled::Config::default() + .use_compression(true) + .compression_factor(15); + + rocket::build() + .mount( + "/api", + routes![ + test, + return_captcha, + check_captcha, + new_recipe + ], + ) + .mount("/", FileServer::from("static/en/")) } diff --git a/src/networking.rs b/src/networking.rs new file mode 100644 index 0000000..3baabc9 --- /dev/null +++ b/src/networking.rs @@ -0,0 +1,12 @@ +use rocket::form::Form; +use crate::structures::RecipeForm; + +#[get("/test")] +pub fn test() -> String { + return "Hello! :3".to_string(); +} + +#[post("/new-recipe", data = "")] +pub fn new_recipe(recipe: Form) -> String { + return recipe.name.clone(); +} diff --git a/src/structures.rs b/src/structures.rs index e03f7b0..43671b3 100644 --- a/src/structures.rs +++ b/src/structures.rs @@ -3,11 +3,12 @@ use chrono::prelude::*; use serde_derive::{Deserialize, Serialize}; use build_html::{self, Html, HtmlContainer, ContainerType, Container, HtmlPage}; use std::fmt; +use rocket::form::Form; use crate::io::write_html; // Content strings to add to html -pub const TITLE: &str = r#"Catgirl Cooking"#; +pub const TITLE: &str = r#"🥘 Catgirl Cooking"#; pub const HEADER: &str = r#"🥘 Catgirl Cooking"#; pub const DESCRIPTION: &str = r#"The cutest cooking site on the net :3"#; pub const DETAILS: &str = r#"Absolutely no ads, tracking, or nazis, ever."#; @@ -104,6 +105,18 @@ impl Tag { } } +#[derive(FromForm)] +pub struct RecipeForm { + pub name: String, + pub author: String, + pub tags: String, + pub prep_time: String, + pub cooking_time: String, + pub servings: String, + pub ingredients: String, + pub directions: String, +} + #[derive(Debug, Serialize, Deserialize)] pub struct Recipe { pub id: Uuid, // Unique recipe ID @@ -274,3 +287,9 @@ pub fn construct_shortcode(full_name: String) -> String { }) .collect(); } + +// Struct for the users answer to a captcha +#[derive(Clone, Debug, FromForm)] +pub struct CaptchaAnswer { + pub text: String, +} diff --git a/static/en/submit.html b/static/en/submit.html index 54a5afa..03a1599 100644 --- a/static/en/submit.html +++ b/static/en/submit.html @@ -12,7 +12,7 @@

Submit a New Recipe


-
+
  • @@ -32,7 +32,7 @@
  • - +