mirror of
https://github.com/mCaptcha/mCaptcha.git
synced 2024-11-29 14:28:46 +03:00
feat: track maximum recorded nonce for captcha levels to render progress bar
This commit is contained in:
parent
49a8757ead
commit
b6497882d7
12 changed files with 356 additions and 0 deletions
|
@ -292,6 +292,21 @@ pub trait MCDatabase: std::marker::Send + std::marker::Sync + CloneSPDatabase {
|
||||||
|
|
||||||
/// Get all psuedo IDs
|
/// Get all psuedo IDs
|
||||||
async fn analytics_get_all_psuedo_ids(&self, page: usize) -> DBResult<Vec<String>>;
|
async fn analytics_get_all_psuedo_ids(&self, page: usize) -> DBResult<Vec<String>>;
|
||||||
|
|
||||||
|
/// Track maximum nonce received against captcha levels
|
||||||
|
async fn update_max_nonce_for_level(
|
||||||
|
&self,
|
||||||
|
captcha_key: &str,
|
||||||
|
difficulty_factor: u32,
|
||||||
|
latest_nonce: u32,
|
||||||
|
) -> DBResult<()>;
|
||||||
|
|
||||||
|
/// Get maximum nonce tracked so far for captcha levels
|
||||||
|
async fn get_max_nonce_for_level(
|
||||||
|
&self,
|
||||||
|
captcha_key: &str,
|
||||||
|
difficulty_factor: u32,
|
||||||
|
) -> DBResult<u32>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq)]
|
#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq)]
|
||||||
|
|
|
@ -310,6 +310,33 @@ pub async fn database_works<'a, T: MCDatabase>(
|
||||||
.unwrap();
|
.unwrap();
|
||||||
// analytics end
|
// analytics end
|
||||||
|
|
||||||
|
// nonce tracking start
|
||||||
|
assert_eq!(
|
||||||
|
db.get_max_nonce_for_level(c.key, l[0].difficulty_factor)
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
db.update_max_nonce_for_level(c.key, l[0].difficulty_factor, 1000)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
db.get_max_nonce_for_level(c.key, l[0].difficulty_factor)
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
1000
|
||||||
|
);
|
||||||
|
db.update_max_nonce_for_level(c.key, l[0].difficulty_factor, 10_000)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
db.get_max_nonce_for_level(c.key, l[0].difficulty_factor)
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
10_000
|
||||||
|
);
|
||||||
|
// nonce tracking end
|
||||||
|
|
||||||
assert_eq!(db.fetch_solve(p.username, c.key).await.unwrap().len(), 1);
|
assert_eq!(db.fetch_solve(p.username, c.key).await.unwrap().len(), 1);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
db.fetch_config_fetched(p.username, c.key)
|
db.fetch_config_fetched(p.username, c.key)
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"db_name": "MySQL",
|
||||||
|
"query": "INSERT INTO\n mcaptcha_track_nonce (level_id, nonce)\n VALUES ((\n SELECT\n level_id\n FROM\n mcaptcha_levels\n WHERE\n config_id = (SELECT config_id FROM mcaptcha_config WHERE captcha_key = ?)\n AND\n difficulty_factor = ?\n AND\n visitor_threshold = ?\n ), ?);",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Right": 4
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "216478d53870d7785cd0be43f030883ab79eaafb558d9197d09aea3adbd7b0bc"
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"db_name": "MySQL",
|
||||||
|
"query": "UPDATE mcaptcha_track_nonce SET nonce = ?\n WHERE level_id = (\n SELECT\n level_id\n FROM\n mcaptcha_levels\n WHERE\n config_id = (SELECT config_id FROM mcaptcha_config WHERE captcha_key = ?)\n AND\n difficulty_factor = ?\n )\n AND nonce <= ?;",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Right": 4
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "349ba17ff197aca7ee9fbd43e227d181c27ae04702fd6bdb6ddc32aab3bcb1ea"
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"db_name": "MySQL",
|
||||||
|
"query": "SELECT nonce FROM mcaptcha_track_nonce\n WHERE level_id = (\n SELECT\n level_id\n FROM\n mcaptcha_levels\n WHERE\n config_id = (SELECT config_id FROM mcaptcha_config WHERE captcha_key = ?)\n AND\n difficulty_factor = ?\n );",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "nonce",
|
||||||
|
"type_info": {
|
||||||
|
"type": "Long",
|
||||||
|
"flags": "NOT_NULL",
|
||||||
|
"char_set": 63,
|
||||||
|
"max_size": 11
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Right": 2
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "b739ec4cfab1ec60947106c8112e931510c3a50a1606facdde0c0ebb540d5beb"
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
-- Add migration script here
|
||||||
|
CREATE TABLE IF NOT EXISTS mcaptcha_track_nonce (
|
||||||
|
level_id INTEGER NOT NULL,
|
||||||
|
nonce INTEGER NOT NULL DEFAULT 0,
|
||||||
|
ID INT auto_increment,
|
||||||
|
PRIMARY KEY(ID),
|
||||||
|
CONSTRAINT `fk_mcaptcha_track_nonce_level_id`
|
||||||
|
FOREIGN KEY (level_id)
|
||||||
|
REFERENCES mcaptcha_levels (level_id)
|
||||||
|
ON DELETE CASCADE
|
||||||
|
ON UPDATE CASCADE
|
||||||
|
);
|
|
@ -433,6 +433,39 @@ impl MCDatabase for Database {
|
||||||
futs.push(fut);
|
futs.push(fut);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try_join_all(futs)
|
||||||
|
.await
|
||||||
|
.map_err(|e| map_row_not_found_err(e, DBError::CaptchaNotFound))?;
|
||||||
|
|
||||||
|
let mut futs = Vec::with_capacity(levels.len());
|
||||||
|
|
||||||
|
for level in levels.iter() {
|
||||||
|
let difficulty_factor = level.difficulty_factor as i32;
|
||||||
|
let visitor_threshold = level.visitor_threshold as i32;
|
||||||
|
let fut = sqlx::query!(
|
||||||
|
"INSERT INTO
|
||||||
|
mcaptcha_track_nonce (level_id, nonce)
|
||||||
|
VALUES ((
|
||||||
|
SELECT
|
||||||
|
level_id
|
||||||
|
FROM
|
||||||
|
mcaptcha_levels
|
||||||
|
WHERE
|
||||||
|
config_id = (SELECT config_id FROM mcaptcha_config WHERE captcha_key = ?)
|
||||||
|
AND
|
||||||
|
difficulty_factor = ?
|
||||||
|
AND
|
||||||
|
visitor_threshold = ?
|
||||||
|
), ?);",
|
||||||
|
&captcha_key,
|
||||||
|
difficulty_factor,
|
||||||
|
visitor_threshold,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.execute(&self.pool);
|
||||||
|
futs.push(fut);
|
||||||
|
}
|
||||||
|
|
||||||
try_join_all(futs)
|
try_join_all(futs)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| map_row_not_found_err(e, DBError::CaptchaNotFound))?;
|
.map_err(|e| map_row_not_found_err(e, DBError::CaptchaNotFound))?;
|
||||||
|
@ -1087,6 +1120,70 @@ impl MCDatabase for Database {
|
||||||
|
|
||||||
Ok(res.drain(0..).map(|r| r.psuedo_id).collect())
|
Ok(res.drain(0..).map(|r| r.psuedo_id).collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Track maximum nonce received against captcha levels
|
||||||
|
async fn update_max_nonce_for_level(
|
||||||
|
&self,
|
||||||
|
captcha_key: &str,
|
||||||
|
difficulty_factor: u32,
|
||||||
|
latest_nonce: u32,
|
||||||
|
) -> DBResult<()> {
|
||||||
|
let latest_nonce = latest_nonce as i64;
|
||||||
|
sqlx::query!(
|
||||||
|
"UPDATE mcaptcha_track_nonce SET nonce = ?
|
||||||
|
WHERE level_id = (
|
||||||
|
SELECT
|
||||||
|
level_id
|
||||||
|
FROM
|
||||||
|
mcaptcha_levels
|
||||||
|
WHERE
|
||||||
|
config_id = (SELECT config_id FROM mcaptcha_config WHERE captcha_key = ?)
|
||||||
|
AND
|
||||||
|
difficulty_factor = ?
|
||||||
|
)
|
||||||
|
AND nonce <= ?;",
|
||||||
|
latest_nonce,
|
||||||
|
&captcha_key,
|
||||||
|
difficulty_factor as i64,
|
||||||
|
latest_nonce
|
||||||
|
)
|
||||||
|
.execute(&self.pool).await
|
||||||
|
.map_err(|e| map_row_not_found_err(e, DBError::CaptchaNotFound))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get maximum nonce tracked so far for captcha levels
|
||||||
|
async fn get_max_nonce_for_level(
|
||||||
|
&self,
|
||||||
|
captcha_key: &str,
|
||||||
|
difficulty_factor: u32,
|
||||||
|
) -> DBResult<u32> {
|
||||||
|
struct X {
|
||||||
|
nonce: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = sqlx::query_as!(
|
||||||
|
X,
|
||||||
|
"SELECT nonce FROM mcaptcha_track_nonce
|
||||||
|
WHERE level_id = (
|
||||||
|
SELECT
|
||||||
|
level_id
|
||||||
|
FROM
|
||||||
|
mcaptcha_levels
|
||||||
|
WHERE
|
||||||
|
config_id = (SELECT config_id FROM mcaptcha_config WHERE captcha_key = ?)
|
||||||
|
AND
|
||||||
|
difficulty_factor = ?
|
||||||
|
);",
|
||||||
|
&captcha_key,
|
||||||
|
difficulty_factor as i32,
|
||||||
|
)
|
||||||
|
.fetch_one(&self.pool).await
|
||||||
|
.map_err(|e| map_row_not_found_err(e, DBError::CaptchaNotFound))?;
|
||||||
|
|
||||||
|
Ok(res.nonce as u32)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "INSERT INTO\n mcaptcha_track_nonce (level_id, nonce)\n VALUES ((\n SELECT\n level_id\n FROM\n mcaptcha_levels\n WHERE\n config_id = (SELECT config_id FROM mcaptcha_config WHERE key = ($1))\n AND\n difficulty_factor = $2\n AND\n visitor_threshold = $3\n ), $4);",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text",
|
||||||
|
"Int4",
|
||||||
|
"Int4",
|
||||||
|
"Int4"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "133ee23ab5ac7c664a86b6edfaa8da79281b6d1f5ba33c642a6ea1b0682fe0b0"
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "SELECT nonce FROM mcaptcha_track_nonce\n WHERE level_id = (\n SELECT\n level_id\n FROM\n mcaptcha_levels\n WHERE\n config_id = (SELECT config_id FROM mcaptcha_config WHERE key = ($1))\n AND\n difficulty_factor = $2\n );",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "nonce",
|
||||||
|
"type_info": "Int4"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text",
|
||||||
|
"Int4"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "96f1f1e45144d5add6c4ba4cd2df8eda6043bc8cd6952787f92a687fef778a6e"
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "UPDATE mcaptcha_track_nonce SET nonce = $3\n WHERE level_id = (\n SELECT\n level_id\n FROM\n mcaptcha_levels\n WHERE\n config_id = (SELECT config_id FROM mcaptcha_config WHERE key = ($1))\n AND\n difficulty_factor = $2\n )\n AND nonce <= $3;",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text",
|
||||||
|
"Int4",
|
||||||
|
"Int4"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "e33ee14cf76cd09d9a157b8784a3fe25b89eaca105aa30e479d31b756cd5c88b"
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
-- Add migration script here
|
||||||
|
CREATE TABLE IF NOT EXISTS mcaptcha_track_nonce (
|
||||||
|
nonce INTEGER NOT NULL DEFAULT 0,
|
||||||
|
level_id INTEGER references mcaptcha_levels(level_id) ON DELETE CASCADE,
|
||||||
|
ID SERIAL PRIMARY KEY NOT NULL
|
||||||
|
);
|
|
@ -445,6 +445,38 @@ impl MCDatabase for Database {
|
||||||
futs.push(fut);
|
futs.push(fut);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try_join_all(futs)
|
||||||
|
.await
|
||||||
|
.map_err(|e| map_row_not_found_err(e, DBError::CaptchaNotFound))?;
|
||||||
|
|
||||||
|
let mut futs = Vec::with_capacity(levels.len());
|
||||||
|
for level in levels.iter() {
|
||||||
|
let difficulty_factor = level.difficulty_factor as i32;
|
||||||
|
let visitor_threshold = level.visitor_threshold as i32;
|
||||||
|
let fut = sqlx::query!(
|
||||||
|
"INSERT INTO
|
||||||
|
mcaptcha_track_nonce (level_id, nonce)
|
||||||
|
VALUES ((
|
||||||
|
SELECT
|
||||||
|
level_id
|
||||||
|
FROM
|
||||||
|
mcaptcha_levels
|
||||||
|
WHERE
|
||||||
|
config_id = (SELECT config_id FROM mcaptcha_config WHERE key = ($1))
|
||||||
|
AND
|
||||||
|
difficulty_factor = $2
|
||||||
|
AND
|
||||||
|
visitor_threshold = $3
|
||||||
|
), $4);",
|
||||||
|
&captcha_key,
|
||||||
|
difficulty_factor,
|
||||||
|
visitor_threshold,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.execute(&self.pool);
|
||||||
|
futs.push(fut);
|
||||||
|
}
|
||||||
|
|
||||||
try_join_all(futs)
|
try_join_all(futs)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| map_row_not_found_err(e, DBError::CaptchaNotFound))?;
|
.map_err(|e| map_row_not_found_err(e, DBError::CaptchaNotFound))?;
|
||||||
|
@ -1097,6 +1129,68 @@ impl MCDatabase for Database {
|
||||||
|
|
||||||
Ok(res.drain(0..).map(|r| r.psuedo_id).collect())
|
Ok(res.drain(0..).map(|r| r.psuedo_id).collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Track maximum nonce received against captcha levels
|
||||||
|
async fn update_max_nonce_for_level(
|
||||||
|
&self,
|
||||||
|
captcha_key: &str,
|
||||||
|
difficulty_factor: u32,
|
||||||
|
latest_nonce: u32,
|
||||||
|
) -> DBResult<()> {
|
||||||
|
sqlx::query!(
|
||||||
|
"UPDATE mcaptcha_track_nonce SET nonce = $3
|
||||||
|
WHERE level_id = (
|
||||||
|
SELECT
|
||||||
|
level_id
|
||||||
|
FROM
|
||||||
|
mcaptcha_levels
|
||||||
|
WHERE
|
||||||
|
config_id = (SELECT config_id FROM mcaptcha_config WHERE key = ($1))
|
||||||
|
AND
|
||||||
|
difficulty_factor = $2
|
||||||
|
)
|
||||||
|
AND nonce <= $3;",
|
||||||
|
&captcha_key,
|
||||||
|
difficulty_factor as i32,
|
||||||
|
latest_nonce as i32,
|
||||||
|
)
|
||||||
|
.execute(&self.pool).await
|
||||||
|
.map_err(|e| map_row_not_found_err(e, DBError::CaptchaNotFound))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get maximum nonce tracked so far for captcha levels
|
||||||
|
async fn get_max_nonce_for_level(
|
||||||
|
&self,
|
||||||
|
captcha_key: &str,
|
||||||
|
difficulty_factor: u32,
|
||||||
|
) -> DBResult<u32> {
|
||||||
|
struct X {
|
||||||
|
nonce: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = sqlx::query_as!(
|
||||||
|
X,
|
||||||
|
"SELECT nonce FROM mcaptcha_track_nonce
|
||||||
|
WHERE level_id = (
|
||||||
|
SELECT
|
||||||
|
level_id
|
||||||
|
FROM
|
||||||
|
mcaptcha_levels
|
||||||
|
WHERE
|
||||||
|
config_id = (SELECT config_id FROM mcaptcha_config WHERE key = ($1))
|
||||||
|
AND
|
||||||
|
difficulty_factor = $2
|
||||||
|
);",
|
||||||
|
&captcha_key,
|
||||||
|
difficulty_factor as i32,
|
||||||
|
)
|
||||||
|
.fetch_one(&self.pool).await
|
||||||
|
.map_err(|e| map_row_not_found_err(e, DBError::CaptchaNotFound))?;
|
||||||
|
|
||||||
|
Ok(res.nonce as u32)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|
Loading…
Reference in a new issue