diff --git a/sqlx-data.json b/sqlx-data.json index d43ca5b8..3af2cf69 100644 --- a/sqlx-data.json +++ b/sqlx-data.json @@ -14,6 +14,19 @@ "nullable": [] } }, + "044e2036a518de2ccac9318ccba07f7ce10e4a1c1d51d0128ea5e8cb94358ac5": { + "query": "INSERT INTO mcaptcha_pow_confirmed_stats \n (config_id, time) VALUES ((SELECT config_id FROM mcaptcha_config WHERE key = $1), $2)", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Timestamptz" + ] + }, + "nullable": [] + } + }, "06699fda6b1542bf4544c0bdece91531a3020c24c9c76bcf967980e71ee25b42": { "query": "SELECT email, secret FROM mcaptcha_users WHERE name = ($1)", "describe": { @@ -40,38 +53,6 @@ ] } }, - "174457087e02e07bfe52618cc8b5525067fa1722684f37a30004074a499774ee": { - "query": "SELECT time FROM mcaptcha_pow_confirmed_stats WHERE config_id = (\n SELECT config_id FROM mcaptcha_config where key = $1)", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "time", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - false - ] - } - }, - "1be6274d5cc6d16f38285b8a62c9f66e8c3014cd403bc599598e911023bfeedb": { - "query": "INSERT INTO mcaptcha_pow_fetched_stats \n (config_id) VALUES ((SELECT config_id FROM mcaptcha_config WHERE key = $1))", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [] - } - }, "238569a64d7dbd252e3b27204f207e8a8548109717b89495ddf8f9a870c7c75d": { "query": "UPDATE mcaptcha_config SET name = $1, duration = $2 \n WHERE user_id = (SELECT ID FROM mcaptcha_users WHERE name = $3)\n AND key = $4", "describe": { @@ -168,6 +149,27 @@ ] } }, + "4303f5c6ef98e0de9d8d3c2d781d3ffaa3dee5f7d27db831d327b26f03ba9d68": { + "query": "SELECT time FROM mcaptcha_pow_confirmed_stats \n WHERE \n config_id = (\n SELECT config_id FROM mcaptcha_config \n WHERE \n key = $1\n AND\n user_id = (\n SELECT \n ID FROM mcaptcha_users WHERE name = $2))\n ORDER BY time DESC", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "time", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + false + ] + } + }, "45d9e9fb6344fe3a18c2529d50c935d3837bfe25c96595beb6970d6067720578": { "query": "insert into mcaptcha_users \n (name , password, email, secret) values ($1, $2, $3, $4)", "describe": { @@ -339,13 +341,14 @@ ] } }, - "73b9bddce90d59193430c5ec38b77ad7bc0e94bac74dcde7ab9949a86cfbddca": { - "query": "INSERT INTO mcaptcha_pow_solved_stats \n (config_id) VALUES ((SELECT config_id FROM mcaptcha_config WHERE key = $1))", + "726a794f7599b78ab749d9f887f5c28db38f072b41f691bde35d23ba0dd72409": { + "query": "INSERT INTO mcaptcha_pow_fetched_stats \n (config_id, time) VALUES ((SELECT config_id FROM mcaptcha_config WHERE key = $1), $2)", "describe": { "columns": [], "parameters": { "Left": [ - "Text" + "Text", + "Timestamptz" ] }, "nullable": [] @@ -404,8 +407,8 @@ "nullable": [] } }, - "85a3482fc65dab99cd01f51411f0567d303018bbb6483a0020db33180ce12cfb": { - "query": "SELECT time FROM mcaptcha_pow_solved_stats WHERE config_id = \n (SELECT config_id FROM mcaptcha_config where key = $1)", + "84484cb6892db29121816bc5bff5702b9e857e20aa14e79d080d78ae7593153b": { + "query": "SELECT time FROM mcaptcha_pow_solved_stats \n WHERE config_id = (\n SELECT config_id FROM mcaptcha_config \n WHERE \n key = $1\n AND\n user_id = (\n SELECT \n ID FROM mcaptcha_users WHERE name = $2)) \n ORDER BY time DESC", "describe": { "columns": [ { @@ -416,6 +419,7 @@ ], "parameters": { "Left": [ + "Text", "Text" ] }, @@ -450,18 +454,6 @@ ] } }, - "931879575bb70dece5596bfae18f55a628d10627e4b6825e54642b254ca4ee64": { - "query": "INSERT INTO mcaptcha_pow_confirmed_stats \n (config_id) VALUES ((SELECT config_id FROM mcaptcha_config WHERE key = $1))", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [] - } - }, "94901d49666b3097b1fed832966697c4a1e3937beb2bd0431df4857402a4de04": { "query": "INSERT INTO mcaptcha_levels (\n difficulty_factor, \n visitor_threshold,\n config_id) VALUES (\n $1, $2, (\n SELECT config_id FROM mcaptcha_config WHERE key = ($3) AND\n user_id = (\n SELECT ID from mcaptcha_users WHERE name = $4\n )\n ));", "describe": { @@ -595,6 +587,27 @@ ] } }, + "c399efd5db1284dcb470c40f9b076851f77498c75a63a3b151d4a111bd3e2957": { + "query": "SELECT time FROM mcaptcha_pow_fetched_stats\n WHERE \n config_id = (\n SELECT \n config_id FROM mcaptcha_config \n WHERE \n key = $1\n AND\n user_id = (\n SELECT \n ID FROM mcaptcha_users WHERE name = $2))\n ORDER BY time DESC", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "time", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + false + ] + } + }, "ca9d5241f1234d1825f7ead391ebe9099fca776e7101ac6e1761881606def5fa": { "query": "DELETE FROM mcaptcha_users WHERE name = ($1)", "describe": { @@ -620,6 +633,19 @@ "nullable": [] } }, + "dbe4307651d94bc6db4f1d8b2c6d076fde6280983d59593216d7765cbbdd669c": { + "query": "INSERT INTO mcaptcha_pow_solved_stats \n (config_id, time) VALUES ((SELECT config_id FROM mcaptcha_config WHERE key = $1), $2)", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Timestamptz" + ] + }, + "nullable": [] + } + }, "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": { @@ -711,25 +737,5 @@ }, "nullable": [] } - }, - "fc6b52054ea35458f597bcd3bd9a99029fca816f1e65a9e12cfd7ea4acc33838": { - "query": "SELECT time FROM mcaptcha_pow_fetched_stats WHERE config_id = \n (SELECT config_id FROM mcaptcha_config where key = $1)", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "time", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - false - ] - } } } \ No newline at end of file diff --git a/src/api/v1/mcaptcha/captcha.rs b/src/api/v1/mcaptcha/captcha.rs index 1bcb1136..d2853c68 100644 --- a/src/api/v1/mcaptcha/captcha.rs +++ b/src/api/v1/mcaptcha/captcha.rs @@ -257,8 +257,10 @@ pub struct StatsPayload { async fn get_stats( payload: web::Json, data: AppData, + id: Identity, ) -> ServiceResult { - let stats = Stats::new(&payload.key, &data.db).await?; + let username = id.identity().unwrap(); + let stats = Stats::new(&username, &payload.key, &data.db).await?; let stats = StatsUnixTimestamp::from_stats(&stats); Ok(HttpResponse::Ok().json(&stats)) } diff --git a/src/api/v1/mcaptcha/levels.rs b/src/api/v1/mcaptcha/levels.rs index 2fae07ca..2b74c2bf 100644 --- a/src/api/v1/mcaptcha/levels.rs +++ b/src/api/v1/mcaptcha/levels.rs @@ -139,7 +139,7 @@ async fn update_levels( defense.build()?; let mut futs = Vec::with_capacity(payload.levels.len() + 2); - let del_fut = sqlx::query!( + sqlx::query!( "DELETE FROM mcaptcha_levels WHERE config_id = ( SELECT config_id FROM mcaptcha_config where key = ($1) @@ -150,7 +150,8 @@ async fn update_levels( &payload.key, &username ) - .execute(&data.db); //.await?; + .execute(&data.db) + .await?; let update_fut = sqlx::query!( "UPDATE mcaptcha_config SET name = $1, duration = $2 @@ -163,7 +164,6 @@ async fn update_levels( ) .execute(&data.db); //.await?; - futs.push(del_fut); futs.push(update_fut); for level in payload.levels.iter() { @@ -291,9 +291,7 @@ mod tests { assert_eq!(res_levels, add_level.levels); // 3. update level - let levels = vec![L1, L2]; - let update_level = UpdateLevels { key: key.key.clone(), levels: levels.clone(), diff --git a/src/api/v1/pow/get_config.rs b/src/api/v1/pow/get_config.rs index 40902793..b5be3942 100644 --- a/src/api/v1/pow/get_config.rs +++ b/src/api/v1/pow/get_config.rs @@ -65,7 +65,10 @@ pub async fn get_config( match res.exists { Some(true) => { match data.captcha.get_pow(payload.key.clone()).await { - Some(config) => Ok(HttpResponse::Ok().json(config)), + Some(config) => { + record_fetch(&payload.key, &data.db).await; + Ok(HttpResponse::Ok().json(config)) + } None => { init_mcaptcha(&data, &payload.key).await?; let config = data diff --git a/src/date.rs b/src/date.rs index 0f1f200e..d48477e3 100644 --- a/src/date.rs +++ b/src/date.rs @@ -14,12 +14,23 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ +use std::fmt::Debug; + use sqlx::types::time::OffsetDateTime; +#[derive(Clone)] pub struct Date { pub time: OffsetDateTime, } +impl Debug for Date { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Date") + .field("time", &self.print_date()) + .finish() + } +} + pub const MINUTE: i64 = 60; pub const HOUR: i64 = MINUTE * 60; pub const DAY: i64 = HOUR * 24; @@ -45,9 +56,15 @@ impl Date { } } + /// print relative time from date pub fn print_date(&self) -> String { Self::format(&self.time) } + + /// print date + pub fn date(&self) -> String { + self.time.format("%F %r %z") + } } #[cfg(test)] diff --git a/src/pages/panel/mod.rs b/src/pages/panel/mod.rs index 61b0900c..a1679de8 100644 --- a/src/pages/panel/mod.rs +++ b/src/pages/panel/mod.rs @@ -66,7 +66,6 @@ pub mod routes { pub sitekey: Sitekey, pub notifications: &'static str, pub settings: Settings, - pub stats: &'static str, } impl Panel { @@ -76,11 +75,10 @@ pub mod routes { sitekey: Sitekey::new(), notifications: "/notifications", settings: Settings::new(), - stats: "/stats", } } - pub const fn get_sitemap() -> [&'static str; 6] { + pub const fn get_sitemap() -> [&'static str; 5] { const PANEL: Panel = Panel::new(); const S: [&str; 2] = Sitekey::get_sitemap(); @@ -90,7 +88,6 @@ pub mod routes { S[0], S[1], Settings::get_sitemap()[0], - PANEL.stats, ] } } diff --git a/src/pages/panel/notifications.rs b/src/pages/panel/notifications.rs index 413c4d69..672a6de2 100644 --- a/src/pages/panel/notifications.rs +++ b/src/pages/panel/notifications.rs @@ -1,19 +1,19 @@ /* -* Copyright (C) 2021 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) 2021 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 actix_identity::Identity; use actix_web::{HttpResponse, Responder}; diff --git a/src/pages/panel/sitekey/edit.rs b/src/pages/panel/sitekey/edit.rs index 64065b3c..034ce997 100644 --- a/src/pages/panel/sitekey/edit.rs +++ b/src/pages/panel/sitekey/edit.rs @@ -16,11 +16,9 @@ */ 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::AppData; const PAGE: &str = "SiteKeys"; @@ -79,7 +77,7 @@ pub async fn edit_sitekey( .fetch_one(&data.db) .await?; - let levels_fut = sqlx::query_as!( + let levels = sqlx::query_as!( Level, "SELECT difficulty_factor, visitor_threshold @@ -89,9 +87,7 @@ pub async fn edit_sitekey( &config.config_id ) .fetch_all(&data.db) - .err_into(); - - let (_stats, levels) = try_join!(Stats::new(&key, &data.db), levels_fut)?; + .await?; let body = IndexPage::new(config, levels, key).render_once().unwrap(); Ok(HttpResponse::Ok() diff --git a/src/pages/panel/sitekey/view.rs b/src/pages/panel/sitekey/view.rs index 789709b7..2dd4599c 100644 --- a/src/pages/panel/sitekey/view.rs +++ b/src/pages/panel/sitekey/view.rs @@ -45,15 +45,22 @@ struct IndexPage { name: String, key: String, levels: Vec, + stats: Stats, } impl IndexPage { - fn new(config: McaptchaConfig, levels: Vec, key: String) -> Self { + fn new( + stats: Stats, + config: McaptchaConfig, + levels: Vec, + key: String, + ) -> Self { IndexPage { duration: config.duration as u32, name: config.name, levels, key, + stats, } } } @@ -91,9 +98,11 @@ pub async fn view_sitekey( .fetch_all(&data.db) .err_into(); - let (_stats, levels) = try_join!(Stats::new(&key, &data.db), levels_fut)?; + let (stats, levels) = try_join!(Stats::new(&username, &key, &data.db), levels_fut)?; - let body = IndexPage::new(config, levels, key).render_once().unwrap(); + let body = IndexPage::new(stats, config, levels, key) + .render_once() + .unwrap(); Ok(HttpResponse::Ok() .content_type("text/html; charset=utf-8") .body(body)) diff --git a/src/stats/fetch.rs b/src/stats/fetch.rs index 2749c498..a714375a 100644 --- a/src/stats/fetch.rs +++ b/src/stats/fetch.rs @@ -14,13 +14,11 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -use actix_web::{web, HttpResponse, Responder}; use serde::{Deserialize, Serialize}; use sqlx::PgPool; use crate::date::Date; use crate::errors::*; -use crate::AppData; #[derive(Debug, Clone, Deserialize, Serialize)] pub struct StatsUnixTimestamp { @@ -29,6 +27,7 @@ pub struct StatsUnixTimestamp { pub confirms: Vec, } +#[derive(Debug, Clone)] pub struct Stats { pub config_fetches: Vec, pub solves: Vec, @@ -40,21 +39,11 @@ pub struct StatsPayload { pub key: String, } -#[my_codegen::post(path = "crate::V1_API_ROUTES.auth.login", wrap = "crate::CheckLogin")] -async fn get_stats( - payload: web::Json, - data: AppData, -) -> ServiceResult { - let stats = Stats::new(&payload.key, &data.db).await?; - let stats = StatsUnixTimestamp::from_stats(&stats); - Ok(HttpResponse::Ok().json(&stats)) -} - impl Stats { - pub async fn new(key: &str, db: &PgPool) -> ServiceResult { - let config_fetches_fut = runners::fetch_config_fetched(key, db); - let solves_fut = runners::fetch_solve(key, db); - let confirms_fut = runners::fetch_confirm(key, db); + pub async fn new(user: &str, key: &str, db: &PgPool) -> ServiceResult { + let config_fetches_fut = runners::fetch_config_fetched(user, key, db); + let solves_fut = runners::fetch_solve(user, key, db); + let confirms_fut = runners::fetch_confirm(user, key, db); let (config_fetches, solves, confirms) = futures::try_join!(config_fetches_fut, solves_fut, confirms_fut)?; @@ -99,14 +88,26 @@ pub mod runners { /// featch PoWConfig fetches #[inline] pub async fn fetch_config_fetched( + user: &str, key: &str, db: &PgPool, ) -> ServiceResult> { let records = sqlx::query_as!( Date, - "SELECT time FROM mcaptcha_pow_fetched_stats WHERE config_id = - (SELECT config_id FROM mcaptcha_config where key = $1)", + "SELECT time FROM mcaptcha_pow_fetched_stats + WHERE + config_id = ( + SELECT + config_id FROM mcaptcha_config + WHERE + key = $1 + AND + user_id = ( + SELECT + ID FROM mcaptcha_users WHERE name = $2)) + ORDER BY time DESC", &key, + &user, ) .fetch_all(db) .await?; @@ -116,12 +117,25 @@ pub mod runners { /// featch PoWConfig solves #[inline] - pub async fn fetch_solve(key: &str, db: &PgPool) -> ServiceResult> { + pub async fn fetch_solve( + user: &str, + key: &str, + db: &PgPool, + ) -> ServiceResult> { let records = sqlx::query_as!( Date, - "SELECT time FROM mcaptcha_pow_solved_stats WHERE config_id = - (SELECT config_id FROM mcaptcha_config where key = $1)", + "SELECT time FROM mcaptcha_pow_solved_stats + WHERE config_id = ( + SELECT config_id FROM mcaptcha_config + WHERE + key = $1 + AND + user_id = ( + SELECT + ID FROM mcaptcha_users WHERE name = $2)) + ORDER BY time DESC", &key, + &user ) .fetch_all(db) .await?; @@ -131,12 +145,26 @@ pub mod runners { /// featch PoWConfig confirms #[inline] - pub async fn fetch_confirm(key: &str, db: &PgPool) -> ServiceResult> { + pub async fn fetch_confirm( + user: &str, + key: &str, + db: &PgPool, + ) -> ServiceResult> { let records = sqlx::query_as!( Date, - "SELECT time FROM mcaptcha_pow_confirmed_stats WHERE config_id = ( - SELECT config_id FROM mcaptcha_config where key = $1)", - &key + "SELECT time FROM mcaptcha_pow_confirmed_stats + WHERE + config_id = ( + SELECT config_id FROM mcaptcha_config + WHERE + key = $1 + AND + user_id = ( + SELECT + ID FROM mcaptcha_users WHERE name = $2)) + ORDER BY time DESC", + &key, + &user ) .fetch_all(db) .await?; @@ -165,7 +193,7 @@ mod tests { let (_, _, _, token_key) = add_levels_util(NAME, PASSWORD).await; let key = token_key.key.clone(); - let stats = Stats::new(&key, &data.db).await.unwrap(); + let stats = Stats::new(&NAME, &key, &data.db).await.unwrap(); assert_eq!(stats.config_fetches.len(), 0); assert_eq!(stats.solves.len(), 0); @@ -177,7 +205,7 @@ mod tests { record_confirm(&key, &data.db) ); - let stats = Stats::new(&key, &data.db).await.unwrap(); + let stats = Stats::new(&NAME, &key, &data.db).await.unwrap(); assert_eq!(stats.config_fetches.len(), 1); assert_eq!(stats.solves.len(), 1); diff --git a/src/stats/mod.rs b/src/stats/mod.rs index 868df0b1..c7a2577f 100644 --- a/src/stats/mod.rs +++ b/src/stats/mod.rs @@ -1,19 +1,19 @@ /* -* Copyright (C) 2021 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) 2021 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 . + */ pub mod fetch; pub mod record; diff --git a/src/stats/record.rs b/src/stats/record.rs index 37664461..ab21ad90 100644 --- a/src/stats/record.rs +++ b/src/stats/record.rs @@ -15,15 +15,18 @@ * along with this program. If not, see . */ +use sqlx::types::time::OffsetDateTime; use sqlx::PgPool; /// record PoWConfig fetches #[inline] pub async fn record_fetch(key: &str, db: &PgPool) { + let now = OffsetDateTime::now_utc(); let _ = sqlx::query!( "INSERT INTO mcaptcha_pow_fetched_stats - (config_id) VALUES ((SELECT config_id FROM mcaptcha_config WHERE key = $1))", + (config_id, time) VALUES ((SELECT config_id FROM mcaptcha_config WHERE key = $1), $2)", &key, + &now, ) .execute(db) .await; @@ -32,10 +35,12 @@ pub async fn record_fetch(key: &str, db: &PgPool) { /// record PoWConfig solves #[inline] pub async fn record_solve(key: &str, db: &PgPool) { + let now = OffsetDateTime::now_utc(); let _ = sqlx::query!( "INSERT INTO mcaptcha_pow_solved_stats - (config_id) VALUES ((SELECT config_id FROM mcaptcha_config WHERE key = $1))", + (config_id, time) VALUES ((SELECT config_id FROM mcaptcha_config WHERE key = $1), $2)", &key, + &now, ) .execute(db) .await; @@ -44,10 +49,12 @@ pub async fn record_solve(key: &str, db: &PgPool) { /// record PoWConfig confirms #[inline] pub async fn record_confirm(key: &str, db: &PgPool) { + let now = OffsetDateTime::now_utc(); let _ = sqlx::query!( "INSERT INTO mcaptcha_pow_confirmed_stats - (config_id) VALUES ((SELECT config_id FROM mcaptcha_config WHERE key = $1))", + (config_id, time) VALUES ((SELECT config_id FROM mcaptcha_config WHERE key = $1), $2)", &key, + &now ) .execute(db) .await; diff --git a/templates/components/_inner-container.scss b/templates/components/_inner-container.scss index 8a87c1d7..60f9a6d3 100644 --- a/templates/components/_inner-container.scss +++ b/templates/components/_inner-container.scss @@ -21,4 +21,5 @@ max-width: 50%; margin: 50px auto; border-radius: 5px; + flex-direction: column; } diff --git a/templates/panel/css/main.scss b/templates/panel/css/main.scss index 4580d736..309616be 100644 --- a/templates/panel/css/main.scss +++ b/templates/panel/css/main.scss @@ -27,4 +27,5 @@ .inner-container { @include inner-container; + flex-direction: column; } diff --git a/templates/panel/navbar/index.html b/templates/panel/navbar/index.html index 507fd34b..45241fd5 100644 --- a/templates/panel/navbar/index.html +++ b/templates/panel/navbar/index.html @@ -30,14 +30,6 @@ -
  • - - <.= crate::BAR_CHART.1 .> -
    - Statistics -
    -
    -
  • <.= crate::SETTINGS_ICON.1 .> diff --git a/templates/panel/notifications/main.scss b/templates/panel/notifications/main.scss index d9c65ffb..f8b16997 100644 --- a/templates/panel/notifications/main.scss +++ b/templates/panel/notifications/main.scss @@ -20,7 +20,7 @@ .notification__table { @include table; - margin: auto; + margin: 20px auto; } .notification__title-text { diff --git a/templates/panel/sitekey/view/index.html b/templates/panel/sitekey/view/index.html index 66c1173b..8bac99db 100644 --- a/templates/panel/sitekey/view/index.html +++ b/templates/panel/sitekey/view/index.html @@ -4,4 +4,11 @@ <. for (count, level) in levels.iter().enumerate() { .> <. include!("./existing-level.html"); .> <. } .> -<. include!("./__form-bottom.html"); .> +<./* synchronise with "./__form-bottom.html" Lines below should break form */.> + + <. include!("./stats.html"); .> + + + + +<. include!("../../../components/footers.html"); .> diff --git a/templates/panel/sitekey/view/stats.html b/templates/panel/sitekey/view/stats.html new file mode 100644 index 00000000..e95a5f1c --- /dev/null +++ b/templates/panel/sitekey/view/stats.html @@ -0,0 +1,26 @@ +
    + <. let tables = [("Configuration Fetches", &stats.config_fetches), ("Proofs generated", &stats.solves), ("Grants Verified", &stats.confirms)]; .> + <. for table in tables.iter() { .> + + + + + + + + <. for (count, val) in table.1.iter().enumerate() { .> + + + + + <. } .> + +
    <.= table.0 .>
    +

    + <.= count + 1 .> +

    +
    +

    <.= val.date() .>

    +
    + <. }; .> +