Added UI for captcha
This commit is contained in:
parent
ef8a4d0e87
commit
5d7536ac45
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Launch Program",
|
||||||
|
"skipFiles": [
|
||||||
|
"<node_internals>/**"
|
||||||
|
],
|
||||||
|
"program": "${workspaceFolder}/src/server.js"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
66
dev/mat.json
66
dev/mat.json
|
@ -1,29 +1,65 @@
|
||||||
{
|
{
|
||||||
"zKsAXcOCNd9U": {
|
"zKsAXcOCNd9U": {
|
||||||
"mat": [
|
"mat": [
|
||||||
0.0, 0.0, 0.7, 0.7,
|
0.30000000000000004,
|
||||||
0.0, 0.0, 0.0, 0.1,
|
0.5,
|
||||||
0.0, 0.0, 0.0, 0.0,
|
0.5,
|
||||||
0.0, 0.0, 0.0, 0.0
|
0.5,
|
||||||
|
0.30000000000000004,
|
||||||
|
0.30000000000000004,
|
||||||
|
0.30000000000000004,
|
||||||
|
0.5,
|
||||||
|
0.10000000000000003,
|
||||||
|
0.10000000000000003,
|
||||||
|
0.10000000000000003,
|
||||||
|
0.10000000000000003,
|
||||||
|
0.10000000000000003,
|
||||||
|
0.10000000000000003,
|
||||||
|
0.10000000000000003,
|
||||||
|
0.10000000000000003
|
||||||
],
|
],
|
||||||
"nums": 2
|
"nums": 6
|
||||||
},
|
},
|
||||||
"eFkYZBMofIVG": {
|
"eFkYZBMofIVG": {
|
||||||
"mat": [
|
"mat": [
|
||||||
0.0, 0.0, 0.8, 0.4,
|
0.30000000000000004,
|
||||||
0.0, 0.0, 0.0, 0.4,
|
0.30000000000000004,
|
||||||
0.0, 0.0, 0.0, 0.0,
|
0.8999999999999999,
|
||||||
0.0, 0.0, 0.0, 0.0
|
0.8999999999999999,
|
||||||
|
0.30000000000000004,
|
||||||
|
0.30000000000000004,
|
||||||
|
0.7,
|
||||||
|
1.0999999999999999,
|
||||||
|
-0.09999999999999998,
|
||||||
|
-0.09999999999999998,
|
||||||
|
-0.09999999999999998,
|
||||||
|
-0.09999999999999998,
|
||||||
|
-0.09999999999999998,
|
||||||
|
-0.09999999999999998,
|
||||||
|
-0.09999999999999998,
|
||||||
|
-0.09999999999999998
|
||||||
],
|
],
|
||||||
"nums": 2
|
"nums": 9
|
||||||
},
|
},
|
||||||
"JOAT6FV7VKKi": {
|
"JOAT6FV7VKKi": {
|
||||||
"mat": [
|
"mat": [
|
||||||
0.7, 0.6, 0.2, 0.7,
|
0.6,
|
||||||
1.0, 0.9, 0.5, 0.1,
|
0.4,
|
||||||
1.0, 1.0, 0.0, 0.0,
|
0.20000000000000004,
|
||||||
0.0, 0.0, 0.0, 0.0
|
0.9999999999999999,
|
||||||
|
0.6,
|
||||||
|
0.6,
|
||||||
|
0.7999999999999999,
|
||||||
|
0.9999999999999999,
|
||||||
|
-0.19999999999999998,
|
||||||
|
-0.19999999999999998,
|
||||||
|
-0.19999999999999998,
|
||||||
|
-0.19999999999999998,
|
||||||
|
-0.19999999999999998,
|
||||||
|
-0.19999999999999998,
|
||||||
|
-0.19999999999999998,
|
||||||
|
-0.19999999999999998
|
||||||
],
|
],
|
||||||
"nums": 2
|
"nums": 8
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,7 +5,7 @@
|
||||||
"main": "src/server.js",
|
"main": "src/server.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"server": "nodemon src/server.js",
|
"server": "nodemon src/server.js --ignore dev/ --ignore public/ --ignore src/client/",
|
||||||
"dev": "NODE_ENV=development; rollup -c -w",
|
"dev": "NODE_ENV=development; rollup -c -w",
|
||||||
"build": "NODE_ENV=production; rollup -c",
|
"build": "NODE_ENV=production; rollup -c",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
|
|
@ -1,29 +1,89 @@
|
||||||
import UserSession from '../shared/models/UserSession.js';
|
import initializeSession from './helpers/initializeSession.js';
|
||||||
|
import getImage from './helpers/getImage.js';
|
||||||
|
import verifyImage from './helpers/verifyImage.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a uCaptcha box
|
* Create a uCaptcha box
|
||||||
* @param {string} key Website key
|
* @param {string} websiteKey Website key
|
||||||
* @return {HTMLElement} uCaptcha box
|
* @return {HTMLElement} uCaptcha box
|
||||||
*/
|
*/
|
||||||
function uCaptchaBox(key) {
|
function uCaptchaBox(websiteKey) {
|
||||||
|
const styles = `
|
||||||
|
#ucaptcha-next {
|
||||||
|
border: none;
|
||||||
|
background-color: royalblue;
|
||||||
|
color: #FFF;
|
||||||
|
padding: 10px 20px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
#ucaptcha-grid {
|
||||||
|
width: 384px;
|
||||||
|
height: 384px;
|
||||||
|
}
|
||||||
|
#ucaptcha-grid td {
|
||||||
|
transition: .1s ease;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ucaptcha-grid td.selected {
|
||||||
|
padding: 0;
|
||||||
|
border: 10px solid white;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
const styleTag = createElement('style');
|
||||||
|
styleTag.innerHTML = styles;
|
||||||
|
document.head.appendChild(styleTag);
|
||||||
|
|
||||||
const checkbox = createElement('div', {
|
const checkbox = createElement('div', {
|
||||||
style: 'cursor:pointer;border-radius:3px;border:2px solid #888;width:25px;height:25px;display:inline-block',
|
style: 'cursor:pointer;border-radius:3px;border:2px solid #888;width:25px;height:25px;display:inline-block',
|
||||||
});
|
});
|
||||||
checkbox.onclick = function() {
|
|
||||||
fetch(`https://localhost:444/api/init?k=${key}`)
|
const captchaBox = createElement('div');
|
||||||
.then((r)=>r.text())
|
captchaBox.appendChild(checkbox);
|
||||||
.then((r)=>r.substr(2))
|
|
||||||
.then((r)=>JSON.parse(r))
|
const imageContainer = createElement('div', {id: 'ucaptcha-container'});
|
||||||
.then((resp)=>{
|
|
||||||
const session = new UserSession();
|
const image = createElement('img', {id: 'ucaptcha-img', style: 'position:absolute;display:block;z-index:-999'});
|
||||||
session.deserialize(resp);
|
imageContainer.appendChild(image);
|
||||||
|
|
||||||
|
const btn = createElement('button', {id: 'ucaptcha-next'});
|
||||||
|
btn.textContent = 'Next';
|
||||||
|
|
||||||
|
captchaBox.appendChild(imageContainer);
|
||||||
|
captchaBox.appendChild(btn);
|
||||||
|
|
||||||
|
checkbox.onclick = async function() {
|
||||||
|
/** @type {import('../shared/models/UserSession.js').UserSession} */
|
||||||
|
const session = await initializeSession(websiteKey);
|
||||||
|
|
||||||
|
const imageGrid = createElement('table', {id: 'ucaptcha-grid'});
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
const tr = createElement('tr');
|
||||||
|
for (let ii = 0; ii < 4; ii++) {
|
||||||
|
const td = createElement('td',
|
||||||
|
{'style': 'cursor:pointer'});
|
||||||
|
td.addEventListener('click', (e)=>{
|
||||||
|
e.target.classList.toggle('selected');
|
||||||
});
|
});
|
||||||
|
tr.appendChild(td);
|
||||||
|
}
|
||||||
|
imageGrid.appendChild(tr);
|
||||||
|
}
|
||||||
|
|
||||||
|
btn.addEventListener('click', (e)=>{
|
||||||
|
verifyImage(session, imageGrid);
|
||||||
|
getImage(session, captchaBox);
|
||||||
|
});
|
||||||
|
|
||||||
|
imageContainer.appendChild(imageGrid);
|
||||||
|
|
||||||
|
getImage(session, captchaBox);
|
||||||
|
|
||||||
checkbox.setAttribute('style',
|
checkbox.setAttribute('style',
|
||||||
checkbox.getAttribute('style') + 'background-color:royalblue;');
|
checkbox.getAttribute('style') + 'background-color:royalblue;');
|
||||||
};
|
};
|
||||||
const captchaBox = createElement('div');
|
|
||||||
captchaBox.appendChild(checkbox);
|
|
||||||
return captchaBox;
|
return captchaBox;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
import request from './util/request.js';
|
||||||
|
import UserSession from '../../shared/models/UserSession.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiate the uCaptcha loop
|
||||||
|
* @param {UserSession} session
|
||||||
|
* @param {HTMLElement} captchaBox
|
||||||
|
*/
|
||||||
|
export default function instantiateLoop(session, captchaBox) {
|
||||||
|
console.log(captchaBox);
|
||||||
|
request(`/api/image?s=${session.sessionId}`, {responseType: 'blob'})
|
||||||
|
.then((blob)=>{
|
||||||
|
const imageUrl = URL.createObjectURL(blob);
|
||||||
|
console.log(imageUrl);
|
||||||
|
captchaBox
|
||||||
|
.querySelector('#ucaptcha-container')
|
||||||
|
.querySelector('#ucaptcha-img')
|
||||||
|
.setAttribute('src', imageUrl);
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
import request from './util/request.js';
|
||||||
|
import UserSession from '../../shared/models/UserSession.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} websiteKey
|
||||||
|
* @return {Promise<UserSession>}
|
||||||
|
*/
|
||||||
|
export default function initializeSession(websiteKey) {
|
||||||
|
return new Promise((resolve, reject)=>{
|
||||||
|
request(`/api/init?k=${websiteKey}`)
|
||||||
|
.then((resp)=>{
|
||||||
|
console.log(resp);
|
||||||
|
const session = new UserSession();
|
||||||
|
session.deserialize(resp);
|
||||||
|
resolve(session);
|
||||||
|
})
|
||||||
|
.catch((e)=>{
|
||||||
|
reject(e);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
/**
|
||||||
|
* @typedef {Object} RequestOptions
|
||||||
|
* @property {Object<string, any>} [body] Request body
|
||||||
|
* @property {string} [method] Request method
|
||||||
|
* @property {XMLHttpRequestResponseType} [responseType] Return raw response from server
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @type {RequestOptions} */
|
||||||
|
const defaultOptions = {
|
||||||
|
method: 'GET',
|
||||||
|
responseType: 'text', // Text because we will need to substr it.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make an HTTP request
|
||||||
|
* @param {string} url
|
||||||
|
* @param {RequestOptions} options
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
|
export default function(url, options) {
|
||||||
|
console.log(options);
|
||||||
|
options = Object.assign({}, defaultOptions, options);
|
||||||
|
console.log(options);
|
||||||
|
return new Promise((resolve, reject)=>{
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
xhr.open(options.method, url);
|
||||||
|
|
||||||
|
xhr.responseType = options.responseType;
|
||||||
|
|
||||||
|
xhr.onload = function() {
|
||||||
|
if (this.status < 400) {
|
||||||
|
if (options.responseType !== 'text') {
|
||||||
|
resolve(this.response);
|
||||||
|
} else {
|
||||||
|
resolve(JSON.parse(this.responseText.substr(2)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reject(JSON.parse(this.responseText));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (options.method === 'POST') {
|
||||||
|
xhr.setRequestHeader('Content-Type', 'application/json');
|
||||||
|
xhr.send(JSON.stringify(options.body));
|
||||||
|
} else {
|
||||||
|
xhr.send();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
import request from './util/request.js';
|
||||||
|
import UserSession from '../../shared/models/UserSession.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify the image
|
||||||
|
* @param {UserSession} session
|
||||||
|
* @param {HTMLElement} captchaGrid
|
||||||
|
*/
|
||||||
|
export default async function(session, captchaGrid) {
|
||||||
|
const tds = captchaGrid.querySelectorAll('td');
|
||||||
|
const selectedTds = [];
|
||||||
|
|
||||||
|
const mat = new Array(tds.length);
|
||||||
|
for (let i = 0; i < tds.length; i++) {
|
||||||
|
if (tds[i].classList.contains('selected')) {
|
||||||
|
mat[i] = 1;
|
||||||
|
selectedTds.push(tds[i]);
|
||||||
|
} else {
|
||||||
|
mat[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const body = {
|
||||||
|
s: session.sessionId,
|
||||||
|
mat,
|
||||||
|
};
|
||||||
|
|
||||||
|
await request('/api/verify', {method: 'POST', body});
|
||||||
|
|
||||||
|
selectedTds.map((td)=>{
|
||||||
|
td.classList.remove('selected');
|
||||||
|
});
|
||||||
|
}
|
|
@ -2,6 +2,7 @@
|
||||||
* Session storage in IDB
|
* Session storage in IDB
|
||||||
* @typedef {Object} IDBSession
|
* @typedef {Object} IDBSession
|
||||||
* @property {string} sessionId User session ID
|
* @property {string} sessionId User session ID
|
||||||
|
* @property {string} websiteKey Website ID
|
||||||
* @property {string} image Image URL for challenge
|
* @property {string} image Image URL for challenge
|
||||||
* @property {number} score How likely is the user to be a human?
|
* @property {number} score How likely is the user to be a human?
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -6,19 +6,26 @@ import serveImage from './serveImage.js';
|
||||||
import verifyImage from './verifyImage.js';
|
import verifyImage from './verifyImage.js';
|
||||||
|
|
||||||
import sendJson from '../../helpers/sendJson.js';
|
import sendJson from '../../helpers/sendJson.js';
|
||||||
import {IMAGES_FOLDER} from '../../R.js';
|
import {IMAGES_FOLDER, MAX_SESSION_TIME} from '../../R.js';
|
||||||
|
|
||||||
const router = new express.Router();
|
const router = new express.Router();
|
||||||
|
|
||||||
router.get('/init', async (req, res)=>{
|
router.get('/init', async (req, res)=>{
|
||||||
const websiteKey = req.query.k;
|
try {
|
||||||
if (!websiteKey) {
|
const websiteKey = req.query.k;
|
||||||
res.end();
|
if (!websiteKey) {
|
||||||
return;
|
res.end();
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const result = await initializeSession(websiteKey);
|
const session = await initializeSession(websiteKey, req.cookies);
|
||||||
sendJson(res, result);
|
res.cookie(websiteKey, session.sessionId,
|
||||||
|
{httpOnly: true, maxAge: MAX_SESSION_TIME * 1000});
|
||||||
|
sendJson(res, session.serialize());
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
res.end();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get('/image', async (req, res)=>{
|
router.get('/image', async (req, res)=>{
|
||||||
|
@ -40,8 +47,12 @@ router.get('/image', async (req, res)=>{
|
||||||
router.post('/verify', async (req, res)=>{
|
router.post('/verify', async (req, res)=>{
|
||||||
const sessionId = req.body.s;
|
const sessionId = req.body.s;
|
||||||
const mat = req.body.mat;
|
const mat = req.body.mat;
|
||||||
|
console.log(req.body);
|
||||||
|
|
||||||
await verifyImage(sessionId, mat);
|
await verifyImage(sessionId, mat);
|
||||||
|
sendJson(res, {
|
||||||
|
ok: 1,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|
|
@ -14,19 +14,34 @@ import {MAX_SESSION_TIME} from '../../R.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} websiteKey
|
* @param {string} websiteKey
|
||||||
* @return {any}
|
* @param {Object} cookies
|
||||||
|
* @return {UserSession}
|
||||||
*/
|
*/
|
||||||
export default async function initializeSession(websiteKey) {
|
export default async function initializeSession(websiteKey, cookies) {
|
||||||
const randomSessionId = randomBytes(8);
|
let randomSessionId;
|
||||||
|
|
||||||
|
if (cookies[websiteKey]) {
|
||||||
|
randomSessionId = cookies[websiteKey];
|
||||||
|
|
||||||
|
const session = new UserSession();
|
||||||
|
session.sessionId = randomSessionId;
|
||||||
|
session.websiteKey = websiteKey;
|
||||||
|
|
||||||
|
return session;
|
||||||
|
} else {
|
||||||
|
randomSessionId = randomBytes(8);
|
||||||
|
}
|
||||||
|
|
||||||
const image = await pickRandomFile();
|
const image = await pickRandomFile();
|
||||||
|
|
||||||
const session = new UserSession();
|
const session = new UserSession();
|
||||||
session.sessionId = randomSessionId;
|
session.sessionId = randomSessionId;
|
||||||
const userPayload = session.serialize();
|
session.websiteKey = websiteKey;
|
||||||
|
|
||||||
/** @type {IDBSession} */
|
/** @type {IDBSession} */
|
||||||
const idbPayload = {
|
const idbPayload = {
|
||||||
sessionId: session.sessionId,
|
sessionId: session.sessionId,
|
||||||
|
websiteKey: session.websiteKey,
|
||||||
image,
|
image,
|
||||||
score: 0.5,
|
score: 0.5,
|
||||||
};
|
};
|
||||||
|
@ -34,5 +49,5 @@ export default async function initializeSession(websiteKey) {
|
||||||
client.setex(
|
client.setex(
|
||||||
session.sessionId, MAX_SESSION_TIME, JSON.stringify(idbPayload));
|
session.sessionId, MAX_SESSION_TIME, JSON.stringify(idbPayload));
|
||||||
|
|
||||||
return userPayload;
|
return session;
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,7 +5,7 @@ import {IMAGES_FOLDER} from '../../R.js';
|
||||||
* @return {Promise<string>} Image file without extension
|
* @return {Promise<string>} Image file without extension
|
||||||
*/
|
*/
|
||||||
export default () => {
|
export default () => {
|
||||||
return new Promise((reject, resolve)=>{
|
return new Promise((resolve, reject)=>{
|
||||||
fs.readdir(IMAGES_FOLDER, (err, files)=>{
|
fs.readdir(IMAGES_FOLDER, (err, files)=>{
|
||||||
if (err) return reject(err);
|
if (err) return reject(err);
|
||||||
return resolve(
|
return resolve(
|
||||||
|
|
|
@ -11,6 +11,7 @@ import {client} from '../../helpers/idb.js';
|
||||||
export default function(sessionId) {
|
export default function(sessionId) {
|
||||||
return new Promise((resolve, reject)=>{
|
return new Promise((resolve, reject)=>{
|
||||||
client.get(sessionId, (err, result)=>{
|
client.get(sessionId, (err, result)=>{
|
||||||
|
// FIXME: `result` is sometimes null on a perfect query
|
||||||
if (err) return reject(err);
|
if (err) return reject(err);
|
||||||
|
|
||||||
/** @type {IDBSession} */
|
/** @type {IDBSession} */
|
||||||
|
|
|
@ -16,20 +16,53 @@ import pickRandomFile from './pickRandomFile.js';
|
||||||
* @param {string} filename
|
* @param {string} filename
|
||||||
* @return {FileMat}
|
* @return {FileMat}
|
||||||
*/
|
*/
|
||||||
function getMat(filename) {
|
function getMat(image) {
|
||||||
const mats = JSON.parse(
|
const mats = JSON.parse(
|
||||||
fs.readFileSync(
|
fs.readFileSync(
|
||||||
path.join(PROJECT_ROOT, 'dev', 'mat.json'), {encoding: 'utf8'}));
|
path.join(PROJECT_ROOT, 'dev', 'mat.json'), {encoding: 'utf8'}));
|
||||||
|
|
||||||
return mats[filename];
|
return mats[image];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the image mat
|
||||||
|
* @param {FileMat} imageMat
|
||||||
|
* @param {Array<number>} userMat
|
||||||
|
* @param {string} image
|
||||||
|
*/
|
||||||
|
function updateMat(imageMat, userMat, image) {
|
||||||
|
const updatedMat = Object.assign([], imageMat.mat);
|
||||||
|
for (let i = 0; i < imageMat.mat.length; i++) {
|
||||||
|
const delta = 0.1;
|
||||||
|
if (userMat[i] === 1) {
|
||||||
|
updatedMat[i] += delta;
|
||||||
|
} else {
|
||||||
|
updatedMat[i] -= delta;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('OLD MAT: ', imageMat.mat);
|
||||||
|
console.log('NEW MAT: ', updatedMat);
|
||||||
|
|
||||||
|
imageMat.nums += 1;
|
||||||
|
imageMat.mat = updatedMat;
|
||||||
|
|
||||||
|
const mats = JSON.parse(
|
||||||
|
fs.readFileSync(
|
||||||
|
path.join(PROJECT_ROOT, 'dev', 'mat.json'), {encoding: 'utf8'}));
|
||||||
|
mats[image] = imageMat;
|
||||||
|
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(PROJECT_ROOT, 'dev', 'mat.json'),
|
||||||
|
JSON.stringify(mats, null, 2), {encoding: 'utf8'});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} sessionId
|
* @param {string} sessionId
|
||||||
* @param {Array<number>} mat
|
* @param {Array<number>} userMat
|
||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
export default function(sessionId, mat) {
|
export default function(sessionId, userMat) {
|
||||||
return new Promise((resolve, reject)=>{
|
return new Promise((resolve, reject)=>{
|
||||||
client.get(sessionId, (err, result)=>{
|
client.get(sessionId, (err, result)=>{
|
||||||
if (err) return reject(err);
|
if (err) return reject(err);
|
||||||
|
@ -38,19 +71,29 @@ export default function(sessionId, mat) {
|
||||||
result = JSON.parse(result);
|
result = JSON.parse(result);
|
||||||
|
|
||||||
const imageMat = getMat(result.image);
|
const imageMat = getMat(result.image);
|
||||||
const trueArgmax = argmaxThresh(imageMat.mat, 0.2).join(',');
|
const trueArgmax = argmaxThresh(imageMat.mat, 0.3).join(',');
|
||||||
const userArgmax = argmaxThresh(mat, 1).join(',');
|
const userArgmax = argmaxThresh(userMat, 1).join(',');
|
||||||
|
|
||||||
|
updateMat(imageMat, userMat, result.image);
|
||||||
|
|
||||||
|
console.log('trueArgmax', trueArgmax);
|
||||||
|
console.log('userArgmax', userArgmax);
|
||||||
|
|
||||||
if (userArgmax !== trueArgmax) {
|
if (userArgmax !== trueArgmax) {
|
||||||
// TODO: Decrease the score based on mat dispersion
|
// TODO: Decrease the score based on mat dispersion
|
||||||
// High variance = small reduction
|
// High variance = small reduction
|
||||||
// Low variance = high reduction
|
// Low variance = high reduction
|
||||||
result.score -= 0.2;
|
result.score -= 0.2;
|
||||||
|
} else {
|
||||||
|
result.score += 0.2;
|
||||||
}
|
}
|
||||||
|
|
||||||
pickRandomFile().then((image)=>{
|
pickRandomFile().then((image)=>{
|
||||||
|
const update = JSON.stringify(
|
||||||
|
Object.assign(result, {image: image}));
|
||||||
|
|
||||||
client.setex(sessionId, MAX_SESSION_TIME,
|
client.setex(sessionId, MAX_SESSION_TIME,
|
||||||
Object.assign(result, {image: image}), (err)=>{
|
update, (err)=>{
|
||||||
if (err) return reject(err);
|
if (err) return reject(err);
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
* User session model
|
* User session model
|
||||||
* @type {Object} Session Object.
|
* @type {Object} Session Object.
|
||||||
* @property {string} _sessionId The session identifier
|
* @property {string} _sessionId The session identifier
|
||||||
|
* @property {string} _websiteKey The website id for session
|
||||||
*/
|
*/
|
||||||
export class UserSession {
|
export class UserSession {
|
||||||
/**
|
/**
|
||||||
|
@ -9,7 +10,6 @@ export class UserSession {
|
||||||
* @param {string} id
|
* @param {string} id
|
||||||
*/
|
*/
|
||||||
set sessionId(id) {
|
set sessionId(id) {
|
||||||
if (id.length !== 8) throw Error('ClientID is not 8 characters');
|
|
||||||
this._sessionId = id;
|
this._sessionId = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,24 +21,40 @@ export class UserSession {
|
||||||
return this._sessionId;
|
return this._sessionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set session ID
|
||||||
|
* @param {string} id
|
||||||
|
*/
|
||||||
|
set websiteKey(id) {
|
||||||
|
this._websiteKey = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the session ID
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
get websiteKey() {
|
||||||
|
return this._websiteKey;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serialize the session into a JSON object
|
* Serialize the session into a JSON object
|
||||||
* @return {Array<any>}
|
* @return {Array<any>}
|
||||||
*/
|
*/
|
||||||
serialize() {
|
serialize() {
|
||||||
const {sessionId} = this;
|
const {sessionId, websiteKey} = this;
|
||||||
if (!sessionId) {
|
if (!sessionId || !websiteKey) {
|
||||||
throw Error('Unable to serialize because of missing field(s)');
|
throw Error();
|
||||||
}
|
}
|
||||||
return [sessionId];
|
return [sessionId, websiteKey];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deserialize session data from JSON object
|
* Deserialize session data from JSON object
|
||||||
* @param {Array<any>} payload
|
* @param {Array<any>} payload
|
||||||
*/
|
*/
|
||||||
deserialize([sessionId]) {
|
deserialize([sessionId, websiteKey]) {
|
||||||
Object.assign(this, {sessionId});
|
Object.assign(this, {sessionId, websiteKey});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue