From 5b007a322026ade15cb7067c02abce9c0b93c021 Mon Sep 17 00:00:00 2001 From: Mo8it Date: Mon, 31 Oct 2022 02:13:42 +0100 Subject: [PATCH] Migrate to Axum --- Cargo.toml | 5 +- Rocket.toml | 6 -- src/context.rs | 2 +- src/forms.rs | 4 +- src/main.rs | 49 ++++++++---- src/routes.rs | 150 ++++++++++++++++++++---------------- templates/form.html.tera | 2 +- templates/success.html.tera | 2 +- 8 files changed, 124 insertions(+), 96 deletions(-) delete mode 100644 Rocket.toml diff --git a/Cargo.toml b/Cargo.toml index faa96fa..db79ff6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,9 +8,10 @@ license-file = "LICENSE.txt" [dependencies] anyhow = "1.0" +axum = "0.5" captcha = "0.0.9" lettre = "0.10" -rocket = "0.5.0-rc.2" -rocket_dyn_templates = { version = "0.1.0-rc.2", features = ["tera"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" +tera = "1.17" +tokio = { version = "1.21", features = ["full"] } diff --git a/Rocket.toml b/Rocket.toml deleted file mode 100644 index 9ea876c..0000000 --- a/Rocket.toml +++ /dev/null @@ -1,6 +0,0 @@ -[default] -template_dir = "templates" - -[release] -address = "0.0.0.0" -port = 80 diff --git a/src/context.rs b/src/context.rs index 9c7ed0d..5d581d2 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,7 +1,7 @@ use serde::Serialize; #[derive(Serialize)] -pub struct ContactFormContext<'a> { +pub struct ContactForm<'a> { pub path_prefix: &'a str, pub was_validated: bool, pub id: u16, diff --git a/src/forms.rs b/src/forms.rs index a13e5e1..2a65dd5 100644 --- a/src/forms.rs +++ b/src/forms.rs @@ -1,6 +1,6 @@ -use rocket::form::FromForm; +use serde::Deserialize; -#[derive(FromForm)] +#[derive(Deserialize)] pub struct ContactForm { pub id: u16, pub name: String, diff --git a/src/main.rs b/src/main.rs index 3b1932a..68d0db9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,30 +5,45 @@ mod forms; mod mailer; mod routes; -use anyhow::Result; -use rocket::{Build, Rocket}; -use rocket_dyn_templates::Template; +use anyhow::{Context, Result}; +use axum::extract::Extension; +use axum::routing::{get, post}; +use axum::{Router, Server}; use std::process; +use std::sync::Arc; +use tera::Tera; + +async fn init() -> Result<()> { + let tera = Arc::new(Tera::new("templates/*").context("Failed to parse templates!")?); -fn init() -> Result> { let mut config = config::Config::new()?; + let path_prefix = config.path_prefix.clone(); + let mailer = Arc::new(mailer::Mailer::new(&mut config)?); + let config = Arc::new(config); + let captcha_solutions = Arc::new(captcha_solutions::SharedCaptchaSolutions::new()); - let rocket = rocket::build() - .mount( - &config.path_prefix, - rocket::routes![routes::index, routes::submit, routes::success], - ) - .manage(captcha_solutions::SharedCaptchaSolutions::new()) - .manage(mailer::Mailer::new(&mut config)?) - .manage(config) - .attach(Template::fairing()); + let routes = Router::new() + .route("/", get(routes::index)) + .route("/submit", post(routes::submit)) + .route("/success", get(routes::success)) + .layer(Extension(config)) + .layer(Extension(mailer)) + .layer(Extension(captcha_solutions)) + .layer(Extension(tera)); - Ok(rocket) + let app = Router::new().nest(&path_prefix, routes); + + Server::bind(&([127, 0, 0, 1], 8080).into()) + .serve(app.into_make_service()) + .await + .unwrap(); + + Ok(()) } -#[rocket::launch] -fn rocket() -> _ { - init().unwrap_or_else(|e| { +#[tokio::main] +async fn main() { + init().await.unwrap_or_else(|e| { eprintln!("{e}"); process::exit(1); }) diff --git a/src/routes.rs b/src/routes.rs index 505d8c2..d9362c4 100644 --- a/src/routes.rs +++ b/src/routes.rs @@ -1,97 +1,115 @@ -use rocket::form::{Form, Strict}; -use rocket::response::status::BadRequest; -use rocket::response::Redirect; -use rocket::{get, post, uri, State}; -use rocket_dyn_templates::{context, Template}; +use anyhow::{Context, Result}; +use axum::extract::{Extension, Form, Query}; +use axum::http::StatusCode; +use axum::response::{Html, IntoResponse, Response}; +use serde::Deserialize; use std::mem; +use std::sync::Arc; +use tera::Tera; -use crate::captcha_solutions; -use crate::config; -use crate::context; -use crate::forms; -use crate::mailer; +use crate::{captcha_solutions, config, context, forms, mailer}; -#[get("/?&&&&")] -pub fn index( +pub struct AppError(anyhow::Error); + +impl IntoResponse for AppError { + fn into_response(self) -> Response { + StatusCode::BAD_REQUEST.into_response() + } +} + +impl From for AppError +where + E: Into, +{ + fn from(err: E) -> Self { + Self(err.into()) + } +} + +#[derive(Deserialize)] +pub struct IndexParams { was_validated: Option, - name: Option<&str>, - email: Option<&str>, - telefon: Option<&str>, - message: Option<&str>, - config: &State, - captcha_solutions: &State, -) -> Result> { + name: Option, + email: Option, + telefon: Option, + message: Option, +} + +pub async fn index( + Query(params): Query, + engine: Extension>, + config: Extension>, + captcha_solutions: Extension>, +) -> Result { 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 captcha_base64 = captcha.as_base64().context("Failed to create a captcha!")?; let id = captcha_solutions.store_solution(&captcha.chars_as_string()); - let form_context = context::ContactFormContext { + let form_context = context::ContactForm { path_prefix: &config.path_prefix, - was_validated: was_validated.unwrap_or(false), + was_validated: params.was_validated.unwrap_or(false), id, - name: name.unwrap_or(""), - email: email.unwrap_or(""), - telefon: telefon.unwrap_or(""), - message: message.unwrap_or(""), + name: ¶ms.name.unwrap_or_default(), + email: ¶ms.email.unwrap_or_default(), + telefon: ¶ms.telefon.unwrap_or_default(), + message: ¶ms.message.unwrap_or_default(), captcha: &captcha_base64, }; - Ok(Template::render("form", form_context)) + let html = engine.render( + "form.html.tera", + &tera::Context::from_serialize(&form_context)?, + )?; + + Ok(Html(html).into_response()) } -#[post("/submit", data = "
")] -pub fn submit( - mut form: Form>, - config: &State, - captcha_solutions: &State, - mailer: &State, -) -> Redirect { - let path_prefix = config.path_prefix.clone(); - +async fn back_to_index( + mut form: forms::ContactForm, + engine: Extension>, + config: Extension>, + captcha_solutions: Extension>, +) -> Result { let name = mem::take(&mut form.name); let email = mem::take(&mut form.email); let telefon = mem::take(&mut form.telefon); let message = mem::take(&mut form.message); + let params = Query(IndexParams { + was_validated: Some(true), + name: Some(name), + email: Some(email), + telefon: Some(telefon), + message: Some(message), + }); + + index(params, engine, config, captcha_solutions).await +} + +pub async fn submit( + Form(form): Form, + engine: Extension>, + config: Extension>, + captcha_solutions: Extension>, + mailer: Extension>, +) -> Result { if !captcha_solutions.check_answer(form.id, &form.captcha_answer) { - return Redirect::to( - path_prefix - + &uri!(index( - Some(true), - Some(&name), - Some(&email), - Some(&telefon), - Some(&message) - )) - .to_string(), - ); + return back_to_index(form, engine, config, captcha_solutions).await; } - match mailer.send(&name, &email, &telefon, &message) { + match mailer.send(&form.name, &form.email, &form.telefon, &form.message) { Ok(_) => (), Err(_) => { - return Redirect::to( - path_prefix - + &uri!(index( - Some(true), - Some(&name), - Some(&email), - Some(&telefon), - Some(&message) - )) - .to_string(), - ) + return back_to_index(form, engine, config, captcha_solutions).await; } } - Redirect::to(path_prefix + &uri!(success()).to_string()) + success(engine).await } -#[get("/success")] -pub fn success() -> Template { - Template::render("success", context! {}) +pub async fn success(engine: Extension>) -> Result { + let html = engine.render("success.html.tera", &tera::Context::new())?; + + Ok(Html(html).into_response()) } diff --git a/templates/form.html.tera b/templates/form.html.tera index 0979a16..973505e 100644 --- a/templates/form.html.tera +++ b/templates/form.html.tera @@ -1,4 +1,4 @@ -{% extends "base" %} +{% extends "base.html.tera" %} {% block body %}

diff --git a/templates/success.html.tera b/templates/success.html.tera index 49d083e..850e383 100644 --- a/templates/success.html.tera +++ b/templates/success.html.tera @@ -1,4 +1,4 @@ -{% extends "base" %} +{% extends "base.html.tera" %} {% block body %}