use cot::db::{Database, query}; use crate::models::Setting; /// Read `turnstile_site_key` from Settings. Returns empty string if not configured. pub async fn get_site_key(db: &Database) -> cot::Result { let key = "turnstile_site_key".to_string(); Ok(query!(Setting, $key == key) .get(db) .await? .map(|s| s.value) .unwrap_or_default()) } /// Verify a Turnstile token against Cloudflare. /// Returns `true` if verification succeeds, or if no secret key is configured (passthrough). pub async fn verify(db: &Database, token: Option<&str>) -> cot::Result { let secret_key_name = "turnstile_secret_key".to_string(); let secret_key = query!(Setting, $key == secret_key_name) .get(db) .await? .map(|s| s.value) .filter(|s| !s.is_empty()); let Some(secret) = secret_key else { return Ok(true); }; let token = token.unwrap_or(""); let client = reqwest::Client::new(); let resp = client .post("https://challenges.cloudflare.com/turnstile/v0/siteverify") .json(&serde_json::json!({ "secret": secret, "response": token })) .send() .await; Ok(match resp { Ok(r) => r .json::() .await .map(|v| v["success"].as_bool() == Some(true)) .unwrap_or(false), Err(_) => false, }) }