diff --git a/Cargo.lock b/Cargo.lock index 91ba0c1e..ebd206a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1650,6 +1650,7 @@ dependencies = [ "actix-web", "actix-web-codegen 4.0.0 (git+https://github.com/realaravinth/actix-web)", "argon2-creds", + "async-trait", "awc", "cache-buster", "config", diff --git a/Cargo.toml b/Cargo.toml index a1e35cb5..9f2080b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ actix-http = "3.0.4" actix-rt = "2" actix-cors = "0.6.1" actix-service = "2.0.0" -#my-codegen = {version="0.5.0-beta.5", package = "actix-web-codegen", git ="https://github.com/realaravinth/actix-web"} +async-trait = "0.1.51" mime_guess = "2.0.3" rust-embed = "6.4.0" cache-buster = { git = "https://github.com/realaravinth/cache-buster" } diff --git a/src/api/v1/pow/get_config.rs b/src/api/v1/pow/get_config.rs index 9110e3a7..c4be496b 100644 --- a/src/api/v1/pow/get_config.rs +++ b/src/api/v1/pow/get_config.rs @@ -24,7 +24,7 @@ use libmcaptcha::{ use serde::{Deserialize, Serialize}; use crate::errors::*; -use crate::stats::record::record_fetch; +//use crate::stats::record::record_fetch; use crate::AppData; use crate::V1_API_ROUTES; @@ -49,7 +49,7 @@ pub async fn get_config( match data.captcha.get_pow(payload.key.clone()).await { Ok(Some(config)) => { - record_fetch(&payload.key, &data.db).await; + data.stats.record_fetch(&data, &payload.key).await; Ok(HttpResponse::Ok().json(config)) } Ok(None) => { @@ -61,7 +61,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 - record_fetch(&payload.key, &data.db).await; + data.stats.record_fetch(&data, &payload.key).await; Ok(HttpResponse::Ok().json(config)) } Err(e) => Err(e.into()), diff --git a/src/api/v1/pow/verify_pow.rs b/src/api/v1/pow/verify_pow.rs index 3202ed4d..40a45eb4 100644 --- a/src/api/v1/pow/verify_pow.rs +++ b/src/api/v1/pow/verify_pow.rs @@ -21,7 +21,6 @@ use libmcaptcha::pow::Work; use serde::{Deserialize, Serialize}; use crate::errors::*; -use crate::stats::record::record_solve; use crate::AppData; use crate::V1_API_ROUTES; @@ -43,7 +42,7 @@ pub async fn verify_pow( ) -> ServiceResult { let key = payload.key.clone(); let res = data.captcha.verify_pow(payload.into_inner()).await?; - record_solve(&key, &data.db).await; + data.stats.record_solve(&data, &key).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 bad1c6fc..4e37e685 100644 --- a/src/api/v1/pow/verify_token.rs +++ b/src/api/v1/pow/verify_token.rs @@ -21,7 +21,6 @@ use libmcaptcha::cache::messages::VerifyCaptchaResult; use serde::{Deserialize, Serialize}; use crate::errors::*; -use crate::stats::record::record_confirm; use crate::AppData; use crate::V1_API_ROUTES; @@ -44,7 +43,7 @@ pub async fn validate_captcha_token( .validate_verification_tokens(payload.into_inner()) .await?; let payload = CaptchaValidateResp { valid: res }; - record_confirm(&key, &data.db).await; + data.stats.record_confirm(&data, &key).await; //println!("{:?}", &payload); Ok(HttpResponse::Ok().json(payload)) } diff --git a/src/data.rs b/src/data.rs index 7eb58e4b..87489259 100644 --- a/src/data.rs +++ b/src/data.rs @@ -46,8 +46,8 @@ use sqlx::PgPool; use db_core::MCDatabase; use crate::errors::ServiceResult; -//use crate::SETTINGS; use crate::settings::Settings; +use crate::stats::{Dummy, Real, Stats}; macro_rules! enum_system_actor { ($name:ident, $type:ident) => { @@ -159,9 +159,10 @@ pub struct Data { pub captcha: SystemGroup, /// email client pub mailer: Option, - /// app settings pub settings: Settings, + /// stats recorder + pub stats: Box, } impl Data { @@ -202,6 +203,12 @@ impl Data { let dblib = connection_options.connect().await.unwrap(); dblib.migrate().await.unwrap(); + let stats: Box = if s.captcha.enable_stats { + Box::new(Real::default()) + } else { + Box::new(Dummy::default()) + }; + let data = Data { creds, db, @@ -209,6 +216,7 @@ impl Data { captcha: SystemGroup::new(s).await, mailer: Self::get_mailer(s), settings: s.clone(), + stats, }; #[cfg(not(debug_assertions))] diff --git a/src/stats/fetch.rs b/src/stats/fetch.rs index 12e8d79a..8cf56714 100644 --- a/src/stats/fetch.rs +++ b/src/stats/fetch.rs @@ -176,7 +176,6 @@ pub mod runners { #[cfg(test)] mod tests { use super::*; - use crate::stats::record::*; use crate::tests::*; #[actix_rt::test] @@ -185,8 +184,7 @@ mod tests { const PASSWORD: &str = "testingpas"; const EMAIL: &str = "statsuser@a.com"; - let data = crate::data::Data::new().await; - let data = &data; + let data = get_data().await; let data = &data; delete_user(data, NAME).await; @@ -200,10 +198,10 @@ mod tests { assert_eq!(stats.solves.len(), 0); assert_eq!(stats.confirms.len(), 0); - futures::join!( - record_fetch(&key, &data.db), - record_solve(&key, &data.db), - record_confirm(&key, &data.db) + let _ = futures::join!( + data.stats.record_fetch(&data, &key,), + data.stats.record_solve(&data, &key,), + data.stats.record_confirm(&data, &key,), ); let stats = Stats::new(NAME, &key, &data.db).await.unwrap(); diff --git a/src/stats/mod.rs b/src/stats/mod.rs index 7216bd28..3f5af9a7 100644 --- a/src/stats/mod.rs +++ b/src/stats/mod.rs @@ -16,4 +16,87 @@ */ pub mod fetch; -pub mod record; +//pub mod record; + +pub use fetch::StatsUnixTimestamp; + +use async_trait::async_trait; +use db_core::errors::DBResult; + +use crate::data::Data; + +#[async_trait] +pub trait Stats: std::marker::Send + std::marker::Sync + CloneStats { + /// record PoWConfig fetches + async fn record_fetch(&self, d: &Data, key: &str) -> DBResult<()>; + + /// record PoWConfig solves + async fn record_solve(&self, d: &Data, key: &str) -> DBResult<()>; + + /// record PoWConfig confirms + async fn record_confirm(&self, d: &Data, key: &str) -> DBResult<()>; +} + +/// Trait to clone MCDatabase +pub trait CloneStats { + /// clone DB + fn clone_stats(&self) -> Box; +} + +impl CloneStats for T +where + T: Stats + Clone + 'static, +{ + fn clone_stats(&self) -> Box { + Box::new(self.clone()) + } +} + +impl Clone for Box { + fn clone(&self) -> Self { + self.clone() + //(*self).clone_stats() + } +} + +#[derive(Clone, Default, PartialEq, Debug)] +pub struct Real; + +#[async_trait] +impl Stats for Real { + /// record PoWConfig fetches + async fn record_fetch(&self, d: &Data, key: &str) -> DBResult<()> { + d.dblib.record_fetch(key).await + } + + /// record PoWConfig solves + async fn record_solve(&self, d: &Data, key: &str) -> DBResult<()> { + d.dblib.record_solve(key).await + } + + /// record PoWConfig confirms + async fn record_confirm(&self, d: &Data, key: &str) -> DBResult<()> { + d.dblib.record_confirm(key).await + } +} + +#[derive(Clone, Default, PartialEq, Debug)] +pub struct Dummy; + +#[async_trait] +impl Stats for Dummy { + /// record PoWConfig fetches + async fn record_fetch(&self, _: &Data, _: &str) -> DBResult<()> { + Ok(()) + } + + /// record PoWConfig solves + async fn record_solve(&self, _: &Data, _: &str) -> DBResult<()> { + Ok(()) + } + + /// record PoWConfig confirms + async fn record_confirm(&self, _: &Data, _: &str) -> DBResult<()> { + Ok(()) + } +} diff --git a/src/stats/record.rs b/src/stats/record.rs index 93ddc6d8..bc03514e 100644 --- a/src/stats/record.rs +++ b/src/stats/record.rs @@ -1,19 +1,19 @@ /* -* Copyright (C) 2022 Aravinth Manivannan -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see . -*/ + * Copyright (C) 2022 Aravinth Manivannan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ use sqlx::types::time::OffsetDateTime; use sqlx::PgPool;