submit benchmark

This commit is contained in:
Aravinth Manivannan 2021-10-14 21:25:19 +05:30
parent 35937134f2
commit f0f265f59e
Signed by: realaravinth
GPG key ID: AD9F0F08E855ED88
17 changed files with 437 additions and 61 deletions

View file

@ -94,27 +94,19 @@ where
fn call(&self, req: ServiceRequest) -> Self::Future { fn call(&self, req: ServiceRequest) -> Self::Future {
let (r, mut pl) = req.into_parts(); let (r, mut pl) = req.into_parts();
let mut is_authenticated = || match self.session_type { let mut is_authenticated = || match self.session_type {
AuthenticatedSession::ActixSession => { AuthenticatedSession::ActixSession => matches!(
if let Ok(Ok(Some(_))) = Session::from_request(&r, &mut pl) Session::from_request(&r, &mut pl)
.into_inner() .into_inner()
.map(|x| x.get::<String>(SURVEY_USER_ID)) .map(|x| x.get::<String>(SURVEY_USER_ID)),
{ Ok(Ok(Some(_)))
true ),
} else {
false
}
}
AuthenticatedSession::ActixIdentity => { AuthenticatedSession::ActixIdentity => matches!(
if let Ok(Some(_)) = Identity::from_request(&r, &mut pl) Identity::from_request(&r, &mut pl)
.into_inner() .into_inner()
.map(|x| x.identity()) .map(|x| x.identity()),
{ Ok(Some(_))
true ),
} else {
false
}
}
}; };
if is_authenticated() { if is_authenticated() {
let req = ServiceRequest::from_parts(r, pl); let req = ServiceRequest::from_parts(r, pl);

View file

@ -23,13 +23,22 @@ use sailfish::TemplateOnce;
use uuid::Uuid; use uuid::Uuid;
use crate::errors::*; use crate::errors::*;
use crate::pages::errors::ErrorPage;
use crate::PAGES; use crate::PAGES;
#[derive(TemplateOnce, Default)] #[derive(TemplateOnce)]
#[template(path = "bench/index.html")] #[template(path = "bench/index.html")]
struct Bench; struct Bench<'a> {
error: Option<ErrorPage<'a>>,
}
const PAGE: &str = "Survey"; const PAGE: &str = "Survey";
impl<'a> Default for Bench<'a> {
fn default() -> Self {
Bench { error: None }
}
}
lazy_static! { lazy_static! {
static ref BENCH: String = Bench::default().render_once().unwrap(); static ref BENCH: String = Bench::default().render_once().unwrap();
} }

View file

@ -17,6 +17,7 @@
const ROUTES = { const ROUTES = {
register: "/survey/api/v1/benches/register", register: "/survey/api/v1/benches/register",
submitBench: (key: string): string => `/survey/api/v1/benches/${key}/submit`,
}; };
export default ROUTES; export default ROUTES;

View file

@ -1,10 +1,26 @@
<. include!("../components/base/top.html"); .> <. include!("../components/base/top.html"); .>
<body class="survey__body"> <body class="survey__body">
<main class="survey__container"> <main class="survey__container">
<. include!("../components/error/index.html"); .>
<div id="pre-bench"> <div id="pre-bench">
<h1>Click to Start Benchmark</h1> <h1>Click to Start Benchmark</h1>
<form class="new-campaign__form" accept-charset="utf-8">
<label class="form__label" for="name">
Device name
<input
class="form__input"
name="name"
required
id="name"
type="text"
/>
</label>
<button id="start" type="submit">Start</button> <!-- <button id="start" type="submit">Start</button> -->
<button id="start" class="form__submit" type="submit">
Start
</button>
</form>
</div> </div>
<noscript> <noscript>
@ -20,6 +36,7 @@
<div id="bench"> <div id="bench">
<h1 id="status" class="name">Running benchmark</h1> <h1 id="status" class="name">Running benchmark</h1>
<div id="submission-proof"></div>
<div id="device-info"></div> <div id="device-info"></div>
<div id="counter"></div> <div id="counter"></div>
@ -47,10 +64,6 @@
align-items: center; align-items: center;
} }
#start {
width: 100px;
}
#pre-bench, #pre-bench,
#bench { #bench {
width: 100%; width: 100%;

View file

@ -14,8 +14,10 @@
* You should have received a copy of the GNU Affero General Public License * 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/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { Perf } from "./types"; import { Bench, Submission, SubmissionProof } from "./types";
import ROUTES from "../api/v1/routes"; import ROUTES from "../api/v1/routes";
import genJsonPaylod from "../utils/genJsonPayload";
import isBlankString from "../utils/isBlankString";
export const index = () => { export const index = () => {
const FACTOR = 500000; const FACTOR = 500000;
@ -25,16 +27,18 @@ export const index = () => {
}; };
const worker = new Worker("/bench.js"); const worker = new Worker("/bench.js");
const res: Array<Perf> = []; const res: Array<Bench> = [];
const stats = document.getElementById("stats"); const stats = document.getElementById("stats");
const CAMPAIGN_ID = window.location.pathname.split("/")[3];
let deviceName = "";
const addResult = (perf: Perf) => { const addResult = (perf: Bench) => {
const row = document.createElement("tr"); const row = document.createElement("tr");
row.className = "data"; row.className = "data";
const diff = document.createElement("td"); const diff = document.createElement("td");
diff.innerHTML = perf.difficulty.toString(); diff.innerHTML = perf.difficulty.toString();
const duration = document.createElement("td"); const duration = document.createElement("td");
duration.innerHTML = perf.time.toString(); duration.innerHTML = perf.duration.toString();
row.appendChild(diff); row.appendChild(diff);
row.appendChild(duration); row.appendChild(duration);
@ -44,6 +48,39 @@ export const index = () => {
res.push(perf); res.push(perf);
}; };
const submitBench = async () => {
const payload: Submission = {
device_user_provided: deviceName,
threads: window.navigator.hardwareConcurrency,
device_software_recognised: window.navigator.userAgent,
benches: res,
};
const resp = await fetch(
ROUTES.submitBench(CAMPAIGN_ID),
genJsonPaylod(payload)
);
if (resp.status == 200) {
const data: SubmissionProof = await resp.json();
const element = document.createElement("div");
const token = document.createElement("b");
token.innerText = "User Agent: ";
const tokenText = document.createTextNode(`${data.token}`);
const proof = document.createElement("b");
proof.innerText = "Proof: ";
const proofText = document.createTextNode(`${data.proof}`);
element.appendChild(token);
element.appendChild(tokenText);
element.appendChild(document.createElement("br"));
element.appendChild(proof);
element.appendChild(proofText);
document.getElementById("submission-proof").appendChild(element);
}
};
const addDeviceInfo = () => { const addDeviceInfo = () => {
const INFO = { const INFO = {
threads: window.navigator.hardwareConcurrency, threads: window.navigator.hardwareConcurrency,
@ -71,14 +108,23 @@ export const index = () => {
document.getElementById("device-info").appendChild(element); document.getElementById("device-info").appendChild(element);
}; };
const finished = () => { const finished = async () => {
await submitBench();
const s = document.getElementById("status"); const s = document.getElementById("status");
s.innerHTML = "Benchmark finished"; s.innerHTML = "Benchmark finished";
}; };
const run = async (e: Event) => { const run = async (e: Event) => {
e.preventDefault(); e.preventDefault();
const deveceNameElement = <HTMLInputElement>document.getElementById("name");
if (isBlankString(deveceNameElement.value, "Device Name", e)) {
return;
}
deviceName = deveceNameElement.value;
await initSession(); await initSession();
document.getElementById("pre-bench").style.display = "none"; document.getElementById("pre-bench").style.display = "none";
document.getElementById("bench").style.display = "flex"; document.getElementById("bench").style.display = "flex";
@ -87,11 +133,11 @@ export const index = () => {
const counterElement = document.getElementById("counter"); const counterElement = document.getElementById("counter");
counterElement.innerText = `${iterations} more to go`; counterElement.innerText = `${iterations} more to go`;
worker.onmessage = (event: MessageEvent) => { worker.onmessage = async (event: MessageEvent) => {
const data: Perf = event.data; const data: Bench = event.data;
addResult(data); addResult(data);
if (res.length == iterations) { if (res.length == iterations) {
finished(); await finished();
counterElement.innerText = "All Done!"; counterElement.innerText = "All Done!";
} else { } else {
counterElement.innerText = `${iterations - res.length} more to go`; counterElement.innerText = `${iterations - res.length} more to go`;

View file

@ -16,7 +16,7 @@
*/ */
import { gen_pow } from "mcaptcha-browser"; import { gen_pow } from "mcaptcha-browser";
import { Perf } from "./types"; import { Bench } from "./types";
type PoWConfig = { type PoWConfig = {
string: string; string: string;
@ -43,11 +43,11 @@ onmessage = function (event) {
const t0 = performance.now(); const t0 = performance.now();
gen_pow(config.salt, config.string, config.difficulty_factor); gen_pow(config.salt, config.string, config.difficulty_factor);
const t1 = performance.now(); const t1 = performance.now();
const time = t1 - t0; const duration = t1 - t0;
const msg: Perf = { const msg: Bench = {
difficulty: difficulty_factor, difficulty: difficulty_factor,
time: time, duration,
}; };
postMessage(msg); postMessage(msg);
}; };

View file

@ -15,8 +15,19 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
export type Bench = {
export type Perf = { difficulty: number;
difficulty: Number; duration: number;
time: Number; };
export type Submission = {
device_user_provided: String;
device_software_recognised: String;
threads: number;
benches: Array<Bench>;
};
export type SubmissionProof = {
token: String;
proof: String;
}; };

View file

@ -0,0 +1,54 @@
/*
* 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/>.
*/
import createError from "./index";
import * as e from "./index";
import setup from "./setUpTests";
"use strict";
jest.useFakeTimers();
it("checks if error boxes work", () => {
document.body.append(setup());
const getMsg = (num: number) => `message ${num}`;
createError(getMsg(1));
let msg = document.querySelector(`.${e.ERR_MSG_CONTAINER}`);
expect(msg.innerHTML).toContain(getMsg(1));
const btn = <HTMLButtonElement>msg.getElementsByClassName(e.ERR_CLOSE)[0];
btn.click();
msg = document.querySelector(`.${e.ERR_MSG_CONTAINER}`);
expect(msg).toEqual(null);
const errElement = document.createElement("p");
errElement.appendChild(document.createTextNode(getMsg(2)));
createError(errElement);
msg = document.querySelector(`.${e.ERR_MSG_CONTAINER}`).querySelector("p");
expect(msg).toEqual(errElement);
const timeOutElement = document.createElement("p");
timeOutElement.appendChild(document.createTextNode(getMsg(2)));
createError(timeOutElement, 200);
msg = document.querySelector(`.${e.ERR_MSG_CONTAINER}`).querySelector("p");
expect(msg).toEqual(timeOutElement);
jest.runOnlyPendingTimers();
msg = document.querySelector(`.${e.ERR_MSG_CONTAINER}`);
expect(msg).toEqual(null);
});

View file

@ -1,6 +1,6 @@
<. if let Some(error) = error { .>
<div class="error__container"> <div class="error__container">
<. if let Some(error) = error { .>
<p class="error__title"><b><.= error.title .></b></p> <p class="error__title"><b><.= error.title .></b></p>
<p class="error__msg"><.= error.message .></p> <p class="error__msg"><.= error.message .></p>
</div>
<. } .> <. } .>
</div>

View file

@ -0,0 +1,77 @@
/*
* 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/>.
*/
export const ERR_CONTAINER_ID = "err__container";
export const ERR_MSG_CONTAINER = "err__msg-container"; // class
export const ERR_CLOSE = "err__close"; // class
export const DEFAULT_LIFETIME = 5000;
const err = () => {
let element;
return (() => {
if (element === undefined) {
element = document.getElementById(ERR_CONTAINER_ID);
}
return element;
})();
};
/**
* create error message
*
* @param {string|HTMLElement} message: an error message
* @param {number} lifetime: duration in milliseconds after which error
* message will be deleted
*/
const createError = (
message: string | HTMLElement,
lifetime: number = DEFAULT_LIFETIME,
): void => {
const box = document.createElement("div");
const msg = () => {
if (typeof message === "string") {
return document.createTextNode(message);
} else {
return message;
}
};
box.className = ERR_MSG_CONTAINER;
box.appendChild(msg());
const deleteBtn = document.createElement("button");
const deleteMsg = document.createTextNode("x");
deleteBtn.appendChild(deleteMsg);
deleteBtn.className = ERR_CLOSE;
box.appendChild(deleteBtn);
err().appendChild(box);
const timer = setTimeout(() => box.remove(), lifetime);
const deleteHandler = (e: Event) => {
e.preventDefault();
window.clearTimeout(timer);
box.remove();
};
deleteBtn.addEventListener("click", e => deleteHandler(e));
};
export default createError;

View file

@ -14,23 +14,37 @@
* You should have received a copy of the GNU Affero General Public License * 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/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
$message-bg: #d63f3f;
$light-text: rgba(255, 255, 255, 0.87);
.error__container { #err__container {
display: felx;
position: fixed;
top: 0;
right: 0;
width: 350px; width: 350px;
background-color: #d63f3f; }
.err__msg-container {
display: flex;
width: 100%; width: 100%;
padding: 10px 0; color: $light-text;
background-color: $message-bg;
padding: 20px;
border-radius: 5px;
opacity: 0.9; opacity: 0.9;
font-size: 0.8rem;
font-family: monospace, monospace;
} }
.error__title { .err__close {
color: #fff; min-width: 10px;
margin-left: 10px; min-height: 10px;
background-color: $message-bg;
margin-left: 20px;
border: none;
} }
.error__msg { .err__close:hover {
color: #fff; cursor: pointer;
margin-left: 10px; width: 20px;
height: 20px;
} }

View file

@ -0,0 +1,25 @@
/*
* 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/>.
*/
import * as e from "./index";
const setup = (): HTMLElement => {
const x = document.createElement("div");
x.id = e.ERR_CONTAINER_ID;
return x;
};
export default setup;

View file

@ -23,6 +23,7 @@
@import "./panel/campaigns/new/main"; @import "./panel/campaigns/new/main";
@import "./panel/campaigns/get/main"; @import "./panel/campaigns/get/main";
@import "./errors/main"; @import "./errors/main";
@import "./components/error/main";
* { * {
margin: 0; margin: 0;

View file

@ -0,0 +1,36 @@
/*
* 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/>.
*/
import genJsonPayload from "./genJsonPayload";
"use strict";
const payload = {
username: "Jhon",
};
const value = {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
};
it("getFromUrl workds", () => {
expect(genJsonPayload(payload)).toEqual(value);
});

View file

@ -0,0 +1,29 @@
/*
* 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/>.
*/
const genJsonPayload = (payload: object): object => {
const value = {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
};
return value;
};
export default genJsonPayload;

View file

@ -0,0 +1,36 @@
/*
* 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/>.
*/
import isBlankString from "./isBlankString";
import {mockAlert} from "../setUpTests";
import setup from "../components/error/setUpTests";
"use strict";
mockAlert();
it("getFromUrl workds", () => {
document.querySelector("body").appendChild(setup());
expect(isBlankString("test", "username")).toBe(false);
try {
isBlankString(" ", "username");
} catch (e) {
expect(e.message).toContain("can't be empty");
}
});

View file

@ -0,0 +1,32 @@
/*
* 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/>.
*/
import createError from "../components/error/";
const isBlankString = (value: string|number, field: string, event?: Event): boolean => {
value = value.toString();
if (!value.replace(/\s/g, "").length) {
if (event !== undefined) {
event.preventDefault();
}
const msg = `${field} can't be empty`;
createError(msg);
throw new Error(msg);
}
return false;
};
export default isBlankString;