Merge pull request #14 from mCaptcha/js-bench
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Collect JavaScript polyfill benchmark using mCaptcha/survey
This commit is contained in:
commit
4f224d782a
19 changed files with 1833 additions and 1322 deletions
|
@ -12,6 +12,7 @@ module.exports = {
|
|||
plugins: ["@typescript-eslint"],
|
||||
rules: {
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-var-requires": "off",
|
||||
"@typescript-eslint/ban-types": "off",
|
||||
indent: ["error", 2],
|
||||
"linebreak-style": ["error", "unix"],
|
||||
|
|
|
@ -66,7 +66,7 @@ mime = "0.3.16"
|
|||
|
||||
#sailfish = "0.3.2"
|
||||
tracing = { version = "0.1.37", features = ["log"] }
|
||||
tera = "1.17.1"
|
||||
tera = { version="1.17.1", features=["builtins"]}
|
||||
|
||||
#tokio = "1.11.0"
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
debug = true
|
||||
allow_registration = true
|
||||
source_code = "https://github.com/mcaptcha/survey"
|
||||
default_campaign = "b6b261fa-3ef9-4d7f-8852-339b8f81bb01"
|
||||
default_campaign = "4e951e01-71ee-4a18-9b97-782965495ae3"
|
||||
support_email="support@example.org"
|
||||
|
||||
[server]
|
||||
|
|
2
migrations/20230131114916_survey_bench_date.sql
Normal file
2
migrations/20230131114916_survey_bench_date.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE survey_responses
|
||||
ADD COLUMN submitted_at TIMESTAMPTZ NOT NULL DEFAULT now();
|
18
migrations/20230201101934_survey_bench_type.sql
Normal file
18
migrations/20230201101934_survey_bench_type.sql
Normal file
|
@ -0,0 +1,18 @@
|
|||
CREATE TABLE IF NOT EXISTS survey_bench_type (
|
||||
name VARCHAR(30) UNIQUE NOT NULL,
|
||||
ID SERIAL PRIMARY KEY NOT NULL
|
||||
);
|
||||
|
||||
INSERT INTO survey_bench_type (name) VALUES ('wasm');
|
||||
INSERT INTO survey_bench_type (name) VALUES ('js');
|
||||
|
||||
|
||||
CREATE OR REPLACE FUNCTION id_in_survey_bench_type(iname varchar)
|
||||
RETURNS int LANGUAGE SQL AS $$
|
||||
SELECT ID FROM survey_bench_type WHERE name = name;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER TABLE survey_responses
|
||||
ADD COLUMN submission_bench_type_id INTEGER references survey_bench_type(ID) NOT NULL
|
||||
DEFAULT id_in_survey_bench_type('wasm');
|
45
package-lock.json
generated
45
package-lock.json
generated
|
@ -8,7 +8,8 @@
|
|||
"name": "mcaptcha-survey",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@mcaptcha/pow-wasm": "0.1.0-alpha-1",
|
||||
"@mcaptcha/pow_sha256-polyfill": "^0.1.0-alpha-1",
|
||||
"@mcaptcha/pow-wasm": "^0.1.0-alpha-1",
|
||||
"@mcaptcha/vanilla-glue": "^0.1.0-alpha-3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -18,7 +19,7 @@
|
|||
"@types/sinon": "^10.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
||||
"@typescript-eslint/parser": "^5.0.0",
|
||||
"@wasm-tool/wasm-pack-plugin": "^1.4.0",
|
||||
"@wasm-tool/wasm-pack-plugin": "^1.6.0",
|
||||
"dart-sass": "^1.25.0",
|
||||
"eslint": "^8.0.1",
|
||||
"jest": "^27.2.5",
|
||||
|
@ -1439,6 +1440,29 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"node_modules/@mcaptcha/pow_sha256-polyfill": {
|
||||
"version": "0.1.0-alpha-1",
|
||||
"resolved": "https://registry.npmjs.org/@mcaptcha/pow_sha256-polyfill/-/pow_sha256-polyfill-0.1.0-alpha-1.tgz",
|
||||
"integrity": "sha512-lnQNBCOnVI9BunHP8FGCsGs310GguQWdxSspXlvWcrLwgl86aq0hlBzZfOV+szG/jeTRAMry0He3MrD/kbqB/Q==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "http://mcaptcha.org/donate"
|
||||
},
|
||||
{
|
||||
"type": "liberapay",
|
||||
"url": "https://liberapay.com/mcaptcha"
|
||||
},
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "http://batsense.net/donate"
|
||||
},
|
||||
{
|
||||
"type": "liberapay",
|
||||
"url": "https://liberapay.com/realaravinth"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/@mcaptcha/pow-wasm": {
|
||||
"version": "0.1.0-alpha-1",
|
||||
"resolved": "https://registry.npmjs.org/@mcaptcha/pow-wasm/-/pow-wasm-0.1.0-alpha-1.tgz",
|
||||
|
@ -2919,9 +2943,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001447",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001447.tgz",
|
||||
"integrity": "sha512-bdKU1BQDPeEXe9A39xJnGtY0uRq/z5osrnXUw0TcK+EYno45Y+U7QU9HhHEyzvMDffpYadFXi3idnSNkcwLkTw==",
|
||||
"version": "1.0.30001448",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001448.tgz",
|
||||
"integrity": "sha512-tq2YI+MJnooG96XpbTRYkBxLxklZPOdLmNIOdIhvf7SNJan6u5vCKum8iT7ZfCt70m1GPkuC7P3TtX6UuhupuA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
|
@ -11164,6 +11188,11 @@
|
|||
"resolved": "https://registry.npmjs.org/@mcaptcha/core-glue/-/core-glue-0.1.0-alpha-5.tgz",
|
||||
"integrity": "sha512-16qWm5O5X0Y9LXULULaAks8Vf9FNlUUBcR5KDt49aWhFhG5++JzxNmCwQM9EJSHNU7y0U+FdyAWcGmjfKlkRLA=="
|
||||
},
|
||||
"@mcaptcha/pow_sha256-polyfill": {
|
||||
"version": "0.1.0-alpha-1",
|
||||
"resolved": "https://registry.npmjs.org/@mcaptcha/pow_sha256-polyfill/-/pow_sha256-polyfill-0.1.0-alpha-1.tgz",
|
||||
"integrity": "sha512-lnQNBCOnVI9BunHP8FGCsGs310GguQWdxSspXlvWcrLwgl86aq0hlBzZfOV+szG/jeTRAMry0He3MrD/kbqB/Q=="
|
||||
},
|
||||
"@mcaptcha/pow-wasm": {
|
||||
"version": "0.1.0-alpha-1",
|
||||
"resolved": "https://registry.npmjs.org/@mcaptcha/pow-wasm/-/pow-wasm-0.1.0-alpha-1.tgz",
|
||||
|
@ -12363,9 +12392,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30001447",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001447.tgz",
|
||||
"integrity": "sha512-bdKU1BQDPeEXe9A39xJnGtY0uRq/z5osrnXUw0TcK+EYno45Y+U7QU9HhHEyzvMDffpYadFXi3idnSNkcwLkTw==",
|
||||
"version": "1.0.30001448",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001448.tgz",
|
||||
"integrity": "sha512-tq2YI+MJnooG96XpbTRYkBxLxklZPOdLmNIOdIhvf7SNJan6u5vCKum8iT7ZfCt70m1GPkuC7P3TtX6UuhupuA==",
|
||||
"dev": true
|
||||
},
|
||||
"chalk": {
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
"@types/sinon": "^10.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
||||
"@typescript-eslint/parser": "^5.0.0",
|
||||
"@wasm-tool/wasm-pack-plugin": "^1.4.0",
|
||||
"@wasm-tool/wasm-pack-plugin": "^1.6.0",
|
||||
"dart-sass": "^1.25.0",
|
||||
"eslint": "^8.0.1",
|
||||
"jest": "^27.2.5",
|
||||
|
@ -33,6 +33,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@mcaptcha/vanilla-glue": "^0.1.0-alpha-3",
|
||||
"@mcaptcha/pow-wasm": "0.1.0-alpha-1"
|
||||
"@mcaptcha/pow_sha256-polyfill": "^0.1.0-alpha-1",
|
||||
"@mcaptcha/pow-wasm": "^0.1.0-alpha-1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,12 +24,15 @@ use uuid::Uuid;
|
|||
|
||||
use super::{get_admin_check_login, get_uuid};
|
||||
use crate::api::v1::bench::Bench;
|
||||
use crate::api::v1::bench::SubmissionType;
|
||||
use crate::errors::*;
|
||||
use crate::AppData;
|
||||
|
||||
pub mod routes {
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::ResultsPage;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
|
||||
pub struct Campaign {
|
||||
pub add: &'static str,
|
||||
|
@ -62,13 +65,33 @@ pub mod routes {
|
|||
self.delete.replace("{uuid}", campaign_id)
|
||||
}
|
||||
|
||||
pub fn get_results_route(&self, campaign_id: &str) -> String {
|
||||
self.results.replace("{uuid}", campaign_id)
|
||||
pub fn get_results_route(
|
||||
&self,
|
||||
campaign_id: &str,
|
||||
modifier: Option<ResultsPage>,
|
||||
) -> String {
|
||||
let mut res = self.results.replace("{uuid}", campaign_id);
|
||||
if let Some(modifier) = modifier {
|
||||
if let Some(page) = modifier.page {
|
||||
res = format!("{res}?page={page}");
|
||||
}
|
||||
|
||||
if let Some(bench_type) = modifier.bench_type {
|
||||
if modifier.page.is_some() {
|
||||
res = format!("{res}&bench_type={}", bench_type.to_string());
|
||||
} else {
|
||||
res = format!("{res}?bench_type={}", bench_type.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod runners {
|
||||
use std::str::FromStr;
|
||||
|
||||
use futures::try_join;
|
||||
|
||||
use crate::api::v1::bench::Bench;
|
||||
|
@ -163,10 +186,12 @@ pub mod runners {
|
|||
#[derive(Debug)]
|
||||
struct InternalSurveyResp {
|
||||
id: i32,
|
||||
submitted_at: OffsetDateTime,
|
||||
user_id: Uuid,
|
||||
threads: Option<i32>,
|
||||
device_user_provided: String,
|
||||
device_software_recognised: String,
|
||||
name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -190,34 +215,98 @@ pub mod runners {
|
|||
data: &AppData,
|
||||
page: usize,
|
||||
limit: usize,
|
||||
filter: Option<SubmissionType>,
|
||||
) -> ServiceResult<Vec<SurveyResponse>> {
|
||||
let mut db_responses = sqlx::query_as!(
|
||||
InternalSurveyResp,
|
||||
"SELECT
|
||||
ID,
|
||||
device_software_recognised,
|
||||
threads,
|
||||
user_id,
|
||||
device_user_provided
|
||||
let mut db_responses = if let Some(filter) = filter {
|
||||
sqlx::query_as!(
|
||||
InternalSurveyResp,
|
||||
"SELECT
|
||||
survey_responses.ID,
|
||||
survey_responses.device_software_recognised,
|
||||
survey_responses.threads,
|
||||
survey_responses.user_id,
|
||||
survey_responses.submitted_at,
|
||||
survey_responses.device_user_provided,
|
||||
survey_bench_type.name
|
||||
FROM
|
||||
survey_responses
|
||||
INNER JOIN survey_bench_type ON
|
||||
survey_responses.submission_bench_type_id = survey_bench_type.ID
|
||||
WHERE
|
||||
survey_bench_type.name = $3
|
||||
AND
|
||||
survey_responses.campaign_id = (
|
||||
SELECT ID FROM survey_campaigns
|
||||
WHERE
|
||||
ID = $1
|
||||
AND
|
||||
user_id = (SELECT ID FROM survey_admins WHERE name = $2)
|
||||
)
|
||||
LIMIT $4 OFFSET $5",
|
||||
uuid,
|
||||
username,
|
||||
filter.to_string(),
|
||||
limit as i32,
|
||||
page as i32,
|
||||
)
|
||||
.fetch_all(&data.db)
|
||||
.await?
|
||||
} else {
|
||||
#[derive(Debug)]
|
||||
struct I {
|
||||
id: Option<i32>,
|
||||
submitted_at: Option<OffsetDateTime>,
|
||||
user_id: Option<Uuid>,
|
||||
threads: Option<i32>,
|
||||
device_user_provided: Option<String>,
|
||||
device_software_recognised: Option<String>,
|
||||
name: Option<String>,
|
||||
}
|
||||
let mut i = sqlx::query_as!(
|
||||
I,
|
||||
"SELECT
|
||||
survey_responses.ID,
|
||||
survey_responses.device_software_recognised,
|
||||
survey_responses.threads,
|
||||
survey_responses.user_id,
|
||||
survey_responses.submitted_at,
|
||||
survey_responses.device_user_provided,
|
||||
survey_bench_type.name
|
||||
FROM
|
||||
survey_responses
|
||||
INNER JOIN survey_bench_type ON
|
||||
survey_responses.submission_bench_type_id = survey_bench_type.ID
|
||||
WHERE
|
||||
campaign_id = (
|
||||
survey_responses.campaign_id = (
|
||||
SELECT ID FROM survey_campaigns
|
||||
WHERE
|
||||
ID = $1
|
||||
AND
|
||||
user_id = (SELECT ID FROM survey_admins WHERE name = $2)
|
||||
)
|
||||
LIMIT $3 OFFSET $4
|
||||
",
|
||||
uuid,
|
||||
username,
|
||||
limit as i32,
|
||||
page as i32,
|
||||
)
|
||||
.fetch_all(&data.db)
|
||||
.await?;
|
||||
LIMIT $3 OFFSET $4",
|
||||
uuid,
|
||||
username,
|
||||
limit as i32,
|
||||
page as i32,
|
||||
)
|
||||
.fetch_all(&data.db)
|
||||
.await?;
|
||||
|
||||
let mut res = Vec::with_capacity(i.len());
|
||||
i.drain(0..).for_each(|x| {
|
||||
res.push(InternalSurveyResp {
|
||||
id: x.id.unwrap(),
|
||||
submitted_at: x.submitted_at.unwrap(),
|
||||
user_id: x.user_id.unwrap(),
|
||||
threads: x.threads,
|
||||
device_user_provided: x.device_user_provided.unwrap(),
|
||||
device_software_recognised: x.device_software_recognised.unwrap(),
|
||||
name: x.name.unwrap(),
|
||||
})
|
||||
});
|
||||
res
|
||||
};
|
||||
|
||||
let mut responses = Vec::with_capacity(db_responses.len());
|
||||
for r in db_responses.drain(0..) {
|
||||
|
@ -256,7 +345,9 @@ pub mod runners {
|
|||
user,
|
||||
device_user_provided: r.device_user_provided,
|
||||
device_software_recognised: r.device_software_recognised,
|
||||
submitted_at: r.submitted_at.unix_timestamp(),
|
||||
id: r.id as usize,
|
||||
submission_type: SubmissionType::from_str(&r.name).unwrap(),
|
||||
threads: r.threads.map(|t| t as usize),
|
||||
})
|
||||
}
|
||||
|
@ -314,6 +405,8 @@ pub struct SurveyResponse {
|
|||
pub device_software_recognised: String,
|
||||
pub id: usize,
|
||||
pub threads: Option<usize>,
|
||||
pub submitted_at: i64,
|
||||
pub submission_type: SubmissionType,
|
||||
pub benches: Vec<Bench>,
|
||||
}
|
||||
|
||||
|
@ -364,12 +457,17 @@ pub fn services(cfg: &mut web::ServiceConfig) {
|
|||
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
|
||||
pub struct ResultsPage {
|
||||
page: Option<usize>,
|
||||
pub bench_type: Option<SubmissionType>,
|
||||
}
|
||||
|
||||
impl ResultsPage {
|
||||
pub fn page(&self) -> usize {
|
||||
self.page.unwrap_or(0)
|
||||
}
|
||||
|
||||
pub fn new(page: Option<usize>, bench_type: Option<SubmissionType>) -> Self {
|
||||
Self { page, bench_type }
|
||||
}
|
||||
}
|
||||
|
||||
#[actix_web_codegen_const_routes::get(
|
||||
|
@ -383,9 +481,12 @@ pub async fn get_campaign_resutls(
|
|||
data: AppData,
|
||||
) -> ServiceResult<impl Responder> {
|
||||
let username = id.identity().unwrap();
|
||||
let query = query.into_inner();
|
||||
let page = query.page();
|
||||
|
||||
let results = runners::get_results(&username, &path, &data, page, 50).await?;
|
||||
let results =
|
||||
runners::get_results(&username, &path, &data, page, 50, query.bench_type)
|
||||
.await?;
|
||||
|
||||
Ok(HttpResponse::Ok().json(results))
|
||||
}
|
||||
|
@ -408,6 +509,7 @@ async fn add(
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::api::v1::bench::Submission;
|
||||
use crate::api::v1::bench::SubmissionType;
|
||||
use crate::errors::*;
|
||||
use crate::tests::*;
|
||||
use crate::*;
|
||||
|
@ -479,6 +581,7 @@ mod tests {
|
|||
device_software_recognised: DEVICE_SOFTWARE_RECOGNISED.into(),
|
||||
threads: THREADS,
|
||||
benches: BENCHES.clone(),
|
||||
submission_type: SubmissionType::Wasm,
|
||||
};
|
||||
|
||||
let _proof =
|
||||
|
@ -493,6 +596,7 @@ mod tests {
|
|||
&AppData::new(data.clone()),
|
||||
0,
|
||||
50,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
@ -503,6 +607,34 @@ mod tests {
|
|||
let mut r = BENCHES.clone();
|
||||
r.sort_by(|a, b| a.difficulty.cmp(&b.difficulty));
|
||||
|
||||
assert_eq!(
|
||||
super::runners::get_results(
|
||||
NAME,
|
||||
&uuid::Uuid::parse_str(&campaign.campaign_id).unwrap(),
|
||||
&AppData::new(data.clone()),
|
||||
0,
|
||||
50,
|
||||
Some(SubmissionType::Wasm),
|
||||
)
|
||||
.await
|
||||
.unwrap(),
|
||||
responses
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
super::runners::get_results(
|
||||
NAME,
|
||||
&uuid::Uuid::parse_str(&campaign.campaign_id).unwrap(),
|
||||
&AppData::new(data.clone()),
|
||||
0,
|
||||
50,
|
||||
Some(SubmissionType::Js),
|
||||
)
|
||||
.await
|
||||
.unwrap(),
|
||||
Vec::default()
|
||||
);
|
||||
|
||||
assert_eq!(l, r);
|
||||
assert_eq!(
|
||||
responses[0].device_software_recognised,
|
||||
|
@ -515,7 +647,7 @@ mod tests {
|
|||
&V1_API_ROUTES
|
||||
.admin
|
||||
.campaign
|
||||
.get_results_route(&campaign.campaign_id),
|
||||
.get_results_route(&campaign.campaign_id, None),
|
||||
cookies.clone()
|
||||
);
|
||||
assert_eq!(results_resp.status(), StatusCode::OK);
|
||||
|
|
|
@ -169,6 +169,28 @@ pub struct Submission {
|
|||
pub device_software_recognised: String,
|
||||
pub threads: i32,
|
||||
pub benches: Vec<Bench>,
|
||||
pub submission_type: SubmissionType,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum SubmissionType {
|
||||
Wasm,
|
||||
Js,
|
||||
}
|
||||
|
||||
impl ToString for SubmissionType {
|
||||
fn to_string(&self) -> String {
|
||||
let s = serde_json::to_string(&self).unwrap();
|
||||
(&s[1..(s.len() - 1)]).to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for SubmissionType {
|
||||
type Err = serde_json::Error;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
serde_json::from_str(&format!("\"{}\"", s))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
|
@ -177,12 +199,11 @@ pub struct SubmissionProof {
|
|||
pub proof: String,
|
||||
}
|
||||
|
||||
fn is_session_authenticated(r: &HttpRequest, mut pl: &mut Payload) -> bool {
|
||||
fn is_session_authenticated(r: &HttpRequest, pl: &mut Payload) -> bool {
|
||||
use actix_web::FromRequest;
|
||||
matches!(
|
||||
Session::from_request(r, pl).into_inner().map(|x| {
|
||||
let val = x.get::<String>(SURVEY_USER_ID);
|
||||
println!("{:#?}", val);
|
||||
val
|
||||
}),
|
||||
Ok(Ok(Some(_)))
|
||||
|
@ -214,6 +235,7 @@ async fn submit(
|
|||
|
||||
let user_id = Uuid::from_str(&username).unwrap();
|
||||
let payload = payload.into_inner();
|
||||
let now = OffsetDateTime::now_utc();
|
||||
|
||||
struct ID {
|
||||
id: i32,
|
||||
|
@ -221,18 +243,25 @@ async fn submit(
|
|||
let resp_id = sqlx::query_as!(
|
||||
ID,
|
||||
"INSERT INTO survey_responses (
|
||||
user_id,
|
||||
campaign_id,
|
||||
device_user_provided,
|
||||
device_software_recognised,
|
||||
threads
|
||||
) VALUES ($1, $2, $3, $4, $5)
|
||||
user_id,
|
||||
campaign_id,
|
||||
device_user_provided,
|
||||
device_software_recognised,
|
||||
threads,
|
||||
submitted_at,
|
||||
submission_bench_type_id
|
||||
) VALUES (
|
||||
$1, $2, $3, $4, $5, $6,
|
||||
(SELECT ID FROM survey_bench_type WHERE name = $7)
|
||||
)
|
||||
RETURNING ID;",
|
||||
&user_id,
|
||||
&campaign_id,
|
||||
&payload.device_user_provided,
|
||||
&payload.device_software_recognised,
|
||||
&payload.threads
|
||||
&payload.threads,
|
||||
&now,
|
||||
&payload.submission_type.to_string(),
|
||||
)
|
||||
.fetch_one(&data.db)
|
||||
.await?;
|
||||
|
@ -311,3 +340,14 @@ async fn fetch(data: AppData, path: web::Path<String>) -> ServiceResult<impl Res
|
|||
.await?;
|
||||
Ok(HttpResponse::Ok().json(config))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn survey_response_type_no_panic_test() {
|
||||
assert_eq!(SubmissionType::Wasm.to_string(), "wasm".to_string());
|
||||
assert_eq!(SubmissionType::Js.to_string(), "js".to_string());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,9 +21,11 @@ use actix_web::{HttpResponse, Responder};
|
|||
use serde::{Deserialize, Serialize};
|
||||
use tera::Context;
|
||||
|
||||
use crate::api::v1::admin::campaigns::ResultsPage;
|
||||
use crate::api::v1::admin::campaigns::{
|
||||
runners::list_campaign_runner, ListCampaignResp,
|
||||
};
|
||||
use crate::api::v1::bench::SubmissionType;
|
||||
use crate::pages::errors::*;
|
||||
use crate::AppData;
|
||||
use crate::Settings;
|
||||
|
@ -55,6 +57,8 @@ pub fn register_templates(t: &mut tera::Tera) {
|
|||
pub mod routes {
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::api::v1::admin::campaigns::ResultsPage;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
|
||||
pub struct Campaigns {
|
||||
pub home: &'static str,
|
||||
|
@ -64,6 +68,7 @@ pub mod routes {
|
|||
pub delete: &'static str,
|
||||
pub results: &'static str,
|
||||
}
|
||||
|
||||
impl Campaigns {
|
||||
pub const fn new() -> Campaigns {
|
||||
Campaigns {
|
||||
|
@ -91,11 +96,22 @@ pub mod routes {
|
|||
pub fn get_results_route(
|
||||
&self,
|
||||
campaign_id: &str,
|
||||
page: Option<usize>,
|
||||
modifier: Option<ResultsPage>,
|
||||
) -> String {
|
||||
let mut res = self.results.replace("{uuid}", campaign_id);
|
||||
if let Some(page) = page {
|
||||
res = format!("{res}?page={page}");
|
||||
if let Some(modifier) = modifier {
|
||||
let page = modifier.page();
|
||||
if page != 0 {
|
||||
res = format!("{res}?page={page}");
|
||||
}
|
||||
|
||||
if let Some(bench_type) = modifier.bench_type {
|
||||
if page != 0 {
|
||||
res = format!("{res}&bench_type={}", bench_type.to_string());
|
||||
} else {
|
||||
res = format!("{res}?bench_type={}", bench_type.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
|
@ -154,7 +170,6 @@ impl From<ListCampaignResp> for TemplateCampaign {
|
|||
impl CtxError for Campaigns {
|
||||
fn with_error(&self, e: &ReadableError) -> String {
|
||||
self.ctx.borrow_mut().insert(ERROR_KEY, e);
|
||||
|
||||
self.render()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,33 +44,88 @@ impl CtxError for CampaignResults {
|
|||
}
|
||||
}
|
||||
|
||||
const RESUTS_LIMIT: usize = 50;
|
||||
const RESUTS_LIMIT: usize = 10;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||
pub struct ResultsPagePayload {
|
||||
next_page: Option<String>,
|
||||
submissions: Vec<SurveyResponse>,
|
||||
pub wasm_only: Option<String>,
|
||||
pub js_only: Option<String>,
|
||||
pub all_benches: Option<String>,
|
||||
}
|
||||
|
||||
impl ResultsPagePayload {
|
||||
pub fn new(
|
||||
submissions: Vec<SurveyResponse>,
|
||||
current_page: usize,
|
||||
campaign_id: &Uuid,
|
||||
modifier: ResultsPage,
|
||||
) -> Self {
|
||||
let next_page = if submissions.len() >= RESUTS_LIMIT {
|
||||
let campaign_id_str = campaign_id.to_string();
|
||||
|
||||
let all_benches;
|
||||
let wasm_only;
|
||||
let js_only;
|
||||
match modifier.bench_type {
|
||||
Some(SubmissionType::Js) => {
|
||||
all_benches = Some(
|
||||
crate::PAGES
|
||||
.panel
|
||||
.campaigns
|
||||
.get_results_route(&campaign_id_str, None),
|
||||
);
|
||||
|
||||
wasm_only = Some(crate::PAGES.panel.campaigns.get_results_route(
|
||||
&campaign_id_str,
|
||||
Some(ResultsPage::new(None, Some(SubmissionType::Wasm))),
|
||||
));
|
||||
js_only = None;
|
||||
}
|
||||
Some(SubmissionType::Wasm) => {
|
||||
js_only = Some(crate::PAGES.panel.campaigns.get_results_route(
|
||||
&campaign_id_str,
|
||||
Some(ResultsPage::new(None, Some(SubmissionType::Js))),
|
||||
));
|
||||
all_benches = Some(
|
||||
crate::PAGES
|
||||
.panel
|
||||
.campaigns
|
||||
.get_results_route(&campaign_id_str, None),
|
||||
);
|
||||
wasm_only = None;
|
||||
}
|
||||
None => {
|
||||
all_benches = None;
|
||||
js_only = Some(crate::PAGES.panel.campaigns.get_results_route(
|
||||
&campaign_id_str,
|
||||
Some(ResultsPage::new(None, Some(SubmissionType::Js))),
|
||||
));
|
||||
wasm_only = Some(crate::PAGES.panel.campaigns.get_results_route(
|
||||
&campaign_id_str,
|
||||
Some(ResultsPage::new(None, Some(SubmissionType::Wasm))),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let next_page = if submissions.len() == RESUTS_LIMIT {
|
||||
let m = ResultsPage::new(Some(modifier.page() + 1), modifier.bench_type);
|
||||
|
||||
Some(
|
||||
PAGES
|
||||
.panel
|
||||
.campaigns
|
||||
.get_results_route(&campaign_id.to_string(), Some(current_page + 1)),
|
||||
.get_results_route(&campaign_id_str, Some(m)),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Self {
|
||||
next_page,
|
||||
submissions,
|
||||
js_only,
|
||||
wasm_only,
|
||||
all_benches,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -105,15 +160,22 @@ pub async fn results(
|
|||
)),
|
||||
Ok(uuid) => {
|
||||
let username = id.identity().unwrap();
|
||||
let query = query.into_inner();
|
||||
let page = query.page();
|
||||
|
||||
let results =
|
||||
runners::get_results(&username, &uuid, &data, page, RESUTS_LIMIT)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
PageError::new(CampaignResults::new(&data.settings, None), e)
|
||||
})?;
|
||||
let payload = ResultsPagePayload::new(results, page, &uuid);
|
||||
let results = runners::get_results(
|
||||
&username,
|
||||
&uuid,
|
||||
&data,
|
||||
page,
|
||||
RESUTS_LIMIT,
|
||||
query.bench_type.clone(),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
PageError::new(CampaignResults::new(&data.settings, None), e)
|
||||
})?;
|
||||
let payload = ResultsPagePayload::new(results, &uuid, query);
|
||||
|
||||
let results_page =
|
||||
CampaignResults::new(&data.settings, Some(payload)).render();
|
||||
|
|
|
@ -131,5 +131,7 @@
|
|||
}
|
||||
</style>
|
||||
|
||||
<!--
|
||||
<script src="{{ assets.glue }}"></script>
|
||||
-->
|
||||
{% endblock body %}
|
||||
|
|
|
@ -19,6 +19,7 @@ import ROUTES from "../api/v1/routes";
|
|||
import genJsonPaylod from "../utils/genJsonPayload";
|
||||
import isBlankString from "../utils/isBlankString";
|
||||
import createError from "../components/error/";
|
||||
import { get_bench_type } from "./prove";
|
||||
|
||||
export const index = () => {
|
||||
const ADV = <HTMLButtonElement>document.getElementById("advance");
|
||||
|
@ -67,12 +68,13 @@ export const index = () => {
|
|||
};
|
||||
|
||||
const submitBench = async () => {
|
||||
const submission_type = await get_bench_type();
|
||||
const payload: Submission = {
|
||||
device_user_provided: deviceName,
|
||||
threads: window.navigator.hardwareConcurrency,
|
||||
device_software_recognised: window.navigator.userAgent,
|
||||
|
||||
benches: res,
|
||||
submission_type,
|
||||
};
|
||||
|
||||
const resp = await fetch(
|
||||
|
@ -96,7 +98,6 @@ export const index = () => {
|
|||
element.appendChild(proof);
|
||||
element.appendChild(proofText);
|
||||
document.getElementById("submission-proof").appendChild(element);
|
||||
document.getElementById("winner-instructions").style.display = "block";
|
||||
}
|
||||
};
|
||||
|
||||
|
|
71
templates/bench/prove.ts
Normal file
71
templates/bench/prove.ts
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* mCaptcha is a PoW based DoS protection software.
|
||||
* This is the frontend web component of the mCaptcha system
|
||||
* Copyright © 2021 Aravinth Manivnanan <realaravinth@batsense.net>.
|
||||
*
|
||||
* Use of this source code is governed by Apache 2.0 or MIT license.
|
||||
* You shoud have received a copy of MIT and Apache 2.0 along with
|
||||
* this program. If not, see <https://spdx.org/licenses/MIT.html> for
|
||||
* MIT or <http://www.apache.org/licenses/LICENSE-2.0> for Apache.
|
||||
*/
|
||||
|
||||
import * as p from "@mcaptcha/pow_sha256-polyfill";
|
||||
import { PoWConfig, SubmissionType } from "./types";
|
||||
|
||||
export const get_bench_type = async (): Promise<SubmissionType> => {
|
||||
console.log(`Wasm support says ${WasmSupported}`);
|
||||
let submission_type: SubmissionType;
|
||||
if (WasmSupported) {
|
||||
submission_type = SubmissionType.wasm;
|
||||
} else {
|
||||
submission_type = SubmissionType.js;
|
||||
}
|
||||
return submission_type;
|
||||
};
|
||||
|
||||
/**
|
||||
* proove work
|
||||
* @param {PoWConfig} config - the proof-of-work configuration using which
|
||||
* work needs to be computed
|
||||
* */
|
||||
const prove = async (config: PoWConfig): Promise<number> => {
|
||||
console.log(`Wasm support says ${WasmSupported}`);
|
||||
let duration: number;
|
||||
if (WasmSupported) {
|
||||
const wasm = await require("@mcaptcha/pow-wasm");
|
||||
const t0 = performance.now();
|
||||
wasm.gen_pow(config.salt, config.string, config.difficulty_factor);
|
||||
const t1 = performance.now();
|
||||
duration = t1 - t0;
|
||||
} else {
|
||||
console.log("WASM unsupported, expect delay during proof generation");
|
||||
const t0 = performance.now();
|
||||
|
||||
await p.generate_work(config.salt, config.string, config.difficulty_factor);
|
||||
const t1 = performance.now();
|
||||
duration = t1 - t0;
|
||||
}
|
||||
return duration;
|
||||
};
|
||||
|
||||
// credits: @jf-bastien on Stack Overflow
|
||||
// https://stackoverflow.com/questions/47879864/how-can-i-check-if-a-browser-supports-webassembly
|
||||
const WasmSupported = (() => {
|
||||
try {
|
||||
if (
|
||||
typeof WebAssembly === "object" &&
|
||||
typeof WebAssembly.instantiate === "function"
|
||||
) {
|
||||
const module = new WebAssembly.Module(
|
||||
Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00)
|
||||
);
|
||||
if (module instanceof WebAssembly.Module)
|
||||
return new WebAssembly.Instance(module) instanceof WebAssembly.Instance;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
return false;
|
||||
})();
|
||||
|
||||
export default prove;
|
|
@ -15,35 +15,24 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { gen_pow } from "@mcaptcha/pow-wasm";
|
||||
import { Bench } from "./types";
|
||||
|
||||
type PoWConfig = {
|
||||
string: string;
|
||||
difficulty_factor: number;
|
||||
salt: string;
|
||||
};
|
||||
import { Bench, PoWConfig } from "./types";
|
||||
import prove from "./prove";
|
||||
|
||||
const SALT = "674243647f1c355da8607a8cdda05120d79ca5d1af8b3b49359d056a0a82";
|
||||
const PHRASE = "6e2a53dbc7d307970d7ba3c0000221722cb74f1c325137251ce8fa5c2240";
|
||||
|
||||
const config: PoWConfig = {
|
||||
string: PHRASE,
|
||||
difficulty_factor: 1,
|
||||
salt: SALT,
|
||||
};
|
||||
|
||||
console.debug("worker registered");
|
||||
|
||||
onmessage = function (event) {
|
||||
onmessage = async (event) => {
|
||||
console.debug("message received at worker");
|
||||
const difficulty_factor = parseInt(event.data);
|
||||
config.difficulty_factor = difficulty_factor;
|
||||
const config: PoWConfig = {
|
||||
string: PHRASE,
|
||||
difficulty_factor,
|
||||
salt: SALT,
|
||||
};
|
||||
|
||||
const t0 = performance.now();
|
||||
gen_pow(config.salt, config.string, config.difficulty_factor);
|
||||
const t1 = performance.now();
|
||||
const duration = t1 - t0;
|
||||
const duration = await prove(config);
|
||||
|
||||
const msg: Bench = {
|
||||
difficulty: difficulty_factor,
|
||||
|
|
|
@ -25,6 +25,7 @@ export type Submission = {
|
|||
device_software_recognised: String;
|
||||
threads: number;
|
||||
benches: Array<Bench>;
|
||||
submission_type: SubmissionType;
|
||||
};
|
||||
|
||||
export type SubmissionProof = {
|
||||
|
@ -35,3 +36,14 @@ export type SubmissionProof = {
|
|||
export type BenchConfig = {
|
||||
difficulties: Array<number>;
|
||||
};
|
||||
|
||||
export type PoWConfig = {
|
||||
string: string;
|
||||
difficulty_factor: number;
|
||||
salt: string;
|
||||
};
|
||||
|
||||
export enum SubmissionType {
|
||||
wasm = "wasm",
|
||||
js = "js",
|
||||
}
|
||||
|
|
|
@ -7,26 +7,44 @@
|
|||
{% block body %}
|
||||
<body class="panel__body">
|
||||
<main class="panel__container">
|
||||
<ul>
|
||||
<h2>Filters</h2>
|
||||
{% if payload.js_only %}
|
||||
<ol><a href="{{ payload.js_only }}">JavaScript polyfil only</a></ol>
|
||||
{% endif %}
|
||||
|
||||
{% if payload.wasm_only %}
|
||||
<ol><a href="{{ payload.wasm_only }}">WASM only</a></ol>
|
||||
{% endif %}
|
||||
|
||||
{% if payload.all_benches %}
|
||||
<ol><a href="{{ payload.all_benches }}">All Benchmarks</a></ol>
|
||||
{% endif %}
|
||||
</ul>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Submission ID</th>
|
||||
<th>Time (UTC)</th>
|
||||
<th>User ID</th>
|
||||
<th>Device make (user provided)</th>
|
||||
<th>Device make (detected)</th>
|
||||
<th>Threads</th>
|
||||
<th>Benchmark Type</th>
|
||||
<th>Benches</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for sub in payload.submissions %}
|
||||
<tr>
|
||||
<th>{{ sub.id }}</th>
|
||||
<th>{{ sub.user.id }}</th>
|
||||
<th>{{ sub.device_user_provided }}</th>
|
||||
<th>{{ sub.device_software_recognised }}</th>
|
||||
<th>{{ sub.threads }}</th>
|
||||
<th>
|
||||
<td>{{ sub.id }}</td>
|
||||
<td>{{ sub.submitted_at | date(format="%Y-%m-%d %H:%M", timezone="GMT") }}</td>
|
||||
<td>{{ sub.user.id }}</td>
|
||||
<td>{{ sub.device_user_provided }}</td>
|
||||
<td>{{ sub.device_software_recognised }}</td>
|
||||
<td>{{ sub.threads }}</td>
|
||||
<td>{{ sub.submission_type }}</td>
|
||||
<td>
|
||||
<table>
|
||||
<thead>
|
||||
<th>Difficulty</th>
|
||||
|
@ -41,13 +59,14 @@
|
|||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</th>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<a href="{{payload.next_page}}">Next ></a>
|
||||
{% if payload.next_page %}
|
||||
<a href="{{payload.next_page}}">Next ></a>
|
||||
{% endif %}
|
||||
</main>
|
||||
</body>
|
||||
{% endblock body %}
|
||||
|
|
|
@ -1,30 +1,30 @@
|
|||
'use strict';
|
||||
const path = require('path');
|
||||
"use strict";
|
||||
const path = require("path");
|
||||
//const WasmPackPlugin = require('@wasm-tool/wasm-pack-plugin');
|
||||
|
||||
module.exports = {
|
||||
devtool: 'inline-source-map',
|
||||
mode: 'development',
|
||||
devtool: "inline-source-map",
|
||||
mode: "production",
|
||||
//mode: 'production',
|
||||
entry: {
|
||||
bundle: './templates/index.ts',
|
||||
bench: './templates/bench/service-worker.ts',
|
||||
glue: './templates/bench/vendor.ts',
|
||||
bundle: "./templates/index.ts",
|
||||
bench: "./templates/bench/service-worker.ts",
|
||||
glue: "./templates/bench/vendor.ts",
|
||||
},
|
||||
output: {
|
||||
filename: '[name].js',
|
||||
path: path.resolve(__dirname, './static/cache/bundle'),
|
||||
filename: "[name].js",
|
||||
path: path.resolve(__dirname, "./static/cache/bundle"),
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
loader: 'ts-loader',
|
||||
loader: "ts-loader",
|
||||
},
|
||||
],
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.ts', '.tsx', '.js'],
|
||||
extensions: [".ts", ".tsx", ".js"],
|
||||
},
|
||||
|
||||
experiments: {
|
||||
|
|
Loading…
Reference in a new issue