mirror of
1
2
Fork 0

code refactor

This commit is contained in:
Devshh 2020-03-31 18:01:16 +01:00
parent 41e2212c11
commit 62c64de359
17 changed files with 966 additions and 258 deletions

3
.vscode/launch.json vendored
View File

@ -4,6 +4,7 @@
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
@ -11,7 +12,7 @@
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}/src/server.js"
"program": "${workspaceFolder}/src/start.js"
}
]
}

View File

@ -1 +1,662 @@
{}
{
"OxXrEtvErYzN": {
"mat": [
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.6,
0.4,
0.4,
0.4,
0.6,
0.6,
0.6,
0.6,
0.6
],
"nums": 1,
"obj": 0
},
"zKsAXcOCNd9U": {
"mat": [
0.4,
0.4,
0.4,
0.6,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4
],
"nums": 1,
"obj": 0
},
"AyxF5UB7GyPC": {
"mat": [
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.6,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4
],
"nums": 1,
"obj": 0
},
"AD8Vjgv2zCjf": {
"mat": [
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.6,
0.6,
0.6,
0.6
],
"nums": 1,
"obj": 0
},
"VEhAvRf5SlqP": {
"mat": [
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.6,
0.4,
0.4,
0.4,
0.6,
0.4,
0.4,
0.6,
0.6
],
"nums": 1,
"obj": 0
},
"REemIAxKWPOr": {
"mat": [
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.5,
0.30000000000000004,
0.30000000000000004,
0.5,
0.5
],
"nums": 2,
"obj": 0
},
"ilzrt3XTmEGI": {
"mat": [
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.5,
0.30000000000000004,
0.30000000000000004,
0.5,
0.7
],
"nums": 2,
"obj": 0
},
"sq8XFCoHMsjh": {
"mat": [
0.20000000000000004,
0.20000000000000004,
0.20000000000000004,
0.20000000000000004,
0.20000000000000004,
0.20000000000000004,
0.20000000000000004,
0.20000000000000004,
0.20000000000000004,
0.20000000000000004,
0.20000000000000004,
0.4,
0.20000000000000004,
0.20000000000000004,
0.4,
0.6
],
"nums": 3,
"obj": 0
},
"Rkb9KvzIK8i6": {
"mat": [
2.7755575615628914e-17,
2.7755575615628914e-17,
2.7755575615628914e-17,
2.7755575615628914e-17,
2.7755575615628914e-17,
2.7755575615628914e-17,
2.7755575615628914e-17,
2.7755575615628914e-17,
2.7755575615628914e-17,
2.7755575615628914e-17,
2.7755575615628914e-17,
0.20000000000000004,
2.7755575615628914e-17,
0.20000000000000004,
0.20000000000000004,
0.4
],
"nums": 5,
"obj": 0
},
"zAQrZUdBtAul": {
"mat": [
2.7755575615628914e-17,
2.7755575615628914e-17,
2.7755575615628914e-17,
2.7755575615628914e-17,
2.7755575615628914e-17,
2.7755575615628914e-17,
2.7755575615628914e-17,
2.7755575615628914e-17,
2.7755575615628914e-17,
2.7755575615628914e-17,
2.7755575615628914e-17,
0.20000000000000004,
2.7755575615628914e-17,
2.7755575615628914e-17,
0.20000000000000004,
0.4
],
"nums": 5,
"obj": 0
},
"s0LDtVUcozVA": {
"mat": [
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0.10000000000000003,
0,
0,
0.10000000000000003,
0.30000000000000004
],
"nums": 6,
"obj": 0
},
"Ern8By1vuGjV": {
"mat": [
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
2.7755575615628914e-17,
0,
0,
2.7755575615628914e-17,
0.20000000000000004
],
"nums": 7,
"obj": 0
},
"hn1uGROIKjnM": {
"mat": [
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0.10000000000000003
],
"nums": 8,
"obj": 0
},
"4q7BkDtACkDx": {
"mat": [
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
2.7755575615628914e-17
],
"nums": 9,
"obj": 0
},
"WlEm9ZGsDOlN": {
"mat": [
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0
],
"nums": 10,
"obj": 0
},
"4d6y7SDP9r9c": {
"mat": [
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0
],
"nums": 11,
"obj": 0
},
"AkGcL0NHOVGy": {
"mat": [
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.5,
0.5,
0.5,
0.5
],
"nums": 2,
"obj": 0
},
"GHI69eo7oMwY": {
"mat": [
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.5,
0.5,
0.7,
0.7,
0.7,
0.7
],
"nums": 2,
"obj": 0
},
"MkvjgP0uimhW": {
"mat": [
0.20000000000000004,
0.20000000000000004,
0.20000000000000004,
0.4,
0.20000000000000004,
0.20000000000000004,
0.4,
0.4,
0.20000000000000004,
0.20000000000000004,
0.6,
0.6,
0.6,
0.6,
0.7999999999999999,
0.7999999999999999
],
"nums": 3,
"obj": 0
},
"gFuaeUebn0lE": {
"mat": [
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.5,
0.5,
0.5,
0.30000000000000004,
0.5,
0.5,
0.5
],
"nums": 2,
"obj": 0
},
"xplUg1qXcCDV": {
"mat": [
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.5,
0.5,
0.5,
0.5,
0.7,
0.7,
0.5
],
"nums": 2,
"obj": 0
},
"KNRZDFuT4X45": {
"mat": [
0.20000000000000004,
0.20000000000000004,
0.20000000000000004,
0.20000000000000004,
0.20000000000000004,
0.20000000000000004,
0.20000000000000004,
0.20000000000000004,
0.20000000000000004,
0.4,
0.4,
0.4,
0.4,
0.6,
0.7999999999999999,
0.6
],
"nums": 3,
"obj": 0
},
"G68Efl2ZE0U1": {
"mat": [
0.10000000000000003,
0.10000000000000003,
0.10000000000000003,
0.10000000000000003,
0.10000000000000003,
0.10000000000000003,
0.10000000000000003,
0.10000000000000003,
0.10000000000000003,
0.30000000000000004,
0.5,
0.5,
0.30000000000000004,
0.5,
0.8999999999999999,
0.7
],
"nums": 4,
"obj": 0
},
"JOAT6FV7VKKi": {
"mat": [
2.7755575615628914e-17,
2.7755575615628914e-17,
2.7755575615628914e-17,
2.7755575615628914e-17,
2.7755575615628914e-17,
2.7755575615628914e-17,
2.7755575615628914e-17,
2.7755575615628914e-17,
2.7755575615628914e-17,
0.20000000000000004,
0.4,
0.4,
0.20000000000000004,
0.4,
0.7999999999999999,
0.6
],
"nums": 5,
"obj": 0
},
"B25hZGGvUjZw": {
"mat": [
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4
],
"nums": 1,
"obj": 0
},
"mX0MuHDZJFVx": {
"mat": [
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004
],
"nums": 2,
"obj": 0
},
"GuOSUS3pciGR": {
"mat": [
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.6,
0.6,
0.4,
0.6,
0.6,
0.6
],
"nums": 1,
"obj": 0
},
"OaeOLRd0m1Nu": {
"mat": [
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4
],
"nums": 1,
"obj": 0
},
"jvceqG1K3aGV": {
"mat": [
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004,
0.30000000000000004
],
"nums": 2,
"obj": 0
},
"0iMT2yXLK0vj": {
"mat": [
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4,
0.4
],
"nums": 1,
"obj": 0
}
}

View File

@ -15,4 +15,3 @@ export const TAGS = [
export const IMAGES_FOLDER = path.join(PROJECT_ROOT, 'public', 'images');
export const MAX_SESSION_TIME = 60 * 30;

View File

@ -70,10 +70,9 @@ function uCaptchaBox(websiteKey) {
captchaBox.appendChild(btn);
checkbox.onclick = async function() {
/** @type {import('../shared/models/UserSession.js').UserSession} */
const session = await reloadSession(websiteKey);
qs('#ucaptcha-caption').innerHTML = `Select all squares with <b>${session.getImageTag()}s</b>`;
qs('#ucaptcha-caption').innerHTML = `Select all squares with <b>${session.imageTag}s</b>`;
const imageGrid = ce('table', {id: 'ucaptcha-grid'});
for (let i = 0; i < 4; i++) {
@ -90,8 +89,7 @@ function uCaptchaBox(websiteKey) {
}
btn.addEventListener('click', (e)=>{
verifyImage(session, imageGrid);
getImage(session, captchaBox);
verifyImage(session, captchaBox);
});
captchaContainer.appendChild(imageGrid);

View File

@ -1,16 +1,15 @@
import request from '../util/request.js';
/**
* @typedef {import('../../models/UserSession.js').UserSession} UserSession
* @typedef {import('../../models/Session.js').Session} Session
*/
/**
* Instantiate the uCaptcha loop
* @param {UserSession} session
* @param {Session} session
* @param {HTMLElement} captchaBox
*/
export default function instantiateLoop(session, captchaBox) {
console.log(captchaBox);
request(`/api/image?s=${session.getSessionId()}`, {_responseType: 'blob'})
export default function getImage(session, captchaBox) {
request(`/api/image?s=${session.sessionId}`, {_responseType: 'blob'})
.then((blob)=>{
const imageUrl = URL.createObjectURL(blob);
console.log(imageUrl);

View File

@ -1,5 +1,5 @@
import request from '../util/request.js';
import Session from '../../models/Session.js';
import {Session} from '../../models/Session.js';
/**
* @param {string} websiteKey

View File

@ -1,18 +1,21 @@
import request from '../util/request.js';
import UserSession from '../../models/UserSession.js';
import getImage from './getImage.js';
import {Session} from '../../models/Session.js';
import {querySelector as qs} from '../util/document.js';
/**
* @typedef {import('../../models/UserSession.js')} UserSession
* @typedef {import('../../models/Session.js').Session} Session
*/
/**
* Verify the image
* @param {UserSession} session
* @param {HTMLElement} captchaGrid
* @param {Session} session
* @param {HTMLElement} captchaBox
*/
export default async function verifyImage(session, captchaGrid) {
export default async function verifyImage(session, captchaBox) {
const captchaGrid = captchaBox.querySelector('#ucaptcha-grid');
captchaGrid.classList.add('blurred');
const tds = captchaGrid.querySelectorAll('td');
const selectedTds = [];
@ -28,7 +31,7 @@ export default async function verifyImage(session, captchaGrid) {
}
const body = {
's': session.getSessionId(),
's': session.sessionId,
'mat': mat,
};
@ -36,7 +39,9 @@ export default async function verifyImage(session, captchaGrid) {
session.deserialize(resp);
qs('#ucaptcha-caption')
.innerHTML = `Select all squares with <b>${session.getImageTag()}s</b>`;
.innerHTML = `Select all squares with <b>${session.imageTag}s</b>`;
getImage(session, captchaBox);
captchaGrid.classList.remove('blurred');

View File

@ -1,5 +1,6 @@
import fs from 'fs';
import {IMAGES_FOLDER} from '../R.js';
import {randomChoice} from '../helpers/utils.js';
/**
* @return {Promise<string>} Image file without extension
@ -8,10 +9,11 @@ export default () => {
return new Promise((resolve, reject)=>{
fs.readdir(IMAGES_FOLDER, (err, files)=>{
if (err) return reject(err);
return resolve(
files[
Math.floor(Math.random() * files.length)
].split('.')[0]);
const filename = randomChoice(files);
console.log(filename);
return resolve(filename);
});
});
};

View File

@ -13,25 +13,27 @@ import {Session} from '../models/Session.js';
* @return {Promise<Session>}
*/
export default async function initializeSession(websiteKey, cookies) {
const image = await pickRandomFile();
const image = (await pickRandomFile()).split('.')[0];
const imageTagId = 1; // TODO: Change later to be dynamic based on `image`
const sessionId = cookies[websiteKey] ? cookies[websiteKey] : randomBytes(8);
const sessionId = cookies[websiteKey] || randomBytes(8);
client.setex(sessionId, MAX_SESSION_TIME, JSON.stringify({
/** @type {import('../models/Session.js').IDBSession} */
const idbPayload = {
sessionId,
websiteKey,
image,
image: image,
obj: imageTagId,
score: 0.5,
}));
};
client.setex(sessionId, MAX_SESSION_TIME, JSON.stringify(idbPayload));
const session = new Session({
sessionId,
websiteKey,
imageTagId: TAGS[imageTagId],
});
const session = new Session();
session.sessionId = sessionId;
session.websiteKey = websiteKey;
session.imageTag = TAGS[imageTagId];
return session;
};

View File

@ -1,4 +1,3 @@
console.log(import.meta.url, ' is deprecated please use res.json() directly')
/**
* @param {import('express').Response} res
* @param {JSON} json
@ -6,5 +5,4 @@ console.log(import.meta.url, ' is deprecated please use res.json() directly')
export default (res, json) => {
res.setHeader('Content-Type', 'application/json');
res.send(`:)${JSON.stringify(json)}`);
return;
};

36
src/helpers/serveImage.js Normal file
View File

@ -0,0 +1,36 @@
import {client} from '../idb.js';
/**
* serveImage
* @param {string} sessionId
* @return {Promise<[Error, string]>}
*/
export default function serveImage(sessionId) {
return new Promise((resolve, reject)=>{
client.get(sessionId, (err, resp)=>{
if (err) {
reject(err);
}
if (!resp) {
// TODO: We need a way to communicate expired session back to client
resolve([
'Session expired',
null,
]);
}
/** @type {import('../models/Session.js').IDBSession} */
const result = JSON.parse(resp);
const {
image,
} = result;
console.log(image);
resolve([
null,
image + '.jpg',
]);
});
});
}

144
src/helpers/verifyImage.js Normal file
View File

@ -0,0 +1,144 @@
import fs from 'fs';
import path from 'path';
import {client} from '../idb.js';
import {PROJECT_ROOT, MAX_SESSION_TIME, TAGS} from '../R.js';
import {argmaxThresh} from './utils.js';
import pickRandomFile from './pickRandomFile.js';
import {Session} from '../models/Session.js';
// Anything above this value is considered a true selection within image matrix
const TOLERANCE = 0.6;
/**
* @typedef {Object} FileMat
* @property {Array<number>} mat Matrix of floats
* @property {number} nums Number of users that have seen this challenge
* @property {number} obj The object in the image
*/
/** @type {FileMat} */
const defaultMat = {
mat: Array(16).fill(0.5),
nums: 0,
obj: 0,
};
/**
* Get file matrix
* @param {string} image
* @return {FileMat}
*/
function getMat(image) {
const mats = JSON.parse(
fs.readFileSync(
path.join(PROJECT_ROOT, 'dev', 'mat.json'), {encoding: 'utf8'}));
return mats[image] || defaultMat;
}
/**
* Update the image mat
* @param {FileMat} imageMat
* @param {Array<number>} userMat
* @param {string} image
*/
function updateMat(imageMat, userMat, image) {
const updatedMat = [...imageMat.mat];
for (let i = 0; i < imageMat.mat.length; i++) {
const delta = 0.1;
if (userMat[i] === 1) {
if (updatedMat[i] + delta > 1) {
updatedMat[i] = 1.0;
} else {
updatedMat[i] += delta;
}
} else {
if (updatedMat[i] < delta) {
updatedMat[i] = 0.0;
} 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 {Array<number>} userMat
* @return {Promise<Session>}
*/
export default function verifyImage(sessionId, userMat) {
return new Promise((resolve, reject)=>{
client.get(sessionId, (err, resp)=>{
if (err) {
reject(err);
}
if (!resp) {
reject(new Error('Session expired'));
}
/** @type {import('../models/Session').IDBSession} */
const result = JSON.parse(resp);
console.log('Result: ', result);
let {image, score, websiteKey} = result;
const imageMat = getMat(image);
const trueArgmax = argmaxThresh(imageMat.mat, TOLERANCE).join(',');
const userArgmax = argmaxThresh(userMat, 1).join(',');
updateMat(imageMat, userMat, image);
console.log('trueArgmax: ', trueArgmax);
console.log('userArgmax: ', userArgmax);
const delta = imageMat.nums * 0.05;
if (userArgmax !== trueArgmax) {
// TODO: Decrease the score based on mat dispersion
// High variance = small reduction
// Low variance = high reduction
score -= delta;
} else {
score += delta;
}
pickRandomFile()
.then((newImageFilename)=>{
console.log('New image', newImageFilename);
// TODO: Make imageTagId dynamic
const newImageTagId = 1;
const update = JSON.stringify(
Object.assign(result, {
image: newImageFilename.split('.')[0],
obj: newImageTagId,
score,
}));
client.setex(sessionId, MAX_SESSION_TIME, update, (err)=>{
if (err) reject(err);
const session = new Session();
session.sessionId = sessionId;
session.websiteKey = websiteKey;
session.imageTag = TAGS[newImageTagId];
resolve(session);
});
});
});
});
}

View File

@ -1,19 +1,18 @@
/**
* Session storage in IDB
* User session model
* @property {string} [sessionId] User session ID
* @property {string} [websiteKey] Website ID
* @property {string} [image] Image URL for challenge
* @property {number} [obj] Image object ID
* @property {number} [score] How likely is the user to be a human?
* @property {string} [imageTag] Human readable text image tag
*/
export class Session {
/**
*
* @param {*} obj
*/
constructor(obj={}) {
Object.assign(this, obj);
*
*/
constructor() {
// Object.assign(this, obj);
}
/**
* Set session ID
* @param {string} id
@ -30,6 +29,7 @@ export class Session {
return this._sessionId;
}
/**
* Set session ID
* @param {string} id
@ -45,37 +45,67 @@ export class Session {
get websiteKey() {
return this._websiteKey;
}
/**
* Set the human readable image tag
* @param {string} imageTag
*/
set imageTag(imageTag) {
this._imageTag = imageTag;
}
/**
* @return {string}
*/
get imageTag() {
return this._imageTag;
}
/**
* Serialize the session into a JSON object
* @return {Array<any>}
*/
serialize() {
const {sessionId, websiteKey} = this;
const {
sessionId,
websiteKey,
imageTag,
} = this;
if (!sessionId || !websiteKey) {
throw Error();
}
return [sessionId, websiteKey];
return [
sessionId,
websiteKey,
imageTag,
];
}
/**
* Deserialize session data from JSON object
* @param {Array<any>} payload
*/
deserialize([sessionId, websiteKey]) {
Object.assign(this, {sessionId, websiteKey});
}
/**
*
* @param {*} sessionId
*/
async get(sessionId) {
if (!window) {
return import('util')
.then(({promisify}) => import('../idb.js')
.then(({client}) => promisify(client.get)(sessionId)));
}
// HTTP Logic here
// fetch(`/api/reload?k=${websiteKey}`)
deserialize([
sessionId,
websiteKey,
imageTag,
]) {
Object.assign(this, {
sessionId,
websiteKey,
imageTag,
});
}
}
export default Session;
/**
* @typedef {Object} IDBSession User session model in IDB
* @property {string} sessionId User session ID
* @property {string} websiteKey Website ID
* @property {string} imageTag Human readable text image tag
* @property {string} image Image URL for challenge
* @property {number} obj Image object ID
* @property {number} score How likely is the user to be a human?
*/

View File

@ -1,8 +1,10 @@
import {Router} from '../../helpers/express-router.js';
import {serveImage} from './verify.js';
import express from 'express';
import path from 'path';
import {IMAGES_FOLDER} from '../../R.js';
const router = new Router();
import serveImage from '../../helpers/serveImage.js';
const router = new express.Router();
export default router.get('/image', async (req, res) => {
const sessionId = req.query.s;
if (!sessionId) {
@ -10,9 +12,12 @@ export default router.get('/image', async (req, res) => {
return;
}
const [suspicious, filename] = await serveImage(sessionId);
if (suspicious) {
res.end();
const [error, filename] = await serveImage(sessionId);
if (error) {
res.json({
error,
});
return;
}

View File

@ -2,14 +2,14 @@ import express from 'express';
import sendJson from '../../helpers/sendJson.js';
import {MAX_SESSION_TIME} from '../../R.js';
import reloadSession from '../../helpers/reloadSession.js';
const router = express.Router();
const router = new express.Router();
/**
*
* @param {express.Request} req
* @param {express.Response} res
*/
const reloadHandler = async (req, res) => {
const reloadSessionHandler = async (req, res) => {
try {
const websiteKey = req.query.k;
if (!websiteKey) {
@ -23,7 +23,7 @@ const reloadHandler = async (req, res) => {
websiteKey, sessionId,
{httpOnly: true, maxAge: MAX_SESSION_TIME * 1000},
);
res.json(session);
sendJson(res, session.serialize());
} catch (e) {
console.error(e);
res.end();
@ -31,4 +31,4 @@ const reloadHandler = async (req, res) => {
};
export default router.get('/reload', reloadHandler);
export default router.get('/reload', reloadSessionHandler);

View File

@ -1,190 +1,19 @@
import fs from 'fs';
import path from 'path';
import {client} from '../../idb.js';
import {promisify} from 'util';
import {PROJECT_ROOT, MAX_SESSION_TIME, TAGS} from '../../R.js';
import {argmaxThresh} from '../../helpers/utils.js';
import pickRandomFile from '../../helpers/pickRandomFile.js';
import Session from '../../models/Session.js';
import express from 'express';
const router = express.Router();
import sendJson from '../../helpers/sendJson.js';
import verifyImage from '../../helpers/verifyImage.js';
const router = new express.Router();
/**
* @typedef {Object} FileMat
* @property {Array<number>} mat Matrix of floats
* @property {number} nums Number of users that have seen this challenge
* @property {number} obj The object in the image
* @param {express.Request} req
* @param {express.Response} res
*/
/** @type {FileMat} */
const defaultMat = {
mat: Array(16).fill(0.5),
nums: 0,
obj: 0,
};
/**
* Get file matrix
* @param {string} image
* @return {FileMat}
*/
function getMat(image) {
const mats = JSON.parse(
fs.readFileSync(
path.join(PROJECT_ROOT, 'dev', 'mat.json'), {encoding: 'utf8'}));
return mats[image] || defaultMat;
}
/**
* Update the image mat
* @param {FileMat} imageMat
* @param {Array<number>} userMat
* @param {string} image
*/
function updateMat(imageMat, userMat, image) {
const updatedMat = [...imageMat.mat];
for (let i = 0; i < imageMat.mat.length; i++) {
const delta = 0.1;
if (userMat[i] === 1) {
if (updatedMat[i] + delta > 1) {
updatedMat[i] = 1.0;
} else {
updatedMat[i] += delta;
}
} else {
if (updatedMat[i] < delta) {
updatedMat[i] = 0.0;
} 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 {Array<number>} userMat
* @return {Promise}
*/
async function verify(sessionId, userMat) {
const redisGet = promisify(client.get);
const session = await redisGet(sessionId)
.then((resp) => {
if (!resp) {
throw new Error('Session expired');
}
/** @type {Session} */
const result = JSON.parse(resp);
let {image, score, websiteKey} = result;
const imageMat = getMat(image);
const trueArgmax = argmaxThresh(imageMat.mat, 0.6).join(',');
const userArgmax = argmaxThresh(userMat, 1).join(',');
updateMat(imageMat, userMat, image);
console.log('trueArgmax', trueArgmax);
console.log('userArgmax', userArgmax);
const delta = imageMat.nums * 0.05;
if (userArgmax !== trueArgmax) {
// TODO: Decrease the score based on mat dispersion
// High variance = small reduction
// Low variance = high reduction
score -= delta;
} else {
score += delta;
}
return pickRandomFile().then((image)=>{
// TODO: Make me dynamic
const newImageTag = 1;
const update = JSON.stringify(
Object.assign(result, {
image: image,
obj: newImageTag,
}));
return promisify(client.setex)(sessionId, MAX_SESSION_TIME, update).then(()=>new Session({
sessionId, websiteKey, imageTag: TAGS[newImageTag],
}));
});
});
}
/**
* @typedef {import('../../models/Session.js').Session} IDBSession
*/
/**
* serveImage
* @param {string} sessionId
* @return {Promise}
*/
export function serveImage(sessionId) {
const redisGet = promisify(client.get);
return redisGet(sessionId)
.then((result) => {
if (!result) {
throw new Error('Session expired');
}
return result;
})
.then((result) => {
/** @type {Session} */
const sessionData = JSON.parse(result);
console.log(sessionData);
const {
// sessionId,
image,
// score,
} = sessionData;
// if (score < 0.1) {
// client.del(sessionId, (err)=>{
// if (err) return reject(err);
// return resolve([
// true,
// null,
// ]);
// });
// }
return [
null,
image + '.jpg',
];
});
}
export default router.post('/verify', async (req, res)=>{
const verifyImageHandler = async (req, res)=>{
const sessionId = req.body.s;
const mat = req.body.mat;
console.log(req.body);
const session = await verify(sessionId, mat);
res.json(session);
});
const session = await verifyImage(sessionId, mat);
sendJson(res, session.serialize());
};
export default router.post('/verify', verifyImageHandler);

View File

@ -20,10 +20,9 @@ if (process.env.NODE_ENV === 'development') {
});
}
//import apis from './routes/api/index.js';
import apiImage from './routes/api/image.js';
import apiVerify from './routes/api/verify.js';
import apiReload from './routes/api/reload.js';
app.use('/api', [apiImage,apiVerify,apiReload]);
app.use('/api', [apiImage, apiVerify, apiReload]);
export {app};