Add base
This commit is contained in:
parent
eb5d7425a5
commit
e619cea3ee
4 changed files with 157 additions and 0 deletions
12
src/main.rs
Normal file
12
src/main.rs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
mod routes;
|
||||||
|
mod states;
|
||||||
|
|
||||||
|
use rocket_dyn_templates::Template;
|
||||||
|
|
||||||
|
#[rocket::launch]
|
||||||
|
fn rocket() -> _ {
|
||||||
|
rocket::build()
|
||||||
|
.mount("/", rocket::routes![routes::index, routes::submit])
|
||||||
|
.manage(states::SharedCaptchaSolutions::new())
|
||||||
|
.attach(Template::fairing())
|
||||||
|
}
|
56
src/routes.rs
Normal file
56
src/routes.rs
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
use rocket::form::{Form, FromForm, Strict};
|
||||||
|
use rocket::response::status::BadRequest;
|
||||||
|
use rocket::{get, post, State};
|
||||||
|
use rocket_dyn_templates::Template;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use crate::states;
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct FormContext {
|
||||||
|
id: u16,
|
||||||
|
captcha: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/")]
|
||||||
|
pub fn index(
|
||||||
|
captcha_solutions: &State<states::SharedCaptchaSolutions>,
|
||||||
|
) -> Result<Template, BadRequest<()>> {
|
||||||
|
let captcha = captcha::by_name(captcha::Difficulty::Easy, captcha::CaptchaName::Lucy);
|
||||||
|
let captcha_base64 = match captcha.as_base64() {
|
||||||
|
Some(s) => s,
|
||||||
|
None => return Err(BadRequest(None)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let id = captcha_solutions.store_solution(&captcha.chars_as_string());
|
||||||
|
|
||||||
|
let form_context = FormContext {
|
||||||
|
id,
|
||||||
|
captcha: captcha_base64,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Template::render("form", &form_context))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(FromForm)]
|
||||||
|
pub struct ContactForm {
|
||||||
|
id: u16,
|
||||||
|
name: String,
|
||||||
|
email: String,
|
||||||
|
telefon: String,
|
||||||
|
message: String,
|
||||||
|
captcha_answer: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/submit", data = "<form>")]
|
||||||
|
pub fn submit(
|
||||||
|
form: Form<Strict<ContactForm>>,
|
||||||
|
captcha_solutions: &State<states::SharedCaptchaSolutions>,
|
||||||
|
) -> Result<String, BadRequest<String>> {
|
||||||
|
println!(
|
||||||
|
"{}",
|
||||||
|
captcha_solutions.check_answer(form.id, &form.captcha_answer)
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok("Ihre Anfrage wurde übermittelt. Wir melden uns bald bei Ihnen zurück.".to_string())
|
||||||
|
}
|
49
src/states.rs
Normal file
49
src/states.rs
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
pub struct CaptchaSolutions {
|
||||||
|
last_id: u16,
|
||||||
|
solutions: Vec<Option<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SharedCaptchaSolutions {
|
||||||
|
arc: Arc<Mutex<CaptchaSolutions>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SharedCaptchaSolutions {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let max_size = (u16::MAX as usize) + 1;
|
||||||
|
let mut solutions = Vec::with_capacity(max_size);
|
||||||
|
solutions.resize(max_size, None);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
arc: Arc::new(Mutex::new(CaptchaSolutions {
|
||||||
|
last_id: 0,
|
||||||
|
solutions,
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn store_solution(&self, solution: &str) -> u16 {
|
||||||
|
let mut captcha_solutions = self.arc.lock().unwrap();
|
||||||
|
|
||||||
|
let new_id = captcha_solutions.last_id.wrapping_add(1);
|
||||||
|
captcha_solutions.last_id = new_id;
|
||||||
|
|
||||||
|
captcha_solutions.solutions[new_id as usize] = Some(solution.to_string());
|
||||||
|
|
||||||
|
new_id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_answer(&self, id: u16, answer: &str) -> bool {
|
||||||
|
let mut captcha_solutions = self.arc.lock().unwrap();
|
||||||
|
|
||||||
|
let id = id as usize;
|
||||||
|
|
||||||
|
if captcha_solutions.solutions[id] == Some(answer.trim().to_string()) {
|
||||||
|
captcha_solutions.solutions[id] = None;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
40
templates/form.html.tera
Normal file
40
templates/form.html.tera
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Contact form</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h2>Kontakt-Formular</h2>
|
||||||
|
|
||||||
|
<form action="/submit" method="post">
|
||||||
|
<input type="hidden" name="id" value="{{ id }}" required>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="name" class="form-label">Name</label>
|
||||||
|
<input type="text" name="name" class="form-control" id="exampleInputEmail1" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="email" class="form-label">E-Mail</label>
|
||||||
|
<input type="email" name="email" class="form-control" id="email" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="telefon" class="form-label">Telefon</label>
|
||||||
|
<input type="number" name="telefon" class="form-control" id="telefon">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="message" class="form-label">Nachricht</label>
|
||||||
|
<input type="text" name="message" class="form-control" id="message" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="captcha_answer" class="form-label">Code vom Bild daneben eingeben</label>
|
||||||
|
<input type="text" name="captcha_answer" class="form-control" id="captcha_answer" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-primary">Abschicken</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<img src="data:image/png;base64,{{ captcha }}">
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in a new issue