captcha stats
This commit is contained in:
parent
df89938f2a
commit
fcdbe66b26
28 changed files with 412 additions and 122 deletions
|
@ -1,4 +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()
|
||||
confirmed_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
|
|
112
sqlx-data.json
112
sqlx-data.json
|
@ -14,32 +14,6 @@
|
|||
"nullable": []
|
||||
}
|
||||
},
|
||||
"0612bd9e6b89ff4bf09be0936bf2262cde3967a54fa103ff09a591f7539d063d": {
|
||||
"query": "SELECT difficulty_factor, visitor_threshold from mcaptcha_levels WHERE config_id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "difficulty_factor",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "visitor_threshold",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int4"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"1be6274d5cc6d16f38285b8a62c9f66e8c3014cd403bc599598e911023bfeedb": {
|
||||
"query": "INSERT INTO mcaptcha_pow_fetched_stats \n (config_id) VALUES ((SELECT config_id FROM mcaptcha_config WHERE key = $1))",
|
||||
"describe": {
|
||||
|
@ -173,6 +147,26 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"4dc1b6d8ae3b92ebff45f683951c087244f9614ed0e95b75578f0d1346887224": {
|
||||
"query": "SELECT fetched_at FROM mcaptcha_pow_fetched_stats WHERE config_id = \n (SELECT config_id FROM mcaptcha_config where key = $1)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "fetched_at",
|
||||
"type_info": "Timestamptz"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"507bea10c7f8417c5b1430211d0137299cd561333bf47f7b4887d0ef801d1ea4": {
|
||||
"query": "UPDATE mcaptcha_config SET key = $1 \n WHERE key = $2 AND user_id = (SELECT ID FROM mcaptcha_users WHERE name = $3)",
|
||||
"describe": {
|
||||
|
@ -355,6 +349,32 @@
|
|||
"nullable": []
|
||||
}
|
||||
},
|
||||
"a1f1e5693dad5c04b85f97d1de9c68b584a1ca99436e61f7c93f2a5acf8fb55f": {
|
||||
"query": "SELECT \n difficulty_factor, visitor_threshold \n FROM \n mcaptcha_levels \n WHERE config_id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "difficulty_factor",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "visitor_threshold",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int4"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"aa9a21fd88c106fe6c4b75a724b202b7bdda66eb9c5fd91780113e2c3ea82719": {
|
||||
"query": "SELECT difficulty_factor, visitor_threshold FROM mcaptcha_levels WHERE\n config_id = (\n SELECT config_id FROM mcaptcha_config WHERE key = ($1)\n AND user_id = (SELECT ID from mcaptcha_users WHERE name = $2)\n );",
|
||||
"describe": {
|
||||
|
@ -501,6 +521,26 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"daebbef26cf04fdc46226304d028528e121a9847c07139d7d3a56a0e7c165879": {
|
||||
"query": "SELECT solved_at FROM mcaptcha_pow_solved_stats WHERE config_id = \n (SELECT config_id FROM mcaptcha_config where key = $1)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "solved_at",
|
||||
"type_info": "Timestamptz"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"dcf0d4f9d803dcb1d6f775899f79595f9c78d46633e0ec822303284430df7a3d": {
|
||||
"query": "-- gets all unread notifications a user has\nSELECT \n mcaptcha_notifications.id,\n mcaptcha_notifications.heading,\n mcaptcha_notifications.message,\n mcaptcha_notifications.received,\n mcaptcha_users.name\nFROM\n mcaptcha_notifications \nINNER JOIN \n mcaptcha_users \nON \n mcaptcha_notifications.tx = mcaptcha_users.id\nWHERE \n mcaptcha_notifications.rx = (\n SELECT \n id \n FROM \n mcaptcha_users\n WHERE\n name = $1\n )\nAND \n mcaptcha_notifications.read IS NULL;\n",
|
||||
"describe": {
|
||||
|
@ -580,6 +620,26 @@
|
|||
"nullable": []
|
||||
}
|
||||
},
|
||||
"fb15883459af48b0f6f1eee5d47641db8fa0875f7f21665dbc40800869e5e353": {
|
||||
"query": "SELECT confirmed_at FROM mcaptcha_pow_confirmed_stats WHERE config_id = (\n SELECT config_id FROM mcaptcha_config where key = $1)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "confirmed_at",
|
||||
"type_info": "Timestamptz"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"fb19fbff4265cc59450d64a8d945f0ae2ad337b97e6192837881e8b6b4c397ee": {
|
||||
"query": "DELETE FROM mcaptcha_levels WHERE \n config_id = (\n SELECT config_id FROM mcaptcha_config WHERE key = $1 AND\n user_id = (SELECT ID from mcaptcha_users WHERE name = $3)\n ) AND difficulty_factor = ($2);",
|
||||
"describe": {
|
||||
|
|
|
@ -22,7 +22,10 @@ use super::auth::Password;
|
|||
use crate::errors::*;
|
||||
use crate::Data;
|
||||
|
||||
#[my_codegen::post(path="crate::V1_API_ROUTES.account.delete", wrap="crate::CheckLogin")]
|
||||
#[my_codegen::post(
|
||||
path = "crate::V1_API_ROUTES.account.delete",
|
||||
wrap = "crate::CheckLogin"
|
||||
)]
|
||||
async fn delete_account(
|
||||
id: Identity,
|
||||
payload: web::Json<Password>,
|
||||
|
@ -61,13 +64,13 @@ async fn delete_account(
|
|||
|
||||
pub fn services(cfg: &mut actix_web::web::ServiceConfig) {
|
||||
cfg.service(delete_account);
|
||||
// use crate::define_resource;
|
||||
// use crate::V1_API_ROUTES;
|
||||
//
|
||||
// define_resource!(
|
||||
// cfg,
|
||||
// V1_API_ROUTES.account.delete,
|
||||
// Methods::ProtectPost,
|
||||
// delete_account
|
||||
// );
|
||||
// use crate::define_resource;
|
||||
// use crate::V1_API_ROUTES;
|
||||
//
|
||||
// define_resource!(
|
||||
// cfg,
|
||||
// V1_API_ROUTES.account.delete,
|
||||
// Methods::ProtectPost,
|
||||
// delete_account
|
||||
// );
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ pub struct Email {
|
|||
pub email: String,
|
||||
}
|
||||
|
||||
#[my_codegen::post(path="crate::V1_API_ROUTES.account.email_exists")]
|
||||
#[my_codegen::post(path = "crate::V1_API_ROUTES.account.email_exists")]
|
||||
pub async fn email_exists(
|
||||
payload: web::Json<AccountCheckPayload>,
|
||||
data: web::Data<Data>,
|
||||
|
@ -53,7 +53,10 @@ pub async fn email_exists(
|
|||
}
|
||||
|
||||
/// update email
|
||||
#[my_codegen::post(path="crate::V1_API_ROUTES.account.update_email", wrap="crate::CheckLogin")]
|
||||
#[my_codegen::post(
|
||||
path = "crate::V1_API_ROUTES.account.update_email",
|
||||
wrap = "crate::CheckLogin"
|
||||
)]
|
||||
async fn set_email(
|
||||
id: Identity,
|
||||
payload: web::Json<Email>,
|
||||
|
@ -88,20 +91,20 @@ async fn set_email(
|
|||
pub fn services(cfg: &mut actix_web::web::ServiceConfig) {
|
||||
cfg.service(email_exists);
|
||||
cfg.service(set_email);
|
||||
// use crate::define_resource;
|
||||
// use crate::V1_API_ROUTES;
|
||||
//
|
||||
// define_resource!(
|
||||
// cfg,
|
||||
// V1_API_ROUTES.account.email_exists,
|
||||
// Methods::Post,
|
||||
// email_exists
|
||||
// );
|
||||
//
|
||||
// define_resource!(
|
||||
// cfg,
|
||||
// V1_API_ROUTES.account.update_email,
|
||||
// Methods::Post,
|
||||
// set_email
|
||||
// );
|
||||
// use crate::define_resource;
|
||||
// use crate::V1_API_ROUTES;
|
||||
//
|
||||
// define_resource!(
|
||||
// cfg,
|
||||
// V1_API_ROUTES.account.email_exists,
|
||||
// Methods::Post,
|
||||
// email_exists
|
||||
// );
|
||||
//
|
||||
// define_resource!(
|
||||
// cfg,
|
||||
// V1_API_ROUTES.account.update_email,
|
||||
// Methods::Post,
|
||||
// set_email
|
||||
// );
|
||||
}
|
||||
|
|
|
@ -29,7 +29,10 @@ pub struct Secret {
|
|||
pub secret: String,
|
||||
}
|
||||
|
||||
#[my_codegen::get(path="crate::V1_API_ROUTES.account.get_secret", wrap="crate::CheckLogin")]
|
||||
#[my_codegen::get(
|
||||
path = "crate::V1_API_ROUTES.account.get_secret",
|
||||
wrap = "crate::CheckLogin"
|
||||
)]
|
||||
async fn get_secret(
|
||||
id: Identity,
|
||||
data: web::Data<Data>,
|
||||
|
@ -47,7 +50,10 @@ async fn get_secret(
|
|||
Ok(HttpResponse::Ok().json(secret))
|
||||
}
|
||||
|
||||
#[my_codegen::post(path="crate::V1_API_ROUTES.account.update_secret", wrap="crate::CheckLogin")]
|
||||
#[my_codegen::post(
|
||||
path = "crate::V1_API_ROUTES.account.update_secret",
|
||||
wrap = "crate::CheckLogin"
|
||||
)]
|
||||
async fn update_user_secret(
|
||||
id: Identity,
|
||||
data: web::Data<Data>,
|
||||
|
|
|
@ -20,7 +20,7 @@ use super::{AccountCheckPayload, AccountCheckResp};
|
|||
use crate::errors::*;
|
||||
use crate::Data;
|
||||
|
||||
#[my_codegen::post(path="crate::V1_API_ROUTES.account.username_exists")]
|
||||
#[my_codegen::post(path = "crate::V1_API_ROUTES.account.username_exists")]
|
||||
async fn username_exists(
|
||||
payload: web::Json<AccountCheckPayload>,
|
||||
data: web::Data<Data>,
|
||||
|
|
|
@ -48,16 +48,15 @@ pub mod routes {
|
|||
}
|
||||
|
||||
pub fn services(cfg: &mut web::ServiceConfig) {
|
||||
|
||||
// protect_get!(cfg, V1_API_ROUTES.auth.logout, signout);
|
||||
// protect_get!(cfg, V1_API_ROUTES.auth.logout, signout);
|
||||
|
||||
cfg.service(signup);
|
||||
cfg.service(signin);
|
||||
cfg.service(signout);
|
||||
|
||||
// define_resource!(cfg, V1_API_ROUTES.auth.register, Methods::Post, signup);
|
||||
// define_resource!(cfg, V1_API_ROUTES.auth.logout, Methods::ProtectGet, signout);
|
||||
// define_resource!(cfg, V1_API_ROUTES.auth.login, Methods::Post, signin);
|
||||
// define_resource!(cfg, V1_API_ROUTES.auth.register, Methods::Post, signup);
|
||||
// define_resource!(cfg, V1_API_ROUTES.auth.logout, Methods::ProtectGet, signout);
|
||||
// define_resource!(cfg, V1_API_ROUTES.auth.login, Methods::Post, signin);
|
||||
//post!(cfg, V1_API_ROUTES.auth.login, signin);
|
||||
}
|
||||
|
||||
|
@ -80,7 +79,7 @@ pub struct Password {
|
|||
pub password: String,
|
||||
}
|
||||
|
||||
#[my_codegen::post(path="crate::V1_API_ROUTES.auth.register")]
|
||||
#[my_codegen::post(path = "crate::V1_API_ROUTES.auth.register")]
|
||||
async fn signup(
|
||||
payload: web::Json<Register>,
|
||||
data: web::Data<Data>,
|
||||
|
@ -126,7 +125,8 @@ async fn signup(
|
|||
.execute(&data.db)
|
||||
.await;
|
||||
}
|
||||
if res.is_ok() { break;
|
||||
if res.is_ok() {
|
||||
break;
|
||||
} else {
|
||||
if let Err(sqlx::Error::Database(err)) = res {
|
||||
if err.code() == Some(Cow::from("23505")) {
|
||||
|
@ -147,7 +147,7 @@ async fn signup(
|
|||
Ok(HttpResponse::Ok())
|
||||
}
|
||||
|
||||
#[my_codegen::post(path="crate::V1_API_ROUTES.auth.login")]
|
||||
#[my_codegen::post(path = "crate::V1_API_ROUTES.auth.login")]
|
||||
async fn signin(
|
||||
id: Identity,
|
||||
payload: web::Json<Login>,
|
||||
|
@ -179,7 +179,7 @@ async fn signin(
|
|||
}
|
||||
}
|
||||
|
||||
#[my_codegen::get(path="crate::V1_API_ROUTES.auth.logout", wrap="crate::CheckLogin")]
|
||||
#[my_codegen::get(path = "crate::V1_API_ROUTES.auth.logout", wrap = "crate::CheckLogin")]
|
||||
async fn signout(id: Identity) -> impl Responder {
|
||||
if let Some(_) = id.identity() {
|
||||
id.forget();
|
||||
|
|
|
@ -44,7 +44,10 @@ pub struct UpdateDuration {
|
|||
pub duration: i32,
|
||||
}
|
||||
|
||||
#[my_codegen::post(path="crate::V1_API_ROUTES.duration.update", wrap="crate::CheckLogin")]
|
||||
#[my_codegen::post(
|
||||
path = "crate::V1_API_ROUTES.duration.update",
|
||||
wrap = "crate::CheckLogin"
|
||||
)]
|
||||
async fn update_duration(
|
||||
payload: web::Json<UpdateDuration>,
|
||||
data: web::Data<Data>,
|
||||
|
@ -82,7 +85,10 @@ pub struct GetDuration {
|
|||
pub token: String,
|
||||
}
|
||||
|
||||
#[my_codegen::post(path="crate::V1_API_ROUTES.duration.get", wrap="crate::CheckLogin")]
|
||||
#[my_codegen::post(
|
||||
path = "crate::V1_API_ROUTES.duration.get",
|
||||
wrap = "crate::CheckLogin"
|
||||
)]
|
||||
async fn get_duration(
|
||||
payload: web::Json<MCaptchaDetails>,
|
||||
data: web::Data<Data>,
|
||||
|
@ -105,21 +111,21 @@ async fn get_duration(
|
|||
pub fn services(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(get_duration);
|
||||
cfg.service(update_duration);
|
||||
// use crate::define_resource;
|
||||
// use crate::V1_API_ROUTES;
|
||||
//
|
||||
// define_resource!(
|
||||
// cfg,
|
||||
// V1_API_ROUTES.duration.get,
|
||||
// Methods::ProtectPost,
|
||||
// get_duration
|
||||
// );
|
||||
// define_resource!(
|
||||
// cfg,
|
||||
// V1_API_ROUTES.duration.update,
|
||||
// Methods::ProtectPost,
|
||||
// update_duration
|
||||
// );
|
||||
// use crate::define_resource;
|
||||
// use crate::V1_API_ROUTES;
|
||||
//
|
||||
// define_resource!(
|
||||
// cfg,
|
||||
// V1_API_ROUTES.duration.get,
|
||||
// Methods::ProtectPost,
|
||||
// get_duration
|
||||
// );
|
||||
// define_resource!(
|
||||
// cfg,
|
||||
// V1_API_ROUTES.duration.update,
|
||||
// Methods::ProtectPost,
|
||||
// update_duration
|
||||
// );
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -67,7 +67,7 @@ pub fn services(cfg: &mut web::ServiceConfig) {
|
|||
|
||||
// TODO redo mcaptcha table to include levels as json field
|
||||
// so that the whole thing can be added/udpaed in a single stroke
|
||||
#[my_codegen::post(path="crate::V1_API_ROUTES.levels.add", wrap="crate::CheckLogin")]
|
||||
#[my_codegen::post(path = "crate::V1_API_ROUTES.levels.add", wrap = "crate::CheckLogin")]
|
||||
async fn add_levels(
|
||||
payload: web::Json<AddLevels>,
|
||||
data: web::Data<Data>,
|
||||
|
@ -120,7 +120,10 @@ pub struct UpdateLevels {
|
|||
pub key: String,
|
||||
}
|
||||
|
||||
#[my_codegen::post(path="crate::V1_API_ROUTES.levels.update", wrap="crate::CheckLogin")]
|
||||
#[my_codegen::post(
|
||||
path = "crate::V1_API_ROUTES.levels.update",
|
||||
wrap = "crate::CheckLogin"
|
||||
)]
|
||||
async fn update_levels(
|
||||
payload: web::Json<UpdateLevels>,
|
||||
data: web::Data<Data>,
|
||||
|
@ -178,7 +181,10 @@ async fn update_levels(
|
|||
Ok(HttpResponse::Ok())
|
||||
}
|
||||
|
||||
#[my_codegen::post(path="crate::V1_API_ROUTES.levels.delete", wrap="crate::CheckLogin")]
|
||||
#[my_codegen::post(
|
||||
path = "crate::V1_API_ROUTES.levels.delete",
|
||||
wrap = "crate::CheckLogin"
|
||||
)]
|
||||
async fn delete_levels(
|
||||
payload: web::Json<UpdateLevels>,
|
||||
data: web::Data<Data>,
|
||||
|
@ -205,7 +211,7 @@ async fn delete_levels(
|
|||
Ok(HttpResponse::Ok())
|
||||
}
|
||||
|
||||
#[my_codegen::post(path="crate::V1_API_ROUTES.levels.get", wrap="crate::CheckLogin")]
|
||||
#[my_codegen::post(path = "crate::V1_API_ROUTES.levels.get", wrap = "crate::CheckLogin")]
|
||||
async fn get_levels(
|
||||
payload: web::Json<MCaptchaDetails>,
|
||||
data: web::Data<Data>,
|
||||
|
|
|
@ -111,7 +111,10 @@ pub async fn add_mcaptcha_util(
|
|||
Ok(resp)
|
||||
}
|
||||
|
||||
#[my_codegen::post(path="crate::V1_API_ROUTES.mcaptcha.update_key", wrap="crate::CheckLogin")]
|
||||
#[my_codegen::post(
|
||||
path = "crate::V1_API_ROUTES.mcaptcha.update_key",
|
||||
wrap = "crate::CheckLogin"
|
||||
)]
|
||||
async fn update_token(
|
||||
payload: web::Json<MCaptchaDetails>,
|
||||
data: web::Data<Data>,
|
||||
|
@ -162,7 +165,10 @@ async fn update_token_helper(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[my_codegen::post(path="crate::V1_API_ROUTES.mcaptcha.get_token", wrap="crate::CheckLogin")]
|
||||
#[my_codegen::post(
|
||||
path = "crate::V1_API_ROUTES.mcaptcha.get_token",
|
||||
wrap = "crate::CheckLogin"
|
||||
)]
|
||||
async fn get_token(
|
||||
payload: web::Json<MCaptchaDetails>,
|
||||
data: web::Data<Data>,
|
||||
|
@ -190,7 +196,10 @@ async fn get_token(
|
|||
Ok(HttpResponse::Ok().json(res))
|
||||
}
|
||||
|
||||
#[my_codegen::post(path="crate::V1_API_ROUTES.mcaptcha.delete", wrap="crate::CheckLogin")]
|
||||
#[my_codegen::post(
|
||||
path = "crate::V1_API_ROUTES.mcaptcha.delete",
|
||||
wrap = "crate::CheckLogin"
|
||||
)]
|
||||
async fn delete_mcaptcha(
|
||||
payload: web::Json<MCaptchaDetails>,
|
||||
data: web::Data<Data>,
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
pub mod duration;
|
||||
pub mod levels;
|
||||
pub mod mcaptcha;
|
||||
pub mod stats;
|
||||
|
||||
pub fn get_random(len: usize) -> String {
|
||||
use std::iter;
|
||||
|
|
|
@ -45,7 +45,7 @@ pub mod routes {
|
|||
}
|
||||
|
||||
/// emmits build details of the bninary
|
||||
#[my_codegen::get(path="crate::V1_API_ROUTES.meta.build_details")]
|
||||
#[my_codegen::get(path = "crate::V1_API_ROUTES.meta.build_details")]
|
||||
async fn build_details() -> impl Responder {
|
||||
let build = BuildDetails {
|
||||
version: VERSION,
|
||||
|
@ -61,7 +61,7 @@ pub struct Health {
|
|||
}
|
||||
|
||||
/// checks all components of the system
|
||||
#[my_codegen::get(path="crate::V1_API_ROUTES.meta.health")]
|
||||
#[my_codegen::get(path = "crate::V1_API_ROUTES.meta.health")]
|
||||
async fn health(data: web::Data<Data>) -> impl Responder {
|
||||
use sqlx::Connection;
|
||||
|
||||
|
|
|
@ -30,7 +30,10 @@ pub struct AddNotification {
|
|||
}
|
||||
|
||||
/// route handler that adds a notification message
|
||||
#[my_codegen::post(path="crate::V1_API_ROUTES.notifications.add", wrap="crate::CheckLogin")]
|
||||
#[my_codegen::post(
|
||||
path = "crate::V1_API_ROUTES.notifications.add",
|
||||
wrap = "crate::CheckLogin"
|
||||
)]
|
||||
pub async fn add_notification(
|
||||
payload: web::Json<AddNotification>,
|
||||
data: web::Data<Data>,
|
||||
|
|
|
@ -52,7 +52,10 @@ impl From<Notification> for NotificationResp {
|
|||
}
|
||||
}
|
||||
/// route handler that gets all unread notifications
|
||||
#[my_codegen::get(path="crate::V1_API_ROUTES.notifications.get", wrap="crate::CheckLogin")]
|
||||
#[my_codegen::get(
|
||||
path = "crate::V1_API_ROUTES.notifications.get",
|
||||
wrap = "crate::CheckLogin"
|
||||
)]
|
||||
pub async fn get_notification(
|
||||
data: web::Data<Data>,
|
||||
id: Identity,
|
||||
|
|
|
@ -37,7 +37,10 @@ pub struct NotificationResp {
|
|||
}
|
||||
|
||||
/// route handler that marks a notification read
|
||||
#[my_codegen::post(path="crate::V1_API_ROUTES.notifications.mark_read", wrap="crate::CheckLogin")]
|
||||
#[my_codegen::post(
|
||||
path = "crate::V1_API_ROUTES.notifications.mark_read",
|
||||
wrap = "crate::CheckLogin"
|
||||
)]
|
||||
pub async fn mark_read(
|
||||
data: web::Data<Data>,
|
||||
payload: web::Json<MarkReadReq>,
|
||||
|
@ -68,7 +71,6 @@ mod tests {
|
|||
use crate::tests::*;
|
||||
use crate::*;
|
||||
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn notification_mark_read_works() {
|
||||
const NAME1: &str = "notifuser122";
|
||||
|
@ -125,7 +127,6 @@ mod tests {
|
|||
assert_eq!(notification.message, MESSAGE);
|
||||
assert_eq!(notification.heading, HEADING);
|
||||
|
||||
|
||||
let mark_read_payload = MarkReadReq {
|
||||
id: notification.id.clone(),
|
||||
};
|
||||
|
@ -138,7 +139,6 @@ mod tests {
|
|||
.await;
|
||||
assert_eq!(mark_read_resp.status(), StatusCode::OK);
|
||||
|
||||
|
||||
let get_notifications_resp = test::call_service(
|
||||
&mut app,
|
||||
test::TestRequest::get()
|
||||
|
@ -151,6 +151,5 @@ mod tests {
|
|||
let mut notifications: Vec<NotificationResp> =
|
||||
test::read_body_json(get_notifications_resp).await;
|
||||
assert!(notifications.pop().is_none());
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,10 +22,10 @@ use m_captcha::{
|
|||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::record_fetch;
|
||||
use super::GetDurationResp;
|
||||
use super::I32Levels;
|
||||
use crate::errors::*;
|
||||
use crate::stats::record::record_fetch;
|
||||
use crate::Data;
|
||||
use crate::V1_API_ROUTES;
|
||||
|
||||
|
@ -43,7 +43,9 @@ pub struct GetConfigPayload {
|
|||
// API keys are mcaptcha actor names
|
||||
|
||||
/// get PoW configuration for an mcaptcha key
|
||||
#[my_codegen::post(path = "V1_API_ROUTES.pow.get_config.strip_prefix(V1_API_ROUTES.pow.scope).unwrap()")]
|
||||
#[my_codegen::post(
|
||||
path = "V1_API_ROUTES.pow.get_config.strip_prefix(V1_API_ROUTES.pow.scope).unwrap()"
|
||||
)]
|
||||
pub async fn get_config(
|
||||
payload: web::Json<GetConfigPayload>,
|
||||
data: web::Data<Data>,
|
||||
|
@ -82,7 +84,6 @@ pub async fn get_config(
|
|||
Some(false) => Err(ServiceError::TokenNotFound),
|
||||
None => Err(ServiceError::TokenNotFound),
|
||||
}
|
||||
|
||||
}
|
||||
/// Call this when [MCaptcha][m_captcha::MCaptcha] is not in master.
|
||||
///
|
||||
|
|
|
@ -24,7 +24,6 @@ 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) {
|
||||
let cors = actix_cors::Cors::default()
|
||||
|
|
|
@ -20,8 +20,8 @@ use actix_web::{web, HttpResponse, Responder};
|
|||
use m_captcha::pow::Work;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::record_solve;
|
||||
use crate::errors::*;
|
||||
use crate::stats::record::record_solve;
|
||||
use crate::Data;
|
||||
use crate::V1_API_ROUTES;
|
||||
|
||||
|
@ -36,7 +36,9 @@ pub struct ValidationToken {
|
|||
|
||||
/// route handler that verifies PoW and issues a solution token
|
||||
/// if verification is successful
|
||||
#[my_codegen::post(path = "V1_API_ROUTES.pow.verify_pow.strip_prefix(V1_API_ROUTES.pow.scope).unwrap()")]
|
||||
#[my_codegen::post(
|
||||
path = "V1_API_ROUTES.pow.verify_pow.strip_prefix(V1_API_ROUTES.pow.scope).unwrap()"
|
||||
)]
|
||||
pub async fn verify_pow(
|
||||
payload: web::Json<Work>,
|
||||
data: web::Data<Data>,
|
||||
|
|
|
@ -20,8 +20,8 @@ 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::stats::record::record_confirm;
|
||||
use crate::Data;
|
||||
use crate::V1_API_ROUTES;
|
||||
|
||||
|
@ -33,7 +33,9 @@ pub struct CaptchaValidateResp {
|
|||
// API keys are mcaptcha actor names
|
||||
|
||||
/// route hander that validates a PoW solution token
|
||||
#[my_codegen::post(path = "V1_API_ROUTES.pow.validate_captcha_token.strip_prefix(V1_API_ROUTES.pow.scope).unwrap()")]
|
||||
#[my_codegen::post(
|
||||
path = "V1_API_ROUTES.pow.validate_captcha_token.strip_prefix(V1_API_ROUTES.pow.scope).unwrap()"
|
||||
)]
|
||||
pub async fn validate_captcha_token(
|
||||
payload: web::Json<VerifyCaptchaResult>,
|
||||
data: web::Data<Data>,
|
||||
|
|
|
@ -194,6 +194,9 @@ pub type ServiceResult<V> = std::result::Result<V, ServiceError>;
|
|||
pub enum PageError {
|
||||
#[display(fmt = "Something weng wrong: Internal server error")]
|
||||
InternalServerError,
|
||||
|
||||
#[display(fmt = "{}", _0)]
|
||||
ServiceError(ServiceError),
|
||||
}
|
||||
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
|
@ -204,6 +207,14 @@ impl From<sqlx::Error> for PageError {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
impl From<ServiceError> for PageError {
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
fn from(e: ServiceError) -> Self {
|
||||
PageError::ServiceError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl ResponseError for PageError {
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
use crate::PAGES;
|
||||
|
@ -223,6 +234,7 @@ impl ResponseError for PageError {
|
|||
fn status_code(&self) -> StatusCode {
|
||||
match self {
|
||||
PageError::InternalServerError => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
PageError::ServiceError(e) => e.status_code(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,15 +28,16 @@ mod api;
|
|||
mod data;
|
||||
mod docs;
|
||||
mod errors;
|
||||
mod middleware;
|
||||
mod pages;
|
||||
mod settings;
|
||||
mod static_assets;
|
||||
#[macro_use]
|
||||
mod routes;
|
||||
mod settings;
|
||||
mod static_assets;
|
||||
mod stats;
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
mod tests;
|
||||
mod middleware;
|
||||
|
||||
pub use api::v1::ROUTES as V1_API_ROUTES;
|
||||
pub use data::Data;
|
||||
|
|
|
@ -15,11 +15,11 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use crate::PAGES;
|
||||
use actix_web::{HttpResponse, Responder};
|
||||
use lazy_static::lazy_static;
|
||||
use sailfish::TemplateOnce;
|
||||
use my_codegen::get;
|
||||
use crate::PAGES;
|
||||
use sailfish::TemplateOnce;
|
||||
|
||||
#[derive(Clone, TemplateOnce)]
|
||||
#[template(path = "auth/login/index.html")]
|
||||
|
@ -37,7 +37,7 @@ lazy_static! {
|
|||
static ref INDEX: String = IndexPage::default().render_once().unwrap();
|
||||
}
|
||||
|
||||
#[get(path="PAGES.auth.login")]
|
||||
#[get(path = "PAGES.auth.login")]
|
||||
pub async fn login() -> impl Responder {
|
||||
HttpResponse::Ok()
|
||||
.content_type("text/html; charset=utf-8")
|
||||
|
|
|
@ -11,9 +11,7 @@
|
|||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
* You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
use actix_web::{web, HttpResponse, Responder};
|
||||
use lazy_static::lazy_static;
|
||||
|
|
|
@ -17,9 +17,11 @@
|
|||
|
||||
use actix_identity::Identity;
|
||||
use actix_web::{web, HttpResponse, Responder};
|
||||
use futures::{future::TryFutureExt, try_join};
|
||||
use sailfish::TemplateOnce;
|
||||
|
||||
use crate::errors::*;
|
||||
use crate::stats::fetch::Stats;
|
||||
use crate::Data;
|
||||
|
||||
const PAGE: &str = "SiteKeys";
|
||||
|
@ -75,13 +77,19 @@ pub async fn view_sitekey(
|
|||
.fetch_one(&data.db)
|
||||
.await?;
|
||||
|
||||
let levels = sqlx::query_as!(
|
||||
let levels_fut = sqlx::query_as!(
|
||||
Level,
|
||||
"SELECT difficulty_factor, visitor_threshold from mcaptcha_levels WHERE config_id = $1",
|
||||
"SELECT
|
||||
difficulty_factor, visitor_threshold
|
||||
FROM
|
||||
mcaptcha_levels
|
||||
WHERE config_id = $1",
|
||||
&config.config_id
|
||||
)
|
||||
.fetch_all(&data.db)
|
||||
.await?;
|
||||
.err_into();
|
||||
|
||||
let (stats, levels) = try_join!(Stats::new(&key, &data.db), levels_fut)?;
|
||||
|
||||
let body = IndexPage::new(config, levels).render_once().unwrap();
|
||||
Ok(HttpResponse::Ok()
|
||||
|
|
152
src/stats/fetch.rs
Normal file
152
src/stats/fetch.rs
Normal file
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::PgPool;
|
||||
|
||||
use crate::errors::*;
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct Stats {
|
||||
pub config_fetches: Vec<i64>,
|
||||
pub solves: Vec<i64>,
|
||||
pub confirms: Vec<i64>,
|
||||
}
|
||||
|
||||
impl Stats {
|
||||
pub async fn new(key: &str, db: &PgPool) -> ServiceResult<Self> {
|
||||
let config_fetches_fut = Self::fetch_config_fetched(key, db);
|
||||
let solves_fut = Self::fetch_solve(key, db);
|
||||
let confirms_fut = Self::fetch_confirm(key, db);
|
||||
|
||||
let (config_fetches, solves, confirms) =
|
||||
futures::try_join!(config_fetches_fut, solves_fut, confirms_fut)?;
|
||||
|
||||
let res = Self {
|
||||
config_fetches,
|
||||
solves,
|
||||
confirms,
|
||||
};
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// featch PoWConfig fetches
|
||||
#[inline]
|
||||
pub async fn fetch_config_fetched(
|
||||
key: &str,
|
||||
db: &PgPool,
|
||||
) -> ServiceResult<Vec<i64>> {
|
||||
let records = sqlx::query!(
|
||||
"SELECT fetched_at FROM mcaptcha_pow_fetched_stats WHERE config_id =
|
||||
(SELECT config_id FROM mcaptcha_config where key = $1)",
|
||||
&key,
|
||||
)
|
||||
.fetch_all(db)
|
||||
.await?;
|
||||
|
||||
let mut res: Vec<i64> = Vec::with_capacity(records.len());
|
||||
|
||||
records
|
||||
.iter()
|
||||
.for_each(|record| res.push(record.fetched_at.unix_timestamp()));
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// featch PoWConfig solves
|
||||
#[inline]
|
||||
pub async fn fetch_solve(key: &str, db: &PgPool) -> ServiceResult<Vec<i64>> {
|
||||
// "SELECT solved_at FROM mcaptcha_pow_solved_stats WHERE config_id =
|
||||
// (SELECT config_id FROM mcaptcha_config where key = $1)"
|
||||
let records = sqlx::query!(
|
||||
"SELECT solved_at FROM mcaptcha_pow_solved_stats WHERE config_id =
|
||||
(SELECT config_id FROM mcaptcha_config where key = $1)",
|
||||
&key,
|
||||
)
|
||||
.fetch_all(db)
|
||||
.await?;
|
||||
|
||||
let mut res: Vec<i64> = Vec::with_capacity(records.len());
|
||||
|
||||
records
|
||||
.iter()
|
||||
.for_each(|record| res.push(record.solved_at.unix_timestamp()));
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// featch PoWConfig confirms
|
||||
#[inline]
|
||||
pub async fn fetch_confirm(key: &str, db: &PgPool) -> ServiceResult<Vec<i64>> {
|
||||
let records = sqlx::query!(
|
||||
"SELECT confirmed_at FROM mcaptcha_pow_confirmed_stats WHERE config_id = (
|
||||
SELECT config_id FROM mcaptcha_config where key = $1)",
|
||||
&key
|
||||
)
|
||||
.fetch_all(db)
|
||||
.await?;
|
||||
|
||||
let mut res: Vec<i64> = Vec::with_capacity(records.len());
|
||||
|
||||
records
|
||||
.iter()
|
||||
.for_each(|record| res.push(record.confirmed_at.unix_timestamp()));
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::stats::record::*;
|
||||
use crate::tests::*;
|
||||
use crate::*;
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn stats_works() {
|
||||
const NAME: &str = "statsuser";
|
||||
const PASSWORD: &str = "testingpas";
|
||||
const EMAIL: &str = "statsuser@a.com";
|
||||
|
||||
let data = Data::new().await;
|
||||
delete_user(NAME, &data).await;
|
||||
|
||||
register_and_signin(NAME, EMAIL, PASSWORD).await;
|
||||
let (_, _, _, token_key) = add_levels_util(NAME, PASSWORD).await;
|
||||
let key = token_key.key.clone();
|
||||
|
||||
let stats = Stats::new(&key, &data.db).await.unwrap();
|
||||
|
||||
assert_eq!(stats.config_fetches.len(), 0);
|
||||
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 stats = Stats::new(&key, &data.db).await.unwrap();
|
||||
|
||||
assert_eq!(stats.config_fetches.len(), 1);
|
||||
assert_eq!(stats.solves.len(), 1);
|
||||
assert_eq!(stats.confirms.len(), 1);
|
||||
}
|
||||
}
|
19
src/stats/mod.rs
Normal file
19
src/stats/mod.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
pub mod fetch;
|
||||
pub mod record;
|
|
@ -31,7 +31,6 @@
|
|||
|
||||
li.help-text__instructions::marker {
|
||||
background-color: $violet;
|
||||
color: $light-text;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 50%;
|
||||
|
|
Loading…
Reference in a new issue