diff --git a/migrations/20210509135118_mcaptcha_pow_solved_stats.sql b/migrations/20210509135118_mcaptcha_pow_solved_stats.sql new file mode 100644 index 00000000..833da2dc --- /dev/null +++ b/migrations/20210509135118_mcaptcha_pow_solved_stats.sql @@ -0,0 +1,4 @@ +CREATE TABLE IF NOT EXISTS mcaptcha_pow_solved_stats ( + config_id INTEGER references mcaptcha_config(config_id) ON DELETE CASCADE, + solved_at timestamptz NOT NULL DEFAULT now() +); diff --git a/migrations/20210509135154_mcaptcha_pow_confirmed_stats.sql b/migrations/20210509135154_mcaptcha_pow_confirmed_stats.sql new file mode 100644 index 00000000..87c28aba --- /dev/null +++ b/migrations/20210509135154_mcaptcha_pow_confirmed_stats.sql @@ -0,0 +1,4 @@ +CREATE TABLE IF NOT EXISTS mcaptcha_pow_confirmed_stats ( + config_id INTEGER references mcaptcha_config(config_id) ON DELETE CASCADE, + confirm_ed timestamptz NOT NULL DEFAULT now() +); diff --git a/src/api/v1/mcaptcha/stats.rs b/src/api/v1/mcaptcha/stats.rs index 958f5907..37664461 100644 --- a/src/api/v1/mcaptcha/stats.rs +++ b/src/api/v1/mcaptcha/stats.rs @@ -17,7 +17,9 @@ use sqlx::PgPool; -pub async fn fetched(key: &str, db: &PgPool) { +/// record PoWConfig fetches +#[inline] +pub async fn record_fetch(key: &str, db: &PgPool) { let _ = sqlx::query!( "INSERT INTO mcaptcha_pow_fetched_stats (config_id) VALUES ((SELECT config_id FROM mcaptcha_config WHERE key = $1))", @@ -26,3 +28,27 @@ pub async fn fetched(key: &str, db: &PgPool) { .execute(db) .await; } + +/// record PoWConfig solves +#[inline] +pub async fn record_solve(key: &str, db: &PgPool) { + let _ = sqlx::query!( + "INSERT INTO mcaptcha_pow_solved_stats + (config_id) VALUES ((SELECT config_id FROM mcaptcha_config WHERE key = $1))", + &key, + ) + .execute(db) + .await; +} + +/// record PoWConfig confirms +#[inline] +pub async fn record_confirm(key: &str, db: &PgPool) { + let _ = sqlx::query!( + "INSERT INTO mcaptcha_pow_confirmed_stats + (config_id) VALUES ((SELECT config_id FROM mcaptcha_config WHERE key = $1))", + &key, + ) + .execute(db) + .await; +} diff --git a/src/api/v1/pow/get_config.rs b/src/api/v1/pow/get_config.rs index 3cf2591b..00133485 100644 --- a/src/api/v1/pow/get_config.rs +++ b/src/api/v1/pow/get_config.rs @@ -20,9 +20,9 @@ use actix_web::{web, HttpResponse, Responder}; use m_captcha::{defense::LevelBuilder, master::AddSiteBuilder, DefenseBuilder, MCaptchaBuilder}; use serde::{Deserialize, Serialize}; +use super::record_fetch; use super::GetDurationResp; use super::I32Levels; -use crate::api::v1::mcaptcha::stats::fetched; use crate::errors::*; use crate::Data; @@ -39,6 +39,7 @@ pub struct GetConfigPayload { // API keys are mcaptcha actor names +/// get PoW configuration for an mcaptcha key pub async fn get_config( payload: web::Json, data: web::Data, @@ -68,7 +69,7 @@ pub async fn get_config( .expect("mcaptcha should be initialized and ready to go"); // background it. would require data::Data to be static // to satidfy lifetime - fetched(&payload.key, &data.db).await; + record_fetch(&payload.key, &data.db).await; Ok(HttpResponse::Ok().json(config)) } } @@ -78,7 +79,10 @@ pub async fn get_config( None => Err(ServiceError::TokenNotFound), } } - +/// Call this when [MCaptcha][m_captcha::MCaptcha] is not in master. +/// +/// This fn gets mcaptcha config from database, builds [Defense][m_captcha::Defense], +/// creates [MCaptcha][m_captcha::MCaptcha] and adds it to [Master][m_captcha::Defense] async fn init_mcaptcha(data: &Data, key: &str) -> ServiceResult<()> { // get levels let levels_fut = sqlx::query_as!( diff --git a/src/api/v1/pow/mod.rs b/src/api/v1/pow/mod.rs index 906a9fc4..e343f23a 100644 --- a/src/api/v1/pow/mod.rs +++ b/src/api/v1/pow/mod.rs @@ -24,6 +24,7 @@ pub mod verify_token; pub use super::mcaptcha::duration::GetDurationResp; pub use super::mcaptcha::levels::I32Levels; +use crate::api::v1::mcaptcha::stats::*; pub fn services(cfg: &mut web::ServiceConfig) { use crate::define_resource; diff --git a/src/api/v1/pow/verify_pow.rs b/src/api/v1/pow/verify_pow.rs index 52af58f6..ea468584 100644 --- a/src/api/v1/pow/verify_pow.rs +++ b/src/api/v1/pow/verify_pow.rs @@ -14,26 +14,34 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ +//! PoW Verification module use actix_web::{web, HttpResponse, Responder}; use m_captcha::pow::Work; use serde::{Deserialize, Serialize}; +use super::record_solve; use crate::errors::*; use crate::Data; #[derive(Clone, Debug, Deserialize, Serialize)] +/// validation token that clients receive as proof for submiting +/// valid PoW pub struct ValidationToken { pub token: String, } // API keys are mcaptcha actor names +/// route handler that verifies PoW and issues a solution token +/// if verification is successful pub async fn verify_pow( payload: web::Json, data: web::Data, ) -> ServiceResult { + let key = payload.key.clone(); let res = data.captcha.verify_pow(payload.into_inner()).await?; + record_solve(&key, &data.db).await; let payload = ValidationToken { token: res }; Ok(HttpResponse::Ok().json(payload)) } diff --git a/src/api/v1/pow/verify_token.rs b/src/api/v1/pow/verify_token.rs index 1b80ebf9..19d93986 100644 --- a/src/api/v1/pow/verify_token.rs +++ b/src/api/v1/pow/verify_token.rs @@ -14,11 +14,13 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ +//! PoW success token module use actix_web::{web, HttpResponse, Responder}; use m_captcha::cache::messages::VerifyCaptchaResult; use serde::{Deserialize, Serialize}; +use super::record_confirm; use crate::errors::*; use crate::Data; @@ -29,16 +31,19 @@ pub struct CaptchaValidateResp { // API keys are mcaptcha actor names +/// route hander that validates a PoW solution token pub async fn validate_captcha_token( payload: web::Json, data: web::Data, ) -> ServiceResult { + let key = payload.key.clone(); let res = data .captcha .validate_verification_tokens(payload.into_inner()) .await?; let payload = CaptchaValidateResp { valid: res }; - println!("{:?}", &payload); + record_confirm(&key, &data.db).await; + //println!("{:?}", &payload); Ok(HttpResponse::Ok().json(payload)) }