// Example of using the `salvo_captcha`, this example is a simple login page with a captcha. // The page will be in // You can see a video of this example here: // // Run the example with `cargo run --example simple_login --features cacache-storage` use std::sync::Arc; use base64::{engine::GeneralPurpose, Engine}; use salvo::prelude::*; use salvo_captcha::*; // To convert the image to base64, to show it in the browser const BASE_64_ENGINE: GeneralPurpose = GeneralPurpose::new( &base64::alphabet::STANDARD, base64::engine::general_purpose::PAD, ); const SIMPLE_GENERATOR: SimpleGenerator = SimpleGenerator::new(CaptchaName::Normal, CaptchaDifficulty::Medium); #[handler] async fn index(res: &mut Response, depot: &mut Depot) { // Get the captcha from the depot let captcha_storage = depot.obtain::>().unwrap(); // Create a new captcha let Ok((token, image)) = captcha_storage.new_captcha(SIMPLE_GENERATOR).await else { res.status_code(StatusCode::INTERNAL_SERVER_ERROR); res.render(Text::Html( "

Server Error 500

", )); return; }; // Convert the image to base64 let image = BASE_64_ENGINE.encode(image); // Set the response content res.render(Text::Html(index_page(image, token))) } #[handler] async fn auth(req: &mut Request, res: &mut Response, depot: &mut Depot) { // Get the captcha state from the depot, where we can know if the captcha is passed let captcha_state = depot.get_captcha_state(); // Not important, just for demo let Some(username) = req.form::("username").await else { res.status_code(StatusCode::BAD_REQUEST); return res.render(Text::Html("Invalid form submission")); }; // Handle the captcha state, that's all let content = match captcha_state { CaptchaState::Passed => { format!("Welcome, {username}!") } CaptchaState::AnswerNotFound => "Captcha answer not found".to_string(), CaptchaState::TokenNotFound => "Captcha token not found".to_string(), CaptchaState::WrongAnswer => "Wrong captcha answer".to_string(), CaptchaState::WrongToken => "Wrong captcha token".to_string(), CaptchaState::Skipped => "Captcha skipped".to_string(), CaptchaState::StorageError => "Captcha storage error".to_string(), }; res.render(Text::Html(captcha_result_page(content))) } #[tokio::main] async fn main() { let captcha_storage = Arc::new(MemoryStorage::new()); let captcha_middleware = CaptchaBuilder::new(Arc::clone(&captcha_storage), CaptchaFormFinder::new()) // Skip the captcha if the request path is /skipped .skipper(|req: &mut Request, _: &Depot| req.uri().path() == "/skipped") .case_insensitive() .build(); let router = Router::new() .hoop(affix::inject(captcha_storage)) .push(Router::with_path("/").get(index)) .push( Router::new() .hoop(captcha_middleware) .push(Router::with_path("/auth").post(auth)) .push(Router::with_path("/skipped").post(auth)), ); let acceptor = TcpListener::new(("127.0.0.1", 5800)).bind().await; Server::new(acceptor).serve(router).await; } fn index_page(captcha_image: String, captcha_token: String) -> String { format!( r#" Salvo Captcha Example

Salvo Captcha Example

Sign In




Or you can skip the captcha


Source Code "# ) } fn captcha_result_page(captcha_result: String) -> String { format!( r#" Salvo Captcha Example

Salvo Captcha Example

Result page

{captcha_result}
Go Back "# ) }